我们知道windows系统有自带的计算器,那么我们也可以用Qt制作一款类似的个人计算器,实现整数的加减乘除括号运算,界面设计使用Qt,计算使用逆波兰算法,下面我就来分享一下个人计算器的制作方法。
一、逆波兰表达式算法介绍
逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasiewicz)于1929年首先提出的一种表达式的表示方法 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。
逆波兰表达式又叫做后缀表达式,是一种没有括号,并严格遵循“从左到右”运算的后缀式表达方法,如下表所示:
正常表达式 | 逆波兰表达式 |
---|---|
a+b | a b + |
a+(b-c)*d | a d b c - * + |
a+(b-c) | a b c - + |
a=1+3 | a = 1 3 |
a*(b+c)+d | a b c + * d + |
算法步骤
- 首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
- 读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
- 从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
- 如果不是数字,该字符则是运算符,此时需比较优先关系。
具体做法是:将该字符与运算符栈顶的运算符的优先关系相比较。如果该字符优先关 系高于此运算符栈顶的运算符,则将该运算符入栈。若不是的话,则将栈顶的运算符 从栈中弹出,直到栈项运算符的优先级低于当前运算符,将该字符入栈。
5.重复步骤1~2,直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,便可 以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
二、个人计算器实现
1.头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QDebug>
#include <QFont>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private slots:
void show_lineedit();
private:
QPushButton *pb[10], *pb1, *pb2, *pb3, *pb4, *pb5, *pb6, *pb7, *pb8;
QLineEdit *le;
};
#endif // WIDGET_H
2.源文件
#include "widget.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <QApplication>
#define MAX_LEN 30
#define MAX 100
//栈的数组实现
typedef struct
{
int data[MAX_LEN];
int top;
}Stack;
//为栈分配空间
Stack *Createstack()
{
Stack *p;
p = (Stack *)malloc(sizeof(Stack));
p->top = -1;
return p;
}
//压栈
int Push(Stack *p,int x)
{
if (p->top == MAX_LEN - 1)
{
return -1;
}
p->top++;
p->data[p->top] = x;
return 0;
}
//出栈
int Pop(Stack *L,int *x)
{
if (L->top == -1)
{
return -1;
}
//利用传出参数传出栈顶元素
*x = L->data[L->top];
L->top--;
return 0;
}
//栈顶
int TOP(Stack *L,int *x)
{
if (L->top == -1)
{
return -1;
}
*x = L->data[L->top];
return 0;
}
//判断栈是否为空
int Empty(Stack *L)
{
return (L->top == -1);
}
//定义符号的优先级
int Priority(int ope)
{
switch(ope)
{
case '(': return 0; //左括号已经在栈内时,如果比较,其优先级最低
case '+':
case '-': return 1;
case '*':
case '%':
case '/': return 2;
case '^': return 3;
default : return -1;
}
}
// 将两个数出栈、根据ope符号计算,然后再次入栈
void Calculation(Stack *snum,int ope)
{
int n,n1,n2;
Pop(snum,&n1);
Pop(snum,&n2);
switch(ope)
{
case '+': n = n1 + n2; break;
case '-': n = n2 - n1; break;
case '*': n = n1 * n2; break;
case '/': n = n2 / n1; break;
//case '%': n = n2 % n1; break;
//case '^': n = pow(n2,n1);break;
}
Push(snum,n);
}
// 先处理除右括号外的符号
void Deal_ope(Stack *snum,Stack *sope,int ope)
{
int old_ope;
if (Empty(sope) || ope == '(')
{
Push(sope,ope);
return ;
}
TOP(sope,&old_ope);
if (Priority(ope) > Priority(old_ope))
{
//传入符号大于当前栈顶,则将传入符号入栈
Push(sope,ope);
return ;
}
//如果传入的符号优先级小于当前栈顶符号
while (Priority(ope) <= Priority(old_ope))
{
//将当前栈顶的符号取出与数字栈中顶端的两个数字进行计算
Pop(sope,&old_ope);
printf("%c",old_ope);
Calculation(snum,old_ope);
if (Empty(sope))
{
break;
}
//再次取出一个当前栈符号与传入符号比较,循环
TOP(sope,&old_ope);
}
Push(sope,ope);
}
//单独处理右括号
void Right(Stack *snum,Stack *sope)
{
int old_ope;
TOP(sope,&old_ope);
while (old_ope != '(')
{
//当前符号出栈然后将数字出栈两个进行计算,在括号内优先级最高
Pop(sope,&old_ope);
printf("%c",old_ope);
Calculation(snum,old_ope);
//循环
TOP(sope,&old_ope);
}
Pop(sope,&old_ope);//出现左括号时将它丢弃
}
// 打印数字栈
void Display(Stack *L)
{
int i;
if (L->top == -1)
{
return ;
}
for (i = 0 ; i <= L->top; i++)
{
printf("%d",L->data[i]);
}
printf("\n");
}
//打印符号栈
void Displayope(Stack *L)
{
int i;
if (L->top == -1)
{
return ;
}
for (i = 0 ; i <= L->top; i++)
{
printf("%c",L->data[i]);
}
printf("\n");
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
le = new QLineEdit;
QFont font("Times", 20, QFont::Bold);
this->setFont(font);
le->setAlignment(Qt::AlignRight);
int i = 0, row = 1, col = 0;
for(i = 0; i < 10; i++)
{
pb[i] = new QPushButton(QString::number(i));
}
pb1 = new QPushButton("+");
pb2 = new QPushButton("-");
pb3 = new QPushButton("*");
pb4 = new QPushButton("/");
pb5 = new QPushButton(")");
pb6 = new QPushButton("c");
pb7 = new QPushButton("=");
pb8 = new QPushButton("(");
QGridLayout *gbox = new QGridLayout;
for(i = 1; i < 10; i++)
{
gbox->addWidget(pb[i], row, col, 1, 1);
col++;
if(col == 3)
{
row++;
col = 0;
}
}
gbox->addWidget(pb[0], 4, 1, 1, 1);
gbox->addWidget(pb1, 3, 3, 1, 1);
gbox->addWidget(pb2, 2, 3, 1, 1);
gbox->addWidget(pb3, 1, 3, 1, 1);
gbox->addWidget(pb4, 0, 3, 1, 1);
gbox->addWidget(pb5, 4, 2, 1, 1);
gbox->addWidget(pb6, 0, 0, 1, 1);
gbox->addWidget(pb7, 4, 3, 1, 1);
gbox->addWidget(pb8, 4, 0, 1, 1);
gbox->addWidget(le, 0, 1, 1, 2);
setLayout(gbox);
for(i = 0; i < 10; i++)
{
connect(pb[i], SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
}
connect(pb1, SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
connect(pb2, SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
connect(pb3, SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
connect(pb4, SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
connect(pb5, SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
connect(pb6, SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
connect(pb7, SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
connect(pb8, SIGNAL(clicked(bool)), this, SLOT(show_lineedit()));
}
Widget::~Widget()
{
}
void Widget::show_lineedit()
{
QPushButton *pb = static_cast<QPushButton *>(sender());
static QString count_str;
if(pb == pb6)
{
count_str.clear();
le->clear();
return;
}
QString buf = pb->text();
count_str += buf;
le->setText(count_str);
if(pb == pb7)
{
char str[MAX];
char* ch;
QByteArray ba = le->text().toLatin1(); // must
ch=ba.data();
strcpy_s(str, ch);
str[strlen(str)-1] = '\0';
printf("str = %s\n", str);
int i = 0,value = 0,flag = 0;
int old_ope;
Stack *numstack,*opestack;
numstack = Createstack(); // 创建存放数字的栈
opestack = Createstack(); // 创建存放运算符的栈
while (str[i] != '\0')
{
if (str[i] >= '0' && str[i] <= '9')
{
value *= 10; //数字的获取,注意可能不止一位
value +=str[i]-'0';
flag = 1;
}
else
{
if (flag) //flag = 1说明value里面存储了数字,将其入栈
{
printf("%d",value);
Push (numstack, value);
//flag标志清零,value存放数字的变量清零
flag = 0;
value = 0;
}
if(str[i] == ')')
{
Right(numstack,opestack);
}
else
{
Deal_ope(numstack,opestack,str[i]);
}
}
i++;
}
if (flag) //如果flag = 1.说明value里面还有数值,将其入栈
{
printf("%d",value);
Push(numstack,value);
}
while (!Empty(opestack)) //如果符号栈不为空,继续计算
{
Pop(opestack,&old_ope);
printf("%c",old_ope);
Calculation(numstack,old_ope);
}
Pop(numstack,&value); //最终答案
printf("\n%d\n",value);
count_str.clear();
le->clear();
le->setText(QString::number(value)); //数值数据显示在行编辑器上
return;
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
3.结果展示