Qt插件开发入门(两种方法:High-Level API接口,Low-Level API接口)
Qt中为我们提供了两种开发插件的方式。一种是使用High-Level API接口,一种是使用Low-Level API接口。所谓High-Level API 是指通过继承Qt为我们提供的特定的插件基类,然后实现一些虚函数、添加需要的宏即可。该种插件开发方式主要是用来扩展Qt库本身的功能,比如自定义数据库驱动、图片格式、文本编码、自定义样式等。而我们为自己的应用程序编写插件来扩展其功能时主要使用第二种方式,即Low-Level API 的方式,该方式不仅能扩展我们自己的应用程序,同样也能像High-Level API 那样用来扩展Qt本身的功能。使用这种方式,我们可以将我们需要扩展的功能写成一个 接口,然后让一个插件类去实现这个接口的功能,再使用Qt提供的用于插件开发的宏,按Qt要求的格式对插件进行声明,之后我们就可以在应用程序中使用QPluginLoader 来动态的加载该插件,从而完成应用程序功能的扩展。由于我们平时主要使用插件来扩展我们自己开发的程序,所以今天主要讲解一下使用Low-Level API开发插件的方式。至于High-Level API 方式,有需要的同学可以自行研读Qt的帮助文档和相关Demo。
想要让Qt编写的应用程序支持插件扩展,需要进行一下步骤:
1.定义一系列的接口,应用程序就是使用这些接口与插件进行功能交互的。(标准c++中没有接口的概念,所以此处的接口指只有纯虚函数的类)。
2.使用 Q_DECLARE_INTERFACE() 宏将这个接口的有关信息告诉Qt的元对象系统。
3.在应用程序中使用QPluginLoader 加载这个插件。
4.使用qobject_cast() 函数检测该插件是否实现了特定的接口。
有了应用程序声明的接口,我们还需要编写我们的插件来真正的实现接口所声明的功能,步骤如下:
1.声明一个插件类,让该类继承QObject 和 应用程序所提供的那个接口。
2.使用Q_INTERFACE() 宏告诉Qt元对象系统这个插件实现了哪些接口。
3.使用Q_PLUGIN_METEDATA() 宏导出这个插件。
4.在.pro 文件的进行相关配置,然后编译该插件。
上面说到使用Low-Level API接口开发插件所用到的几个宏定义,下面我们再来详细的看下每个宏的具体含义。
Q_DECLARE_INTERFACE(ClassName, Identifier):这个宏将一个给定的字符串标识符和ClassName所表示的接口相关联,其中Identifier必须唯一。例如:
#define BrushInterface_iid "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface"
Q_DECLARE_INTERFACE(BrushInterface, BrushInterface_iid)
这个宏通常直接在接口所在的头文件中使用。还有,如果你的接口声明在了一个名称空间中,那么你要确保这个宏的使用位于名称空间外面。例如:
namespace Foo
{
struct MyInterface { ... };
}
Q_DECLARE_INTERFACE(Foo::MyInterface, "org.examples.MyInterface")
Q_IMPORT_PLUGIN(PluginName): 这个宏向应用程序中导入名字为PluginName的插件,这个名字对应于Q_PLUGIN_METADATA() 所在类的类名。这个宏主要用来导入静态插件。
Q_PLUGIN_METADATA(IID ... FILE ...) :这个宏用来声明插件的元数据信息。需要传入被实现接口的IID,和一个保护该插件元数据信息的json文件。注意,这个宏所在的class必须是可默认构造的;另外,FILE是可选的,若传入了一个json文件,则要确保编译系统能找到这个的文件,不然,moc(meta-object compiler) 会因为找不到该文件而失败退出。
刚才讲 Q_IMPORT_PLUGIN 时,提到了静态插件,相对于的也就有动态插件,并且我们使用最多的就是动态插件。下面分别通过一个例子来学习。
动态插件 本质上仍然是一个dll,只不过我们在编写时根据Qt的要求将其配置成了插件,这样我们在使用时就可以通过QPluginLoader 来直接加载该dll,并调用其中的函数;并且,在定义插件时不需要写一堆的函数导出声明。下面,为了便于测试,我们在QtCreator 中新建一个子目录项目(用于包含其他项目的项目,类似于vs的解决方案)并且添加两个项目,一个是dll项目,一个是测试项目。步骤如下:
启动QtCreator,点击文件->新建文件或项目,选择其他项目->子目录项目
输入工程名即可,建立好后,如下:
此时项目为空,因为没有添加子项目。
在工程上 右键->新的子项目,先添加一个测试插件的项目test,如下
选择 QWidget 作为我们窗口的基类,如下:
同理,在DynamicPlugin上点右键->新的子项目,在此我选择一个空的qmake项目作为我们的插件项目,如下:
最终的项目结构如下:
然后,在test工程上右键->添加新文件,添加一个c++头文件interface.h,即我们的接口文件,一会就让我们的插件类实现这个接口。
该文件的内容如下:
#ifndef INTERFACE_H
#define INTERFACE_H
#include <QWidget>
class PluginInterface
{
public:
virtual ~PluginInterface() {}
virtual void SayHello(QWidget *parent) = 0;
};
#define pluginInterface_iid "io.qt.dynamicplugin"
Q_DECLARE_INTERFACE(PluginInterface, pluginInterface_iid)
#endif // INTERFACE_H
在此,为简单起见,我们只定义了一个SayHello() 纯虚函数,并使用Q_DECLARE_INTERFACE宏向Qt元对象系统声明了这个接口。
然后,在plugin工程上点右键->添加新文件->c++类,新建一个plugin类,让其继承QObject和我们自定义的接口,并实现SayHello() 纯虚函数。plugin.h内容如下:
#ifndef PLUGIN_H
#define PLUGIN_H
#include <QObject>
#include "../test/interface.h"
class plugin : public QObject, PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID pluginInterface_iid FILE "plugin.json")
Q_INTERFACES(PluginInterface)
public:
void SayHello(QWidget *parent) Q_DECL_OVERRIDE;
};
#endif // PLUGIN_H
在此,我们同时使用相关宏向Qt元对象系统声明了该插件的相关信息。当然我们还要新建一个json文件,目前我们只想在plugin.json中写一个表示json格式的{} 即可。其实现文件如下:
void Plugin::SayHello(QWidget *parent)
{
QMessageBox::information(parent, "Plugin", "Hello, I'm dynamically loaded.");
}
为简单起见,我在此只弹出一个消息框。
最后也是最重要的一步,就是通过.pro文件,将该项目配置成动态插件,如下:
QT += widgets
TEMPLATE = lib
CONFIG += plugin
HEADERS += \
plugin.h
SOURCES += \
plugin.cpp
DISTFILES += \
plugin.json
其中,TEMPLATE指明这是一个dll工程,不是一个exe工程;config就是用类配置该工程为插件的。
构建该工程,即可在磁盘上生成该插件对应的dll。
接下来,我们在test工程中测试该插件。首先,在test工程的窗口上放一个按钮,并为该按钮关联一个槽函数。所实现的功能就是当点击按钮时,加载插件并调用SayHello() 弹出一个对话框。槽函数内容如下:
void Widget::OnClick()
{
PluginInterface *interface = nullptr;
QPluginLoader pluginLoader("plugin.dll");
QObject *plugin = pluginLoader.instance();
if(plugin)
{
interface = qobject_cast<PluginInterface*>(plugin);
if(interface)
{
interface->SayHello(this);
}
}
}
其中我们先定义了一个插件接口的指针,然后使用QPluginLoader 动态加载我们刚才生成的插件(若不在当前文件夹 下,需指明具体路径),在通过instance() 函数生成一个插件指针,若生成成功,在尝试将该指针转成我们实际需要的插件类型,然后调用插件的SayHello() 函数,弹出对话框。运行如下:
至此,动态插件的开发实例就完成了。
静态插件
上面我们开发动态插件时说过,动态插件其实也是一个dll文件,同理,静态插件其实也就是一个lib文件。所以,我们还以上面的例子来说明。仿照上面的过程,新建一个StaticPlugin的子目录工程,并新建好相关文件。然后,只需要修改三个地方即可实现静态插件的开发。
1.修改plugin工程的pro文件,在config后面添加static配置,即:CONFIG += plugin static
2.修改test工程的pro文件,添加 LIBS += ./libplugin.a,即为test工程引入静态插件所对应的.a文件(gcc)或.lib文件(vs)。若文件不在当前目录下,则需指定具体路径。
3.在main() 函数前添加 Q_IMPORT_PLUGIN(Plugin),即导入静态插件。
其使用方式如下:
void Widget::OnClick()
{
PluginInterface *interface = nullptr;
foreach (QObject *plugin, QPluginLoader::staticInstances())
{
interface = qobject_cast<PluginInterface*>(plugin);
if(interface)
{
interface->SayHello(this);
}
}
}
通过QPluginLoader的静态方法staticInstances()使用加载到当前工程的所有静态插件。我们只需通过遍历,找到我们所需要的特定类型的插件即可。测试结果如下:
---------------------
作者:求道玉
来源:CSDN
原文:https://blog.csdn.net/Amnes1a/article/details/62223210
版权声明:本文为博主原创文章,转载请附上博文链接!
Qt插件开发入门(两种方法:High-Level API接口,Low-Level API接口)的更多相关文章
- jQuery插件开发的两种方法及$.fn.extend的详解(转)
jQuery插件开发的两种方法及$.fn.extend的详解 jQuery插件开发分为两种:1 类级别.2 对象级别,下面为大家详细介绍下 jQuery插件开发分为两种: 1 类级别 类级别你可以 ...
- jQuery插件开发的两种方法及$.fn.extend的详解
jQuery插件开发分为两种: 1 类级别 类级别你可以理解为拓展jquery类,最明显的例子是$.ajax(...),相当于静态方法. 开发扩展其方法时使用$.extend方法,即jQuery.ex ...
- Qt连接数据库的两种方法
我曾经想过,无论在哪个平台下开发,都不要再接触SQL Server了,但显然不行.我们是来看世界的,不是来改变世界的,想通就好. 前两天,尝试了一下Qt下远程访问数据库.在macOS下,用Qt 5.1 ...
- Qt中的布局浅析与弹簧的使用,以及Qt居中的两种方法
1. 布局 为什么要布局: 布局之后窗口的排列是有序的 布局之后窗口的大小发生变化, 控件的大小也会对应变化 如果不对控件布局, 窗口显示出来之后有些控件的看不到的 布局是可以嵌套使用 常用的布局方式 ...
- vue插件开发的两种方法:以通知插件toastr为例
方法一: 1.写插件: 在 src 文件夹下面建 lib 文件夹用于存放插件,lib 文件夹下再建toastr文件夹,在toastr文件夹下新建 toastr.js 和 toastr.vue两个文件. ...
- jQuery插件开发的两种方法
1 类级别 类级别你可以理解为拓展jquery类,最明显的例子是$.ajax(...),相当于静态方法. 开发扩展其方法时使用$.extend方法,即jQuery.extend(object); $. ...
- Qt 之 设置窗口边框的圆角(使用QSS和PaintEvent两种方法)
Qt在设置窗口边框圆角时有两种方式,一种是设置样式,另一种是在paintEvent事件中绘制窗口.下面分别叙述用这两种方式来实现窗口边框圆角的效果. 一.使用setStyleSheet方法 this- ...
- [转]Qt中定时器使用的两种方法
Qt中定时器的使用有两种方法,一种是使用QObject类提供的定时器,还有一种就是使用QTimer类. 其精确度一般依赖于操作系统和硬件,但一般支持20ms.下面将分别介绍两种方法来使用定时器. 方法 ...
- Qt中显示图像的两种方法
博客转载自:https://blog.csdn.net/lg1259156776/article/details/52325361 在Qt中处理图片一般都要用到QImage类,但是QImage的对象不 ...
- QT中获取选中的radioButton的两种方法(动态取得控件的objectName之后,对名字进行比较)
QT中获取选中的radioButton的两种方法 QT中要获取radioButton组中被选中的那个按钮,可以采用两种如下两种办法进行: 方法一:采用对象名称进行获取 代码: 1 QRadioBu ...
随机推荐
- App开发者博客之: 包建强 (专注移动app开发)
http://www.cnblogs.com/Jax/p/4912606.html 著有"App研发录" 一书. Android EventBus源码解析 带你深入理解EventB ...
- 转: MyEclipse 10.0,9.0,8.0 下添加jadClipse反编译插件
MyEclipse 10.0,9.0,8.0 下添加jadClipse反编译插件 (2012-11-19 15:36:35) 转载▼ 标签: myeclipse jad 反编译 插件 it 分类: M ...
- caffe卷积层代码阅读笔记
卷积的实现思想: 通过im2col将image转为一个matrix,将卷积操作转为矩阵乘法运算 通过调用GEMM完毕运算操作 以下两个图是我在知乎中发现的,"盗"用一下,确实非常好 ...
- 自己写的粗糙的Excel数据驱动Http接口测试框架(一)
1.excel用例: 2.用例执行: @Testpublic void BindBank() throws Exception { String fileName = "src/main/j ...
- Linux 命令 indent 用法
此命令用于调整C源码的格式. 在LKD中的例子: indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl filename 参数解释如下: -i :设置缩进的格数 - ...
- C++Singleton的DCLP(双重锁)实现以及性能测评
本文系原创,转载请注明:http://www.cnblogs.com/inevermore/p/4014577.html 根据维基百科,对单例模式的描述是: 确保一个类只有一个实例,并提供对该 ...
- 类的成员函数指针和mem_fun适配器的用法
先来看一个最简单的函数: void foo(int a) { cout << a << endl; } 它的函数指针类型为 void (*)(int); 我们可以这样使用: v ...
- [转]php-fpm配置具体解释
[转]php-fpm配置具体解释 php-fpm具体解释 原文链接:http://php-fpm.anight.org/wiki:http://www.php-fpm.com/翻译:http://sy ...
- python数据类型整理
Python中常见的数据结构可以统称为容器(container).序列(如列表和元组).映射(如字典)以及集合(set)是三类主要的容器. 一.序列(列表.元组和字符串) 序列中的每个元素都有自己的编 ...
- 奥巴马(Obama)获胜演讲全文[中英对照]+高清视频下载
http://www.amznz.com/obama-speech/如果还有人对美国是否凡事都有可能存疑,还有人怀疑美国奠基者的梦想在我们所处的时代是否依然鲜活,还有人质疑我们的民主制度的力量,那么今 ...