Search code examples
c++qtvalidationqlineedit

Allow entry in QLineEdit only within range of QDoubleValidator


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?


Solution

  • 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;
            }
        }
    };