1. 程式人生 > >用Qt實現的計算器

用Qt實現的計算器

需求分析

模組圖

這裡寫圖片描述

類圖

這裡寫圖片描述

核心演算法

中綴表示式轉化為字尾表示式

規則:從左到右遍歷中綴表示式(表示式運算子在兩數字之間,比如(2+1)3)的每個數字和符號,若是數字就輸出,即成為字尾表示式(表示式運算子在數字之後,不包含括號,比如2 1+3 )的一部分;若是符號,則判斷其與棧頂符號的優先順序,是右括號或優先順序低於棧頂符號(乘除優先加減)則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最終輸出字尾表示式為止。

轉化為字尾表示式方便計算(所有的計算按運算子出現的順序,嚴格從左向右進行,不再考慮運算子的優先規則。)。

規則分解

  • 遇到運算元:直接輸出(新增到字尾表示式中)
  • 棧為空時,遇到運算子,直接入棧
  • 遇到左括號:將其入棧
  • 遇到右括號:執行出棧操作,並將出棧的元素輸出,直到彈出棧的是左括號,左括號不輸出。
  • 遇到其他運算子:加減乘除:彈出所有優先順序大於或者等於該運算子的棧頂元素,然後將該運算子入棧
  • 最終將棧中的元素依次出棧,輸出。
    現在按規則將中綴表示式(2+1)*3轉化為字尾表示式
    首先是左括號,進棧

然後是數字2 直接輸出 2
接著是+號,進棧

+

然後是數字1 直接輸出在2後面 2 1
然後遇到右括號,棧中所有元素出棧,輸出加號,左括號不輸出 2 1 +
接著*號,進棧

*

然後是數字3 直接輸出 2 1 + 3
表示式結束,棧中元素出棧 輸出號 2 1 + 3

程式碼

    QStack<QString> stack;
    QString temp;//數字輸出
    QStringList list;//全部輸出
    while(!expression.isEmpty())
    {

        if(expression.startsWith('+')||expression.startsWith('-')||expression.startsWith('*')||expression.startsWith
('/')||expression.startsWith('(')||expression.startsWith(')')) { //遇到運算子及左右括號,可能進棧,出棧 if(!temp.isEmpty()) { list.append(temp); temp.clear(); } //+-)優先順序高於或等於其它運算子及左括號,遇到則使棧中元素彈出 if(expression.startsWith('+')||expression.startsWith('-')||expression.startsWith(')')) { while(!stack.isEmpty()) { if(stack.top()==QString('(')) { //棧頂為左括號,遇到右括號,直接讓左括號出棧,不輸出 if(expression.startsWith(')')) { stack.pop(); } break; } else { //將棧中元素輸出,直到棧頂為左括號 list.append(stack.pop()); } } } if(expression.startsWith(')')) expression.remove(0,1); if(!expression.isEmpty()) { stack.push(expression.left(1)); expression.remove(0,1); } } else { //遇到數字,直接輸出 temp+=expression.left(1); expression.remove(0,1); } } if(!temp.isEmpty()) list.append(temp); while(!stack.isEmpty()) { list.append(stack.pop()); }

計算字尾表示式

規則:從左到右遍歷表示式的每個數字和符號,遇到是數字就進棧,遇到是符號,就將處於棧頂兩個數字出棧,進行運算,運算結果進棧,一直到最終獲得結果。

現在按規則計算字尾表示式 2 1 + 3 *
遇到數字2 進棧

2

遇到數字1 進棧

1
2

遇到+號 將處於棧頂的兩個數字 1 2 出棧 進行加法運算 得結果為3 進棧

3

遇到數字3 進棧

3
3

遇到乘號* 將處於棧頂的兩個數字3 3 進行乘法運算 得結果為9 結束。

程式碼

    QString operand1;
    QString operand2;
    QString result;
    int row=0;
    while(!list.isEmpty())
    {
        if(list.startsWith("+"))
        {
            operand2 = stack.pop();
            operand1 = stack.pop();
            result = QString::number(operand1.toDouble()+operand2.toDouble());
            stack.push(result);
        }
        else if(list.startsWith("-"))
        {
            operand2 = stack.pop();
            operand1 = stack.pop();
            result = QString::number(operand1.toDouble()-operand2.toDouble());
            stack.push(result);
        }
        else if(list.startsWith("*"))
        {
            operand2 = stack.pop();
            operand1 = stack.pop();
            result = QString::number(operand1.toDouble()*operand2.toDouble());
            stack.push(result);
        }
        else if(list.startsWith("/"))
        {
            operand2 = stack.pop();
            if(operand2.toDouble()==0)
            {
                lineEdit->setText(trUtf8("除數不能為0"));
                flag = true;
            }
            operand1 = stack.pop();
            result = QString::number(operand1.toDouble()/operand2.toDouble());
            stack.push(result);
        }
        else
        {
            stack.push(list.first());          
        }
        list.removeFirst();
        operand1.clear();
        operand2.clear();
    }

介面

部分程式碼

    for(int i=0;i<10;i++)
    {
        digitButtons[i]=createButtons(QString::number(i));
        digitButtons[i]->setStyleSheet("font-size:30px;color:white;background-color:rgb(71,71,71)");


        //QPalette pal = digitButtons[i]->palette();
        //pal.setColor(QPalette::Button,QColor(112,128,105));
        //pal.setColor(QPalette::ButtonText,QColor(225,225,225));
        //digitButtons[i]->setPalette(pal);
    }

    symbolButtons[0]=createButtons("*");
    symbolButtons[1]=createButtons("/");
    symbolButtons[2]=createButtons("+");
    symbolButtons[3]=createButtons("-");
    symbolButtons[4]=createButtons(".");
    symbolButtons[5]=createButtons("=");
    symbolButtons[6]=createButtons("(");
    symbolButtons[7]=createButtons(")");
    symbolButtons[8]=createButtons(trUtf8("刪除"));
    for(int i=0;i<8;i++)
    {
        symbolButtons[i]->setStyleSheet("font-weight:bold;font-size:30px;color:white;background-color:rgb(33,33,33)");
    }
    symbolButtons[8]->setStyleSheet("font-size:20px;color:white;background-color:rgb(26,26,26)");

一開始我是這麼寫的,看了一下覺得還可以再優化一下,就成了下面這樣

    QString buttonText[8] = {"*","/","+","-",".","=","(",")"};
    for(int i=0;i<8;i++)
    {
        symbolButtons[i]=createButtons(buttonText[i]);
        symbolButtons[i]->setStyleSheet("font-weight:bold;font-size:30px;color:white;background-color:rgb(33,33,33)");
    }
    symbolButtons[8]=createButtons(trUtf8("刪除"));
    symbolButtons[8]->setStyleSheet("font-size:20px;color:white;background-color:rgb(26,26,26)");

下面是createButtons函式及buttonClicked函式

QToolButton *MainWindow::createButtons(const QString &text)
{
    QToolButton *tb = new QToolButton;
    tb->setText(text);
    connect(tb,SIGNAL(clicked()),this,SLOT(buttonClicked()));
    tb->setFixedSize(60,83);
    if(text == QString("0"))
        rightLayout->addWidget(tb,4,1);
    else if(text == QString("("))
        rightLayout->addWidget(tb,0,0);
    else if(text == QString(")"))
        rightLayout->addWidget(tb,0,1);
    else if(text == QString(trUtf8("刪除")))
        rightLayout->addWidget(tb,0,3);
    else if(text == QString("*"))
        rightLayout->addWidget(tb,1,3);
    else if(text == QString("/"))
        rightLayout->addWidget(tb,2,3);
    else if(text == QString("+"))
        rightLayout->addWidget(tb,3,3);
    else if(text == QString("-"))
        rightLayout->addWidget(tb,4,3);
    else if(text == QString("."))
        rightLayout->addWidget(tb,4,0);
    else if(text == QString("="))
        rightLayout->addWidget(tb,4,2);
    else
        rightLayout->addWidget(tb,(text.toInt()-1)/3+1,(text.toInt()-1)%3);

    return tb;

}
void MainWindow::buttonClicked()
{
    if(flag == true)
    {
        lineEdit->clear();
        flag = false;
    }
    QString expression = lineEdit->text();
    QToolButton *tb = qobject_cast<QToolButton *>(sender());
    QString text = tb->text();

    if(text == QString(trUtf8("刪除")))
        lineEdit->backspace();
    else if(text == QString("("))
    {
        lineEdit->cursorBackward(true);
        QString leftLetter = lineEdit->selectedText();
        if(leftLetter == QString("*")||leftLetter == QString("/")||leftLetter == QString("+")||leftLetter == QString("-"))
            lineEdit->setText(expression+=text);
        else
        {
            lineEdit->setText(expression);
            return;
        }

    }
    else if(text == QString(")"))
    {
        lineEdit->cursorBackward(true);
        QString leftLetter = lineEdit->selectedText();
        int countLeft=expression.count("(");
        int countRight=expression.count(")");

        if(leftLetter == QString("*")||leftLetter == QString("/")||leftLetter == QString("+")||leftLetter == QString("-")||leftLetter == QString(".")||leftLetter == QString("(")||countLeft<=countRight)
            lineEdit->setText(expression);
        else
            lineEdit->setText(expression+=text);
    }

    else if(text == QString("*")||text == QString("/")||text == QString("+")||text == QString("-")||text == QString("."))
    {
        lineEdit->cursorBackward(true);
        QString leftLetter = lineEdit->selectedText();
        if(leftLetter == QString("*")||leftLetter == QString("/")||leftLetter == QString("+")||leftLetter == QString("-")||leftLetter == QString(".")|| leftLetter.isEmpty() )
        {
            lineEdit->setText(expression);
            return;
        }
        else
            lineEdit->setText(expression+=text);


    }
    else if(text == QString("="))
        calculate();
    else
        lineEdit->setText(expression+=text);

}

計算器輸入框選用QLineEdit類來實現,可以用鍵盤輸入表示式,不過為了防止使用者輸入錯誤的資訊,通過正則表示式來限制輸入的內容只能為數字,小數點,加減乘除號和括號,而字母和其它符號將通通不被接受,不予輸入!

QRegExp regExp("[0-9+*/()-.]*");
    QRegExpValidator *validator =new QRegExpValidator(regExp,this);
    lineEdit->setValidator(validator);

執行效果

這裡寫圖片描述
輸入表示式,包含三位數,小數,加減乘除,括號
這裡寫圖片描述
點選圖中紅色標註 等號 進行運算。
這裡寫圖片描述
紅色標註輸入框顯示運算結果。
這裡寫圖片描述
紅色標註顯示框為計算過程。
這裡寫圖片描述
點選紅色框框標註的切換按鈕切換顯示棧中資料變化情況
這裡寫圖片描述
這裡寫圖片描述
紅色框框標註顯示棧中資料變化情況,上下兩圖分別為中綴表示式轉字尾表示式和字尾表示式運算時的棧中資料變化情況。

原始碼

程式碼有問題的地方以及說錯的地方還請指出,謝謝!

有任何不解的地方請留言,很樂意為您解答。