QT外掛學習

pamxy發表於2013-07-28

轉自:http://blog.sina.com.cn/s/blog_63578f140100qr3v.html

外掛是什麼

  • 注意:這兒暫時不考慮靜態外掛(潛意識中總覺得它根本就不算外掛)。

外掛是一個動態庫(共享庫)。動態庫是一個獨立的檔案中的獨立模組,可被多個程式訪問。

先看動態庫的兩種用法

1. 程式連結時指明動態庫

這時程式中包含相應的標頭檔案,編譯時指定標頭檔案路徑,對於qmake來說:

LIBS += -L/path1/path2/.../ -labcd
INCLUDEPATH += /p1/p2/.../

這樣一來,程式啟動時會自動載入需要的連結庫。

2. 程式中動態載入動態庫

執行過程中找到來查詢某個動態庫,載入並解析出其中的某個函式。 Qt提供了QLibrary這個類來封裝各個平臺下的差異。

外掛是提供特定介面的動態庫

  • 它是動態庫
  • 它需要一個或幾個特定的介面(這樣程式才能感知它)
  • 它採用第二種載入方式
  • Qt 為外掛提供了 QPluginLoader,比通用 QLibrary 好用

Qt外掛位置

動態載入,那麼程式怎麼知道去哪兒載入外掛呢?

很多人抱怨,裝有Qt的機器上一切正常,釋出後,jpeg等格式圖片看不成,資料庫無法連線,漢字亂碼...

其實答案很簡單:

QCoreApplication 的 libraryPaths() 中有一些路徑,比如:

  • $QTDIR/plugins
  • 可執行程式所在資料夾

然後程式啟動後,會去這些路徑下的下列子目錄

  • imagesformats

    中 找圖片外掛

    sqldrivers

    中 找資料庫驅動外掛

    codecs

    中 找字元的編解碼外掛

    ...

     

這就是為什麼:將 jpeg4.dll 放到可執行程式所在資料夾的 imagesformats 中即可解決問題的原因。

除此之外,要設定外掛路徑,我們還可以修改:

QCoreApplication::libraryPaths()

通過 addLibraryPath()

QLibraryInfo::location(QLibraryInfo::PluginsPath)

通過 qt.conf 檔案

QT_PLUGIN_PATH

環境變數

Manual 中對此有詳細介紹。

外掛API

Qt 提供了兩個層次的Api

Higher-level

用於擴充套件Qt自身的工程,比如前述的圖片外掛、編解碼外掛等等
前面的外掛位置提到的主要是這個

Lower-level

用於擴充套件 應用程式的功能,比如 QtCreator 本身用了非常多的專有外掛

higher-level

這部分,外掛本身的api很簡單,困難在功能實現上。比如實現一個 style 外掛:

  • 派生 QStyle 或它的子類,實現自己的 Style 類(難點)
  • 派生 QStylePlugin,實現外掛,在它內部建立 Style 的物件

這兩個都沒什麼好說的,只是一個巨集特別關鍵:

Q_EXPORT_PLUGIN2(PluginName, ClassName)

看它的原始碼,也是一堆巨集,貼出來也不好看

# define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) \
Q_PLUGIN_VERIFICATION_DATA \
Q_EXTERN_C Q_DECL_EXPORT \
const char * Q_STANDARD_CALL qt_plugin_query_verification_data() \
{ return qt_plugin_verification_data; } \
Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) * Q_STANDARD_CALL qt_plugin_instance() \
Q_PLUGIN_INSTANCE(PLUGINCLASS)

展開看一下:

static const char qt_plugin_verification_data[] = \
"pattern=QT_PLUGIN_VERIFICATION_DATA\n" \
"version=4.7.0\ndebug=false\nbuildkey=xxx";

extern "C" Q_DECL_EXPORT const char * qt_plugin_query_verification_data()
{
return qt_plugin_verification_data;
}

extern "C" Q_DECL_EXPORT qt_plugin_instance()
{
static QPointer<QObject_instance;
if (!_instance)
_instance = new ClassName;
return _instance;
}

這樣一來舒服多了,匯出了兩個函式:

  • 一個返回值是一個長長的字串,用來校驗差價和程式所用Qt版本什麼的是否匹配
  • 另一個返回的是外掛類的物件,這樣通過該函式就可以使用外掛了。

lower-level

這一部分和本文其他部分關係不是太緊密,而且內容可能比較多,還是單獨出來準備"外掛學習二"吧。

  • 不像圖片外掛、資料庫外掛等,Qt為他們提供了現成的介面。這兒我們必須實現自己的介面(別人也不清楚我們的需求,對吧)

靜態外掛

  • 這個東西怎麼說呢,儘管靜態編譯過Qt,但基本沒用過。所以在這靜態外掛上面應該更沒有什麼發言權。
  • 熟悉Qt靜態編譯的,應該對此更熟一些,因為這是外掛都是靜態的。

靜態外掛使用

這時它是一個靜態庫。靜態庫如何使用?不用多說了,肯定直接連結到程式中。於是我們需要:

  • 包含標頭檔案
  • 指定連結庫

對與Qt提供的 higher-level 外掛,比如圖片外掛 jpeg,這意味著:

程式碼中,(使用巨集,也就是使動態編譯時它不起作用)

  • #include<QtPlugin>

  • Q_IMPORT_PLUGIN(qjpeg)

pro檔案內,(指定要連結的庫)

  • QTPLUGIN += qjpeg

靜態外掛用到的巨集

當靜態編譯時,外掛中的巨集

Q_EXPORT_PLUGIN2(PLUGINNAME, ClassName)

展開為

qt_plugin_instance_PLUGINNAME()
{
static QPointer<QObject> _instance;
if (!_instance)
_instance = new ClassName;
return _instance;
}

同時,工程中的巨集

Q_IMPORT_PLUGIN(PluginName)

展開為:

QObject *qt_plugin_instance_PLUGINNAME();
class StaticPLUGINNAMEPluginInstance
{
public:
StaticPLUGINNAMEPluginInstance() 
{
qRegisterStaticPluginInstanceFunction(qt_plugin_instance_PLUGINNAME);
}
};
static StaticPLUGINNAMEPluginInstance staticPLUGINNAMEInstance;

這也很容易理解,Q_EXPORT_PLUGIN2(PLUGINNAME, ClassName) 中的第一個引數做什麼用了。

參考


相關文章