Is there any easy way to call a function or method in the main thread from any other thread or QThread?
I heard that Slot
s and Signal
s can be used as a proxy between a thread and the main thread but it feels like too much work to create such a proxy for every single time I want to transfer data to my main thread.
(My answer describes a very universal way to acclompish this so I won't be providing a "minimal" example here, you can look at the answer.)
Qt has a function called invokeMethod
(https://doc.qt.io/qt-5/qmetaobject.html#invokeMethod) that can be used to invoke a method on the main thread by using the Qt.QueuedConnection
connection type.
In PySide however this won't work if you want to call a function with arguments!
Solution:
So to allow a similar and even easier functionality with PySide, I have written this class which can be used every time you want to run a function or method on the main thread:
from typing import Callable
from PySide2.QtCore import QObject, Signal, Slot
from PySide2.QtGui import QGuiApplication
class InvokeMethod(QObject):
def __init__(self, method: Callable):
"""
Invokes a method on the main thread. Taking care of garbage collection "bugs".
"""
super().__init__()
main_thread = QGuiApplication.instance().thread()
self.moveToThread(main_thread)
self.setParent(QGuiApplication.instance())
self.method = method
self.called.connect(self.execute)
self.called.emit()
called = Signal()
@Slot()
def execute(self):
self.method()
# trigger garbage collector
self.setParent(None)
This will internally create a Signal
and a Slot
without any parameters. The Slot
however will be called on the main thread as it has been connected using Qt.AutoConnection
(the default) and moved to the main thread with moveToThread(...)
.
To make sure no function arguments get lost due to the garbage collector, the parent of the class is temporarily set to the QGuiApplication
instance (you might need to change this if you don't rely on the QGuiApplication
. Any QObject
will be fine as the parent).
Here is an example on how to use this class:
InvokeMethod(lambda: print("hello"))