PyQt崩溃为Windows APPCRASH

时间:2014-01-10 18:28:23

标签: python pyqt

我有一个very short PyQt program(nb是一个PythonFiddle链接 - 这似乎在Firefox中崩溃,所以代码也在下面发布),它将输出打印到QTextEdit(使用代码{{3 }})。当我运行代码(在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
  操作系统版本: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_())

1 个答案:

答案 0 :(得分:5)

好的,首先要了解程序的线程安全性。由于您要将QObject与stdout相关联,因此对print的调用将与此QObject进行互动。但是你应该只与它创建的线程中的QObject进行交互。如果你从一个以外的线程调用print,你的实现可能是不安全QObject位于。

在您的情况下,当print位于主线程中时,您从QThread调用EmmittingStream(QObject)。我建议你这样更改代码,使其成为线程安全的,如下所示:

self.myLongTask = TaskThread()
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
sys.stdout.moveToThread(self.myLongTask)
self.myLongTask.start()

请注意,现在从应用程序的MainThread调用print将导致线程不安全的行为。既然你现在没有这样做,你现在就可以了,但要注意!

我真的建议你阅读http://qt-project.org/doc/qt-4.8/threads-qobject.html并了解它所谈论的一切。理解该文档是避免因线程而导致恼人崩溃的关键。

-

现在,崩溃问题显然是由您致电processEvents()引起的。我还没有解决为什么(我想它与某些方面的线程有关......),但我可以告诉你,你不需要那条线!因为您正在使用信号/槽,所以一旦normalOutputWritten方法运行,控制就会返回到Qt事件循环,它将继续正常处理事件。因此没有必要强迫Qt处理事件!

希望有所帮助!

编辑:有关如何使EmittingStream / print调用线程安全的示例,请参阅此处:Redirecting stdout and stderr to a PyQt4 QTextEdit from a secondary thread