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

今天我将介绍三种为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. python脚本对 mysql数据库进行增删改查操作

    # -*- coding: utf-8 -*-import pymysqlimport xlrd# import codecsconn = pymysql.connect(host='127.0.0. ...

  2. mybatis 异常Result Maps collection does not contain value for java.lang.String

    Result Maps collection does not contain value for java.lang.String 以上是我报的错. 只要报Result Maps collectio ...

  3. mySql入门-(一)

    学了很多乱七杂八的东西,但是依然停留在前端,在工作中一直和后端交流,但是不太了解数据库是怎么回事,为了加强学习,准备学习一些关于数据库相关的东西. 说起数据库可能会有很多很多,SQLServer.Or ...

  4. Telerik控件集-2019.R1.SP1.All

    Telerik 专注于微软.Net平台的表示层与内容管理控件,提供高度稳定性和丰富性能的组件产品DevCraft,并可应用在非常严格的环境中.Telerik拥有 Microsoft, HP, Alco ...

  5. 手写事件代理函数 (Delegated function)

    ‘手写 ’ 这个词 ,面试是不是听过无数遍呢 ! 今天我们来手写一个这样的事件委托函数 => function( parent, selector, type ,  handle)  {} 你需 ...

  6. Redis 实战篇之搭建集群

    Redis 集群简介# Redis Cluster 即 Redis 集群,是 Redis 官方在 3.0 版本推出的一套分布式存储方案.完全去中心化,由多个节点组成,所有节点彼此互联.Redis 客户 ...

  7. 服务部署到Swarm Cluster中

    对于已存在的镜像,将其部署到服务器中并开始对外服务,便是它的职责,而我们要做的便是帮助它完成职责,前两个应用环节都已产生了相应的镜像,在这一环节,将完成服务部署到容器集群的工作,对于这一过程,实际执行 ...

  8. 从零到一详聊如何创建Vue工程及遇到的常见问题

    前言 本文也会在github上我的web-study仓库中同步更新,欢迎star. 戳这里,传送 准备工作 判断是否需要FQ或安装镜像,镜像一般可安装国内淘宝镜像,详情可看这里:cnpm npm in ...

  9. 看懂 ,学会 .NET 事件的正确姿势-简单版

    发现之前写了一篇关于事件的阐述写的过于抽象.现在想想先理解本质由简入难比较合适  之前的一篇博客地址:https://www.cnblogs.com/LiMin/p/7212217.html 参照网上 ...

  10. [PHP] 使用反射实现的控制反转

    搬家进程中反射实现控制反转,样做的好处是可以通过配置项动态的控制下面那个类的属性 1.$this->getObject($class, $config->getConfig('param' ...