Files
scuc-qt-course/docs/teaching/slides/day4.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

222 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 4 · 2D绘图/多线程/动画框架与状态机</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 4</h1>
<h3>2D 图形绘制 / 多线程 / 动画框架与状态机</h3>
<p class="module-badge">C++/Qt 方向实训 · 第 4 / 5 天 · 讲:练 ≈ 1:2.7</p>
<ul>
<li>模块 A(3 课时):2D 绘图 / 多线程</li>
<li>模块 B(3 课时):动画框架 / 状态机 → 交互式小游戏案例</li>
</ul>
</section>
<!-- ===================== 模块 A:讲 ===================== -->
<section>
<section>
<p class="module-badge">模块 A · 讲授 35min</p>
<h2>2D 绘图 / 多线程 <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>绘图:QPainter <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
void paintEvent(QPaintEvent*) override {
QPainter painter(this);
painter.drawLine(QLine(10, 10, 200, 10));
painter.drawRect(QRect(10, 30, 100, 50));
painter.drawEllipse(QRect(150, 30, 100, 80));
}
</code></pre>
<p><small>所有绘制都发生在 <code>paintEvent</code> 里,由 Qt 在需要重绘时自动调用。
出处:<a class="filelink" target="_blank"
href="../../../p03/ch10/qpainter_basic.cpp">p03/ch10/qpainter_basic.cpp</a>;绘图设备
<code>QImage</code>/<code>QPixmap</code>/<code>QPicture</code>)见
<a class="filelink" target="_blank" href="../../../p03/ch10/paint_device_qimage.cpp">paint_device_qimage.cpp</a>
<a class="filelink" target="_blank" href="../../../p03/ch10/paint_device_pixmaps.cpp">paint_device_pixmaps.cpp</a></small></p>
</section>
<section>
<h3>QThread 多线程(教学补充) <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
class Worker : public QObject {
Q_OBJECT
public slots:
void process() {
qint64 sum = 0;
for (int i = 1; i <= 1000000; ++i) sum += i;
emit resultReady(sum); // 排队连接自动把结果带回主线程
}
signals:
void resultReady(qint64 result);
};
worker.moveToThread(&workerThread); // 官方推荐写法:worker-object + moveToThread
connect(&workerThread, &QThread::started, &worker, &Worker::process);
</code></pre>
<p><small>出处:<a class="filelink" target="_blank"
href="../examples/qthread_worker/qt_thread_worker.cpp">docs/teaching/examples/qthread_worker/qt_thread_worker.cpp</a>
<a class="filelink" target="_blank" href="../examples/qthread_worker/README.md">配套 README</a>)——
仓库 <code>p03/</code> 的 wiki 抓取内容没有多线程,这是本轮补写的教学补充示例,已验证跑通。</small></p>
</section>
</section>
<!-- ===================== 模块 A:练 ===================== -->
<section>
<section>
<p class="module-badge">模块 A · 练习 95min</p>
<h2>动手:绘图 + 多线程 <span class="tag tag-practice"></span></h2>
</section>
<section>
<div class="task-card">
<h4>任务卡 1 · 画一个简单图形界面 <span class="tag tag-practice"></span></h4>
<dl class="task-spec">
<dt>目标</dt>
<dd>仿 <code>qpainter_basic.cpp</code>,在 <code>paintEvent</code> 里画一个组合图形
(如棋盘格/仪表盘刻度)</dd>
<dt>验收标准</dt>
<dd>编译通过,离屏或 X 环境下能看到图形正确绘制(可截图验证)</dd>
<dt>基础知识 · 参考</dt>
<dd><a class="filelink" target="_blank" href="../../../p03/ch10/qpainter_basic.cpp">p03/ch10/qpainter_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><code>qt_thread_worker.cpp</code> 里的耗时计算换成一个更长的循环或
<code>QThread::msleep</code>,观察主线程/事件循环没有被卡住</dd>
<dt>验收标准</dt>
<dd>能说清楚"如果把这段计算直接放主线程会发生什么"(界面无响应),并解释
<code>moveToThread</code> 起的作用</dd>
<dt>基础知识 · 参考</dt>
<dd><a class="filelink" target="_blank" href="../examples/qthread_worker/qt_thread_worker.cpp">qt_thread_worker.cpp</a> ·
<a class="filelink" target="_blank" href="../examples/qthread_worker/README.md">README</a></dd>
</dl>
</div>
</section>
</section>
<!-- ===================== 模块 B:讲 ===================== -->
<section>
<section>
<p class="module-badge">模块 B · 讲授 35min</p>
<h2>动画框架 / 状态机 <span class="tag tag-lecture"></span></h2>
</section>
<section>
<h3>QPropertyAnimation 动画(教学补充) <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
auto* anim = new QPropertyAnimation(button, "geometry", &window);
anim->setDuration(800);
anim->setStartValue(QRect(10, 10, 100, 30));
anim->setEndValue(QRect(180, 140, 120, 40));
anim->setEasingCurve(QEasingCurve::OutBounce);
anim->start(QAbstractAnimation::DeleteWhenStopped);
</code></pre>
<p><small>出处:<a class="filelink" target="_blank"
href="../examples/property_animation/qt_property_animation.cpp">docs/teaching/examples/property_animation/qt_property_animation.cpp</a>
<a class="filelink" target="_blank" href="../examples/property_animation/README.md">配套 README</a></small></p>
</section>
<section>
<h3>QStateMachine 状态机(教学补充) <span class="tag tag-lecture"></span></h3>
<pre><code class="cpp" data-trim data-noescape>
auto* redState = new QState(&machine);
auto* greenState = new QState(&machine);
redState->assignProperty(&button, "text", QStringLiteral("红灯:禁止通行"));
greenState->assignProperty(&button, "text", QStringLiteral("绿灯:允许通行"));
redState->addTransition(&button, &QPushButton::clicked, greenState);
greenState->addTransition(&button, &QPushButton::clicked, redState);
machine.setInitialState(redState);
machine.start();
</code></pre>
<p><small>出处:<a class="filelink" target="_blank"
href="../examples/state_machine/qt_state_machine.cpp">docs/teaching/examples/state_machine/qt_state_machine.cpp</a>
<a class="filelink" target="_blank" href="../examples/state_machine/README.md">配套 README</a>)——
<code>QStateMachine</code>/<code>QState</code> 属于 QtCore,不需要额外模块。以上两个
(动画、状态机)都是仓库 <code>p03/</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 · 动画 + 状态机组合 <span class="tag tag-practice"></span></h4>
<dl class="task-spec">
<dt>目标</dt>
<dd>把动画和状态机组合到同一个窗口——例如按钮在"红灯"状态时禁止移动,"绿灯"状态时
点击才触发动画</dd>
<dt>验收标准</dt>
<dd>状态切换正确(红灯不触发动画,绿灯触发动画);动画播放完整不报错</dd>
<dt>基础知识 · 参考</dt>
<dd><a class="filelink" target="_blank" href="../examples/property_animation/">property_animation/</a> ·
<a class="filelink" target="_blank" href="../examples/state_machine/">state_machine/</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>结合任务卡 1 + 鼠标事件(Day3 <code>mouse_event.cpp</code>+ 多线程计时
Day4 <code>qt_thread_worker.cpp</code>),做一个"状态变化后限时点击"的最小交互游戏</dd>
<dt>验收标准</dt>
<dd>游戏至少有"开始→进行中→结束"三个可辨识状态,计时或计分逻辑不崩溃</dd>
<dt>基础知识 · 参考</dt>
<dd>上述三个教学补充示例 +
<a class="filelink" target="_blank" href="../../../p03/ch09/mouse_event.cpp">mouse_event.cpp</a></dd>
</dl>
</div>
</section>
</section>
<!-- ===================== 收尾 ===================== -->
<section>
<h2>当日产出验证</h2>
<div class="verify-box">
<h4>现场演示</h4>
<pre><code class="bash" data-trim>QT_QPA_PLATFORM=offscreen tools/run_qt.sh &lt;target&gt;
# 有 X 环境:去掉 QT_QPA_PLATFORM=offscreen 直接看窗口</code></pre>
<p>分组演示动画+状态机组合小工具(任务卡 2 选做,量力而行)。</p>
</div>
<p class="footer-note"><small>明天 Day5:团队协同开发 / 综合案例复盘 / OpenCV(可选自学) ——
大纲:<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="day3.html">← 上一天</a> ·
<a class="filelink" href="day5.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>