HoloCubic 固件中的 LVGL Switch 开关控件实战解析:从 lv_ex_switch 示例到源码级原理 嵌入式物联网智能硬件嵌入式GUI硬件开发【免费下载链接】HoloCubic带网络功能的伪全息透明显示桌面站项目地址https://gitcode.com/gh_mirrors/ho/HoloCubic点击查看免费下载本文以 LVGL 官方示例 lv_ex_switch 文档 为核心骨架逐行拆解 Switch开关控件的创建、事件响应、动画开关与样式定制方法并结合 HoloCubic 固件中实际集成的 lv_switch 源码 与 lv_conf.h 配置讲透该控件在 240×240 伪全息屏幕上的落地方式。读完你将掌握 LVGL Switch 的完整 API、底层实现机制以及如何在 HoloCubic 这类嵌入式 GUI 工程中启用与运行它。一、示例文档与源码的对应关系在 HoloCubic 固件自带的 LVGL 源码树中Switch 控件示例由三部分构成位于同一目录下index.rstSphinx 格式的示例索引页标题为Simple Switch通过.. lv_example:: lv_ex_widgets/lv_ex_switch/lv_ex_switch_1指令将 C 语言示例嵌入文档并注明 MicroPython: No examples yet当时尚无官方 MicroPython 版本lv_ex_switch_1.cC 语言参考实现演示创建 事件回调 复制 动画开启四个关键操作lv_ex_switch_1.png示例在 320×240 屏幕上的渲染效果图即上方文章头图。示例入口函数lv_ex_switch_1()在 lv_ex_widgets.h 中声明并被 examples.txt 收录进自动化示例构建清单——这是 LVGL 官方示例体系的标准组织方式任何新增控件示例都会被自动纳入文档与构建流程。二、C 语言示例逐行拆解示例完整代码位于 lv_ex_switch_1.c核心逻辑如下#include ../../../lv_examples.h #include stdio.h #if LV_USE_SWITCH static void event_handler(lv_obj_t * obj, lv_event_t event) { if(event LV_EVENT_VALUE_CHANGED) { printf(State: %s\n, lv_switch_get_state(obj) ? On : Off); } } void lv_ex_switch_1(void) { /*Create a switch and apply the styles*/ lv_obj_t *sw1 lv_switch_create(lv_scr_act(), NULL); lv_obj_align(sw1, NULL, LV_ALIGN_CENTER, 0, -50); lv_obj_set_event_cb(sw1, event_handler); /*Copy the first switch and turn it ON*/ lv_obj_t *sw2 lv_switch_create(lv_scr_act(), sw1); lv_switch_on(sw2, LV_ANIM_ON); lv_obj_align(sw2, NULL, LV_ALIGN_CENTER, 0, 50); } #endif这段代码展示了 Switch 控件的四个典型用法创建开关lv_switch_create(lv_scr_act(), NULL)在当前活动屏幕lv_scr_act()上创建一个全新开关copy参数为NULL对齐定位lv_obj_align(sw1, NULL, LV_ALIGN_CENTER, 0, -50)将开关水平居中、垂直上移 50 像素绑定事件lv_obj_set_event_cb(sw1, event_handler)注册回调。当用户点击开关时控件内部会发送LV_EVENT_VALUE_CHANGED事件回调中通过lv_switch_get_state(obj)读取布尔状态并打印On/Off复制对象lv_switch_create(lv_scr_act(), sw1)以第一个开关为模板创建第二个开关会自动复制样式、事件回调与对象属性随后lv_switch_on(sw2, LV_ANIM_ON)以带动画方式将开关拨到开启位置lv_obj_align(sw2, NULL, LV_ALIGN_CENTER, 0, 50)将其放置在下方的对称位置。值得注意的是#if LV_USE_SWITCH条件编译只有当lv_conf.h中启用了LV_USE_SWITCH示例才会被编译这保证了控件未启用时不会引入无用的代码。三、MicroPython 版本样式定制增强尽管 index.rst 标注当时暂无 MicroPython 示例仓库中实际附带了一份更完整的 lv_ex_switch_1.py它对 C 版本做了样式层面的增强正好补充了 C 示例中注释apply the styles未展开的部分def event_handler(obj, event): if event lv.EVENT.VALUE_CHANGED: print(State: %s % (On if obj.get_state() else Off)) # Create styles for the switch bg_style lv.style_t() indic_style lv.style_t() knob_on_style lv.style_t() knob_off_style lv.style_t() lv.style_copy(bg_style, lv.style_pretty) bg_style.body.radius 800 bg_style.body.padding.top 6 bg_style.body.padding.bottom 6 lv.style_copy(indic_style, lv.style_pretty_color) indic_style.body.radius 800 indic_style.body.main_color lv.color_hex(0x9fc8ef) indic_style.body.grad_color lv.color_hex(0x9fc8ef) indic_style.body.padding.left 0 indic_style.body.padding.right 0 indic_style.body.padding.top 0 indic_style.body.padding.bottom 0 lv.style_copy(knob_off_style, lv.style_pretty) knob_off_style.body.radius 800 knob_off_style.body.shadow.width 4 knob_off_style.body.shadow.type lv.SHADOW.BOTTOM lv.style_copy(knob_on_style, lv.style_pretty_color) knob_on_style.body.radius 800 knob_on_style.body.shadow.width 4 knob_on_style.body.shadow.type lv.SHADOW.BOTTOM # Create a switch and apply the styles sw1 lv.sw(lv.scr_act()) sw1.set_style(lv.sw.STYLE.BG, bg_style) sw1.set_style(lv.sw.STYLE.INDIC, indic_style) sw1.set_style(lv.sw.STYLE.KNOB_ON, knob_on_style) sw1.set_style(lv.sw.STYLE.KNOB_OFF, knob_off_style) sw1.align(None, lv.ALIGN.CENTER, 0, -50) sw1.set_event_cb(event_handler) # Copy the first switch and turn it ON sw2 lv.sw(lv.scr_act(), sw1) sw2.on(lv.ANIM.ON) sw2.align(None, lv.ALIGN.CENTER, 0, 50) sw2.set_event_cb(lambda o,e: None)该版本完整揭示了 Switch 的三大部分样式模型与底层部件枚举一一对应样式角色代码标识对应部件作用背景槽lv.sw.STYLE.BGLV_SWITCH_PART_BG开关整体凹槽圆角radius 800上下内边距 6px填充指示lv.sw.STYLE.INDICLV_SWITCH_PART_INDIC开启时高亮区域此处用0x9fc8ef浅蓝填充滑块开lv.sw.STYLE.KNOB_ONLV_SWITCH_PART_KNOB开启态滑块带 4px 底部投影滑块关lv.sw.STYLE.KNOB_OFFLV_SWITCH_PART_KNOB关闭态滑块同样带投影其中radius 800是 LVGL 将圆角值设为最大的惯用写法值远大于控件尺寸即等价于全圆角滑块被绘制为圆形。事件逻辑与 C 版一致但第二个开关的事件回调被替换为lambda o,e: None空函数仅演示复制对象语义。四、底层实现原理基于 lv_bar 的派生控件Switch 不是从零实现的独立控件而是lv_bar进度条的派生类。这一点在 lv_switch.h 与 lv_switch.c 中体现得十分清晰typedef struct { lv_bar_ext_t bar; /*Ext. of ancestor*/ /*New data for this type */ lv_style_list_t style_knob; /*Style of the knob*/ } lv_switch_ext_t;扩展数据结构直接内嵌lv_bar_ext_t仅在祖先基础上新增一个滑块样式列表。创建流程lv_switch_create()内部先调用lv_bar_create(par, copy)随后设置默认参数lv_obj_set_click(sw, true); lv_obj_add_protect(sw, LV_PROTECT_PRESS_LOST); lv_obj_set_size(sw, LV_DPX(60), LV_DPX(35)); /* 默认 60×35 尺寸 */ lv_bar_set_range(sw, 0, 1); /* 取值范围固定为 0 和 1 */ lv_theme_apply(sw, LV_THEME_SWITCH);从源码可以推断 Switch 的状态模型取值范围被锁定为 0/1开关的 ON/OFF 实际就是 bar 值为 1/0。这一点在 getter 中得到了直接印证static inline bool lv_switch_get_state(const lv_obj_t * sw) { return lv_bar_get_value(sw) 1 ? true : false; }交互与信号处理lv_switch_signal()是控件的信号中枢处理三类关键交互点击释放LV_SIGNAL_RELEASED检测当前状态并取反——lv_switch_get_state(sw)为真则lv_switch_off(sw, LV_ANIM_ON)否则lv_switch_on(sw, LV_ANIM_ON)随后发送LV_EVENT_VALUE_CHANGED事件通知应用层回调键盘控制LV_SIGNAL_CONTROL在启用LV_USE_GROUP时方向键右键/上键lv_switch_on左键/下键lv_switch_off同样会触发LV_EVENT_VALUE_CHANGED可编辑性声明LV_SIGNAL_GET_EDITABLE显式返回false——虽然父类 slider 可编辑但 Switch 在组导航中不可用方向键连续调节。绘制机制lv_switch_design()完成自定义绘制先调用祖先bar的绘制函数画出背景槽与指示区再由指示区宽度与槽宽度的比例关系计算滑块的横向位置——滑块 x 坐标由ext-bar.indic_areabar 计算出的指示区动态推导因此开启/关闭动画的滑块移动本质上是bar 值在 0→1 之间动画过渡时指示区宽度变化的可视化结果同时该函数支持LV_BIDI_DIR_RTL从右向左的镜像布局。此外lv_switch_on/lv_switch_off/lv_switch_toggle三者内部都会先检查LV_USE_ANIMATION若编译期未启用动画anim参数会被强制降级为LV_ANIM_OFF直接跳变。五、完整 API 速查结合 lv_switch.hSwitch 对外暴露的接口全集如下API说明关键参数lv_switch_create(par, copy)创建开关copy非空时复制其属性与样式copy NULL表示全新创建lv_switch_on(sw, anim)打开开关anim取LV_ANIM_ON/LV_ANIM_OFFlv_switch_off(sw, anim)关闭开关同上lv_switch_toggle(sw, anim)翻转状态返回翻转后的结果bool同上lv_switch_set_anim_time(sw, anim_time)设置滑块动画时长ms实际委托给lv_bar_set_anim_timeuint16_tlv_switch_get_state(sw)读取状态false为 OFF、true为 ON无lv_switch_get_anim_time(sw)读取当前动画时长无部件枚举LV_SWITCH_PART_BG、LV_SWITCH_PART_INDIC复用自LV_BAR_PART_*LV_SWITCH_PART_KNOB为虚拟部件之后的新成员用于lv_obj_set_style系列样式接口与示例中的lv.sw.STYLE.KNOB_ON对应。六、在 HoloCubic 固件中启用与运行1. 配置开关lv_conf.hHoloCubic 固件使用的是 LVGL v7 分支配置集中在 lv_conf.h。与 Switch 直接相关的三个开关均已默认启用#define LV_USE_ANIMATION 1 // lv_conf.h:149开关动画的前置依赖 #define LV_USE_SLIDER 1 // lv_conf.h:712Switch 的编译期硬依赖 #define LV_USE_SWITCH 1 // lv_conf.h:718Switch 控件本体其中LV_USE_SLIDER是硬性依赖而非可选优化——lv_switch.c 开头有强制校验#if LV_USE_SLIDER 0 #error lv_sw: lv_slider is required. Enable it in lv_conf.h (LV_USE_SLIDER 1) #endif若关闭LV_USE_SLIDER而保留LV_USE_SWITCH编译会直接报错默认配置通过 lv_conf_internal.h 将三者都置 1。2. 在 240×240 屏幕上创建开关HoloCubic 的显示环境由 display.cpp 初始化LVGL 显示驱动注册为 240×240disp_drv.hor_res 240; disp_drv.ver_res 240;刷新回调my_disp_flush通过 TFT_eSPI 的pushColors将帧缓冲写入 ST7789 屏。在该工程中运行示例时只需在lv_holo_cubic_gui()初始化完成之后调用lv_ex_switch_1()即可在屏幕中央看到上下两个开关点击上方开关时串口115200 波特率会打印State: On/State: Off。需注意两点差异一是官方示例效果图320×240宽度大于 HoloCubic 屏幕控件相对居中位置会略有偏移可改用LV_ALIGN_IN_TOP_MID/LV_ALIGN_IN_BOTTOM_MID适配二是 HoloCubic 主循环 main.cpp 中由screen.routine()周期性调用lv_task_handler()驱动 LVGL 任务调度示例中的事件响应依赖此循环持续运行。3. 与现有 GUI 体系的衔接HoloCubic 固件目前采用 GUI Guider 生成的lv_ui结构管理界面见 gui_guider.c 与 setup_scr_home.c事件绑定集中在 events_init.c 的events_init(lv_ui *ui)中当前为空实现。若要将 Switch 融入现有界面建议在lv_holo_cubic_gui()或setup_scr_*中创建开关对象并在events_init()中注册LV_EVENT_VALUE_CHANGED回调与 LVGL 官方示例保持一致的创建—绑定—派发模式。七、小结LVGL Switch 示例lv_ex_switch_1虽然体量极小却是理解 LVGL 派生控件设计范式的绝佳入口它以 0/1 取值的 bar 为基础通过信号函数接管点击翻转与键盘控制通过设计函数绘制滑块位置最终以LV_EVENT_VALUE_CHANGED事件向应用层传递状态变化。在 HoloCubic 固件中启用LV_USE_SWITCH后即可在 240×240 屏幕上直接复用该示例并可按 MicroPython 版本提供的三部分样式模型自由定制外观为后续在桌面站界面中实现功能开关类交互如 WiFi、背光、灯效的启停控制提供现成的控件基础。赞分享嵌入式物联网智能硬件嵌入式GUI硬件开发【免费下载链接】HoloCubic带网络功能的伪全息透明显示桌面站项目地址https://gitcode.com/gh_mirrors/ho/HoloCubic点击查看免费下载相关推荐HoloCubic 固件中的 LVGL List 列表控件实战从 lv_ex_list_1 示例到源码级解析HoloCubic 固件中的 LVGL List 列表控件实战从 lv_ex_list_1 示例到源码级解析 导读 本文以 HoloCubic 固件内置的 L嵌入式物联网智能硬件嵌入式GUI硬件开发HoloCubic 固件中 LVGL lv_table 表格组件实战从 Simple Table 示例到源码级剖析HoloCubic 固件中 LVGL lv_table 表格组件实战从 Simple Table 示例到源码级剖析 HoloCubic 是一款带网络功能的伪全嵌入式物联网智能硬件嵌入式GUI硬件开发HoloCubic 固件中的 LVGL 折线控件lv_ex_line 示例与 lv_line 源码深度解析HoloCubic 固件中的 LVGL 折线控件lv_ex_line 示例与 lv_line 源码深度解析 导读 本文以 HoloCubic 伪全息桌面站固件嵌入式物联网智能硬件嵌入式GUI硬件开发上一篇tinygrad MNIST 入门教程从模型定义、训练到 TinyJit 提速的完整实战下一篇Django Silk 查询分析功能EXPLAIN ANALYZE 实战应用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考