user15854338
user15854338

Reputation: 17

Tkinter GUI in for loop

I'm doing a tkinter GUI for my program and I have to display some real time data. I made a simple program (below) to demonstrate my problem on a simple case. I'm actually plotting some data every iteration of my for loop so I can observe data while the program in still calculating. Note that the real program si calculating a bit slower and have more iterations.

Now I would like to add 2 buttons (one to pause the program and one to continue) and a label (diplay variable k so i know where my program is), but I am unable to do it.

I've already lost a lot of time on it so if anyone have a hint or a solution i would love to see it.

import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.pyplot as plt
from matplotlib import style

def func_A(a, x):
    import numpy
    data_x = numpy.arange(0, x)
    data_y = a * numpy.sin(data_x/5)
    return data_x, data_y

a = 1

root = tk.Tk()
root.title("Graph")
root.geometry("800x400")

fig = plt.figure(figsize=(5, 5), dpi=100)
canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

plt.grid("both")
style.use("ggplot")
for k in range(0, 100):
    data_x, data_y = func_A(a, k)
    print("iteration", k)
    print("data_x", data_x)
    print("data_y", data_y)
    if k == 0:
        ax1 = plt.subplot(111)
        line1, = ax1.plot([0], [0])
    else:
        line1.set_xdata(data_x)
        line1.set_ydata(data_y)

    ax1.set_ylim([-1, 1])
    ax1.set_xlim([0, 100])
    plt.grid("both")
    canvas.draw()
    canvas.flush_events()
root.mainloop()

Upvotes: 0

Views: 1494

Answers (2)

Jeff Secor
Jeff Secor

Reputation: 61

add a check box to enable streaming

import customtkinter as ct
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

import numpy as np
import subprocess
import time



ct.set_appearance_mode('dark')
ct.set_default_color_theme('green')
root = ct.CTk()
width=int(0.9*root.winfo_screenwidth())
height=int(0.75*root.winfo_screenheight())
root.geometry(f'{width}x{height}')
#root.geometry('1500x1200')
frame=ct.CTkFrame(master=root)
frame.pack(pady=10,padx=5,fill='both', expand=True)


def plot():
    plotButton.configure(text=f'{np.random.randint(0,100)}')
    ax[1].clear()
    ax[1].scatter(x=np.random.randint(0,10,10),y=np.random.randint(0,10,10))
    canvas.draw()
    canvas.flush_events()

    
def streamSpec():
    while streamStop.get():
        plotButton.configure(text=f'{np.random.randint(0,100)}')
        ax[1].clear()
        ax[1].scatter(x=np.random.randint(0,10,10),y=np.random.randint(0,10,10))
        canvas.draw()
        canvas.flush_events()
        root.after(10,plot)

plotButton=ct.CTkButton(master=frame,text='plot',command=plot)
plotButton.grid(pady=10)

stream=ct.CTkButton(master=frame,text='stream',command=streamSpec)
stream.grid(pady=10)

streamStop=ct.CTkCheckBox(master=frame,text='Enable Stream')
streamStop.grid(pady=10)



fig,ax=plt.subplots(2,1)
plt.close(fig)
canvas=FigureCanvasTkAgg(fig,master=frame)
canvas.get_tk_widget().grid(padx=10,pady=10,row=0,column=5,columnspan=4,rowspan=6)



root.mainloop()

Upvotes: 0

acw1668
acw1668

Reputation: 46678

To add pause/resume function:

  • create a frame to hold the progress label and the two buttons: pause and resume
  • create a tkinter BooleanVar() to store the pause/resume state
  • move the update plot code inside a function, e.g. update_plot()
  • use .after() to replace the for loop to call update_plot() periodically
  • inside update_plot(), check the pause/resume state to determine whether to update the plot or not

Below is a modified example based on your code:

import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.pyplot as plt
from matplotlib import style
import matplotlib
matplotlib.use("Agg")

root = tk.Tk()
root.title("Graph")
#root.geometry("800x400")

# progress label, pause and resume buttons
frame = tk.Frame(root)
frame.pack(fill="x", side=tk.TOP)

progress = tk.Label(frame)
progress.pack(side="left")

is_paused = tk.BooleanVar()  # variable to hold the pause/resume state
tk.Button(frame, text="Pause", command=lambda: is_paused.set(True)).pack(side="right")
tk.Button(frame, text="Resume", command=lambda: is_paused.set(False)).pack(side="right")

# the plot

fig = plt.figure(figsize=(10, 5), dpi=100)

canvas = FigureCanvasTkAgg(fig, master=root)
toolbar = NavigationToolbar2Tk(canvas, root)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

plt.grid("both")
style.use("ggplot")

a = 1
ax1 = plt.subplot(111)
line1, = ax1.plot([0], [0])

def func_A(a, x):
    import numpy
    data_x = numpy.arange(0, x)
    data_y = a * numpy.sin(data_x/5)
    return data_x, data_y

# function to update ploat
def update_plot(k=0):
    if not is_paused.get():
        progress["text"] = f"iteration: {k}"

        data_x, data_y = func_A(a, k)
        #print("iteration", k)
        #print("data_x", data_x)
        #print("data_y", data_y)

        line1.set_xdata(data_x)
        line1.set_ydata(data_y)

        ax1.set_ylim([-1, 1])
        ax1.set_xlim([0, 100])
        plt.grid("both")
        canvas.draw()
        canvas.flush_events()
        k += 1
    if k <= 100:
        # update plot again after 10ms. You can change the delay to whatever you want
        root.after(10, update_plot, k)

update_plot() # start updating plot
root.mainloop()

Upvotes: 1

Related Questions