Visual Studio 2012製作MFC計算器-TC王者
Visual Studio 2012 MFC計算器教程
大家好!我是TC王者。首次做教程,希望大家勿噴。。。。。
原始碼下載地址:http://download.csdn.net/detail/tc00tc/6948977
本次教程主要講VS2012 MFC製作視覺化四則計算器,包括很多細小問題,比如MFC中的型別轉換,字串操作(追加,去除一個等等)等等。我這是拋磚引玉,很多功能沒有,請大家不要見怪。言歸正傳,教程開始!
第一章 介面
開啟VS2012(什麼版本無所謂,思路都一樣),新建一個MFC應用程式,輸入工程名,選擇儲存目錄,點選確定。注意,工程名字不要帶中文字元,即使VS識別,這是一個良好的習慣。如圖1.1
圖1.1
點選確定後,出現MFC應用程式嚮導,第一頁是概述,顯示當前預設的要建立的應用程式設定,這裡如果是基於對話方塊的應用程式,就可以直接點選完成了,不過還是建議大家一步一步的自己設定。如圖1.2
圖1.2
點選下一步,是設定應用程式型別的,是單文件(SDI),多文件(MDI),還是基於對話方塊的。那麼什麼是單文件程式呢?簡單的說就是隻能同時開啟一個的程式,比如我們的記事本程式,開啟第二個的時候第一個會被關閉。什麼是多文件程式呢?就是可以同時開啟多個的程式(可多開),一般還可以同時處理多個檔案,比如我們Word,瀏覽器等等。對話方塊程式呢?就是對話方塊程式啦,通常沒有選單和工具條,只有按鈕等等控制元件,比如我們的
圖1.3
從上一步來到這裡,此處是設定應用程式介面功能的,包括對話方塊標題,有無最大化,最小化按鈕,程式執行是最大化顯示還是最小化顯示等等。設定好後點擊下一步。如圖1.4
圖1.4
好了,直到上一步完成,就可以直接點選完成了。然後,按照我的樣式或者自定義一個佈局安放按鈕和編輯框吧!如圖1.5
圖1.5
先忽略按鈕禁用的問題,那是後期程式碼搞定的問題了。
有沒有遇到問題呢?比如你的編輯框不能出滾動條?這個問題要設定編輯框的屬性。我們要設定編輯框自動換行,並且垂直和水平滾動條都設為TRUE
圖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);
}
具體的請大家下載程式碼看看吧。
至此,計算器的大體功能已大致實現。哦哦,那個各種高階的計算是吧?只是個按鈕而以......大家可以自己完善......放在這裡我是用來撐個場面的......勿噴.........