2014-11-02 84 views
11

我听说Python中的线程不容易处理,他们变得更加纠结于tkinter。线程和tkinter python 3

我有以下问题。我有两个类,一个用于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 :异步处理程序由错误的线程删除

我不知道它为什么发生或它的意思,请帮助!

+0

您的简历版适用于我...必须有一些你忘记添加,这是导致你的问题 – mguijarr 2014-11-02 19:52:30

+0

@mguijarr我在谷歌阅读,这个错误是更常见的窗口,你的SO?我的是Windows 7 x64。也许窗口是问题:/ – 2014-11-02 20:14:50

回答

10

All Tcl commands need to originate from the same thread。由于tkinter的 依赖于Tcl,通常必须使所有tkinter gui语句来自同一个线程。因为 mainWindowtkinterGui线程中被实例化,但是 - 因为mainWindowtkinterGui的属性 - 直到tkinterGui在主线程中被销毁才被销毁。即改变self.mainWindowmainWindow -

该问题可以通过不使mainWindowtkinterGui 的属性来避免。当run方法在tkinterGui线程中结束时,这允许mainWindow被销毁。但是,往往就可以避免线程完全使用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() 

如果要定义一个类里面的图形用户界面,你还可以这样做:

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主循环:

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() 
+0

非常感谢你!你是主人! – 2014-11-02 20:17:29

+0

但有时你需要将mainWindow作为一个属性,例如,如果你想使用: self.mainWindow.protocol(“WM_DELETE_WINDOW”,self。退出) 当你定义的函数退出()和你写的: self.mainWindow.destroy() self.mainWindow.quit() 主窗口必须是一个属性,否则该函数退出将无法识别主窗口。 – 2014-11-02 20:41:22

+0

你仍然可以使用一个类;只是不要使该实例成为'threading.Thread'的一个属性。我已经在上面添加了一些代码来提示如何。 – unutbu 2014-11-02 20:57:18

0

此处的修复很简单,但很难发现:

呼叫mainWindow.quit()后立即mainwindow.mainloop(),使清理发生在同一个线程创建了TK UI,而不是主线程时蟒蛇退出上一个上。

+1

这似乎不是一个通用的解决方案。我的代码展示了同样的问题(Tcl_AsyncDelete错误),并在'mainWindow.mainloop()'后面添加'mainWindow.quit()'没有效果。 – 2017-03-03 22:19:01

+0

@BryanOakley是的,同样的问题:在试图避免Tcl_AsyncDelete我发现'.quit()'在Python 2.7.14中是不必要的,而在Python 3.6.3中是无效的。还没有解决方案。 – jez 2017-11-06 19:19:25