教案对齐官方方案书:重写为 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 的分层预案挂钩
This commit is contained in:
+138
-223
@@ -2,7 +2,7 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Day 4 · Qt 桌面全景</title>
|
||||
<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">
|
||||
@@ -15,198 +15,19 @@
|
||||
|
||||
<section>
|
||||
<h1>Day 4</h1>
|
||||
<h3>Qt 桌面全景</h3>
|
||||
<p class="module-badge">Qt 方向实训 · 第 4 / 4 天 · 讲:练 ≈ 1:2.7</p>
|
||||
<h3>2D 图形绘制 / 多线程 / 动画框架与状态机</h3>
|
||||
<p class="module-badge">C++/Qt 方向实训 · 第 4 / 5 天 · 讲:练 ≈ 1:2.7</p>
|
||||
<ul>
|
||||
<li>模块 A(3 课时):项目结构 / 第一个程序 / 信号槽 / 主窗口</li>
|
||||
<li>模块 B(3 课时):对话框 / 控件 / 事件 / 绘图 / 文件(速览 + 分组选做)</li>
|
||||
<li>模块 A(3 课时):2D 绘图 / 多线程</li>
|
||||
<li>模块 B(3 课时):动画框架 / 状态机 → 交互式小游戏案例</li>
|
||||
</ul>
|
||||
<p><small>定位声明:Day1–3 是「强化已知」,今天是「Qt 零基础入门」——讲授压缩为
|
||||
骨架级讲解,练习是「跟着例子改」而不是从零写。9 个章节、29 个目标不追求全覆盖。</small></p>
|
||||
<div class="verify-box" style="font-size:0.5em;">
|
||||
<h4>开始前准备(一次性)</h4>
|
||||
<pre><code class="bash" data-trim>tools/install_qt_host.sh
|
||||
cmake -S . -B build -G Ninja -DBUILD_QT_PART=ON \
|
||||
-DCMAKE_PREFIX_PATH=$PWD/.qt514/5.14.2/gcc_64 -DCMAKE_BUILD_TYPE=Debug
|
||||
cmake --build build</code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 模块 A:讲 ===================== -->
|
||||
<section>
|
||||
<section>
|
||||
<p class="module-badge">模块 A · 讲授 35min</p>
|
||||
<h2>项目结构 / 信号槽 / 主窗口 <span class="tag tag-lecture">讲</span></h2>
|
||||
<ul>
|
||||
<li>最小 Qt 程序结构</li>
|
||||
<li>控件创建与显示</li>
|
||||
<li>内置 / 自定义 / lambda 信号槽</li>
|
||||
<li>QMainWindow 中心部件</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>最小 Qt 程序:QApplication + exec() <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
class Widget : public QWidget {
|
||||
Q_OBJECT // 用到信号槽/属性系统的类都要有这个宏(触发 moc 代码生成)
|
||||
public:
|
||||
Widget(QWidget* parent = nullptr) : QWidget(parent) {}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication app(argc, argv); // 整个程序唯一的 QApplication
|
||||
Widget w;
|
||||
w.show();
|
||||
return app.exec(); // 进入事件循环,程序在这里“活着”
|
||||
}
|
||||
</code></pre>
|
||||
<p><small>出处:<a class="filelink" target="_blank"
|
||||
href="../../../p03/ch02/minimal_qt_app.cpp">p03/ch02/minimal_qt_app.cpp</a>(注意仓库版本额外注入了
|
||||
<code>QTimer::singleShot</code> 200ms 自动退出,便于离屏自动化验证,真实项目不需要这一行)</small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>控件创建:两种写法 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
// 第一种:先构造再设属性
|
||||
QPushButton* btn = new QPushButton;
|
||||
btn->setParent(this);
|
||||
btn->setText(QStringLiteral("德玛西亚"));
|
||||
btn->move(100, 100);
|
||||
|
||||
// 第二种:构造时指定文本与父亲
|
||||
QPushButton* btn2 = new QPushButton(QStringLiteral("孙悟空"), this);
|
||||
</code></pre>
|
||||
<p><small>出处:<a class="filelink" target="_blank"
|
||||
href="../../../p03/ch03/button_creation.cpp">p03/ch03/button_creation.cpp</a></small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>信号槽:内置 / 自定义 / 重载消歧义 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
class Teacher : public QObject {
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void hungry(); // 无参信号
|
||||
void hungry(QString food); // 带参信号(重载)
|
||||
public slots:
|
||||
void classIsOver(){ emit hungry(); emit hungry(QString("汉堡")); }
|
||||
};
|
||||
// 连接重载信号时必须用 QOverload 显式指定版本
|
||||
connect(t, QOverload<>::of(&Teacher::hungry), s, QOverload<>::of(&Student::treat));
|
||||
connect(t, QOverload<QString>::of(&Teacher::hungry), s, QOverload<QString>::of(&Student::treat));
|
||||
</code></pre>
|
||||
<p><small>出处:<a class="filelink" target="_blank"
|
||||
href="../../../p03/ch04/custom_signal_slot.cpp">p03/ch04/custom_signal_slot.cpp</a>;
|
||||
lambda 槽 + 值捕获/引用捕获对比见
|
||||
<a class="filelink" target="_blank" href="../../../p03/ch04/lambda_signal_slot.cpp">lambda_signal_slot.cpp</a></small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>QMainWindow:中心部件 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
class DemoMainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DemoMainWindow(){
|
||||
QTextEdit* edit = new QTextEdit(this);
|
||||
setCentralWidget(edit); // QMainWindow 有固定布局:菜单栏/工具栏/中心部件/停靠窗口/状态栏
|
||||
}
|
||||
};
|
||||
</code></pre>
|
||||
<p><small>出处:<a class="filelink" target="_blank"
|
||||
href="../../../p03/ch05/central_widget.cpp">p03/ch05/central_widget.cpp</a>;停靠窗口见
|
||||
<a class="filelink" target="_blank" href="../../../p03/ch05/dock_widget.cpp">dock_widget.cpp</a></small></p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 模块 A:练 ===================== -->
|
||||
<section>
|
||||
<section>
|
||||
<p class="module-badge">模块 A · 练习 95min</p>
|
||||
<h2>动手:第一个 Qt 程序 <span class="tag tag-practice">练</span></h2>
|
||||
</section>
|
||||
<section>
|
||||
<div class="task-card">
|
||||
<h4>任务卡 1 · 跑通最小程序(离屏验证) <span class="tag tag-practice">练</span></h4>
|
||||
<pre><code class="bash" data-trim>QT_QPA_PLATFORM=offscreen tools/run_qt.sh minimal_qt_app
|
||||
QT_QPA_PLATFORM=offscreen tools/run_qt.sh button_creation</code></pre>
|
||||
<p>有 X 环境的同学可去掉 <code>QT_QPA_PLATFORM=offscreen</code> 直接看窗口弹出(200ms 后自动退出)。</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="task-card">
|
||||
<h4>任务卡 2 · 自定义信号槽改文本 <span class="tag tag-practice">练</span></h4>
|
||||
<p>仿 <a class="filelink" target="_blank"
|
||||
href="../../../p03/ch04/custom_signal_slot.cpp">custom_signal_slot.cpp</a>,
|
||||
给一个按钮连接自定义信号槽:点击后改变一个 <code>QLabel</code> 的文本内容。</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="task-card">
|
||||
<h4>任务卡 3 · 窗口骨架 <span class="tag tag-practice">练</span></h4>
|
||||
<p>用 <code>QMainWindow</code> + <a class="filelink" target="_blank"
|
||||
href="../../../p03/ch05/dock_widget.cpp">dock_widget.cpp</a> 搭一个「中心区 + 侧边栏」
|
||||
的窗口骨架。</p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<!-- ===================== 模块 B:讲 ===================== -->
|
||||
<section>
|
||||
<section>
|
||||
<p class="module-badge">模块 B · 讲授 35min(五个方向各约 7min 速览)</p>
|
||||
<h2>对话框 / 控件 / 事件 / 绘图 / 文件 <span class="tag tag-lecture">讲</span></h2>
|
||||
<p><small>只求「这个方向能做什么」的骨架认知,不逐行讲解</small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>对话框:文件对话框 + QTextStream <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
void openFile() {
|
||||
QString path = QFileDialog::getOpenFileName(this, QStringLiteral("打开文件"));
|
||||
if (path.isEmpty()) return;
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { /* 提示失败 */ return; }
|
||||
QTextStream in(&file);
|
||||
QString text = in.readAll();
|
||||
QMessageBox::information(this, QStringLiteral("内容"), text);
|
||||
}
|
||||
</code></pre>
|
||||
<p><small>出处:<a class="filelink" target="_blank"
|
||||
href="../../../p03/ch06/file_dialog.cpp">p03/ch06/file_dialog.cpp</a>;模态/非模态对话框见
|
||||
<a class="filelink" target="_blank" href="../../../p03/ch06/modal_dialog.cpp">modal_dialog.cpp</a></small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>常用控件:QLabel 显示图片 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
QPixmap pixmap;
|
||||
pixmap.load(":/Image/boat.jpg"); // 从 Qt 资源系统(.qrc)加载
|
||||
QLabel* label = new QLabel(this);
|
||||
label->setPixmap(pixmap);
|
||||
</code></pre>
|
||||
<p><small>出处:<a class="filelink" target="_blank"
|
||||
href="../../../p03/ch08/label_pixmap.cpp">p03/ch08/label_pixmap.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></small></p>
|
||||
<h2>2D 绘图 / 多线程 <span class="tag tag-lecture">讲</span></h2>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -221,74 +42,168 @@ void paintEvent(QPaintEvent*) override {
|
||||
</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></small></p>
|
||||
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>文件系统:QFile 读文本 <span class="tag tag-lecture">讲</span></h3>
|
||||
<h3>QThread 多线程(教学补充) <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(); }
|
||||
|
||||
QFileInfo info(file);
|
||||
qDebug() << info.baseName() << info.suffix();
|
||||
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="../../../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>
|
||||
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>
|
||||
|
||||
<!-- ===================== 模块 B:练(分组三选一) ===================== -->
|
||||
<!-- ===================== 模块 A:练 ===================== -->
|
||||
<section>
|
||||
<section>
|
||||
<p class="module-badge">模块 B · 练习 95min(三选一,分组完成)</p>
|
||||
<h2>动手:做一个可跑的小工具 <span class="tag tag-practice">练</span></h2>
|
||||
<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>方案 A · 图片查看器雏形 <span class="tag tag-practice">练</span></h4>
|
||||
<p>结合 <a class="filelink" target="_blank"
|
||||
href="../../../p03/ch08/label_pixmap.cpp">label_pixmap.cpp</a> +
|
||||
<a class="filelink" target="_blank" href="../../../p03/ch06/file_dialog.cpp">file_dialog.cpp</a>:
|
||||
用文件对话框选一张图片,加载到 <code>QLabel</code> 显示。</p>
|
||||
<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>方案 B · 简易绘图板 <span class="tag tag-practice">练</span></h4>
|
||||
<p>结合 <a class="filelink" target="_blank"
|
||||
href="../../../p03/ch10/qpainter_basic.cpp">qpainter_basic.cpp</a> +
|
||||
<a class="filelink" target="_blank" href="../../../p03/ch09/mouse_event.cpp">mouse_event.cpp</a>:
|
||||
监听鼠标拖动事件,记录轨迹点,在 <code>paintEvent</code> 里用 <code>QPainter</code> 画出轨迹。</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="task-card">
|
||||
<h4>方案 C · 文本文件小工具 <span class="tag tag-practice">练</span></h4>
|
||||
<p>结合 <a class="filelink" target="_blank"
|
||||
href="../../../p03/ch11/qfile_basic.cpp">qfile_basic.cpp</a> +
|
||||
<a class="filelink" target="_blank" href="../../../p03/ch11/qtextstream_io.cpp">qtextstream_io.cpp</a>:
|
||||
读一个文本文件,统计行数/字符数,把统计结果写回一个新文件。</p>
|
||||
<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>
|
||||
<h2>当日产出验证</h2>
|
||||
<div class="verify-box">
|
||||
<h4>现场演示</h4>
|
||||
<pre><code class="bash" data-trim>QT_QPA_PLATFORM=offscreen tools/run_qt.sh <target>
|
||||
# 有 X 环境:去掉 QT_QPA_PLATFORM=offscreen 直接看窗口</code></pre>
|
||||
<p>每组演示自己选做的方案(A/B/C 三选一),教师验收「编译通过 + 交互符合预期」。</p>
|
||||
<p>分组演示动画+状态机组合小工具(任务卡 2 选做,量力而行)。</p>
|
||||
</div>
|
||||
<p class="footer-note"><small>4 天实训到此结束——大纲:
|
||||
<a class="filelink" href="../OUTLINE.md">docs/teaching/OUTLINE.md</a></small></p>
|
||||
<p><a class="filelink" href="index.html">返回目录</a></p>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user