Корректно ли отправлять класс наследуемый от QThread в свой поток?

Я наследую класс Worker от PyQt5.QtCore.QThread. В нём я определяю функцию, которую хочу исполнить в отдельном потоке, а конкретно именно в том же экземпляре Worker, что и сама функция. Что бы исполнить, нужно перенести на нужный поток QObject с помощью moveToThread. Так как функция в классе, наследуемом от QThread, то я получается переношу поток сам в себя.

from PyQt5.QtCore import QThread

class Worker(QThread):
    def __init__(self, func, signals):
        super().__init__()
        self.func = func
        self.signals = signals
        self.moveToThread(self)
        self.args = []
        self.kwargs = {}

    def execute_function(self):
        self.func(*self.args, **self.kwargs)
        self.signals.finished.emit()

    def __call__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        self.start()

С ошибкой это не падает, функция выполняется в отдельном потоке. Но как-то очень сомнительно делать self.moveToThread(self). Можно ли так делать?


Ответы (1 шт):

Автор решения: S. Nick

Реализация QThread-moveToThread выглядит так:

from PyQt5 import QtCore, QtWidgets


class Worker(QtCore.QObject):
    started  = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()
    data     = QtCore.pyqtSignal(str)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.running = False

    @QtCore.pyqtSlot()
    def read_data_from_sensor(self):
        self.started.emit()
        QtCore.QThread.msleep(1000)                   # Моделируем процесс блокировки
        while self.running:
            dt  = QtCore.QDateTime.currentDateTime().toString('hh:mm:ss') 
            self.data.emit(dt)
            QtCore.QThread.msleep(1000)               # Моделируем процесс блокировки
        self.finished.emit()


class App(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Реализация QThread-moveToThread")
        self.resize(400, 300)
        centralWidget = QtWidgets.QWidget(self)
        self.setCentralWidget(centralWidget)

        self.boton = QtWidgets.QPushButton('Начать', self)
        self.boton.clicked.connect(self.read_data)

        self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
        self.label_data.setText('В ожидании')
        self.label_data.adjustSize()

        layout = QtWidgets.QGridLayout(centralWidget)        
        layout.addWidget(self.label_data)
        layout.addWidget(self.boton)

        self._worker = Worker()                                          # Worker
        self._worker.started.connect(self.on_started)
        self._worker.finished.connect(self.on_finished)
        self._worker.data.connect(self.update_label)

        self._thread = QtCore.QThread(self)                              # QThread
        self._thread.start()
        self._worker.moveToThread(self._thread)

    @QtCore.pyqtSlot()
    def on_started(self):
        """ slot будет вызван, когда начнется сбор данных """
        self.label_data.setText("Начиная читать")
        self.boton.setText("Стоп")
        self.boton.setEnabled(True)

    @QtCore.pyqtSlot()
    def on_finished(self):
        """ slot будет вызван, когда сбор данных закончится """
        self.label_data.setText("В ожидании")
        self.boton.setText("Начать")
        self.boton.setEnabled(True)

    @QtCore.pyqtSlot()
    def read_data(self):
        """ Начать / остановить чтение при нажатии кнопки """
        if self._worker.running:
            self._worker.running = False
        else:
            self._worker.running = True
            QtCore.QTimer.singleShot(0, self._worker.read_data_from_sensor)
        self.boton.setEnabled(False)

    @QtCore.pyqtSlot(str)
    def update_label(self, data):
        """ Slot будет вызываться при появлении новых данных в метке """
        self.label_data.setText(data)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    ventana = App()
    ventana.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка