前言
       一般来说使用12864进行显示,配合按键作为人机界面交互,如果显示的内容较多的话,往往不能在一个页面显示完全,这里就需要用到多级菜单来进行管理,根据显示内容的不同,将其划分成不同的菜单,通过按键操作来选择不同的界面进行显示。这里介绍一种我自己使用的多级菜单实现原理和框架,并且分析一下其实现的方法,分享出来供大家参考。
一、菜单数据结构体

/************ 定义菜单列表结构体 **************/
typedef struct menu{unsigned char menu_sum;        /* 一个菜单中选项的个数 选项编号从0开始 一级最多256个选项 */unsigned char menu_cursor_num; /* 当前选中的选项在菜单列表中的编号 */unsigned char menu_cursor_lcd; /* 光标在屏幕上显示的位置 1~DP_NUM行 */unsigned char menu_dp[DP_NUM]; /* 用于存储目前显示在屏幕上的3行菜单选项在各自列表中的序号 */char **menu_str_point;          /* 菜单选项名称字符串指针,指向各级菜单选项的名称,选项名称为一个指针数组 */struct menu *menu_before;      /* 指向上一级菜单列表 如果是主菜单就为NULL */struct menu **menu_next;      /* 指向下一级菜单列表 */void (**menu_function)();      /* 函数指针 按下确认按键后 根据选项调用不同的函数执行操作 */
}menu;

多级菜单的实现最重要的就是定义一个菜单结构体,每级菜单都用一个菜单结构体来描述。
1. menu_sum表示每级菜单中的选项数量,由于其数据类型我定义为了unsigned char的类型,所以最大的选项个数类256个,基本够用了,一般没有人会设置超过256个选项吧。
2. menu_cursor_num表示光标位置,这个参数的类型需要和menu_sum的类型一致,为0时表示当前光标在第一个选项上。多级菜单势必要涉及到光标的操作,通过按键来上下移动光标,光标选中的那一行被反白显示,按下确认键之后,根据光标的位置进入到对应的下级菜单中或者执行对应的菜单函数。
3. menu_cursor_lcd用来确定当前光标在屏幕上的位置,12864一般采用3行来显示菜单列表信息,这个参数用来表示当前光标在第几行。
4. menu_dp[DP_NUM]表示当前显示在屏幕上的选项列表,DP_NUM表示屏幕最多可以显示几行列表,一般12864的液晶显示3行选项列表。0,1,2表示当前显示在屏幕上的是列表中标号为0,1,2的选项。
5. **menu_str_point指向一个字符串指针数组,数组里面存放着各个选项的名字字符串,一般定义成如下形式。然后将这个指针指向这个数组即可。

char const *menu_0_str[MENU_0_SUM] = {{"放大增益"},{"缩小比例"},{"DDS频率"},{"直流衰减"},{"Ui峰峰值"},{"继电器控制"}
};

6. *menu_before这个指针指向上级菜单,主菜单没有上级菜单设置为NULL,子菜单想返回上级菜单时即可通过这个指针返回上级菜单。通过这条语句即可now_menu = now_menu->menu_before;
7. **menu_next这是一个指向下级菜单列表的指针,由于一个菜单里面可能包含多个选项子菜单,每个子菜单又有可能对应一个菜单结构体,所以这个参数是一个双重指针,指向一个菜单结构体的列表。想进入光标所指的下级菜单式now_menu = now_menu->menu_next[now_menu->menu_cursor_num];
8. void (**menu_function)();回调函数指针参数,如果菜单选项对应的是一个具体的功能函数时,执行这个选项就会跳转到某个具体的功能函数去执行,一个菜单下面可能有很多的选项,每个选项都对应一个功能函数,这些函数就组成了一个函数列表,同样的用一个双重指针去描述。需要注意的是这里定义的回调函数是void fun(void)类型的函数,所以每个回调函数都必须是这个类型。
定义一个具体的菜单结构体时,可以在其中给其赋予初值,示例如下:

/*************** 数据查看 MENU_0下级菜单结构体定义 *************/char const *menu_0_str[MENU_0_SUM] = {{"放大增益"},{"缩小比例"},{"DDS频率"},{"直流衰减"},{"Ui峰峰值"},{"继电器控制"}
};menu menu_0 = {MENU_0_SUM,               /* 选项个数 */0,                            /* 初始化默认指向 第0个选项 */1,                           /* 屏幕上显示默认在第1行  */{0,1,2},                  /* 初始化默认显示前3个选项 */(char **)menu_0_str,          /* 指向菜单选项信息的指针数组 */&menu_main,                  /* 指向上级目录 */NULL,                       /* 指向下级目录 */NULL,                       /* 函数指针 确认选项后操作的函数 */
};

二、菜单初始化
       程序中通过一个菜单初始化函数来对所有的菜单结构体进行初始化,主要是为结构体中的双重指针申请内存,然后将双重指针指向对应的结构体和函数。

void Menu_Init(void)
{/********************** 主菜单结构体中各成员变量初始化 **********************************/menu_main.menu_next = Menu_Malloc( sizeof(int)* MENU_MAIN_SUM); /* 为下级菜单指针申请内存 *//* 将主菜单结构体中的指向下级菜单的指针 一一指向各个菜单结构体 */menu_main.menu_next[0] = &menu_0;  /* 选项0 的下级菜单 没有就为NULL *//* 将主菜单结构体中的指向下级菜单的执行函数 一一指向各个函数 */menu_main.menu_function = Menu_Malloc( FUNC_SIZE* MENU_MAIN_SUM); /* 为选项的函数指针申请内存 */menu_main.menu_function[0] = NULL;       /* 选项0的执行函数指针 指向相对应的函数 没有就为NULL *//*********************** menu_0菜单结构体中各成员变量初始化 *****************************//* menu_0菜单没有下级菜单不需要申请内存 */menu_0.menu_next = NULL; /* 没有下级菜单了 将下级菜单指针初始化为NULL */menu_0.menu_function = Menu_Malloc(FUNC_SIZE* MENU_0_SUM); /* 为menu_0选项的函数指针 申请内存 */menu_0.menu_function[0] = &Func_0_0;/* 选项0的执行函数指针 指向相对应的函数 没有就为NULL */menu_0.menu_function[1] = &Func_0_1;/* 选项1的执行函数指针 指向相对应的函数 没有就为NULL */menu_0.menu_function[2] = &Func_0_2;/* 选项2的执行函数指针 指向相对应的函数 没有就为NULL */menu_0.menu_function[3] = &Func_0_3;/* 选项3的执行函数指针 指向相对应的函数 没有就为NULL */menu_0.menu_function[4] = &Func_0_4;/* 选项4的执行函数指针 指向相对应的函数 没有就为NULL */menu_0.menu_function[5] = &Func_0_5;/* 选项5的执行函数指针 指向相对应的函数 没有就为NULL */
}

程序中有两个菜单,一个就是主菜单,主菜单下只有一个设置选项子菜单,设置菜单下有6个选项,这6个选项没有子菜单,都是具体的执行函数。首先初始化主菜单结构体,为指针申请内存,根据子菜单的数量申请内存,根据顺序指向子菜单结构体,之后为回调函数指针申请内存,由于是子菜单没有具体的执行函数,指针指向NULL。之后是设置子菜单结构体的初始化,过程是相同的,由于没有子菜单了,子菜单指针设置为NULL,设置回调函数指针指向每个选项的执行函数。

三、菜单的操作和显示
       菜单的操作和显示主要通过两个函数来实现,一个就是Menu_Handler()菜单操作函数主要负责处理各种按键消息,根据不同的按键消息来进行不同的操作,还有一个就是Menu_Display()负责显示菜单列表。所有的菜单操作都是通过一个菜单结构体指针进行的,Current_Menu指针指向当前正在操作的结构体,为NULL时表示没有进行菜单操作。

menu *Current_Menu = NULL; /* 初始化时默认为NULL,不显示菜单 */
void Menu_Display(void)
{if(Current_Menu != NULL) /* 有菜单时 */{/**************** 根据光标的位置显示向上和向下箭头 *******************/ if(Current_Menu->menu_sum > 1)//菜单的选项大于一个才显示箭头{if(Current_Menu->menu_cursor_num == 0) /* 当前的光标位置在第一个选项了,就没有那个向上箭头显示,只有一个向下箭头显示 */{Menu_ShowBmp(Menu_Arrow_X+8,0,Menu_Arrow_X+16,2,Menu_ShowNor,Menu_BMP_DOWN); /* 显示向下的箭头 */Menu_Rect(Menu_Arrow_X,0,Menu_Arrow_X+8,2,0); /* 清除上箭头显示的位置 */}else if(Current_Menu->menu_cursor_num == (Current_Menu->menu_sum-1)) /* 光标在菜单的最后一个选项的时候,只显示向上的箭头 */{Menu_ShowBmp(Menu_Arrow_X,0,Menu_Arrow_X+8,2,Menu_ShowNor,Menu_BMP_UP); /* 显示向下的箭头 */Menu_Rect(Menu_Arrow_X+8,0,Menu_Arrow_X+16,2,0); /* 清除下箭头显示的位置 */}else /* 其他光标在菜单中间选项时,同时显示两个箭头 */{Menu_ShowBmp(Menu_Arrow_X+8,0,Menu_Arrow_X+16,2,Menu_ShowNor,Menu_BMP_DOWN); /* 显示向下的箭头 */Menu_ShowBmp(Menu_Arrow_X,0,Menu_Arrow_X+8,2,Menu_ShowNor,Menu_BMP_UP); /* 显示向下的箭头 */}}if((Current_Menu->menu_cursor_lcd == 1)||(Current_Menu->menu_cursor_lcd == 2)||(Current_Menu->menu_cursor_lcd == 3))  /* 有效的光标位置 同时有选项 */{Menu_Display_PrintfShowClear(Menu_X,Menu_Line2,Menu_Size16,((Current_Menu->menu_cursor_lcd == 1)?Menu_ShowClear2:Menu_ShowClear0/* 光标选中的一行反白加填充 */),\"%d.%s", Current_Menu->menu_dp[0]+1,Current_Menu->menu_str_point[Current_Menu->menu_dp[0]]); /* 显示菜单的第一个显示选项 */if(Current_Menu->menu_sum > 1) /* 如果菜单的选项大于1 显示第二个选项 */{Menu_Display_PrintfShowClear(Menu_X,Menu_Line3,Menu_Size16,((Current_Menu->menu_cursor_lcd == 2)?Menu_ShowClear2:Menu_ShowClear0/* 光标选中的一行反白加填充 */),\"%d.%s",Current_Menu->menu_dp[1]+1,Current_Menu->menu_str_point[Current_Menu->menu_dp[1]]); /* 显示菜单的第二个显示选项 */}if(Current_Menu->menu_sum > 2) /* 如果菜单的选项大于2 显示第三个选项 */{Menu_Display_PrintfShowClear(Menu_X,Menu_Line4,Menu_Size16,((Current_Menu->menu_cursor_lcd == 3)?Menu_ShowClear2:Menu_ShowClear0/* 光标选中的一行反白加填充 */),\"%d.%s",Current_Menu->menu_dp[2]+1,Current_Menu->menu_str_point[Current_Menu->menu_dp[2]]); /* 显示菜单的第三个显示选项 */}}}
}/*****************************************
*函数名称:Menu_Handler
*输入参数:Menu_News:一般为按键消息 上、下、确认、取消 光标在菜单中只能上下移动
*返回值  :无
*功能描述:根据传递进来的消息改变菜单结构体值
*更新时间:2019-02-02
*****************************************/
void Menu_Handler(u8 Menu_News)
{if((Menu_News == Menu_News_up)||(Menu_News == Menu_News_down)||(Menu_News == Menu_News_conf)||(Menu_News == Menu_News_cancel)) /* 判断消息的有效性,是否为有效的按键按下了 */{switch(Menu_News){case(Menu_News_up): /* 向上按键按下 */if(Current_Menu != NULL) /* 屏幕上已经显示了菜单 */{if(Current_Menu->menu_cursor_num != 0) /* 光标还没有移动到当前菜单的第一个选项 */{Current_Menu->menu_cursor_num--; /* 光标上移一个选项 */if(Current_Menu->menu_cursor_lcd > 1) /* 屏上的光标显示不在第一行 */{Current_Menu->menu_cursor_lcd--; /* 光标上移一个选项 */}else /* 光标在屏幕上的第一行选项同时也不是菜单的第一个选项 需要改变屏幕上显示的三个选项 */{Current_Menu->menu_dp[0]--; /* 屏幕上显示的三个选项,整体上移一个选项 */Current_Menu->menu_dp[1]--; /* 屏幕上显示的三个选项,整体上移一个选项 */Current_Menu->menu_dp[2]--; /* 屏幕上显示的三个选项,整体上移一个选项 */}}}break;case(Menu_News_down): /* 向下按键按下 */if(Current_Menu != NULL) /* 屏幕上已经显示了菜单 */{if(Current_Menu->menu_cursor_num != (Current_Menu->menu_sum-1)) /* 光标是否已经移动到了最后一个选项 */{Current_Menu->menu_cursor_num++; /* 光标下移一个选项 */if(Current_Menu->menu_cursor_lcd < DP_NUM) /* 屏上的光标显示不在最后一行 */{Current_Menu->menu_cursor_lcd++; /* 光标下移一个选项 */}else /* 光标在屏幕上的第三行选项同时也不是菜单的最后一个选项 需要改变屏幕上显示的三个选项 */{Current_Menu->menu_dp[0]++; /* 屏幕上显示的三个选项,整体下移一个选项 */Current_Menu->menu_dp[1]++; /* 屏幕上显示的三个选项,整体下移一个选项 */Current_Menu->menu_dp[2]++; /* 屏幕上显示的三个选项,整体下移一个选项 */}}}break;case(Menu_News_conf): /* 确认按键被按下 */if(Current_Menu == NULL) /* 屏幕上还没有显示菜单选项 */{Display_Clear();//清屏Current_Menu = &menu_main; /* 菜单指针指向主菜单 */Menu_Display_PrintfShowClear(0,0,Menu_Size16,0,"主菜单"); /* 显示菜单标题 */}else /* 屏幕上已经有显示的菜单了 */{Menu_Display_Clear(); /* 清屏 */if(Current_Menu->menu_next != NULL) /* 如果当前的光标选择的选项有下级菜单 */{Menu_Display_PrintfShowClear(0,0,Menu_Size16,0,Current_Menu->menu_str_point[Current_Menu->menu_cursor_num]); /* 显示菜单标题 */Current_Menu = Current_Menu->menu_next[Current_Menu->menu_cursor_num]; /* 菜单指针指向下级菜单 */}else if(Current_Menu->menu_function[Current_Menu->menu_cursor_num] != NULL) /* 如果没有下级菜单了,但是有回调函数,则执行回调函数 */{//                      Menu_Display_Clear(); /* 清屏 */Menu_Display_PrintfShowClear(0,0,Menu_Size16,0,Current_Menu->menu_str_point[Current_Menu->menu_cursor_num]); /* 显示菜单标题 */Current_Menu->menu_function[Current_Menu->menu_cursor_num]();  /* 回调函数执行 */Menu_Display_PrintfShowClear(0,0,Menu_Size16,0,Current_Menu->menu_before->menu_str_point[Current_Menu->menu_before->menu_cursor_num]); /* 显示菜单标题 */}else /* 既没有下级菜单,也没有回调函数,说明没有菜单初始化完成 */{Menu_Assert(0); /* 错误断言 */}}break;case(Menu_News_cancel): /* 取消返回 */if(Current_Menu != NULL) /* 如果屏幕上现在是有菜单的才执行取消 */{Current_Menu->menu_cursor_num = 0; /* 光标位置清零 */Current_Menu->menu_cursor_lcd = 1; /* 光标在屏幕上回到第一行 */Current_Menu->menu_dp[0] = 0;            /* 显示的选项初始化 */Current_Menu->menu_dp[1] = 1;             /* 显示的选项初始化 */Current_Menu->menu_dp[2] = 2;             /* 显示的选项初始化 */Current_Menu = Current_Menu->menu_before; /* 回到上级菜单 */Menu_Display_Clear(); /* 清屏 */if(Current_Menu != NULL){if(Current_Menu->menu_before == NULL) /* 主菜单 */{Menu_Display_PrintfShowClear(0,0,Menu_Size16,0,"主菜单"); /* 显示菜单标题 */}else{Menu_Display_PrintfShowClear(0,0,Menu_Size16,0,Current_Menu->menu_before->menu_str_point[Current_Menu->menu_before->menu_cursor_num]); /* 显示菜单标题 */}}else /* 回到主界面了 */{}}break;}}
}

Menu_Display()函数主要是菜单显示,显示菜单选项、光标、箭头指示等。Menu_Handler()函数主要是根据按键操作来改变光标和菜单状态的。这两个函数注释的比较详细的,有兴趣的可以研究一下,可以参考借鉴,这里不再多做分析。

四、回调函数编写
       回调函数是底层菜单最终需要执行的函数,根据这个函数来实现功能和显示不同的界面,由于结构体中的函数指针定义关系,回调函数的形式都是void fun(void)类型的,可以根据自己的需要进行更改。我这里来分享一下我的回调函数的编写框架。一个具体的回调函数如下所示:

const uint16_t CountDown_Ui[] = {100,10,1}; /* 根据光标位置加减数值 */
/*****************************************
*函数名称:Func_0_0 (放大增益)
*功能描述:主菜单 0_0选项的回调执行函数
*输入参数:无
*返回值  :无
*更新时间:2018-12-30
*****************************************/
void Func_0_0(void)
{uint8_t key,cursor_sta = 0,res;uint16_t Ui_gain_old = Ui_gain;AV_MEASURE_UO_IN();//继电器控制到 测试Uidelay_ms(50);//等待继电器动作完成ADC_sample_sta = 0; //测量Uiwhile(1){Menu_Display_PrintfShowClear(0,Menu_Line2,Menu_Size16,Menu_ShowNor,"增益:%02d.%d",Ui_gain/10,Ui_gain%10);Menu_Display_PrintfShowClear(0,Menu_Line3,Menu_Size16,Menu_ShowNor,"Ui_Vpp:%02d.%01dmV",ADC_sample_data.Ui/10,ADC_sample_data.Ui%10);switch(cursor_sta){case(0):Menu_Display_PrintfShow(40,Menu_Line2,Menu_Size16,Menu_ShowWhite,"%d",Ui_gain/100); /* 显示光标 */break;case(1):Menu_Display_PrintfShow(40+8,Menu_Line2,Menu_Size16,Menu_ShowWhite,"%d",(Ui_gain%100)/10); /* 显示光标 */break;case(2):Menu_Display_PrintfShow(40+24,Menu_Line2,Menu_Size16,Menu_ShowWhite,"%d",Ui_gain%10); /* 显示光标 */break;}res = xQueueReceive(Key_QueueHandle,&key,200); /* 200ms */if(res == pdTRUE) //检测到按键{if((key == Menu_News_conf)||(key == Menu_News_cancel)) /* 按下了取消键或者确认键 跳出循环 */break;switch(key){case(Menu_News_up): /* 上键 这里做加键 */if((Ui_gain + CountDown_Ui[cursor_sta]) <= 500) /* 小于等于最大的设定值 */{Ui_gain = Ui_gain + CountDown_Ui[cursor_sta];}break;case(Menu_News_down): /* 下键 这里做减键 */if(((Ui_gain - CountDown_Ui[cursor_sta]) >= 200)&&(Ui_gain > CountDown_Ui[cursor_sta])) /* 大于最小设定值 */{Ui_gain = Ui_gain - CountDown_Ui[cursor_sta];}break;case(Menu_News_left): /* 左键 */if(cursor_sta == 0)cursor_sta = 2;elsecursor_sta--;break;case(Menu_News_right): /* 右键 */if(cursor_sta == 2)cursor_sta = 0;elsecursor_sta++;break;}}}if(Conf_Revise() == 0xaa ) /* 确认 */{Menu_Display_PrintfShowCent(Menu_Line2,Menu_Size16,Menu_ShowClear0,"修改中...");Write_Gain();//写入存储delay_ms(500);}else /* 取消 */{Ui_gain = Ui_gain_old; //增益调回原值}ADC_sample_sta = 0xff;memset(ADC_sample_data.ADC_Ui_buf,0,sizeof(ADC_sample_data.ADC_Ui_buf));ADC_sample_data.ADC_Ui_count = 0;
}

这个函数是一个参数设置函数,用来设置参数并保存,一个while(1)的死循环,在加上一个if break的语句,判断按下取消或确认键就退出死循环,退出循环后选择是否确认要保存参数,Conf_Revise返回0xaa表示确认保存,否则不保存修改的参数。

总结
       上面分析了一下关于多级菜单实现的方法,介绍的方法最好基于RTOS来实现,创建一个任务来运行多级菜单的操作和显示,并且由于回调函数是一个死循环的形式,如果没有RTOS的任务调度和阻塞功能,就无法在回调函数执行的过程中处理别的数据,实现起来较为困难。

源码下载

12864多级菜单实现方法相关推荐

  1. 分享一个按键液晶多级菜单设计方法,工控行业中沿用多年,屡试不爽,附带本人一个调试通过

    转自:https://www.amobbs.com/forum.php?mod=viewthread&tid=4001689 typedef struct { uchar KeyStateIn ...

  2. qml中使用combobox实现多级菜单_excel实用技巧:如何构建多级下拉菜单

    编按:哈喽,大家好!说到做下拉菜单,小伙伴们都知道直接使用Excel中的数据验证就可以实现,但是二级.三级,甚至更多级的下拉菜单,可能就有点蒙圈了.其实用Excel制作三级下拉菜单,一点都不难,它就跟 ...

  3. qml中使用combobox实现多级菜单_Excel教程:还不会做Excel三级下拉菜单?其实它跟复制粘贴一样简单...

    关注回复[目录]学习113篇Excel教程 全套Excel视频教程,扫码观看 编按: 哈喽,大家好!说到做下拉菜单,小伙伴们都知道直接使用Excel中的数据验证就可以实现,但是二级.三级,甚至更多级的 ...

  4. html分级显示的下拉列表,javascript实现在下拉列表中显示多级树形菜单的方法

    本文实例讲述了javascript实现在下拉列表中显示多级树形菜单的方法.分享给大家供大家参考.具体如下: 这里演示在下拉列表框中显示分级的菜单,在很多网站都可以看到的效果,很实用,下拉列表框中的选项 ...

  5. mysql vue 菜谱_vue+ java 实现多级菜单递归效果

    效果如图: 大概思路:树形视图使用的是vue官方事例代码,java负责封装数据,按照vue官方事例的数据结构封装数据即可.有两个需要关注的点: 1.官方事例的数据结构是一个对象里面包含着集合,而不是一 ...

  6. c# mvc5 view 多层_MVC5+EF6 入门完整教程13 -- 动态生成多级菜单

    稍微有一定复杂性的系统,多级菜单都是一个必备组件. 本篇专题讲述如何生成动态多级菜单的通用做法. 我们不用任何第三方的组件,完全自己构建灵活通用的多级菜单. 需要达成的效果:容易复用,可以根据mode ...

  7. day1作业二:多级菜单操作

    作业二:多级菜单 (1)三级菜单 (2)可以次选择进入各子菜单 (3)所需新知识点:列表.字典 要求:输入back返回上一层,输入quit退出整个程序 思路: (1)首先定义好三级菜单字典: (2)提 ...

  8. java递归实现多级菜单栏_vue+ java 实现多级菜单递归效果

    效果如图: 大概思路:树形视图使用的是vue官方事例代码,java负责封装数据,按照vue官方事例的数据结构封装数据即可.有两个需要关注的点: 1.官方事例的数据结构是一个对象里面包含着集合,而不是一 ...

  9. java递归实现多级菜单栏_Java构建树形菜单以及支持多级菜单的实例代码

    这篇文章主要介绍了Java构建树形菜单的实例代码(支持多级菜单),非常不错,具有参考借鉴价值,需要的朋友可以参考下 效果图:支持多级菜单. 菜单实体类: public class Menu { // ...

最新文章

  1. JavaScript 数据拷贝
  2. 设置Eclipse智能提示(转)
  3. [你必须知道的.NET]第十二回:参数之惑---传递的艺术(下)
  4. VC++多线程编程[转]
  5. 【机器学习】——纯Python建立BP模型
  6. 华为云UGO正式公测:4大核心优势破解异构数据库迁移难题
  7. 双精度浮点数转换_模型压缩一半,精度几乎无损,TensorFlow推出半精度浮点量化工具包,还有在线Demo...
  8. ubuntu16 安装opencv
  9. android中一个应用程序启动另外一个应用程序,并传递数据。
  10. matlab的四个取整函数!
  11. Vue前端页面关键词快速搜索方法
  12. 牙疼怎么快速止痛,三招解决牙痛立竿见影
  13. NVIDIA边缘端设备(nano、tx2、xavier等)查看安装的jetpack版本
  14. PHP求职宝典系列——PHP Web 编程篇
  15. 【3】Kubernetes资源管理
  16. expdp和impdp备份数据库(数据泵备份)
  17. 数据结构(从概念到C++实现)
  18. IIC总线协议---以存储芯片at24c64为例
  19. C/C++编译器 GCC 默认 C++ 17
  20. python朋友圈头像_Python读取微信好友头像,拼成祝福语九宫格

热门文章

  1. 《浅谈F5健康检查常用的几种方式》—那些你应该知道的知识(二)
  2. C#上位机开发(十二)—— SQLite的使用
  3. Java面试知识总结(带基础)
  4. iQQ 基于WebQQ3.0协议Java开发 跨平台QQ客户端
  5. Android 天气APP(二十七)增加地图天气的逐小时天气、太阳和月亮数据
  6. 离散数学程序实践——判断是否(反)自反,(反)对称,可传递——c
  7. 如何用c语言写上位机软件,上位机的工作原理_上位机编程用什么软件
  8. win10快捷方式变成空白图标怎么办?
  9. 换行符后需要加逗号吗c语言,为什么大多数编程语言都在换行符之前而不是换行符之后使用逗号?...
  10. python制作自动交易软件下载_python自动量化交易系统