很多时候我们会发现输入的一长串内容不得不全部删除重新输入,这时比起一直按着退格键不放一个清除内容按钮更受欢迎。

今天我将介绍三种为QLineEdit添加清除内容按钮的方法,其中两种方法有较强的功能针对性,另一种方法则是通用的,不仅可以用来实现清除输入内容,还可以扩展出其他功能。

本文索引

方法1:setClearButtonEnabled显示清除按钮

这是Qt5.2之后提供的方法,当使用了`setClearButtonEnabled(true);`之后会在 QLineEdit的右侧显示一个图标为`QStyle::SP_DialogResetButto`的QAction,点击后会清除输入内容:
```c++
// 方案1
auto edit1 = new QLineEdit;
edit1->setClearButtonEnabled(true);
```
效果:

看到右边那个图标,如果是Qt自带的话会是一个类似扫把的图形,如果使用了系统主题那么会有些许差异,点击它,输入内容就会全部清除。

方法2:使用QAction实现清除按钮

如前所述,`setClearButtonEnabled`其实只是让实现存在的QAction显示出来而已,所以我们也可以自己实现这一过程。

要实现这一功能,需要Qt5.2之后提供的addAction方法。它负责把一个QAction添加到edit的指定位置。

不过要注意的是,这个QAction只能显示出图标,文字内容的显示不出的。

// 方案2
auto clearAction = new QAction;
clearAction->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogResetButton));
auto edit2 = new QLineEdit;
// QLineEdit::TrailingPosition表示将action放置在右边
edit2->addAction(clearAction, QLineEdit::TrailingPosition);
QObject::connect(clearAction,
&QAction::triggered,
edit2,
[edit2]{ edit2->setText(""); });

因为我们知道lineedit默认使用的清除按钮的图标,也知道如何清除输入,所以可以自己实现这一过程。

这是效果,与方法1时几乎没什么区别:

不过方法二的威力不止于此,基于我们可以使用自己的QAction,那么就可以定制一些操作,比如使用我们自己的图标:

clearAction->setIcon(QIcon(":/clear.png"));

这种方法相比前一种略显复杂,然而却提供了更好的扩展性。

接下来要介绍的最后一种方法更加的灵活,你不仅可以显示自定义图标,还可以显示自定义文字,当然作为代价它比第二种方法要复杂不少。

方法3:自定义QLineEdit为其添加按钮

这种方法对Qt的版本没有什么要求,所以它也足够通用。

想要在QLineEdit上添加一个widget一点也不复杂,首先我们要弄清以下几个原理:

  • qt的widget和layout是可以堆叠的,之前在实现半透明遮罩中有提过
  • 你可以为QLineEdit设置layout,如你所料layout会堆叠在edit的输入框上
  • edit的layout会只使用控件的最小尺寸,这样不会导致将整个输入框遮盖掉
  • edit的可输入区域是可以设置的,你可以合理的设置输入区的大小避免文字进入layout之下被遮盖

所以如果我们想为QLineEdit或是其派生类添加一个widget比如QPushButton,那么需要如下几部:

  1. 创建你需要的widget以及一个布局管理器
  2. 添加拉伸因子和widget至布局管理器,拉伸因子可以不添加,只要设置好布局管理器的排列方向即可
  3. 设置布局管理器里组件的排列方向并把布局管理器添加到QLineEdit
  4. 获取你添加的widget的宽度,然后在加上合适的边框距离,将QLineEdit的输入区域限制在合理的大小

说起来简单做起来难,我们边看代码边讲解。

我们先看类的定义,ButtonEdit是一个带有按钮的QLineEdit:

#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QString>
#include <QIcon> class ButtonEdit: public QLineEdit {
Q_OBJECT
public:
explicit ButtonEdit(const QString &btnText, QWidget *parent = nullptr);
explicit ButtonEdit(const QIcon &icon, QWidget *parent = nullptr);
~ButtonEdit() override = default; private:
// 设置文本按钮或图标按钮的大小和外观
void setTextButton();
void setIconButton();
// 将按钮添加到edit
void addButton(); QPushButton *button; Q_SIGNALS:
void buttonClicked(bool);
}; // 按钮和输入内容的边距
constexpr int buttonMargin = 3;

我们的类可以从一个string或者icon构建,当edit的按钮被点击那么我们就发出buttonClicked信号。

也许你会觉得对于按钮的设置分成两类没什么必要。事实不然,图形应用的开发有很多麻烦事,而其中比较头疼的要数如何让控件保持一个恰到好处的尺寸,而对于图标的处理和文本是不一样的,所以有分开的必要。当然,如果你不介意文字或者图标只显示一半或者突出到编辑框的话也可以跳过这一步。

下面我们来看下类成员的实现,构造函数没什么亮点,无非构造button,然后交由其他成员去处理:

ButtonEdit::ButtonEdit(const QString &btnText, QWidget *parent)
: QLineEdit(parent)
{
button = new QPushButton(btnText);
setTextButton();
addButton();
} ButtonEdit::ButtonEdit(const QIcon &icon, QWidget *parent)
: QLineEdit(parent)
{
button = new QPushButton;
button->setIcon(icon);
setIconButton();
addButton();
}

接着是addButton,在这里我们先把button添加进layout,随后又设置了输入区域的大小避免输入内容被遮住:

void ButtonEdit::addButton() {
connect(button,
&QPushButton::clicked,
this,
&ButtonEdit::buttonClicked);
// 按钮已经是edit的一部分了,不应该再能被单独聚焦,否则可能导致误触
button->setFocusPolicy(Qt::NoFocus);
// 设置鼠标,否则点击按钮时仍然会显示输入内容时的鼠标图标
button->setCursor(Qt::ArrowCursor); auto btnLayout = new QHBoxLayout;
btnLayout->addStretch();
btnLayout->addWidget(button);
// 设置组件右对齐,按钮会显示在edit的右侧
btnLayout->setAlignment(Qt::AlignRight);
btnLayout->setContentsMargins(0, 0, 0, 0);
setLayout(btnLayout);
// 设置输入区域的范围,从edit的最左到按钮的最左(包含了按钮设置的buttonMargin)
setTextMargins(0, 0, button->width(), 0);
}

下面就是如何设置button的大小和样式了,大小与我们设置的图标/文本的大小一样大,然后两边加上buttonMargin

对于图标按钮我们还要设置按钮背景平时不可见,毕竟图标周围有个buttonMargin宽度的框不太好看:

// 帮助函数,设置按钮的width,大小策略为fixed,不可放大或缩小
static void setButtonSize(QPushButton *button, int width) {
auto policy = button->sizePolicy();
policy.setHorizontalPolicy(QSizePolicy::Fixed);
button->setSizePolicy(policy);
// 固定宽度,加上边距
button->setFixedWidth(width + buttonMargin*2);
} void ButtonEdit::setTextButton() {
if (!button) {
return;
} // 获得当前字体下文本内容的像素宽度
auto width = QWidget::fontMetrics().width(button->text());
setButtonSize(button, width);
} void ButtonEdit::setIconButton() {
if (!button) {
return;
} // 获取图标的width简单得多
auto width = button->iconSize().width();
setButtonSize(button, width);
// 设置背景和边框在非点击时不可见
button->setFlat(true);
}

现在工作完成了,不管我们添加什么样的图标还是多长的文本,按钮都可以保证有一个合适的大小,输入内容也不会被按钮遮住。

现在我们看下使用:

// 方案3
// 使用文本按钮
auto edit3_1 = new ButtonEdit("clear");
QObject::connect(edit3_1,
&ButtonEdit::buttonClicked,
edit3_1,
[edit3_1]{ edit3_1->setText(""); });
// 使用图标按钮
auto edit3_2 = new ButtonEdit(QApplication::style()->standardIcon(QStyle::SP_DialogResetButton));
QObject::connect(edit3_2,
&ButtonEdit::buttonClicked,
edit3_2,
[edit3_2]{ edit3_2->setText(""); });

效果如下:

这种方案是最复杂的,但也是最灵活的,我们可以定制button的外观,通过buttonClicked信号我们可以定制按钮按下后的行为。所以我在上一节才说这是扩展性最好的方法。

不过方案二和三都有一个显著的缺点,即使输入框中没有内容按钮或QAction也会一直显示,有些时候这不是我们需要的行为。解决办法也很简单,合理利用QLineEdit的信号加上QWidget::hideQAction::setVisible就能实现按钮的隐藏,这一功能的实现就当做练习吧。

最终的显示效果

现在我们将三种方法合并显示在一起,以便大家看到各个方案带来的显示效果:

#include <QLineEdit>
#include <QApplication>
#include <QWidget>
#include <QAction>
#include <QObject>
#include <QIcon>
#include <QFormLayout>
#include <QStyle> #include "ButtonEdit" int main(int argc, char *argv[]) {
QApplication app(argc, argv); // 方案1
auto edit1 = new QLineEdit;
edit1->setClearButtonEnabled(true); // 方案2
auto clearAction = new QAction;
clearAction->setIcon(QIcon(":/clear.png"));
auto edit2 = new QLineEdit;
edit2->addAction(clearAction, QLineEdit::TrailingPosition);
QObject::connect(clearAction,
&QAction::triggered,
edit2,
[edit2]{ edit2->setText(""); }); // 方案3
// 使用文本按钮
auto edit3_1 = new ButtonEdit("clear");
QObject::connect(edit3_1,
&ButtonEdit::buttonClicked,
edit3_1,
[edit3_1]{ edit3_1->setText(""); });
// 使用图标按钮
auto edit3_2 = new ButtonEdit(QApplication::style()->standardIcon(QStyle::SP_DialogResetButton));
QObject::connect(edit3_2,
&ButtonEdit::buttonClicked,
edit3_2,
[edit3_2]{ edit3_2->setText(""); }); auto win = new QWidget;
auto layout = new QFormLayout;
layout->addRow("方案1:", edit1);
layout->addRow("方案2:", edit2);
layout->addRow("方案3_1:", edit3_1);
layout->addRow("方案3_2:", edit3_2);
win->setLayout(layout); win->show(); return app.exec();
}

当无输入内容时:

当有输入内容时:

这样三种方法都介绍完了,选用哪种需要自己决定。

当然最后两种方案不仅仅能用来做清除内容按钮,只要加入一点点想象力还有更高级的功能可以用它们来实现。

三种方法为QLineEdit添加清除内容按钮的更多相关文章

  1. C# winform三种方法判断文本框textBox内容是否为空

    使用系统API函数,需要使用命名空间:System.Runtime.InteropServices: 1.if (textBoxPath.Text ==  String.Empty ) 2.if (t ...

  2. 【转】css清除浮动float的三种方法总结,为什么清浮动?浮动会有那些影响?

    摘要: css清除浮动float的三种方法总结,为什么清浮动?浮动会有那些影响?     一.抛一块问题砖(display: block)先看现象: 分析HTML代码结构: <div class ...

  3. MYSQL添加远程用户或允许远程访问三种方法

    添加远程用户admin密码为password GRANT ALL PRIVILEGES ON *.* TO admin@localhost IDENTIFIED BY \'password\' WIT ...

  4. HOSt ip is not allowed to connect to this MySql server, MYSQL添加远程用户或允许远程访问三种方法

    HOSt ip is not allowed to connect to this MySql server 报错:1130-host ... is not allowed to connect to ...

  5. linux清空文件内容的三种方法

    linux系统中清空文件内容的三种方法 1.使用vi/vim命令打开文件后,输入"%d"清空,后保存即可.但当文件内容较大时,处理较慢,命令如下:vim file_name:%d: ...

  6. Ubuntu Linux系统三种方法添加本地软件库

    闲着没事教教大家以Ubuntu Linux系统三种方法添加本地软件库,ubuntu Linux使用本地软件包作为安装源——转2007-04-26 19:47新手重新系统的概率很高,每次重装系统后都要经 ...

  7. mysql 中添加索引的三种方法

    原文:http://www.andyqian.com/2016/04/06/database/mysqleindex/ 在mysql中有多种索引,有普通索引,全文索引,唯一索引,多列索引,小伙伴们可以 ...

  8. Linux 添加开机启动项的三种方法

    linux 添加开机启动项的三种方法. (1)编辑文件 /etc/rc.local 输入命令:vim /etc/rc.local 将出现类似如下的文本片段: #!/bin/sh## This scri ...

  9. 详解linux下批量替换文件内容的三种方法(perl,sed,shell)

    在建设本网站的时候,发现新建了很多的网页,突然发现,每个文件都需要进行修改一样的内容,一个一个打开很是麻烦,所以,总结了一下如何快速修改一个目录下多个文件进行内容替换.第三种方法用的不多 方法一 使用 ...

随机推荐

  1. 如何改变XCode的默认设置

    改变bundle ID 进入 /Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Appl ...

  2. 面试题之小炼牛刀zip,lambda,map

    # 现有两元祖,(('a'),('b')),(('c'),('d'))# 请使用python中匿名函数生成列表[{'a':'c'},{'b':'d'}]t1=(('a'),('b'))t2=(('c' ...

  3. 为什么说 Java 程序员到了必须掌握 Spring Boot 的时候?

    Spring Boot 2.0 的推出又激起了一阵学习 Spring Boot 热,就单从我个人的博客的访问量大幅增加就可以感受到大家对学习 Spring Boot 的热情,那么在这么多人热衷于学习 ...

  4. 深度学习之Batch Normalization

    在机器学习领域中,有一个重要的假设:独立同分布假设,也就是假设训练数据和测试数据是满足相同分布的,否则在训练集上学习到的模型在测试集上的表现会比较差.而在深层神经网络的训练中,当中间神经层的前一层参数 ...

  5. flutter初体验

    flutter初体验 和flutter斗争了两个周末,基本弄清楚了这个玩意的布局和一些常用组件了. 在flutter里面,所有东西都是组件Widget.我们像拼接积木一样拼接Widget,拼接的关键词 ...

  6. 欢迎使用IdentityModel文档!- IdentityModel 中文文档(v1.0.0)

    IdentityModel是基于声明的身份,OAuth 2.0和OpenID Connect的.NET标准帮助程序库. 它具有以下高级功能: 标准OAuth 2.0和OpenID Connect端点的 ...

  7. 【3】Asp.Net Core2.2新版管道处理模型

    [前言] 上一篇完成了Asp.Net Core 2.2项目的一个最简单功能的添加,从控制器-视图-实体轻松交互了一下,感觉跟之前的MVC没啥差别!但这些都是在组件封装的基础上完成的,在Core里面,其 ...

  8. Rest_framework Router 路由器(含SimplyRouter源码浅解)

    目录 Rest_framework Router 路由器 ViewSet结合Router,自动生成url. 将ViewSet注册到Router中,需要三个要素: 关于路由规则,细分有四类: rest_ ...

  9. react-native 金币彩带雨下落动画

    日常项目中,经常遇到一些表情雨/金币雨/彩带雨 等下落的动画,之前做android原生的时候,写过类似的效果,主要通过自定义view 在onDraw里绘制下落的过程,具体可以看下我的这篇github地 ...

  10. idea上maven使用心得(三)——用pom.xml添加jar包

    下面是如何使用maven,maven在idea里面得结构应该是这样: scr底下是main,java是存放web的.java文件 resource目录一般是存放数据库连接数据的,或者存各种资源文件,音 ...