来自 tkinter 应用程序的多线程

multithreading from a tkinter app

我有一个在主线程上运行的 tkinter 应用程序。在收到用户的一些输入后,将创建一个新线程以在单独的 class 中执行功能。主线程继续 Toplevel 进度 window。

我的问题是,当第二个线程完成其任务时,如何与主线程通信以关闭进度window?

import tkinter as tk
from tkinter import ttk
from threading import Thread
import time


class Application:
    def __init__(self, master):
        # set main window
        frame = tk.Frame(master, width=300, height=100)
        frame.pack(fill=tk.BOTH)

        # button widget
        run_button = tk.Button(frame, text="GO", command=self.do_something)
        run_button.pack()

        # simulate some gui input from user
        self.user_input = "specified by user"

    def do_something(self):
        thread1 = Thread(target=FunctionClass, args=(self.user_input,))
        thread1.start()  # launch thread
        ProgressWindow()  # main thread continues to new tkinter window


class ProgressWindow(tk.Toplevel):
    """ displays progress """

    def __init__(self):
        super().__init__()

        self.progress = ttk.Progressbar(
            self, orient="horizontal", length=300, mode="indeterminate")
        self.progress.pack()
        self.note = "Processing data..."
        self.p_label = tk.Label(self, text=self.note)
        self.p_label.pack()
        self.progress.start(50)


class FunctionClass:
    """ thread1 works on this class """

    def __init__(self, user_data):
        self.user_data = user_data
        self.do_something_else()

    def do_something_else(self):
        # simulate thread 1 working
        time.sleep(3)
        print("Thread1 job done")


if __name__ == "__main__":
    root = tk.Tk()
    Application(root)
    root.mainloop()

* 更新 *

tkinter 的 event_generate 按照 iCart 的建议运行良好。请参阅下面的代码更新。

import tkinter as tk
from tkinter import ttk, messagebox
from threading import Thread
import time


class Application:
    def __init__(self, master):
        # set main window
        frame = tk.Frame(master, width=300, height=100)
        frame.pack(fill=tk.BOTH)

        # button widget
        run_button = tk.Button(frame, text="GO", command=self.do_something)
        run_button.pack()

        # simulate some gui input from user
        self.user_input = "specified by user"

    def do_something(self):
        thread1 = Thread(target=FunctionClass, args=(self.user_input,))
        thread1.start()  # launch thread
        ProgressWindow()  # main thread continues to new tkinter window


class ProgressWindow(tk.Toplevel):
    """ displays progress """

    def __init__(self):
        super().__init__()

        self.progress = ttk.Progressbar(self, orient="horizontal", length=300, mode="indeterminate")
        self.progress.pack()
        self.note = "Processing data..."
        self.p_label = tk.Label(self, text=self.note)
        self.p_label.pack()
        self.progress.start(50)


class FunctionClass:
    """ thread1 works on this class """

    def __init__(self, user_data):
        self.user_data = user_data
        self.do_something_else()

    def do_something_else(self):
        # simulate thread 1 working
        time.sleep(3)
        print("Thread1 job done")
        # call <<stop>> virtual event (unsure if 'tail' is necessary here)
        root.event_generate("<<stop>>", when="tail")


def end_program(*args):
    """ called with tkinter event_generate command after thread completion """
    messagebox.showinfo("Complete", "Processing Complete")
    root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    Application(root)
    root.bind("<<stop>>", end_program)   # bind to event
    root.mainloop()

我对 tkinter 不太熟悉,所以我不能给你看代码,但你应该看看 tkinter 的事件系统,特别是触发自定义事件。

This question 可能会帮助您入门

您可以使用 Queue,来自 Queue 模块。

def get_queue(self, queue):
    status = queue.get()
    if status:
        self.do_your_action()
    self.master.after(1000, self.get_queue)

并且在进度条中window,你传递队列并在进度条完成时将一些东西放入其中。