基於Qt的CTK框架的使用
QT的plugin插件的創建方式
在介紹CTK框架的使用方法之前我們首先介紹一下QT的plugin插件的創建方式。
QT提供兩種API來創建插件:
- 擴展Qt庫本身的高級API。例如:定製database drivers, image formats, text codecs, custom styles, etc.
- 擴展應用程序的低級API
如果你像創建一個可以在Qt Designer 下使用的插件,請查看Qt Designer module documentation 。
擴展應用程序的低級API:
不是隻用Qt自己可以加載插件,Qt Application 也可以擴展插件。 qt加載插件使用QPluginLoader類來實現。
要擴展Qt Application 使用插件需要完成如下的步驟:
1. 定義一系列和插件調用的純虛類來作爲調用的接口
2. 使用 Q_DECLARE_INTERFACE()
註冊接口到 元對象系統
3. 使用QpluginLoader 加載插件
4. 使用 qobject_cast() 測試該插件是否實現了該接口
要創建插件需要如下步驟:
1. 首先聲明一個繼承QObject和 插件接口類
2. 使用Q_INTERFACES() 註冊接口到元對象系統
3. 使用Q_PLUGIN_METADATA()導出數據到元對象系統
4. 對。pro文件編譯
示例代碼:
class FilterInterface
{
public:
virtual ~FilterInterface() {}
virtual QStringList filters() const = 0;
virtual QImage filterImage(const QString &filter, const QImage &image,
QWidget *parent) = 0;
};
#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);
};
技術原理
插件就是動態庫, 插件技術是使用c++中的多態,定義抽象類接口, 然後再PLugin中實現抽象類接口。
plugin可以動態加載, 動態更新。 可以實現應用的熱插拔技術。 對內存的使用效率比較高。
當要使用的時候用interface 類指針指向 Plugin 就可以使用插件中的函數了。
CTK系統可以管理這些插件, 當需要的時候就可以直接從系統中獲取相應的插件。 然後用想用的抽象接口指向這個插件就可以使用了。
技術框架
插件框架採用分層, 模塊化管理。CTK系統可以管理插件的生命週期, 在運行時動態加載和卸載插件從而實現軟件用的熱插拔。
ctk框架就像是一個倉庫來存儲所有的插件, 要使用的時候只需要從倉庫裏拿出相應的插件實例就可以。而不用關係怎麼找到這個實例, 怎麼管理這個實例
技術使用
項目架構分析
每一箇中型或大型的項目,在實際開發之前,其代碼組織架構一定會經過仔細的規劃,比如:目錄架構(頭文件放在哪個目錄;
lib庫放在哪個目錄;開發源代碼放在哪個目錄;生成的項目插件放在哪個目錄;最終發佈程序放在哪個目錄)。
現在,假設我們要爲圖書館開發一個圖書管理程序,稱之爲:LibraryProject,然後在該文件夾下有:includes,libs,plugins,bin,application等文件夾。
其基本用途如下:applications用於放置項目加載程序的源碼;bin下包含conf、plugins目錄,bin下存放項目入口exe程序以及獨立運行時依賴的dll文件,
bin/conf下存放項目配置文件,bin/plugins下放置項目中所有使用到自己開發的插件;includes目錄用於放置頭文件,包括第三方頭文件以及項目中自己定義的接口文件等;
libs用於放置項目中使用的第三方開源庫的lib文件;plugins用於放置項目中所有的插件開發源碼;而base.pri是用來定義或加載項目中通用的內容,
比如:INCLUDEPATH在該文件中定義則在其它插件子項目中只要include一下這個文件,就可以使用includes中包含的所有頭文件。
基於CTK框架的插件的編寫規則
1 interface接口定義
首先是interface接口我們以後稱之爲service(服務) 該類是抽象類, 在c++裏叫做純虛類(如果不知到自己搜索 這裏不做過多的介紹)
服務類的定義示例代碼如下:
#ifndef CONTROLPLUGIN_SERVICE_H
#define CONTROLPLUGIN_SERVICE_H
#include <qobject.h>
struct ControlPlugin_Service
{
public:
virtual ~ControlPlugin_Service(){}
virtual void init()=0;
};
Q_DECLARE_INTERFACE(ControlPlugin_Service, "org.commontk.pluginControl")
#endif // CONTROLPLUGIN_SERVICE_H
virtual ~ControlPlugin_Service(){}
析構函數我們定義成虛函數, 原因: 由於提供的接口只有服務類, 要使用插件的內容必須要服務類指針指向我們的實現類。
由於調用者沒有我們的實現類的定義和聲明所有不可能直接析構我們的實現類這樣就可能造成內存泄漏,
但是使用虛析構函數這樣當析構這個服務類是就會自動析構對應的實現類的實例。
Q_DECLARE_INTERFACE(ControlPlugin_Service, "org.commontk.pluginControl")
//聲明使用服務的接口和唯一id, 系統會定義一些宏函數
2服務類的實現
接下來就是我們服務類的實現了, 在這裏我們必須繼承QObject, 和服務類 並且聲明接口Q_INTERFACE()
//h
class ControlPlugin_Plugin : public QObject, public ControlPlugin_Service
{
Q_OBJECT
Q_INTERFACES(ControlPlugin_Service)
public:
ControlPlugin_Plugin(ctkPluginContext *pc);
virtual void init();
};
#endif // CONTROLPLUGIN_PLUGIN_H
//cpp
#include "controlplugin_plugin.h"
#include <ctkPluginContext.h>
#include <QDebug>
ControlPlugin_Plugin::ControlPlugin_Plugin(ctkPluginContext *pc)
{
pc->registerService<ControlPlugin_Service>(this);
//把服務類註冊到我們的ctk系統供我們獲取該類的實現類實例
}
void ControlPlugin_Plugin::init()
{
qDebug()<<"hello";
}
3 ctkPluginActivator (ctk的生命週期類)
接下來是最重要的類 該類必須繼承ctkPluginActivator (ctk的生命週期類), 裏面函數得有start(), stop(),
s是一個指向服務類的指針,該類析構後自動析構服務類。
//h
#ifndef CONTROLSHARELIB_H
#define CONTROLSHARELIB_H
#include <ctkPluginActivator.h>
#include "controlplugin_service.h"
class ControlShareLib : public QObject, public ctkPluginActivator
{
Q_OBJECT
Q_INTERFACES(ctkPluginActivator)
#if (QT_VERSION_MAJOR>4)
Q_PLUGIN_METADATA(IID "ControlShareLibd")
#endif
public:
void start(ctkPluginContext *contex);
void stop(ctkPluginContext *context);
private:
QScopedPointer<ControlPlugin_Service> s;
};
#endif // CONTROLSHARELIB_H
//cpp
void ControlShareLib::start(ctkPluginContext *contex)
{
s.reset(new ControlPlugin_Plugin(contex));
}
void ControlShareLib::stop(ctkPluginContext *context)
{
Q_UNUSED(context)
}
4內部資源文件MANIFEST.MF 對插件的描述
資源路徑名必須是 $TARGET/META-INF/MANIFEST.MF
例如生成庫是 libplugin.so.0.0 TARGET是plugin
MANIFEST.MF 內容示例如下:
Plugin-SymbolicName: ControlShareLib
Plugin-ActivationPolicy: eager
Plugin-Category: test
Plugin-ContactAddress: http://www.commontk.org
Plugin-Description: Test plugin for framework, ControlShareLib
Plugin-Name: ControlShareLib
Plugin-Vendor: CommonTK
Plugin-Version: 1.0.1
詳細內容可以書寫如下:
const QString ctkPluginConstants::SYSTEM_PLUGIN_LOCATION = "System Plugin";
const QString ctkPluginConstants::SYSTEM_PLUGIN_SYMBOLICNAME = "system.plugin";
const QString ctkPluginConstants::FRAMEWORK_VERSION = "org.commontk.pluginfw.version";
const QString ctkPluginConstants::FRAMEWORK_VENDOR = "org.commontk.pluginfw.vendor";
const QString ctkPluginConstants::FRAMEWORK_STORAGE = "org.commontk.pluginfw.storage";
const QString ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN = "org.commontk.pluginfw.storage.clean";
const QString ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT = "onFirstInit";
const QString ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS = "org.commontk.pluginfw.loadhints";
const QString ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES = "org.commontk.pluginfw.preloadlibs";
const QString ctkPluginConstants::PLUGIN_SYMBOLICNAME = "Plugin-SymbolicName";
const QString ctkPluginConstants::PLUGIN_COPYRIGHT = "Plugin-Copyright";
const QString ctkPluginConstants::PLUGIN_DESCRIPTION = "Plugin-Description";
const QString ctkPluginConstants::PLUGIN_NAME = "Plugin-Name";
const QString ctkPluginConstants::PLUGIN_VENDOR = "Plugin-Vendor";
const QString ctkPluginConstants::PLUGIN_LOCALIZATION = "Plugin-Localization";
const QString ctkPluginConstants::PLUGIN_LOCALIZATION_DEFAULT_BASENAME = "CTK-INF/l10n/plugin";
const QString ctkPluginConstants::REQUIRE_PLUGIN = "Require-Plugin";
const QString ctkPluginConstants::PLUGIN_VERSION_ATTRIBUTE = "plugin-version";
const QString ctkPluginConstants::PLUGIN_VERSION = "Plugin-Version";
const QString ctkPluginConstants::PLUGIN_ACTIVATIONPOLICY = "Plugin-ActivationPolicy";
const QString ctkPluginConstants::PLUGIN_UPDATELOCATION = "Plugin-UpdateLocation";
const QString ctkPluginConstants::ACTIVATION_EAGER = "eager";
const QString ctkPluginConstants::ACTIVATION_LAZY = "lazy";
const QString ctkPluginConstants::RESOLUTION_DIRECTIVE = "resolution";
const QString ctkPluginConstants::RESOLUTION_MANDATORY = "mandatory";
const QString ctkPluginConstants::RESOLUTION_OPTIONAL = "optional";
const QString ctkPluginConstants::OBJECTCLASS = "objectclass";
// ATTENTION!!! If the value is changed, change also ctkEventConstants::SERVICE_ID
const QString ctkPluginConstants::SERVICE_ID = "service.id";
// ATTENTION!!! If the value is changed, change also ctkEventConstants::SERVICE_PID
const QString ctkPluginConstants::SERVICE_PID = "service.pid";
const QString ctkPluginConstants::SERVICE_RANKING = "service.ranking";
const QString ctkPluginConstants::SERVICE_VENDOR = "service.vendor";
const QString ctkPluginConstants::SERVICE_DESCRIPTION = "service.description";
5最後附上Qt的工程文件。
#-------------------------------------------------
#
# Project created by QtCreator 2016-12-27T16:04:46
#
#-------------------------------------------------
QT -= gui
TEMPLATE = lib //生成目標爲庫
CONFIG += c++11 //支持c++11
DEFINES += CONTROLSHARELIB_LIBRARY
DESTDIR = $$PWD/../../bin/plugins //生成插件的存放路徑
SOURCES += controlsharelib.cpp \
controlplugin_plugin.cpp
HEADERS += controlsharelib.h\
controlplugin_plugin.h \
controlplugin_service.h
unix {
target.path = /usr/lib
INSTALLS += target
}
INCLUDEPATH += $$PWD/../../include/ctkH //需要用到的頭文件路徑
LIBS += -L$$PWD/../../bin -lCTKCore -lCTKPluginFramework \ //需要用到庫文件路徑
RESOURCES += \
controlsharelib.qrc
ctkPluginContext 詳解
類作用:ctkPluginContext 是一個在框架內部的插件執行上下文。 該上下文可以存取其它插件的方法, 實現插件間的通信。
內部方法作用如下:
- 通過框架發送事件。
- 用框架服務註冊服務類。
- 從框架註冊服務中檢索服務引用 (ServiceReferences)
- 獲取和釋放一個參考的引用
- 安裝插件到服務
- 獲取框架中更新的插件列表
- 獲取一個插件 ctkPlugin 類
- 通過框架爲plugin創建一個flile存儲在數據庫裏