当前位置: 首页 > 面试题库 >

Python-Tkinter:如何使用线程防止主事件循环“冻结”

祁渊
2023-03-14
问题内容

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

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

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

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

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 在此提供的信息,我了解我需要使用线程。我尝试创建一个线程,但是我猜测由于该线程是从主线程中启动的,因此没有帮助。

我有想法放置在不同的类中的逻辑部分,以及从该类,类似于由A.罗达斯示例代码内实例化GUI 这里。

我的问题:

我不知道如何编码,以便此命令:

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

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

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


问题答案:

当您将新线程加入主线程时,它将等待直到线程完成,因此即使您正在使用多线程,GUI也会阻塞。

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

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

然后,您必须解决以下问题:如果用户两次单击同一按钮两次(每次单击都会产生一个新线程),但是可以通过禁用开始按钮并在调用后再次启用它来解决此问题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")


 类似资料:
  • 我有一个带有“开始”按钮和进度条的小型GUI测试。期望的行为是: 单击开始 Progressbar振荡5秒 进度条停止 观察到的行为是“开始”按钮冻结5秒钟,然后显示进度条(无振荡)。 以下是我目前的代码: 根据Bryan Oakley提供的信息,我知道我需要使用线程。我试着创建一个线程,但我猜,由于线程是从主线程中开始的,所以没有帮助。 我的想法是将逻辑部分放在一个不同的类中,并从该类中实例化G

  • 我正在为我的公司开发一个“多层”GUI来监控温度和状态。因为我是python编程新手,所以我的代码需要一些帮助。 代码由类构成。“Main”初始化主窗口(tkinter)并创建其他要显示的帧(如果需要)。除“canvas”外,其他每个类都是一个框架,它将显示不同的内容。 每个画布都包含一个图像和一些文本/可变文本。线程用于从数据库获取数据并更改画布中的文本。 每次线程访问画布并尝试更改文本或创建新

  • 问题内容: 如何从单独的对象调用tkinter ? 我在寻找类似wxWidgets的东西。例如,如果我创建一个对象,并将其根实例传递给它,然后尝试从我的对象中调用该根窗口的方法,则我的应用程序将锁定。 我能想到的最好的方法是使用该方法并从单独的对象检查状态,但这似乎很浪费。 问题答案: 要回答“如何从单独的对象调用TkInter事件”这一特定问题,请使用命令。它允许您将事件注入到根窗口的事件队列中

  • 我有一个程序,它每秒钟响一次,直到停止。问题是,在我按下“开始”并发出嘟嘟声后,我无法单击“停止”按钮,因为窗口冻结。欢迎任何帮助。

  • 问题内容: 到现在为止,我以前以:结束我的Tkiter程序,否则什么都不会出现!参见示例: 但是,当尝试该程序的下一步(使球随着时间移动)时,该书正在阅读,并说要执行以下操作。将绘图功能更改为: 并将以下代码添加到我的程序中: 但是我注意到,添加此代码块使之无用,因为即使没有它,所有内容也会显示出来!!! 此时,我应该提到我的书从未谈论过(也许是因为它使用了Python 3),但是由于我的程序无法

  • 现在我们已经初步了解了libevent的Reactor组件——event_base和事件管理框架,接下来就是libevent事件处理的中心部分——事件主循环,根据系统提供的事件多路分发机制执行事件循环,对已注册的就绪事件,调用注册事件的回调函数来处理事件。 1 阶段性的胜利 Libevent将I/O事件、定时器和信号事件处理很好的结合到了一起,本节也会介绍libevent是如何做到这一点的。