Qt Creator 源码学习笔记02,认识框架结构
阅读本文大概需要 6 分钟
在上一篇大概了解了关于Qt Creator 基础知识后[1],本篇先学习下框架基本结构,这样能够清晰的知道这个框架当中包含哪些文件、文件夹、工程文件,这些文件分别代表什么意思以及有什么作用
文件结构
打开下载好的源码,如下目录所示

可以看出来,文件和文件夹很多,不要被这些表面吓着,我们真正需要关心的没有几个,需要重点关注的我加粗显示了
- bin文件夹
- dist 文件夹
- doc 文件夹
- qbs 文件夹
- scripts 文件夹
- share 文件夹
- src 文件夹
- tests 文件夹
- docs.pri
- qtcreator.pri
- qtcreator.pro
- qtcreator.qbs
- qtcreatordata.pri
- README.md
这里我们主要要关注src文件夹,这个下面是这个框架的源码,其它的文件夹先不看
qtcreator.pri文件是项目工程中的一些通用配置,比如版本号,一些库的输出路径定义,每个插件或者子工程都会包含该配置文件,方便直接配置工程一些变量(具体怎么配置,后面会讲解到)
qtcreator.pro文件是主工程文件,要打开编译源码也是需要打开该工程文件进行加载的
PS: 涉及到 qbs 相关内容可以不用关注了,Qt Build Suite 也是一种跨平台的编译工具,目前使用较少无需关注
框架结构
下面来详细看下工程结构是如何管理的,以及整个框架原理

使用 Qt Creator 打开工程后你会发现有很多子工程项目,这个时候不要乱、不要怕,我们目前只需要关心三个部分就可以了
- libs
- plugins
- app
libs部分
libs工程下面包含了常用的一些通用方法,我们目前关注三个即可
Aggregation工程
这个类提供了「打包」功能,可以将很多组件打包成一个整体,整个理解起来有点抽象,你可以理解为将多个对象封装成一个对象,这个对象对外提供了所有对象的接口属性和方法
Aggregation集合内部每个组件对象都可以互相转化Aggregation集合内每个对象的生命周期被绑定在了一起,即一个在全部在,一个被删除析构那么其余的组件也就会被析构
extensionsystem工程
这个类实现了插件的管理功能,是整个框架的核心部分,所有的插件生命周期管理都在这个类里面实现
IPlugin插件基类,后面所有的插件都是继承自它来实现所有功能的,有三个重点方法需要关注
virtual bool initialize(const QStringList &arguments, QString *errorString) = 0;
virtual void extensionsInitialized() = 0;
virtual bool delayedInitialize() { return false; }
插件的初始化,外部依赖初始化,延迟初始化,这三个虚函数用来初始化每个插件各自的一些资源信息。外部依赖那个也尤为重要,比如我们某个插件同时依赖多个其它插件,那么就需要在这里处理等待其它插件加载完成才算完成
PluginManager插件管理单例类,整个框架只有一份,负责框架插件的管理,随着程序退出它的声明周期才结束PluginManagerPrivate插件管理具体实现逻辑类,看名字就很清楚,典型的P-D指针关系,这样是为了把插件系统扩展的具体实现隐藏不给外部暴露,这种技巧在后面很多代码中经常会见到,也是值的我们去学习PluginSpec插件核心类,该类实现插件的所有属性
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;
// 插件版权
QString copyright() const;
// 插件协议
QString license() const;
// 插件描述
QString description() const;
// 插件主页 URL
QString url() const;
// 插件类别,用于在界面分组显示插件信息
QString category() const;
}
每个插件(每个动态库)都有一份该对象,用来记录该插件的所有属性信息,这些属性信息是通过 json配置文件读入的,这些信息被称为插件的「元信息」,后面关注插件实现会提到
utils 工程
这个工程里面封装了一些基础功能算法类,比如文件操作、数据排序操作、json交互操作、字符串操作集合等,还有一些基础封装控件实现也在这个里面
比如后面要提到的核心插件主窗口QMainWindow类,基类就在在这里
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;
};
这里主要是一些事件变化后通知外部处理,比如这里如果主题发生改变发送对应信号出去,设备发生改变(插拔光驱等)发出一个设备改变事件到 Qt 事件队列去处理
plugin 部分
这部分是每个插件实现部分,重点需要关注核心插件corePlugin的实现,其它插件都是要依赖核心插件来实现业务功能

在这个插件里面主要初始化了主窗口、菜单管理类实例以及一些模式管理对象初始化
后面我们会看到各种各样的插件,比如你打开Qt Creator的时候首页显示的内容,也是单独的一个插件,名字叫做weilcome
每个插件都有一个标识ID,用来区分是你自己写的插件,防止别人恶意修改插件
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Core.json")
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Welcome.json")
每个插件还有一个对应的元数据描述配置文件,这个文件配置了该插件的一些基本信息,比如插件名字、版本、所有权、依赖那些插件等,这些配置信息在编译时会写进该插件动态库当中,采用的是Qt的元对象技术来实现的,这样在插件加载运行时就能通过反射动态获取这些信息,继而用来进行一些插件之间加载关系的验证
一个简单的配置描述如下所示
{
\"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
}
其中很关键的是一些变量值,比如$$QTCREATOR_VERSION,通过这个变量直接可以读取到我们在工程qtcreator.pri中定义的变量值,继而快速统一加载显示,其它变量值获取类似
其次,需要关注的是每个插件的配置依赖文件比如welcome_dependencies.pri,该文件中包含了依赖那些库那些插件
# 插件名字
QTC_PLUGIN_NAME = Welcome
# 插件依赖的库
QTC_LIB_DEPENDS += \
extensionsystem \
utils
# 插件依赖的插件
QTC_PLUGIN_DEPENDS += \
coreplugin
某个插件依赖那些插件和动态库,只需要在对应位置追加其名字即可,工程配置文件会自动进行加载,这样编写可以减少很多重复工作,而且插件依赖关系也很清楚的看到
app 部分
这个部分是程序入口实现部分,这里主要是获取插件路径,初始化插件、配置文件,加载每个插件,如果都没有错误,那么初始化完成后主界面就会显示出来,直接看主函数入口看就行
关键部分是插件管理器的初始化,设置插件搜索路径后对每个插件进行初始化操作
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();
在这里还有一个需要注意的地方,就是这个文件app_version.h.in
这个是一个模板文件,qmake加载执行完毕后,会在临时目录下生成对应的头文件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
这个模板文件定义了一些常量,某些变量值引用的是宏定义,最后编译后宏定义会被替换掉真正的值,在我们代码中引入时真正起作用,更加详细使用过程后面统一分析pro文件技巧时会提到
总结
学习到这里,已经大概清楚了Qt Creator框架的基本结构了,首先是一些基本库,这些动态库封装了一些基本功能和用法,方便在多个模块重复调用使用,其次是插件管理系统的实现,主要包含插件对象声明周期管理,插件加载、插件卸载、插件直接依赖关系处理
比如有插件A、B、C,C插件现在同时依赖于插件A和B,那么在加载时就需要特殊考虑
最后就是多个插件的初始化,主窗口和菜单组件管理类,方便拓展到其它插件进行访问管理
整个QTC插件系统是由一个个动态库构成的,每个插件互相配合实现了这样一个复杂的跨平台的IDE,仔细研究下就可以发现很多奇妙的用法和知识
相关阅读
- Qt Creator 学习笔记01,初识 QTC[1:1]
Qt Creator 源码学习笔记02,认识框架结构的更多相关文章
- Qt Creator 源码学习笔记03,大型项目如何管理工程
阅读本文大概需要 6 分钟 一个项目随着功能开发越来越多,项目必然越来越大,工程管理成本也越来越高,后期维护成本更高.如何更好的组织管理工程,是非常重要的 今天我们来学习下 Qt Creator 是如 ...
- Qt Creator 源码学习笔记04,多插件实现原理分析
阅读本文大概需要 8 分钟 插件听上去很高大上,实际上就是一个个动态库,动态库在不同平台下后缀名不一样,比如在 Windows下以.dll结尾,Linux 下以.so结尾 开发插件其实就是开发一个动态 ...
- Qt Creator 源码学习笔记01,初识QTC
阅读本文大概需要 4 分钟 Qt Creator 是一款开源的轻量级 IDE,整个架构代码全部使用 C++/Qt 开发而成,非常适合用来学习C++和Qt 知识,这也是我们更加深入学习Qt最好的方式,学 ...
- Qt Creator 源码学习 03:qtcreator.pro
当我们准备好 Qt Creator 的源代码之后,首先进入到它的目录,来看一下它的源代码目录有什么奥秘. 这里一共有 9 个文件夹和 9 个文件.我们来一一看看它们都是干什么用的. .git: 版本控 ...
- 一行一行分析JQ源码学习笔记-02
1.防止冲突 设置新变量保存
- JUC源码学习笔记2——AQS共享和Semaphore,CountDownLatch
本文主要讲述AQS的共享模式,共享和独占具有类似的套路,所以如果你不清楚AQS的独占的话,可以看我的<JUC源码学习笔记1> 主要参考内容有<Java并发编程的艺术>,< ...
- JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析
JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ...
- Underscore.js 源码学习笔记(下)
上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...
- Underscore.js 源码学习笔记(上)
版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}()); 这样的东西,我们应该知道这是一个 IIFE(立即执行 ...
随机推荐
- MyBatis 面试复习整理
MyBatis MyBatis 是一款优秀的ORM(对象关系映射)框架,可以通过对象和数据库之间的映射,将程序中的对象自动存储到数据库中.它内部封装了 JDBC ,使开发者只需要关注 SQL语句本身, ...
- 字符串编码js第三方类库text-encoding
GITHUB地址:https://github.com/BCode001/text-encoding
- bzoj2038 小z的袜子 (莫队)
题目大意 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编 ...
- 从0到1使用Kubernetes系列(三):使用Ansible安装Kubernetes集群
前两期的文章介绍了Kubernetes基本概念和架构,用Kubeadm+Ansible搭建Kubernetes集群所需要的工具及其作用.本篇介绍怎么使用Ansible安装Kubernetes集群. 启 ...
- rocketmq优雅停机往事
1 时间追溯到2018年12月的某一天夜晚,那天我正准备上线一个需求完就回家,刚点下发布按钮,告警就响起,我擦,难道回不了家了?看着报错量只有一两个,断定只是偶发,稳住不要慌. 把剩下的机器发完,又出 ...
- Java集合 - 集合知识点总结概述
集合概述 概念:对象的容器,定义了对多个对象进项操作的的常用方法.可实现数组的功能. 和数组的区别: 数组长度固定,集合长度不固定. 数组可以存储基本类型和引用类型,集合只能存储引用类型. 位置: j ...
- MySQL:基础语法-1
MySQL:基础语法-1 记录一下 MySQL 基础的一些语法,便于查询,该部分内容主要是参考:bilibili 上 黑马程序员 的课程而做的笔记,由于时间有点久了,课程地址忘记了 关于数据库的安装操 ...
- 理解ASP.NET Core - 路由(Routing)
注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 Routing Routing(路由):更准确的应该叫做Endpoint Routing,负责 ...
- Myod 选做
一.题目要求 1.复习c文件处理内容 2.编写myod.c 用myod XXX实现Linux下od -tc -tx XXX的功能 3.main与其他分开,制作静态库和动态库 4.编写Makefile ...
- threading python2 和python3
from __future__ import division from __future__ import print_function import threading balance = 0 d ...