链表多级菜单

前言

闲来无事,整理了一下智能车时期的相关文件,无意中看到了当时写的菜单,主要用来显示车速、陀螺仪角度、裁判系统发送数据以及最重要的调节参数。当时用的是索引法,修修改改的感觉比较麻烦,重新用链表写了一个多级菜单的框架,可以快速添加子目录和绑定功能函数和显示函数,便于以后使用。

介绍

菜单结构

在多级菜单中。不同级的目录有上下级关系,同级目录中也有上下级的关系,同级位号是为了确认该级目录的个数,在后面的显示和控制部分会用到,功能函数绑定和显示函数绑定则是为了有更大的自由度。

1
2
3
4
5
6
7
8
9
10
11
typedef struct menu
{
struct menu* last; // 父级菜单
struct menu* next; // 子级菜单
struct menu* equal_last; // 同级上一项
struct menu* equal_next; // 同级下一项
int number; // 同级位号
const char* name; // 名称
void (*function)(int); // 功能函数绑定
void (*ShowFunc)(int); // 显示函数绑定
}menu;

系统信息

sys_info 里的 sys_menu 是为了定位当前的链表位置,页号和位号也是如此,会在显示和控制部分用到。func_choose 记录了系统状态,是菜单选择还是功能实现,在按键切换部分起作用。

1
2
3
4
5
6
7
typedef struct sys_info
{
struct menu* sys_menu;
int page; // 页号
int number; // 位号
int func_choose; // 0-菜单选择 1-功能实现
}sys_info;

添加菜单

在添加菜单时,只需按需填入下面的参数就可以创建目录了,不需要的直接 NULL 就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 添加菜单
// @param: last 上一页菜单
// @param: equal_last 同页上级菜单
// @param: func 功能函数绑定
// @param: show 显示函数绑定
// @param: name 名字
menu* add_page(menu* last, menu* equal_last, void (*func)(int), void (*show)(int), const char* name)
{
menu* p = NULL;
p = (menu*)malloc(sizeof(menu));
if (NULL == p)
{
printf("Fail to Allocate");
exit(0);
}
if (NULL == last) // 无父节点,为头节点
{
// 创建头节点
p->last = NULL;
}
else
{
// 中间节点
p->last = last;
}
if (NULL == equal_last) // 无同级上一项,为首项
{
if (last != NULL)
{
last->next = p;
}
p->equal_last = NULL;
p->number = 0; // 首项位号为0
}
else
{
equal_last->equal_next = p;
p->equal_last = equal_last;
p->number = equal_last->number + 1;
}
p->next = NULL;
p->equal_next = NULL;
p->name = name;
p->function = func;
p->ShowFunc = show;
return p;
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#include "menu.h"
#include "headfile.h"

struct sys_info* my_sys_info = NULL;

// 菜单初始化
void menu_init()
{
sys_info* p = NULL;
p = (sys_info*)malloc(sizeof(sys_info));
if (NULL == p)
{
printf("Fail to Allocate");
exit(0);
}
my_sys_info = p;
//---------------------------------------------
// 目录结构
struct menu* my_menu_0 = NULL;
struct menu* my_menu_1_0 = NULL;
struct menu* my_menu_1_1 = NULL;
struct menu* my_menu_1_2 = NULL;
struct menu* my_menu_1_3 = NULL;
struct menu* my_menu_1_4 = NULL;
struct menu* my_menu_1_5 = NULL;
struct menu* my_menu_2_0_1 = NULL;
struct menu* my_menu_2_0_2 = NULL;
struct menu* my_menu_2_0_3 = NULL;
struct menu* my_menu_2_0_4 = NULL;
my_menu_0 = add_page(NULL, NULL, NULL, NULL, "菜单");
my_menu_1_0 = add_page(my_menu_0, NULL, NULL, NULL, "PID参数调整");
my_menu_1_1 = add_page(my_menu_0, my_menu_1_0, NULL, NULL, "转速");
my_menu_1_2 = add_page(my_menu_0, my_menu_1_1, NULL, NULL, "温度");
my_menu_1_3 = add_page(my_menu_0, my_menu_1_2, NULL, NULL, "陀螺仪角度");
my_menu_1_4 = add_page(my_menu_0, my_menu_1_3, NULL, NULL, "串口数据");
my_menu_1_5 = add_page(my_menu_0, my_menu_1_4, NULL, NULL, "摄像头图像");
my_menu_2_0_1 = add_page(my_menu_1_0, NULL, menu_pid_change, menu_pid_change_show, "P");
my_menu_2_0_2 = add_page(my_menu_1_0, my_menu_2_0_1, menu_pid_change, menu_pid_change_show, "I");
my_menu_2_0_3 = add_page(my_menu_1_0, my_menu_2_0_2, menu_pid_change, menu_pid_change_show, "D");
my_menu_2_0_4 = add_page(my_menu_1_0, my_menu_2_0_3, menu_pid_change, menu_pid_change_show, "EXPECT");
//---------------------------------------------
my_sys_info->sys_menu = my_menu_0;
my_sys_info->page = 0;
my_sys_info->number = 0;
my_sys_info->func_choose = 0;
}

// 控制菜单
// @param: opreat-按键状态
void menu_control(int opreat)
{
if (1 == my_sys_info->func_choose) // 1-功能实现
{
switch (opreat)
{
case LEFT: // left
my_sys_info->func_choose = 0; // 退出
break;
default:
my_sys_info->sys_menu->function(opreat);
break;
}
}
else // 0-菜单选择
{
switch (opreat)
{
case UP: // up
if (my_sys_info->sys_menu->equal_last != NULL)
{
my_sys_info->sys_menu = my_sys_info->sys_menu->equal_last;
my_sys_info->number = my_sys_info->number - 1;
}
break;
case DOWN: // down
if (my_sys_info->sys_menu->equal_next != NULL)
{
my_sys_info->sys_menu = my_sys_info->sys_menu->equal_next;
my_sys_info->number = my_sys_info->number + 1;
}
break;
case LEFT: // left
if (my_sys_info->sys_menu->last != NULL)
{
my_sys_info->sys_menu = my_sys_info->sys_menu->last;
my_sys_info->page = my_sys_info->page - 1;
my_sys_info->number = 0;
}
break;
case RIGHT: // right
if (my_sys_info->sys_menu->next != NULL) // 有下一级菜单,切换到下一级
{
my_sys_info->sys_menu = my_sys_info->sys_menu->next;
my_sys_info->page = my_sys_info->page + 1;
}
else // 没有下一级菜单,执行功能
{
if (my_sys_info->sys_menu->function != NULL) // 绑定了功能,就执行功能
{
//my_sys_info->sys_menu->function(MENU_DEFAULT);
my_sys_info->func_choose = 1;
}
}
break;
default:
//std::cout << "default" << std::endl;
break;
}
}
}

// 添加菜单
// @param: last 上一页菜单
// @param: equal_last 同页上级菜单
// @param: func 功能函数绑定
// @param: show 显示函数绑定
// @param: name 名字
menu* add_page(menu* last, menu* equal_last, void (*func)(int), void (*show)(int), const char* name)
{
menu* p = NULL;
p = (menu*)malloc(sizeof(menu));
if (NULL == p)
{
printf("Fail to Allocate");
exit(0);
}
if (NULL == last) // 无父节点,为头节点
{
// 创建头节点
p->last = NULL;
}
else
{
// 中间节点
p->last = last;
}
if (NULL == equal_last) // 无同级上一项,为首项
{
if (last != NULL)
{
last->next = p;
}
p->equal_last = NULL;
p->number = 0; // 首项位号为0
}
else
{
equal_last->equal_next = p;
p->equal_last = equal_last;
p->number = equal_last->number + 1;
}
p->next = NULL;
p->equal_next = NULL;
p->name = name;
p->function = func;
p->ShowFunc = show;
return p;
}

void menu_show()
{
if (0 == my_sys_info->func_choose) // 菜单显示
{
int MaxNum; // 该页最大同级位号
menu* p = NULL;
p = my_sys_info->sys_menu;
while (p->equal_next != NULL)
{
p = p->equal_next;
}
MaxNum = p->number;
if (my_sys_info->number == 0 || MaxNum < 2) // 第一个 || 数量0,1
{
std::cout << "* " << my_sys_info->sys_menu->name << std::endl;
p = my_sys_info->sys_menu->equal_next;
if (p != NULL)
{
std::cout << " " << p->name << std::endl;
p = p->equal_next;
if (p != NULL)
{
std::cout << " " << p->name << std::endl;
}
}
}
else if (my_sys_info->number == MaxNum) // 最后一个
{
p = my_sys_info->sys_menu->equal_last;
p = p->equal_last;
if (p == NULL)exit(0);
std::cout << " " << p->name << std::endl;
p = p->equal_next;
if (p == NULL)exit(0);
std::cout << " " << p->name << std::endl;
std::cout << "* " << my_sys_info->sys_menu->name << std::endl;
}
else
{
p = my_sys_info->sys_menu->equal_last;
if (p == NULL)exit(0);
std::cout << " " << p->name << std::endl;
std::cout << "* " << my_sys_info->sys_menu->name << std::endl;
p = my_sys_info->sys_menu->equal_next;
if (p == NULL)exit(0);
std::cout << " " << p->name << std::endl;
}
}
else
{
my_sys_info->sys_menu->ShowFunc(MENU_DEFAULT);
}
}

//********************************************************
//**********************功能添加区************************
//*************一个功能函数对应一个显示函数***************
//********************************************************

void menu_pid_change(int opreat)
{
float value;
switch (opreat)
{
case UP:
switch (my_sys_info->number)
{
case 0:MENU_PID.kp = MENU_PID.kp + MENU_PID.step[MENU_PID.step_num];break;
case 1:MENU_PID.ki = MENU_PID.ki + MENU_PID.step[MENU_PID.step_num];break;
case 2:MENU_PID.kd = MENU_PID.kd + MENU_PID.step[MENU_PID.step_num];break;
case 3:MENU_PID.EXPECT = MENU_PID.EXPECT + MENU_PID.step[MENU_PID.step_num];break;
default:
break;
}
break;
case DOWN:
switch (my_sys_info->number)
{
case 0:MENU_PID.kp = MENU_PID.kp - MENU_PID.step[MENU_PID.step_num]; break;
case 1:MENU_PID.ki = MENU_PID.ki - MENU_PID.step[MENU_PID.step_num]; break;
case 2:MENU_PID.kd = MENU_PID.kd - MENU_PID.step[MENU_PID.step_num]; break;
case 3:MENU_PID.EXPECT = MENU_PID.EXPECT - MENU_PID.step[MENU_PID.step_num]; break;
default:
break;
}
break;
case RIGHT:
MENU_PID.step_num = (MENU_PID.step_num + 1) % 5;
break;
default:
//my_sys_info->sys_menu->function(opreat);
break;
}
}

void menu_pid_change_show(int opreat)
{
std::cout << " step = " << MENU_PID.step[MENU_PID.step_num] << std::endl;
switch (my_sys_info->number)
{
case 0:
std::cout << " kp = " << MENU_PID.kp << std::endl;
break;
case 1:
std::cout << " ki = " << MENU_PID.ki << std::endl;
break;
case 2:
std::cout << " kd = " << MENU_PID.kd << std::endl;
break;
case 3:
std::cout << " EXPECT = " << MENU_PID.EXPECT << std::endl;
break;
default:break;
}
}