文章目录
Qt插件开发流程
- 定义一个接口集(只有纯虚函数的类)。
- 用宏
Q_DECLARE_INTERFACE()
将该接口告诉Qt元对象系统 - 声明插件类,插件类继承自
QObject
和插件实现的接口。 - 用宏
Q_INTERFACES()
将插件接口告诉 Qt元对象系统(在头文件中)。
Qt插件调用流程
- 包含接口头文件(只有纯虚函数的类)。
- 应用程序中用
QPluginLoader
来加载插件。 - 用宏
qobject_cast()
来判断一个插件是否实现了接口。
在Vs中开发Qt插件开发实例
创建GUI应用工程PluginsTest
在确保安装有Qt VS Tools
并能正常工作后,创建一个Qt Widgets
应用程序。
创建插件子工程MyPlugin
一个Vs解决方案可以拥有多个工程。Qt的插件和动态库很像,都提供了动态加载的功能,它其实就是生成一个动态库供开发者使用。在解决方法(而不是在刚刚的创建的GUI工程) 上鼠标右键-->add-->new project->Qt Class Library
。
创建完插件子工程后,鼠标右键
->属性
->常规
,我们可以看到Vs默认为我们将MyPlugin
子工程的类型设置成了动态库
类型。该工程默认会将动态库生成到$(SolutionDir)$(Platform)\$(Configuration)
路径下,其中$(SolutionDir)
代表解决解决方案的路径,$(Platform)
代表当前电脑是x86
还是x64
架构,$(Configuration)
表示解决方案是debug
还是release
。若想要指定动态库的生成路径,将该值替换成自己所需要的即可。我们需要记住动态库的生成路径,因为在GUI主工程
中我们需要加载此动态库来加载Qt的插件。
由于GUI工程PluginsTest
需要调用插件工程MyPlugin
产生的动态库,所以需要把MyPlugin
添加为PluginTest
的依赖工程。在PluginTest
上鼠标右键->构建依赖
->工程依赖
->选择MyPlugin
。
插件实现
1. 定义一个接口
在PluginTest
应用中增加一个接口Echonterface
用于调用插件提供的方法,此接口定义在EchoInterface.h
文件中。
#pragma once
#include <QString>
#include <QtPlugin>
// 1.定义一个接口集(只有纯虚函数的类)
class EchoInterface
{
public:
virtual ~EchoInterface() {}
virtual QString echo(const QString& message) = 0;
};
// 2.用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统
QT_BEGIN_NAMESPACE
#define EchoInterface_iid "org.qt-project.Qt.Examples.EchoInterface"
Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
QT_END_NAMESPACE
2. 实现接口
在插件子工程中添加一个插件类EchoPlugin
,该类继承自QObject
和上面讲到的插件接口EchoInterface
:
EchoPlugin.h
文件内容:
#pragma once
#include <QObject>
#include <QtPlugin>
#include "../EchoInterface.h" //在GUI主工程中定义的插件接口
// 3.声明插件类,插件类继承自QObject和插件实现的接口
class EchoPlugin : public QObject, EchoInterface
{
// 3.用宏Q_INTERFACES()将插件接口告诉Qt元对象系统(在头文件中)
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.EchoInterface") // 宏需要声明通过对象实现的接口的IID,并引用一个包含插件元数据的文件
Q_INTERFACES(EchoInterface)
public:
QString echo(const QString& message) override; // 实现的接口:返回字符串消息
};
EchoPlugin.cpp 文件:
#include "EchoPlugin.h"
// 实现的接口:返回字符串消息
QString EchoPlugin::echo(const QString& message)
{
return message;
}
运行代码后,MyPlugin
插件子工程会将动态库生成到$(SolutionDir)$(Platform)\$(Configuration)\
中:
然后我们再在GUI主工程中加载
就可以使用了。
3. GUI应用的实现
PluginsTest.h
文件:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QMessageBox>
#include <QDir>
#include <QPluginLoader>
#include "EchoInterface.h"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget();
private slots:
void sendEcho();
private:
void initUI(); // 初始化UI
bool loadPlugin(); // 加载插件
EchoInterface *m_pEchoInterface;
QLineEdit *m_pLineEdit;
QLabel *m_pLabel;
QPushButton *m_pBtn;
};
PluginsTest.cpp
文件:
#include "PluginsTest.h"
PluginsTest::PluginsTest(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
// 初始化UI
initUI();
// 加载插件
if (!loadPlugin()) {
QMessageBox::information(this, "Error", "Could not load the plugin");
m_pLineEdit->setEnabled(false);
m_pBtn->setEnabled(false);
}
}
PluginsTest::~PluginsTest()
{}
void PluginsTest::sendEcho()
{
// 调用插件接口 - EchoPlugin::echo
QString text = m_pEchoInterface->echo(m_pLineEdit->text());
m_pLabel->setText(text);
}
// 初始化UI
void PluginsTest::initUI()
{
m_pLineEdit = new QLineEdit;
m_pLabel = new QLabel;
m_pLabel->setFrameStyle(QFrame::Box | QFrame::Plain);
m_pBtn = new QPushButton(tr("Send Message"));
connect(m_pLineEdit, &QLineEdit::editingFinished,
this, &PluginsTest::sendEcho);
connect(m_pBtn, &QPushButton::clicked,
this, &PluginsTest::sendEcho);
QGridLayout* m_pLayoutMain = new QGridLayout(this);
m_pLayoutMain->addWidget(new QLabel(tr("Message:")), 0, 0);
m_pLayoutMain->addWidget(m_pLineEdit, 0, 1);
m_pLayoutMain->addWidget(new QLabel(tr("Answer:")), 1, 0);
m_pLayoutMain->addWidget(m_pLabel, 1, 1);
m_pLayoutMain->addWidget(m_pBtn, 2, 1, Qt::AlignRight);
m_pLayoutMain->setSizeConstraint(QLayout::SetFixedSize);
}
// 加载插件
bool PluginsTest::loadPlugin()
{
bool ret = true;
// 获取当前应用程序所在路径
QDir pluginsDir(QDir::currentPath());
#if QT_POINTER_SIZE == 4
pluginsdir.cd("x32");
#elif QT_POINTER_SIZE == 8
pluginsDir.cd("x64");
#endif
#ifdef QT_DEBUG
pluginsDir.cd("debug");
#else
pluginsdir.cd("release");
#endif
qDebug() << pluginsDir.currentPath();
// 遍历plugins目录下所有文件
foreach(QString fileName, pluginsDir.entryList(QDir::Files)){
QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
QObject* plugin = pluginLoader.instance();
if (plugin){
// 获取插件名称
QString pluginName = plugin->metaObject()->className();
if (pluginName == "EchoPlugin"){
// 对插件初始化
m_pEchoInterface = qobject_cast<EchoInterface*>(plugin);
if (m_pEchoInterface)
ret = true;
break;
}else ret = false;
}
}
return ret;
}
运行结果: