我的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里有几个方法,没全试,不是太好
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?
1,17255 silver badges2020 bronze badges
asked Oct 24 '13 at 16:05
1,80888 gold badges3232 silver badges6969 bronze badges
add a comment
6 Answers
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;
}
}
};
answered Oct 24 '13 at 16:36
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.
answered Jun 18 '15 at 10:24
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;
}
}
};
answered Sep 1 '14 at 23:26
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.
- Equip
QLineEdit
withQDoubleValidator
which performs no range checking. - In a handler for
QLineEdit
editingFinished
signal do range checking and if necessary reset ofQLineEdit
text.
This approach disallows typing of illegal characters, takes care of localization and corrects values outside of desired range.
Works well for me.
1,17255 silver badges2020 bronze badges
answered May 2 '15 at 15:31
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;
}
answered Oct 28 '16 at 11:23
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:
};
answered May 25 '17 at 22:18
2,9361515 silver badges28