c++可视化操作(一)——控制台点击型菜单(windows.h)

起因:程序设计大作业(计算器、校园卡管理系统、象棋游戏)越来越复杂,设计界面和键盘输入报错过于繁琐。我查阅网络资料,发现windows.h属于我认识的范畴,于是开始查阅、学习。以下是部分小结,面向初学者。

部分名字名词

句柄(handle):

控制台(console):

光标(cursor):


二次封装

windows.h中,

有鼠标坐标的结构体COORD,并增加输出坐标、判定点击等功能

有鼠标事件的结构体MOUSE_EVENT_RECORD,含位置、按键、事件类型等信息

通过SetConsoleTextAttribute函数,设置控制台输出的颜色


console.hpp

#ifndef CONSOLE_HPP
#define CONSOLE_HPP
#include <windows.h>
#include <conio.h>
#include <iostream>
#include <iomanip>
using std::ios_base;
using std::ostream;
//dwButtonState 鼠标按钮状态
#define L_BUTTON 0x1
#define R_BUTTON 0x2

// dwEventFlags 鼠标事件状态
#define MOUSE_CLICK 0x0
#define MOUSE_MOVED 0x1
#define DOUBLE_CLICK 0x2

//颜色
#define COLOR_default SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
#define COLOR_Black_Cyan SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0b);
#define COLOR_Yellow_Blue SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xe9);

// 移动光标位置
void setcursor(short x = 0, short y = 0)
{
    COORD temp = {x, y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), temp);
}
void setcursor(const COORD &temp)
{
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), temp);
}

// 获得光标位置
void getcursor(COORD &other)
{
    CONSOLE_SCREEN_BUFFER_INFO temp;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &temp);
    other = temp.dwCursorPosition;
}
COORD getcursor()
{
    CONSOLE_SCREEN_BUFFER_INFO temp;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &temp);
    return temp.dwCursorPosition;
}
//输出光标坐标
ostream &operator<<(ostream &pout, const COORD &temp)
{
    pout.setf(ios_base::fixed);
    pout << "[Cursor Position] X: " << std::setw(2) << temp.X << "  Y: " << std::setw(2) << temp.Y;
    return pout;
}

//是否隐藏光标
void hidecursor(bool hide = true)
{
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CursorInfo);
    CursorInfo.bVisible = !hide;
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CursorInfo);
}

//button - click
//判断鼠标点击位置是否在指定的有效范围(本行,本列至5列之间)
bool operator-(const COORD &button, const COORD &click)
{
    if (button.Y == click.Y && button.X <= click.X && click.X <= button.X + 20)
        return true;
    else
        return false;
};

// 更改控制台设置,防止控制台属性导致的判定失败
void setmode()
{
    DWORD mode;
    GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode);
    mode &= ~ENABLE_QUICK_EDIT_MODE; //移除快速编辑模式
    mode &= ~ENABLE_INSERT_MODE;     //移除插入模式
    // mode &= ~ENABLE_MOUSE_INPUT;     //移除鼠标输入
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode);
}

//等待鼠标事件,可排除鼠标移动
MOUSE_EVENT_RECORD waitmouse(bool move = true)
{
    INPUT_RECORD record; //输入事件
    DWORD reg;           //临时寄存
    while (1)
    {
        Sleep(10);
        ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &record, 1, &reg);                                  //将输入事件存入record
        if (record.EventType == MOUSE_EVENT && (move | record.Event.MouseEvent.dwEventFlags != MOUSE_MOVED)) //是鼠标事件 && 移动事件与模式对应
            return record.Event.MouseEvent;
    }
}
// 清屏函数
void clean()
{
    COORD temp = getcursor();
    setcursor(0, 0);
    for (int i = 0; i <= temp.Y; i++)
        std::cout << "                                                                                                    \n";
    setcursor(0, 0);
}
//暂停
void pause()
{
    std::cout << "请按任意键继续\n";
    std::cin.sync();
    std::cin.get();
}
#endif

有关数据:

输入事件:(我会把与本次操作无关的部分注释掉)

struct INPUT_RECORD
{
    WORD EventType; //事件类型
    union
    {
        struct MOUSE_EVENT_RECORD
        {
            COORD dwMousePosition;   // 当前鼠标位置
            DWORD dwButtonState;     // 鼠标按钮状态
            DWORD dwControlKeyState; // 键盘控制键状态
            DWORD dwEventFlags;      // 鼠标事件状态
        } MouseEvent;
        // KEY_EVENT_RECORD KeyEvent;
        // WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
        // MENU_EVENT_RECORD MenuEvent;
        // FOCUS_EVENT_RECORD FocusEvent;
    } Event;
};

 部分有关常量 :

// 事件类型
#define KEY_EVENT 0x1      //键盘事件
#define MOUSE_EVENT 0x2    //鼠标事件

// 鼠标按钮状态
#define FROM_LEFT_1ST_BUTTON_PRESSED 0x1    //鼠标左键
#define RIGHTMOST_BUTTON_PRESSED 0x2        //鼠标右键

// 鼠标事件状态
#define MOUSE_MOVED 0x1    //移动
#define DOUBLE_CLICK 0x2   //双击 

有关颜色信息:

 

 


测试程序:

点击型菜单,点击相应选项(输出的字符)进入函数功能

#ifndef MENU_HPP
#define MENU_HPP
#include "console.hpp"
#include <vector>
using std::cout;
using std::endl;
using std::string;
using std::vector;
class Menu
{
private:
    struct node
    {
        COORD pos_;
        string display_;
        void (*pf_)();
        node(string d = "Unkown.", void (*p)() = nullptr) : display_(d), pf_(p){};
    };
    vector<node> nodes_;
    string title_;

protected:
    void recordposition()
    {
        for (auto p = nodes_.begin(); p != nodes_.end(); p++)
        {
            getcursor(p->pos_);
            cout << p->display_ << "\n\n";
        }
    };

    bool implement(COORD clickpos)
    {
        for (auto p = nodes_.begin(); p != nodes_.end(); p++)
        {
            if (p->pos_ - clickpos) //点击成功
            {
                hidecursor(0);
                clean();
                p->pf_();
                hidecursor(1);
                return true;
            }
        }
        return false;
    };
    void highlight(COORD hang)
    {
        clean();
        cout << title_ << endl //显示内容
             << "========================================" << endl;
        for (auto p = nodes_.begin(); p != nodes_.end(); p++)
        {
            if (p->pos_ - hang) //悬停点
            {
                COLOR_Black_Cyan;
                cout << p->display_ << "\n\n";
                COLOR_default;
            }
            else
                cout << p->display_ << "\n\n";
        }
    }

public:
    //constructor
    Menu(){};
    ~Menu(){};
    //methods
    Menu &settitle(string s)
    {
        title_ = s;
        return *this;
    }

    Menu &add(void (*p)() = nullptr, string d = "Unkown.")
    {
        nodes_.push_back(node(d, p));
        return *this;
    };

    void start()
    {
        setmode();
        MOUSE_EVENT_RECORD mouse;
        hidecursor(1);
        clean();
        cout << title_ << endl //显示内容
             << "========================================" << endl;
        recordposition(); //记录位置
        do
        {
            Sleep(10); //减少次数
            mouse = waitmouse();
            if (mouse.dwEventFlags == MOUSE_MOVED)
                highlight(mouse.dwMousePosition);
            else if (mouse.dwButtonState == L_BUTTON)
                implement(mouse.dwMousePosition);
        } while (mouse.dwButtonState != R_BUTTON);
        Sleep(100); //过滤
    };
};
#endif


void show1() { cout << "choice1\n"; }
void show2() { cout << "choice2\n"; }
int main(void)
{
    Menu loop;
    void (*p)();
    p = show1;
    loop.settitle("test").add(p, "show1").add(show2, "show2");
    loop.start();
}

参考信息:

https://blog.csdn.net/bnb45/article/details/8042819

https://blog.csdn.net/o_longzhong/article/details/80276645

  • 3
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岚花落_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值