PyQt作为Windows应用程序崩溃而崩溃

时间:2021-07-26 23:03:55

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:

我有一个非常短的PyQt程序(nb,这是一个PythonFiddle的链接——它在Firefox中似乎崩溃了,所以代码也在下面),它将输出输出到一个QTextEdit(使用这个答案的代码)。当我运行代码(在Windows上)时,它会导致一个APPCRASH。一些观察:

  • if I add a time.sleep call (i.e. uncomment out line 53), then the program completes fine
  • 如果我加上一个时间。睡眠调用(即取消注释第53行),然后程序完成得很好
  • if I don’t redirect the output to the QEdit (i.e. comment out line 34) then it works regardless of whether the time.sleep call is commented out or not
  • 如果我不将输出重定向到QEdit(即注释掉第34行),那么无论何时它都可以工作。睡眠呼叫是否被注释掉

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!

我认为这意味着重定向stdout的代码以某种方式被破坏了——但是我很难理解它有什么问题导致了这种行为——任何收到的指针都是感激的!


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

问题签名:问题事件名:APPCRASH应用程序名:pythonw。exe应用程序版本:0.0.0.0应用时间戳:5193f3be故障模块名称:QtGui4。mso - fareast - font - family:宋体;mso - bidi - font - family: " times new roman "; mso - bidi - theme - font


Pythonfiddle crashes horribly on Firefox (for me at least), so code below too:

python在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 个解决方案

#1


5  

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.

好的,首先关于你的程序的线程安全。因为您正在将QObject连接到stdout,所以对print的调用将与这个QObject交互。但是,您应该只与创建在其中的线程中的QObject进行交互。如果从QObject所在的线程以外的线程调用print,那么您拥有的实现可能是不安全的。

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:

在您的示例中,您正在从QThread调用print,而EmmittingStream(QObject)驻留在主线程中。因此,我建议您修改代码,使其成为线程安全的,如下所示:

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!

注意,现在从应用程序的主线上调用print将导致线程不安全的行为。既然你现在还没这么做,你现在还可以,但是要小心!

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.

我建议您阅读http://qt-project.org/doc/qt-4.8/threads-qobject.html,并仔细阅读它所谈论的所有内容。理解该文档是避免由于线程导致的恼人崩溃的关键。

--

- - -

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!

现在,崩溃的问题显然是由您给processEvents()打电话引起的。我还没弄明白为什么(我想这与线程有关……),但我可以告诉你,您不需要这一行!因为您正在使用信号/插槽,所以一旦normalOutputWritten方法运行后,control将返回到Qt事件循环,它将继续正常处理事件。因此,不需要强制Qt处理事件!

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

编辑:关于如何使您的发送流/打印调用线程安全的示例,请参见这里:将stdout和stderr从辅助线程重定向到PyQt4 QTextEdit

#1


5  

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.

好的,首先关于你的程序的线程安全。因为您正在将QObject连接到stdout,所以对print的调用将与这个QObject交互。但是,您应该只与创建在其中的线程中的QObject进行交互。如果从QObject所在的线程以外的线程调用print,那么您拥有的实现可能是不安全的。

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:

在您的示例中,您正在从QThread调用print,而EmmittingStream(QObject)驻留在主线程中。因此,我建议您修改代码,使其成为线程安全的,如下所示:

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!

注意,现在从应用程序的主线上调用print将导致线程不安全的行为。既然你现在还没这么做,你现在还可以,但是要小心!

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.

我建议您阅读http://qt-project.org/doc/qt-4.8/threads-qobject.html,并仔细阅读它所谈论的所有内容。理解该文档是避免由于线程导致的恼人崩溃的关键。

--

- - -

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!

现在,崩溃的问题显然是由您给processEvents()打电话引起的。我还没弄明白为什么(我想这与线程有关……),但我可以告诉你,您不需要这一行!因为您正在使用信号/插槽,所以一旦normalOutputWritten方法运行后,control将返回到Qt事件循环,它将继续正常处理事件。因此,不需要强制Qt处理事件!

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

编辑:关于如何使您的发送流/打印调用线程安全的示例,请参见这里:将stdout和stderr从辅助线程重定向到PyQt4 QTextEdit