Search code examples
pythonmultithreadingtkinterpython-multithreading

Why do i have to create my thread in another class, to avoid freezing my Tkinter UI?


I have a Tkinter application, in which I have a button, calling a function. To avoid freezing the UI, while this function processes, I'm starting it within its own thread.

If I spawn the thread within the same class, as the method I'm calling is defined, my UI still freezes. The only way I have been able to fix this is by creating a Threader class, whose sole purpose, is to call a function within a new thread.

I have tried shaving down my app class, to only the relevant things, for this problem, but let me know if you are suspecting that I cut away too much:

from threader import Threader
from threading import Thread
from tkinter import ttk
import tkinter as tk


class App(tk.Tk):

        def __init__(self):
                ...
                self.run_btn=ttk.Button(self, text='Run', command=self.start_clicked, width=15)
                self.Threader = Threader()
                ...
        
        def start_clicked(self):
                Thread(target=self.my_func()).start() # this freezes my ui 
                # self.Threader.run_thread(self.my_func()) # this does not freeze my ui


        def my_func(self):
                # some logic

This is my Threader class:

from threading import Thread

class Threader:

    def run_thread(self, name, func):
        Thread(target=func).start()

In my App class I have included both ways I have tried spawning the thread, within the start_clicked function. The first one is the one freezing my UI, and the one commented-out, in which I spawn the thread, through my Threader class, is the one which doesn't.

In my mind, there shouldn't be a difference, so I can't figure out why one works, and the other doesn't.


Solution

  • Consider this line of code:

    Thread(target=self.my_func()).start()
    

    Functionally, it is exactly the same as this:

    result = self.my_func()
    Thread(target=result).start()
    

    See the problem? You are immediately calling self.my_func() in the current thread.

    The target needs to be a reference to a function:

    Thread(target=self.my_func)