Сигнал QThread.finished не испускается после завершения рабочего процесса

У меня есть следующий код:

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. Но это не так. Почему?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
30
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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)

Другие вопросы по теме