[Python] PySide6 QThread with Worker never emits finish signal

Discussão em 'Python' iniciado por Stack, Setembro 2, 2025.

  1. Stack

    Stack Membro Participativo

    Before asking the question, I am aware of people having similar problems and reviewed similar questions, but I did not find the answer.

    I have tried to follow the example from here to do proper threading on PyQt. I did an example from here, it works well. However, I want a slight modification. I wrote my own class that need to run on a thread without me having to do all of the boilerplate code, like this:

    def task_to_run(self)
    ... # a long task that I want to run on a thread

    worker = JZWorker(self.task_to_run, None, "Name")
    worker.start()


    and this is the class that I have built

    from PySide6.QtCore import QObject, QThread, Signal
    from loguru import logger

    class Worker(QObject):
    finished = Signal()

    def __init__(self, task, finished_callback, name = "") -> None:
    super().__init__()
    self._task = task
    self._finished = False
    self._name = name or f"JZWorker-{self._task.__name__}"

    if finished_callback:
    self.finished.connect(fin_cb)
    self.finished.connect(self._on_finished)

    def run(self) -> None:
    logger.debug(f"Running separate task in a JZWorker thread: {self._name}")

    try:
    self._task()
    except Exception as ex:
    logger.exception(f"Error in worker thread {self._name}: {ex}")
    self.finished.emit()

    def _on_finished(self) -> None:
    self._finished = True

    def is_finished(self) -> bool:
    return self._finished


    class JZWorker(QObject):
    """
    JZWorker is a class that can be used to run a non-blocking task in a separate thread without having to do all of the boilerplate code
    """
    finished = Signal()

    def __init__(
    self, task, fin_cb = None, name = "") -> None:
    super().__init__()
    self._thread = QThread()
    self._worker = Worker(task, fin_cb, name)
    self._worker.moveToThread(self._thread)
    self._thread.started.connect(self._worker.run)
    self._worker.finished.connect(self._thread.quit)
    self._worker.finished.connect(self._worker.deleteLater)
    self._worker.finished.connect(self.finished)
    self._thread.finished.connect(self._thread.deleteLater)
    # self._worker.finished.connect(
    # lambda: logger.warning(f"Worker thread {self._worker._name} finished"))
    # self._thread.finished.connect(
    # lambda: logger.error(f"Thread for worker {self._worker._name} finished"))

    def start(self) -> None:
    """
    Starts the thread and runs the task in a separate thread. This is a non-blocking call.
    """
    self._thread.start()


    The issue is, that even if the thread is 100% finished, the finished signal of self._thread from JZWorker is never emitted. However, uncommenting the line below, does stop my app from crashing when set worker = None, but the _thread.finished signal is still never emitted. I assume it has to do something with reference to worker still existing so that the garbage collector doesnt collect it, but I am not sure.

    self._thread.finished.connect(lambda: logger.error(f"Thread for worker {self._worker._name} finished"))


    I am analzying an example in the page that was provided and there is no answer that I can find.

    Continue reading...

Compartilhe esta Página