Files
scuc-qt-course/docs/superpowers/specs/2026-07-08-lineedit-eye-toggle-design.md
T
张宗平 01b264fe97 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>
2026-07-09 16:51:09 +08:00

6.1 KiB
Raw Blame History

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 扩展——在右侧加一个 小眼睛图标,点击动态切换 echoModePasswordNormal),并显式处理与 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.txtadd_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)。