写在前边:这篇文章只是来带着大家一起实现一个基于Qt的计算器。中间会出现很多概念,我们不做拓展介绍,大家只要知道怎么用就好,如果有需要我们后续再详细的对每个模块解析。
1.首先介绍一下Qt,尽管大家再明白不过了。Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。是一个面向对象的框架。由于其良好的跨平台性以及适当的封装,使得它成为很多C++程序员(当然,QT发展到现在已经是一个很完备的IDE,支持java以及安卓等)写界面的首选。
2.signal&slot机制。也就是Qt独创的信号和槽的机制。这种机制其实在现在是很常用的,但是现在很多用来完成界面的语言已经把这一机制封装的十分简洁,用起来就十分的方便。至于这个机制的作用我们就不再详解,这个机制的作用我们就不再继续的深入讨论了,现在我们只需要知道的就是他是用来给按钮添加事件的。什么?什么叫做给按钮添加时间?给按钮添加时间值得是点击按钮要做的事情,比如说我们点击删除按钮,就会删除文件,就是这个意思。
3.信号的槽的实现原理:connect函数/直接转到槽函数。很多同学上来就是无法理解什么叫做槽什么叫做信号。槽其实很简单,槽就是函数。而且是一类很特殊的函数,之所以说它特殊,是因为它的很多东西都是固定的:它的功能就是为了处理对应的信号,它的返回类型就是void,它只能被这样声明:public slots/private slots/protected/slots。信号就是按钮的想要说的话,例如,我们假如有一个按钮被点击,他就会发射一个我被点击了的信号,这个信号被槽接收,槽也就是这个函数做出处理,例如,如果这个槽接收到被点击的信号他可能会说一句话:哪个胆子大的敢点击你,老子去安排他。然而,这个槽该如何接收信号呢?信号发射出去但是槽接收不到啊,这个时候connect就出现了。connect函数是将信号和槽链接起来的一个重要的中间支持。connect有四个参数我们给出这个函数调用时的一般形式:connect(a,b,c,d)。这四个参数的意义也是很好记的,第一个是信号发射的对象,第二个是信号的类型,第三个是当前接收信号的对象,第四个是对象的槽函数,也就是槽。没看懂?OK我们来个栗子解释一下:
connect(ui->first,SIGNAL(clicked(bool)),this,SLOT(button_1_clicked()));对于这个connect我们来详细的解释一下。
首先是第一个参数,说明我们的UI文件里面有一个叫做first的按钮,第二个参数说明这个按钮发射出来的信号是被点击(注意:这里的信号是系统的内置信号,就是说不用你自己定义,直接抄上去就可以用的。当然也可以自定义按钮的信号,这里我们就不在深入的探讨了,这里用不到)。第三个参数我们也不用管,直接写上this就好,它表示当前对象接收信号,第四个就是我们的槽函数了。这个函数是由我们自己定义的一个函数。这样就可以完成了按钮和它对应的处理函数的链接了。当然了,这看起来可能有点复杂,也有一种简单的方法,就是在可视化布局的时候布局按钮的时候我们可以直接找到这个按钮的属性,然后转到槽,这样即使不使用connect也是可以的。
有了上面的知识我们就来实现一下计算器。我们首先分析一下这个问题:我们需要在输入框中获取当前用户输入的字符串,然后再设计函数实现对这个字符串的计算之后再输出到文本显示框。那么其实我们程序的模块已经很清楚了:数据获取&数据处理。 4.数据获取的实现:这个模块要求我们实时的将按钮点击对应的字符加到原字符串上并显示出来,那么我们首先需要一个字符串,这个字符串被所有的函数共享,所以我们把它定义为类的一个私有的成员变量。我们来具体分析一个:如果我点击代表数字1的按钮,那么当前字符串就需要加上1,也就是执行s+="1",之后再显示到文本框内。也就是set这个文本框内的值。然后对每个按钮做这样的映射,就可以完成表达式的显示。
5.获取完字符串之后,我们需要定义相应的处理函数计算表达式的值。这是一个经典的算法问题,中缀表达式的计算,常用的方法就是首先转为后缀表达式然后再计算后缀表达式,这会使用一种数据结构:栈。这个算法的实现我就不再详细的说了,现在VS里面把算法弄好再移植到Qt,因为VS调试功能十分强大,要是在QT里面直接写,会出现很多难受的错误,又不好调试。
6.ui文件的设计,大佬们都是不需要可视化的操作的,他们都是直接写代码来完成界面的设计,而我还是需要来回的拖控件,布局界面。ui文件的设计是一个很重要的模块,建议给每一个控件一个名字,这个名字可以反映它的功能,并且具有相同的命名规则,例如首字母大写啊什么的,这会在写后台的处理函数,也就是槽函数的时候带来很大的帮助,因为你不用一直去翻UI来看这个控件的名称。我的UI就不附上了,大家可以在connect函数中看到每个控件的名称,我会把这个界面的XML文件附上。
7.具体代码:
.h:
#ifndef CAL_H
#define CAL_H
#include<QString>
#include <QMainWindow>
#include<QStringList>
namespace Ui {
class cal;
}
class cal : public QMainWindow
{
Q_OBJECT
public:
explicit cal(QWidget *parent = 0);
~cal();
private:
QString s;
Ui::cal *ui;
private slots:
void button_0_clicked();
void button_1_clicked();
void button_2_clicked();
void button_3_clicked();
void button_4_clicked();
void button_5_clicked();
void button_6_clicked();
void button_7_clicked();
void button_8_clicked();
void button_9_clicked();
void button_ac_clicked();
void button_result_clicked();
void button_mul_clicked();
void button_devide_clicked();
void button_add_clicked();
void button_sub_clicked();
void button_sin_clicked();
void button_cos_clicked();
void button_tan_clicked();
void button_sqrt_clicked();
void button_pow_clicked();
void button_ln_clicked();
void button_log_clicked();
void button_point_clicked();
void button_mod_clicked();
void button_AC_clicked();
void button_left_clicked();
void button_right_clicked();
void Cal5();
private:
QByteArray Cal2();
double Cal3(double a,char op,double b);
double Cal4(char op,double a);
QStringList stackChange;
QStringList calculateProcess;
bool flag;
bool buttonFlag;
int pri[256];
bool one[256];
};
#endif // CAL_H
.cpp:
#include "cal.h"
#include "ui_cal.h"
#include<QString>
#include<QStack>
#include<QDebug>
#include<QColor>
#include<QMap>
#include<QByteArray>
#include<qmath.h>
cal::cal(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::cal)
{
ui->setupUi(this);
connect(ui->first,SIGNAL(clicked(bool)),this,SLOT(button_1_clicked()));
connect(ui->zero,SIGNAL(clicked(bool)),this,SLOT(button_0_clicked()));
connect(ui->second,SIGNAL(clicked(bool)),this,SLOT(button_2_clicked()));
connect(ui->third,SIGNAL(clicked(bool)),this,SLOT(button_3_clicked()));
connect(ui->four,SIGNAL(clicked(bool)),this,SLOT(button_4_clicked()));
connect(ui->five,SIGNAL(clicked(bool)),this,SLOT(button_5_clicked()));
connect(ui->six,SIGNAL(clicked(bool)),this,SLOT(button_6_clicked()));
connect(ui->seven,SIGNAL(clicked(bool)),this,SLOT(button_7_clicked()));
connect(ui->eight,SIGNAL(clicked(bool)),this,SLOT(button_8_clicked()));
connect(ui->nine,SIGNAL(clicked(bool)),this,SLOT(button_9_clicked()));
connect(ui->add,SIGNAL(clicked(bool)),this,SLOT(button_add_clicked()));
connect(ui->sub,SIGNAL(clicked(bool)),this,SLOT(button_sub_clicked()));
connect(ui->mul,SIGNAL(clicked(bool)),this,SLOT(button_mul_clicked()));
connect(ui->Divide,SIGNAL(clicked(bool)),this,SLOT(button_devide_clicked()));
connect(ui->Delete,SIGNAL(clicked(bool)),this,SLOT(button_ac_clicked()));
// connect(ui->Cal,SIGNAL(clicked(bool)),this,SLOT(button_result_clicked()));
connect(ui->sin,SIGNAL(clicked(bool)),this,SLOT(button_sin_clicked()));
connect(ui->cos,SIGNAL(clicked(bool)),this,SLOT(button_cos_clicked()));
connect(ui->tan,SIGNAL(clicked(bool)),this,SLOT(button_tan_clicked()));
// connect(ui->ln,SIGNAL(clicked(bool)),this,SLOT(button_ln_clicked()));
connect(ui->log,SIGNAL(clicked(bool)),this,SLOT(button_log_clicked()));
connect(ui->pow,SIGNAL(clicked(bool)),this,SLOT(button_pow_clicked()));
//connect(ui->Cal,SIGNAL(clicked(bool)),this,SLOT(Cal()));
connect(ui->point,SIGNAL(clicked(bool)),this,SLOT(button_point_clicked()));
connect(ui->AC,SIGNAL(clicked(bool)),this,SLOT(button_AC_clicked()));
connect(ui->left,SIGNAL(clicked(bool)),this,SLOT(button_left_clicked()));
connect(ui->right,SIGNAL(clicked(bool)),this,SLOT(button_right_clicked()));
connect(ui->Cal,SIGNAL(clicked(bool)),this,SLOT(Cal5()));
connect(ui->sqrt,SIGNAL(clicked(bool)),this,SLOT(button_sqrt_clicked()));
connect(ui->mod,SIGNAL(clicked(bool)),this,SLOT(button_mod_clicked()));
connect(ui->ln,SIGNAL(clicked(bool)),this,SLOT(button_ln_clicked()));
QPalette pal = this->palette();
pal.setColor(QPalette::Window,QColor(120,120,120));
this->setPalette(pal);
ui->lineEdit->setPalette(pal);
ui->lineEdit->setStyleSheet("font-size:40px;border-width:0;");
ui->lineEdit->setAlignment(Qt::AlignRight);
}
cal::~cal()
{
delete ui;
}
void cal::button_0_clicked()
{
s+="0";
ui->lineEdit->setText(s);
}
void cal::button_1_clicked()
{
s+="1";
ui->lineEdit->setText(s);
}
void cal::button_2_clicked()
{
s+="2";
ui->lineEdit->setText(s);
}
void cal::button_3_clicked()
{
s+="3";
ui->lineEdit->setText(s);
}
void cal::button_4_clicked()
{
s+="4";
ui->lineEdit->setText(s);
}
void cal::button_5_clicked()
{
s+="5";
ui->lineEdit->setText(s);
}
void cal::button_6_clicked()
{
s+="6";
ui->lineEdit->setText(s);
}
void cal::button_7_clicked()
{
s+="7";
ui->lineEdit->setText(s);
}
void cal::button_8_clicked()
{
s+="8";
ui->lineEdit->setText(s);
}
void cal::button_9_clicked()
{
s+="9";
ui->lineEdit->setText(s);
}
void cal::button_add_clicked()
{
s+="+";
ui->lineEdit->setText(s);
}
void cal::button_sub_clicked()
{
s+="-";
ui->lineEdit->setText(s);
}
void cal::button_mul_clicked()
{
s+="*";
ui->lineEdit->setText(s);
}
void cal::button_devide_clicked()
{
s+="/";
ui->lineEdit->setText(s);
}
void cal::button_result_clicked()
{
ui->lineEdit->setText(s);
}
void cal::button_ac_clicked()
{
s="";
ui->lineEdit->setText(s);
}
void cal::button_sin_clicked()
{
s+="sin";
ui->lineEdit->setText(s);
}
void cal::button_cos_clicked()
{
s+="cos";
ui->lineEdit->setText(s);
}
void cal::button_tan_clicked()
{
s="tan";
ui->lineEdit->setText(s);
}
void cal::button_sqrt_clicked()
{
s+="Sqrt";
ui->lineEdit->setText(s);
}
void cal::button_pow_clicked()
{
s+="^";
ui->lineEdit->setText(s);
}
void cal::button_log_clicked()
{
s+="log";
ui->lineEdit->setText(s);
}
void cal::button_ln_clicked()
{
s+="ln";
ui->lineEdit->setText(s);
}
void cal::button_point_clicked()
{
s+=".";
ui->lineEdit->setText(s);
}
void cal::button_left_clicked()
{
s+="(";
ui->lineEdit->setText(s);
}
void cal::button_right_clicked()
{
s+=")";
ui->lineEdit->setText(s);
}
void cal::button_mod_clicked()
{
s+="%";
ui->lineEdit->setText(s);
}
void cal::button_AC_clicked()
{
ui->lineEdit->backspace();
}
QByteArray cal::Cal2()
{
pri['+']=1;one['+']=false;
pri['-']=1;one['-']=false;
pri['*']=2;one['*']=false;
pri['/']=2;one['/']=false;
pri['%']=2;one['%']=false;
pri['^']=3;one['^']=false;
pri['@']=3;one['@']=true;
pri['s']=3;one['s']=true;
pri['c']=3;one['c']=true;
pri['t']=3;one['t']=true;
pri['l']=3;one['l']=true;
pri['p']=3;one['p']=true;
pri['S']=3;one['S']=true;
pri['L']=3;one['L']=true;
QString in2=this->s;
QByteArray in=in2.toLocal8Bit();
QByteArray out;
QStack<char>op;
for(int i=0;i<in.size();i++)
{
if(in[i]>='0'&&in[i]<='9')out.push_back(in[i]);
else if(in[i]=='(')op.push(in[i]);
else if(in[i]==')')
{
while(op.top()!='(')
{
out.push_back(op.top());
op.pop();
}
op.pop();
}
else if(one[in[i]]||(in[i]=='-'&&(i==0||!isdigit(in[i-1]))))
{
if(in[i]=='-')
{
op.push('@');
}
else if(in[i]=='s')
{
op.push('s');
i+=2;
}
else if(in[i]=='c')
{
op.push('c');
i+=2;
}
else if(in[i]=='t')
{
op.push('t');
i+=2;
}
else if(in[i]=='l')
{
op.push('l');
i++;
}
else if(in[i]=='S')
{
op.push('S');
i+=3;
}
else if(in[i]=='L')
{
op.push('L');
i+=2;
}
else op.push(in[i]);
}
else
{
while((!op.empty())&&pri[op.top()]>=pri[in[i]])
{
//操作符栈内优先级高
out.push_back(op.top());
op.pop();
}
op.push(in[i]);
}
}
while(!op.empty())
{
out.push_back(op.top());
op.pop();
}
return out;
}
double cal::Cal3(double a, char op, double b)
{
switch (op){
case '+':return a+b;
case '-':return a-b;
case '*':return a*b;
case '/':return a/b;
case '%':return (int)a%(int)b;
case '^':return qPow(a,b);
}
}
double cal::Cal4(char op, double a)
{
if(op=='@')return -a;
else if(op=='s')return qSin(a);
else if(op=='c')return qCos(a);
else if(op=='t')return qTan(a);
else if(op=='S')return qSqrt(a);
else if(op=='l')return qLn(a);
else if(op=='L') return (qLn(a)/qLn(10));
}
void cal::Cal5()
{
QByteArray in=this->Cal2();
QStack<double>num;
for(int i=0;i<in.size();i++){
if(in[i]>='0'&&in[i]<='9')num.push(in[i]-'0');
else if(one[in[i]]){
double a=num.top();num.pop();
num.push(Cal4(in[i],a));
}else{
double a=num.top();num.pop();
double b=num.top();num.pop();
num.push(Cal3(b,in[i],a));
}
}
double a=num.top();
QString tem=QString::number(a,10,5);
ui->lineEdit->setText(tem);
}
main函数:
#include "cal.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
cal w;
w.show();
return a.exec();
}
XML文件:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>cal</class>
<widget class="QMainWindow" name="cal">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>352</width>
<height>464</height>
</rect>
</property>
<property name="windowTitle">
<string>cal</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QLineEdit" name="lineEdit">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>321</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>0.00</string>
</property>
</widget>
<widget class="QPushButton" name="mod">
<property name="geometry">
<rect>
<x>11</x>
<y>46</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>%</string>
</property>
</widget>
<widget class="QPushButton" name="sqrt">
<property name="geometry">
<rect>
<x>96</x>
<y>46</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Sqrt</string>
</property>
</widget>
<widget class="QPushButton" name="left">
<property name="geometry">
<rect>
<x>266</x>
<y>46</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>(</string>
</property>
</widget>
<widget class="QPushButton" name="pow">
<property name="geometry">
<rect>
<x>181</x>
<y>46</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>^</string>
</property>
</widget>
<widget class="QPushButton" name="right">
<property name="geometry">
<rect>
<x>11</x>
<y>99</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>)</string>
</property>
</widget>
<widget class="QPushButton" name="AC">
<property name="geometry">
<rect>
<x>96</x>
<y>99</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>AC</string>
</property>
</widget>
<widget class="QPushButton" name="Delete">
<property name="geometry">
<rect>
<x>181</x>
<y>99</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>CLEAR</string>
</property>
</widget>
<widget class="QPushButton" name="Divide">
<property name="geometry">
<rect>
<x>266</x>
<y>99</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>/</string>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
<property name="autoRepeatInterval">
<number>200</number>
</property>
</widget>
<widget class="QPushButton" name="second">
<property name="geometry">
<rect>
<x>96</x>
<y>152</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>2</string>
</property>
</widget>
<widget class="QPushButton" name="first">
<property name="geometry">
<rect>
<x>11</x>
<y>152</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>1</string>
</property>
</widget>
<widget class="QPushButton" name="mul">
<property name="geometry">
<rect>
<x>266</x>
<y>152</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>*</string>
</property>
</widget>
<widget class="QPushButton" name="third">
<property name="geometry">
<rect>
<x>181</x>
<y>152</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>3</string>
</property>
</widget>
<widget class="QPushButton" name="six">
<property name="geometry">
<rect>
<x>181</x>
<y>205</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>6</string>
</property>
</widget>
<widget class="QPushButton" name="four">
<property name="geometry">
<rect>
<x>11</x>
<y>205</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>4</string>
</property>
</widget>
<widget class="QPushButton" name="five">
<property name="geometry">
<rect>
<x>96</x>
<y>205</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>5</string>
</property>
</widget>
<widget class="QPushButton" name="sub">
<property name="geometry">
<rect>
<x>266</x>
<y>205</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
<widget class="QPushButton" name="seven">
<property name="geometry">
<rect>
<x>11</x>
<y>258</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>7</string>
</property>
</widget>
<widget class="QPushButton" name="add">
<property name="geometry">
<rect>
<x>266</x>
<y>258</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
<widget class="QPushButton" name="eight">
<property name="geometry">
<rect>
<x>96</x>
<y>258</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>8</string>
</property>
</widget>
<widget class="QPushButton" name="nine">
<property name="geometry">
<rect>
<x>181</x>
<y>258</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>9</string>
</property>
</widget>
<widget class="QPushButton" name="cos">
<property name="geometry">
<rect>
<x>96</x>
<y>311</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>cos</string>
</property>
</widget>
<widget class="QPushButton" name="sin">
<property name="geometry">
<rect>
<x>11</x>
<y>311</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>sin</string>
</property>
</widget>
<widget class="QPushButton" name="tan">
<property name="geometry">
<rect>
<x>181</x>
<y>311</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>tan</string>
</property>
</widget>
<widget class="QPushButton" name="Cal">
<property name="geometry">
<rect>
<x>266</x>
<y>311</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>=</string>
</property>
</widget>
<widget class="QPushButton" name="point">
<property name="geometry">
<rect>
<x>181</x>
<y>364</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>.</string>
</property>
</widget>
<widget class="QPushButton" name="ln">
<property name="geometry">
<rect>
<x>11</x>
<y>364</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>ln</string>
</property>
</widget>
<widget class="QPushButton" name="log">
<property name="geometry">
<rect>
<x>96</x>
<y>364</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>log</string>
</property>
</widget>
<widget class="QPushButton" name="zero">
<property name="geometry">
<rect>
<x>266</x>
<y>364</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>352</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>