From e57e21a8a55d93f98bd65ca6ff3f541ad6f9a13d Mon Sep 17 00:00:00 2001 From: Adrian Herrmann Date: Wed, 31 Jan 2024 18:12:41 +0100 Subject: QObject: Add connect() overload with context arg On C++, there is an overload of QObject::connect() that allows passing a context object. This creates a connection in the context object's event loop instead of that of the caller. This implements said overload in Python. Change-Id: Ia6a098a04470741efd450818c31aa5bffb1bd9f8 Reviewed-by: Cristian Maureira-Fredes (cherry picked from commit acab25a3ccb836818e5089b23d40196bc7414b7a) Reviewed-by: Qt Cherry-pick Bot --- .../PySide6/QtCore/typesystem_core_common.xml | 4 ++ sources/pyside6/PySide6/glue/qtcore.cpp | 6 +++ sources/pyside6/libpyside/CMakeLists.txt | 4 ++ sources/pyside6/libpyside/pysideqslotobject_p.cpp | 36 ++++++++++++++++++ sources/pyside6/libpyside/pysideqslotobject_p.h | 39 ++++++++++++++++++++ sources/pyside6/libpyside/qobjectconnect.cpp | 43 ++++++++++++++++++++++ sources/pyside6/libpyside/qobjectconnect.h | 5 +++ 7 files changed, 137 insertions(+) create mode 100644 sources/pyside6/libpyside/pysideqslotobject_p.cpp create mode 100644 sources/pyside6/libpyside/pysideqslotobject_p.h diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml index bccd5f02d..5168373fd 100644 --- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml +++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml @@ -1748,6 +1748,10 @@ return-type="QMetaObject::Connection" static="yes"> + + + diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index a687d8447..ed1cb100f 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -321,6 +321,12 @@ PyModule_AddStringConstant(module, "__version__", qVersion()); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-connect-4 +// @snippet qobject-connect-4-context +// %FUNCTION_NAME() - disable generation of function call. +%RETURN_TYPE %0 = PySide::qobjectConnectCallback(%1, %2, %3, %PYARG_4, %5); +%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); +// @snippet qobject-connect-4-context + // @snippet qobject-connect-5 // %FUNCTION_NAME() - disable generation of function call. %RETURN_TYPE %0 = PySide::qobjectConnectCallback(%CPPSELF, %1, %PYARG_2, %3); diff --git a/sources/pyside6/libpyside/CMakeLists.txt b/sources/pyside6/libpyside/CMakeLists.txt index 01a0a66b0..e9fd0d56e 100644 --- a/sources/pyside6/libpyside/CMakeLists.txt +++ b/sources/pyside6/libpyside/CMakeLists.txt @@ -5,7 +5,10 @@ project(libpyside) set(libpyside_libraries Qt::Core Qt::CorePrivate) +set(CMAKE_AUTOMOC ON) + set(libpyside_HEADERS # installed below + pysideqslotobject_p.h class_property.h dynamicqmetaobject.h feature_select.h @@ -41,6 +44,7 @@ set(libpyside_HEADERS # installed below ) set(libpyside_SRC + pysideqslotobject_p.cpp class_property.cpp dynamicqmetaobject.cpp feature_select.cpp diff --git a/sources/pyside6/libpyside/pysideqslotobject_p.cpp b/sources/pyside6/libpyside/pysideqslotobject_p.cpp new file mode 100644 index 000000000..914be898a --- /dev/null +++ b/sources/pyside6/libpyside/pysideqslotobject_p.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "pysideqslotobject_p.h" + +#include +#include + +namespace PySide +{ + +void PySideQSlotObject::impl(int which, QSlotObjectBase *this_, QObject *receiver, + void **args, bool *ret) +{ + auto self = static_cast(this_); + switch (which) { + case Destroy: + delete self; + break; + case Call: + { + Shiboken::GilState state; + Shiboken::AutoDecRef arglist(PyTuple_New(0)); + Shiboken::AutoDecRef ret(PyObject_CallObject(self->callable, arglist)); + break; + } + case Compare: + case NumOperations: + Q_UNUSED(receiver); + Q_UNUSED(args); + Q_UNUSED(ret); + break; + } +} + +} // namespace PySide diff --git a/sources/pyside6/libpyside/pysideqslotobject_p.h b/sources/pyside6/libpyside/pysideqslotobject_p.h new file mode 100644 index 000000000..d7d258505 --- /dev/null +++ b/sources/pyside6/libpyside/pysideqslotobject_p.h @@ -0,0 +1,39 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef PYSIDEQSLOTOBJECT_P_H +#define PYSIDEQSLOTOBJECT_P_H + +#include "pysidemacros.h" +#include + +#include +#include + +namespace PySide +{ + +class PySideQSlotObject : public QtPrivate::QSlotObjectBase +{ + PyObject *callable; + + static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **args, bool *ret); + +public: + PySideQSlotObject(PyObject *callable) : QtPrivate::QSlotObjectBase(&impl), callable(callable) + { + Py_INCREF(callable); + } + + ~PySideQSlotObject() + { + auto gstate = PyGILState_Ensure(); + Py_DECREF(callable); + PyGILState_Release(gstate); + } +}; + + +} // namespace PySide + +#endif // PYSIDEQSLOTOBJECT_P_H diff --git a/sources/pyside6/libpyside/qobjectconnect.cpp b/sources/pyside6/libpyside/qobjectconnect.cpp index 15b71766c..c47c96eb8 100644 --- a/sources/pyside6/libpyside/qobjectconnect.cpp +++ b/sources/pyside6/libpyside/qobjectconnect.cpp @@ -3,6 +3,7 @@ #include "qobjectconnect.h" #include "pysideqobject.h" +#include "pysideqslotobject_p.h" #include "pysidesignal.h" #include "pysideutils.h" #include "signalmanager.h" @@ -15,6 +16,8 @@ #include #include +#include + #include static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self) @@ -259,6 +262,46 @@ QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *sign return connection; } +QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *signal, QObject *context, + PyObject *callback, Qt::ConnectionType type) +{ + if (!signal || !PySide::Signal::checkQtSignal(signal)) + return {}; + + const int signalIndex = + PySide::SignalManager::registerMetaMethodGetIndex(source, signal + 1, + QMetaMethod::Signal); + if (signalIndex == -1) + return {}; + + // Extract receiver from callback + const GetReceiverResult receiver = getReceiver(source, signal + 1, callback); + if (receiver.receiver == nullptr && receiver.self == nullptr) + return {}; + + PySide::SignalManager &signalManager = PySide::SignalManager::instance(); + + PySideQSlotObject *slotObject = new PySideQSlotObject(callback); + + QMetaObject::Connection connection{}; + Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify() + connection = QObjectPrivate::connect(source, signalIndex, context, slotObject, type); + Py_END_ALLOW_THREADS + if (!connection) { + if (receiver.usingGlobalReceiver) + signalManager.releaseGlobalReceiver(source, receiver.receiver); + return {}; + } + + Q_ASSERT(receiver.receiver); + if (receiver.usingGlobalReceiver) + signalManager.notifyGlobalReceiver(receiver.receiver); + + const QMetaMethod signalMethod = receiver.receiver->metaObject()->method(signalIndex); + static_cast(source)->connectNotify(signalMethod); + return connection; +} + bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObject *callback) { if (!PySide::Signal::checkQtSignal(signal)) diff --git a/sources/pyside6/libpyside/qobjectconnect.h b/sources/pyside6/libpyside/qobjectconnect.h index 70b862233..c99b8006e 100644 --- a/sources/pyside6/libpyside/qobjectconnect.h +++ b/sources/pyside6/libpyside/qobjectconnect.h @@ -33,6 +33,11 @@ PYSIDE_API QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *signal, PyObject *callback, Qt::ConnectionType type); +/// Helpers for QObject::connect(): Make a connection to a Python callback and a context object +PYSIDE_API QMetaObject::Connection + qobjectConnectCallback(QObject *source, const char *signal, QObject *context, + PyObject *callback, Qt::ConnectionType type); + /// Helpers for QObject::disconnect(): Disconnect a Python callback PYSIDE_API bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObject *callback); -- cgit v1.2.3