2013-07-30 2 views
19

Как объявить Qt-сигнал в абстрактном классе/интерфейсе, когда класс реализации уже вырван из QObject/QWidget?Объявление абстрактного сигнала в классе интерфейса

class IEmitSomething 
{ 
    public: 
    // this should be the signal known to others 
    virtual void someThingHappened() = 0; 
} 

class ImplementEmitterOfSomething : public QWidget, public IEmitSomething 
{ 
    // signal implementation should be generated here 
    signals: void someThingHappended(); 
} 
+0

Вы можете написать только 'сигналы: void someThingHappened();'. Это не требуется. Реализация сигнала. – Ruu

+0

Я знаю, что сигнал генерируется в реализации, но как наблюдающий знает, что это сигнал (qt), если известен только интерфейс? – Beachwalker

+0

@Beachwalker Я заменил защищенное ключевое слово сигналами в своем ответе. Это наведет ваш вопрос. –

ответ

37

Как я узнал, что в последние дни ... в Qt способ сделать это так:

class IEmitSomething 
{ 
    public: 
    virtual ~IEmitSomething(){} // do not forget this 

    signals: // <- ignored by moc and only serves as documentation aid 
      // The code will work exactly the same if signals: is absent. 
    virtual void someThingHappened() = 0; 
} 

Q_DECLARE_INTERFACE(IEmitSomething, "IEmitSomething") // define this out of namespace scope 

class ImplementEmitterOfSomething : public QWidget, public IEmitSomething 
{ 
    Q_OBJECT 
    Q_INTERFACES(IEmitSomething) 

    signals: 
     void someThingHappended(); 
} 

Теперь вы можете подключиться к этим сигналам интерфейса.

Если у вас нет доступа к реализации при подключении к сигналу ваш оператор подключения потребуется динамическое приведение к QObject:

IEmitSomething* es = ... // your implementation class 

connect(dynamic_cast<QObject*>(es), SIGNAL(someThingHappended()), ...); 

... и таким образом, вы не вынуждены подвергать класс реализации для подписчиков и клиентов. Да!!!

+0

Где вы прочли информацию о нем? –

+0

Проходит время ... Я не помню ссылку, но если у вас есть ключевые слова Q_INTERFACES и Q_DECLARE_INTERFACE, вы можете использовать это для Google. Проблема во время моего сообщения заключалась в том, что я не знал их. Вы можете прочитать что-то здесь http://doc.qt.io/qt-5/qtplugin.html#Q_DECLARE_INTERFACE и просмотреть информацию об этом интерфейсе. – Beachwalker

+0

Любая идея, почему виртуальная пустота someThingHappended(); приводит к предупреждению компилятора, см. здесь: http://stackoverflow.com/questions/28614607/virtual-override-for-signal-signals-cannot-be-declared-virtual –

14

В Qt «сигналы» являются синонимами для «защищенных». Но это помогает MOC генерировать необходимый код. Итак, если вам нужен интерфейс с некоторыми сигналами - вы должны объявить их как виртуальные абстрактные защищенные методы. Весь необходимый код будет создан MOC - вы можете увидеть подробности, что «emit somesignal» будет заменен виртуальным вызовом защищенного метода с тем же именем. Обратите внимание, что тело с методом также генерируется Qt.

UPDATE: Код Пример:

MyInterfaces.h

#pragma once 

struct MyInterface1 
{ 
signals: 
    virtual void event1() = 0; 
}; 

struct MyInterface2 
{ 
signals: 
    virtual void event2() = 0; 
}; 

MyImpl.h

#ifndef MYIMPL_H 
#define MYIMPL_H 

#include <QObject> 
#include "MyInterfaces.h" 

class MyImpl 
    : public QObject 
    , public MyInterface1 
    , public MyInterface2 
{ 
    Q_OBJECT 

public: 
    MyImpl(QObject *parent); 
    ~MyImpl(); 

    void doWork(); 

signals: 
    void event1(); 
    void event2(); 
}; 

class MyListner 
    : public QObject 
{ 
    Q_OBJECT 

public: 
    MyListner(QObject *parent); 
    ~MyListner(); 

public slots: 
    void on1(); 
    void on2(); 
}; 

#endif // MYIMPL_H 

MyImpl.cpp

#include "MyImpl.h" 
#include <QDebug> 

MyImpl::MyImpl(QObject *parent) 
    : QObject(parent) 
{} 

MyImpl::~MyImpl() 
{} 

void MyImpl::doWork() 
{ 
    emit event1(); 
    emit event2(); 
} 

MyListner::MyListner(QObject *parent) 
{} 

MyListner::~MyListner() 
{} 

void MyListner::on1() 
{ 
    qDebug() << "on1"; 
} 

void MyListner::on2() 
{ 
    qDebug() << "on2"; 
} 

main.cpp

#include <QCoreApplication> 
#include "MyImpl.h" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    MyImpl *invoker = new MyImpl(NULL); 
    MyListner *listner = new MyListner(NULL); 

    MyInterface1 *i1 = invoker; 
    MyInterface2 *i2 = invoker; 

    // i1, i2 - not QObjects, but we are sure, that they will be. 
    QObject::connect(dynamic_cast< QObject * >(i1), SIGNAL(event1()), listner, SLOT(on1())); 
    QObject::connect(dynamic_cast< QObject * >(i2), SIGNAL(event2()), listner, SLOT(on2())); 

    invoker->doWork(); 

    return a.exec(); 
} 
+0

thx, добавил виртуальный, я пропустил его только в этом примере – Beachwalker

+0

, если вы уверены, что i1 является QObject, тогда dynamic_cast не требуется. static_cast отлично работает в этом случае. –

+2

@ FernandoPelliccioni нет, вы совершенно неправы. Потому что интерфейс не наследует 'QObject'. Может использоваться только 'reinterpret_cast', но он не работает, потому что существует многократное наследование. –

3

Есть две проблемы с объявляя сигналы в качестве абстрактных методов в интерфейсах:

  1. сигнал представляет собой сигнал, с точки зрения Qt только , когда реализованы определенным образом - а именно, когда реализация генерируется moc и включается в метаданные для объекта.

  2. Обычно плохой дизайн излучает сигналы непосредственно снаружи объекта.

Как следствие, поскольку интерфейс является абстрактным, вы действительно не нужно объявлять свои сигналы на всех - это не служит никакой другой цели, кроме документирования намерения, так как:

  1. Если сигнал реализован в классе, который получен из интерфейса, вы можете использовать систему метаобъектов, чтобы проверить его присутствие.

  2. В любом случае вы не должны напрямую обращаться к этим методам сигнала.

  3. После того, как вы динамически применили интерфейс без объекта к QObject, теперь не имеет значения, что реализация была получена из интерфейса.

Допустимы только причины, оставленные для выполнения такой гимнастики будут:

  1. Коаксиального Doxygen или другой документация генератора в предоставлении документации для вашего кода.

  2. Заставить конкретный класс иметь реализацию метода с тем же именем. Это, конечно, не гарантирует, что это сигнал.

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