Как использовать QTimer в отдельном QThread - PullRequest
1 голос
/ 12 апреля 2019

У меня есть сложная вычислительная задача, которую я хочу запускать в цикле каждые 5 секунд, не блокируя основной цикл обработки событий. Для этого я намерен использовать QTimer и отдельный поток для его запуска. Я пробовал следующий код, но он пока не работает:

@pyqtSlot()
def heavy_task_function():
    # Sleep for 10 seconds to simulate heavy computation
    time.sleep(10)
    print "First Timer Fired"

if __name__ == "__main__":
    app = QCoreApplication.instance()
    if app is None:
        app = QApplication(sys.argv)

    threaded_timer = ModbusComThread(heavy_task_function)
    threaded_timer.start()

    sys.exit(app.exec_())

Где:

class ModbusComThread(QThread):

    def __init__(self, slot_function):
        QThread.__init__(self)
        self.slot_function = slot_function
        self.send_data_timer = None

    def run(self):
        print "Timer started on different thread"
        self.send_data_timer = QTimer(self)
        self.send_data_timer.timeout.connect(self.slot_function)
        self.send_data_timer.start(5000)

    def stop(self):
        self.send_data_timer.stop()

slot_function никогда не запускается QTimer в threaded_timer. Правильна ли моя многопотоковая архитектура?

1 Ответ

2 голосов
/ 12 апреля 2019

A QTimer нужен работающий цикл обработки событий. По умолчанию QThread.run() запускает локальный цикл обработки событий для потока, но если вы полностью переопределите его так, как вы это сделали, этого не произойдет, поэтому события таймера никогда не будут обрабатываться.

В общем, когда вам нужен локальный цикл обработки событий, вы должны создать рабочий объект для выполнения всей обработки, а затем использовать moveToThread , чтобы поместить его в отдельный поток. Если нет, то вполне нормально переопределить QThread.run().

Демонстрация ниже показывает, как это сделать. Обратите внимание, что очень важно создать таймер после запуска потока , иначе он будет создан в неправильном потоке, а его события таймера не будут обработаны циклом обработки потока. Также важно, чтобы вся связь между рабочим потоком и основным потоком осуществлялась с помощью сигналов, чтобы обеспечить безопасность потока. Никогда не пытайтесь напрямую выполнять операции с графическим интерфейсом вне основного потока, так как Qt вообще не поддерживает это. В целях демонстрации второй таймер в основном потоке используется для остановки всей обработки после фиксированного интервала. Если бы был GUI, пользовательское вмешательство через кнопку могло бы достичь того же.

Демо

import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class ModbusComWorker(QObject):
    finished = pyqtSignal()

    def start(self):
        self._timer = QTimer(self)
        self._timer.timeout.connect(self.process)
        self._timer.start(2000)

    def stop(self):
        self._timer.stop()
        self.finished.emit()

    def process(self):
        print('processing (thread: %r)' % QThread.currentThread())
        QThread.sleep(3)

if __name__ == "__main__":

    app = QCoreApplication.instance()
    if app is None:
        app = QApplication(sys.argv)

    thread = QThread()
    worker = ModbusComWorker()
    worker.moveToThread(thread)

    def finish():
        print('shutting down...')
        thread.quit()
        thread.wait()
        app.quit()
        print('stopped')

    worker.finished.connect(finish)
    thread.started.connect(worker.start)
    thread.start()

    timer = QTimer()
    timer.setSingleShot(True)
    timer.timeout.connect(worker.stop)
    timer.start(15000)

    print('starting (thread: %r)' % QThread.currentThread())

    sys.exit(app.exec_())

выход

starting (thread: <PyQt5.QtCore.QThread object at 0x7f980d096b98>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
shutting down...
stopped
...