Tkinter:如何使用线程来防止主事件循环“冻结". [英] Tkinter: How to use threads to preventing main event loop from "freezing"

查看:91
本文介绍了Tkinter:如何使用线程来防止主事件循环“冻结".的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有开始"按钮和进度条的小型GUI测试.所需的行为是:

I have a small GUI test with a "Start" button and a Progress bar. The desired behavior is:

  • 点击开始
  • 进度条振荡5秒钟
  • 进度栏停止

观察到的行为是开始"按钮冻结5秒钟,然后显示进度栏(无振荡).

The observed behavior is the "Start" button freezes for 5 seconds, then a Progressbar is displayed (no oscillation).

到目前为止,这是我的代码:

Here is my code so far:

class GUI:
    def __init__(self, master):
        self.master = master
        self.test_button = Button(self.master, command=self.tb_click)
        self.test_button.configure(
            text="Start", background="Grey",
            padx=50
            )
        self.test_button.pack(side=TOP)

    def progress(self):
        self.prog_bar = ttk.Progressbar(
            self.master, orient="horizontal",
            length=200, mode="indeterminate"
            )
        self.prog_bar.pack(side=TOP)

    def tb_click(self):
        self.progress()
        self.prog_bar.start()
        # Simulate long running process
        t = threading.Thread(target=time.sleep, args=(5,))
        t.start()
        t.join()
        self.prog_bar.stop()

root = Tk()
root.title("Test Button")
main_ui = GUI(root)
root.mainloop()

根据Bryan Oakley的信息,此处,我知道我需要使用线程.我尝试创建一个线程,但是我猜测由于该线程是从主线程中启动的,因此没有帮助.

Based on the information from Bryan Oakley here, I understand that I need to use threads. I tried creating a thread, but I'm guessing that since the thread is started from within the main thread, it doesn't help.

我的想法是将逻辑部分放在不同的类中,并从该类中实例化GUI,类似于A. Rodas的示例代码

I had the idea to place the logic portion in a different class, and instantiate the GUI from within that class, similar to the example code by A. Rodas here.

我的问题:

我不知道如何编写代码,以便执行以下命令:

I can't figure out how to code it so that this command:

self.test_button = Button(self.master, command=self.tb_click)

调用另一个类中的函数.这是一件坏事吗,甚至有可能吗?我将如何创建可以处理self.tb_click的第二个类?我尝试遵循A. Rodas的示例代码,该示例代码效果很好.但是我无法弄清楚在按钮小部件触发动作的情况下如何实现他的解决方案.

calls a function that is located in the other class. Is this a Bad Thing to do or is it even possible? How would I create a 2nd class that can handle the self.tb_click? I tried following along to A. Rodas' example code which works beautifully. But I cannot figure out how to implement his solution in the case of a Button widget that triggers an action.

如果我应该从单个GUI类中处理线程,那么如何创建一个不会干扰主线程的线程?

If I should instead handle the thread from within the single GUI class, how would one create a thread that doesn't interfere with the main thread?

推荐答案

在主线程中加入新线程时,它将等到线程完成后,即使使用多线程,GUI也将阻塞.

When you join the new thread in the main thread, it will wait until the thread finishes, so the GUI will block even though you are using multithreading.

如果要将逻辑部分放在不同的类中,则可以直接对Thread进行子类化,然后在按下按钮时启动该类的新对象. Thread的此子类的构造函数可以接收Queue对象,然后您就可以与GUI部件进行通信.所以我的建议是:

If you want to place the logic portion in a different class, you can subclass Thread directly, and then start a new object of this class when you press the button. The constructor of this subclass of Thread can receive a Queue object and then you will be able to communicate it with the GUI part. So my suggestion is:

  1. 在主线程中创建一个队列对象
  2. 创建一个可以访问该队列的新线程
  3. 定期检查主线程中的队列

然后,您必须解决以下问题:如果用户两次单击同一按钮两次(每次单击都会产生一个新线程),但是可以通过禁用开始按钮并在启用后再次启用它来解决此问题.呼叫self.prog_bar.stop().

Then you have to solve the problem of what happens if the user clicks two times the same button (it will spawn a new thread with each click), but you can fix it by disabling the start button and enabling it again after you call self.prog_bar.stop().

import Queue

class GUI:
    # ...

    def tb_click(self):
        self.progress()
        self.prog_bar.start()
        self.queue = Queue.Queue()
        ThreadedTask(self.queue).start()
        self.master.after(100, self.process_queue)

    def process_queue(self):
        try:
            msg = self.queue.get(0)
            # Show result of the task if needed
            self.prog_bar.stop()
        except Queue.Empty:
            self.master.after(100, self.process_queue)

class ThreadedTask(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
    def run(self):
        time.sleep(5)  # Simulate long running process
        self.queue.put("Task finished")

这篇关于Tkinter:如何使用线程来防止主事件循环“冻结".的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆