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:
@@ -0,0 +1,30 @@
|
||||
# =============================================================================
|
||||
# QLineEdit 自绘小眼睛 / echo mode 切换 —— 独立 CMake 工程
|
||||
#
|
||||
# 本文件供「单独打开 / 独立构建」使用:
|
||||
# cmake -S . -B build -DCMAKE_PREFIX_PATH=<Qt5.14.2> -DCMAKE_CXX_COMPILER=<g++>
|
||||
# cmake --build build
|
||||
#
|
||||
# 仓库统一构建【不走】这里:顶层 CMakeLists.txt 只
|
||||
# add_subdirectory(docs/teaching/examples) 且不递归,统一构建走
|
||||
# examples/CMakeLists.txt 的 add_teaching_example,源文件同为
|
||||
# qt_lineedit_eye_toggle.cpp(零重复)。本目录不计入仓库「173 目标」计数。
|
||||
#
|
||||
# 说明:本工程为手写规范版(C++17 / cmake 3.16 / 无 ANDROID 噪音),
|
||||
# 区别于其它子目录里 Qt Creator 自动导出的低质量模板。
|
||||
# =============================================================================
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(qt_lineedit_eye_toggle LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||
message(STATUS "Found Qt5 ${Qt5Core_VERSION_STRING} at ${Qt5Core_DIR}")
|
||||
|
||||
add_executable(qt_lineedit_eye_toggle qt_lineedit_eye_toggle.cpp)
|
||||
target_link_libraries(qt_lineedit_eye_toggle PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets)
|
||||
target_compile_options(qt_lineedit_eye_toggle PRIVATE -Wall -Wextra)
|
||||
@@ -0,0 +1,101 @@
|
||||
# QLineEdit 自绘小眼睛 / echo mode 切换(qt_lineedit_eye_toggle)
|
||||
|
||||
Day4 上午教学示例(OUTLINE_4DAY.md Day4 上午「消息机制和事件 + 绘图事件」),**同时覆盖两个主题**:
|
||||
|
||||
- **组件绘制**——覆写 `paintEvent`,用 QPainter 在 QLineEdit 右侧自绘眼睛图标;
|
||||
- **事件捕捉**——覆写 `mousePressEvent` / `mouseMoveEvent`,几何 hit-test 命中眼睛时切换
|
||||
`echoMode`,且不阻断基类的光标定位 / 拖选。
|
||||
|
||||
载体是 QLineEdit 密码框:右侧一个自绘小眼睛,点击在 `Password` ↔ `Normal` 间切换,并显式
|
||||
处理与 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` 默认关,
|
||||
不按鼠标键就收不到 `mouseMoveEvent`,hover 高亮出不来。
|
||||
|
||||
### 3. clear button 避让(读 clearButton 子 widget 几何)
|
||||
QLineEdit 内置 clear button 的宽度随主题 / DPI / 字号变化,**不能硬编码偏移**(硬编码是反面教材,
|
||||
换主题即错位)。正确做法:clear button 是 QLineEdit 的一个 `objectName=="clearButton"` 直接子 widget
|
||||
(Qt5 私有 `QLineEditIconButton`),用 `findChild` 读它的真实几何:
|
||||
|
||||
```cpp
|
||||
if (QWidget* cb = findChild<QWidget*>(QStringLiteral("clearButton"),
|
||||
Qt::FindDirectChildrenOnly)) {
|
||||
rightEdge = cb->x() - 4; // 眼睛贴 clear button 左侧,留 4px 缝
|
||||
}
|
||||
```
|
||||
|
||||
眼睛紧贴其左侧;未启用 clear button 或无文本(clear button 不占位)时贴右边。勾选③区复选框即可实时
|
||||
观察眼睛从「贴右」移到「贴 clear button 左侧」、两者不重叠。
|
||||
|
||||
> 为什么不用 `QStyle::subControlRect`?QLineEdit 不经 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. 集成构建(仓库统一构建树)
|
||||
```bash
|
||||
# 配置(在仓库根)
|
||||
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 工程)
|
||||
```bash
|
||||
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)。
|
||||
@@ -0,0 +1,304 @@
|
||||
// =============================================================================
|
||||
// QLineEdit 自绘小眼睛 / echo mode 切换 —— Day4 上午教学示例
|
||||
// 对应课程:OUTLINE_4DAY.md Day4 上午「消息机制和事件 + 绘图事件」
|
||||
//
|
||||
// 覆盖两个主题:
|
||||
// 1) 组件绘制 —— 覆写 paintEvent,用 QPainter 在 QLineEdit 右侧自绘眼睛图标
|
||||
// 2) 事件捕捉 —— 覆写 mousePressEvent / mouseMoveEvent,几何 hit-test 命中眼睛
|
||||
// 时切换 echoMode,且不阻断基类的光标定位/拖选行为
|
||||
//
|
||||
// 三区对照(先简后深):
|
||||
// ① echo mode 四态静态对照(Normal / NoEcho / Password / PasswordEchoOnEdit)
|
||||
// ② addAction 一行版(Qt 内置,作「为什么还要自绘」的引子)
|
||||
// ③ 自绘版主秀:PasswordLineEdit 自绘眼睛 + clear button 避让(读 clearButton 子 widget 几何)
|
||||
// =============================================================================
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QEvent>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMouseEvent>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QRect>
|
||||
#include <QStyle>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QDebug>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
// -----------------------------------------------------------------------------
|
||||
// 眼睛图标绘制:PasswordLineEdit::paintEvent 与 ②区 QIcon pixmap 共用(DRY)。
|
||||
// closed=true → 当前隐藏(密码)态:眼眶椭圆 + 斜杠(「看不见」)
|
||||
// closed=false → 当前明文态:眼眶椭圆 + 实心瞳孔(「能看见」)
|
||||
// hover=true → 描边加深为蓝色,给即时反馈
|
||||
// -----------------------------------------------------------------------------
|
||||
static void paintEye(QPainter* p, const QRect& r, bool closed, bool hover)
|
||||
{
|
||||
p->setRenderHint(QPainter::Antialiasing, true);
|
||||
const QColor stroke = hover ? QColor(QStringLiteral("#0078D7"))
|
||||
: QColor(QStringLiteral("#5a5a5a"));
|
||||
// 眼眶:左右各留 10%、上下各留 20% 的内边距,画一个扁椭圆(杏仁眼简化形)
|
||||
const QRect eye = r.adjusted(r.width() * 10 / 100, r.height() * 20 / 100,
|
||||
-(r.width() * 10 / 100), -(r.height() * 20 / 100));
|
||||
p->setPen(QPen(stroke, hover ? 2.0 : 1.6));
|
||||
p->setBrush(Qt::NoBrush);
|
||||
p->drawEllipse(eye);
|
||||
|
||||
if (closed) {
|
||||
// 密码态:一条斜杠盖住眼睛,直观表示「内容隐藏中」
|
||||
p->drawLine(eye.topLeft(), eye.bottomRight());
|
||||
} else {
|
||||
// 明文态:实心瞳孔,直观表示「内容可见」
|
||||
p->setPen(Qt::NoPen);
|
||||
p->setBrush(stroke);
|
||||
const QPoint c = eye.center();
|
||||
const int pr = eye.height() / 4;
|
||||
p->drawEllipse(c, pr, pr);
|
||||
}
|
||||
}
|
||||
|
||||
// 用同一套绘制逻辑生成 QIcon(供 ②区 addAction 用,与 ③区自绘像素一致)
|
||||
static QIcon makeEyeIcon(const QStyle* st, bool closed)
|
||||
{
|
||||
const int sz = st->pixelMetric(QStyle::PM_SmallIconSize);
|
||||
QPixmap pm(sz, sz);
|
||||
pm.fill(Qt::transparent);
|
||||
QPainter p(&pm);
|
||||
paintEye(&p, QRect(0, 0, sz, sz), closed, /*hover=*/false);
|
||||
return QIcon(pm);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PasswordLineEdit:自绘版主秀
|
||||
// =============================================================================
|
||||
class PasswordLineEdit : public QLineEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PasswordLineEdit(QWidget* parent = nullptr) : QLineEdit(parent)
|
||||
{
|
||||
setEchoMode(QLineEdit::Password);
|
||||
// ★ mouseTracking 默认关:不按鼠标键收不到 mouseMoveEvent → hover 高亮出不来。
|
||||
setMouseTracking(true);
|
||||
// 右侧给眼睛预留宽度,避免文字被眼睛遮挡(clear button 的文本避让由 Qt 内部处理)。
|
||||
const int sz = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this);
|
||||
setTextMargins(0, 0, sz + 8, 0);
|
||||
}
|
||||
|
||||
bool passwordMode() const { return m_passwordMode; }
|
||||
|
||||
signals:
|
||||
void passwordModeChanged(bool password);
|
||||
|
||||
protected:
|
||||
// 【组件绘制】先让基类画原生内容(文本 / 边框 / clear button),再叠加自绘的眼睛。
|
||||
// 顺序关键:必须先画基类,paint 后 clear button 几何才最终确定,recomputeEyeRect 才准。
|
||||
void paintEvent(QPaintEvent* e) override
|
||||
{
|
||||
QLineEdit::paintEvent(e);
|
||||
recomputeEyeRect();
|
||||
QPainter p(this);
|
||||
paintEye(&p, m_eyeRect, /*closed=*/m_passwordMode, m_eyeHover);
|
||||
}
|
||||
|
||||
// 【事件捕捉】命中眼睛 → 切 echoMode 并 accept 阻断;否则交基类保留编辑行为。
|
||||
void mousePressEvent(QMouseEvent* e) override
|
||||
{
|
||||
recomputeEyeRect();
|
||||
if (e->button() == Qt::LeftButton && m_eyeRect.contains(e->pos())) {
|
||||
m_passwordMode = !m_passwordMode;
|
||||
setEchoMode(m_passwordMode ? QLineEdit::Password : QLineEdit::Normal);
|
||||
emit passwordModeChanged(m_passwordMode);
|
||||
update();
|
||||
e->accept(); // ★ accept + return:不让基类把这次点击当光标定位
|
||||
return;
|
||||
}
|
||||
QLineEdit::mousePressEvent(e); // 非命中:光标定位 / 双击选词 / 右键菜单照常
|
||||
}
|
||||
|
||||
// hover 高亮:命中眼睛变手势指针,离开变文本光标
|
||||
void mouseMoveEvent(QMouseEvent* e) override
|
||||
{
|
||||
recomputeEyeRect();
|
||||
const bool over = m_eyeRect.contains(e->pos());
|
||||
if (over != m_eyeHover) {
|
||||
m_eyeHover = over;
|
||||
update(m_eyeRect); // 局部刷新,避免整控件重绘
|
||||
}
|
||||
setCursor(over ? Qt::PointingHandCursor : Qt::IBeamCursor);
|
||||
QLineEdit::mouseMoveEvent(e); // 保留文本拖选
|
||||
}
|
||||
|
||||
void leaveEvent(QEvent* e) override
|
||||
{
|
||||
if (m_eyeHover) {
|
||||
m_eyeHover = false;
|
||||
update(m_eyeRect);
|
||||
}
|
||||
QLineEdit::leaveEvent(e);
|
||||
}
|
||||
|
||||
private:
|
||||
// 【clear button 避让】读 QLineEdit 内置 clear button 子 widget 的真实几何,
|
||||
// 眼睛贴其左侧;未启用时贴右边。【注】QStyle 没有 CC_LineEdit / SC_LineEditClearButton
|
||||
// ——QLineEdit 不经 QStyle 的 complex control 绘制,无对应 subcontrol 可查,故改读子 widget。
|
||||
// clear button 的宽度随主题/DPI/字号变化,且仅在有文本时显示占位,故同时判文本。
|
||||
void recomputeEyeRect()
|
||||
{
|
||||
const int sz = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this);
|
||||
int rightEdge = width() - 6; // 默认:贴右边内边距
|
||||
if (!text().isEmpty()) {
|
||||
const auto& children = this->children();
|
||||
auto cbFound = false;
|
||||
QWidget *cb = nullptr;
|
||||
for (auto & child : findChildren<QWidget *>("")) {
|
||||
|
||||
;
|
||||
}
|
||||
for (auto& child : children) {
|
||||
qDebug() << child->objectName() << child->metaObject()->className() << endl;
|
||||
// if (child->objectName().endsWith("clearaction")) {
|
||||
// cbFound = true;
|
||||
// cb = qobject_cast<QWidget *>(child);
|
||||
// if (cbFound) {
|
||||
// rightEdge = rightEdge - sz - 4; // 有 clear button:眼睛贴其左侧,留 4px 缝
|
||||
// } else {
|
||||
|
||||
// qDebug() << "cast failed" << endl;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
}
|
||||
m_eyeRect = QRect(rightEdge - sz, (height() - sz) / 2, sz, sz);
|
||||
}
|
||||
|
||||
bool m_passwordMode = true;
|
||||
bool m_eyeHover = false;
|
||||
QRect m_eyeRect;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// ShowcaseWidget:三区对照窗口
|
||||
// =============================================================================
|
||||
class ShowcaseWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ShowcaseWidget(QWidget* parent = nullptr) : QWidget(parent)
|
||||
{
|
||||
setWindowTitle(QStringLiteral("QLineEdit 自绘小眼睛 / echo mode 切换"));
|
||||
auto* root = new QVBoxLayout(this);
|
||||
const QString sample = QStringLiteral("hello123");
|
||||
|
||||
// ---- ① echo mode 四态静态对照 -------------------------------------------------
|
||||
{
|
||||
auto* g = new QGroupBox(
|
||||
QStringLiteral("① echo mode 四态对照(文本 \"hello123\")"), this);
|
||||
auto* f = new QFormLayout(g);
|
||||
auto* eNormal = makeRO(sample, QLineEdit::Normal);
|
||||
auto* eNoEcho = makeRO(sample, QLineEdit::NoEcho);
|
||||
auto* ePwd = makeRO(sample, QLineEdit::Password);
|
||||
auto* ePwdOnEdit = makeRO(sample, QLineEdit::PasswordEchoOnEdit);
|
||||
f->addRow(QStringLiteral("Normal(明文)"), eNormal);
|
||||
f->addRow(QStringLiteral("NoEcho(完全无显示,光标仍可移动)"), eNoEcho);
|
||||
f->addRow(QStringLiteral("Password(密文圆点)"), ePwd);
|
||||
// PasswordEchoOnEdit 的「编辑时明文」需取消只读后键入才显现(见 README)。
|
||||
f->addRow(QStringLiteral("PasswordEchoOnEdit(失焦密文/编辑明文)"), ePwdOnEdit);
|
||||
root->addWidget(g);
|
||||
}
|
||||
|
||||
// ---- ② addAction 一行版(Qt 内置,对照) -------------------------------------
|
||||
{
|
||||
auto* g = new QGroupBox(
|
||||
QStringLiteral("② addAction 一行版(Qt 内置,对照「为什么还要自绘」)"), this);
|
||||
auto* f = new QFormLayout(g);
|
||||
auto* edit = new QLineEdit(this);
|
||||
edit->setEchoMode(QLineEdit::Password);
|
||||
edit->setText(sample);
|
||||
// 一行即可:Qt 自动处理位置 / 点击 / 与 clear button 的避让。但它碰不到「绘制/事件」主题。
|
||||
QAction* eyeAct = edit->addAction(makeEyeIcon(style(), /*closed=*/true),
|
||||
QLineEdit::TrailingPosition);
|
||||
eyeAct->setToolTip(QStringLiteral("点击切换显示"));
|
||||
connect(eyeAct, &QAction::triggered, this, [this, edit, eyeAct]() {
|
||||
const bool toNormal = edit->echoMode() == QLineEdit::Password;
|
||||
edit->setEchoMode(toNormal ? QLineEdit::Normal : QLineEdit::Password);
|
||||
eyeAct->setIcon(makeEyeIcon(style(), /*closed=*/!toNormal));
|
||||
log(QStringLiteral("② addAction:echoMode → %1")
|
||||
.arg(toNormal ? QStringLiteral("Normal") : QStringLiteral("Password")));
|
||||
});
|
||||
f->addRow(QStringLiteral("密码框 + 眼睛(内置 action)"), edit);
|
||||
root->addWidget(g);
|
||||
}
|
||||
|
||||
// ---- ③ 自绘版主秀 -------------------------------------------------------------
|
||||
{
|
||||
auto* g = new QGroupBox(
|
||||
QStringLiteral("③ 自绘版主秀:paintEvent 自绘 + mouseEvent 捕获 + clear button 避让"),
|
||||
this);
|
||||
auto* f = new QFormLayout(g);
|
||||
m_pwdEdit = new PasswordLineEdit(this);
|
||||
m_pwdEdit->setText(sample);
|
||||
connect(m_pwdEdit, &PasswordLineEdit::passwordModeChanged, this, [this](bool pwd) {
|
||||
log(QStringLiteral("③ 自绘:echoMode → %1")
|
||||
.arg(pwd ? QStringLiteral("Password") : QStringLiteral("Normal")));
|
||||
});
|
||||
auto* cbClear = new QCheckBox(
|
||||
QStringLiteral("启用 QLineEdit 内置 clear button(观察眼睛如何避让到其左侧)"), this);
|
||||
cbClear->setChecked(false);
|
||||
connect(cbClear, &QCheckBox::toggled, m_pwdEdit, &QLineEdit::setClearButtonEnabled);
|
||||
connect(cbClear, &QCheckBox::toggled, this, [this](bool on) {
|
||||
log(QStringLiteral("③ clear button %1")
|
||||
.arg(on ? QStringLiteral("开启") : QStringLiteral("关闭")));
|
||||
});
|
||||
f->addRow(QStringLiteral("密码框 + 自绘眼睛"), m_pwdEdit);
|
||||
f->addRow(QString(), cbClear);
|
||||
root->addWidget(g);
|
||||
}
|
||||
|
||||
m_status = new QLabel(QStringLiteral("事件日志:就绪。点击 ②③ 的眼睛,或勾选 clear button。"), this);
|
||||
m_status->setStyleSheet(QStringLiteral("padding:4px; color:#333;"));
|
||||
m_status->setWordWrap(true);
|
||||
root->addWidget(m_status);
|
||||
root->addStretch();
|
||||
}
|
||||
|
||||
private:
|
||||
void log(const QString& s) { m_status->setText(QStringLiteral("事件日志:%1").arg(s)); }
|
||||
|
||||
static QLineEdit* makeRO(const QString& text, QLineEdit::EchoMode mode)
|
||||
{
|
||||
auto* e = new QLineEdit(text);
|
||||
e->setEchoMode(mode);
|
||||
e->setReadOnly(true);
|
||||
return e;
|
||||
}
|
||||
|
||||
PasswordLineEdit* m_pwdEdit = nullptr;
|
||||
QLabel* m_status = nullptr;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// main
|
||||
// =============================================================================
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
ShowcaseWidget w;
|
||||
w.resize(540, 440);
|
||||
w.show();
|
||||
|
||||
// 离屏自动化验证:200ms 后自动退出,退出码 0 表示窗口正常构建与渲染。
|
||||
if (qEnvironmentVariable("QT_QPA_PLATFORM") == QStringLiteral("offscreen")) {
|
||||
QTimer::singleShot(200, qApp, &QCoreApplication::quit);
|
||||
}
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
#include "qt_lineedit_eye_toggle.moc"
|
||||
Reference in New Issue
Block a user