Разрешить вход в QLineEdit только в пределах диапазона QDoubleValidator

у меня есть набор QLineEdits, которые должны принимать двойные значения в определенном диапазоне (например, от -15 до 15).

у меня есть что-то в этом роде при настройке каждого:

lineEdit->setValidator(new QDoubleValidator(minVal, maxVal, 5, lineEdit));

В идеале редактирование строк будет работать так, чтобы можно было вводить только значения в диапазоне. Когда я попробовал это сделать, то заметил, что при желании можно набирать только цифры, но они все еще могут выходить за пределы диапазона.

как я могу динамически заставить вход вписаться в диапазон (например, если диапазон от -15 до 15 и пользователь вводит 1, а затем пытается ввести 9, он не работает/отображает 9...но ввод 1, а затем 2 работает/отображает 2.) ?

мне нужно подключиться и вызвать

6 ответов


потому что 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 - сигнал в textChangedSlot, проверка выполняется после каждого изменения поля ввода. Мы также можем подключить editingFinished () или returnPressed ()- сигналы к слоту проверки. В этом случае проверка выполняется только тогда, когда пользователь прекращает редактирование строки.


Я пробовал отличный класс выше, и он все еще нуждается в нескольких изменениях. Поиск десятичной точки уменьшал диапазон, указанный "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() оказалось пустой тратой времени. Более ранние ответы в этой теме гораздо более полезны, но все же имеют недостатки. В конце концов я выбрал другой, более простой подход.

  1. оборудовать QLineEdit С QDoubleValidator который не выполняет проверку диапазона.
  2. в обработчик QLineEdit editingFinished сигнал делает проверку ряда и при необходимости возврат QLineEdit текст.

этот подход запрещает ввод незаконных символов, заботится о локализации и корректирует значения за пределами желаемого диапазона.

хорошо работает для меня.


ответ ВВВ отлично подходит для оригинального вопроса Николь. Это когда диапазон от отрицательного к положительному.

однако в качестве общего решения для QDoubleValidator он имеет один побочный эффект, когда диапазон от положительного до положительного:

пример:


я столкнулся с этим решением при поиске решения, которое поддерживает научную, а также стандартную нотацию. Он вдохновлен предложением Петри Pyöriä, вот решение, которое использует сигнал editingFinished.

я перегружен validate обеспечить QValidator::Acceptable возвращается, даже если значение находится вне диапазона. Это вызывает editingFinished, который я использую для усечения выходных. Таким образом, как научные, так и стандартные обозначения могут использоваться точно так же, как реализованы 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:
};