T-573 修复封面画廊候选图宽度
This commit is contained in:
+45
-10
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import QEvent
|
||||
from PySide6.QtCore import QEvent, QSize
|
||||
|
||||
from ..models import GenerateTaskTableModel
|
||||
from ..widgets import *
|
||||
@@ -188,13 +188,14 @@ class CoverGalleryDialog(QDialog):
|
||||
self.status_label.setWordWrap(True)
|
||||
layout.addWidget(self.status_label)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setObjectName("coverGalleryScrollArea")
|
||||
scroll.setWidgetResizable(False)
|
||||
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
content = QWidget()
|
||||
self.candidate_layout = QHBoxLayout(content)
|
||||
self.candidate_scroll = QScrollArea()
|
||||
self.candidate_scroll.setObjectName("coverGalleryScrollArea")
|
||||
self.candidate_scroll.setWidgetResizable(False)
|
||||
self.candidate_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
self.candidate_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.candidate_content = QWidget()
|
||||
self.candidate_content.setObjectName("coverGalleryCandidateContent")
|
||||
self.candidate_layout = QHBoxLayout(self.candidate_content)
|
||||
self.candidate_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.candidate_layout.setSpacing(12)
|
||||
if self.candidates:
|
||||
@@ -207,8 +208,9 @@ class CoverGalleryDialog(QDialog):
|
||||
empty_label.setMinimumSize(320, self.THUMBNAIL_SIZE)
|
||||
empty_label.setAlignment(Qt.AlignCenter)
|
||||
self.candidate_layout.addWidget(empty_label)
|
||||
scroll.setWidget(content)
|
||||
layout.addWidget(scroll, 1)
|
||||
self._sync_candidate_content_size()
|
||||
self.candidate_scroll.setWidget(self.candidate_content)
|
||||
layout.addWidget(self.candidate_scroll, 1)
|
||||
return panel
|
||||
|
||||
def _candidate_item(self, candidate_path, index):
|
||||
@@ -252,6 +254,38 @@ class CoverGalleryDialog(QDialog):
|
||||
layout.addStretch(1)
|
||||
return item
|
||||
|
||||
def _sync_candidate_content_size(self):
|
||||
layout = getattr(self, "candidate_layout", None)
|
||||
content = getattr(self, "candidate_content", None)
|
||||
if layout is None or content is None:
|
||||
return
|
||||
layout.activate()
|
||||
margins = layout.contentsMargins()
|
||||
spacing = max(0, layout.spacing())
|
||||
hint = layout.sizeHint()
|
||||
if self.candidates:
|
||||
candidate_count = len(self.candidates)
|
||||
min_width = (
|
||||
margins.left()
|
||||
+ margins.right()
|
||||
+ candidate_count * (self.THUMBNAIL_SIZE + 16)
|
||||
+ max(0, candidate_count - 1) * spacing
|
||||
)
|
||||
else:
|
||||
min_width = margins.left() + margins.right() + 320
|
||||
min_height = max(
|
||||
self.THUMBNAIL_SIZE,
|
||||
hint.height(),
|
||||
content.minimumHeight(),
|
||||
)
|
||||
size = QSize(max(min_width, hint.width()), min_height)
|
||||
content.setMinimumSize(size)
|
||||
content.resize(size)
|
||||
content.updateGeometry()
|
||||
scroll = getattr(self, "candidate_scroll", None)
|
||||
if scroll is not None:
|
||||
scroll.updateGeometry()
|
||||
|
||||
def _sync_initial_selection(self):
|
||||
for candidate_path, radio in self.candidate_buttons.items():
|
||||
if _same_file(candidate_path, self.current_path):
|
||||
@@ -560,6 +594,7 @@ class CoverGalleryDialog(QDialog):
|
||||
empty_label.setMinimumSize(320, self.THUMBNAIL_SIZE)
|
||||
empty_label.setAlignment(Qt.AlignCenter)
|
||||
self.candidate_layout.addWidget(empty_label)
|
||||
self._sync_candidate_content_size()
|
||||
self._install_navigation_event_filters()
|
||||
|
||||
def _on_regenerate_progress(self, payload):
|
||||
|
||||
+10
-2
@@ -3,7 +3,7 @@ id: T-573
|
||||
title: ②封面画廊候选图横向滚动内容宽度修复
|
||||
phase: 7
|
||||
deps: [T-567, T-569]
|
||||
status: TODO
|
||||
status: DONE
|
||||
created: 2026-07-09
|
||||
---
|
||||
|
||||
@@ -65,4 +65,12 @@ T-568/T-569 后封面画廊支持切换任务和重新生成,候选图列表
|
||||
|
||||
## 执行记录
|
||||
|
||||
(做完在这里写:改了什么文件、跑了什么验证命令及结果、关键决策。)
|
||||
- 2026-07-09:已完成。
|
||||
- `app/gui/tabs/generate.py`:`CoverGalleryDialog` 保存 `candidate_scroll` / `candidate_content` 引用,新增 `_sync_candidate_content_size()`;候选图初始化和 `_rebuild_candidate_items()` 后按候选数量、item 宽度、spacing 与 layout sizeHint 同步 content 最小尺寸和实际尺寸,保留横向滚动行为。
|
||||
- `tests/test_gui.py`:新增多候选图内容宽度测试,以及从少候选任务切换到多候选任务后的宽度重新同步测试。
|
||||
- 验证通过:
|
||||
- `py -3.10 -m unittest tests.test_gui`
|
||||
- `python -m ruff check app tests main.py`
|
||||
- `py -3.10 -m compileall app main.py`
|
||||
- `py -3.10 -m unittest discover -s tests`
|
||||
- `git diff --check`
|
||||
|
||||
@@ -2277,6 +2277,60 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_cover_gallery_candidate_content_width_fits_multiple_candidates(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg, account, tasks, canonicals, _archives = self._cover_gallery_task_set(temp_dir, count=1)
|
||||
prefix = os.path.splitext(canonicals[0])[0]
|
||||
self.write_test_image(f"{prefix}_20260709101100.jpg")
|
||||
self.write_test_image(f"{prefix}_20260709101200.jpg")
|
||||
|
||||
dialog = CoverGalleryDialog(tasks[0], cfg["image_dir"], cfg["db_path"], account=account)
|
||||
self.addCleanup(dialog.close)
|
||||
|
||||
self.assertGreaterEqual(len(dialog.candidates), 4)
|
||||
expected_width = (
|
||||
len(dialog.candidates) * (dialog.THUMBNAIL_SIZE + 16)
|
||||
+ (len(dialog.candidates) - 1) * dialog.candidate_layout.spacing()
|
||||
)
|
||||
self.assertIs(dialog.candidate_scroll.widget(), dialog.candidate_content)
|
||||
self.assertGreaterEqual(dialog.candidate_content.minimumWidth(), expected_width)
|
||||
self.assertGreaterEqual(dialog.candidate_content.width(), expected_width)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_cover_gallery_switch_task_resyncs_candidate_content_width(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg, account, tasks, canonicals, archives = self._cover_gallery_task_set(temp_dir, count=2)
|
||||
os.remove(archives[0])
|
||||
prefix = os.path.splitext(canonicals[1])[0]
|
||||
self.write_test_image(f"{prefix}_20260709101100.jpg")
|
||||
self.write_test_image(f"{prefix}_20260709101200.jpg")
|
||||
dialog = CoverGalleryDialog(
|
||||
tasks[0],
|
||||
cfg["image_dir"],
|
||||
cfg["db_path"],
|
||||
account=account,
|
||||
visible_tasks=tasks,
|
||||
task_index=0,
|
||||
account_by_alias={"alias-a": account},
|
||||
)
|
||||
self.addCleanup(dialog.close)
|
||||
initial_width = dialog.candidate_content.minimumWidth()
|
||||
|
||||
self.assertTrue(dialog.switch_task(1))
|
||||
|
||||
expected_width = (
|
||||
len(dialog.candidates) * (dialog.THUMBNAIL_SIZE + 16)
|
||||
+ (len(dialog.candidates) - 1) * dialog.candidate_layout.spacing()
|
||||
)
|
||||
self.assertEqual(tasks[1].id, dialog.task.id)
|
||||
self.assertGreater(len(dialog.candidates), 2)
|
||||
self.assertGreater(dialog.candidate_content.minimumWidth(), initial_width)
|
||||
self.assertGreaterEqual(dialog.candidate_content.minimumWidth(), expected_width)
|
||||
self.assertGreaterEqual(dialog.candidate_content.width(), expected_width)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_cover_gallery_save_switches_current_cover(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
|
||||
Reference in New Issue
Block a user