for 循环中的 Tkinter GUI

问题描述

我正在为我的程序做一个 tkinter GUI,我必须显示一些实时数据。我做了一个简单的程序(如下)来在一个简单的案例中演示我的问题。我实际上是在 for 循环的每次迭代中绘制一些数据,这样我就可以在程序仍在计算时观察数据。请注意,实际程序 si 计算速度稍慢,并且迭代次数更多。

现在我想添加 2 个按钮(一个是暂停程序,一个是继续)和一个标签显示变量 k,以便我知道我的程序在哪里),但我无法做到。

我已经在这上面浪费了很多时间,所以如果有人有任何提示解决方案,我很乐意看到。

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

解决方法

添加暂停/恢复功能:

  • 创建一个框架来保存进度标签和两个按钮:暂停和恢复
  • 创建一个 tkinter BooleanVar() 来存储暂停/恢复状态
  • 在函数内移动更新绘图代码,例如update_plot()
  • 使用 .after() 替换 for 循环定期调用 update_plot()
  • update_plot() 中,检查暂停/恢复状态以确定是否更新绘图

以下是根据您的代码修改的示例:

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