Reputation: 5068
I have a very short PyQt program (n.b. that is a PythonFiddle link - this seems to crash horribly in Firefox, so code is also posted below) which prints output to a QTextEdit
(using code from this SO answer). When I run the code (on Windows), it results in an APPCRASH. Some observations:
time.sleep
call (i.e. uncomment out line 53), then the program completes finetime.sleep
call is commented out or notI assume that this implies that the code redirecting the stdout
is broken somehow - but I'm struggling to understand what's wrong with it to result in this behaviour - any pointers gratefully received!
Full error message
Problem signature:
Problem Event Name: APPCRASH
Application Name: pythonw.exe
Application Version: 0.0.0.0
Application Timestamp: 5193f3be
Fault Module Name: QtGui4.dll
Fault Module Version: 4.8.5.0
Fault Module Timestamp: 52133a81
Exception Code: c00000fd
Exception Offset: 00000000005cbdb7
OS Version: 6.1.7601.2.1.0.256.48
Locale ID: 2057
Additional Information 1: 5c9c
Additional Information 2: 5c9c27bb85eb40149b414993f172d16f
Additional Information 3: bc7e
Additional Information 4: bc7e721eaea1ec56417325adaec101aa
Pythonfiddle crashes horribly on Firefox (for me at least), so code below too:
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_())
Upvotes: 3
Views: 3557
Reputation: 11849
Ok, one thing first about the thread safety of your program. Because you are connecting a QObject
to stdout, calls to print
will interact with this QObject
. But you should only interact with a QObject
from the thread it was created in. The implementation you have is potentially thread unsafe if you call print
from a thread other than the one the QObject
resides in.
In your case, you are calling print
from a QThread
while the EmmittingStream(QObject)
resides in the main thread. I suggest you thus change your code to make it threadsafe, like so:
self.myLongTask = TaskThread()
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
sys.stdout.moveToThread(self.myLongTask)
self.myLongTask.start()
Note that now calling print
from the MainThread of your application will result in thread-unsafe behaviour. Since you aren't doing that at the moment, you are OK for now, but be warned!
I really suggest you read http://qt-project.org/doc/qt-4.8/threads-qobject.html and get your head around everything it talks about. Understanding that document is the key to avoiding annoying crashes due to threads.
--
Now, The issue with the crash is apparently caused by your call to processEvents()
. I haven't worked out why (I imagine it is related to threads somehow...), but I can tell you that you do not need that line! Because you are using signals/slots, once the normalOutputWritten
method runs, control returns to the Qt Event Loop anyway, and it will continue to process events as normal. There is thus no need to force Qt to process events!
Hope that helps!
EDIT: For an example of how to make your EmittingStream
/print
calls thread-safe, see here: Redirecting stdout and stderr to a PyQt4 QTextEdit from a secondary thread
Upvotes: 5