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 下午项目答辩。

返回目录 · ← 上一天