我有一个非常短的PyQt程序(这是一个PythonFiddle链接 - 这似乎在Firefox中崩溃,所以代码也发布在下面),它打印输出到QTextEdit
(使用来自这个SO答案的代码).当我运行代码(在Windows上)时,它会导致APPCRASH.一些观察:
如果我添加一个time.sleep
调用(即取消注释第53行),则程序完成正常
如果我不将输出重定向到QEdit(即注释掉第34行),那么无论time.sleep
呼叫是否被注释掉,它都能正常工作
我认为这意味着重定向的代码stdout
以某种方式被破坏了 - 但是我很难理解它导致这种行为的错误 - 任何指针都感激不尽!
完整的错误消息
问题签名:
问题事件名称:APPCRASH
应用程序名称:pythonw.exe
应用程序版本:0.0.0.0
应用程序时间戳:5193f3be
故障模块名称:QtGui4.dll
故障模块版本:4.8.5.0
故障模块时间戳:52133a81
异常代码:c00000fd
异常偏移: 00000000005cbdb7
OS版本:6.1.7601.2.1.0.256.48
区域设置ID:2057
附加信息1:5c9c
附加信息2:5c9c27bb85eb40149b414993f172d16f
附加信息3:bc7e
附加信息4:bc7e721eaea1ec56417325adaec101aa
Pythonfiddle在Firefox上崩溃(至少对我而言),所以下面的代码也是如此:
import os, sys, time, calendar, math from PyQt4 import QtCore, QtGui class EmittingStream(QtCore.QObject): textWritten = QtCore.pyqtSignal(str) def write(self, text): self.textWritten.emit(str(text)) class myWrapper(QtGui.QMainWindow): def __init__(self): super(myWrapper, self).__init__() self.toolbar = self.addToolBar("MainMenu") self.toolbar.addAction(QtGui.QAction("myProg", self, triggered=self.myProgActions)) def myProgActions(self): self.setCentralWidget(myWidget()) class myWidget(QtGui.QWidget): def __init__(self): super(myWidget, self).__init__() self.myBtn = QtGui.QPushButton('Run!', self) self.myBtn.clicked.connect(self.startTest) self.outputViewer = QtGui.QTextEdit() self.grid = QtGui.QGridLayout() self.grid.addWidget(self.myBtn) self.grid.addWidget(self.outputViewer) self.setLayout(self.grid) def startTest(self): self.myLongTask = TaskThread() sys.stdout = EmittingStream(textWritten=self.normalOutputWritten) self.myLongTask.start() def normalOutputWritten(self, text): cursor = self.outputViewer.textCursor() cursor.movePosition(QtGui.QTextCursor.End) cursor.insertText(text) self.outputViewer.setTextCursor(cursor) self.outputViewer.ensureCursorVisible() QtGui.qApp.processEvents() class TaskThread(QtCore.QThread): def __init__(self): super(TaskThread, self).__init__() def run(self): myProgClass() class myProgClass: def __init__(self): for i in range(0,100): print "thread", i+1, " ", math.sqrt(i) #time.sleep(0.005) if __name__ == '__main__': app = QtGui.QApplication(sys.argv) myApp = myWrapper() myApp.show() sys.exit(app.exec_())
three_pineap.. 5
好的,首先要了解程序的线程安全性.因为您要连接QObject
到标准输出,所以调用print
将与此进行交互QObject
.但是你应该只与QObject
它创建的线程中的a进行交互.如果你从一个不在其中的线程调用,那么你所拥有的实现可能是线程不安全的.print
QObject
在你的情况下,你在print
一段QThread
时间内调用EmmittingStream(QObject)
驻留在主线程中.我建议你这样更改你的代码,使其成为线程安全,如下所示:
self.myLongTask = TaskThread() sys.stdout = EmittingStream(textWritten=self.normalOutputWritten) sys.stdout.moveToThread(self.myLongTask) self.myLongTask.start()
请注意,现在print
从应用程序的MainThread 调用将导致线程不安全的行为.既然你现在没有这样做,你现在就可以了,但要注意!
我真的建议你阅读http://qt-project.org/doc/qt-4.8/threads-qobject.html并了解它所谈论的一切.理解该文档是避免因线程而导致恼人崩溃的关键.
-
现在,崩溃的问题显然是由你的电话引起的processEvents()
.我没有弄清楚为什么(我想它与某些方面的线程有关...),但我可以告诉你,你不需要那条线!因为您正在使用信号/插槽,所以一旦normalOutputWritten
方法运行,控制就会返回到Qt事件循环,并且它将继续正常处理事件.因此没有必要强迫Qt处理事件!
希望有所帮助!
编辑:有关如何使EmittingStream
/ print
调用线程安全的示例,请参阅此处:将stdout和stderr从辅助线程重定向到PyQt4 QTextEdit
好的,首先要了解程序的线程安全性.因为您要连接QObject
到标准输出,所以调用print
将与此进行交互QObject
.但是你应该只与QObject
它创建的线程中的a进行交互.如果你从一个不在其中的线程调用,那么你所拥有的实现可能是线程不安全的.print
QObject
在你的情况下,你在print
一段QThread
时间内调用EmmittingStream(QObject)
驻留在主线程中.我建议你这样更改你的代码,使其成为线程安全,如下所示:
self.myLongTask = TaskThread() sys.stdout = EmittingStream(textWritten=self.normalOutputWritten) sys.stdout.moveToThread(self.myLongTask) self.myLongTask.start()
请注意,现在print
从应用程序的MainThread 调用将导致线程不安全的行为.既然你现在没有这样做,你现在就可以了,但要注意!
我真的建议你阅读http://qt-project.org/doc/qt-4.8/threads-qobject.html并了解它所谈论的一切.理解该文档是避免因线程而导致恼人崩溃的关键.
-
现在,崩溃的问题显然是由你的电话引起的processEvents()
.我没有弄清楚为什么(我想它与某些方面的线程有关...),但我可以告诉你,你不需要那条线!因为您正在使用信号/插槽,所以一旦normalOutputWritten
方法运行,控制就会返回到Qt事件循环,并且它将继续正常处理事件.因此没有必要强迫Qt处理事件!
希望有所帮助!
编辑:有关如何使EmittingStream
/ print
调用线程安全的示例,请参阅此处:将stdout和stderr从辅助线程重定向到PyQt4 QTextEdit