閱讀本文大概需要 6 分鐘
在上一篇大概了解了關(guān)于Qt Creator
基礎(chǔ)知識(shí)后[1],本篇先學(xué)習(xí)下框架基本結(jié)構(gòu)曾我,這樣能夠清晰的知道這個(gè)框架當(dāng)中包含那些文件粉怕、文件夾、工程文件抒巢,這些文件分別代表什么意思以及有什么作用
文件結(jié)構(gòu)
打開(kāi)下載好的源碼贫贝,如下目錄所示
可以看出來(lái),文件和文件夾很多蛉谜,不要被這些表面嚇著稚晚,我們真正需要關(guān)心的沒(méi)有幾個(gè),需要重點(diǎn)關(guān)注的我加粗顯示了
- bin文件夾
- dist 文件夾
- doc 文件夾
- qbs 文件夾
- scripts 文件夾
- share 文件夾
- src 文件夾
- tests 文件夾
- docs.pri
- qtcreator.pri
- qtcreator.pro
- qtcreator.qbs
- qtcreatordata.pri
- README.md
這里我們主要要關(guān)注src
文件夾型诚,這個(gè)下面是這個(gè)框架的源碼,其它的文件夾先不看
qtcreator.pri
文件是項(xiàng)目工程中的一些通用配置客燕,比如版本號(hào),一些庫(kù)的輸出路徑定義狰贯,每個(gè)插件或者子工程都會(huì)包含該配置文件也搓,方便直接配置工程一些變量(具體怎么配置,后面會(huì)講解到)
qtcreator.pro
文件是主工程文件涵紊,要打開(kāi)編譯源碼也是需要打開(kāi)該工程文件進(jìn)行加載的
PS: 涉及到 qbs 相關(guān)內(nèi)容可以不用關(guān)注了傍妒,Qt Build Suite 也是一種跨平臺(tái)的編譯工具,目前使用較少無(wú)需關(guān)注
框架結(jié)構(gòu)
下面來(lái)詳細(xì)看下工程結(jié)構(gòu)是如何管理的栖袋,以及整個(gè)框架原理
使用 Qt Creator
打開(kāi)工程后你會(huì)發(fā)現(xiàn)有很多子工程項(xiàng)目拍顷,這個(gè)時(shí)候不要亂、不要怕塘幅,我們目前只需要關(guān)心三個(gè)部分就可以了
- libs
- plugins
- app
libs部分
libs
工程下面包含了常用的一些通用方法昔案,我們目前關(guān)注三個(gè)即可
Aggregation
工程
這個(gè)類(lèi)提供了「打包」功能,可以將很多組件打包成一個(gè)整體电媳,整個(gè)理解起來(lái)有點(diǎn)抽象踏揣,你可以理解為將多個(gè)對(duì)象封裝成一個(gè)對(duì)象,這個(gè)對(duì)象對(duì)外提供了所有對(duì)象的接口屬性和方法
-
Aggregation
集合內(nèi)部每個(gè)組件對(duì)象都可以互相轉(zhuǎn)化 -
Aggregation
集合內(nèi)每個(gè)對(duì)象的生命周期被綁定在了一起匾乓,即一個(gè)在全部在捞稿,一個(gè)被刪除析構(gòu)那么其余的組件也就會(huì)被析構(gòu)
extensionsystem
工程
這個(gè)類(lèi)實(shí)現(xiàn)了插件的管理功能,是整個(gè)框架的核心部分拼缝,所有的插件生命周期管理都在這個(gè)類(lèi)里面實(shí)現(xiàn)
-
IPlugin
插件基類(lèi)娱局,后面所有的插件都是繼承自它來(lái)實(shí)現(xiàn)所有功能的,有三個(gè)重點(diǎn)方法需要關(guān)注
virtual bool initialize(const QStringList &arguments, QString *errorString) = 0;
virtual void extensionsInitialized() = 0;
virtual bool delayedInitialize() { return false; }
插件的初始化咧七,外部依賴(lài)初始化衰齐,延遲初始化,這三個(gè)虛函數(shù)用來(lái)初始化每個(gè)插件各自的一些資源信息继阻。外部依賴(lài)那個(gè)也尤為重要耻涛,比如我們某個(gè)插件同時(shí)依賴(lài)多個(gè)其它插件废酷,那么就需要在這里處理等待其它插件加載完成才算完成
-
PluginManager
插件管理單例類(lèi),整個(gè)框架只有一份抹缕,負(fù)責(zé)框架插件的管理澈蟆,隨著程序退出它的聲明周期才結(jié)束 -
PluginManagerPrivate
插件管理具體實(shí)現(xiàn)邏輯類(lèi),看名字就很清楚卓研,典型的P-D
指針關(guān)系,這樣是為了把插件系統(tǒng)擴(kuò)展的具體實(shí)現(xiàn)隱藏不給外部暴露趴俘,這種技巧在后面很多代碼中經(jīng)常會(huì)見(jiàn)到,也是值的我們?nèi)W(xué)習(xí) -
PluginSpec
插件核心類(lèi)奏赘,該類(lèi)實(shí)現(xiàn)插件的所有屬性
class EXTENSIONSYSTEM_EXPORT PluginSpec
{
public:
enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
~PluginSpec();
// 插件名字
QString name() const;
// 插件版本
QString version() const;
// 插件兼容版本
QString compatVersion() const;
// 插件提供者
QString vendor() const;
// 插件版權(quán)
QString copyright() const;
// 插件協(xié)議
QString license() const;
// 插件描述
QString description() const;
// 插件主頁(yè) URL
QString url() const;
// 插件類(lèi)別哮幢,用于在界面分組顯示插件信息
QString category() const;
}
每個(gè)插件(每個(gè)動(dòng)態(tài)庫(kù))都有一份該對(duì)象,用來(lái)記錄該插件的所有屬性信息志珍,這些屬性信息是通過(guò) json
配置文件讀入的橙垢,這些信息被稱(chēng)為插件的「元信息」,后面關(guān)注插件實(shí)現(xiàn)會(huì)提到
utils 工程
這個(gè)工程里面封裝了一些基礎(chǔ)功能算法類(lèi)伦糯,比如文件操作柜某、數(shù)據(jù)排序操作、json交互操作敛纲、字符串操作集合等喂击,還有一些基礎(chǔ)封裝控件實(shí)現(xiàn)也在這個(gè)里面
比如后面要提到的核心插件主窗口QMainWindow
類(lèi),基類(lèi)就在在這里
class QTCREATOR_UTILS_EXPORT AppMainWindow : public QMainWindow
{
Q_OBJECT
public:
AppMainWindow();
public slots:
void raiseWindow();
signals:
void deviceChange();
#ifdef Q_OS_WIN
protected:
virtual bool winEvent(MSG *message, long *result);
virtual bool event(QEvent *event);
#endif
private:
const int m_deviceEventId;
};
這里主要是一些事件變化后通知外部處理淤翔,比如這里如果主題發(fā)生改變發(fā)送對(duì)應(yīng)信號(hào)出去翰绊,設(shè)備發(fā)生改變(插拔光驅(qū)等)發(fā)出一個(gè)設(shè)備改變事件到 Qt
事件隊(duì)列去處理
plugin 部分
這部分是每個(gè)插件實(shí)現(xiàn)部分,重點(diǎn)需要關(guān)注核心插件corePlugin
的實(shí)現(xiàn)旁壮,其它插件都是要依賴(lài)核心插件來(lái)實(shí)現(xiàn)業(yè)務(wù)功能
在這個(gè)插件里面主要初始化了主窗口监嗜、菜單管理類(lèi)實(shí)例以及一些模式管理對(duì)象初始化
后面我們會(huì)看到各種各樣的插件,比如你打開(kāi)Qt Creator
的時(shí)候首頁(yè)顯示的內(nèi)容抡谐,也是單獨(dú)的一個(gè)插件裁奇,名字叫做weilcome
每個(gè)插件都有一個(gè)標(biāo)識(shí)ID
,用來(lái)區(qū)分是你自己寫(xiě)的插件麦撵,防止別人惡意修改插件
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Core.json")
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Welcome.json")
每個(gè)插件還有一個(gè)對(duì)應(yīng)的元數(shù)據(jù)描述配置文件刽肠,這個(gè)文件配置了該插件的一些基本信息,比如插件名字免胃、版本音五、所有權(quán)、依賴(lài)那些插件等羔沙,這些配置信息在編譯時(shí)會(huì)寫(xiě)進(jìn)該插件動(dòng)態(tài)庫(kù)當(dāng)中躺涝,采用的是Qt
的元對(duì)象技術(shù)來(lái)實(shí)現(xiàn)的,這樣在插件加載運(yùn)行時(shí)就能通過(guò)反射動(dòng)態(tài)獲取這些信息撬碟,繼而用來(lái)進(jìn)行一些插件之間加載關(guān)系的驗(yàn)證
一個(gè)簡(jiǎn)單的配置描述如下所示
{
\"Name\" : \"Welcome\",
\"Version\" : \"$$QTCREATOR_VERSION\",
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
\"Vendor\" : \"The Qt Company Ltd\",
\"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
\"License\" : [ \"Commercial Usage\",
\"\",
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
\"\",
\"GNU General Public License Usage\",
\"\",
\"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
],
\"Category\" : \"Qt Creator\",
\"Description\" : \"Secondary Welcome Screen Plugin.\",
\"Url\" : \"http://www.qt.io\",
$$dependencyList
}
其中很關(guān)鍵的是一些變量值诞挨,比如$$QTCREATOR_VERSION
,通過(guò)這個(gè)變量直接可以讀取到我們?cè)诠こ?code>qtcreator.pri中定義的變量值呢蛤,繼而快速統(tǒng)一加載顯示惶傻,其它變量值獲取類(lèi)似
其次,需要關(guān)注的是每個(gè)插件的配置依賴(lài)文件比如welcome_dependencies.pri
其障,該文件中包含了依賴(lài)那些庫(kù)那些插件
# 插件名字
QTC_PLUGIN_NAME = Welcome
# 插件依賴(lài)的庫(kù)
QTC_LIB_DEPENDS += \
extensionsystem \
utils
# 插件依賴(lài)的插件
QTC_PLUGIN_DEPENDS += \
coreplugin
某個(gè)插進(jìn)依賴(lài)那些插件和動(dòng)態(tài)庫(kù)银室,只需要在對(duì)應(yīng)位置追加其名字即可,工程配置文件會(huì)自動(dòng)進(jìn)行加載励翼,這樣編寫(xiě)可以減少很多重復(fù)工作蜈敢,而且插件依賴(lài)關(guān)系也很清楚的看到
app 部分
這個(gè)部分是程序入口實(shí)現(xiàn)部分,這里主要是獲取插件路徑汽抚,初始化插件抓狭、配置文件,加載每個(gè)插件造烁,如果都沒(méi)有錯(cuò)誤否过,那么初始化完成后主界面就會(huì)顯示出來(lái),直接看主函數(shù)入口看就行
關(guān)鍵部分是插件管理器的初始化惭蟋,設(shè)置插件搜索路徑后對(duì)每個(gè)插件進(jìn)行初始化操作
PluginManager pluginManager;
PluginManager::setPluginIID(QLatin1String("org.qt-project.Qt.QtCreatorPlugin"));
PluginManager::setGlobalSettings(globalSettings);
PluginManager::setSettings(settings);
......
const QStringList pluginPaths = getPluginPaths() + customPluginPaths;
PluginManager::setPluginPaths(pluginPaths);
......
PluginManager::loadPlugins();
在這里還有一個(gè)需要注意的地方苗桂,就是這個(gè)文件app_version.h.in
這個(gè)是一個(gè)模板文件,qmake
加載執(zhí)行完畢后告组,會(huì)在臨時(shí)目錄下生成對(duì)應(yīng)的頭文件app_version.h
#pragma once
namespace Core {
namespace Constants {
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
const char IDE_DISPLAY_NAME[] = \"$${IDE_DISPLAY_NAME}\";
const char IDE_ID[] = \"$${IDE_ID}\";
const char IDE_CASED_ID[] = \"$${IDE_CASED_ID}\";
#define IDE_VERSION $${QTCREATOR_VERSION}
#define IDE_VERSION_STR STRINGIFY(IDE_VERSION)
#define IDE_VERSION_DISPLAY_DEF $${QTCREATOR_DISPLAY_VERSION}
#define IDE_VERSION_MAJOR $$replace(QTCREATOR_VERSION, "^(\\d+)\\.\\d+\\.\\d+(-.*)?$", \\1)
#define IDE_VERSION_MINOR $$replace(QTCREATOR_VERSION, "^\\d+\\.(\\d+)\\.\\d+(-.*)?$", \\1)
#define IDE_VERSION_RELEASE $$replace(QTCREATOR_VERSION, "^\\d+\\.\\d+\\.(\\d+)(-.*)?$", \\1)
const char * const IDE_VERSION_LONG = IDE_VERSION_STR;
const char * const IDE_VERSION_DISPLAY = STRINGIFY(IDE_VERSION_DISPLAY_DEF);
const char * const IDE_AUTHOR = \"The Qt Company Ltd\";
const char * const IDE_YEAR = \"$${QTCREATOR_COPYRIGHT_YEAR}\";
#ifdef IDE_REVISION
const char * const IDE_REVISION_STR = STRINGIFY(IDE_REVISION);
#else
const char * const IDE_REVISION_STR = \"\";
#endif
...
} // Constants
} // Core
這個(gè)模板文件定義了一些常量煤伟,某些變量值引用的是宏定義,最后編譯后宏定義會(huì)被替換掉真正的值木缝,在我們代碼中引入時(shí)真正起作用便锨,更加詳細(xì)使用過(guò)程后面統(tǒng)一分析pro
文件技巧時(shí)會(huì)提到
總結(jié)
學(xué)習(xí)到這里,已經(jīng)大概清楚了Qt Creator
框架的基本結(jié)構(gòu)了我碟,首先是一些基本庫(kù)鸿秆,這些動(dòng)態(tài)庫(kù)封裝了一些基本功能和用法,方便在多個(gè)模塊重復(fù)調(diào)用使用怎囚,其次是插件管理系統(tǒng)的實(shí)現(xiàn)卿叽,主要包含插件對(duì)象聲明周期管理,插件加載恳守、插件卸載考婴、插件直接依賴(lài)關(guān)系處理
比如有插件A、B催烘、C沥阱,C插件現(xiàn)在同時(shí)依賴(lài)于插件A和B,那么在加載時(shí)就需要特殊考慮
最后就是多個(gè)插件的初始化伊群,主窗口和菜單組件管理類(lèi)考杉,方便拓展到其它插件進(jìn)行訪(fǎng)問(wèn)管理
整個(gè)QTC
插件系統(tǒng)是由一個(gè)個(gè)動(dòng)態(tài)庫(kù)構(gòu)成的策精,每個(gè)插件互相配合實(shí)現(xiàn)了這樣一個(gè)復(fù)雜的跨平臺(tái)的IDE
,仔細(xì)研究下就可以發(fā)現(xiàn)很多奇妙的用法和知識(shí)
相關(guān)閱讀
- Qt Creator 學(xué)習(xí)筆記01崇棠,初識(shí) QTC[1]