Qt的源代碼中經過 Q<pluginType>Factory、Q<pluginType>Plugin 和 Q<pluginType> 這三個類實現了Qt的插件載入機制,linux
這個機制可用於載入特定種類的插件。比方經過 QPlatformIntegrationFactory\QPlatformIntegrationPlugin\QPlatformIntegration
三個類可以實現平臺類QPA插件(PlatformIntegration)的載入,經過QPlatformInputContextFactory\QPlatformInputContextPlugin\
QPlatformInputContext三個類可以實現輸入法類插件(InputContext)的載入。
如下自底向上介紹Qt的插件載入機制
app
class Q<pluginType>Plugin { ... Q<pluginType> * creat(...) ; // 返回一個Q<pluginType>類型的指針。這個函數的功能通常都很easy, // 其內部僅僅需要 new 一個 Q<pluginName> 類的對象,並返回其指針 }
QFactoryLoader::QObject *instance(int index)
instance 返回一個
QOjbect類或其子類的指針,而這個指針一般會被映射成 Q<pluginType>Plugin 類型。
這樣一來。Qt 框架要載入某個插件時,僅僅需要先調用 QFactoryLoader::instance(index) 返回一個該插件相應的
Q<pluginType>Plugin指針。再經過這個指針調用 Q<pluginType>Plugin::creat(...) 方法,就可以得到插件主體
類Q<pluginName>的一個實例。
框架
template <class PluginInterface, class FactoryInterface> PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key) { const int index = loader->indexOf(key); // 依據插件的keyword查找該插件所屬的庫在庫列表中的索引 if (index != -1) { QObject *factoryObject = loader->instance(index); // 載入插件所屬的庫 if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject)) if (PluginInterface *result = factory->create(key)) return result; // 返回插件的實體類 } return 0; } template <class PluginInterface, class FactoryInterface, class Parameter1> PluginInterface *qLoadPlugin1(const QFactoryLoader *loader, const QString &key, const Parameter1 ¶meter1) { const int index = loader->indexOf(key); if (index != -1) { QObject *factoryObject = loader->instance(index); if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject)) if (PluginInterface *result = factory->create(key, parameter1)) return result; } return 0; }
其定義大體例如如下:
函數
class Q<pluginType>Factory { public: static QStringList keys(...) ; // 得到與 Q<pluginType> 類型的插件相關的keyword列表, // keyword通常用於描寫敘述插件的名稱等屬性,這個keyword列表中的每個 // 元素都相應一個實際的插件。如 QPlatformInputContextFactory::keys(...) // 返回的就是輸入法插件的名稱列表。 static Q<pluginType> * creat(...) ; // 返回一個Q<pluginType>類型的指針(應用程序所需的插件) }
比方QPlatformIntegrationFactory用於「生產」平臺類插件。
QPlatformInputContextFactory用於「生產」輸入法類插件。普通狀況下,Q<pluginType>Factory 類並無實際的成員變量。而僅僅有幾個
靜態的方法,所以一個Qt應用程序中不需要將這個類實例化。而是直接使用這個類的靜態方法。另外,Qt還會爲每個Q<pluginType>Factory 類
綁定一個 QFactoryLoader 對象,這個對象專門負責載入這一類別的插件。
Qt 框架的頂層要載入某一類插件時,僅僅需與相應的Q<pluginType>Factory 類打交道就能夠。post
比方。在Qt應用程序初始化過程當中,它發現本身
ui
需要一個輸入法插件了,就會直接調用 QPlatformInputContextFactory::creat(...) 來生產一個輸入法類插件。而不用再管這個插件載入過程當中的細節。spa
返回的這個輸入法類插件到底是什麼。是ibus? 仍是fcitx?插件
這些全然由用戶經過環境變量QT_IM_MODULE指定,指針
QPlatformInputContextFactory::create()方法中會去檢測這個環境變量。code
static QPlatformIntegration *platform_integration; static QPlatformIntegration *platformIntegration() { return platform_integration; }
GuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); ....
QPlatformIntegration *ret = loadIntegration(directLoader(), platform, paramList, argc, argv))
static inline QPlatformIntegration *loadIntegration(QFactoryLoader *loader, const QString &key, const QStringList ¶meters, int &argc, char ** argv) { const int index = loader->indexOf(key); if (index != -1) { // factory 指向相應的平臺插件類的實例。如QLinuxFbIntegrationPlugin類; // 接着調用其creat方法生成並返回一個 QPlatformIntegration 類的實例的指針, // 這個指針將終於賦值給 QGuiApplicationPrivate::platform_integration, // 應用程序就獲得了本身的執行平臺. // 同一時候由此可知。假設想本身寫一個QPA平臺插件,僅僅需派生一個QPlatformIntegrationPlugin類和一個QPlatformIntegration類就能夠。 if (QPlatformIntegrationPlugin *factory = qobject_cast<QPlatformIntegrationPlugin *>(loader->instance(index))) if (QPlatformIntegration *result = factory->create(key, parameters, argc, argv)) return result; } return 0; }
QPlatformInputContext *QPlatformInputContextFactory::create() { QPlatformInputContext *ic = 0; QString icString = QString::fromLatin1(qgetenv("QT_IM_MODULE")); // 檢測環境變量QT_IM_MODULE。依據它選擇要載入的輸入法插件 if (icString == QLatin1String("none")) return 0; ic = create(icString); // 調用還有一個create函數載入輸入法插件 if (ic && ic->isValid()) return ic; // 如下的代碼暫不理會 delete ic; ic = 0; QStringList k = keys(); for (int i = 0; i < k.size(); ++i) { if (k.at(i) == icString) continue; ic = create(k.at(i)); if (ic && ic->isValid()) return ic; delete ic; ic = 0; } return 0; } QPlatformInputContext *QPlatformInputContextFactory::create(const QString& key) { QStringList paramList = key.split(QLatin1Char(':')); const QString platform = paramList.takeFirst().toLower(); #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) if (QPlatformInputContext *ret = qLoadPlugin1<QPlatformInputContext, QPlatformInputContextPlugin>(loader(), platform, paramList)) // 依據key(插件名稱)來載入相應的庫並實例化(產生一個QPlatformInputContext類型的指針)。 return ret; #endif return 0; }
Qt中能載入庫或插件的幾個類:
QLibrary ,
QPluginLoader ,
QFactoryLoader ,
QStaticPlugin (臨時不研究這個)
QLibrary 和 QPluginLoader 依賴的'私有數據類'都是 QLibraryPrivate。 一個QLibrary或QPluginLoader的對象都有一個QLibraryPrivate對象,相應一個庫或插件;
QFactoryLoader 依賴的'私有數據類'是 QFactoryLoaderPrivate , 但 QFactoryLoaderPrivate 類中又包括了一個QLibraryPrivate列表。這個列表中有多個
QLibraryPrivate類型的元素。相應一系列的庫或插件;
因此可見,QLibraryPrivate是Qt中與庫或插件相關的核心數據類,每個庫都相應一個QLibraryPrivate對象。
<2>
1. Qt Assistant 中搜索 How to Create Qt Plugins ,這一段具體說明了建立插件的方法。
主要有高級API(Higher-level API)和低級API(Lower-level API)兩種API可以用來寫插件。
高級API的用法可以在Qt源代碼中看到很是多實例。低級API的使用演示樣例在本系列文章的最後給出。
2. 假設不會寫插件的 .pro 文件,可以在Qt Assistant 中搜索 qmake Manual , 這一頁裏有很是多連接是與編寫project文件相關的,如 qmake Language 連接講 .pro 文件的語法,Variables 連接講.pro 文件裏的變量(如QT、CONFIG、TEMPLATE等變量), Replace Functions 和 Replace Functions 連接講一些內建的函數(可以在.pro文件裏使用)