feat: 今日实时 0% 画等号+金额小号 ¥/万,数值与状态栏数字加粗

- 环比/同比增长率恰为 0 时方向记 flat,三角位画等号(与用量行 pace 持平一致)
- 金额拆为「¥ / 主数字 / 万」三段,¥ 与 万 用 F12 小号伪粗体、与 F24 主数字基线对齐;单量列不变
- 环比/同比数值、BTC 涨跌幅、顶部电量/温度/湿度数字改 F12 伪粗体(字号不变),占位 -- 不加粗
- 等号绘制抽为基类 _eq 供 pace 与环比/同比共用;网页版 flat 同样输出等号

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 17:10:21 +08:00
parent d35f233ef3
commit 3fa6bb5de6
5 changed files with 71 additions and 36 deletions

File diff suppressed because one or more lines are too long

22
data.py
View File

@@ -145,33 +145,37 @@ def weather_fetch():
# ---------- 格式化 ----------
# 数值统一拆成三段 (前缀, 主数字, 后缀)渲染器把主数字画大、前后缀画小如「¥」「万」2026-09-14 用户要求),
# 拼接后的整串仍放在 col["v"],供网页版等只认字符串的消费方使用。
def _fmt_count(n):
return f"{int(n):,}" # 千分位,如 1,284
return ("", f"{int(n):,}", "") # 千分位,如 1,284
def _fmt_money(yuan):
"""元金额 → 紧凑展示≥1万显示「X.XX万」(两位小数),否则取整元"""
"""元金额 → 紧凑展示≥1万显示「¥X.XX万」(两位小数),否则「¥整数元」;人民币符号作小号前缀、万作小号后缀"""
v = float(yuan)
return f"{v / 10000:.2f}" if abs(v) >= 10000 else f"{v:.0f}"
return ("¥", f"{v / 10000:.2f}", "") if abs(v) >= 10000 else ("¥", f"{v:.0f}", "")
def _fmt_rate(rate):
"""增长率字符串 → (方向, 文本)。null/空 → (None, '--'),方向 None 表示不画三角
"""增长率字符串 → (方向, 文本)。null/空 → (None, '--'),方向 None 表示不画三角
恰为 0 → 'flat',渲染器在三角位画等号「=」(与用量行 pace 持平一致2026-09-14 用户要求,原来 0 也画上三角)。
数值统一两位小数;整数部分不足两位用前导 0 补齐(6.5→06.50),便于环比/同比上下对齐;
三/四位数(≥100%)原样完整输出不截断。"""
if rate is None or rate == "":
return (None, "--")
v = float(rate)
return ("down" if v < 0 else "up", f"{abs(v):05.2f}%")
direction = "flat" if v == 0 else "down" if v < 0 else "up"
return (direction, f"{abs(v):05.2f}%")
def _col(k, v, wow, yoy, wide=False):
"""一列指标。wide=True 表示仅宽版800×480展示400×300 小屏三列放不下、渲染时跳过。"""
return {"k": k, "v": v, "hb": _fmt_rate(wow), "tb": _fmt_rate(yoy), "wide": wide}
def _col(k, parts, wow, yoy, wide=False):
"""一列指标。parts 为 (前缀, 主数字, 后缀)wide=True 表示仅宽版800×480展示400×300 小屏三列放不下、渲染时跳过。"""
return {"k": k, "v": "".join(parts), "vp": tuple(parts), "hb": _fmt_rate(wow), "tb": _fmt_rate(yoy), "wide": wide}
def _placeholder_col(k, wide=False):
return {"k": k, "v": "--", "hb": (None, "--"), "tb": (None, "--"), "wide": wide}
return {"k": k, "v": "--", "vp": ("", "--", ""), "hb": (None, "--"), "tb": (None, "--"), "wide": wide}
# 今日实时五列:(标签、接口字段前缀、格式化、是否仅宽版)。字段前缀 base 对应 today{Base} / {base}WowRate / {base}YoyRate

View File

@@ -96,7 +96,8 @@ def _usage_card(usage):
def _cmp(label, pair):
direction, val = pair
col = GREEN if direction == "up" else RED if direction == "down" else BLACK
tri = '<i class="tri up"></i>' if direction == "up" else ('<i class="tri dn"></i>' if direction == "down" else "")
tri = ('<i class="tri up"></i>' if direction == "up" else '<i class="tri dn"></i>' if direction == "down"
else "= " if direction == "flat" else "") # flat增长率恰为 0等号表示持平与 PNG 版一致)
return f'<div>{_e(label)} <span style="color:{col}">{tri}{_e(val)}</span></div>'

View File

@@ -108,8 +108,8 @@ class DashboardRenderer:
"""环比/同比一行的总宽度(标签 + 间隙 + 三角 + 数值),供居中/右对齐排布。"""
direction, val = pair
k = self.k
trw, tgap = (8 * k, 4 * k) if direction else (0, 0)
return self._tw(label, self.F12) + 5 * k + trw + tgap + self._tw(val, self.F12)
trw, tgap, vb = (8 * k, 4 * k, k) if direction else (0, 0, 0) # vb数值伪粗体的偏移宽度占位 -- 不加粗)
return self._tw(label, self.F12) + 5 * k + trw + tgap + self._tw(val, self.F12) + vb
def _cmp_line(self, cx, y, label, pair, fg=BLACK):
"""环比/同比一行:标签 + 涨跌三角 + 数值,整体居中于 cx。direction 为 None 时不画三角。"""
@@ -119,20 +119,49 @@ class DashboardRenderer:
"""环比/同比「三角 + 数值」的颜色钩子:三色版 涨红、其余沿用 fg。宽版覆写。"""
return RED if direction == "up" else fg
def _eq(self, cx, cy, w, fill=BLACK):
"""等号「=」:两道横杠各宽 w、厚 k中间隔 2k整体以 (cx, cy) 为中心——占三角的位子表示「持平」。
用量行 pacediff=0与今日实时环比/同比(增长率恰为 0共用。"""
k = self.k
for top in (cy - 2 * k, cy + k):
self.d.rectangle([cx - w / 2, top, cx + w / 2 - 1, top + k - 1], fill=fill)
def _cmp_at(self, x, y, label, pair, fg=BLACK):
"""环比/同比一行,左对齐于 x宽版把它放在数值右侧时用"""
"""环比/同比一行,左对齐于 x宽版把它放在数值右侧时用
direction: up 上三角 / down 下三角 / flat 等号(增长率恰为 02026-09-14 用户要求,原来 0 也画上三角)/ None 不画。"""
direction, val = pair
k = self.k
lw = self._tw(label, self.F12); gap = 5 * k
trw, tgap = (8 * k, 4 * k) if direction else (0, 0)
self.text(x, y, label, self.F12, fill=fg); x += lw + gap
# 方向语义配色由 _cmp_color 决定(三色版:红涨、跌/无 → 黑;宽版覆写为 涨绿跌红 与 K 线一致);标签始终黑
up = direction == "up"
# 方向语义配色由 _cmp_color 决定(三色版:红涨、跌/无/持平 → 黑;宽版覆写为 涨绿跌红 与 K 线一致);标签始终黑
val_color = self._cmp_color(direction, fg)
if direction:
if direction == "flat":
self._eq(x + trw / 2, y + 8 * k, 7 * k, fill=val_color); x += trw + tgap # 横杠落在第 6/9 行,中心 8 ≈ 数字墨迹中心 8.5
elif direction:
# 三角中心对齐 F12 数字墨迹竖直中心(y+8.5),否则箭头偏高
self.tri(x + trw / 2, y + 8.5 * k, 7 * k, up=up, fill=val_color); x += trw + tgap
self.text(x, y, val, self.F12, fill=val_color)
self.tri(x + trw / 2, y + 8.5 * k, 7 * k, up=direction == "up", fill=val_color); x += trw + tgap
# 数值伪粗体、字号不变2026-09-14 用户要求);标签常规字重;占位「--」不加粗(加粗会粘成一条长横)
self.text(x, y, val, self.F12, fill=val_color, bold=k if direction else 0)
def _value(self, cx, y, col, bold=1):
"""今日实时的数值:主数字 F24 粗,前缀(¥)/后缀F12 小号、与主数字基线对齐2026-09-14 用户要求「¥ 与 万 字号小一点」),
整体水平居中于 cx。col["vp"] 为 (前缀, 主数字, 后缀),缺失时退回整串 col["v"] 按 F24 画。"""
pre, num, suf = col.get("vp") or ("", col["v"], "")
k = self.k
GAP = 1 * k # 小号段与主数字的间距
sb = bold # 小号段同样伪粗体,否则挨着粗体主数字显得单薄
wp = self._tw(pre, self.F12) + sb + GAP if pre else 0
ws = GAP + self._tw(suf, self.F12) + sb if suf else 0
wn = self._tw(num, self.F24) + bold
x = cx - (wp + wn + ws) / 2.0
# 基线对齐F12/F24 数字墨迹底沿分别为 13k/26kgetbbox小号段下移其差值
ys = y + (self.F24.getbbox("0")[3] - self.F12.getbbox("0")[3])
if pre:
self.text(x, ys, pre, self.F12, bold=sb); x += wp
self.text(x, y, num, self.F24, bold=bold); x += wn
if suf:
self.text(x + GAP, ys, suf, self.F12, bold=sb)
def _label(self, y, title, right=None, x0=None, x1=None):
"""区块标题(两区块统一,对齐主分支 _title_bar 风格):
@@ -174,9 +203,7 @@ class DashboardRenderer:
if diff != 0:
self.tri(cx, cy, TRI, up=up, fill=color)
else:
k = self.k # 等号「=」:两道横杠,各宽 6k、厚 k中间隔 2k整体以 cy 为中心
for top in (cy - 2 * k, cy + k):
self.d.rectangle([cx - TRI / 2, top, cx + TRI / 2 - 1, top + k - 1], fill=color)
self._eq(cx, cy, TRI, fill=color) # 等号与三角等宽
return tw + TRI_GAP + TRI
def _usage_row(self, x0, x1, y, bar, warn, label_w=None):
@@ -477,7 +504,7 @@ class DashboardRenderer:
for i, c in enumerate(cols):
cx = ax0 + cw * (i + 0.5)
self.ctext(cx, 211, c["k"], self.F12) # 指标名(标题+18
self.ctext(cx, 227, c["v"], self.F24, bold=1) # 数值 伪粗体+34
self._value(cx, 227, c, bold=1) # 数值 F24 伪粗体 + 小号 ¥/万+34
self._cmp_line(cx, 257, "环比", c["hb"]) # +64
self._cmp_line(cx, 275, "同比", c["tb"]) # +82墨迹止于≈289底部留≈10px

View File

@@ -96,7 +96,7 @@ class E1002Renderer(DashboardRenderer):
# ---------- 组件 ----------
def _status_battery(self, x, cy, dev):
"""状态项·电池F12墨迹中心 cy外框 16×9 + 正极 2×4内部按电量实心填充——充电中 GREEN、≤20% RED、其余 BLACK
"""状态项·电池F12数字伪粗体,墨迹中心 cy外框 16×9 + 正极 2×4内部按电量实心填充——充电中 GREEN、≤20% RED、其余 BLACK
右侧电量数字,缺失 "--"。返回末端 x。"""
F, GAP_I = self.F12, 5
lvl, chg = (dev or {}).get("battery"), (dev or {}).get("charging")
@@ -109,11 +109,12 @@ class E1002Renderer(DashboardRenderer):
self.d.rectangle([x + 2, cy - 2, x + 2 + fw, cy + 2], fill=fill)
x += 18 + GAP_I
txt = f"{lvl}%" if lvl is not None else "--"
self._mid(x, cy, txt, F)
return x + self._tw(txt, F)
b = 1 if lvl is not None else 0 # 数字伪粗体、字号不变2026-09-14 用户要求);占位不加粗
self._mid(x, cy, txt, F, bold=b)
return x + self._tw(txt, F) + b
def _status_env(self, x, cy, dev):
"""状态项·环境F12墨迹中心 cy温度计细管 3×8 + 底部球 7×7管内水银线温度 蓝色水滴(上尖下圆)+湿度,
"""状态项·环境F12数字伪粗体,墨迹中心 cy温度计细管 3×8 + 底部球 7×7管内水银线温度 蓝色水滴(上尖下圆)+湿度,
项间距 16、图标↔数字 5缺失 "--"。返回末端 x。"""
F, GAP_I, GAP = self.F12, 5, 16
dev = dev or {}
@@ -123,14 +124,16 @@ class E1002Renderer(DashboardRenderer):
x += 7 + GAP_I
t = dev.get("temp")
txt = f"{t:.1f}" if t is not None else "--"
self._mid(x, cy, txt, F); x += self._tw(txt, F) + GAP
b = 1 if t is not None else 0 # 数字伪粗体、字号不变2026-09-14 用户要求);占位不加粗
self._mid(x, cy, txt, F, bold=b); x += self._tw(txt, F) + b + GAP
self.d.polygon([(x + 4, cy - 6), (x, cy + 2), (x + 8, cy + 2)], fill=BLUE)
self.d.ellipse([x, cy - 1, x + 8, cy + 6], fill=BLUE)
x += 9 + GAP_I
h = dev.get("humidity")
txt = f"{h:.0f}%" if h is not None else "--"
self._mid(x, cy, txt, F)
return x + self._tw(txt, F)
b = 1 if h is not None else 0
self._mid(x, cy, txt, F, bold=b)
return x + self._tw(txt, F) + b
def _color_strip(self, y0, y1, x0=None, x1=None):
"""六色测试色条:六块等宽实心色块,白块加 1px 黑框以便可见。真机校准用。"""
@@ -210,7 +213,7 @@ class E1002Renderer(DashboardRenderer):
for i, c in enumerate(cols):
cx = x0 + cw * (i + 0.5)
self.ctext(cx, TAG_Y, c["k"], self.F12)
self.ctext(cx, VAL_Y, c["v"], self.F24, bold=1)
self._value(cx, VAL_Y, c, bold=1) # F24 粗主数字 + 小号 ¥ / 万
self._cmp_line(cx, HB_Y, "环比", c["hb"])
self._cmp_line(cx, TB_Y, "同比", c["tb"])
return y0 + 104
@@ -236,7 +239,7 @@ class E1002Renderer(DashboardRenderer):
col = GREEN if chg >= 0 else RED
xr = x1
pct_txt = f"{abs(chg):.2f}%"
self.rtext(xr, y0, pct_txt, self.F12, fill=col); xr -= self._tw(pct_txt, self.F12) + 4
self.rtext(xr, y0, pct_txt, self.F12, fill=col, bold=1); xr -= self._tw(pct_txt, self.F12) + 1 + 4 # 涨跌幅伪粗体(+1 为粗体偏移)
self.tri(xr - 4, y0 + 8.5, 7, up=chg >= 0, fill=col); xr -= 8 + 6
if btc.get("chg_label"): # 涨跌幅口径24h / 1d
self.rtext(xr, y0, btc["chg_label"], self.F12); xr -= self._tw(btc["chg_label"], self.F12) + 10