C++/Qt 方向实训 · 综合案例日 · 明天起进入项目开发
目标:通过一个完整小软件讲清 Qt 开发的流程、规范及方法—— 项目 = 已学知识点的组装。参考实现: examples/student_manager/ (README)。 Day6–9 学员项目开发,Day10 下午答辩。
上午 1 · 需求拆解 → 界面骨架
setCentralWidget(table);exec() + QFormLayout + QDialogButtonBox;现场跑:tools/run_qt.sh qt_student_manager(离屏自测版:
QT_QPA_PLATFORM=offscreen,自动走完整验证流程)
上午 2 · 数据层
struct Student { QString name; double score; QString tag; };
QJsonObject toJson(const Student&); // 互逆的一对
Student fromJson(const QJsonObject&); // round-trip = 正确性标准
Student 结构体不知道 QTableWidget 的存在——
这是 Model/View 思想的朴素版;open() 返回值、QJsonParseError、
路径用 applicationDirPath() 拼。下午 1 · 提升:多线程防卡界面(新授)
msleep 循环直接放按钮槽里 → 整个窗口冻结:点不动、拖不动、
系统提示"未响应"——Day4 讲过:主线程阻塞 = 事件循环停摆。
这是学员项目里最高频的"卡死"来源。
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();
moveToThread
搬进工作线程,而不是继承 QThread 重写 run;moveToThread 写在 start() 之后 / worker 带了 parent——都不生效。最小版:examples/qthread_worker/ · 案例集成版:student_manager 的 startImport()
下午 2 · 开发流程与规范 + 分组定方向
给界面加动效: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 下午项目答辩。
案例参考实现与讲解建议: examples/student_manager/README.md · 大纲:docs/teaching/OUTLINE_4DAY.md §3