Изящно завершить QThread в PySide2

Итак, я пишу приложение с использованием PySide2, которое должно перенаправлять все с stdout на промежуточное queue.Queue. Этот Queue испускает сигнал, который будет обработан QThread, добавляя все строки в очереди к QTextEdit.

Я немного огляделся, но, к сожалению, у меня ничего не работает.

Именно этот код я имею в виду.

from PySide2 import QtWidgets, QtGui, QtCore

import sys
from queue import Queue


class WriteStream(object):
    """ Redirects sys.stdout to a thread-safe Queue

    Arguments:
        object {object} -- base class
    """
    def __init__(self, queue):
        self.queue = queue

    def write(self, msg):
        self.queue.put(msg)

    def flush(self):
        """ Passing to create non-blocking stream (?!)
            https://docs.python.org/3/library/io.html#io.IOBase.flush
        """
        pass


class WriteStreamThread(QtCore.QThread):
    queue_updated = QtCore.Signal(str)

    def __init__(self, queue):
        super(WriteStreamThread, self).__init__()
        self.stop = False
        self.queue = queue

    def set_stop(self):
        self.stop = True
        self.wait() # waits till finished signal has been emitted

    def run(self):
        while not self.stop:    # i guess this is blocking
            msg = self.queue.get()
            self.queue_updated.emit(msg)
            self.sleep(1)   # if commented out, app crashes
        self.finished.emit()


class Verifyr(QtWidgets.QMainWindow):
    def __init__(self, queue, parent=None):
        super(Verifyr, self).__init__(parent)
        self.centralwidget = QtWidgets.QWidget(self)
        layout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.textedit = QtWidgets.QTextEdit(self)
        layout.addWidget(self.textedit)
        self.setLayout(layout)
        self.setCentralWidget(self.centralwidget)
        print("Verifyr initialised...")

        self.listener_thread = WriteStreamThread(queue)
        self.listener_thread.queue_updated.connect(self._log_to_qtextedit)
        self.listener_thread.start()

    @QtCore.Slot(str)
    def _log_to_qtextedit(self, msg):
        self.textedit.insertPlainText(msg)


if __name__ == '__main__':
    # create Queue to be passed to WriteStream and WriteStreamListener
    queue = Queue()
    # redirect stdout to WriteStream()
    sys.stdout = WriteStream(queue)
    print("Redirected sys.stdout to WriteStream")

    # launching the app
    app = QtWidgets.QApplication(sys.argv)
    window = Verifyr(queue)
    app.aboutToQuit.connect(window.listener_thread.set_stop)
    window.show()
    sys.exit(app.exec_())

В WriteStreamThread я использую цикл while, который продолжает испускать сигналы, перехваченные основным приложением, для добавления к его QTextEdit. Когда я закомментирую self.sleep(1), приложение застрянет в бесконечном цикле. Если нет, поток выйдет просто отлично.

Может кто-нибудь, пожалуйста, объясните мне это?

ОБНОВЛЕНИЕ: как я уже упоминал в своем комментарии, я нашел ошибку... вот обновленный метод run() для WriteStreamThread:

def run(self):
    while not self.stop:
        try:
            msg = self.queue.get(block=False) # nasty little kwarg
        except:
            msg = "No items in queue. Sleeping 1sec.."
            self.sleep(1)
        self.queue_updated.emit(msg)
    self.finished.emit()    # optional

person tweak-wtf    schedule 09.07.2019    source источник


Ответы (1)


Я нашел ошибку. Это был queue.get() в методе run() WriteStreamThread, который блокировал. Изменение его на queue.get(block=False) и окружение try/catch делает свою работу. Глупый я...

person tweak-wtf    schedule 09.07.2019