У меня есть следующий код:
import time
from PyQt5.QtCore import QThread, QObject
from PyQt5.QtWidgets import QWidget, QApplication
class Worker(QObject):
def run(self):
time.sleep(1)
print("Worker is finished")
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.show()
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.thread.finished.connect(self.on_thread_finished)
self.thread.start()
def on_thread_finished(self):
print("Thread finished")
self.thread.deleteLater()
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
app.exec_()
Запускаю, показывает окно, печатает Worker is finished, больше ничего. Это странно. ИМХО, когда worker завершится, поток тоже должен быть закончен, а значит, должен быть вызван метод on_thread_finished и напечатано Thread finished. Но это не так. Почему?






when the worker is finished, the thread should be finished too
Это не так работает. Ваш метод Worker::run вызывается как slot с помощью обычного механизма signal/slot, после чего QThread продолжит обработку событий в обычном режиме.
Если вы хотите завершить QThread, когда Worker::run завершится, вам нужно указать это сделать явно...
import time
from PyQt5.QtCore import Qt, QThread, QObject
from PyQt5.QtWidgets import QWidget, QApplication
class Worker(QObject):
# Constructor accepts the QThread as a parameter and stashes it
# for later use.
def __init__(self, thread):
super(Worker, self).__init__()
self.m_thread = thread
def run(self):
time.sleep(1)
print("Worker is finished")
# We're done so ask the `QThread` to terminate.
if self.m_thread:
self.m_thread.quit()
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.show()
self.thread = QThread()
# Pass the QThread to the Worker's ctor.
self.worker = Worker(self.thread)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.thread.finished.connect(self.on_thread_finished)
self.thread.start()
def on_thread_finished(self):
print("Thread finished")
self.thread.deleteLater()
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
app.exec_()
Очень «неэлегантно», но передает идею.
Более простой альтернативой, которая приходит на ум, было бы просто использовать QThread::currentThread(), и в этом случае ваш метод Worker::run становится...
class Worker(QObject):
def run(self):
time.sleep(1)
print("Worker is finished")
# We're done so ask the `QThread` to terminate.
QThread.currentThread().quit()
Когда вы используете moveToThread вместо повторной реализации QThread.run, поток запустит свой собственный цикл обработки событий, который будет ждать, пока exit/quit не будет явно вызван. Сигнал finished испускается только после остановки цикла обработки событий (и/или после возврата run). Обычный способ справиться с этим сценарием — выдать пользовательский сигнал от рабочего процесса и подключить его к слоту quit потока. (Примечание: соединения сигнал-слот с перекрестной резьбой гарантированно являются потокобезопасными).
Таким образом, ваш пример будет работать, как и ожидалось, со следующими изменениями:
class Worker(QObject):
finished = QtCore.pyqtSignal()
def run(self):
time.sleep(1)
print("Worker is finished")
self.finished.emit()
class MainWindow(QWidget):
def __init__(self):
...
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)