0

I want to have two live plots on two pages in a tkinter GUI with buttons to stop the live updating. So far I've managed to create one live plot in my first page. However, I have problems with creating threads inside other functions.

api = csb.SeaBreezeAPI()
devices = list_devices() #to get the serial number of the device

spec = sb.Spectrometer.from_serial_number("FLMS15412")

xs = spec.wavelengths()
ys = spec.intensities()

fig1 = Figure(figsize=(10,6),tight_layout=True)
ax1 = fig1.add_subplot(111)

root = tk.Tk()
root.configure(background = 'light blue')
root.geometry("1400x800")

canvas1=FigureCanvasTkAgg(fig1,master=root)
canvas1.get_tk_widget().place(x=5,y=55) 

cond = False
def starts():                 
    root.after(1,starts)

cond = False
def plot_data():
    if (cond == True):
        ax1.cla()
        ax1.plot(spec.wavelengths(),spec.intensities
        (correct_dark_counts=True),'w',linewidth=1.0)
        ax1.set_xlabel('Wavelength (nm)')
        ax1.set_ylabel('Intensity (a.u.)')
        ax1.set_xlim(200,1000)
        ax1.set_ylim(0,60000) 
        ax1.set_facecolor('#1B1B2A')
        ax1.grid(color='w',linestyle='--',linewidth=0.2)
        canvas1.draw()
    root.after(1,plot_data)    
   
def plot_start():
    global cond
    cond = True

def plot_stop():
    global cond
    cond = False

root.update()
startplot = tk.Button(root, text = "Start", font = ('calibri', 12), command = lambda: plot_start())
startplot.place(x=5,y=25)

root.update()
stopplot = tk.Button(root, text = "Stop", font = ('calibri', 12), command = lambda: plot_stop())
stopplot.place(x=65,y=25)

root.update()
start = tk.Button(root, text = "Stage", font = ('calibri', 12), 
command=threading.Thread(target=plot_data).start())
start.place(x=125,y=25)

threading.Thread(target=plot_data).start()

root.after(1,plot_data)
root.mainloop()

This is a minimal code for the live plot in a single page. I want to create another page with another live plot. How can I do that with tkinter. Any help would be appreciated.

  • You can use TopLevel and pretty much copy the code maybe? – Matiiss Apr 14 '21 at 17:17
  • If I use a TopLevel, isn't it still be in the mainloop? Can I use another thread in a toplevel window? –  Apr 14 '21 at 17:23
  • Change `command = threading.Thread(...).start()` to `command = threading.Thread(...).start` – TheLizzard Apr 14 '21 at 17:26
  • Also don't use threading when using tkinter. Some times tkinter crashes if you try to access it from a different thread. – TheLizzard Apr 14 '21 at 17:28
  • But it shouldn't be too much of a problem You could still change widget attributes from a thread while keeping all widgets in the same thread (including windows) – Matiiss Apr 14 '21 at 17:30
  • @NicoleWaves you may are intrested in [this](https://stackoverflow.com/q/63414254/13629335) question. It uses a different approach for threading as the canonical way to use a `queue.Queue()` and check for *data* with `.after(*ms, *func)` in a interval. Anyway, there are a bunch of questions for this topic and your question seems way to broad. Please try for yourself and come back with a specific problem. kind regards. – Thingamabobs Apr 14 '21 at 17:50
  • @Matiiss would you be kindly able to provide an example of how to place a live plot in a toplevel/popup window? I think my answer lies there –  Apr 14 '21 at 18:26

1 Answers1

0

Here is an example of how You can plot values to windows (not sure if this is what @TheLizzard said not to do, but this works (at least it seems so)):

from tkinter import Tk, Toplevel, Label, StringVar, Button
from _thread import start_new_thread
from time import sleep


def update_root():
    counter = 0
    while True:
        root_var.set(f'{counter}')
        sleep(0.2)
        counter += 1


def update_tp():
    counter = 0
    while True:
        tp_var.set(f'{counter}')
        sleep(0.2)
        counter += 1


def second_window():
    tp = Toplevel(root)
    tp.title('Window 2')
    tp_label = Label(tp, textvariable=tp_var, font='default 20 normal')
    tp_label.pack(padx=200, pady=50)


root = Tk()
root.title('Window 1')

root_var = StringVar()
root_label = Label(root, textvariable=root_var, font='default 20 normal')
root_label.pack(padx=200, pady=50)

Button(root, text='Open other window', command=second_window).pack()

tp_var = StringVar()

start_new_thread(update_root, ())
start_new_thread(update_tp, ())

root.mainloop()

Some of the code is just for the looks, but the main part is the two functions. They run on separate threads and just update the textvariable which makes it pretty easy. Also remember to put some pause between loops (like I have done with sleep(0.2)) otherwise running just the while loop will freeze the GUI (values will be updated but it won't be possible to interact with it)

Matiiss
  • 5,970
  • 2
  • 12
  • 29
  • Why are you using `_thread.start_new_thread` instead of `threading.Thread`? – TheLizzard Apr 14 '21 at 19:23
  • @TheLizzard because that is the one I know how to use better; I have looked into threading too but using `_thread` is way easier in this case (at least for me) however how to start a thread is the only thing I know about `_thread` – Matiiss Apr 14 '21 at 19:51
  • @Matiiss this is very helpful, and exactly what I want! Can we also add a button to the first page so that once it is pressed the second page pops up? –  Apr 15 '21 at 16:28
  • @NicoleWaves yes it is possible but do You want the thread to run from the start or only when the window opens? actually I think it may be easier and perhaps better if the other thread run from the beginning too – Matiiss Apr 15 '21 at 16:30
  • @NicoleWaves ok so I edited the code should work now – Matiiss Apr 15 '21 at 16:34
  • @Matiiss When the button is pressed from the first page, the second page should popup with a live updating plot. I should have a live updating plot in the first page too. Is it possible? –  Apr 15 '21 at 16:35
  • @NicoleWaves already done (from tkinter side at least) – Matiiss Apr 15 '21 at 16:35
  • @Matiiss Many thanks!! Now it works exactly as I wanted :) –  Apr 15 '21 at 16:38
  • @Matiiss what modifications should I do if I want to start the second thread only when the window opens? Or can I start the second thread with a button? because I want to start it multiple times –  Apr 19 '21 at 16:10
  • @NicoleWaves I suggest You use `threading` module for that (I yet have to learn about it) but I suppose it will be easier to start with that than `_thread` maybe I will myself try doing that but then it will take a while – Matiiss Apr 19 '21 at 16:33