插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序。
2个特点:
1、基于接口编程
2、提供扩展性
可以有2种插件形式
1、动态插件,也就是动态库
2、静态插件
插件是一个动态库,可以在运行时加载该库以扩展应用程序。Qt使得创建自定义插件并使用QPluginLoader加载它们成为可能。为了确保插件不会丢失,还可以将它们静态链接到可执行文件
示例可以查看Qt的demo: https://doc.qt.io/qt-5/qtwidgets-tools-plugandpaint-app-example.html
对于插件工程来说,需要实现一个接口:
Q_INTERFACES()
声明使用的接口类型;在moc中支持按照对应的接口转换。Q_PLUGIN_METADATA()
宏导出元信息;在moc中导出元数据和类。class ExtraFiltersPlugin : public QObject, public FilterInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface" FILE "extrafilters.json")
Q_INTERFACES(FilterInterface)
public:
QStringList filters() const override;
QImage filterImage(const QString &filter, const QImage &image,
QWidget *parent) override;
};
对于主工程来说,需要定一个插件的接口:
动态:
Q_DECLARE_INTERFACE()
是生成对应接口转换函数 qobject_cast ;Q_INTERFACES 也会用到。
QPluginLoader
来加载插件;qobject_cast()
进行插件造型,判断插件实现了哪个接口;静态:
1、Q_IMPORT_PLUGIN(BasicToolsPlugin),导入类
2、QPluginLoader::staticInstances() 加载静态插件
void MainWindow::loadPlugins()
{
//静态插件
const auto staticInstances = QPluginLoader::staticInstances();
for (QObject *plugin : staticInstances)
populateMenus(plugin);
//动态插件
const auto entryList = pluginsDir.entryList(QDir::Files);
for (const QString &fileName : entryList) {
QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
QObject *plugin = loader.instance();
if (plugin) {
populateMenus(plugin);
pluginFileNames += fileName;
}
}
}
//根据接口判断,也就IID来判断的,下面会讲
void MainWindow::populateMenus(QObject *plugin)
{
//一个插件可以实现多个接口,所以需要多次判断
auto iBrush = qobject_cast(plugin);
if (iBrush)
addToMenu(plugin, iBrush->brushes(), brushMenu, &MainWindow::changeBrush,
brushActionGroup);
auto iShape = qobject_cast(plugin);
if (iShape)
addToMenu(plugin, iShape->shapes(), shapesMenu, &MainWindow::insertShape);
auto iFilter = qobject_cast(plugin);
if (iFilter)
addToMenu(plugin, iFilter->filters(), filterMenu, &MainWindow::applyFilter);
}
宏的作用生成2种函数,主要作用是声明接口转换函数 qobject_cast ;
1、qobject_interface_iid 返回当前类接口id IId,
2、qobject_cast 判断参数是否可以转成当前类,
关联一个唯一的标识类,通过唯一标识可以访问类
#define BrushInterface_iid "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface/1.0"
Q_DECLARE_INTERFACE(BrushInterface, BrushInterface_iid)
# define Q_DECLARE_INTERFACE(IFace, IId) \
template <> inline const char *qobject_interface_iid() \
{ return IId; } \
template <> inline IFace *qobject_cast(QObject *object) \
{ return reinterpret_cast((object ? object->qt_metacast(IId) : nullptr)); } \
template <> inline IFace *qobject_cast(const QObject *object) \
{ return reinterpret_cast((object ? const_cast(object)->qt_metacast(IId) : nullptr)); }
例如:qobject_cast 所依赖qt_metacast是由元对象系统生成的
class BasicToolsPlugin : public QObject,
public BrushInterface,
public ShapeInterface,
public FilterInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface" FILE "basictools.json")
Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)
}
在moc_BasicToolsPlugin.cpp中就生成了qt_metacast方法来判断是否可以转换
void *BasicToolsPlugin::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_BasicToolsPlugin.stringdata0))
return static_cast(this);
if (!strcmp(_clname, "BrushInterface"))
return static_cast< BrushInterface*>(this);
if (!strcmp(_clname, "ShapeInterface"))
return static_cast< ShapeInterface*>(this);
if (!strcmp(_clname, "FilterInterface"))
return static_cast< FilterInterface*>(this);
if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface/1.0"))
return static_cast< BrushInterface*>(this);
if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.ShapeInterface/1.0"))
return static_cast< ShapeInterface*>(this);
if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0"))
return static_cast< FilterInterface*>(this);
return QObject::qt_metacast(_clname);
}
这个宏的作用,是在moc_**.cpp中生成元数据,并且导出插件类
class BasicToolsPlugin : public QObject,
public BrushInterface,
public ShapeInterface,
public FilterInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface" FILE "basictools.json")
}
//moc_**.cpp中生成元数据
static constexpr unsigned char qt_pluginMetaData[] = {
'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',
// metadata version, Qt version, architectural requirements
0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),
0xbf,
// "IID"
0x02, 0x78, 0x36, 'o', 'r', 'g', '.', 'q',
't', '-', 'p', 'r', 'o', 'j', 'e', 'c',
't', '.', 'Q', 't', '.', 'E', 'x', 'a',
'm', 'p', 'l', 'e', 's', '.', 'P', 'l',
'u', 'g', 'A', 'n', 'd', 'P', 'a', 'i',
'n', 't', '.', 'B', 'r', 'u', 's', 'h',
'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
'e',
// "className"
0x03, 0x70, 'B', 'a', 's', 'i', 'c', 'T',
'o', 'o', 'l', 's', 'P', 'l', 'u', 'g',
'i', 'n',
0xff,
};
QT_MOC_EXPORT_PLUGIN(BasicToolsPlugin, BasicToolsPlugin)
在moc_**.cpp中支持通过接口名称转成对应的类型,例如:
org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0
class ExtraFiltersPlugin : public QObject, public FilterInterface
{
Q_OBJECT
Q_INTERFACES(FilterInterface)
};
//为了支持通过接口名称转成对应的类型,org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0
void *ExtraFiltersPlugin::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_ExtraFiltersPlugin.stringdata0))
return static_cast(this);
if (!strcmp(_clname, "FilterInterface"))
return static_cast< FilterInterface*>(this);
if (!strcmp(_clname, "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface/1.0"))
return static_cast< FilterInterface*>(this);
return QObject::qt_metacast(_clname);
}
如果没有使用Q_DECLARE_INTERFACE 对 FilterInterface 进行声明就会报错
//Q_DECLARE_INTERFACE(FilterInterface, FilterInterface_iid)
原因就是 template <> inline const char *qobject_interface_iid
先看一下一个平台的插件,windows的类图
1、基于接口开发,插件只是返回 QPlatformIntegration