2026-05-09-ui-redesign-apple-theme-design.md 13.3 KB

UI 重设计:Apple 高级浅色主题 + 浅深双模式

日期:2026-05-09 分支feat/ui-redesign-apple-theme 状态:设计已定,实施进行中


1. 背景与目标

1.1 问题陈述

当前 UI 是 PySide6 默认皮肤 + 65+ 处零散 inline setStyleSheet(image_generator.py:65 处、task_queue.py:4 处),没有统一设计语言。具体表现:

  • 全灰白默认 Qt 风格,缺现代感
  • "生成图片"和"下载图片"主次按钮视觉权重一样
  • GroupBox 边框过重(Win95 观感)
  • "任务队列"sidebar 标题在视觉上飘到主区 tab bar 旁边
  • 字号/间距/圆角不统一
  • macOS 系统切换深色主题时整个应用观感断裂

1.2 目标

  1. 统一设计语言:Apple Human Interface Guidelines 浅色风格 + Apple Blue (#0071E3) 主色
  2. 跨平台一致:macOS 和 Windows 11 视觉一致(用户原话:"统一两端的设计语言")
  3. 支持系统深浅主题自动切换:跟随 QGuiApplication.styleHints().colorScheme()
  4. 修复关键 layout 问题:按钮主次分级、sidebar 头部、卡片化 GroupBox
  5. 保持业务逻辑不变:纯视觉层重构,不动生成 / 队列 / 历史功能

1.3 非目标(YAGNI)

  • 不引第三方 UI 库(qfluentwidgets、qt-material 等)
  • 不重做信息架构(tabs、表单字段)
  • 不增加新功能
  • 不做主题色自定义(用户改色等高级功能)

2. 已确认的设计决策

决策点 结果 用户原话/理由
设计方向 C · Apple 高级感浅色 "可以统一 MacOS 与 Windows 的设计语言"
主色调 A · Apple Blue #0071E3 "跟我们的品牌元素的颜色契合"
重写范围 B · 外观 + 关键 layout 修复 (选择推荐项)
深色模式 完整双套主题 "macos 会让软件跟随时间自由转换深浅主题,不做就巨丑巨难看"
二级窗口 一并重做(登录 + 所有 dialog) (选择推荐项)
实现路径 B · 设计令牌生成器(Python tokens → build_qss) Python dict 比双 .qss 文件不容易飘,dark mode 90% 是规则反转

3. 设计令牌(Design Tokens)

所有数值在 theme.py 中以 Python dict 定义,单一真相源。

3.1 颜色 — 浅色(Light)

# 表面层级
bg_canvas       #fbfbfd   主窗口背景(极浅冷灰)
bg_surface      #ffffff   卡片 / dialog 背景
bg_elevated     #ffffff   带投影的浮起容器
bg_subtle       #f4f4f7   GroupBox / 输入框 hover 底色

# 文字层级
text_primary    #1d1d1f   主文字
text_secondary  #6e6e73   次要文字 / 标签
text_tertiary   #86868b   占位符 / 禁用文字
text_on_accent  #ffffff   主按钮上的白字

# 边框 / 分隔
border_default  rgba(0,0,0,0.10)  默认边框(投影替代式)
border_strong   rgba(0,0,0,0.15)  焦点 / 选中
divider         rgba(0,0,0,0.06)  分隔线

# 主色
accent          #0071e3   Apple Blue
accent_hover    #0077ed   悬停(略亮)
accent_pressed  #0062c4   按下
accent_subtle   rgba(0,113,227,0.08)  主色淡背景(hover 卡片等)

# 状态色
success         #34c759   成功 / 在线
warning         #ff9500   警告
danger          #ff3b30   危险 / 错误

3.2 颜色 — 深色(Dark)

bg_canvas       #1a1a1c
bg_surface      #2c2c2e
bg_elevated     #3a3a3c
bg_subtle       #242426

text_primary    #ffffff
text_secondary  #98989d
text_tertiary   #6e6e73
text_on_accent  #ffffff

border_default  rgba(255,255,255,0.10)
border_strong   rgba(255,255,255,0.18)
divider         rgba(255,255,255,0.06)

accent          #0a84ff   (Dark mode 下 Apple Blue 略亮)
accent_hover    #1d8cff
accent_pressed  #006edc
accent_subtle   rgba(10,132,255,0.16)

success         #30d158
warning         #ff9f0a
danger          #ff453a

设计原则:浅深对照不是简单灰阶反转,而是参考 Apple SF Symbols / SwiftUI 的语义对照表。bg_canvasbg_surface 低半级(浅色:surface 比 canvas 亮;深色:surface 比 canvas 亮)—— 卡片永远比底板"凸"。

3.3 间距与尺寸

# 圆角
radius_sm        4px    标签 / chip
radius_md        8px    输入框 / 普通按钮 / 卡片
radius_lg        12px   大卡片 / dialog
radius_pill      980px  胶囊主按钮(Apple HIG 标志性)

# 间距(4px grid)
space_1          4px
space_2          8px
space_3          12px
space_4          16px
space_5          20px
space_6          24px
space_8          32px

# 字号(pt - Qt 跨平台)
font_xs          10pt   提示 / 标签
font_sm          11pt   次要文字 / 按钮文字
font_base        12pt   正文(Windows)/ 13pt(macOS, 系统检测加 1)
font_lg          14pt   小标题
font_xl          17pt   主标题

# 控件高度
control_h_sm     28px   小按钮 / 紧凑输入框
control_h_md     32px   常规按钮 / 输入框(默认)
control_h_lg     40px   主操作按钮 / 大输入框

3.4 字体栈

font_family = "-apple-system, 'SF Pro Text', 'PingFang SC',
               'Microsoft YaHei UI', 'Segoe UI Variable',
               'Segoe UI', sans-serif"

策略:让操作系统选最合适的字体——macOS 走 SF Pro / 苹方,Windows 走 Segoe UI Variable / 微软雅黑。Qt 会自动 fallback。


4. 模块架构

4.1 theme.py(新文件,约 300 行)

# 公共 API
def apply_theme(app: QApplication) -> ThemeManager
    """应用主题。在 main() 创建 QApplication 后立即调用。返回 ThemeManager
    实例(持有引用以保持信号连接)。"""

class ThemeManager(QObject):
    theme_changed = Signal(str)  # 'light' | 'dark'
    def current_mode() -> str
    def force_mode(mode: str | None)  # None = 跟随系统
    # 内部:监听 QGuiApplication.styleHints().colorSchemeChanged

# 令牌
TOKENS_LIGHT: dict[str, str]
TOKENS_DARK: dict[str, str]

def build_qss(mode: str) -> str
    """根据 mode(light/dark)生成完整 QSS 字符串。"""

4.2 集成点

main 入口 image_generator.py:4940

app = QApplication(sys.argv)
from theme import apply_theme
theme_manager = apply_theme(app)  # 必须在创建任何窗口前

preflight 入口 preflight.py:215, 218, 256, 258:同上

所有 widget:通过 setObjectName()setProperty() 来命中 QSS 选择器,禁止再用 inline setStyleSheet(除少数语义性状态色,见 §6.2)。

4.3 数据流

系统切换深色 → Qt 触发 colorSchemeChanged
              → ThemeManager 接收信号
              → build_qss(new_mode)
              → app.setStyleSheet(new_qss)
              → 所有 widget 自动重绘(Qt 内置)
              → 发射 theme_changed 信号(业务代码可监听,目前不需要)

5. 组件样式映射

5.1 按钮分级

QPushButton                       默认次按钮(白底蓝边/字)
QPushButton[variant="primary"]    主按钮(实色 accent 背景,白字,pill 圆角)
QPushButton[variant="ghost"]      幽灵按钮(透明背景,hover 出 accent_subtle)
QPushButton[variant="danger"]     危险按钮(红字描边)

应用清单

  • 生成图片variant="primary"(pill 圆角 + accent 背景)
  • 下载图片 / 添加图片 / 粘贴图片 → 默认次按钮
  • 删除 / 清空历史variant="danger"
  • 收藏 之类的图标按钮 → variant="ghost"

5.2 GroupBox → 卡片化

去掉边框 + Win95 风格标题,改为:

  • 白色底(bg_surface
  • 极淡的投影(box-shadow: 0 1px 2px rgba(0,0,0,0.04),Qt QSS 不直接支持 box-shadow,用 border: 1px solid border_default 模拟)
  • 标题不再镶嵌在边框上,改为顶部独立 label(uppercase 小字 + text_secondary

5.3 Tab 栏

QTabBar::tab               默认状态:透明背景 + text_secondary 文字
QTabBar::tab:selected      选中:accent 文字 + 底部 2px accent 横线
QTabBar::tab:hover         悬停:text_primary 文字

去掉默认 Qt tab 的"凸起带边框"外观,改为下划线式(更现代,Notion/VSCode/Chrome 都这风格)。

5.4 输入框 / 下拉

QLineEdit, QTextEdit, QComboBox
  默认:bg_surface + 1px border_default + radius_md
  焦点:1px border_strong + 1px accent 外发光(QSS 不支持 outline,用双层 border 实现)
  禁用:bg_subtle + text_tertiary

5.5 状态色(保留 inline,但走 property)

# 之前
self.status_label.setStyleSheet("QLabel { color: #34C759; }")
# 之后
self.status_label.setProperty("status", "success")
self.status_label.style().polish(self.status_label)  # 重绘

QSS 里:

QLabel[status="success"] { color: palette(success); }
QLabel[status="error"]   { color: palette(danger); }
QLabel[status="muted"]   { color: palette(text_secondary); }

5.6 任务队列 sidebar header 修复

当前 sidebar 顶部"任务队列"label 在视觉上飘到主区 tab bar 右侧。修复:sidebar 顶部加一个 32px 高的 header bar,使 sidebar 头部与主区 tab bar 在同一基线。


6. 迁移策略

6.1 全量删除清单

image_generator.py: 65 处 setStyleSheet
task_queue.py:       4 处 setStyleSheet
合计 69 处

按性质分类:

类型 数量估算 处理方式
全局 QSS 块(line 1331/1602/1644/1659/1676/2427) 6 删除,迁移到 theme.py
装饰色(按钮背景、容器边框、字体大小) ~50 删除,依赖全局主题
状态色(红/绿/灰) ~10 改 setProperty + style().polish()
平台兼容 hack(Mac 输入可见性等) ~3 保留并在 design doc 标注

6.2 保留的 inline setStyleSheet

只有以下场景允许 inline:

  1. 平台 hack(如 macOS 输入框可见性 fix,line 1285)
  2. 临时高亮反馈(鼠标悬停后 1 秒消失这种)

其他一律走 QSS。

6.3 OBJECT_NAME 命名约定

loginDialog              登录对话框
mainWindow               主窗口
generationTab            生成 tab
styleDesignerTab         款式设计 tab
historyTab               历史记录 tab
taskQueueSidebar         任务队列侧边栏
sidebarHeader            侧边栏头部 32px
previewArea              预览区
referenceImageDrop       参考图拖拽区

QSS 通过 #objectName 命中。


7. Layout 修复清单(Scope B 范围内)

问题 修复方案
"任务队列"飘右上角 sidebar 顶部加 32px header bar 与 tab bar 对齐
主次按钮不分 "生成图片"加 variant="primary"
GroupBox 边框过重 卡片化(白底 + 极淡边框 + 圆角)
控件间距不一 统一改用 space_3 (12px) 间距
状态文字颜色 inline 改 property
登录页和主窗口风格断裂 全部走全局主题

不做的 layout 改动(避免范围蔓延):

  • 不重排 tab 顺序
  • 不动 70/30 主区与 sidebar 比例
  • 不改字段顺序
  • 不改"款式设计"tab 的 9 个 GroupBox 信息架构

8. 测试策略

8.1 自动化测试

项目目前没有 GUI 测试框架。不为本次重构新增测试(避免引入 pytest-qt 等依赖)。 保留现有 test_config_loading.py 跑通即可(QSS 重构不影响配置)。

8.2 手工验收清单

启动后按顺序检查:

  • 登录页能正常显示和登录
  • 主窗口三个 tab 切换正常
  • "生成图片"按钮视觉权重明显高于"下载图片"
  • GroupBox 看起来是"卡片"而不是"边框"
  • 任务队列侧边栏头部不再飘到主区 tab 上
  • 输入框有焦点蓝边
  • 系统切换深色(Mac: 系统设置 → 外观 / Win11: 设置 → 个性化 → 颜色)→ 应用自动跟随
  • QMessageBox 弹窗也跟主题(Qt 默认会,确认一下)
  • 所有"删除"按钮有 danger 视觉
  • 业务功能完全没动(生成、队列、历史)

8.3 打包验证

PyInstaller 打包跑一次,确保:

  • exe 能启动到登录页
  • 启动到主窗口
  • 主题资源(如有)被打包进去

9. 风险与回退

9.1 已知风险

风险 缓解
Qt QSS 不支持 box-shadow,圆角阴影效果有限 用双层 border 模拟,效果接近但不等同 web
StyleHint colorSchemeChanged 在某些 Linux DE 不发射 项目实际部署只在 macOS + Windows,无影响
删 inline setStyleSheet 时漏改某个 widget → 视觉断层 每次 commit 单独跑一次启动验证
全局 palette 改变后 QMessageBox 系统 dialog 可能不跟随 全局 setStyleSheet 会覆盖;如不行再单独处理

9.2 回退方案

每次 commit 颗粒度小,单点回退:

git revert <commit-sha>

最坏情况整分支扔掉:

git checkout master
git branch -D feat/ui-redesign-apple-theme

master 完全不动。


10. 实施步骤(参考 TaskList)

  1. :white_check_mark: 创建分支
  2. :white_check_mark: 写本设计文档
  3. 实现 theme.py
  4. 主入口接入
  5. 迁移 ImageGeneratorWindow
  6. 迁移 LoginDialog
  7. 迁移 StyleDesignerTab + DraggableThumbnail
  8. 迁移 TaskQueueWidget
  9. 打包验证
  10. 推送 + 写 OVERNIGHT_SUMMARY.md

每步 1-2 个 commit,每步完成后启动跑一次冒烟(python image_generator.py 能进登录页即可)。


11. 给明天的你

醒来先看仓库根目录的 OVERNIGHT_SUMMARY.md,里面有完整进度、commit 列表、怎么运行、怎么验收、已知问题。

如果某处方向不对,直接 git checkout master 当无事发生。