Tcl_AsyncDelete 错误多线程 Python [英] Tcl_AsyncDelete Error Multithreading Python

查看:85
本文介绍了Tcl_AsyncDelete 错误多线程 Python的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我听说 Python 中的线程不容易处理,而且它们与 tkinter 变得更加纠缠不清.

I've heard that threads in Python are not easy to handle and they become more tangled with tkinter.

我有以下问题.我有两个类,一个用于 GUI,另一个用于无限进程(我必须为两者使用类).首先,我启动 GUI 类,然后是无限进程的类.我希望当你关闭 GUI 时,它也完成了无限进程并且程序结束.

I have the following problem. I have two classes, one for the GUI and another for an infinite process (I MUST use classes for both). First, I start the GUI class and then the infinite process' class. I want that when you close the GUI, it also finishes the infinite process and the program ends.

代码的简化版本如下:

import time, threading
from tkinter import *
from tkinter import messagebox

class Interface(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.attrib1 = "Attrib from Interface class"

    def run(self): 
        #Main Window
        self.mainWindow = Tk()
        self.mainWindow.geometry("200x200")
        self.mainWindow.title("My GUI Title")
        self.mainWindow.protocol("WM_DELETE_WINDOW", self.quit)
        #Label
        lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)
        #Start
        self.mainWindow.mainloop()

    #The Interface class contains methods that use attributes from itself and attributes from Process class.
    def method1(self): 
        print(self.attrib1)
        print(SecondThread.attrib2)

    def quit(self):
        if messagebox.askyesno('App','Are you sure you want to quit?'):
            #In order to use quit function, mainWindow MUST BE an attribute of Interface. 
            self.mainWindow.destroy()
            self.mainWindow.quit()  

class Process(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.attrib2 = "Attrib from Process class"

    def run(self):
        global finish
        while not finish:
            print("Proceso infinito")
            #Inside the infinite process a method from Interface class is used.
            GUI.method1()
            time.sleep(3)

finish = False  
#Starts the GUI
GUI = Interface()
GUI.start()
#Starts the infinity process
SecondThread = Process()
SecondThread.start()    
#Waits until GUI is closed
GUI.join()
print("When GUI is closed this message appears")
#When GUI is closed we set finish to True, so SecondThread will be closed.
finish = True
#After all the program should finish but it raises the error: Tcl_AsyncDelete: async handler deleted by the wrong thread

非常感谢您的帮助!

推荐答案

出现这种情况是因为您在线程上创建了 Tk 主窗口,而您没有在进程主线程上运行 UI.当您退出进程时,正在从进程主线程完成清理.此处示例的最简单解决方案是在主线程(进程默认线程)上创建 UI,并且仅将另一个线程用于工作任务.如果您的实际应用程序无法在主线程上创建 UI,您将需要考虑从其自己的线程终止 Tk.删除 Tcl 解释器可能会为您做到这一点.

This occurs because you created the Tk main window on a thread and you don't have the UI running on the processes main thread. When you exit the process the cleanup is being done from the process primary thread. The simplest solution for your example here is to create the UI on the primary thread (the processes default thread) and only use another thread for the worker task. If your real application cannot create the UI on the primary thread you will need to look into terminating Tk from its own thread. Deleting the Tcl interpreter might do that for you.

我修改了示例代码以显示将 UI 保留在主线程上可以避免此错误消息.由于您希望在创建 UI 之后但在运行之前创建您的工作器,我们可以在 Tk 主循环运行后使用 Tk after 方法启动工作器.

I modified the example code to show that keeping the UI on the main thread avoids this error message. As you want your worker to be created after the UI is created but before it is running we can use the Tk after method to start the worker once the Tk mainloop is running.

import time, threading
from tkinter import *
from tkinter import messagebox

class Interface:
    def __init__(self):
        #threading.Thread.__init__(self)
        self.attrib1 = "Attrib from Interface class"
        #Main Window
        self.mainWindow = Tk()
        self.mainWindow.geometry("200x200")
        self.mainWindow.title("My GUI Title")
        self.mainWindow.protocol("WM_DELETE_WINDOW", self.quit)
        #Label
        lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)

    #def run(self): 

    def start(self): #Start
        self.mainWindow.mainloop()

    #The Interface class contains methods that use attributes from itself and attributes from Process class.
    def method1(self): 
        print(self.attrib1)
        print(SecondThread.attrib2)

    def quit(self):
        if messagebox.askyesno('App','Are you sure you want to quit?'):
            #In order to use quit function, mainWindow MUST BE an attribute of Interface. 
            self.mainWindow.destroy()
            self.mainWindow.quit()  

class Process(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.attrib2 = "Attrib from Process class"

    def run(self):
        global finish
        while not finish:
            print("Proceso infinito")
            #Inside the infinite process a method from Interface class is used.
            GUI.method1()
            time.sleep(3)

finish = False
#Starts the GUI

GUI = Interface()
#Starts the infinity process
SecondThread = Process()
GUI.mainWindow.after(50, SecondThread.start)   
#Waits until GUI is closed
GUI.start()
#GUI.join()
print("When GUI is closed this message appears")
#When GUI is closed we set finish to True, so SecondThread will be closed.
finish = True
#After all the program should finish but it raises the error: Tcl_AsyncDelete: async handler deleted by the wrong thread

这篇关于Tcl_AsyncDelete 错误多线程 Python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆