feat: color title wait status

This commit is contained in:
2026-07-04 17:04:23 +08:00
parent b5f289fddd
commit 92fa48bb01
4 changed files with 59 additions and 8 deletions
+2
View File
@@ -551,8 +551,10 @@ Excel 行 → `OutfitTask` 列表的转换由 `excel_service` 完成;核心只
- 计时每秒刷新,直到请求返回、失败、超时或用户停止。 - 计时每秒刷新,直到请求返回、失败、超时或用户停止。
- 请求返回后进入 Excel 写回阶段时,文案可改为 `写入中 已完成/总数`;全部写回后显示 `完成 N 条` 并在短暂停留后复位。 - 请求返回后进入 Excel 写回阶段时,文案可改为 `写入中 已完成/总数`;全部写回后显示 `完成 N 条` 并在短暂停留后复位。
- 超时或失败时显示 `等待超时` / `生成失败`,同时保留弹窗和日志里的详细原因。 - 超时或失败时显示 `等待超时` / `生成失败`,同时保留弹窗和日志里的详细原因。
- 文字颜色按状态变化:`等待中` 使用信息蓝 `#0078d4`,`写入中` / `完成 N 条` 使用完成绿 `#107c10`,`等待超时` / `生成失败` / `完成 N 条,失败 M` 使用错误红 `#c42b1c`。
产品边界: 产品边界:
- 这不是模型真实进度条,不能用百分比暗示“已生成多少”。它只表达“已等待多久 / 最长等多久”。 - 这不是模型真实进度条,不能用百分比暗示“已生成多少”。它只表达“已等待多久 / 最长等多久”。
- 现有右栏本次进度条继续按 Excel 行/写回进度推进;标题 label 右侧计时只覆盖“单次标题请求等待”这段长等待空窗。 - 现有右栏本次进度条继续按 Excel 行/写回进度推进;标题 label 右侧计时只覆盖“单次标题请求等待”这段长等待空窗。
- 只给文字本身上色,不加背景色、不做闪烁、不放大字号,避免在左栏窄空间里形成过强干扰。
+16 -8
View File
@@ -55,7 +55,12 @@ logger = logging.getLogger(__name__)
_RESOLUTIONS = ["512", "1K", "2K", "4K"] _RESOLUTIONS = ["512", "1K", "2K", "4K"]
_QUALITIES = ["小文件", "均衡", "高清"] _QUALITIES = ["小文件", "均衡", "高清"]
_COLS = ["行", "标题", "货号", "衣服图", "状态", "结果 / 原因"] _COLS = ["行", "标题", "货号", "衣服图", "状态", "结果 / 原因"]
_TITLE_WAIT_COLORS = {
"waiting": "#0078d4", # 信息蓝:请求已发出,等待模型返回
"writing": "#107c10", # 完成绿:已拿到标题,正在写回 Excel
"success": "#107c10",
"error": "#c42b1c",
}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Background worker # Background worker
@@ -1027,7 +1032,7 @@ class AiOutfitPanel(QWidget):
def _on_title_progress(self, completed, total, result): def _on_title_progress(self, completed, total, result):
if self._title_wait_timer.isActive(): if self._title_wait_timer.isActive():
self._title_wait_timer.stop() self._title_wait_timer.stop()
self._set_title_wait_text("写入中 {}/{}".format(completed, total)) self._set_title_wait_text("写入中 {}/{}".format(completed, total), "writing")
row = self._row_to_table.get(result.task.row_index) row = self._row_to_table.get(result.task.row_index)
if row is not None: if row is not None:
@@ -1046,9 +1051,9 @@ class AiOutfitPanel(QWidget):
def _on_title_finished(self, success, fail): def _on_title_finished(self, success, fail):
self._set_title_running(False) self._set_title_running(False)
if fail: if fail:
self._finish_title_wait("完成 {} 条,失败 {}".format(success, fail)) self._finish_title_wait("完成 {} 条,失败 {}".format(success, fail), "error")
else: else:
self._finish_title_wait("完成 {} 条".format(success)) self._finish_title_wait("完成 {} 条".format(success), "success")
msg = "标题生成结束:成功 {},失败 {}".format(success, fail) msg = "标题生成结束:成功 {},失败 {}".format(success, fail)
self._append_log(msg) self._append_log(msg)
self._reload_after_titles() self._reload_after_titles()
@@ -1061,7 +1066,7 @@ class AiOutfitPanel(QWidget):
def _on_title_failed(self, message): def _on_title_failed(self, message):
self._set_title_running(False) self._set_title_running(False)
self._finish_title_wait("生成失败") self._finish_title_wait("生成失败", "error")
self._append_log("标题生成无法开始:" + message) self._append_log("标题生成无法开始:" + message)
QMessageBox.warning(self, "无法开始", message) QMessageBox.warning(self, "无法开始", message)
@@ -1115,13 +1120,15 @@ class AiOutfitPanel(QWidget):
self._format_wait_seconds(elapsed), self._format_wait_seconds(elapsed),
self._format_wait_seconds(self._title_wait_limit_seconds))) self._format_wait_seconds(self._title_wait_limit_seconds)))
def _set_title_wait_text(self, text): def _set_title_wait_text(self, text, tone="waiting"):
self._title_wait_label.setText(str(text)) self._title_wait_label.setText(str(text))
color = _TITLE_WAIT_COLORS.get(tone, _TITLE_WAIT_COLORS["waiting"])
self._title_wait_label.setStyleSheet("color:{};".format(color))
self._title_wait_label.setVisible(bool(text)) self._title_wait_label.setVisible(bool(text))
def _finish_title_wait(self, text): def _finish_title_wait(self, text, tone="success"):
self._title_wait_timer.stop() self._title_wait_timer.stop()
self._set_title_wait_text(text) self._set_title_wait_text(text, tone)
token = self._title_wait_generation token = self._title_wait_generation
QTimer.singleShot(4000, lambda: self._reset_title_wait_label(token)) QTimer.singleShot(4000, lambda: self._reset_title_wait_label(token))
@@ -1130,6 +1137,7 @@ class AiOutfitPanel(QWidget):
return return
self._title_wait_timer.stop() self._title_wait_timer.stop()
self._title_wait_label.clear() self._title_wait_label.clear()
self._title_wait_label.setStyleSheet("")
self._title_wait_label.setVisible(False) self._title_wait_label.setVisible(False)
def _format_wait_seconds(self, seconds): def _format_wait_seconds(self, seconds):
+27
View File
@@ -1616,3 +1616,30 @@
- [x] 计时文案不暗示模型内部完成百分比 - [x] 计时文案不暗示模型内部完成百分比
- [x] 标题生成和图片生成互斥、原有进度/明细行为不回退 - [x] 标题生成和图片生成互斥、原有进度/明细行为不回退
- [x] 验证:`python -m py_compile src/app/widgets/ai_outfit_panel.py tests/test_ai_outfit_panel.py`、`python tests/test_ai_outfit_panel.py`、离屏启动主窗口通过;全套 `python -m unittest discover -s tests` 仅失败于既有未提交 `packaging/default_config/ai_models.json` 含非空 `api_key`,与本任务无关 - [x] 验证:`python -m py_compile src/app/widgets/ai_outfit_panel.py tests/test_ai_outfit_panel.py`、`python tests/test_ai_outfit_panel.py`、离屏启动主窗口通过;全套 `python -m unittest discover -s tests` 仅失败于既有未提交 `packaging/default_config/ai_models.json` 含非空 `api_key`,与本任务无关
### 19.33 标题等待计时状态颜色 — docs/11 §17.10
前置阅读:
- `docs/11-ai-outfit.md`(§17.10)
- `src/app/widgets/ai_outfit_panel.py`(`_title_wait_label`、`_set_title_wait_text`、`_finish_title_wait`)
- `tests/test_ai_outfit_panel.py`(标题等待计时离屏测试)
背景:
标题等待计时 label 已能显示 `等待中 mm:ss / MM:SS`,但纯黑文字在左栏里容易被标题和提示词区域淹没。需要用低干扰状态色提高可见性,但不能做成真实进度或强警告。
任务:
- [x] `等待中` 使用信息蓝 `#0078d4`
- [x] `写入中` 和纯成功完成使用完成绿 `#107c10`
- [x] `生成失败`、`等待超时`、部分失败完成使用错误红 `#c42b1c`
- [x] 只改变文字颜色,不增加背景色、闪烁或字号变化
- [x] 测试:离屏断言等待、写入、成功、失败状态使用对应颜色
验收:
- [x] 长等待时 label 更醒目但不喧宾夺主
- [x] 成功/失败状态一眼可辨
- [x] 不影响原有等待计时、写回进度和复位行为
- [x] 验证:`python -m py_compile src/app/widgets/ai_outfit_panel.py tests/test_ai_outfit_panel.py`、`python tests/test_ai_outfit_panel.py`、离屏启动主窗口通过;全套 `python -m unittest discover -s tests` 仅失败于既有未提交 `packaging/default_config/ai_models.json` 含非空 `api_key`,与本任务无关
+14
View File
@@ -131,8 +131,10 @@ class TestAiOutfitPanelDefaults(unittest.TestCase):
self.assertFalse(panel._title_wait_label.isHidden()) self.assertFalse(panel._title_wait_label.isHidden())
self.assertEqual(panel._title_wait_label.text(), "等待中 01:35 / 10:00") self.assertEqual(panel._title_wait_label.text(), "等待中 01:35 / 10:00")
self.assertIn("#0078d4", panel._title_wait_label.styleSheet())
self.assertTrue(panel._title_wait_timer.isActive()) self.assertTrue(panel._title_wait_timer.isActive())
panel._reset_title_wait_label() panel._reset_title_wait_label()
self.assertEqual(panel._title_wait_label.styleSheet(), "")
def test_title_wait_limit_uses_model_timeout_override(self): def test_title_wait_limit_uses_model_timeout_override(self):
from services.ai_image_service import AiModelConfig from services.ai_image_service import AiModelConfig
@@ -162,6 +164,18 @@ class TestAiOutfitPanelDefaults(unittest.TestCase):
self.assertFalse(panel._title_wait_timer.isActive()) self.assertFalse(panel._title_wait_timer.isActive())
self.assertFalse(panel._title_wait_label.isHidden()) self.assertFalse(panel._title_wait_label.isHidden())
self.assertEqual(panel._title_wait_label.text(), "写入中 1/3") self.assertEqual(panel._title_wait_label.text(), "写入中 1/3")
self.assertIn("#107c10", panel._title_wait_label.styleSheet())
panel._reset_title_wait_label()
def test_title_wait_finish_uses_success_and_error_colors(self):
panel = self._panel()
panel._finish_title_wait("完成 3 条", "success")
self.assertFalse(panel._title_wait_label.isHidden())
self.assertIn("#107c10", panel._title_wait_label.styleSheet())
panel._finish_title_wait("生成失败", "error")
self.assertIn("#c42b1c", panel._title_wait_label.styleSheet())
panel._reset_title_wait_label() panel._reset_title_wait_label()
def test_prompt_buttons_save_row_insert_title_and_preview(self): def test_prompt_buttons_save_row_insert_title_and_preview(self):