|
|
本帖最后由 nemon 于 2013-1-25 15:44 编辑
话说奇侠镇上铜壶客栈里最近生意着实不错。皆因书生吕轻侯以一条三寸不烂之舌以柔克刚,轻取大魔头肌无力之性命。那书生本欲事了拂衣去,深藏功与名。谁料差役多事,将此报与上峰。上峰闻之大喜“此等流窜惯犯,入我辖境而击毙。不占发案率,却有结案功,岂可不报?”于是为吕书生申评得一“关中大侠”名号,而各级府衙层层邀功,逐级有奖,皆大欢喜。放下官府不表,单说书生文弱,得此封号,恐惧有江湖人士前来挑战切磋,终日里躲于柜台之后,惶惶不敢见人,偶有携兵器者入店,更是称病不出。常宅于室,偶翻得一《48小时精通XXX语言》,略加修习,竟至小成,只是之前八股训诂学得实在太久,经常穿凿附会、咬文嚼字、固执的很,实在令人啼笑皆非。
这一日,客栈之中来一古怪之人,形貌混乱,身负ThinkPad一台,倒也不和他人争抢电源,进门之后就在门口一坐,打开笔记本,盯着屏幕一言不发。书生凑过去一看,客人正在调GUI,就问:“您是现在点菜啊,还是先来壶茶?”
客人抬头便说“菜单拿来”,说罢突然眉头紧锁,似有心事。
书生见状,心道莫不是这位挨踢人士身上不方便,没有现银?这要是吃霸王餐,老板娘要扣自己工钱的啊。于是说“客官有何心事?”
客人说“自昨夜至今日此时,做一菜单界面,谁料一直编译不过,故而心中烦忧。”
书生闻此,忽然想起日前所看一古董文章,于是说道:“先生此言差矣!《计算机科学技术名词》早有规定,‘选单(menu)’过去多处从英文直译成‘菜单’,考虑到术语的科学性及避免与生活用语混淆,定为‘选单’,而不用‘菜单。因此说,您连做的东西叫什么都不知道,这界面自然编译不过去了。"
客人听了,言道“真是奇谈!我不管什么‘选单’、‘菜单’,反正你我都知道这是什么。再说,我叫它‘选单’,难不成就编译过去了?你这跑堂实在聒噪,快去拿碗面来!”
书生闻听,怪脾气上来了,说“此正所谓‘乌不为乌,鹊不为鹊’也,子曾经曰过的……”
正在这时,早在店门外观望多时的钙帮五袋弟子小米突然插话了“什么曰不曰的,关中大侠,你倒是上菜去啊,小心老板娘扣你工钱。”又转过头对客人道“喏,这里,少了个分号。”
霎时间店中一片寂静。须臾之后,传来了挨踢人士和关中大侠的呜咽之声“情何以堪,情何以堪啊……”
沧海桑田,世事变迁。楼主几番探访,终于从钙帮板主手中拿到了这套令人荡气回肠的代码,以飨诸君。
先看一下实现的效果——用普通的串口调试工具做显示界面。
首先打开串口调试助手(
),配好串口和波特率打开串口,然后按下板子上的reset:
这个就是传说中的煮菜单了,从左到右依次是:父级菜单id、快捷键、菜单类型、是否选中标志、菜单命令行handle、菜单项内容。
按1,这是第一个菜单项(菜单命令行handle为8)的快捷键,就会变成这样:
“command 0x8 is be selected”表示菜单命令行handle为8的command菜单被选中了。
然后又回到了煮菜单,那么我们按2,这是第2个菜单项(菜单类型为0,表示有子菜单)的快捷键:
进入了第二个菜单项的子菜单,所有的菜单类型都为2,表示这是单选菜单组,也就是说,在这一级菜单中只能有一个菜单项的“是否选中标志”可以为1,下面是分别按快捷键a、b、c之后的效果:
看完了单选菜单组,我们按“@”号退出到上一级菜单:
然后按3,这是第3个菜单项的快捷键:
于是进入到多选菜单组里了,这里的菜单类型都是1,表示可以单独选中。可以看到初始状态是A为0、B为1,下面我们按A:
再按B:
再按A:
再按B:
然后按@到上一级:
这次按4,这又是一个有子菜单的项目:
出来了3个,都是可以响应的菜单,handle分别为0xa、0xb、0xc,我们按1选第一个:
响应了,再按2:
也响应了,在按3,注意这次会进入一个收集多个字符输入的过程,退出方式也是按@号:
所以我们输入了“12345697890123456789@”之后,就退出了处理过程,并且回显在控制台上:
之后按@退出就可以回到上级菜单了:
之后无论按多少次@都会在这个界面上不动,因为这是顶层菜单。
这个菜单系统的核心是数据结构定义,下面直接把h文件贴出来,附注解(话说这个注解附的过程很艰辛啊,Atmel Studio里输入了全角字符之后,Ctrl Y和CtrlZ的功能会暴走的,大段的代码会随机选择互相排列组合的穿插重组,比股市还乱……)- /*
- * vConsoleMenu.h
- *
- * Created: 2013-1-24 16:44:32
- * Author: Nemon
- */
- #ifndef VCONSOLEMENU_H
- #define VCONSOLEMENU_H
- #include <stdio.h>
- #include <string.h>
- #define MENU_LABEL_LENGTH 16 //每个菜单项的长度限制(半角)
- #define MENU_TYPE_COMMAND 3 //类型:命令菜单,触发 ptrFuncMenuOnCommand
- #define MENU_TYPE_PARENT 0 //类型:单选菜单,触发 ptrFuncMenuOnSubMenu
- #define MENU_TYPE_CHECK 1 //类型:多选菜单,触发 ptrFuncMenuOnSelected
- #define MENU_TYPE_OPTION 2 //类型:单选菜单,触发 ptrFuncMenuOnSelected
- #define MENU_ID_ROOT 0 //根菜单用必须0,不能变更
- typedef unsigned int MENU_COMMAND_DATA_TYPE ; //菜单命令ID的类型定义,注意低三位用于存储菜单类型和是否被选
- //以下掩码和定义用于将菜单命令ID和菜单类型存储在 MENU_COMMAND_DATA_TYPE 里
- #define MENU_TYPE_SHIFT 0
- #define MENU_TYPE_MASK 0x3
- #define MENU_CHECK_SHIFT 2
- #define MENU_CHECK_MASK 0x1
- #define MENU_CHECK_MASK_1 0x0004 //bin(0000000000000100)
- #define MENU_CHECK_MASK_0 0xfffb //bin(1111111111111011)
- #define MENU_CHECK_TRUE 1
- #define MENU_CHECK_FALSE 0
- #define MENU_COMMAND_SHIFT 3
- #define MENU_COMMAND_NONE 0
- #define MENU_GET_TYPE(menu_type_and_cmd) (((menu_type_and_cmd)>>MENU_TYPE_SHIFT)&MENU_TYPE_MASK)
- #define MENU_GET_CHECKED(menu_type_and_cmd) (((menu_type_and_cmd)>>MENU_CHECK_SHIFT)&MENU_CHECK_MASK)
- #define MENU_GET_CMD_ID(menu_type_and_cmd) (((menu_type_and_cmd)>>MENU_COMMAND_SHIFT))
- //MENU_COMMAND_DATA_TYPE相关的掩码和定义结束
- //每个菜单项的定义,依次是:父级菜单、菜单命令ID&单类型&是否被选、菜单显示内容、快捷键
- struct stCosnoleMenu{
- unsigned int menu_parent_id;
- MENU_COMMAND_DATA_TYPE menu_type_and_cmd;//type[0:1]select[2]cmd_id[4:]
- char menu_label[MENU_LABEL_LENGTH];
- char shortcut_key;
- };
- //菜单响应函数的定义
- typedef void(*MENU_FUNC_EVENT_ON_SELECTED)(unsigned int ui_ID);
- //生成菜单响应函数的宏(限制形参名)
- #define MENU_DEFINE_SELECTED_EVENT_FUNC(funcname) void funcname(unsigned int uiID)
- //默认的菜单响应函数(啥都不干)
- void Cosnole_DefaultMenuOnSelected(unsigned int uiID);
- #define MENU_DEFAULT_SELECTED_EVENT_FUNC Cosnole_DefaultMenuOnSelected
- //菜单的定义,依次是:菜单项的个数(注意不包括有根菜单0)、所有菜单项的存储数组、当前的父级菜单ID、最后是三个响应函数
- struct stCosnoleMenuRoot{
- unsigned int menu_length;
- struct stCosnoleMenu *root;
- unsigned int menu_current_parent;
- MENU_FUNC_EVENT_ON_SELECTED funcMenuOnSelected ;
- MENU_FUNC_EVENT_ON_SELECTED funcMenuOnCommand ;
- MENU_FUNC_EVENT_ON_SELECTED funcMenuOnSubMenu ;
- };
- //初始化一个菜单定义,参数参考struct stCosnoleMenuRoot 定义
- void Cosnole_Menu_Init
- (
- struct stCosnoleMenuRoot *stCosnoleMenuRoot
- ,struct stCosnoleMenu *ptr_root
- ,char s_menu_label_back_string[MENU_LABEL_LENGTH]
- ,MENU_FUNC_EVENT_ON_SELECTED ptrFuncMenuOnSelected
- ,MENU_FUNC_EVENT_ON_SELECTED ptrFuncMenuOnCommand
- ,MENU_FUNC_EVENT_ON_SELECTED ptrFuncMenuOnSubMenu
- );
- //增加一个菜单项,参数参考struct stCosnoleMenuRoot 定义
- unsigned int Cosnole_Add_A_Menu(struct stCosnoleMenuRoot *stRoot,unsigned int ui_menu_parent_id,MENU_COMMAND_DATA_TYPE i_menu_cmd_id,char s_menu_label[MENU_LABEL_LENGTH],char c_menu_type,char c_menu_checked,char c_shortcut_key);
- //向指针 stRoot 所指的菜单发送 uiMenuID 项被选中的消息
- void Cosnole_MenuOnSelected (struct stCosnoleMenuRoot *stRoot,unsigned int uiMenuID);
- //向指针 stRoot 所指的菜单发送 c_short_key 键被选中的消息
- void Cosnole_MenuOnSelected_by_ShortKey (struct stCosnoleMenuRoot *stRoot,char c_short_key);
- //向指针 stRoot 所指的菜单发送 返回上级菜单的消息
- void Cosnole_MenuBackUp (struct stCosnoleMenuRoot *stRoot);
- //向指针 stRoot 所指的菜单发送 将当前级下的不超过uiMenuCount个的菜单,填入menuBuff所指的数组中
- unsigned int Cosnole_FillCurrentMenus(struct stCosnoleMenuRoot *stRoot,struct stCosnoleMenu **menuBuff,unsigned int uiMenuCount);
- //这个用于直接定义 stCosnoleMenuRoot.root 所指的数组用,未测试
- #define MENU_DEFINE_A_MENU(ucPID,cLabel,ucCommandID,ucType,ucChecked) {(ucPID),(cLabel),((ucCommandID)<<MENU_COMMAND_SHIFT)|((ucType)<<MENU_TYPE_SHIFT)|((ucChecked)<<MENU_CHECK_SHIFT)}
- #endif //VCONSOLEMENU_H
复制代码 看过了头文件,c文件170行,这里就不贴了,来看一下实例吧。
引入和自定义的部分:- #include "vConsoleMenu.h"
- #define MENU_BUFF_SIZE 20
- #define MENU_VIEW_SIZE 5
- #define VIEW_CURRENT_MENU show_current_menu
- struct stCosnoleMenuRoot stMenu;
- void show_current_menu(struct stCosnoleMenuRoot *stRt)
- {
- struct stCosnoleMenu *stTmpMenu[MENU_VIEW_SIZE];
- int i,iCnt=Cosnole_FillCurrentMenus(stRt,stTmpMenu,MENU_VIEW_SIZE);
- puts("-- vConsoleMenu '@'to back.--\r\n");
- for(i=0;i<iCnt;i++)
- {
- printf("%d %c %x %x %x %s\r\n"
- ,stTmpMenu[i]->menu_parent_id
- ,stTmpMenu[i]->shortcut_key
- ,((stTmpMenu[i]->menu_type_and_cmd)>>MENU_TYPE_SHIFT)&MENU_TYPE_MASK
- ,((stTmpMenu[i]->menu_type_and_cmd)>>MENU_CHECK_SHIFT)&MENU_CHECK_MASK
- ,(stTmpMenu[i]->menu_type_and_cmd)>>MENU_COMMAND_SHIFT
- ,stTmpMenu[i]->menu_label);
- }
- }
- void my_command_event(struct stCosnoleMenuRoot *stCosnoleMenuRoot,unsigned int ui_ID)
- {
- printf("command 0x%x is be selected\r\n",ui_ID);
- if(0xc==ui_ID)
- {
- uint8_t uc_char;
- unsigned int ui_len=0;
- char pc_buff[512];
- for(ui_len=0;ui_len<512;ui_len++)pc_buff[ui_len]=0;
- ui_len=0;
- uc_char=0;
- uart_read(CONSOLE_UART, &uc_char);
- while(uc_char!='@' && ui_len<512-2)
- {
- if(uc_char>=32 && uc_char<=126 )
- {
- pc_buff[ui_len++]=uc_char;
- uc_char=0;
- }
- uart_read(CONSOLE_UART, &uc_char);
- }
- pc_buff[ui_len]=0;
- printf("%s",pc_buff);
- puts("\r\n");
- }
- show_current_menu(&stMenu);
- }
- void my_submenu_event(struct stCosnoleMenuRoot *stCosnoleMenuRoot,unsigned int ui_ID)
- {
- show_current_menu(&stMenu);
- }
- void my_selected_event(struct stCosnoleMenuRoot *stCosnoleMenuRoot,unsigned int ui_ID)
- {
- show_current_menu(&stMenu);
- }
复制代码 然后这么用:- int i,n,iCnt;
- char console_buff[1000];
- struct stCosnoleMenu st_CosnoleMenu_buff[MENU_BUFF_SIZE];
- //Cosnole_Menu_Init(&stMenu,stCosnoleMenu,"back up",MENU_DEFAULT_SELECTED_EVENT_FUNC,MENU_DEFAULT_SELECTED_EVENT_FUNC,MENU_DEFAULT_SELECTED_EVENT_FUNC);
- Cosnole_Menu_Init(&stMenu,st_CosnoleMenu_buff,"back up",my_selected_event,my_command_event,my_submenu_event);
- iCnt=Cosnole_Add_A_Menu(&stMenu,0,0x8,"the 1st menu",MENU_TYPE_COMMAND,MENU_CHECK_TRUE,'1');//1
- iCnt=Cosnole_Add_A_Menu(&stMenu,MENU_ID_ROOT,MENU_COMMAND_NONE,"the options" ,MENU_TYPE_PARENT ,MENU_CHECK_TRUE,'2');//2
- Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"option 1" ,MENU_TYPE_OPTION ,MENU_CHECK_FALSE,'a');//3
- Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"option 2" ,MENU_TYPE_OPTION ,MENU_CHECK_TRUE ,'b');//4
- Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"option 3" ,MENU_TYPE_OPTION ,MENU_CHECK_FALSE,'c');//5
- iCnt=Cosnole_Add_A_Menu(&stMenu,MENU_ID_ROOT,MENU_COMMAND_NONE,"the check" ,MENU_TYPE_PARENT ,MENU_CHECK_TRUE,'3');//6
- Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"check n" ,MENU_TYPE_CHECK ,MENU_CHECK_FALSE,'A');//7
- Cosnole_Add_A_Menu(&stMenu,iCnt,MENU_COMMAND_NONE,"check y" ,MENU_TYPE_CHECK ,MENU_CHECK_TRUE ,'B');//8
- iCnt=Cosnole_Add_A_Menu(&stMenu,MENU_ID_ROOT,MENU_COMMAND_NONE,"parent" ,MENU_TYPE_PARENT ,MENU_CHECK_TRUE,'4');//9
- Cosnole_Add_A_Menu(&stMenu,iCnt,0xa,"menu 1" ,MENU_TYPE_COMMAND ,MENU_CHECK_FALSE,'1');//10
- Cosnole_Add_A_Menu(&stMenu,iCnt,0xb,"menu 2" ,MENU_TYPE_COMMAND ,MENU_CHECK_TRUE ,'2');//11
- Cosnole_Add_A_Menu(&stMenu,iCnt,0xc,"get a line" ,MENU_TYPE_COMMAND ,MENU_CHECK_TRUE ,'3');//12
- show_current_menu(&stMenu);
- while (1) {
- uc_char = 0;
- uart_read(CONSOLE_UART, &uc_char);
- switch (uc_char) {
- case 0x0:
- break;
- case '@':
- Cosnole_MenuBackUp(&stMenu);
- break;
- default:
- Cosnole_MenuOnSelected_by_ShortKey (&stMenu,uc_char);
- break;
- }
- }
复制代码 以上代码,不知是否算是又造了一次轮子呢?反正后面的实验全靠它了。
哦,还有,这是完整的Menu库文件:
|
|