Qt的插件機制
Qt有兩種與插件有關的API。一種用來擴展Qt本身的功能,如自定義數據庫驅動,圖像格式,文本編解碼,自定義分格,等等,稱為Higher-Level API。另一種用于應用程序的功能擴展,稱為Lower-Level API。前一種是建立在后一種的基礎之上的。這里討論的是后一種,即用來擴展應用程序的Lower-level API。
讓應用程序支持插件擴展的步驟:
1. 定義一個接口集(只有純虛函數的類),用來與插件交流。
2. 用宏Q_DECLARE_INTERFACE()將該接口告訴Qt元對象系統。
- Q_DECLARE_INTERFACE(BrushInterface,"com.trolltech.PlugAndPaint.BrushInterface/1.0")
3. 應用程序中用QPluginLoader來裝載插件。
4. 用宏qobject_cast()來確定一個插件是否實現了接口。
- QObject *obj = new QTimer;
- QTimer *timer = qobject_cast<QTimer *>(obj);
寫一個插件的步驟:
1. 聲明插件類,該類從QObject和該插件希望實現的接口繼承而來。
2. 用宏Q_INTERFACES()將該接口告訴Qt元對象系統。
- class BasicToolsPlugin : public QObject,
- public BrushInterface,
- public ShapeInterface,
- public FilterInterface
- {
- Q_OBJECT
- Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)
- public:
- ...
- };
3. 用宏Q_EXPORT_PLUGIN2()導出插件。
- 1 Q_EXPORT_PLUGIN2 ( PluginName, ClassName )
4. 用適當的.pro文件構建插件。下面的代碼聲明了一個接口類:
- class FilterInterface
- {
- public:
- virtual ~FilterInterface() {}
- virtual QStringList filters() const = 0;
- virtual QImage filterImage(const QString &filter, const QImage &image, QWidget* parent)=0;
- };
- Q_DECLARE_INTERFACE(FilterInterface, "com.trolltech.PlugAndPaint.FilterInterface/1.0")
這里是實現該接口的插件類的定義:
- #include <QObject>
- #include <QStringList>
- #include <QImage>
- #include <plugandpaint/interfaces.h>
- class ExtraFiltersPlugin : public QObject, public FilterInterface
- {
- Q_OBJECT
- Q_INTERFACES(FilterInterface)
- public:
- QStringList filters() const;
- QImage filterImage(const QString &filter, const QImage &image,
- QWidget *parent);
- };
示例 Plug & Paint 的文檔詳細解釋了這一過程。與Qt Designer有關的問題請看Creating Custom Widgets for Qt Designer 。 Echo Plugin Example 是一個關于如何實現擴展Qt應用程序的詳細示例。
#p#
裝載插件
裝載插件時。Qt庫有一些健全檢查來確定插件能否被裝載和使用。這就可以同時安裝多個版本和Qt庫配置。
與較高主版本和(或)次版本號的Qt庫鏈接的插件不能被主版本和(或)次版本號較低的庫裝載。
原理: 一個使用新版Qt庫的插件可能用了老版本沒有的新特征。Trolltech有一個只在次版本號升級時添加新功能和API的政策,這就是為什么該測試只看主次版本號,而不看補丁號。
Qt庫和所有插件用一個聯編關鍵字來聯編。Qt庫中的聯編關鍵字被與插件中的聯編關鍵字對照,如果相符,插件就被裝載。如果聯編關鍵字不符,Qt庫就拒絕裝載該插件。
原理: 見下文對聯編關鍵字的解釋。
編譯插件來擴展應用程序時,確保插件和應用程序用同樣的配置這一點很重要。這意味著如果應用程序是release模式編譯的,那么插件也要是release模式。
若將Qt配置為debug和release模式都編譯,但只在release模式下編譯應用程序,就要確保你的插件也是在release模式下編譯的。缺省的,若Qt的debug編譯可用,插件就只在debug模式下編譯。要強制插件用release模式編譯,要在工程中添加:
CONFIG += release這能確保插件兼容應用程序中所用的庫版本。
關鍵字
裝載插件時,Qt核對每一個插件的聯編關鍵字要和自己的匹配,以保證所裝載的是兼容的插件;任何不匹配的插件不會被裝載。
聯編關鍵字包含一下信息:
- Architecture, operating system and compiler.
原理: 在同一編譯器的不同版本并不產生二進制兼容代碼的場合,編譯器的版本也體現在聯編關鍵字里。
Qt庫的配置。這個配置是庫中所缺少特性的列表,因為這些功能對應的API在該庫中不可用。
原理: 兩個同一版本的Qt庫的不同配置不是二進制兼容的。裝載插件的Qt庫使用這個(缺少的)特性列表來判斷插件是不是二進制兼容的。
注意: 也存在這種情況,插件可以使用在兩個不同配置里可用到的特性。但是,編寫插件的開發者需要知道,哪些特性在他們的插件和Qt的公用工具類中都在被使用。Qt庫在裝載插件時會需要復雜的特性與依賴性的查詢確認。這些需求給開發者添了一個不必要的負擔,也增加了裝載插件的系統開銷。為了減少開發時間,降低應用的運行時消耗,可以使用對聯編關鍵字的簡單字符串比較。
可選地,可以在配置腳本命令行指定一個附加的字符串Optionally, an extra string may be specified on the configure script command line.
原理: 在發布帶有應用程序的Qt庫的二進制時,這給開發者提供了一個編寫插件的辦法,這樣寫出來的插件只能被插件鏈接的那個庫所裝載。
為了調試可能需要關閉聯編關鍵字校驗功能,這可以通過將你運行應用程序的環境的環境變量QT_NO_PLUGIN_CHECK設置為非零來實現。
#p#
靜態類
插件能被靜態地鏈接到應用程序。如果你創建了Qt的靜態版本,這僅僅是用來包含Qt的預定義插件的一個選項。
當被作為靜態庫編譯時,Qt提供下面這些靜態插件:
要靜態鏈接這些插件,你的應用程序中要用到宏Q_IMPORT_PLUGIN() 并且要用QTPLUGIN將需要的插件添加到你的編譯中。例如,在main.cpp中:
- #include <QApplication>
- #include <QtPlugin>
- Q_IMPORT_PLUGIN(qjpeg)
- Q_IMPORT_PLUGIN(qgif)
- Q_IMPORT_PLUGIN(qkrcodecs)
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- ...
- return app.exec();
- }
應用程序的.pro 文件中要用下列條目:
- QTPLUGIN += qjpeg \
- qgif \
- qkrcodecs
也可以創建自己的靜態庫,步驟如下:
在插件的 .pro 文件中添加 CONFIG += static 。
應用程序中用宏 Q_IMPORT_PLUGIN() 。
應用程序的.pro 文件中用 LIBS 將靜態庫鏈接進來。
參見示例 Plug & Paint 和相關的插件 Basic Tools 來獲得詳情。
The Plugin Cache
為了加速插件的裝載和確認,裝載插件時收集的信息被緩存到QSettings中。這包括插件是否被成功裝載的信息,以使后面的裝載操作不用再嘗試裝載無效的插件。但是,若一個插件的 last modified 時間戳被修改,插件的緩存條目是無效的并且插件會不管緩存條目中的值而被重新裝載,同時緩存條目本身也會被新的值替代。
這也意味著每一次插件或任何依賴資源(如共享庫)被更新之后時間戳也必須被更新,因為依賴資源可能影響一個插件裝載的結果。
有時,開發插件時,需要從插件緩存中移除條目。因為Qt用QSettings來管理插件緩存,插件的位置是依賴于平臺的;更多關于每一個平臺的信息請參看the QSettings documentation。
例如,Windows中這些條目存儲在注冊表中,每個插件的路徑是以下面兩個字串中的一個開始的:
- HKEY_CURRENT_USER\Software\Trolltech\OrganizationDefaults\Qt Plugin Cache 4.2.debug
- HKEY_CURRENT_USER\Software\Trolltech\OrganizationDefaults\Qt Plugin Cache 4.2.falseDebugging Plugins
有許多問題可能影響到插件在應用程序中的正常運轉。 許多與插件和應用程序的創建方法不同有關, 通常發生在不同的創建系統和過程中。
下表描述的是開發者創建插件時遇到的問題的常見原因:
也可以用QT_DEBUG_PLUGINS環境變量來從Qt中獲得嘗試去裝載的每一個插件的診斷信息。在應用程序的運行環境中把該變量的值設置為非零。
【編輯推薦】