Reputation: 525
I have a project where a passive GUI runs in its own thread and is manipulated by the main thread. Especially, the window is closed by the main thread using event_generate
:
from tkinter import Tk
import threading
import time
import queue
q = queue.Queue()
class Window:
def __init__(self):
self.root = Tk()
self.root.title("test")
self.root.bind("<<custom_close_event>>", self.close)
def close(self, event):
print("quit")
self.root.destroy()
def create_window():
window = Window()
q.put(window)
window.root.mainloop()
print("###########")
# Window creation executed in different thread
t1 = threading.Thread(target=create_window)
t1.start()
window = q.get()
time.sleep(2)
window.root.event_generate("<<custom_close_event>>")
print("end")
The program crashes with the following output:
quit
###########
Tcl_AsyncDelete: async handler deleted by the wrong thread
[1] 21572 IOT instruction (core dumped) python window_test.py
According to this discussion, it seems that the order of objects cleanup in multithreaded environment is in fault. The advice to nullify objects (in my case window
) and to call gc.collect
did not solve the problem.
How should I do?
Upvotes: 0
Views: 891
Reputation: 7680
Using @AndrewPye's answer but inheriting from Tk
instead of Toplevel
:
from tkinter import *
class Window(Tk):
def __init__(self, **kwargs):
super().__init__(**kwargs)
super().after(1000, self.do_something)
def do_something(self):
print("I am in a loop that runs every 1000ms = 1s")
super().after(1000, self.do_something)
root = Window()
root.mainloop()
Upvotes: 0
Reputation: 622
Instead of using a separate thread to create a second reference to Tk(), Just inherit tk.Toplevel when you create the "Window" class. This will allow you to have really as many windows as you want. You can use tk.after in order to monitor processes and do pseudo-multithreading things. Here's an example of how to do that
class Window(Toplevel):
def __init__(self, parent, *args, **kwargs):
super().__init__(*args, **kwargs)
self.parent = parent
...
self.parent.after(1000, self.do_something)
def do_something(self):
...
<code>
...
self.parent.after(1000, self.do_something)
root = Tk()
Window(root)
root.mainloop()
Upvotes: 2