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?
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;
}
}
};
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.
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