nbout
nbout

Reputation: 1269

Use tkinter virtual event from thread

I would like to update a Tkinter Label from a Thread using a virtual event.

from tkinter import *
from tkinter import ttk
import threading
import time


def run_test_event():
    while True:
        root.event_generate("<<test_eventA>>")
        root.event_generate("<<test_eventB>>")
        time.sleep(1)


if __name__ == '__main__':
    root = Tk()
    l = ttk.Label(root, text="Starting...")
    l.grid()
    l.bind('<Enter>', lambda e: l.configure(text='Moved mouse inside'))
    l.bind('<<test_eventA>>', lambda e: l.configure(text='test_eventA'))
    l.bind('<<test_eventB>>', print('test_eventB'))

    threading.Thread(target=run_test_event).start()
    root.mainloop()

Console print this at startup:

test_eventB

Console print this when GUI is closed:

Exception in thread Thread-1:
Python\Python38\lib\tkinter\__init__.py", line 1849, in event_generate
    self.tk.call(args)
RuntimeError: main thread is not in main loop

Label is never updated and does not display test_eventA

Setting the Thread as daemon does help. It avoid to generate above Exception but still the label is not updated. Any idea ?

Upvotes: 0

Views: 765

Answers (1)

acw1668
acw1668

Reputation: 46678

You are generating the virtual event on root window, not the label. You should call .event_generate() on the label:

from tkinter import *
from tkinter import ttk
import threading
import time


def run_test_event(label):
    while True:
        label.event_generate("<<test_eventA>>")
        label.event_generate("<<test_eventB>>")
        time.sleep(1)


if __name__ == '__main__':
    root = Tk()
    l = ttk.Label(root, text="Starting...")
    l.grid()
    l.bind('<Enter>', lambda e: l.configure(text='Moved mouse inside'))
    l.bind('<<test_eventA>>', lambda e: l.configure(text='test_eventA'))
    l.bind('<<test_eventB>>', lambda e: print('test_eventB')) # should use lambda here

    # pass label to target function
    threading.Thread(target=run_test_event, args=(l,), daemon=True).start()
    root.mainloop()

Upvotes: 3

Related Questions