b89819d5db
大纲按「3天C++强化(含OOP)+1天Qt桌面」组织,讲:练≈1:2.7;每个知识点直接 链接仓库真实代码文件与 ERRATA/VERSION_NOTES 里记录的具体坑。幻灯片 vendor 了 reveal.js 5.2.1(核心+highlight插件),零 CDN 依赖,配 serve.py 本地起服务(仅绑定 127.0.0.1)供离线播放,代码链接可点开查看源码。
307 lines
13 KiB
HTML
307 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>Day 4 · Qt 桌面全景</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>Qt 桌面全景</h3>
|
||
<p class="module-badge">Qt 方向实训 · 第 4 / 4 天 · 讲:练 ≈ 1:2.7</p>
|
||
<ul>
|
||
<li>模块 A(3 课时):项目结构 / 第一个程序 / 信号槽 / 主窗口</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>
|
||
</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></small></p>
|
||
</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(); }
|
||
|
||
QFileInfo info(file);
|
||
qDebug() << info.baseName() << info.suffix();
|
||
</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>
|
||
|
||
<!-- ===================== 模块 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>
|
||
</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>
|
||
</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>每组演示自己选做的方案(A/B/C 三选一),教师验收「编译通过 + 交互符合预期」。</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>
|
||
</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>
|