第六次作业——利用MFC实现计算器图形界面以及简单四则运算表达式批处理

时间:2022-11-26 17:02:14

参考资料:

     1.MFC响应键盘
     2.计算器实例
     3.MFC文件对话框
     4.MFCUpdateData()函数的使用
     5.MFC教程
     6.winuser.h
     7.C++ 中int,char,string,CString类型转换

GitHub链接:传送门

题目链接:传送门

关于本次作业

一开始完全是无从下手,从选择"qt"还是"MFC"就开始犹豫,最后想到VS2015自带有MFC,省去重新安装"qt"的麻烦,选择了"MFC"(后来听说qt在实现上会简单很多,知道真相的喔眼泪掉下来)。因为这次作业所以重新开始使用VS,之前安装好就没怎么用,所以在软件的使用上又浪费了点时间去熟悉。查找了关于MFC的资料= =嗯,我想现在也没有时间去系统的学习它,所以放弃了整套的学习,直接查找关于MFC实现计算器方面的资料,又在GitHub上下载了几个计算器成品,把这些成品拆分掉才开始有了一点头绪。开始动手后,感觉跟高中时候会考要用的那个VB有点像,也是拖动各种工具然后去加代码= =但是还是天真了,毕竟不太一样的东西。最后算是勉勉强强实现了计算器图形界面。

实践中遇到的问题:

  • 1、关于键盘响应,一开始查找资料,顺利完成小键盘上数字的响应,但是主键盘上的数字却一直找不到其所对应的VK值,困扰了很久,最后才从一份文档中发现,MFC并没有关于主键盘上的字母所对应的VK值,而是采用直接引用的办法= =知道真相后有种欲哭无泪的感觉,很简单的解决办法,但是这过程花费了大把的时间。
  • 2、从编辑框中读取文件路径,一开始采用的方法是在编辑框中添加响应,通过UpdateData来更新,在输入文件路径的编辑框还算顺利,同样的办法用到输出文件路径的编辑框,程序运行后整个都卡住了。特别是如果我先在输出文件路径的编辑框中输入然后再在输入文件路径的编辑框输入路径,程序就不会卡,极度郁闷= =也找不到解决方法,其实现在对于这个问题还是懵懵的,最后加了一个button,按下之后才获取两个编辑框中的值进行批处理。
  • 3、在输入输出文件路径的编辑框中,不支持删除功能,最后用了两个button来清空,没有采用逐字删除的功能。
  • 4、键盘响应的问题,如果在输入输出文件的编辑框中使用了数字进行输入,计算器的显示区也会显示出这些数字,目前想到的解决办法是通过判断光标的位置,如果停在输入输出文件编辑框,则不键盘响应,但是这个方法没有成功,不知道是不是我判断焦点那块代码写得有问题,暂时没有很好的解决。

第三次更新:

为什么没有写第二次更新呢= =我忘了第二次更新的时候修复了什么bug。

  • 1、本次修改了光标停在文件编辑框输入以字母形式的文件名也会出现在计算显示区的问题。
  • 2、增加了支持文件输入输出编辑框的逐字删除。
  • 3、关于光标停在编辑框,键盘按键无法响应的问题,初次解决方案通过“关于本程序”的按钮弹出操作说明来告知使用者如何解决,后来考虑到并非人人都会点击“关于本程序”按钮,额外加了一个“消除”焦点的按钮。
  • 4、更改了显示区字体大小以及字体为“微软雅黑”(使用者电脑如果没有这个字体的话= =)

第四次更新

  • 1、修复了显示精度,诸如“(85.18-(-6140))(-93.28+1)=”能得出-233023.6104而不再是-233024以及对于表达式“-72-((-50-53)(-45*-91))=”得出-10851822而不是-1.08518e+007的问题
  • 2、改正了显示区当表达式过长停留在最先输入的字符上,支持从左到右的阅读顺序来显示表达式。
  • 3、主界面增加了最大化、最小化。
  • 4、更正了计算结果为0时显示为“-0”的错误(不小心发现学长的数据也有“-0”)
  • 5、修改了生成程序的默认图标,以及属性中版本等默认信息为个人信息。

未实现功能:

  • 1、使用者输入不合法表达式没有以弹窗模式进行警告,仅只是通过判断使程序不响应此操作。
  • 2、界面被吐槽丑= =确实硬伤,时间关系= =都是泪。
  • 3、没有增加按钮来支持显示科学表达式与普通表达式的转换

写在后面:

程序界面:

第六次作业——利用MFC实现计算器图形界面以及简单四则运算表达式批处理
第六次作业——利用MFC实现计算器图形界面以及简单四则运算表达式批处理

由于时间问题,对于界面的样式、字体等也没细弄,批处理区设在了主界面,没有弄成模态对话框的形式(我觉得不会很丑啊= =别打我= =),倒是增加了一个“关于本程序”的按钮,利用模态对话框来弹出另一个界面介绍程序。以后如果还有弄界面= = 我想我会果断选择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(""));
    }
}