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