Tcl_AsyncDelete:异步处理程序被错误的线程删除,但我没有从错误的线程中删除?



我的python程序允许用户通过拖放干扰来制造迷宫。它甚至在迷宫中寻找正确的路径。然而,需要为构建迷宫的网格定义和绑定720个变量,为此,我使用了一个需要一些时间的循环,从此我制作了一个加载屏幕。当程序完全加载时,加载屏幕关闭,线程终止,但当我关闭主窗口时,我会收到错误(不是在加载屏幕关闭时,而是在我关闭主窗时(Tcl_AsyncDelete: async handler deleted by the wrong thread。以下是我的代码的简化版本,以显示我正在尝试做的事情:

from tkinter import *
from threading import Thread
loaded = False
def load():
load = Tk()
Label(load, text = "Loading...").pack()
def check():
if loaded:
load.destroy()
else:
load.after(1, check)
load.mainloop()

t = Thread(target = load)
t.start()
root = Tk()
#defining the 720 variables
for x in range(0, 720):
print(x)
exec("var" + str(x) + " = " + str(x))  
loaded = True
root.mainloop()

我确实读过一些关于堆栈溢出的其他问题,我发现当你从另一个线程(而不是主线程(删除窗口时会发生这种情况,但我正在从它自己运行的线程(而非主线程(中删除加载屏幕。有什么帮助吗?

此错误是由一个应用程序中存在多个Tk实例引起的。tkinter应用程序中应该始终只有一个Tk实例。如果要创建辅助窗口,请使用Toplevel而不是Tk

但您会注意到,如果用load = Toplevel替换load = Tk,则根本不会显示load。在tkinter应用程序中使用线程时,最好将所有tkinter内容保留在一个线程中。如果将任何tkinter小部件或操作拆分为单独的线程,可能会导致问题。您应该将for循环放入load()中,并将当前位于load()中的所有内容放入for循环所在的位置。这可以防止任何tkinter函数调用或小部件创建在两个不同的线程中发生。

第三,我强烈建议您不要在这样的循环中定义变量。在创建变量后尝试访问这些变量时会很麻烦,而且速度很慢。对于这么多数字,您可以创建一个列表,然后将数字附加到列表中。在我的示例中,该列表被称为numbers

第四,您需要在load函数中使用global,以便在线程中正确使用loading变量。您还有两个名为load的变量:函数和窗口。你应该重命名其中一个,这样一个就不会盖过另一个。在我的示例中,我将窗口load重命名为load_window

要使Toplevel显示在顶部,可以设置load_window.wm_attributes("-topmost", True)

代码应该是这样的:

from tkinter import *
# import time # Uncomment this to show the loading window for a little longer
from threading import Thread
loaded = False
numbers = []
def load():
global loaded, numbers
# Add the numbers to the list
numbers = [x for x in range(0, 720)]
# time.sleep(1) # Uncomment this to show the loading window for a little bit longer
loaded = True
root = Tk()
load_window = Toplevel(root)
Label(load_window, text = "Loading...").pack()
t = Thread(target = load)
t.start()
def check():
global loaded, load_window
if loaded:
load_window.destroy()
else:
load_window.after(1, check)
check()
load_window.wm_attributes("-topmost", True)
load_window.mainloop()
root.mainloop()

请注意,当您运行代码时,您会得到一个_tkinter.TclError:

Traceback (most recent call last):
File "/home/user/test.py", line 29, in <module>
load_window.wm_attributes("-topmost", True)
File "/usr/lib/python3.8/tkinter/__init__.py", line 1976, in wm_attributes
return self.tk.call(args)
_tkinter.TclError: bad window path name ".!toplevel"

这是因为load()运行得太快,以至于它在设置-topmost之前破坏了loaded_window。正如您可能知道的那样,在已销毁的小部件上调用函数会引发错误。因此,只有更高的数字才会真正需要整个加载窗口。即使我使用range(0, 900000)而不是range(0, 720),代码也会运行,但它仍然非常快,以至于您只能在一瞬间看到load_window

如果您仍然想查看加载窗口,可以取消对import timetime.sleep(...行的注释,这将导致load_window在关闭前等待一秒钟。

最新更新