编辑推荐: |
本文详细介绍了插件使用、部分通信逻辑及插件构建逻辑相关内容。 希望能为大家提供一些参考或帮助。
文章来自于CSDN,由火龙果Linda编辑推荐。 |
|
一、项目示例
1.导航栏操作页面操作示例图
下图演示了通过导航栏打开和关闭页面并在主页插件显示的操作。

2.打开所有页面操作示例图
下图演示了一键打开所有界面并同事更新导航来页面状态的操作。

3.打开指定界面操作示例图
下图演示了根据不同项按钮打开指定界面的操作,并且同时更新对应导航栏状态。

4.插件重载操作演示
下图演示了指定页面重载指定页面的逻辑演示。
注:本文代码此处重载并非同一插件的重载,而是将当前使用插件卸载,然后通过更新插件路径从而加载一个全新的插件逻辑。

二、插件逻辑个人理解
1.QPluginLoader的简单使用
使用步骤如下:
**设置库文件:**创建一个QPluginLoader对象,可通过构造传参或setFileName函数设置库文件;
**加载库文件:**使用QPluginLoader对象的load函数加载库文件;
**获取插件指针:**通过QPluginLoader对象的instance函数获取一个QObject指针,然后通过转换获取,再使用插件指针获取控件对象等操作;
卸载插件:. 通过QPluginLoader对象的unload函数卸载插件,该函数会自动释放插件指针对象。
2.子插件的基本要素
继承自定义接口并根据功能实现对应的接口函数。
使用Q_DECLARE_INTERFACE:将接口对象声明给元对象,保证可使用Q_INTERFACES添加该接口。
使用Q_INTERFACES:添加后可使用qobject_cast转换为定义接口类(不添加该标识符,使用dynamic_cast强制转换也可使用,但不推荐)。
使用Q_PLUGIN_METADATA:添加后可使得QPluginLoader的instance函数获取到接口(类)指针。
三、项目结构(思路)简述
1.定义插件接口类
插件与插件之间无法直接通信,此时就需要一个接口作为中间类建立通信的桥梁(提供保障插件正常工作的函数,如收、发数据的函数),并且要求进行通信的子插件都需要继承接口类并实现对应的通信函数;以及在该类文件中定义唯一标识符(使得Qt能通过标识符识别该接口类),。
插件唯一标识符
#define InterfaceIdent "plugin.plugindatabusinterface"
|
插件相关必要函数
signals:
void sigPluginCommTriggered(const StCommData &data);
public:
virtual void recvPluginCommData(const StCommData &data) = 0;
virtual void initialize() = 0;
virtual bool isInitialized() const = 0;
virtual QString name() const = 0;
virtual QWidget *createWidget() = 0;
public slots:
virtual void slotInitialized() = 0;
|
2.定义插件类别
一个主项目
管理子插件集合以及负责各个子插件的互相通信数据转发;主项目负责加载、管理、卸载子插件并处理各个插件间的消息转发,并且主项目包含main.cpp,使得程序从该入口运行。
主项目的目录结构若干子插件
某一功能的集合,负责该功能的消息发送及消息处理;要求继承接口类并个性化实现接口类虚函数,且子插件中的插件类中需添加指定的插件宏。
子插件接口宏使用
Q_INTERFACES(IPluginDataBusInterface)
#if QT_VERSION >= 0x050000
Q_PLUGIN_METADATA(IID InterfaceIdent)
#endif
|
子插件目录结构
其他子插件
除开初始化显示的导航栏插件和主页插件固定外,子页插件是通过导航栏插件解析配置文件获取,因此导航栏插件目录下添加其他插件信息配置文件。
如下图:3.主项目及子插件的关联
以本文项目举例(详情请看源码)
在主项目头文件中添加插件信息容器集合处理。
插件初始化时将对应插件存储至相关容器,并关联数据信号处理消息。 四、源码(此处列举主项目和一个子插件源码为例)
1.主项目相关文件
iplugindatabusinterface.h
#ifndef IPLUGINDATABUSINTERFACE_H
#define IPLUGINDATABUSINTERFACE_H
#include <QObject>
#include "commondefins.h"
#define InterfaceIdent "plugin.plugindatabusinterface"
class IPluginDataBusInterface : public QObject
{
Q_OBJECT
public:
IPluginDataBusInterface(QObject *parent = Q_NULLPTR):QObject(parent){}
signals:
void sigPluginCommTriggered(const StCommData &data);
public:
virtual void recvPluginCommData(const StCommData &data) = 0;
virtual void initialize() = 0;
virtual bool isInitialized() const = 0;
virtual QString name() const = 0;
virtual QWidget *createWidget() = 0;
public slots:
virtual void slotInitialized() = 0;
protected:
bool m_initialized;
};
Q_DECLARE_INTERFACE(IPluginDataBusInterface, InterfaceIdent)
#endif
|
commondefins
commondefins.h
#ifndef COMMONDEFINS_H
#define COMMONDEFINS_H
#include <QHash>
#include <QPair>
#include <QString>
#include <QJsonObject>
#include <QMessageBox>
#define DefaultPluginLoadPath QString("./")
enum EmDataCode{
PAGE_COMM = 0,
PAGE_OPEN,
PAGE_CLOSE,
PAGE_RELOAD,
PAGE_RELOAD_FAILED,
PAGE_OPEN_ALL,
PAGE_INIT,
PAGE_INIT_FAILED,
};
typedef struct StCommData {
EmDataCode code;
QString pageName;
QJsonObject commData;
StCommData(){
}
StCommData(EmDataCode code, QString pageName, QJsonObject commData) {
this->code = code;
this->pageName = pageName;
this->commData = commData;
}
}StCommData;
typedef struct StInitPluginLoaderInfo {
#ifdef QT_DEBUG
const QString navigationBar = DefaultPluginLoadPath + "navigationbarplugind.dll";
const QString homePage = DefaultPluginLoadPath + "homepageplugind.dll";
#else
const QString navigationBar = DefaultPluginLoadPath + "navigationbarplugin.dll";
const QString homePage = DefaultPluginLoadPath + "homepageplugin.dll";
#endif
}StInitPluginLoaderInfo;
typedef struct StPluginCommInfo {
const QStringList listNavigationBarInfo = {"PageFirst", "PageSecond"};
}StHomePagePluginLoaderInfo;
#endif
|
commondefins.cpp
#include "commondefins.h"
StInitPluginLoaderInfo g_stInitPluginLoaderInfo;
|
mainwindow
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPluginLoader>
#include "iplugindatabusinterface.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = Q_NULLPTR);
~MainWindow();
void initial();
signals:
void sigInitialized();
private slots:
void slotPluginCommTriggered(const StCommData &data);
private:
Ui::MainWindow *ui;
QPair<QPluginLoader *, IPluginDataBusInterface *> m_pairNavigateInfo;
QPair<QPluginLoader *, IPluginDataBusInterface *> m_pairHomePageInfo;
QHash<QString, QPair<QPluginLoader *, IPluginDataBusInterface *>> m_hashPageLoaderIPlugin;
};
#endif
|
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "commondefins.h"
#include <QDebug>
#include <QDateTime>
#include <QFile>
extern StInitPluginLoaderInfo g_stInitPluginLoaderInfo;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
initial();
}
MainWindow::~MainWindow()
{
m_pairNavigateInfo.first->unload();
m_pairHomePageInfo.first->unload();
foreach(auto info,m_hashPageLoaderIPlugin) {
info.first->unload();
}
delete ui;
}
void MainWindow::initial()
{
m_pairNavigateInfo.first = new QPluginLoader(g_stInitPluginLoaderInfo.navigationBar, this);
m_pairNavigateInfo.second = qobject_cast<IPluginDataBusInterface *>(m_pairNavigateInfo.first->instance());
connect(m_pairNavigateInfo.second, &IPluginDataBusInterface::sigPluginCommTriggered, this, &MainWindow::slotPluginCommTriggered);
connect(this, &MainWindow::sigInitialized, m_pairNavigateInfo.second, &IPluginDataBusInterface::slotInitialized);
ui->layoutNavigation->addWidget(m_pairNavigateInfo.second->createWidget());
m_pairHomePageInfo.first = new QPluginLoader(g_stInitPluginLoaderInfo.homePage, this);
m_pairHomePageInfo.second = qobject_cast<IPluginDataBusInterface *>(m_pairHomePageInfo.first->instance());
connect(m_pairHomePageInfo.second, &IPluginDataBusInterface::sigPluginCommTriggered, this, &MainWindow::slotPluginCommTriggered);
connect(this, &MainWindow::sigInitialized, m_pairHomePageInfo.second, &IPluginDataBusInterface::slotInitialized);
ui->tabWidget->addTab(m_pairHomePageInfo.second->createWidget(), u8"主页");
emit sigInitialized();
}
void MainWindow::slotPluginCommTriggered(const StCommData &data)
{
switch (data.code) {
case PAGE_INIT: {
#ifdef QT_DEBUG
QString pluginName = data.commData.value("pluginName").toString() + "d.dll";
#else
QString pluginName = data.commData.value("pluginName").toString() + ".dll";
#endif
QPluginLoader *pageLoader = new QPluginLoader(DefaultPluginLoadPath + pluginName, this);
IPluginDataBusInterface *pageInteface = qobject_cast<IPluginDataBusInterface *>(pageLoader->instance());
if(pageInteface) {
if(m_hashPageLoaderIPlugin.contains(data.pageName)) {
QMessageBox::information(this, u8"提示", data.pageName + u8"页面名称已存在");
break;
}
connect(pageInteface, &IPluginDataBusInterface::sigPluginCommTriggered, this, &MainWindow::slotPluginCommTriggered);
m_hashPageLoaderIPlugin[data.pageName] = QPair<QPluginLoader *, IPluginDataBusInterface *>(pageLoader, pageInteface);
m_pairHomePageInfo.second->recvPluginCommData(data);
}
else {
StCommData reply = data;
reply.code = PAGE_INIT_FAILED;
QMessageBox::information(this, u8"提示", data.pageName + u8"页面信息初始化失败" + pageLoader->errorString());
m_pairNavigateInfo.second->recvPluginCommData(reply);
}
break;
}
case PAGE_OPEN: {
if(!m_hashPageLoaderIPlugin.contains(data.pageName)) {
QMessageBox::information(this, u8"提示", data.pageName + u8"页面不存在");
}
else if (-1 != ui->tabWidget->indexOf(m_hashPageLoaderIPlugin[data.pageName].second->createWidget())) {
QMessageBox::information(this, u8"提示", data.pageName + u8"页面已打开");
}
else {
QWidget *page = m_hashPageLoaderIPlugin[data.pageName].second->createWidget();
ui->tabWidget->addTab(page, data.pageName);
ui->tabWidget->setCurrentWidget(page);
m_pairNavigateInfo.second->recvPluginCommData(data);
}
break;
}
case PAGE_OPEN_ALL: {
foreach(auto pageInfo, m_hashPageLoaderIPlugin) {
ui->tabWidget->addTab(pageInfo.second->createWidget(), m_hashPageLoaderIPlugin.key(pageInfo));
}
m_pairNavigateInfo.second->recvPluginCommData(data);
break;
}
case PAGE_CLOSE: {
if (!m_hashPageLoaderIPlugin.contains(data.pageName)) {
QMessageBox::information(this, u8"提示", data.pageName + u8"页面不存在");
}
else if (-1 == ui->tabWidget->indexOf(m_hashPageLoaderIPlugin[data.pageName].second->createWidget())) {
QMessageBox::information(this, u8"提示", data.pageName + u8"页面暂未打开/已关闭");
}
else{
ui->tabWidget->removeTab(ui->tabWidget->indexOf(m_hashPageLoaderIPlugin[data.pageName].second->createWidget()));
m_pairNavigateInfo.second->recvPluginCommData(data);
}
break;
}
case PAGE_RELOAD: {
if (!m_hashPageLoaderIPlugin.contains(data.pageName)) {
QMessageBox::information(this, u8"提示", data.pageName + u8"重载页面不存在");
}
else if(m_hashPageLoaderIPlugin[data.pageName].first->unload()) {
QString fileName = m_hashPageLoaderIPlugin[data.pageName].first->fileName().replace(DefaultPluginLoadPath, DefaultPluginLoadPath + "backup/");
if (QFile::exists(fileName)) {
m_hashPageLoaderIPlugin[data.pageName].first->setFileName(fileName);
}
if (m_hashPageLoaderIPlugin[data.pageName].first->load()) {
m_hashPageLoaderIPlugin[data.pageName].second = qobject_cast<IPluginDataBusInterface *>(m_hashPageLoaderIPlugin[data.pageName].first->instance());
m_pairNavigateInfo.second->recvPluginCommData(data);
m_hashPageLoaderIPlugin[data.pageName].second->createWidget();
}
else {
StCommData tmpData = data;
tmpData.code = PAGE_RELOAD_FAILED;
m_pairNavigateInfo.second->recvPluginCommData(tmpData);
QMessageBox::information(this, u8"提示", data.pageName + u8"页面重载失败 " + m_hashPageLoaderIPlugin[data.pageName].first->errorString());
}
}
else {
QMessageBox::information(this, u8"提示", data.pageName + u8"页面重载失败 " + m_hashPageLoaderIPlugin[data.pageName].first->errorString());
}
break;
}
default:
break;
}
}
|
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
|
2.子插件(页面1)相关文件
注:该插件控件仅ui添加标识label,故仅展示插件文件
pagefirstplugin
pagefirstplugin.h
#ifndef PAGEFIRSTPLUGIN_H
#define PAGEFIRSTPLUGIN_H
#include "iplugindatabusinterface.h"
#include <QDesignerCustomWidgetInterface>
class FormPageFirst;
class PageFirstPlugin : public IPluginDataBusInterface
{
Q_OBJECT
Q_INTERFACES(IPluginDataBusInterface)
#if QT_VERSION >= 0x050000
Q_PLUGIN_METADATA(IID InterfaceIdent)
#endif
public:
PageFirstPlugin(QObject *parent = Q_NULLPTR);
~PageFirstPlugin();
public:
void initialize();
bool isInitialized() const;
QString name() const;
QWidget *createWidget();
void recvPluginCommData(const StCommData &data);
public slots:
void slotInitialized();
private:
FormPageFirst *m_formPageFirst = Q_NULLPTR;
};
#endif
|
pagefirstplugin.cpp
#include "formpagefirst.h"
#include "pagefirstplugin.h"
#include <QtPlugin>
PageFirstPlugin::PageFirstPlugin(QObject *parent)
: IPluginDataBusInterface(parent)
{
m_initialized = false;
}
PageFirstPlugin::~PageFirstPlugin()
{
if(Q_NULLPTR != m_formPageFirst) {
delete m_formPageFirst;
m_formPageFirst = Q_NULLPTR;
}
}
void PageFirstPlugin::initialize()
{
if (m_initialized)
return;
m_initialized = true;
}
void PageFirstPlugin::slotInitialized()
{
}
void PageFirstPlugin::recvPluginCommData(const StCommData &data)
{
}
bool PageFirstPlugin::isInitialized() const
{
return m_initialized;
}
QWidget *PageFirstPlugin::createWidget()
{
if(Q_NULLPTR == m_formPageFirst) {
m_formPageFirst = new FormPageFirst();
}
return m_formPageFirst;
}
QString PageFirstPlugin::name() const
{
return QLatin1String("PageFirstPlugin");
}
#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(pagefirstplugin, PageFirstPlugin)
#endif
|
总结
本文简易介绍插件使用、部分通信逻辑及插件构建逻辑,下一篇插件创建详细步骤。
|