C++随笔

156 阅读3分钟

Qt 自定义验证器

背景问题

在使用 QIntValidator 或自定义整数验证器时,如果为 QLineEdit 设置了范围(如 1-200),可能会遇到以下问题:即使输入999也是可以输入成功


验证器机制

Qt 验证器的工作主要分为两种场景:实时验证提交验证

1. 实时验证

实时验证发生在用户每次键入或删除字符时,验证器通过其 validate 方法校验当前输入是否合法。

  • QValidator::Acceptable: 当前输入合法,满足所有约束。
  • QValidator::Intermediate: 当前输入暂时无效,但可能成为合法输入(如用户正在编辑)。
  • QValidator::Invalid: 当前输入完全无效,禁止输入。

2. 提交验证

当用户完成输入后,验证器会执行最终检查,通常由以下事件触发:

  • 用户按下 Enter 键。
  • 输入框失去焦点(鼠标点击其他地方)。

此时,Qt 会调用验证器的 fixup 方法,尝试将输入修正为合法值。


解决方案

通过自定义验证器,可以实现以下功能:

  1. 支持空字符串和中间状态:允许用户在输入过程中删除字符,保持编辑灵活性。
  2. 在输入完成时强制范围约束:当用户提交输入时,自动修正非法值为合法范围内的默认值。

自定义整数验证器示例

下面是 CustomIntValidator 的实现:

#include <QLineEdit>
#include <QIntValidator>
#include <QString>

class CustomIntValidator : public QIntValidator {
public:
    CustomIntValidator(int min, int max, QObject* parent = nullptr)
        : QIntValidator(min, max, parent) {}

    QValidator::State validate(QString& input, int& pos) const override {
        if (input.isEmpty()) {
            return QValidator::Intermediate; // 允许空字符串
        }
        bool ok;
        int value = input.toInt(&ok);
        if (!ok) {
            return QValidator::Invalid; // 非数字输入
        }
        if (value < bottom() || value > top()) {
            return QValidator::Intermediate; // 暂时无效状态
        }
        return QValidator::Acceptable; // 输入合法
    }

    void fixup(QString& input) const override {
        bool ok;
        int value = input.toInt(&ok);
        if (!ok || value < bottom()) {
            input = QString::number(bottom()); // 修正为最小值
        } else if (value > top()) {
            input = QString::number(top()); // 修正为最大值
        }
    }
};

自定义浮点数验证器示例

如果需要处理浮点数,可以创建类似的验证器:

#include <QDoubleValidator>

class CustomDoubleValidator : public QDoubleValidator {
public:
    CustomDoubleValidator(double min, double max, int decimals, QObject* parent = nullptr)
        : QDoubleValidator(min, max, decimals, parent) {}

    QValidator::State validate(QString& input, int& pos) const override {
        if (input.isEmpty()) {
            return QValidator::Intermediate; // 允许空字符串
        }
        bool ok;
        double value = input.toDouble(&ok);
        if (!ok) {
            return QValidator::Invalid; // 非数字输入
        }
        if (value < bottom() || value > top()) {
            return QValidator::Intermediate; // 暂时无效状态
        }
        return QValidator::Acceptable; // 输入合法
    }

    void fixup(QString& input) const override {
        bool ok;
        double value = input.toDouble(&ok);
        if (!ok || value < bottom()) {
            input = QString::number(bottom(), 'f', decimals()); // 修正为最小值
        } else if (value > top()) {
            input = QString::number(top(), 'f', decimals()); // 修正为最大值
        }
    }
};

使用示例

设置验证器范围

通过模板函数动态更新 QLineEdit 的验证器范围:

template <typename ValidatorType, typename MinType, typename MaxType>
void updateValidatorRange(QLineEdit* lineEdit, MinType min, MaxType max, int decimals = 2) 
{
	ValidatorType* validator = qobject_cast<ValidatorType*>(const_cast<QValidator*>(lineEdit->validator()));
	if constexpr (std::is_same_v<ValidatorType, CustomDoubleValidator>) {
		// 处理 CustomDoubleValidator 的更新或创建
		if (validator) {
			validator->setRange(static_cast<double>(min), static_cast<double>(max), decimals);
		}
		else {
			validator = new CustomDoubleValidator(static_cast<double>(min), static_cast<double>(max), decimals, lineEdit);
			validator->setNotation(CustomDoubleValidator::StandardNotation);
			lineEdit->setValidator(validator);
		}
	}
	else if constexpr (std::is_same_v<ValidatorType, CustomIntValidator>) {
		// 处理 QIntValidator 的更新或创建
		if (validator) {
			validator->setRange(static_cast<int>(min), static_cast<int>(max));
		}
		else {
			validator = new CustomIntValidator(static_cast<int>(min), static_cast<int>(max), lineEdit);
			lineEdit->setValidator(validator);
		}
	}
}

应用验证器到 QLineEdit

QLineEdit* lineEdit = new QLineEdit(this);
updateValidatorRange<CustomIntValidator>(lineEdit, 1, 1000);
updateValidatorRange<CustomDoubleValidator>(lineEdit, 0.1, 99.9, 1);

测试行为

  1. 当用户输入 100 时,验证器会接受。
  2. 当用户尝试删除最后一位,变为 10 或空字符串时,验证器允许继续编辑。
  3. 输入完成后(如失去焦点),空值或非法值会自动修正为合法范围内的值(如最小值 10.1)。

  1. 实时验证:通过 validate 方法实时检查输入,支持空字符串和临时无效值。
  2. 提交验证:通过 fixup 方法在输入完成后修正非法值,确保数据合法性。

通过上述方法,可以有效解决用户输入受限的问题,同时保证数据范围约束。

微信公众号:拾荒coder

本文由博客一文多发平台 OpenWrite 发布!