zoukankan      html  css  js  c++  java
  • 6.QT-简易计算器实现(详解)


    界面展示









    1.用户界面类设计

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

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

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

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

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

    数字前面不能为:右括号

    比如:

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

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

    比如:

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

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

    比如: 

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

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

    比如: 

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

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

    比如: 

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

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

    比如:  

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

    2.逻辑模块类设计

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

     

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

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

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

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

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

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

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

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

     

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

     

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

    str[0] = "+9"
    str[1] = "+"
    str[2] = "("
    str[3] = "-3"
    str[4] = "-"
    str[5] = "-1"
    str[6] = ")"
    str[7] = "*"
    str[8] = "-5"

     

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

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

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

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

    str[0] = "+9"
    str[1] = "-3"
    str[2] = "-1"
    str[3] = "-"
    str[4] = "-5"
    str[5] = "*"
    str[6] = "+"  

    思路

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

    转换过程

    -当队列元素为数字时

    • 直接保存到队列

    -当队列元素为加减时

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

    -当队列元素为乘除时

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

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

    • 直接入栈

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

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

    -当队列元素判断结束后

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

    示意图如下所示

     

    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[20];        //按钮成员
        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[20]=
        {
            "<-","CE",
            "7","8","9","+","(",
            "4","5","6","-",")",
            "1","2","3","*","=",
            "0",    ".","/",
        };
    
        const int butnPos[20][4]=       //存放 x y w h
        {
          {10,50,90,40},{110,50,140,40},                                                    //<- CE
          {10,100,40,40},{60,100,40,40},{110,100,40,40},{160,100,40,40},{210,100,40,40},    //7 8 9 + (
          {10,150,40,40},{60,150,40,40},{110,150,40,40},{160,150,40,40},{210,150,40,40},    //4 5 6 - )
          {10,200,40,40},{60,200,40,40},{110,200,40,40},{160,200,40,40},{210,200,40,90},    //1 2 3 * =
          {10,250,90,40},               {110,250,40,40},{160,250,40,40},                    //0   . /
        };
    
        mline  =new QLineEdit(this);
        if(mline==NULL)
            return false;
        mline->resize(240,30);
        mline->move(10,10);
        mline->setAlignment(Qt::AlignRight);
        mline->setReadOnly(1);
       // mline->setFont(QFont(0,10));        //设置字体
        this->setWindowTitle("计算器");
        for(int i=0;i<20;i++)
       {
             mbuton[i]= new  QPushButton(butnText[i],this);
             if(mbuton[i]==NULL)
                 return false;
    
             mbuton[i]->resize(butnPos[i][2],butnPos[i][3]);
             mbuton[i]->move(butnPos[i][0],butnPos[i][1]);
    
             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>=0;i--)
        {
            for(unsigned int j=0;j<strlen(str2);j++)
                if(str1[i]==str2[j])
                     return i;
        }
       return -1;
    }
    
    int  QCalculatorUI::MatchingBoth(QString& str1,const char* str2)          //匹配str1和str2,判断str1是否有str2的字符
    {
        for(int i=0;i<str1.length();i++)
        {
            for(unsigned int j=0;j<strlen(str2);j++)
                if(str1[i]==str2[j])
                     return i;
        }
       return -1;
    }
    
    void QCalculatorUI::handler_clicked()      //处理按键消息
    {
        static int ClearLine=0;
        static int bracket_cnt=0;           //圆括号计数
        QPushButton *btn =dynamic_cast<QPushButton* >(sender()); //获取对象
        QString line = mline->text();
        QString text = btn->text();     //获取消息
    
        if(ClearLine)
        {
            mline->setText("");
            line.clear();
            ClearLine=0;
        }if(text>="0"&&text<="9")    //数字
        {
            QString tmp= line.right(1);
            if(tmp.length() && tmp[0]==')')   //数字前面不能为右括号
            {
                return;
            }
            line+=text;
        }
    
        else if(text=="." )    //小数点
        {
            QString tmp= line.right(1);
            if(tmp.length()) //小数点前面只能是数字
           {
                if(MatchingBoth(tmp,"0123456789")== -1)  //没找到数字
                {
                    return;
                }
           }
           else             //小数点前面为空
           {
                    return ;
           }
    
           int pos= LastMatchingBoth(line,"+-*/.()");   //反向查找
            if(pos!= -1 &&line[pos]=='.' )        //一串数字只能有一个小数点
            {
                return ;
            }
             line+=text;
        }
    
        else if(text=="+"||text=="-")       //加减号
        {
            QString tmp= line.right(1);
           if(tmp.length()&& tmp[0]=='.')     //前面不能为:小数点
           {
              return ;
           }
           tmp= line.right(2);
           if(tmp.length()==2)          //前面不能连续有两次加减乘除
           {
               if(tmp[0]=='+'||tmp[0]=='-'||tmp[0]=='*'||tmp[0]=='/'||tmp[0]=='(')
                    if(tmp[1]=='+'||tmp[1]=='-'||tmp[1]=='*'||tmp[1]=='/')
                                return ;
           }
            line+=text;
        }
    
        else if(text=="*"||text=="/")       //乘除号
        {
             QString tmp= line.right(1);
             if(tmp.length())       //前面不能为:左括号,小数点,加减乘除,
             {
                 if(MatchingBoth(tmp,"(.+-*/")!= -1) //查找左括号,小数点,加减乘除
                 {
                     return;
                 }
             }
             else                   //乘除号前面不能为空
                  return;
    
            line+=text;
        }
    
        else if(text=="(")       //左括号
        {
            QString tmp= line.right(1);
            if(tmp.length())             //前面不能为:右括号,数字,小数点
            {
                if(MatchingBoth(tmp,")0123456789.")!= -1) //查找右括号,数字,小数点
                {
                    return;
                }
            }
    
            tmp= line.right(2);
            if(tmp.length()==2)          //前面不能连续有两次加减乘除
            {
                 if(tmp[0]=='+'||tmp[0]=='-'||tmp[0]=='*'||tmp[0]=='/')
                     if(tmp[1]=='+'||tmp[1]=='-'||tmp[1]=='*'||tmp[1]=='/')
                                 return ;
            }
             line+=text;
             bracket_cnt++;
        }
    
        else if(text==")")       //右括号
        {
            QString tmp= line.right(1);
            if(bracket_cnt==0)  //前面没有左括号
               return;
    
            if(tmp.length())             //前面不能为:加减乘除,小数点,左括号
            {
               if(MatchingBoth(tmp,"+-*/.(")!= -1) //查找加减乘除,小数点,左括号
               {
                   return;
               }
            }
            else                    //右括号前面不能为空
               return;
    
            line+=text;
            bracket_cnt--;
        }
    
        else if(text=="<-")       //<-
        {
            if(line.length())
            line.chop(1);
        }
    
        else if(text=="CE")       //清空
        {
            line.clear();
            bracket_cnt=0;
        }
    
        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 =1;
        }
        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=0;i<exp.length();i++)
        {
            if( (exp[i]=='.') || ( (exp[i]>='0') && (exp[i]<='9') ))    //判断小数点和数字
            {
                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==0)       //表达式开头,说明是正负号
                {
                 num+= exp[i];
                }
    
                else if(exp[i-1]=='(' || exp[i-1]=='+' || exp[i-1]=='-' || exp[i-1]=='*' || exp[i-1]=='/')
                {
                 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(".")== -1) //判断是否小数 return str; while(str.length()>1) //避免0被去掉 { num=str.right(1); if(num=="."||num=="0") { str.chop(1); 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()<2) return "Error"; R= stack.pop(); L= stack.pop(); ret = Calculate(L,symbol,R ); if(ret==NULL) return ret; stack.push(ret); } } if(stack.size()==1) //遍历完成,结果只有一个 { 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(); }

     

  • 相关阅读:
    leetcode5 Longest Palindromic Substring
    leetcode17 Letter Combinations of a Phone Number
    leetcode13 Roman to Integer
    leetcode14 Longest Common Prefix
    leetcode20 Valid Parentheses
    leetcode392 Is Subsequence
    leetcode121 Best Time to Buy and Sell Stock
    leetcode198 House Robber
    leetcode746 Min Cost Climbing Stairs
    tomcat下使用druid配置jnid数据源
  • 原文地址:https://www.cnblogs.com/lifexy/p/8901369.html
Copyright © 2011-2022 走看看