Объявить абстрактный сигнал в классе интерфейса

Как объявить сигнал 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();
}

28

Решение

Как я узнал в последние дни … 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()), ...);

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

45

Другие решения

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

ОБНОВИТЬ:
Образец кода:

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();
}
14

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

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

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

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

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

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

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

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

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

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

4
По вопросам рекламы [email protected]