如何捕获Python解释器的输出并在文本小部件中显示?

问题描述 投票:0回答:5

我有一个使用 PyQt 的 Python 程序,旨在在 Windows 上运行。 该程序进行大量操作并打印大量信息。 但由于我想冻结它并且不希望出现提示屏幕,所以我希望所有这些信息都出现在主应用程序中,在 QTextEdit 等中。 我怎样才能使程序工作,以便它从解释器获取输出并同时将其显示在文本编辑上,就像在真正的解释器上一样?

python python-3.x pyqt
5个回答
41
投票

我假设“解释器的输出”是指写入控制台或终端窗口的输出,例如使用

print()
生成的输出。

Python 生成的所有控制台输出都会写入程序的输出流

sys.stdout
(正常输出)和
sys.stderr
(错误输出,例如异常回溯)。这些是类似文件的对象。

您可以用您自己的类似文件的对象替换这些流。您的所有自定义实现必须提供一个

write(text)
函数。通过提供您自己的实现,您可以将所有输出转发到您的小部件:

class MyStream(object):
    def write(self, text):
        # Add text to a QTextEdit...

sys.stdout = MyStream()
sys.stderr = MyStream()

如果您需要重置这些流,它们仍然可以作为

sys.__stdout__
sys.__stderr__
:

sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

更新

这里是 PyQt4 的一些工作代码。首先定义一个流,报告使用 Qt 信号写入其中的数据:

from PyQt4 import QtCore

class EmittingStream(QtCore.QObject):

    textWritten = QtCore.pyqtSignal(str)

    def write(self, text):
        self.textWritten.emit(str(text))

现在,在 GUI 中,将此流的实例安装到

sys.stdout
并将
textWritten
信号连接到将文本写入
QTextEdit
的槽:

# Within your main window class...

def __init__(self, parent=None, **kwargs):
    # ...

    # Install the custom output stream
    sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)

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

def normalOutputWritten(self, text):
    """Append text to the QTextEdit."""
    # Maybe QTextEdit.append() works as well, but this is how I do it:
    cursor = self.textEdit.textCursor()
    cursor.movePosition(QtGui.QTextCursor.End)
    cursor.insertText(text)
    self.textEdit.setTextCursor(cursor)
    self.textEdit.ensureCursorVisible()

7
投票

不幸的是,该示例不适用于 PySide。它给出以下错误:

sys.stdout = EmittingStream(textWritten=self.write2Console)
AttributeError: 'textWritten()' is not a Qt property or a signal

我们需要进行以下更改才能使其与 PySide 一起使用:

sys.stdout = EmittingStream()
self.connect(sys.stdout,QtCore.SIGNAL('textWritten(QString)'),self.write2Console)

2
投票

我建议你使用日志库。 http://docs.python.org/library/logging.html 您可以编写自己的日志处理程序来与 QTextEdit 进行通信。这是一个很好的教程,可以帮助您入门:http://pantburk.info/?blog=77


1
投票

我不久前在此发布了一些有关 PySide 终端应用程序的内容PySide 中的类似终端应用程序。如果您正在查看 PyQt,那么也请检查 PySide。除了许可和语法上的一些差异之外,它们基本上是相同的。


1
投票

谢谢费迪南德和汤米。我已经发布了 PySide6 的新窗口解决方案,因为该线程经常出现在搜索中。当我有一个 QThread 在后台做一堆事情时,我想创建一个“加载”窗口。我在启动线程时创建此窗口,然后从线程完成时连接 .close() 。我从另一个 SO Q&A 中得到了 closeEvent 的想法。我可以从 Windows 终端运行 GUI,在终端中查看常用的标准输出,调用我的加载线程函数,窗口弹出,标准输出转到窗口,然后完成后窗口关闭,标准输出返回到终端。

class StdOutWindow(QtWidgets.QWidget):
    def __init__(self):
        super(StdOutWindow, self).__init__()
        layout = QtWidgets.QVBoxLayout()
        self.setWindowTitle('Loading Data')
        self.txt_edit = QtWidgets.QTextEdit()
        layout.addWidget(self.txt_edit)
        self.setLayout(layout)
        self.setMinimumSize(600, 300)

        sys.stdout = EmittingStream()
        sys.stdout.text_written.connect(self.normal_output_written)

    def closeEvent(self, event: QtGui.QCloseEvent) -> None:
        sys.stdout = sys.__stdout__
        event.accept()

    def normal_output_written(self, text):
        cursor = self.txt_edit.textCursor()
        cursor.movePosition(QtGui.QTextCursor.End)
        cursor.insertText(text)
        self.txt_edit.setTextCursor(cursor)
        self.txt_edit.ensureCursorVisible()

class EmittingStream(QtCore.QObject):
    text_written = QtCore.Signal(str)

    def write(self, text):
        self.text_written.emit(str(text))
© www.soinside.com 2019 - 2024. All rights reserved.