用C语言写一个耦合性低、完全可移植的轻量级菜单框架

2024-01-26 19:28

本文主要是介绍用C语言写一个耦合性低、完全可移植的轻量级菜单框架,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

作为嵌入式软件开发,可能经常会使用命令行或者显示屏等设备实现人机交互的功能,功能中通常情况都包含 UI 菜单设计;对于复杂的UI设计,可能最多优先考虑的是使用开源的GUI库。

但GUI使用起来复杂,在简单的UI设计中则臃肿或者较难实现(比如OLED这种);基于这种情况,很多开发人员都会有自己的菜单框架模块,避免重复造轮子,网上有很多这种菜单框架的代码,但是大多耦合性太强。

代码层面上大部分都耦合了按键和不同平台(不同尺寸的OLED)等模块;并无法独立出来适配不同的菜单设计。

而多级菜单的设计也使得上层软件被迫耦合,比如一张表包含了多级菜单内容等。

基于以上种种痛点,本文介绍一个耦合性低、完全可移植的轻量级菜单框架,菜单显示风格和显示平台完全由自己根据需求设计,而菜单操作统一由菜单框架处理即可,提高程序的移植性。

二、特点

主要特点就是耦合性低,移植无需修改。且不和任何模块耦合,同时对于上层软件设计,也可以做到解耦实现。

可以为不同菜单设计不同的显示风格。

三、介绍

1、多级菜单

同级菜单以数组的方式体现,父菜单和子菜单的关联则使用链表实现。

数组元素内容有:

  • 菜单选项字符串描述(多语种可设置)

  • 菜单选项进入回调函数:当前菜单选项进入时(从父菜单进入)需要执行一次的函数

  • 菜单选项退出回调函数:当前菜单选项进入后退出时(退出至父菜单)需要执行一次的函数

  • 菜单选项重加载回调函数:当前菜单选项每次加载时(从父菜单进入或子菜单退出)需要执行一次的函数

  • 菜单选项周期调度回调函数:当前菜单选项的周期调度函数

  • 菜单选项的扩展数据

链表内存可以选择采用动态内存分配或者数组实现

方便对不同菜单界面功能解耦:

大部分菜单采用的都是数组中包含了所有不同级别的菜单选项内容实现,无法做到很好的解耦方式;

该模块通过动态绑定子菜单和链表的方式可以达到较好的解耦状态

2、显示效果

该框架只负责菜单选项控制操作,不负责在图像界面显示效果,需要在对应的回调函数中实现菜单显示效果。

设置对应的效果显示函数,即可为不同的菜单设置不同的菜单显示效果,比如图标式、列表式或右侧弹窗式等。

可以在不同显示平台体现,比如LCD、OLED或终端界面等。

3、可扩展

每级菜单选项都可以设置自定义数据,用来实现更多的菜单操作或者显示效果等。

不同级别的菜单可以设置自定义数据(比如菜单选项隐藏/图标数据等)。

4、可配置

配置选项描述
_COT_MENU_USE_MALLOC_定义则采用 malloc/free 的方式实现多级菜单, 否则通过数组的形式
_COT_MENU_USE_SHORTCUT_定义则启用快捷菜单选项进入功能
COT_MENU_MAX_DEPTH多级菜单深度
COT_MENU_MAX_NUM菜单支持的最大选项数目
COT_MENU_SUPPORT_LANGUAGE菜单支持的语种数目

5、功能多样化

多语种。

  • 支持菜单选项多语种切换,至少设置一种语言;

  • 多语种除了该方式,还可以使用多语种配置数据实现,比如键值对,键作为菜单选项字符串体现。

支持快速进入指定菜单界面。

  • 可以通过相对选项索引或者绝对选项索引路径实现。

可以实现有限界面内显示少量的菜单选项内容。

  • 有现成的函数可用,无需担心使用不同尺寸重新实现菜单选项部分可见。

四、使用说明

1、菜单初始化和使用

// 定义菜单信息,函数由主菜单模块定义并提供static cotMainMenuCfg_t sg_tMainMenu = {{"主菜单", "Main Menu"}, Hmi_EnterMainHmi, NULL, NULL, NULL};
int main(void){    cotMenu_Init(&sg_tMainMenu);
    while (1)    {        ...
        if (timeFlag)        {            timeFlag = 0;            cotMenu_Task(); // 周期调度        }    }}
 

2、主菜单定义和绑定

定义一个主菜单选项内容、主菜单显示效果函数和主菜单进入函数等。

// 扩展数据为图标文件名字cotMenuList_t sg_MainMenuTable[] = {    {{"音乐", "Music"},  Hmi_MusicEnter, Hmi_MusicExit, Hmi_MusicLoad, Hmi_MusicTask, "music"},    {{"视频", "Video"},  NULL, Hmi_VideoExit, Hmi_VideoLoad, Hmi_VideoTask, "video"},    {{"摄像机", "Camera"},  Hmi_CameraEnter, Hmi_CameraExit, Hmi_CameraLoad, Hmi_CameraTask, "camera"},    {{"设置", "Setting"}, Hmi_SetEnter, Hmi_SetExit, Hmi_SetLoad,   Hmi_SetTask, "setting"},};
/* 主菜单显示效果 */static void ShowMainMenu(cotMenuShow_t *ptShowInfo){    char *pszSelectDesc = ptShowInfo->pszItemsDesc[ptShowInfo->selectItem];    oledsize_t idx = (128 - 6 * strlen(pszSelectDesc)) / 2;
    cotOled_DrawGraphic(40, 0, (const char *)ptShowInfo->pItemsExData[ptShowInfo->selectItem], 1);
    cotOled_SetText(0, 50, "                ", 0, FONT_12X12);    cotOled_SetText(idx, 50, pszSelectDesc, 0, FONT_12X12);}
void Hmi_EnterMainHmi(void){    cotMenu_Bind(sg_MainMenuTable, COT_GET_MENU_NUM(sg_MainMenuTable), ShowMainMenu);}

3、子菜单定义和绑定

如果菜单选项有子菜单,则该菜单选项调用 cotMenu_Enter,进入回调函数不能为NULL,且该回调函数需调用 cotMenu_Bind进行绑定。

/* 设置的子菜单内容 */cotMenuList_t sg_SetMenuTable[] = {    {{"语言", "Language"},   NULL, NULL, NULL, OnLanguageFunction, NULL},    {{"蓝牙", "Bluetooth"},  NULL, NULL, NULL, OnBluetoothFunction, NULL},    {{"电池", "Battery"},    NULL, NULL, NULL, OnBatteryFunction, NULL},    {{"储存", "Store"},      NULL, NULL, NULL, OnStorageFunction, NULL},    {{"更多", "More"},       Hmi_MoreSetEnter, Hmi_MoreSetExit, Hmi_MoreSetLoad, Hmi_MoreSetTask, NULL},};
/* 设置菜单显示效果 */static void ShowSetMenu(cotMenuShow_t *ptShowInfo){    uint8_t showNum = 3;    menusize_t  tmpselect;
    cotMenu_LimitShowListNum(ptShowInfo, &showNum);
    printf("\e[0;30;46m ------------- %s ------------- \e[0m\n", ptShowInfo->pszDesc);
    for (int i = 0; i < showNum; i++)    {        tmpselect = i + ptShowInfo->showBaseItem;
        if (tmpselect == ptShowInfo->selectItem)        {            printf("\e[0;30;47m %d. %-16s\e[0m |\n", tmpselect + 1, ptShowInfo->pszItemsDesc[tmpselect]);        }        else        {            printf("\e[7;30;47m %d. %-16s\e[0m |\n", tmpselect + 1, ptShowInfo->pszItemsDesc[tmpselect]);        }    }}
void Hmi_SetEnter(void){    // 进入设置选项后绑定子菜单,同时为当前绑定的菜单设置显示效果函数    cotMenu_Bind(sg_SetMenuTable, COT_GET_MENU_NUM(sg_SetMenuTable), ShowSetMenu); }

4、菜单控制

通过调用相关函数实现菜单选项选择、进入、退出等。

​​​​​​​

// 需要先进入主菜单cotMenu_MainEnter();
// 选择上一个,支持循环选择(即第一个可跳转到最后一个)cotMenu_SelectPrevious(true);
// 选择下一个,不支持循环选择(即最后一个不可跳转到第一个)cotMenu_SelectNext(false);
// 进入,会执行菜单选项的 pfnEnterCallFun 回调函数cotMenu_Enter();
// 退出,会执行父菜单该选项的 pfnExitCallFun 回调函数,并在退出后父菜单选项列表复位从头选择cotMenu_Exit(true);

五、Demo显示效果

示例代码采用的平台是命令行输出输入显示效果:

demo中提供了如何实现图形菜单(主菜单有点粗糙)、普通列表菜单、右侧弹窗菜单(更多设置)等效果演示,菜单样式可自由扩展,足够自由;快捷菜单操作、中英文切换演示。(windows中编译需要将 demo.c转 GBK 编码,Linux 转 utf8 编码,不然可能出现汉字乱码的问题)。

图片

以下是通过单片机驱动 OLED 显示的菜单界面显示效果:

图片

六、下载链接

下载链接(点击阅读原文),或更新内容可看:

https://gitee.com/cot_package/cot_menu

这篇关于用C语言写一个耦合性低、完全可移植的轻量级菜单框架的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/647820

相关文章

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1

基于Flask框架添加多个AI模型的API并进行交互

《基于Flask框架添加多个AI模型的API并进行交互》:本文主要介绍如何基于Flask框架开发AI模型API管理系统,允许用户添加、删除不同AI模型的API密钥,感兴趣的可以了解下... 目录1. 概述2. 后端代码说明2.1 依赖库导入2.2 应用初始化2.3 API 存储字典2.4 路由函数2.5 应

Python GUI框架中的PyQt详解

《PythonGUI框架中的PyQt详解》PyQt是Python语言中最强大且广泛应用的GUI框架之一,基于Qt库的Python绑定实现,本文将深入解析PyQt的核心模块,并通过代码示例展示其应用场... 目录一、PyQt核心模块概览二、核心模块详解与示例1. QtCore - 核心基础模块2. QtWid

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

C语言实现两个变量值交换的三种方式

《C语言实现两个变量值交换的三种方式》两个变量值的交换是编程中最常见的问题之一,以下将介绍三种变量的交换方式,其中第一种方式是最常用也是最实用的,后两种方式一般只在特殊限制下使用,需要的朋友可以参考下... 目录1.使用临时变量(推荐)2.相加和相减的方式(值较大时可能丢失数据)3.按位异或运算1.使用临时

使用C语言实现交换整数的奇数位和偶数位

《使用C语言实现交换整数的奇数位和偶数位》在C语言中,要交换一个整数的二进制位中的奇数位和偶数位,重点需要理解位操作,当我们谈论二进制位的奇数位和偶数位时,我们是指从右到左数的位置,本文给大家介绍了使... 目录一、问题描述二、解决思路三、函数实现四、宏实现五、总结一、问题描述使用C语言代码实现:将一个整

最新Spring Security实战教程之Spring Security安全框架指南

《最新SpringSecurity实战教程之SpringSecurity安全框架指南》SpringSecurity是Spring生态系统中的核心组件,提供认证、授权和防护机制,以保护应用免受各种安... 目录前言什么是Spring Security?同类框架对比Spring Security典型应用场景传统

Linux find 命令完全指南及核心用法

《Linuxfind命令完全指南及核心用法》find是Linux系统最强大的文件搜索工具,支持嵌套遍历、条件筛选、执行动作,下面给大家介绍Linuxfind命令完全指南,感兴趣的朋友一起看看吧... 目录一、基础搜索模式1. 按文件名搜索(精确/模糊匹配)2. 排除指定目录/文件二、根据文件类型筛选三、时间

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st