Let me use an example to explain the issue.
If we have a TextField
like this,
TextField {
text: "0.0"
validator: DoubleValidator { bottom: -359.9;
top: 359.9;
decimals: 1;
notation: DoubleValidator.StandardNotation }
onEditingFinished: {
console.log("I'm here!");
}
}
We can type numbers such as 444.9
, 399.9
or -555.5
. As you can see, the values are not between -359.9
and 359.9
.
In the documentation we can find the following information:
Input is accepted but invalid if it contains a double that is outside the range or is in the wrong format; e.g. with too many digits after the decimal point or is empty.
I thought DoubleValidator
didn't accept this kind of things, but unfortunately it does.
So I suppose the solution would be to check the final input, but again we have a problem: editingFinished
is only emitted if the validator returns an acceptable state and this is not always the case.
Perhaps I'm not doing a good approach, I'm not understanding how to use DoubleValidator
or maybe I need some code in C++.
By the way, I'm working with Qt 5.4.
The problem lies in the fact that QML TextField accepts intermediate input:
validator : Validator
Allows you to set a validator on the TextField. When a validator is set, the TextField will only accept input which leaves the text property in an intermediate state. The accepted signal will only be sent if the text is in an acceptable state when enter is pressed.
The validate()
-function of QDoubleValidator describes when it returns QValidator::Intermediate
:
State QValidator::validate(QString & input, int & pos) const
This virtual function returns Invalid if input is invalid according to this validator's rules, Intermediate if it is likely that a little more editing will make the input acceptable (e.g. the user types "4" into a widget which accepts integers between 10 and 99), and Acceptable if the input is valid.
So that means, the validator returns QValidator::Intermediate
, as long as a double value is entered and because TextField is okay with "intermediate", you can type anything as long as it is a number.
What you can do is to subclass QDoubleValidator
and to override validate()
, so that it does not return Intermediate
when the values are out of bounds:
class TextFieldDoubleValidator : public QDoubleValidator {
public:
TextFieldDoubleValidator (QObject * parent = 0) : QDoubleValidator(parent) {}
TextFieldDoubleValidator (double bottom, double top, int decimals, QObject * parent) :
QDoubleValidator(bottom, top, decimals, parent) {}
QValidator::State validate(QString & s, int & pos) const {
if (s.isEmpty() || (s.startsWith("-") && s.length() == 1)) {
// allow empty field or standalone minus sign
return QValidator::Intermediate;
}
// check length of decimal places
QChar point = locale().decimalPoint();
if(s.indexOf(point) != -1) {
int lengthDecimals = s.length() - s.indexOf(point) - 1;
if (lengthDecimals > decimals()) {
return QValidator::Invalid;
}
}
// check range of value
bool isNumber;
double value = locale().toDouble(s, &isNumber);
if (isNumber && bottom() <= value && value <= top()) {
return QValidator::Acceptable;
}
return QValidator::Invalid;
}
};
I found an easier way.
TextField {
id: control
onTextChanged:
{
if(!acceptableInput)
control.undo()
}
}
When text in TextField is invalid, acceptableInput would change to false, so when text changed ,check the property, if it's false, then call undo() to undo the changes.
The answer provided by @xsquared was perfect. I think it's a good idea to share with anyone curious how to integrate the solution with QML
.
TextFieldDoubleValidator
is the class which @xsquared suggested.
So the first thing is to register the new type in our main.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "textfielddoublevalidator.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<TextFieldDoubleValidator>("TextFieldDoubleValidator", 1,0,
"TextFieldDoubleValidator");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
After that, we can use the new type in our QML
application:
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import TextFieldDoubleValidator 1.0
Window {
visible: true
// DoubleValidator doesn't clear the TextField when
// text > 359.9 or < -359.9
TextField {
text: "0.0"
validator: DoubleValidator {
bottom: -359.9;
top: 359.9;
decimals: 1;
notation: DoubleValidator.StandardNotation
}
}
// Solution: use your own DoubleValidator.
// This works OK and text is cleared out when
// text > 359.9 or < -359.9
TextField {
y: 50
text: "0.0"
validator: TextFieldDoubleValidator {
bottom: -359.9;
top: 359.9;
decimals: 1;
notation: DoubleValidator.StandardNotation
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With