最近没事在学习Qt,做了一个简易计算器的小设计。记录在此,方便大家,也方便自己。
一、整体方案设计
本设计总体可分为两个部分,界面设计部分和内部逻辑部分。下面分别进行讲解。
二、界面设计部分:
界面设计入上图所示一些按钮和一个QLineEdit(用于输入数字和显示结果)和QLabel(用于显示运算表达式)。
本部分主要是采用界面设计师(直接拖拽设计)和代码设计部分。其中代码设计部分主要是为了完成图中所示的布局。具体的代码如下所示:
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this,&Widget::SendOpe,this,&Widget::RecOpe);
connect(this,&Widget::SendNum,this,&Widget::RecNum);
setFixedSize(this->width(), this->height()); //禁止改变窗口大小
//设置布局
QVBoxLayout *layout1 = new QVBoxLayout();
layout1->addWidget(ui->label_show_exp);
layout1->addWidget(ui->lineEdit) ;
QGridLayout *layout = new QGridLayout();
layout->addWidget(ui->pushButton_CE,0,0,1,1);
layout->addWidget(ui->pushButton_C,0,1,1,1);
layout->addWidget(ui->pushButton_bac,0,2,1,3);
layout->addWidget(ui->pushButton_7,1,0,1,1);
layout->addWidget(ui->pushButton_8,1,1,1,1);
layout->addWidget(ui->pushButton_9,1,2,1,1);
layout->addWidget(ui->pushButton_add,1,3,1,1);
layout->addWidget(ui->pushButton_lef,1,4,1,1);
layout->addWidget(ui->pushButton_4,2,0,1,1);
layout->addWidget(ui->pushButton_5,2,1,1,1);
layout->addWidget(ui->pushButton_6,2,2,1,1);
layout->addWidget(ui->pushButton_sub,2,3,1,1);
layout->addWidget(ui->pushButton_rig,2,4,1,1);
layout->addWidget(ui->pushButton_1,3,0,1,1);
layout->addWidget(ui->pushButton_2,3,1,1,1);
layout->addWidget(ui->pushButton_3,3,2,1,1);
layout->addWidget(ui->pushButton_mul,3,3,1,1);
layout->addWidget(ui->pushButton_0,4,0,1,2);
layout->addWidget(ui->pushButton_poi,4,2,1,1);
layout->addWidget(ui->pushButton_div,4,3,1,1);
layout->addWidget(ui->pushButton_equ,3,4,2,1);
layout1->addLayout(layout);
setLayout(layout1);
setWindowTitle(tr("简易计算器")); //设置程序标题
ui->lineEdit->setText("0"); //初始化
flag = 0; //括号标志
//设置背景,按钮透明等样式
SetSty();
}
代码分析:以上代码主要是完成了界面的布局。基本上外部的垂直布局里面套了个网格布局(网格布局里主要是处于下方的按钮)。
还有SetSty()函数主要是为了突出一些逼格,重新包装了一下整个程序,其实也就是设置了下背景,按钮样式之类。包装之后的界面入下图所示:
三、逻辑设计部分
逻辑设计部分主要又包括两个部分:一是保证输入正确的表达式。二是计算正确的表达式。
(1)保证输入正确的表达式。这里既包括输入数字和运算符,也包括正确输入他们的关系。
其中,前者,主要是利用按钮的信号和槽,即按下一个数字,将会触发一个槽,在这个槽中输入这个数字。具体的,在本程序中为了程序的简洁化,中间又加了一个公共的信号,这样按下任意一个数字都会触发同样的信号SendNum(int num)。这个信号有一个参数,就是对应的数字值。同理,符号也是一样。具体代码(后面粘贴的代码也有涉及)如下:
void Widget::on_pushButton_7_clicked()
{
emit SendNum(7); //发送输入数字7信号
}
void Widget::on_pushButton_add_clicked()
{
emit SendOpe('+'); //发送输入加号 信号
}
//接收输入数字信号需要进行的操作
void Widget::RecNum(int num)
{
QString input_num = ui->lineEdit->text();
if(input_num == "0")
{
ui->lineEdit->setText(QString::number(num)); //去掉前导0,并添加num
}
else
{
if(input_num[0]=='A' || input_num[0] == 'T') //上一次结果的第一个字符(An 或者An或者The)
{
ui->lineEdit->clear();
ui->label_show_exp->clear();
}
ui->lineEdit->insert(QString::number(num)); //在末尾插入数字num
}
ui->lineEdit->setFocus (); //继续追加光标
}
对于后者,再输入的时候主要有一下要考虑的:
(a)、浮点数的正确输入。(如 :“0.3…2”就不合法)
(b)、左右括号要匹配。( 如:“3+)))”) 肯定有问题)
©、 左括号前面是运算符或者左括号,后面是数字或者左括号;右括号前面是数字或者右括号,右括号后面是运算符或者右括号。(如:“((33+)、(8+2)3”等输入不正确 )
(d)、本程序每次输入运算符或者右括号时提交数字。
基本的代码如下(上面粘贴的代码也有涉及):
//接收输入运算符信号需要进行的操作
void Widget::RecOpe(QChar ch)
{
QString input_num0 = ui->lineEdit->text(); //获取输入的字符串
QString ori_exp = ui->label_show_exp->text(); //获取显示板上的字符串
if(ori_exp.isEmpty() == false) //提交字符串不为空时
{
if(input_num0.isEmpty() == true) //输入数字为空
{
int length = ori_exp.size();
if(ori_exp[length-1] != ')') //不等于右括号才能,改变输入错误的运算符号
{
ori_exp.replace(length-1,1,QString(ch));
ui->label_show_exp->setText(ori_exp);
}
else
{
ui->label_show_exp->setText(ori_exp+QString(ch)); //提交运算符
}
}
else
{
int length = ori_exp.size();
if(ori_exp[length-1] != ')') //不等于右括号才能加入运算符和数字
{
//将输入的数字和运算符提取出来,然后输出至显示板
QRegExp rx;
rx.setPattern("(\\.){0,1}0+$");
bool ok;
QString input_num = QString("%1").arg(input_num0.toDouble(&ok),0,'f',-1).replace(rx,"");
QString input_inf = input_num+QString(ch);
ui->label_show_exp->setText(ori_exp+input_inf);
}
else //只能加入运算符
{
ui->label_show_exp->setText(ori_exp+QString(ch));
}
}
ui->lineEdit->clear();
ui->lineEdit->setFocus (); //取回焦点
} //已经提交(在显示板上显示的)的字符串为空
else
{
if(input_num0.isEmpty() == false)
{
QRegExp rx;
rx.setPattern("(\\.){0,1}0+$");
bool ok;
QString input_num = QString("%1").arg(input_num0.toDouble(&ok),0,'f',-1).replace(rx,"");
QString input_inf = input_num+QString(ch);
ui->label_show_exp->setText(ori_exp+input_inf);
}
ui->lineEdit->clear();
ui->lineEdit->setFocus ();
}
}
(2)、计算正确输入的表达式:本程序采用的过程是先把中序表达式转化为后续表达式,然后利用后续表达式进行计算。其中的原理网上很多,在此就不赘述了。涉及到的主要代码如下:
//获取后缀表达式
int Widget::GetPostExp(QString exp_str,QString pos_exp[])
{
int pos_exp_top = 0;
QChar opes_temp[MAXSIZE]; //存放临时符号栈
int opes_temp_top = 0;
opes_temp[opes_temp_top++] = QChar('#');
int length = exp_str.size();
for(int i = 0;i= 0)
{
QChar ch_top = opes_temp[opes_temp_top-1];
int pri_j = GetPriOfOpe(ch_top,0); //出栈优先级
if(pri_j >= pri_i)
{
--opes_temp_top; //栈递减
if(ch_top != '(')
pos_exp[pos_exp_top++] = QString(ch_top); //入后缀表达式
else
break; //右括号匹配到左括号,退出
}
else
break;
}
if(exp_str[i] != ')')
opes_temp[opes_temp_top++] = exp_str[i]; //入暂时栈
}
}
//将剩余的栈中元素装入后缀表达式
for(int i = opes_temp_top-1;i>0;--i)
pos_exp[pos_exp_top++] = QString(opes_temp[i]);
return pos_exp_top;
}
//根据字符串计算数值
double Widget::CalFromStr(QString exp_str,bool &ok)
{
QString pos_exp[MAXSIZE];
int num = GetPostExp(exp_str,pos_exp);
double nums[num];
int nums_top = 0;
ok = true; //运算成功
for(int i = 0;i
四、设计结果
基本的设计结果如上。可以进行+、-、*、/以及带括号的相关计算。并没有画太多时间在这个上面,如果有错误,还请大家指针。
具体的源码以及整个工程见:点这儿