Tkinter mainloop,之后和线程与时间密集型代码混淆
Tkinter mainloop, after and threading confusion with time intensive code
我有一个神经网络训练的工作代码。现在我想创建一个 TkInter GUI 以便能够例如即时更改参数。我想象它就像一个遥控器。
但是我正在努力获得响应式界面。一个 epoch 的训练需要几分钟,而我无法拦截。
我见过很多使用 .after() 方法来更新 GUI 上的时钟的示例,这很好,因为更新不需要几分钟。当回调需要几分钟时,我无法让它工作。
我用最少的代码重现了我的问题:
from time import sleep
import tkinter as tk
def trainEpoch(i):
print ("Training", i)
sleep(1) #in reality more like sleep(300)
def clickBtn():
print ("Button pressed")
root = tk.Tk()
btn = tk.Button(root, text="Click Me", command=clickBtn)
btn.grid(column=0, row=0)
for i in range (5):
trainEpoch(i)
print ("finished" )
root.mainloop()
此代码首先“训练”网络,然后创建响应式 TkInter window(我完全理解)。
明确地说,我希望有以下行为:
我希望 GUI 在训练开始之前打开,并在训练进行时可以点击。我不在乎它以后会不会被销毁。
这种问题需要线程吗?
您不能在与 tkinter 相同的线程中使用睡眠。由于 tkinter 是单线程的,您将阻止主循环执行任何操作,直到睡眠完成。
这是一个使用 after()
执行相同任务而不阻塞您的 tkinter 应用程序的示例。
import tkinter as tk
def train_epoch(i):
if i <= 5:
print("Training", i)
i += 1
root.after(2000, lambda i=i: train_epoch(i))
else:
print("finished")
def click_btn():
print("Button pressed")
root = tk.Tk()
tk.Button(root, text="Click Me", command=click_btn).grid(column=0, row=0)
train_epoch(1)
root.mainloop()
结果:
如果您需要使用线程,您可以与 tkinter 和线程函数之间的全局命名空间中的变量进行交互。
看这个例子:
import tkinter as tk
import threading
import time
def train_epoch():
global some_var
while some_var <= 5:
print(some_var)
time.sleep(0.5)
def click_btn():
global some_var
some_var += 1
print("Button pressed")
root = tk.Tk()
some_var = 0
tk.Button(root, text="Click Me", command=click_btn).pack()
thread = threading.Thread(target=train_epoch)
thread.start()
root.mainloop()
我有一个神经网络训练的工作代码。现在我想创建一个 TkInter GUI 以便能够例如即时更改参数。我想象它就像一个遥控器。 但是我正在努力获得响应式界面。一个 epoch 的训练需要几分钟,而我无法拦截。
我见过很多使用 .after() 方法来更新 GUI 上的时钟的示例,这很好,因为更新不需要几分钟。当回调需要几分钟时,我无法让它工作。
我用最少的代码重现了我的问题:
from time import sleep
import tkinter as tk
def trainEpoch(i):
print ("Training", i)
sleep(1) #in reality more like sleep(300)
def clickBtn():
print ("Button pressed")
root = tk.Tk()
btn = tk.Button(root, text="Click Me", command=clickBtn)
btn.grid(column=0, row=0)
for i in range (5):
trainEpoch(i)
print ("finished" )
root.mainloop()
此代码首先“训练”网络,然后创建响应式 TkInter window(我完全理解)。
明确地说,我希望有以下行为:
我希望 GUI 在训练开始之前打开,并在训练进行时可以点击。我不在乎它以后会不会被销毁。
这种问题需要线程吗?
您不能在与 tkinter 相同的线程中使用睡眠。由于 tkinter 是单线程的,您将阻止主循环执行任何操作,直到睡眠完成。
这是一个使用 after()
执行相同任务而不阻塞您的 tkinter 应用程序的示例。
import tkinter as tk
def train_epoch(i):
if i <= 5:
print("Training", i)
i += 1
root.after(2000, lambda i=i: train_epoch(i))
else:
print("finished")
def click_btn():
print("Button pressed")
root = tk.Tk()
tk.Button(root, text="Click Me", command=click_btn).grid(column=0, row=0)
train_epoch(1)
root.mainloop()
结果:
如果您需要使用线程,您可以与 tkinter 和线程函数之间的全局命名空间中的变量进行交互。
看这个例子:
import tkinter as tk
import threading
import time
def train_epoch():
global some_var
while some_var <= 5:
print(some_var)
time.sleep(0.5)
def click_btn():
global some_var
some_var += 1
print("Button pressed")
root = tk.Tk()
some_var = 0
tk.Button(root, text="Click Me", command=click_btn).pack()
thread = threading.Thread(target=train_epoch)
thread.start()
root.mainloop()