第1章:Qt项目结构分析
做Qt开发这么多年,我接手过的项目少说也有几十个。说实话,每次看到新项目的目录结构,心里都会咯噔一下——有的项目文件堆得跟垃圾场似的,有的又规整得像教科书。今天咱们就来聊聊,一个合格的Qt项目到底该怎么组织。
1.1 现有项目目录结构剖析
先看一个典型的「反面教材」。我曾经接手过一个工业控制项目,目录长这样:
MyProject/
├── main.cpp
├── widget.cpp
├── widget.h
├── dialog.cpp
├── dialog.h
├── utils.cpp
├── utils.h
├── config.ini
├── image1.png
├── image2.png
├── style.qss
├── database.db
└── ...(还有30多个文件散落在这里)
你发现问题了吗?所有文件都堆在根目录下。找个头文件得翻半天,想改个资源文件更是大海捞针。我个人习惯,项目一启动就先搭好目录骨架,不然后面重构起来真要命。
那一个好的目录结构长什么样?我给大家看个参考:
MyProject/
├── src/ # 源代码
│ ├── app/ # 应用入口
│ ├── core/ # 核心业务逻辑
│ ├── ui/ # 界面相关
│ │ ├── widgets/ # 自定义控件
│ │ ├── dialogs/ # 对话框
│ │ └── pages/ # 页面
│ ├── data/ # 数据层
│ └── utils/ # 工具类
├── resources/ # 资源文件
│ ├── images/
│ ├── styles/
│ └── translations/
├── tests/ # 单元测试
├── docs/ # 文档
├── CMakeLists.txt # 构建文件
└── README.md
核心原则:按功能模块分目录,而不是按文件类型。把相关的.h、.cpp、.ui文件放在一起,而不是把所有.h扔一个文件夹。
1.2 模块划分原则
模块划分这事儿,说白了就是「高内聚、低耦合」。我在项目中遇到过最头疼的情况,就是两个模块互相引用,改一个地方崩一片。
给大家几个实用的划分原则:
- 单一职责:一个模块只做一件事。比如网络模块只管收发数据,别掺和界面逻辑
- 接口清晰:模块之间通过信号槽或接口类通信,别直接访问对方的私有成员
- 可替换性:每个模块都应该能独立测试、独立替换
举个例子,一个典型的Qt项目可以这样分模块:
| 模块名 | 职责 | 依赖关系 |
|---|---|---|
| core | 业务逻辑、数据模型 | 无 |
| ui | 界面展示、用户交互 | 依赖core |
| network | 网络通信、协议解析 | 依赖core |
| database | 数据持久化、ORM | 依赖core |
| utils | 通用工具函数 | 无 |
小技巧:模块之间的依赖关系应该是单向的。比如ui可以依赖core,但core绝对不能依赖ui。这样以后换界面库(比如从QWidget换成QML)才不会伤筋动骨。
1.3 依赖关系梳理
依赖关系这东西,就像人际关系——理不清就会乱成一锅粥。我曾经在一个项目里发现,A模块依赖B,B依赖C,C又依赖A,形成了循环依赖。编译倒是能过,但每次改代码都得小心翼翼,生怕触发连锁反应。
怎么梳理依赖关系?我一般用三步走:
- 画依赖图:把每个模块的#include列出来,画成有向图
- 找循环:用工具(比如CMake的--graphviz选项)检测循环依赖
- 拆解:把循环依赖中的公共部分提取出来,放到新的模块里
下面我用SVG画一个典型的依赖关系图,大家感受一下:
注意:千万别让core模块依赖任何界面相关的东西。我见过有人把QWidget的头文件include到core里,结果整个项目耦合得死死的,想换界面库?门儿都没有。
1.4 构建系统评估
Qt的构建系统,说白了就两个选择:qmake和CMake。嗯,这里要注意,qmake虽然简单,但已经快被Qt官方抛弃了。我个人强烈建议新项目直接用CMake。
为什么?给你看个对比:
| 特性 | qmake | CMake |
|---|---|---|
| 跨平台支持 | 一般 | 优秀 |
| 第三方库集成 | 麻烦 | 简单(find_package) |
| 测试框架集成 | 不支持 | 支持(CTest) |
| IDE支持 | Qt Creator | VS、CLion、Qt Creator |
| 模块化支持 | 弱 | 强(add_subdirectory) |
一个典型的CMakeLists.txt长这样:
cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets Network Sql)
add_subdirectory(src/core)
add_subdirectory(src/ui)
add_subdirectory(src/network)
add_subdirectory(src/database)
target_link_libraries(MyApp
core_lib
ui_lib
network_lib
database_lib
)
避坑指南:我曾经在迁移一个老项目时,发现qmake的.pro文件里写了各种奇奇怪怪的路径。换成CMake后,用target_include_directories和target_link_libraries,依赖关系一目了然。嗯,真香。
最后说一句,构建系统的选择其实没那么玄乎。如果你项目小、团队就你一个人,用qmake也不是不行。但如果你打算长期维护、多人协作,CMake绝对是更明智的选择。
好了,项目结构这块儿就聊到这儿。记住一句话:好的结构是重构出来的,不是设计出来的。别怕改,改着改着就顺了。