使用Qt框架开发的简单的计算器工具,能够实现简单的运算。展示如下:
核心设计思路:首先将每个点击后的字符存入字符串,此时字符串中存储的即为中缀表达式,然后将该中缀表达式转换为后缀表达式,最后进行计算。
项目代码已上传至码云:https://gitee.com/riabs/calculator
后缀表达式
假设现有表达式:1 * ( 2 + 3 - 4 ) / 5,
像这样的表达式称为中缀表达式,生活中平时使用的就是中缀表达式,人很好理解应该先计算哪部分;
而后缀表达式则是计算机更好理解的形式,先说说规则。
=》中缀表达式转后缀表达式的规则:
准备一个字符串和存储操作符的栈。
首先遍历中缀表达式,遇到操作数则直接存入字符串,遇到操作符则进行如下判断:假如栈空或该操作符的优先级高于栈顶操作符,则入栈;否则先弹出栈顶元素并存入字符串,直到栈空或者该操作符的优先级高于栈顶操作符时,将该操作符入栈。遍历完中缀表达式后,依次弹出栈顶元素并存入字符串。
其中比较特殊的是左括号和右括号。当遍历中缀表达式遇到左括号时,不做比较直接入栈,并且其在栈中的优先级最低;当遇见右括号时,弹出栈顶元素并存入字符串,直到栈顶元素为左括号为止,此时弹出左括号,不存入字符串。
/ 注:笔者在编写程序时,遇到操作符后,只要栈空或该操作符的优先级不低于栈顶操作符即可入栈。虽然规则有些许不同,但最终计算结果与如上规则无异,只是计算顺序有所不同。/
=》计算后缀表达式的方式:
准备一个存储操作数的栈。
遍历后缀表达式,遇到操作数则直接入栈;遇到操作符op时弹出栈顶元素a,继续弹出栈顶元素b,以b op a的顺序进行计算,并将结果压入栈中,直到遍历完后缀表达式,输出栈顶元素,即为最终计算结果。
下面以1 * ( 2 + 3 - 4 ) / 5为例,对以上两部分附上过程展示:
1.中缀表达式转后缀表达式
2.计算后缀表达式
下面是项目主要实现:
一、创建项目
开发工具:Qt Creator;
使用版本:Qt 5.15.2 MinGW 64-bit;
打开Qt Creator,创建项目,选择 Qt Widgets Application;
设置名称和路径;
选择qmake;
设置类名;
在构建套件中选择MinGW;
然后下一步,完成。
二、设计界面
双击calculator.ui,拖入若干Push Button控件,和一个Label控件,并给每个控件设置名称;
界面完成。
三、主要代码
为了界面看起来更加美观,初始化按键字体和颜色;
//初始化按键字体和颜色
void Calculator::initButtonStyle()
{
ui->btn_0->setStyleSheet("font-size:20pt");
ui->btn_1->setStyleSheet("font-size:20pt");
ui->btn_2->setStyleSheet("font-size:20pt");
ui->btn_3->setStyleSheet("font-size:20pt");
ui->btn_4->setStyleSheet("font-size:20pt");
ui->btn_5->setStyleSheet("font-size:20pt");
ui->btn_6->setStyleSheet("font-size:20pt");
ui->btn_7->setStyleSheet("font-size:20pt");
ui->btn_8->setStyleSheet("font-size:20pt");
ui->btn_9->setStyleSheet("font-size:20pt");
ui->btn_point->setStyleSheet("font-size:20pt");
ui->btn_mod->setStyleSheet("font-size:20pt");
ui->btn_clear->setStyleSheet("font-size:20pt");
ui->btn_del->setStyleSheet("font-size:20pt");
ui->btn_leftBrackets->setStyleSheet("font-size:20pt");
ui->btn_rightBrackets->setStyleSheet("font-size:20pt");
ui->btn_add->setStyleSheet("font-size:20pt;background-color:#22aabb");
ui->btn_sub->setStyleSheet("font-size:20pt;background-color:#bbaaee");
ui->btn_multi->setStyleSheet("font-size:20pt;background-color:#ffaa22");
ui->btn_div->setStyleSheet("font-size:20pt;background-color:#ffaaee");
ui->btn_result->setStyleSheet("font-size:20pt;background-color:RED");
//显示框颜色和字体
ui->label->setStyleSheet("font-size:20pt;background-color:WHITE");
}
声明QString类型的成员变量m_expression;利用Qt信号槽机制,点击算数表达式的按钮时,将其对应的字符存入字符串;
//将按键存入表达式
void Calculator::setExperssion()
{
connect(ui->btn_0,&QPushButton::clicked,this,[=](){
m_expression += '0';
emit showLabel();
});
connect(ui->btn_1,&QPushButton::clicked,this,[=](){
m_expression += '1';
emit showLabel();
});
connect(ui->btn_2,&QPushButton::clicked,this,[=](){
m_expression += '2';
emit showLabel();
});
connect(ui->btn_3,&QPushButton::clicked,this,[=](){
m_expression += '3';
emit showLabel();
});
connect(ui->btn_4,&QPushButton::clicked,this,[=](){
m_expression += '4';
emit showLabel();
});
connect(ui->btn_5,&QPushButton::clicked,this,[=](){
m_expression += '5';
emit showLabel();
});
connect(ui->btn_6,&QPushButton::clicked,this,[=](){
m_expression += '6';
emit showLabel();
});
connect(ui->btn_7,&QPushButton::clicked,this,[=](){
m_expression += '7';
emit showLabel();
});
connect(ui->btn_8,&QPushButton::clicked,this,[=](){
m_expression += '8';
emit showLabel();
});
connect(ui->btn_9,&QPushButton::clicked,this,[=](){
m_expression += '9';
emit showLabel();
});
connect(ui->btn_point,&QPushButton::clicked,this,[=](){
m_expression += '.';
emit showLabel();
});
connect(ui->btn_add,&QPushButton::clicked,this,[=](){
m_expression += '+';
emit showLabel();
});
connect(ui->btn_sub,&QPushButton::clicked,this,[=](){
m_expression += '-';
emit showLabel();
});
connect(ui->btn_mod,&QPushButton::clicked,this,[=](){
m_expression += '%';
emit showLabel();
});
connect(ui->btn_multi,&QPushButton::clicked,this,[=](){
m_expression += '*';
emit showLabel();
});
connect(ui->btn_div,&QPushButton::clicked,this,[=](){
m_expression += '/';
emit showLabel();
});
connect(ui->btn_leftBrackets,&QPushButton::clicked,this,[=](){
m_expression += '(';
emit showLabel();
});
connect(ui->btn_rightBrackets,&QPushButton::clicked,this,[=](){
m_expression += ')';
emit showLabel();
});
}
获取到表达式后,将其转换为后缀表达式,方便求值;
在转换为后缀表达式式时,数字或操作符之间用空格隔开;
//转换为后缀表达式
QString Calculator::toRPN(QString str)
{
QString rpn;
QStack<QChar> opStack;//存操作符
for(auto i : str){
//数字或小数点直接存入字符串
if((i <= '9' && i >= '0') || i == '.'){
rpn += i;
}
//左括号直接入栈
if(i == '('){
opStack.push(i);
}
//遇到右括号,出栈栈顶元素直到栈顶元素为左括号
if(i == ')'){
while(opStack.top() != '('){
rpn += ' ';
rpn += opStack.pop();
}
opStack.pop();//将左括号出栈
}
//运算符优先级不低于栈顶元素则入栈,否则出栈栈顶元素后入栈
if(i == '+'||i == '-'){
rpn += ' ';
while(!opStack.empty() && opStack.top() != '(' && opStack.top() != '+' && opStack.top() != '-'){
rpn += opStack.pop();
rpn += ' ';
}
opStack.push(i);
}
if(i == '*'||i == '/'||i == '%'){
rpn += ' ';
opStack.push(i);
}
}
//出栈所有元素
while(!opStack.empty()){
rpn += ' ';
rpn += opStack.pop();
}
qDebug() << str << rpn;
return rpn;
}
然后对后缀表达式进行运算;
double Calculator::operation(QString rpn)
{
QStack<double> result;
QString num;
//遍历后缀表达式
for(auto i : rpn){
if((i >= '0' && i <= '9') || i == '.' || i == ' '){
if((i >= '0' && i <= '9') || i == '.' ){
num += i;
}
if(i == ' '){
if(!num.isEmpty() && num.at(0) >= '0' && num.at(0) <= '9'){
result.push(num.toDouble());
num.clear();
}
}
}
//加法
if(i == '+'){
double temp1 = result.pop();
double temp2 = result.pop();
result.push(temp2+temp1);
}
//减法
if(i == '-'){
double temp1 = result.pop();
double temp2 = result.pop();
result.push(temp2-temp1);
}
//乘法
if(i == '*'){
double temp1 = result.pop();
double temp2 = result.pop();
result.push(temp2*temp1);
}
//除法
if(i == '/'){
double temp1 = result.pop();
double temp2 = result.pop();
result.push(temp2/temp1);
}
//模运算
if(i == '%'){
double temp1 = result.pop();
double temp2 = result.pop();
result.push(fmod(temp2,temp1));
}
}
return result.top();
}
当点击等于号时,调用求值函数并显示在Label框中。除了等于号,还要处理删除键和清空键的事件;
//特殊按键功能实现
void Calculator::buttonFunction()
{
//删除键
connect(ui->btn_del,&QPushButton::clicked,this,[=](){
if(!m_expression.isEmpty()){
m_expression.chop(1);//删除字符串最后一位元素
}
emit showLabel();
});
//清空键
connect(ui->btn_clear,&QPushButton::clicked,this,[=](){
m_expression.clear();
emit showLabel();
});
//等于键
connect(ui->btn_result,&QPushButton::clicked,this,[=](){
ui->label->setNum(Calculator::operation(toRPN(m_expression)));
});
}