feat(ai-studio): export final selections

This commit is contained in:
chengma
2026-07-11 14:26:01 +08:00
parent 5754e4831a
commit cddadf6364
9 changed files with 539 additions and 8 deletions
+110 -1
View File
@@ -7,12 +7,13 @@ import os
from PySide6.QtCore import QMimeData
from PySide6.QtWidgets import QListWidget, QListWidgetItem
from ... import accounts, appconfig, db, image_studio, prompts
from ... import accounts, appconfig, db, image_studio, image_studio_export, prompts
from .. import file_manager
from ..widgets import *
from ..workers import (
ImageStudioDownloadOriginalWorker as _RealImageStudioDownloadOriginalWorker,
)
from ..workers import ImageStudioExportWorker as _RealImageStudioExportWorker
from ..workers import ImageStudioGenerateJobsWorker as _RealImageStudioGenerateJobsWorker
from ..workers import ImageStudioPullImagesWorker as _RealImageStudioPullImagesWorker
@@ -47,6 +48,15 @@ def ImageStudioGenerateJobsWorker(*args, **kwargs):
)
def ImageStudioExportWorker(*args, **kwargs):
return _call_package_attr(
"ImageStudioExportWorker",
_RealImageStudioExportWorker,
*args,
**kwargs,
)
def _drop_event_position(event):
if hasattr(event, "position"):
return event.position().toPoint()
@@ -469,8 +479,20 @@ class ImageStudioTab(QWidget):
self.detail_selection_list.setMinimumHeight(96)
detail_layout.addWidget(self.detail_selection_label)
detail_layout.addWidget(self.detail_selection_list)
action_panel = QWidget()
action_layout = QVBoxLayout(action_panel)
action_layout.setContentsMargins(0, 0, 0, 0)
self.export_button = QPushButton("导出终选")
self.export_button.setObjectName("imageStudioExportButton")
self.export_hint_label = QLabel("可部分导出,不要求主图/详情图满额")
self.export_hint_label.setObjectName("imageStudioExportHintLabel")
self.export_hint_label.setWordWrap(True)
action_layout.addWidget(self.export_button)
action_layout.addWidget(self.export_hint_label)
action_layout.addStretch(1)
layout.addWidget(main_panel, 1)
layout.addWidget(detail_panel, 1)
layout.addWidget(action_panel, 0)
return panel
def _connect_signals(self):
@@ -491,6 +513,7 @@ class ImageStudioTab(QWidget):
self.prompt_edit.textChanged.connect(self._save_project_prompt)
self.start_button.clicked.connect(self.start_generation)
self.stop_button.clicked.connect(self.stop_generation)
self.export_button.clicked.connect(self.export_selections)
def refresh_accounts(self):
self.account_combo.clear()
@@ -1032,6 +1055,91 @@ class ImageStudioTab(QWidget):
self._append_log("[AI工场] 已请求停止,正在等待安全边界")
self._status("AI工场生成已请求停止", "warning")
def export_selections(self, checked=False):
if self.current_project is None:
self._message("项目未打开", "请先打开一个AI工场项目。")
return
main_count = len(self._selection_asset_ids("main"))
detail_count = len(self._selection_asset_ids("detail"))
if main_count + detail_count <= 0:
self._message("没有可导出图片", "主图和详情图终选都为空。")
return
parent_dir = QFileDialog.getExistingDirectory(self, "选择导出父目录")
if not parent_dir:
return
mode = self._export_existing_mode(parent_dir, main_count, detail_count)
if mode is None:
return
worker = ImageStudioExportWorker(
self.current_project.id,
parent_dir,
existing_mode=mode,
db_path=self.db_path,
config=self.config,
)
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工场导出终选")
def _export_existing_mode(self, parent_dir, main_count, detail_count):
try:
target = image_studio_export.target_dir_for_project(self.current_project, parent_dir)
except Exception as exc:
self._message("导出目录不可用", str(exc))
return None
if not os.path.exists(target):
return image_studio_export.EXISTING_FAIL
box = QMessageBox(self)
box.setWindowTitle("商品目录已存在")
box.setText(
f"目标目录已存在:{target}\n"
f"本次将导出主图 {main_count} 张、详情图 {detail_count} 张。请选择处理方式。"
)
overwrite_button = box.addButton("覆盖本软件导出的图片", QMessageBox.AcceptRole)
timestamp_button = box.addButton("新建带时间目录", QMessageBox.ActionRole)
box.addButton("取消", QMessageBox.RejectRole)
box.setDefaultButton(timestamp_button)
box.exec()
clicked = box.clickedButton()
if clicked is overwrite_button:
return image_studio_export.EXISTING_OVERWRITE_MANAGED
if clicked is timestamp_button:
return image_studio_export.EXISTING_TIMESTAMP
return None
def _on_export_finished(self, summary):
if summary.get("ok") is False:
if self._running_worker is not None:
self._on_worker_failed(-1, summary.get("error") or "导出终选失败")
return
self._finish_worker()
target_dir = summary.get("target_dir") or ""
main_count = int(summary.get("main_count") or 0)
detail_count = int(summary.get("detail_count") or 0)
file_count = int(summary.get("file_count") or 0)
self._append_log(f"[AI工场] 导出完成:主图{main_count},详情图{detail_count},文件{file_count}")
self._status(f"AI工场终选已导出:{file_count} 个文件", "success")
self._show_export_success(target_dir, main_count, detail_count, file_count)
def _show_export_success(self, target_dir, main_count, detail_count, file_count):
box = QMessageBox(self)
box.setWindowTitle("导出完成")
box.setText(
f"已导出 {file_count} 个 JPEG 文件。\n"
f"主图 {main_count} 张,详情图 {detail_count} 张。\n"
f"目录:{target_dir}"
)
open_button = box.addButton("打开目录", QMessageBox.AcceptRole)
box.addButton("确定", QMessageBox.RejectRole)
box.setDefaultButton(open_button)
box.exec()
if box.clickedButton() is open_button:
try:
file_manager.open_in_file_manager(target_dir)
except Exception as exc:
self._message("打开目录失败", str(exc))
def _on_generate_progress(self, payload):
total = max(1, int(payload.get("total") or self.progress_bar.maximum() or 1))
done = min(total, int(payload.get("done") or 0))
@@ -1096,6 +1204,7 @@ class ImageStudioTab(QWidget):
self.aspect_combo.setEnabled(not running)
self.start_button.setEnabled(not running)
self.stop_button.setEnabled(running)
self.export_button.setEnabled(not running)
def _set_account_combo(self, alias):
index = self.account_combo.findData(alias)