☰
HoloCubic 开发笔记:LVGL 按钮矩阵(lv_btnmatrix)控件实战与源码解析——从 Simple Button Matrix 示例入手
2026/10/15 2:34:44 网站建设 项目流程
  • 嵌入式
  • 物联网
  • 智能硬件
  • 嵌入式GUI
  • 硬件开发

【免费下载链接】HoloCubic

带网络功能的伪全息透明显示桌面站

项目地址:https://gitcode.com/gh_mirrors/ho/HoloCubic
点击查看免费下载

导读

本篇技术指南以 HoloCubic 仓库 LvglSimulator 工程中的lv_ex_btnmatrix示例文档为骨架,围绕 LVGL 的按钮矩阵(Button Matrix,lv_btnmatrix)控件,完整讲解其 map 字符串数组的编排规则、按钮宽度控制、可勾选(Checkable)状态位以及LV_EVENT_VALUE_CHANGED事件回调,并结合 LVGL 源码剖析控制位(ctrl bits)的存储与事件发送机制。读完本文,你将能在自己的 LVGL 界面(包括 HoloCubic 的伪全息桌面站 UI)中快速搭建数字键盘、菜单工具栏等密集型按钮布局,并理解其底层实现原理。

一、示例文档骨架:Simple Button matrix

该示例位于3.Software/LvglSimulator/vs2019_proj/lv_examples/src/lv_ex_widgets/lv_ex_btnmatrix/index.rst,主题为Simple Button matrix,通过.. lv_example::指令引用lv_ex_widgets/lv_ex_btnmatrix/lv_ex_btnmatrix_1(C 语言示例);文档同时保留了 MicroPython 章节,标注 "No examples yet."。仓库中实际同时提供 C 与 MicroPython 两个实现文件:

  • C 语言实现
  • MicroPython 实现
  • 运行效果图(320x240,模拟器渲染结果)

说明:index.rst中 MicroPython 章节标注 "No examples yet.",但仓库lv_ex_btnmatrix目录下实际已包含lv_ex_btnmatrix_1.py,两者可对照阅读。函数原型统一声明于 lv_ex_widgets.h(void lv_ex_btnmatrix_1(void);),由模拟器工程统一调用。

二、示例完整代码走读

C 版本完整代码如下(lv_ex_btnmatrix_1.c):

#include "../../../lv_examples.h" #include <stdio.h> #if LV_USE_BTNMATRIX static void event_handler(lv_obj_t * obj, lv_event_t event) { if(event == LV_EVENT_VALUE_CHANGED) { const char * txt = lv_btnmatrix_get_active_btn_text(obj); printf("%s was pressed\n", txt); } } static const char * btnm_map[] = {"1", "2", "3", "4", "5", "\n", "6", "7", "8", "9", "0", "\n", "Action1", "Action2", ""}; void lv_ex_btnmatrix_1(void) { lv_obj_t * btnm1 = lv_btnmatrix_create(lv_scr_act(), NULL); lv_btnmatrix_set_map(btnm1, btnm_map); lv_btnmatrix_set_btn_width(btnm1, 10, 2); /*Make "Action1" twice as wide as "Action2"*/ lv_btnmatrix_set_btn_ctrl(btnm1, 10, LV_BTNMATRIX_CTRL_CHECKABLE); lv_btnmatrix_set_btn_ctrl(btnm1, 11, LV_BTNMATRIX_CTRL_CHECK_STATE); lv_obj_align(btnm1, NULL, LV_ALIGN_CENTER, 0, 0); lv_obj_set_event_cb(btnm1, event_handler); } #endif

MicroPython 版本(lv_ex_btnmatrix_1.py)与 C 版本一一对应:

def event_handler(obj, event): if event == lv.EVENT.VALUE_CHANGED: txt = obj.get_active_btn_text() print("%s was pressed" % txt) btnm_map = ["1", "2", "3", "4", "5", "\n", "6", "7", "8", "9", "0", "\n", "Action1", "Action2", ""] btnm1 = lv.btnm(lv.scr_act()) btnm1.set_map(btnm_map) btnm1.set_btn_width(10, 2) # Make "Action1" twice as wide as "Action2" btnm1.align(None, lv.ALIGN.CENTER, 0, 0) btnm1.set_event_cb(event_handler)

示例演示了按钮矩阵的核心用法:创建对象、设置按钮 map、调整按钮相对宽度、为按钮设置可勾选状态,并监听数值变化事件。

三、map 字符串数组:按钮布局的“图纸”

按钮矩阵的核心是lv_btnmatrix_set_map()(C)或set_map()(Python),其入参是一个以""(空字符串)结尾的const char *数组。数组语义规则在 lv_btnmatrix.h 中有明确定义:

  • 普通字符串:代表一个按钮,按钮文本即该字符串;
  • "\n":强制换行,其后的字符串进入下一行;
  • "":数组结束标志,表示 map 终止(注意不是按钮)。

示例中btnm_map由 14 个元素组成,布局为两行数字键盘(每行 5 个键)+ 一行两个操作按钮:

1 2 3 4 5 6 7 8 9 0 Action1 Action2

从源码结构看,lv_btnmatrix_create()创建对象时默认使用内置 maplv_btnmatrix_def_map[] = {"Btn1", "Btn2", "Btn3", "\n", "Btn4", "Btn5", ""}(见 lv_btnmatrix.c),即默认是 2 行 3 + 2 的按钮布局。一旦调用lv_btnmatrix_set_map(),按钮会按新 map 重新创建。

3.1 按钮索引的计数方式

需要特别留意:按钮索引从 0 开始,且不把"\n"计入索引。例如示例中 "Action1" 的索引是 10,"Action2" 的索引是 11。这一点在set_btn_width(10, 2)、set_btn_ctrl(btnm1, 10, ...)等调用中直接体现,也与 lv_btnmatrix.h 中 "0 based index of the button to modify. (Not counting new lines)" 的注释一致。

四、按钮宽度:相对宽度机制

lv_btnmatrix_set_btn_width(btnm1, 10, 2); /* Action1 宽度是 Action2 的 2 倍 */

宽度值width的取值范围是[1..7],表示该按钮相对同一行内其他按钮的宽度倍数。示例将索引 10 的 "Action1" 设为 2,而 "Action2"(索引 11)保持默认宽度 1,因此视觉上 "Action1" 占满 2/3 行宽、"Action2" 占 1/3 行宽。

宽度值实际存放在每个按钮的 ctrl 位低 3 位(LV_BTNMATRIX_WIDTH_MASK 0x0007,见 lv_btnmatrix.h)。实现上lv_btnmatrix_set_btn_width()会先清除低 3 位再写入新宽度(lv_btnmatrix.c):

ext->ctrl_bits[btn_id] &= (~LV_BTNMATRIX_WIDTH_MASK); ext->ctrl_bits[btn_id] |= (LV_BTNMATRIX_WIDTH_MASK & width);

布局计算时通过get_button_width()(lv_btnmatrix.c)读取该低 3 位,把每行所有按钮的宽度单位累加后按比例分配像素宽度。

注意:头文件注释提示,set_btn_width属于相对耗时的“动态修改”接口,会触发矩阵重新布局;如需在创建时就指定多个按钮宽度,推荐使用lv_btnmatrix_set_ctrl_map()一次性传入 ctrl 数组(宽度位与控制位可通过按位或组合,例如width | LV_BTNMATRIX_CTRL_CHECKABLE,见 lv_btnmatrix.h)。

五、按钮控制位(ctrl bits):隐藏、禁用与勾选

控制位定义于 lv_btnmatrix.h,类型为lv_btnmatrix_ctrl_t(uint16_t)。常用标志如下:

控制位值含义
LV_BTNMATRIX_CTRL_HIDDEN0x0008隐藏该按钮(不显示、不参与布局)
LV_BTNMATRIX_CTRL_NO_REPEAT0x0010长按时不重复触发
LV_BTNMATRIX_CTRL_DISABLED0x0020禁用按钮(灰显且不可点击)
LV_BTNMATRIX_CTRL_CHECKABLE0x0040按钮可以被勾选(按下后保持选中状态)
LV_BTNMATRIX_CTRL_CHECK_STATE0x0080按钮当前处于勾选(按下)状态
LV_BTNMATRIX_CTRL_CLICK_TRIG0x01001:在 CLICK 时发送选中事件;0:在 PRESS 时发送

示例中用到了后两个:

lv_btnmatrix_set_btn_ctrl(btnm1, 10, LV_BTNMATRIX_CTRL_CHECKABLE); /* "Action1" 可勾选 */ lv_btnmatrix_set_btn_ctrl(btnm1, 11, LV_BTNMATRIX_CTRL_CHECK_STATE); /* "Action2" 初始即被勾选 */

配合lv_btnmatrix_set_one_check()可将按钮矩阵变成“单选器”(同一时间仅一个按钮保持勾选),这与lv_tabview、lv_msgbox的底层按钮布局共享同一套机制(在 lvgl/src/lv_widgets/lv_tabview.h、lv_msgbox.h 中均有 “lv_btnmis required” 的编译期依赖检查)。

六、事件处理:捕捉按钮点击

if(event == LV_EVENT_VALUE_CHANGED) { const char * txt = lv_btnmatrix_get_active_btn_text(obj); printf("%s was pressed\n", txt); }

按钮矩阵在被点击时向事件回调发送LV_EVENT_VALUE_CHANGED。lv_btnmatrix_get_active_btn_text()(声明于 lv_btnmatrix.h)返回最近一次被激活(按下/释放)按钮的文本,配合lv_btnmatrix_get_active_btn()可拿到按钮索引,进而读取 ctrl 状态、动态修改宽度或文本。

从源码看,LV_EVENT_VALUE_CHANGED在信号处理函数lv_btnmatrix_signal()中多次由lv_event_send(btnm, LV_EVENT_VALUE_CHANGED, &b)触发(见 lv_btnmatrix.c),覆盖按下、释放、重复按压等不同点击路径,因此回调内无需区分具体来源,统一读取get_active_btn_text()即可。

七、如何在 HoloCubic 工程中启用并运行

按钮矩阵示例通过#if LV_USE_BTNMATRIX编译开关包裹(见 lv_ex_btnmatrix_1.c)。在 LvglSimulator 工程的 lv_conf.h 中LV_USE_BTNMATRIX已默认开启:

/*Button matrix (dependencies: -)*/ #define LV_USE_BTNMATRIX 1

运行方式:

  1. 在 VS2019 中打开 lvgl_similator.sln,编译并运行 LvglSimulator(基于 SDL2 + LVGL,工程配置见 lvgl_similator.vcxproj);
  2. 在模拟器代码中调用lv_ex_btnmatrix_1()(原型位于 lv_ex_widgets.h),即可看到上述两行数字键盘 + 两个操作按钮的界面,点击按钮时终端输出XXX was pressed。

在 HoloCubic 固件(2.Firmware/HoloCubic-fw)中,GUI 由 LVGL 驱动(见src/lv_cubic_gui.c与src/setup_scr_home.c等),按钮矩阵非常适合用来实现设置菜单、数字输入面板等需要大量规则按钮的场景——每个按钮只占用一个字符串数组元素,内存占用远低于逐个创建独立按钮对象。启用方式与模拟器一致,在固件侧 lv_conf.h 中开启LV_USE_BTNMATRIX即可。

八、常见误区与小结

  • map 数组必须保持存活:头文件明确提示按钮矩阵只是“持有”map 指针,字符串数组在矩阵生命周期内不能被释放或改写(见 lv_btnmatrix.h),因此示例把btnm_map声明为静态数组;
  • 索引不含"\n":给按钮设置宽度/控制位/文本时,索引从 0 计数且跳过换行符,写错索引会作用到错误按钮;
  • 宽度上限 7:lv_btnmatrix_set_btn_width()的宽度参数范围为[1..7](低 3 位掩码0x0007限制);
  • 勾选三要素:要让按钮具备“按下保持选中”行为,至少需要LV_BTNMATRIX_CTRL_CHECKABLE;读取当前勾选状态用LV_BTNMATRIX_CTRL_CHECK_STATE,实现单选互斥则调用lv_btnmatrix_set_one_check()。

综上,lv_ex_btnmatrix示例虽短,却完整覆盖了按钮矩阵控件的 map 编排、相对宽度、控制位与事件回调四大核心知识点;结合 lv_btnmatrix.h 与 lv_btnmatrix.c 源码,即可在 HoloCubic 的 LVGL 界面中高效复用这套规则化的按钮布局能力。

  • 嵌入式
  • 物联网
  • 智能硬件
  • 嵌入式GUI
  • 硬件开发

【免费下载链接】HoloCubic

带网络功能的伪全息透明显示桌面站

项目地址:https://gitcode.com/gh_mirrors/ho/HoloCubic
点击查看免费下载

相关推荐

上一篇:Ultimaker Cura - 开源的3D打印切片软件
下一篇:FreeJoy 游戏设备控制器安装和配置指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询