QTimer не работает в дополнительном потоке QThread

Рейтинг: 1Ответов: 1Опубликовано: 28.04.2023

Я создаю отдельный дополнительный поток и пытаюсь в нем запустить QTimer, чтобы раз в секунду он вызывал функцию, которая должна запускаться в этом же потоке.

Но этого не происходит, таймер просто не работает.
В чем может быть ошибка?

import sys
from time import sleep
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QMainWindow
from PyQt5.QtCore import QThread, QObject, QTimer

class Worker(QObject):
    def __init__(self):
        super().__init__()

    def print_2(self):
        print(2)

    def run(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.print_2)
        self.timer.start(1000)
        while True:
            print(1)
            sleep(1)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.central_widget = QWidget(self)
        self.setCentralWidget(self.central_widget)
        self.layout_window = QVBoxLayout()
        self.central_widget.setLayout(self.layout_window)
        
        self.btn = QPushButton('Запустить поток')
        self.btn.clicked.connect(self.start_thread)
        self.layout_window.addWidget(self.btn)

    def start_thread(self):
        self.main_thread = QThread(parent=self)
        self.worker = Worker()
        self.worker.moveToThread(self.main_thread)
        self.main_thread.started.connect(self.worker.run)
        self.main_thread.start()


app = QApplication(sys.argv)
app.setStyle('Fusion')
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())

Ответы

▲ 2Принят

Я не понимаю зачем вам в отдельном потоке запускать QTimer?
Я не знаю, что это за задача, которую вы хотите периодически запускать раз в секунду?

QTimer — это класс, который наследуется от QObject, а QObject принадлежит тому же, что и родитель, и если у него нет родителя, он принадлежит потоку, в котором он был создан.

С другой стороны, QThread — это поток Qt, но это не так,
QThread — это класс, который позволяет обрабатывать жизненный цикл собственного потока, и это четко указано в документации: QThread class предоставляет независимый от платформы способ управления потоками.

Также обратите внимание, что новый поток, который обрабатывает QThread,
имеет только область действия метода run(), если метод находится в другом месте, относится к полю, где был создан QThread.

QTimer - работает только в потоке, в котором он был создан.
Попробуйте проанализировать свой код (threading.get_ident()) какой метод в каком потоке выполняется.
Вы увидите, что метод run() и метод print_2() выполняются в разных потоках.
Вот почему таймер просто не работает.

Учитывая, что я не совсем понимаю, то о чем писал выше и только для демонстрации области действия метода run():

import sys
#from time import sleep
import threading

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, \
    QPushButton, QMainWindow
from PyQt5.QtCore import QThread, QObject, QTimer


class Worker(QObject):
    def __init__(self):
        super().__init__()

        self.timer = QTimer()
        self.timer.timeout.connect(self.print_2)
        self.timer.setInterval(300)                       # 1000
        self.timer.start()
        print(f"timer from : {threading.get_ident()}")
        self.flag = True

    def print_2(self):
#        print(f'print_2(self): 2')
        print(f"print_2(self)        :  {threading.get_ident()}")

    def run(self):
        QThread.msleep(1000)
        while self.flag:
            print(f"run(self): while True:  {threading.get_ident()}\n")
            QThread.msleep(1000)
            

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.central_widget = QWidget(self)
        self.setCentralWidget(self.central_widget)
        
        self.main_thread = None
        
        self.btn = QPushButton('Запустить поток')
        self.btn.clicked.connect(self.start_thread)
        
        self.layout_window = QVBoxLayout(self.central_widget)
        self.layout_window.addWidget(self.btn)

    def start_thread(self):
        self.btn.setEnabled(False)
        self.main_thread = QThread()                    #-parent=self
        self.worker = Worker()
        print(f"self.worker: {threading.get_ident()}\n")
        
        self.worker.moveToThread(self.main_thread)
        self.main_thread.started.connect(self.worker.run)
        self.main_thread.start()
        
    def closeEvent(self, event):
        if self.main_thread:        
            self.worker.flag = False
            self.worker.timer.stop()
            self.main_thread.terminate()
            self.main_thread.wait(1)
        super().closeEvent(event)
            

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle('Fusion')
    w = MainWindow()
    w.resize(300, 200)
    w.show()
    sys.exit(app.exec_())

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