83620c0cf5
- 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 的分层预案挂钩
278 lines
12 KiB
HTML
278 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>Day 2 · Qt 开发环境/信号槽/基础控件/布局与 QSS</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 2</h1>
|
||
<h3>Qt 开发环境 / 信号槽 / 基础控件 / 布局与 QSS</h3>
|
||
<p class="module-badge">C++/Qt 方向实训 · 第 2 / 5 天 · 讲:练 ≈ 1:2.7</p>
|
||
<ul>
|
||
<li>模块 A(3 课时):项目结构 / 第一个程序 / 信号槽</li>
|
||
<li>模块 B(3 课时):常用控件 / 布局管理 / QSS 界面美化 → 计算器案例</li>
|
||
</ul>
|
||
<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>
|
||
</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>
|
||
|
||
<!-- ===================== 模块 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>
|
||
<dl class="task-spec">
|
||
<dt>目标</dt>
|
||
<dd>编译运行 <code>minimal_qt_app</code> 与 <code>button_creation</code></dd>
|
||
<dt>验收标准</dt>
|
||
<dd><code>QT_QPA_PLATFORM=offscreen tools/run_qt.sh minimal_qt_app</code> 与
|
||
<code>button_creation</code> 均退出码为 0,无崩溃</dd>
|
||
<dt>基础知识 · 参考</dt>
|
||
<dd><a class="filelink" target="_blank" href="../../../p03/ch02/minimal_qt_app.cpp">p03/ch02/minimal_qt_app.cpp</a> ·
|
||
<a class="filelink" target="_blank" href="../../../p03/ch03/button_creation.cpp">p03/ch03/button_creation.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>QLabel</code> 的文本内容</dd>
|
||
<dt>验收标准</dt>
|
||
<dd>编译通过;离屏或有 X 环境下点击/模拟点击后 <code>QLabel</code> 文本确实改变;
|
||
用 <code>connect</code> 语法而非直接函数调用</dd>
|
||
<dt>基础知识 · 参考</dt>
|
||
<dd><a class="filelink" target="_blank" href="../../../p03/ch04/custom_signal_slot.cpp">custom_signal_slot.cpp</a>(主参考)·
|
||
<a class="filelink" target="_blank" href="../../../p03/ch04/lambda_signal_slot.cpp">lambda_signal_slot.cpp</a>(lambda 槽写法对比)</dd>
|
||
</dl>
|
||
</div>
|
||
</section>
|
||
<section>
|
||
<div class="task-card">
|
||
<h4>任务卡 3(选做)· 重载信号消歧义 <span class="tag tag-practice">练</span></h4>
|
||
<dl class="task-spec">
|
||
<dt>目标</dt>
|
||
<dd>仿 <code>builtin_signal_slot.cpp</code>,给一个类定义两个同名不同参的信号,用
|
||
<code>QOverload</code> 消歧义连接</dd>
|
||
<dt>验收标准</dt>
|
||
<dd>两个重载信号都能各自触发对应的槽</dd>
|
||
<dt>基础知识 · 参考</dt>
|
||
<dd><a class="filelink" target="_blank" href="../../../p03/ch04/builtin_signal_slot.cpp">p03/ch04/builtin_signal_slot.cpp</a></dd>
|
||
</dl>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
|
||
<!-- ===================== 模块 B:讲 ===================== -->
|
||
<section>
|
||
<section>
|
||
<p class="module-badge">模块 B · 讲授 35min</p>
|
||
<h2>常用控件 / 布局管理 / QSS 美化 <span class="tag tag-lecture">讲</span></h2>
|
||
</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>;
|
||
HTML 富文本标签见 <a class="filelink" target="_blank" href="../../../p03/ch08/label_text_html.cpp">label_text_html.cpp</a></small></p>
|
||
</section>
|
||
|
||
<section>
|
||
<h3>自定义控件与布局:QHBoxLayout <span class="tag tag-lecture">讲</span></h3>
|
||
<pre><code class="cpp" data-trim data-noescape>
|
||
QSpinBox* spin = new QSpinBox(this);
|
||
QSlider* slider = new QSlider(Qt::Horizontal, this);
|
||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||
layout->addWidget(spin);
|
||
layout->addWidget(slider);
|
||
// spin 变化 → slider 跟随,slider 变化 → spin 跟随
|
||
connect(spin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), slider, &QSlider::setValue);
|
||
connect(slider, &QSlider::valueChanged, spin, &QSpinBox::setValue);
|
||
</code></pre>
|
||
<p><small>出处:<a class="filelink" target="_blank"
|
||
href="../../../p03/ch08/custom_widget.cpp">p03/ch08/custom_widget.cpp</a>——布局管理器负责排布,
|
||
不用手算控件坐标。</small></p>
|
||
</section>
|
||
|
||
<section>
|
||
<h3>QSS 界面美化(教学补充) <span class="tag tag-lecture">讲</span></h3>
|
||
<pre><code class="cpp" data-trim data-noescape>
|
||
okBtn->setObjectName("okButton");
|
||
setStyleSheet(
|
||
"QPushButton#okButton { background-color: #2f855a; border-radius: 6px; color: white; }"
|
||
"QPushButton#okButton:hover { background-color: #276749; }"
|
||
);
|
||
</code></pre>
|
||
<p><small>出处:<a class="filelink" target="_blank"
|
||
href="../examples/qss_styling/qt_qss_styling.cpp">docs/teaching/examples/qss_styling/qt_qss_styling.cpp</a>
|
||
(<a class="filelink" target="_blank" href="../examples/qss_styling/README.md">配套 README</a>)——
|
||
仓库 <code>p03/</code> 的 wiki 抓取内容没有 QSS,这是本轮补写的教学补充示例,已离屏验证跑通。</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>组合 <code>button_creation.cpp</code>(数字/运算符按钮)+ <code>custom_signal_slot.cpp</code>
|
||
(点击信号槽)+ <code>custom_widget.cpp</code>(布局排布按钮),做出一个能加减乘除的最小计算器</dd>
|
||
<dt>验收标准</dt>
|
||
<dd>至少支持两个数 + 一种运算符的计算并在 <code>QLabel</code>/<code>QLineEdit</code> 上正确显示结果;
|
||
界面不重叠、按钮可点击</dd>
|
||
<dt>基础知识 · 参考</dt>
|
||
<dd><a class="filelink" target="_blank" href="../../../p03/ch03/button_creation.cpp">button_creation.cpp</a> ·
|
||
<a class="filelink" target="_blank" href="../../../p03/ch04/custom_signal_slot.cpp">custom_signal_slot.cpp</a> ·
|
||
<a class="filelink" target="_blank" href="../../../p03/ch08/custom_widget.cpp">custom_widget.cpp</a></dd>
|
||
</dl>
|
||
</div>
|
||
</section>
|
||
<section>
|
||
<div class="task-card">
|
||
<h4>任务卡 2 · 用 QSS 美化计算器 <span class="tag tag-practice">练</span></h4>
|
||
<dl class="task-spec">
|
||
<dt>目标</dt>
|
||
<dd>给任务卡 1 的计算器套一份 QSS 样式(数字键/运算键/等号键区分颜色)</dd>
|
||
<dt>验收标准</dt>
|
||
<dd>至少 2 类按钮有不同的 <code>background-color</code>;至少 1 个按钮有
|
||
<code>:hover</code> 或 <code>:pressed</code> 效果</dd>
|
||
<dt>基础知识 · 参考</dt>
|
||
<dd><a class="filelink" target="_blank" href="../examples/qss_styling/qt_qss_styling.cpp">qt_qss_styling.cpp</a> ·
|
||
<a class="filelink" target="_blank" href="../examples/qss_styling/README.md">README</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 <target>
|
||
# 有 X 环境:去掉 QT_QPA_PLATFORM=offscreen 直接看窗口</code></pre>
|
||
<p>现场演示计算器,至少完成一次正确运算 + QSS 美化。</p>
|
||
</div>
|
||
<p class="footer-note"><small>明天 Day3:高级控件 Model/View / 事件机制 / 文件 IO 与 JSON ——
|
||
大纲:<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="day1.html">← 上一天</a> ·
|
||
<a class="filelink" href="day3.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>
|