Day4 教学示例补三个:QDialog 综合 / QLineEdit 小眼睛 / 文件 IO(+顶层构建注册)

- qdialog_showcase:wiki「6 对话框 QDialog」6.2–6.5 综合演示。含两套实现:
  · Designer 表单版(dialog.{h,cpp,ui} + resources.qrc + source.gif,QMovie 播 GIF)
  · 纯代码版(qt_dialog_showcase.cpp,单文件,QWidget 布满按钮逐个演示 QDialog 行为)
- lineedit_eye_toggle:子类化 QLineEdit,paintEvent 自绘小眼睛、mouseEvent 命中切换
  echoMode,并与内置 clear button 动态避让(读 objectName=="clearButton" 子 widget 几何)。
  含独立子目录 CMakeLists.txt 供单独打开(顶层 add_subdirectory 不递归)。
- file_io_demo:QFile/QTextStream/QDataStream/QFileInfo/QDir/QTextCodec/QJson 五主题
  综合示例,每个按钮产出一个文件落到 demo_output/ 便于课堂手动查看。

构建注册:examples/CMakeLists.txt 用 add_teaching_example 注册前两个纯代码示例,
mandelbrot 多源文件单独 add_executable;calculator_mainwindow_qss 加 themes.qrc 与
AUTORCC;all_teaching_examples 依赖列表同步补齐。

附 lineedit_eye_toggle 设计文档(docs/superpowers/specs)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
张宗平
2026-07-09 16:51:09 +08:00
parent 993aa30a1c
commit 01b264fe97
16 changed files with 1694 additions and 3 deletions
@@ -0,0 +1,100 @@
# QLineEdit 自绘小眼睛 / echo mode 切换 教学示例 —— 设计
- **日期**2026-07-08
- **对应课程**:Day4 上午「消息机制和事件 + 绘图事件」(OUTLINE_4DAY.md §2 Day4 上午)
- **目标产物**`docs/teaching/examples/lineedit_eye_toggle/`,交付为两种形态——
① 仓库统一构建里的 `qt_lineedit_eye_toggle` target
② 可独立 `cmake -S . -B build` 构建的单独 CMake 工程。
## 1. 背景与目标
需要一个 Day4 上午课堂演示示例,**同时覆盖两个主题**:组件绘制(`paintEvent`)、
事件捕捉(`mousePressEvent` / `mouseMoveEvent`)。载体是 QLineEdit 扩展——在右侧加一个
小眼睛图标,点击动态切换 `echoMode``Password``Normal`),并显式处理与 QLineEdit
内置 clear button 的位置冲突。
> 选 QLineEdit 而非自造控件:密码框是「自绘内嵌图标 + 点击切换」最真实的工业场景,
> 且复用 QLineEdit 的文本/编辑能力,让示例聚焦在「绘制 + 事件」本身。
## 2. 关键设计决策
### 2.1 组织形态:梯度对照版
窗口分三区,先简后深,让学生看清「为什么」:
| 区 | 内容 | 教学作用 |
|---|---|---|
| ① | 4 种 echo mode 静态对照(Normal/NoEcho/Password/PasswordEchoOnEdit | 先给 echo mode 全貌,眼睛只是切换载体 |
| ② | `addAction(QIcon, TrailingPosition)` 一行版 | Qt 内置做法,1 行实现——「为什么还要自绘」的引子 |
| ③ | `PasswordLineEdit : public QLineEdit` 自绘版 + 「启用 clear button」复选框 | **主题落地**:自绘 + 事件捕获 + clear button 避让 |
### 2.2 实现方式锁定:子类化 + 自绘 + 自捕获
`addAction` 那条路由 Qt 全权处理位置/点击/避让,**碰不到**绘制与事件这两个主题。
故核心交付物必须是子类化 QLineEdit、覆写 `paintEvent`/`mouse*Event`。addAction 仅作
②区对照存在。
### 2.3 clear button 避让:读 clearButton 子 widget 几何
QLineEdit 内置 clear button 的宽度随主题/DPI/字号变化,**不能硬编码偏移**(坑卡)。
clear button 是 QLineEdit 的一个 `objectName=="clearButton"` 直接子 widgetQt5 私有
`QLineEditIconButton`),用 `findChild<QWidget*>("clearButton")` 读其 `geometry()`,眼睛紧贴
其左侧;未启用或无文本时贴右边。
> 注:初版误用 `QStyle::subControlRect(CC_LineEdit, …, SC_LineEditClearButton)`,编译期即报错
> ——`QStyle` 没有这两个成员,QLineEdit 不经 complex-control 绘制,无对应 subcontrol。读子 widget 是唯一可靠途径。
### 2.4 眼睛图标:QPainter 纯代码绘制
- 不引入 `.qrc` 资源(YAGNI),完全契合「组件绘制」主题。
- 两态:`closed=true`(密码态)= 眼眶椭圆 + 斜杠;`closed=false`(明文态)= 眼眶椭圆 + 实心瞳孔。
- 绘制逻辑抽成自由函数 `paintEye(QPainter*, QRect, closed, hover)`,③区自绘与②区
`QIcon` pixmap 共用(DRY,并让对照更明显)。
- hover 态描边加深为蓝色(`#0078D7`),需 `setMouseTracking(true)` 才能在无按键时收到 move。
### 2.5 双形态 CMake(源文件零重复)
- **唯一源文件**`qt_lineedit_eye_toggle.cpp`
- **两个 CMake 上下文**引用它:
- 独立工程:`lineedit_eye_toggle/CMakeLists.txt`(手写规范版,C++17,非 Qt Creator 模板)。
- 仓库集成:`examples/CMakeLists.txt``add_teaching_example(qt_lineedit_eye_toggle lineedit_eye_toggle)`
- **零污染**:顶层 `CMakeLists.txt:58` `add_subdirectory(docs/teaching/examples)` 不递归,
子目录 `CMakeLists.txt` 永不被统一构建触发;`examples/CMakeLists.txt` 无 GLOB。
不计入 README/CLAUDE.md 里「173 目标」的生成器计数。
## 3. `PasswordLineEdit` 子类要点
```
构造: setEchoMode(Password); setMouseTracking(true); setTextMargins(0,0,iconSize+8,0)
paintEvent: base::paintEvent 先画原生; 再 recomputeEyeRect(); QPainter 画眼睛
mousePressEvent: recomputeEyeRect(); 命中眼睛 → 切 m_passwordMode + setEchoMode + accept;
否则 base::mousePressEvent(光标定位/双击选词不受影响)
mouseMoveEvent: recomputeEyeRect(); hit-test 更新 m_eyeHover + setCursor; base 保留拖选
leaveEvent: 清 m_eyeHover 局部刷新
recomputeEyeRect: findChild("clearButton") 读其几何 → 眼睛贴其左 / 贴右
信号: passwordModeChanged(bool) 供窗口状态栏显示
```
**关键不变量**:命中眼睛时 `e->accept()``return`**不**调基类——否则 QLineEdit 会
把点击当光标定位;非命中时务必调基类,保留原生编辑行为。这是「事件捕获」教学点的核心。
## 4. 文件清单
```
docs/teaching/examples/lineedit_eye_toggle/
├── qt_lineedit_eye_toggle.cpp # 唯一源文件(paintEye + PasswordLineEdit + ShowcaseWidget + main
├── CMakeLists.txt # 独立顶层工程(手写规范版,可 cmake -S . -B build
└── README.md # 教学点 / 目录结构 / 构建(集成+独立两条)/ 坑卡
```
另:`docs/teaching/examples/CMakeLists.txt` 注册目标 + 加入 `all_teaching_examples` 依赖列表。
## 5. 验证标准(可证伪)
1. **集成构建**`cmake --build build_mw --target qt_lineedit_eye_toggle` 绿。
2. **独立构建**`cmake -S lineedit_eye_toggle -B lineedit_eye_toggle/build ... && cmake --build` 绿。
3. **离屏**:两路径产物 `QT_QPA_PLATFORM=offscreen` 跑 200ms 后退出码 0。
4. **真实交互**(本机 Qt 实跑,截图为证):③区眼睛可见;点击切 `Password↔Normal`
勾选 clear button 后眼睛正确让到其左侧不重叠;hover 高亮生效。
## 6. 显式假设
- 眼睛用 QPainter 代码绘制,不引入资源文件(契合主题 + YAGNI)。
- 独立 `CMakeLists.txt` 手写规范版(C++17 / `cmake_minimum_required(3.16)` / 无 ANDROID 噪音),
不沿用子目录里 Qt Creator 导出的低质量模板。
- ①区 4 个对照框 `setReadOnly(true)` 防误改;`PasswordEchoOnEdit` 的「编辑时明文」效果在
README 注明需取消只读才能体验。
- **不提交 git**(项目红线:未经请求不操作 git)。