Qt点滴 | Toolbar in plugin-based app -- 基于插件的Qt程序 之 工具条

目录:

摘要

本文主要记录如何实现使用dll插件在主窗体中添加工具条。

如何创建Qt插件

Qt提供了两套API创建插件:

  • 高级API —— 扩展Qt自身自定义数据库驱动、图片格式、文字编码、自定义样式等;
  • 底层API —— 扩展Qt编写的应用程序。

高级API:

以后有机会碰到再写

底层API:

不仅Qt可以通过插件扩展自身,Qt应用程序也可以。应用程序须通过QPluginLoader去检测和加载插件。如此一来,插件就可以提供不限于数据库驱动、图片格式、文字编码、自定义样式等功能。

通过插件来扩展应用程序涉及以下步骤:
1. 定义一系列的接口(只有纯虚函数的类)与插件进行交互;
2. 使用宏Q_DECLARE_INTERFACE()告诉Qt的meta-object system关于插件的信息;
3. 在应用程序中使用QPluginLoader加载插件;
4. 使用qobject_cast()测试插件是否实现了指定的接口。

例如,下面有一个接口类的定义:

class FilterInterface
{
public:
    virtual ~FilterInterface() {}
    virtual QStringList filters() const = 0;
    virtual QImage filterImage(const QString &filter, const QImage &image,
                               QWidget *parent) = 0;
};

来自 http://doc.qt.io/qt-5/plugins-howto.html
下面是实现该接口插件的定义:

#include <QObject>
#include <QtPlugin>
#include <QStringList>
#include <QImage>
#include <plugandpaint/interfaces.h>
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;
    QImage filterImage(const QString &filter, const QImage &image,
                       QWidget *parent);
};

来自 http://doc.qt.io/qt-5/plugins-howto.html

简单代码实现

完整代码参见我的Github:GEO-LIKE
1.定义接口
创建一个头文件,类名为ShapePluginInterface,类型为shared library。接口类中所有函数都必须为纯虚函数,且接口设计要包含所有主函数中要用的功能。
定义接口

#include <QObject>
#include <QString>
#include <QtWidgets/QMainWindow>

class ShapePluginInterface{
public:
    virtual ~ShapePluginInterface(){}
    virtual QString description() = 0;
    virtual void generateShape(QString shapeType) = 0;
    virtual void initialization(QMainWindow* window) = 0;
};
#define SHAPEPLUGININTERFACE_IID "com.geolike.shapeplugininterface"
Q_DECLARE_INTERFACE(ShapePluginInterface, SHAPEPLUGININTERFACE_IID)

接口文件中显式定义了4个虚函数,都必须实现。

  • ~ShapePluginInterface()
  • QString description()对插件的简单描述,主程序可以通过该函数读取一些信息。
  • void generateShape(QString shapeType)主要功能函数,输入为一个字符串。可以定义其他的输出类型,如
QPixmap filterImage(QPixmap img);
  • void initialization(QMainWindow* window)初始化插件,调用该函数在主窗体创建工具条。

关于接口的一些知识:

#define SHAPEPLUGININTERFACE_IID "com.geolike.shapeplugininterface"
Q_DECLARE_INTERFACE(ShapePluginInterface, SHAPEPLUGININTERFACE_IID)

定义了一个常量SHAPEPLUGININTERFACE_IID并将其声明为接口的ID。关于这些知识,这里介绍的很详细

2. 实现接口的插件类
在Qt Creator中创建一个C++ Library,类名为BasicShapePlugin
创建插件库

//basicshapeplugin.h

#include "basicshapeplugin_global.h"
#include "shapeplugininterface.h" //要包含接口文件
··· ···

class BASICSHAPEPLUGINSHARED_EXPORT BasicShapePlugin:
        public QObject,
        public ShapePluginInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "com.geolike.shapeplugininterface")
    Q_INTERFACES(ShapePluginInterface)

public:
    BasicShapePlugin();
    ~BasicShapePlugin();
    QString description();
    void generateShape(QString shapeType);
    void initialization(QMainWindow* window);

public slots:
    void on_Action_triggered();
};

要点:

  • 插件要使用宏Q_OBJECT声明为QObject对象
  • Q_PLUGIN_METADATA 声明接口ID
  • Q_INTERFACES(ShapePluginInterface) 声明接口
    一个接口文件中可能定义了许多个接口,一个接口文件貌似只有一个IID。(参考Qt官方示例Plug & Paint

最后我只定义了一个槽函数void on_Action_triggered() 该函数用来响应工具条中的Actions。

//basicshapeplugin.cpp
#include "basicshapeplugin.h"

BasicShapePlugin::BasicShapePlugin(){}
BasicShapePlugin::~BasicShapePlugin(){}

QString BasicShapePlugin::description()
{
    return "This is a plugin to generate shapes.";
}

void BasicShapePlugin::generateShape(QString shapeType)
{
    qDebug() << shapeType;
}

void BasicShapePlugin::on_Action_triggered()
{
    qDebug() << "trigered!";
    generateShape("Action Triggered!");
}

void BasicShapePlugin::initialization(QMainWindow *window)
{
    auto *toolBar = new QToolBar;
    auto *addShapeAction = toolBar->addAction("Shapes");
    addShapeAction->setToolTip("Return a shape");
    connect(addShapeAction, &QAction::triggered, this, &BasicShapePlugin::on_Action_triggered);
    window->addToolBar(toolBar);
}

要点:

  • generateShape(QString shapeType)直接打印输入的字符串
  • initialization(QMainWindow *window)最重要的一个函数,输入主窗体的指针*window,然后使用下面语句,将tooBar添加到主窗体中。
window->addToolBar(toolBar);
  • initialization(QMainWindow *window)定义了一个ActionaddShapeAction,这里注意Action的创建方法,是toolBar->addAction(QString)的返回值,之前尝试过直接new QAction,但是一直报信号和接口参数不匹配,在StackOverFlow请教别人之后,得到了可行的方法:
auto *toolBar = new QToolBar;
auto *addShapeAction = toolBar->addAction("Shapes");

3. 主窗体调用

//mainwindow.h

#include "shapeplugininterface.h"
··· ···
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
··· ···
private:
    void getPluginList();
    Ui::MainWindow *ui;
};

这里定义了一个 getPluginList() 来加载插件

//mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#define PLUGIN_SUBFOLDER "/plugins/"

··· ···
void MainWindow::getPluginList()
{
    QDir pluginsDir(qApp->applicationDirPath() +  PLUGIN_SUBFOLDER);
    QFileInfoList plugins = pluginsDir.entryInfoList( QDir::NoDotAndDotDot | QDir::Files, QDir::Name);
    foreach (QFileInfo plugin, plugins) {
        if(QLibrary::isLibrary(plugin.absoluteFilePath())){
            QPluginLoader pluginLoader(plugin.absoluteFilePath(),this);
            if(dynamic_cast<ShapePluginInterface*>(pluginLoader.instance())) {
                ui->pluginList->addItem(plugin.fileName());
                ShapePluginInterface *shape_plugin =
                        dynamic_cast<ShapePluginInterface*>(pluginLoader.instance());
                shape_plugin->initialization(this);
//                pluginLoader.unload();
                emit pluginLoaded();
            }
            else
                {//do somthing}
        }
        else
        { //some warrings  }
        if(ui->pluginList->count() <= 0)
        { //some warrings  }
    }
}

使用下面的语句调用,最重要的还是shape_plugin->initialization(this)

ShapePluginInterface *shape_plugin =dynamic_cast<ShapePluginInterface*>(pluginLoader.instance());
shape_plugin->initialization(this);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值