为 M5Stack PaperS3 (4.7 英寸电子墨水屏) 设计的 MicroPython UI 框架,提供完整的应用生命周期管理、UI 组件库和墨水屏刷新优化。
main.py ← 入口:setup + asyncio event loop
core.py ← 基础设施:Node / Framework / ThemeManager / TaskPool / Colors / SD
components.py ← 控件库:Rect / Label / Button / ListView / BatteryIcon
- Node — 统一的 UI 基类。AppBase 和 UIElement 合二为一,所有界面元素都是 Node,可递归嵌套。
- 生命周期:
PAUSED → ACTIVE → DELETED(三态最简) - 脏标记渲染:
mark_dirty()→update()→draw() - 子树快速退出:
_any_dirty=False时跳过整棵树 - 触摸分发:倒序子节点命中测试,只命中重写了
on_touch的节点
- 生命周期:
- Framework — 应用管理器。管理 Node 页面集合、主循环驱动、主题切换。
add_page()/switch_to()/get_page()/back_to()handle_touch()— 将 M5.Touch 原始事件简化:0=按下 / 1=释放 / 2=长按set_theme()— 切换主题 → 递归config()+ 全局脏标记run(fps)— async 主循环:每帧调 M5.update → handle_touch → update,await asyncio.sleep_ms(0)让出时间片
- ThemeManager — 主题单例。管理 16 级灰阶配色与 HarmonyOS 字库。
- 内置
LIGHT_THEME/DARK_THEME load_font()/unload_font()— 字库加载/卸载text_extent(text)/font_height()— 文字测量
- 内置
- TaskPool — 定时任务调度池。组件把轮询函数丢进来,各走各的间隔,统一协程调度。
add(name, fn, interval_ms, source)/remove()/remove_all(source)/clear()pause(name)/resume(name)— 暂停/恢复单个任务get_result(name)/get_error(name)— 取上次执行结果- 独立
asyncio协程,每秒 tick,到时的任务同步执行
- Colors — 16 级灰度调色板(
BLACK/DARK/MEDIUM_GRAY/WHITE等) - SD — SD 卡管理器(单例,PaperS3 专用引脚:CS=47, SCK=39, MOSI=38, MISO=40, slot=3, 20MHz)
- Rect — 矩形填充/描边,颜色跟随主题或自定义
- Label — 文字标签,文本宽度自适应缓存,
set_text()触发局部重绘 - Button — 圆角矩形按钮,按压态外框高亮,支持
on_click/on_long_press回调 - ListView — 垂直列表,滚动条触摸翻页,内联批量绘制 Label(非 Label 项目 mark dirty 交给原生 draw)
- BatteryIcon — 电池图标 + 充电闪电,通过 TaskPool 两个独立任务轮询:
- 充电状态:每 1s 查一次
- 电量百分比:每 60s 查一次
CounterApp 演示核心功能:
| 功能 | 说明 |
|---|---|
| 加减计数 | _on_inc / _on_dec,Label 局部重绘 |
| 重置 | _on_reset |
| 深浅主题切换 | LIGHT_THEME ↔ DARK_THEME,全量重绘 |
| TaskPool 状态 | 查任务数 + 电池电量结果 |
| TaskPool debug | 打印各任务间隔、下次触发时间 |
| 退出应用 | Framework.exit() 清理退出 |
入口流程:
setup()
├─ M5.begin()
├─ SD().mount()
├─ ThemeManager.load_font()
├─ Framework() + add_page(CounterApp)
└─ fw.switch_to("Counter")
asyncio.run(_main())
├─ TaskPool.start() ← 启动调度协程
└─ await _fw.run() ← 主循环(一直跑直到 exit)
异常处理:KeyboardInterrupt 或任何 Exception 时 TaskPool.stop() + 打印错误 + M5.Display.endWrite() 确保显示不坏。
- 数据与绘制分离 —
config()做测量/缓存,draw()纯绘制,零测量零主题访问 - 增量重绘 — 脏标记驱动,不脏不画;
_any_dirty快速退出跳过整棵子树 - 极简 — 无 Canvas 抽象层、无事件总线、无动画系统。每个组件管好自己的状态,Framework 只管编排
- M5Stack PaperS3 (ESP32-S3 port, 8MB PSRAM / 16MB Flash)
- M5Stack uiflow2 固件(M5 库)
- SD 卡字库:
/sd/fonts/HarmonyOS_Sans_SC_Regular_28.vlw
- 将core和components丢到flash/libs里面,main丢到flash/apps里面,即可使用官方固件的applist启动main程序(开发阶段使用方便)
- 也可以将main安装官方的示例,设置为主要启动程序,这样开机就会启动main程序。
有一点小想法,有一点小梦想,有一点遗憾,总归是有了一些结果。 接下来会更新一个主页app,相当于手机的桌面,可以显示安装的应用。
Made with ❤️ for M5Stack PaperS3
MIT