feat(ai-studio): export final selections
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user