从零开始玩转LVGL界面设计:手把手教你用可视化工具高效开发嵌入式UI
你有没有遇到过这样的场景?
想给STM32或ESP32加个漂亮的屏幕,结果一上来就要写一堆lv_obj_create()、lv_label_set_text()……改个按钮位置得重新编译下载,反复试错效率极低。更别提字体加载、样式统一、多屏切换这些细节了——还没做出界面,人已经累趴。
如果你正被这些问题困扰,那今天这篇文章就是为你准备的。
我们不讲复杂的API,也不堆砌术语,而是带你用“拖拽”的方式搞定嵌入式GUI开发。主角就是目前LVGL生态中最实用的可视化利器——SquareLine Studio(即文中所说的lvgl界面编辑器)。它能让你像做PPT一样设计UI,一键生成可运行的C代码,直接烧录到板子上就能看到效果。
更重要的是,哪怕你是第一次听说LVGL,也能跟着本文一步步走通全流程。准备好迎接“原来GUI开发可以这么简单”的震撼了吗?我们这就开始。
为什么你需要一个界面编辑器?
在深入工具之前,先回答一个问题:为什么非要用图形化工具?我自己写不行吗?
当然行。但代价是什么?
- 每次调整布局都要改坐标、重新编译、下载验证;
- 多人协作时风格难以统一,A做的按钮和B做的看起来完全不是一套;
- 修改需求来了,“把所有字体换成思源黑体”,你得翻遍每个
.c文件手动替换; - 调试靠猜:“为啥这个标签没显示?”——原来是忘了
lv_scr_load()……
这些问题的本质是:我们本该专注“设计”,却被迫花大量时间处理“实现”细节。
而 SquareLine Studio 的出现,正是为了解决这一痛点。它不是替代LVGL,而是作为它的“外挂大脑”,帮你自动完成那些重复、易错、枯燥的代码编写工作。
你可以把它理解为:
Figma + VS Code + 编译器 的嵌入式定制合体版
接下来我们就来看看,它是怎么做到的。
认识你的新搭档:SquareLine Studio 到底是什么?
简单说,SquareLine Studio 是一款专为 LVGL 打造的可视化UI设计工具,支持 LVGL v8 及以上版本,基于 Electron 开发,因此能在 Windows、macOS 和 Linux 上无缝运行。
它长什么样?打开后你会看到熟悉的“画布+组件库”界面:
- 左边是控件面板(按钮、滑块、图表、列表等);
- 中间是可视化的画布,支持缩放、对齐辅助线;
- 右侧是属性检查器,可以精细调节颜色、字体、事件回调;
- 底部还有屏幕管理器,轻松实现多页面跳转逻辑。
最爽的是:点一下“预览”按钮,就能在电脑上实时看到界面动效,连触摸手势都能模拟!不用再一遍遍烧录调试。
而且它生成的代码不是“黑盒”,而是结构清晰、注释完整的 C 文件,比如ui.c和ui.h,你可以随时打开阅读、修改,甚至和其他模块对接业务逻辑。
一句话总结:
👉 它让不懂LVGL API的人也能做出专业级界面;
👉 它让熟悉LVGL的老手彻底告别重复劳动。
安装与配置:5分钟搭建开发环境
第一步:下载并安装 SquareLine Studio
前往官网 https://squareline.io/ 下载对应系统的安装包:
- Windows 用户下载
.exe - macOS 用户选择
.dmg - Linux 用户有
.AppImage或.deb
安装过程非常标准,一路“下一步”即可。启动后首次会提示登录(可跳过),然后就能创建新项目。
⚠️ 注意:确保你的系统已安装 Node.js 和 Python(部分功能依赖),推荐使用较新的 LTS 版本(如 Node 18+, Python 3.9+)
第二步:创建第一个项目
点击 “New Project” → 输入项目名 → 选择目标 LVGL 版本(建议选最新稳定版 v8.x)
接着设置屏幕参数:
- 分辨率(如 320x240)
- 色深(通常 RGB565 即 16-bit)
- 默认字体(可以选择内置的montserrat-16等)
完成后你会看到一个空白画布,名字叫Screen1,这就是你的主界面舞台。
动手实战:拖两个控件,看看能发生什么
让我们来做一个极简 demo:添加一个标题标签 + 一个按钮。
1. 添加 Label 标签
- 从左侧控件栏找到
Label,拖到画布中央; - 在右侧属性面板中找到 “Text” 项,改成
"欢迎使用LVGL"; - 调整字体大小:展开
Style → Text → Font,选择更大的字体(如montserrat-24); - 改个颜色:在
Text Color里设成白色#FFFFFF。
2. 添加 Button 按钮
- 再拖一个
Button到 label 下方; - 修改文本:双击按钮或在属性里改
Label Text为"开始"; - 设置位置:X=110, Y=180,宽100高40;
- 绑定事件:在
Events栏点击+,选择CLICKED,函数名填btn_start_event_handler。
做完之后,点击顶部的“Preview”按钮。
Boom!一个带按钮和标题的小界面就在你眼前跑起来了!还能用鼠标点击按钮触发事件预览!
这时候你可能会问:“这只是一个模拟器吧?真能用在单片机上吗?”
答案是:完全可以。而且只差最后一步——导出代码。
自动生成C代码:这才是真正的魔法时刻
回到主界面,点击菜单栏的Export → Export to Code。
弹出窗口让你选择导出格式:
- 可以单独导出ui.c/ui.h
- 也可以导出完整的 ESP-IDF 或 STM32CubeIDE 工程模板
我们选最通用的 “C Code Only”,然后保存到本地文件夹。
进去一看,目录结构如下:
/exported_ui/ ├── ui.h ├── ui.c └── generated/ ├── font_montserrat_24.c └── ...打开ui.c,你会发现刚才做的所有操作都被翻译成了标准 LVGL API 调用:
void ui_init(lv_disp_t * display) { ui_screen_1 = lv_obj_create(NULL); lv_obj_set_style_bg_color(ui_screen_1, lv_color_black(), 0); ui_label_title = lv_label_create(ui_screen_1); lv_label_set_text(ui_label_title, "欢迎使用LVGL"); lv_obj_set_pos(ui_label_title, 80, 50); lv_obj_set_style_text_font(ui_label_title, &lv_font_montserrat_24, 0); ui_btn_start = lv_button_create(ui_screen_1); lv_obj_set_pos(ui_btn_start, 110, 180); lv_obj_set_size(ui_btn_start, 100, 40); lv_obj_add_event_cb(ui_btn_start, btn_start_event_handler, LV_EVENT_CLICKED, NULL); }是不是和你自己写的代码风格几乎一样?唯一的区别是:你花了半小时写的代码,它几秒钟就生成了。
而且变量命名规范(ui_label_title,ui_btn_start)、结构清晰、层级分明,后期维护起来毫无压力。
如何集成到真实项目中?
现在我们有了ui.c和ui.h,怎么让它在你的开发板上跑起来?
前提条件
你的嵌入式工程必须已经成功移植了 LVGL(包括显示驱动、输入设备、定时器等HAL层)。如果你还没搞定这部分,建议先参考官方示例(如 ESP32 的 lv_port_lcd_template)。
集成步骤
- 将
ui.c和ui.h复制到项目源码目录; - 在主程序包含头文件:
c #include "ui.h" - 在初始化完 LVGL 后调用 UI 初始化函数:
c lv_init(); hal_init(); // 自定义硬件抽象层初始化 ui_init(NULL); // 传NULL表示使用默认显示器 - 实现你在编辑器里声明的事件回调函数:
c void btn_start_event_handler(lv_event_t * e) { printf("按钮被点击了!\n"); // 这里添加你的业务逻辑 }
编译、烧录、上电……恭喜你,屏幕上出现了刚刚设计的界面!
提升效率的秘密武器:Python脚本自动化资源处理
虽然 SquareLine Studio 很强大,但有些任务它不会自动帮你做,比如:
- 把
.ttf字体转成 LVGL 可用的数组 - 把PNG图片压缩成适合嵌入式的格式
- 批量生成不同字号的字体文件
这时候就可以借助Python 脚本来提升效率。
示例:自动批量生成字体文件
LVGL 使用lv_font_conv工具将 TrueType 字体转换为C数组。我们可以写个脚本来自动化这个过程。
import subprocess import os def build_font(font_name, size, charset="ascii"): output = f"{font_name}_{size}.h" cmd = [ "lv_font_conv", "--font", f"{font_name}.ttf", "--size", str(size), "--bpp", "4", "--range", charset, "-o", output, "--no-compress" ] try: subprocess.run(cmd, check=True) print(f"✅ 成功生成: {output}") except subprocess.CalledProcessError: print(f"❌ 生成失败,请确认 {font_name}.ttf 是否存在") # 批量生成多个尺寸 for s in [12, 16, 20, 24]: build_font("roboto", s, "basic")把这个脚本保存为gen_fonts.py,只要当前目录下有roboto.ttf文件,运行一下就会自动生成四种尺寸的字体头文件。
然后在ui.h中引用:
#include "roboto_24.h" LV_FONT_DECLARE(font_roboto_24);再在 SquareLine Studio 中设置控件使用该字体,完美闭环。
实战经验分享:老司机才知道的6个坑点与秘籍
用了半年 SquareLine Studio,踩过不少坑,也总结了一些最佳实践,分享给你:
🔧 秘籍1:命名一定要有意义
编辑器默认可能生成label_1,button_2这种名字,千万别留着!改成ui_label_temp,ui_btn_power_on,后期查问题省一半力气。
🧱 秘籍2:合理拆分屏幕,别全塞在一个界面上
每个逻辑页面独立建一个 Screen 对象。比如主菜单、设置页、数据图表页分开管理,方便导航和内存释放。
💡 秘籍3:利用「自定义变量区」保护你的代码
SquareLine Studio 会在/* AutoGen */注释之间重写代码。如果你想在生成文件里加自己的全局变量或函数,一定要写在注释块之外,否则下次导出就被清空了!
📦 秘籍4:资源路径要相对化
如果用了图片或动画资源,导出时注意路径是否正确。建议把资源放在项目根目录下的assets/文件夹,并在脚本中统一处理路径映射。
🚫 秘籍5:发布前关闭调试输出
在项目设置里关闭 “Enable Debug Logs”,避免敏感信息泄露,也能减小固件体积。
🔐 秘籍6:启用反混淆机制(高级)
通过插件或手动修改生成代码,打乱对象创建顺序或隐藏关键控件名,防止别人轻易逆向你的UI结构。
它适合哪些项目?我该怎么判断要不要用?
别误会,不是所有项目都必须用界面编辑器。以下是几个典型适用场景:
| 场景 | 是否推荐使用 |
|---|---|
| 快速原型验证、Demo展示 | ✅ 强烈推荐,一天做出可用界面 |
| 产品级HMI开发(家电、工控屏) | ✅ 团队协作必备,风格统一 |
| 极致资源受限设备(<64KB RAM) | ⚠️ 谨慎使用,需手动优化生成代码 |
| 纯程序员个人项目,追求完全掌控 | ❌ 可不用,直接写代码更快 |
| 需要动态生成大量控件的场景 | ⚠️ 混合使用,静态部分用编辑器,动态部分手写 |
总的来说:
只要你需要频繁修改UI、团队合作、或者希望快速交付成果,那就值得投入时间学习 SquareLine Studio。
写在最后:工具背后的思想转变
掌握一个工具很容易,但真正改变你开发方式的,是它背后的思维方式。
过去我们习惯“代码驱动开发”:先想好怎么写,再一步步实现。
而现在,我们进入了“设计驱动开发”的时代:先想清楚用户体验,再让工具帮我们落地。
SquareLine Studio 不只是个拖拽工具,它是嵌入式开发走向现代化、工程化的重要标志。就像当年 Keil 被 VS Code + PlatformIO 取代一样,未来的嵌入式UI开发,一定是可视化 + 自动化 + 协作化的组合拳。
所以,不妨从今天开始,试着放下键盘,拿起鼠标,去“画”出你的下一个作品。
也许你会发现,原来做界面,也可以是一件快乐的事。
如果你在使用过程中遇到具体问题,比如“导出后中文乱码怎么办?”、“如何加载PNG图片?”、“怎样实现页面跳转?”,欢迎留言交流,我们可以一起探讨解决方案。