前言
最近在做一个简单的界面,需要用到多级菜单,一开始使用的是传统的索引法,在修改时比较乱,在网上有用链表写的,虽然一致都在用c语言,却很少使用链表,于是今天早上便打算用链表制作一个多级菜单,经过一天的努力,终于完成了一个简单的框架,现在只可以添加菜单,而且没有级数限制。 目前只是在gcc下运行成功,还没有在单片机上运行。
主体
传统的双向链表可以寻找上下级的关系,而在多级菜单中还包括同级关系,因此链表的解构应该是树形,包括,父级,子级,同级上和同级下
这里定义一个结构体
typedef struct _menu
{
struct _menu * last;//父极菜单指针
struct _menu * same_last;//同级菜单下一项指针
struct _menu * same_next;//同级菜单上一项指针
char * menu_name;//当前功能名称
int id;//当前功能号
void (*funsion)();//功能
struct _menu*next;//子级菜单
}menu_t;
这里,我们还需要几个变量
1.我们需要知道链表当前位置
2.通过按键上下可以在同级之间切换,左右分别表示退出和返回,但是如果我们进入一个界面不想通过上下左右切换链表,所以我们还需要一个全局变量,当这个变量为1时是正常操作,当变量为非1时,我们将获取的按键值交给一个全局变量,故我们需要两个变量
struct {
menu_t *now;//当前链表位置
int ctrl;
char input;
}sys_info;
接下来便是创建表头
menu_t*add_funsion(menu_t *head,void * fun,char * str){ //添加一个功能 head为该界面的第一项,自动在该界面后添加一项功能
menu_t *p = NULL,*pr = head;
p = (menu_t *)malloc(sizeof(menu_t)) ;
if(p == NULL){
printf("NO enough momery to allocate!\n");
exit(0);
}
if(head == NULL){
head = p; //head为空,创建头节点
printf("创建头节点\n");
}else{ //如果不是创建头节点
int i=1;
while(pr->same_next!= NULL){ //找到同级菜单最后的表
{
i++;
pr = pr->same_next;
}
}
pr->same_next = p; //在同级菜单尾部创建一个功能
p->same_last=pr;
p->last=head->last;
p->id=i;
}
p->funsion=fun;
p->menu_name=str;
p->same_next = NULL;//下一个同级功能为空
p->next=NULL; //下一个子集功能为空
return head;
}
上面的函数中如果head是一个空指针,则创建头节点,如果不是空指针,则在其同级最后添加一个结构
然后是在一个目录功能下新建一个目录
menu_t * set_page(menu_t * head,int num)//输入为页的表头
{
for(int i=0;i<num;i++)
{
if(head->same_next!=NULL)
head=head->same_next;
else
{
printf("超出当前功能号\n");
}
}
if(head->next!=NULL)
head=head->next;
else
{
printf("创建子菜单\n");
menu_t *p = NULL;
p = (menu_t *)malloc(sizeof(menu_t)) ;
if(p == NULL){
printf("NO enough momery to allocate!\n");
exit(0);
}
head->next=p;
p->last=head;
p->id=0;//当前功能号为0
p->same_last=NULL;
p->same_next=NULL;
return p;
}
return head;
}
第一个head指针为当前目录真正,第二个num代表要在当前目录那一项创建子级,并将创建子级的指针返回,但是,需要注意的是返回的指针已经开辟了内存,并没有使用,因此需要对此指针内容进行赋值
void set_fun(menu_t *menu,void * fun,char * str)//设置函数和功能
{
menu->funsion=fun;
menu->menu_name=str;
}
至此主要函数就已经写完了
然后就是进入目录时的刷新
void fun1()
{
printf("%s","\033[1H\033[2J");//linux下的清屏函数,window下请用system("cls");
printf("使用键盘选择以下功能\n");
if(sys_info.now->last!=NULL)//如果父极不为空
{
menu_t * start=sys_info.now->last->next;//start指向同级第一个
while(start!=NULL)//将所有功能打在屏幕上,如果功能和选中功能一样,变色
{
if(start->menu_name==sys_info.now->menu_name)//
{
printf("********%s*********\n",start->menu_name);
}
else
{
printf(" ****%s****\n",start->menu_name);
}
start=start->same_next;
}
}
else
{
printf("父极为空");
}
}
由于是在linux下写完,清屏函数在window下需要system
下面是完整代码
#include <stdio.h>
#include <stdlib.h>
typedef struct _menu
{
struct _menu * last;//父极菜单指针
struct _menu * same_last;//同级菜单下一项指针
struct _menu * same_next;//同级菜单上一项指针
char * menu_name;//当前功能名称
int id;//当前功能号
void (*funsion)();//功能
struct _menu*next;//子级菜单
}menu_t;
struct {
menu_t *now;//当前链表位置
int ctrl;
char input;
}sys_info;
menu_t * set_page(menu_t * head,int num)//输入为页的表头
{
for(int i=0;i<num;i++)
{
if(head->same_next!=NULL)
head=head->same_next;
else
{
printf("超出当前功能号\n");
}
}
if(head->next!=NULL)
head=head->next;
else
{
printf("创建子菜单\n");
menu_t *p = NULL;
p = (menu_t *)malloc(sizeof(menu_t)) ;
if(p == NULL){
printf("NO enough momery to allocate!\n");
exit(0);
}
head->next=p;
p->last=head;
p->id=0;//当前功能号为0
p->same_last=NULL;
p->same_next=NULL;
return p;
}
return head;
}
menu_t*add_funsion(menu_t *head,void * fun,char * str){ //添加一个功能 head为该界面的第一项,自动在该界面后添加一项功能
menu_t *p = NULL,*pr = head;
p = (menu_t *)malloc(sizeof(menu_t)) ;
if(p == NULL){
printf("NO enough momery to allocate!\n");
exit(0);
}
if(head == NULL){
head = p; //head为空,创建头节点
printf("创建头节点\n");
}else{ //如果不是创建头节点
int i=1;
while(pr->same_next!= NULL){ //找到同级菜单最后的表
{
i++;
pr = pr->same_next;
}
}
pr->same_next = p; //在同级菜单尾部创建一个功能
p->same_last=pr;
p->last=head->last;
p->id=i;
}
p->funsion=fun;
p->menu_name=str;
p->same_next = NULL;//下一个同级功能为空
p->next=NULL; //下一个子集功能为空
return head;
}
menu_t * add_child(menu_t *head)//添加子级
{
menu_t *p = NULL;
p = (menu_t *)malloc(sizeof(menu_t)) ;
if(p == NULL){
printf("NO enough momery to allocate!\n");
exit(0);
}
if(head == NULL){
printf("输入head错误,添加子级失败\n");
}else{
head->next=p;
p->last=head;
p->same_last=NULL;
p->same_next=NULL;
}
return p;
}
void fun1()
{
printf("%s","\033[1H\033[2J");//linux下的清屏函数,window下请用system("cls");
printf("使用键盘选择以下功能\n");
if(sys_info.now->last!=NULL)//如果父极不为空
{
menu_t * start=sys_info.now->last->next;//start指向同级第一个
while(start!=NULL)//将所有功能打在屏幕上,如果功能和选中功能一样,变色
{
if(start->menu_name==sys_info.now->menu_name)//
{
printf("********%s*********\n",start->menu_name);
}
else
{
printf(" ****%s****\n",start->menu_name);
}
start=start->same_next;
}
}
else
{
printf("父极为空");
}
}
void kong()//空函数
{
printf("%s","\033[1H\033[2J");//linux下的清屏函数,window下请用system("cls");
printf("***多级菜单显示****");
}
void set_fun(menu_t *menu,void * fun,char * str)//设置函数和功能
{
menu->funsion=fun;
menu->menu_name=str;
}
void switch_input()//判断输入
{
scanf("%c", & sys_info.input);//把a仍给系统,交给对应函数处理
if (sys_info.ctrl)//按键控制链表
{
switch (sys_info.input)
{
case 'w'://上
if(sys_info.now->same_last!=NULL)
sys_info.now=sys_info.now->same_last;
break;
case 's'://下
if(sys_info.now->same_next!=NULL)
sys_info.now=sys_info.now->same_next;
break;
case 'a':// 左相当于返回
if(sys_info.now->last!=NULL)
sys_info.now=sys_info.now->last;
break;
case 'd'://右相当于确认
if(sys_info.now->next!=NULL)
sys_info.now=sys_info.now->next;
break;
default:
break;
}
sys_info.now->funsion();
}
}
void main()
{
sys_info.ctrl=1;
menu_t *head=NULL;
head=add_funsion(head,kong,"添加头节点");//添加头节点
menu_t *first=set_page(head,0);//主页面
set_fun(first,fun1,"第一页选项一");
add_funsion(first,fun1,"第一页选项二");
add_funsion(first,fun1,"第一页选项三");
menu_t *second=set_page(first,0);//第一个功能的页面 二级菜单
set_fun(second,fun1,"选项一的选项一");
add_funsion(second,fun1,"选项一的选项二");
add_funsion(second,fun1,"选项一的选项三");
sys_info.now=first;
menu_t *three=set_page(first,1);//第一个功能的页面 二级菜单
set_fun(three,fun1,"选项二的选项一");
add_funsion(three,fun1,"选项二的选项二");
add_funsion(three,fun1,"选项二的选项三");
menu_t *four=set_page(second,0);//第一个功能的页面的第一个页面 三级菜单
set_fun(four,fun1,"选项一的选项一的选项一");
add_funsion(four,fun1,"选项一的选项一选项二");
add_funsion(four,fun1,"选项一的选项一选项三");
fun1();
sys_info.now=first;
while(1)
{
switch_input();
}
}