销毁线程中的 Tkinter 窗口

时间:2021-03-08 12:09:48

标签: python multithreading tkinter

我有一个项目,其中一个被动 GUI 在它自己的线程中运行并由主线程操作。特别是,主线程使用 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")

程序崩溃,输出如下:

quit
###########
Tcl_AsyncDelete: async handler deleted by the wrong thread
[1]    21572 IOT instruction (core dumped)  python window_test.py

根据this discussion,看来多线程环境中对象清理的顺序有问题。取消对象(在我的例子中为 window)和调用 gc.collect 的建议没有解决问题。

我该怎么办?

2 个答案:

答案 0 :(得分:1)

不是使用单独的线程来创建对 Tk() 的第二个引用, 创建“Window”类时只需继承 tk.Toplevel 即可。 这将允许您拥有任意数量的窗口。 您可以使用 tk.after 来监视进程并执行伪多线程处理。这是如何做到这一点的示例

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()

答案 1 :(得分:0)

使用@AndrewPye 的答案,但继承自 Tk 而不是 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()