Подтвердить что ты не робот

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

Как объявить 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();
}
4b9b3361

Ответ 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 или другой генератор документации для предоставления документации для вашего кода.

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