【问题标题】:Tcl_AsyncDelete: async handler deleted by the wrong thread, but I didn't delete from wrong thread?Tcl_AsyncDelete:异步处理程序被错误的线程删除,但我没有从错误的线程中删除?
【发布时间】:2021-12-23 15:15:54
【问题描述】:

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

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

【问题讨论】:

    标签: python multithreading tkinter


    【解决方案1】:

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

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

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

    第四,您需要在load 函数中使用global,以便loading 变量可以在线程中正确使用。您还有两个名为load 的变量:函数和窗口。您应该重命名其中一个,以免其中一个遮盖另一个。在我的示例中,我将窗口重命名为 loadload_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 在关闭前等待一秒钟。

    【讨论】:

    • 但我没有在列表中添加数字,我正在定义 720 个变量(按钮)
    • @Devyansh 是否有特定原因需要定义 720 个变量,而不是使用列表?清单不适合你吗?您仍然可以将Buttons 附加到列表中。
    • 我尝试过使用数组,我尝试过dictlist,但都没有成功
    • @Devyansh 为了提供帮助,我需要更多关于 what 不起作用的信息。您是否收到此答案中的代码错误,或者当您尝试制作按钮列表时?还是别的什么?
    • 我正在制作一个包含许多标签(tkinter 标签)的网格,每个标签都有 绑定的绑定,因此我必须使用 exec 函数定义 720 个变量for 循环
    猜你喜欢
    • 2015-01-24
    • 1970-01-01
    • 2015-01-20
    • 2019-01-26
    • 2013-08-09
    • 2014-09-29
    • 1970-01-01
    • 2019-07-05
    • 1970-01-01
    相关资源
    最近更新 更多