feat(ai-outfit): button palette matching the print module (§19.11)

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 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 15:56:25 +08:00
co-authored by Claude Opus 4.8
parent d962b60816
commit c59a348d67
3 changed files with 90 additions and 1 deletions
+23
View File
@@ -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「配置集中、放数据目录、凭据不入库」约定:
+56 -1
View File
@@ -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)
+11
View File
@@ -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 文件绿