对于嵌入式开发者而言,GUI想必都不陌生,但GUI的引入通常会耗费我们大量的精力去处理菜单之间的关系,而且占用往往会很大,所以我开发了一款GUI库,目的是让GUI的引入变得方便快捷,并能够让程序员集中注意力于功能的开发
开源地址:
GitHub:https://github.com/SuiXinSc/SXGUI--Simple.X.GUI
Gitee:https://gitee.com/SuiXinSc/SXGUI--Simple.X.GUI
交流Q群:659512171
目录
关于该GUI库的简要介绍和数据结构图在之前的开源预告中有,推荐先看一遍(原谅我鸽了这么长时间才写文,是真的抽不开空):传送门
Demo演示:
【开源】【人人可复刻】丝滑GUI库
一,程序功能:
1,菜单管理:
该GUI库支持动态创建,管理菜单项,即所有的菜单项不是被写在程序里定死的,可以通过调用函数的方式来更改
2,可拓展性:
支持无限级的菜单与APP添加,内置APP的处理,只需要调用函数便可将自己的APP添加至GUI中(若APP中出现死循环,要在死循环中处理按键读取,显示屏刷屏以及主动退出等操作,主动退出仅支持2024.8.5之后的版本)。另外,库中提供了默认的菜单界面,用户也可以自行设计菜单的界面(仅限v1.1及以上版本)。为了防止编写界面函数和APP函数时填写参数出错,我添加了 INTERFACE_PARAMETERS 和 APP_PARAMETERS 这两个宏定义,编写函数时只需要直接在括号中填入即可。
3,兼容性:
使用Keil的 O3 等级优化,开启微库,该GUI库依然能够正常使用,没有任何影响,同时也支持在RTOS中作为独立的线程使用
4,动画:
动画由 graphics_api 这个文件提供,使用的是PID动画,页间的过渡采用虚化,在SXGUI_Main 函数中调用 Graphics_Bokeh 函数实现
二,程序设计思路:
1,数据结构及处理:
为了方便管理,我采用了四向链表,形成了网络状结构(调用函数自动创建,不用手写),同时为了减少可能的逻辑错误,我加入了一个ROOT菜单项作为根,这样就由网状结构转变成了树状结构。
如果你看过我的程序,你会发现链表结构体中都是指针,因为每一个结构体本身就是数据,就不需要再储存其他数据了(名称除外)。
对于数据的处理,则全是指针的操作(突击检查,指针不扎实的自觉复习),可以说该库的内核就是通过指针指向树状结构的不同节点,每个节点之间也通过指针链接,其数据处理的实质是对不同节点指针指向的操作。当然,作为使用者,大可以不用了解它是如何处理的,只需要知道封装好的函数有什么功能,该怎么用,毕竟编写该库的初衷就是让GUI的开发简单化。
2, 跨平台的实现:
正如预告中说的那样,我采用逻辑图形分离的方式,在不同的平台中只需要改动 graphics_api.c 这个文件的部分基础功能函数即可正常运行,我使用的是 0.96 OLED显示屏,目前还没对彩屏进行专门的优化适配。
3,显示屏驱动:
采用我编写的 OLED驱动 v1.3M,M的意思也就是精简版,只保留了最基础的图形绘制,如画点,显示字符,其余的图形绘制均在 graphics_api.c 中。
该版驱动支持:
1,中英混合显示:字符中出现字库中含有的中文时,可以直接显示,无需专门用一个显示中文的函数像显示图片一样显示中文。
2,二元光栅:该版本驱动库支持二元光栅的基本运算,可以胜任遮挡,层叠等关系的处理
3,图形的批量绘制:调用任何绘图函数都只会在显存中运算,只有调用发送显存的函数时,才会一起发送给显示屏(可以理解为垂直同步),有效提高效率和帧率
4,调用方式:
正如前面所说,我在开发时已经将各种功能封装成函数了,使用者只需要调用这些函数即可实现功能,完全不涉及任何的指针和变量操作,下面是一个示例:
SXGUI_Init("Hub",SXGUI_Interface); //初始化GUI库
SXGUI_MenuItem *Sub1 = SXGUI_CreateMenu("信息",SXGUI_Interface);
SXGUI_MenuItem *Sub2 = SXGUI_CreateMenu("GUI信息",SXGUI_Interface);
SXGUI_APPItem *App1 = SXGUI_CreateApp("Draw Line",Draw_Line);
SXGUI_APPItem *App2 = SXGUI_CreateApp("Draw Round",Draw_Round);
SXGUI_APPItem *App3 = SXGUI_CreateApp("Round Rect",Round_Rect);
SXGUI_APPItem *App4 = SXGUI_CreateApp("内核版本",Kernel_Infor);
SXGUI_APPItem *App5 = SXGUI_CreateApp("图形库版本",Graphics_Infor);
SXGUI_APPItem *App6 = SXGUI_CreateApp("驱动信息",Driver_Infor);
SXGUI_APPItem *App7 = SXGUI_CreateApp("作者信息",Writer_Infor);
SXGUI_APPItem *App8 = SXGUI_CreateApp("PID Parameter",PID_Infor);
//创建菜单项、APP项
SXGUI_RootAddSubMenu(Sub1);
SXGUI_AddSubMenu(Sub1,Sub2);
SXGUI_RootAddApp(App1);
SXGUI_RootAddApp(App2);
SXGUI_RootAddApp(App3);
SXGUI_AddApp(Sub2,App4);
SXGUI_AddApp(Sub2,App5);
SXGUI_AddApp(Sub2,App6);
SXGUI_AddApp(Sub2,App7);
SXGUI_AddApp(Sub2,App8);
//添加菜单、APP(顺序随意)
这样就完成了一个GUI界面的创建,后续还可进行实时更改。
三,结构体:
1,SXGUI_MenuItem:
该结构体为菜单项的链表结构体;
typedef struct SXGUI_MenuItem{
char *name;
struct SXGUI_MenuItem *Pre;
struct SXGUI_MenuItem *Next;
struct SXGUI_MenuItem *Parent;
struct SXGUI_MenuItem *Sub;
struct SXGUI_APPItem *App;
void (*Interface)(INTERFACE_PARAMETERS); //界面绘制函数
const uint8_t *Icon; //图标的指针
} SXGUI_MenuItem;
参数 | 描述 |
*name | 名称字符串指针 |
*Pre | 上个目录的指针 |
*Next | 下个目录的指针 |
*Parent | 父目录指针 |
*Sub | 子级首目录的指针 |
*Interface | 界面绘制函数的指针 |
*Icon | 目录图标的指针 |
2,SXGUI_APPItem:
该结构体为应用项的结构体;
typedef struct SXGUI_APPItem{
char *name;
void (*Function)(APP_PARAMETERS);
struct SXGUI_APPItem *Pre;
struct SXGUI_APPItem *Next;
struct SXGUI_MenuItem *Parent;
const uint8_t *Icon; //图标的指针
} SXGUI_APPItem;
参数 | 描述 |
*name | 名称字符串指针 |
*Function | APP函数指针 |
*Pre | 上个应用的指针 |
*Next | 下个应用的指针 |
*Parent | 父目录指针 |
*Icon | 目录图标的指针 |
3,SXGUI_KeyItem:
该结构体为按键的结构体;
typedef struct SXGUI_KeyItem{
bool Back;
bool Pre;
bool Next;
bool OK;
int *MoreKey; //更多按键
} SXGUI_KeyItem;
参数 | 描述 |
Back | 回到上一级,布尔值,占用 1bit |
Pre | 上一个,布尔值,占用 1bit |
Next | 下一个,布尔值,占用 1bit |
OK | 确定,布尔值,占用 1bit |
*MoreKey | 更多扩展按键的指针,可直接用数组,int型值 |
注意:该结构体需要自己定义,同时会传入APP函数内,非阻塞型的APP函数可以直接通过该结构体读取按键。
四,主要函数:
1,SXGUI_Interface:
该函数为提供的默认界面函数,不想自己写界面函数的可以直接调用;
void SXGUI_Interface(INTERFACE_PARAMETERS) {
int List_x = Fontsize / 2,
List_y = Fontsize + 1;
Graphics_ListChoose(List_x, List_y, 128 - List_x - 1, 64 - Fontsize - 1,
Fontsize, Menu, MenuNum, AppNum, option, style, FontColor);
Graphics_ShowString(0, 0, Menu->name, Fontsize, FontColor);
}
函数参数 INTERFACE_PARAMETERS 是一个宏定义,具体如下:
#define INTERFACE_PARAMETERS int Fontsize, struct SXGUI_MenuItem* Menu, int option,int MenuNum,\
int AppNum, uint32_t BackColor, uint32_t FontColor, int style //界面参数的宏定义
该宏为界面绘制函数的参数,在自己编写界面绘制函数的时候为避免填错参数,直接填入该宏即可
2, SXGUI_CreateRoot:
该函数为创建根目录的函数,在 SXGUI_Init 中已经调用,无需用户再次调用;
int SXGUI_CreateRoot(char* Name, void (*Interface)(INTERFACE_PARAMETERS)){
if(ROOT != NULL) {
return SXGUI_ERROR; //如果已创建
}
ROOT = malloc(sizeof(SXGUI_MenuItem));
ROOT->name = Name;
ROOT->Interface = Interface;
return SXGUI_OK;
}
其中的 ROOT 是全局的指针变量,定义如下:
SXGUI_MenuItem* ROOT;
3,SXGUI_CreateMenu:
该函数为创建菜单项的函数,返回值为 SXGUI_MenuItem 结构体指针;
SXGUI_MenuItem* SXGUI_CreateMenu(char* Name, void (*Interface)(INTERFACE_PARAMETERS)){
SXGUI_MenuItem* Menu = malloc(sizeof(SXGUI_MenuItem));
Menu->name = Name;
Menu->Interface = Interface;
return Menu;
}
4,SXGUI_CreateApp:
该函数为创建APP的函数返回值为 SXGUI_APPItem 结构体指针;
SXGUI_APPItem* SXGUI_CreateApp(char* Name, void (*AppFunction)(APP_PARAMETERS)) {
SXGUI_APPItem* APP = malloc(sizeof(SXGUI_APPItem));
APP->name = Name;
APP->Function = AppFunction;
return APP;
}
5,删除项:
有释放GUI,删除菜单项,删除APP项,这里不做过多讲解
6,添加项:
分为在根目录下添加和在普通目录下添加,也分为添加菜单项和添加APP项;
//在根目录下添加菜单项
int SXGUI_RootAddSubMenu(SXGUI_MenuItem* SubMenu) {
if(SubMenu->Parent != NULL) { //如果已被添加
return SXGUI_ERROR;
}
SubMenu->Parent = ROOT;
SXGUI_MenuItem* Ptr = ROOT->Sub;
if(Ptr != NULL) {
for(; Ptr->Next != NULL; Ptr = Ptr->Next);
Ptr->Next = SubMenu;
} else {
ROOT->Sub = SubMenu;
}
SubMenu->Pre = Ptr;
return SXGUI_OK;
}
//根目录下添加APP项
int SXGUI_RootAddApp(SXGUI_APPItem* APP) {
if(APP->Parent != NULL) { //如果已被添加
return SXGUI_ERROR;
}
APP->Parent = ROOT;
SXGUI_APPItem* Ptr = ROOT->App;
if(Ptr != NULL) {
for(; Ptr->Next != NULL; Ptr = Ptr->Next);
Ptr->Next = APP;
} else {
ROOT->App = APP;
}
APP->Pre = Ptr;
return SXGUI_OK;
}
//菜单下添加子菜单
int SXGUI_AddSubMenu(SXGUI_MenuItem* ParentMenu, SXGUI_MenuItem* SubMenu) {
if(SubMenu->Parent != NULL) { //如果已被添加
return SXGUI_ERROR;
}
SubMenu->Parent = ParentMenu;
SXGUI_MenuItem* Ptr = ParentMenu->Sub;
if(Ptr != NULL) {
for(; Ptr->Next != NULL; Ptr = Ptr->Next);
Ptr->Next = SubMenu;
} else {
ParentMenu->Sub = SubMenu;
}
SubMenu->Pre = Ptr;
return SXGUI_OK;
}
//菜单下添加APP
int SXGUI_AddApp(SXGUI_MenuItem* ParentMenu, SXGUI_APPItem* APP) {
if(APP->Parent != NULL) { //如果已被添加
return SXGUI_ERROR;
}
APP->Parent = ParentMenu;
SXGUI_APPItem* Ptr = ParentMenu->App;
if(Ptr != NULL) {
for(; Ptr->Next != NULL; Ptr = Ptr->Next);
Ptr->Next = APP;
} else {
ParentMenu->App = APP;
}
APP->Pre = Ptr;
return SXGUI_OK;
}
7,SXGUI_Init:
该函数作用是初始化变量;
void SXGUI_Init(char* HubName, void (*Interface)(INTERFACE_PARAMETERS)){
SXGUI_CreateRoot(HubName,Interface); //创建根目录
NowMenu = ROOT; //初始化指针变量
}
8,SXGUI_Main:
该函数是GUI的主逻辑程序,在主程序中循环执行,由于代码较长,这里不再贴出,只展示函数声明;
void SXGUI_Main(int Fontsize, uint32_t BackColor, uint32_t FontColor, int style, SXGUI_KeyItem* Key);
里面的参数也很好看懂,这里不解释了
五,常用图形库函数:
函数 | 功能 |
Graphics_Init | 初始化图形库 |
Graphics_Clear | 清空显存 |
Graphics_Display | 发送显存数据 |
Graphics_DrawPoint | 画点 |
Graphics_ShowBMP | 显示图片 |
Graphics_Bokeh | 背景虚化效果 |
Graphics_ShowString | 显示字符串,支持中英混合显示 |
Graphics_DrawLine | 画虚/实线 |
Graphics_DrawQuarterRound | 画四分之一圆 |
Graphics_DrawRound | 画整圆,填充/不填充 |
Graphics_DrawRect | 画矩形,填充/不填充 |
Graphics_DrawRoundRect | 画圆角矩形,填充/不填充 |
六,结语:
该图形库会不定期更新,主要是根据应用中出现的不足和需求进行更新,各位有什么想法也可以留言或私信我,广泛的交流才能更好
开发不易,点个关注再走吧 (>▽<)