QLineEdit浮點數輸入限制QDoubleValidator

   我的qt版本是qt4.8.4

    QLineEdit *edit1 = new QLineEdit();

    int nPrecision = 2;

    QDoubleValidator *validator = new QDoubleValidator(-999999999, 999999999, nPrecision, edit1);
    validator->setNotation(QDoubleValidator::StandardNotation);//這個很重要
    validator->setLocale(QLocale::C);//這個也很重要
    edit1->setValidator(validator);

 

https://stackoverflow.com/questions/19571033/allow-entry-in-qlineedit-only-within-range-of-qdoublevalidator裏有幾個方法,沒全試,不是太好

 

Ask Question

Asked 5 years, 11 months ago

Active 1 year, 5 months ago

Viewed 10k times

 

6

 

4

I have a set of QLineEdits that are supposed to accept double values within a certain range, (e.g., -15 to 15).

I have something along these lines when setting up each:

lineEdit->setValidator(new QDoubleValidator(minVal, maxVal, 5, lineEdit));

Ideally, the line edits would work such that only values in range can be entered. When I tried this out, I noticed that only numbers could be typed, as desired, but that they could still go out of range.

How can I dynamically force the input to fit into the range (e.g., if range is -15 to 15 and user types a 1, then attempts to type a 9, it doesn't work/display the 9...but typing 1 and then 2 does work/display the 2.) ?

Do I need to connect and call the validate() function somewhere?

c++ qt validation qlineedit

shareimprove this question

edited May 1 '18 at 12:01

 

AAEM

1,17255 silver badges2020 bronze badges

asked Oct 24 '13 at 16:05

nicole

1,80888 gold badges3232 silver badges6969 bronze badges

add a comment

6 Answers

activeoldestvotes

10

 

That's because QDoubleValidator returns QValidator::Intermediate if the value is outside the bounds and QLineEdit accepts QValidator::Intermediate values.

To implement the behavior you want you can make your own QDoubleValidator subclass like this:

class MyValidator : public QDoubleValidator
{
public:
    MyValidator(double bottom, double top, int decimals, QObject * parent) :
        QDoubleValidator(bottom, top, decimals, parent)
    {
    }

    QValidator::State validate(QString &s, int &i) const
    {
        if (s.isEmpty()) {
            return QValidator::Intermediate;
        }

        bool ok;
        double d = s.toDouble(&ok);

        if (ok && d > 0 && d < 15) {
            return QValidator::Acceptable;
        } else {
            return QValidator::Invalid;
        }
    }
};

UPDATE: This will solve the negative sign issue, and also will accept locale double formats:

class MyValidator : public QDoubleValidator
{
public:
    MyValidator(double bottom, double top, int decimals, QObject * parent) :
        QDoubleValidator(bottom, top, decimals, parent)
    {
    }

    QValidator::State validate(QString &s, int &i) const
    {
        if (s.isEmpty() || s == "-") {
            return QValidator::Intermediate;
        }

        QChar decimalPoint = locale().decimalPoint();

        if(s.indexOf(decimalPoint) != -1) {
            int charsAfterPoint = s.length() - s.indexOf(decimalPoint) - 1;

            if (charsAfterPoint > decimals()) {
                return QValidator::Invalid;
            }
        }

        bool ok;
        double d = locale().toDouble(s, &ok);

        if (ok && d >= bottom() && d <= top()) {
            return QValidator::Acceptable;
        } else {
            return QValidator::Invalid;
        }
    }
};

shareimprove this answer

edited Oct 28 '14 at 11:32

answered Oct 24 '13 at 16:36

 

VVV

43644 silver badges1111 bronze badges

  • I adapted this to save the min and max as members since they vary for my different line edits, and it works like a charm. It does have trouble, however, with allowing a user to enter the initial negative sign for a negative number. I'm thinking that might just be the cost of this solution.. – nicole Oct 24 '13 at 22:36

  • Thank you! I have wasted a day on QDoubleValidator, setNotation(QDoubleValidator::StandardNotation), and QLineEdit input masks. None of it behaved as I expected. It would have been quicker to implement old-school-parse character by character entry. Thank you for a solution that actually works! (I made a few changes to allow "+" in addition to "-", fixed char after decimal count, and made range test inclusive of top and bottom ) – Ed of the Mountain Oct 22 '14 at 14:24 

add a comment

4

 

It is possible to do this also without subclassing.

lineEdit = new QLineEdit();
connect(lineEdit,SIGNAL(textChanged(QString)), this, SLOT(textChangedSlot(QString)));

QDoubleValidator *dblVal = new QDoubleValidator(minVal, maxVal, 1000, lineEdit);
dblVal->setNotation(QDoubleValidator::StandardNotation);
dblVal->setLocale(QLocale::C);
lineEdit->setValidator(dblVal);

Setting of the locale may be important because it defines which characters are interpreted as a decimal separator. Format of the input string defines which locales should be used.

In the textChangedSlot, we can validate input this way:

QString str = lineEdit->text();
int i = 0;
QDoubleValidator *val = (QDoubleValidator *) lineEdit->validator();
QValidator::State st = val->validate(str, i);

if (st == QValidator::Acceptable) {
    // Validation OK
} else {
    // Validation NOK
}

In this case also QValidator::Intermediate state is interpreted as a failed case.

If we connect textChanged -signal to the textChangedSlot, validation is done after every input field change. We could also connect editingFinished() or returnPressed() -signals to the validation slot. In that case, validation is done only when user stops editing the string.

shareimprove this answer

answered Jun 18 '15 at 10:24

 

Petri Pyöriä

17155 bronze badges

add a comment

3

 

I tried the excellent class above and it still needs a couple edits. The decimal point search was reducing the range specified by "top" because it returned a "-1" when there is no decimal point. I added a conditional statement that fixes that.

Also, it still needs to be tweaked for the case where the user tries to delete the decimal point and the resulting value is larger than the range. Right now it just prohibits that behavior rather than changing it to the maximum value which seems more intuitive to me.

class MyValidator : public QDoubleValidator
{
    public:
    MyValidator(double bottom, double top, int decimals, QObject * parent) :
    QDoubleValidator(bottom, top, decimals, parent)
    {
    }

    QValidator::State validate(QString &s, int &i) const
    {
        if (s.isEmpty() || s == "-") {
            return QValidator::Intermediate;
        }

        QLocale locale;

        QChar decimalPoint = locale.decimalPoint();
        int charsAfterPoint = s.length() - s.indexOf(decimalPoint) -1;

        if (charsAfterPoint > decimals() && s.indexOf(decimalPoint) != -1) {
            return QValidator::Invalid;
        }

        bool ok;
        double d = locale.toDouble(s, &ok);

        if (ok && d >= bottom() && d <= top()) {
            return QValidator::Acceptable;
        } else {
            return QValidator::Invalid;
        }
    }
};

shareimprove this answer

answered Sep 1 '14 at 23:26

user2896552

3122 bronze badges

add a comment

2

 

I spent almost a day trying to make QDoubleValidator work with reasonable user feedback when checking for acceptable range of QLineEdit input. My attempts to use Qt prescribed validator::fixup() turned out to be a waste of time. Earlier answers in this thread are much more useful but still have shortcomings. In the end I opted for a different and simpler approach.

  1. Equip QLineEdit with QDoubleValidator which performs no range checking.
  2. In a handler for QLineEdit editingFinished signal do range checking and if necessary reset of QLineEdit text.

This approach disallows typing of illegal characters, takes care of localization and corrects values outside of desired range.

Works well for me.

shareimprove this answer

edited May 1 '18 at 13:17

 

AAEM

1,17255 silver badges2020 bronze badges

answered May 2 '15 at 15:31

LRaiz

38922 silver badges1515 bronze badges

add a comment

1

 

The answer of VVV works great for the orignal question of nicole. This is when the range is from negative to positive.

However as a general solution for QDoubleValidator it has one side effect when the range is from positive to positive:

Example: Range: [87.5 ... 1000.0], Input: "15" (as intermediate to reach the value 150)

The input will be declined when the QLineEdit goes under the lower limit (or starts empty). Hence I extended the solution of VVV for a general solution:

/*
 * Empty string and the negative sign are intermediate
 */
if( input.isEmpty() || input == "-" )
{
    return QValidator::Intermediate;
}
/*
 * Check numbers of decimals after the decimal point
 * and the number of decimal points
 */
QChar decimalPoint = locale().decimalPoint();
if( input.count( decimalPoint, Qt::CaseInsensitive ) > 1 )
{
    return QValidator::Invalid;
}
else if( input.indexOf( decimalPoint ) != -1)
{
    const int charsAfterPoint = input.length() - input.indexOf( decimalPoint) - 1;
    if( charsAfterPoint > decimals() )
    {
        return QValidator::Invalid;
    }
}
/*
 * Check for valid double conversion and range
 */
bool ok;
const double d = locale().toDouble( input, &ok );
if( ok && d <= top() )
{
    if( d >= bottom() )
    {
        return QValidator::Acceptable;
    }
    else
    {
        return QValidator::Intermediate;
    }
}
else
{
    return QValidator::Invalid;
}

shareimprove this answer

answered Oct 28 '16 at 11:23

Martin H.

3888 bronze badges

add a comment

0

 

I came across this solution when searching for a solution, which supports scientific as well as standard notation. It is inspired by the suggestion by Petri Pyöriä, here is a solution, which uses the signal editingFinished.

I have overloaded validate to ensure that QValidator::Acceptable is returned even when the value is out of range. This triggers the editingFinished, which I use for truncating the output. In this way, both Scientific and Standard notation can be used exactly as implemented by QDoubleValidator

#include <QDoubleValidator>

class TruncationValidator : public QDoubleValidator
{
    Q_OBJECT
public:
    explicit TruncationValidator(QObject *parent = 0) : QDoubleValidator(parent) {
      connect(this->parent(), SIGNAL(editingFinished()), this, SLOT(truncate()));
    }
    TruncationValidator(double bottom, double top, int decimals, QObject * parent) : QDoubleValidator(bottom, top, decimals, parent) {
      connect(this->parent(), SIGNAL(editingFinished()), this, SLOT(truncate()));
    }

    QValidator::State validate(QString &s, int &i) const {
      QValidator::State state = QDoubleValidator::validate(s,i);

      if (s.isEmpty()) {
        return state;
      }

      bool ok;
      double d = s.toDouble(&ok);

      if (ok) {
        // QDoubleValidator returns QValidator::Intermediate if out of bounds
        return QValidator::Acceptable;
      }
      return state;
    }

private slots:
    void truncate() {
      QLineEdit* le = dynamic_cast<QLineEdit*>(parent());
      if (le) {
        QString s = le->text();
        bool ok;
        double d = s.toDouble(&ok);
        if (ok) {
          if (d > this->top() || d < this->bottom()) {
            d = std::min<double>(d, this->top());
            d = std::max<double>(d, this->bottom());
            le->setText(QString::number(d));
          }
        }
      }
    }
private:
};

shareimprove this answer

answered May 25 '17 at 22:18

Jens Munk

2,9361515 silver badges28

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章