2014-11-19 11 views
6

innerhalb des Konstruktors zu trennen, gibt es eine Verbindung:elegante Art und Weise Schlitz nach dem ersten Anruf

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&))); 

Und die Methode:

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 
} 

Und ich möchte diesen Slot nach dem ersten trennen Anruf.

Die Frage ist: Gibt es eine Möglichkeit, den Steckplatz nur einmal aufzurufen? Ohne die explizite Trennung? Wie eine Einzelschussmethode? Ist es möglich?

+2

Was ist das Problem mit der Funktion 'disconnect()'? – vahancho

+0

Warum willst du nicht 'disconnect' verwenden? Gibt es einen bestimmten Grund? – Robert

+0

Nur zu wissen, ob es einen anderen Weg gibt. Für einige Entwickler, rufen Sie die Disonnect nach dem ersten Aufruf ist ärgerlich, und ich würde gerne wissen, ob es einen Weg gibt, es zu tun. –

Antwort

7

Die Kernidee besteht darin, einen Wrapper zu erstellen, einen speziellen "connect", der das Signal automatisch trennt. Dies ist nützlich, wenn Sie viele "Call Me Once" Verbindungen verwenden; sonst würde ich einen Qobject :: disconnect am Anfang des Slots empfehlen.

Diese Implementierung funktioniert durch Erstellen von 2 Verbindungen: eine "normale", und eine, die & entfernen Sie alles nur danach trennen.

Eine Implementierung (C++ 11/Qt 5, verwendet wird):

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; 
} 

Caveats/Aktivitäten zu verbessern:

  • die Bereinigung erfolgt nach den regulären Aufruf Slot. Wenn der reguläre Slot dazu führt, dass das Signal erneut ausgegeben wird, wird der reguläre Slot erneut ausgeführt (was möglicherweise zu einer unendlichen Rekursion führt).
  • keine ordnungsgemäße Art zu trennen, außer durch das Ausstrahlen des Signals. (Sie können QObject::disconnect verwenden, aber das würde einen kleinen Speicherverlust verursachen)
  • hängt von der Reihenfolge der Ausführung von Steckplätzen ab. Scheint gut für den Moment.
  • die Benennung

Getestet mit:

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_; 
}; 

und

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 

Prüfregeln Ausgabe:

a1 : 
    B : 10 a: 1 
    B : 20 a: 1 
a2 : 
    B : 20 a: 2 
a1 : 
a2 : 

Loswerden von C++ 11/Qt5 (Ich habe nicht Qt 4.8/GCC4.4.7, also nicht mit diesen getestet) Nach dem Dokument, Qt 4.8 hat keinen connect funktioniert, also verwende ich einen Wrapper:

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_; 
}; 

(Ich bin der Konstruktor privat‘ConnectJanitor machen, weil die Instanz selbst zerstört (delete this))

und für 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; 
} 

Wenn Sie manuell die Verbindungen zu brechen, schlage ich weakConnect() Rückkehr der ConnectJanitor des Zeigers zu haben.