界面展示







1.用户界面类设计

需要使用QWidget组件作为顶层窗口,QLineEdit组件作为输入框,QPsuhButton作为按钮

1.1 在代码里处理按键消息时,需要处理下用户输入的格式(方便逻辑模块计算)

1)匹配括号成对出现,左括号必然先于右括号出现

  • 当有左括号出现时,则status++
  • 当有右括号出现时,并且status!=0时,则右括号有效,并status--  

2)判断每个按键是否合法

数字前面不能为:右括号

比如:

+)*          //出错,数字5前面不能为右括号

小数点前面不能为空,只能是数字,并且一串数字只能有一个小数点

比如:

1.23.      //出错,一串数字只能有一个小数点

加减号前面不能为:小数点,并且前面不能连续有两次加减乘除,或者是(和运算符

比如:

*-+           //出错,+号前面出现两次加减乘除
. +    //出错,+号前面不能有小数点
-(--) //出错, -5数字前面有个减号

乘除号前面不能为:左括号,空,小数点,加减乘除,

比如:

*+(/+)                   //出错, *前面不能为空,且除法前面不能为左括号

左括号前面不能为:右括号,数字,小数点,并且前面不能连续有两次加减乘除

比如:

( )+(+)               //出错,( 前面不能为数字

右括号前面不能为:空,加减乘除,小数点,左括号,并且与左括号成对出现

比如:

) + (+ .)                   //出错,右括号不能出现在开头,并且右括号前面不能有小数点

2.逻辑模块类设计

如何计算四则运算表达式.比如:

2.1 将中缀表达式进行数字和运算符的分离,并保存到队列里

1)需要考虑 + - 是正负号,还是加减运算符

+-出现在表达式开头时,表示为正负号,比如:

+-;                    //+出现在开头,说明这个+,表示的是正号,而不是加号

当出现+-时,并且前面还有运算符时,表示为正负号,比如:

*-;                            //-前面还有*,说明这个-,表示的是负号,而不是减号

当出现+-时,并且前面还有左括号时,表示为正负号,比如:

+(-+)               //-前面还有(,说明这个-,表示负号,而不是减号

2)以下图的中缀表达式为例

分离后,队列的每个元素应该为:

str[] = "+9"
str[] = "+"
str[] = "("
str[] = "-3"
str[] = "-"
str[] = "-1"
str[] = ")"
str[] = "*"
str[] = "-5"

2.2 将分解出来的中缀表达式队列 转换为后缀表达式队列

比如+9 + (-3 - -1)* -5,转换为后缀表达式为:

 +9,  -3, -1, -, -5, *, +

后缀表达式队列的每个元素应该为:

str[] = "+9"
str[] = "-3"
str[] = "-1"
str[] = "-"
str[] = "-5"
str[] = "*"
str[] = "+"

思路

由于运算符处于后缀,所以需要使用栈,用来存储运算符以及括号

转换过程

-当队列元素为数字时

  • 直接保存到队列

-当队列元素为加减时

  • 判断栈顶的运算优先级,由于+-的优先级小于等于所有运算符
  • 所以循环取出栈顶的运算符并入队列
  • 直到遇到栈为空遇到左括号时才停止,最后再将当前+-入栈

-当队列元素为乘除时

  • 判断栈顶的运算优先级,由于*/的优先级只小于等于*/
  • 所以循环判断栈顶运算符,如果栈顶运算符是*/,则取出并入栈
  • 直到遇到栈为空遇到左括号遇到+-时才停止,最后再将当前*/入栈

-当前队列元素为左括号时

  • 直接入栈

-当前队列元素为右括号时

  • 循环将栈顶运算符出栈并入队列
  • 直到遇到左括号停止,并将左括号出栈弃掉.

-当队列元素判断结束后

  • 判断栈是否为空,如果不为空,则将栈存储的运算符出栈并入队列

示意图如下所示

2.3 将后缀表达式的值计算出来

通过逆波兰表达式计算,思路如下

遇到数字时

  • 入栈

遇到运算符时

  • 依次取出右、左操作数,然后进行计算(有除法时,需要判断除数是否为0)
  • 计算完成后,再将结果入栈

当后缀表达式队列对空时

  • 表示遍历结束,此时栈中若只剩下唯一数字,则算出了结果答案.

示意图如下所示

3.代码实现

3.1 与界面相关的模块,用QCalculatorUI类实现

QCalculatorUI.h代码如下:

#ifndef QCALCULATORUI_H
#define QCALCULATORUI_H #include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QDebug>
#include <QString>
#include "QCalculatorDec.h" class QCalculatorUI : public QWidget
{
Q_OBJECT private:
QCalculatorDec mDec;
QLineEdit *mline; //显示行
QPushButton *mbuton[]; //按钮成员
QCalculatorUI();
bool construct(); private slots:
void handler_clicked(); //处理按键消息 public:
int MatchingBoth(QString &str1,const char *str2); //匹配str1和str2,判断str1是否有str2的字符
int LastMatchingBoth(QString &str1,const char *str2); //反向匹配str1和str2
static QCalculatorUI* NewIntance(); //成员需要资源申请,所以使用二阶构造
void show();
};
#endif // QCALCULATORUI_H

QCalculatorUI.cpp代码如下:

#include "QCalculatorUI.h"

QCalculatorUI::QCalculatorUI() : QWidget(NULL,Qt::WindowCloseButtonHint)
{
} bool QCalculatorUI::construct()
{
int ret;
const char* butnText[]=
{
"<-","CE",
"","","","+","(",
"","","","-",")",
"","","","*","=",
"", ".","/",
}; const int butnPos[][]= //存放 x y w h
{
{,,,},{,,,}, //<- CE
{,,,},{,,,},{,,,},{,,,},{,,,}, //7 8 9 + (
{,,,},{,,,},{,,,},{,,,},{,,,}, //4 5 6 - )
{,,,},{,,,},{,,,},{,,,},{,,,}, //1 2 3 * =
{,,,}, {,,,},{,,,}, //0 . /
}; mline =new QLineEdit(this);
if(mline==NULL)
return false;
mline->resize(,);
mline->move(,);
mline->setAlignment(Qt::AlignRight);
mline->setReadOnly();
// mline->setFont(QFont(0,10)); //设置字体
this->setWindowTitle("计算器");
for(int i=;i<;i++)
{
mbuton[i]= new QPushButton(butnText[i],this);
if(mbuton[i]==NULL)
return false; mbuton[i]->resize(butnPos[i][],butnPos[i][]);
mbuton[i]->move(butnPos[i][],butnPos[i][]); ret = QObject::connect(mbuton[i],SIGNAL(clicked()),this,SLOT(handler_clicked()));
if(ret==false)
return false;
}
return true;
} QCalculatorUI* QCalculatorUI::NewIntance() //二阶构造
{
QCalculatorUI* ret = new QCalculatorUI();
if(ret==NULL || !ret->construct())
{
delete ret;
return NULL;
}
return ret;
} int QCalculatorUI::LastMatchingBoth(QString& str1,const char* str2) //反向匹配str1和str2
{
for(int i=str1.length();i>=;i--)
{
for(unsigned int j=;j<strlen(str2);j++)
if(str1[i]==str2[j])
return i;
}
return -;
} int QCalculatorUI::MatchingBoth(QString& str1,const char* str2) //匹配str1和str2,判断str1是否有str2的字符
{
for(int i=;i<str1.length();i++)
{
for(unsigned int j=;j<strlen(str2);j++)
if(str1[i]==str2[j])
return i;
}
return -;
} void QCalculatorUI::handler_clicked() //处理按键消息
{
static int ClearLine=;
static int bracket_cnt=; //圆括号计数
QPushButton *btn =dynamic_cast<QPushButton* >(sender()); //获取对象
QString line = mline->text();
QString text = btn->text(); //获取消息 if(ClearLine)
{
mline->setText("");
line.clear();
ClearLine=;
}if(text>=""&&text<="") //数字
{
QString tmp= line.right();
if(tmp.length() && tmp[]==')') //数字前面不能为右括号
{
return;
}
line+=text;
} else if(text=="." ) //小数点
{
QString tmp= line.right();
if(tmp.length()) //小数点前面只能是数字
{
if(MatchingBoth(tmp,"")== -) //没找到数字
{
return;
}
}
else //小数点前面为空
{
return ;
} int pos= LastMatchingBoth(line,"+-*/.()"); //反向查找
if(pos!= - &&line[pos]=='.' ) //一串数字只能有一个小数点
{
return ;
}
line+=text;
} else if(text=="+"||text=="-") //加减号
{
QString tmp= line.right();
if(tmp.length()&& tmp[]=='.') //前面不能为:小数点
{
return ;
}
tmp= line.right();
if(tmp.length()==) //前面不能连续有两次加减乘除
{
if(tmp[]=='+'||tmp[]=='-'||tmp[]=='*'||tmp[]=='/'||tmp[]=='(')
if(tmp[]=='+'||tmp[]=='-'||tmp[]=='*'||tmp[]=='/')
return ;
}
line+=text;
} else if(text=="*"||text=="/") //乘除号
{
QString tmp= line.right();
if(tmp.length()) //前面不能为:左括号,小数点,加减乘除,
{
if(MatchingBoth(tmp,"(.+-*/")!= -) //查找左括号,小数点,加减乘除
{
return;
}
}
else //乘除号前面不能为空
return; line+=text;
} else if(text=="(") //左括号
{
QString tmp= line.right();
if(tmp.length()) //前面不能为:右括号,数字,小数点
{
if(MatchingBoth(tmp,")0123456789.")!= -) //查找右括号,数字,小数点
{
return;
}
} tmp= line.right();
if(tmp.length()==) //前面不能连续有两次加减乘除
{
if(tmp[]=='+'||tmp[]=='-'||tmp[]=='*'||tmp[]=='/')
if(tmp[]=='+'||tmp[]=='-'||tmp[]=='*'||tmp[]=='/')
return ;
}
line+=text;
bracket_cnt++;
} else if(text==")") //右括号
{
QString tmp= line.right();
if(bracket_cnt==) //前面没有左括号
return; if(tmp.length()) //前面不能为:加减乘除,小数点,左括号
{
if(MatchingBoth(tmp,"+-*/.(")!= -) //查找加减乘除,小数点,左括号
{
return;
}
}
else //右括号前面不能为空
return; line+=text;
bracket_cnt--;
} else if(text=="<-") //<-
{
if(line.length())
line.chop();
} else if(text=="CE") //清空
{
line.clear();
bracket_cnt=;
} else if(text=="="&& line.length())
{
QString ret=mDec.Result(line);
if(ret==NULL) //除数为0
{
line += " : ";
line +="除数不能为0";
}
else if(ret=="Error")
{
line += ":";
line +="格式出错";
}
else
{
line += "=";
line += ret;
}
ClearLine =;
}
mline->setText(line);
} void QCalculatorUI::show() //显示窗口
{
QWidget::show();
this->setFixedSize(this->width(),this->height());
}

3.2 与逻辑相关的用QCalculatorDec类实现

QCalculatorDec.h代码如下:

#ifndef QCALCULATORDEC_H
#define QCALCULATORDEC_H
#include <QString>
#include <QStack>
#include <QQueue>
#include <QDebug>
class QCalculatorDec
{
private:
QQueue<QString> Split(const QString& exp); //分离前缀
QQueue<QString> Transfer(QQueue<QString>& exp); //将中缀队列转换为后缀队列
QString Calculate(QQueue<QString>& exp); //将后缀队列计算出结果 QString Calculate(QString& l,QString& op,QString& r );
QString ValidNum(QString str); public:
QCalculatorDec();
QString Result(const QString& exp);
}; #endif // QCALCULATORDEC_H

QCalculatorDec.cpp代码如下:

#include "QCalculatorDec.h"

QCalculatorDec::QCalculatorDec()
{
} QQueue<QString> QCalculatorDec::Split(const QString& exp) //分离前缀
{
QQueue<QString> ret;
QString num=""; for(int i=;i<exp.length();i++)
{
if( (exp[i]=='.') || ( (exp[i]>='') && (exp[i]<='') )) //判断小数点和数字
{
num += exp[i];
} else if(exp[i]== '(' || exp[i]== ')' || exp[i]== '*' || exp[i]== '/' )
{
if(!num.isEmpty())
{
ret.enqueue(num); //将数字入队列
num.clear();
}
ret.enqueue(exp[i]);
} else if(exp[i]== '+' || exp[i]== '-') // + - 需要特殊处理
{
if(i==) //表达式开头,说明是正负号
{
num+= exp[i];
} else if(exp[i-]=='(' || exp[i-]=='+' || exp[i-]=='-' || exp[i-]=='*' || exp[i-]=='/')
{
num+= exp[i];
}
else //否则是加减运算符
{
if(!num.isEmpty())
{
ret.enqueue(num); //将数字入队列
num.clear();
}
ret.enqueue(exp[i]);
}
}
} if(!num.isEmpty()) //遍历完成,判断是否还有数字
{
ret.enqueue(num);
num.clear();
}
return ret;
} QQueue<QString> QCalculatorDec::Transfer(QQueue<QString>& exp) //将中缀队列转换为后缀队列
{
QStack<QString> stack;
QQueue<QString> ret;
bool num_ok;
QString symbol; while(!exp.isEmpty())
{
symbol = exp.dequeue(); //出队列
symbol.toDouble(&num_ok); if(num_ok==true) //数字
{
stack.push(symbol);
} else if(symbol=="+"||symbol=="-")
{
while(!stack.isEmpty() &&(stack.top()!="("))
{
ret.enqueue(stack.pop()); //取出栈顶运算符并入队列
}
stack.push(symbol);
} else if(symbol=="*"||symbol=="/")
{
while(!stack.isEmpty() && (stack.top()!="(") && (stack.top()!="+") && (stack.top()!="-"))
{
ret.enqueue(stack.pop()); //取出栈顶运算符并入队列
}
stack.push(symbol);
} else if(symbol == "(")
{
stack.push(symbol);
} else if(symbol ==")")
{
while(!stack.isEmpty() && (stack.top()!="("))
{
ret.enqueue(stack.pop()); //取出栈顶运算符并入队列
}
if(stack.top()=="(")
stack.pop();
}
} while(!stack.isEmpty()&& (stack.top()!="(")) //遍历完成,判断栈里是否为空
{
ret.enqueue(stack.pop()); //取出栈顶运算符并入队列
}return ret;
} QString QCalculatorDec::ValidNum(QString str)
{
QString num;
if(str.indexOf(".")== -) //判断是否小数
return str; while(str.length()>) //避免0被去掉
{
num=str.right();
if(num=="."||num=="")
{
str.chop();
if(num==".")
return str;
}
else
return str;
}
return str;
} QString QCalculatorDec::Calculate(QString& l,QString& op,QString& r )
{
double left,right,res;
QString ret="";
left = l.toDouble();
right = r.toDouble();
if(op == "+")
{
res = left + right;
} else if(op == "-")
{
res = left - right;
} else if(op == "*")
{
res = left * right;
} else if(op == "/")
{
if( (right>(-0.000000000000001)) && (right<(0.000000000000001)) ) //判断除数为0
return NULL;
else
res = left/right;
} ret.sprintf("%f",res);
return ret;
} QString QCalculatorDec::Calculate(QQueue<QString>& exp) //将后缀队列计算出结果
{
QStack<QString> stack;
QString symbol,L,R,op,ret;
bool num_ok; while(!exp.isEmpty())
{
symbol = exp.dequeue(); //出队列
symbol.toDouble(&num_ok); if(num_ok==true) //数字
{
stack.push(symbol);
}
else //运算符
{
if(stack.size()<)
return "Error"; R= stack.pop();
L= stack.pop();
ret = Calculate(L,symbol,R );
if(ret==NULL)
return ret; stack.push(ret);
}
}
if(stack.size()==) //遍历完成,结果只有一个
{
return ValidNum(stack.pop());
}
else
{return "Error";
}
} QString QCalculatorDec::Result(const QString& exp)
{
QQueue<QString> q=Split(exp); //分离中缀
q=Transfer(q); //转换为后缀
return Calculate(q); //返回结果
}

3.3 main.cpp代码如下

#include <QtGui>
#include "QCalculatorUI.h"
#include "QCalculatorDec.h"
int main(int argc, char* argv[])
{
/*设置字体为GBK*/
QTextCodec *codec = QTextCodec::codecForName("GBK");
QTextCodec::setCodecForTr(codec);
QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForCStrings(codec); QApplication app(argc,argv);
QCalculatorUI* ui = QCalculatorUI::NewIntance();
if(ui==NULL)
return false; ui->show();
return app.exec();
}

6.QT-简易计算器实现(详解)的更多相关文章

  1. Windows计算器使用详解

    (1)Backspace:退格,删除当前输入数字中的最后一位 (2)CE:清除,清除显示的数字. (3)C:归零,清除当前的计算. (4)MC:清除存储器中的数值. (5)MR:将存于存储器中的数显示 ...

  2. QT中的qmake详解

    关于qmake,好一段时间令我一头雾水,不知道用来干嘛的,只知道怎么用,而且也只懂那么一两个命令,详细看过资料以后整理如下: 1.首先,感性的认识是,qmake可以利用源文件(包括头文件h,实现文件c ...

  3. Qt 之 pro 配置详解

    原文地址:https://blog.csdn.net/liang19890820/article/details/51774724 简述 使用Qt的时候,我们经常会对pro进行一系列繁琐的配置,为方便 ...

  4. 关于QT的QPainterPath::arcTo 详解

    这个函数文档的意思就是画弧,看了文档也不太明白,自己做了demo终于明白了意思 移动到圆心,画180度半圆 void TestArcTo::paintEvent(QPaintEvent *) { QP ...

  5. Qt之pro配置详解

    简述 使用Qt的时候,我们经常会对pro进行一系列繁琐的配置,为方便大家理解.查找,现将常用的配置进行整理. 简述 配置 注释 CONFIG DEFINES DEPENDPATH DESTDIR FO ...

  6. Qt样式表——选择器详解(父子关系)

    在上一节中,小豆君给大家介绍了样式表的基本概念和大致用法.今天我们来详细了解下样式表中选择器的用法. 所谓选择器,就是指定你所设置的样式对哪个或哪些控件起作用. 到目前为止,Qt样式表支持CSS2中定 ...

  7. Qt *.pro工程文件 详解

    先介绍一下QT中关于项目的相关介绍 app - 建立一个应用程序的makefile.这是默认值,所以如果模板没有被指定,这个将被使用. lib - 建立一个库的makefile. vcapp - 建立 ...

  8. Python使用socketServer包搭建简易服务器过程详解

    官方提供了socketserver包去方便我们快速的搭建一个服务器框架. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的 ...

  9. PyQt学习随笔:Qt事件类QEvent详解

    QEvent类是PyQt5.QtCore中定义的事件处理的基类,事件对象包含了事件对应的参数. <Python & PyQt学习随笔:PyQt主程序的基本框架>介绍了PyQt程序通 ...

随机推荐

  1. Comparable接口和Comparator接口

    1.一个类在设计之初就要实现对该类对象的排序功能,那么这个类要实现Comparable接口,实现public int compareTo(T t)方法.如代码中的Student类.对于实现Compar ...

  2. Alpha冲刺Day9

    Alpha冲刺Day9 一:站立式会议 今日安排: 经过为期5天的冲刺,基本完成企业人员模块的开发.因第三方机构与企业存在委托的关系.第三方人员对于风险的自查.风险列表的展示以及自查风险的统计展示(包 ...

  3. 2017-2018-1 我爱学Java 第八周 作业

    团队六七周作业 团队分工 UML图 工具选择 小编(金立清)有话说 参考资料 团队分工 返回目录 UML图 用例图 类图 活动图 状态图 返回目录 工具选择 ProcessOn - 免费在线作图,实时 ...

  4. 在深度linux下安装pip3与jupyter

    前言 以下安装说明基于已经正确安装python3 文件下载 https://pypi.python.org/pypi/pip 下载pip-9.0.1.tar.gz (md5, pgp)文件 安装准备工 ...

  5. Scrum 冲刺 第三日

    Scrum 冲刺 第三日 目录 要求 项目链接 燃尽图 问题 今日任务 明日计划 成员贡献量 要求 各个成员今日完成的任务(如果完成的任务为开发或测试任务,需给出对应的Github代码签入记录截图:如 ...

  6. HTML5 拖放(Drag 和 Drop)详解与实例(转)

    公司要开一个技术分享会,给我们出了几个简单的题去实现,其中有如何实现表格中列之间的拖拽,我知道html5中有个新方法可以实现,但是没有认真学习,现在闲了去学学,发现关于drag和drop的文章有很多, ...

  7. php中函数和方法的区别

    php的方法就是定义在类里面的方法,一般不建议在方法内部定义方法,但是这种也可以这种叫做内部方法,一般只能本方法调用. 如果定义在同一个类中的方法,在同类的其他方法中调用是$this->方法名就 ...

  8. 用python实现与小米网关通讯

    python 与小米网关通讯的三块内容: 以下内容的理解需要配合<绿米网关局域网通讯协议>使用 1.监听网关发出的组播信息:(有网关及连接设备的生命信号,事件信息) 2.读取需要获得的信息 ...

  9. ASP.NET MVC 5 SmartCode Scaffolding for Visual Studio.Net

    介绍 ASP.NET MVC 5 SmartCode Scaffolding是集成在Visual Studio.Net开发工具中一个ASP.NET MVC Web应用程序代码生成框架,使用SmartC ...

  10. win-zabbix_agent端配置解析

    Zabbix agent 在windows上安装部署 部署windows服务器需要在agent服务器上添加到10.4.200.2的路由 蓝泰服务器需要添加10.0.90.5的网关,联通的机器需要添加到 ...