三種方法爲QLineEdit添加清除內容按鈕

不少時候咱們會發現輸入的一長串內容不得不所有刪除從新輸入,這時比起一直按着退格鍵不放一個清除內容按鈕更受歡迎。html

今天我將介紹三種爲QLineEdit添加清除內容按鈕的方法,其中兩種方法有較強的功能針對性,另外一種方法則是通用的,不只能夠用來實現清除輸入內容,還能夠擴展出其餘功能。app

本文索引

方法1:setClearButtonEnabled顯示清除按鈕

這是Qt5.2以後提供的方法,當使用了setClearButtonEnabled(true);以後會在 QLineEdit的右側顯示一個圖標爲QStyle::SP_DialogResetButto的QAction,點擊後會清除輸入內容:ide

// 方案1
auto edit1 = new QLineEdit;
edit1->setClearButtonEnabled(true);

效果:函數

看到右邊那個圖標,若是是Qt自帶的話會是一個相似掃把的圖形,若是使用了系統主題那麼會有些許差別,點擊它,輸入內容就會所有清除。佈局

方法2:使用QAction實現清除按鈕

如前所述,setClearButtonEnabled其實只是讓實現存在的QAction顯示出來而已,因此咱們也能夠本身實現這一過程。字體

要實現這一功能,須要Qt5.2以後提供的addAction方法。它負責把一個QAction添加到edit的指定位置。this

不過要注意的是,這個QAction只能顯示出圖標,文字內容的顯示不出的。3d

// 方案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默認使用的清除按鈕的圖標,也知道如何清除輸入,因此能夠本身實現這一過程。code

這是效果,與方法1時幾乎沒什麼區別:orm

不過方法二的威力不止於此,基於咱們可使用本身的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();
}

當無輸入內容時:

當有輸入內容時:

這樣三種方法都介紹完了,選用哪一種須要本身決定。

固然最後兩種方案不只僅能用來作清除內容按鈕,只要加入一點點想象力還有更高級的功能能夠用它們來實現。

相關文章
相關標籤/搜索