简体   繁体   English

PyQT:在窗口中获取实时打印输出

[英]PyQT: get real time print output in window

I'm working on a GUI for calling a function 'my_function' when the button 'my_button' is pushed.我正在开发一个 GUI,用于在按下按钮“my_button”时调用函数“my_function”。

This function processes data iteratively.此函数以迭代方式处理数据。 It contains a 'for' loop, and at each iteration it prints out a message that shows the progress of my function.它包含一个“for”循环,并且在每次迭代时打印出一条消息,显示我的函数的进度。 I would like this prints to be displayed in my GUI (in this example in a textEdit widget), in real time .我希望此打印件实时显示在我的 GUI 中(在此示例中为 textEdit 小部件)。 How could I do that?我怎么能那样做?

I would like to make it clear that I need real time display.我想说清楚我需要实时显示。 I found some solutions online, but all prints only appear when the function finishes execution.我在网上找到了一些解决方案,但只有在函数完成执行时才会出现所有打印。 I need the prints to display in real time in order to appreciate the function progress.我需要实时显示打印件以了解功能进度。 Thanks in advance for your tips!预先感谢您的提示!

Here's a minimal reproductible example (I obtained the template via qt designer):这是一个最小的可复制示例(我通过 qt 设计器获得了模板):

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(163, 225)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
        self.formLayout.setObjectName("formLayout")
        self.my_button = QtWidgets.QPushButton(self.centralwidget)
        self.my_button.setObjectName("my_button")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.my_button)
        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setObjectName("textEdit")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.textEdit)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 163, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        self.my_button.clicked.connect(self.my_function)

    def my_function(self):
        for idx in range(10):
            print('Executing iteration '+str(idx)+' ...')
            time.sleep(1) # replaces time-consuming task
        print('Finished!')

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.my_button.setText(_translate("MainWindow", "Execute"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Golden rule of Qt: You should not execute tasks that consume a lot of time in the main thread of the GUI since they block the event loop of Qt. Qt 的黄金法则:你不应该在 GUI 的主线程中执行消耗大量时间的任务,因为它们会阻塞 Qt 的事件循环。

Considering the above, you must execute the time-consuming task in another thread and send the information to the GUI thread through signals.考虑到上述情况,您必须在另一个线程中执行耗时的任务,并通过信号将信息发送到 GUI 线程。

On the other hand you should not modify the class generated by Qt Designer, instead create another class that inherits from the appropriate widget and is filled in with the initial class.另一方面,您不应修改 Qt Designer 生成的类,而是创建另一个类,该类继承自适当的小部件并填充初始类。

import threading
import time

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(163, 225)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
        self.formLayout.setObjectName("formLayout")
        self.my_button = QtWidgets.QPushButton(self.centralwidget)
        self.my_button.setObjectName("my_button")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.my_button)
        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setObjectName("textEdit")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.textEdit)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 163, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.my_button.setText(_translate("MainWindow", "Execute"))


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    valueChanged = QtCore.pyqtSignal(int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.valueChanged.connect(self.on_value_changed)
        self.my_button.clicked.connect(self.on_clicked)

    @QtCore.pyqtSlot()
    def on_clicked(self):
        threading.Thread(target=self.my_function, daemon=True).start()

    @QtCore.pyqtSlot(int)
    def on_value_changed(self, value):
        self.textEdit.append("Value: {}".format(value))

    def my_function(self):
        for idx in range(10):
            self.valueChanged.emit(idx)
            print("Executing iteration " + str(idx) + " ...")
            time.sleep(1)  # replaces time-consuming task
        print("Finished!")


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM