线程和tkinter

时间:2014-11-02 19:39:35

标签: python python-3.x multithreading tkinter tcl

我听说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()

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

Tcl_AsyncDelete: async handler deleted by the wrong thread

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

2 个答案:

答案 0 :(得分:15)

All Tcl commands need to originate from the same thread。由于tkinter 依赖于Tcl,通常需要发出所有tkinter个gui语句 源自同一个线程。出现问题是因为 mainWindowtkinterGui线程中实例化,但是 - 因为mainWindowtkinterGui的属性 - 在tkinterGui被破坏之前不会被销毁线。

mainWindow属性tkinterGui可以避免此问题 - 即将self.mainWindow更改为mainWindow。这允许mainWindow方法在run线程中结束时销毁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()

如果要在类中定义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()

甚至更简单,只需使用主线程来运行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()

答案 1 :(得分:0)

这里的修复很简单,但很难发现:

mainWindow.quit() 之后立即调用mainwindow.mainloop(),以便清理发生在与创建tk UI的线程相同的线程上,而不是在python上的主线程上退出。