pythonqt
pythonqt copied to clipboard
Queued slot calls
The merge request is a proposal for the implementation of queued slot calls.
In our application we run the Python interpreter in a worker thread and not in the UI main thread. That means, with the current implementation it is not possible/allowed to call slots of UI objects / widgets from our python scripts.
We changed the PythonQtSlotInfo::invokeQtMethod
to check if the given object thread is the current thread. If the threads are equal, then we use the default implementation. If the slots are differents (Python interpreter in worker thread and Widget in main UI thread), then we call the slot via a blocking queued connection - like it is done from Qt in the QMetaMehod::invoke
implementation.
In the proposal we provided two different implementations. The first one posts a QMetaCallEvent
like the QMetaMehod::invoke
implementation. The advantages are, that the slot is called in the given object thread and that it has less overhead then the second implementation. The disadvandtage is, that is uses private API - the current implementation may only compile with Qt 5.15.
The second implementation uses a PythonQtMetaCallDelegate
object that is created in the UI main thread. That means, the slot call is delegated to the UI main thread via invocation of a PythonQtMetaCallDelegate
slot. This implementation does not use private API but the slots are always called in the UI main thread and not in the thread of the given object.
I updated the merge request. I decided to use the PythonQtMetaCallDelegate
implementation to get rid of private API dependency. I also implemented proper exception handling for delegated meta calls.
With this implementation it is possible to run the python interpreter in a worker thread and call slots of widgets in the main UI thread.
No, this will not do. This code assumes that every Qt object lives in the main thread, which is an invalid assumption. One can start new Qt threads with their own event loops from Python (we do this in MeVisLab to handle asyncio tasks).
I also don't like that this will do cross-thread communication implicitly.
Personally I would prefer an approach where the QMetaObject wrapper gets a static invokeMethod method that takes the target object, the method name, connection style, and a list of arguments (almost like in C++, but more user-friendly). Of course this would mean figuring out which slot to use specifically (in case of overloads), but this is not much different from what is done already. Depending on whether Qt.QueuedConnection or Qt.BlockedQueuedConnection is used one would get a return value or not - if no return value is required this can be infinitely faster if the receiving thread is not idle in its event loop at the moment.
Thank you for your feedback. I'm closing this pull request.