每个「讲」知识点补齐核心概念 + 常见的坑,幻灯片与大纲同步
此前一版「讲」部分基本只是代码演示(贴代码 + 一句出处说明),没有讲透 知识点本身的原理。改为覆盖清单原则:每个知识点在大纲里至少固定两项—— 核心概念(是什么/为什么,不只是怎么写)+ 常见的坑(注意事项/误区), 课堂实际讲解深度按学员反馈现场调整,但大纲必须列全,以免漏讲或退化成 纯代码演示。 - custom.css 新增 .concept-box/.pitfall-box 卡片样式 - day1–5.html 每个知识点前插入对应卡片,内容与 OUTLINE.md 的覆盖清单一一对应
This commit is contained in:
@@ -30,6 +30,30 @@
|
||||
<h2>Model/View 架构 / 事件机制 <span class="tag tag-lecture">讲</span></h2>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Model/View:数据与展示分离 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li>Model/View 把"数据"和"怎么展示数据"彻底拆开:模型(<code>QAbstractItemModel</code>
|
||||
及其子类)只负责存数据、回答"这个位置是什么",视图(<code>QListView</code>/
|
||||
<code>QTableView</code>/...)只负责画——同一份模型可以同时喂给多个不同视图。</li>
|
||||
<li>模型用统一的 <code>QModelIndex</code>(行/列/父项)寻址数据,视图向模型要数据时
|
||||
还要带上"角色"(<code>Qt::DisplayRole</code> 显示文本、<code>Qt::DecorationRole</code>
|
||||
图标……),同一个位置可以按不同角色返回不同内容。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>直接改模型内部数据却不发信号(<code>dataChanged</code>/<code>beginInsertRows</code>+
|
||||
<code>endInsertRows</code>),视图不会自动刷新,界面和数据"对不上"。</li>
|
||||
<li>用了简单的 <code>QStandardItemModel</code> 却又想自定义复杂数据结构:应该继承
|
||||
<code>QAbstractItemModel</code> 自己实现,而不是硬塞进 Item 模型。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Model/View 模型视图架构(教学补充) <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
@@ -50,6 +74,31 @@ connect(view->selectionModel(), &QItemSelectionModel::currentChanged, ...);
|
||||
仓库 <code>p03/</code> 的 wiki 抓取内容没有 Model/View,这是本轮补写的教学补充示例,已离屏验证跑通。</small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>事件循环:事件 vs 信号 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li><code>app.exec()</code> 进入的就是事件循环(复用
|
||||
<a class="filelink" target="_blank" href="day2.html">Day2</a> 概念)——鼠标点击、
|
||||
键盘输入、定时器触发都会变成"事件"(<code>QEvent</code>),被塞进这个循环里依次分发给
|
||||
对应控件。</li>
|
||||
<li>事件和信号是两条不同的机制:事件是"操作系统/框架推给控件"的通知,走
|
||||
<code>event()</code>/<code>xxxEvent()</code> 虚函数或事件过滤器;信号槽是"对象之间"
|
||||
主动通知,走 <code>connect()</code>。控件收到事件后,常见做法就是在事件处理函数里
|
||||
<code>emit</code> 一个信号,把"底层事件"转成"业务语义通知"。</li>
|
||||
<li>今天讲的"事件过滤器"就是在事件被分发给控件之前,插一个"拦截点"。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>在事件循环所在的主线程做耗时操作会直接卡住整个界面(无法重绘、按钮点了没反应)——
|
||||
这也是 <a class="filelink" target="_blank" href="day4.html">Day4</a> 要讲 QThread 的原因。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>事件循环机制(复用 Day2 概念) <span class="tag tag-lecture">讲</span></h3>
|
||||
<p><code>app.exec()</code> 进入的就是事件循环——鼠标点击、键盘输入、定时器
|
||||
@@ -59,6 +108,29 @@ connect(view->selectionModel(), &QItemSelectionModel::currentChanged, ...);
|
||||
href="../../../p03/ch02/minimal_qt_app.cpp">p03/ch02/minimal_qt_app.cpp</a></small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>自定义事件处理:核心概念 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li>拦截事件有两条路:重写 <code>xxxEvent()</code>(只影响这一个控件自己)或装事件过滤器
|
||||
<code>installEventFilter()</code>(可以在事件到达目标控件之前,由另一个对象统一拦截,
|
||||
常用于"一个地方管多个控件"的场景)。</li>
|
||||
<li>事件对象有 <code>accept()</code>/<code>ignore()</code> 状态:处理完事件要表明"我
|
||||
吃掉了这个事件"还是"继续往上传",决定事件是否会继续传播给父控件。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>重写 <code>xxxEvent()</code> 时忘记调用基类实现(如 <code>QWidget::mouseMoveEvent</code>),
|
||||
可能丢失控件本身需要的默认行为。</li>
|
||||
<li>事件过滤器安装到一个对象上后,如果被观察对象先于过滤器对象销毁(或反过来),要注意
|
||||
生命周期——Qt 会在对象销毁时自动清理,但过滤逻辑里持有的裸指针不会自动清空。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>自定义事件处理 / 事件过滤器 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
@@ -123,6 +195,29 @@ protected:
|
||||
<h2>文件 IO / JSON 解析 <span class="tag tag-lecture">讲</span></h2>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>QIODevice:统一的读写抽象 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li><code>QFile</code>/<code>QTextStream</code>/<code>QDataStream</code> 都建立在
|
||||
<code>QIODevice</code> 这一统一读写接口上——同样的"打开/读/写/关闭"套路,以后遇到网络
|
||||
套接字/串口一类设备也是同一套接口,学一次可以迁移到别的设备类型。</li>
|
||||
<li><code>QTextStream</code> 面向"文本行"(人可读),<code>QDataStream</code> 面向
|
||||
"结构化二进制"(保留类型信息,程序间高效互传但人不可直接读)——按需求选,不是谁更好。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>忘记检查 <code>open()</code> 的返回值:文件不存在/无权限时静默失败,后续读写在一个
|
||||
没打开的设备上操作,容易读出空数据而不报错。</li>
|
||||
<li>混淆文本模式和二进制模式(<code>QIODevice::Text</code>):文本模式下 Qt 会做换行符
|
||||
转换,用来读写二进制数据会破坏内容。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>文件系统:QFile 读文本 <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
@@ -136,6 +231,29 @@ while (!file.atEnd()) { qDebug() << file.readLine(); }
|
||||
<a class="filelink" target="_blank" href="../../../p03/ch11/qdatastream_io.cpp">qdatastream_io.cpp</a></small></p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>JSON:序列化与反序列化的往返 <span class="tag tag-lecture">讲</span></h3>
|
||||
<div class="concept-box">
|
||||
<h4>核心概念</h4>
|
||||
<ul>
|
||||
<li>JSON 读写的本质是"内存对象 ↔ 文本"的一对互逆操作:序列化(<code>QJsonDocument::toJson</code>)
|
||||
把内存数据变成可存/可传的文本,反序列化(<code>fromJson</code>)再变回来——正确性的标准是
|
||||
"写出去再读回来,数据要一致"(round-trip)。</li>
|
||||
<li><code>QJsonValue</code> 是一个变体(variant)类型,能装字符串/数字/布尔/数组/对象,
|
||||
取值时要按预期类型调用 <code>toString()</code>/<code>toInt()</code> 等。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pitfall-box">
|
||||
<h4>常见的坑</h4>
|
||||
<ul>
|
||||
<li>忘记检查 <code>QJsonDocument::fromJson</code> 的 <code>QJsonParseError</code>:
|
||||
文本格式错误时返回一个空文档,后续取值全是默认值,不会崩溃也不会报错,很难定位。</li>
|
||||
<li>类型不匹配时 <code>toInt()</code>/<code>toString()</code> 等不会抛异常,只会静默
|
||||
返回默认值(0/空串),排查时容易误以为"数据没写进去"。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>JSON 解析与写入(教学补充) <span class="tag tag-lecture">讲</span></h3>
|
||||
<pre><code class="cpp" data-trim data-noescape>
|
||||
|
||||
Reference in New Issue
Block a user