1, Interface layout
The following picture is the interface layout of the calculator. You can know the basic functions of the calculator from the interface, including basic operations of addition, subtraction, multiplication and division, cosine, sine and other function operations.
2, Function brief introduction and operation display
The above operations involve logarithm, exponent, multiplication and addition. Multiplication has a multiplier sign between "25" and ln by default (which is specifically reflected in the code).
Function introduction:
The calculator can realize simple addition, subtraction, multiplication and division, as well as the operation of bracketed, basic trigonometric function, logarithm and exponential expressions.
Fault tolerant processing:
1. When entering an expression in the text box, it will automatically detect whether the expression is correct. If the character to be entered will lead to an expression error, the character will not be entered into the text box, and qvector expr (described in the variable code) will not record the character.
2. Press the "=" key when the expression is incomplete, and no processing will be done.
3, Inverse Polish (infix to suffix)
Algorithm idea:
1: Traverse infix expression from left to right;
2: Direct output in case of digital;
3: Operator encountered:
①. If it is "(", directly enter the symbol stack;
②. If it is ")", the elements in the symbol stack will be output in turn until "(", "(" is out of the stack, and will not be output;
③. For other operators, the elements in the symbol stack are output in turn until an operator or "(" with lower priority than the current operator is encountered, and then the symbol is put on the stack.
④. After traversal, if the symbol stack is not empty, the elements in the symbol stack will be output successively until it is empty, and the new output expression is the suffix expression of the desired expression.
4: Suffix expression evaluation
①. Traverse the expression from left to right;
②. If it is a number, enter it directly into the number stack;
③. If it is binocular operator c, the two elements of the stack are recorded as a1 (first out of the stack) and A2 (second out of the stack); Make the number a3=a2 c a1(a2 in front and a1 in rear), and then press a3 into the number stack;
④. If it is a monocular operator c, an element is taken out of the number stack and recorded as a1 and A2 respectively (later out of the stack); Make the number a= c a1, and then press a into the number stack;
⑤ After traversal, there is still one element left in the number stack, which is the result of the expression.
4, Code analysis
// 1.expression.h
The expression class is designed to store key information. The key symbol is represented by QString data, and the key type is represented by btntype_ Type.
#include <QObject> enum BtnType{ Num,//number Point,//spot BinocularOp,//Binocular operator MonocularOp,//unary operator Special,//Special symbols LeftParentheses,//Left parenthesis RightParentheses,//Right parenthesis PercentSign,//Percent sign Index,//index Back,//Backspace Equl,//be equal to Clear,//eliminate }; 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
The key information is directly obtained by the constructor of expression (i.e. expression (qstring, MDATA, btntype, mtype): data (MDATA)_ type(mType){};) Information extraction is realized by getData and getType.
#include "expression.h" expression::expression() {//default constructor } 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();//Infix expression to suffix expression bool checkParentheses();//Check whether the brackets in infix expression are correct int checkPoint(const QString& str);//Detect the number of points in the data and return. bool Operation();//Operation (calculated according to suffix expression) void TextOpention(const QString& newExprssion);//Operation of the text box after pressing the key bool checkLastChar();//Detects the last character of the expression 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;//Infix expression QVector<expression> poexp;//Postfix Expression QStack<double> num;//Digital stack QStack<expression> symbols;//Symbol stack }; #endif // WIDGET_H
// 4.btnConnect function
void Widget::btnConnect() { //Numeric key binding 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");}); //Binocular operator binding 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,"-");}); //index 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,"^");}); //spot connect(ui->BtnPoint,&QPushButton::clicked,[this](){onclicked(Point,".");}); //Monocular operator binding 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");}); //Percent sign connect(ui->BtnPercentSign,&QPushButton::clicked,[this](){onclicked(PercentSign,"%");}); //Bracket binding connect(ui->BtnLeftParenthesis,&QPushButton::clicked,[this](){onclicked(LeftParentheses,"(");}); connect(ui->BtnRightParenthesis,&QPushButton::clicked,[this](){onclicked(RightParentheses,")");}); //Special symbol binding connect(ui->BtnE,&QPushButton::clicked,[this](){onclicked(Special,"e");}); connect(ui->BtnPai,&QPushButton::clicked,[this](){onclicked(Special,"π");}); //empty connect(ui->BtnClear,&QPushButton::clicked,[this](){onclicked(Clear,"");}); //Backspace connect(ui->BtnDelete,&QPushButton::clicked,[this](){onclicked(Back,"");}); //be equal to connect(ui->BtnEqual,&QPushButton::clicked,[this](){onclicked(Equl,"");}); }
The percent sign is a monocular operator and the index is a binocular operator. Because it is different from other operators, a symbol type is set separately, which can still be classified as monocular and binocular operators when calculating the expression at the end. The above binds a type of key signal to the slot function onclicked.
// 5.onclicked function
Due to the length of the code, only the code when the number key is pressed is listed here.
void Widget::onclicked(BtnType _type, QString _btn) { int rowCount=ui->textEdit->document()->lineCount(); switch (_type) { case Num: {//number if(rowCount==0||rowCount==1) { if(!expr.isEmpty()) { switch (expr.back().getType()) { case Num: { QString preNum=expr.back().getData(); if(checkPoint(preNum)==0)//There is no decimal point in the previous number { double tempNum; tempNum=expr.back().getData().toDouble(); tempNum*=10; tempNum+=_btn.toDouble(); expr.pop_back();//Delete previous data expression curData(QString::number(tempNum),_type); expr.push_back(curData);//Press the current data into the array QString curExpression=ui->textEdit->toPlainText(); curExpression+=_btn; TextOpention(curExpression); break; } else if(checkPoint(preNum)==1)//There is a decimal point in the previous number { 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 function (middle suffix)
void Widget::InToPo() { while(!expr.isEmpty()) { switch(expr.first().getType()) { case Num: case Special: {//Number direct input suffix expression expression curData(expr.first().getData(),expr.first().getType()); poexp.push_back(curData); break; } case LeftParentheses: {//The left bracket has the highest priority and is directly pushed into the symbol stack expression curChar(expr.first().getData(),expr.first().getType()); symbols.push(curChar); break; } case RightParentheses: {//If the symbol is a right bracket, the symbols in the symbol stack will be out of the stack one by one and the suffix expression will be input until the left bracket is encountered while(symbols.top().getType()!=LeftParentheses) { expression curChar(symbols.top().getData(),symbols.top().getType()); poexp.push_back(curChar); symbols.pop(); } symbols.pop();//Left bracket out of stack break; } case BinocularOp: {//binary operator if(expr.first().getData()=="+"||expr.first().getData()=="-") {//+, - has the lowest priority while(!symbols.isEmpty()&&symbols.top().getType()!=LeftParentheses) {//Push the elements in the symbol stack out of the stack until the symbol stack is empty or encounters an open parenthesis expression curChar(symbols.top().getData(),symbols.top().getType()); poexp.push_back(curChar); symbols.pop(); } //Current symbol stack expression curChar(expr.first().getData(),expr.first().getType()); symbols.push(curChar); break; } if(expr.first().getData()=="*"||expr.first().getData()=="÷") {//*The priority of ÷ is only higher than +- while(!symbols.isEmpty()&&(symbols.top().getType()!=LeftParentheses&&symbols.top().getData()!="+"&&symbols.top().getData()!="-")) {//Push the elements in the symbol stack out of the stack until the symbol stack is empty or encounters an open bracket or +- expression curChar(symbols.top().getData(),symbols.top().getType()); poexp.push_back(curChar); symbols.pop(); } //Current symbol stack expression curChar(expr.first().getData(),expr.first().getType()); symbols.push(curChar); break; } break; } case MonocularOp: case Index: case PercentSign: {//Priority is second only to the left parenthesis while(!symbols.isEmpty()&&(symbols.top().getType()==MonocularOp||symbols.top().getType()==Index||symbols.top().getType()==PercentSign)) {//When the symbol stack is not empty, and the top element of the stack is a monocular operator, exponential symbol and percent sign, enter the top element of the stack into the suffix expression and delete the top element expression curChar(symbols.top().getData(),symbols.top().getType()); poexp.push_back(curChar); symbols.pop(); } //Current symbol stack 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()) {//The remaining symbols in the symbol stack are sequentially out of the stack and input into the suffix expression expression curChar(symbols.top().getData(),symbols.top().getType()); poexp.push_back(curChar); symbols.pop(); } } }
// 7. Operation (calculation of suffix expression)
bool Widget::Operation() {//The result is calculated according to the suffix expression 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();//Operand 1 num.pop(); double temp2=num.top();//Operand 2 num.pop(); double temp; if(poexp.first().getData()=="+") {//addition temp=temp2+temp1; } else if(poexp.first().getData()=="-") {//subtraction temp=temp2-temp1; } else if(poexp.first().getData()=="*") {//multiplication temp=temp2*temp1; } else if(poexp.first().getData()=="÷") {//division if(temp1==0) {//Divisor cannot be zero ui->textEdit->append("Divisor is zero,Incorrect expression!"); chushu=false; } else temp=temp2/temp1; } num.push(temp); poexp.pop_front(); break; } case PercentSign: {//percentage double temp1=num.top();//Operand 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();//Operand 1 num.pop(); double temp2=num.top();//Operand 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();//Operand 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; }
This is my first time to write a blog. There are many shortcomings. If you have any questions, you can raise them and make progress together.
Source code and executable file download address:
Link: Project source code.