From 043eff9b702459485e6abd369fb68047024a7f4a Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 22 Nov 2021 10:01:47 +0100 Subject: PySide6: Move QObject signal connection code into libpyside The rather large functions should not be in code snippets. Move them into libpyside and rearrange and streamline them a bit, fixing some potential null pointer crashes as pointed out by clang checking. Change-Id: I5be6468577f2a107a286775c7e8b5874531c30fe Reviewed-by: Christian Tismer Reviewed-by: Qt CI Bot --- sources/pyside6/PySide6/glue/qtcore.cpp | 241 +---------------------- sources/pyside6/libpyside/CMakeLists.txt | 2 + sources/pyside6/libpyside/qobjectconnect.cpp | 284 +++++++++++++++++++++++++++ sources/pyside6/libpyside/qobjectconnect.h | 78 ++++++++ 4 files changed, 373 insertions(+), 232 deletions(-) create mode 100644 sources/pyside6/libpyside/qobjectconnect.cpp create mode 100644 sources/pyside6/libpyside/qobjectconnect.h diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index 7229fd378..14025e72a 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -323,265 +323,42 @@ PyModule_AddStringConstant(module, "__version__", qVersion()); // @snippet qt-version // @snippet qobject-connect -static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self) -{ - Shiboken::AutoDecRef methodName(PyObject_GetAttr(method, Shiboken::PyMagicName::name())); - if (!PyObject_HasAttr(self, methodName)) - return true; - Shiboken::AutoDecRef otherMethod(PyObject_GetAttr(self, methodName)); - - PyObject *function1, *function2; - - // PYSIDE-1523: Each could be a compiled method or a normal method here, for the - // compiled ones we can use the attributes. - if (PyMethod_Check(otherMethod.object())) { - function1 = PyMethod_GET_FUNCTION(otherMethod.object()); - } else { - function1 = PyObject_GetAttr(otherMethod.object(), Shiboken::PyName::im_func()); - Py_DECREF(function1); - // Not retaining a reference inline with what PyMethod_GET_FUNCTION does. - } - - if (is_pymethod) { - function2 = PyMethod_GET_FUNCTION(method); - } else { - function2 = PyObject_GetAttr(method, Shiboken::PyName::im_func()); - Py_DECREF(function2); - // Not retaining a reference inline with what PyMethod_GET_FUNCTION does. - } - - return function1 != function2; -} - -static bool getReceiver(QObject *source, - const char *signal, - PyObject *callback, - QObject **receiver, - PyObject **self, - QByteArray *callbackSig) -{ - bool forceGlobalReceiver = false; - if (PyMethod_Check(callback)) { - *self = PyMethod_GET_SELF(callback); - if (%CHECKTYPE[QObject *](*self)) - *receiver = %CONVERTTOCPP[QObject *](*self); - forceGlobalReceiver = isMethodDecorator(callback, true, *self); - } else if (PyCFunction_Check(callback)) { - *self = PyCFunction_GET_SELF(callback); - if (*self && %CHECKTYPE[QObject *](*self)) - *receiver = %CONVERTTOCPP[QObject *](*self); - } else if (PyObject_HasAttr(callback, Shiboken::PyName::im_func()) - && PyObject_HasAttr(callback, Shiboken::PyName::im_self())) { - *self = PyObject_GetAttr(callback, Shiboken::PyName::im_self()); - Py_DECREF(*self); - - if (%CHECKTYPE[QObject *](*self)) - *receiver = %CONVERTTOCPP[QObject *](*self); - forceGlobalReceiver = isMethodDecorator(callback, false, *self); - } else if (PyCallable_Check(callback)) { - // Ok, just a callable object - *receiver = nullptr; - *self = nullptr; - } - - bool usingGlobalReceiver = !*receiver || forceGlobalReceiver; - - // Check if this callback is a overwrite of a non-virtual Qt slot. - if (!usingGlobalReceiver && receiver && self) { - *callbackSig = PySide::Signal::getCallbackSignature(signal, *receiver, callback, usingGlobalReceiver).toLatin1(); - const QMetaObject *metaObject = (*receiver)->metaObject(); - int slotIndex = metaObject->indexOfSlot(callbackSig->constData()); - if (slotIndex != -1 && slotIndex < metaObject->methodOffset() && PyMethod_Check(callback)) - usingGlobalReceiver = true; - } - - const auto receiverThread = *receiver ? (*receiver)->thread() : nullptr; - - if (usingGlobalReceiver) { - PySide::SignalManager &signalManager = PySide::SignalManager::instance(); - *receiver = signalManager.globalReceiver(source, callback); - // PYSIDE-1354: Move the global receiver to the original receivers's thread - // so that autoconnections work correctly. - if (receiverThread && receiverThread != (*receiver)->thread()) - (*receiver)->moveToThread(receiverThread); - *callbackSig = PySide::Signal::getCallbackSignature(signal, *receiver, callback, usingGlobalReceiver).toLatin1(); - } - - return usingGlobalReceiver; -} - -static QMetaObject::Connection qobjectConnect(QObject *source, const char *signal, - QObject *receiver, const char *slot, - Qt::ConnectionType type) -{ - if (!signal || !slot) - return {}; - - if (!PySide::Signal::checkQtSignal(signal)) - return {}; - signal++; - - if (!PySide::SignalManager::registerMetaMethod(source, signal, QMetaMethod::Signal)) - return {}; - - bool isSignal = PySide::Signal::isQtSignal(slot); - slot++; - PySide::SignalManager::registerMetaMethod(receiver, slot, isSignal ? QMetaMethod::Signal : QMetaMethod::Slot); - return QObject::connect(source, signal - 1, receiver, slot - 1, type); -} - -static QMetaObject::Connection qobjectConnect(QObject *source, QMetaMethod signal, - QObject *receiver, QMetaMethod slot, - Qt::ConnectionType type) -{ - return qobjectConnect(source, signal.methodSignature(), receiver, slot.methodSignature(), type); -} - -static QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *signal, - PyObject *callback, Qt::ConnectionType type) -{ - if (!signal || !PySide::Signal::checkQtSignal(signal)) - return {}; - signal++; - - int signalIndex = PySide::SignalManager::registerMetaMethodGetIndex(source, signal, QMetaMethod::Signal); - if (signalIndex == -1) - return {}; - - PySide::SignalManager &signalManager = PySide::SignalManager::instance(); - - // Extract receiver from callback - QObject *receiver = nullptr; - PyObject *self = nullptr; - QByteArray callbackSig; - bool usingGlobalReceiver = getReceiver(source, signal, callback, &receiver, &self, &callbackSig); - if (receiver == nullptr && self == nullptr) - return {}; - - const QMetaObject *metaObject = receiver->metaObject(); - const char *slot = callbackSig.constData(); - int slotIndex = metaObject->indexOfSlot(slot); - QMetaMethod signalMethod = metaObject->method(signalIndex); - - if (slotIndex == -1) { - if (!usingGlobalReceiver && self && !Shiboken::Object::hasCppWrapper(reinterpret_cast(self))) { - qWarning("You can't add dynamic slots on an object originated from C++."); - if (usingGlobalReceiver) - signalManager.releaseGlobalReceiver(source, receiver); - - return {}; - } - - if (usingGlobalReceiver) - slotIndex = signalManager.globalReceiverSlotIndex(receiver, slot); - else - slotIndex = PySide::SignalManager::registerMetaMethodGetIndex(receiver, slot, QMetaMethod::Slot); - - if (slotIndex == -1) { - if (usingGlobalReceiver) - signalManager.releaseGlobalReceiver(source, receiver); - - return {}; - } - } - auto connection = QMetaObject::connect(source, signalIndex, receiver, slotIndex, type); - if (connection) { - if (usingGlobalReceiver) - signalManager.notifyGlobalReceiver(receiver); - #ifndef AVOID_PROTECTED_HACK - source->connectNotify(signalMethod); //Qt5: QMetaMethod instead of char * - #else - // Need to cast to QObjectWrapper * and call the public version of - // connectNotify when avoiding the protected hack. - reinterpret_cast(source)->connectNotify(signalMethod); //Qt5: QMetaMethod instead of char * - #endif - - return connection; - } - - if (usingGlobalReceiver) - signalManager.releaseGlobalReceiver(source, receiver); - - return {}; -} - - -static bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObject *callback) -{ - if (!PySide::Signal::checkQtSignal(signal)) - return false; - - PySide::SignalManager &signalManager = PySide::SignalManager::instance(); - - // Extract receiver from callback - QObject *receiver = nullptr; - PyObject *self = nullptr; - QByteArray callbackSig; - QMetaMethod slotMethod; - bool usingGlobalReceiver = getReceiver(nullptr, signal, callback, &receiver, &self, &callbackSig); - if (receiver == nullptr && self == nullptr) - return false; - - const QMetaObject *metaObject = receiver->metaObject(); - int signalIndex = source->metaObject()->indexOfSignal(++signal); - int slotIndex = -1; - - slotIndex = metaObject->indexOfSlot(callbackSig); - slotMethod = metaObject->method(slotIndex); - - bool disconnected; - disconnected = QMetaObject::disconnectOne(source, signalIndex, receiver, slotIndex); - - if (disconnected) { - if (usingGlobalReceiver) - signalManager.releaseGlobalReceiver(source, receiver); - - #ifndef AVOID_PROTECTED_HACK - source->disconnectNotify(slotMethod); //Qt5: QMetaMethod instead of char * - #else - // Need to cast to QObjectWrapper * and call the public version of - // connectNotify when avoiding the protected hack. - reinterpret_cast(source)->disconnectNotify(slotMethod); //Qt5: QMetaMethod instead of char * - #endif - return true; - } - return false; -} +#include // @snippet qobject-connect // @snippet qobject-connect-1 // %FUNCTION_NAME() - disable generation of function call. -%RETURN_TYPE %0 = qobjectConnect(%1, %2, %CPPSELF, %3, %4); +%RETURN_TYPE %0 = PySide::qobjectConnect(%1, %2, %CPPSELF, %3, %4); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-connect-1 // @snippet qobject-connect-2 // %FUNCTION_NAME() - disable generation of function call. -%RETURN_TYPE %0 = qobjectConnect(%1, %2, %3, %4, %5); +%RETURN_TYPE %0 = PySide::qobjectConnect(%1, %2, %3, %4, %5); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-connect-2 // @snippet qobject-connect-3 // %FUNCTION_NAME() - disable generation of function call. -%RETURN_TYPE %0 = qobjectConnect(%1, %2, %3, %4, %5); +%RETURN_TYPE %0 = PySide::qobjectConnect(%1, %2, %3, %4, %5); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-connect-3 // @snippet qobject-connect-4 // %FUNCTION_NAME() - disable generation of function call. -%RETURN_TYPE %0 = qobjectConnectCallback(%1, %2, %PYARG_3, %4); +%RETURN_TYPE %0 = PySide::qobjectConnectCallback(%1, %2, %PYARG_3, %4); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-connect-4 // @snippet qobject-connect-5 // %FUNCTION_NAME() - disable generation of function call. -%RETURN_TYPE %0 = qobjectConnectCallback(%CPPSELF, %1, %PYARG_2, %3); +%RETURN_TYPE %0 = PySide::qobjectConnectCallback(%CPPSELF, %1, %PYARG_2, %3); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-connect-5 // @snippet qobject-connect-6 // %FUNCTION_NAME() - disable generation of function call. -%RETURN_TYPE %0 = qobjectConnect(%CPPSELF, %1, %2, %3, %4); +%RETURN_TYPE %0 = PySide::qobjectConnect(%CPPSELF, %1, %2, %3, %4); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-connect-6 @@ -592,13 +369,13 @@ static bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObj // @snippet qobject-disconnect-1 // %FUNCTION_NAME() - disable generation of function call. -%RETURN_TYPE %0 = qobjectDisconnectCallback(%CPPSELF, %1, %2); +%RETURN_TYPE %0 = PySide::qobjectDisconnectCallback(%CPPSELF, %1, %2); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-disconnect-1 // @snippet qobject-disconnect-2 // %FUNCTION_NAME() - disable generation of function call. -%RETURN_TYPE %0 = qobjectDisconnectCallback(%1, %2, %3); +%RETURN_TYPE %0 = PySide::qobjectDisconnectCallback(%1, %2, %3); %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); // @snippet qobject-disconnect-2 diff --git a/sources/pyside6/libpyside/CMakeLists.txt b/sources/pyside6/libpyside/CMakeLists.txt index 6f662e6a0..74104263c 100644 --- a/sources/pyside6/libpyside/CMakeLists.txt +++ b/sources/pyside6/libpyside/CMakeLists.txt @@ -19,6 +19,7 @@ set(libpyside_SRC pyside.cpp pyside_numpy.cpp pysidestaticstrings.cpp + qobjectconnect.cpp ) qt6_add_resources(libpyside_SRC libpyside.qrc) @@ -100,6 +101,7 @@ set(libpyside_HEADERS pysideproperty.h pysideqflags.h pysideweakref.h + qobjectconnect.h ) if (CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/sources/pyside6/libpyside/qobjectconnect.cpp b/sources/pyside6/libpyside/qobjectconnect.cpp new file mode 100644 index 000000000..fb9660e0a --- /dev/null +++ b/sources/pyside6/libpyside/qobjectconnect.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qobjectconnect.h" +#include "pyside.h" +#include "pysidesignal.h" +#include "signalmanager.h" + +#include "shiboken.h" +#include "basewrapper.h" +#include "autodecref.h" + +#include +#include +#include + +static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self) +{ + Shiboken::AutoDecRef methodName(PyObject_GetAttr(method, Shiboken::PyMagicName::name())); + if (!PyObject_HasAttr(self, methodName)) + return true; + Shiboken::AutoDecRef otherMethod(PyObject_GetAttr(self, methodName)); + + // PYSIDE-1523: Each could be a compiled method or a normal method here, for the + // compiled ones we can use the attributes. + PyObject *function1; + if (PyMethod_Check(otherMethod.object())) { + function1 = PyMethod_GET_FUNCTION(otherMethod.object()); + } else { + function1 = PyObject_GetAttr(otherMethod.object(), Shiboken::PyName::im_func()); + Py_DECREF(function1); + // Not retaining a reference in line with what PyMethod_GET_FUNCTION does. + } + + PyObject *function2; + if (is_pymethod) { + function2 = PyMethod_GET_FUNCTION(method); + } else { + function2 = PyObject_GetAttr(method, Shiboken::PyName::im_func()); + Py_DECREF(function2); + // Not retaining a reference in line with what PyMethod_GET_FUNCTION does. + } + + return function1 != function2; +} + +struct GetReceiverResult +{ + QObject *receiver = nullptr; + PyObject *self = nullptr; + QByteArray callbackSig; + bool usingGlobalReceiver = false; + int slotIndex = -1; +}; + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const GetReceiverResult &r) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "GetReceiverResult(receiver=" << r.receiver << ", self=" << r.self + << ", sig=" << r.callbackSig << "slotIndex=" << r.slotIndex + << ", usingGlobalReceiver=" << r.usingGlobalReceiver << ')'; + return d; +} +#endif // QT_NO_DEBUG_STREAM + +static GetReceiverResult getReceiver(QObject *source, const char *signal, + PyObject *callback) +{ + GetReceiverResult result; + + bool forceGlobalReceiver = false; + if (PyMethod_Check(callback)) { + result.self = PyMethod_GET_SELF(callback); + result.receiver = PySide::convertToQObject(result.self, false); + forceGlobalReceiver = isMethodDecorator(callback, true, result.self); + } else if (PyCFunction_Check(callback)) { + result.self = PyCFunction_GET_SELF(callback); + result.receiver = PySide::convertToQObject(result.self, false); + } else if (PyObject_HasAttr(callback, Shiboken::PyName::im_func()) + && PyObject_HasAttr(callback, Shiboken::PyName::im_self())) { + result.self = PyObject_GetAttr(callback, Shiboken::PyName::im_self()); + Py_DECREF(result.self); + result.receiver = PySide::convertToQObject(result.self, false); + forceGlobalReceiver = isMethodDecorator(callback, false, result.self); + } else if (PyCallable_Check(callback)) { + // Ok, just a callable object + result.receiver = nullptr; + result.self = nullptr; + } + + result.usingGlobalReceiver = !result.receiver || forceGlobalReceiver; + + // Check if this callback is a overwrite of a non-virtual Qt slot. + if (!result.usingGlobalReceiver && result.receiver && result.self) { + result.callbackSig = + PySide::Signal::getCallbackSignature(signal, result.receiver, callback, + result.usingGlobalReceiver).toLatin1(); + const QMetaObject *metaObject = result.receiver->metaObject(); + result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData()); + if (result.slotIndex != -1 && result.slotIndex < metaObject->methodOffset() + && PyMethod_Check(callback)) { + result.usingGlobalReceiver = true; + } + } + + const auto receiverThread = result.receiver ? result.receiver->thread() : nullptr; + + if (result.usingGlobalReceiver) { + PySide::SignalManager &signalManager = PySide::SignalManager::instance(); + result.receiver = signalManager.globalReceiver(source, callback); + // PYSIDE-1354: Move the global receiver to the original receivers's thread + // so that autoconnections work correctly. + if (receiverThread && receiverThread != result.receiver->thread()) + result.receiver->moveToThread(receiverThread); + result.callbackSig = + PySide::Signal::getCallbackSignature(signal, result.receiver, callback, + result.usingGlobalReceiver).toLatin1(); + const QMetaObject *metaObject = result.receiver->metaObject(); + result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData()); + } + + return result; +} + +namespace PySide +{ +class FriendlyQObject : public QObject // Make protected connectNotify() accessible. +{ +public: + using QObject::connectNotify; + using QObject::disconnectNotify; +}; + +QMetaObject::Connection qobjectConnect(QObject *source, const char *signal, + QObject *receiver, const char *slot, + Qt::ConnectionType type) +{ + if (!signal || !slot || !PySide::Signal::checkQtSignal(signal)) + return {}; + + if (!PySide::SignalManager::registerMetaMethod(source, signal + 1, QMetaMethod::Signal)) + return {}; + + const auto methodType = PySide::Signal::isQtSignal(slot) + ? QMetaMethod::Signal : QMetaMethod::Slot; + PySide::SignalManager::registerMetaMethod(receiver, slot + 1, methodType); + return QObject::connect(source, signal, receiver, slot, type); +} + +QMetaObject::Connection qobjectConnect(QObject *source, QMetaMethod signal, + QObject *receiver, QMetaMethod slot, + Qt::ConnectionType type) +{ + return qobjectConnect(source, signal.methodSignature().constData(), + receiver, slot.methodSignature().constData(), type); +} + +QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *signal, + 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 {}; + + int slotIndex = receiver.slotIndex; + + PySide::SignalManager &signalManager = PySide::SignalManager::instance(); + if (slotIndex == -1) { + if (!receiver.usingGlobalReceiver && receiver.self + && !Shiboken::Object::hasCppWrapper(reinterpret_cast(receiver.self))) { + qWarning("You can't add dynamic slots on an object originated from C++."); + if (receiver.usingGlobalReceiver) + signalManager.releaseGlobalReceiver(source, receiver.receiver); + + return {}; + } + + const char *slotSignature = receiver.callbackSig.constData(); + slotIndex = receiver.usingGlobalReceiver + ? signalManager.globalReceiverSlotIndex(receiver.receiver, slotSignature) + : PySide::SignalManager::registerMetaMethodGetIndex(receiver.receiver, slotSignature, + QMetaMethod::Slot); + + if (slotIndex == -1) { + if (receiver.usingGlobalReceiver) + signalManager.releaseGlobalReceiver(source, receiver.receiver); + + return {}; + } + } + + auto connection = QMetaObject::connect(source, signalIndex, receiver.receiver, slotIndex, type); + 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)) + return false; + + // Extract receiver from callback + const GetReceiverResult receiver = getReceiver(nullptr, signal, callback); + if (receiver.receiver == nullptr && receiver.self == nullptr) + return false; + + const int signalIndex = source->metaObject()->indexOfSignal(signal + 1); + const int slotIndex = receiver.slotIndex; + + if (!QMetaObject::disconnectOne(source, signalIndex, receiver.receiver, slotIndex)) + return false; + + Q_ASSERT(receiver.receiver); + const QMetaMethod slotMethod = receiver.receiver->metaObject()->method(slotIndex); + static_cast(source)->disconnectNotify(slotMethod); + + if (receiver.usingGlobalReceiver) { // might delete the receiver + PySide::SignalManager &signalManager = PySide::SignalManager::instance(); + signalManager.releaseGlobalReceiver(source, receiver.receiver); + } + return true; +} + +} // namespace PySide diff --git a/sources/pyside6/libpyside/qobjectconnect.h b/sources/pyside6/libpyside/qobjectconnect.h new file mode 100644 index 000000000..9c471e313 --- /dev/null +++ b/sources/pyside6/libpyside/qobjectconnect.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOBJECTCONNECT_H +#define QOBJECTCONNECT_H + +#include "pysidemacros.h" + +#include + +#include + +QT_FORWARD_DECLARE_CLASS(QObject) +QT_FORWARD_DECLARE_CLASS(QMetaMethod) + +namespace PySide +{ + +/// Helpers for QObject::connect(): Make a string-based connection +PYSIDE_API QMetaObject::Connection + qobjectConnect(QObject *source, const char *signal, + QObject *receiver, const char *slot, + Qt::ConnectionType type); + +/// Helpers for QObject::connect(): Make a connection based on QMetaMethod +PYSIDE_API QMetaObject::Connection + qobjectConnect(QObject *source, QMetaMethod signal, + QObject *receiver, QMetaMethod slot, + Qt::ConnectionType type); + +/// Helpers for QObject::connect(): Make a connection to a Python callback +PYSIDE_API QMetaObject::Connection + qobjectConnectCallback(QObject *source, const char *signal, + PyObject *callback, Qt::ConnectionType type); + +/// Helpers for QObject::disconnect(): Disconnect a Python callback +PYSIDE_API bool qobjectDisconnectCallback(QObject *source, const char *signal, + PyObject *callback); + +} // namespace PySide + +#endif // QOBJECTCONNECT_H -- cgit v1.2.3