multithreading from a tkinter app
<p>I have a tkinter application that runs on the main thread. After receiving some input from the user, a new thread is created to perform functions in a separate class. The main thread continues to a Toplevel progress window. </p>

<p>My question is, how can I communicate to the main thread when the second thread has finished its tasks, in order to close the progress window?</p>

<pre><code>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()
</code></pre>

<p><strong>* UPDATE *</strong></p>

<p>tkinter's <code>event_generate</code> as suggested by iCart works well. See code update below.</p>

<pre><code>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 &lt;&lt;stop&gt;&gt; virtual event (unsure if 'tail' is necessary here)
        root.event_generate("&lt;&lt;stop&gt;&gt;", 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("&lt;&lt;stop&gt;&gt;", end_program)   # bind to event
    root.mainloop()
</code></pre>

<p>You can use <code>Queue</code>, from <code>Queue</code> module.</p>

<pre><code>def get_queue(self, queue):
    status = queue.get()
    if status:
        self.do_your_action()
    self.master.after(1000, self.get_queue)
</code></pre>

<p>And in the progress bar window, You pass queue and put something in it when progress bar finishes.</p>

python multithreading tkinter python-3.4