【问题标题】:Allow entry in QLineEdit only within range of QDoubleValidator只允许在 QDoubleValidator 范围内进入 QLineEdit
【发布时间】:2013-11-03 11:04:36
【问题描述】:

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

在设置每个方面时,我都有以下几点:

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

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

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

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

【问题讨论】:

标签: c++ qt validation qlineedit


【解决方案1】:

这是因为 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;
        }
    }
};

【讨论】:

  • 我对此进行了调整,以将最小值和最大值保存为成员,因为它们因我的不同行编辑而异,而且它就像一个魅力。但是,它确实存在问题,它允许用户输入负数的初始负号。我想这可能只是这个解决方案的成本..
  • 谢谢!我在 QDoubleValidator、setNotation(QDoubleValidator::StandardNotation) 和 QLineEdit 输入掩码上浪费了一天。它没有一个像我预期的那样表现。逐个字符输入实现老式解析字符会更快。感谢您提供真正有效的解决方案! (我做了一些更改,除了“-”之外还允许“+”,固定小数点后的字符,并进行了包括顶部和底部的范围测试)
【解决方案2】:

不用子类化也可以做到这一点。

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() 信号连接到验证槽。在这种情况下,只有当用户停止编辑字符串时才会进行验证。

【讨论】:

    【解决方案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;
            }
        }
    };
    

    【讨论】:

      【解决方案4】:

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

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

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

      很适合我。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          我在搜索支持科学和标准表示法的解决方案时遇到了这个解决方案。它受到 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:
          };
          

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              【解决方案8】:

              这里是为那些使用 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
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2020-01-13
                • 1970-01-01
                • 2015-11-16
                • 1970-01-01
                • 2016-10-05
                • 1970-01-01
                • 1970-01-01
                • 2016-02-24
                相关资源
                最近更新 更多