创建第二个 Toplevel 小部件时,线程 Tkinter 脚本崩溃

Posted

技术标签:

【中文标题】创建第二个 Toplevel 小部件时,线程 Tkinter 脚本崩溃【英文标题】:Threaded Tkinter script crashes when creating the second Toplevel widget 【发布时间】:2011-04-03 19:02:02 【问题描述】:

我有一个 Python 脚本,它使用 Tkinter 作为 GUI。我的小脚本应该每 X 秒创建一个 Toplevel 小部件。当我运行我的代码时,第一个 Toplevel 小部件已成功创建,但当它尝试创建第二个时,程序崩溃了。

我正在做的是使用 after 方法在 root 的主循环旁边每 5 秒调用一次函数 startCounting。每次调用此函数时,我都会将 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()

【问题讨论】:

【参考方案1】:

Tkinter 仅设计为从主线程运行。见the docs:

只需在 main 中运行所有 UI 代码 线程,并让作者写到 队列对象;例如

...下面是一个重要的示例,显示辅助线程将请求写入队列,主循环专门负责与 Tk 的所有直接交互。

许多对象和子系统不喜欢接收来自多个不同线程的请求,并且在 GUI 工具包的情况下,特别需要仅使用 ma​​in 线程的情况并不少见。

解决这个问题的正确 Python 架构始终是使用一个线程(主线程,如果必须的话)来服务于挑剔的对象或子系统;需要与所述子系统或对象交互的所有其他线程必须通过将请求排队到专用线程来获得它(如果某些请求需要结果,则可能在“返回队列”上等待结果)。这也是一个非常完善的用于通用线程的 Python 架构(我在“Python in a Nutshell”中详细阐述了它,但这是另一个主题;-)。

【讨论】:

我遇到了这个限制,这正是我使用的策略。我有一种检查所有队列的方法。然后我通过调用 root.after(ms, my_method) 在 Tkinter 的主循环中注册该方法。 my_method 中的最后一个调用是对 root.after 的另一个调用,以便 my_method 不断地在主循环中重新注册自己。重要的是 my_method 中的异常不会导致您错过向主循环注册。您可能希望在 try/finally 的最后部分调用 root.after。【参考方案2】:

Tkinter 在处理来自多个线程的输入时存在问题,我使用 mtTkinter 代替,您无需更改任何代码,一切都会正常工作。只需导入 mtTkinter 而不是 Tkinter。

你可以在这里得到它:

http://tkinter.unpythonic.net/wiki/mtTkinter

【讨论】:

【参考方案3】:

您是否有理由希望(或认为您需要)每个顶层窗口一个事件循环?单个事件循环能够处理数十个(如果不是数百或数千个)顶层窗口。而且,正如另一个答案中所指出的,您不能在单独的线程中运行此事件循环。

因此,要修复您的代码,您只需要使用一个事件循环,并让它在主线程中运行。

【讨论】:

非常感谢您的回答,多亏了你们,我才得以解决问题,我从来没有在这样的地方发过帖子,得到这么多帮助让我感到惊喜。

以上是关于创建第二个 Toplevel 小部件时,线程 Tkinter 脚本崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何检测按下的鼠标何时进入不同框架中的小部件

我在列中有几个扩展的小部件。首先我展开一个小部件。当我展开第二个时,第一个应该自动折叠

多个 QGLWidgets 和 gluUnProject

激活 Tkinter 顶层

按下保存按钮pyqt5后关闭第二个小部件

动态图像上传到备用gridster小部件上