From 049d4b6bbc470dac78faf3bcd9e24697340ee510 Mon Sep 17 00:00:00 2001 From: chengma Date: Mon, 20 Jul 2026 16:39:18 +0800 Subject: [PATCH] feat(brand): add application logo for exe and window icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增品牌标识并接入运行时与打包流程。 - `scripts/gen_logo.py` 从单一几何定义同时产出 SVG 母版、多尺寸 ICO 和 PNG 预览,避免母版与位图漂移;改样式改脚本后重新生成即可。 - 标记为暖橙圆角徽章 + 白色上升箭头(提升并发布)+ 四角星芒(AI 生成), 刻意避开蝦皮官方购物袋标识,避免暗示官方关联。 - ICO 含 16/20/24/32/40/48/64/128/256 九档,每档从矢量几何独立渲染而非 由大图缩放,保证任务栏与资源管理器小尺寸清晰。 - `app/assets` 作为可导入包提供无 Qt 依赖的路径解析,兼容源码树、 PyInstaller onedir 的 _MEIPASS 与 exe 同级目录三种布局。 - `widgets.app_icon()` 在 Qt 缺失或资源未生成时返回 None,调用方容错; 在 QApplication 上设置图标以便任务栏、对话框和消息框一并继承, MainWindow 另行设置以覆盖直接构造窗口的测试路径。 - 两个 spec 均加 `icon=`,主 spec 收集资源到 datas 并在资源缺失时直接 报错提示先跑生成脚本。 Co-Authored-By: Claude Fable 5 --- app/assets/__init__.py | 57 ++++++++++++ app/assets/cmshopee-256.png | Bin 0 -> 15513 bytes app/assets/cmshopee-logo.svg | 14 +++ app/assets/cmshopee.ico | Bin 0 -> 37280 bytes app/gui/__init__.py | 5 + app/gui/main_window.py | 3 + app/gui/widgets.py | 18 ++++ cmshopee-updater.spec | 6 ++ cmshopee.spec | 15 ++- docs/ui/README.md | 2 + scripts/gen_logo.py | 172 +++++++++++++++++++++++++++++++++++ 11 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 app/assets/__init__.py create mode 100644 app/assets/cmshopee-256.png create mode 100644 app/assets/cmshopee-logo.svg create mode 100644 app/assets/cmshopee.ico create mode 100644 scripts/gen_logo.py diff --git a/app/assets/__init__.py b/app/assets/__init__.py new file mode 100644 index 0000000..a9a05b5 --- /dev/null +++ b/app/assets/__init__.py @@ -0,0 +1,57 @@ +"""Bundled static assets (application icon). + +The icon ships inside the package so the same file backs the window icon at +runtime and the executable icon at build time. Keep this module free of Qt +imports: the packaging spec imports it while collecting data files. + +Regenerate the files with ``py -3.10 scripts/gen_logo.py``. +""" + +from __future__ import annotations + +import os +import sys + +ICON_NAME = "cmshopee.ico" +ICON_PNG_NAME = "cmshopee-256.png" + +_PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__)) + + +def _candidate_paths(name): + """Locations to probe, covering source tree and PyInstaller onedir.""" + + yield os.path.join(_PACKAGE_DIR, name) + + bundle_dir = getattr(sys, "_MEIPASS", None) + if bundle_dir: + yield os.path.join(bundle_dir, "app", "assets", name) + + if getattr(sys, "frozen", False): + yield os.path.join( + os.path.dirname(os.path.abspath(sys.executable)), + "app", + "assets", + name, + ) + + +def asset_path(name): + """Return an existing asset path, or None when it is not bundled.""" + + for candidate in _candidate_paths(name): + if candidate and os.path.isfile(candidate): + return candidate + return None + + +def icon_path(): + """Path to the multi-size Windows icon, or None when unavailable.""" + + return asset_path(ICON_NAME) + + +def icon_png_path(): + """Path to the 256px PNG fallback, or None when unavailable.""" + + return asset_path(ICON_PNG_NAME) diff --git a/app/assets/cmshopee-256.png b/app/assets/cmshopee-256.png new file mode 100644 index 0000000000000000000000000000000000000000..c1464850560ad1a9e3c7913bfcbeb93cd32b9808 GIT binary patch literal 15513 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�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 D=U;r# literal 0 HcmV?d00001 diff --git a/app/assets/cmshopee-logo.svg b/app/assets/cmshopee-logo.svg new file mode 100644 index 0000000..a92e58d --- /dev/null +++ b/app/assets/cmshopee-logo.svg @@ -0,0 +1,14 @@ + + 蝦皮圈優化助手 + + + + + + + + + + + + diff --git a/app/assets/cmshopee.ico b/app/assets/cmshopee.ico new file mode 100644 index 0000000000000000000000000000000000000000..bbe19236621d4cb55cae49b6f5a200e26af1acb5 GIT binary patch literal 37280 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%0DgDtRQ*t>TD(^DXZ>rvRq5u$?^6p`rpuYRII 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 int: return 1 _ensure_offscreen_for_headless_tests() app = QApplication.instance() or QApplication(sys.argv) + # Set on the application so the taskbar button, dialogs and message boxes + # all inherit it, not just the main window. + startup_icon = app_icon() + if startup_icon is not None: + app.setWindowIcon(startup_icon) health_context = update_health.context_from_argv( sys.argv, appconfig.app_base_dir(), diff --git a/app/gui/main_window.py b/app/gui/main_window.py index 6a37062..699d2c8 100644 --- a/app/gui/main_window.py +++ b/app/gui/main_window.py @@ -98,6 +98,9 @@ class MainWindow(QMainWindow): or appconfig.ai_models_config_path(self.config) ) self.setWindowTitle(display_name()) + window_icon = app_icon() + if window_icon is not None: + self.setWindowIcon(window_icon) _fit_and_center_window(self) self.setStyleSheet(BUTTON_BASE_STYLE) self._settings_tab_index = TAB_TITLES.index("设置") diff --git a/app/gui/widgets.py b/app/gui/widgets.py index 4e3618d..2f7ee82 100644 --- a/app/gui/widgets.py +++ b/app/gui/widgets.py @@ -8,6 +8,8 @@ import threading import time from concurrent.futures import ThreadPoolExecutor, as_completed +from .. import assets + try: from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, QTimer from PySide6.QtGui import QColor, QIcon, QImage, QPainter, QPixmap @@ -136,6 +138,22 @@ QPushButton:disabled {{ _RawQMessageBox = QMessageBox if QT_IMPORT_ERROR is None else None +def app_icon(): + """Application icon, or None when Qt is unavailable or the file is missing. + + Callers must tolerate None: the icon is a packaged asset, and a source + checkout that has not run scripts/gen_logo.py still has to start. + """ + + if QT_IMPORT_ERROR is not None: + return None + path = assets.icon_path() or assets.icon_png_path() + if not path: + return None + icon = QIcon(path) + return None if icon.isNull() else icon + + class _GuiAttributeProxy: def __init__(self, name, fallback): self._name = name diff --git a/cmshopee-updater.spec b/cmshopee-updater.spec index 454456b..c65234d 100644 --- a/cmshopee-updater.spec +++ b/cmshopee-updater.spec @@ -1,5 +1,10 @@ # -*- mode: python ; coding: utf-8 -*- +import os + +# Same mark as the main executable: the updater window belongs to this product. +APP_ICON = os.path.join(SPECPATH, "app", "assets", "cmshopee.ico") + a = Analysis( ["app/updater_entry.py"], pathex=[], @@ -25,4 +30,5 @@ exe = EXE( strip=False, upx=True, console=False, + icon=APP_ICON, ) diff --git a/cmshopee.spec b/cmshopee.spec index b7b1e7a..4d7cc87 100644 --- a/cmshopee.spec +++ b/cmshopee.spec @@ -7,6 +7,8 @@ include config files, SQLite databases, Chrome profiles, generated images, logs, prompts, or other operator data. """ +import os + from PyInstaller.utils.hooks import collect_data_files block_cipher = None @@ -14,12 +16,22 @@ default_prompt_datas = collect_data_files( "app.default_prompts", includes=["**/*.txt"], ) +# Window icon at runtime; the same file is the executable icon below. +asset_datas = collect_data_files( + "app.assets", + includes=["*.ico", "*.png", "**/*.ico", "**/*.png"], +) +if not asset_datas: + raise SystemExit( + "app/assets 下没有找到图标资源,请先运行:py -3.10 scripts/gen_logo.py" + ) +APP_ICON = os.path.join(SPECPATH, "app", "assets", "cmshopee.ico") a = Analysis( ["main.py"], pathex=[], binaries=[], - datas=default_prompt_datas, + datas=default_prompt_datas + asset_datas, hiddenimports=[ "PySide6.QtCore", "PySide6.QtGui", @@ -48,6 +60,7 @@ exe = EXE( upx=True, console=False, disable_windowed_traceback=False, + icon=APP_ICON, ) coll = COLLECT( exe, diff --git a/docs/ui/README.md b/docs/ui/README.md index 1210430..6067374 100644 --- a/docs/ui/README.md +++ b/docs/ui/README.md @@ -19,4 +19,6 @@ | [tab6-suite-prompt-dialog-T637.svg](tab6-suite-prompt-dialog-T637.svg) | 商品套图(T-637 提案):标题行「AI帮写/取消」紧随 label、最右新增「提示词设置」;提示词设置弹窗——左编辑基础模板(行序对齐调研笔记组装顺序,套图名称+补充描述、插入变量菜单、占位符校验、只读规则占位符内联)、右只读实时最终预览(预览分类下拉)、保存/恢复默认共用原子写盘;第一版不做同类多张序号/差异化 | | [workbench-redesign-v1.html](workbench-redesign-v1.html) | 工作台视觉提案(HTML 可交互原型):以当前 6 Tab 信息架构为基础,统一「数据准备区→表格命令区→表格→状态反馈区→完成任务区」页面骨架;命令按作用域分区(导入/筛选/选择集/工作流完成),表格补齐当前行、选择集、三态全选与排序契约;范围确认、部分成功、取消中间态和破坏性确认按干扰层级分配到对话框、信息条与 Toast;覆盖浅色/深色/高对比度近似、键盘(Tab/方向键/F6/Ctrl+F/F5)与 Windows 断点。商品状态相关交互已对齐 T-662b/c/d、T-663~T-665、T-667 定稿(无状态列与状态下拉筛选,③无强制更新入口)。颜色 Token 取自 `app/gui/widgets.py`。使用模拟数据,不接生产接口;页脚「原型说明与验收边界」列出假设与不可仅靠 HTML 验收的项。 | +应用图标(非效果图,是随程序发布的资源):`app/assets/cmshopee-logo.svg` 是可编辑母版,`cmshopee.ico`(16–256 共 9 档)用于 exe 图标和 GUI 窗口左上角,`cmshopee-256.png` 为预览与非 Windows 回退。三者由 `py -3.10 scripts/gen_logo.py` 从同一份几何定义生成,改样式请改脚本后重新生成,不要单独手改某一个文件。 + > 仅为效果图或交互原型,最终样式以实现为准。带 `-T584`/`-v2` 等后缀的为**改版提案**,未定稿。HTML 原型不能验证原生 Qt 标题栏、系统主题、DPI、无障碍树、线程和真实任务生命周期;界面职责与流程见 [../routes.md](../routes.md)。 diff --git a/scripts/gen_logo.py b/scripts/gen_logo.py new file mode 100644 index 0000000..6542196 --- /dev/null +++ b/scripts/gen_logo.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +"""Generate the cmshopee application logo. + +Geometry lives here once and is emitted to both the editable SVG master and +the rasterised Windows icon, so the two can never drift apart. + +Usage: + py -3.10 scripts/gen_logo.py + +Outputs (all under app/assets/): + cmshopee-logo.svg editable master + cmshopee.ico multi-size Windows icon (exe + window icon) + cmshopee-256.png preview / non-Windows fallback + +The mark is an upward arrow rising from a base bar: the product listing being +lifted and published. It deliberately avoids Shopee's own bag glyph so the +tool is not mistaken for an official Shopee application. +""" + +from __future__ import annotations + +import os +import sys + +try: + from PIL import Image, ImageDraw +except ImportError: # pragma: no cover - developer tooling only + sys.exit("需要 Pillow 才能生成图标:py -3.10 -m pip install Pillow") + + +CANVAS = 256 +SUPERSAMPLE = 8 +ICO_SIZES = (16, 20, 24, 32, 40, 48, 64, 128, 256) + +# Badge +BADGE = (10, 10, 246, 246) # x0, y0, x1, y1 +BADGE_RADIUS = 54 +GRADIENT_FROM = (245, 121, 63) # #F5793F +GRADIENT_TO = (214, 55, 28) # #D6371C + +# Mark (white): an arrow lifting a listing off its base, plus an AI sparkle. +ARROW_HEAD = ((112, 58), (56, 116), (168, 116)) +ARROW_STEM = (90, 110, 134, 162) # x0, y0, x1, y1 +ARROW_STEM_RADIUS = 10 +BASE_BAR = (56, 178, 168, 204) +BASE_BAR_RADIUS = 13 +MARK_COLOR = (255, 255, 255) + +# Four-point sparkle: the AI generation step. Degrades to a small accent dot +# at 16px rather than turning to mush. +SPARKLE_CENTER = (194, 74) +SPARKLE_OUTER = 34 +SPARKLE_INNER = 11 + +ASSETS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "app", "assets") + + +def _hex(rgb): + return "#%02X%02X%02X" % rgb + + +def _gradient(size): + """Diagonal linear gradient, built small and scaled up (it is smooth).""" + + base = Image.new("RGB", (CANVAS, CANVAS)) + pixels = base.load() + span = 2 * (CANVAS - 1) + for y in range(CANVAS): + for x in range(CANVAS): + t = (x + y) / span + pixels[x, y] = tuple( + round(GRADIENT_FROM[i] + (GRADIENT_TO[i] - GRADIENT_FROM[i]) * t) + for i in range(3) + ) + return base.resize((size, size), Image.LANCZOS) + + +def _scaled(values, factor): + return [value * factor for value in values] + + +def _sparkle_points(): + """Eight alternating outer/inner points forming a four-point star.""" + + cx, cy = SPARKLE_CENTER + o, i = SPARKLE_OUTER, SPARKLE_INNER + diagonal = i * (2 ** 0.5) / 2 + return [ + (cx, cy - o), + (cx + diagonal, cy - diagonal), + (cx + o, cy), + (cx + diagonal, cy + diagonal), + (cx, cy + o), + (cx - diagonal, cy + diagonal), + (cx - o, cy), + (cx - diagonal, cy - diagonal), + ] + + +def render(size): + """Render one square icon at the given pixel size.""" + + work = size * SUPERSAMPLE + factor = work / CANVAS + + badge_mask = Image.new("L", (work, work), 0) + draw = ImageDraw.Draw(badge_mask) + draw.rounded_rectangle(_scaled(BADGE, factor), radius=BADGE_RADIUS * factor, fill=255) + + mark_mask = Image.new("L", (work, work), 0) + draw = ImageDraw.Draw(mark_mask) + draw.polygon([(x * factor, y * factor) for x, y in ARROW_HEAD], fill=255) + draw.rounded_rectangle(_scaled(ARROW_STEM, factor), radius=ARROW_STEM_RADIUS * factor, fill=255) + draw.rounded_rectangle(_scaled(BASE_BAR, factor), radius=BASE_BAR_RADIUS * factor, fill=255) + draw.polygon([(x * factor, y * factor) for x, y in _sparkle_points()], fill=255) + + icon = Image.new("RGBA", (work, work), (0, 0, 0, 0)) + icon.paste(_gradient(work), (0, 0), badge_mask) + icon.paste(Image.new("RGBA", (work, work), MARK_COLOR + (255,)), (0, 0), mark_mask) + return icon.resize((size, size), Image.LANCZOS) + + +def write_svg(path): + head = "M {} {} L {} {} L {} {} Z".format(*[c for point in ARROW_HEAD for c in point]) + sparkle = " ".join(f"{x:.1f},{y:.1f}" for x, y in _sparkle_points()) + svg = f""" + 蝦皮圈優化助手 + + + + + + + + + + + + +""" + with open(path, "w", encoding="utf-8") as fh: + fh.write(svg) + + +def main(): + os.makedirs(ASSETS_DIR, exist_ok=True) + + # Each size is rendered from the vector geometry rather than downscaled from + # one bitmap, so 16px and 20px stay crisp in Explorer and the taskbar. + images = [render(size) for size in ICO_SIZES] + largest = images[-1] + + ico_path = os.path.join(ASSETS_DIR, "cmshopee.ico") + largest.save( + ico_path, + format="ICO", + sizes=[(size, size) for size in ICO_SIZES], + append_images=images[:-1], + ) + + png_path = os.path.join(ASSETS_DIR, "cmshopee-256.png") + largest.save(png_path, format="PNG") + + svg_path = os.path.join(ASSETS_DIR, "cmshopee-logo.svg") + write_svg(svg_path) + + for path in (svg_path, ico_path, png_path): + print(f"已生成 {os.path.relpath(path, os.path.dirname(ASSETS_DIR))} {os.path.getsize(path)} bytes") + + +if __name__ == "__main__": + main()