← 返回博客列表

一、框架基本情况

Qt ExtensionSystem 是 Qt Creator 团队开发的一套 插件化框架,它为构建可扩展、可插拔的应用程序提供了完整的解决方案。Qt Creator 本身就是基于该框架构建的——编辑器、调试器、构建系统等功能模块均以插件形式存在。

该框架的核心价值在于:

核心类:ExtensionSystem::PluginManager 是框架的入口,负责插件的发现、加载、依赖解析和生命周期管理。

二、核心架构与类图

ExtensionSystem 由以下几个核心类组成:

ExtensionSystem 类图

核心类职责:

三、插件加载时序图

下图展示了应用启动后,PluginManager 与 PluginSpec、IPlugin 之间的交互流程,包括路径扫描、元数据解析、依赖排序、加载初始化等关键步骤:

ExtensionSystem 插件加载时序图

四、插件开发核心代码

4.1 实现 IPlugin 接口

class MyPlugin : public ExtensionSystem::IPlugin {
    Q_OBJECT
    public:
    MyPlugin() = default;

    bool initialize(ExtensionSystem::PluginManager* manager) override {
        // 第一阶段:获取依赖的插件,建立通信
        auto* depPlugin = manager->getPlugin("com.example.dependency");
        if (!depPlugin) return false;
        m_dep = new DepWrapper(depPlugin);
        return true;
    }

    void extensionsInitialized() override {
        // 第二阶段:所有插件初始化完成,进行互联
        qDebug() << "MyPlugin: 所有扩展已初始化";
    }

    void aboutToShutdown() override {
        // 第三阶段:应用关闭前的清理工作
        delete m_dep;
    }

private:
    DepWrapper* m_dep = nullptr;
};

4.2 注册插件到 Qt 元对象系统

// 在 .cpp 文件中注册插件
#include 
ExtensionSystem::Plugin<MyPlugin> MyPluginPlugin;

4.3 主程序加载插件

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    ExtensionSystem::PluginManager manager;

    // 1. 添加插件搜索路径
    manager.addPluginPath("./plugins");

    // 2. 加载插件(内部自动解析依赖、排序、加载)
    bool ok = manager.loadPlugins();
    if (!ok) {
        qWarning() << "插件加载失败:" << manager.pluginErrors();
        return -1;
    }

    // 3. 通过 ID 获取指定插件并使用
    auto* plugin = manager.getPlugin("com.example.myplugin");
    if (plugin) {
        auto* myPlugin = qobject_cast<MyPlugin*>(plugin);
        myPlugin->doSomething();
    }

    return app.exec();
}

五、元数据解析机制

每个插件通过同目录下的 metadatainfo.json(或 .xml)文件描述自身信息。PluginManager 加载时,PluginSpec 会自动解析该文件。

5.1 元数据文件示例(JSON 格式)

{
    "IID": "com.example.myplugin",
    "Name": "我的插件",
    "Version": "1.0.0",
    "Vendor": "杨超",
    "Description": "这是一个示例插件",
    "Category": "General",
    "Dependencies": [
        {
            "IID": "com.example.dependency",
            "Version": "1.0.0",
            "Type": "Hard"
        }
    ],
    "Arguments": [
        {
            "name": "autoload",
            "value": "true"
        }
    ]
}

5.2 元数据解析流程

解析步骤:
1. PluginManager 扫描所有插件目录,找到 metadatainfo.json 文件
2. PluginSpec::parse() 使用 QJsonDocument 解析 JSON 结构
3. 解析出 IID、版本、厂商、描述等基本信息
4. 解析 Dependencies 数组,构建依赖关系图
5. 解析 Arguments 数组,供插件初始化时使用

5.3 IID 的作用

IID(Interface Identifier)是插件的唯一标识符,采用反向域名格式(如 com.example.myplugin)。IID 的作用:

六、插件依赖机制

6.1 依赖类型

6.2 依赖解析算法

PluginManager 使用 拓扑排序(Topological Sort) 确定插件加载顺序,确保被依赖的插件先加载。

// PluginManager 内部的依赖排序逻辑(简化示意)
void PluginManager::resolveDependencies() {
    QList<PluginSpec*> sorted;
    QSet<PluginSpec*> visited;

    for (auto* spec : m_pluginSpecs) {
        if (!visited.contains(spec))
            topoSort(spec, visited, sorted);
    }

    // 按 sorted 顺序加载,保证依赖先加载
    for (auto* spec : sorted) {
        spec->loadPlugin();
    }
}

void PluginManager::topoSort(
        PluginSpec* spec,
        QSet<PluginSpec*>& visited,
        QList<PluginSpec*>& sorted) {
    visited.insert(spec);
    for (auto* dep : spec->dependencies()) {
        if (!visited.contains(dep))
            topoSort(dep, visited, sorted);
    }
    sorted.append(spec);
}

6.3 版本匹配

依赖声明中可以指定版本范围,PluginManager 会检查版本兼容性:

6.4 循环依赖检测

注意:ExtensionSystem 会检测循环依赖并报错。如果 A 依赖 B,B 又依赖 A,两个插件都无法加载。设计时应避免循环依赖,可通过引入第三各方插件或调整依赖方向解决。

七、实战:构建可插拔的机器人示教器

实战场景:基于 ExtensionSystem 构建机器人示教器,将运动控制、IO 管理、安全策略等模块插件化。新增机器人型号时,只需开发对应的驱动插件,无需修改主程序。

目录结构:

teachPendant/
├── bin/
│   └── teachPendant.exe
└── plugins/
    ├── motioncontrol/
    │   ├── motioncontrol.dll
    │   └── metadatainfo.json
    ├── iomanager/
    │   ├── iomanager.dll
    │   └── metadatainfo.json
    ├── safetystrategy/
    │   ├── safetystrategy.dll
    │   └── metadatainfo.json
    └── robotdriver_xsta/
        ├── robotdriver_xsta.dll
        └── metadatainfo.json  ← 依赖 motioncontrol, iomanager

robotdriver_xsta 的元数据:

{
    "IID": "com.teachpendant.robotdriver.xsta",
    "Name": "新时达机器人驱动",
    "Version": "1.0.0",
    "Dependencies": [
        { "IID": "com.teachpendant.motioncontrol", "Type": "Hard" },
        { "IID": "com.teachpendant.iomanager", "Type": "Hard" },
        { "IID": "com.teachpendant.safetystrategy", "Type": "Soft" }
    ]
}

八、Qt .pro 与 CMake 集成

.pro 配置

QT += extensionsystem

SOURCES += \
    myplugin.cpp

HEADERS += \
    myplugin.h

DISTFILES += \
    metadatainfo.json

CMake 配置

find_package(Qt6 REQUIRED COMPONENTS Core ExtensionSystem)

qt_add_plugin(myplugin
    SHARED
    myplugin.cpp
    myplugin.h
)

qt6_add_resources(myplugin metadatainfo
    PREFIX "/"
    FILES metadatainfo.json
)

八、最佳实践与总结

延伸阅读:Qt Creator 源码中 src/plugins/ 目录下的所有模块都是基于 ExtensionSystem 构建的,阅读其源码是深入理解该框架的最佳方式。