2014-11-19 2 views
6

Внутри конструктора, есть связь:Элегантный способ отключить слот после первого вызова

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

И метод:

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 
} 

И я хотел бы, чтобы отключить этот слот после первого вызов.

Вопрос: есть ли способ вызвать слот только один раз? Без явного отключения? Как метод одиночного выстрела? Является ли это возможным?

+2

В чем проблема с использованием функции 'disconnect()'? – vahancho

+0

Почему вы не хотите использовать 'disconnect'? Есть ли конкретная причина? – Robert

+0

Просто знать, есть ли другой способ. Для некоторых разработчиков вызовите disonnect после первого вызова раздражает, и я хотел бы знать, есть ли способ сделать это. –

ответ

7

Основная идея заключается в создании обертки, специального «соединения», который автоматически отключает сигнал. Это полезно, если вы используете много соединений «назовите меня один раз»; иначе я бы посоветовал Qobject :: disconnect в начале слота.

Эта реализация работает, создавая 2 соединения: «обычный», и один, который отключает &.

Реализация (с использованием 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; 
} 

Предостережения/Вещи для улучшения:

  • зачистка происходит после вызова регулярное слот. Если обычный слот заставляет сигнал снова излучаться, обычный слот будет выполнен снова (потенциально вызывающий бесконечную рекурсию).
  • нет надлежащего способа отключения, кроме как путем испускания сигнала. (вы можете использовать QObject::disconnect, но это может вызвать небольшую утечку памяти)
  • опирается на порядок выполнения слотов. Кажется, сейчас хорошо.
  • именования

Испытано с помощью:

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

и

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 

Тестовый выход код:

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

Избавление от C++ 11/Qt5 (у меня нет Qt 4.8/GCC4.4.7, поэтому не тестировался с ними) Согласно документу Qt 4.-Не имеет подключения функционировать, поэтому я использую обертку:

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

(я Заставить ConnectJanitor «конструктору приватный, так как экземпляр самоликвидируется (delete this))

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

Если вам нужно вручную разорвать связи, я предлагаю, чтобы иметь weakConnect(), возвращающую указатель на ConnectJanitor в.

Смежные вопросы