Qt - Plugin

qt 的插件

什么是插件

插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序。

2个特点:

1、基于接口编程

2、提供扩展性

 

qt插件的2种形式

可以有2种插件形式

1、动态插件,也就是动态库

2、静态插件

插件是一个动态库,可以在运行时加载该库以扩展应用程序。Qt使得创建自定义插件并使用QPluginLoader加载它们成为可能。为了确保插件不会丢失,还可以将它们静态链接到可执行文件

示例可以查看Qt的demo: https://doc.qt.io/qt-5/qtwidgets-tools-plugandpaint-app-example.html

 

plugin实现

对于插件工程来说,需要实现一个接口:

  1. 定义一个继承 QObject 和 接口的插件;
  2. 使用 Q_INTERFACES() 声明使用的接口类型;在moc中支持按照对应的接口转换。
  3. 使用 Q_PLUGIN_METADATA() 宏导出元信息;在moc中导出元数据和类。
class ExtraFiltersPlugin : public QObject, public FilterInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface" FILE "extrafilters.json")
    Q_INTERFACES(FilterInterface)

public:
    QStringList filters() const override;
    QImage filterImage(const QString &filter, const QImage &image,
                       QWidget *parent) override;
};

主程序实现

对于主工程来说,需要定一个插件的接口:

动态:

  1. 使用 Q_DECLARE_INTERFACE() 是生成对应接口转换函数 qobject_cast ;Q_INTERFACES 也会用到。
  2. 使用 QPluginLoader 来加载插件;
  3. 使用 qobject_cast() 进行插件造型,判断插件实现了哪个接口;

静态:

    1、Q_IMPORT_PLUGIN(BasicToolsPlugin),导入类

    2、QPluginLoader::staticInstances() 加载静态插件

 

void MainWindow::loadPlugins()
{
//静态插件
 const auto staticInstances = QPluginLoader::staticInstances();
    for (QObject *plugin : staticInstances)
        populateMenus(plugin);

//动态插件
const auto entryList = pluginsDir.entryList(QDir::Files);
    for (const QString &fileName : entryList) {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = loader.instance();
        if (plugin) {
            populateMenus(plugin);
            pluginFileNames += fileName;
        }
    }
}

//根据接口判断,也就IID来判断的,下面会讲
void MainWindow::populateMenus(QObject *plugin)
{
    //一个插件可以实现多个接口,所以需要多次判断
    auto iBrush = qobject_cast(plugin);
    if (iBrush)
        addToMenu(plugin, iBrush->brushes(), brushMenu, &MainWindow::changeBrush,
                  brushActionGroup);

    auto iShape = qobject_cast(plugin);
    if (iShape)
        addToMenu(plugin, iShape->shapes(), shapesMenu, &MainWindow::insertShape);

    auto iFilter = qobject_cast(plugin);
    if (iFilter)
        addToMenu(plugin, iFilter->filters(), filterMenu, &MainWindow::applyFilter);
}

 

下面介绍下几个宏的作用

Q_DECLARE_INTERFACE

宏的作用生成2种函数,主要作用是声明接口转换函数 qobject_cast ;

1、qobject_interface_iid   返回当前类接口id IId,

2、qobject_cast 判断参数是否可以转成当前类,

关联一个唯一的标识类,通过唯一标识可以访问类

  #define BrushInterface_iid "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface/1.0"

  Q_DECLARE_INTERFACE(BrushInterface, BrushInterface_iid)




#  define Q_DECLARE_INTERFACE(IFace, IId) \
    template <> inline const char *qobject_interface_iid() \
    { return IId; } \
    template <> inline IFace *qobject_cast(QObject *object) \
    { return reinterpret_cast((object ? object->qt_metacast(IId) : nullptr)); } \
    template <> inline IFace *qobject_cast(const QObject *object) \
    { return reinterpret_cast((object ? const_cast(object)->qt_metacast(IId) : nullptr)); }

 例如:qobject_cast 所依赖qt_metacast是由元对象系统生成的



class BasicToolsPlugin : public QObject,
                         public BrushInterface,
                         public ShapeInterface,
                         public FilterInterface
{
    Q_OBJECT

    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface" FILE "basictools.json")

    Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)


}



在moc_BasicToolsPlugin.cpp中就生成了qt_metacast方法来判断是否可以转换

void *BasicToolsPlugin::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_BasicToolsPlugin.stringdata0))
        return static_cast(this);
    if (!strcmp(_clname, "BrushInterface"))
        return static_cast< BrushInterface*>(this);
    if (!strcmp(_clname, "ShapeInterface"))
        return static_cast< ShapeInterface*>(this);
    if (!strcmp(_clname, "FilterInterface"))
        return static_cast< FilterInterface*>(this);
    if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface/1.0"))
        return static_cast< BrushInterface*>(this);
    if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.ShapeInterface/1.0"))
        return static_cast< ShapeInterface*>(this);
    if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0"))
        return static_cast< FilterInterface*>(this);
    return QObject::qt_metacast(_clname);
}

 

Q_PLUGIN_METADATA

这个宏的作用,是在moc_**.cpp中生成元数据,并且导出插件类

class BasicToolsPlugin : public QObject,
                         public BrushInterface,
                         public ShapeInterface,
                         public FilterInterface
{
    Q_OBJECT

    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface" FILE "basictools.json")

}


//moc_**.cpp中生成元数据
static constexpr unsigned char qt_pluginMetaData[] = {
    'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',
    // metadata version, Qt version, architectural requirements
    0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),
    0xbf, 
    // "IID"
    0x02,  0x78,  0x36,  'o',  'r',  'g',  '.',  'q', 
    't',  '-',  'p',  'r',  'o',  'j',  'e',  'c', 
    't',  '.',  'Q',  't',  '.',  'E',  'x',  'a', 
    'm',  'p',  'l',  'e',  's',  '.',  'P',  'l', 
    'u',  'g',  'A',  'n',  'd',  'P',  'a',  'i', 
    'n',  't',  '.',  'B',  'r',  'u',  's',  'h', 
    'I',  'n',  't',  'e',  'r',  'f',  'a',  'c', 
    'e', 
    // "className"
    0x03,  0x70,  'B',  'a',  's',  'i',  'c',  'T', 
    'o',  'o',  'l',  's',  'P',  'l',  'u',  'g', 
    'i',  'n', 
    0xff, 
};
QT_MOC_EXPORT_PLUGIN(BasicToolsPlugin, BasicToolsPlugin)

Q_INTERFACES

在moc_**.cpp中支持通过接口名称转成对应的类型,例如:

org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0
class ExtraFiltersPlugin : public QObject, public FilterInterface
{
    Q_OBJECT
  
    Q_INTERFACES(FilterInterface)

};


//为了支持通过接口名称转成对应的类型,org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0
void *ExtraFiltersPlugin::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_ExtraFiltersPlugin.stringdata0))
        return static_cast(this);
    if (!strcmp(_clname, "FilterInterface"))
        return static_cast< FilterInterface*>(this);
    if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0"))
        return static_cast< FilterInterface*>(this);
    return QObject::qt_metacast(_clname);
}

如果没有使用Q_DECLARE_INTERFACE 对 FilterInterface 进行声明就会报错

//Q_DECLARE_INTERFACE(FilterInterface, FilterInterface_iid)

原因就是 template <> inline const char *qobject_interface_iid() 没有声明导致的。

Qt - Plugin_第1张图片

 

Qt 自带插件

先看一下一个平台的插件,windows的类图

1、基于接口开发,插件只是返回 QPlatformIntegration

Qt - Plugin_第2张图片

你可能感兴趣的:(Qt)