Files
scuc-qt-course/docs/teaching/slides/day3.html
T
张宗平 83620c0cf5 教案对齐官方方案书:重写为 5 天 C++/Qt 教学设计,补齐 6 个 Qt 技术缺口示例
- docs/teaching/OUTLINE.md、slides/day1-5.html、index.html:替换此前自定义的
  4 天方案,严格对齐 docs/TRAINING_PLAN_2026.md「C++方向」Day1-5 课堂内容
  (Day6-10 为纯项目实作,不在教案范围)
- docs/teaching/examples/:官方方案书要求但仓库 p03/(wiki 抓取内容) 未覆盖的
  QSS/Model-View/JSON/QThread/QPropertyAnimation/QStateMachine 六个技术点,
  各补一个最小可运行示例 + README 教学文档,均已离屏验证跑通;手工维护,
  不属于 gen_part3.py 生成产物
- 根 CMakeLists.txt:接入 docs/teaching/examples 构建(BUILD_QT_PART 分支内),
  不改动生成器管理的 p01/p03 CMakeLists
- 任务卡改为目标/验收标准/基础知识参考三段式模板,custom.css 配套新增样式
- docs/teaching/PRETEST.md:10 题 10 分钟 C++ 摸底测验,验证教案假定的受众
  基础是否成立,并与 OUTLINE.md §0 的分层预案挂钩
2026-07-02 11:31:44 +08:00

229 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Day 3 · 高级控件 Model/View / 事件机制 / 文件IO与JSON</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="reveal/reveal.css">
<link rel="stylesheet" href="reveal/theme.css">
<link rel="stylesheet" href="reveal/plugin/highlight/monokai.css">
<link rel="stylesheet" href="custom.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h1>Day 3</h1>
<h3>高级控件 Model/View / 事件机制 / 文件 IO 与 JSON</h3>
<p class="module-badge">C++/Qt 方向实训 · 第 3 / 5 天 · 讲:练 ≈ 1:2.7</p>
<ul>
<li>模块 A3 课时):Model/View 架构 / 事件机制</li>
<li>模块 B(3 课时):文件 IO / JSON 解析 → 本地数据存储案例</li>
</ul>
</section>
<!-- ===================== 模块 A:讲 ===================== -->
<section>
<section>
<p class="module-badge">模块 A · 讲授 35min</p>
<h2>Model/View 架构 / 事件机制 <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>Model/View 模型视图架构(教学补充) <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
// 模型:只管数据
auto* model = new QStandardItemModel(this);
model->appendRow(new QStandardItem(QStringLiteral("需求分析")));
// 视图:只管展示,不持有数据
auto* view = new QListView(this);
view->setModel(model);
// 选中状态变化 → 通知外部,模型和视图之间没有直接耦合
connect(view->selectionModel(), &QItemSelectionModel::currentChanged, ...);
</code></pre>
<p><small>出处:<a class="filelink" target="_blank"
href="../examples/model_view/qt_model_view_basics.cpp">docs/teaching/examples/model_view/qt_model_view_basics.cpp</a>
<a class="filelink" target="_blank" href="../examples/model_view/README.md">配套 README</a>)——
仓库 <code>p03/</code> 的 wiki 抓取内容没有 Model/View,这是本轮补写的教学补充示例,已离屏验证跑通。</small></p>
</section>
<section>
<h3>事件循环机制(复用 Day2 概念) <span class="tag tag-lecture"></span></h3>
<p><code>app.exec()</code> 进入的就是事件循环——鼠标点击、键盘输入、定时器
触发都会变成事件,被塞进这个循环里依次分发。今天讲的"事件过滤器"就是在
事件被分发给控件之前,插一个"拦截点"。</p>
<p><small>出处:<a class="filelink" target="_blank"
href="../../../p03/ch02/minimal_qt_app.cpp">p03/ch02/minimal_qt_app.cpp</a></small></p>
</section>
<section>
<h3>自定义事件处理 / 事件过滤器 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
class EventLabel : public QLabel {
Q_OBJECT
public:
explicit EventLabel(QWidget* p=nullptr):QLabel(p){ setMouseTracking(true); }
protected:
void mouseMoveEvent(QMouseEvent* event) override {
setText(QString("Move: (%1, %2)").arg(event->x()).arg(event->y()));
}
};
</code></pre>
<p><small>重写 <code>xxxEvent</code> 是 Qt 事件处理的主线之一(另一条线是事件过滤器
<code>installEventFilter</code>)。出处:<a class="filelink" target="_blank"
href="../../../p03/ch09/mouse_event.cpp">p03/ch09/mouse_event.cpp</a>
事件过滤器见 <a class="filelink" target="_blank" href="../../../p03/ch09/event_filter_mainwindow.cpp">event_filter_mainwindow.cpp</a></small></p>
</section>
</section>
<!-- ===================== 模块 A:练 ===================== -->
<section>
<section>
<p class="module-badge">模块 A · 练习 95min</p>
<h2>动手:Model/View + 事件 <span class="tag tag-practice"></span></h2>
</section>
<section>
<div class="task-card">
<h4>任务卡 1 · Model/View 换视图 <span class="tag tag-practice"></span></h4>
<dl class="task-spec">
<dt>目标</dt>
<dd><code>qt_model_view_basics.cpp</code> 里的 <code>QListView</code> 换成
<code>QTableView</code>,模型代码不改</dd>
<dt>验收标准</dt>
<dd>编译通过,界面正常展示同一份数据,证明"同一个模型可以喂给不同视图"</dd>
<dt>基础知识 · 参考</dt>
<dd><a class="filelink" target="_blank" href="../examples/model_view/qt_model_view_basics.cpp">qt_model_view_basics.cpp</a> ·
<a class="filelink" target="_blank" href="../examples/model_view/README.md">README</a></dd>
</dl>
</div>
</section>
<section>
<div class="task-card">
<h4>任务卡 2 · 事件过滤器实操 <span class="tag tag-practice"></span></h4>
<dl class="task-spec">
<dt>目标</dt>
<dd>仿 <code>event_filter_mainwindow.cpp</code>,给一个控件装事件过滤器,拦截鼠标按下事件并打印坐标</dd>
<dt>验收标准</dt>
<dd>点击控件后终端/调试输出能看到坐标信息</dd>
<dt>基础知识 · 参考</dt>
<dd><a class="filelink" target="_blank" href="../../../p03/ch09/event_filter_mainwindow.cpp">event_filter_mainwindow.cpp</a> ·
<a class="filelink" target="_blank" href="../../../p03/ch09/mouse_event.cpp">mouse_event.cpp</a></dd>
</dl>
</div>
</section>
</section>
<!-- ===================== 模块 B:讲 ===================== -->
<section>
<section>
<p class="module-badge">模块 B · 讲授 35min</p>
<h2>文件 IO / JSON 解析 <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>文件系统:QFile 读文本 <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
QFile file("in.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug() << "打开失败"; return -1; }
while (!file.atEnd()) { qDebug() << file.readLine(); }
</code></pre>
<p><small>出处:<a class="filelink" target="_blank"
href="../../../p03/ch11/qfile_basic.cpp">p03/ch11/qfile_basic.cpp</a>;结构化读写见
<a class="filelink" target="_blank" href="../../../p03/ch11/qtextstream_io.cpp">qtextstream_io.cpp</a>
<a class="filelink" target="_blank" href="../../../p03/ch11/qdatastream_io.cpp">qdatastream_io.cpp</a></small></p>
</section>
<section>
<h3>JSON 解析与写入(教学补充) <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
QJsonObject student;
student["name"] = QStringLiteral("张三");
student["score"] = 92;
QJsonDocument doc(student);
QByteArray bytes = doc.toJson(QJsonDocument::Indented); // 序列化
QJsonDocument parsed = QJsonDocument::fromJson(bytes); // 反序列化
QJsonObject obj = parsed.object();
qDebug() << obj["name"].toString() << obj["score"].toInt();
</code></pre>
<p><small>出处:<a class="filelink" target="_blank"
href="../examples/json_io/qt_json_parse_write.cpp">docs/teaching/examples/json_io/qt_json_parse_write.cpp</a>
<a class="filelink" target="_blank" href="../examples/json_io/README.md">配套 README</a>)——
仓库 <code>p03/ch11</code> 的 wiki 抓取内容只有 QFile/QTextStream/QDataStream,没有 JSON
这是本轮补写的教学补充示例;不需要 GUI,<code>tools/run_qt.sh qt_json_parse_write</code>
直接跑即可,不需要 <code>QT_QPA_PLATFORM=offscreen</code></small></p>
</section>
</section>
<!-- ===================== 模块 B:练(本地数据存储案例) ===================== -->
<section>
<section>
<p class="module-badge">模块 B · 练习 95min(本地数据存储案例,练习任务,非预建方案)</p>
<h2>动手:本地数据存储小工具 <span class="tag tag-practice"></span></h2>
</section>
<section>
<div class="task-card">
<h4>任务卡 1 · JSON 落盘 <span class="tag tag-practice"></span></h4>
<dl class="task-spec">
<dt>目标</dt>
<dd><code>qt_json_parse_write.cpp</code> 的 JSON 数据实际写入一个文件(结合
<code>qfile_basic.cpp</code><code>QFile</code> 写法),再读回来解析校验</dd>
<dt>验收标准</dt>
<dd>程序运行后磁盘上生成一个 <code>.json</code> 文件,内容可读;重新运行程序能正确解析出
之前写入的数据</dd>
<dt>基础知识 · 参考</dt>
<dd><a class="filelink" target="_blank" href="../examples/json_io/qt_json_parse_write.cpp">qt_json_parse_write.cpp</a> ·
<a class="filelink" target="_blank" href="../examples/json_io/README.md">README</a> ·
<a class="filelink" target="_blank" href="../../../p03/ch11/qfile_basic.cpp">qfile_basic.cpp</a></dd>
</dl>
</div>
</section>
<section>
<div class="task-card">
<h4>任务卡 2 · 本地数据存储小工具 <span class="tag tag-practice"></span></h4>
<dl class="task-spec">
<dt>目标</dt>
<dd>设计一个"学员信息"结构(姓名/成绩/标签),支持增加一条记录并追加保存到 JSON 文件,
重启程序后能读出之前保存的全部记录</dd>
<dt>验收标准</dt>
<dd>至少能存 2 条以上记录并在重启后完整读回;数据字段不丢失</dd>
<dt>基础知识 · 参考</dt>
<dd><a class="filelink" target="_blank" href="../examples/json_io/">docs/teaching/examples/json_io/</a> ·
<a class="filelink" target="_blank" href="../../../p03/ch11/qtextstream_io.cpp">qtextstream_io.cpp</a></dd>
</dl>
</div>
</section>
</section>
<!-- ===================== 收尾 ===================== -->
<section>
<h2>当日产出验证</h2>
<div class="verify-box">
<h4>现场演示</h4>
<p>现场演示本地数据存储小工具,验证"写入 → 重启 → 读回"完整闭环。</p>
</div>
<p class="footer-note"><small>明天 Day4:2D 图形绘制 / 多线程 / 动画框架与状态机 ——
大纲:<a class="filelink" href="../OUTLINE.md">docs/teaching/OUTLINE.md</a></small></p>
<p><a class="filelink" href="index.html">返回目录</a> ·
<a class="filelink" href="day2.html">← 上一天</a> ·
<a class="filelink" href="day4.html">下一天 →</a></p>
</section>
</div>
</div>
<script src="reveal/reveal.js"></script>
<script src="reveal/plugin/highlight/highlight.js"></script>
<script>
Reveal.initialize({
hash: true, controls: true, progress: true, center: true, slideNumber: true,
plugins: [ RevealHighlight ]
});
</script>
</body>
</html>