From c59a348d67c336f514f53ad5c360e3b1d732a89b Mon Sep 17 00:00:00 2001 From: ila Date: Mon, 22 Jun 2026 15:56:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai-outfit):=20button=20palette=20matching?= =?UTF-8?q?=20the=20print=20module=20(=C2=A719.11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Panel had no stylesheet -> all buttons rendered native gray. Add setObjectName('aiOutfitPanel') + _apply_styles() with QSS scoped to #aiOutfitPanel (so dialogs keep native buttons): primary blue for 开始生成 (aiStartBtn, mirrors 开始批量导出/打开文件夹), quiet red for 停止生成 and the 话术 删除 (aiStopBtn/aiPromptDeleteBtn, mirrors 删除), secondary gray for the rest. docs/11 §10.5 + tasks §19.11. Offscreen grab verified; full suite (12) green. Co-Authored-By: Claude Opus 4.8 --- docs/11-ai-outfit.md | 23 ++++++++++++ src/app/widgets/ai_outfit_panel.py | 57 +++++++++++++++++++++++++++++- tasks.md | 11 ++++++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/docs/11-ai-outfit.md b/docs/11-ai-outfit.md index 954a56c..3c79aa4 100644 --- a/docs/11-ai-outfit.md +++ b/docs/11-ai-outfit.md @@ -237,6 +237,29 @@ Excel 行 → `OutfitTask` 列表的转换由 `excel_service` 完成;核心只 现状:`_on_tasks_loaded` 里调 `_fill_sample_combo(tasks)`,空任务时把预览下拉清成占位文案,破坏 §10.3 的修复。 改为:`_on_tasks_loaded` 不再动预览样本下拉;预览样本行只由 `read_all_rows`(`_reload_sample_rows`)维护,与运行解耦。 +### 10.5 按钮配色(对齐「添加印花」) + +**现状**:AI 穿搭面板没有 stylesheet,按钮全是系统灰。复用印花页各 widget 已有的按钮规范,让两页一致。 + +三类按钮(取自印花页 QSS): + +- **主操作(蓝填充)** = `#openFolderBtn` / `#queueBatchBtn`:`color:#fff; font-weight:bold; border:none; border-radius:3px; background:#0078d4; :hover #106ebe; :pressed #005a9e`。 +- **次级(灰描边)** = `#queueActionBtn` / `#templateBtn`:`border:1px solid #d6d6d6; border-radius:3px; background:#f0f0f0; :hover #e0e0e0; :pressed #d0d0d0; :disabled color #aaa, bg #f7f7f7`。 +- **危险(安静红)** = `#templateDeleteBtn`:次级底 + `color:#c42b1c; :hover bg #fde7e9 border #c42b1c; :pressed #f9d4d7`。 + +映射: + +| 按钮 | 配色 | +|---|---| +| 开始生成 | 主操作蓝(运行中禁用 → 灰) | +| 停止生成 | 危险安静红(空闲禁用 → 灰) | +| 话术「删除」 | 危险安静红 | +| 浏览…(×2) / 新建 / 另存为 / 重命名 / 插入标题 / 保存话术 / 导出失败清单 / 打开输出目录 | 次级灰 | + +原则:蓝色只留给唯一主动作(开始生成,对齐「开始批量导出 / 打开文件夹」),红色标停止/删除(对齐「删除」),其余用印花页通用的次级灰。 + +**实现**:面板 `setObjectName("aiOutfitPanel")` + `_apply_styles()`,QSS 全部以 `#aiOutfitPanel` 前缀**限定作用域**(避免波及 `QMessageBox`/`QInputDialog` 等弹窗按钮);开始/停止/话术删除三个按钮各设 objectName(`aiStartBtn` / `aiStopBtn` / `aiPromptDeleteBtn`),其余走面板内 `QPushButton` 默认次级样式。纯样式改动,不动行为。 + ## 11. 配置与数据位置 遵循 cmbot「配置集中、放数据目录、凭据不入库」约定: diff --git a/src/app/widgets/ai_outfit_panel.py b/src/app/widgets/ai_outfit_panel.py index ec45faa..7c89dfd 100644 --- a/src/app/widgets/ai_outfit_panel.py +++ b/src/app/widgets/ai_outfit_panel.py @@ -158,6 +158,7 @@ class AiOutfitPanel(QWidget): # -- construction --------------------------------------------------- def _build_ui(self): + self.setObjectName("aiOutfitPanel") outer = QVBoxLayout(self) outer.setContentsMargins(0, 0, 0, 0) @@ -171,6 +172,57 @@ class AiOutfitPanel(QWidget): splitter.setStretchFactor(2, 0) splitter.setSizes([360, 540, 400]) outer.addWidget(splitter) + self._apply_styles() + + def _apply_styles(self): + """Button palette mirroring the 添加印花 widgets (docs/11 §10.5). + + Scoped under #aiOutfitPanel so QMessageBox/QInputDialog buttons keep the + native look. Blue = the single primary action (开始生成); red = stop/delete; + everything else is the neutral secondary used across the print panels. + """ + self.setStyleSheet(""" + #aiOutfitPanel QPushButton { + font-size: 12px; + padding: 4px 10px; + border: 1px solid #d6d6d6; + border-radius: 3px; + background: #f0f0f0; + color: #202020; + } + #aiOutfitPanel QPushButton:hover { background: #e0e0e0; } + #aiOutfitPanel QPushButton:pressed { background: #d0d0d0; } + #aiOutfitPanel QPushButton:disabled { color: #aaaaaa; background: #f7f7f7; } + + #aiOutfitPanel QPushButton#aiStartBtn { + font-weight: bold; + color: #ffffff; + border: none; + background: #0078d4; + } + #aiOutfitPanel QPushButton#aiStartBtn:hover { background: #106ebe; } + #aiOutfitPanel QPushButton#aiStartBtn:pressed { background: #005a9e; } + #aiOutfitPanel QPushButton#aiStartBtn:disabled { + color: #aaaaaa; background: #f7f7f7; + } + + #aiOutfitPanel QPushButton#aiStopBtn, + #aiOutfitPanel QPushButton#aiPromptDeleteBtn { + color: #c42b1c; + background: #f0f0f0; + border: 1px solid #d6d6d6; + } + #aiOutfitPanel QPushButton#aiStopBtn:hover, + #aiOutfitPanel QPushButton#aiPromptDeleteBtn:hover { + background: #fde7e9; border-color: #c42b1c; + } + #aiOutfitPanel QPushButton#aiStopBtn:pressed, + #aiOutfitPanel QPushButton#aiPromptDeleteBtn:pressed { background: #f9d4d7; } + #aiOutfitPanel QPushButton#aiStopBtn:disabled, + #aiOutfitPanel QPushButton#aiPromptDeleteBtn:disabled { + color: #aaaaaa; background: #f7f7f7; border-color: #d6d6d6; + } + """) def _build_left(self): scroll = QScrollArea() @@ -207,6 +259,8 @@ class AiOutfitPanel(QWidget): for text, slot in (("新建", self._prompt_new), ("另存为", self._prompt_save_as), ("重命名", self._prompt_rename), ("删除", self._prompt_delete)): b = QPushButton(text) + if text == "删除": + b.setObjectName("aiPromptDeleteBtn") b.clicked.connect(slot) trow.addWidget(b) pv.addLayout(trow) @@ -378,10 +432,11 @@ class AiOutfitPanel(QWidget): run_row = QHBoxLayout() self._start_btn = QPushButton("开始生成") - self._start_btn.setObjectName("primaryBtn") + self._start_btn.setObjectName("aiStartBtn") self._start_btn.clicked.connect(self._start) run_row.addWidget(self._start_btn) self._stop_btn = QPushButton("停止生成") + self._stop_btn.setObjectName("aiStopBtn") self._stop_btn.setEnabled(False) self._stop_btn.clicked.connect(self._stop) run_row.addWidget(self._stop_btn) diff --git a/tasks.md b/tasks.md index d43bd93..8bdd6d4 100644 --- a/tasks.md +++ b/tasks.md @@ -1134,3 +1134,14 @@ - [x] 改进 1:`_on_finished(total==0)` 弹 `_show_no_pending_message`——统计 `read_all_rows` 的「完成 N / 失败 M」,提示「已完成 N 跳过;失败 M 可勾『重试上次失败的行』;重做已完成请清空状态(E)列」(无有效行时另提示) - [x] 改进 2:`_on_tasks_loaded` 去掉 `_fill_sample_combo(tasks)`,预览样本下拉只由 `read_all_rows`/`_reload_sample_rows` 维护,与运行解耦 - [x] 离屏验证:全表完成 → `tasks_loaded([])` 后预览下拉仍 2 行、`_on_finished(total=0)` 弹"没有待处理的行"(含已完成1/失败1);`total>0` 仍弹常规"AI 穿搭"结束框;全套 12 文件绿 + +### 19.11 AI 穿搭按钮配色(对齐添加印花)— docs/11 §10.5 + +前置阅读:`docs/11-ai-outfit.md`(§10.5)、`src/app/widgets/image_list_panel.py` / `queue_panel.py` / `template_panel.py`(按钮 QSS 范式) + +背景:AI 穿搭面板无 stylesheet,按钮全是系统灰;复用印花页的「主操作蓝 / 次级灰 / 危险红」三类按钮规范。 + +- [x] `ai_outfit_panel.py`:面板 `setObjectName("aiOutfitPanel")` + `_apply_styles()`(`_build_ui` 末尾调用),QSS 以 `#aiOutfitPanel` 限定作用域,避免波及弹窗按钮 +- [x] 主操作蓝(`#openFolderBtn`/`#queueBatchBtn` 同款)= 开始生成(`aiStartBtn`);危险安静红(`#templateDeleteBtn` 同款)= 停止生成(`aiStopBtn`)+ 话术删除(`aiPromptDeleteBtn`);其余走面板内 `QPushButton` 默认次级灰(`#queueActionBtn`/`#templateBtn` 同款) +- [x] 给 开始/停止/话术删除 三个按钮设 objectName;纯样式、不动行为 +- [x] 离屏 Qt grab 截图核对:开始=蓝、停止=红(禁用→灰)、话术删除=红、其余=灰描边;面板 stylesheet 非空;全套 12 文件绿