如何在 Python 中的每次迭代中使用 for 循环内的 TopLevel Tkinter 显示和更新包含结果的消息或标签?
How to Display and Update a Message or Label containg results using TopLevel Tkinter inside a for loop at every iteration in Python?
我正在使用 tkinter 构建 GUI Python 代码。我有一个在 for 循环内求解的 CSTR 模型;值在 for 循环内更新。我有四个正在更新的参数 T、Ca、Tc、q。我正在使用 TopLevel() 在 for 循环内的每次迭代中显示结果。换句话说,我希望每次迭代的结果在结果显示在显示器上时显示出来,一旦我进入另一个交互,我想再次打开一个新的 window 文件并使用更新值for 循环中的新计算值。我在 for 循环的迭代中使用了 destroy 函数。
请在下面找到我的代码(为了简单起见,我附上了部分代码):
# Time Interval (min)
t = np.linspace(0,800,401) # every 2 seconds
duration=8000 # I also tried 2000,4000,6000,1000,etc.
# Simulate CSTR
for i in range(len(t)-1):
# simulate one time period (0.02 sec each loop)
ts = [t[i],t[i+1]]
#read CSTR Process T and Ca (to be controlled)
y = odeint(cstr,x0,ts,args=(u[i],u2[i],Tf,Caf))
# retrieve measurements
Ca[i+1] = y[-1][0]
T[i+1] = y[-1][1]
# insert measurement
m.T.MEAS = T[i+1]
m.Ca.MEAS=Ca[i+1]
#run MPC controller
# solve MPC
m.solve(disp=True)
# retrieve new Tc and q values
u[i+1] = m.Tc.NEWVAL
u2[i+1]=m.q.NEWVAL
#run optimization
x=optimizer(numa,numb,numc,numd,Tsp[i+1],Casp[i+1],u[i+1],u2[i+1],numi,numj,nume,numf,numg,numh,numk,numl) # or put T and Ca (not sp)
#updating the setpoints of the controller from the optimizer
#Set the setpoint temperature equal to the optimized Temperature
Tsp[i+1]=x[1]
m.T.SPHI = Tsp[i+1] + DT
m.T.SPLO = Tsp[i+1] - DT
#Set the setpoint conc. equal to the optimized conc.
Casp[i+1]=x[4]
m.Ca.SPHI = Casp[i+1] + DT
m.Ca.SPLO = Casp[i+1] - DT
# retrieve new Tc and q values
#update Tc and q values from optimizer as targets
f[i]=m.Tc.NEWVAL #MPC
f2[i]=x[2] #Optimizer (target)
z[i]=m.q.NEWVAL #MPC
z2[i]=x[3] #Optimizer (target)
x2=optimize(f[i],f2[i],nume,numf)# MINIMIZES THE ERROR DIFFERENCE BETWEEN MPC AND OPTIMIZER Tc value
x3=optimize2(z[i],z2[i],numg,numh)# MINIMIZES THE ERROR DIFFERENCE BETWEEN MPC AND OPTIMIZER q value
#updating the Tc and q MV values
u[i+1] = x2
u2[i+1]=x3
# update initial conditions
x0[0] = Ca[i+1]
x0[1] = T[i+1]
#to show the results on GUI
top1=Toplevel()
output1=" q optimum= %d m^3/s \n"%(z[i]) #takes the output q of MPC
output1+="q target= %d m^3/s \n"%(z2[i])#takes the output of the optimizer
output1+="Tc optimum= %d K \n"%(f[i])#takes the output Tc of MPC
output1+="Tc target= %d K \n"%(f2[i])#takes the output of the optimizer
top1.title('Manipulated Variables')
Message(top1,text=output1,padx=30,pady=30).pack()
top1.after(duration,top1.destroy)
实际发生的情况是 window 仅在 for 循环的所有运行完成后才打开(花费大量时间),而不是在 for 循环中的每个 iteration/calculation 都打开!如何在每次迭代时在 for 循环内显示 window 而不是在所有循环完成后显示?是否有某种功能或我可以手动强制显示 window 的东西?例如,我能够使用 plt 模块显示绘图,该模块在 for 循环内的每次迭代中都会更新,我想使用 toplevel() 来做完全相同的事情。
有什么建议吗?
谢谢。
在 GUI 中,您的代码基本上是 GUI 工具包事件循环中的 guest(tkinter 中的 mainloop
)。
所以你不能在 GUI 程序中有一个 long-运行ning 循环;您必须以不同的方式构建代码。例如,假设您有这样一个循环;
# *Long* list of data
data = [ ... ]
# storage for results
result = []
for item in data:
rv = process(item)
result.append(rv)
如果您在 GUI 程序中 运行 这样一个循环,它会中断事件流。实际上,它会冻结 GUI 并且渲染没有响应。
因此在 tkinter GUI 中,您必须在超时处理程序中进行计算。下面是将长时间的 运行ning 作业分成可以在 GUI 的事件循环内处理的小块的原理草图。
import tkinter as tk
from tkinter import messagebox
# *Long* list of data
data = [ ... ]
# storage for results
result = []
index = 0
def iteration():
'''Handles one iteration in the event loop.'''
rv = process(data[index])
result.append(rv)
index += 1
if index < len(data):
root.after(100, iteration)
else:
messagebox.showinfo('Calculations', 'Finished!')
def create_UI():
# Create and place the widgets here.
pass
if __name__ == '__main__':
root = tk.Tk(None)
widgets = create_UI(root)
root.after(100, iteration)
root.mainloop()
如您所见,在这种情况下,您的代码结构必须不同。
请注意,在 CPython 上一次只能 一个 线程可以执行 Python 字节码,因此使用线程并不是一个很好的解决方案这个。特别是因为很多 GUI 工具包不是线程安全的,所以你应该只从一个线程调用 GUI 函数。
另一种解决方案是 运行 在不同的程序中进行计算。然后该程序可以使用 Queue
与 GUI 通信。您必须在 GUI 中使用超时功能来定期监视队列并显示结果。这不是一个更简单的解决方案,但可能会提供更高的性能,您可以将计算分成几个过程。
编辑:
请注意,在第一个解决方案中,每次迭代只需要相对较少的时间,例如100 毫秒。如果需要更长的时间,GUI 会感觉反应迟钝。如果这样做不可行,则使用第二种解决方案(单独的程序)。
对于这个问题,我真的没有方便的参考。但是一个简单的模式是这样的;
你编写了两个程序。
第一个程序是一个简单的 Python 脚本,它在常规的 for 循环中进行计算。唯一特别的是,在每次迭代结束时,它会将 T、Ca、Tc 和 q 的值写入文件。
第二个程序是Python tkinter 程序,它使用after
方法检查上述文件的修改时间。如果检测到文件已更改,它会读取文件并更新其显示。
这种方法完全解耦进行计算和显示中间结果的问题,这意味着您可以单独编写和测试它们。
我认为此解决方案的通用名称称为 "file watcher"。这个解决方案太常见了,我不记得我第一次说的地方了。
我正在使用 tkinter 构建 GUI Python 代码。我有一个在 for 循环内求解的 CSTR 模型;值在 for 循环内更新。我有四个正在更新的参数 T、Ca、Tc、q。我正在使用 TopLevel() 在 for 循环内的每次迭代中显示结果。换句话说,我希望每次迭代的结果在结果显示在显示器上时显示出来,一旦我进入另一个交互,我想再次打开一个新的 window 文件并使用更新值for 循环中的新计算值。我在 for 循环的迭代中使用了 destroy 函数。
请在下面找到我的代码(为了简单起见,我附上了部分代码):
# Time Interval (min)
t = np.linspace(0,800,401) # every 2 seconds
duration=8000 # I also tried 2000,4000,6000,1000,etc.
# Simulate CSTR
for i in range(len(t)-1):
# simulate one time period (0.02 sec each loop)
ts = [t[i],t[i+1]]
#read CSTR Process T and Ca (to be controlled)
y = odeint(cstr,x0,ts,args=(u[i],u2[i],Tf,Caf))
# retrieve measurements
Ca[i+1] = y[-1][0]
T[i+1] = y[-1][1]
# insert measurement
m.T.MEAS = T[i+1]
m.Ca.MEAS=Ca[i+1]
#run MPC controller
# solve MPC
m.solve(disp=True)
# retrieve new Tc and q values
u[i+1] = m.Tc.NEWVAL
u2[i+1]=m.q.NEWVAL
#run optimization
x=optimizer(numa,numb,numc,numd,Tsp[i+1],Casp[i+1],u[i+1],u2[i+1],numi,numj,nume,numf,numg,numh,numk,numl) # or put T and Ca (not sp)
#updating the setpoints of the controller from the optimizer
#Set the setpoint temperature equal to the optimized Temperature
Tsp[i+1]=x[1]
m.T.SPHI = Tsp[i+1] + DT
m.T.SPLO = Tsp[i+1] - DT
#Set the setpoint conc. equal to the optimized conc.
Casp[i+1]=x[4]
m.Ca.SPHI = Casp[i+1] + DT
m.Ca.SPLO = Casp[i+1] - DT
# retrieve new Tc and q values
#update Tc and q values from optimizer as targets
f[i]=m.Tc.NEWVAL #MPC
f2[i]=x[2] #Optimizer (target)
z[i]=m.q.NEWVAL #MPC
z2[i]=x[3] #Optimizer (target)
x2=optimize(f[i],f2[i],nume,numf)# MINIMIZES THE ERROR DIFFERENCE BETWEEN MPC AND OPTIMIZER Tc value
x3=optimize2(z[i],z2[i],numg,numh)# MINIMIZES THE ERROR DIFFERENCE BETWEEN MPC AND OPTIMIZER q value
#updating the Tc and q MV values
u[i+1] = x2
u2[i+1]=x3
# update initial conditions
x0[0] = Ca[i+1]
x0[1] = T[i+1]
#to show the results on GUI
top1=Toplevel()
output1=" q optimum= %d m^3/s \n"%(z[i]) #takes the output q of MPC
output1+="q target= %d m^3/s \n"%(z2[i])#takes the output of the optimizer
output1+="Tc optimum= %d K \n"%(f[i])#takes the output Tc of MPC
output1+="Tc target= %d K \n"%(f2[i])#takes the output of the optimizer
top1.title('Manipulated Variables')
Message(top1,text=output1,padx=30,pady=30).pack()
top1.after(duration,top1.destroy)
实际发生的情况是 window 仅在 for 循环的所有运行完成后才打开(花费大量时间),而不是在 for 循环中的每个 iteration/calculation 都打开!如何在每次迭代时在 for 循环内显示 window 而不是在所有循环完成后显示?是否有某种功能或我可以手动强制显示 window 的东西?例如,我能够使用 plt 模块显示绘图,该模块在 for 循环内的每次迭代中都会更新,我想使用 toplevel() 来做完全相同的事情。
有什么建议吗?
谢谢。
在 GUI 中,您的代码基本上是 GUI 工具包事件循环中的 guest(tkinter 中的 mainloop
)。
所以你不能在 GUI 程序中有一个 long-运行ning 循环;您必须以不同的方式构建代码。例如,假设您有这样一个循环;
# *Long* list of data
data = [ ... ]
# storage for results
result = []
for item in data:
rv = process(item)
result.append(rv)
如果您在 GUI 程序中 运行 这样一个循环,它会中断事件流。实际上,它会冻结 GUI 并且渲染没有响应。
因此在 tkinter GUI 中,您必须在超时处理程序中进行计算。下面是将长时间的 运行ning 作业分成可以在 GUI 的事件循环内处理的小块的原理草图。
import tkinter as tk
from tkinter import messagebox
# *Long* list of data
data = [ ... ]
# storage for results
result = []
index = 0
def iteration():
'''Handles one iteration in the event loop.'''
rv = process(data[index])
result.append(rv)
index += 1
if index < len(data):
root.after(100, iteration)
else:
messagebox.showinfo('Calculations', 'Finished!')
def create_UI():
# Create and place the widgets here.
pass
if __name__ == '__main__':
root = tk.Tk(None)
widgets = create_UI(root)
root.after(100, iteration)
root.mainloop()
如您所见,在这种情况下,您的代码结构必须不同。
请注意,在 CPython 上一次只能 一个 线程可以执行 Python 字节码,因此使用线程并不是一个很好的解决方案这个。特别是因为很多 GUI 工具包不是线程安全的,所以你应该只从一个线程调用 GUI 函数。
另一种解决方案是 运行 在不同的程序中进行计算。然后该程序可以使用 Queue
与 GUI 通信。您必须在 GUI 中使用超时功能来定期监视队列并显示结果。这不是一个更简单的解决方案,但可能会提供更高的性能,您可以将计算分成几个过程。
编辑:
请注意,在第一个解决方案中,每次迭代只需要相对较少的时间,例如100 毫秒。如果需要更长的时间,GUI 会感觉反应迟钝。如果这样做不可行,则使用第二种解决方案(单独的程序)。
对于这个问题,我真的没有方便的参考。但是一个简单的模式是这样的;
你编写了两个程序。
第一个程序是一个简单的 Python 脚本,它在常规的 for 循环中进行计算。唯一特别的是,在每次迭代结束时,它会将 T、Ca、Tc 和 q 的值写入文件。
第二个程序是Python tkinter 程序,它使用after
方法检查上述文件的修改时间。如果检测到文件已更改,它会读取文件并更新其显示。
这种方法完全解耦进行计算和显示中间结果的问题,这意味着您可以单独编写和测试它们。
我认为此解决方案的通用名称称为 "file watcher"。这个解决方案太常见了,我不记得我第一次说的地方了。