pythontkinterreal-time-updates

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()

Solution

  • To add pause/resume function:

    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()