Qt/C++实现多功能计算器

一、界面布局

下面图片是计算器的界面布局。从界面可以得知计算器的基本功能,有加减乘除基本运算,还有余弦,正弦等函数运算。
在这里插入图片描述

二、功能简单介绍及运算展示

在这里插入图片描述
在以上运算中涉及了对数、指数、乘法和加法,乘法是在“25”与ln之间默认有一个乘号(在代码中有具体体现)。
功能介绍:
该计算器可以实现简单的加减乘除的运算以及带括号、带基本的三角函数、对数、指数的表达式的运算。
容错处理
1.在文本框中输入表达式时,会自动检测表达是否正确,若即将输入的字符会导致表达式错误,则该字符不会被输入到文本框中,QVector expr(该变量代码中有介绍)也不会记录下该字符。
2.在表达式不完整的情况下按下“=”键,不会做任何处理。

三、逆波兰式(中缀转后缀)

算法思想:
1:从左到右遍历中缀表达式;
2:遇到数字,直接输出;
3:遇到运算符:
①.若为"(“,直接入符号栈;
②.若为”)“,将符号栈中元素依次输出,直到遇到”(“,”(“出栈,不输出;
③.若为其他运算符,将符号栈中元素依次输出,直至遇到比当前运算符优先级更低的运算符或”(",再将该符号入栈。
④.遍历完后,若符号栈不为空,则将符号栈中的元素依次输出,直至为空,输出得到的新的表达式就是所求表达式的后缀表达式。
4:后缀表达式计算
①.从左到右遍历表达式;
②.若为数字,直接入数字栈;
③.若为双目运算符c,则从数字栈中出栈两个元素,分别记为a1(先出栈),a2(后出栈);令数字a3=a2 c a1(a2在前,a1在后),再将a3压入数字栈中;
④.若为单目运算符c,则从数字栈中出一个元素,分别记为a1,a2(后出栈);令数字a= c a1,再将a压入数字栈中;
⑤遍历完后,数字栈中还剩下一个元素,即为所求表达式的结果。

四、代码分析

// 1.expression.h
设计了expression类来存储按键信息,按键符号由QString data来表示,按键类型由BtnType _type表示。

#include <QObject>
enum BtnType{
Num,//数字
Point,//点
BinocularOp,//双目操作符
MonocularOp,//单目运算符
Special,//特殊符号
LeftParentheses,//左括号
RightParentheses,//右括号
PercentSign,//百分号
Index,//指数
Back,//退格
Equl,//等于
Clear,//清除
};

class expression
{
public:
    expression();
    expression(QString mData,BtnType mType):data(mData),_type(mType){};
    void setData();
    void setType();
    QString getData();
    BtnType getType();
private:
    QString data;
    BtnType _type;  
};

// 2.expression.cpp
按键信息的获取直接由expression的构造函数(即expression(QString mData,BtnType mType):data(mData),_type(mType){};)来实现,信息的提取由getData和getType实现。

#include "expression.h"

expression::expression()
{//default构造函数
}

QString expression::getData()
{
    return data;
}

BtnType expression::getType()
{
    return _type;
}

// 3.widget.h

#ifndef WIDGET_H
#define WIDGET_H
#include<QStack>
#include "expression.h"
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    void btnConnect();
    void onclicked(BtnType _type,QString _btn);

    void InToPo();//中缀表达式转后缀表达式
    bool checkParentheses();//检测中缀表达式中括号是否正确
    int checkPoint(const QString& str);//检测数据中点的个数,并返回。
    bool Operation();//运算(根据后缀表达式进行计算得出结果
    void TextOpention(const QString& newExprssion);//按键后对文本框的操作
    bool checkLastChar();//检测表达式的最后一个字符
private slots:
    void on_BtnINV_clicked();

    void on_BtnRAD_clicked();

    void on_BtnMc_clicked();

    void on_BtnM_clicked();

    void on_Btnm_clicked();

    void on_BtnMr_clicked();

private:
    Ui::Widget *ui;
    QVector<expression> expr;//中缀表达式
    QVector<expression> poexp;//后缀表达式
    QStack<double> num;//数字栈
    QStack<expression> symbols;//符号栈
};
#endif // WIDGET_H

// 4.btnConnect函数

void Widget::btnConnect()
{
    //数字键绑定
    connect(ui->BtnZero,&QPushButton::clicked,[this](){onclicked(Num,"0");});
    connect(ui->BtnOne,&QPushButton::clicked,[this](){onclicked(Num,"1");});
    connect(ui->BtnTwo,&QPushButton::clicked,[this](){onclicked(Num,"2");});
    connect(ui->BtnThree,&QPushButton::clicked,[this](){onclicked(Num,"3");});
    connect(ui->BtnFour,&QPushButton::clicked,[this](){onclicked(Num,"4");});
    connect(ui->BtnFive,&QPushButton::clicked,[this](){onclicked(Num,"5");});
    connect(ui->BtnSix,&QPushButton::clicked,[this](){onclicked(Num,"6");});
    connect(ui->BtnSeven,&QPushButton::clicked,[this](){onclicked(Num,"7");});
    connect(ui->BtnEight,&QPushButton::clicked,[this](){onclicked(Num,"8");});
    connect(ui->BtnNine,&QPushButton::clicked,[this](){onclicked(Num,"9");});

    //双目运算符绑定
    connect(ui->BtnMultiply,&QPushButton::clicked,[this](){onclicked(BinocularOp,"*");});
    connect(ui->BtnDividing,&QPushButton::clicked,[this](){onclicked(BinocularOp,"÷");});
    connect(ui->BtnPlus,&QPushButton::clicked,[this](){onclicked(BinocularOp,"+");});
    connect(ui->BtnSubtracts,&QPushButton::clicked,[this](){onclicked(BinocularOp,"-");});

    //指数
    connect(ui->BtnSemicolon,&QPushButton::clicked,[this](){onclicked(Index,"^(-1)");});
    connect(ui->BtnSquare,&QPushButton::clicked,[this](){onclicked(Index,"^(2)");});
    connect(ui->BtnIndex,&QPushButton::clicked,[this](){onclicked(Index,"^");});

    //点
    connect(ui->BtnPoint,&QPushButton::clicked,[this](){onclicked(Point,".");});

    //单目运算符绑定
    connect(ui->BtnRoot,&QPushButton::clicked,[this](){onclicked(MonocularOp,"√");});
    connect(ui->BtnLn,&QPushButton::clicked,[this](){onclicked(MonocularOp,"ln");});
    connect(ui->BtnLog,&QPushButton::clicked,[this](){onclicked(MonocularOp,"log");});
    connect(ui->BtnSin,&QPushButton::clicked,[this](){onclicked(MonocularOp,"sin");});
    connect(ui->BtnCos,&QPushButton::clicked,[this](){onclicked(MonocularOp,"cos");});
    connect(ui->BtnTan,&QPushButton::clicked,[this](){onclicked(MonocularOp,"tan");});

    //百分号
    connect(ui->BtnPercentSign,&QPushButton::clicked,[this](){onclicked(PercentSign,"%");});

    //括号绑定
    connect(ui->BtnLeftParenthesis,&QPushButton::clicked,[this](){onclicked(LeftParentheses,"(");});
    connect(ui->BtnRightParenthesis,&QPushButton::clicked,[this](){onclicked(RightParentheses,")");});

    //特殊符号绑定
    connect(ui->BtnE,&QPushButton::clicked,[this](){onclicked(Special,"e");});
    connect(ui->BtnPai,&QPushButton::clicked,[this](){onclicked(Special,"π");});

    //清空
    connect(ui->BtnClear,&QPushButton::clicked,[this](){onclicked(Clear,"");});

    //退格
    connect(ui->BtnDelete,&QPushButton::clicked,[this](){onclicked(Back,"");});

    //等于
    connect(ui->BtnEqual,&QPushButton::clicked,[this](){onclicked(Equl,"");});
}

其中百分号为单目运算符,指数为双目运算符,因与其他运算符有所差异,故单独设置一个符号类型,在最后计算表达式时任然归为单目、双目运算符。上面将个类型的按键信号与槽函数onclicked绑定。

// 5.onclicked函数
因代码长度问题此处只列出当按下数字键时的代码。

void Widget::onclicked(BtnType _type, QString _btn)
{
  int rowCount=ui->textEdit->document()->lineCount();

        switch (_type)
        {
        case Num:
        {//数字
            if(rowCount==0||rowCount==1)
            {
                if(!expr.isEmpty())
                {
                    switch (expr.back().getType())
                    {
                    case Num:
                    {
                        QString preNum=expr.back().getData();
                        if(checkPoint(preNum)==0)//前一数字中无小数点
                        {
                            double tempNum;
                            tempNum=expr.back().getData().toDouble();
                            tempNum*=10;
                            tempNum+=_btn.toDouble();
                            expr.pop_back();//删掉前一数据
                            expression curData(QString::number(tempNum),_type);
                            expr.push_back(curData);//将当前数据压入数组中

                            QString curExpression=ui->textEdit->toPlainText();
                            curExpression+=_btn;
                            TextOpention(curExpression);
                            break;
                        }
                       else if(checkPoint(preNum)==1)//前一数字中有小数点
                        {
                            QString tempNum=expr.back().getData();
                            tempNum+=_btn;
                            expr.pop_back();
                            expression curData(tempNum,_type);
                            expr.push_back(curData);

                            QString curExpression=ui->textEdit->toPlainText();
                            curExpression+=_btn;
                            TextOpention(curExpression);
                            break;
                        }
                        break;
                    }
                    case Special: case RightParentheses: case PercentSign:
                    {
                        expression multiply("*",BinocularOp);
                        expr.push_back(multiply);

                        QString curExpression=ui->textEdit->toPlainText();
                        curExpression+=_btn;
                        TextOpention(curExpression);
                        expression curData(_btn,_type);
                        expr.push_back(curData);
                        break;
                    }
                    case LeftParentheses: case BinocularOp:
                    {
                        QString curExpression=ui->textEdit->toPlainText();
                        curExpression+=_btn;
                        TextOpention(curExpression);
                        expression curData(_btn,_type);
                        expr.push_back(curData);
                        break;
                    }
                    default:break;
                    }
                }
                else
                {
                    QString curExpression=ui->textEdit->toPlainText();
                    curExpression+=_btn;
                    TextOpention(curExpression);
                    expression curData(_btn,_type);
                    expr.push_back(curData);
                    break;
                }
                break;
            }
            break;
        }
      }
}

// 6.InToPo函数(中·缀转后缀)

void Widget::InToPo()
{
    while(!expr.isEmpty())
    {
        switch(expr.first().getType())
        {
        case Num: case Special:
        {//数字直接输入后缀表达式
            expression curData(expr.first().getData(),expr.first().getType());
            poexp.push_back(curData);
            break;
        }
        case LeftParentheses:
        {//左括号优先级最高直接压入符号栈
            expression curChar(expr.first().getData(),expr.first().getType());
            symbols.push(curChar);
            break;
        }
        case RightParentheses:
        {//符号为右括号则将符号栈中符号一一出栈并输入后缀表达式,直到碰到左括号
            while(symbols.top().getType()!=LeftParentheses)
            {
                expression curChar(symbols.top().getData(),symbols.top().getType());
                poexp.push_back(curChar);
                symbols.pop();
            }
            symbols.pop();//左括号出栈
            break;
        }
        case BinocularOp:
        {//双目运算符
            if(expr.first().getData()=="+"||expr.first().getData()=="-")
            {//+、-的优先级最低
                while(!symbols.isEmpty()&&symbols.top().getType()!=LeftParentheses)
                {//将符号栈中元素出栈直到符号栈为空或遇到左括号
                    expression curChar(symbols.top().getData(),symbols.top().getType());
                    poexp.push_back(curChar);
                    symbols.pop();
                }
                //当前符号入栈
                expression curChar(expr.first().getData(),expr.first().getType());
                symbols.push(curChar);
                break;
            }
            if(expr.first().getData()=="*"||expr.first().getData()=="÷")
            {//*、÷的优先级只高于+、-
                while(!symbols.isEmpty()&&(symbols.top().getType()!=LeftParentheses&&symbols.top().getData()!="+"&&symbols.top().getData()!="-"))
                {//将符号栈中元素出栈直到符号栈为空或遇到左括号或+、-
                    expression curChar(symbols.top().getData(),symbols.top().getType());
                    poexp.push_back(curChar);
                    symbols.pop();
                }
                //当前符号入栈
                expression curChar(expr.first().getData(),expr.first().getType());
                symbols.push(curChar);
                break;
            }
            break;
        }
        case MonocularOp: case Index: case PercentSign:
        {//优先级仅次于左括号
            while(!symbols.isEmpty()&&(symbols.top().getType()==MonocularOp||symbols.top().getType()==Index||symbols.top().getType()==PercentSign))
            {//符号栈不为空,且栈顶元素为单目运算符或指数符号、百分号时,将栈顶元素输入到后缀表达式中并删除栈顶元素
                expression curChar(symbols.top().getData(),symbols.top().getType());
                poexp.push_back(curChar);
                symbols.pop();
            }
            //当前符号入栈
            expression curChar(expr.first().getData(),expr.first().getType());
            symbols.push(curChar);
            break;
        }
        default:break;
        }
        expr.pop_front();
    }
    if(expr.isEmpty()&&!symbols.isEmpty())
    {
        while(!symbols.isEmpty())
        {//将符号栈中剩余符号依次出栈输入到后缀表达式中
            expression curChar(symbols.top().getData(),symbols.top().getType());
            poexp.push_back(curChar);
            symbols.pop();
        }
    }
}

// 7.Operation(后缀表达式的计算)

bool Widget::Operation()
{//根据后缀表达式计算出结果
    bool chushu=true;
    while(!poexp.isEmpty())
    {
        switch(poexp.first().getType())
        {
        case Num:
        {
            double temp=poexp.first().getData().toDouble();
            num.push(temp);
            poexp.pop_front();
            break;
        }
        case BinocularOp:
        {
            double temp1=num.top();//操作数1
            num.pop();
            double temp2=num.top();//操作数2
            num.pop();
            double temp;
            if(poexp.first().getData()=="+")
            {//加法
                 temp=temp2+temp1;
            }
            else if(poexp.first().getData()=="-")
            {//减法
                 temp=temp2-temp1;
            }
            else if(poexp.first().getData()=="*")
            {//乘法
                 temp=temp2*temp1;
            }
            else if(poexp.first().getData()=="÷")
            {//除法
                if(temp1==0)
                {//除数不可为零
                    ui->textEdit->append("除数为零,表达式有误!");
                    chushu=false;
                }
                else
                    temp=temp2/temp1;
            }
            num.push(temp);
            poexp.pop_front();
            break;
        }
        case PercentSign:
        {//百分比
            double temp1=num.top();//操作数
            num.pop();
            double temp=temp1/100;
            num.push(temp);
            poexp.pop_front();
            break;
        }
        case Special:
        {
            double temp;
            if(poexp.first().getData()=="e")
                temp=exp(1);
            else if(poexp.first().getData()=="π")
                temp=acos(-1);
            num.push(temp);
            poexp.pop_front();
            break;
        }
        case Index:
        {
            double temp1=num.top();//操作数1
            num.pop();
            double temp2=num.top();//操作数2
            num.pop();
            double temp;
            temp=pow(temp2,temp1);
            num.push(temp);
            poexp.pop_front();
            break;
        }
        case MonocularOp:
        {
            QString op=poexp.first().getData();
            double temp=num.top();//操作数
            if(op=="√")
                temp=sqrt(temp);
            else if(op=="sin")
                temp=sin(temp);
            else if(op=="cos")
                temp=cos(temp);
            else if(op=="tan")
                temp=tan(temp);
            else if(op=="ln")
                temp=log(temp);
            else if(op=="log")
                temp=log10(temp);
            num.pop();
            num.push(temp);
            poexp.pop_front();
           break;
        }
        default:break;
        }
    }
    return chushu;
}

这是我第一次写博客,有许多不足的地方。有什么问题大家可以提出来,一起进步。
源码及可执行文件下载地址:
链接: 项目源码.
百度网盘链接:https://pan.baidu.com/s/1tiTodh2O4JGfK3aVaPeN9g
提取码:adh3

  • 10
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zmq1998

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

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

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

打赏作者

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

抵扣说明:

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

余额充值