当实现类已经从QObject / QWidget中获取时,如何在抽象类/接口中声明Qt信号?
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();
}
答案 0 :(得分:46)
正如我在最后几天发现的那样...... 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();
}
现在您可以连接到这些接口信号。
如果您在连接信号时无法访问该实现,则您的connect语句将需要动态转换为QObject
:
IEmitSomething* es = ... // your implementation class
connect(dynamic_cast<QObject*>(es), SIGNAL(someThingHappended()), ...);
...这样你就不会被迫将实现类暴露给订阅者和客户端。呀!
答案 1 :(得分:14)
在Qt中,“信号”是“受保护”的同义词。但它有助于MOC生成必要的代码。因此,如果您需要具有某些信号的接口 - 您应该将它们声明为虚拟抽象保护方法。所有必要的代码都将由MOC生成 - 您可能会看到详细信息,“发出一些信号”将被替换为具有相同名称的受保护方法的虚拟调用。注意,用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();
}
答案 2 :(得分:4)
在接口中将信号声明为抽象方法有两个问题:
当以特定方式实现时,信号是来自Qt视点的信号 - 即,当实现由moc生成时,并且包含在对象的元数据中。 / p>
直接从物体外部发出信号通常是不好的设计。
作为必然结果,由于接口是抽象的,你根本不需要声明它的信号 - 除了记录意图之外它没有任何意义,因为:
如果信号是在从接口派生的类中实现的,则可以使用元对象系统来验证其存在。
无论如何,你不应该直接调用这些信号方法。
将非对象接口动态转换为QObject
后,实现从接口派生出来并不重要。
进行这种体操的唯一有效理由是:
使用coax doxygen或其他文档生成器为您的代码提供文档。
强制具体类具有同名方法的实现。这当然不能保证它实际上是一个信号。
答案 3 :(得分:0)
我们所有人都希望永久摆脱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);
}
需要注意的是,您必须在每个派生类中编写connect函数,并且必须使用old-style-connect(或者可以使用模板函数),但这就是它。