In tkinter, you can submit an event from a background thread to the GUI thread using event_generate. This allows you to update widgets without threading errors.
- Create the tkinter objects in the main thread
- Bind the root to a vitual event (ie << event1 >>), specifying an event handler. Arrow brackets are required in the event name.
- Start the background thread
- In the background thread, use
event_generate
to trigger the event in the main thread. Use the state
property to pass data (number) to the event.
- In the event handler, process the event
Here's an example:
from tkinter import *
import datetime
import threading
import time
root = Tk()
root.title("Thread Test")
print('Main Thread', threading.get_ident()) # main thread id
def timecnt(): # runs in background thread
print('Timer Thread',threading.get_ident()) # background thread id
for x in range(10):
root.event_generate("<<event1>>", when="tail", state=123) # trigger event in main thread
txtvar.set(' '*15 + str(x)) # update text entry from background thread
time.sleep(1) # one second
def eventhandler(evt): # runs in main thread
print('Event Thread',threading.get_ident()) # event thread id (same as main)
print(evt.state) # 123, data from event
string = datetime.datetime.now().strftime('%I:%M:%S %p')
lbl.config(text=string) # update widget
#txtvar.set(' '*15 + str(evt.state)) # update text entry in main thread
lbl = Label(root, text='Start') # label in main thread
lbl.place(x=0, y=0, relwidth=1, relheight=.5)
txtvar = StringVar() # var for text entry
txt = Entry(root, textvariable=txtvar) # in main thread
txt.place(relx = 0.5, rely = 0.75, relwidth=.5, anchor = CENTER)
thd = threading.Thread(target=timecnt) # timer thread
thd.daemon = True
thd.start() # start timer loop
root.bind("<<event1>>", eventhandler) # event triggered by background thread
root.mainloop()
thd.join() # not needed
Output (note that the main and event threads are the same)
Main Thread 5348
Timer Thread 33016
Event Thread 5348
......
I added an Entry widget to test if the StringVar
can be updated from the background thread. It worked for me, but you can update the string in the event handler if you prefer. Note that updating the string from multiple background threads could be a problem and a thread lock should be used.
Note that if the background threads exits on its own, there is no error. If you close the application before it exits, you will see the 'main thread' error.