2026-09-02 13:37:25 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""Generate page previews mimicking the firmware layout with 0902 assets."""
|
|
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
SRC = ROOT / "tools" / "assets_0902"
|
|
|
|
|
|
OUT = ROOT / "tools" / "out"
|
|
|
|
|
|
MISANS = r"c:\Users\qjyu\Documents\SoundWalker\一诺国际吉他\Doc\UI\MiSans\MiSans 字体\MiSans-Medium.ttf"
|
|
|
|
|
|
PAGE_BG = (0, 8, 19)
|
|
|
|
|
|
ROW_BG = (36, 43, 58)
|
|
|
|
|
|
DIVIDER = (41, 48, 62)
|
|
|
|
|
|
|
|
|
|
|
|
def load(rel):
|
|
|
|
|
|
return Image.open(SRC / rel).convert("RGBA")
|
|
|
|
|
|
|
|
|
|
|
|
def paste_trans(page, im, x, y):
|
|
|
|
|
|
page.paste(im, (x, y), im)
|
|
|
|
|
|
|
|
|
|
|
|
def draw_status_bar(page, d, bt_on=True, pct=75, vol=3):
|
|
|
|
|
|
d.rectangle([0, 0, 239, 25], fill=PAGE_BG)
|
|
|
|
|
|
paste_trans(page, load("02_顶部图标/音量图标-10.png"), 8, 6)
|
|
|
|
|
|
if bt_on:
|
|
|
|
|
|
paste_trans(page, load("02_顶部图标/蓝牙图标-10.png"), 36, 5)
|
|
|
|
|
|
# volume level bars x=55.., 5 bars
|
|
|
|
|
|
for i in range(5):
|
|
|
|
|
|
h = 4 + i * 2
|
|
|
|
|
|
c = (255, 255, 255) if i < vol else (90, 96, 104)
|
|
|
|
|
|
d.rectangle([55 + i * 5, 21 - h, 55 + i * 5 + 2, 21], fill=c)
|
|
|
|
|
|
# battery
|
|
|
|
|
|
outline = load("02_顶部图标/电池图标-10.png").copy()
|
|
|
|
|
|
px = outline.load()
|
|
|
|
|
|
for yy in range(2, 10):
|
|
|
|
|
|
for xx in range(2, 27):
|
|
|
|
|
|
px[xx, yy] = (0, 0, 0, 0)
|
|
|
|
|
|
fill = load("02_顶部图标/电池电量-10-11.png")
|
|
|
|
|
|
fw = int(22 * pct / 100)
|
|
|
|
|
|
if fw > 0:
|
|
|
|
|
|
paste_trans(page, fill.crop((0, 0, fw, 9)), 206, 9)
|
|
|
|
|
|
paste_trans(page, outline, 203, 7)
|
|
|
|
|
|
d.line([0, 26, 239, 26], fill=DIVIDER)
|
|
|
|
|
|
|
|
|
|
|
|
def draw_bottom_bar(page, d, sel=None, back=False):
|
|
|
|
|
|
d.rectangle([0, 275, 239, 319], fill=PAGE_BG)
|
|
|
|
|
|
if back:
|
|
|
|
|
|
first = load("03_底部图标/底部图标-17.png") # back not-sel
|
|
|
|
|
|
else:
|
|
|
|
|
|
first = load("03_底部图标/底部图标-11.png") if sel != 0 else load("03_底部图标/底部图标-10.png")
|
|
|
|
|
|
second = load("03_底部图标/底部图标-12.png") if sel == 1 else load("03_底部图标/底部图标-13.png")
|
|
|
|
|
|
third = load("03_底部图标/底部图标-14.png") if sel == 2 else load("03_底部图标/底部图标-15.png")
|
|
|
|
|
|
for im, cx in ((first, 40), (second, 120), (third, 200)):
|
|
|
|
|
|
paste_trans(page, im, cx - im.width // 2, 288)
|
|
|
|
|
|
|
|
|
|
|
|
def new_page():
|
|
|
|
|
|
im = Image.new("RGB", (240, 320), PAGE_BG)
|
|
|
|
|
|
return im, ImageDraw.Draw(im)
|
|
|
|
|
|
|
|
|
|
|
|
def text_center(d, cx, y, s, size, color=(255, 255, 255)):
|
|
|
|
|
|
f = ImageFont.truetype(MISANS, size)
|
|
|
|
|
|
bb = d.textbbox((0, 0), s, font=f)
|
|
|
|
|
|
d.text((cx - (bb[2] - bb[0]) / 2 - bb[0], y), s, fill=color, font=f)
|
|
|
|
|
|
|
|
|
|
|
|
# ---------- 1. mode select ----------
|
|
|
|
|
|
p, d = new_page()
|
|
|
|
|
|
draw_status_bar(p, d)
|
2026-09-02 16:23:18 +08:00
|
|
|
|
rows = [("04_模式图标/完整版/模式界面栏背景-19.png", 43),
|
|
|
|
|
|
("04_模式图标/完整版/模式界面栏背景-17.png", 135),
|
|
|
|
|
|
("04_模式图标/完整版/模式界面栏背景-18.png", 225)]
|
2026-09-02 13:37:25 +08:00
|
|
|
|
for rel, y in rows:
|
2026-09-02 16:23:18 +08:00
|
|
|
|
paste_trans(p, load(rel).resize((222, 88), Image.LANCZOS), 9, y)
|
2026-09-02 13:37:25 +08:00
|
|
|
|
p.save(OUT / "preview_01_mode_select.png")
|
|
|
|
|
|
|
|
|
|
|
|
# ---------- 2. mixer ----------
|
|
|
|
|
|
p, d = new_page()
|
|
|
|
|
|
draw_status_bar(p, d)
|
|
|
|
|
|
text_center(d, 120, 6, "调音台", 16)
|
|
|
|
|
|
track = load("06_控件/调音台控件/调音台控件-3.png")
|
|
|
|
|
|
knob = load("06_控件/调音台控件/调音台控件-1.png")
|
|
|
|
|
|
labels = ["06_控件/调音台控件/文字/调音台文字-13.png", # MIC
|
|
|
|
|
|
"06_控件/调音台控件/文字/调音台文字-14.png", # 吉他
|
|
|
|
|
|
"06_控件/调音台控件/文字/调音台文字-15.png", # 伴奏
|
|
|
|
|
|
"06_控件/调音台控件/文字/调音台文字-16.png"] # 人声效果
|
|
|
|
|
|
vals = [7, 7, 7, 7]
|
|
|
|
|
|
for i, cx in enumerate((30, 90, 150, 210)):
|
|
|
|
|
|
paste_trans(p, track, cx - 15, 35)
|
|
|
|
|
|
ky = 35 + int((10 - vals[i]) / 10 * (215 - 24))
|
|
|
|
|
|
paste_trans(p, knob, cx - 15, ky)
|
|
|
|
|
|
text_center(d, cx, ky + 5, str(vals[i]), 11, color=(20, 25, 35))
|
|
|
|
|
|
lab = load(labels[i])
|
|
|
|
|
|
paste_trans(p, lab, cx - lab.width // 2, 258)
|
|
|
|
|
|
draw_bottom_bar(p, d, sel=1)
|
|
|
|
|
|
p.save(OUT / "preview_05_mixer.png")
|
|
|
|
|
|
|
|
|
|
|
|
# ---------- 3. settings ----------
|
|
|
|
|
|
p, d = new_page()
|
|
|
|
|
|
draw_status_bar(p, d)
|
|
|
|
|
|
text_center(d, 120, 6, "设置", 16)
|
|
|
|
|
|
rows_cfg = [
|
|
|
|
|
|
("05_设置图标/设置图标-01.png", "蓝牙设置", "开"),
|
|
|
|
|
|
("05_设置图标/设置图标-02.png", "自动关机", "15分钟"),
|
|
|
|
|
|
("06_控件/系统设置/系统设置图标-11.png", "恢复出厂设置", None),
|
|
|
|
|
|
("06_控件/系统设置/系统设置图标-13.png", "版本信息", "0.1.11"),
|
|
|
|
|
|
]
|
|
|
|
|
|
chev = load("05_设置图标/设置图标-03.png")
|
|
|
|
|
|
for i, (icon, label, val) in enumerate(rows_cfg):
|
|
|
|
|
|
y = 42 + i * 46
|
|
|
|
|
|
d.rounded_rectangle([13, y, 226, y + 32], radius=8, fill=ROW_BG)
|
|
|
|
|
|
ic = load(icon)
|
|
|
|
|
|
paste_trans(p, ic, 20, y + (33 - ic.height) // 2)
|
|
|
|
|
|
f = ImageFont.truetype(MISANS, 13)
|
|
|
|
|
|
d.text((62, y + 9), label, fill=(255, 255, 255), font=f)
|
|
|
|
|
|
if val:
|
|
|
|
|
|
fv = ImageFont.truetype(MISANS, 11)
|
|
|
|
|
|
bb = d.textbbox((0, 0), val, font=fv)
|
|
|
|
|
|
d.text((200 - (bb[2] - bb[0]), y + 11), val, fill=(150, 156, 165), font=fv)
|
|
|
|
|
|
paste_trans(p, chev, 212, y + 7)
|
|
|
|
|
|
draw_bottom_bar(p, d, back=True)
|
|
|
|
|
|
p.save(OUT / "preview_06_settings.png")
|
|
|
|
|
|
|
|
|
|
|
|
# ---------- 4. restore dialog ----------
|
|
|
|
|
|
p, d = new_page()
|
|
|
|
|
|
draw_status_bar(p, d)
|
|
|
|
|
|
text_center(d, 120, 6, "恢复出厂设置", 16)
|
|
|
|
|
|
text_center(d, 120, 120, "确认恢复出厂设置,恢复后所有设置将", 12)
|
|
|
|
|
|
text_center(d, 120, 142, "清除并重启。", 12)
|
|
|
|
|
|
paste_trans(p, load("06_控件/控件-10.png"), 13, 185)
|
|
|
|
|
|
# derived cancel button
|
|
|
|
|
|
im = Image.new("RGBA", (106, 56), (0, 0, 0, 0))
|
|
|
|
|
|
dd = ImageDraw.Draw(im)
|
|
|
|
|
|
dd.rounded_rectangle([0, 0, 105, 55], radius=12, fill=ROW_BG + (255,))
|
|
|
|
|
|
f24 = ImageFont.truetype(MISANS, 24)
|
|
|
|
|
|
bb = dd.textbbox((0, 0), "取消", font=f24)
|
|
|
|
|
|
dd.text(((106 - (bb[2] - bb[0])) // 2 - bb[0], (56 - (bb[3] - bb[1])) // 2 - bb[1]), "取消", fill=(255, 255, 255), font=f24)
|
|
|
|
|
|
paste_trans(p, im, 121, 185)
|
|
|
|
|
|
draw_bottom_bar(p, d, back=True)
|
|
|
|
|
|
p.save(OUT / "preview_07_restore.png")
|
|
|
|
|
|
|
|
|
|
|
|
print("previews saved")
|