输出控制台(打印功能)到 Gui TextArea

Posted

技术标签:

【中文标题】输出控制台(打印功能)到 Gui TextArea【英文标题】:Output Console (print function) to Gui TextArea 【发布时间】:2021-08-14 11:03:24 【问题描述】:

我在 Windows 上为 Python 3.8 制作了 QtQuick Window Gui 应用程序。我想不通的最后一件事是如何在 Gui 文本区域中显示 Python print()。我想要的是,无论在我的 Python 代码中,无论在哪里打印语句并在运行时执行,我都想将它输出到我的 Gui 应用程序中的 TextArea 中

我阅读了以下帖子,但未能实现它,发生了不同的错误并且比以前更困惑:

最接近和最有用的是这个:

How to capture output of Python's interpreter and show in a Text widget?

还有其他一些人:

Python/PyQt/Qt Threading: How do I print stdout/stderr right away?

How to Redirect a Python Console output to a QTextBox

How can I flush the output of the print function?

How do I direct console output to a pyqt5 plainTextEdit widget with Python?

Python Printing StdOut As It Received

将字符串从 Python 发送到 QML TextArea 的工作示例代码

main.py

import os
from pathlib import Path
import sys
from vantage import daily

# load GUI libs
from PySide2.QtGui import QGuiApplication
from PySide2.QtCore import QSettings, QObject, Signal, Slot
from PySide2.QtQml import QQmlApplicationEngine


# load app
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))



class Backend(QObject):
     textwritten = Signal(str, arguments=['writen'])   


     def __init__(self):
         super().__init__()

         self.timer = QTimer()
         self.timer.setInterval(100)  
         self.timer.timeout.connect(self.writer)
         self.timer.start()

        
         # console output write function
         def writer(self):
             towrite = 'i am writing'
             self.textwritten.emit(str(towrite))


# create an instance of the Python object (Backend class)
back_end = Backend()


# give data back to QML
engine.rootObjects()[0].setProperty('writen', back_end)

# close app
sys.exit(app.exec_())

main.qml

import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.15



Window 
    width: 640
    height: 480
    visible: true
    color: "#2f2f2f"
    title: qsTr("alpha")

    /*print out console text*/
    property string texted: "Console Text"
    property QtObject writen
    ScrollView 
        id: scrollViewCon
        x: 58
        y: 306
        width: 507
        height: 100
        ScrollBar.vertical.verticalPadding: 4
        ScrollBar.vertical.minimumSize: 0.4
        ScrollBar.vertical.contentItem: Rectangle 
             implicitWidth: 6
             implicitHeight: 100
             radius: width / 2
             color: control.pressed ? "#81e889" : "#f9930b"
        

    TextArea 
        font.family: control.font
        font.pointSize: 8
        color:"#f9930b"
        wrapMode: TextEdit.Wrap
        KeyNavigation.priority: KeyNavigation.BeforeItem
        KeyNavigation.tab: textField
        placeholderTextColor : "#f9930b"
        opacity: 1
        
        text: texted
        placeholderText: texted //qsTr("Console")
        readOnly: true
        background: Rectangle 
            radius: 12
            border.width: 2
            border.color: "#f9930b"
        
    

Connections 
    target: writen

    function onTextwritten(msg) 
        texted = msg;
    
  

我认为需要发生的是每次 sys.stdout 被 print() 调用时,它都会自己发出一个信号?

保持 main.qml 不变,只更改 main.py

main.py

...

class Backend(QObject):
     textwritten = Signal(str, arguments=['writen'])   


     def __init__(self):
         super().__init__()

         sys.stdout = self.writer(str(sys.stdout))


         def writer(self, message):
             #towrite = 'i am writing'
             self.textwritten.emit(message)

         ...

【问题讨论】:

您是坚持使用 print() 还是只需要一种从代码中的任何位置输出文本的方法?如果是后者,我建议看一下 qDebug 和/或 python 日志库。我个人将日志输出重定向到 QPlainTextEdit 并且效果很好。 是的,基本上只是打印出标准输出流和标准错误,我想稍后在没有控制台窗口的情况下打包应用程序,以便用户可以得到一些反馈代码中当前发生的事情 【参考方案1】:

print 函数覆盖 sys.stdout,因此解决方案是分配一些 QObject,该 QObject 具有发出信号的 write 方法。为此,您可以使用 contextlib.redirect_stdout:

import os
import sys
from contextlib import redirect_stdout
from functools import cached_property
from pathlib import Path

from PySide2.QtCore import (
    QCoreApplication,
    QDateTime,
    QObject,
    Qt,
    QTimer,
    QUrl,
    Signal,
)
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine

CURRENT_DIRECTORY = Path(__file__).resolve().parent


class RedirectHelper(QObject):
    stream_changed = Signal(str, name="streamChanged", arguments=["stream"])

    def write(self, message):
        self.stream_changed.emit(message)


class TimerTest(QObject):
    @cached_property
    def timer(self):
        return QTimer(interval=1000, timeout=self.handle_timeout)

    def handle_timeout(self):
        print(QDateTime.currentDateTime().toString())

    def start(self):
        self.timer.start()


def main():
    ret = 0
    redirect_helper = RedirectHelper()
    with redirect_stdout(redirect_helper):

        app = QGuiApplication(sys.argv)
        engine = QQmlApplicationEngine()

        engine.rootContext().setContextProperty("redirectHelper", redirect_helper)
        filename = os.fspath(CURRENT_DIRECTORY / "main.qml")
        url = QUrl.fromLocalFile(filename)

        def handle_object_created(obj, obj_url):
            if obj is None and url == obj_url:
                QCoreApplication.exit(-1)

        engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
        engine.load(url)

        timer_test = TimerTest()
        timer_test.start()

        ret = app.exec_()

    sys.exit(ret)


if __name__ == "__main__":
    main()
import QtQuick 2.12
import QtQuick.Controls 2.12

ApplicationWindow 
    id: root

    width: 640
    height: 480
    visible: true

    Flickable 
        id: flickable

        flickableDirection: Flickable.VerticalFlick
        anchors.fill: parent

        TextArea.flickable: TextArea 
            id: textArea

            anchors.fill: parent
            readOnly: true
            font.pointSize: 8
            color: "#f9930b"
            wrapMode: TextEdit.Wrap
            placeholderTextColor: "#f9930b"
            opacity: 1
            placeholderText: qsTr("Console")

            background: Rectangle 
                radius: 12
                border.width: 2
                border.color: "#f9930b"
            

        

        ScrollBar.vertical: ScrollBar 
        

    

    Connections 
        function onStreamChanged(stream) 
            textArea.insert(textArea.length, stream);
        

        target: redirectHelper
    


【讨论】:

谢谢 eyllanesc,据我测试,它符合我的要求。效果很好。您将 Scrollview 更改为 flickable 的任何特定原因,我的意思是它适用于两者。

以上是关于输出控制台(打印功能)到 Gui TextArea的主要内容,如果未能解决你的问题,请参考以下文章

从标准输出实时输出到 wx.TextArea

将命令行文本输出到 Javafx Textarea

记住 QML 元素中的滚动位置

在 JtextArea 或 JtextPane 上打印

jqPrint 无法打印textArea

如何打印 TextArea 的内容?