Объявлять абстрактный сигнал в классе интерфейса
Как объявить 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();
}
Ответы
Ответ 1
Как я узнал в последние дни... способ 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()), ...);
... и таким образом вы не должны подвергать класс реализации подписчикам и клиентам. Да!!!
Ответ 2
В 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();
}
Ответ 3
Есть две проблемы с объявлением сигналов в виде абстрактных методов в интерфейсах:
-
Сигнал является сигналом с точки зрения Qt только тогда, когда он реализуется определенным образом - а именно, когда реализация генерируется moc и включается в метаданные для объекта.
-
Обычно плохой дизайн излучает сигналы непосредственно извне объекта.
Как следствие, поскольку интерфейс является абстрактным, вам совсем не нужно декларировать его сигналы вообще - он не служит цели, кроме как для документирования намерения, поскольку:
-
Если сигнал реализован в классе, который получен из интерфейса, вы можете использовать систему метаобъектов, чтобы проверить его наличие.
-
В любом случае вы не должны напрямую обращаться к этим методам сигнала.
-
Как только вы динамически передаете не-объектный интерфейс в QObject
, теперь не имеет значения, что реализация была получена из интерфейса.
Единственными действительными причинами, положенными для занятий такой гимнастикой, было бы следующее:
-
Коаксиальный doxygen или другой генератор документации для предоставления документации для вашего кода.
-
Приведите конкретный класс к реализации метода с тем же именем. Это, конечно, не гарантирует, что это на самом деле сигнал.