天天看点

Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用

概述

插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序。插件与宿主程序之间通过接口联系,就像硬件插卡一样,可以被随时删除,插入和修改,所以结构很灵活,容易修改,方便软件的升级和维护。

Qt中的插件

Qt提供了两种API用于创建插件:

  • 一种是高阶API用于扩展Qt本身的功能:如自定义数据库驱动,图像格式,文本编码,自定义样式等等;
  • 一种低阶API用于扩展Qt应用程序。

本文主要是通过低阶API来创建Qt插件,并通过静态、动态两种方式来调用插件,即通过应用程序预留四则预算接口,通过插件来定义四则运算的简单例子。

框架自动搭建

我们利用QT Creator把应用程序和插件的运行框架搭建起来,在EchoPluginTest项目项目新建一个名为EchoPluginApp的应用程序,和一个名为EchoPluginLib的插件。

  1. 首先创建一个子目录项目,名为EchoPluginTest
    Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用
    Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用
  2. 创建应用程序EchoPluginApp
    Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用

这里我们选择的是Qt Widgets Application,并命名为EchoPluginApp

Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用

3.创建EchoPluginLib插件

Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用

选择:Library –> C++库,并在“项目介绍和位置”选择类型为“Qt Plugin”,名称为“EchoPluginLib”

Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用

在类信息中输入类名:“EchoPlugin”,基类默认为“QGenericPlugin”

Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用

至此,基本框架自动生成完成,我们还需进一步对其进行修改。

应用程序中的插件接口创建

在子项目EchoPluginApp中添加一个echointerface.h文件,在该头文件中定义插件接口,应用程序正是通过这些接口来实现额外的功能。

class EchoInterface
{
public:
    virtual ~EchoInterface() {}

    virtual QStringList CalculateType(void) const =;
    virtual double Calculate(QString &type,double xvar, double yvar) =;
};
#define EchoInterface_iid "EchoPluginTest.EchoPluginLib.EchoInterface"
Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
           

EchoInterface

类定义了两个纯虚函数,第一个函数

CalculateType()

返回

QStringList

用于标识插件中定义的运算类型,之所以采用

QStringList

是因为一个插件中可能存在多个运算类型。第二个函数

Calculate()

,用于根据传入的运算类型和运算参数xvar和yvar来返回计算值。

为了能够在运行时查询插件是否实现给定的接口,我们必须使用宏

Q_DECLARE_INTERFACE()

,该宏的第一参数为接口类的名称,第二个参数是一个字符串,用于唯一标记该接口类。

应用程序中插件接口的使用

在应用程序中,首先我们需要定义

loadPlugins()

用于查找插件

void MainWindow::loadPlugins()
{
    //调用静态插件
    foreach (QObject *plugin, QPluginLoader::staticInstances())
        AddToCombo(plugin);
           

首先通过

QPluginLoader::staticInstances()

函数寻找静态插件,并更新到界面中下拉控件中。

接下来,我们去加载动态插件:

//动态调用插件
    QDir pluginDir(qApp->applicationDirPath());
    if(pluginDir.dirName().toLower() == tr("debug") || pluginDir.dirName().toLower() == tr("release") )
    {
        pluginDir.cdUp();
        pluginDir.cdUp();
        pluginDir.cd("plugins");
    }

    //遍历当前 文件夹下文件
    foreach (QString filename, pluginDir.entryList(QDir::Files))
    {
        QPluginLoader pluginloader(pluginDir.absoluteFilePath(filename));
        QObject *plugin = pluginloader.instance();
        if(plugin != )
            AddToCombo(plugin);
    }
           

将变量

pluginDir

设置到当前应用程序工作目录对应的Plugins目录下,并通过函数

entryList

遍历该目录下所有的文件,对每个文件利用

QPluginLoader

去尝试加载插件。通过

QPluginLoader::instance()

函数去识别由插件返回的

QObject

对象,因为如果动态链接库不是qt插件或者编译版本不兼容则,函数将返回空指针。

当函数返回有效插件时,我们将其更新到下拉列表中去。

AddToCombo

函数

void MainWindow::AddToCombo(QObject *pplugin)
{
    EchoInterface *eInterface = qobject_cast<EchoInterface *>(pplugin);
    if(eInterface != )
    {
        QStringList typelist = eInterface->CalculateType();
        foreach (QString ctype, typelist)
        {
            ui->calType->addItem(ctype);
            PluginItem item;
            item.text = ctype;
            item.plugin =pplugin;
            m_pluginItemList.append(item);
        }
    }
}
           

对于每一个插件(不管是动态的,还是静态的),我们都利用

qobject_cast()

函数去检验它的接口类,如果为EchoInterface接口则将插件的运算类型添加到下拉列表中去。

下拉列表选择变化响应槽函数

void MainWindow::on_calType_currentIndexChanged(int index)
{
    if(index >= && index < m_pluginItemList.size())
    {
        curType = m_pluginItemList.at(index).text;
        m_EchoInterface = qobject_cast<EchoInterface *>(m_pluginItemList.at(index).plugin);
    }
    else
    {
        curType = tr("");
        m_EchoInterface = ;
    }
}
           

计算按钮响应槽函数

void MainWindow::on_calculate_clicked()
{
    if(m_EchoInterface != )
    {
        double value = m_EchoInterface->Calculate(curType, xvar, yvar);
        ui->resultVal->setValue(value);
    }
}
           

至此,简单的应用程序基本完成,下面可以通过预留的接口来开发具备各种运算功能的插件了。

插件开发

在开发插件之前,我们需要对Qt Creator自动生成的插件项目做一些修改,因为其是基于高阶应用生成的。

首先,在

EchoPluginLib.pro

文件中的DESTDIR改为

其次,在

EchoPluginLib.h

文件中将类

EchoPlugin

从继承自

public QGenericPlugin

改为继承自

public QObject, public EchoInterface

,更改宏

Q_PLUGIN_METADATA

中IID参数为”EchoPluginTest.EchoPluginLib.EchoInterface”,并添加宏

Q_INTERFACES

,和重写接口类的虚函数。

class EchoPlugin : public QObject, public EchoInterface
{
    Q_OBJECT
#if QT_VERSION >= 0x050000
    Q_PLUGIN_METADATA(IID "EchoPluginTest.EchoPluginLib.EchoInterface" FILE "EchoPluginLib.json")
#endif // QT_VERSION >= 0x050000

    Q_INTERFACES(EchoInterface)

public:
    EchoPlugin(QObject *parent = );
    virtual QStringList CalculateType() const override;
    virtual double Calculate(QString &type,double xvar, double yvar)override;
};
           
  • Q_INTERFACES

    是必须的,用于告诉qt的meta-object compiler,插件接口的基础类,如果没有这个宏,那么在应用程序中,我们无法使用

    qobject_cast()

    去检测接口类。
  • Q_PLUGIN_METADATA

    用于导出该插件,必须包含插件的IID参数,和可选的参数(用于指向一个包含插件MetaData的Json文件)。

插件中重写函数的实现

QStringList EchoPlugin::CalculateType() const
{
    return QStringList()<< tr("Add")<<tr("Sub");
}
double EchoPlugin::Calculate(QString &type, double xvar, double yvar)
{
    if(type == tr("Add"))
        return xvar + yvar;
    else if(type == tr("Sub"))
        return xvar - yvar;
    else return ;
}
           

至此,一个简单的插件就开发完成了。

编译运行

为了项目EchoPluginTest项目编译子项目的顺序(先EchoPluginLib后EchoPluginApp),我们打开EchoPluginTest.pro文件,将其中的SUBDIRS设置如下:

SUBDIRS += \
    EchoPluginLib \
    EchoPluginApp
           

编译并运行EchoPluginTest,应用程序EchoPluginApp中可使用插件EchoPluginLib中定义的加法、减法运算。

Qt Plugin创建及调用概述Qt中的插件框架自动搭建应用程序中的插件接口创建应用程序中插件接口的使用插件开发编译运行插件的静态调用

插件的静态调用

如需静态调用插件,以上项目需做如下更改:

1. 在子项目EchoPluginLib中,打开EchoPluginLib.pro文件,在CONFIG后添加static:

2.在子项目EchoPluginApp中,打开main.cpp文件,添加宏

Q_IMPORT_PLUGIN

,如

Q_IMPORT_PLUGIN(EchoPlugin)

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}
           

3.在子项目EchopluginApp中,打开EchoPluginApp.pro文件中添加插件库。可在EchopluginApp通过右键,选择“添加库”–>“外部库”来自动添加。例如

win32: LIBS += -L$$PWD/../../***/plugins/ -lEchoPluginLib

INCLUDEPATH += $$PWD/../../***/plugins
DEPENDPATH += $$PWD/../../***/plugins
           

4.在子项目EchopluginApp项目中的loadplugins函数中使用

QPluginLoader::staticInstances()

函数来加载插件。

示例代码:EchoPluginTest

更多的资料可参考官网相关例子:Plug & Paint Example, Plug & Paint Basic Tools Example, Plug & Paint Extra Filters Example, Echo Plugin Example。