QT表格函数表达式计算工具


前言

在公司产品中又新增了一个需求:需要在低代码工具平台中配置类似excel的函数表达式计算,如sum(单价*库存数量),其中“单价”和“库存数量”为该表格表头的标题,表示计算每一行的总金额然后对其求总和,之后显示到界面上显示。

一、难点分析

1.与excel表达式不同的是,需要开发的表达式中的变量并不是划定具体界限(如sum(A1, A2)),而是针对某一列或多列。
2.由于存在多列求和的情况,还需要使用方法解析出所有变量然后定位到表格上的表头。
3.真正复杂的表达式不止sum,还有平均、计数、最大值和最小值等,虽然当前只需要开发sum,但是也要考虑后期的扩展性。

二、开发流程

以下内容都以sum(price * number)为例

1.表达式变量解析

主要依赖QRegularExpression进行解析,然后利用chatgpt生成解析表达式:

QMap<QString, double> ExpressTool::extractVariables(const QString &runExpression) {
    // 定义匹配变量名的正则表达式
    QRegularExpression varRegex("\\b[a-zA-Z_][a-zA-Z0-9_]*\\b");

    // 获取所有变量名
    QRegularExpressionMatchIterator varIterator = varRegex.globalMatch(expression);

    QMap<QString, double> variables;

    while (varIterator.hasNext()) {
        QRegularExpressionMatch match = varIterator.next();
        QString varName = match.captured();
        variables[varName] = 0.0; // 将变量映射到默认值0
    }

    return variables;
}

所有表达式变量,包括运算符都存到m_tokens中去(包括price 和number)
注意:以上runExpression为去掉sum()的表达式,也可利用chatgpt生成想要的解析方式

2.表达式运算符解析

主要解析该表达式为sum或其他,非常简单,按正常字符串解析即可:

QString expressSymple = expression.left(expression.indexOf("("));
    if(expressSymple == "sum"){
    	//TODO sum
    }
    else{
    	//TODO other 
    }

3.表达式计算

这里为一大难点,需要找到变量对于表格数据的映射值。
在接口设计上,既要保证该代码的通用性,又要使接口简洁好用,因此设计出以下接口:

    /**
    * @brief		计算表达式(sum、avg)
    * @param[in]	express:表达式
    * @param[in]	rowCount:行总数
    * @param[in]	getValFunc:获取此行对应的值
    * @retval       计算结果
    */
    virtual double calcExpress(const QString &express, int rowCount, const std::function<void(int row, QMap<QString, double>&)>& getValFunc) = 0;

关键在于最后一个function中的(int row, QMap<QString, double>&),需要外部调用的代码手动给出QMap中某行对应key的数据,之后再传回内部进行计算:

double ExpressTool::evaluateExpression(const QString &runExpression, const QMap<QString, double> &variables) {
    QString calcExpression = runExpression;
    for(auto iter = variables.begin(); iter != variables.end(); ++iter){
        QString regex = "\\b" + iter.key() + "\\b";
        QRegularExpression re(regex);
        calcExpression = calcExpression.replace(re, QString::number(iter.value()));
    }

    expression_t expressionTool;
    parser_t parser;
    parser.compile(calcExpression.toStdString(), expressionTool);

    double resultVal = expressionTool.value();
    return resultVal;
}

之后的每一行都进行如此计算,然后根据表达式运算符来判断是求和算法还是平均算法即可完成
注意: 1.在这里我使用了一个开源的数学计算代码exprtk,方便进行复杂的数学计算 2.替换文本时需要使用正则表达式确定完整的变量名,避免被某些不完整且有包含内容的变量名意外替换

三、应用举例

void MainWindow::on_pushButton_clicked()
{
    QString express = ui->lineEdit->text();
    auto tool = IExpressTool::getObj(this);
    double ret = tool->calcExpress(express, ui->tableWidget->rowCount(), [this](int row, QMap<QString, double>& filed_m){
        for(auto iter = filed_m.begin(); iter != filed_m.end(); ++iter){
            int column = headerTitle.indexOf(iter.key());
            double val = 0.0;
            if(column != -1){
                val = ui->tableWidget->item(row, column)->data(Qt::DisplayRole).toDouble();
            }
            iter.value() = val;
        }
    });
    ui->doubleSpinBox->setValue(ret);
}

当点击计算按钮后,即可计算出结果显示到界面上:
运行结果

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值