目录
一、插件
1.1 插件概念
插件是一种(遵循一定规范的应用程序接口编写出来的)程序,定位于开发实现应用软件平台不具备的功能的程序。插件必须依赖于应用程序才能发挥自身功能,仅靠插件是无法正常运行的;相反地,应用程序并不需要依赖插件就可以运行,这样一来,插件就可以加载到应用程序上并且动态更新而不会对应用程序造成任何改变(热更新)。插件就像硬件插卡一样,可以被随时删除、插入和修改,所以结构很灵活,容易修改,方便软件的升级和维护。
1.2 插件和动态库的区别
两者都是用于封装部分功能的实现,并降低模块代码耦合度。但其实插件也是被部署为动态库的形式,但是和传统的动态库还是有一些差别的。
插件:插件主要面向接口编程,无需访问.lib文件,热插拔、利于团队开发。即使在程序运行时.dll不存在,也可以正常启动,只是相应插件功能无法正常使用而已;
动态库:动态库需要访问.lib文件,而且在程序运行时必须保证.lib存在,否则无法正常启动;
1.3 Qt提供了两种API用于创建插件
-
一种是高阶API,用于扩展Qt本身的功能:例如自定义数据库驱动、图像格式、文本编码、自定义样式等等;
Qt 有很多可以扩展的接口。例如,您可以为样式、数据库驱动程序、文本编解码器和图像格式添加插件。Qt 的可扩展性有很多好处。首先,它让 Qt 更加耐用,因为它可以适应新技术。还可以让 Qt 更轻便,因为不需要的插件不需要部署。这也确保了你可以继续使用 Qt 的应用编程接口,即使你需要针对特殊的技术。
-
一种是低阶API,用于扩展Qt应用程序;
1.4 Qt低阶API扩展Qt应用程序插件
对于 Qt 应用程序来说,插件只是类的另一个实例。可用的方法由接口类决定。一个接口类通常只包含纯虚方法,所以接口类中没有实现任何函数。然后,插件继承了QObject类和接口类,并实现了所有具有特定功能的方法。当应用程序加载一个带有QPluginLoader类的潜在插件时,它会得到一个QObject指针。通过尝试使用qobject_cast将给定的对象转换为接口类,应用程序可以判断插件是否实现了预期的接口,是否可以被视为实际的插件。
为了让QPluginLoader正常工作,接口类必须通过使用Q_DECLARE_INTERFACE宏声明为接口,插件必须通过使用Q_INTERFACES宏声明它们实现了一个接口。这两个宏使您能够安全地将给定的插件匹配到正确的界面。这是 Qt 信任插件必须满足的一系列标准中的一步。
Qt插件开发:
主要分为主程序部分和插件程序部分
主程序部分:定义插件的接口并提供插件的管理器用于管理插件的加载与使用。
插件程序部分:按照主程序中定义的插件接口来定义插件,实现插件功能,生成共主程序部分调用方的插件。
主程序开发流程:
1.定义一组用于与插件通信的接口(C++纯虚函数的类)
2.使用Q_DECLARE_INTERFACE()宏来告诉Qt的元对象系统有关接口的情况。
3.在应用程序中使用QPluginLoader加载插件。
4.使用qobject_cast()来测试插件是否实现指定接口。
Qt插件程序部分开发流程
1.声明一个继承自QObject和插件接口的插件类
2.使用Q_INTERFACES()宏告诉Qt元对象系统有关接口的情况;
3.使用Q_PLUGIN_METADATA()宏导出插件
二、Qt插件代码实战
2.1 主程序部分
定义接口类:
#pragma once
#include<QObject>
class CalculatInterface
{
public:
//插件:面向接口 定义一组用于与插件通信的接口(只有纯虚函数)
virtual ~CalculatInterface() {};
virtual int add(int a, int b) = 0;
virtual int mins(int a, int b) = 0;
virtual int mul(int a, int b) = 0;
virtual int devid(int a,int b) = 0;
};
//插件唯一的标识符
#define CalculatInterface_iid "Plugin_CalculatInterface_iid"
//告诉元对象系统接口 将类声明为接口
Q_DECLARE_INTERFACE(CalculatInterface, CalculatInterface_iid)
主程序调用插件:
void LogTest::TestPlugin()
{
InitLog("MyLog");
QString strPath = qApp->applicationDirPath() + QDir::separator()+ "QcalculatePlugin.dll";
// 判断是否是库
if (!QLibrary::isLibrary(strPath))
{
getLogger("TestPlugin")->error("{} is not library", strPath.toStdString().c_str());
return;
}
QPluginLoader pluginLoader(strPath);
if (pluginLoader.load())//load插件
{
m_pInterface = qobject_cast<CalculatInterface*>(pluginLoader.instance());
getLogger("TestPlugin")->info("1 + 1 = {}", m_pInterface->add(1,1));
getLogger("TestPlugin")->info("1 - 1 = {}", m_pInterface->mins(1, 1));
getLogger("TestPlugin")->info("2 * 3 = {}", m_pInterface->mul(2, 3));
getLogger("TestPlugin")->info("3 / 2 = {}", m_pInterface->add(3, 2));
}
}
2.2 插件部分
插件项目建立

插件接口实现
#pragma once
#include "qcalculateplugin_global.h"
#include "../BaseInterface/CalculatInterface.h"
class QCALCULATEPLUGIN_EXPORT QcalculatePlugin:public QObject,public CalculatInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID CalculatInterface_iid) //添加关于插件的元数据 IID 插件IID唯一
Q_INTERFACES(CalculatInterface) //用于声明插件中实现的接口
public:
QcalculatePlugin();
~QcalculatePlugin() = default;
//实现接口
int add(int a, int b);
int mins(int a, int b);
int mul(int a, int b) ;
int devid(int a, int b);
};
#include "QcalculatePlugin.h"
QcalculatePlugin::QcalculatePlugin()
{
}
int QcalculatePlugin::add(int a, int b)
{
return a + b;
}
int QcalculatePlugin::mins(int a, int b)
{
return a - b;
}
int QcalculatePlugin::mul(int a, int b)
{
return a * b;
}
int QcalculatePlugin::devid(int a, int b)
{
return a / b;
}
三、实际项目重构中插件的使用
3.1项目背景
在停车场管理项目中不同省市会有各种智慧停车平台的接入,接入协议各不相同,但是基本都是过车相关数据上传、扣费、查费、对账、无牌车扫码、包期车、黑名单、场库车位信息、设备状态信息、道闸LED控制等等;之前老项目每接入一个平台均需要修改厂库管理程序进行平台对接,且随着厂库程序的升级对接业务也要随之迁移测试,高度耦合及其麻烦;
在项目重构中,将这些业务均提前在厂库管理程序中进行基线接口实现;同时提供上行业务的推送接口和下行业务的调用实现;平台以插件形式接入,实现厂库上行接口数据的订阅、接收和按需改造转发平台;对于平台下行业务插件调用厂库程序的相关实现进行数据适配转发;从而平台插件作为一个数据转发中间件,可以根据自身协议实现传输层、订阅上行数据、调用厂库下行实现等,达到平台业务与厂库管理程序解耦和的目的。极大的方便了不通平台的接入以及程序的升级。
3.2插件在项目重构中具体使用
3.2.1主程序接口声明
#pragma once
#include<QObject>
#include<functional>
enum PluginApiType
{
PassVehData,//过车数据能力
VehBillData,//过车账单
VehRequestIn,//入场请求
VehRequestOut//出场请求
//...
};
Q_DECLARE_METATYPE(PluginApiType)
enum ProcessApiType
{
GetRegisterVeh,//获取白名单
AddRegisterVeh,//添加白名单
DeleteRegister,//删除白名单
GetAlarmVeh, //获取黑名单
DeleteAlarmVeh,//删除黑名单
AddAlarmVeh, //添加黑名单
CalculateFee, //计算过车费用
PayBill //支付账单
//...
};
Q_DECLARE_METATYPE(ProcessApiType)
using ProcessFunctorBiz = std::function<
void(const ProcessApiType& type, const QString& request, QString& reply, const QVariant& userData)>;
class PlateformInterface
{
public:
//插件:面向接口 定义一组用于与插件通信的接口(只有纯虚函数)
virtual ~PlateformInterface() {};
virtual QString plateformName() = 0; //获取插件展示平台名称
virtual void cleanup() = 0; //释放插件资源 便于程序退出时使用
virtual QWidget *configWidget() = 0;//提供插件自定义界面 主程序添加插件界面
//主程序向插件发送请求 获取插件处理结果
virtual int processData2Plugin(const PluginApiType& api, const QString&request,const QString&reply) = 0;
//注册程序ProcessFunctorBiz函数 便于插件调用获取程序数据
virtual QString registerProcessApi(const ProcessFunctorBiz&) = 0;
//订阅主程序上传数据
bool subsrribe(PluginApiType type);
};
//插件唯一的标识符
#define PlateformInterface_iid "Plugin_PlateformInterface_iid"
//告诉元对象系统接口 将类声明为接口
Q_DECLARE_INTERFACE(PlateformInterface, PlateformInterface_iid)
3.2.2 主程序插件管理
//头文件插件管理类定义头文件
#pragma once
#include <QObject>
#include "platform_plugin.h"
class PlatformManager : public QObject
{
Q_OBJECT
public:
void queryInVeh(const PassVeh& );
bool queryOutVeh(const PassVeh& );
//...
public slots:
bool getPluginAbility(const ApiType& type);
private:
PlatformManager();
~PlatformManager();
void functorBiz(const ApiType& type, const QString& request, QString& reply, const QVariant& userData);
//车厂服务API
void GetRegisterVeh(const QString& request, QString& reply);//按条件获取固定车信息
void addRegisterVeh(const QString& request, QString& reply);//添加固定车信息
void delRegisterVeh(const QString& request, QString& reply);//按条件删除固定车信息
void addOrModAlarmVeh(const QString& request, QString& reply);//添加黑名单车信息
void delAlarmVeh(const QString& request, QString& reply);//删除黑名单车信息
//...
};
PlatformManager::PlatformManager()
{
/*
...
*/
if (PluginManager::instance())
{
for (int i = 0; i < loaderList.size(); i++)
{
auto loader = loaderList.at(i);
platformPluginInterface *plugin = qobject_cast<platformPluginInterface *>(loader->instance());
if (plugin)
{
//注册插件调用主程序的函数
plugin->registerProcessApi(std::bind(&PlatformManager::functorBiz, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
m_pluginList.append(plugin);
platformNames.append(plugin->platfromName());
}
}
}
}
void PlatformManager::queryInVeh(const PassVeh&vehInfo )
{
QString QueryInJsonStr = vehInfo.toJson();
QString replyStr;
for(auto iter:m_pluginList)
{
if(iter.subscribe(VehRequestIn))
{
iter.processData2Plugin(PluginApiType::VehRequestOut,QueryInJsonStr,replyStr);
//...process replyStr
}
}
}
void PlatformManager::queryOutVeh(const PassVeh& )
{
QString QueryOutJsonStr = vehInfo.toJson();
QString replyStr;
for(auto iter:m_pluginList)
{
if(iter.subscribe(VehRequestOut))
{
iter.processData2Plugin(PluginApiType::VehRequestOut,QueryOutJsonStr,replyStr);
//...process replyStr
}
}
}
void PlatformManager::functorBiz(const ApiType & type, const QString & request, QString & reply, const QVariant & userData)
{
switch (type)
{
case GetRegisterVeh://获取固定车信息
{
getRegisterVehInfo(request, reply);
}
break;
case AddRegisterVeh://添加固定车信息
{
addRegisterVehicle(request, reply);
}
break;
case DeleteRegister://删除固定车信息
{
delRegisterVehicle(request, reply);
}
break;
case AddAlarmVeh://添加黑名单车信息
{
addOrModAlarmVehicle(request, reply, true);
}
break;
case DeleteAlarmVeh://删除黑名单车信息
{
delAlarmVehicle(request, reply);
}
break;
/*
...
*/
break;
default:
break;
}
}
3.3.3 插件接口实现
#pragma once
#include "platformplugin_global.h"
#include "../BaseInterface/PlatformInterface.h"
#include "QConfigWidget.h"
class PLATFORMPLUGIN_EXPORT PlatformPlugin :public QObject, public PlateformInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID PlateformInterface_iid) //添加关于插件的元数据 IID 插件IID唯一
Q_INTERFACES(PlateformInterface) //用于声明插件中实现的接口
public:
PlatformPlugin();
virtual QString plateformName() final; //获取插件展示名称
virtual void cleanup()final; //释放插件资源 便于程序退出时使用
virtual QWidget *configWidget() final;//提供插件自定义界面 主程序添加插件界面
virtual QString processData2Plugin(const PluginApiType& api, const QString&request) final;//主程序向插件发送请求 获取插件处理结果
virtual QString registerProcessApi(const ProcessFunctorBiz&) final; //注册程序ProcessFunctorBiz函数 便于插件调用获取程序数据
private:
QString ProcessDownRequest(const QString&request,const QString&reply,ProcessApiType type);
ProcessFunctorBiz m_proceeeFuncBiz;
QConfigWidget*m_ConfigWidget;
};
QWidget* PlatformPlugin::configWidget()
{
if (!m_ConfigWidget)
{
m_ConfigWidget = new QConfigWidget(nullptr);
}
return m_ConfigWidget;
}
//处理主程序上传信息,转发至平台
int PlatformPlugin::processData2Plugin(const PluginApiType& api, const QString&request, const QString&reply)
{
switch (api)
{
case VehRequestIn:
{
request = convetClient2Platform(request);
sendData2Platfofm(request,reply,VehRequestIn);
reply = convetPlatform2Client(reply);
}break;
case VehRequestOut:
{
request = convetClient2Platform(request);
sendData2Platfofm(request,reply,VehRequestIn);
reply = convetPlatform2Client(reply);
}break;
/*
...
*/
}
return 0;
}
bool PlatformPlugin::subsrribe(PluginApiType type)
{
switcb(type)
{
case PluginApiType::VehRequestIn:
{
return true;
}
case PluginApiType::VehRequestOut:
{
return true;
}
/*
...
*/
}
return false;
}
QString PlatformPlugin::registerProcessApi(const ProcessFunctorBiz&funcBiz)
{
m_proceeeFuncBiz = funcBiz;
return "";
}
QString PlatformPlugin::ProcessDownRequest(const QString&request,const QString&reply,ProcessApiType type)
{
QString reply;
if (m_proceeeFuncBiz)
{
switch(type)
{
case GetRegisterVeh:
{
//转化平台下行数据格式为客户端本地请求格式
request = ConvertPlatform2Client(request,GetRegisterVeh);
//插件通过主程序注册的函数向主程序转发获取固定车指令获取固定车信息
m_proceeeFuncBiz(ProcessApiType::GetRegisterVeh, request, reply,QVariant());
//转化客户端形式到平台形式
reply = ConvertClient2Platform(reply);
break;
}
case AddRegisterVeh:
{
//转化平台下行数据格式为客户端本地请求格式
request = ConvertPlatform2Client(request,AddRegisterVeh);
//插件通过主程序注册的函数向主程序转发添加固定车指令
m_proceeeFuncBiz(ProcessApiType::AddRegisterVeh, request, reply,QVariant());
//转化客户端形式到平台形式
reply = ConvertClient2Platform(reply);
break;
}
/*
...
*/
}
}
return reply;
}

1469

被折叠的 条评论
为什么被折叠?



