线程和tkinter

Mar*_*DLF 14 python multithreading tkinter tcl python-3.x

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

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

代码的简化版本如下:

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

finish = False

class tkinterGUI(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):  
        global finish
        #Main Window
        self.mainWindow = Tk()
        self.mainWindow.geometry("200x200")
        self.mainWindow.title("My GUI Title")
        #Label
        lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)
        #Start
        self.mainWindow.mainloop()
        #When the GUI is closed we set finish to "True"
        finish = True

class InfiniteProcess(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global finish
        while not finish:
            print("Infinite Loop")
            time.sleep(3)

GUI = tkinterGUI()
GUI.start()
Process = InfiniteProcess()
Process.start()
Run Code Online (Sandbox Code Playgroud)

当我单击关闭按钮(右上角)时,控制台中会出现以下错误:

Tcl_AsyncDelete: async handler deleted by the wrong thread
Run Code Online (Sandbox Code Playgroud)

我不知道它为什么会发生或它意味着什么.

unu*_*tbu 16

所有Tcl命令都需要来自同一个线程.由于tkinter依赖于Tcl,通常需要使所有tkintergui语句都来自同一个线程.出现问题的原因 mainWindow是在tkinterGui线程中实例化,但是 - 因为mainWindow是 - 的属性,tkinterGui直到tkinterGui在主线程中被销毁才被销毁.

通过不创建mainWindow属性tkinterGui - 即改变self.mainWindow为... 来避免问题mainWindow.mainWindowrun方法tkinterGui线程中结束时,这允许 被销毁.但是,通常可以通过使用mainWindow.after调用来完全避免线程:

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

def infinite_process():
    print("Infinite Loop")
    mainWindow.after(3000, infinite_process)


mainWindow = Tk()
mainWindow.geometry("200x200")
mainWindow.title("My GUI Title")
lbCommand = Label(mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)
mainWindow.after(3000, infinite_process)
mainWindow.mainloop()
Run Code Online (Sandbox Code Playgroud)

如果要在类中定义GUI,您仍然可以这样做:

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

class App(object):
    def __init__(self, master):
        master.geometry("200x200")
        master.title("My GUI Title")
        lbCommand = Label(master, text="Hello world", 
                          font=("Courier New", 16)).place(x=20, y=20)

def tkinterGui():  
    global finish
    mainWindow = Tk()
    app = App(mainWindow)
    mainWindow.mainloop()
    #When the GUI is closed we set finish to "True"
    finish = True

def InfiniteProcess():
    while not finish:
        print("Infinite Loop")
        time.sleep(3)

finish = False
GUI = threading.Thread(target=tkinterGui)
GUI.start()
Process = threading.Thread(target=InfiniteProcess)
Process.start()
GUI.join()
Process.join()
Run Code Online (Sandbox Code Playgroud)

甚至更简单,只需使用主线程来运行GUI mainloop:

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

class App(object):
    def __init__(self, master):
        master.geometry("200x200")
        master.title("My GUI Title")
        lbCommand = Label(master, text="Hello world", 
                          font=("Courier New", 16)).place(x=20, y=20)

def InfiniteProcess():
    while not finish:
        print("Infinite Loop")
        time.sleep(3)

finish = False
Process = threading.Thread(target=InfiniteProcess)
Process.start()

mainWindow = Tk()
app = App(mainWindow)
mainWindow.mainloop()
#When the GUI is closed we set finish to "True"
finish = True
Process.join()
Run Code Online (Sandbox Code Playgroud)