Visual Studio 2012 MFC计算器教程
大家好!我是TC王者。首次做教程,希望大家勿喷。。。。。
源码下载地址:http://download.csdn.net/detail/tc00tc/6948977
本次教程主要讲VS2012 MFC制作可视化四则计算器,包括很多细小问题,比如MFC中的类型转换,字符串操作(追加,去除一个等等)等等。我这是抛砖引玉,很多功能没有,请大家不要见怪。言归正传,教程开始!
第一章 界面
打开VS2012(什么版本无所谓,思路都一样),新建一个MFC应用程序,输入工程名,选择保存目录,点击确定。注意,工程名字不要带中文字符,即使VS识别,这是一个良好的习惯。如图1.1
点击确定后,出现MFC应用程序向导,第一页是概述,显示当前默认的要建立的应用程序设置,这里如果是基于对话框的应用程序,就可以直接点击完成了,不过还是建议大家一步一步的自己设置。如图1.2
图1.2
点击下一步,是设置应用程序类型的,是单文档(SDI),多文档(MDI),还是基于对话框的。那么什么是单文档程序呢?简单的说就是只能同时打开一个的程序,比如我们的记事本程序,打开第二个的时候第一个会被关闭。什么是多文档程序呢?就是可以同时打开多个的程序(可多开),一般还可以同时处理多个文件,比如我们Word,浏览器等等。对话框程序呢?就是对话框程序啦,通常没有菜单和工具条,只有按钮等等控件,比如我们的Windows计算器。我们今天要做的计算器也是基于对话框的。选择好之后,点击下一步。如图1.3
图1.3
从上一步来到这里,此处是设置应用程序界面功能的,包括对话框标题,有无最大化,最小化按钮,程序运行是最大化显示还是最小化显示等等。设置好后点击下一步。如图1.4
图1.4
好了,直到上一步完成,就可以直接点击完成了。然后,按照我的样式或者自定义一个布局安放按钮和编辑框吧!如图1.5
图1.5
先忽略按钮禁用的问题,那是后期代码搞定的问题了。
有没有遇到问题呢?比如你的编辑框不能出滚动条?这个问题要设置编辑框的属性。我们要设置编辑框自动换行,并且垂直和水平滚动条都设为TRUE。注意,如果Multiline的值不是TRUE的话,两个滚动条不可设为TRUE。如图1.6
图1.6
第二章 实现代码
好了,界面已经完成,现在就是我们实现具体功能的时候了。首先是数字按键和运算符按键的实现。在资源编辑器下双击按钮VS会自动为这个按钮添加事件。如图2.1
图2.1
双击之后会跳到代码实现的cpp文件中,我们在这里写代码。那么数字按键和运算符按键都是怎么实现呢?没错,字符串追加!这里对编辑框内的字符串追加一个字符有两种方法,一种是为编辑框添加变量,一种是直接追加。这里为了教学,我都实现了下。
编辑框变量追加法:
在资源编辑器中,在欲添加变量的控件(这里是我们的编辑框,其他的控件一样的方法)上右击,选择添加变量选项。如图2.2
图2.2
然后在添加变量向导中输入变量名,这里我用的Result。如图2.3
图2.3
好了,假设你的按钮0是第21个,ID是IDC_BUTTON21,那么你双击它之后产生的响应函数应该是 void CCalcDlg::OnBnClickedButton21()。
具体代码:
void CCalcDlg::OnBnClickedButton21()
{
//按键0
UpdateData(TRUE); //把编辑框显示的字符串传给相应的变量Result
Result+="0"; //在按下数字键的情况下,则在Result后加"0"
UpdateData(FALSE); //把Result的值传给编辑框显示。
}
好了,写完代码马上运行下,看看是不是每按一次按钮编辑框就多显示一个字符呢?其他按钮也一样的实现方法。
直接追加法:
直接追加法没有那么多事要做了,直接写代码就OK了,只是比上一种方法不易理解。
代码如下:
void CCalcDlg::OnBnClickedButton19()
{
CString str=L"2"; //要追加的字符
CEdit *p=(CEdit*)GetDlgItem(IDC_EDIT1); //对编辑框1追加
int Len=p->SendMessage(WM_GETTEXTLENGTH);//获取长度
p->SetSel(Len,Len);
p->ReplaceSel(str);//追加
}
好了,以上两种方法任选一种即可。把所有的按钮(当然不包括运算作用的“=”等等了)都这样处理,并且测试成功之后再继续进行。
接下来,就是关键的计算了。这里大家可以用很多方法,比如用数据结构的栈和二叉树,或者自己写计算算法。我数据结构太糟了,只能去看人家写的代码了~~~
首先,按钮的禁用问题我们没解决呢!进制转换的问题也是大问题。我们的进制是用复选框控件实现的。为复选框添加事件,写入代码:
GetDlgItem(IDC_BUTTON28)->EnableWindow(1);。
这句的意思是当复选框被选择是按钮28是可用状态。如果EnableWindow(0)就是不可用状态。大家自己根据自己的按钮顺序自己复制代码吧。比如我的是这样的:
除了0和1外都不能使用。
然后,就是最要命的等号了。不说废话,看代码:
void CCalcDlg::OnBnClickedButton27()
{
// 等号
UpdateData(TRUE);
//L1=true;
int p=0,i=0,g[10]={0},k=0,j;
for(;i<Result.GetLength();i++)
{
if(Result.GetAt(i)=='(')
{
j=p;
g[j]=i;
p++;
}
}
for(;k<p;k++)
{
Bracket(Result,&g[j]);//调用Bracket(Result)函数,解决括号问题
j--;
}
GetStr(Result);
//调用函数GetStr(Result),作用是把m_strResult转化成数字和加减乘除四则运算
GetResult(); //调用函数GetResult(),作用是计算出表达式的值
Result.Format(_T("%1f"),a[0]);
while(L==1)
{
MOD(Result);
L=0;
}
while(PB=='E')
{
EXP10(Result);
PB='#';
}
while(PB=='X')
{
Xy(Result);
PB='#';
}
while(PB=='c')
{
SqrtMul(Result);
}
ClearZero(Result);
//特别注意,下面的{}是一个块,不是一个语句或函数的执行体。
{
CString str1=L"=";
CString str=str1+Result;
CEdit *p=(CEdit*)GetDlgItem(IDC_EDIT1);
int Len=p->SendMessage(WM_GETTEXTLENGTH);
p->SetSel(Len,Len);
//p->ReplaceSel(L"\n"); //想换行的话就不注释这一行
p->ReplaceSel(str);
}
}
被调用的函数:
void CCalcDlg::ClearZero(CString str)//**********有小数点后,清零函数*******
{
int i=0,f=str.GetLength(),t;
for(;i<f;i++)
if(str.GetAt(i)=='.') //判断是否有小数点
{ t=i; //将i赋给t,作为标识‘.’的位置
while((str.GetAt(--f)=='0' || str.GetAt(f)=='.')&&(f>=t))//当遇到零或者点并且f>=t
{
str.Delete(f);//清除‘0’和‘.’
}
Result=str;//把清除零的数赋给编辑框m_Edit3
}
}
void CCalcDlg::GetResult()//自己定义添加这个函数,代码如下
{
int i=0,x=0,y=1,t=0,z=1;
//这个循环的目的是把所有除法运算都改为乘法计算
for(i=0;i<=s;i++)
{
if(b[i]=='/') //当循环遇到除号时
{
a[i+1]=1/a[i+1]; //把a[i+1]的值改为1/a[i+1]
b[i]='*'; //把b[i]的值改为乘号
}
}
//这个循环可以对所有乘法进行计算
for(i=0;i<=s;i++)
{
if(b[i]=='*') //当循环遇到乘号时
{
//如果i-t==2(再上次循环中,t被赋值成了i,这次2个值只相差2,说明表达式的情况是连续乘法,比如2*3*4)以2*3*4为例,在这个条件之前,函数已经进行了2*3的运算,其计算结果保存在原来的2中,把2的值覆盖了,这个条件的作用是让2*3的结果再和4相乘,结果保存在原来的2中
if(i-t==2)
{
a[t-z]=a[t-z]*a[i+1];
//进行乘法运算,把结果保存在前面的数组元素中
z=z+2;
//这里引进变量z,可以解决很多数连续相乘的问题,比如2*2*2*2*2*2
}
else
a[i-1]=a[i-1]*a[i+1];
//碰到乘号后把乘号前后2个数字相乘,结果保存在前面的数组元素
a[i+1]=1000000.0; //对乘号后面的数字初始化为0.0
b[i]=' '; //对乘号初始化为空格
//2数相乘后把乘号及其后面的数字初始化了,如果是2*3*4的情况,将会无法计算*4,所以要用开始的条件语句来解决
t=i; //把i的值赋给t
}
}
//这个循环的目的是对数组a和b进行整理,因为已经进行了乘法和除法运算,进行运算的时候,把一些数字和运算符初始化了这个函数的作用可以使中间一些初始化了的数字和符号被后面的数字和符号代替,让数组可以重新排列
for(i=0;i<=s;i++)
{
if(a[i]!=1000000.0) //如果a[i]的值不为0.0
{
a[x]=a[i]; //把a[i]的值赋给a[x]
x+=2; //x自加2
}
if(b[i]!=' ') //如果b[i]的值不为空格
{
b[y]=b[i]; //把b[i]的值赋给b[y]
y=y+2; //y自加2
}
}
//这个循环是进行最后的加减法运算
for(i=0,x=2;i<=y-2;i++)
//这里i的上限小于等于y-2,可以保证不进行多余的运算
{
if(b[i]=='+') //如果b[i]等于加号
{
a[0]=a[0]+a[x];
x+=2;
}
//把加号后的数字和a[0]相加,结果保存在a[0]中
else if(b[i]=='-') //如果b[i]等于减号
{
a[0]=a[0]-a[x];
x+=2;
}
//把减号后的数字和a[0]相减,结果保存在a[0]中
}
}
void CCalcDlg::GetStr(CString str)//这个自己根据类向导自己定义的函数,添加
{
int i=0,z=0;
double rate=10.0,itemp=0.0;
for(i=0;i<20;i++)
a[i]=1000000.0; //对数组a[20]初始化
for(i=0;i<10;i++)
b[i]=' '; //对数组b[10]初始化
i=0; //把0赋给i
for(;i<str.GetLength();i++)
{
if(isdigit(str.GetAt(i))) //如果字符串str.GetAt(i)的内容是整数
{
if(rate==10.0) //整数部分
itemp=itemp*rate+(str.GetAt(i)-'0');//获得整数
else //小数部分
{
itemp=itemp+rate*(str.GetAt(i)-'0');//获得小数
rate=rate/10; //每次让rate小10倍
}
}
else if(str.GetAt(i)=='.') //如果str.GetAt(i)是小数点
rate=0.1; //让rate=0.1,开始计算小数部分
else if(str.GetAt(i)=='+') //如果str.GetAt(i)是加号
{
a[z]=itemp;itemp=0;
//把itemp的值放入双精度数组a中,并把itemp的值改为0
z++; //让z自加一次
b[z]='+'; //把加号放入字符数组b中
z++; //让z自加一次
rate=10.0;
//把10赋给rate,确保读取下个数字时,先计算整数部分
}
else if(str.GetAt(i)=='-') //如果str.GetAt(i)是减号
{
a[z]=itemp;itemp=0;
//把itemp的值放入双精度数组a中,并把itemp的值改为0
z++; //让z自加一次
b[z]='-'; //把减号放入字符数组b中
z++; //让z自加一次
rate=10.0;
//把10赋给rate,确保读取下个数字时,先计算整数部分
}
else if(str.GetAt(i)=='*') //如果str.GetAt(i)是乘号
{
a[z]=itemp;itemp=0;
//把itemp的值放入双精度数组a中,并把itemp的值改为0
z++; //让z自加一次
b[z]='*'; //把减号放入字符数组b中
z++; //让z自加一次
rate=10.0;
//把10赋给rate,确保读取下个数字时,先计算整数部分
}
else if(str.GetAt(i)=='/') //如果str.GetAt(i)是除号
{
a[z]=itemp;itemp=0;
//把itemp的值放入双精度数组a中,并把itemp的值改为0
z++; //让z自加一次
b[z]='/'; //把除号放入字符数组b中
z++; //让z自加一次
rate=10.0;
//把10赋给rate,确保读取下个数字时,先计算整数部分
}
}
a[z]=itemp;
//把最后一个整数itemp的值放入双精度数组a中
s=z;
//把z的值赋给变量s,用来控制计算结果的循环中的条件
}
void CCalcDlg::Bracket(CString str0,int *h)//这个代码也是自己添加
{
CString str1=_T(""),str2=_T(""),str3=_T("");
int i=0;
for(;i<str0.GetLength();i++)
{
if(*h==i) //如果碰到了左括号
{
i++; //i自加一次,用来跳过左括号
for(;i<str0.GetLength();i++)
{
if(str0.GetAt(i)==')') break; //如果碰到右括号,跳出循环
str2+=str0.GetAt(i); //把括号内的表达式赋给str2
}
i++; //i自加一次,用来跳过右括号
for(;i<str0.GetLength();i++)
{
str3+=str0.GetAt(i); //把括号后边的表达式赋给str3
}
}
else
str1+=str0.GetAt(i); //把括号前边的表达式赋给str3
}
GetStr(str2);
//调用GetStr(str2)函数,把str2里的数字和运算符保存在数组中
GetResult(); //计算出表达式str2的值
for(i=0;i<str0.GetLength();i++)
if(str0.GetAt(i)=='(') //如果Result中有括号
str2.Format(_T("%1f"),a[0]); //把a[0]转化为字符串赋给str2
Result=str1+str2+str3;
//把str1,str2,str3的值加起来赋给Result
}
下面是三角函数的计算算法:
void CCalcDlg::OnBnClickedButton40()
{
//COS
UpdateData(TRUE);
double result;
if(JH==1)//JH是全局变量,用于指示角度制或者弧度制。
result=cos(_wtof(Result)*6.2831853071795864769/360.0);
else
result=cos(_wtof(Result));
Result.Format(_T("%1f"),result);//格式转化
ClearZero(Result);//清除多余的“0”
UpdateData(FALSE);
}
void CCalcDlg::OnBnClickedButton42()
{
// tan
UpdateData(TRUE);
double result;
CString str=Result;
Result=str.Right(str.GetLength()-str.ReverseFind('\n')-1);
if(JH==1)
result=tan(_wtof(Result)*6.2831853071795864769/360.0);
else
result=tan(_wtof(Result));
Result.Format(_T("%1f"),result);
ClearZero(Result);
UpdateData(FALSE);
}
void CCalcDlg::OnBnClickedButton39()
{
// sin按键
UpdateData(TRUE);
double result;
if(JH==1)
result=sin(_wtof(Result)*6.2831853071795864769/360.0);
else
result=sin(_wtof(Result));
Result.Format(_T("%1f"),result);
ClearZero(Result);
UpdateData(FALSE);
}
具体的请大家下载代码看看吧。
至此,计算器的大体功能已大致实现。哦哦,那个各种高级的计算是吧?只是个按钮而以......大家可以自己完善......放在这里我是用来撑个场面的......勿喷.........