Tkinter: How to use threads to preventing main event loop from "freezing"
When you join the new thread in the main thread, it will wait until the thread finishes, so the GUI will block even though you are using multithreading.
If you want to place the logic portion in a different class, you can subclass Thread directly, and then start a new object of this class when you press the button. The constructor of this subclass of Thread can receive a Queue object and then you will be able to communicate it with the GUI part. So my suggestion is:
- Create a Queue object in the main thread
- Create a new thread with access to that queue
- Check periodically the queue in the main thread
Then you have to solve the problem of what happens if the user clicks two times the same button (it will spawn a new thread with each click), but you can fix it by disabling the start button and enabling it again after you call
self.queue = queue.Queue()
msg = self.queue.get_nowait()
# Show result of the task if needed
def __init__(self, queue):
self.queue = queue
time.sleep(5) # Simulate long running process
Tkinter got freeze with threading
There are some unwritten rules about threading in combination with tkinter.
One of them is not to touch any widget outside of the thread of tkinter.
This line violates this rule.
label1.config(text = content)
In order to do this you can use a
tkinter.StingVar which isnt directly in the main event loop of tkinter.
The following line also violates the rule since it referece to the instance of tk.Tk().
In addition, the call from inside the function to itself
after some time would create a loop that you already have built with a
whileloop. Its therefor unnecessary and wrong at the same time. Below you find a working exampel which has its limits. For another approache you can take a look at this or this.
from tkinter import *
win = Tk()
var = StringVar(value="this is a test on the left")
label1 = Label(win,textvariable=var)
label2 = Label(win, text="this is a test on the right")
content=input("let enter the substuition:")
setTextthr=threading.Thread(target = set_text)
How can I prevent a tkinter Gui from freezing while an async task is running?
An example with async-tkinter-loop library (written by me):
import tkinter as tk
from async_tkinter_loop import async_handler, async_mainloop
self.root = tk.Tk()
start_button = tk.Button(self.root, text="start", command=async_handler(self.await_fun))
self.testfield = tk.Label(self.root, text="test")
async def await_fun(self):
self.testfield["text"] = "start waiting"
self.testfield["text"] = "end waiting"
if __name__ == '__main__':
gui = pseudo_example()
Threading In Tkinter To Stop Mainloop From Freezing
You don't need threads and you don't need a while loop for something this simple.
You already have an infinite loop running -- the event loop (eg:
mainloop). You can put work on the event queue to be executed in the future with the
after method. To perform an animation, just call a function to draw one frame, and have that function add itself back to the event queue to draw the next frame.
Here's a simple modification of your code to illustrate the point:
spryte1_pos = canvas.bbox(spryte1)
newpos1 = spryte1_pos
newpos2 = spryte1_pos-30
newpos3 = spryte1_pos-20
spryte2 = canvas.create_rectangle(newpos1, newpos2, newpos3, newpos3, fill="blue")
shoot(spryte2, 10, 0)
def shoot(spryte, x, y):
canvas.move(spryte, x, y)
x0,y0,x1,y1 = canvas.bbox(spryte)
if x0 < 600
# call again in 100ms, unless the bullet is off the screen
canvas.after(100, shoot, spryte, x, y)
The canvas can probably handle several dozen sprytes being moved like this before it bogs down.
BTW, you can also remove all of the calls to
Tk.update(window1) in your move functions -- it adds overhead and slows the program down. As a general rule of thumb you should almost never call
Why do i have to create my thread in another class, to avoid freezing my Tkinter UI?
Consider this line of code:
Functionally, it is exactly the same as this:
result = self.my_func()
See the problem? You are immediately calling
self.my_func() in the current thread.
target needs to be a reference to a function:
Call thread with loop out of gui without freezing gui
I could not figure out the solution I thought would work but I found this answer which I could adapt to my project and it now works as I wished.