热重载
hot_reload 的启用方式、状态保留和调试边界。
appui.run(body_func, state=None, hot_reload=False, presentation='sheet') 在 hot_reload=True 时监视调用脚本文件,保存后自动重新执行脚本并刷新界面,便于迭代 UI。body_func 可以写成 body(),也可以写成 body(state)。
#预期效果
示例会展示热重载保留 State、刷新页面结构,并在错误时保持预览可恢复。
#启用方式
已复制
import appui
state = appui.State(count=0)
def increment():
state.count += 1
def body():
return appui.VStack([
appui.Text(f"count = {state.count}").font("title2"),
appui.Button("+1", action=increment)
.button_style("bordered_prominent"),
], spacing=16).padding()
appui.run(body, state=state, hot_reload=True, presentation="sheet")
要点:
- 监视的是
run()调用栈所在文件(inspect.stack),因此热重载入口应放在真实脚本中,而不是交互式stdin。 - 保存文件后,运行环境会尽快重新加载脚本并刷新界面;触发速度取决于当前设备和文件系统。
#状态保留:仅 State 自动合并
热重载重新执行脚本后,若旧页面和新页面都使用 State,会按字段名保留仍然存在的状态值,从而让计数器、开关等继续保持当前值。
ReactiveState 不会自动合并到新实例;热重载后若脚本重新构造 ReactiveState(),字段会回到脚本初值,除非你在脚本里自行从文件恢复。
已复制
"""说明:热重载后 State 合并逻辑只针对 appui.State。"""
import appui
s = appui.State(a=1)
snapshot = s.to_dict()
assert snapshot["a"] == 1
(在应用内以 hot_reload=True 运行时可观察计数在保存后仍连续。)
#开发流程建议
- 将
body与小组件函数放在同一.py文件,run(..., hot_reload=True)置于if __name__ == "__main__":中(若在应用内直接调用亦可)。 - 每次保存后,运行环境会重新读取当前文件并绑定新的
body。 - 首帧会完整刷新界面树,避免旧界面结构影响新代码。
#出错时会发生什么
若新代码 语法错误 或 导入失败,异常会显示在预览错误面板;进程不退出,修正后再保存即可。
已复制
import appui
state = appui.State(ok=True)
def body():
return appui.VStack([
appui.Text("保存本文件即可触发热重载").font("footnote"),
appui.Button("关闭", action=appui.dismiss).button_style("bordered"),
], spacing=12).padding()
appui.run(body, state=state, hot_reload=True, presentation="sheet")
#调试技巧
- 打印:保存后可在控制台观察重载成功或错误信息。
- 按钮短暂不可点:保存瞬间正在替换回调,等界面刷新完成后再点一次。
- 不要用
<stdin>作为脚本路径:无法可靠监视;请使用磁盘上的.py文件运行。 - 与
presentation组合:fullscreen/fullscreen_with_close同样支持热重载;关闭仍用appui.dismiss()。
已复制
import appui, inspect
def f():
return appui.Text("x")
# 本段仅验证 run 的签名包含 hot_reload(不实际连接桥)
sig = inspect.signature(appui.run)
assert "hot_reload" in sig.parameters
#与 auto_refresh 的区别
| 特性 | hot_reload=True | auto_refresh(interval) |
|---|---|---|
| 触发条件 | 源文件变更 | 定时器周期 |
| 目的 | 开发时替换代码 | 时钟、轮询仪表盘 |
| 状态 | 合并 State | 不重新加载模块 |
已复制
import appui
import time
state = appui.State()
def body():
return appui.Text(time.strftime("%H:%M:%S")).font("title").padding()
appui.auto_refresh(interval=1.0)
appui.run(body, state=state, hot_reload=False, presentation="sheet")
#小结
hot_reload=True:监视文件 → exec → 保留State→ 强制全量刷新。- 适合 UI 布局与回调逻辑的快速迭代。
- 对
ReactiveState/ 文件外缓存 需自行设计持久化策略。
更多运行器参数见 appui-ref-functions.md。