前面已经介绍了按钮的创建与显示,(MFCApplication中最后加上的链接部分)
通过定义一个CButton m_btn 变量,然后使用Create和ShowWindow函数创建按钮和显示,如下面两行代码:
m_btn.Create(_T("我是按钮"), WS_CHILD | BS_DEFPUSHBUTTON, CRect(300, 0, 400, 100), this, 123); 
m_btn.ShowWindow(SW_SHOWNORMAL);
也可以直接使用btn_number.Create(temp_num, WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE, CRect, this, 123);来实现上述的两行代码所代表的功能。


接下来尝试做一个 计算器应用程序(理论上是参考Microsoft自带的计算器,科学计数法or标准模式),具备基本的加减乘除功能
具体而言,具备0-9这10个数字按钮,和=+-*/这五个符号按钮,
按照上一次的MFCApplication程序,这次同样建立了一个单文档项目。同样可以直接运行,但是也只有一个界面框架,还需要我们自己设计,添加内容。
整个项目分以下几个步骤:
第一步是添加按钮,一共十五个按钮,分成三行,每一行五个,但是需要输出显示,所以设计四行,第一行的空间用于文本输出,显示字符,后面三行摆放十五个按钮,用于点击输入数据。
第二步则是响应事件,当我们点击按钮时,捕捉消息,然后在内部存储数据
第三步是计算,当按下等号之后,进行事件响应,然后对得到的表达式进行计算,
第四步就是输出显示,显示整个表达式以及计算结果。

 第一步,添加按钮。在View中添加按钮,在OnCreate添加代码,可以一句一句的添加,但是发现除了按钮的坐标和指定按钮控件的文本(标题)不一样其他都是一致的,是可以利用循环来解决的。(其实也可以把数字按钮和符号按钮用一个循环实现的,即用一个字符数组存储数字字符和运算符字符,然后循环创建显示)

1     wchar_t  temp_num[5];
2     for (int i = 0; i < 10; i++) {
3         wsprintfW(temp_num, L"%d", i);
4         btn_number[i].Create(temp_num, WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE, CRect(i % 5 * 50, (i / 5 + 1) * 50, i % 5 * 50 + 50, (i / 5 + 2) * 50), this, 100 + i);
5     }
6     wchar_t  temp_op[5][5] = { L"+" ,L"-" ,L"*" ,L"/" ,L"=" };
7     for (int i = 0; i < 5; i++) {
8         btn_operator[i].Create(temp_op[i], WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE, CRect(i * 50, 150, (i + 1) * 50, 200), this, 110 + i);
9     }

第二步,响应点击按钮这个消息事件,显然会点击鼠标左键按下,故而想响应这个左键按下的事件,然后再去判断是哪一个按钮,之前好像是利用OnLButtonDown响应WM_LBUTTONDOWN(见类向导,消息)消息来处理的,但是发生了问题,没有解决,然后删了代码,忘记了
后来百度找到了PreTranslateMessage函数,(类向导,虚函数中),(百度百科:在MFC中,PreTranslateMessage是虚函数,是用来截获消息的。我们可以通过重载它来处理键盘和鼠标消息。)在这个函数里面做判断 if(pMsg->message == WM_LBUTTONDOWN),是否是按下左键的消息,然后再去检查是那一个按钮被按下,可以利用MSG中存储的句柄hwnd和按钮的句柄进行对比,相同则是按下了该按钮。然后就可以把对应的字符信息添加到表达式字符串中,构成我们的表达式字符串。

第三步,当检测到输入等号之后,利用表达式字符串进行计算,显然这里需要用到中缀表达式后缀表达式的相关知识,可以先把其转化为后缀表达式,然后再计算,数据结构,栈,实现 --> 之前有写过一片关于后缀表达式的笔记,借鉴一下,(中缀表达式转后缀表达式并计算——栈

此处是定义了一个Tool类,(就是添加了一个Tool.h和Tool.cpp文件,写的有点烂,勉强用一下)用于实现表达式的计算。

JSQTool.h

 1 #pragma once
 2 
 3 #include <stack>
 4 #include <string>
 5 
 6 using namespace std;
 7 
 8 class Tool {
 9 public:
10     string str;            // 中缀表达式
11 
12 
13 public:
14     string Trans(string str);
15     double Compvalue(string str);
16 };
View Code
JSQTool.cpp
#include "stdafx.h"
#include "JSQTool.h"

#include <stack>
#include <string>

using namespace std;

// 中缀转后缀表达式
string Tool::Trans(string str)
{
    stack<char> st;
    string result = "";
    char e;
    int i = 0;
    while (str[i] != '=')
    {
        switch (str[i])
        {
        case '+':
        case '-':
            while (!st.empty())
            {
                e = st.top();
                result += e;
                st.pop();
            }
            st.push(str[i]);
            i++;
            break;
        case '*':
        case '/':
            while (!st.empty())
            {
                e = st.top();
                if (e == '*' || e == '/')
                {
                    result += e;
                    st.pop();
                }
                else
                    break;
            }
            st.push(str[i]);
            i++;
            break;
        default:            // 数字字符处理 
            while (str[i] >= '0' && str[i] <= '9') {
                result += str[i];
                i++;
            }
            result += '#';        // 标识一个数字串结束 
        }
    }
    while (!st.empty())
    {
        e = st.top();
        st.pop();
        result += e;
    }
    // result = '\0';            // 反正不能加
    return result;
}

// 后缀表达式计算
double Tool::Compvalue(string str) {
    stack<double> st;
    double result = 0, a = 0, b = 0, temp = 0;
    int i = 0;
    while (str[i] != '\0') {
        switch (str[i]) {
        case '+':a = st.top(); st.pop();
            b = st.top(); st.pop();        // + ->出栈两个元素
            st.push(a + b);
            break;                
        case '-':a = st.top(); st.pop();
            b = st.top(); st.pop();
            st.push(b - a);
            break;
        case '*':a = st.top(); st.pop();
            b = st.top(); st.pop();
            st.push(a * b);
            break;
        case '/':a = st.top(); st.pop();
            b = st.top(); st.pop();
            if (a != 0)
                st.push(b / a);
            else
                exit(0);// 错误警告
            break;
        default:            // 数字字符处理
            temp = 0;
            while (str[i] >= '0' && str[i] <= '9') {
                temp = 10 * temp + str[i] - '0';
                i++;
            }
            st.push(temp);    // 进栈
            break;
        }
        i++;
    }
    return st.top();
}
View Code

第四步,计算表达式,得到了整个表达式的运算结果,此时需要显示运算结果,目前只知道利用MessageBox显示数据
还可以用下面两行代码实现:
CClientDC dc(this);
dc.TextOutW(0, 0, LPCTSTR(result + value));
但是,当接着进行第二次计算时(按下等号之后表达式被清空,重新开始一轮,输出的字符会覆盖在前面计算的字符上,显示区域没清空。。。而且TextOut不支持换行,需要手动控制一下(虽然这里不需要换行)


 结果显示:


 主要代码:View.cpp中的OnCreate函数和PreTranslateMessage函数

 1 // CJSQ0715View 消息处理程序
 2 
 3 
 4 int CJSQ0715View::OnCreate(LPCREATESTRUCT lpCreateStruct)
 5 {
 6     if (CView::OnCreate(lpCreateStruct) == -1)
 7         return -1;
 8 
 9     // TODO:  在此添加您专用的创建代码
10     // 创建数字按钮
11     wchar_t  temp_num[5];
12     for (int i = 0; i < 10; i++) {
13         wsprintfW(temp_num, L"%d", i);
14         btn_number[i].Create(temp_num, WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE, CRect(i % 5 * 50, (i / 5 + 1) * 50, i % 5 * 50 + 50, (i / 5 + 2) * 50), this, 100 + i);
15     }
16     wchar_t  temp_op[5][5] = { L"+" ,L"-" ,L"*" ,L"/" ,L"=" };
17     for (int i = 0; i < 5; i++) {
18         btn_operator[i].Create(temp_op[i], WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE, CRect(i * 50, 150, (i + 1) * 50, 200), this, 110 + i);
19     }
20 
21     return 0;
22 }
23 
24 
25 BOOL CJSQ0715View::PreTranslateMessage(MSG* pMsg)
26 {
27     // TODO: 在此添加专用代码和/或调用基类
28     char char_jsq[20] = { "0123456789+-*/=" };
29 
30     if (pMsg->message == WM_LBUTTONDOWN)            // 鼠标左键按下,按钮按下,但不一定按着按钮
31     {                                                // 判断按着哪一个按钮
32         for (int i = 0; i < 15; i++)                // 检查十五个按钮,判断哪一个被按下
33         {                
34             if (pMsg->hwnd == GetDlgItem(100 + i)->m_hWnd) 
35             {
36                 // MessageBox(_T("按下按键"));
37                 result += char_jsq[i];                    // 表达式
38                 if (char_jsq[i] == '=')                    // 输入结束,可以计算
39                 {                
40                     MessageBox(LPCTSTR(result));
41                     Tool tool;
42                     string index = CW2A(result.GetString());
43                     CString value;
44                     value.Format(_T("%0.2f"), tool.Compvalue(tool.Trans(index)));
45                     MessageBox(LPCTSTR(result + value));
46                     result = "";                        // 新一轮
47                 }
48                 pMsg->hwnd = NULL;
49             }
50         }
51     }
52     return CView::PreTranslateMessage(pMsg);
53 }
View.h中添加的按钮成员变量,和记录表达式的CString变量:
1     // 0-9,+-*/=按键
2 private:
3     CButton btn_number[10];
4     CButton btn_operator[5];
5 
6 public:
7     // wchar_t result[100];
8     CString result;

用CClientDC dc(this);
dc.TextOutW(0, 0, LPCTSTR(result + value));

进行输出的结果(即直接在View上输出字符):

但是问题是,接着计算时,没有清空上面输出的字符,而是直接覆盖(理论上也是可以解决的。

无妨,本来就存在缺陷,
至此,这个计算器应用程序勉强可以用一下,
下一篇继续改进。MFC实现计算器02之对话框实现
完整文件有时间整理一下。


 2022-08-11回来改进:关于上面说的,没有清空TextOut输出的字符问题,TextOut清除 解决TextOut输出重叠 
在TextOut函数前加上两行代码就行,完成!
InvalidateRect(&rc);    // rc -> 字符显示的矩形区域
UpdateWindow();
----------------------------------- 本程序中就是下面两行代码;
InvalidateRect(&CRect(0,0,200,50));
UpdateWindow();

11:26:18又回来了,这里明明还有其他方法,当初怎么没想到。。。。就孙鑫教的那个,直接把它抹白,然后重新输出就好了。。。当初竟然忘记了,还苦苦寻找方案。。。。

 

2022-08-02(0718。。。。)

posted on 2022-08-02 22:57  长风青云  阅读(192)  评论(0)    收藏  举报