声明在接口类抽象信号

问题描述 投票:29回答:4

如何在一个抽象类/接口声明Qt的信号时,执行类已经从QObject中/ QWidget的derrived?

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();
}
c++ qt qt-signals
4个回答
50
投票

正如我在最后的日子里发现了......这样做的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()), ...);

......而这样一来,你就不必非得揭露实现类的用户和客户。是啊!


14
投票

在Qt中,“信号”是synonim的“保护”。但它有助于MOC生成必要的代码。所以,如果你需要一些信号接口 - 你应该声明为虚拟抽象的保护方法。所有neccessary代码将通过MOC生成 - 你可能会看到细节,即“发出somesignal”将与具有相同名称的保护方法的虚拟呼叫所取代。注意,这与方法本体ASLO被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();
}

4
投票

有两个问题与声明的信号用接口抽象方法:

  1. 的信号是在一个特定的实施方式,只有当从Qt的视点的信号 - 即,当通过MOC生成的执行,并且包括在该对象的元数据。
  2. 它通常是坏的设计直接从对象的外部发射信号。

作为一个必然结果,因为该接口是抽象的,你并不需要在所有申报的信号 - 它提供不超过用于记录的意图,因为其他目的:

  1. 如果信号在从接口派生类实现时,你可以使用元对象系统来验证它的存在。
  2. 你不应该反正直接调用这些信号的方法。
  3. 一旦你施放动态的非对象接口QObject,不要紧了该实现从接口派生。

唯一有效的原因离开做这种体操将是:

  1. 同轴电缆的doxygen或其他文档生成器为您的代码提供文档。
  2. 强制混凝土类具有相同名称的方法的实现。这当然并不保证它实际上是一个信号。

1
投票

我们都希望为好摆脱MOC的,但在此之前我想补充一点,而不包括QObject.h并没有在接口类使用Q_OBJECT和Q_INTERFACE作品替代。

首先定义的接口抽象连接功能:

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

现在,在派生类中,覆盖功能。也宣告信号,加Q_OBJECT等。

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

那么类中的.cpp做连接:

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

需要说明的是,你必须写在每一个派生类中的连接功能,你必须使用旧式连接(或者可能使用模板功能),但仅此而已。

© www.soinside.com 2019 - 2024. All rights reserved.