现状
- 开发自定义输入数据控件时,例如自定义QLineEdit,经常需要设置数据输入范围
- QLineEdit设置QDoubleValidator的效果差强人意、非常糟糕(相信搜这个问题的人已经知道是什么意思)
- CSDN上很多僵尸文章,复制粘贴,搜索半天极其痛苦仍未解决问题
- 部分有作用的文章,情况覆盖的仍然不够全面,仍会出现输入01、-01、.、-.1、等其实不该出现的数字串
解决
自己动手,丰衣足食;直接自定义重写两个重要的功能相关函数:
QValidator::State validate(QString &s, int &i) const;
void fixup(QString & input) const;
代码
1. QValidator::State validate(QString &s, int &i) const;
QValidator:State MyDoublevalidator::validate(QString &str,int &i) const
{
//先判断是不是空字符串
if(str.isEmpty())
return Intermediate;
//当最小值大于零时不能输入负数
if(bottom() >=0 && str.startsWith('-'))
return Invalid;
//当最大值小于零时不能输入正数
if(top() <=0 && (0 != str.toDouble()) && !str.startsWith('-'))
return Invalid;
//不能输入多个小数点
if(str.count(".") > 1)
return Invalid;
//获取小数点位置
int dotPos = str.indexOf(".");
//判断小数点位数是否合法
if(dotPos > 0 && str.right(str.length() - dotPos - 1).length()> decimals())
return Invalid;
bool ok = false;
double val = str.toDouble(&ok);
QString strTem; //用于对输入前导0和只有小数点无小数输入这两种情况下的合法输入模板
if(dotPos > 0)
{
//有小数点,将小数点前后分开
QStringList strNum = str.split(".");
//小数位转化后为空,说明数据末尾只输入了一个'.',将其补充为'.0'
if("" == strNum.at(1))
strTem = QString("%1").arg(arg, str.length() - 1, 'f', 0) + QString(".0");
else
strTem = str;
}
else
{
//如果没有小数,double val = str.toDouble(&ok);时已经自动去掉前导0
strTem = QString::number(val, 'f', 0);
}
//限制输入字符集为:数字、点、-
if(!ok)
return (bottom() < 0 && !str.compare("-")) ? Intermediate : Invalid;
//防止数字前面输入0,如:01, 012,-01。 (这种情况一定是在输入小数点之前发生的)
if(dotPos <= 0)
{
//合法模板长度小于输入字符串,说明有前导0
if(strTem.length() < str.length())
{
//正数有前导0,直接禁止
if(!str.startsWith('-'))
return Invalid;
if(0 == val) //-0的情况不禁止,等待修正
return Intermediate;
else //负数禁止有前导0
return Invalid;
}
}
//有小数点,且转换煎后长度不一样,说明末尾有0,进行去0操作
if(dotPos > 0)
{
QStringList strNum = strTem.split(".");
if(strNum.size() > 1)
{
QString decimalStr = strNum.at(1);
if(0 == decimalStr.toDouble())
return Intermediate;
if(decimalStr.length() != QString::number((QString("0.") + decimalStr).toDouble()).length() - 2)
return Intermediate;
}
}
//进行范围验证
//①top() <= 0
if(top()<= 0)
{
if(val >= bottom())
{
if(val <= top())
return Acceptable;
else
return Intermediate;
}
else
return Invalid;
}
//②bottom() >= 0
if(bottom() >= 0)
{
if(val <= top())
{
if(val >= bottom())
return Acceptable;
else
return Intermediate;
}
else
return Invalid;
}
//③top() >= 0, bottom() <= 0
if(val >= 0)
{
if(val <= top())
return Acceptable;
if(-val >= bottom())
return Intermediate;
else
return Invalid;
}
else
{
if(val >= bottom())
return Acceptable;
else
return Invalid;
}
}
2. void fixup(QString & input) const;
void MyDoublevalidator::fixup(QString &input) const
{
//如果有小数点,则修正小数后的赘余0
if(input.indexOf(".") > 0)
{
//对小数点前后进行分隔
QStringList strNum = input.split("."); //小数点前后字符串列表
QString integerStr = strNum.at(0); //整数部分
QString decimalStr = strNum.at(1); //小数部分
double newDecimal = (QString("0.") + decimalStr).toDouble(); //去掉小数点后面赘余0,获得修正后小数
//区分正负,重新获得修正后的数据
double newDouble = (input.startsWith('-')) ? (integerStr.toDouble() - newDecimal) : (integerStr.toDouble() + newDecimal);
//去0后小数为0,则原小数为0、 00、 000等类型,则小数位算术上等于0,直接不要小数位
if(0 == newDecimal)
input = QString::number(newDouble, 'f', 0);
else
input = QString::number(newDouble, 'f', QString::number(newDecimal).length() - 2); //-2是减去新小数前面的'0.'两个字符位
}
//当不合法输入值为小于最小值时,填充为最小值
if(input.toDouble() < bottom())
{
QString bottomStr = QString("%1").arg(bottom(), 15, 'f', 0);
input = bottomStr.simplified();
}
//当不合法输入值为大于最大值时,填充为最大值
if(input.toDouble() > top())
{
QString topStr = QString("%1").arg(top(), 15, 'f', 0);
input = topStr.simplified();
}
//当输入为'-0'时,修正为0
if("-0" == input)
input = "0";
}
测试
测试目前效果满足几乎所有日常输入需求,当然本人没优化代码,如果觉得可以写的更简单、或者有其他更好的实现方法可以评论区交流一下,互相学习。