当前位置:网站首页>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
边栏推荐
- uploads-labs靶场(附源码分析)(更新中)
- Microservice - declarative interface call openfeign
- How idea starts run dashboard
- CString在多线程中的问题
- 首发!!lancet饿了么官方文档
- nifi从入门到实战(保姆级教程)——flow
- Mb10m-asemi rectifier bridge mb10m
- Pyinstaller is not an internal or external command, nor is it a runnable program or batch file
- Detailed explanation of string function and string function with unlimited length
- Create gradle project
猜你喜欢

From "zero sum game" to "positive sum game", PAAS triggered the third wave of cloud computing

Famous blackmail software stops operation and releases decryption keys. Most hospital IOT devices have security vulnerabilities | global network security hotspot on February 14

Jmeter线程组功能介绍

软件逆向破解入门系列(1)—xdbg32/64的常见配置及功能窗口

Detailed pointer advanced 1

Brush questions -- sword finger offer

Microservice API gateway

The accept attribute of the El upload upload component restricts the file type (detailed explanation of the case)

秒殺系統3-商品列錶和商品詳情
![[200 opencv routines] 217 Mouse interaction to obtain polygon area (ROI)](/img/04/460734209ec315c5c02cb3fae4bf0e.png)
[200 opencv routines] 217 Mouse interaction to obtain polygon area (ROI)
随机推荐
Detailed pointer advanced 1
2022年Q2加密市场投融资报告:GameFi成为投资关键词
How to use AAB to APK and APK to AAB of Google play apps on the shelves
Brush questions -- sword finger offer
无心剑中译泰戈尔《漂鸟集(1~10)》
Effect of ARP package on FTP dump under vxworks-6.6 system
Unityshader - materialcapture material capture effect (Emerald axe)
uploads-labs靶场(附源码分析)(更新中)
Location of software installation information and system services in the registry
Redis installation under windows and Linux systems
Nine ways to define methods in scala- Nine ways to define a method in Scala?
Large CSV split and merge
Backtracking method to solve batch job scheduling problem
The wonderful use of do{}while()
Microservices Seata distributed transactions
[系统安全] 四十三.Powershell恶意代码检测系列 (5)抽象语法树自动提取万字详解
Intelij idea efficient skills (III)
Create gradle project
Famous blackmail software stops operation and releases decryption keys. Most hospital IOT devices have security vulnerabilities | global network security hotspot on February 14
How idea starts run dashboard