widget 排错
基线排查、预览≠桌面、裁切/点击/动画、w.validate 诊断。
预览正常但桌面异常、点击无反应、文字被裁切、动画不动——按现象 → 原因 → 修复逐项排查。先跑最小基线,再二分加回功能。
边界:本篇是故障手册,不教 API 写法。入门见 widget 概览;发布流程见 从脚本到桌面。
#失败路径
按下方「五步排查流程」与「现象对照表」处理空白、裁切、点击无效与桌面缓存问题。
#本篇目标
| 项 | 说明 |
|---|---|
| 第一步 | 用最小健康基线确认环境与 w.render() |
| 定位法 | 现象对照表 → 专题文档 → w.validate() 诊断 |
| 高发问题 | 空白、裁切、参数缺失、点击无效、预览≠桌面、缓存 |
| 工具 | Studio 构建日志、w.validate(family=)、逐 family 预览 |
| 原则 | 先减功能再定位,不要同时在脚本里改五处 |
#最小健康基线
全系列唯一可运行基线示例(API 地图 等页请链到此处,勿各写一份)。下面只有根容器 + 一行字 + render()。若它也预览空白,问题在环境或运行方式,不在业务逻辑。
预期效果:小组件预览面板显示与脚本一致的布局与交互。
已复制
import widget
w = widget.Widget(background=("#FFFFFF", "#111827"), padding=14)
w.text("Widget 基线 OK", size=16, weight="semibold").line_limit(1).min_scale(0.72).line_limit(1)
w.render()
基线通过后,每次只加回一类功能(参数 → 状态按钮 → 图片 → timeline),看哪一步开始坏。
#五步排查流程
已复制
1. 基线能预览吗? ─否→ 检查 w.render()、是否混用 appui/scene、构建报错
↓ 是
2. 文档预览还是 Studio? ─仅文档→ 要上桌面必须 Studio 发布
↓
3. 三个 family 都看过吗? ─裁切→ 布局分支 / line_limit / min_scale
↓
4. 交互/动画在预览能复现吗? ─否→ state action / content_transition
↓
5. 预览正常、桌面仍旧? ─→ 重新发布 → 删主屏小组件 → 重新添加
#现象速查
| 现象 | 优先检查 | 深入阅读 |
|---|---|---|
| 预览空白 | w.render()、只 import widget | 下文 §空白 |
| 文档能预览、桌面没有 | 是否 Studio 发布 + 系统添加 | 从脚本到桌面 |
| 文字裁切 / 挤在一起 | is_family、family_value、line_limit | 布局与尺寸 |
| 参数侧栏为空 | widget.param 在 render() 前 | 参数面板 |
| 按钮 / 开关无效 | widget.state 的 .increment() 等 | 状态和交互 |
| 数字无动画 | content_transition("numericText")、.id() | 时间线和动画 |
| 颜色 / 图片不对 | 双色资源、accentable | 资源与外观 |
| Studio 报「Widget 诊断」 | w.validate()、逐 family 修 | 下文 §布局诊断 |
#空白 / 构建失败
| 原因 | 识别 | 修复 |
|---|---|---|
忘记 w.render() | 预览全白、无 IR 输出 | 脚本末尾调用一次 |
多个 Widget() | 行为未定义 | 只保留一个根 w = widget.Widget(...) |
混入 appui / scene | 构建异常或空白 | Widget 脚本只用 widget API |
| 语法 / 运行时错误 | Studio 控制台有 Traceback | 修报错行;先用基线脚本验证 |
svg() / image() 无路径 | TypeError: requires name | 提供路径、param.file 或 save_image |
toggle(..., tint=) | TypeError: unexpected keyword | 改用 color= |
symbol(..., color=, size=) | TypeError: unexpected keyword | w.symbol("name").color(c).font_size(n);symbol 仅支持 rendering/palette/variant/scale |
link(..., icon=).line_limit() | AttributeError: no line_limit | 带 icon 的 link 不能链修饰符;改无 icon 或去掉链式调用 |
widget.context() / w.context() | TypeError: not callable | 用 ctx = widget.context 或 w.context(属性) |
已复制
# ❌ 构建期就会失败
w.toggle("完成", state=done, tint="#22C55E")
# ✅
w.toggle("完成", state=done, color="#22C55E")
#预览正常,桌面不对
| 原因 | 识别 | 修复 |
|---|---|---|
只在文档里 previewWidget | 主屏从未添加组件 | 代码复制到 Widget Studio 发布 |
| 未在系统里添加小组件 | App 已发布但主屏无入口 | 长按主屏 → + → 选 Python IDE |
| 选错 Studio 项目 / 槽位 | 显示别的 widget 内容 | 添加时核对项目名称 |
| WidgetKit 缓存旧快照 | 改代码后桌面仍旧 | Studio 重新运行发布 |
| 仍不更新 | 缓存顽固 | 删除主屏小组件 → 重新添加 |
改了 widget.state 的 key | 点击计数错乱或归零 | 重新发布;建议删组件重装 |
| 文档预览 ≠ Studio 预览 | 参数/资源路径不同 | 以 Studio 项目目录为准 |
发布检查清单见 从脚本到桌面。
#裁切与布局过挤
| 原因 | 识别 | 修复 |
|---|---|---|
| large 布局塞进 small | 仅 small 裁切 | ctx.is_family("small") 减行或藏次要内容 |
| 字号 / 边距不分 family | medium 正常、small 爆版 | widget.family_value(16, small=14, large=18) |
| 可变文案无保护 | 长标题被 «…» 截断 | .line_limit(1).min_scale(0.65)(格内可降到 0.55) |
row 塞太多列 | small 数字叠在一起 | 改 column 或减少列数 |
| 表格线发粗 | 格子边框像厚框 | 用 w.table(..., line_width="hairline"),勿 rect 拼线 |
| 锁屏 accessory 套 medium UI | circular 几乎看不见 | 为 circular/rectangular/inline 单独写布局 |
防裁切清单见 布局与尺寸。
#参数与配置
| 原因 | 识别 | 修复 |
|---|---|---|
param 写在 render() 后 | 侧栏无参数 | 全部移到 UI 构建之前 |
choice 的 default 非法 | 构建报错 | default 必须是 options 之一 |
slider 默认值为字符串 | 类型异常 | 用数字 18,不是 "18" |
| 混淆 param 与 state | 预览能改、桌面不变(或反之) | 配色用 param,点击用 state |
| 改参数名后桌面仍旧 | 旧实例绑旧 key | 重新发布,必要时删组件重装 |
#点击 / 链接无效
| 原因 | 识别 | 修复 |
|---|---|---|
action 写死字符串 | 点击完全无反应 | action=count.increment() |
| 传入 Python 函数 | 需拉起 App,Widget 内不执行 | 用 widget.state 工厂方法 |
| Toggle 不记忆 | 显示开关但状态不回写 | w.toggle(..., state=done) |
toggle_item 类型不一致 | 勾选状态错乱 | contains(i) 与 toggle_item(i) 同类型 |
| 链接与按钮同区域 | 点哪都不对 | 链接、按钮分块;见 交互 |
| 预览能点、桌面不能 | 未发布或 state key 已变 | 重新发布 + 桌面验证 |
已复制
count = widget.state.int("count", 0)
# ❌
w.button("+", action="increment")
# ✅
w.button("+", action=count.increment())
#动画与时间线
| 原因 | 识别 | 修复 |
|---|---|---|
无 content_transition | 数字直接跳变 | .content_transition("numericText") |
| 无稳定身份 | 动画时有时无 | .id("唯一名") 或绑 state/entry key |
用 entry 绑交互数字 | 点击不变 | 交互用 widget.state,计划变化用 widget.entry |
timeline 无 date | entry 顺序乱 | 每条 entry 写 ISO date |
| 期望 60fps 循环 | Widget 能力不支持 | 改用 timeline 分段或 countdown |
| 桌面动画少于预览 | 系统合并刷新 | 正常;重新发布后仍无则查 entry/state |
详见 时间线和动画。
#外观与资源
| 原因 | 识别 | 修复 |
|---|---|---|
| 深色模式发灰 | 只有浅色图 | background_image((light, dark)) 或 image(light=, dark=) |
| Tinted 模式颜色乱 | 节点默认参与染色 | 设计色 .accentable(False),要跟系统 .accentable(True) |
| 透明背景无效 | 仍有不透明底 | transparent_background + container_background(removable=True) |
| 背景图字看不清 | 照片太亮 | scrim="bottom" + scrim_opacity |
| SVG / 图片不显示 | 预览路径空 | 文件放进 assets/ 或侧栏 param.file 重选 |
详见 资源与外观。
#布局诊断(w.validate)
构建后可用 w.validate() 检查当前 family 的布局风险。Studio 预览若出现 「Widget 诊断」,需按提示修阻断项后再发布。
已复制
import widget
w = widget.Widget(background="#FFFFFF", padding=14)
w.text("标题", size=16).line_limit(1)
# ... 构建 UI ...
report = w.validate(family=widget.SMALL) # 在 w.render() 之前调用
print(report["status"]) # clean | warning | error
for issue in report.get("issues", []):
print(issue["code"], issue["message"])
w.render()
validate() 读取当前已构建的 UI 树;须在 w.render() 之前调用。Studio 构建时也会按 family 自动跑诊断。
常见诊断码(非完整列表):
| 代码 | 含义 | 建议 |
|---|---|---|
text_size_too_large | 字号相对 family 过大 | 减小 size 或 family_value 分支 |
offset_clip_risk | 偏移过大可能裁切 | 减小 offset;用布局容器代替硬偏移 |
chart_height_risk | 图表过高挤占版面 | 降低 height;small 隐藏图表 |
frame_width_overflow | 固定宽超出内容区 | 减小 frame 或改用流式布局 |
frame_height_overflow | 固定高超出内容区 | 同上 |
对 small、medium、large 各跑一次验证(脚本在 Studio 会按 family 多次构建)。Widget 建议 为可优化提示;阻断项必须修掉。
#二分定位模板
复杂脚本卡住时,按顺序恢复功能:
| 步骤 | 加回什么 | 验证什么 |
|---|---|---|
| 1 | 仅 Widget + text + render | 基线预览 |
| 2 | widget.param 一两个 | 侧栏参数出现 |
| 3 | widget.state + 一个按钮 | 点击有反应 |
| 4 | row / column / 图表 | 各 family 无裁切 |
| 5 | 图片 / SVG / background_image | 资源路径正确 |
| 6 | w.timeline + entry / flip | 预览 entry 轮播 |
| 7 | Studio 发布 → 桌面添加 | 端到端一致 |
某步开始失败时,问题就在刚加回的那一类 API。
#仍解决不了?
收集这些信息便于反馈或自查:
- 入口:文档预览 / Widget Studio / MiniApp 工程
- family:small / medium / large / 锁屏 accessory
- 现象截图 + Studio 构建日志最后 30 行
w.validate()对应该 family 的issues- 是否已 重新发布 并 删除重装 桌面小组件
#相关文档
| 文档 | 用途 |
|---|---|
| widget 概览 | API 与心智模型 |
| 从脚本到桌面 | 发布与桌面验证 |
| 布局与尺寸 | 裁切、family 预算 |
| 参数面板 | widget.param |
| 状态和交互 | 按钮、开关、链接 |
| 时间线和动画 | numericText、timeline |
| 资源与外观 | 双色图、accentable |
| API 参考 | 签名核对 |
相关 API:widget-api-reference.md