第一部分:在第一个工程中(宿主工程),写一个插件需要使用的头文件(接口类,没有直接实现的.cpp)
#ifndef ABSTRACTINTERFACE_H
#define ABSTRACTINTERFACE_H
#include
class QWidget;
class AbstractInterface
{
public:
virtual ~AbstractInterface() {}
//-- 由于我想创建的插件是带有UI的,所以类型是QWidget
virtual QWidget *createPluginWidget(QWidget *parent) = 0;
virtual void testFunc() = 0;
};
#define AbstractInterface_iid "Welcome to pay attention to the public number"
Q_DECLARE_INTERFACE(AbstractInterface, AbstractInterface_iid)
#endif // ABSTRACTINTERFACE_H
//-- https://baijiahao.baidu.com/s?id=1652356608638861677&wfr=spider&for=pc
//-- 上次我们是直接在Qt 自带的例子基础上做的修改,直接运行。我们的插件需要继承Qt 的Style插件,之后重新实现自己想要实现的部分。在主程序中直接通过QApplication::setStyle进行调用。
//-- 下面开展我们本次的内容,官方文档说明
//-- 通过插件不仅可以扩展Qt本身,而且可以扩展Qt应用程序。
//-- 这要求应用程序使用QPluginLoader检测和加载插件。 在这种情况下,插件可以提供任意功能,并且不仅限于数据库驱动程序,图像格式,文本编解码器,样式以及扩展Qt功能的其他类型的插件。
// 1. 通过插件使应用程序可扩展涉及以下步骤(应用程序扩展插件步骤):
// * ①编写仅具有纯虚函数的类.即定义一组用于与插件对话的接口(仅具有纯虚函数的类)。
// * ②使用Q_DECLARE_INTERFACE ()宏向Qt的元对象系统声明该接口。该宏就是向 Qt 元对象系统注册一下我写的这个接口。
// * “注册”是什么意思?就是登记呗。我在 Qt 的花名册上写下我的名字,那 Qt 从此就认识我了。该宏的第一个参数是接口类的名字,第二个参数是 iid(相当于一个身份证信息,可以随便写个字符串)。
// *
// * 此时 “通过插件使应用程序可以被扩展” 的前两步就完成了,后面两步之后在宿主程序中加载插件时再介绍。
//至此,我的程序给外界提供了个公共接口,并且为了 Qt 编程方便还向元对象系统注册了一下。
//以后所有的插件都通过这个接口来扩展 app 的功能,当然也可以写更多的接口供外界调用。
后面两步来了,看代码。
在宿主程序中,运行加载插件。
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
#include "abstractinterface.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_clicked();
private:
bool loadPlugin();
private:
Ui::Widget *ui;
//本宿主程序中有一个AbstractInterface对象
AbstractInterface * m_pluginInterface;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "QDir"
#include
//宿主程序加载插件:在应用程序中使用QPluginLoader加载插件
#include
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
loadPlugin();
}
Widget::~Widget()
{
delete ui;
}
//-- 运行时加载插件
bool Widget::loadPlugin()
{
QDir pluginsDir(qApp->applicationDirPath());
qDebug() << qApp->applicationDirPath() << endl;
//遍历PluginWidget目录下的文件,如果实例化成功则使用qobject_cast()测试插件是否实现了给定的接口。【应用程序扩展插件步骤的③和④】
foreach(QString fileName, pluginsDir.entryList(QDir::Files)) {
//-- ③在应用程序中使用QPluginLoader加载插件。
//QPluginLoader 加载插件名
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
qDebug() << fileName << endl;
//-- ④使用qobject_cast()测试插件是否实现了给定的接口
//获取 QObject 示例;
QObject *plugin = pluginLoader.instance();
//转化成接口指针,然后判断不为空即为正常加载。
if(plugin)
{
//-- 在那个项目里有个 AbstractInterface 接口类。我的主程序里是有这个指针的.
m_pluginInterface = qobject_cast<AbstractInterface *>(plugin);
if(m_pluginInterface)
{
//-- 完成插件的调用
m_pluginInterface->testFunc();
//m_pluginInterface->createPluginWidget(ui->pluginWidget)->show();
m_pluginInterface->createPluginWidget(NULL)->show();
}
}
}
}
void Widget::on_pushButton_clicked()
{
m_pluginInterface->createPluginWidget(NULL)->show();
}
main.cpp
#include "widget.h"
#include
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
第二步骤:另一个工程,写插件。这个工程编译出来的dll 文件可以放到合适位置,用于提供给宿主程序加载(即宿主程序加载插件)。
//-- 利用插件类的头文件也称为(接口类),继承这个接口类,进而进行实现。
myfirstplugin.h 继承接口类,并在元对象系统注册,他是一个插件库。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include "abstractinterface.h"
//-- MainWidget的app提供了接口,那么插件当然就是要使用这个接口
//(1)继承这个接口和 QObject。继承接口这个好理解,继承 QObject 是为了使用 Qt 的元对象系统;
class MyFirstPlugin : public QObject, public AbstractInterface
{
Q_OBJECT
public:
explicit MyFirstPlugin(QWidget *parent = 0);
~MyFirstPlugin();
//-- (2)使用Q_INTERFACES ()宏告诉Qt的元对象系统有关接口的信息,告诉元对象系统“我要用这个接口”;
Q_INTERFACES(AbstractInterface)
//-- (3)使用Q_PLUGIN_METADATA ()宏导出插件 (4)pro 文件中添加“CONFIG += plugin”
Q_PLUGIN_METADATA(IID "Welcome to pay attention to the public number." FILE "myfirstplugin.json")
//-- Qt4的导出
//Q_EXPORT_PLUGIN2(pluginName,class);
QWidget * createPluginWidget(QWidget *parent);
void testFunc();
};
#endif // MAINWINDOW_H
// * 插件编写步骤
// * ①声明一个插件类,该类继承自QObject和该插件要提供的接口
// * ②使用Q_INTERFACES ()宏告诉Qt的元对象系统有关接口的信息
// * ③使用Q_PLUGIN_METADATA ()宏导出插件。
// * ④使用合适的.pro文件构建插件。即pro 文件中添加“CONFIG += plugin” 。
// * 在myfirstplugin.h中无法直接包含abstractinterface.h,这时需要修改PluginWidget.pro。
// * 根据创建的目录添加INCLUDEPATH += ../MainWidget,这时就可以include abstractinterface.h了。
myfirstplugin.cpp 实现这个继承接口的 类,在纯虚函数中,做我们要想 具体想要 干啥的事情。(我这里就是返回一个widget,还有打印字符串)
#include "myfirstplugin.h"
#include "ui_pluginWidget.h"
#include "pluginwidget.h"
#include "QDebug"
MyFirstPlugin::MyFirstPlugin(QWidget *parent) :
QObject(parent)
{
}
MyFirstPlugin::~MyFirstPlugin()
{
// delete ui;
}
QWidget *MyFirstPlugin::createPluginWidget(QWidget *parent)
{
PluginWidget *pluginWidget = new PluginWidget(parent);
return pluginWidget;
}
void MyFirstPlugin::testFunc()
{
qDebug() << "this is PluginWodget" << endl;
}
//-- 上述,主要的插件的接口已经写完了;下面我们要写 插件重要干些啥。(可能表述不准确,就是后面的东西是具体插件的功能的实现)
也就是 pluginWidget 对象。和插件关系不大了。
这里就是一个普通的UI
#ifndef PLUGINWIDGET_H
#define PLUGINWIDGET_H
#include
namespace Ui {
class PluginWidget;
}
class PluginWidget : public QWidget
{
Q_OBJECT
public:
explicit PluginWidget(QWidget *parent = 0);
~PluginWidget();
private:
Ui::PluginWidget *ui;
};
#endif // PLUGINWIDGET_H
#include "pluginwidget.h"
#include "ui_pluginwidget.h"
PluginWidget::PluginWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::PluginWidget)
{
ui->setupUi(this);
}
PluginWidget::~PluginWidget()
{
delete ui;
}
自己瞎总结:就是宿主程序 怎么去加载插件的问题。
Qt的插件 主要就是 接口类和实现接口类的类的 元对象系统注册的问题。
其它都是c++中的特性。 比如利用多态,纯抽象类、纯抽象类的实现。