Qt Plugin创建及调用3(接口间通信)
简述
插件接口(Interface)的作用,就是提供一个与其他系统交互的方法。其他系统无需(也无法)了解内部的具体细节,只能通过对外提供的接口来与进行通信。在上一篇的PluginInterface
接口中,我们定义了两个纯虚函数
virtual void setInitData(QStringList &strlist) = 0;
virtual void getResultData(QStringList &strlist) = 0;
这两个纯虚函数可以在插件实现类中重写,同样对于槽函数也可以同样定义,那么信号呢?
在 Qt 中,定义一个纯虚信号有效吗?
的确,这个话题非常有意思。。。通常,我们会定义一些纯虚的槽函数,但关于纯虚信号这个话题讨论的比较少!那么,信号可不可以是纯虚的呢?
一些尝试
关于信号和纯虚,我们知道:
- 信号永远不会有实现(也就是说,在
.h
中定义了信号,在.cpp
中不需要实现) - 声明一个纯虚函数的主要目的,是强制继承的类提供一个实现。
信号没有实现,如果将其声明为纯虚的,需要继承的类来提供一个实现,这与“信号没有实现”直接冲突。就好比让一个人同时出现在两个地方,这是不可能的。因此,似乎声明一个纯虚信号是一个错误。
在编写完一个接口时,为了能使用 Qt 的信号槽特性,很多人可能会写出类似下面的代码:
#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H
#include <QStringList>
#include <QObject>
class PluginInterface : public QObject
{
Q_OBJECT
public:
virtual ~PluginInterface() {}
public:
virtual void setInitData(QStringList &strlist) = 0;
virtual void getResultData(QStringList &strlist) = 0;
signals:
virtual void information() = 0;
};
#define PluginInterface_iid "QtPluginsTest.QtPluginsManager.PluginInterface"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)
#endif // PLUGININTERFACE_H
很遗憾,Qt 发出了警告:
warning: Signals cannot be declared virtual
那么,如何解决这个问题呢?
解决方案
下面,列出三种解决方案:
- 派生自
QObject
,信号不使用virtual
- 将“信号”定义为一个纯虚函数
- 在接口中定义信号槽的连接方式
派生自 QObject
,信号不使用 virtual
不让用 virtual
,好吧,那就不用了,这算是最简单的解决方式!
#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H
#include <QStringList>
#include <QObject>
class PluginInterface : public QObject
{
Q_OBJECT
public:
virtual ~PluginInterface() {}
public:
virtual void setInitData(QStringList &strlist) = 0;
virtual void getResultData(QStringList &strlist) = 0;
signals:
virtual void information() = 0;
};
#define PluginInterface_iid "QtPluginsTest.QtPluginsManager.PluginInterface"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)
#endif // PLUGININTERFACE_H
但是这种方式并不算理想,因为一般来说,接口是一个简单的 C++ 类,不需要派生自 QObject
。
将“信号”定义为一个纯虚函数
将“信号”定义为一个纯虚函数:
#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H
#include <QStringList>
class PluginInterface
{
public:
virtual ~PluginInterface() {}
public:
virtual void setInitData(QStringList &strlist) = 0;
virtual void getResultData(QStringList &strlist) = 0;
//signals:不是一个信号(但可以当做信号来用)
virtual void information() = 0;
};
#define PluginInterface_iid "QtPluginsTest.QtPluginsManager.PluginInterface"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)
#endif // PLUGININTERFACE_H
注意: 对于 IClock
来说,alarm()
只是一个纯虚函数,并不是一个“信号”(但可以当做信号来用),因为 IClock
不是 QObject
。
由于需要信号支持,所以具体实现需要派生自 QObject
。此外,还需要将 information()
定义为 signals
:
#ifndef PLUGIN_H
#define PLUGIN_H
#include "plugininterface.h"
#include <QObject>
#include <QtDebug>
class Plugin : public QObject, public PluginInterface
{
Q_OBJECT
public:
Plugin() {}
void setInitData(QStringList &strlist) override{
//do something
}
void getResultData(QStringList &strlist)override{
//do something
}
signals:
// 实现由 moc 来完成
void information() override;
};
#endif // PLUGIN_H
这时,alarm()
的具体的实现在内部是由 moc
来完成的。
void monitorInformation(Plugin *p_Plugin) {
QObject *pluginObject = dynamic_cast<QObject *>(p_Plugin);
if (pluginObject) {
connect(pluginObject, SIGNAL(information()), this, SLOT(onInformation()));
} else {
qWarning() << "cannot monitor Information";
}
}
注意: 连接信号时,需要将其转换为 QObject
。
在接口中定义信号槽的连接方式
除了上述方式之外,还可以在接口中定义信号槽的连接方式。
首先,定义一个连接信号槽的接口 - connect_information()
:
#ifndef PLUGININTERFACE_H
#define PLUGININTERFACE_H
#include <QStringList>
class PluginInterface
{
public:
virtual ~PluginInterface() {}
public:
virtual void setInitData(QStringList &strlist) = 0;
virtual void getResultData(QStringList &strlist) = 0;
//connect to signals
virtual bool connect_information(QObject *receiver, const char* pszSlot, bool isConnect = true) = 0;
};
#define PluginInterface_iid "QtPluginsTest.QtPluginsManager.PluginInterface"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)
#endif // PLUGININTERFACE_H
然后,在派生类中实现信号槽的具体连接:
bool GenericPlugin2::connect_information(QObject *receiver, const char *pszSlot, bool isConnect)
{
if(isConnect)
return connect(this, SIGNAL(information(QString&)), receiver, pszSlot);
else
return disconnect(this, SIGNAL(information(QString&)), receiver, pszSlot);
}
除了信号槽之外,Qt 还可以通过事件机制(sendEvent()
和 postEvent()
)来实现接口之间的通信。正如上所示,只要通过接口来获得 QObject
即可。