[Qt插件]-02創建應用程序插件(插件化開發的一種思路)


本篇是學習Qt Creator快速入門,插件開發的筆記
 
分為兩部分
  1. 創建插件
  2. 使用插件的應用程序(測試插件)
 
插件是被使用的應用程序加載使用的。 是使用插件的應用程序定義接口,插件按照接口來實現。
有幾個需要注意的宏,其他的都是常規的CPP代碼
 
1.創建插件
 
創建一個插件包括以下幾步:
①定義一個插件類,它需要同時繼承自QObject類和 該插件所提供的功能對應的接口類;
②使用Q_INTERFACES()宏在Qt的元對象系統中注冊該接口;
③使用Q_PLUGIN_METADATA()宏導出該插件;
④使用合適的.pro文件構建該插件。
 
 
2.使用插件的應用程序
 
使一個應用程序可以通過插件進行擴展要進行以下幾步:
①定義一組接口(只有純虛函數的抽象類);
②使用Q_DECLARE_INTERFACE()宏在Qt的元對象系統中注冊該接口;
③在應用程序中使用QPluginLoader來加載插件;
④使用qobject_cast()來測試插件是否實現了給定的接口。
 
注意事項: 使用插件的應用程序想要正常運行,需要先編譯插件項目(生成插件嘛)
 
動手實踐
 
下面通過一個小demo來學習這個知識。
這里需要創建兩個項目,一個項目用來生成插件,即dll/so文件;另一個項目是一個測試程序,用來使用插件。
因為這兩個項目中有共用的文件,所以這里將它們放同一個文件夾下,目錄結構如下:
myplugin
----plugin (插件項目)
----plugins (存放生成的插件)
----regexpwindow (使用插件的項目(測試項目))
 
創建插件
創建一個空的qmake項目。Add New添加C++類 RegExpPlugin
----plugin.pro
----regexpplugin.h
----regexpplugin.cpp
----myplugin.json
 
plugin.pro
TARGET = regexppligin
TEMPLATE = lib
CONFIG += plugin
DESTDIR = ../plugins
INCLUDEPATH += ../regexpwindow
HEADERS += \
regexpplugin.h
SOURCES += \
regexpplugin.cpp
 
 
myplugin.json
{}
 
regexpplugin.h
 1 #ifndef REGEXPPLUGIN_H
 2 #define REGEXPPLUGIN_H
 3  
 4 #include <QObject>
 5 #include "regexpinterface.h"
 6  
 7 class RegExpPlugin : public QObject,RegExpInterface
 8 {
 9 Q_OBJECT
10 Q_PLUGIN_METADATA(IID "org.qter.Example.myplugin.RexExpInterface"
11 FILE "myplugin.json")
12 Q_INTERFACES(RegExpInterface)
13  
14 public:
15 QString regexp(const QString &message)override;
16 };
17  
18 #endif // REGEXPPLUGIN_H
 
regexpplugin.cpp
#include "regexpplugin.h"
#include <QRegExp>
#include <QtPlugin>
 
QString RegExpPlugin::regexp(const QString &message)
{
QRegExp rx("\\d+");
rx.indexIn(message);
QString str = rx.cap(0);
return str;
}
說明:
為了使這個類作為一個插件,它需要同時繼承自QObject和RegExpInterface.
RegExpInterface是接口類,用來指明插件要實現的功能,其在regexpinterface.h文件
中定義。這個接口由使用它的程序設計。因為插件是為了擴展原有的程序嘛。
 
Q_PLUGIN_METADATA()宏用於聲明插件的元數據:
其中必須指明IID標識符,標識符是一個字符串,必須保證它的唯一性;
FILE指定一個JSON格式的插件元數據文件,該參數是可選的,其命名一般使用項目名稱即可,內容一般只包含一組大括號。
這里還需要使用Q_INTERFACES()宏將這個接口注冊到Qt的元對象系統中,告知Qt這個類實現了哪個接口。
 
測試插件的程序


 
regexpinterface.h //定義接口,這個類中只能包含純虛函數。
#ifndef REGEXPINTERFACE_H
#define REGEXPINTERFACE_H
 
#include <QString>
 
class RegExpInterface{
 
public:
virtual ~RegExpInterface(){}
virtual QString regexp(const QString &message) = 0;
};
 
//這個是寫在類外面的。浪費了好大一會時間。
Q_DECLARE_INTERFACE(RegExpInterface,
"org.qter.Example.myplugin.RexExpInterface")
 
#endif // REGEXPINTERFACE_H
 
說明:
使用Q_DECLARE_INTERFACE()宏在Qt元對象系統中注冊了該接口,其中第二個參數就是前面指定的IID。
 
 
widget.h // 加載插件
 
引入 #include "regexpinterface.h"
private:
RegExpInterface *regexpinterface_;
bool loadPlugin();
 
widget.cpp
#include "widget.h"
#include "ui_widget.h"

#include <QPluginLoader>
#include <QMessageBox>
#include <QDir>


Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    if(!loadPlugin()){ //如果加載插件失敗
        QMessageBox::information(this,"Error","");
        ui->lineEdit->setEnabled(false);
        ui->pushButton->setEnabled(false);
    }
}

Widget::~Widget()
{
    delete ui;
}

/**
 * @brief Widget::loadPlugin
 * @return
 */
bool Widget::loadPlugin(){

    QDir pluginsDir("../plugins");
    // 遍歷插件目錄
    for (QString fileName : pluginsDir.entryList(QDir::Files)) {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin =  loader.instance();
        if(plugin){
            regexpinterface_ =  qobject_cast<RegExpInterface *>(plugin);
            if(regexpinterface_){
                return true;
            }
        }
    }

    return false;
}

void Widget::on_pushButton_clicked()
{
    QString str = regexpinterface_->regexp(ui->lineEdit->text());
    ui->label_num->setText(str);
}

 

編譯plugin 然后在運行regexpwindow 
 
完!
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM