Qt Creator 源碼學(xué)習(xí)筆記02宫补,認(rèn)識(shí)框架結(jié)構(gòu)

閱讀本文大概需要 6 分鐘

在上一篇大概了解了關(guān)于Qt Creator 基礎(chǔ)知識(shí)后[1],本篇先學(xué)習(xí)下框架基本結(jié)構(gòu)曾我,這樣能夠清晰的知道這個(gè)框架當(dāng)中包含那些文件粉怕、文件夾、工程文件抒巢,這些文件分別代表什么意思以及有什么作用

文件結(jié)構(gòu)

打開(kāi)下載好的源碼贫贝,如下目錄所示

image

可以看出來(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è)框架原理

image

使用 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ù)功能

image

在這個(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]

  1. http://kevinlq.com/2021/11/01/learn01_studyQTC/ ? ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咽袜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子枕稀,更是在濱河造成了極大的恐慌询刹,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萎坷,死亡現(xiàn)場(chǎng)離奇詭異凹联,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)哆档,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)蔽挠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瓜浸,你說(shuō)我怎么就攤上這事象泵。” “怎么了斟叼?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵偶惠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我朗涩,道長(zhǎng)忽孽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任谢床,我火速辦了婚禮兄一,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘识腿。我一直安慰自己出革,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布渡讼。 她就那樣靜靜地躺著骂束,像睡著了一般。 火紅的嫁衣襯著肌膚如雪成箫。 梳的紋絲不亂的頭發(fā)上展箱,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音蹬昌,去河邊找鬼混驰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的栖榨。 我是一名探鬼主播昆汹,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼婴栽!你這毒婦竟也來(lái)了满粗?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤居夹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后本冲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體准脂,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年檬洞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狸膏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡添怔,死狀恐怖湾戳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情广料,我是刑警寧澤砾脑,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站艾杏,受9級(jí)特大地震影響韧衣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜购桑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一畅铭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勃蜘,春花似錦硕噩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至阳惹,卻和暖如春坑资,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背穆端。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工袱贮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓攒巍,卻偏偏與公主長(zhǎng)得像嗽仪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子柒莉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容