qt 从零开始搭建插件框架

系统设计

搭建插件框架的好处

分析

至少分为以下几个模块

  1. 需要至少一个界面插件,用于展示界面
  2. 需要一些功能插件,为界面提供所需功能,例如FTP功能,网络服务,数据库等等
  3. 需要一个插件用于管理其他插件,插件间的通信,维护等等
  4. 需要一个启动程序作为入口

流程

  1. mainApp项目作为启动整个程序的入口,生成传统意义上的exe文件,点击它启动软件
  2. 启动之后,需要加载管理插件的插件项目(命名为QtPluginFramework)
  3. QtPluginFramework加载各个插件:包括界面插件,功能插件等等
  4. 界面显示,用户互动产生命令调用功能插件
  5. 用户退出界面,QtPluginFramework卸载各个插件
  6. mainApp退出

QtPluginFramework项目

分析

插件式开发的好处在于,每个人可以独立的负责某个小的模块,这个模块还可以给其他的项目组用,而且很方便添加其他功能。所以QtPluginFramework项目的作用就是

  1. 定义一个公共接口QtPluginInterface,所有的插件都必须实现这个接口
  2. 加载和卸载单个插件的功能,定义QtPlugin类
  3. 集中管理所有插件的功能,定义QtPluginManage类
  4. 插件间的通信功能,定义QtEvents类
  5. 最后定义QtPluginFramework类,集成以上所有功能

实现

QtPluginInterface

#ifndef QtPluginInterface_H_
#define QtPluginInterface_H_

class CPluginFramework;
class QtPluginInterface
{
public:
	virtual ~QtPluginInterface(){}
	//
	virtual void Start(QtPluginFramework* pPluginFramework) = 0;
	//
	virtual void Stop(QtPluginFramework* pPluginFramework) = 0;
};

Q_DECLARE_INTERFACE(QtPluginInterface, "Plugin.QtPluginInterface")

#endif 

QtPlugin

加载和卸载单个插件

在这里插入代码片

参考文献

参考文献1
参考 qt 官方示例 Echo Plugin Example
怎样用 Qt 写个插件?
在项目中创建并使用自定义Qt插件【VS+Qt项目开发系列】(六)
使用ctkPluginFramework插件系统构建项目实战 **
ctk github
CTK框架使用简明教程
QT——插件的创建和使用

实战

新建库项目

qt 从零开始搭建插件框架_第1张图片
选择chose进入下一下页面,类型选择Qt Plugin,输入一个名称

新建如下文件
qt 从零开始搭建插件框架_第2张图片

首先需要一个虚接口 echointerface.h,在插件工程和要运行插件的工程中都要使用

#ifndef ECHOINTERFACE_H
#define ECHOINTERFACE_H

#include 
#include 

//! [0]
class EchoInterface
{
public:
    virtual ~EchoInterface() = default;
    virtual QString echo(const QString &message) = 0;
};


QT_BEGIN_NAMESPACE

#define EchoInterface_iid "org.qt-project.Qt.Examples.EchoInterface"

Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
QT_END_NAMESPACE

//! [0]
#endif

Q_DECLARE_INTERFACE() 向 Qt 元对象系统注册一下我写的这个接口。
该宏的第一个参数是接口类的名字,第二个参数是 iid(相当于一个身份证信息,可以随便写个字符串)。

至此,我的程序给外界提供了个公共接口,并且为了 Qt 编程方便还向元对象系统注册了一下。以后所有的插件都通过这个接口来扩展 app 的功能,当然也可以写更多的接口供外界调用。

接下来是写插件,要继承这个接口
echoplugin.h


#ifndef ECHOPLUGIN_H
#define ECHOPLUGIN_H

#include 
#include 
#include "echointerface.h"

//! [0]
class EchoPlugin : public QObject, EchoInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.EchoInterface" FILE "MyQtPlugin.json")
    Q_INTERFACES(EchoInterface)

public:
    QString echo(const QString &message) override;
};
//! [0]

#endif

echoplugin.cpp

#include "echoplugin.h"

//! [0]
QString EchoPlugin::echo(const QString &message)
{
    return message;
}
//! [0]

然后编译生成,我们需要
MyQtPlugin.dll MyQtPlugin.lib echointerface.h这三个文件

我们再新建一个工程 做一个界面
qt 从零开始搭建插件框架_第3张图片
把刚才那三个文件复制到现在工程的目录下,把echointerface.h添加到工程里面(右键添加现有文件)
qt 从零开始搭建插件框架_第4张图片
echowindow.h

#ifndef ECHOWINDOW_H
#define ECHOWINDOW_H

#include 
#include "echointerface.h"

QT_BEGIN_NAMESPACE
class QString;
class QLineEdit;
class QLabel;
class QPushButton;
class QGridLayout;
namespace Ui { class echowindow; }
QT_END_NAMESPACE

class echowindow : public QWidget
{
    Q_OBJECT

public:
    echowindow();
    ~echowindow();

private slots:
    void sendEcho();

private:
    void createGUI();
    bool loadPlugin();

    EchoInterface *echoInterface;
    QLineEdit *lineEdit;
    QLabel *label;
    QPushButton *button;
    QGridLayout *layout;
};
#endif // ECHOWINDOW_H

echowindow.cpp

#include "echowindow.h"
#include "ui_echowindow.h"
#include "echowindow.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//! [0]
echowindow::echowindow()
{
    createGUI();
    setLayout(layout);
    setWindowTitle("Echo Plugin Example");

    if (!loadPlugin()) {
        QMessageBox::information(this, "Error", "Could not load the plugin");
        lineEdit->setEnabled(false);
        button->setEnabled(false);
    }
}
//! [0]

//! [1]
void echowindow::sendEcho()
{
    QString text = echoInterface->echo(lineEdit->text());
    label->setText(text);
}
//! [1]

//! [2]
void echowindow::createGUI()
{
    lineEdit = new QLineEdit;
    label = new QLabel;
    label->setFrameStyle(QFrame::Box | QFrame::Plain);
    button = new QPushButton(tr("Send Message"));

    connect(lineEdit, &QLineEdit::editingFinished,
            this, &echowindow::sendEcho);
    connect(button, &QPushButton::clicked,
            this, &echowindow::sendEcho);

    layout = new QGridLayout;
    layout->addWidget(new QLabel(tr("Message:")), 0, 0);
    layout->addWidget(lineEdit, 0, 1);
    layout->addWidget(new QLabel(tr("Answer:")), 1, 0);
    layout->addWidget(label, 1, 1);
    layout->addWidget(button, 2, 1, Qt::AlignRight);
    layout->setSizeConstraint(QLayout::SetFixedSize);
}
//! [2]

//! [3]
bool echowindow::loadPlugin()
{
    QDir pluginsDir(QCoreApplication::applicationDirPath());
#if defined(Q_OS_WIN)
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
    if (pluginsDir.dirName() == "MacOS") {
        pluginsDir.cdUp();
        pluginsDir.cdUp();
        pluginsDir.cdUp();
    }
#endif
    pluginsDir.cd("plugins");
    const QStringList entries = pluginsDir.entryList(QDir::Files);
    for (const QString &fileName : entries) {
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = pluginLoader.instance();
        if (plugin) {
            echoInterface = qobject_cast<EchoInterface *>(plugin);
            if (echoInterface)
                return true;
            pluginLoader.unload();
        }
    }

    return false;
}

echowindow::~echowindow()
{

}


main.cpp

#include "echowindow.h"

#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    echowindow w;
    w.show();
    return a.exec();
}

你可能感兴趣的:(qt,c++)