У меня есть набор QLineEdits
которые должны принимать двойные значения в определенном диапазоне (например, от -15 до 15).
У меня есть что-то вроде этого при настройке каждого:
lineEdit->setValidator(new QDoubleValidator(minVal, maxVal, 5, lineEdit));
В идеале, редактирование линии должно работать так, чтобы можно было вводить только значения в диапазоне. Когда я попробовал это, я заметил, что по желанию можно набирать только цифры, но они все еще могут выходить за пределы допустимого диапазона.
Как я могу динамически принудительно ввести ввод в диапазон (например, если диапазон от -15 до 15 и пользователь вводит 1, затем пытается набрать 9, он не работает / не отображает 9 …, но печатает 1 а потом 2 работает / отображается 2.)?
Нужно ли подключиться и позвонить validate()
функционировать где-то?
Это потому что QDoubleValidator
возвращается QValidator::Intermediate
если значение находится за пределами и QLineEdit
принимает QValidator::Intermediate
ценности.
Чтобы реализовать желаемое поведение, вы можете сделать свое собственное QDoubleValidator
подкласс, как это:
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;
}
}
};
ОБНОВИТЬ: Это решит проблему отрицательного знака, а также примет двойные форматы локали:
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;
}
}
};
Это можно сделать также без подклассов.
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);
Установка языкового стандарта может быть важной, поскольку она определяет, какие символы интерпретируются как десятичный разделитель. Формат входной строки определяет, какие локали следует использовать.
В textChangedSlot мы можем проверить ввод следующим образом:
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
}
В этом случае также состояние QValidator :: Intermediate интерпретируется как неудачный случай.
Если мы подключим textChanged -signal к textChangedSlot, проверка выполняется после каждого изменения поля ввода. Мы также можем подключить editFinished () или returnPressed () -signals к слоту проверки. В этом случае проверка выполняется только тогда, когда пользователь прекращает редактирование строки.
Я попробовал превосходный класс выше, и это все еще нуждается в паре правок. Поиск десятичной точки уменьшал диапазон, указанный в параметре «top», потому что он возвращал «-1», когда десятичной точки нет. Я добавил условное утверждение, которое исправляет это.
Кроме того, он все еще должен быть настроен для случая, когда пользователь пытается удалить десятичную точку, и результирующее значение больше, чем диапазон. Сейчас он просто запрещает такое поведение, а не изменяет его до максимального значения, которое кажется мне более интуитивным.
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;
}
}
};
Я провел почти день, пытаясь сделать QDoubleValidator
работать с разумной обратной связью пользователей при проверке приемлемого диапазона QLineEdit
вход. Мои попытки использовать Qt прописаны validator::fixup()
оказалось пустой тратой времени. Более ранние ответы в этой теме намного полезнее, но все же имеют недостатки. В итоге я выбрал другой и более простой подход.
QLineEdit
с QDoubleValidator
который не выполняет проверку диапазона. QLineEdit
editingFinished
Сигнал сделать проверку диапазона и при необходимости сбросить QLineEdit
текст.Этот подход запрещает ввод недопустимых символов, заботится о локализации и корректирует значения за пределами желаемого диапазона.
Хорошо работает для меня.
Ответ VVV отлично работает для оригинального вопроса Nicole. Это когда диапазон от отрицательного до положительного.
Однако, как общее решение для QDoubleValidator, он имеет один побочный эффект, когда диапазон от положительного до положительного:
Пример: Спектр: [87,5 … 1000,0], Входные данные: «15» (как промежуточное значение для достижения значения 150)
Входные данные будут отклонены, когда QLineEdit опустится ниже нижнего предела (или начнется пустой). Поэтому я расширил решение VVV для общего решения:
/*
* 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;
}
Я сталкивался с этим решением при поиске решения, которое поддерживает как научную, так и стандартную нотацию. Это вдохновлено предложением Петри Пюриа, вот решение, которое использует сигнал editingFinished
,
Я перегружен validate
чтобы убедиться, что QValidator::Acceptable
возвращается, даже если значение выходит за пределы диапазона. Это вызывает editingFinished
, который я использую для усечения вывода. Таким образом, научная и стандартная нотация может использоваться точно так, как 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:
};