Menú multinivel de un solo chip


prefacio

El proyecto anterior requería el uso de menús de varios niveles. Dado que estoy usando un OLED de 128 * 64, no se puede usar software de interfaz de usuario como LVGL. Además, debido a la particularidad del proyecto, la interfaz de usuario en el mercado no cumple con los requisitos. Necesito usar algún software de dibujo profesional para crear mi propia interfaz de usuario y estilo, por lo que necesito una función de menú de varios niveles.

El esquema de menús de varios niveles comúnmente utilizado en Internet se basa en una estructura de índice o árbol, y la mayoría de ellos son índices . Basado en mi propia experiencia y análisis, el esquema de construir un índice en línea no es propicio para la expansión , y la legibilidad también es deficiente. No es necesario, y es fácil causar la desventaja del acceso a la memoria fuera de los límites . En base a estas deficiencias, desarrollé una función de menú de varios niveles simple por mí mismo. Este menú de varios niveles es en realidad un modo de árbol, pero usé un método inteligente para convertir directamente los datos estructurados en árbol en una tabla hash, utilizando una tabla hash para crear un menú de varios niveles .


1. Comprender los conceptos básicos de los menús de varios niveles

De hecho, este menú de varios niveles también se basa en el índice, pero tiene una buena legibilidad y capacidad de expansión.El rendimiento de búsqueda es casi óptimo, pero ocupa un poco de espacio en la memoria.

1.1 Tabla hash

Este menú de varios niveles utiliza un punto que pocos desarrolladores de microcontroladores aprenderán, es decir, la estructura de datos. Aquí se utiliza una de las estructuras de datos más simples para crear un menú de varios niveles. Esta estructura de datos es una tabla hash o una tabla hash.

Aquí usamos una tabla hash para ilustrar. También se puede decir que una tabla hash es un diccionario en Python, es decir, hay una palabra clave en la tabla hash. El programa puede usar la palabra clave para averiguar dónde se almacenan los datos.

Podemos entender aproximadamente la relación entre palabras clave (Clave) y tablas hash con un gráfico. Como se muestra en la figura a continuación, el programa puede generar una clave a través de la función hash , donde la clave es igual a la dirección almacenada , y obtenemos la clave, es decir, la dirección almacenada, luego podemos acceder directamente a la dirección para obtener los datos.

Tenga en cuenta que la clave aquí se genera por sí misma a través de una función hash , y podemos usar esta clave para encontrar los datos almacenados.

inserte la descripción de la imagen aquí

1.11 Funciones hash

Una función hash es una función que genera una clave, que es única , para que podamos encontrar lo que queremos a través de la dirección. Hay muchas funciones hash, aquí hay un ejemplo simple, mira el código:

/*最简单的哈希函数*/
#define MAXARRY 50
int HASH_TABLE[MAXARRY];
int create_hashkey(int num)
{
    
    
	return (num*2+3%MAXARRY);
}
/*找到相关数据*/
int Find_data(int num)
{
    
    
	int key=create_hashkey(num);
	return HASH_TABLE[key];
}

Desde aquí se puede ver que la tabla hash en realidad establece una conexión entre una dirección y una palabra clave , y la dirección relevante se puede encontrar a través de la palabra clave , para encontrar el contenido relevante.

1.12 Colisiones de hash

El conflicto hash significa que las palabras clave generadas por la función hash son las mismas, es decir, la dirección de mapeo es la misma, por lo que no podemos encontrar los datos requeridos a través de la dirección y es imposible que almacenemos diferentes datos en la misma área. .

Esto se denomina conflicto hash y hay formas de resolverlos, como el método simple de dirección abierta, es decir, si hay un conflicto, el valor hash se vuelve a calcular para encontrar la siguiente dirección vacía y luego se almacena.

1.13 adecuado para la escena

Las tablas hash son más adecuadas para resolver problemas que buscan registros que son iguales a un valor dado . El punto clave aquí es que el valor dado es muy especial y no se repite, es decir, la palabra clave para calcular el valor hash debe ser especial . Cada uno es bastante especial y único, lo cual es excelente para buscar problemas. Como extrapolación, la complejidad temporal de la búsqueda es O(1).

2. Tabla hash y menú multinivel

Habiendo dicho tanto, ¿cuál es la conexión entre la tabla hash y el menú de niveles múltiples? Hemos observado cuidadosamente que los menús de niveles múltiples requieren consultas de alta frecuencia, y la posición absoluta de cada opción en el menú de niveles múltiples es única. Mirando hacia atrás en el escenario de aplicación de la tabla hash,
es adecuado para encontrar datos que son iguales a un valor dado. Cada una de nuestras opciones es una posición absoluta única, que es muy adecuada para el escenario de uso de la tabla hash, perfecto.

2.1 Asignación de opciones del menú multinivel

Si podemos identificar la especificidad de cada opción, entonces podemos mapearla y consultar el menú que configuramos. Entonces su icono debería verse así.

Podemos suponer que la subopción 1.1 es el siguiente nivel de la opción 1. Según esta suposición, cuando accedemos a la tabla hash, ¿podemos ingresar directamente una cadena similar a "1.1" para conocer su contenido real?

inserte la descripción de la imagen aquí

2.2 Código de referencia

Después del simple análisis anterior, podemos construir un menú de varios niveles usando una tabla hash, pero no nos importan los objetos señaladores de los niveles superior e inferior del menú. Solo necesitamos ingresar la posición del elemento relevante. opción y pasar el hash todo el tiempo.La función obtiene lo que apunta. Si necesitamos cambiar la situación de la opción, podemos saberlo cambiando la posición de la entrada.

Aquí hay una función básica para agregar , eliminar, modificar y verificar tablas hash , y un ejemplo de cómo construir y usar un menú de varios niveles basado en estas cuatro funciones básicas.

2.3 Definición de ubicación real

Usaré una cadena a continuación para obtener la clave única de la tabla hash. El formato de cadena de esto es una personalización personal. Si desea usar esto y comprender este código, primero puede comprender la siguiente figura.

inserte la descripción de la imagen aquí

Se puede ver en la figura que este directorio de varios niveles es en realidad una estructura de árbol. Si desea atravesar el árbol, la idea general es usar punteros para señalar sus nodos secundarios y nodos principales. Puede saber que el árbol almacena cada uno de sus nodos en forma de punteros La posición relativa de un miembro. Y utilicé un método más ingenioso para expresar la posición absoluta de cada posición directamente en forma de cadena, que en realidad es un poco similar a la forma de almacenamiento de archivos, excepto que la posición real se almacena en la tabla hash.

2.4 Código de referencia de la tabla hash

La implementación se basa en el pensamiento orientado a objetos y el código es un poco complicado. Si comprende los punteros de función, estática y const, ¡la estructura no será particularmente difícil de entender!

En el código, la actualización de GUI y la actualización de datos se escriben como dos punteros de función, que son convenientes para usar por separado.

El código está escrito en C puro, si desea usarlo en una microcomputadora de un solo chip, ¡simplemente cópielo directamente!

2.4.1 Código de lógica central

/************************************************* 
Copyright:None 
Author: Silent Knight
Date:2022/7/18
Description:基于哈希表的多级菜单
**************************************************/ 
#include "stdio.h"
#include "string.h"

#define MAX_EXPLAIN    10        /*最大说明缓冲*/
#define HASH_SKEY_LEN  15        /*哈希关键字最大长度*/
#define HASH_SIZE 50 
#define HASH_NULLKEY '\0'
#define HASH_OK    1
#define HASH_ERROR (HASH_SIZE+2)


typedef unsigned long long uint64_t;
typedef unsigned int  uint16_t;
typedef unsigned char uint8_t;
typedef unsigned char Status;
/*初始化函数*/
static void * my_memset(void *s,int c,int n)/*初始化*/
{
    
    
    char *c_s=(char *)s;
    while(n--) *c_s++=c;
    return s;
}
/*复制函数*/
static void * my_memcpy(void *dest,void *src,unsigned int size)
{
    
    
    char *b_dest=(char *)dest,*b_src=(char *)src;
    unsigned int len;
    for(len=size;len>0;len--)
    {
    
    
        *b_dest++=*b_src++;
    }
    return dest;
}


/*函数行为表*/
struct hashmenu_vtbl;
/*
选项位置使用说明:pos赋值为相应的格式的内容即可访问相应节点的内容
1 表示一级选项的第一个
2.2,二级选的下一级子选项的第二个
1.1.1表示一级选的下一级子选项的第一个,
*/
typedef struct 
{
    
    
    char pos[HASH_SKEY_LEN];     /*选项位置也是关键字*/
    char explain[MAX_EXPLAIN];   /*选项说明*/
    void (*gui)(void *parameter);/*选项GUI更新函数*/
    void (*act)(void *parameter);/*选项动作函数*/
    void (*default_act)(void *parameter);/*选项默认动作,执行上下左右切换界面后默认执行*/
}hashmenu_member;                /*每个成员的属性*/
/*哈希表构成菜单*/
typedef struct 
{
    
    
    hashmenu_member hashtable[HASH_SIZE];  /*哈希表*/
    struct hashmenu_vtbl *c_vptr;/*哈希表行为函数指针*/
    int hash_table_size;  //哈希表关键字的数量 
}hashmenu;/*表头*/

/*函数行为表*/
struct hashmenu_vtbl
{
    
    
    Status  (*insert)(hashmenu * const Me,hashmenu_member *const member);     /*添加选项*/
    Status  (*remove)(hashmenu * const Me,hashmenu_member * const member);    /*删除选项*/
    Status  (*modify)(hashmenu * const Me,hashmenu_member * const member);    /*设置选项*/
    Status  (*search)(hashmenu * const Me,hashmenu_member * const member);    /*查找选项*/

    /*找到上下左右选项*/
    Status  (*searchleft)(hashmenu * const Me,hashmenu_member * const member);    /*查找当前节点左选项*/
    Status  (*searchright)(hashmenu * const Me,hashmenu_member * const member);    /*查找当前节点右选项*/
    Status  (*searchup)(hashmenu * const Me,hashmenu_member * const member);     /*查找当前节点上一个选项*/
    Status  (*searchdown)(hashmenu * const Me,hashmenu_member * const member);    /*查找当前节点下一个选项*/
};

/*仅留创建和删除接口留给其他文件进行访问*/
void HashMenuCreate(hashmenu * const Me);
void HashMenuDelete(hashmenu * const Me);

/*增删改查接口*/
static Status HashOpenInser(hashmenu *  const Me,hashmenu_member  *const member);
static Status HashOpenRemove(hashmenu * const Me,hashmenu_member * const member);
static Status HashOpenSearch(hashmenu * const Me,hashmenu_member * const member);
static Status HashOpenModify(hashmenu * const Me,hashmenu_member * const member);

/*菜单切换接口*/
static Status HashMenuPeerLeft(hashmenu * const Me,hashmenu_member * const member);/*找到当前节点同级的选项*/
static Status HashMenuPeerRight(hashmenu * const Me,hashmenu_member * const member);/*找到当前节点下一级的选项*/
static Status HashMenuDepthUp(hashmenu * const Me,hashmenu_member * const member);/*找到当前节点下一级的选项*/
static Status HashMenuDepthDown(hashmenu * const Me,hashmenu_member * const member);/*找到当前节点下一级的选项*/
/*去符号化哈希表*/    
static uint16_t HashOpenKey(const char* skey)  
{
    
      
    char *p = (char*)skey;  
    uint16_t hasy_key = 0;  
    if(*p)
    {
    
      
        for(;*p != '\0'; ++p)  
            hasy_key = (hasy_key << 5) - hasy_key + *p;
    }
    
    return hasy_key%(HASH_SIZE-1);  
}

/*找到唯一的哈希值*/
static uint16_t HashOpenFindUniqueKey(hashmenu * const Me,hashmenu_member  *const member)  
{
    
       
    uint16_t unique_addr;
    int cout=HASH_SIZE-1;
    uint16_t clen;

    if(Me->hash_table_size<=0) return HASH_ERROR;
 
    unique_addr=HashOpenKey(member->pos);
    
    /*根据内容查看哈希值是不是指向需要的地址*/
    clen=strlen(member->pos);
    clen = (clen >HASH_SKEY_LEN) ?HASH_SKEY_LEN:clen;    
    while((strncmp(Me->hashtable[unique_addr].pos,member->pos,clen)!=0)&&cout--) 
    {
    
    
        unique_addr=(unique_addr+1)%HASH_SIZE;    /*开放地址法则线性探测*/
    }
    
    if(cout<0) return HASH_ERROR;/*遍历完这个哈希表都没有找到*/
    return unique_addr;
}
/*创建哈希表*/
void HashMenuCreate(hashmenu * const Me)  
{
    
      
    int i;
    static struct hashmenu_vtbl vtable;
    Me->hash_table_size=0; 
    my_memset(Me, 0, sizeof(hashmenu));
   
    vtable.insert=&HashOpenInser;
    vtable.remove=&HashOpenRemove;
    vtable.modify=&HashOpenModify;
    vtable.search=&HashOpenSearch;

    vtable.searchleft=&HashMenuPeerLeft;
    vtable.searchright=&HashMenuPeerRight;
    vtable.searchup=&HashMenuDepthUp;
    vtable.searchdown=&HashMenuDepthDown;

    Me->c_vptr=&vtable;

}  
/*删除哈希表*/
void HashMenuDelete(hashmenu * const Me)
{
    
    
    int i;
    my_memset(Me, 0, sizeof(hashmenu));
}

/*增*/
Status HashOpenInser(hashmenu * const Me,hashmenu_member  *const member)
{
    
    
    uint16_t addr;
    if(Me->hash_table_size>=HASH_SIZE-1) return HASH_ERROR;
    
    addr=HashOpenKey(member->pos);
    /*开放地址法则线性探测解决冲突*/
    while(Me->hashtable[addr].explain[0]!=HASH_NULLKEY) 
        addr=(addr+1)%HASH_SIZE-1;                
    
    /*插入数据*/
    my_memcpy(&(Me->hashtable[addr]),member,sizeof(hashmenu_member));
    Me->hash_table_size++;

    return HASH_OK;
}

/*删*/
Status HashOpenRemove(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint16_t addr;

    addr=HashOpenFindUniqueKey(Me,member);
    if(addr) 
    {
    
    
        my_memset(&(Me->hashtable[addr]),0,sizeof(hashmenu_member));
        Me->hash_table_size--;
        return HASH_OK;
    }
    else return HASH_ERROR;
    return HASH_OK;
}

/*查*/
Status HashOpenSearch(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint16_t addr;
    addr=HashOpenFindUniqueKey(Me,member);
    if(addr!=HASH_ERROR) 
    {
    
    
        my_memcpy(member,&(Me->hashtable[addr]),sizeof(hashmenu_member));
        return HASH_OK;
    }
    else return HASH_ERROR;
    return HASH_OK;
}

/*改*/
Status HashOpenModify(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint16_t addr;
    addr=HashOpenFindUniqueKey(Me,member);
    if(addr) 
    {
    
    
        my_memcpy(&(Me->hashtable[addr]),member,sizeof(hashmenu_member));
        return HASH_OK;
    }
    else return HASH_ERROR;         

    return HASH_OK;
}

/*找到当前节点同级的左选项*/
/*例如:当前pos="1.1.2",左选项节点就是pos="1.1.1"*/
static Status HashMenuPeerLeft(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint8_t *c_pos=(uint8_t *)member->pos;
    uint8_t t_chang;
    Status flag;
    if(Me->hash_table_size<=0) return HASH_ERROR;
    
    while(*++c_pos!='\0');/*找到位置最后的位置*/
    t_chang=*(--c_pos)-1;/*将最后位置的上一个字符值-1*/
    *c_pos=t_chang;
    
    flag=Me->c_vptr->search(Me,member);
    if(flag!=HASH_ERROR) return HASH_OK;
    else
    {
    
    
        /*重新指向先前内容*/
        t_chang=(*c_pos)+1;
        *c_pos=t_chang;        
        Me->c_vptr->search(Me,member);
    }
    /*执行默认函数*/
    member->default_act(NULL);
    return HASH_OK;
}

/*找到当前节点同级的右选项*/
/*例如:当前pos="1.1.2",右选项节点就是pos="1.1.3"*/
static Status HashMenuPeerRight(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint8_t *c_pos=(uint8_t *)member->pos;
    uint8_t t_chang;
    Status flag;
    if(Me->hash_table_size<=0) return HASH_ERROR;
    
    while(*++c_pos!='\0');/*找到位置最后的位置*/
    t_chang=*(--c_pos)+1;/*将最后位置的上一个字符值-1*/
    *c_pos=t_chang;
    
    flag=Me->c_vptr->search(Me,member);
    if(flag!=HASH_ERROR) return HASH_OK;
    else
    {
    
    
        /*重新指向先前内容*/
        t_chang=(*c_pos)-1;
        *c_pos=t_chang;        
        Me->c_vptr->search(Me,member);
    }
    /*执行默认函数*/
    member->default_act(NULL);
    return HASH_OK;
}

/*找到当前节点下一级的选项*/
/*例如:当前pos="1.1",下一级的选项节点就是pos="1.1.1"*/
static Status HashMenuDepthDown(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint8_t *c_pos=(uint8_t *)member->pos;
    Status flag;
    uint16_t clen;
    if(Me->hash_table_size<=0) return HASH_ERROR;
    
    clen=strlen(member->pos);
    if(clen>=HASH_SKEY_LEN-3) return HASH_ERROR; /*避免内存越界*/

    while(*++c_pos!='\0');/*找到位置最后的位置*/
    *c_pos='.';
    *(c_pos+1)='1';
    *(c_pos+2)='\0';

    flag=Me->c_vptr->search(Me,member);
    if(flag!=HASH_ERROR) return HASH_OK;
    else
    {
    
    
        /*重新指向先前内容*/
        *c_pos='\0';
        *(c_pos+1)='\0';
        *(c_pos+2)='\0';      
        Me->c_vptr->search(Me,member);
    }
    /*执行默认函数*/
    member->default_act(NULL);
    return HASH_OK;
}
/*找到当前节点上一级的选项*/
/*例如:当前pos="1.1",上一级的选项节点就是pos="1"*/
static Status HashMenuDepthUp(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint8_t *c_pos=(uint8_t *)member->pos;
    uint8_t last_pos;/*保存上次数字*/
    Status flag;
    uint16_t clen;
    
    if(Me->hash_table_size<=0) return HASH_ERROR;
    clen=strlen(member->pos);
    if(clen<=1) /*避免内存越界*/
    {
    
    
        Me->c_vptr->search(Me,member);
        return HASH_OK; 
    }
    while(*++c_pos!='\0');/*找到位置最后的位置*/
    last_pos=*(c_pos-1);/*保存数字*/

    *(c_pos-1)='\0';/*.1,删除数字*/
    *(c_pos-2)='\0';/*.,删除点*/

    flag=Me->c_vptr->search(Me,member);
    if(flag!=HASH_ERROR) return HASH_OK;
    else
    {
    
    
        /*重新指向先前内容*/
        *(c_pos-1)=last_pos;
        *(c_pos-2)='.';        
        Me->c_vptr->search(Me,member);
    }
    /*执行默认函数*/
    member->default_act(NULL);
    return HASH_OK;
}
/*打印成员信息*/ 
void PrintMember(hashmenu_member  member)  
{
    
     
    printf("pos=%s\r\n",member.pos);
}  
/*打印哈希表所有内容*/
void HashOpenPrint(hashmenu const * const Me)  
{
    
     
    for(int i=0;i<HASH_SIZE;i++)
    {
    
    
        if(Me->hashtable[i].explain[0]!=HASH_NULLKEY)
        {
    
    
            printf("key is %s explain = %s\n",Me->hashtable[i].pos,Me->hashtable[i].explain);
        }
    }
}  

2.4.2 Código de referencia de prueba

Usando el código de referencia, puede ver principalmente el formato de los datos que creé. La lógica es la misma, pero es un poco más larga.

/*测试GUI*/
void GUI_1(void *parameter)
{
    
    
    printf("--------------- -GUI 123--------------\r\n");
    printf("---------------->OPTION 1<-------------\r\n");
    printf("-----------------OPTION 2-------------\r\n");
    printf("-----------------OPTION 3-------------\r\n");
}

void GUI_2(void *parameter)
{
    
    
    printf("--------------- -GUI 123--------------\r\n");
    printf("-----------------OPTION 1-------------\r\n");
    printf("---------------->OPTION 2<------------\r\n");
    printf("-----------------OPTION 3-------------\r\n");
}

void GUI_3(void *parameter)
{
    
    
    printf("--------------- -GUI 123--------------\r\n");
    printf("-----------------OPTION 1-------------\r\n");
    printf("-----------------OPTION 2-------------\r\n");
    printf("---------------->OPTION 3<------------\r\n");
}


void GUI_1_1(void *parameter)
{
    
    
    printf("------------------GUI 1_1-------------\r\n");
    printf("---------------->OPTION 1.1<------------\r\n");
    printf("-----------------OPTION 1.2-------------\r\n");
    printf("-----------------OPTION 1.3------------\r\n");
}

void GUI_1_2(void *parameter)
{
    
    
    printf("------------------GUI 1_1-------------\r\n");
    printf("-----------------OPTION 1.1-------------\r\n");
    printf("---------------->OPTION 1.2<------------\r\n");
    printf("-----------------OPTION 1.3------------\r\n");
}

void GUI_1_3(void *parameter)
{
    
    
    printf("------------------GUI 1_1-------------\r\n");
    printf("-----------------OPTION 1.1-------------\r\n");
    printf("-----------------OPTION 1.2-------------\r\n");
    printf("---------------->OPTION 1.3<------------\r\n");
}
/*测试功能函数*/
void func1(void *num)
{
    
    
    hashmenu_member *mem=(hashmenu_member *)num;
    printf("mem->explain =%s\r\n",mem->explain);
}


#define UP      'w'
#define DOWM    's'
#define LEFT    'a'
#define RIGHT   'd'
#define ENTER   'e'
#define QUIT    'q'
hashmenu tmenu;
void test()
{
    
    
    hashmenu_member t_member;
    hashmenu_member d_member;
    int x=1;
    char choice;
    /*创建哈希表*/
    HashMenuCreate(&tmenu);
    /*输入内容,插入哈希表*/
    t_member.act=func1;
    t_member.gui=GUI_1;
    strcpy(t_member.pos,"1");
    strcpy(t_member.explain,"1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    t_member.gui=GUI_2;
    strcpy(t_member.pos,"2");
    strcpy(t_member.explain,"2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    strcpy(t_member.pos,"3");
    t_member.gui=GUI_3;
    t_member.act=func1;
    strcpy(t_member.explain,"3 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    t_member.gui=GUI_1_1;
    strcpy(t_member.pos,"1.1");
    strcpy(t_member.explain,"1.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.gui=GUI_1_2;
    t_member.act=func1;
    strcpy(t_member.pos,"1.2");
    strcpy(t_member.explain,"1.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.gui=GUI_1_3;
    t_member.act=func1;
    strcpy(t_member.pos,"1.3");
    strcpy(t_member.explain,"1.3 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    strcpy(t_member.pos,"2.1");
    strcpy(t_member.explain,"2.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"2.2");
    strcpy(t_member.explain,"2.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);


    strcpy(t_member.pos,"3.1");
    strcpy(t_member.explain,"2.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"3.2");
    strcpy(t_member.explain,"2.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"1.1.1");
    strcpy(t_member.explain,"1.1.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    strcpy(t_member.pos,"1.1.2");
    strcpy(t_member.explain,"1.1.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    strcpy(t_member.pos,"1.1.3");
    strcpy(t_member.explain,"1.1.3 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    strcpy(t_member.pos,"2.1.1");
    strcpy(t_member.explain,"2.1.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"2.2.1");
    strcpy(t_member.explain,"2.2.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"3.1.1");
    strcpy(t_member.explain,"3.1.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    strcpy(t_member.pos,"3.1.2");
    strcpy(t_member.explain,"3.1.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    strcpy(t_member.pos,"3.1.3");
    strcpy(t_member.explain,"3.1.3 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);
    HashOpenPrint(&tmenu);
    printf("--------------------------------------\r\n");
    strcpy(d_member.pos,"1");

    tmenu.c_vptr->search(&tmenu,&d_member);
    PrintMember(d_member);
    while(choice!=QUIT)
    {
    
    
        /*模拟按键或者串口输入*/
        scanf("%c",&choice);
        /*根据输入执行动作*/
        switch (choice)
        {
    
    
        case UP:
            if(tmenu.c_vptr->searchup(&tmenu,&d_member)!=HASH_ERROR)      d_member.act(&d_member);;
            
            break;
        case DOWM:
            if(tmenu.c_vptr->searchdown(&tmenu,&d_member)!=HASH_ERROR)    d_member.act(&d_member);
            break;   
        case LEFT:
            if(tmenu.c_vptr->searchleft(&tmenu,&d_member)!=HASH_ERROR)    d_member.act(&d_member);
            break;   
        case RIGHT:
            if(tmenu.c_vptr->searchright(&tmenu,&d_member)!=HASH_ERROR)    d_member.act(&d_member);
            break;   
        case ENTER:
            d_member.act(&d_member);
            break;              
        default:
            break;
        }
        /*根据查询内容更新GUI*/
        if(choice!='\n') d_member.gui(NULL);
    }

}

int main()
{
    
    
    test();
    return 0;
}

2.4.3 Resultados de la ejecución

pos=1
context=1
pos=2
context=2
pos=3
context=3
pos=1.1
context=11
pos=1.2
context=12
pos=1.3
context=13
-------------all content-------------
key is 1 explain = 1 EXPLAIN

key is 2 explain = 2 EXPLAIN

key is 3 explain = 3 EXPLAIN

key is 1.1 explain = 1.1 EXPLAIN

key is 1.2 explain = 1.2 EXPLAIN

key is 1.3 explain = 1.3 EXPLAIN

--------------------------------------
pos=1
context=1
d
mem->explain =2 EXPLAIN
,content=2
--------------- -GUI 123--------------
-----------------OPTION 1-------------
---------------->OPTION 2<------------
-----------------OPTION 3-------------
d
mem->explain =3 EXPLAIN
,content=3
--------------- -GUI 123--------------
-----------------OPTION 1-------------
-----------------OPTION 2-------------
---------------->OPTION 3<------------
a
mem->explain =2 EXPLAIN
,content=2
--------------- -GUI 123--------------
-----------------OPTION 1-------------
---------------->OPTION 2<------------
-----------------OPTION 3-------------
a
mem->explain =1 EXPLAIN
,content=1
--------------- -GUI 123--------------
---------------->OPTION 1<-------------
-----------------OPTION 2-------------
-----------------OPTION 3-------------
s
mem->explain =1.1 EXPLAIN
,content=11
------------------GUI 1_1-------------
---------------->OPTION 1.1<------------
-----------------OPTION 1.2-------------
-----------------OPTION 1.3------------
d
mem->explain =1.2 EXPLAIN
,content=12
------------------GUI 1_1-------------
-----------------OPTION 1.1-------------
---------------->OPTION 1.2<------------
-----------------OPTION 1.3------------
d
mem->explain =1.3 EXPLAIN
,content=13
------------------GUI 1_1-------------
-----------------OPTION 1.1-------------
-----------------OPTION 1.2-------------
---------------->OPTION 1.3<------------
a
mem->explain =1.2 EXPLAIN
,content=12
------------------GUI 1_1-------------
-----------------OPTION 1.1-------------
---------------->OPTION 1.2<------------
-----------------OPTION 1.3------------
a
mem->explain =1.1 EXPLAIN
,content=11
------------------GUI 1_1-------------
---------------->OPTION 1.1<------------
-----------------OPTION 1.2-------------
-----------------OPTION 1.3------------


3. Pensamientos rotos

De hecho, este menú es muy adecuado para depurar o cuando el hardware es muy simple . En un entorno relativamente simple, por ejemplo, solo hay botones y una pantalla muy simple (como una pantalla OLED de 128 x 64), o no hay ninguna pantalla. De hecho, se recomienda que aprenda algunas bibliotecas de gráficos de GUI integradas de uso común, como LVGL, QT, etc. Si no es como yo y necesita dibujar una GUI adecuada de forma independiente, no se recomienda usarla cuando no esté depurando o en un entorno donde el hardware es realmente simple.

Este es el final del intercambio. He estado pensando en esta idea durante mucho tiempo. De tener una idea adecuada a escribir código mientras escribía un artículo, volqué la idea anterior dos veces, y ha sido un día entero. Espero ¡Será útil para todos! ! !

4. Código de referencia completo

El código de referencia completo, incluidas las pruebas y el código central, se publica aquí.

/************************************************* 
Copyright:None 
Author: Silent Knight
Date:2022/7/18
Description:基于哈希表的多级菜单
**************************************************/ 
#include "stdio.h"
#include "string.h"

#define MAX_EXPLAIN    10        /*最大说明缓冲*/
#define HASH_SKEY_LEN  15        /*哈希关键字最大长度*/
#define HASH_SIZE 50 
#define HASH_NULLKEY '\0'
#define HASH_OK    1
#define HASH_ERROR (HASH_SIZE+2)


typedef unsigned long long uint64_t;
typedef unsigned int  uint16_t;
typedef unsigned char uint8_t;
typedef unsigned char Status;
/*初始化函数*/
static void * my_memset(void *s,int c,int n)/*初始化*/
{
    
    
    char *c_s=(char *)s;
    while(n--) *c_s++=c;
    return s;
}
/*复制函数*/
static void * my_memcpy(void *dest,void *src,unsigned int size)
{
    
    
    char *b_dest=(char *)dest,*b_src=(char *)src;
    unsigned int len;
    for(len=size;len>0;len--)
    {
    
    
        *b_dest++=*b_src++;
    }
    return dest;
}


/*函数行为表*/
struct hashmenu_vtbl;
/*
选项位置使用说明:pos赋值为相应的格式的内容即可访问相应节点的内容
1 表示一级选项的第一个
2.2,二级选的下一级子选项的第二个
1.1.1表示一级选的下一级子选项的第一个,
*/
typedef struct 
{
    
    
    char pos[HASH_SKEY_LEN];     /*选项位置也是关键字*/
    char explain[MAX_EXPLAIN];   /*选项说明*/
    void (*gui)(void *parameter);/*选项GUI更新函数*/
    void (*act)(void *parameter);/*选项动作函数*/
    void (*default_act)(void *parameter);/*选项默认动作,执行上下左右切换界面后默认执行*/
}hashmenu_member;                /*每个成员的属性*/
/*哈希表构成菜单*/
typedef struct 
{
    
    
    hashmenu_member hashtable[HASH_SIZE];  /*哈希表*/
    struct hashmenu_vtbl *c_vptr;/*哈希表行为函数指针*/
    int hash_table_size;  //哈希表关键字的数量 
}hashmenu;/*表头*/

/*函数行为表*/
struct hashmenu_vtbl
{
    
    
    Status  (*insert)(hashmenu * const Me,hashmenu_member *const member);     /*添加选项*/
    Status  (*remove)(hashmenu * const Me,hashmenu_member * const member);    /*删除选项*/
    Status  (*modify)(hashmenu * const Me,hashmenu_member * const member);    /*设置选项*/
    Status  (*search)(hashmenu * const Me,hashmenu_member * const member);    /*查找选项*/

    /*找到上下左右选项*/
    Status  (*searchleft)(hashmenu * const Me,hashmenu_member * const member);    /*查找当前节点左选项*/
    Status  (*searchright)(hashmenu * const Me,hashmenu_member * const member);    /*查找当前节点右选项*/
    Status  (*searchup)(hashmenu * const Me,hashmenu_member * const member);     /*查找当前节点上一个选项*/
    Status  (*searchdown)(hashmenu * const Me,hashmenu_member * const member);    /*查找当前节点下一个选项*/
};

/*仅留创建和删除接口留给其他文件进行访问*/
void HashMenuCreate(hashmenu * const Me);
void HashMenuDelete(hashmenu * const Me);

/*增删改查接口*/
static Status HashOpenInser(hashmenu *  const Me,hashmenu_member  *const member);
static Status HashOpenRemove(hashmenu * const Me,hashmenu_member * const member);
static Status HashOpenSearch(hashmenu * const Me,hashmenu_member * const member);
static Status HashOpenModify(hashmenu * const Me,hashmenu_member * const member);

/*菜单切换接口*/
static Status HashMenuPeerLeft(hashmenu * const Me,hashmenu_member * const member);/*找到当前节点同级的选项*/
static Status HashMenuPeerRight(hashmenu * const Me,hashmenu_member * const member);/*找到当前节点下一级的选项*/
static Status HashMenuDepthUp(hashmenu * const Me,hashmenu_member * const member);/*找到当前节点下一级的选项*/
static Status HashMenuDepthDown(hashmenu * const Me,hashmenu_member * const member);/*找到当前节点下一级的选项*/
/*去符号化哈希表*/    
static uint16_t HashOpenKey(const char* skey)  
{
    
      
    char *p = (char*)skey;  
    uint16_t hasy_key = 0;  
    if(*p)
    {
    
      
        for(;*p != '\0'; ++p)  
            hasy_key = (hasy_key << 5) - hasy_key + *p;
    }
    
    return hasy_key%(HASH_SIZE);  
}

/*找到唯一的哈希值*/
static uint16_t HashOpenFindUniqueKey(hashmenu * const Me,hashmenu_member  *const member)  
{
    
       
    uint16_t unique_addr;
    int cout=HASH_SIZE-1;
    uint16_t clen;

    if(Me->hash_table_size<=0) return HASH_ERROR;
 
    unique_addr=HashOpenKey(member->pos);
    
    /*根据内容查看哈希值是不是指向需要的地址*/
    clen=strlen(member->pos);
    clen = (clen >HASH_SKEY_LEN) ?HASH_SKEY_LEN:clen;    
    while((strncmp(Me->hashtable[unique_addr].pos,member->pos,clen)!=0)&&cout--) 
    {
    
    
        unique_addr=(unique_addr+1)%HASH_SIZE;    /*开放地址法则线性探测*/
    }
    
    if(cout<0) return HASH_ERROR;/*遍历完这个哈希表都没有找到*/
    return unique_addr;
}
/*创建哈希表*/
void HashMenuCreate(hashmenu * const Me)  
{
    
      
    int i;
    static struct hashmenu_vtbl vtable;
    Me->hash_table_size=0; 
    my_memset(Me, 0, sizeof(hashmenu));
   
    vtable.insert=&HashOpenInser;
    vtable.remove=&HashOpenRemove;
    vtable.modify=&HashOpenModify;
    vtable.search=&HashOpenSearch;

    vtable.searchleft=&HashMenuPeerLeft;
    vtable.searchright=&HashMenuPeerRight;
    vtable.searchup=&HashMenuDepthUp;
    vtable.searchdown=&HashMenuDepthDown;

    Me->c_vptr=&vtable;

}  
/*删除哈希表*/
void HashMenuDelete(hashmenu * const Me)
{
    
    
    int i;
    my_memset(Me, 0, sizeof(hashmenu));
}

/*增*/
Status HashOpenInser(hashmenu * const Me,hashmenu_member  *const member)
{
    
    
    uint16_t addr;
    if(Me->hash_table_size>=HASH_SIZE-1) return HASH_ERROR;
    
    addr=HashOpenKey(member->pos);
    /*开放地址法则线性探测解决冲突*/
    while(Me->hashtable[addr].explain[0]!=HASH_NULLKEY) 
        addr=(addr+1)%HASH_SIZE;                
    
    /*插入数据*/
    my_memcpy(&(Me->hashtable[addr]),member,sizeof(hashmenu_member));
    Me->hash_table_size++;

    return HASH_OK;
}

/*删*/
Status HashOpenRemove(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint16_t addr;

    addr=HashOpenFindUniqueKey(Me,member);
    if(addr) 
    {
    
    
        my_memset(&(Me->hashtable[addr]),0,sizeof(hashmenu_member));
        Me->hash_table_size--;
        return HASH_OK;
    }
    else return HASH_ERROR;
    return HASH_OK;
}

/*查*/
Status HashOpenSearch(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint16_t addr;
    addr=HashOpenFindUniqueKey(Me,member);
    if(addr!=HASH_ERROR) 
    {
    
    
        my_memcpy(member,&(Me->hashtable[addr]),sizeof(hashmenu_member));
        return HASH_OK;
    }
    else return HASH_ERROR;
    return HASH_OK;
}

/*改*/
Status HashOpenModify(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint16_t addr;
    addr=HashOpenFindUniqueKey(Me,member);
    if(addr) 
    {
    
    
        my_memcpy(&(Me->hashtable[addr]),member,sizeof(hashmenu_member));
        return HASH_OK;
    }
    else return HASH_ERROR;         

    return HASH_OK;
}

/*找到当前节点同级的左选项*/
/*例如:当前pos="1.1.2",左选项节点就是pos="1.1.1"*/
static Status HashMenuPeerLeft(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint8_t *c_pos=(uint8_t *)member->pos;
    uint8_t t_chang;
    Status flag;
    if(Me->hash_table_size<=0) return HASH_ERROR;
    
    while(*++c_pos!='\0');/*找到位置最后的位置*/
    t_chang=*(--c_pos)-1;/*将最后位置的上一个字符值-1*/
    *c_pos=t_chang;
    
    flag=Me->c_vptr->search(Me,member);
    if(flag!=HASH_ERROR) return HASH_OK;
    else
    {
    
    
        /*重新指向先前内容*/
        t_chang=(*c_pos)+1;
        *c_pos=t_chang;        
        Me->c_vptr->search(Me,member);
    }
    /*执行默认函数*/
    member->default_act(NULL);
    return HASH_OK;
}

/*找到当前节点同级的右选项*/
/*例如:当前pos="1.1.2",右选项节点就是pos="1.1.3"*/
static Status HashMenuPeerRight(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint8_t *c_pos=(uint8_t *)member->pos;
    uint8_t t_chang;
    Status flag;
    if(Me->hash_table_size<=0) return HASH_ERROR;
    
    while(*++c_pos!='\0');/*找到位置最后的位置*/
    t_chang=*(--c_pos)+1;/*将最后位置的上一个字符值-1*/
    *c_pos=t_chang;
    
    flag=Me->c_vptr->search(Me,member);
    if(flag!=HASH_ERROR) return HASH_OK;
    else
    {
    
    
        /*重新指向先前内容*/
        t_chang=(*c_pos)-1;
        *c_pos=t_chang;        
        Me->c_vptr->search(Me,member);
    }
    /*执行默认函数*/
    member->default_act(NULL);
    return HASH_OK;
}

/*找到当前节点下一级的选项*/
/*例如:当前pos="1.1",下一级的选项节点就是pos="1.1.1"*/
static Status HashMenuDepthDown(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint8_t *c_pos=(uint8_t *)member->pos;
    Status flag;
    uint16_t clen;
    if(Me->hash_table_size<=0) return HASH_ERROR;
    
    clen=strlen(member->pos);
    if(clen>=HASH_SKEY_LEN-3) return HASH_ERROR; /*避免内存越界*/

    while(*++c_pos!='\0');/*找到位置最后的位置*/
    *c_pos='.';
    *(c_pos+1)='1';
    *(c_pos+2)='\0';

    flag=Me->c_vptr->search(Me,member);
    if(flag!=HASH_ERROR) return HASH_OK;
    else
    {
    
    
        /*重新指向先前内容*/
        *c_pos='\0';
        *(c_pos+1)='\0';
        *(c_pos+2)='\0';      
        Me->c_vptr->search(Me,member);
    }
    /*执行默认函数*/
    member->default_act(NULL);
    return HASH_OK;
}
/*找到当前节点上一级的选项*/
/*例如:当前pos="1.1",上一级的选项节点就是pos="1"*/
static Status HashMenuDepthUp(hashmenu * const Me,hashmenu_member * const member)
{
    
    
    uint8_t *c_pos=(uint8_t *)member->pos;
    uint8_t last_pos;/*保存上次数字*/
    Status flag;
    uint16_t clen;
    
    if(Me->hash_table_size<=0) return HASH_ERROR;
    clen=strlen(member->pos);
    if(clen<=1) /*避免内存越界*/
    {
    
    
        Me->c_vptr->search(Me,member);
        return HASH_OK; 
    }
    while(*++c_pos!='\0');/*找到位置最后的位置*/
    last_pos=*(c_pos-1);/*保存数字*/

    *(c_pos-1)='\0';/*.1,删除数字*/
    *(c_pos-2)='\0';/*.,删除点*/

    flag=Me->c_vptr->search(Me,member);
    if(flag!=HASH_ERROR) return HASH_OK;
    else
    {
    
    
        /*重新指向先前内容*/
        *(c_pos-1)=last_pos;
        *(c_pos-2)='.';        
        Me->c_vptr->search(Me,member);
    }
    /*执行默认函数*/
    member->default_act(NULL);
    return HASH_OK;
}
/*打印成员信息*/ 
void PrintMember(hashmenu_member  member)  
{
    
     
    printf("pos=%s\r\n",member.pos);
}  
/*打印哈希表所有内容*/
void HashOpenPrint(hashmenu const * const Me)  
{
    
     
    for(int i=0;i<HASH_SIZE;i++)
    {
    
    
        if(Me->hashtable[i].explain[0]!=HASH_NULLKEY)
        {
    
    
            printf("key is %s explain = %s\n",Me->hashtable[i].pos,Me->hashtable[i].explain);
        }
    }
}  

/*测试GUI*/
void GUI_1(void *parameter)
{
    
    
    printf("--------------- -GUI 123--------------\r\n");
    printf("---------------->OPTION 1<-------------\r\n");
    printf("-----------------OPTION 2-------------\r\n");
    printf("-----------------OPTION 3-------------\r\n");
}

void GUI_2(void *parameter)
{
    
    
    printf("--------------- -GUI 123--------------\r\n");
    printf("-----------------OPTION 1-------------\r\n");
    printf("---------------->OPTION 2<------------\r\n");
    printf("-----------------OPTION 3-------------\r\n");
}

void GUI_3(void *parameter)
{
    
    
    printf("--------------- -GUI 123--------------\r\n");
    printf("-----------------OPTION 1-------------\r\n");
    printf("-----------------OPTION 2-------------\r\n");
    printf("---------------->OPTION 3<------------\r\n");
}


void GUI_1_1(void *parameter)
{
    
    
    printf("------------------GUI 1_1-------------\r\n");
    printf("---------------->OPTION 1.1<------------\r\n");
    printf("-----------------OPTION 1.2-------------\r\n");
    printf("-----------------OPTION 1.3------------\r\n");
}

void GUI_1_2(void *parameter)
{
    
    
    printf("------------------GUI 1_1-------------\r\n");
    printf("-----------------OPTION 1.1-------------\r\n");
    printf("---------------->OPTION 1.2<------------\r\n");
    printf("-----------------OPTION 1.3------------\r\n");
}

void GUI_1_3(void *parameter)
{
    
    
    printf("------------------GUI 1_1-------------\r\n");
    printf("-----------------OPTION 1.1-------------\r\n");
    printf("-----------------OPTION 1.2-------------\r\n");
    printf("---------------->OPTION 1.3<------------\r\n");
}
/*测试功能函数*/
void func1(void *num)
{
    
    
    hashmenu_member *mem=(hashmenu_member *)num;
    printf("mem->explain =%s\r\n",mem->explain);
}


#define UP      'w'
#define DOWM    's'
#define LEFT    'a'
#define RIGHT   'd'
#define ENTER   'e'
#define QUIT    'q'
hashmenu tmenu;
void test()
{
    
    
    hashmenu_member t_member;
    hashmenu_member d_member;
    int x=1;
    char choice;
    /*创建哈希表*/
    HashMenuCreate(&tmenu);
    /*输入内容,插入哈希表*/
    t_member.act=func1;
    t_member.gui=GUI_1;
    strcpy(t_member.pos,"1");
    strcpy(t_member.explain,"1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    t_member.gui=GUI_2;
    strcpy(t_member.pos,"2");
    strcpy(t_member.explain,"2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    strcpy(t_member.pos,"3");
    t_member.gui=GUI_3;
    t_member.act=func1;
    strcpy(t_member.explain,"3 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    t_member.gui=GUI_1_1;
    strcpy(t_member.pos,"1.1");
    strcpy(t_member.explain,"1.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.gui=GUI_1_2;
    t_member.act=func1;
    strcpy(t_member.pos,"1.2");
    strcpy(t_member.explain,"1.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.gui=GUI_1_3;
    t_member.act=func1;
    strcpy(t_member.pos,"1.3");
    strcpy(t_member.explain,"1.3 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    strcpy(t_member.pos,"2.1");
    strcpy(t_member.explain,"2.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"2.2");
    strcpy(t_member.explain,"2.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);


    strcpy(t_member.pos,"3.1");
    strcpy(t_member.explain,"2.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"3.2");
    strcpy(t_member.explain,"2.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"1.1.1");
    strcpy(t_member.explain,"1.1.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    strcpy(t_member.pos,"1.1.2");
    strcpy(t_member.explain,"1.1.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    strcpy(t_member.pos,"1.1.3");
    strcpy(t_member.explain,"1.1.3 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    strcpy(t_member.pos,"2.1.1");
    strcpy(t_member.explain,"2.1.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"2.2.1");
    strcpy(t_member.explain,"2.2.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    t_member.act=func1;
    PrintMember(t_member);

    strcpy(t_member.pos,"3.1.1");
    strcpy(t_member.explain,"3.1.1 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    strcpy(t_member.pos,"3.1.2");
    strcpy(t_member.explain,"3.1.2 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);

    t_member.act=func1;
    strcpy(t_member.pos,"3.1.3");
    strcpy(t_member.explain,"3.1.3 EXPLAIN\r\n");
    tmenu.c_vptr->insert(&tmenu,&t_member);
    PrintMember(t_member);
    HashOpenPrint(&tmenu);
    printf("--------------------------------------\r\n");
    strcpy(d_member.pos,"1");

    tmenu.c_vptr->search(&tmenu,&d_member);
    PrintMember(d_member);
    while(choice!=QUIT)
    {
    
    
        /*模拟按键或者串口输入*/
        scanf("%c",&choice);
        /*根据输入执行动作*/
        switch (choice)
        {
    
    
        case UP:
            if(tmenu.c_vptr->searchup(&tmenu,&d_member)!=HASH_ERROR)      d_member.act(&d_member);;
            
            break;
        case DOWM:
            if(tmenu.c_vptr->searchdown(&tmenu,&d_member)!=HASH_ERROR)    d_member.act(&d_member);
            break;   
        case LEFT:
            if(tmenu.c_vptr->searchleft(&tmenu,&d_member)!=HASH_ERROR)    d_member.act(&d_member);
            break;   
        case RIGHT:
            if(tmenu.c_vptr->searchright(&tmenu,&d_member)!=HASH_ERROR)    d_member.act(&d_member);
            break;   
        case ENTER:
            d_member.act(&d_member);
            break;              
        default:
            break;
        }
        /*根据查询内容更新GUI*/
        if(choice!='\n') d_member.gui(NULL);
    }

}

int main()
{
    
    
    test();
    return 0;
}

5. PD

De hecho, la estructura del artículo es casi la misma que la ubicación de la definición del código que escribí, puedes subirla y echarle un vistazo.

Supongo que te gusta

Origin blog.csdn.net/weixin_46185705/article/details/125838527
Recomendado
Clasificación