From 339089133eff7b2b70abb236b6b035da5787838f Mon Sep 17 00:00:00 2001 From: Uwe Siems Date: Wed, 26 Aug 2026 19:00:31 +0200 Subject: [PATCH 1/2] Create a separate receiver object for each signal/callable connect This way we can't run out of slot IDs. This fixes #362 This also enables us to associate the receiver with the instance object of the callable (if it is a method of a QObject-derived class) instead of the sender, so that it is associated with the correct thread, which is important for the AutoConnection used. In a way this also fixes the problem in #363 [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PythonQt.cpp | 116 +++++++++++--------- src/PythonQt.h | 22 ++-- src/PythonQtSignalReceiver.cpp | 195 +++++++++++++-------------------- src/PythonQtSignalReceiver.h | 67 +++++------ 4 files changed, 185 insertions(+), 215 deletions(-) diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index c7e76ea61..4f5022823 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -34,7 +34,6 @@ /*! // \file PythonQt.cpp // \author Florian Link -// \author Last changed by $Author: florian $ // \date 2006-05 */ //---------------------------------------------------------------------------------- @@ -324,8 +323,7 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) void PythonQt::cleanup() { if (_self) { - // Remove signal handlers in advance, since destroying them calls back into - // PythonQt::priv()->removeSignalEmitter() + // Remove all created signal receivers _self->removeSignalHandlers(); delete _self; @@ -879,65 +877,36 @@ PyObject* PythonQtPrivate::createNewPythonQtEnumWrapper(const char* enumName, Py return result; } -PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj) -{ - PythonQtSignalReceiver* r = _p->_signalReceivers[obj]; - if (!r) { - r = new PythonQtSignalReceiver(obj); - _p->_signalReceivers.insert(obj, r); - } - return r; -} - bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname) { - bool flag = false; PythonQtObjectPtr callable = lookupCallable(module, objectname); if (callable) { - PythonQtSignalReceiver* r = getSignalReceiver(obj); - flag = r->addSignalHandler(signal, callable); - if (!flag) { - // signal not found - } + return _p->addSignalHandler(obj, signal, callable); } else { // callable not found } - return flag; + return false; } -bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* receiver) +bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* callable) { - bool flag = false; - PythonQtSignalReceiver* r = getSignalReceiver(obj); - if (r) { - flag = r->addSignalHandler(signal, receiver); - } - return flag; + return _p->addSignalHandler(obj, signal, callable); } bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname) { - bool flag = false; PythonQtObjectPtr callable = lookupCallable(module, objectname); if (callable) { - PythonQtSignalReceiver* r = _p->_signalReceivers[obj]; - if (r) { - flag = r->removeSignalHandler(signal, callable); - } + return _p->removeSignalHandler(obj, signal, callable); } else { // callable not found } - return flag; + return false; } -bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver) +bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* callable) { - bool flag = false; - PythonQtSignalReceiver* r = _p->_signalReceivers[obj]; - if (r) { - flag = r->removeSignalHandler(signal, receiver); - } - return flag; + return _p->removeSignalHandler(obj, signal, callable); } PythonQtObjectPtr PythonQt::lookupCallable(PyObject* module, const QString& name) @@ -1619,20 +1588,14 @@ void PythonQtPrivate::registerQObjectClassNames(const QStringList& names) } } -void PythonQtPrivate::removeSignalEmitter(QObject* obj) -{ - _signalReceivers.remove(obj); -} - void PythonQt::removeSignalHandlers() { - QList signalReceivers = _p->_signalReceivers.values(); - - // just delete all signal receivers, they will remove themselves via removeSignalEmitter() - for (PythonQtSignalReceiver* receiver : qAsConst(signalReceivers)) { - delete receiver; + auto it = _p->_signalReceivers.begin(); + while (it != _p->_signalReceivers.end()) { + it.value()->markAsRemoved(); + delete it.value(); + it++; } - // just to be sure, clear the receiver map as well _p->_signalReceivers.clear(); } @@ -2012,6 +1975,57 @@ PythonQtClassInfo* PythonQtPrivate::lookupClassInfoAndCreateIfNotPresent(const c return info; } +bool PythonQtPrivate::addSignalHandler(QObject* sender, const char* signal, PyObject* callable) +{ + // Note: It is assumed that the GIL is held when this is called + bool ok = false; + int sigId = PythonQtSignalReceiver::getSignalIndex(sender, signal); + if (sigId >= 0) { + // create PythonQtMethodInfo from signal + auto* receiver = new PythonQtSignalReceiver(sender, sigId, callable); + _signalReceivers.insert(SignalKey(sender, sigId), receiver); + ok = true; + } + return ok; +} + +bool PythonQtPrivate::removeSignalHandler(QObject* sender, const char* signal, PyObject* callable) +{ + // Note: It is assumed that the GIL is held when this is called + int foundCount = 0; + int sigId = PythonQtSignalReceiver::getSignalIndex(sender, signal); + if (sigId >= 0) { + SignalKey hashKey(sender, sigId); + auto it = _signalReceivers.find(hashKey); + while (it != _signalReceivers.end() && it.key() == hashKey) { + if (!callable || it.value()->isSameCallable(callable)) { + it.value()->markAsRemoved(); + // delete later in case the connection is removed from the receiver callable itself + it.value()->deleteLater(); + foundCount++; + it = _signalReceivers.erase(it); + } else { + it++; + } + } + } + return foundCount > 0; +} + +void PythonQtPrivate::removeSignalReceiver(PythonQtSignalReceiver* receiver) +{ + PYTHONQT_GIL_SCOPE + SignalKey hashKey(receiver->sender(), receiver->signalId()); + auto it = _signalReceivers.find(hashKey); + while (it != _signalReceivers.end() && it.key() == hashKey) { + if (it.value() == receiver) { + _signalReceivers.erase(it); + break; // each receiver is only entered once + } + it++; + } +} + void PythonQt::addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb) { _p->addPolymorphicHandler(typeName, cb); diff --git a/src/PythonQt.h b/src/PythonQt.h index 3f84061c3..fe8169dd9 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -37,7 +37,6 @@ /*! // \file PythonQt.h // \author Florian Link -// \author Last changed by $Author: florian $ // \date 2006-05 */ //---------------------------------------------------------------------------------- @@ -55,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -654,9 +654,6 @@ class PYTHONQT_EXPORT PythonQt : public QObject //! callback for stderr redirection, emits pythonStdErr signal static void stdErrRedirectCB(const QString& str); - //! get (and create if not available) the signal receiver of that QObject, signal receiver is made child of the passed \c obj - PythonQtSignalReceiver* getSignalReceiver(QObject* obj); - PythonQt(int flags, const QByteArray& pythonQtModuleName); ~PythonQt() override; static PythonQt* _self; @@ -729,8 +726,18 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject //! lookup existing classinfo and return new if not yet present PythonQtClassInfo* lookupClassInfoAndCreateIfNotPresent(const char* typeName); - //! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map - void removeSignalEmitter(QObject* obj); + //! add a signal handler + //! + //! The GIL should be held when calling this + bool addSignalHandler(QObject* sender, const char* signal, PyObject* callable); + + //! remove a signal handler for given callable (or all callables on that signal if callable is NULL) + //! + //! The GIL should be held when calling this + bool removeSignalHandler(QObject* sender, const char* signal, PyObject* callable = nullptr); + + //! called when a signal receiver is deleted to remove it from the hash map + void removeSignalReceiver(PythonQtSignalReceiver* receiver); //! wrap the given QObject into a Python object (or return existing wrapper!) PyObject* wrapQObject(QObject* obj); @@ -865,7 +872,8 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject QHash _knownLazyClasses; //! stores signal receivers for QObjects - QHash _signalReceivers; + typedef QPair SignalKey; + QMultiHash _signalReceivers; //! the PythonQt python module PythonQtObjectPtr _pythonQtModule; diff --git a/src/PythonQtSignalReceiver.cpp b/src/PythonQtSignalReceiver.cpp index fd1814fd4..eb9f21346 100644 --- a/src/PythonQtSignalReceiver.cpp +++ b/src/PythonQtSignalReceiver.cpp @@ -34,7 +34,6 @@ /*! // \file PythonQtSignalReceiver.cpp // \author Florian Link -// \author Last changed by $Author: florian $ // \date 2006-05 */ //---------------------------------------------------------------------------------- @@ -46,10 +45,6 @@ #include #include -// use -2 to signal that the variable is uninitialized -int PythonQtSignalReceiver::_destroyedSignal1Id = -2; -int PythonQtSignalReceiver::_destroyedSignal2Id = -2; - void PythonQtSignalTarget::call(void** arguments) const { PYTHONQT_GIL_SCOPE @@ -144,148 +139,116 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf return result; } -bool PythonQtSignalTarget::isSame(int signalId, PyObject* callable) const +bool PythonQtSignalTarget::isSame(PyObject* callable) const { - return PyObject_RichCompareBool(callable, _callable, Py_EQ) && (signalId == _signalId); + return PyObject_RichCompareBool(callable, _callable, Py_EQ); } //------------------------------------------------------------------------------ -PythonQtSignalReceiver::PythonQtSignalReceiver(QObject* obj) - : PythonQtSignalReceiverBase(obj) +int PythonQtSignalReceiver::_slotCount = 0; + +PythonQtSignalReceiver::PythonQtSignalReceiver(QObject* sender, int signalId, PyObject* callable) + : _sender(sender) + , _signalId(signalId) + , _alreadyRemoved(false) { - if (_destroyedSignal1Id == -2) { - // initialize these once - _destroyedSignal1Id = QObject::staticMetaObject.indexOfSignal("destroyed()"); - _destroyedSignal2Id = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); - if (_destroyedSignal1Id == -1 || _destroyedSignal2Id == -1) { + static int destroyedSignalId = []() -> int { + int id = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); + if (id == -1) { std::cerr << "PythonQt: could not find destroyed signal index, should never happen!" << std::endl; } - } - - _destroyedSignalCount = 0; - _obj = obj; - - // fetch the class info for object, since we will need to for correct enum resolution in - // signals - _objClassInfo = PythonQt::priv()->getClassInfo(obj->metaObject()); - if (!_objClassInfo || !_objClassInfo->isQObject()) { - PythonQt::self()->registerClass(obj->metaObject()); - _objClassInfo = PythonQt::priv()->getClassInfo(obj->metaObject()); + // while we are already thread-safe: also determine _slotCount + _slotCount = PythonQtSignalReceiver::staticMetaObject.methodOffset(); + return id; + }(); + + // fetch the class info for object, since we will need it for correct enum resolution in signals + auto metaObject = _sender->metaObject(); + PythonQtClassInfo* senderClassInfo = PythonQt::priv()->getClassInfo(metaObject); + if (!senderClassInfo || !senderClassInfo->isQObject()) { + PythonQt::self()->registerClass(metaObject); + senderClassInfo = PythonQt::priv()->getClassInfo(metaObject); } // force decorator/enum creation - _objClassInfo->decorator(); - - _slotCount = staticMetaObject.methodOffset(); -} - -PythonQtSignalReceiver::~PythonQtSignalReceiver() -{ - if (PythonQt::priv()) { - // we need the GIL scope here, because the targets keep references to Python objects - PYTHONQT_GIL_SCOPE; - PythonQt::priv()->removeSignalEmitter(_obj); - _targets.clear(); + senderClassInfo->decorator(); + + QMetaMethod meta = _sender->metaObject()->method(_signalId); + const PythonQtMethodInfo* signalInfo = PythonQtMethodInfo::getCachedMethodInfo(meta, senderClassInfo); + _target = PythonQtSignalTarget(signalInfo, callable); + // now connect to ourself with the next free slot id + QMetaObject::connect(_sender, _signalId, this, _slotCount, Qt::AutoConnection, nullptr); + // also connect to destroyed signal of sender, so we can remove the receiver accordingly + // (but with QueuedConnection, in case the original connect is to the destroyed signal too, + // so that the callable is guaranteed to be called before this receiver is destroyed!) + QMetaObject::connect(_sender, destroyedSignalId, this, _slotCount + 1, Qt::QueuedConnection, nullptr); + + // Check if the callable is a method of a QObject instance: + if (PyMethod_Check(callable)) { + PyObject* instance = PyMethod_Self(callable); + if (PyObject_TypeCheck(instance, &PythonQtInstanceWrapper_Type)) { + PythonQtInstanceWrapper* typedInstance = (PythonQtInstanceWrapper*)instance; + if (!typedInstance->_wrappedPtr) { + // It's a QObject-derived class + QObject* targetObj = typedInstance->_obj; + // move the receiver to the same thread as the "self" of the callable + moveToThread(targetObj->thread()); + // make the receiver a child of this object, so it will automatically change threads with it, + // and also will be deleted with it + setParent(targetObj); + } + } } } -bool PythonQtSignalReceiver::addSignalHandler(const char* signal, PyObject* callable) +PythonQtSignalReceiver::~PythonQtSignalReceiver() { - bool flag = false; - int sigId = getSignalIndex(signal); - if (sigId >= 0) { - // create PythonQtMethodInfo from signal - QMetaMethod meta = _obj->metaObject()->method(sigId); - const PythonQtMethodInfo* signalInfo = PythonQtMethodInfo::getCachedMethodInfo(meta, _objClassInfo); - PythonQtSignalTarget t(sigId, signalInfo, _slotCount, callable); - _targets.append(t); - // now connect to ourselves with the new slot id - QMetaObject::connect(_obj, sigId, this, _slotCount, Qt::AutoConnection, nullptr); - - _slotCount++; - flag = true; - - if (sigId == _destroyedSignal1Id || sigId == _destroyedSignal2Id) { - _destroyedSignalCount++; - if (_destroyedSignalCount == 1) { - // make ourself parent of PythonQt, to not get deleted as a child of the QObject we are - // listening to, since we do that manually when we receive the destroyed signal - this->setParent(PythonQt::priv()); - } - } + if (!_alreadyRemoved) { + // remove from list of all receiver objects + PythonQt::self()->priv()->removeSignalReceiver(this); } - return flag; } -bool PythonQtSignalReceiver::removeSignalHandler(const char* signal, PyObject* callable) +bool PythonQtSignalReceiver::isSameCallable(PyObject* callable) const { - int foundCount = 0; - int sigId = getSignalIndex(signal); - if (sigId >= 0) { - QMutableListIterator i(_targets); - if (callable) { - while (i.hasNext()) { - if (i.next().isSame(sigId, callable)) { - QMetaObject::disconnect(_obj, sigId, this, i.value().slotId()); - i.remove(); - foundCount++; - break; - } - } - } else { - while (i.hasNext()) { - if (i.next().signalId() == sigId) { - QMetaObject::disconnect(_obj, sigId, this, i.value().slotId()); - i.remove(); - foundCount++; - } - } - } - } - if ((foundCount > 0) && ((sigId == _destroyedSignal1Id) || (sigId == _destroyedSignal2Id))) { - _destroyedSignalCount -= foundCount; - if (_destroyedSignalCount == 0) { - // make ourself child of QObject again, to get deleted when the object gets deleted - this->setParent(_obj); - } - } - return foundCount > 0; + return _target.isSame(callable); } -int PythonQtSignalReceiver::getSignalIndex(const char* signal) +int PythonQtSignalReceiver::getSignalIndex(QObject* sender, const char* signal) { - int sigId = _obj->metaObject()->indexOfSignal(signal + 1); + int sigId = sender->metaObject()->indexOfSignal(signal + 1); if (sigId < 0) { QByteArray tmpSig = QMetaObject::normalizedSignature(signal + 1); - sigId = _obj->metaObject()->indexOfSignal(tmpSig); + sigId = sender->metaObject()->indexOfSignal(tmpSig); } return sigId; } +void PythonQtSignalReceiver::markAsRemoved() +{ + _alreadyRemoved = true; + // disconnect from potential parent, to prevent situations where the receiver is deleted recursively + // a second time via the connected callable, whose refcount might go to 0 in the destructor + setParent(nullptr); +} + int PythonQtSignalReceiver::qt_metacall(QMetaObject::Call c, int id, void** arguments) { - // mlabDebugConst("PythonQt", "PythonQtSignalReceiver invoke " << _obj->className() << " " << _obj->name() << " " << id); + // mlabDebugConst("PythonQt", "PythonQtSignalReceiver invoke " << _sender->className() << " " << _sender->name() << " " << id); if (c != QMetaObject::InvokeMetaMethod) { - QObject::qt_metacall(c, id, arguments); - } - - bool shouldDelete = false; - for (const PythonQtSignalTarget& t : qAsConst(_targets)) { - if (t.slotId() == id) { - const int sigId = t.signalId(); - t.call(arguments); - // if the signal is the last destroyed signal, we delete ourselves - if ((sigId == _destroyedSignal1Id) || (sigId == _destroyedSignal2Id)) { - _destroyedSignalCount--; - if (_destroyedSignalCount == 0) { - shouldDelete = true; - } - } - break; + return QObject::qt_metacall(c, id, arguments); + } + + if (!_alreadyRemoved) { + if (id == _slotCount) { + _target.call(arguments); + } else if (id == _slotCount + 1) { + // disconnect from potential parent, to prevent situations where the receiver is deleted recursively + // a second time via the connected callable, whose refcount might go to 0 in the destructor + setParent(nullptr); + // sender was destroyed + delete this; } } - if (shouldDelete) { - delete this; - } return 0; } diff --git a/src/PythonQtSignalReceiver.h b/src/PythonQtSignalReceiver.h index a8ca3f774..88c32e87e 100644 --- a/src/PythonQtSignalReceiver.h +++ b/src/PythonQtSignalReceiver.h @@ -37,7 +37,6 @@ /*! // \file PythonQtSignalReceiver.h // \author Florian Link -// \author Last changed by $Author: florian $ // \date 2006-05 */ //---------------------------------------------------------------------------------- @@ -56,45 +55,30 @@ class PythonQtClassInfo; class PYTHONQT_EXPORT PythonQtSignalTarget { public: - PythonQtSignalTarget() - { - _signalId = -1; - _methodInfo = nullptr; - _slotId = -1; - } + PythonQtSignalTarget() { _methodInfo = nullptr; } - PythonQtSignalTarget(int signalId, const PythonQtMethodInfo* methodInfo, int slotId, PyObject* callable) + PythonQtSignalTarget(const PythonQtMethodInfo* methodInfo, PyObject* callable) { - _signalId = signalId; - _slotId = slotId; _methodInfo = methodInfo; _callable = callable; }; ~PythonQtSignalTarget() {}; - //! get the id of the original signal - int signalId() const { return _signalId; } - - //! get the id that was assigned to this simulated slot - int slotId() const { return _slotId; } - //! get the signals parameter info const PythonQtMethodInfo* methodInfo() const { return _methodInfo; } //! call the python callable with the given arguments (as defined in methodInfo) void call(void** arguments) const; - //! check if it is the same signal target - bool isSame(int signalId, PyObject* callable) const; + //! check if it is this targets the same callable + bool isSame(PyObject* callable) const; //! call the given callable with arguments described by PythonQtMethodInfo, returns a new reference as result value (or NULL) static PyObject* call(PyObject* callable, const PythonQtMethodInfo* methodInfo, void** arguments, bool skipFirstArgumentOfMethodInfo = false); private: - int _signalId; - int _slotId; const PythonQtMethodInfo* _methodInfo; PythonQtSafeObjectPtr _callable; }; @@ -106,42 +90,43 @@ class PythonQtSignalReceiverBase : public QObject { Q_OBJECT public: - PythonQtSignalReceiverBase(QObject* obj) - : QObject(obj) {}; + PythonQtSignalReceiverBase() = default; }; -//! receives all signals for one QObject +//! connects a signal to a callable /*! we derive from our base but do not declare the QObject macro because we want to reimplement qt_metacall only. */ class PythonQtSignalReceiver : public PythonQtSignalReceiverBase { public: - PythonQtSignalReceiver(QObject* obj); + PythonQtSignalReceiver(QObject* sender, int signalId, PyObject* callable); ~PythonQtSignalReceiver() override; - //! add a signal handler - bool addSignalHandler(const char* signal, PyObject* callable); + //! Returns the signal sender for which this receiver was created. + QObject* sender() const { return _sender; } - //! remove a signal handler for given callable (or all callables on that signal if callable is NULL) - bool removeSignalHandler(const char* signal, PyObject* callable = nullptr); + //! Returns the signal ID for which this receiver was created. + int signalId() const { return _signalId; } + + //! Check if this object targets the same callable given as argument. + bool isSameCallable(PyObject* callable) const; + + //! Mark this object as already removed from the global list of signal receivers. + void markAsRemoved(); - //! we implement this method to simulate a number of slots that match the ids in _targets + //! We implement this method to either call the callable, or react to the fact that the sender was destroyed. int qt_metacall(QMetaObject::Call c, int id, void** arguments) override; + //! Get the index of a sender's signal. + static int getSignalIndex(QObject* sender, const char* signal); + private: - //! get the index of the signal - int getSignalIndex(const char* signal); - - QObject* _obj; - PythonQtClassInfo* _objClassInfo; - int _slotCount; - int _destroyedSignalCount; - // linear list may get slow on multiple targets, but I think typically we have many objects and just a few signals - QList _targets; - - static int _destroyedSignal1Id; - static int _destroyedSignal2Id; + static int _slotCount; + QObject* _sender; + int _signalId; + bool _alreadyRemoved; + PythonQtSignalTarget _target; }; #endif From 63601e69c52a08d6f3493c7bf1dd83c580430c7e Mon Sep 17 00:00:00 2001 From: Uwe Siems Date: Fri, 28 Aug 2026 18:20:08 +0200 Subject: [PATCH 2/2] Address review comments --- src/PythonQt.cpp | 15 ++++++--------- src/PythonQt.h | 4 ---- src/PythonQtSignalReceiver.h | 4 ++-- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 4f5022823..38b6ca314 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -882,9 +882,8 @@ bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* modu PythonQtObjectPtr callable = lookupCallable(module, objectname); if (callable) { return _p->addSignalHandler(obj, signal, callable); - } else { - // callable not found } + // callable not found return false; } @@ -898,9 +897,8 @@ bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* m PythonQtObjectPtr callable = lookupCallable(module, objectname); if (callable) { return _p->removeSignalHandler(obj, signal, callable); - } else { - // callable not found } + // callable not found return false; } @@ -1977,21 +1975,20 @@ PythonQtClassInfo* PythonQtPrivate::lookupClassInfoAndCreateIfNotPresent(const c bool PythonQtPrivate::addSignalHandler(QObject* sender, const char* signal, PyObject* callable) { - // Note: It is assumed that the GIL is held when this is called - bool ok = false; + PYTHONQT_GIL_SCOPE int sigId = PythonQtSignalReceiver::getSignalIndex(sender, signal); if (sigId >= 0) { // create PythonQtMethodInfo from signal auto* receiver = new PythonQtSignalReceiver(sender, sigId, callable); _signalReceivers.insert(SignalKey(sender, sigId), receiver); - ok = true; + return true; } - return ok; + return false; } bool PythonQtPrivate::removeSignalHandler(QObject* sender, const char* signal, PyObject* callable) { - // Note: It is assumed that the GIL is held when this is called + PYTHONQT_GIL_SCOPE int foundCount = 0; int sigId = PythonQtSignalReceiver::getSignalIndex(sender, signal); if (sigId >= 0) { diff --git a/src/PythonQt.h b/src/PythonQt.h index fe8169dd9..ae931fb97 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -727,13 +727,9 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject PythonQtClassInfo* lookupClassInfoAndCreateIfNotPresent(const char* typeName); //! add a signal handler - //! - //! The GIL should be held when calling this bool addSignalHandler(QObject* sender, const char* signal, PyObject* callable); //! remove a signal handler for given callable (or all callables on that signal if callable is NULL) - //! - //! The GIL should be held when calling this bool removeSignalHandler(QObject* sender, const char* signal, PyObject* callable = nullptr); //! called when a signal receiver is deleted to remove it from the hash map diff --git a/src/PythonQtSignalReceiver.h b/src/PythonQtSignalReceiver.h index 88c32e87e..4faf65e65 100644 --- a/src/PythonQtSignalReceiver.h +++ b/src/PythonQtSignalReceiver.h @@ -71,7 +71,7 @@ class PYTHONQT_EXPORT PythonQtSignalTarget //! call the python callable with the given arguments (as defined in methodInfo) void call(void** arguments) const; - //! check if it is this targets the same callable + //! check if this targets the same callable bool isSame(PyObject* callable) const; //! call the given callable with arguments described by PythonQtMethodInfo, returns a new reference as result value (or NULL) @@ -109,7 +109,7 @@ class PythonQtSignalReceiver : public PythonQtSignalReceiverBase //! Returns the signal ID for which this receiver was created. int signalId() const { return _signalId; } - //! Check if this object targets the same callable given as argument. + //! Check if this object targets the callable given as argument. bool isSameCallable(PyObject* callable) const; //! Mark this object as already removed from the global list of signal receivers.