每个「讲」知识点补齐核心概念 + 常见的坑,幻灯片与大纲同步
此前一版「讲」部分基本只是代码演示(贴代码 + 一句出处说明),没有讲透 知识点本身的原理。改为覆盖清单原则:每个知识点在大纲里至少固定两项—— 核心概念(是什么/为什么,不只是怎么写)+ 常见的坑(注意事项/误区), 课堂实际讲解深度按学员反馈现场调整,但大纲必须列全,以免漏讲或退化成 纯代码演示。 - custom.css 新增 .concept-box/.pitfall-box 卡片样式 - day1–5.html 每个知识点前插入对应卡片,内容与 OUTLINE.md 的覆盖清单一一对应
This commit is contained in:
@@ -42,6 +42,30 @@ cmake --build build</code></pre>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>最小 Qt 程序:核心概念 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li><code>QApplication</code> 是整个进程唯一的"事件循环拥有者"——<code>app.exec()</code>
|
||||
阻塞在这里进入事件循环,不断从操作系统取事件(鼠标/键盘/定时器/重绘请求)分发出去,
|
||||
程序的"活着"就是这个循环在转,不是运行完就退出的普通程序。</li>
|
||||
<li><code>Q_OBJECT</code> 宏触发 moc(meta-object compiler)在编译前生成额外代码——
|
||||
信号槽、属性系统、<code>qobject_cast</code> 等运行时反射能力都靠这份生成代码实现,
|
||||
这是 Qt 自己的预处理步骤,不是普通 C++ 语法。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>一个进程只能有一个 <code>QApplication</code> 实例,创建第二个直接崩溃/未定义行为。</li>
|
||||
<li>忘记调用 <code>exec()</code>:窗口一闪而过就退出,因为根本没进入事件循环。</li>
|
||||
<li>类里用了信号槽却忘记写 <code>Q_OBJECT</code>:编译能过,链接阶段报
|
||||
<code>undefined reference</code>——报错信息完全不提示"缺宏",是新手最常踩的坑。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>最小 Qt 程序:QApplication + exec() <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
@@ -63,6 +87,28 @@ int main(int argc, char* argv[]) {
|
||||
<code>QTimer::singleShot</code> 200ms 自动退出,便于离屏自动化验证,真实项目不需要这一行)</small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>控件创建:对象树与父子所有权 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li>QObject 有一套"对象树"父子所有权模型:给控件传入 <code>parent</code> 后,
|
||||
parent 被销毁时会自动 <code>delete</code> 所有子对象——这就是为什么 Qt 代码里到处是
|
||||
<code>new</code> 却几乎不见 <code>delete</code>。</li>
|
||||
<li>两种写法(先构造后设属性 / 构造时一次性传参)本质相同,父对象的作用一样。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>忘记传 <code>parent</code>:控件永远不会自动释放(真实内存泄漏),也不会自动出现在
|
||||
窗口里(没有父容器就没有布局位置)。</li>
|
||||
<li>用 <code>move()</code> 手动摆放坐标的控件,一旦外层套上布局管理器,手动坐标会被
|
||||
布局在下次刷新时覆盖——这是"混用绝对定位和布局"最常见的诡异 bug 来源。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>控件创建:两种写法 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
@@ -79,6 +125,32 @@ QPushButton* btn2 = new QPushButton(QStringLiteral("孙悟空"), this);
|
||||
href="../../../p03/ch03/button_creation.cpp">p03/ch03/button_creation.cpp</a></small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>信号槽:观察者模式 + moc <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li>信号槽本质是"观察者模式"的 Qt 实现:发送者不知道、也不关心谁在监听,彻底解耦发送方
|
||||
和接收方——这和"直接调用某个函数"是完全不同的设计取向。</li>
|
||||
<li><code>connect()</code> 背后依赖 moc 生成的元对象信息做类型检查:新语法
|
||||
<code>&Class::signal</code> 是<strong>编译期</strong>检查,字符串旧语法
|
||||
<code>SIGNAL()/SLOT()</code> 是<strong>运行时</strong>检查,容易悄悄连接失败。</li>
|
||||
<li>连接方式分 Direct(同线程直接调用)/ Queued(跨线程排队,下次事件循环处理)/
|
||||
Auto(默认,同线程 Direct、跨线程自动 Queued)——<a class="filelink" target="_blank"
|
||||
href="day4.html">Day4</a> 讲 QThread 时还会用到。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>旧的字符串语法 <code>SIGNAL(clicked())</code> 函数名/参数类型写错不会编译报错,
|
||||
只会运行时"连接失败"且经常没有提示——能用新语法就不用旧语法,编译期就能发现问题。</li>
|
||||
<li>重载信号(如右侧例子的两个 <code>hungry()</code>)不加 <code>QOverload</code>
|
||||
消歧义,编译器不知道该连哪个重载,直接报错。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>信号槽:内置 / 自定义 / 重载消歧义 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
@@ -160,6 +232,27 @@ connect(t, QOverload<QString>::of(&Teacher::hungry), s, QOverload<QStri
|
||||
<h2>常用控件 / 布局管理 / QSS 美化 <span class="tag tag-lecture">讲</span></h2>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>资源系统:.qrc 是什么 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li><code>:/Image/boat.jpg</code> 这种路径不是文件系统路径,是 Qt 资源系统(QRC)——
|
||||
图片等资源在编译期被打包进可执行文件本身,运行时不依赖外部文件是否存在,这对
|
||||
"打包发布"很关键。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li><code>.qrc</code> 里资源路径/文件名和代码里 <code>:/...</code> 引用的不一致,会在
|
||||
运行时静默失败(<code>QPixmap</code> 为空、图不显示),不会报编译错误。</li>
|
||||
<li>忘记把 <code>.qrc</code> 接入 CMake 的 <code>qt5_add_resources</code>/AUTORCC,
|
||||
资源系统压根不生效。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>常用控件:QLabel 显示图片 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
@@ -173,6 +266,28 @@ label->setPixmap(pixmap);
|
||||
HTML 富文本标签见 <a class="filelink" target="_blank" href="../../../p03/ch08/label_text_html.cpp">label_text_html.cpp</a></small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>布局管理:为什么不手算坐标 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li>布局管理器(<code>QLayout</code>)接管子控件的位置和大小计算,随窗口缩放自动重新
|
||||
排布——这是"不要自己算坐标"的根本原因,也是跨平台/跨分辨率 UI 能正确显示的基础。</li>
|
||||
<li>布局对象本身也遵循 QObject 父子所有权:<code>new QHBoxLayout(this)</code>
|
||||
把布局设为 <code>this</code> 的布局,同时隐式接管了 <code>addWidget</code> 进来的子控件。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>同一个控件不能同时属于两个布局,重复 <code>addWidget</code> 到不同布局会导致界面
|
||||
异常或崩溃。</li>
|
||||
<li>布局设置之后又用 <code>setGeometry</code>/<code>move</code> 手动改控件位置,效果
|
||||
会被布局在下次刷新时覆盖(呼应上一节"控件创建"的坑)。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>自定义控件与布局:QHBoxLayout <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
@@ -190,6 +305,29 @@ connect(slider, &QSlider::valueChanged, spin, &QSpinBox::setValue);
|
||||
不用手算控件坐标。</small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>QSS:类 CSS 的样式层叠 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li>QSS 语法脱胎于 CSS:选择器(类型选择器 <code>QPushButton</code>、ID 选择器
|
||||
<code>#okButton</code>、伪状态 <code>:hover</code>/<code>:pressed</code>)+ 属性声明,
|
||||
但作用对象是 QWidget 树而不是 DOM。</li>
|
||||
<li>样式表遵循层叠规则:作用在父控件上的样式默认会被子控件继承,除非子控件有自己更
|
||||
具体的规则覆盖。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>用 ID 选择器(<code>#okButton</code>)必须先 <code>setObjectName("okButton")</code>,
|
||||
忘记设置会导致规则完全不生效且没有任何报错。</li>
|
||||
<li>在某个容器上 <code>setStyleSheet</code> 会级联影响它所有子控件(包括不想改的),
|
||||
范围失控是 QSS 最容易踩的坑,通常要用更具体的选择器限定范围。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>QSS 界面美化(教学补充) <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
|
||||
Reference in New Issue
Block a user