本篇是學習Qt Creator快速入門,插件開發的筆記
分為兩部分
-
創建插件
-
使用插件的應用程序(測試插件)
插件是被使用的應用程序加載使用的。 是使用插件的應用程序定義接口,插件按照接口來實現。
有幾個需要注意的宏,其他的都是常規的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
完!