• Qt插件之自定义插件构建和使用



    上一篇文章介绍了如何构建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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    插件的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 \
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    创建插件工程之后,跟编写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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    //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 "用来选取颜色";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    该插件负责加载一个用户自定义的控件,这里同样以颜色下拉列表控件为例进行说明,具体实现如下所示:

    //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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    //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 : "");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    动态加载自定义插件

    编写完成自定义插件之后,我们就可以使用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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    //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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    自定义插件的动态加载效果如下所示:
    在这里插入图片描述

    分发SDK

    定义好插件的SDK之后,我们就可以将SDK分发给插件开发者。只要插件开发者开发的插件实现了对应的SDK接口,那么开发的插件就可以集成到对应的系统中。这样就把插件开发和主应用开发的工作完全解耦开了。开发者不需要知道主应用的细节就可以开发出能够集成到主应用中的插件。

    参考实例
    链接:https://pan.baidu.com/s/1HoG3OAAyEbozs7CshH0B1w
    提取码:vt3x

  • 相关阅读:
    51系列—基于51单片机的RS232通信示例
    9款AI让你在2分钟内创建任何东西
    C++面试题(丝)-计算机网络部分(1)
    自动驾驶中常见的位姿表示和坐标系问题
    数据结构-顺序存储二叉树
    MySQL:面试问的范式设计
    计网总结☞应用层
    生产经验篇(2)——真实环境的MySQL机器配置规划
    你管这叫操作系统源码(十五)
    Qt应用开发(基础篇)——组合框容器 QGroupBox
  • 原文地址:https://blog.csdn.net/yang1fei2/article/details/125570298