当前位置:网站首页>Qt插件之自定义插件构建和使用
Qt插件之自定义插件构建和使用
2022-07-03 16:01:00 【码农飞飞】
上一篇文章介绍了如何构建Qt Designer插件。其实插件化的这套机制QT是对外开放的,这里就介绍一下如何使用QT开发自定义插件。在开发自定义插件之前我们先定义插件的SDK。插件的SDK就是插件的接口描述,任何开发者开发的插件都应该实现对应的接口。同时只要实现了对应的接口的插件,就可以被集成到系统当中,这其实就是给自定义插件提供了一个统一的接口标准。
定义插件的SDK
这里我们定义插件的SDK,实现如下所示:
//sdk/MyPluginInterface.h
#ifndef MY_PLUGIN_INTERFACE_H
#define MY_PLUGIN_INTERFACE_H
#include <QWidget>
//防止命名冲突添加命名空间
namespace Plugin {
class MyPluginInterface
{
public:
virtual ~MyPluginInterface() {
}
//插件实例的名称
virtual QString name() const = 0;
//创建UI的实例
virtual QWidget* createWidget(QWidget* parent) = 0;
//获得插件的展示名称
virtual QString displayName() const = 0;
};
}
//定义了在QT系统中该接口的全局唯一的ID
//实现该SDK的插件也要定义相同的ID
//接口的ID中包含了版本信息,通过该ID我们可以区别不同版本的SDK和插件
//Q_DECLARE_INTERFACE宏将类型和ID关联起来,这样QT就可以验证加载的插件是否可以转换成MyPluginInterface类型
#define interface_iid "org.pluginqt.custompluginuse.myplugin.myplugin1.0.0"
Q_DECLARE_INTERFACE(Plugin::MyPluginInterface, interface_iid)
#endif
插件的SDK中定义了插件接口在QT对象系统中的全局ID。同时为了防止命名冲突,我们还给插件的接口添加了命名空间,防止在不同环境下产生命名冲突。
编写自定义插件
首先新建一个动态库工程。工程配置如下:
QT += core widgets
TARGET = $$qtLibraryTarget(custom-colorcombo-plugin)
TEMPLATE = lib
CONFIG += plugin
#包含对应的插件SDK目录
INCLUDEPATH += $$PWD/sdk
DEPENDPATH += $$PWD/sdk
#指定输出路径
debug:DESTDIR = $$PWD/../CustomPlugin_output/debug/
release:DESTDIR =$$PWD/../CustomPlugin_output/release/
SOURCES += \
customplugin.cpp \
colorcombox.cpp
HEADERS += \
customplugin.h \
colorcombox.h
#添加SDK文件
OTHER_FILES += \
sdk/MyPluginInterface.h \
创建插件工程之后,跟编写QT Designer插件一样,添加一个继承自MyPluginInterface和QObject类的插件信息描述类,插件的ID应该和SDK中的ID保持一致。具体的实现如下:
//customplugin.h
#ifndef CUSTOMPLUGIN_H
#define CUSTOMPLUGIN_H
#include "sdk/MyPluginInterface.h"
#include <QObject>
class CustomPlugin : public QObject, Plugin::MyPluginInterface
{
Q_OBJECT
//声明QT识别的唯一标识符
//和SDK中的ID保持一致
Q_PLUGIN_METADATA(IID "org.pluginqt.custompluginuse.myplugin.myplugin1.0.0")
//声明实现的插件接口
Q_INTERFACES(Plugin::MyPluginInterface)
public:
CustomPlugin(QObject* parent = 0);
QString name() const override;
QWidget* createWidget(QWidget* parent) override;
QString displayName() const override;
};
#endif // CUSTOMPLUGIN_H
//customplugin.cpp
#include "customplugin.h"
#include "colorcombox.h"
CustomPlugin::CustomPlugin(QObject *parent):QObject(parent)
{
}
QString CustomPlugin::name() const
{
return "colorCombox";
}
QWidget* CustomPlugin::createWidget(QWidget* parent)
{
return new ColorComboBox(parent);
}
QString CustomPlugin::displayName() const
{
return "用来选取颜色";
}
该插件负责加载一个用户自定义的控件,这里同样以颜色下拉列表控件为例进行说明,具体实现如下所示:
//colorcombox.h
#ifndef COLORCOMBOBOX_H
#define COLORCOMBOBOX_H
#include <QComboBox>
class ColorComboBox : public QComboBox
{
Q_OBJECT
public:
explicit ColorComboBox(QWidget *parent = 0);
private slots:
void slot_currentIndexChanged(int index);
public:
bool getShowColorName();
QString getColorName();
public slots:
void setShowColorName(bool showColorName);
void setColorName(QString colorName);
void initColorItems();
signals:
//名称和颜色变化信号
void colorChanged(QString colorName);
void colorChanged(QColor color);
private:
//是否显示英文名称
bool menableColorName;
//当前颜色名称
QString mcolorName;
};
#endif // COLORCOMBOBOX_H
//colorcombox.cpp
#include "colorcombox.h"
ColorComboBox::ColorComboBox(QWidget *parent) : QComboBox(parent),
menableColorName(true)
{
initColorItems();
connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(slot_currentIndexChanged(int)));
}
void ColorComboBox::slot_currentIndexChanged(int index)
{
if (index >= 0)
{
mcolorName = QColor::colorNames().at(index);
emit colorChanged(mcolorName);
emit colorChanged(QColor(mcolorName));
}
}
bool ColorComboBox::getShowColorName()
{
return menableColorName;
}
QString ColorComboBox::getColorName()
{
return mcolorName;
}
void ColorComboBox::setShowColorName(bool showColorName)
{
if (menableColorName != showColorName) {
menableColorName = showColorName;
}
}
void ColorComboBox::setColorName(QString colorName)
{
if (mcolorName != colorName)
{
mcolorName = colorName;
int index = QColor::colorNames().indexOf(colorName);
this->setCurrentIndex(index);
}
}
void ColorComboBox::initColorItems()
{
clear();
QStringList colorList = QColor::colorNames();
for(QString strColor: colorList)
{
QPixmap pix(this->iconSize());
pix.fill(strColor);
this->addItem(QIcon(pix), menableColorName ? strColor : "");
}
}
动态加载自定义插件
编写完成自定义插件之后,我们就可以使用QPluginLoader动态的加载自定义插件了。通过QPluginLoader类加载插件,可以自动的检查编写插件的QT版本和编写主应用的QT版本是否一致,同时通过instance() 可以直接将插件对象转换成QObject对象。
在加载插件的时候,我们也可以通过QPluginLoader获取插件的METADATA,然后根据不同的Q_PLUGIN_METADATA执行不同的操作。这样主应用就可以兼容不同版本的插件了。
在主应用中动态加载插件的示例如下所示:
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
public slots:
void slot_load_plugin();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
//widget.cpp
#pragma execution_character_set("utf-8")
#include "ui_widget.h"
#include "sdk/MyPluginInterface.h"
#include <QFileDialog>
#include <QPluginLoader>
#include <QHBoxLayout>
#include <memory>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->load_plugin_btn,SIGNAL(clicked(bool)),this, SLOT(slot_load_plugin()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::slot_load_plugin()
{
QString file_path = QFileDialog::getOpenFileName(NULL,"加载插件","../cutomplugin_output","dll (*.dll *.so)");
if(file_path.isEmpty())
{
return;
}
QPluginLoader pluginLoader(file_path);
QObject* plugin = pluginLoader.instance();
if (plugin) {
std::unique_ptr<Plugin::MyPluginInterface> interface_ptr = std::unique_ptr<Plugin::MyPluginInterface>(qobject_cast<Plugin::MyPluginInterface*>(plugin));
QWidget* widget = interface_ptr->createWidget(this);
widget->setFixedHeight(40);
widget->setFixedWidth(200);
QHBoxLayout* mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(widget);
ui->widget->setLayout(mainLayout);
}
}
自定义插件的动态加载效果如下所示:
分发SDK
定义好插件的SDK之后,我们就可以将SDK分发给插件开发者。只要插件开发者开发的插件实现了对应的SDK接口,那么开发的插件就可以集成到对应的系统中。这样就把插件开发和主应用开发的工作完全解耦开了。开发者不需要知道主应用的细节就可以开发出能够集成到主应用中的插件。
参考实例
链接:https://pan.baidu.com/s/1HoG3OAAyEbozs7CshH0B1w
提取码:vt3x
边栏推荐
- ASEMI整流桥UMB10F参数,UMB10F规格,UMB10F封装
- Batch files: list all files in a directory with relative paths - batch files: list all files in a directory with relative paths
- How to use AAB to APK and APK to AAB of Google play apps on the shelves
- [系统安全] 四十三.Powershell恶意代码检测系列 (5)抽象语法树自动提取万字详解
- CString getbuffer and releasebuffer instructions
- Redis installation under windows and Linux systems
- About text selection in web pages and counting the length of selected text
- QT use qzxing to generate QR code
- Remote file contains actual operation
- Reading notes of "micro service design" (Part 2)
猜你喜欢
Microservice - fuse hystrix
Rk3399 platform development series explanation (WiFi) 5.54. What is WiFi wireless LAN
Salary 3000, monthly income 40000 by "video editing": people who can make money never rely on hard work!
Detailed pointer advanced 2
【Proteus仿真】8×8LED点阵屏仿电梯数字滚动显示
SDNU_ ACM_ ICPC_ 2022_ Winter_ Practice_ 4th [individual]
近视:摘镜or配镜?这些问题必须先了解清楚
Microservice API gateway zuul
初试scikit-learn库
Find mapping relationship
随机推荐
Microservices - load balancing ribbon
远程文件包含实操
The difference between calling by value and simulating calling by reference
Detailed explanation of string function and string function with unlimited length
Microservices Seata distributed transactions
[200 opencv routines] 217 Mouse interaction to obtain polygon area (ROI)
How idea starts run dashboard
Rk3399 platform development series explanation (WiFi) 5.54. What is WiFi wireless LAN
Pychart error updating package list: connect timed out
Intelij idea efficient skills (III)
Go language self-study series | if else if statement in golang
[系统安全] 四十三.Powershell恶意代码检测系列 (5)抽象语法树自动提取万字详解
Redis high availability and persistence
Approval process design
Redis在Windows以及Linux系统下的安装
Asemi rectifier bridge umb10f parameters, umb10f specifications, umb10f package
Microservice sentinel flow control degradation
近视:摘镜or配镜?这些问题必须先了解清楚
【Proteus仿真】74HC595+74LS154驱动显示16X16点阵
“用Android复刻Apple产品UI”(2)——丝滑的AppStore卡片转场动画