对于一个大型软件系统来说,实现plugin是一件很美妙的事情,一个成功的plugin系统可以使软件增色不少。Plugin最大的功能是在一定程度内提高了软件的灵活度和可扩展性。一个设计精良的server软件plugin系统甚至在server程序不退出的情况下可以调用新加入的plugin,实现不间断服务的升级。那么,Qt是怎样实现它的plugin系统呢?
使用Qt创建plugin和在程序中调用plugin是很简单的事情,Qt提供了很多helper class供大家使用。总体来说,Qt的plugin分为2种,按照Qt文档的说法,一种是高等级的plugin。其实说白了就是已经确定interface的Qt本身的plugin。(大家可能都知道,Qt的很多功能,像数据库驱动、图片格式支持、文字内码等都是通过plugin实现的)举个例子来说,Qt可能本身没有西班牙语(希望这次西班牙夺冠:-)的文字内码,但是程序员可以通过按照codec interface写出西班牙语的codec plugin从而使Qt支持西班牙语。
另一种是低等级的plugin。就是该plugin的interface也需要程序员自己编写。所以如果你知道怎么写一个低等级的plugin并使用它之后,高等级的plugin也就完全掌握了。下面我就重点说说低等级的plugin在Qt里实现的一些要点。
从编程的角度,重点还是OOP。所谓的plugin,其实就是一些按照特定interface写成的子类。该Interface必须是虚基类,且所有函数(除了析构)都是虚函数。而所谓的plugin就是继承该虚基类和QObject的子类。当程序调用该plugin的某个函数时,是通过该plugin的虚基类在运行时动态绑定至子类的vtable执行的。所以Qt实现plugin的基础还是OOP的继承和多态。
举个大家都知道的例子来说明,PhotoShop(可能它并不是这么实现的)的所有滤镜有个统一的interface虚基类,该类提供了一个虚函数doSomeWork用于实现滤镜效果。当用户选择某个特殊滤镜时,程序会调用plugin中该滤镜class的doSomeWork实现函数来执行该操作,从而实现特定的滤镜功能。
那么为什么该plugin类不但要继承interface类,还需要继承QObject类呢?原因是调用plugin时需要该plugin类QObject那部分的meta信息。如果大家看过例子代码,会发现,用QPluginLoader调用plugin的文件后,关键的一步是确定该plugin是什么类型的。简单的另人惊讶,一句qobject_cast就搞定了。刚看到这句我百思不得其解,好在Qt有源代码可看,看了源代码发现qobject_cast类似于标准C++的dynamic_cast,且无需RTTI支持并能跨DLL。在代码中,qobject_cast是通过QObject的metaobject的cast函数来实现的。那么该函数是怎么写的呢?
plugin 是一个实现了一个或多个接口的DLL,下面我们将介绍在QT中如何创建PLUG IN 和如何动态加载PLUGIN
一、创建Plugin(插件)
先新建一个库项目
选择chose进入下一下页面,类型选择Qt Plugin,输入一个名称:我输入的是plugin
再点击下一步到
再点击下一步直到完成
我们在.pro文件里加上
QT +=widgets
DESTDIR =./Mydll
它的意思是我们把生成的内容放到这个文件夹里,如果没有它会自动生成
点击项目把Shadow build 去掉勾选
这样一个Qt Plugin框架就搭建好了。
1、定义一个接口MyInterface.h
MyInterface.h代码如下
#ifndef MYINTERFACE_H
#define MYINTERFACE_H
#include
class MyInterface {
public:
virtual QWidget *CreateWidget(QWidget *parent)=0;
virtual int add(int a,int b)=0;
};
Q_DECLARE_INTERFACE(MyInterface,"sdafasdfsadfsadfsadfsadf");
#endif // MYINTERFACE_H
2、插件类头文件MyPlugin.h
#ifndef MYPLUGIN_H
#define MYPLUGIN_H
#include
#include "myinterface.h"
class myPlugin : public QObject,public MyInterface
{
Q_OBJECT
#if QT_VERSION >= 0x050000
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QGenericPluginFactoryInterface" FILE "plugin.json")
Q_INTERFACES(MyInterface);
#endif // QT_VERSION >= 0x050000
public:
//myPlugin(QObject *parent = 0);
QWidget *CreateWidget(QWidget *parent);
int add(int a,int b);
};
这里添加一个MyForm的widget为实现CreateWidget调用
3、Myplugin.cpp
#include "myplugin.h"
#include "myform.h" //这里我加了一个form
#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(plugin, myPlugin)
#endif // QT_VERSION < 0x050000
QWidget *myPlugin::CreateWidget(QWidget *parent)
{
return new MyForm(parent);
}
int myPlugin::add(int a, int b)
{
return a+b;
}
重新构建在mydll里面就生成了plugin.dll
二、如何加载使用plugin插件
使用plugin插件需要接口头文件 Myinterface.h和plugin.dll两个文件
1、新建一个mainwindow的项目文件
在类里定义一个 QWidget*m_Form;
加载函数如下
//加载plugin
bool MainWindow::LoadPlugin(QString pluginpath)
{
QFile file(pluginpath);
if (!file.exists())
{
QMessageBox::warning(this,tr("错误信息"),tr("找不到%1文件").arg(pluginpath));
return false;
}
QPluginLoader loader(pluginpath);
QObject *instance = loader.instance(); //
if (instance!= NULL)
{
qDebug()<is loaded";
MyInterface *avc = qobject_cast<MyInterface *>(instance);
m_Form = avc->CreateWidget(this);
m_Form->show();
ui->lineEdit->setText(QString::number(avc->add(7,8)));
return true;
}
else {
QMessageBox::information(this,"failed to load plugin",loader.errorString());
}
// 需要手动释放
delete instance;
}