建立一個樹狀的菜單結(jié)構(gòu),用鏈表實現(xiàn)鏈表中包含:
本文引用地址:http://m.butianyuan.cn/article/201611/322426.htm1、指向同級左右菜單和指向父菜單、子菜單的四個菜單結(jié)構(gòu)體指針;
2、進入該菜單時需要執(zhí)行的初始化函數(shù)指針
3、退出該菜單時需要執(zhí)行的結(jié)束函數(shù)指針
4、該菜單內(nèi)的按鍵處理函數(shù)指針數(shù)組的指針操作菜單模塊需要的按鍵操作有:左、右、確
認(rèn)、退出。
采用這種辦法,可以方便的添加或刪減菜單。并且只需要在其頭文件中修改初始變量就可
以實現(xiàn),完全無須修改C文件中的任何函數(shù)。
具體結(jié)構(gòu)定義
我的定義,做個參考:
#defineMENU_HLP_EN//菜單幫助信息使能
typedef struct
{
void (*pMenuTaskInit)(void);//指向菜單任務(wù)初始化函數(shù)的指針
void (*pMenuTaskEnd)(void);//指向菜單任務(wù)結(jié)束函數(shù)的指針
}MENU_TASK_TYP;
typedef struct MenuTyp
{
INT8U*MenuName;//菜單名稱字符串
WORK_MODWorkMod;//工作狀態(tài)編號
MENU_TASK_TYP*pMenuTask;//指向菜單任務(wù)的指針
void (**pTaskKeyDeal)(void);//指向菜單任務(wù)按鍵處理函數(shù)數(shù)組的指針
#ifdef MENU_HLP_EN
INT8U*MenuHlp;//菜單幫助字符串
#endif
struct MenuTyp*pParent;//指向上層菜單的指針
struct MenuTyp*pChild;//指向子菜單的指針
struct MenuTyp*pRight;//指向右菜單的指針
struct MenuTyp*pLeft;//指向左菜單的指針
}MENU_TYP;
我根據(jù)網(wǎng)上的資料做的一個菜單:
struct KeyTabStruct{
uint8MenuIndex;//當(dāng)前狀態(tài)索引號
uint8MaxItems;//本級菜單最大條目數(shù)
uint8ShowLevel;//菜單顯示內(nèi)容
uint8PressOk;//按下"回車"鍵時轉(zhuǎn)向的狀態(tài)索引號
uint8PressEsc;//按下"返回"鍵時轉(zhuǎn)向的狀態(tài)索引號
uint8PressDown;//按下"向下"鍵時轉(zhuǎn)向的狀態(tài)索引號
uint8PressUp;//按下"向上"鍵時轉(zhuǎn)向的狀態(tài)索引號
void(*CurrentOperate)();//當(dāng)前狀態(tài)應(yīng)該執(zhí)行的功能操作
};
uint8MenuID;//菜單ID號
uint8MenuNextID;//下級菜單ID號
//CurMenuID=本菜單ID
//MaxMenuItem=同級菜單最大項數(shù)
//OkMenuID=子菜單層所對應(yīng)的菜單ID,ID=999為菜單已經(jīng)到底了
//EscMenuID=父菜單層所對應(yīng)的菜單ID,ID=999為菜單已經(jīng)到頂了
//DownMenuID=弟菜單層所對應(yīng)的菜單ID,ID=999為菜單是獨生子
//UpMenuID=兄菜單層所對應(yīng)的菜單ID,ID=999為菜單是獨生子
//CurFunction=本菜單所對應(yīng)的菜單函數(shù)指針
const struct KeyTabStruct KeyTab[MAX_KEYTABSTRUCT_NUM]={
//CurMenuID,axMenuItem,MenuShowLevel,OkMenuID,EscMenuID,DownMenuID,UpMenuID,CurFunction
{MENU_EDIT,0,0,MENU_DATA_VIEW,MENU_NO,MENU_NO,MENU_NO,*MenuEdit},
{MENU_DATA_VIEW,3,1,MENU_DATA_VIEW_FIRE,MENU_EDIT,MENU_SYS_EDIT,MENU_PRINT_DATA,*MenuEdit},
{MENU_DATA_VIEW_FIRE,5,MENU_NO,MENU_NO,MENU_DATA_VIEW,MENU_DATA_VIEW_TROUBLE, MENU_STEP_FOLLOW, *MenuDataViewIn},
{MENU_DATA_VIEW_TROUBLE, 5,MENU_NO,MENU_NO,MENU_DATA_VIEW,MENU_DATA_VIEW_REPEAT,MENU_DATA_VIEW_FIRE,*MenuDataViewIn},
{MENU_DATA_VIEW_REPEAT,5,MENU_NO,
MENU_NO,MENU_DATA_VIEW,MENU_FACE_CHECK,
MENU_DATA_VIEW_TROUBLE,*MenuDataViewIn},
{MENU_FACE_CHECK,5,MENU_NO,
MENU_NO,MENU_DATA_VIEW,MENU_STEP_FOLLOW,
MENU_DATA_VIEW_REPEAT,*MenuFaceCheck},
{MENU_STEP_FOLLOW,5,MENU_NO,
MENU_NO,MENU_DATA_VIEW,MENU_DATA_VIEW_FIRE,MENU_FACE_CHECK,
*MenuStepFollow},
{MENU_SYS_EDIT,3,
2,MENU_SUM_SET,MENU_EDIT,
MENU_PRINT_DATA,MENU_DATA_VIEW,*MenuEdit},
{MENU_SUM_SET,6,MENU_NO,
MENU_NO,MENU_SYS_EDIT,MENU_EDIT_INSULATE,
MENU_TIME_SET,*MenuSumSet},
{MENU_EDIT_INSULATE,6,MENU_NO,
MENU_NO,MENU_SYS_EDIT,MENU_EDIT_HZ,MENU_SUM_SET,
*MenuEditInsulate},
{MENU_EDIT_HZ,6,MENU_NO,
MENU_NO,MENU_SYS_EDIT,MENU_LD_CONTROL,
MENU_EDIT_INSULATE,*MenuEditHZ},
{MENU_LD_CONTROL,6,
MENU_NO,MENU_NO,MENU_SYS_EDIT,MENU_LD_DELAY,
MENU_EDIT_HZ,*MenuLDControl},
{MENU_LD_DELAY,6,
MENU_NO,MENU_NO,MENU_SYS_EDIT,MENU_TIME_SET,
MENU_LD_CONTROL,*MenuLDDelay},
{MENU_TIME_SET,6,MENU_NO,
MENU_NO,MENU_SYS_EDIT,MENU_SUM_SET,MENU_LD_DELAY,
*MenuTimeSet},
{MENU_PRINT_DATA,3,3,
MENU_PRINT_DATA_FIRE,MENU_EDIT,MENU_DATA_VIEW,
MENU_SYS_EDIT,*MenuEdit},
{MENU_PRINT_DATA_FIRE,4,
MENU_NO,MENU_NO,MENU_PRINT_DATA,
MENU_PRINT_DATA_TROUBLE,MENU_PRINT_SET,*MenuPrintDataIn},
{MENU_PRINT_DATA_TROUBLE,4,MENU_NO,
MENU_NO,MENU_PRINT_DATA,MENU_PRINTER_CHECK,
MENU_PRINT_DATA_FIRE,*MenuPrintDataIn},
{MENU_PRINTER_CHECK,4,MENU_NO,
MENU_NO,MENU_PRINT_DATA,MENU_PRINT_SET,
MENU_PRINT_DATA_TROUBLE,*MenuPrintDataIn},
{MENU_PRINT_SET,4,MENU_NO,
MENU_NO,MENU_PRINT_DATA,MENU_PRINT_DATA_FIRE,
MENU_PRINTER_CHECK,*MenuPrintSet},
};
const struct MenuDispDataMenuEditShow[][MENU_MAX] = {
{{MENU_NO, 0, 0, "選擇:消音→退出"},//主菜單
{MENU_DATA_VIEW, 1, 6, "⒈數(shù)據(jù)查看"},
{MENU_SYS_EDIT, 2, 6, "⒉系統(tǒng)編程"},
{MENU_PRINT_DATA, 3, 6, "⒊數(shù)據(jù)打印"}},
{{MENU_NO, 0, 0, "數(shù)據(jù)查看:消音→退出"},//數(shù)據(jù)查
看
{MENU_DATA_VIEW_FIRE, 1, 4, "⒈火警"},
{MENU_DATA_VIEW_TROUBLE, 2, 4, "⒉故障"},
{MENU_DATA_VIEW_REPEAT , 3, 4, "⒊重碼"},
{MENU_FACE_CHECK, 1,12, "⒋面板檢測"},
{MENU_STEP_FOLLOW, 2,12, "⒌單步跟蹤"}},
{{MENU_NO, 0, 0, "系統(tǒng)編程:消音→退出"},//系統(tǒng)編程
{MENU_SUM_SET, 1, 0, "⒈容量設(shè)置"},
{MENU_EDIT_INSULATE, 2, 0, "⒉隔離點"},
{MENU_EDIT_HZ, 3, 0, "⒊漢字描述"},
{MENU_LD_CONTROL, 1,12, "⒋聯(lián)動控制"},
{MENU_LD_DELAY, 2,12, "⒌模塊延時"},
{MENU_TIME_SET, 3,12, "⒍時鐘調(diào)整"}},
{{MENU_NO, 0, 0, "數(shù)據(jù)打印:消音→退出"},//數(shù)據(jù)打印
{MENU_PRINT_DATA_FIRE, 1, 0, "⒈火警數(shù)據(jù)"},
{MENU_PRINT_DATA_TROUBLE,2, 0, "⒉故障數(shù)據(jù)"},
{MENU_PRINTER_CHECK, 3, 0, "⒊打印機自檢"},
{MENU_PRINT_SET, 1,12, "⒋打印設(shè)置"}},
};
void WaitKey(void)
{
uint32 time;
time = RTCFlag;
WhichKey = KEY_NONE;
while(!EscFlag){
if(RTCFlag - time >= EDIT_TIME)
EscFlag = TRUE;
if(WhichKey != KEY_NONE){
KeySound(300);//按鍵音
return;
}
}
}
void MenuEdit()
{
uint32 i,j=0;
uint32 oldid;
j = KeyTab[MenuID].ShowLevel;
if(WhichKey == KEY_ESC || WhichKey == KEY_OK){
ClearScreen();
for(i=0;i
ShowString(MenuEditShow[j][i].Lin,MenuEditShow[j]
[i].Column,MenuEditShow[j][i].Pdata,0);//初始化顯示
oldid =
0;
//沒有原先選擇的項
}else{
if(WhichKey == KEY_UP)
oldid = KeyTab[MenuNextID].PressDown;
else
oldid = KeyTab
[MenuNextID].PressUp;
//指示原先的項
}
for(i=1;i
if(MenuEditShow[j][i].Id == oldid)
ShowString(MenuEditShow[j][i].Lin,MenuEditShow[j]
[i].Column,MenuEditShow[j][i].Pdata,0);//正常顯示原先的項
else{
if(MenuEditShow[j][i].Id == MenuNextID)
ShowString(MenuEditShow[j][i].Lin,MenuEditShow
[j][i].Column,MenuEditShow[j][i].Pdata,1);//反顯當(dāng)前選擇的項
}
}
WhichKey = KEY_NONE;
}
uint32 Edit(void)
{
struct KeyTabStruct NowKeyTab;//指示當(dāng)前的菜單值
uint32 escflag = FALSE;
ResetFlag = FALSE;
ChangeFlag = FALSE;
EscFlag = FALSE;
MenuID = MENU_EDIT;
NowKeyTab = KeyTab[MenuID];
MenuNextID = NowKeyTab.PressOk;
(*NowKeyTab.CurrentOperate)();//顯示主菜單
do{
if(WhichKey == KEY_NONE)
WaitKey();//等待按鍵
switch(WhichKey){
case KEY_ESC : if(NowKeyTab.PressEsc != MENU_NO)
{
MenuID =
NowKeyTab.PressEsc;
MenuNextID =
NowKeyTab.MenuIndex;
NowKeyTab = KeyTab
[MenuID];
NowKeyTab.PressOk =
MenuNextID;
(*NowKeyTab.CurrentOperate)
();//顯示當(dāng)前菜單
}else
escflag =
TRUE;//退出編程狀態(tài)
break;
case KEY_OK:if(NowKeyTab.PressOk != MENU_NO)
{
MenuID =
NowKeyTab.PressOk;
NowKeyTab = KeyTab
[MenuID];
MenuNextID =
NowKeyTab.PressOk;
}
(*NowKeyTab.CurrentOperate)
();//執(zhí)行當(dāng)前按鍵的操作
break;
case KEY_UP:if((MenuNextID != MENU_NO) &&
(KeyTab[MenuNextID].PressUp != MENU_NO)){
NowKeyTab.PressOk =
KeyTab[MenuNextID].PressUp;
MenuNextID = KeyTab
[MenuNextID].PressUp;
(*NowKeyTab.CurrentOperate)();//執(zhí)行當(dāng)前按鍵的操作
}
break;
case KEY_DOWN:if((MenuNextID != MENU_NO) &&
(KeyTab[MenuNextID].PressDown != MENU_NO)){
NowKeyTab.PressOk =
KeyTab[MenuNextID].PressDown;
MenuNextID = KeyTab
[MenuNextID].PressDown;
(*NowKeyTab.CurrentOperate)();//執(zhí)行當(dāng)前按鍵的操作
}
break;
case KEY_RESET: ResetFlag = TRUE;
break;
default: break;
}
}while(!ResetFlag && !EscFlag && !escflag);
if(ChangeFlag && !EscFlag && !ResetFlag)
EditDataChange();
if(ResetFlag)
returnSYS_RESET;
else{
return0;
}
}
關(guān)于這個菜單的說明:
1.我用的是ARM處理器,所以51的時候把const改成code,uint32改成unsigned char。
2.在網(wǎng)上的資料中,結(jié)構(gòu)體數(shù)組是存在RAM中的,我把它放在也flash中了,然后再定義一個
結(jié)構(gòu)體變量,就樣就可以省很多RAM,比較適合51.
3.在網(wǎng)上資料中,因為保存了原來的選擇,當(dāng)你離開編程狀態(tài)重新進行后,會發(fā)現(xiàn)選擇上會
是原來進行的順序,我改動之后,退出上一級菜單還是你選的那一項,但重新進入后就是第
一個指定項。
4.增加UP和DOWN顯示,可以反顯最新選定的選項,正常顯示原來的選項。
評論