线程和tkinter python 3
我听说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 :异步处理程序由错误的线程删除
我不知道它为什么发生或它的意思,请帮助!
All Tcl commands need to originate from the same thread。由于tkinter
的 依赖于Tcl,通常必须使所有tkinter
gui语句来自同一个线程。因为 mainWindow
在tkinterGui
线程中被实例化,但是 - 因为mainWindow
是tkinterGui
的属性 - 直到tkinterGui
在主线程中被销毁才被销毁。即改变self.mainWindow
到mainWindow
-
该问题可以通过不使mainWindow
的tkinterGui
的属性来避免。当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()
非常感谢你!你是主人! – 2014-11-02 20:17:29
但有时你需要将mainWindow作为一个属性,例如,如果你想使用: self.mainWindow.protocol(“WM_DELETE_WINDOW”,self。退出) 当你定义的函数退出()和你写的: self.mainWindow.destroy() self.mainWindow.quit() 主窗口必须是一个属性,否则该函数退出将无法识别主窗口。 – 2014-11-02 20:41:22
你仍然可以使用一个类;只是不要使该实例成为'threading.Thread'的一个属性。我已经在上面添加了一些代码来提示如何。 – unutbu 2014-11-02 20:57:18
此处的修复很简单,但很难发现:
呼叫mainWindow.quit()
后立即mainwindow.mainloop()
,使清理发生在同一个线程创建了TK UI,而不是主线程时蟒蛇退出上一个上。
这似乎不是一个通用的解决方案。我的代码展示了同样的问题(Tcl_AsyncDelete错误),并在'mainWindow.mainloop()'后面添加'mainWindow.quit()'没有效果。 – 2017-03-03 22:19:01
@BryanOakley是的,同样的问题:在试图避免Tcl_AsyncDelete我发现'.quit()'在Python 2.7.14中是不必要的,而在Python 3.6.3中是无效的。还没有解决方案。 – jez 2017-11-06 19:19:25
您的简历版适用于我...必须有一些你忘记添加,这是导致你的问题 – mguijarr 2014-11-02 19:52:30
@mguijarr我在谷歌阅读,这个错误是更常见的窗口,你的SO?我的是Windows 7 x64。也许窗口是问题:/ – 2014-11-02 20:14:50