參考資料:
MFC響應鍵盤
計算器實例
MFC文件對話框
MFCUpdateData()函數的使用
MFC教程
winuser.h
C++ 中int,char,string,CString類型轉換
關於本次作業
一開始完全是無從下手,從選擇"qt"還是"MFC"就開始猶豫,最后想到VS2015自帶有MFC,省去重新安裝"qt"的麻煩,選擇了"MFC"(后來聽說qt在實現上會簡單很多,沒實際試過不好評價)。因為這次作業所以重新開始使用VS,之前安裝好就沒怎么用,所以在軟件的使用上又浪費了點時間去熟悉。查找了關於MFC的資料= =嗯,我想現在也沒有時間去系統的學習它,所以放棄了整套的學習,直接查找關於MFC實現計算器方面的資料,又在GitHub上下載了幾個計算器成品,把這些成品拆分掉才開始有了一點頭緒。開始動手后,感覺跟高中時候會考要用的那個VB有點像,也是拖動各種工具然后去加代碼= =但是還是天真了,畢竟不太一樣的東西。最后算是勉勉強強實現了計算器圖形界面。
實踐中遇到的問題:
- 關於鍵盤響應,一開始查找資料,順利完成小鍵盤上數字的響應,但是主鍵盤上的數字卻一直找不到其所對應的VK值,困擾了很久,最后才從一份文檔中發現,MFC並沒有關於主鍵盤上的字母所對應的VK值,而是采用直接引用的辦法= =知道真相后有種欲哭無淚的感覺,很簡單的解決辦法,但是這過程花費了大把的時間。
- 從編輯框中讀取文件路徑,一開始采用的方法是在編輯框中添加響應,通過UpdateData來更新,在輸入文件路徑的編輯框還算順利,同樣的辦法用到輸出文件路徑的編輯框,程序運行后整個都卡住了。特別是如果我先在輸出文件路徑的編輯框中輸入然后再在輸入文件路徑的編輯框輸入路徑,程序就不會卡,極度郁悶= =也找不到解決方法,其實現在對於這個問題還是懵懵的,最后加了一個button,按下之后才獲取兩個編輯框中的值進行批處理。
- 在輸入輸出文件路徑的編輯框中,不支持刪除功能,最后用了兩個button來清空,沒有采用逐字刪除的功能。
- 鍵盤響應的問題,如果在輸入輸出文件的編輯框中使用了數字進行輸入,計算器的顯示區也會顯示出這些數字,目前想到的解決辦法是通過判斷光標的位置,如果停在輸入輸出文件編輯框,則不鍵盤響應,但是這個方法沒有成功,不知道是不是我判斷焦點那塊代碼寫得有問題,暫時沒有很好的解決。
第二次更新:
- 本次修改了光標停在文件編輯框輸入以字母形式的文件名也會出現在計算顯示區的問題。
- 增加了支持文件輸入輸出編輯框的逐字刪除。
- 關於光標停在編輯框,鍵盤按鍵無法響應的問題,初次解決方案通過“關於本程序”的按鈕彈出操作說明來告知使用者如何解決,后來考慮到並非人人都會點擊“關於本程序”按鈕,額外加了一個“消除”焦點的按鈕。
- 更改了顯示區字體大小以及字體為“微軟雅黑”(使用者電腦如果沒有這個字體的話= =)
第三次更新
- 修復了顯示精度,諸如“(85.18-(-6140))(-93.28+1)=”能得出-233023.6104而不再是-233024以及對於表達式“-72-((-50-53)(-45*-91))=”得出-10851822而不是-1.08518e+007的問題
- 改正了顯示區當表達式過長停留在最先輸入的字符上,支持從左到右的閱讀順序來顯示表達式。
- 主界面增加了最大化、最小化。
- 更正了計算結果為0時顯示為“-0”的錯誤(不小心發現學長的數據也有“-0”)
- 修改了生成程序的默認圖標,以及屬性中版本等默認信息為個人信息。
未實現功能:
- 使用者輸入不合法表達式沒有以彈窗模式進行警告,僅只是通過判斷使程序不響應此操作。
- 界面被吐槽丑= =確實硬傷
- 沒有增加按鈕來支持顯示科學表達式與普通表達式的轉換
寫在后面:
程序界面:


由於時間問題,對於界面的樣式、字體等也沒細弄,批處理區設在了主界面,沒有弄成模態對話框的形式(我覺得不會很丑啊= =別打我= =),倒是增加了一個“關於本程序”的按鈕,利用模態對話框來彈出另一個界面介紹程序。以后如果還有弄界面= = 我想我會果斷選擇qt了= =另外程序界面支持被隨意拉伸,其實加個函數就能固定程序界面大小,但是我覺得這樣隨意拉伸挺好的,也就沒有更改。還在不斷改進,想到bug再來修改吧。
核心代碼:
// 如果上一次按的是“=”按鈕,清屏
void CCalculatorDlg::Clear()
{
if (lastPress == true)
{
mEdit.SetWindowText(_T(""));
lastPress = false;
}
}
void CCalculatorDlg::OnBnClicked1()
{
// 數字“1”按鈕
Clear();
UpdateData(TRUE);
CString str;
mEdit.GetWindowText(str);
str = str + _T("1");
mEdit.SetWindowText(str);
UpdateData(FALSE);
}
void CCalculatorDlg::OnBnClicked2()
{
// 數字“2”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str + _T("2");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClicked3()
{
// 數字“3”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str + _T("3");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClicked4()
{
// 數字“4”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str + _T("4");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClicked5()
{
// 數字“5”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str + _T("5");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClicked6()
{
// 數字“6”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str + _T("6");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClicked7()
{
// 數字“7”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str + _T("7");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClicked8()
{
// 數字“8”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str + _T("8");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClicked9()
{
// 數字“9”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str + _T("9");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClicked0()
{
// 數字“0”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
if (str != "0")
{
str = str + _T("0");
mEdit.SetWindowText(str);
}
}
void CCalculatorDlg::OnBnClickedClear()
{
// “清屏”按鈕
mEdit.SetWindowText(_T(""));
}
void CCalculatorDlg::OnBnClickedBack()
{
// “后退”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
str = str.Left(str.GetLength()-1);
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClickedLeft()
{
// “左括號”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
if (str == "")
{
str = str + _T("(");
mEdit.SetWindowText(str);
}
else
{
if(str.GetAt(str.GetLength()-1)<'0' || str.GetAt(str.GetLength()-1)>'9')
{
str = str + _T("(");
mEdit.SetWindowText(str);
}
}
}
void CCalculatorDlg::OnBnClickedRight()
{
// “右括號”按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
if(str != "")
{
str = str + _T(")");
mEdit.SetWindowText(str);
}
}
void CCalculatorDlg::OnBnClickedDot()
{
// "."按鈕
Clear();
CString str;
mEdit.GetWindowText(str);
if(str != "")
{
if(str.GetAt(str.GetLength()-1)>='0' && str.GetAt(str.GetLength()-1)<='9')
{
str = str + _T(".");
mEdit.SetWindowText(str);
}
}
}
void CCalculatorDlg::OnBnClickedAdd()
{
// 加號
lastPress = false;
CString str;
mEdit.GetWindowText(str);
str = str + _T("+");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClickedSub()
{
// 減號
lastPress = false;
CString str;
mEdit.GetWindowText(str);
str = str + _T("-");
mEdit.SetWindowText(str);
}
void CCalculatorDlg::OnBnClickedMul()
{
// 乘號
lastPress = false;
CString str;
mEdit.GetWindowText(str);
if(str != "")
{
if(str.GetAt(str.GetLength()-1)!='+' && str.GetAt(str.GetLength()-1)!='-'
&& str.GetAt(str.GetLength()-1)!='*' && str.GetAt(str.GetLength()-1)!='/')
{
str = str + _T("*");
mEdit.SetWindowText(str);
}
}
}
void CCalculatorDlg::OnBnClickedDiv()
{
// 除號
lastPress = false;
CString str;
mEdit.GetWindowText(str);
if(str != "")
{
if(str.GetAt(str.GetLength()-1)!='+' && str.GetAt(str.GetLength()-1)!='-'
&& str.GetAt(str.GetLength()-1)!='*' && str.GetAt(str.GetLength()-1)!='/')
{
str = str + _T("/");
mEdit.SetWindowText(str);
}
}
}
void CCalculatorDlg::OnBnClickedEql()
{
// 等號,計算結果
CString str;
mEdit.GetWindowText(str);
if(str.Find('+')==-1 && str.Find('-')==-1 && str.Find('*')==-1 && str.Find('/')==-1)
return;
else
lastPress = true;
CT2CA pszConvertedAnsiString(str); // 將 TCHAR 轉換為 LPCSTR
string exp_str(pszConvertedAnsiString); // 從 LPCSTR 構造 string
que = ipt.ToStringQueue(exp_str);
if (que.empty())
{
str = "ERROR";
}
else
{
string tmp;
stringstream ss;
ss.precision(10);
stk = cal.NumCalculator(que);
ss << stk.top();
ss >> tmp;
str = tmp.c_str();
}
mEdit.SetWindowText(str);
}
BOOL CCalculatorDlg::PreTranslateMessage(MSG* pMsg)
{
bool flag = true;
CWnd *pFocusWnd = GetFocus();
if (pFocusWnd && (pFocusWnd == GetDlgItem(IDC_OPEN_EDIT) || pFocusWnd == GetDlgItem(IDC_SAVE_EDIT)))
{
flag = false;
}
else
{
flag = true;
}
if (pMsg->message == WM_KEYDOWN && flag)
{
switch (pMsg->wParam)
{
case VK_NUMPAD0:
OnBnClicked0(); break;
case VK_NUMPAD1:
case '1':
OnBnClicked1(); break;
case '2':
case VK_NUMPAD2:
OnBnClicked2(); break;
case '3':
case VK_NUMPAD3:
OnBnClicked3(); break;
case '4':
case VK_NUMPAD4:
OnBnClicked4(); break;
case '5':
case VK_NUMPAD5:
OnBnClicked5(); break;
case '6':
case VK_NUMPAD6:
OnBnClicked6(); break;
case '7':
case VK_NUMPAD7:
OnBnClicked7(); break;
case VK_NUMPAD8:
OnBnClicked8(); break;
case VK_NUMPAD9:
OnBnClicked9();
break;
case '8':
if (GetKeyState(VK_SHIFT) < 0)
{
OnBnClickedMul();
}
else
{
OnBnClicked8();
}
break;
case '9':
if (GetKeyState(VK_SHIFT) < 0)
{
OnBnClickedLeft();
}
else
{
OnBnClicked9();
}
break;
case '0':
if (GetKeyState(VK_SHIFT) < 0)
{
OnBnClickedRight();
}
else
{
OnBnClicked0();
}
break;
case VK_OEM_PLUS:
if (GetKeyState(VK_SHIFT) < 0)
{
OnBnClickedAdd();
}
else
{
OnBnClickedEql();
}
break;
case VK_OEM_PERIOD:
case VK_DECIMAL:
OnBnClickedDot(); break;
case VK_SUBTRACT:
case VK_OEM_MINUS:
OnBnClickedSub(); break;
case VK_DIVIDE:
case VK_OEM_2:
OnBnClickedDiv(); break;
case VK_ADD:
OnBnClickedAdd(); break;
case VK_MULTIPLY:
OnBnClickedMul(); break;
case VK_BACK:
OnBnClickedBack(); break;
case VK_RETURN:
OnBnClickedEql(); return TRUE;
case VK_ESCAPE:
OnBnClickedClear(); return TRUE;
default:
break;
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
void CCalculatorDlg::OnEnChangeShow()
{
// TODO: 如果該控件是 RICHEDIT 控件,它將不
// 發送此通知,除非重寫 CDialogEx::OnInitDialog()
// 函數並調用 CRichEditCtrl().SetEventMask(),
// 同時將 ENM_CHANGE 標志“或”運算到掩碼中。
// TODO: 在此添加控件通知處理程序代碼
}
void CCalculatorDlg::OnBnClickedOpenButton()
{
// TODO: 在此添加控件通知處理程序代碼
// 設置過濾器
TCHAR szFilter[] = _T("文本文件(*.txt)|*.txt|所有文件(*.*)|*.*||");
// 構造打開文件對話框
CFileDialog fileDlg(TRUE, _T("txt"), NULL, 0, szFilter, this);
CString strFilePath;
// 顯示打開文件對話框
if (IDOK == fileDlg.DoModal())
{
// 如果點擊了文件對話框上的“打開”按鈕,則將選擇的文件路徑顯示到編輯框里
strFilePath = fileDlg.GetPathName();
//tEdit.SetWindowText(strFilePath);
SetDlgItemText(IDC_OPEN_EDIT, strFilePath);
//SetDlgItemText(IDC_OPEN_EDIT, L" ");
}
}
void CCalculatorDlg::OnBnClickedSaveButton()
{
// TODO: 在此添加控件通知處理程序代碼
// 設置過濾器
TCHAR szFilter[] = _T("文本文件(*.txt)|*.txt|Word文件(*.doc)|*.doc|所有文件(*.*)|*.*||");
// 構造保存文件對話框
CFileDialog fileDlg(FALSE, _T("doc"), _T("my"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter, this);
CString strFilePath;
// 顯示保存文件對話框
if (IDOK == fileDlg.DoModal())
{
// 如果點擊了文件對話框上的“保存”按鈕,則將選擇的文件路徑顯示到編輯框里
strFilePath = fileDlg.GetPathName();
SetDlgItemText(IDC_SAVE_EDIT, strFilePath);
}
}
void CCalculatorDlg::OnEnChangeOpenEdit()
{
// TODO: 在此添加控件通知處理程序代碼
CString strFilePath;
CString strFile;
UpdateData(TRUE);
tEdit.GetWindowText(strFile);
strFilePath = strFilePath + strFile;
UpdateData(FALSE);
CT2CA pszConvertedAnsiString(strFilePath); // 將 TCHAR 轉換為 LPCSTR
string test_str(pszConvertedAnsiString); // 從 LPCSTR 構造 string
test_str_ = test_str;
}
void CCalculatorDlg::OnEnChangeSaveEdit()
{
// TODO: 在此添加控件通知處理程序代碼
CString strFilePath;
CString strFile;
UpdateData(TRUE);
rEdit.GetWindowText(strFile);
strFilePath = strFilePath + strFile;
UpdateData(FALSE);
CT2CA pszConvertedAnsiString(strFilePath); // 將 TCHAR 轉換為 LPCSTR
string results_str(pszConvertedAnsiString); // 從 LPCSTR 構造 string
results_str_ = results_str;
}
void CCalculatorDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知處理程序代碼
ipt.FileOpen(test_str_);
opt.FileOpen(results_str_);
if (!ipt.FileIsOpen())
{
cerr << "Could not open " << results_str_ << endl;
ipt.FileClear();
}
else
{
while (!ipt.IsEof())
{
ipt.Read();
//getline(ipt.fin, ipt.in);
if (ipt.in == "")
{
continue;
}
que = ipt.ToStringQueue(ipt.in);
if (que.empty())
{
opt.OutputToFile(que);
}
else
{
stk = cal.NumCalculator(que);
opt.PutAnsTofile(stk);
}
}
}
ipt.FileClose();
opt.FileClose();
}
void CCalculatorDlg::OnBnClickedClear1()
{
//清空文件輸入路徑內容
tEdit.SetWindowText(_T(""));
}
void CCalculatorDlg::OnBnClickedClear2()
{
//清空文件輸出路徑內容
rEdit.SetWindowText(_T(""));
}
void CCalculatorDlg::OnBnClickedButtonAbout()
{
// TODO: 在此添加控件通知處理程序代碼
INT_PTR nRes; // 用於保存DoModal函數的返回值
CTipDlg tipDlg; // 構造對話框類CTipDlg的實例
nRes = tipDlg.DoModal(); // 彈出對話框
if (IDCANCEL == nRes) // 判斷對話框退出后返回值是否為IDCANCEL,如果是則return,否則繼續向下執行
return;
}
void CCalculatorDlg::OnBnClickedButtonOk()
{
// TODO: 在此添加控件通知處理程序代碼
CWnd *pFocusWnd = GetFocus();
if (pFocusWnd && (pFocusWnd == GetDlgItem(IDC_OPEN_EDIT)))
{
tEdit.SetWindowText(_T(""));
}
else if (pFocusWnd && (pFocusWnd == GetDlgItem(IDC_SAVE_EDIT)))
{
rEdit.SetWindowText(_T(""));
}
}
