diff --git a/src/app/widgets/ai_outfit_panel.py b/src/app/widgets/ai_outfit_panel.py index d4cb584..1441adc 100644 --- a/src/app/widgets/ai_outfit_panel.py +++ b/src/app/widgets/ai_outfit_panel.py @@ -146,6 +146,8 @@ class AiOutfitPanel(QWidget): self._worker = None self._row_to_table = {} # excel row_index -> table row self._failures = [] # list of OutfitResult (failed) + self._last_resolution = "" # for "value actually changed" check (§10.2) + self._last_model = "" self._build_ui() # -- construction --------------------------------------------------- @@ -246,6 +248,8 @@ class AiOutfitPanel(QWidget): self._resolution = QComboBox() self._resolution.addItems(_RESOLUTIONS) self._resolution.currentIndexChanged.connect(self._refresh_preview) + # activated = user click only; programmatic sets won't pop (docs/11 §10.2) + self._resolution.activated.connect(self._on_resolution_activated) self._quality = QComboBox() self._quality.addItems(_QUALITIES) @@ -266,6 +270,7 @@ class AiOutfitPanel(QWidget): # AI 模型 下拉(移到生成设置下方) self._model_combo = QComboBox() self._compact_combo(self._model_combo) + self._model_combo.activated.connect(self._on_model_activated) gv.addWidget(self._field("AI 模型", self._model_combo)) return gen @@ -395,6 +400,11 @@ class AiOutfitPanel(QWidget): self._model_combo.addItem(m.get("name") or m.get("model") or "(未命名)") self._set_combo(self._model_combo, config.get("outfit_model", "")) + # Snapshot current dropdown values so a later user re-select of the same + # item doesn't trigger the info popup (§10.2). + self._last_resolution = self._resolution.currentText() + self._last_model = self._model_combo.currentText() if self._models else "" + # Fill the preview's sample-row dropdown from the remembered Excel. self._reload_sample_rows() @@ -483,6 +493,37 @@ class AiOutfitPanel(QWidget): else: self._preview_view.setPlainText(render_prompt(template, task, resolution)) + # -- switch info popups (user-only; §10.2) -------------------------- + + def _on_resolution_activated(self, _index): + res = self._resolution.currentText() + if res == self._last_resolution: + return + self._last_resolution = res + from services.ai_image_service import resolution_timeout + QMessageBox.information( + self, "分辨率已切换", + "已切换分辨率到 {}。\n\n" + "· 单任务超时约 {} 秒,分辨率越高越慢。\n" + "· 仅在下次「开始生成」生效,不影响正在进行的批次。".format( + res, resolution_timeout(res))) + + def _on_model_activated(self, _index): + name = self._model_combo.currentText() + if name == self._last_model: + return + self._last_model = name + api_type = "auto" + idx = self._model_combo.currentIndex() + if self._models and 0 <= idx < len(self._models): + api_type = self._models[idx].get("api_type", "auto") or "auto" + QMessageBox.information( + self, "AI 模型已切换", + "已切换模型到 {}。\n\n" + "· 调用方式:{}。\n" + "· 不同模型的计费与效果可能不同。\n" + "· 仅在下次「开始生成」生效。".format(name, api_type)) + # -- run control ---------------------------------------------------- def _start(self): diff --git a/tasks.md b/tasks.md index ad31230..4db4117 100644 --- a/tasks.md +++ b/tasks.md @@ -1098,7 +1098,7 @@ 需求:用户手动切换「分辨率」或「AI 模型」下拉时弹信息框告知影响(**只告知、不拦截、不还原**)。决策:每次实际更换都弹一次。 -- [ ] `app/widgets/ai_outfit_panel.py`:给 `_resolution` / `_model_combo` 接 **`activated`** 信号(非 `currentIndexChanged`,避免启动/载配置误弹);仅值实际改变时 `QMessageBox.information`,保持新选项、不回退 -- [ ] 分辨率文案带 `ai_image_service.resolution_timeout` 的超时秒数(512/1K/2K/4K→180/240/360/600)+「下次生效」 -- [ ] AI 模型文案带该模型 `api_type` + 计费/效果可能不同 +「下次生效」 -- [ ] 离屏验证:程序化赋值(`apply_config`/`_set_combo`/`_fill_sample_combo`)不弹;用户切换才弹 +- [x] `app/widgets/ai_outfit_panel.py`:给 `_resolution` / `_model_combo` 接 **`activated`** 信号(非 `currentIndexChanged`,避免启动/载配置误弹);仅值实际改变时 `QMessageBox.information`,保持新选项、不回退 +- [x] 分辨率文案带 `ai_image_service.resolution_timeout` 的超时秒数(512/1K/2K/4K→180/240/360/600)+「下次生效」 +- [x] AI 模型文案带该模型 `api_type` + 计费/效果可能不同 +「下次生效」 +- [x] 离屏验证:程序化赋值(`apply_config`/`_fill_sample_combo`)不弹(0 次);用户 1K→4K 弹 1 次(含 600s)、同项重选不弹、换模型弹(含 api_type)