关于QLineEdit设置QDoubleValidator设置范围无效问题 的终极解决方法(非复制粘贴水文章)

现状

  • 开发自定义输入数据控件时,例如自定义QLineEdit,经常需要设置数据输入范围
  • QLineEdit设置QDoubleValidator的效果差强人意、非常糟糕(相信搜这个问题的人已经知道是什么意思)
  • CSDN上很多僵尸文章,复制粘贴,搜索半天极其痛苦仍未解决问题
  • 部分有作用的文章,情况覆盖的仍然不够全面,仍会出现输入01、-01、.、-.1、等其实不该出现的数字串

解决

自己动手,丰衣足食;直接自定义重写两个重要的功能相关函数:

  1. QValidator::State validate(QString &s, int &i) const;
  2. 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";
}

测试

测试目前效果满足几乎所有日常输入需求,当然本人没优化代码,如果觉得可以写的更简单、或者有其他更好的实现方法可以评论区交流一下,互相学习。


评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

F_WORKS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值