Skip to content

Commit 3390891

Browse files
committed
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
1 parent 01a50e5 commit 3390891

4 files changed

Lines changed: 185 additions & 215 deletions

File tree

src/PythonQt.cpp

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
/*!
3535
// \file PythonQt.cpp
3636
// \author Florian Link
37-
// \author Last changed by $Author: florian $
3837
// \date 2006-05
3938
*/
4039
//----------------------------------------------------------------------------------
@@ -324,8 +323,7 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName)
324323
void PythonQt::cleanup()
325324
{
326325
if (_self) {
327-
// Remove signal handlers in advance, since destroying them calls back into
328-
// PythonQt::priv()->removeSignalEmitter()
326+
// Remove all created signal receivers
329327
_self->removeSignalHandlers();
330328

331329
delete _self;
@@ -879,65 +877,36 @@ PyObject* PythonQtPrivate::createNewPythonQtEnumWrapper(const char* enumName, Py
879877
return result;
880878
}
881879

882-
PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj)
883-
{
884-
PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
885-
if (!r) {
886-
r = new PythonQtSignalReceiver(obj);
887-
_p->_signalReceivers.insert(obj, r);
888-
}
889-
return r;
890-
}
891-
892880
bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
893881
{
894-
bool flag = false;
895882
PythonQtObjectPtr callable = lookupCallable(module, objectname);
896883
if (callable) {
897-
PythonQtSignalReceiver* r = getSignalReceiver(obj);
898-
flag = r->addSignalHandler(signal, callable);
899-
if (!flag) {
900-
// signal not found
901-
}
884+
return _p->addSignalHandler(obj, signal, callable);
902885
} else {
903886
// callable not found
904887
}
905-
return flag;
888+
return false;
906889
}
907890

908-
bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
891+
bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* callable)
909892
{
910-
bool flag = false;
911-
PythonQtSignalReceiver* r = getSignalReceiver(obj);
912-
if (r) {
913-
flag = r->addSignalHandler(signal, receiver);
914-
}
915-
return flag;
893+
return _p->addSignalHandler(obj, signal, callable);
916894
}
917895

918896
bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
919897
{
920-
bool flag = false;
921898
PythonQtObjectPtr callable = lookupCallable(module, objectname);
922899
if (callable) {
923-
PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
924-
if (r) {
925-
flag = r->removeSignalHandler(signal, callable);
926-
}
900+
return _p->removeSignalHandler(obj, signal, callable);
927901
} else {
928902
// callable not found
929903
}
930-
return flag;
904+
return false;
931905
}
932906

933-
bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
907+
bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* callable)
934908
{
935-
bool flag = false;
936-
PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
937-
if (r) {
938-
flag = r->removeSignalHandler(signal, receiver);
939-
}
940-
return flag;
909+
return _p->removeSignalHandler(obj, signal, callable);
941910
}
942911

943912
PythonQtObjectPtr PythonQt::lookupCallable(PyObject* module, const QString& name)
@@ -1619,20 +1588,14 @@ void PythonQtPrivate::registerQObjectClassNames(const QStringList& names)
16191588
}
16201589
}
16211590

1622-
void PythonQtPrivate::removeSignalEmitter(QObject* obj)
1623-
{
1624-
_signalReceivers.remove(obj);
1625-
}
1626-
16271591
void PythonQt::removeSignalHandlers()
16281592
{
1629-
QList<PythonQtSignalReceiver*> signalReceivers = _p->_signalReceivers.values();
1630-
1631-
// just delete all signal receivers, they will remove themselves via removeSignalEmitter()
1632-
for (PythonQtSignalReceiver* receiver : qAsConst(signalReceivers)) {
1633-
delete receiver;
1593+
auto it = _p->_signalReceivers.begin();
1594+
while (it != _p->_signalReceivers.end()) {
1595+
it.value()->markAsRemoved();
1596+
delete it.value();
1597+
it++;
16341598
}
1635-
// just to be sure, clear the receiver map as well
16361599
_p->_signalReceivers.clear();
16371600
}
16381601

@@ -2012,6 +1975,57 @@ PythonQtClassInfo* PythonQtPrivate::lookupClassInfoAndCreateIfNotPresent(const c
20121975
return info;
20131976
}
20141977

1978+
bool PythonQtPrivate::addSignalHandler(QObject* sender, const char* signal, PyObject* callable)
1979+
{
1980+
// Note: It is assumed that the GIL is held when this is called
1981+
bool ok = false;
1982+
int sigId = PythonQtSignalReceiver::getSignalIndex(sender, signal);
1983+
if (sigId >= 0) {
1984+
// create PythonQtMethodInfo from signal
1985+
auto* receiver = new PythonQtSignalReceiver(sender, sigId, callable);
1986+
_signalReceivers.insert(SignalKey(sender, sigId), receiver);
1987+
ok = true;
1988+
}
1989+
return ok;
1990+
}
1991+
1992+
bool PythonQtPrivate::removeSignalHandler(QObject* sender, const char* signal, PyObject* callable)
1993+
{
1994+
// Note: It is assumed that the GIL is held when this is called
1995+
int foundCount = 0;
1996+
int sigId = PythonQtSignalReceiver::getSignalIndex(sender, signal);
1997+
if (sigId >= 0) {
1998+
SignalKey hashKey(sender, sigId);
1999+
auto it = _signalReceivers.find(hashKey);
2000+
while (it != _signalReceivers.end() && it.key() == hashKey) {
2001+
if (!callable || it.value()->isSameCallable(callable)) {
2002+
it.value()->markAsRemoved();
2003+
// delete later in case the connection is removed from the receiver callable itself
2004+
it.value()->deleteLater();
2005+
foundCount++;
2006+
it = _signalReceivers.erase(it);
2007+
} else {
2008+
it++;
2009+
}
2010+
}
2011+
}
2012+
return foundCount > 0;
2013+
}
2014+
2015+
void PythonQtPrivate::removeSignalReceiver(PythonQtSignalReceiver* receiver)
2016+
{
2017+
PYTHONQT_GIL_SCOPE
2018+
SignalKey hashKey(receiver->sender(), receiver->signalId());
2019+
auto it = _signalReceivers.find(hashKey);
2020+
while (it != _signalReceivers.end() && it.key() == hashKey) {
2021+
if (it.value() == receiver) {
2022+
_signalReceivers.erase(it);
2023+
break; // each receiver is only entered once
2024+
}
2025+
it++;
2026+
}
2027+
}
2028+
20152029
void PythonQt::addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb)
20162030
{
20172031
_p->addPolymorphicHandler(typeName, cb);

src/PythonQt.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
/*!
3838
// \file PythonQt.h
3939
// \author Florian Link
40-
// \author Last changed by $Author: florian $
4140
// \date 2006-05
4241
*/
4342
//----------------------------------------------------------------------------------
@@ -55,6 +54,7 @@
5554
#include <QVariant>
5655
#include <QList>
5756
#include <QHash>
57+
#include <QMultiHash>
5858
#include <QByteArray>
5959
#include <QStringList>
6060
#include <QtDebug>
@@ -654,9 +654,6 @@ class PYTHONQT_EXPORT PythonQt : public QObject
654654
//! callback for stderr redirection, emits pythonStdErr signal
655655
static void stdErrRedirectCB(const QString& str);
656656

657-
//! get (and create if not available) the signal receiver of that QObject, signal receiver is made child of the passed \c obj
658-
PythonQtSignalReceiver* getSignalReceiver(QObject* obj);
659-
660657
PythonQt(int flags, const QByteArray& pythonQtModuleName);
661658
~PythonQt() override;
662659
static PythonQt* _self;
@@ -729,8 +726,18 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject
729726
//! lookup existing classinfo and return new if not yet present
730727
PythonQtClassInfo* lookupClassInfoAndCreateIfNotPresent(const char* typeName);
731728

732-
//! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map
733-
void removeSignalEmitter(QObject* obj);
729+
//! add a signal handler
730+
//!
731+
//! The GIL should be held when calling this
732+
bool addSignalHandler(QObject* sender, const char* signal, PyObject* callable);
733+
734+
//! remove a signal handler for given callable (or all callables on that signal if callable is NULL)
735+
//!
736+
//! The GIL should be held when calling this
737+
bool removeSignalHandler(QObject* sender, const char* signal, PyObject* callable = nullptr);
738+
739+
//! called when a signal receiver is deleted to remove it from the hash map
740+
void removeSignalReceiver(PythonQtSignalReceiver* receiver);
734741

735742
//! wrap the given QObject into a Python object (or return existing wrapper!)
736743
PyObject* wrapQObject(QObject* obj);
@@ -865,7 +872,8 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject
865872
QHash<QByteArray, QByteArray> _knownLazyClasses;
866873

867874
//! stores signal receivers for QObjects
868-
QHash<QObject*, PythonQtSignalReceiver*> _signalReceivers;
875+
typedef QPair<QObject*, int> SignalKey;
876+
QMultiHash<SignalKey, PythonQtSignalReceiver*> _signalReceivers;
869877

870878
//! the PythonQt python module
871879
PythonQtObjectPtr _pythonQtModule;

0 commit comments

Comments
 (0)