【问题标题】:Trigger an event when a thread is finished线程完成时触发事件
【发布时间】:2018-04-11 12:55:35
【问题描述】:

嗨 Python 和 Tkinter 大师, 我正在尝试构建一个具有两个按钮的简单 GUI。单击 Button 时,将启动一个线程来执行某些工作。这项工作通常需要 10s/15s。而且 GUI 也可以正常工作。

但我想实现一个弹出窗口来通知哪个线程已经完成。我已经检查了t.isAlive()函数,但无法实现它,因为我不知道如何在主循环中触发基于isAlive的事件。

这是我的示例代码

from threading import Thread
from time import sleep
import Tkinter
import ttk

class SmallGui:
    def __init__(self, master):
        self.master = master
        self.master.title('test gui')

        self.button_1 = ttk.Button(self.master,
                                   text='Start 1',
                                   command=lambda: self.init_thread(1))

        self.button_2 = ttk.Button(self.master,
                                   text='Start 2',
                                   command=lambda: self.init_thread(2))

        self.button_1.pack()
        self.button_2.pack()

    def init_thread(self, work):
        if work == 1:
            t = Thread(target=self.work_1)
            t.start()
        else:
            t = Thread(target=self.work_2)
            t.start()

    @staticmethod
    def work_1():
        print 'Work 1 started'
        # Do some Task and return a list
        sleep(10)

    @staticmethod
    def work_2():
        print 'Work 2 Started'
        # Do some Task and return a list
        sleep(15)


if __name__ == '__main__':
    root = Tkinter.Tk()
    run_gui = SmallGui(root)
    root.mainloop()

【问题讨论】:

  • 为什么不在线程中正在运行的函数的最后一行添加弹出消息。您可以在此处使用来自 tkinter 的消息框。

标签: python multithreading tkinter


【解决方案1】:

您可以使用 tkinter 的消息框。 Tkinter 有一个内置方法,可用于各种弹出消息或问题。这里我们将使用messagebox.showinfo

我正在研究 Python 3.X,所以我添加了一个适用于 3.X 和 2.X 版本的 python 的导入方法。

from threading import Thread
from time import sleep

try:
    import Tkinter as tk
    import tkMessageBox as mb
    import ttk
except ImportError:
    import tkinter as tk
    from tkinter import messagebox as mb
    import tkinter.ttk as ttk

class SmallGui:
    def __init__(self, master):
        self.master = master
        self.master.title('test gui')

        self.button_1 = ttk.Button(self.master,
                                   text='Start 1',
                                   command=lambda: self.init_thread(1))

        self.button_2 = ttk.Button(self.master,
                                   text='Start 2',
                                   command=lambda: self.init_thread(2))

        self.button_1.pack()
        self.button_2.pack()

    def init_thread(self, work):
        if work == 1:
            t = Thread(target=self.work_1)
            t.start()
        else:
            t = Thread(target=self.work_2)
            t.start()

    @staticmethod
    def work_1():
        print ('Work 1 started')
        # Do some Task and return a list
        sleep(1)
        mb.showinfo("test", "Work 1 complete")

    @staticmethod
    def work_2():
        print ('Work 2 Started')
        # Do some Task and return a list
        sleep(1)
        mb.showinfo("test", "Work 2 complete")


if __name__ == '__main__':
    root = tk.Tk()
    run_gui = SmallGui(root)
    root.mainloop()

更新:

无论出于何种原因,我的上述解决方案在 python 3 中有效,但在 2.7.14 中无效。

但下面的示例在 2.7.14 中确实有效,应该适合您。

我在这里所做的是创建 2 个类属性来监视每个线程。 我创建了一个方法,如果线程处于活动状态,它将每 1 秒检查一次,如果线程变为非活动状态,则会弹出一个消息框。

from threading import Thread
from time import sleep

import Tkinter as tk
import tkMessageBox as mb
import ttk


class SmallGui(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master
        self.master.title('test gui')

        self.button_1 = ttk.Button(self.master,
                                   text='Start 1',
                                   command=lambda: self.init_thread(1))

        self.button_2 = ttk.Button(self.master,
                                   text='Start 2',
                                   command=lambda: self.init_thread(2))

        self.button_1.pack()
        self.button_2.pack()

        self.work1_status = None
        self.work2_status = None

    def init_thread(self, work):
        if work == 1:
            self.work1_status = Thread(target=self.work_1)
            self.work1_status.start()
            self.check_thread(self.work1_status, work)
        else:
            self.work2_status = Thread(target=self.work_2)
            self.work2_status.start()
            self.check_thread(self.work2_status, work)

    def check_thread(self, pass_thread, thread_name):
        if pass_thread.isAlive() == False:
                pass_thread = None
                mb.showinfo("test", "Work {} complete".format(thread_name))
        else:
            self.after(1000, lambda: self.check_thread(pass_thread, thread_name))

    @staticmethod
    def work_1():
        print ('Work 1 started')
        # Do some Task and return a list
        sleep(5)

    @staticmethod
    def work_2():
        print ('Work 2 Started')
        # Do some Task and return a list
        sleep(5)

if __name__ == '__main__':
    root = tk.Tk()
    run_gui = SmallGui(root)
    root.mainloop()

【讨论】:

  • 感谢您的回复。我运行了代码,但是一旦单击 Start 1 按钮,GUI 就会变得无响应。
  • @daanav 你是复制了我的代码还是更新了你自己的代码?
  • @daanav 我现在正在使用 2.7 对其进行测试,并且在 2.7 中确实存在问题。我不知道为什么它在 python 3 中没有问题。我会继续调查的。
  • 谢谢。对于您之前的问题,是的,我复制了您的代码
  • @daanav 线程有时在 tkinter 中有点难以使用。也就是说,我想出了一些对你有用的东西。看看我的第二个例子。
猜你喜欢
  • 1970-01-01
  • 2020-09-12
  • 1970-01-01
  • 2015-10-16
  • 2021-05-06
  • 2018-01-30
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
相关资源
最近更新 更多