跟前面的章节一样,了解一下,会使用qt的插件
Qt插件(Qt Plugin)就是一个共享库(dll文件),可以使用它进行功能的扩展。
Qt中提供了两种API来创建插件:
- 用来扩展Qt本身的高级API,如自定义数据库驱动,图片格式、文本编码和自定义风格等;
- 用来扩展Qt应用程序的低级API。
如果要写一个插件来扩展Qt本身,那么可以子类化合适的插件基类,然后重写一些函数并添加一个宏。
可以通过在帮助中查看How to Create Qt Plugins
关键字来了解本节的内容,这里还可以查看Qt提供的插件基类。Qt中提供了一个风格插件的例子,它是用来扩展Qt风格的,这个可以在帮助中通过Style Plugin Example
关键字查看。这一节主要讲解一个创建Qt应用程序插件的例子,然后还会讲解创建Qt Designer自定义部件的方法。
在设计模式提升窗口部件
讲解创建插件以前,先来讲一下如何在设计模式提升窗口部件。
一般的,使用代码生成的部件无法直接在设计器中使用,但是可以通过生成设计器插件来实现,不过,更简单的方法是使用提升窗口部件的做法,这样可以将设计器中的部件指定为自定义类的实例。下面来看一个具体例子。
新建Qt Widgets应用,项目名称为mybutton,类名为MainWindow,基类保持QMainWindow不变。建立好项目后向其中添加新文件,模板选择C++类,类名为MyButton,基类设置为QPushButton。添加文件完成后进入mybutton.h文件,更改如下:
#ifndef MYBUTTON_H
#define MYBUTTON_H
#include <QPushButton>
class MyButton : public QPushButton
{
Q_OBJECT
public:
explicit MyButton(QWidget *parent = 0);
QString getName(){return "My Button!";}
};
#endif // MYBUTTON_H
然后更改mybutton.cpp文件如下:
#include "mybutton.h"
MyButton::MyButton(QWidget *parent) :
QPushButton(parent)
{
}
下面打开mainwindow. ui文件,在设计模式中向界面上放入一个Push Button,并在pushButton上右击,在弹出的级联菜单中选择“提升为”。
在弹出的对话框中将提升的类名称改为MyButton,头文件会自动生成为 mybutton.h。这时单击右边的“添加”按钮,则会在提升的类列表中进行显示,单击“提升”按钮退出对话框。
现在界面上的这个 PushButton部件已经是MyButton类的实例了,可以通过它调用MyButton 的 getName()函数。在 mainwindow.cpp文件的构造函数中添加如下代码:
QString str = ui->pushButton->getName();
ui->pushButton->setText(str);
这时候运行可以看到,通过这种方式就可以在设计模式使用代码生成的自定义类
创建应用程序插件
创建一个插件时,要先创建一个接口,接口就是一个类,它只包含纯虚函数。插件类要继承自该接口。插件类存储在一个共享库中,因此可以在应用程序运行时进行加载。创建一个插件包括以下几步:
- 定义一个插件类,它需要同时继承自QObject类和该插件所提供的功能对应的接口类;
- 使用Q_INTERFACES()宏在Qt的元对象系统中注册该接口;
- 使用Q_PLUGIN_METADATA()宏导出该插件;
- 使用合适的.pro文件构建该插件。
使一个应用程序可以通过插件进行扩展要进行以下几步:
-
定义一组接口(只有纯虚函数的抽象类);
-
使用Q_DECLARE_INTERFACE()宏在Qt的元对象系统中注册该接口;
-
在应用程序中使用QPluginLoader来加载插件;
-
使用qobject_cast()来测试插件是否实现了给定的接口。
下面通过创建一个过滤字符串中出现的第一个数字的插件来讲解应用程序插件的创建过程。
这里需要创建两个项目,一个项目用来生成插件,即dll文件;另一个项目是一个测试程序﹐用来使用插件。
因为这两个项目中有共用的文件,所以这里将它们放到一个目录中。
1 创建插件
第一步,创建插件类。新建空项目Empty qmake Project ,项目名称为 plugin,在选择路径时指定到一个新建的myplugin目录中。建立好项目后向其中添加一个C++类,类名为RegExpPlugin,基类保持为空。
第二步,定义插件类。将regexpplugin.h文件中的内容更改如下:
#ifndef REGEXPPLUGIN_H
#define REGEXPPLUGIN_H
#include <QObject>
#include "regexpinterface.h"
class RegExpPlugin : public QObject, RegExpInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qter.Examples.myplugin.RegExpInterface"
FILE "myplugin.json")
Q_INTERFACES(RegExpInterface)
public:
QString regexp(const QString &message);
};
#endif
为了使这个类作为一个插件,它需要同时继承自QObject和 RegExpInterface。RegExpInterface
是接口类,用来指明插件要实现的功能,其在regexpinterface.h文件中定义,这个文件在后面的测试程序项目中。
Q_PLUGIN_METADATA()
宏用于声明插件的元数据,其中必须指明IID标识符,标识符是一个字符串,必须保证它的唯一性;FILE指定一个JSON格式的插件元数据文件,该参数是可选的,其命名一般使用项目名称即可,内容一般只包含一组大括号。这里还需要使用Q_INTERFACES()宏将这个接口注册到Qt的元对象系统中,告知Qt这个类实现了哪个接口。最后还声明了一个regexp()函数﹐它是在 RegExpInterface中定义的一个纯虚函数。这里通过重写它来实现该插件具体的功能,就是将字符串中的第一个数字提取出来并返回。
下面到项目目录plugin中新建一个文本文档,输入一组大括号{},然后另存为myplugin.json。
第三步,导出插件。将regexpplugin. cpp文件中的内容更改如下:
#include "regexpplugin.h"
#include <QRegExp>
#include <QtPlugin>
QString RegExpPlugin::regexp(const QString &message)
{
QRegExp rx("\\d+");
rx.indexIn(message);
QString str = rx.cap(0);
return str;
}
定义正则表达式
\\d+ 就表示多个数字,形如 12、44、6763……
str =cap0取第一个字符
第四步,更改项目文件。打开plugin.pro文件,将其内容更改如下:
TEMPLATE = lib
CONFIG += plugin
INCLUDEPATH += ../regexpwindow
HEADERS = regexpplugin.h
SOURCES = regexpplugin.cpp
TARGET = regexpplugin
DESTDIR = ../plugins
这里使用“TEMPLATE=lib”表明该项目要构建库文件,而不是像以前那样的可执行文件;
使用“CONFIG+= plugin”告知 qmake要创建一个插件;
因为项目中使用了regexpwindow目录中regexpinterface.h文件,所以这里将该目录的路径添加到了INCLUDEPATH中;
TARGET 指定了产生的dll文件的名字;
最后,使用 DESTDIR指定了生成的dll文件所在的目录。
因为这个项目中使用了regexpinterface.h文件,而这个文件在另一个项目中,所以现在还无法构建该项目。
2. 使用插件扩展应用程序
第一步,新建Qt Widgets应用。项目名称为regexpwindow,选择路径时仍选择前面建立的myplugin目录。基类选择QWidget,类名保持Widget不变。建立完成后,向该项目中添加新文件,模板选择C++头文件,名称为regexpinterface.h。
第二步,定义接口。将regexpinterface.h文件的内容更改如下:
#ifndef REGEXPINTERFACE_H
#define REGEXPINTERFACE_H
#include <QString>
class RegExpInterface
{
public:
virtual ~RegExpInterface() {}
virtual QString regexp(const QString &message) = 0;
};
Q_DECLARE_INTERFACE(RegExpInterface,
"org.qter.Examples.myplugin.RegExpInterface")
#endif
在接口类中定义了插件要实现的函数,比如这里定义了regexp()函数,可以看到在前面的RegExpPlugin类中已经实现了该函数。这个类中只能包含纯虚函数。最后使用Q_DECLARE_INTERFACE()宏在Qt元对象系统中注册了该接口,其中第二个参数就是前面指定的IID。
第三步,加载插件。先单击widget. ui文件进入设计模式,设计的界面如图所示。将其中显示“无”字的Label的objectName属性更改为labelNum。
然后进入 widget.h文件,先添加头文件#include“regexpinterface.h”,然后在private部分定义一个接口对象指针,再声明一个加载插件函数:
RegExpInterface *regexpInterface;
bool loadPlugin();
现在到widget.cpp文件中:
先添加头文件
#include <QPluginLoader>
#include <QMessageBox>
#include <QDir>
然后在构造函数中调用加载插件函数,如果加载失败,则进行警告:
if (!loadPlugin()) { // 如果无法加载插件
QMessageBox::information(this, "Error", "Could not load the plugin");
ui->lineEdit->setEnabled(false);
ui->pushButton->setEnabled(false);
}
下面添加加载插件函数的定义:
bool Widget::loadPlugin()
{
QDir pluginsDir("../plugins");
// 遍历插件目录
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
if (plugin) {
regexpInterface = qobject_cast<RegExpInterface *>(plugin);
if (regexpInterface)
return true;
}
}
return false;
}
这里使用QDir类指定到存放dll文件的 plugins目录,然后遍历该目录,使用QPluginLoader类来加载插件,并使用qobject_cast()来测试插件是否实现了RegEx-pInterface接口。
最后到设计模式,转到“过滤”按钮的clicked()信号对应的槽,更改如下:
void Widget::on_pushButton_clicked()
{
QString str = regexpInterface->regexp(ui->lineEdit->text());
ui->labelNum->setText(str);
}
使用了regexpInterface接口的regexp函数来获取lineEdit部件输入字符串第一个出现的数字,然后在labelNum显示出来。
第四步,运行程序。先构建plugin项目,在编辑模式左侧项目列表中的plugin目录上右击,在弹出的级联菜单中选择“构建”。构建完成后,在 myplugin目录中会生成plugins目录,里面包含了生成的dll文件。
然后运行regexpwindow项目,输入一些字符串,查看运行结果,如图所示。
到这里为止,创建插件并在应用程序中使用插件的整个过程就介绍完了。这个例子是基于Qt的 Echo Plugin Example示例程序的,可以在帮助中查看这个例子。
Qt中还有一个Plug &. Paint Example演示程序,这是一个比较综合的使用插件扩展应用程序的例子。
创建Qt Designer自定义部件
Qt Designer基于插件的架构使得它可以使用用户设计或者第三方提供的自定义部件,就像使用标准的Qt部件一样。自定义部件中的所有特性在Qt Designer中都是可用的,这包含了部件属性、信号和槽等。
下面通过例子来看一下在 Qt Creator中创建Qt Designer自定义部件的过程。
第一步,创建项目。新建项目,模板选择“其他项目”分类中的“Qt4设计师自定义控件”,项目名称为mydesignerplugin,控件类改为MyDesignerPlugin,然后在右侧指定图标文件的路径,随意选择―张图片即可,其他选项保持默认。后面步骤全部保持默认,单击“下一步”直到完成项目的创建。
第二步,更改部件。可以通过修改mydesignerplugin.h和 mydesignerplugin.cpp文件来修改部件,就像编写普通的类一样。这里进入 mydesignerplugin. cpp文件,添加头文件:
#include <QPushButton>
#include <QHBoxLayout>
然后在构造函数添加如下代码:
//mydeisgnerplugin.cpp
QPushButton *button1 = new QPushButton(this);
QPushButton *button2 = new QPushButton(this);
button1->setText("hello");
button2->setText("Qt!");
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
setLayout(layout);
这里需要在Qt Creator左下角的目标选择器中将构建选项选择为Release,因为只有Release版本的插件在Qt Designer中才可以使用。
现在按下Ctrl+Shift+B构建项目,完成后可以看到,项目目录下生成的目录中的release目录中已经生成了相应的dll文件。
第三步,在Qt Designer中使用插件。为了使Qt Designer可以自动检测到插件﹐需要将生成的dll文件复制到Qt安装目录的plugins目录下的designer目录中,笔者这里的路径为C:\Qt\Qt5.6.0\5.6\mingw49_32\plugins \designer。这时在开始菜单中运行Qt Designer,可以发现已经可以使用自定义的部件了,效果如图所示。
这里需要说明一下,现在生成的插件只能在Qt Designer中使用,却不能在QtCreator的设计模式中使用,Adding Qt Designer Plugins关键字对应的文档中提到了这一点。
这是因为现在使用的 Windows版本的Qt Creator是使用Microsoft VisualStudio的编译器生成的,而在Qt Creator中编译项目使用的是 MinGW/g++编译器
它们的Bulid Keys(包含了体系结构,操作系统、编译器等信息,具体可以参见 Deploying Plugins关键字)不同,所以生成的插件无法在Qt Creator中使用。
如果安装了MSVC版本的Qt,则使用该版本编译插件时将生成的mydesignerpluginplugin. dll 文件复制到Qt Creator安装目录中,笔者这里的路径是C:\Qt\Qt5.6.1 -VS2013\Tools\QtCreator \ bin\plugins \designer,重新启动Qt Creator,在设计模式就可以看到自定义的部件了,如图所示。
创建Qt Designer自定义部件的更多内容可以在帮助中通过Creating CustomWidgets for Qt Designer
关键字查看。Qt中还提供了几个创建Qt Designer插件的例子,比如 Custom Widget Plugin等,可以作为参考。