Multi threading in Tkinter GUI, threads in different classes

10,041

Solution 1

You don't need threading for something this simple.

The GUI is freezing because you're putting a time.sleep inside the function which is blocking the main thread until it's finished.

Simply use Tk's built in after method. Change your function to.

def countNum(self, num=0):
    if num < 10:
        print num
        root.after(2000, lambda: self.countNum(num + 1))
    else:
        print "Stopping after call"

The after method takes the following arguments:

after(delay_ms, callback, arguments)

The time is in milliseconds, and 1000 ms = 1 second. So, we pass 2,000 ms for a 2 second delay.

Solution 2

Pythonista's answer is excellent. But I'd like to touch upon some additional points.

  • GUI's are event-driven. They run in a loop processing events, calling pieces of your code (called callbacks) every now and then. So your code is more or less a guest in the event-loop. As you have noticed, your pieces of code should finish quickly otherwise they stall event processing making the GUI unresponsive. This is a completely different programming model from the linear programs that is often seen in tutorials. To perform longer-running calculations or tasks you can split them up in small pieces and use after. Or you could to them in another process with multiprocessing. But then you'd still need to check periodically (with after again) if they have finished.

The following points stem from the fact that doing multithreading right is hard.

  • CPython (the most used Python implementation) has what is called a Global Interpreter Lock. This ensures that only one thread at a time can be executing Python bytecode. When other threads are busy executing Python bytecode, the thread running the GUI is doing nothing. So multithreading is not a certain solution to the problem of an unresponsive GUI.

  • a lot of GUI toolkits are not thread-safe, and tkinter is not an exception. This means that you should only make tkinter calls from the thread that's running the mainloop. (In Python 3.x, tkinter has been made thread safe.)

Share:
10,041
das
Author by

das

Updated on June 16, 2022

Comments

  • das
    das almost 2 years

    I'm currently learning the Tkinter GUI programming. And I'm stuck in somewhere in multi threading concept. Even though this topic is discussed several times here, I couldn't catch the concept and apply it to my small sample program.

    Below is my code:

    from PIL import Image, ImageTk 
    from Tkinter import Tk, Label, BOTH
    from ttk import Frame, Style
    from Tkinter import *
    import time
    
    
    class Widgets(Frame):
    
        def __init__(self, parent):
            Frame.__init__(self, parent)
            self.grid()
            self.parent = parent
            self.initUI(parent)
    
        def initUI(self, parent):
            self.parent.title("Count Numbers")
    
            for r in range(10):
                self.parent.rowconfigure(r, weight=1)    
            for c in range(10):
                self.parent.columnconfigure(c, weight=1)        
    
            self.button1 = Button(parent, text = "count")
            self.button1.grid(row = 1, column = 1, rowspan = 1, columnspan = 2, sticky = W+E+N+S )
            self.button1["command"] = self.countNum
    
            self.button2 = Button(parent, text = "say Hello")
            self.button2.grid(row = 1, column = 7, rowspan = 1, columnspan = 2, sticky = W+E+N+S) 
            self.button2["command"] = PrintHello(self).helloPrint
    
        def countNum(self):
            for i in range(10):
                print i
                time.sleep(2)   
    
    class PrintHello(Frame):
    
        def __init__(self, parent):
            Frame.__init__(self, parent)
            self.grid()
            self.parent = parent
    
        def helloPrint(self):
            print "Hello"
    
    def main():
        root = Tk()
        root.geometry("300x200")
        app = Widgets(root)
        root.mainloop()
    
    if __name__ == '__main__':
        main() 
    

    enter image description here

    The output is a GUI with 2 buttons- first one prints the numbers and second prints "Hello". But on clicking the first button, the GUI gets frozen while the numbers are getting printed. And while searching for a solution, I found that 'multi threading' may help. But after several attempts, I couldn't apply multi-threading to my sample program given.