feat(ai-studio): streamline final selection rails

This commit is contained in:
chengma
2026-07-11 17:46:58 +08:00
parent dd88d12cf4
commit c46fe0e543
3 changed files with 161 additions and 37 deletions
+92 -31
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
import os
from PySide6.QtCore import QObject, QMimeData, QSize, Signal
from PySide6.QtWidgets import QListView, QListWidget, QListWidgetItem
from PySide6.QtWidgets import QListView, QListWidget, QListWidgetItem, QSizePolicy
from ... import accounts, appconfig, cmhub_models, db, image_studio, image_studio_export, image_studio_images, prompts
from .. import file_manager
@@ -136,6 +136,16 @@ class ImageStudioSelectionList(QListWidget):
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.setViewMode(QListView.IconMode)
self.setFlow(QListView.LeftToRight)
self.setWrapping(False)
self.setResizeMode(QListView.Fixed)
self.setMovement(QListView.Static)
self.setSpacing(4)
self.setUniformItemSizes(True)
self.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.customContextMenuRequested.connect(self._show_context_menu)
def mimeData(self, items):
@@ -332,7 +342,8 @@ class ImageStudioTab(QWidget):
self.main_splitter.setStretchFactor(2, 2)
self.main_splitter.setSizes([150, 640, 360])
root.addWidget(self.main_splitter, 1)
root.addWidget(self._build_final_panel(), 0)
self.final_panel = self._build_final_panel()
root.addWidget(self.final_panel, 0)
self._apply_workspace_style()
self._update_generation_action_text()
@@ -579,51 +590,68 @@ class ImageStudioTab(QWidget):
def _build_final_panel(self):
panel = QWidget()
panel.setObjectName("imageStudioFinalPanel")
layout = QHBoxLayout(panel)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(10)
main_panel = QWidget()
main_layout = QVBoxLayout(main_panel)
main_layout.setContentsMargins(0, 0, 0, 0)
panel.setMinimumHeight(150)
panel.setMaximumHeight(170)
panel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
layout = QGridLayout(panel)
layout.setContentsMargins(10, 8, 10, 8)
layout.setHorizontalSpacing(8)
layout.setVerticalSpacing(4)
header_layout = QHBoxLayout()
final_title = QLabel("终选与排序")
final_title.setObjectName("imageStudioSectionTitle")
main_layout.addWidget(final_title)
header_hint = QLabel("从照片池拖入,可在同一行拖动排序,双击查看大图")
header_hint.setObjectName("imageStudioMutedLabel")
header_layout.addWidget(final_title)
header_layout.addWidget(header_hint)
header_layout.addStretch(1)
layout.addLayout(header_layout, 0, 0, 1, 2)
self.main_selection_label = QLabel("主图终选 0/9")
self.main_selection_label.setObjectName("imageStudioMainSelectionLabel")
self.main_selection_label.setFixedWidth(64)
self.main_selection_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.main_selection_list = ImageStudioSelectionList("main", self)
self.main_selection_list.setObjectName("imageStudioMainSelectionList")
self.main_selection_list.setIconSize(QSize(48, 48))
self.main_selection_list.setMinimumHeight(96)
main_layout.addWidget(self.main_selection_label)
main_layout.addWidget(self.main_selection_list)
self.main_selection_list.setGridSize(QSize(60, 62))
self.main_selection_list.setFixedHeight(66)
layout.addWidget(self.main_selection_label, 1, 0)
layout.addWidget(self.main_selection_list, 1, 1)
detail_panel = QWidget()
detail_layout = QVBoxLayout(detail_panel)
detail_layout.setContentsMargins(0, 0, 0, 0)
detail_layout.addWidget(QLabel(" "))
self.detail_selection_label = QLabel("详情图终选 0/12")
self.detail_selection_label.setObjectName("imageStudioDetailSelectionLabel")
self.detail_selection_label.setFixedWidth(64)
self.detail_selection_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.detail_selection_list = ImageStudioSelectionList("detail", self)
self.detail_selection_list.setObjectName("imageStudioDetailSelectionList")
self.detail_selection_list.setIconSize(QSize(42, 42))
self.detail_selection_list.setMinimumHeight(96)
detail_layout.addWidget(self.detail_selection_label)
detail_layout.addWidget(self.detail_selection_list)
self.detail_selection_list.setIconSize(QSize(38, 38))
self.detail_selection_list.setGridSize(QSize(50, 52))
self.detail_selection_list.setFixedHeight(56)
layout.addWidget(self.detail_selection_label, 2, 0)
layout.addWidget(self.detail_selection_list, 2, 1)
action_panel = QWidget()
action_layout = QVBoxLayout(action_panel)
action_layout.setContentsMargins(0, 0, 0, 0)
action_layout.addWidget(QLabel(" "))
action_layout.setSpacing(3)
self.export_summary_label = QLabel("当前未选择终选图片")
self.export_summary_label.setObjectName("imageStudioExportSummaryLabel")
self.export_summary_label.setWordWrap(True)
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_summary_label)
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)
action_layout.addWidget(self.export_button)
action_panel.setMinimumWidth(248)
action_panel.setMaximumWidth(280)
layout.addWidget(action_panel, 1, 2, 2, 1)
layout.setColumnStretch(1, 1)
return panel
def _apply_workspace_style(self):
@@ -652,6 +680,18 @@ class ImageStudioTab(QWidget):
#imageStudioExportHintLabel {
color: #6e7781;
}
#imageStudioMainSelectionLabel {
color: #0969da;
font-weight: 600;
}
#imageStudioDetailSelectionLabel {
color: #1a7f37;
font-weight: 600;
}
#imageStudioExportSummaryLabel {
color: #1a7f37;
font-weight: 600;
}
#imageStudioCurrentProjectLabel {
color: #24292f;
font-weight: 600;
@@ -686,6 +726,17 @@ class ImageStudioTab(QWidget):
selection-background-color: #ddf4ff;
selection-color: #24292f;
}
QListWidget#imageStudioMainSelectionList::item,
QListWidget#imageStudioDetailSelectionList::item {
border: 1px dashed #bfc8d3;
border-radius: 4px;
padding: 0;
}
QListWidget#imageStudioMainSelectionList::item:selected,
QListWidget#imageStudioDetailSelectionList::item:selected {
border: 2px solid #0969da;
background: #eaf2ff;
}
"""
)
@@ -1979,6 +2030,13 @@ class ImageStudioTab(QWidget):
detail_target = getattr(self.current_project, "target_detail_count", 12) if self.current_project else 12
self.main_selection_label.setText(f"主图终选 {main_count}/{main_target}")
self.detail_selection_label.setText(f"详情图终选 {detail_count}/{detail_target}")
total_count = main_count + detail_count
if total_count:
self.export_summary_label.setText(
f"当前已选 {total_count} 张:主图 {main_count}/{main_target} · 详情 {detail_count}/{detail_target}"
)
else:
self.export_summary_label.setText("当前未选择终选图片")
self._fill_selection_list(self.main_selection_list, "main")
self._fill_selection_list(self.detail_selection_list, "detail")
@@ -1988,17 +2046,19 @@ class ImageStudioTab(QWidget):
for index, selection in enumerate(selected, start=1):
asset = self._asset_by_id(selection.asset_id)
if asset is None:
text = f"{index}. 缺失照片 #{selection.asset_id}"
text = f"{index}\n缺失"
item = QListWidgetItem(text)
item.setData(Qt.UserRole, int(selection.asset_id))
item.setToolTip("终选引用的照片记录不存在")
item.setForeground(_qcolor(COLOR_DANGER))
item.setSizeHint(widget.gridSize())
widget.addItem(item)
continue
text = f"{index}. {_asset_badge(asset.kind)} #{asset.id} · {asset.aspect_ratio or '未知比例'}"
text = str(index)
item = QListWidgetItem(text)
item.setIcon(_asset_icon(asset, str(index)))
item.setIcon(_asset_icon(asset, str(index), size=widget.iconSize()))
item.setData(Qt.UserRole, int(asset.id))
item.setSizeHint(widget.gridSize())
tooltip = _selection_tooltip(selection_type, asset)
item.setToolTip(tooltip)
if _selection_ratio_warning(selection_type, asset):
@@ -2010,12 +2070,13 @@ class ImageStudioTab(QWidget):
widget.addItem(item)
target = self._selection_target_count(selection_type)
for index in range(len(selected) + 1, target + 1):
item = QListWidgetItem(f"{index}. 空位")
item.setIcon(QIcon(_placeholder_pixmap("空", QSize(48, 48), "#f6f8fa")))
item = QListWidgetItem("")
item.setIcon(QIcon(_placeholder_pixmap(str(index), widget.iconSize(), "#f6f8fa")))
item.setData(Qt.UserRole, None)
item.setSizeHint(widget.gridSize())
item.setToolTip(f"{_selection_label(selection_type)}空位,可从照片池拖入图片。")
item.setForeground(_qcolor(COLOR_MUTED))
item.setFlags(Qt.NoItemFlags)
item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsDropEnabled)
widget.addItem(item)
def _asset_from_grid_item(self, item):