堆栈溢出。我再次在你这里寻求帮助,岌岌可危地悬在疯狂的边缘上。正如标题所示,这个问题是我在这里看到的几个其他问题的综合。
我有一个PyQt应用程序,我想将stdout和stderr流重新路由到我的GUI中的QTextEdit,没有延迟。
最初,我找到了以下堆栈溢出答案:https://dev59.com/P3PYa4cB1Zd3GeqPh1_c#17145093
这个方法完美地解决了问题,但有一个限制:如果stdout或stderr在CPU处理相对较长的方法时被多次更新,当主线程返回应用程序循环时,所有更新都会同时显示出来。不幸的是,我有一些需要花费20秒钟才能完成(与网络相关的)的方法,因此应用程序变得无响应 - 并且QTextEdit不会更新 - 直到它们完成。
为了解决这个问题,我将所有GUI处理委托给主线程,并使用pyqtSignals生成第二个线程来处理更长的网络操作,以通知主线程何时完成工作并传回结果。但是,当我开始测试这种编写方式的代码时,Python解释器开始在没有任何警告的情况下崩溃。这就是非常令人沮丧的地方:Python崩溃是因为 - 使用上面链接中的类 - 我已经将sys.stdout/err流分配给了QTextEdit小部件; PyQt小部件不能从应用程序线程以外的任何线程修改,而由于stdout和stderr的更新来自我创建的辅助工作线程,它们违反了此规则。我已经注释掉了重定向输出流的代码部分,确实,程序运行时没有错误。
这让我回到了原点,让我陷入了困惑的境地;假设我继续在主线程中处理GUI相关操作,并在辅助线程中处理计算和长时间操作(我已经了解到这是保持应用程序不会在用户触发事件时阻塞的最佳方法),那么如何将来自两个线程的Stdout和Stderr重定向到QTextEdit小部件?上面链接中的类对于主线程工作得很好,但当更新来自第二个线程时,它会导致Python崩溃 - 原因如上所述。
WriteStream
直接发布事件到主线程。理论上,您可以使用QApplication.postEvent()
将新构造的事件发布到您自己创建并驻留在MainThread中的QObject
,这将更新文本框。不幸的是,QApplication.postEvent
在 PySide 中会泄漏内存(https://bugreports.qt-project.org/browse/PYSIDE-205),因此我更喜欢使用中介线程,以使其与 PySide 代码兼容。我认为,在我的示例中跟踪发生的事情也更容易。 - three_pineapplesLock()
来实现线程安全(例如我使用的库如h5py、zeromq、pandas),但显然并非所有库都可以使用锁来实现线程安全(比如Qt就不行)。你真的必须根据情况具体分析,并询问库的用户/开发者。 - three_pineapplesrun
方法中检查get()
调用(对于特定的对象或字符串)的结果,并适当地跳出while循环。由于队列是线程安全的,你可以在主线程上退出时将对象/字符串/任何其他内容放入队列中。 - three_pineapples