ChrisW
ChrisW

Reputation: 5068

PyQt crashing out as a Windows APPCRASH

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:

I 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

Answers (1)

three_pineapples
three_pineapples

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

Related Questions