[PySide6] Slot Executed in Signal's Thread Even With Auto & QueuedConnection
-
I'm having trouble with signal & slot in different threads. The slot is always executed in the signal caller's thread, even with explicit
QueuedConnection
. Below is the minimal example.import sys from PySide6.QtCore import QObject, Signal, QThread, Qt from PySide6.QtWidgets import QMainWindow, QApplication class Worker(QObject): update = Signal() def run(self): print(f"worker: {QThread.currentThread().objectName()}") self.update.emit() class Window(QMainWindow): def __init__(self, parent=None): super().__init__(parent) worker = Worker() thread = QThread() thread.setObjectName('test thread') worker.moveToThread(thread) thread.started.connect(worker.run) worker.update.connect(self.update) thread.start() def update(self): print(f"window: {QThread.currentThread().objectName()}") if __name__ == '__main__': app = QApplication() w = Window() w.show() sys.exit(app.exec())
The output of this minimal example is
worker: Qt mainThread window: test thread
Seems like the slot is always executed in signal caller's thread, because the thread of
thread.start
is main thread, and the thread ofworker.update
is test thread. The result is the same after I addQueuedConnection
explicitly.I've read the document of
QThread
several times, including this one, and it seems like I'm using threads in the correct way. Please correct me if I'm wrong.My PySide version is 6.8.1. Any help would be greatly appreciated.
-
-
The correct way to implement threads is to overwrite QThread.run() to do the work. We also recommend using the @Slot decorator. Consider:
import sys from PySide6.QtCore import QObject, Signal, QThread, Qt, Slot from PySide6.QtWidgets import QMainWindow, QApplication class Worker(QThread): update = Signal() def run(self): print(f"worker: {QThread.currentThread().objectName()}") self.update.emit() class Window(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self._worker = Worker() self._worker.setObjectName('test thread') self._worker.update.connect(self.update) self._worker.start() def closeEvent(self, e): self._worker.wait() e.accept() @Slot() def update(self): print(f"window: {QThread.currentThread().objectName()}") if __name__ == '__main__': app = QApplication() w = Window() w.show() sys.exit(app.exec())
-
Hi @friedemannkleint ,
Thanks for the reply. Sorry I forgot to add
Slot
decorator in the example. The result is still the same with the decorator.import sys from PySide6.QtCore import QObject, Signal, QThread, Qt, Slot from PySide6.QtWidgets import QMainWindow, QApplication class Worker(QObject): update = Signal() def run(self): print(f"worker: {QThread.currentThread().objectName()}") self.update.emit() class Window(QMainWindow): def __init__(self, parent=None): super().__init__(parent) worker = Worker() thread = QThread(self) thread.setObjectName('test thread') worker.moveToThread(thread) thread.started.connect(worker.run) worker.update.connect(self.update) thread.start() @Slot() def update(self): print(f"window: {QThread.currentThread().objectName()}") if __name__ == '__main__': app = QApplication() w = Window() w.show() sys.exit(app.exec())
Subclassing
QThread
did yield correct result.import sys from PySide6.QtCore import QObject, Signal, QThread, Qt, Slot from PySide6.QtWidgets import QMainWindow, QApplication class Thread(QThread): update = Signal() def run(self): print(f"thread: {QThread.currentThread().objectName()}") self.update.emit() class Window(QMainWindow): def __init__(self, parent=None): super().__init__(parent) thread = Thread(self) thread.setObjectName('test thread') thread.update.connect(self.update) thread.start() @Slot() def update(self): print(f"window: {QThread.currentThread().objectName()}") if __name__ == '__main__': app = QApplication() w = Window() w.show() sys.exit(app.exec())
However, I remember it's suggested to use a separate worker in some posts I read. Is it a PySide bug that the worker method doesn't work as expected? It's almost identical to the C++ example in the official document.
-
@friedemannkleint said in [PySide6] Slot Executed in Signal's Thread Even With Auto & QueuedConnection:
The correct way to implement threads is to overwrite QThread.run() to do the work.
Why is there only one correct approach?!
Shouldn't the worker thread approach work the same (as also when using C++)?Here in this topic the worker approach seems to function correctly (besides the mistakes OP made there).
-
It's reported to Qt Bug Tracker: https://bugreports.qt.io/browse/PYSIDE-2990
Update: tested cpp equivalent version and it worked fine. It should be a PySide issue.
-
Please don't use that technique of connecting a worker to the QThread.start() signal. It leaks a thread hanging in its exec() function since that is called by the default implementation of run(), plus the signal execution is blocked by the work.