--- id: T-626 title: ②封面画廊生成图随窗口自适应显示 phase: 7 deps: [T-574] status: DONE created: 2026-07-13 --- ## 问题 / 背景 ②「AI生成」列表双击任务行后会打开 `CoverGalleryDialog`。当前候选生成图使用固定 `THUMBNAIL_SIZE = 180`,候选项宽度也固定为 `THUMBNAIL_SIZE + 16`;弹窗 `_fit_to_screen()` 只设置一次窗口尺寸,没有在窗口变化后重新计算图片尺寸。因此在大屏幕上图片仍然很小、候选区留白明显,在较小屏幕或调整窗口尺寸后也不会随可用区域变化。 T-573 只修复了候选图横向容器没有按数量撑开导致的遮挡问题,没有实现响应式缩放。 候选图再次双击后打开的 `OriginalImageDialog` 也不是自适应预览:当前按图片原始像素设置 `QLabel` 尺寸,`QScrollArea.setWidgetResizable(False)`,大图只能通过滚动条局部查看。窗口虽然会限制在屏幕可用区域内,但图片本身不会适应窗口。 ## 方案 ### 1. 封面画廊候选图自适应 - 修改 `app/gui/tabs/generate.py` 的 `CoverGalleryDialog`: - 不再把 `180px` 作为所有屏幕和窗口尺寸下唯一的候选图显示尺寸;保留合理最小值,并设置约 `180~360px` 的显示范围。 - 根据候选区 viewport 的实际宽高、候选数量、布局间距及候选说明文字所需高度计算正方形预览尺寸。 - 单张候选图可使用更多空间;多张候选图优先完整展示 2~3 张,更多候选继续使用 T-573 的横向滚动,不压缩到难以辨认,也不互相遮挡。 - 候选 item 宽度、图片 label 尺寸及 `candidate_content` 宽度必须使用同一轮计算结果,避免图片放大后重新出现遮挡或错误滚动范围。 - 覆盖 `resizeEvent()` 或使用等价的窗口级尺寸变化处理,在弹窗放大、缩小后重新计算候选图尺寸。 - `_rebuild_candidate_items()`、上一条/下一条切换、重置图片及候选重新生成后,按当前窗口尺寸重新同步候选布局。 ### 2. 保持图片比例并控制重绘成本 - 使用 `Qt.KeepAspectRatio` + `Qt.SmoothTransformation` 重新生成预览图,不使用会拉伸图片的 `QLabel.setScaledContents(True)`。 - 缓存候选图的原始 `QImage`,窗口连续拖动时不重复从磁盘读取图片。 - 对连续 resize 事件做短时间合并,或只在计算出的目标尺寸发生变化时重绘,避免拖动窗口时卡顿。 - 图片读取失败时仍显示固定区域的中文失败占位,不因自适应逻辑造成布局跳动。 ### 3. 原图窗口默认适应窗口 - 修改 `OriginalImageDialog`: - 默认使用“适应窗口”模式,按图片显示区域等比例缩小或放大预览,让整张生成图在当前窗口内可见。 - 增加“原始尺寸 / 适应窗口”模式切换;原始尺寸模式保留滚动条,便于检查细节。 - 弹窗首次打开、窗口尺寸变化和模式切换时重新计算预览图。 - 窗口标题继续显示文件名和原始分辨率;模式切换按钮、tooltip 等用户可见文字全部使用中文。 ### 4. 测试 - 更新 `tests/test_gui.py`: - 覆盖画廊窗口放大、缩小后候选图显示尺寸随可用空间变化,并保持宽高比例。 - 覆盖单候选、多候选及候选数量超出可视宽度时的横向滚动内容宽度。 - 覆盖切换上一条/下一条和动态重建候选后,图片按当前窗口尺寸重新计算。 - 覆盖 `OriginalImageDialog` 默认适应窗口,以及切换原始尺寸后恢复图片原始像素和滚动行为。 - 覆盖图片读取失败占位状态。 ## 验收要点 - 在常见的 `1366×768`、`1920×1080` 和 Windows 高 DPI 环境中打开封面画廊,候选生成图均完整、清晰且不变形。 - 放大或缩小画廊弹窗时,候选图跟随可用区域合理变化,不保持固定 `180px`,也不超出窗口或互相遮挡。 - 单张候选图能够合理利用可用空间;多张候选图可以比较,超出范围时可横向滚动查看全部候选。 - 切换任务、重置图片或候选列表重建后,当前窗口尺寸和位置保持不变,候选图尺寸正确刷新。 - 双击候选图打开原图窗口时,默认能看到完整图片;切换到“原始尺寸”后可以通过滚动条检查细节,再切回“适应窗口”可恢复完整预览。 - 不影响候选选择、保存提示、未保存确认、重置图片和 `↑ / ↓` 切换任务。 - 验证命令: - `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` ## 边界(不改什么) - 不修改候选图文件发现、归档或排序规则。 - 不修改提示词、AI/cmhub 请求、生图并发或图片保存格式。 - 不修改 SQLite schema、Excel、③更新蝦皮、CDP 或蝦皮网页操作逻辑。 - 不改变保存封面、重置图片、上一条/下一条和未保存选择确认的业务语义。 ## 执行记录 - 2026-07-14:完成 T-626。 - `app/gui/tabs/generate.py`:封面画廊候选图改为按候选区 viewport、候选数量和可用高度在 `180~360px` 内动态计算;候选 item、缩略图和横向内容宽度使用同一尺寸,窗口 resize、任务切换和候选重建后自动同步。 - 候选原始 `QImage` 在当前任务画廊内缓存,连续 resize 使用单次计时器合并重绘;预览继续使用 `Qt.KeepAspectRatio` 和平滑缩放,读取失败时保持中文占位。 - `OriginalImageDialog` 默认按窗口完整显示图片,新增「原始尺寸 / 适应窗口」切换;原始尺寸保留滚动查看,窗口尺寸变化后自动更新预览。 - `tests/test_gui.py`:新增候选图随窗口变化、非方图比例、损坏图片占位、任务切换后响应式尺寸,以及原图双模式和失败占位测试;既有候选内容宽度断言改为使用当前动态尺寸。 - 主工作区存在与本任务无关的默认封面模板未提交改名,导致两项旧模板断言失败;未回退用户改动,在基于 `HEAD` 的干净 worktree 中仅套用 T-626 代码和测试差异完成验证。 - 验证通过:`py -3.10 -m unittest tests.test_gui`(179 tests)、`py -3.10 -m unittest discover -s tests`(444 tests)、`python -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check`。