只允许在QDoubleValidator的范围内输入QLineEdit

Allow entry in QLineEdit only within range of QDoubleValidator

本文关键字:范围内 输入 QLineEdit QDoubleValidator      更新时间:2023-10-16

我有一组QLineEdits应该接受一定范围内的双精度值(例如,-15 到 15)。

在设置每个时,我有一些类似的东西:

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

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

我如何动态强制输入适合范围(例如,如果范围是 -15 到 15 并且用户键入 1,然后尝试键入 9,它不起作用/显示 9...但键入 1 然后 2 确实有效/显示 2。

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

这是因为如果值超出边界并且QLineEdit接受QValidator::Intermediate值,则QDoubleValidator返回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::中间状态也被解释为失败的情况。

如果我们将 textChanged -signal 连接到 textChangedSlot,则在每个输入字段更改后都会进行验证。我们还可以将 editorDone() 或 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;
        }
    }
};

我花了将近一天的时间试图在检查可接受的QLineEdit输入范围时QDoubleValidator合理的用户反馈下工作。我尝试使用Qt规定的validator::fixup()结果证明是浪费时间。此线程中的早期答案更有用,但仍然存在缺点。最后,我选择了一种不同且更简单的方法。

  1. QLineEdit配备不执行范围检查的QDoubleValidator
  2. 在用于QLineEdit editingFinished信号的处理程序中,进行范围检查,并在必要时重置QLineEdit文本。

此方法不允许键入非法字符,负责本地化并更正所需范围之外的值。

对我来说效果很好。

VVV 的答案非常适合妮可的原始问题。这是范围从负到正的时候。

然而,作为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;
}

在寻找支持科学和标准符号的解决方案时,我遇到了这个解决方案。它的灵感来自Petri Pyö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:
};

这是一个解决方法:您可以简单地使用 QDoubleSpinBox 将其buttonSymbols设置为 NoButtons ,这看起来像一个QLineEdit,但您可以使用本机 setMinimum(double min)setMaximum(double max) 设置其范围。

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

这是使用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