第一个Qt程序--计算器,对其做如下介绍
一、功能:
1.多项相加
2.一次计算结束后,计算结果可以继续运算
3.对+,-是作为符号还是运算符的判断
4.清零
随时可以清零重新计算
5.按下一个数字后,再按下一个或不按运算符,最后按下等号时,计算结果为这个数字本身。
但是按下一个数字后,按了多个运算符,结果为"Error"
二、实现:
如果只是写两个数进行运算的,其算法很简单,但是对于多项相加的运算,逻辑较复杂一些。核心的计算逻辑主要是用了栈的思想,将多项表达式作两次变化计算结果,即中缀表达式转换为后缀表达式。
其中还有一些较为复杂的逻辑判断,例如判断+,-是作为符号还是运算符等
做了背景,每个按钮做一个小icon,素材是网上下载的图片
背景的实现方法是Qpalette 的setBrush方法
icon是Button的seticon方法
代码如下,大部分已标注里注释
Widget.h文件如下
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QString>
#include <QStack>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
//三个全局变量
static QString formula;
static QString tempnum;
static QString lastres;
//初始化
void Init();
QStack<QString> stk;
QStringList list;
private slots:
void onNumBntClicked(int);
void onSignBntClicked(QString);
void onEqualBntClicked(bool);
void onClearBntClicked(bool);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
Widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QString>
#include <QSignalMapper>
#include <QStack>
#include <QStringList>
#include <qDebug>
#include <QtMath>
//2.17 1.30 完成基础功能
//2.18 1:50 完成所有功能 还需布置GUI
//2.18 14.28 完成
//检查字符串是否为纯数字,带有符号,小数点
bool Check(QString temp)
{
int count = 0;
if( !(temp.at(0)=='.' || temp.at(0)=='+' || temp.at(0)=='-' || (temp.at(0)>='0' && temp.at(0)<='9')) )
return false;
for(int i=1;i<temp.size();i++)
{
if(temp[i] == '.')
{
count++;
if(count>=2)
return false;
}
else if(!(temp.at(i)>='0' && temp.at(i)<='9' ) )
return false;
}
return true;
}
//静态成员初始化
QString Widget::formula="";
QString Widget::tempnum="";
QString Widget::lastres="";
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
Init();
}
Widget::~Widget()
{
delete ui;
}
void Widget::Init()
{
//给按钮添加图标
QIcon icon1,icon2,icon3;
icon1.addFile(tr("F:\\C++ Qt\\sl1.png"));
icon2.addFile(tr("F:\\C++ Qt\\sl2.png"));
icon3.addFile(tr("F:\\C++ Qt\\sl3.png"));
ui->bnt_1->setIcon(icon1);
ui->bnt_2->setIcon(icon1);
ui->bnt_3->setIcon(icon1);
ui->bnt_4->setIcon(icon1);
ui->bnt_5->setIcon(icon1);
ui->bnt_6->setIcon(icon1);
ui->bnt_7->setIcon(icon1);
ui->bnt_8->setIcon(icon1);
ui->bnt_9->setIcon(icon1);
ui->bnt_0->setIcon(icon2);
ui->bnt_left->setIcon(icon2);
ui->bnt_right->setIcon(icon2);
ui->bnt_point->setIcon(icon2);
ui->bnt_clear->setIcon(icon2);
ui->bnt_equal->setIcon(icon2);
ui->bnt_plus->setIcon(icon3);
ui->bnt_minus->setIcon(icon3);
ui->bnt_multiply->setIcon(icon3);
ui->bnt_divided->setIcon(icon3);
ui->bnt_exp->setIcon(icon3);
QString sign[7]={"+","-","*","/","(",")","^"}; //运算符数组
QSignalMapper *numsignal = new QSignalMapper(this); //数字信号发射器,按不同的数字按钮处理结果不同,其中包括小数点
QSignalMapper *signsignal = new QSignalMapper(this); //符号信号发射器,按不同的符号按钮处理结果不同
QButtonGroup *numgroup = new QButtonGroup(this); //数字按钮组,储存0~9数字按钮
QButtonGroup *signgroup = new QButtonGroup(this); //符号按钮组,储存'+','-','*','/','(',')','^'.
//将数字按钮插入数字按钮组
numgroup->addButton(ui->bnt_0,0);
numgroup->addButton(ui->bnt_1,1);
numgroup->addButton(ui->bnt_2,2);
numgroup->addButton(ui->bnt_3,3);
numgroup->addButton(ui->bnt_4,4);
numgroup->addButton(ui->bnt_5,5);
numgroup->addButton(ui->bnt_6,6);
numgroup->addButton(ui->bnt_7,7);
numgroup->addButton(ui->bnt_8,8);
numgroup->addButton(ui->bnt_9,9);
numgroup->addButton(ui->bnt_point,10);
//将符号按钮插入符号按钮组
signgroup->addButton(ui->bnt_plus,1);
signgroup->addButton(ui->bnt_minus,2);
signgroup->addButton(ui->bnt_multiply,3);
signgroup->addButton(ui->bnt_divided,4);
signgroup->addButton(ui->bnt_left,5);
signgroup->addButton(ui->bnt_right,6);
signgroup->addButton(ui->bnt_exp,7);
//将数字按钮信号与信号发射器相连接
for(int i=0;i<11;i++)
{
QObject::connect(numgroup->button(i),SIGNAL(pressed()),numsignal,SLOT(map()));
numsignal->setMapping(numgroup->button(i),i);
}
QObject::connect(numsignal,SIGNAL(mapped(int)),this,SLOT(onNumBntClicked(int))); //信号发射器与槽连接
//将符号按钮信号与信号发射器相连接
for(int i=1;i<8;i++)
{
QObject::connect(signgroup->button(i),SIGNAL(pressed()),signsignal,SLOT(map()));
signsignal->setMapping(signgroup->button(i),sign[i-1]);
}
QObject::connect(signsignal,SIGNAL(mapped(QString)),this,SLOT(onSignBntClicked(QString))); //信号发射器与槽连接
QObject::connect(ui->bnt_equal,SIGNAL(clicked(bool)),this,SLOT(onEqualBntClicked(bool)));//等号按钮,槽与信号的链接
QObject::connect(ui->bnt_clear,SIGNAL(clicked(bool)),this,SLOT(onClearBntClicked(bool)));//清除按钮,槽与信号的链接
}
void Widget::onNumBntClicked(int a) //按下数字按钮
{
lastres.clear();
if(a>=0 && a<=9) //按下的是数点
{
formula += QString::number(a);
tempnum += QString::number(a);
}
else //按下的是小数点
{
tempnum += ".";
formula += ".";
}
ui->output->setText(formula);
}
void Widget::onSignBntClicked(QString tempsign) //按下符号按钮
{
if(formula.size()==0 && lastres != "" && lastres != "Error") //如果直接按了运算符,上一次计算结果不为空
{
list<<lastres;
formula += lastres;
} /* eg:先计算了1+2按下等号后,直接又按了*3,此时再按等号,结果应为9 */
formula += tempsign;
if(tempnum != "") //可能会有连续输入运算符的可能,如 2*(1+2),(-2),此时tempnum为空,不输入进list
list<<tempnum;
if( lastres == "" && tempnum=="" && (tempsign=="+" || tempsign=="-") ) //+,-为数字的符号时的情况
tempnum += tempsign;
else //否者 +,-为运算符
{
if(stk.isEmpty() || tempsign=="(") //栈为空或运算符为(,直接入栈
stk.push(tempsign);
else if(stk.top()=="(" && (tempsign =="+"||tempsign=="-") && tempnum=="" ) //在括号里输入了一个负数或正数
tempnum += tempsign; /* eg: 1+(-2)*3 输入"-"时,此时栈顶比为(,且tempnum为空 */
else if(stk.top()=="(" && tempsign!=")") //栈顶是(,且输入不是),直接入栈
stk.push(tempsign);
else if(tempsign == ")") //输入时 ) ,默认前面必有(
{
while(stk.top()<"(" || stk.top()>")") //如果栈顶不为( 或 ) ,有)的情况是多重括号
{
QString temp = stk.pop(); //栈顶元素出栈,进入list
list<<temp;
}
stk.pop();
}
//不同级别运算符进出栈规则 :将栈内级别大于等于 输入运算符 的运算符输入到list 如果栈为空则停止,最后将输入符号入栈
else if( tempsign=="+" || tempsign=="-" )
{
while(!stk.isEmpty() && stk.top() != "(" ) //一定要先判断栈是否为空,否则如果栈为空了,stk.pop(),stk.top()调用了空指针
{
QString temp = stk.pop();
list<<temp;
}
stk.push(tempsign);
}
else if( tempsign=="*" || tempsign=="/" )
{
while(!stk.isEmpty() && (stk.top()=="*"||stk.top()=="/"||stk.top()=="^"))
{
QString temp = stk.pop();
list<<temp;
}
stk.push(tempsign);
}
else if(tempsign=="^" )
{
while(!stk.isEmpty() && stk.top() == "^")
{
QString temp = stk.pop();
list<<temp;
}
stk.push(tempsign);
}
else
stk.push(tempsign);
}
if(!(tempnum == "+" || tempnum == "-") && tempres!="") //如果tempnum中的只有+,- 表示当做符号,不清楚
tempnum.clear();
ui->output->setText(formula);
}
void Widget::onEqualBntClicked(bool) //按下等号按钮
{
if(tempnum != "") //运算符个数为偶数的情况
list<<tempnum; /* eg: 1+2+3 在符号按钮里3没有输入到list*/
while(!stk.isEmpty()) //栈内可能会有多余的运算符,输入到list
{
QString temp = stk.pop();
list<<temp;
}
QString tempres; //一次运算的结果
if(list.size()!=0)
{
QList<QString>::Iterator it = list.begin();
if(list.size() <= 2 && *it>="0" && *it<="9" ) // 只按下一个数字,没有或只按了一个运算符,或只按了运算符
tempres = *it; //结果为该数字
else if( !( Check(*it) && Check(*(it+1)) ) ) //前两个字符串不为数字,必然是多按了运算符,报错
tempres = "Error";
else
{
//规则:碰到数字直接入栈;碰到运算符,让栈顶前两个数字做运算,后栈顶元素在运算符前;
// 将运算结果再次压入栈内
for(it=list.begin();it!=list.end();it++) //遍历list
{
if(list.startsWith("+"))
{
QString temp1 = stk.pop();
QString temp2 = stk.pop();
stk.push( QString::number(temp2.toDouble() + temp1.toDouble()) );
}
else if(list.startsWith("-"))
{
QString temp1 = stk.pop();
QString temp2 = stk.pop();
stk.push( QString::number(temp2.toDouble() - temp1.toDouble()) );
}
else if(list.startsWith("*"))
{
QString temp1 = stk.pop();
QString temp2 = stk.pop();
stk.push( QString::number(temp2.toDouble() * temp1.toDouble()) );
}
else if(list.startsWith("/"))
{
QString temp1 = stk.pop();
QString temp2 = stk.pop();
stk.push( QString::number(temp2.toDouble() / temp1.toDouble()) );
}
else if(list.startsWith("^"))
{
QString temp1 = stk.pop();
QString temp2 = stk.pop();
stk.push(QString::number(qPow(temp2.toDouble(),temp1.toDouble())));
}
else
{stk.push(*it);}
list.removeFirst();
}
tempres = stk.pop(); //最后的栈顶元素为最后计算结果
}
ui->output_2->setText(formula);
ui->output->setText(tempres);
lastres = tempres; //此次计算结果存入lastres
formula.clear();
tempnum.clear();
list.clear();
}
}
void Widget::onClearBntClicked(bool)
{
formula.clear();
tempnum.clear();
list.clear();
stk.clear();
lastres.clear();
ui->output_2->setText("");
ui->output->setText("");
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
//添加背景
w.setAutoFillBackground(true);
QPalette palette;
palette.setBrush(QPalette::Background,QBrush(QPixmap("F:\\C++ Qt\\background1.jpg")));
w.setPalette(palette);
//题目
w.setWindowTitle("计算器");
w.show();
return a.exec();
}