PyQt QThread 中的错误未打印

1 投票
1 回答
4368 浏览
提问于 2025-04-17 23:26

我在使用QThread的时候遇到问题,就是没有看到错误信息。我有一个Worker(QThread):

class Worker(QThread):
    def __init__(self, parent=None):
        QThread.__init__(self, parent)

    def run(self):
        #   do some heavy work here
        print 1     #   1 is printed in my OutputWidget
        print a     #   a is not defined and it should print an error

如果我在一个新文件里只运行最后一行(print a),而且a没有定义,我会得到一个错误(这个是正确的,也是我预期的):

Traceback (most recent call last):
  File "C:\...\output_widget\output_from_qthread_V2.py", line 1, in run
    print a
NameError: global name 'a' is not defined

OutputWidget

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore

from output_widget_ui import Ui_Form

class TextOutputSignal(QtCore.QObject):
    textWritten = QtCore.pyqtSignal(str)
    def write(self, text):
        self.textWritten.emit(str(text))

class OutputWidget(QtGui.QWidget):

    def __init__(self, parent=None, flags=0):
        super(OutputWidget, self).__init__(parent)
        sys.stdout = TextOutputSignal(textWritten=self.normalOutputWritten)

        self.ui = Ui_Form()
        self.ui.setupUi(self)

    def __del__(self):
        #    Restore sys.stdout
        sys.stdout = sys.__stdout__

    def normalOutputWritten(self, text):
        cursor = self.ui.textOutputWidget.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End)
        cursor.insertText(text)
        self.ui.textOutputWidget.ensureCursorVisible()

我想知道为什么这个错误既没有在OutputWidget里显示,也没有在控制台(用的是eclipse)里显示?我该怎么解决这个问题呢?

1 个回答

5

首先,你需要在你的线程中添加一个“捕获所有异常”的代码块,否则如果出现了异常(比如说变量a不存在),这个异常就会被忽视,导致线程和应用程序直接退出。然后,从这个捕获所有异常的地方,你可以调用sys.exc_info()来获取错误的详细信息,并使用sys.excepthook()来处理这些错误信息。这样,错误信息就会发送到错误输出(stderr),而不是普通输出(stdout)。所以你可以尝试在OutputWidget中使用:

def __init__(...):
    ...
    sys.stdout = TextOutputSignal(textWritten=self.normalOutputWritten)
    sys.stderr = TextOutputSignal(textWritten=self.errorOutputWritten)
    ...

def errorOutputWritten(self, text):
    self.normalOutputWritten("*** ERROR: " + text)

然后在你的工作线程中:

class Worker(QThread):
    ...

    def run(self):
        try: 
            #   do some heavy work here
            print 1     #   1 is printed in my OutputWidget
            print a     #   a is not defined and it should print an error
        except: 
            (type, value, traceback) = sys.exc_info()
            sys.excepthook(type, value, traceback)
            ...decide if should exit thread or what...

在上面的代码中,如果出现异常,线程会优雅地退出,并且你会在控制台看到错误信息。一般来说,你还需要向主线程发送一些错误状态,这样应用程序才能决定该怎么做:比如告诉用户网络连接丢失了等等,应用程序必须决定是退出、保存工作,还是其他操作。

需要注意的是,如果你有一些特定的异常想要直接处理,比如除以0或者NameError,你可以直接捕获这些异常,同时保留“捕获所有异常”的代码块来处理真正意外的情况(也就是那些你没有预料到的异常):

    def run(self):
        try: 
            #   do some heavy work here
            print 1     #   1 is printed in my OutputWidget
            print a     #   a is not defined and it should print an error
        except NameError, exc: 
            sys.stderr.write( "Name Error\n" );
            ...decide if should exit thread or what...
        except: 
            (type, value, traceback) = sys.exc_info()
            sys.excepthook(type, value, traceback)
            ...decide if should exit thread or what...

撰写回答