使用 Qt 运行日志流送时 Python 崩溃

Posted

技术标签:

【中文标题】使用 Qt 运行日志流送时 Python 崩溃【英文标题】:Python crashes when running a log streamer using Qt 【发布时间】:2020-07-13 11:39:36 【问题描述】:

目标

我有一个在运行时记录文件 (realtime.log) 的进程,我想在我的应用程序中实时打印该文件的每一新行。换句话说,我想将进程的输出重定向到 GUI。这意味着我有两个不同的进程在运行:“引擎”和 GUI。

我已经通过使用 Tkinter 实现了这一点,但由于我必须制作更复杂、更专业和更美观的 GUI,我决定改用 Qt for Python (PySide2)。

问题

Python 经常在我启动 GUI 时崩溃并显示错误消息:Python 已停止工作。窗口开始打印线条,但有时它会停止工作。

经过多次尝试和搜索,我发现程序只有在单击 GUI 窗口时才会崩溃。此外,程序不会突然崩溃,而是在引擎执行结束时崩溃。

环境

Windows 10 Python 3.6.5 PySide2 5.12.6

代码

请注意,这是一个简化版本。

datalog_path = "realtime.log"


def get_array_from_file(file_path):
    try:
        with open(file_path, 'r') as file:
            lines = file.readlines()
        return lines
    except:
        print('error in file access')


class Streamer(QRunnable):
    def __init__(self, stream, old_array, edit):
        super().__init__()
        self.stream = stream
        self.old_array = old_array
        self.edit = edit

    def run(self):
        try:
            while self.stream:
                array_file = get_array_from_file(datalog_path)
                if len(array_file) != len(self.old_array):
                    for line in array_file[len(self.old_array) - 1:len(array_file)]:
                        self.edit.append(line)
                        # print(line)
                        self.old_array.append(line)
        except:
            print('problem in streaming funct')


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()

        self.setWindowTitle("DATALOG")
        self.thread_pool = QThreadPool()
        self.edit = QTextEdit()

        self.stream = True
        self.old_array = get_array_from_file(datalog_path)
        self.streamer = Streamer(self.stream, self.old_array, self.edit)
        self.thread_pool.start(self.streamer)

        window = QWidget()
        layout.addWidget(self.edit)
        window.setLayout(layout)
        self.setCentralWidget(window)


    def closeEvent(self, event):
        self.stream = False
        event.accept()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    app.exec_()

【问题讨论】:

【参考方案1】:

@hyde answer 指出了问题的原因,但它的解决方案不适用于 PySide2(在 PyQt5 中必须进行小修改,请参阅 this),另一种方法是创建一个具有信号:

class Signaller(QtCore.QObject):
    textChanged = Signal(str)
class Streamer(QRunnable):
    def __init__(self, stream, old_array):
        super().__init__()
        self.stream = stream
        self.old_array = old_array
        self.signaller = Signaller()

    def run(self):
        try:
            while self.stream:
                array_file = get_array_from_file(datalog_path)
                if len(array_file) != len(self.old_array):
                    for line in array_file[len(self.old_array) - 1:len(array_file)]:
                        self.signaller.textChanged.emit(line)
                        # print(line)
                        self.old_array.append(line)
        except:
            print('problem in streaming funct')
self.stream = True
self.old_array = get_array_from_file(datalog_path)
self.streamer = Streamer(self.stream, self.old_array)
self.streamer.signaller.textChanged.connect(self.edit.append)
self.thread_pool.start(self.streamer)

【讨论】:

【参考方案2】:

虽然我对 Python Qt 不太熟悉,但问题可能是,您使用来自不同线程的 GUI 对象 edit。这是不允许的,GUI 部分必须全部在同一个(主)线程中运行!

要解决此问题,您需要为线程提供一些其他方式来传达 UI 更改。由于您的 QRunnable 不是 QObject,因此您不能只发出信号,而是可以在其可调用方法上使用 QMetaObject::invokeMethod。请让我知道这是否直接有效:

# self.edit.append(line) # can't do this from a thread!
# instead, invoke append through GUI thread event loop
QtCore.QMetaObject.invokeMethod(self.edit, 
                                'append', 
                                QtCore.Qt.QueuedConnection, 
                                QtCore.QGenericArgument('QString', line)

【讨论】:

非常感谢您的回答。正如@eyllanesc 所说,您的解决方案不适用于 PySide2,但这个概念绝对是正确的。

以上是关于使用 Qt 运行日志流送时 Python 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

由于导入,在使用 qt4agg 运行 matplotlib 后关闭时 Python 脚本崩溃

Qt Creator 在尝试运行 OpenCV 程序时崩溃。 [ntdll.dll 崩溃]

使用 Qt 的程序,OpenCV 从 VS 启动时运行良好,但在使用 OpenCV 功能时运行可执行文件通常会崩溃

如何让 QT 应用程序写入崩溃日志

Qt串口程序在另一个系统上运行时崩溃

UE4(虚幻4)预算上的纹理流送池(texture streaming poor over)报警解决方法