An elegant way to disconnect a slot after the first call

There is a connection inside the constructor:

connect(&amskspace::on_board_computer_model::self(), SIGNAL(camera_status_changed(const amskspace::camera_status_t&)), this, SLOT(set_camera_status(const amskspace::camera_status_t&))); 

And the method:

 void camera_model:: set_camera_status(const amskspace::camera_status_t& status) { disconnect(&amskspace::on_board_computer_model::self(), SIGNAL(camera_status_changed(const amskspace::camera_status_t&)), this, SLOT(set_camera_status(const amskspace::camera_status_t&))); // do the job } 

And I would like to disable this slot after the first call.

Question: is there a way to call a slot only once? Without an explicit shutdown? How is the single shot method? Is it possible?

+6
source share
1 answer

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&); // not implemented ConnectJanitor& operator=(ConnectJanitor const&); QMetaObject::Connection conn_normal_; QMetaObject::Connection* conn_delete_; }; 

(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.

+7
source

Source: https://habr.com/ru/post/978403/


All Articles