Qt 自定义验证器
背景问题
在使用 QIntValidator 或自定义整数验证器时,如果为 QLineEdit 设置了范围(如 1-200),可能会遇到以下问题:即使输入999也是可以输入成功
验证器机制
Qt 验证器的工作主要分为两种场景:实时验证和提交验证。
1. 实时验证
实时验证发生在用户每次键入或删除字符时,验证器通过其 validate 方法校验当前输入是否合法。
QValidator::Acceptable: 当前输入合法,满足所有约束。QValidator::Intermediate: 当前输入暂时无效,但可能成为合法输入(如用户正在编辑)。QValidator::Invalid: 当前输入完全无效,禁止输入。
2. 提交验证
当用户完成输入后,验证器会执行最终检查,通常由以下事件触发:
- 用户按下
Enter键。 - 输入框失去焦点(鼠标点击其他地方)。
此时,Qt 会调用验证器的 fixup 方法,尝试将输入修正为合法值。
解决方案
通过自定义验证器,可以实现以下功能:
- 支持空字符串和中间状态:允许用户在输入过程中删除字符,保持编辑灵活性。
- 在输入完成时强制范围约束:当用户提交输入时,自动修正非法值为合法范围内的默认值。
自定义整数验证器示例
下面是 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);
测试行为
- 当用户输入
100时,验证器会接受。 - 当用户尝试删除最后一位,变为
10或空字符串时,验证器允许继续编辑。 - 输入完成后(如失去焦点),空值或非法值会自动修正为合法范围内的值(如最小值
1或0.1)。
- 实时验证:通过
validate方法实时检查输入,支持空字符串和临时无效值。 - 提交验证:通过
fixup方法在输入完成后修正非法值,确保数据合法性。
通过上述方法,可以有效解决用户输入受限的问题,同时保证数据范围约束。
微信公众号:拾荒coder
本文由博客一文多发平台 OpenWrite 发布!