Qt-自定义插件

一 Qt插件简介

1. Qt 插件是一种扩展 Qt 应用程序功能的机制。通过重写父类的虚函数,Qt 插件可以在不重新编译主程序的前提下,动态添加、更新、移除功能模块。

2. 相较于动态链接库,它增加了额外的元数据和接口约定;且在插件 dll 文件不存在时,程序也能正常启动,只是插件功能无法使用而已。

3. 编译插件项目生成的文件就是动态库文件(.dll 或 .so)

二 QPluginLoader

1. 说明:QPluginLoader 是 QLibrary 的上层封装,专为 Qt 的插件体系设计。这个类可以:加载和卸载动态库、解析派生于 QObject 的导出类、利用 Qt 的元对象系统特性(如信号槽、属性系统等)。

2. 常用API

1. 构造函数,构造一个 QPluginLoader 对象并指定要加载的插件文件路径
//参数1:fileName:字符串,代表动态库(插件)的路径和文件名。
//参数2:parent:可选的 QObject 指针,用于设置父对象,便于内存管理。
QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent = nullptr);
参数:

2. 尝试加载指定的动态库文件。如果库已经加载,则直接返回 true。
bool QPluginLoader::load();

3. 卸载当前已加载的动态库
bool QPluginLoader::unload()

4. 从已加载的动态库中创建一个 QObject 派生类的实例(导出类),利用Qt元对象系统。
//返回值:指向 QObject 的指针,成功创建插件实例则返回实例指针,否则返回 nullptr。
QObject* QPluginLoader::instance();

5. 返回最近一次操作失败的错误信息
QString QPluginLoader::errorString() const;

6. 返回由插件定义的所有静态实例化的 QObject 对象列表。
//返回值:包含多个 QObject 指针的列表。
QList<QObject*> QPluginLoader::staticInstances() const;

三  自定义插件 - 将Qt动态库改造成插件

1. 接口端:定义接口和实现插件功能

1.1 使用 Qt 向导创建一个动态库项目:具体步骤可以参考我的另一篇文章:Qt-动态库

1.2 定义接口

第一步:创建一个头文件,在头文件中定义接口类;

第二步:接口类是一个抽象类,需要定义纯虚函数,这些纯虚函数是插件要实现的功能;

第三步:需要使用宏 Q_DECLARE_INTERFACE 声明接口IID

1.3 创建插件

第一步:添加一个继承于 QObject 和上一步创建的接口类的插件类,生成插件类的 .h 和 .cpp 文件

第二步:需要使用宏 QTPLUGIN_EXPORT 将插件类定义为导入/导出类

第三步:插件类需要继承自 QObject 和接口抽象类 PluginInterface

第四步:需要使用宏Q_OBJECT启用 Qt 的元对象系统

第五步:需要使用宏Q_INTERFACES声明插件实现的接口

第六步:需要使用宏Q_PLUGIN_METADATA向插件中添加包含自身信息的元数据

第七步:需要重写接口类中的纯虚函数以实现插件功能

2. 主程序端:使用插件

2.1 将接口定义所在的头文件复制到主程序目录下,并添加到主程序项目中

2.2 可以像使用动态库一样,隐式链接插件库,静态调用插件提供的功能函数

2.3 通过 PluginInterface 动态加载插件,使用插件提供的功能函数

四 自定义插件 - 使用Qt向导直接创建自定义插件

1. 接口端:定义接口和实现插件功能

1.1 使用 Qt 向导创建一个插件项目:利用 QtCreator 向导创建一个插件项目,步骤和创建动态库一样,只是类型选择 "Qt Plugin",其他的选项默认即可。

1.2 定义接口

第一步:使用向导创建的插件项目默认会生成一个派生于 QGenericPlugin 的接口类,我们只需要重写默认接口类的 create() 函数即可。

第二步:我们需要在 create() 函数中实例化插件类对象,并传出对象指针。

第三步:我们也可以继承 Qt 向导生成的接口类,根据需求自定义接口。

第四步:Qt 向导生成插件项目时会生成一个用于记录插件信息 Json 文件(元数据),并在接口类中加载了该 Json 文件。我们可以将插件信息写入到这个 Json 文件中。

1.3 创建插件

第一步:插件类需要继承于 QObject 或 QObject 的子类,可在插件类的成员函数中实现插件的功能

第二步:若插件类为窗口类的子类,则在通过 Qt 向导添加 "Qt 设计器界面类"后,还需要修改 CMakeLists.txt 文件,将 Gui 改为对应的窗口类

第三步:需要把插件类的头文件给主程序,才能在主程序中调用插件类的成员函数,若不给,则只能调用插件类继承的 Qt 内置类的成员函数

2. 主程序端:使用插件 

2.1 需要将插件类所在的头文件复制到主程序目录下,并添加到主程序项目中

2.2 可以像使用动态库一样,隐式链接插件库,静态调用插件提供的功能函数

2.3 通过 PluginInterface 动态加载插件,使用插件提供的功能函数

五 代码示例,完整的代码在绑定的资源中,审核通过后,大家可以免费下载

QPluginLoader *loader = nullptr;

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

    //三 使用插件

	//1. 动态加载插件

    //实例化 QPluginLoader,传入父对象,以管理其生命周期
    loader = new QPluginLoader(a.parent());

    //设置插件的文件路径
    loader->setFileName("G:\\0_Code\\Qt\\15_CustomControl\\3_QtPlugin\\CustomPlugin2\\QtPlugin\\build\\Desktop_Qt_6_7_1_MinGW_64_bit-Debug\\libQtPlugin.dll");

    //获取插件实例
    QObject *plugin = loader->instance();
    if (plugin)
    {
        //将插件实例转换为接口指针
        QGenericPlugin* interface = qobject_cast<QGenericPlugin*>(plugin);
        if (interface)
        {
            //传入插件类名和参数,创建插件对象并获取指向插件对象的指针
            QObject *widget = interface->create("Plugin", NULL);
            if (widget)
            {
                //调用插件对象中的函数
                qobject_cast<QWidget*>(widget)->show();
            }
        }
        else
        {
            qDebug() << "插件实例转换为接口指针失败!";
        }
    }
    else
    {
        //若获取插件实例失败,打印错误信息
        qDebug() << "获取插件实例失败,错误信息:" << loader->errorString();
    }


    //2. 卸载插件

    //5秒后卸载插件
    QTimer::singleShot(5000, a.parent(), [&](){

        //检查插件是否已加载
        if (loader->isLoaded())
        {
            //卸载插件
            loader->unload();
        }
        //delete loader;
    });


    Widget w;
    w.show();
    return a.exec();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值