Files
scuc-qt-course/docs/teaching/examples/lineedit_eye_toggle
张宗平 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
..

QLineEdit 自绘小眼睛 / echo mode 切换(qt_lineedit_eye_toggle

Day4 上午教学示例(OUTLINE_4DAY.md Day4 上午「消息机制和事件 + 绘图事件」),同时覆盖两个主题

  • 组件绘制——覆写 paintEvent,用 QPainter 在 QLineEdit 右侧自绘眼睛图标;
  • 事件捕捉——覆写 mousePressEvent / mouseMoveEvent,几何 hit-test 命中眼睛时切换 echoMode,且不阻断基类的光标定位 / 拖选。

载体是 QLineEdit 密码框:右侧一个自绘小眼睛,点击在 PasswordNormal 间切换,并显式 处理与 QLineEdit 内置 clear button 的位置冲突。

三区对照(先简后深)

内容 教学作用
① echo mode 四态 Normal / NoEcho / Password / PasswordEchoOnEdit 静态对照 先给 echo mode 全貌,眼睛只是切换载体
② addAction 一行版 addAction(QIcon, TrailingPosition) 加眼睛,点击切换 Qt 内置、1 行实现——「为什么还要自绘」的引子
③ 自绘版主秀 PasswordLineEdit 自绘眼睛 + 「启用 clear button」复选框 主题落地 + clear button 避让

关键教学点

1. 组件绘制

  • paintEvent 先调基类画原生内容(文本 / 边框 / clear button),再用 QPainter 叠加眼睛。 顺序不能反——基类画完后 clear button 几何才最终确定,眼睛避让才准。
  • 眼睛用 QPainter 纯代码绘制(眼眶椭圆 + 瞳孔 / 斜杠两态),无外部资源。绘制逻辑抽成自由函数 paintEye(),③区自绘与②区 QIcon pixmap 共用(DRY,且对照更直观)。

2. 事件捕捉

  • mousePressEvent:命中眼睛矩形 → 切 echoMode + e->accept() + return调基类, 否则 QLineEdit 把这次点击当光标定位);非命中 → 调基类(光标定位 / 双击选词 / 右键菜单照常)。 这是「自绘交互区与原生编辑行为共存」的核心。
  • mouseMoveEvent + setMouseTracking(true):实现 hover 高亮。坑卡mouseTracking 默认关, 不按鼠标键就收不到 mouseMoveEventhover 高亮出不来。

3. clear button 避让(读 clearButton 子 widget 几何)

QLineEdit 内置 clear button 的宽度随主题 / DPI / 字号变化,不能硬编码偏移(硬编码是反面教材, 换主题即错位)。正确做法:clear button 是 QLineEdit 的一个 objectName=="clearButton" 直接子 widget Qt5 私有 QLineEditIconButton),用 findChild 读它的真实几何:

if (QWidget* cb = findChild<QWidget*>(QStringLiteral("clearButton"),
                                      Qt::FindDirectChildrenOnly)) {
    rightEdge = cb->x() - 4;   // 眼睛贴 clear button 左侧,留 4px 缝
}

眼睛紧贴其左侧;未启用 clear button 或无文本(clear button 不占位)时贴右边。勾选③区复选框即可实时 观察眼睛从「贴右」移到「贴 clear button 左侧」、两者不重叠。

为什么不用 QStyle::subControlRectQLineEdit 不经 QStyle 的 complex-control 绘制, QStyle 没有 CC_LineEdit / SC_LineEditClearButton,无对应 subcontrol 可查——读子 widget 是唯一可靠途径。

目录结构

lineedit_eye_toggle/
├── qt_lineedit_eye_toggle.cpp   # 唯一源文件(paintEye + PasswordLineEdit + ShowcaseWidget + main
├── CMakeLists.txt               # 独立顶层工程(可单独 cmake -S . -B build,不走仓库统一构建)
└── README.md                    # 本文档

构建与运行(Windows + MinGW 7.3.0 + Qt 5.14.2

必须用与 Qt 5.14.2 ABI 匹配的 mingw730_64,不可用 PATH 中更高版本的 g++。

A. 集成构建(仓库统一构建树)

# 配置(在仓库根)
cmake -S . -B build_mw -G "MinGW Makefiles" \
  -DCMAKE_PREFIX_PATH=E:/Qt/Qt5.14.2/5.14.2/mingw73_64 \
  -DCMAKE_CXX_COMPILER=E:/Qt/Qt5.14.2/Tools/mingw730_64/bin/g++.exe \
  -DCMAKE_C_COMPILER=E:/Qt/Qt5.14.2/Tools/mingw730_64/bin/gcc.exe

cmake --build build_mw --target qt_lineedit_eye_toggle

# 运行(GUI,点 ②③ 的眼睛、勾选 clear button 体验)
PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/bin:$PATH \
QT_QPA_PLATFORM_PLUGIN_PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/plugins/platforms \
  ./build_mw/docs/teaching/examples/qt_lineedit_eye_toggle.exe

# 离屏自动退出验证(不交互,退出码应为 0)
QT_QPA_PLATFORM=offscreen \
QT_QPA_PLATFORM_PLUGIN_PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/plugins/platforms \
PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/bin:$PATH \
  ./build_mw/docs/teaching/examples/qt_lineedit_eye_toggle.exe; echo "exit=$?"

B. 独立构建(单独 cmake 工程)

cd docs/teaching/examples/lineedit_eye_toggle
cmake -S . -B build -G "MinGW Makefiles" \
  -DCMAKE_PREFIX_PATH=E:/Qt/Qt5.14.2/5.14.2/mingw73_64 \
  -DCMAKE_CXX_COMPILER=E:/Qt/Qt5.14.2/Tools/mingw730_64/bin/g++.exe \
  -DCMAKE_C_COMPILER=E:/Qt/Qt5.14.2/Tools/mingw730_64/bin/gcc.exe
cmake --build build
PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/bin:$PATH \
QT_QPA_PLATFORM_PLUGIN_PATH=/e/Qt/Qt5.14.2/5.14.2/mingw73_64/plugins/platforms \
  ./build/qt_lineedit_eye_toggle.exe

两条路径编译同一份 qt_lineedit_eye_toggle.cpp,行为一致。集成路径进入仓库统一构建; 独立路径可把这个目录单独拷走即用(仅需本目录三文件 + 一个 Qt 5.14.x)。