Перенаправление stdout из вторичного потока (многопоточность с функцией вместо класса?) - PullRequest
0 голосов
/ 28 апреля 2018

Я пытаюсь отобразить мой stdout на QTextEdit, сделанном через Qt Designer (PyQt5). На самом деле, я заставил его работать, но он не показывает информацию в то же время, когда был сделан. Вместо этого он ожидает полного завершения процесса и только затем показывает всю информацию сразу. Я понимаю, что это должно быть решено с помощью потоков. Кроме того, поскольку QTextEdit (само по себе) является элементом графического интерфейса, мне нужен другой подход. Я нашел ответ, который искал здесь:

На этот вопрос ссылаются: Перенаправление stdout и stderr в PyQt4 QTextEdit из вторичного потока

@ three_pineapples предоставили ответ.

Мой вопрос почти такой же, поэтому ответ также правильный. Но мой сценарий немного отличается, и у меня возникают проблемы с его работой.

Во всех ответах на темы я вижу их только с помощью классов. Но дело в том, что в моем основном классе у меня есть функция, которая выполняет все, что будет напечатано на QTextEdit Иногда это занимает несколько минут. Я ищу способ работы приведенного ниже примера кода, используя ответ, предоставленный @ three_pineapples.

Вот пример кода:

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()

Есть ли способ заставить предоставленное решение работать напрямую с функцией (test_write) в моем основном классе? Как бы я это реализовал?

Чтобы было понятнее, из справочной ссылки класс "LongRunningThing" для меня недоступен. Функция, которая должна выполняться в отдельном потоке, находится в основном классе (названном Form () в примере кода). Возможно, можно использовать вложенный класс, который инкапсулирует функцию test_write внутри моего основного класса? Это вообще возможно?

1 Ответ

0 голосов
/ 28 апреля 2018

Для этого случая вы можете использовать нативный threading 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()
...