Skip to content

Commit a6be76a

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 Note that this commit also changes the disconnect behavior slightly in that all connections to a callable are removed, while previously only a single connection was removed when there were multiple connections from the same signal to the same callable.
1 parent f4d8fd2 commit a6be76a

4 files changed

Lines changed: 182 additions & 219 deletions

File tree

src/PythonQt.cpp

Lines changed: 66 additions & 55 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,34 @@ 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-
}
902-
} else {
903-
// callable not found
884+
return _p->addSignalHandler(obj, signal, callable);
904885
}
905-
return flag;
886+
// callable not found
887+
return false;
906888
}
907889

908-
bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
890+
bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* callable)
909891
{
910-
bool flag = false;
911-
PythonQtSignalReceiver* r = getSignalReceiver(obj);
912-
if (r) {
913-
flag = r->addSignalHandler(signal, receiver);
914-
}
915-
return flag;
892+
return _p->addSignalHandler(obj, signal, callable);
916893
}
917894

918895
bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
919896
{
920-
bool flag = false;
921897
PythonQtObjectPtr callable = lookupCallable(module, objectname);
922898
if (callable) {
923-
PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
924-
if (r) {
925-
flag = r->removeSignalHandler(signal, callable);
926-
}
927-
} else {
928-
// callable not found
899+
return _p->removeSignalHandler(obj, signal, callable);
929900
}
930-
return flag;
901+
// callable not found
902+
return false;
931903
}
932904

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

943910
PythonQtObjectPtr PythonQt::lookupCallable(PyObject* module, const QString& name)
@@ -1619,20 +1586,14 @@ void PythonQtPrivate::registerQObjectClassNames(const QStringList& names)
16191586
}
16201587
}
16211588

1622-
void PythonQtPrivate::removeSignalEmitter(QObject* obj)
1623-
{
1624-
_signalReceivers.remove(obj);
1625-
}
1626-
16271589
void PythonQt::removeSignalHandlers()
16281590
{
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;
1591+
auto it = _p->_signalReceivers.begin();
1592+
while (it != _p->_signalReceivers.end()) {
1593+
it.value()->markAsRemoved();
1594+
delete it.value();
1595+
it++;
16341596
}
1635-
// just to be sure, clear the receiver map as well
16361597
_p->_signalReceivers.clear();
16371598
}
16381599

@@ -2012,6 +1973,56 @@ PythonQtClassInfo* PythonQtPrivate::lookupClassInfoAndCreateIfNotPresent(const c
20121973
return info;
20131974
}
20141975

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

src/PythonQt.h

Lines changed: 11 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,14 @@ 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+
bool addSignalHandler(QObject* sender, const char* signal, PyObject* callable);
731+
732+
//! remove a signal handler for given callable (or all callables on that signal if callable is NULL)
733+
bool removeSignalHandler(QObject* sender, const char* signal, PyObject* callable = nullptr);
734+
735+
//! called when a signal receiver is deleted to remove it from the hash map
736+
void removeSignalReceiver(PythonQtSignalReceiver* receiver);
734737

735738
//! wrap the given QObject into a Python object (or return existing wrapper!)
736739
PyObject* wrapQObject(QObject* obj);
@@ -865,7 +868,8 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject
865868
QHash<QByteArray, QByteArray> _knownLazyClasses;
866869

867870
//! stores signal receivers for QObjects
868-
QHash<QObject*, PythonQtSignalReceiver*> _signalReceivers;
871+
typedef QPair<QObject*, int> SignalKey;
872+
QMultiHash<SignalKey, PythonQtSignalReceiver*> _signalReceivers;
869873

870874
//! the PythonQt python module
871875
PythonQtObjectPtr _pythonQtModule;

0 commit comments

Comments
 (0)