- https://doc .qt.io/qt-6/thread-basics.html
- https://wiki.qt.io/Threads_Events_QObjects
- https:/ /www.haccs.com/posts/how-to-use-qthread-correctly-p1/
- https://www.haccks.com/posts/how-to-use- qthread-correctly-p2/
- https://www.kdab.com/wp-content/uploads ... h-qt-1.pdf
Теперь я конвертирую этот пример PySide6, который использует бесконечный цикл в run() работника (и, следовательно, блокируя цикл событий потока), чтобы использовать QTimer, чтобы цикл событий больше не блокировался.
В этом примере счетчик считает от одного до пяти и выводит ход выполнения на стандартный вывод:
[img]https://i.sstatic. net/MjA4lNpB.png[/img]
У меня два вопроса:
- Когда в конструкторе создается экземпляр QTimer(), я получаю ошибку QObject::startTimer: Таймеры не могут быть запущены из другого потока (см. весь код в конце почта). Почему? Согласно документу moveToThread(), все дочерние элементы перемещаются в новый поток и, следовательно, также таймер из конструктора (и начать отсчет отправляет сигнал в слот run()) который должен находиться во вновь созданном потоке). Что здесь не так?
class WorkerQTimerBased(QObject):
finished = Signal()
signal_stop = Signal()
def __init__(self, parent: QObject = None):
super().__init__(parent)
self.counter = 1
# Question 1: Why is it not allowed to create a QTimer object in the constructor?
# This provokes a "QObject::startTimer: Timers cannot be started from another thread"!
self.timer = QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.timer_timeout)
self.signal_stop.connect(self.stop)
... - Слот stop_counting() вызывается при нажатии кнопки «Стоп». Слот должен отправить работнику self.worker.signal_stop.emit(). Но сигнал принимается только в том случае, если оператор print() раскомментирован. Почему?
@Slot()
def stop_counting(self):
if self.worker and self.worker_thread:
self.worker.signal_stop.emit()
# print("emitted()") # Question 2: Why is the signal not emitted/received? It is emitted/received when this line is uncommented.
self.worker_thread.quit()
self.worker_thread.wait()
- Нажатие «Начать отсчет» работает.
- Нажатие «Стоп» не работает.
import sys
from PySide6.QtCore import QObject, QThread, QTimer, Signal, Slot
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
class WorkerQTimerBased(QObject):
finished = Signal()
signal_stop = Signal()
def __init__(self, parent: QObject = None):
super().__init__(parent)
self.counter = 1
# Question 1: Why is it not allowed to create a QTimer object in the constructor?
# This provokes a "QObject::startTimer: Timers cannot be started from another thread"!
# self.timer = QTimer()
# self.timer.setInterval(1000)
# self.timer.timeout.connect(self.timer_timeout)
self.timer = None
self.signal_stop.connect(self.stop)
def run(self):
self.timer = QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.timer_timeout)
self.timer.start()
@Slot()
def timer_timeout(self) -> None:
print(self.counter)
self.counter += 1
if self.counter > 5:
self.stop()
@Slot()
def stop(self):
print("signal_stop received")
if self.timer:
self.timer.stop()
self.finished.emit()
class MainWindowQTimerBased(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Counting Application (QTimer-based)")
self.start_button = QPushButton("Start Counting")
self.start_button.clicked.connect(self.start_counting)
self.stop_button = QPushButton("Stop")
self.stop_button.clicked.connect(self.stop_counting)
layout = QVBoxLayout()
layout.addWidget(self.start_button)
layout.addWidget(self.stop_button)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.worker = None
self.worker_thread = None
@Slot()
def start_counting(self):
self.start_button.setEnabled(False)
self.worker_thread = QThread()
self.worker = WorkerQTimerBased()
self.worker.moveToThread(self.worker_thread)
self.worker.finished.connect(self.counting_finished)
self.worker.finished.connect(self.worker_thread.quit)
self.worker_thread.started.connect(self.worker.run)
# self.worker_thread.finished.connect(self.worker.deleteLater)
# self.worker_thread.finished.connect(self.worker_thread.deleteLater)
self.worker_thread.finished.connect(self.clean_up_threading_resources)
self.worker_thread.start()
@Slot()
def clean_up_threading_resources(self):
self.worker.deleteLater()
self.worker_thread.deleteLater()
self.worker = None
self.worker_thread = None
@Slot()
def stop_counting(self):
if self.worker and self.worker_thread:
self.worker.signal_stop.emit()
# print("emitted()") # Question 2: Why is the signal not emitted/received?
self.worker_thread.quit()
self.worker_thread.wait()
@Slot()
def counting_finished(self):
print("Counting finished")
self.start_button.setEnabled(True)
@Slot()
def closeEvent(self, event):
self.stop_counting()
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindowQTimerBased()
window.show()
sys.exit(app.exec())
Подробнее здесь: https://stackoverflow.com/questions/790 ... ker-thread