#!/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) rows = [("04_模式图标/完整版/模式界面栏背景-19.png", 43), ("04_模式图标/完整版/模式界面栏背景-17.png", 135), ("04_模式图标/完整版/模式界面栏背景-18.png", 225)] for rel, y in rows: paste_trans(p, load(rel).resize((222, 88), Image.LANCZOS), 9, y) 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")