如何在我的tk UI中创建一个按钮,使得程序在按下时不会冻结窗口时会执行大量操作?

时间:2011-07-15 22:07:16

标签: python user-interface tkinter

我刚开始使用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分钟后所有处理完成才会出现这种状态。如何使其不冻结并更新状态文本?

2 个答案:

答案 0 :(得分:1)

是时候学习多线程了!

发生的事情是GUI(主线程)正在等待方法返回,以便它可以继续更新界面。

您需要使按钮的操作产生threading.Thread,而不是在主线程中运行繁重的代码。您还需要创建一个队列来访问另一个线程中的数据(因为只能从主线程发送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

答案 1 :(得分:1)

如果您可以控制do_lots_of_stuff并且可以将其分成小块,则可以在事件队列上放置小作业来执行每个块。

例如,如果您do_lots_of_stuff正在从文件中读取行,则创建一个读取一行的方法,然后将作业放在事件队列上,以便在一两个ms后调用自身。这样,事件循环继续运行,并且您的代码在每次迭代时都会进行一些处理。这是一种非常有效的技术,但它只有在你能够将你的“大量东西”分解成原子块时才有效。

如果你不能这样做,你将不得不使用多个线程或多个进程。我个人更喜欢后者 - 多处理代码(可以说)编写和调试的难度较小。