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:
22
data.py
22
data.py
@@ -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,
|
||||
|
||||
@@ -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>'
|
||||
|
||||
|
||||
|
||||
51
renderer.py
51
renderer.py
@@ -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) 为中心——占三角的位子表示「持平」。
|
||||
用量行 pace(diff=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 等号(增长率恰为 0,2026-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/26k(getbbox),小号段下移其差值
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user