# Day 5

### 综合项目案例讲解：学员信息管理器（全天）

C++/Qt 方向实训 · 综合案例日 · 明天起进入项目开发

- 上午：需求拆解 → 界面骨架 → 数据层（Day1–4 全部知识点的收束）
- 下午：提升——QThread 防卡界面；开发流程与规范；分组定项目方向

目标：通过一个完整小软件讲清 Qt 开发的流程、规范及方法—— 项目 = 已学知识点的组装。参考实现： examples/student_manager/ （README）。 Day6–9 学员项目开发，Day10 下午答辩。

上午 1 · 需求拆解 → 界面骨架

## 从需求到窗口 讲

### 需求拆解：先想清楚再动手 讲

#### 核心概念——开发流程的第一步不是写代码

- 需求：管理学员记录（姓名/成绩/标签），支持添加、保存、读取、批量导入。
- 拆功能 → 映射到已学组件：列表展示=QTableWidget(Day4)、添加=模态对话框(Day3)、 保存读取=QFile+JSON(Day4)、批量导入=后台线程(今天新授)、外壳=QMainWindow(Day3)。
- 拆完就得到任务清单——这正是 Day6 起你们在 Jira 上要做的事。

### 界面骨架走查 讲

#### 回指 Day3–4

- QMainWindow：菜单（文件/记录/帮助）+ 工具栏 + 状态栏 + `setCentralWidget(table)`；
- QAction 一个动作三处复用（菜单/工具栏/快捷键），每个 action 都要 connect；
- 添加记录：模态 `exec()` + `QFormLayout` + `QDialogButtonBox`；
- 表格 item 所有权：new 出来交给表格——父子所有权模型第 N 次出现。

现场跑：`tools/run_qt.sh qt_student_manager`（离屏自测版： `QT_QPA_PLATFORM=offscreen`，自动走完整验证流程）

上午 2 · 数据层

## 记录 ↔ JSON ↔ 磁盘 讲

### 数据层走查 讲

    struct Student { QString name; double score; QString tag; };
    QJsonObject toJson(const Student&);      // 互逆的一对
    Student fromJson(const QJsonObject&);    // round-trip = 正确性标准

#### 回指 Day4（昨天作业的完整版）

- 数据结构与界面分离：`Student` 结构体不知道 QTableWidget 的存在—— 这是 Model/View 思想的朴素版；
- 健壮性三处检查全部在场：`open()` 返回值、`QJsonParseError`、 路径用 `applicationDirPath()` 拼。

#### 复盘昨天作业的常见问题

- 保存和读取的字段名拼写不一致（"score" vs "Score"）——round-trip 一测就露馅， 这就是为什么验收标准是"写进去读出来一致"而不是"能生成文件"。

下午 1 · 提升：多线程防卡界面（新授）

## QThread：耗时操作不冻结界面 讲

### 先看病症：把耗时活儿放主线程 讲

#### 现场演示

- 把 `msleep` 循环直接放按钮槽里 → 整个窗口冻结：点不动、拖不动、 系统提示"未响应"——Day4 讲过：主线程阻塞 = 事件循环停摆。 这是学员项目里最高频的"卡死"来源。

### 正解：worker-object 模式 讲

    auto* thread = new QThread(this);
    auto* worker = new ImportWorker;          // 不给 parent：有 parent 禁止 moveToThread
    worker->moveToThread(thread);             // 必须在 start() 之前
    connect(thread, &QThread::started, worker, [worker]{ worker->doImport(10); });
    connect(worker, &ImportWorker::rowReady, this, /* 更新表格 */);   // 自动 Queued
    thread->start();

#### 核心概念（三件套）

- 线程亲和性：每个 QObject 归属一个线程，GUI 控件必须归主线程；
- worker 对象：官方推荐做法——耗时逻辑放 worker，`moveToThread` 搬进工作线程，而不是继承 QThread 重写 run；
- 跨线程信号槽自动 Queued（Day2 的连接方式今天闭环）：worker 发信号、 主线程槽更新表格，不需要手动加锁。

#### 常见的坑

- 在工作线程直接操作 GUI 控件——未定义行为，跨线程崩溃的头号来源；
- `moveToThread` 写在 `start()` 之后 / worker 带了 parent——都不生效。

最小版：examples/qthread_worker/ · 案例集成版：student_manager 的 startImport()

下午 2 · 开发流程与规范 + 分组定方向

## 进项目前的最后一课 讲

### Git 提交习惯 讲

#### 核心概念

- 每完成一个任务就提交，提交粒度对应任务粒度；commit 信息说清"做了什么、为什么"—— 它是项目的可追溯记录，评审和回滚都靠它。

#### 常见误区

- 攒几天代码一次性提交、信息写"update"——历史失去可追溯性，出问题无法定位是哪次 改动引入的。从 Day6 的第一次 commit 开始就按规范来。

### 考核标准与分组定方向 讲

#### 核心概念

- 考核不只看代码：项目管理（任务拆解/进度可见）+ 项目技术 + 职业素养（协作/答辩表达） 多维度评估——只埋头写代码会丢掉其他维度的分。
- 今天散场前每组确认：项目选题 + 初步分工 + 任务拆解草稿（上午"需求拆解"那页 就是模板）。

#### 兴趣钩子（各 5 分钟演示，选学）

给界面加动效：QPropertyAnimation（任何 Q_PROPERTY 都能被缓动曲线驱动） · 多状态 UI：QStateMachine（状态成为一等对象， 替代一堆 bool 标志）· 线程的更高抽象：QtConcurrent/线程池。

## 当日产出验证 · 进入项目期

#### 验收

    QT_QPA_PLATFORM=offscreen tools/run_qt.sh qt_student_manager
    # 自测 1/2：JSON 写入→清空→读回，2 条记录完整
    # 自测 2/2：后台线程导入 3 条，总计 5 条，界面未阻塞

每组提交：项目选题 + 分工说明 + 任务拆解草稿。明天 Day6 起：学员编码 + 工程师指导， Day10 下午项目答辩。

返回目录 · ← 上一天