문제

How to declare a Qt signal in an abstract class / interface when the implementing class is already derrived from 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();
}
도움이 되었습니까?

해결책

As I found out in the last days... the Qt way of doing this is like this:

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

Now you can connect to those interface signals.

If you don't have access to the implementation when connecting to the signal your connect statement will require a dynamic cast to QObject:

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

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

... and this way you're not forced to expose the implementation class to subscribers and clients. Yeah!!!

다른 팁

In Qt, "signals" is synonim for "protected". But it helps MOC to generate necessary code. So, if you require interface with some signals - you should declare them as virtual abstract protected methods. All neccessary code will be generated by MOC - you may see details, that "emit somesignal" will be replaced with virtual call of protected method with same name. Note, that the body of with method aslo generated by Qt.

UPDATE: Sample code:

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

There are two problems with declaring signals as abstract methods in interfaces:

  1. A signal is a signal from Qt's viewpoint only when implemented in a particular way - namely, when the implementation is generated by moc, and is included in the metadata for the object.

  2. It is usually bad design to emit signals directly from the outside of an object.

As a corollary, since the interface is abstract, you don't really need to declare its signals at all - it serves no purpose other than for documenting the intent, since:

  1. If a signal is implemented in a class that derives from the interface, you can use the metaobject system to verify its presence.

  2. You're not supposed to directly call these signal methods anyway.

  3. Once you dynamically cast the non-object interface to QObject, it doesn't matter anymore that the implementation was deriving from the interface.

The only valid reasons left for doing such gymnastics would be to:

  1. Coax doxygen or another documentation generator into providing documentation for your code.

  2. Force the concrete class to have an implementation of a method with the same name. This doesn't of course guarantee that it is in fact a signal.

We all want to get rid of MOC for good, but until that happens I want to add an alternative that works without including QObject.h and without using Q_OBJECT and Q_INTERFACE in the interface class.

First define an abstract connect function in the interface:

class I_Foo
{
public:
    virtual void connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) = 0;
};

Now in the derived class, override the function. Also declare the signal, add Q_OBJECT etc.

class Bar : public QObject, public I_Foo
{
    Q_OBJECT

public:
    void connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);

signals:
    void A();
};

Then inside that classes .cpp do the connect:

Bar::connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType void type)
{
    connect(this, SIGNAL(A()), receiver, method, type);
}

The caveat is, that you have to write the connect function in every derived class and you have to use the old-style-connect (or maybe use a template function), but that's about it.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top