创建第二个 Toplevel 小部件时线程化 Tkinter 脚本崩溃
我有一个用Python写的脚本,里面用到了Tkinter来做图形界面。我的小脚本应该每隔X秒创建一个新的Toplevel窗口。当我运行代码时,第一个Toplevel窗口能成功创建,但当它尝试创建第二个窗口时,程序就崩溃了。
我现在做的是使用after方法每5秒调用一次startCounting这个函数,同时运行root的主循环。每次这个函数被调用时,我会把一个Toplevel窗口对象添加到一个列表里,并启动一个新的线程,希望这个线程能运行新的主循环。
如果有人能帮我解决这个问题,我会非常感激。顺便说一下,这只是一个我目前用来解决问题的小脚本,这个问题让我无法继续我的真正的学校项目。
代码:
import threading,thread
from Tkinter import *
def startCounting():
global root
global topLevelList
global classInstance
topLevelList.append (Toplevel())
topLevelList[len(topLevelList)-1].title("Child")
classInstance.append(mainLoopThread(topLevelList[len(topLevelList)-1]))
root.after(5000,startCounting)
class mainLoopThread(threading.Thread):
def __init__(self,toplevelW):
self.toplevelW = toplevelW
threading.Thread.__init__(self)
self.start()
def run(self):
self.toplevelW.mainloop()
global classInstance
classInstance = []
global topLevelList
topLevelList = []
global root
root = Tk()
root.title("Main")
startCounting()
root.mainloop()
3 个回答
你有没有想过为什么你想要每个顶层窗口都有一个事件循环?其实一个事件循环就能处理很多个顶层窗口,可能有几十个,甚至几百个、几千个都没问题。而且,正如其他回答提到的,你不能在单独的线程里运行这个事件循环。
所以,要解决你的代码问题,你只需要使用一个事件循环,并让它在主线程里运行就可以了。
Tkinter在处理来自多个线程的输入时会有一些问题,所以我使用mtTkinter。你不需要修改任何代码,一切都会正常工作。只需把mtTkinter导入替代Tkinter就可以了。
你可以在这里获取它:
Tkinter 是一个图形界面库,它只能在主线程中运行。你可以查看 相关文档:
所有的用户界面代码都应该在主线程中运行,让其他线程把请求写入一个队列;例如:
接下来会有一个详细的例子,展示了其他线程如何把请求写入队列,而主循环则完全负责与 Tk 的所有直接交互。
很多对象和子系统不喜欢接收来自多个不同线程的请求,尤其是在图形界面工具包中,通常需要特别使用主线程。
解决这个问题的正确 Python 结构是始终为这个挑剔的对象或子系统分配一个线程(如果必须的话,就是主线程);其他任何需要与这个子系统或对象交互的线程,都必须通过将请求放入队列来获取服务(如果需要结果,还可能需要在一个“返回队列”上等待结果)。这也是一种非常合理的 Python 线程架构,适用于一般的多线程编程(我在《Python 速查手册》中详细讲解过,但那是另一个话题;-)。