4

在我的多线程(*)控制台应用程序中使用 tk 会导致它在没有堆栈跟踪的情况下崩溃,并给出消息“Tcl_WaitForEvent: Notifier not initialized Abort trap”。

症状是我的所有程序功能都运行良好,直到我打开 tk 窗口 - 然后下一个操作将导致崩溃。

立即搜索发现 Tkinter 相对于 Python 线程并不安全,因此我确保在主线程以外的任何地方都没有调用任何 Tk 函数。崩溃仍在继续。

我损失了几个小时,因为我相信是我使用的特定命令导致程序崩溃——但最终我意识到任何键盘输入都会导致程序崩溃。

经过大量调试,我最终将其归结为一个演示问题的小程序,暴露了我认为是错误或肯定需要 Tkinter 库中文档的功能。

我正在处理我正在调试的这个帖子。我将发布它并回答我自己的问题,希望它能防止下一个人在它上面浪费一天。

--

(* - 是的,它当然需要是多线程的。我有一个用于套接字连接的线程,一个监听麦克风并查找级别的线程,一个驱动我的串行端口的线程等等。在每种情况下,事情我在线程上阅读大部分时间自然会阻塞。)

4

2 回答 2

8

解决方案!

问题是,如果您在与 tk 线程不同的线程中从 Python 的 raw_input 读取数据,则 tk 会崩溃!

一个演示这个问题的小程序在这里。如果你运行它,它会很高兴地从第二个线程获得键盘输入——直到你输入命令“tk”,它会打开一个空的 tk 窗口。你可以用那个窗口做任何你喜欢的事情——直到你在控制台窗口中输入并按下回车键,当整个程序崩溃时。

为什么我在不是主线程的线程中从 raw_input 读取?

我的程序是一个控制台应用程序,但我控制着许多不同的部分,其中之一是 pi3d OpenGL ES 2.0 图形库,它必须以或接近主 Python 线程的帧速率进行更新。

如何解决它?

足够“简单” - 注册 tk 菜单事件并直接获取密钥!除了这是一个糟糕的解决方案,因为您必须模拟控制台已经为您所做的所有事情——删除、左右箭头和诸如此类的事情。但这就是我必须做的。

该程序是否应该成为一个成熟的传统知识应用程序?

但我不能那样做——这个程序的全部意义在于你可以通过终端窗口运行它——经常连接到无头机器。坦率地说,我更有可能把它变成一个诅咒程序!

tk 窗口只是整个事情的一小部分 - 如果您没有连接硬件(或者不想一直在脸上闪烁),那么它只是一个在开发时显示模拟灯的窗口。我不会尝试在无头机器上使用它,这很好。

这是一个错误吗?

我总是不愿意在不是我自己的软件上贴上这样的标签,但我很难想出任何其他描述。它会导致崩溃,并且该崩溃不会产生任何类型的有用信息。我认为 Tkinter 在从不同线程调用时简单地崩溃有点蹩脚,但至少记录了这种行为(如果你深入研究的话) - 在这种情况下,我正在调用 Python 内置,所以我没有基于期望它会与这个库进行交互,并且没有关于这个问题的文档。

有没有办法解决?

我有点希望有一个解决方法——这个单页程序是一长串功能中的一个项目,现在变成了一整天令人头疼的调试会议,我不想这样做至少在此之后再扔一天,因为这段时间实际上都没有产生新功能。

最好的情况是,如果 tk 团队承认这是一个错误并提出了修复方案。但我不希望在一年后在我的桌面上...

所以也许真正最好的事情是如果有某种方法可以让 tk 简单地忽略键盘,而不是崩溃。我用 tk 的“忙”做了一个小实验,但这没有用,而且似乎不是正确的事情。

稍后,我现在正在考虑将照明作为一个独立的程序运行,一个使用 Python 的子进程库的单独子进程,并通过标准输入向它发送文本命令。如果这是唯一正在解决的问题,这将是矫枉过正,但事实上

知道了。

用 sys.stdin.readline() 替换 raw_input() 就可以了——至少在演示中(我更新了)。随意下载并自己尝试!

我希望这可以节省其他人的时间。

于 2013-04-04T17:14:07.987 回答
1

就我而言(如@Tom Swirly 回答下的评论中所述),解决方案是切换到非交互式后端:

import matplotlib
matplotlib.use('Agg')
于 2019-09-19T15:43:13.250 回答