我刚刚开始使用Tk并在python中使用它。我设置了一个按钮,当你按下它时,它会在幕后做很多事情(比如两分钟)。我还设置了一个状态文本来显示正在发生的事情。我这样设置:
status_text = Tkinter.StringVar()
ttk.Button(mainframe, text="Stats!", command=go).grid(column=1, row=4)
def go(*args):
status_text.set("Logging in...")
do_lots_of_stuff()
status_text.set("Doing stuff #1...")
do_even_more_stuff()
status_text.set("Success!")
问题是,当你按下那个按钮时,窗口冻结,状态文本实际上从未改变。它看起来坏了,直到2-3分钟后所有的处理完成后才从这个状态出来。我如何使它不冻结和更新状态文本?
是时候学习多线程了!
发生的事情是GUI(主线程)正在等待方法返回,以便它可以继续更新接口。
你想让按钮的动作产生一个threading.Thread
,而不是在主线程中运行繁重的代码。你还需要创建一个Queue来访问其他线程的数据(因为发送GUI请求应该只从主线程发出)。
import threading, Queue #queue in 3.x
my_queue = Queue.Queue()
def go(*args):
my_thread = threading.Thread(target=function_that_does_stuff)
def function_that_does_stuff():
my_queue.put("Logging in...")
do_lots_of_stuff()
my_queue.put("Doing stuff #1...")
do_even_more_stuff()
my_queue.put("Success!")
那么你需要一个函数在更新事件发生时运行。
def OnUpdate(*args):
try:
status_text.set(my_queue.get())
except Queue.Empty:
pass
如果您控制了do_lots_of_stuff
,并且可以将其分成小块,则可以在事件队列上放置小作业来执行每个块。
例如,如果您的do_lots_of_stuff
正在从文件中读取行,则创建一个方法,读取一行,然后在事件队列中放置一个作业,以便在一两毫秒后调用自己。通过这种方式,事件循环继续运行,代码在每次迭代中进行一些处理。这是一种令人惊讶的有效技术,尽管它只有在你能够将你的"许多东西"分解成原子块的情况下才有效。
如果你不能这样做,你将不得不使用多个线程或多个进程。我个人更喜欢后者——多处理代码编写和调试(可以说)更容易。