在控制台真正实现选择菜单(可用方向键选择)|小知识

发布于 2021-07-10  21 次阅读


在控制台实现选择菜单

制作起因

做这个的原因是以前每次制作主菜单时都是输入数字选择,感觉虽然不麻烦,但是很不直观,不像正常主菜单方向键可以上下移动选择按钮,这一次写代码的时候就觉得干脆改了好了,所以想要改成正常样子

效果图片

在这里插入图片描述
按方向键选择移动方向,回车确定选择

完成原理

在说完成原理之前我们先来想想,我们要在控制台实现选择需要有哪些工作呢?

1、往上移动的时候光标也要跟着往上,往下的时候光标也要往下 (平时的菜单也是,选到什么按键,什么就会高亮) 2、要实现选择的按钮高亮 3、按回车键能够跳转 (要注意!我们给用户看到的逻辑并不是我们内部的逻辑,不是说按下去按钮就真的有个按钮,而是视觉上是这样的,而代码实现又是不一样的)

1、光标移动

首先要在控制台要控制光标位置,所以我们写一个光标定位函数

//定位函数
void gotoxy(int x, int y)
{
    COORD pos = { x,y };
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hOut, pos);
}

COORD pos = { x,y };是表示pos坐标位置为参数对应的(x,y)

COORD是Windows API中定义的一种结构,表示一个字符在控制台屏幕上的坐标。其定义为:
typedef struct _COORD {
SHORT X; // horizontal coordinate
SHORT Y; // vertical coordinate
} COORD;

HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);创建一个hOut句柄来接收标准输出句柄

SetConsoleCursorPosition(hOut, pos);该函数设置控制台(cmd)光标位置 使用这个函数需要两个参数:第一个参数类型为HANDLE,第二个参数类型为COORD(获得设备句柄hOut后,设置光标位置为pos)

有了这个函数,我们就可以任意控制光标的位置了

然后我们要让光标“听我们的话”,跟着方向键走
首先我做了个测试发现
(注意:读取方向键并不是一次读取单个,而是会有两个字符读入)

while (1) {
        if (c1 = getch()) {
            c2 = getch();
        }
    }

分别按方向键上下左右

上(UPKEY)
在这里插入图片描述

下(DOWNKEY)
在这里插入图片描述
左(LEFTKEY)
在这里插入图片描述
右(RIGHTKEY)
在这里插入图片描述
整理一下我们发现不管按什么键
首个c1字符都是同一个值 -32”?“,我估计应该是用来判断是否为方向键的标识字符
其次c2字符排序后分别为72 75 77 80,是有一定规律的,知道这些我们就可以完成移动光标啦

//定义方向键(这里我做了下修改均减37)
#define UpKey 35
#define DownKey 43
#define LeftKey 38
#define RightKey 40
char c1 = 'a', c2 = 'a', ch = 'a';//初始化为‘a’
int line= 3;//行数,初始设为3(可以设置为你想要初始光标的位置)
//主菜单(建议自己写一个,这里只是简易说明主菜单样子)
void Menu()
{
    printf("*****\n");
    printf("主菜单\n");
    printf("*****\n");
    printf("选项1\n");
    printf("选项2\n");
    printf("退出3\n");
    printf("*****\n");
}
//只写了完成的基本要素,并没有对他加以限制
Menu();//原本已经有的主菜单
gotoxy(0, line);//初始光标所在位置

while((c1 = getch())!='\r') 
        {
            c2 = getch();
            if (c1== -32)
            {
                ch = c2 - 37;
            }
            //向上移动
            if (ch== UpKey)
            {
                line -= 1;//向上移动                            
                gotoxy(0, line);
            }
            //向下移动
            if (ch == DownKey)
            {
                line += 1;
                gotoxy(0, line);
            }
        }

上面便可以完成简单的移动操作,line=3是初始光标所在行数,当按方向键UPDOWN时,line--;然后调用gotoxy改变光标位置向上移动一行,向下同理,左右的话则是改变x坐标,可以写个列变量,也是同理
(注意,上面代码只是实现基本移动,但我们没有对line做限制,对比如万一向上超过了我们可选的范围,程序是会出问题的,后面第三步再加以完善)

2、实现按钮高亮

printf彩色输出
是通过控制台的转义序列来实现的,这是文本模式下的系统显示功能。转义序列是以ESC开始的,即\033,ESC的十进制是27,转为八进制就是33了。
通用格式大致为ESC[{attr1};{attr2};…;{attrn}m,其中attr表示的是属性,也是属性值,通常直接就是数字表示了。在此我们进行三个常用属性的设置,主要是显示方式字体颜色背景色

显示方式
0(默认值)
1(高亮显示,顺便加粗?不确定)
2(暗淡)
22(非粗体,不确定)
4(下划线)
5(闪烁,但是我没看出有什么效果。。)
25(非闪烁)
7(反显,我也没看出效果)
27(非反显)
8(隐藏)
字体颜色
30(黑色)
31(红色)
32(绿色)
33(黄色)
34(蓝色)
35(品红)
36(青色)
37(白色)
背景色
40(黑色)
41(红色)
42(绿色)
43(黄色)
44(蓝色)
45(品红)
46(青色)
47(白色)

测试代码

printf("\033[4;31;42m*             1.直接插入排序                      *\033[0m");

效果图
在这里插入图片描述

利用这个我们可以在光标定位好后printf彩色输出,得到高亮效果(如果你想要按下去也有效果,可以在回车按下后在代码里也添加其他颜色printf彩色输出)

代码:

Menu();//原本已经有的主菜单
gotoxy(0, line);
        printf("\033[4;31;42m选项1\033[0m");
while((c1 = getch())!='\r') 
        {
            c2 = getch();
            if (c1== -32)
            {
                ch = c2 - 37;
            }
            //向上移动
            if (ch== UpKey)
            {
                line -= 1;//向上移动                            
                gotoxy(0, line);
                printf("\033[4;31;42m选项1\033[0m");
            }
            //向下移动
            if (ch == DownKey)
            {
                line += 1;
                gotoxy(0, line);
                printf("\033[4;31;42m选项2\033[0m");
            }
        }

(但是上述逻辑并不完整,首先你最开始在选项一,此时按上就会有问题,其次,你按downkey虽然到的了选项二,会发现原本的选项一的高亮还存在,后面第三步会加以完善)

3、按回车键能够跳转

我们前面只是完成视觉上的显示,但是并不是真正能按下选择按钮,这里我们添加变量choice用来完成正确选择

int choice=1//因为光标在选项1上,所以就默认为1
do
    {
        Menu();
        gotoxy(0, line);
        printf("\033[4;31;42m*选项1\033[0m");
        while((c1 = getch())!='\r') 
        {
            c2 = getch();
            if (c1== -32)
            {
                ch = c2 - 37;
            }
            if (ch== UpKey&&choice>1)
            {
                choice-= 1;
                if (choice==1)
                {
                    gotoxy(0, line);
                    printf("选项2");
                    line -= 1;
                    gotoxy(0, line);
                    printf("\033[4;31;42m选项1\033[0m");
                }
                if (choice==2)
                {
                    gotoxy(0, line);
                    printf("退出3");
                    line -= 1;
                    gotoxy(0, line);
                    printf("\033[4;31;42m选项2\033[0m");
                }
            }

            if (ch == DownKey && choice<3)
            {
                choice+= 1;
                if (choice == 2)
                {
                    gotoxy(0, line);
                    printf("选项1");
                    line += 1;
                    gotoxy(0, line);
                    printf("\033[4;31;42m选项2\033[0m");
                }
                if (choice == 3)
                {
                    gotoxy(0, line);
                    printf("选项2");
                    line += 1;
                    gotoxy(0, line);
                    printf("\033[4;31;42m退出3\033[0m");
                }
            }
        }
        //重载光标到正确位置
        gotoxy(0, 8);
        //具体实现选择的代码
        switch (choice)
        {
        case 1:
            break;
        case 2:
            break;
        case 3:
            break;
        default:
            printf("请不要乱输入选项!\n");
            getch();
            system("cls");
            break;
        }
    } while (choice!=3);

这一大段代码里基本实现了选择功能,还加了限制

选择功能

在移动光标过程中利用choice记录当前选项,在按回车后退出
在利用switch进行选择

限制
1、
例如

if (ch== UpKey&&choice>1)

只有在按UPKEY的时候choice是大于1的才会执行,即当choice为1,此时光标在最上面,不会让光标再往上了,同理在按DOWNKEY也不会让光标在最下面时再往下
2、
例如

if (choice==1)
                {
                    gotoxy(0, line);
                    printf("选项2");
                    line -= 1;
                    gotoxy(0, line);
                    printf("\033[4;31;42m选项1\033[0m");
                }

当你choice为1,这里光标首先得在选项2这,把他的高亮复原,再line--;
把选项1高亮

结语

这样基本就实现完成啦,如果要把代码实现更好看,可以尝试添加按键音效,以及改变高亮弄成渐变色等,具体的实现还是要看个人做法,大概就这样啦,如果文章有什么问题欢迎指正,也欢迎大家提问!