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]);

         QObject::connect(mbuton[i],SIGNAL(clicked()),this,SLOT(handler_clicked()));
         
   }
   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(); }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM