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

Output Console (print function) to Gui TextArea

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

中的 TextArea

我阅读了以下内容post,但未能实现,出现了不同的错误并且比之前更加困惑:

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

和其他一些人:

工作示例代码将字符串从 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 函数覆盖 sys.stdout 所以解决方案是分配一些具有 write 方法的 QObject 来发出信号。为此,您可以使用 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
    }

}