c++qtqlineeditqvalidator

QLineEdit with QValidator: React to editing finished regardless of input validity?


QLineEdit has a signal QLineEdit::editingFinished that gets emitted when the user finished editing, for example by pressing enter. However if a validator or an input mask was set, then editingFinished only gets emitted if the input is valid.

But how can I react to the user finishing the editing regardless of the validity of the input? Do I have to manually check for enter, return, the widget losing focus, etc. ?

The reason for this: I wanted to create a custom widget for editing numbers using a QDoubleValidator. When the user finishes editing and the input is invalid (wrong range, empty text, ...) I want to reset it to some valid default value. Something like this:

class NumberEdit: public QLineEdit
{
public:
    NumberEdit(double min, double max)
    {
        setValidator(new QDoubleValidator(min, max, 10));
        setText(QString::number(min));

        connect(this, /* this is the problem */, [this, min]() {
            if(!hasAcceptableInput())
                setText(QString::number(min));    // Reset to valid number
        });
    }
};

Solution

  • I don't think you really need to subclass QLineEdit.

    The clean way to change a QLineEdit value when it does not have an Acceptable input, is to override QValidator and provide an implementation for fixup(). From the docs:

    fixup() is provided for validators that can repair some user errors. The default implementation does nothing. QLineEdit, for example, will call fixup() if the user presses Enter (or Return) and the content is not currently valid. This allows the fixup() function the opportunity of performing some magic to make an Invalid string Acceptable.

    Since you are using QDoubleValidator, you can subclass it and provide an implementation for fixup(), as it does not have one, see here.

    The class should look something like this:

    class MyDoubleValidator : public QDoubleValidator{
    public:
        explicit MyDoubleValidator(QObject* parent= nullptr)
            :QDoubleValidator(parent){}
        MyDoubleValidator(double bottom, double top, int decimals, QObject* parent=nullptr)
            :QDoubleValidator(bottom, top, decimals, parent){}
    
        virtual void fixup(QString& input)const override{
            input= QString::number(bottom(), 'f', decimals());
        }
    };
    

    That way, you can just use your validator with any QLineEdit like this:

    lineEdit->setValidator(new MyDoubleValidator(100, 200, 2, lineEdit));
    

    Note that, this way lineEdit will emit editingFinished() after finishing every edit, because even if the contents of QLineEdit were not Acceptable, our fixup() implementation will always fix the contents to be Acceptable.