feat(ai-studio): keep workspace usable during generation

This commit is contained in:
chengma
2026-07-13 09:38:30 +08:00
parent 14282306f6
commit 42fe233ed9
4 changed files with 198 additions and 25 deletions
+59 -22
View File
@@ -303,6 +303,7 @@ class ImageStudioTab(QWidget):
self._running_worker = None
self._running_thread = None
self._operation_running = False
self._operation_kind = ""
self._worker_error_handled = False
self._download_open_after = {}
self._thumbnail_loader = thumbnail_loader or image_studio_images.ThumbnailLoader()
@@ -811,6 +812,9 @@ class ImageStudioTab(QWidget):
self.project_table.resizeColumnsToContents()
def pull_main_images(self, checked=False):
if self._operation_running:
self._status("AI工场当前任务仍在进行,暂不能拉取蝦皮主图", "warning")
return
alias = str(self.account_combo.currentData() or "").strip()
item_id = self.item_id_edit.text().strip()
if not alias or not item_id:
@@ -845,7 +849,7 @@ class ImageStudioTab(QWidget):
worker.log.connect(self._append_log)
worker.finished.connect(self._on_pull_finished)
worker.failed.connect(self._on_worker_failed)
self._start_worker(worker, "AI工场拉取蝦皮主图")
self._start_worker(worker, "AI工场拉取蝦皮主图", operation_kind="pull_images")
self._append_log("[AI工场] 拉取蝦皮主图开始")
def _local_original_count(self, project):
@@ -1006,6 +1010,9 @@ class ImageStudioTab(QWidget):
self._status(f"已打开项目文件夹:{opened}", "success")
def delete_current_project(self, checked=False):
if self._operation_running:
self._status("AI工场当前任务仍在进行,暂不能删除项目", "warning")
return
project = self.current_project
if project is None:
self._message("未选择商品", "请先从商品列表选择要删除的项目。")
@@ -1518,10 +1525,13 @@ class ImageStudioTab(QWidget):
self._refresh_project_summary()
def _update_project_action_buttons(self):
enabled = self.current_project is not None and not self._operation_running
self.open_folder_button.setEnabled(enabled)
has_project = self.current_project is not None
self.open_folder_button.setEnabled(
has_project and (not self._operation_running or self._is_generation_operation())
)
self.delete_project_button.setEnabled(
enabled
has_project
and not self._operation_running
and not self._has_original_download_activity(
getattr(self.current_project, "id", None)
)
@@ -1661,6 +1671,9 @@ class ImageStudioTab(QWidget):
self.remove_asset(asset_id)
def remove_asset(self, asset_id):
if self._operation_running:
self._status("AI工场当前任务仍在进行,暂不能移除照片", "warning")
return
if not self._confirm("移除照片", "只从AI工场照片池移除记录,不删除本地图片文件。"):
return
try:
@@ -1767,6 +1780,9 @@ class ImageStudioTab(QWidget):
self._status(f"保存AI工场草稿提示词失败:{exc}", "warning")
def start_generation(self, checked=False):
if self._operation_running:
self._status("AI工场当前任务仍在进行,请等待完成或停止后再生成", "warning")
return
if self.current_project is None:
self._message("未选择商品", "请先从商品列表选择商品,或拉取蝦皮主图。")
return
@@ -1801,7 +1817,7 @@ class ImageStudioTab(QWidget):
worker.log.connect(self._append_log)
worker.finished.connect(self._on_generation_finished)
worker.failed.connect(self._on_worker_failed)
self._start_worker(worker, "AI工场生成图片")
self._start_worker(worker, "AI工场生成图片", operation_kind="generate")
def stop_generation(self, checked=False):
if self._running_worker is not None and hasattr(self._running_worker, "cancel"):
@@ -1810,6 +1826,9 @@ class ImageStudioTab(QWidget):
self._status("AI工场生成已请求停止", "warning")
def resume_generation_jobs(self, checked=False):
if self._operation_running:
self._status("AI工场当前任务仍在进行,请等待完成或停止后再继续查询", "warning")
return
if self.current_project is None:
self._message("未选择商品", "请先从商品列表选择商品,或拉取蝦皮主图。")
return
@@ -1836,9 +1855,12 @@ class ImageStudioTab(QWidget):
worker.log.connect(self._append_log)
worker.finished.connect(self._on_generation_finished)
worker.failed.connect(self._on_worker_failed)
self._start_worker(worker, "AI工场继续查询")
self._start_worker(worker, "AI工场继续查询", operation_kind="resume")
def export_selections(self, checked=False):
if self._operation_running:
self._status("AI工场当前任务仍在进行,暂不能导出终选", "warning")
return
if self.current_project is None:
self._message("未选择商品", "请先从商品列表选择商品,或拉取蝦皮主图。")
return
@@ -1863,7 +1885,7 @@ class ImageStudioTab(QWidget):
worker.log.connect(self._append_log)
worker.finished.connect(self._on_export_finished)
worker.failed.connect(self._on_worker_failed)
self._start_worker(worker, "AI工场导出终选")
self._start_worker(worker, "AI工场导出终选", operation_kind="export")
def _export_existing_mode(self, parent_dir, main_count, detail_count):
try:
@@ -1963,14 +1985,18 @@ class ImageStudioTab(QWidget):
self._on_worker_failed(-1, summary.get("error") or fallback_message)
return True
def _start_worker(self, worker, thread_name):
def _start_worker(self, worker, thread_name, *, operation_kind="exclusive"):
if self._running_thread is not None:
self._status("AI工场上一项任务仍在收尾,请稍候", "warning")
return False
thread = run_worker(worker, thread_name=thread_name, start=False)
thread.finished.connect(lambda: self._forget_running_thread(thread))
self._running_worker = worker
self._running_thread = thread
self._worker_error_handled = False
self._set_running(True)
self._set_running(True, operation_kind=operation_kind)
thread.start()
return True
def _finish_worker(self):
self._set_running(False)
@@ -1980,27 +2006,38 @@ class ImageStudioTab(QWidget):
self._running_thread = None
self._running_worker = None
def _set_running(self, running):
def _is_generation_operation(self):
return self._operation_running and self._operation_kind in {"generate", "resume"}
def _set_running(self, running, *, operation_kind="exclusive"):
self._operation_running = bool(running)
self._operation_kind = str(operation_kind or "exclusive") if running else ""
generation_running = self._is_generation_operation()
self.pull_images_button.setEnabled(not running)
self.account_combo.setEnabled(not running)
self.item_id_edit.setEnabled(not running)
self.project_table.setEnabled(not running)
self.original_grid.setEnabled(not running)
self.pool_grid.setEnabled(not running)
self.main_selection_list.setEnabled(not running)
self.detail_selection_list.setEnabled(not running)
self.template_combo.setEnabled(not running)
self.template_new_button.setEnabled(not running)
self.template_rename_button.setEnabled(not running)
self.template_save_button.setEnabled(not running)
self.template_delete_button.setEnabled(not running)
self.prompt_edit.setEnabled(not running)
self.job_type_combo.setEnabled(not running)
self.count_spin.setEnabled(not running)
self.aspect_combo.setEnabled(not running)
self.pool_grid.setEnabled(not running or generation_running)
self.main_selection_list.setEnabled(not running or generation_running)
self.detail_selection_list.setEnabled(not running or generation_running)
self.template_combo.setEnabled(not running or generation_running)
self.template_new_button.setEnabled(not running or generation_running)
self.template_rename_button.setEnabled(not running or generation_running)
self.template_save_button.setEnabled(not running or generation_running)
self.template_delete_button.setEnabled(not running or generation_running)
self.prompt_edit.setEnabled(not running or generation_running)
self.job_type_combo.setEnabled(not running or generation_running)
self.count_spin.setEnabled(not running or generation_running)
self.aspect_combo.setEnabled(not running or generation_running)
self.start_button.setEnabled(not running)
self.resume_button.setEnabled(not running)
self.stop_button.setEnabled(running)
self.export_button.setEnabled(not running)
if generation_running:
self.generation_guard_label.setText("本轮任务已固定;此处修改将在下一轮生成时生效。")
else:
self.generation_guard_label.setText("不会在失败后静默切换生成来源。")
self._update_project_action_buttons()
def _set_account_combo(self, alias):