Redirecting stdout from a secondary thread (multithreading with a function instead of class?)

允我心安 提交于 2019-12-04 05:48:41

问题


I am trying to get my stdout displayed on a QTextEdit made via Qt Designer (PyQt5). Actually I made it work yet it doesn't show the info at the same time it was made. Instead it waits for the process to completely end and only then it shows all the information at once. I understand that this should be solved via threading. Also since the QTextEdit (itself) is a GUI element i need a different approach. I found the answer I was looking for here:

This question is referenced to: Redirecting stdout and stderr to a PyQt4 QTextEdit from a secondary thread

@three_pineapples provided the answer.

My question is pretty much exactly the same, thus the answer is also correct. But my scenario is a little different and I'm having trouble making it work.

In all the threading answers I only see them using Classes. But the thing is in my main class I have a function that does all the stuff that would be printed on the QTextEdit. Sometimes it takes minutes to complete. I am looking for a way for the example code below to work using the answer provided by @three_pineapples.

Here is the example code:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtGui import QTextCursor

from ui_form import Ui_Form


class EmittingStream(QObject):  # test
    textWritten = pyqtSignal(str)

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


class Form(QMainWindow):

    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

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

        self.ui = Ui_Form()
        self.ui.setupUi(self)

        self.ui.pushButton_text.clicked.connect(self.test_write)

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

    def normalOutputWritten(self, text):  # test
        """Append text to the QTextEdit."""
        cursor = self.ui.textEdit.textCursor()
        cursor.movePosition(QTextCursor.End)
        cursor.insertText(text)
        self.ui.textEdit.setTextCursor(cursor)
        self.ui.textEdit.ensureCursorVisible()

    def test_write(self):  # this is a long, complicated function. its nested in this class. I don't have a way to get it out as a different class.
        print("something written")


def main():
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Is there a way to get the provided solution to work -directly- on the (test_write) function in my main class? How would I implement it?

To make it more clear, from the reference link "LongRunningThing" class is not available for me. The function that needs to run on a separate thread is within the main class (named Form() in the example code). Perhaps a nested class could be used that encapsulates the test_write function inside my main class? Is that even possible?


回答1:


For this case you can use the native threading of python:

import sys

import threading
import time

from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtGui import QTextCursor

from ui_form import Ui_Form


class EmittingStream(QObject):  # test
    textWritten = pyqtSignal(str)

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


class Form(QMainWindow):
    finished = pyqtSignal()
    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

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

        self.ui = Ui_Form()
        self.ui.setupUi(self)

        self.ui.pushButton_text.clicked.connect(self.start_task)
        self.finished.connect(lambda: self.ui.pushButton_text.setEnabled(True))

    def start_task(self):
        var = self.ui.lineEdit.text()
        self.thread = threading.Thread(target=self.test_write, args=(args, ))
        self.thread.start()
        self.ui.pushButton_text.setEnabled(False)

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

    def normalOutputWritten(self, text):  # test
        """Append text to the QTextEdit."""
        cursor = self.ui.textEdit.textCursor()
        cursor.movePosition(QTextCursor.End)
        cursor.insertText(text)
        self.ui.textEdit.setTextCursor(cursor)
        self.ui.textEdit.ensureCursorVisible()

    def test_write(self, *args):
        var1 = args[0]
        print("something written")
        time.sleep(5) # simulate expensive task
        print("something written ----")
        self.finished.emit()


def main():
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()


来源:https://stackoverflow.com/questions/50077356/redirecting-stdout-from-a-secondary-thread-multithreading-with-a-function-inste

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!