允许仅在QDoubleValidator范围内输入QLineEdit

时间:2013-10-24 16:05:31

标签: c++ qt validation qlineedit

我有一组QLineEdits,它们应该接受某个范围内的双倍值(例如,-15到15)。

在设置每个内容时,我都有以下几点:

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

理想情况下,行编辑可以使用,只能输入范围内的值。当我尝试这个时,我注意到只有数字可以根据需要输入,但它们仍然可以超出范围。

如何动态强制输入以适应范围(例如,如果范围是-15到15并且用户键入1,则尝试键入9,它不起作用/显示9 ...但输入1然后输入2确实可以工作/显示2.)?

我是否需要在某处连接并调用validate()功能?

7 个答案:

答案 0 :(得分:10)

这是因为如果值超出范围且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;
        }
    }
};

答案 1 :(得分:4)

在没有子类化的情况下也可以这样做。

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,则在每次输入字段更改后进行验证。我们还可以将editingFinished()或returnPressed() - 信号连接到验证槽。在这种情况下,仅在用户停止编辑字符串时才进行验证。

答案 2 :(得分:3)

我尝试了上面的优秀课程,但仍然需要进行一些编辑。小数点搜索减少了&#34; top&#34;指定的范围。因为它返回了一个&#34; -1&#34;当没有小数点时。我添加了一个修复它的条件语句。

此外,还需要针对用户尝试删除小数点并且结果值大于范围的情况进行调整。现在它只是禁止这种行为,而不是将其改为最大值,这对我来说似乎更直观。

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

在搜索支持科学和标准符号的解决方案时,我遇到了这个解决方案。它的灵感来自PetriPyöriä的建议,这是一个使用信号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:
};

答案 5 :(得分:0)

这是一种解决方法:您可以简单地将QDoubleSpinBox设置为buttonSymbols的{​​{1}}使用,看起来像NoButtons,但是可以使用本机设置其范围QLineEditsetMinimum(double min)

此方法可直接在Qt Designer中使用。

答案 6 :(得分:0)

这里有一个适用于 PyQt 的 Python 版本:

from PyQt5.QtGui import QDoubleValidator, QValidator


class DoubleValidator(QDoubleValidator):
    def __init__(self, *__args):
        super().__init__(*__args)

    def validate(self, p_str, p_int):

        if not p_str:
            return QValidator.Intermediate, p_str, p_int

        if self.bottom() <= float(p_str) <= self.top():
            return QValidator.Acceptable, p_str, p_int
        else:
            return QValidator.Invalid, p_str, p_int
相关问题