说明

近期入职新公司,新公司的项目用到了Qt的插件系统,花时间了解了一下,还以为Qt的插件系统有多么高级呢,原来归根到底还是 dll 的动态调用时获取其中的类那一招啊,原理和之前的文章《DLL的动态加载》 的里使用 使用dll中的类 描述的方法如出一辙,只是Qt利用了其库的优势。

动态加载dll获取类

《DLL的动态加载》 文章说已经说明,dll只可以导出函数,不可以导出指针,但是为了能得到dll中的类,可以导出一个接口,使用接口获取对象指针。但是在dll的调用一方,却无法识别获取到的类指针,所以利用C++的多态和继承的特性来完成。使用抽象类,声明好抽象接口,将需要导出的类继承自该抽象类,并实现其声明的接口。dll的导出方和调用方公用一个抽象类声明文件,在dll导出函数内部,利用多态,将抽象的类指针new成子类对象,并返回出来,则调用方就可以识别得到的类指针了。具体方法说明请参考文章《DLL的动态加载》

Qt的插件系统

Qt的插件系统原理(这里只讨论High-Level API)与此相同,只是Qt利用了其内部非特性,使用QPluginLoader替代了导出类指针的接口,并使用宏定义进行导入和导出,避免了在导出导出上因为为了兼容C语言而导致的一些问题,并简化了一些操作。

使用方法

既然原理相同,则需要的文件也相同,需要导出的dll文件和抽象类的声明文件。不同点是,不需要在使用C风格的函数导出类指针了,Qt里面使用宏定义替代。

步骤:

  1. 定义抽象类,并声明需要用到的接口;
  2. 使用 Q_DECLARE_INTERFACE() 宏定义将这个抽象类的信息告诉Qt的元对象系统;
  3. 建立dll项目,继承自 QObject 和这个抽象类,并实现其接口,我们称这个项目为插件;
  4. 在插件的头文件中使用 Q_PLUGIN_METADATA() 宏定义导出这个插件;
  5. 在插件项目的.pro文件中配置导出设置,编译生成dll;
  6. 在dll调用程序中加载dll;

示例代码:

插件项目plugin:
HEADERS += \
plugin.h SOURCES += \
plugin.cpp QT += widgets DISTFILES += \
plugin.json CONFIG += plugin #表示用于lib模板:库是一个插件
TEMPLATE = lib #表示生成库文件
//plugin.h
class Plugin : public QObject, public PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID pluginInterface_iid FILE "plugin.json")
Q_INTERFACES(PluginInterface)
public:
explicit Plugin(QObject *parent = 0);
~Plugin();
void SayHello(QWidget* parent) Q_DECL_OVERRIDE;
};

说明:

  • 必须多继承自 QObject 和抽象类;
  • Q_PLUGIN_METADATA 用于声明元数据,IID 是必须且唯一的,FILE 是可选的,后面跟着一个json文件,用于描述插件的相关数据信息;
  • Q_INTERFACES 的作用是将所实现的插件接口通知给元类型系统,参数是抽象类类名;
  • Q_DECL_OVERRIDE 用于表示这是一个虚函数,编译器会检查这个方法是不是父类中所有的,若没有则报错(类似java中的@Override注解);
//plugin.cpp
Plugin::Plugin(QObject *parent) : QObject(parent)
{
qDebug()<<"constructor";
} Plugin::~Plugin()
{
qDebug()<<"destructor";
} void Plugin::SayHello(QWidget *parent)
{
QMessageBox::information(parent, "plugin", "hello, this is dynamically loaded.");
}
{
"Keys" : [ "plugin" ]
}
调用项目test:

插件生成的dll文件copy到执行目录下,共调用方加载

//interface.h
class PluginInterface
{
public:
virtual ~PluginInterface(){}
virtual void SayHello(QWidget* parent) = 0;
}; #define pluginInterface_iid "io.qt.dynamicplugin"
Q_DECLARE_INTERFACE(PluginInterface, pluginInterface_iid)

说明:

  • Q_DECLARE_INTERFACE 宏告诉Qt,这是一个插件接口,第一个参数是接口类名,第二个参数是插件标识符,大小写敏感,且唯一;
//调用部分
PluginInterface* interface = NULL;
QPluginLoader plugin_loader("plugin.dll");
QObject* plugin = plugin_loader.instance();
if(plugin)
{
interface = qobject_cast<PluginInterface*>(plugin);
if(interface)
{
interface->SayHello(this);
}
//delete plugin;
plugin_loader.unload();
}

注:Qt文档中说明,QPluginLoader 释放之后,其获取到的插件不会释放,所以,需要手动 delete 插件,或者使用 unload() 方法释放。

Qt插件系统的更多相关文章

  1. 【大话QT之十六】使用ctkPluginFramework插件系统构建项目实战

    "使用ctkPluginFramework插件系统构建项目实战",这篇文章是写博客以来最纠结的一篇文章. 倒不是由于技术都多么困难,而是想去描写叙述一个项目架构採用ctkPlugi ...

  2. 【夸QT在十五】ctkPluginFrameWork插件系统Windows编译器

    采用ctkPluginFramework作为一个插件系统开发框架确实有很多优点. 有些车站最近收到的一封信,每个人都想用ctkPluginFramework但我不知道如何建立,本教程对谈ctkPlug ...

  3. ctkPlugin插件系统实现项目插件式开发

    插件式开发体会: 自开始写[大话QT]系列就开始接触渲染客户端的开发,说是开发不如更多的说是维护以及重构,在接手这块的东西之前自己还有点犹豫,因为之前我一直认为客户端嘛,没什么技术含量,总是想做比较有 ...

  4. QT皮肤系统的动态切换

    应用需求: 提供皮肤切换选项,在不重启应用程序的情况下实现皮肤的动态切换. 理论基础: 1) 图片资源是如何被利用的 这里先简要说明一下实现原理,皮肤的动态切换其关键在于图片资源的加载方式.QT中每个 ...

  5. 【Win10+eclipse+MinGW+QT安装教程】已有eclipse环境下配置QT插件出错详解

    (纪念一下装了一整天濒临绝望的自己[/doge]) (以下所有下载项一律使用32位) step1:下载安装eclipse.适配的jdk.MinGW和QT 1.下载32位eclipse for c++以 ...

  6. [Qt Creator 快速入门] 第9章 国际化、帮助系统和Qt插件

    一.国际化 国际化的英文表述为Internationalization,通常简写为I18N(首尾字母加中间的字符数),一个应用程序的国际化就是使该应用程序可以让其他国家的用户使用的过程. Qt支持现在 ...

  7. 设置qt插件路径

    1.在Qt中使用 WebKit 浏览器核心 使用 QtWebKit 需要在工程文件(*.pro)中加入: QT +=webkitQT += network 2.QtWebKit的flash支持 QtW ...

  8. Win7下安装VS2017、安装Qt5.10.1以及在VS2017添加qt插件

    一.安装VS2017 1.下载VS2017 进入vs下载官网https://www.visualstudio.com/zh-hans/downloads/,选择所需要的vs版本,进行在线安装. 2.安 ...

  9. CloudNotes之桌面客户端篇:插件系统的实现

    [CloudNotes版本更新历史与各版本下载地址请点击此处] [CloudNotes中文系列文章汇总列表请点击此处] [查看CloudNotes源代码请点击此处] 有时候,同一个名词,针对不同的人群 ...

随机推荐

  1. 【Java】封装、继承、多态

    封装 在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装.隐藏起来的方法. 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代 ...

  2. 详解 Hashtable

    至于HashTable,本人只想说,除了它们各自的特点是截然相反外,其余性质 以及 用法和HashMap的性质几乎一样, (有关Map集合的基本性质,请观看本人博文-- <详解 Map集合> ...

  3. PHP 将字符串转换为字符集格式UTF8/GB2312/GBK 函数iconv()

     iconv()介绍 iconv函数可以将一种已知的字符集文件转换成另一种已知的字符集文件 iconv('要转化的格式',‘转化后的格式’,‘转化的数据’); 但是转化是经常出错,一般需要在转成的编码 ...

  4. python之pymysql库连接mysql实现增、删、改、查

    安装第三方库pymysql 命令行cmd下通过pip install pymysql进行安装,安装完成后自行pip list可查看对应的版本信息 建立连接 1 #导入pymysql库 2 import ...

  5. mysql 更改默认字符集

    mysql 默认字符集概述 首先,MySQL的字符集问题主要是两个概念: haracter Sets Collations 前者是字符内容及编码,后者是对前者进行比较操作的一些规则.这两个参数集可以在 ...

  6. 基于TextRank算法的文本摘要

    本文介绍TextRank算法及其在多篇单领域文本数据中抽取句子组成摘要中的应用. TextRank 算法是一种用于文本的基于图的排序算法,通过把文本分割成若干组成单元(句子),构建节点连接图,用句子之 ...

  7. 这是那些大佬程序员常用的学习java网站,这就是别人薪资上万的原因

    大学四年,看课本是不可能一直看课本的了,对于学习,特别是自学,善于搜索网上的一些资源来辅助,还是非常有必要的,下面我就把这几年私藏的各种资源,网站贡献出来给你们.主要有:电子书搜索.实用工具.在线视频 ...

  8. tp5.0看点

    前置操作:操作一些其他动作,例如要操作其他表格的数据啊,操作之前要有什么动作为前提或者要注意的动作. 模型事件:操作数据,例如照片的上传修改和删除. 两者的区别在于“前置操作”是动作,而“模型事件”只 ...

  9. 2019-2020-20199303《Linux内核原理与分析》第四周作业

    构造一个简单的Linux内核 Linux是一种开源电脑操作系统内核,它是一个用C语言写成.主要子系统: 1.系统调用接口 2.进程管理 3.内存管理 4.虚拟文件系统 qemu是一个开源模拟处理器,在 ...

  10. redis部署与卸载

    1.先到Redis官网(redis.io)下载redis安装包 cd /tmp wget http://download.redis.io/releases/redis-4.0.10.tar.gz 2 ...