本文从我原先发布在 CSDN 的文章迁移而来,原文链接:https://blog.csdn.net/wang112031/article/details/105348798。

feiyangqingyun大神写的Qt-组态软件设计 非常值得仔细学习研读。

应用程序插件化,提高了程序可扩展性和灵活性,降低了程序的耦合度,有利于各模块的独立维护,加快项目的维护更新。越来越多的软件都是基于插件化实现,比如Qt Creator, Notepad++ 等等。

上一篇博客主要实现了自定义Designer 插件,Qt 已经帮我们实现接口类,所以实现起来也非常容易;减少造轮子,开发应用程序插件也是可以完全使用这一套接口类的,所以这里自定义应用程序控件,没有在重新编译插件库,直接使用上一次博客编译好的插件,在应用程序里面使用就可以。

通过插件扩展应用程序功能(即应用使用插件)主要分为如下几步:

  1. 定义一个接口集(只有纯虚函数的类),用来与插件通信。
  2. 用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统。 3)应用程序中用QPluginLoader来加载插件。 4)用宏qobject_cast()来判断一个插件是否实现了接口。

创建一个Qt Widgets Application 任意项目,由于使用了Designer 的接口类,所以1),2)两步接口类已经帮我们实现,可以看一下接口类的头文件:

#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H

#include <QtCore/qobject.h>
#include <QtCore/qstring.h>
#include <QtGui/qicon.h>

QT_BEGIN_NAMESPACE

class QWidget;
class QDesignerFormEditorInterface;

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

    virtual QString name() const = 0;
    virtual QString group() const = 0;
    virtual QString toolTip() const = 0;
    virtual QString whatsThis() const = 0;
    virtual QString includeFile() const = 0;
    virtual QIcon icon() const = 0;

    virtual bool isContainer() const = 0;

    virtual QWidget *createWidget(QWidget *parent) = 0;

    virtual bool isInitialized() const { return false; }
    virtual void initialize(QDesignerFormEditorInterface *core) { Q_UNUSED(core); }

    virtual QString domXml() const
    {
        return QString::fromUtf8("<widget class=\"%1\" name=\"%2\"/>")
        .arg(name()).arg(name().toLower());
    }

virtual QString codeTemplate() const { return QString(); }
};

#define QDesignerCustomWidgetInterface_iid "org.qt-project.QDesignerCustomWidgetInterface"

Q_DECLARE_INTERFACE(QDesignerCustomWidgetInterface, QDesignerCustomWidgetInterface_iid)

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

    virtual QList<QDesignerCustomWidgetInterface*> customWidgets() const = 0;
};

#define QDesignerCustomWidgetCollectionInterface_iid "org.qt-project.Qt.QDesignerCustomWidgetCollectionInterface"

Q_DECLARE_INTERFACE(QDesignerCustomWidgetCollectionInterface, QDesignerCustomWidgetCollectionInterface_iid)

QT_END_NAMESPACE

#endif // CUSTOMWIDGET_H

故pro 文件需要添加:

QT += designer

需要我们实现的就只有3),4)步骤,添加代码如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPluginLoader>
#include <QDebug>
#include <QDir>
#include <QStringList>
#include <QDesignerCustomWidgetInterface> /*包含接口类头文件*/
#include <QMetaProperty>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QString DirPath=QApplication::applicationDirPath();
    DirPath=DirPath.left(DirPath.lastIndexOf("/"));
    QString pluginfilename;
    pluginfilename = DirPath+"/qmybatteryplugind.dll";
    qDebug()<<pluginfilename;
    QPluginLoader loader( pluginfilename );

    bool b = loader.load();
    if(b)
        qDebug("loder ok!\r\n");
    else
        qDebug("loder error!\r\n");

    QObject *plugin = loader.instance();
    if (plugin){
        QDesignerCustomWidgetInterface *Interface = qobject_cast<QDesignerCustomWidgetInterface *>(plugin);
        if( Interface ){
            qDebug("loader.instance() OK\r\n");
            qDebug()<< "name:" << Interface->name() <<"icon:"<<Interface->icon();

            QWidget *Widget = Interface->createWidget(ui->widget);
            Widget->resize(100,60);

            const QMetaObject *metaobject = Widget->metaObject();

            int count = metaobject->propertyCount(); //获取元对象的所有属性
            int index = metaobject->propertyOffset();//获取当前控件的第一个属性index

            for (int i = index; i < count; ++i) {
                QMetaProperty metaproperty = metaobject->property(i);

                const char *name = metaproperty.name();
                QVariant value = Widget->property(name);

                qDebug() << name << value;
            }

        Widget->setProperty("value", 30.0);

        //ui->objectController->setObject(Widget);
    }
}
else
    qDebug("loader.instance() error\r\n");
}

最终运行结果: