3

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.

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?

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()

* UPDATE *

tkinter's event_generate as suggested by iCart works well. See code update below.

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()
qwertysmack
  • 188
  • 1
  • 12
  • Does this answer your question? [Tkinter: invoke event in main loop](https://stackoverflow.com/questions/270648/tkinter-invoke-event-in-main-loop) – stovfl Feb 08 '20 at 14:20

2 Answers2

2

I'm not too familiar with tkinter so i can't show you ze code, but you should look at tkinter's event system, particularly firing custom events.

This question may help you get started

Community
  • 1
  • 1
iCart
  • 2,179
  • 3
  • 27
  • 36
1

You can use Queue, from Queue module.

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

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

Fejs
  • 2,734
  • 3
  • 21
  • 40