6

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

在设置每个时,我都有这些方面的内容:

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

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

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

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

4

8 回答 8

11

这是因为如果值超出范围并接受值,则QDoubleValidator返回。QValidator::IntermediateQLineEditQValidator::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;
        }
    }
};
于 2013-10-24T16:36:59.930 回答
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 信号连接到 textChangedSlot,则每次输入字段更改后都会进行验证。我们还可以将 editorFinished() 或 returnPressed() 信号连接到验证槽。在这种情况下,仅当用户停止编辑字符串时才进行验证。

于 2015-06-18T10:24:53.243 回答
3

我尝试了上面的优秀课程,它仍然需要一些编辑。小数点搜索缩小了“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;
        }
    }
};
于 2014-09-01T23:26:34.867 回答
3

QDoubleValidator在检查可接受的QLineEdit输入范围时,我花了将近一天的时间来尝试使用合理的用户反馈。我尝试使用规定的 Qtvalidator::fixup()被证明是浪费时间。该线程中的早期答案更有用,但仍有缺点。最后,我选择了另一种更简单的方法。

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

这种方法不允许输入非法字符,负责本地化并纠正超出所需范围的值。

对我来说效果很好。

于 2015-05-02T15:31:57.277 回答
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;
}
于 2016-10-28T11:23:47.607 回答
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
于 2021-08-04T19:25:31.987 回答
0

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

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

于 2020-09-04T09:03:19.850 回答
0

我在寻找支持科学和标准符号的解决方案时遇到了这个解决方案。它受到 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:
};
于 2017-05-25T22:18:45.100 回答