The main idea is to create a wrapper, a special "connect", which automatically disconnects the signal. This is useful if you use many "call me once" connections; otherwise, I would advise disconnecting Qobject :: disconnect at the beginning of the slot.
This implementation works by creating 2 connections: βnormalβ and one that immediately disconnects and clears.
Implementation (using C ++ 11 / Qt 5):
template <typename Func1, typename Func2> static inline QMetaObject::Connection weakConnect( typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot) { QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot); QMetaObject::Connection* conn_delete = new QMetaObject::Connection(); *conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete](){ QObject::disconnect(conn_normal); QObject::disconnect(*conn_delete); delete conn_delete; }); return conn_normal; }
Cautions / Things to Improve:
- cleaning occurs after , causing a regular slot. If a regular slot causes the signal to radiate again, a regular slot will be executed again (potentially causing infinite recursion).
- there is no proper way to shut off, except by emitting a signal. (you can use
QObject::disconnect
, but this can lead to a small memory leak). - relies on the execution order of the slots. Seems good now.
- Naming
Tested using:
class A : public QObject { Q_OBJECT signals: void sig(int a); }; class B : public QObject { Q_OBJECT public: B(int b) : QObject(), b_(b) {} int b() const { return b_; } public slots: void slo(int a) { qDebug() << "\tB :" << b_ << "a:" << a; } private: int b_; };
and
A a1; A a2; B b10(10); B b20(20); weakConnect(&a1, &A::sig, &b10, &B::slo); weakConnect(&a1, &A::sig, &b20, &B::slo); weakConnect(&a2, &A::sig, &b20, &B::slo); qDebug() << "a1 :"; emit a1.sig(1);// Should trigger b10 and b20 slo qDebug() << "a2 :"; emit a2.sig(2);// Should trigger b20 slo qDebug() << "a1 :"; emit a1.sig(3);// Should do nothing qDebug() << "a2 :"; emit a2.sig(4);// Should do nothing
Test code output:
a1 : B : 10 a: 1 B : 20 a: 1 a2 : B : 20 a: 2 a1 : a2 :
Getting rid of C ++ 11 / Qt5 (I do not have Qt 4.8 / GCC4.4.7, so I have not tested with them) According to the document, Qt 4.8 does not have a function connection, so I use the shell:
class ConnectJanitor : public QObject { Q_OBJECT public slots: void cleanup() { QObject::disconnect(conn_normal_); QObject::disconnect(*conn_delete_); delete conn_delete_; delete this; } public: static ConnectJanitor* make(QMetaObject::Connection conn_normal, QMetaObject::Connection* conn_delete) { return new ConnectJanitor(conn_normal, conn_delete); } private: ConnectJanitor(QMetaObject::Connection conn_normal, QMetaObject::Connection* conn_delete) : QObject(0) , conn_normal_(conn_normal), conn_delete_(conn_delete) {} ConnectJanitor(const ConnectJanitor&);
(I make the ConnectJanitor
constructor private, because the instance is self-destructs ( delete this
))
and for weakConnect
:
static inline QMetaObject::Connection weakConnect(const QObject * sender, const char * signal, const QObject * receiver, const char * slot) { QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot); QMetaObject::Connection* conn_delete = new QMetaObject::Connection(); *conn_delete = QObject::connect(sender, signal, ConnectJanitor::make(conn_normal, conn_delete), SLOT(cleanup())); return conn_normal; }
If you need to manually break the connections, I suggest that weakConnect () return a ConnectJanitor pointer.