diff --git a/app/gui/models.py b/app/gui/models.py index 528c1ef..cd2af92 100644 --- a/app/gui/models.py +++ b/app/gui/models.py @@ -330,7 +330,7 @@ class GenerateTaskTableModel(QAbstractTableModel): def _cover_reset_tooltip(self, task): count = self._cover_reset_count(task) - return f"该商品封面已重置 {count} 次,可能有多张候选图,双击可查看封面画廊" + return f"该商品封面已重置 {count} 次,可能有多张候选图,左键双击可查看封面画廊" def _has_new_title(self, task): return bool(str(getattr(task, "new_title", "") or "").strip()) diff --git a/app/gui/tabs/generate.py b/app/gui/tabs/generate.py index ca7c93a..ccf782e 100644 --- a/app/gui/tabs/generate.py +++ b/app/gui/tabs/generate.py @@ -2,7 +2,7 @@ from __future__ import annotations -from PySide6.QtCore import QEvent, QRectF, QSize +from PySide6.QtCore import QEvent, QModelIndex, QRectF, QSize, Signal from PySide6.QtGui import QColor, QPainter from ... import product_status @@ -16,6 +16,20 @@ def GenerateWorker(*args, **kwargs): return _call_package_attr("GenerateWorker", _RealGenerateWorker, *args, **kwargs) +class _GenerateTaskTableView(QTableView): + """Task table that exposes only left-button double-clicks for previews.""" + + leftDoubleClicked = Signal(QModelIndex) + + def mouseDoubleClickEvent(self, event): + super().mouseDoubleClickEvent(event) + if event.button() != Qt.LeftButton: + return + index = self.indexAt(event.position().toPoint()) + if index.isValid(): + self.leftDoubleClicked.emit(index) + + class _CoverThumbnailLabel(QLabel): def __init__(self, image_path, open_callback, parent=None): super().__init__(parent) @@ -1158,7 +1172,7 @@ class GenerateTab(QWidget): ) = _build_empty_state_card("generateEmptyStateCard") if self.open_accounts_callback is not None: self.empty_state_button.clicked.connect(self.open_accounts_callback) - self.task_table = QTableView() + self.task_table = _GenerateTaskTableView() self.model = GenerateTaskTableModel(self.task_table, db_path=self.db_path, status_callback=self._set_status) self.model.set_generate_mode(self._current_generate_mode()) self.task_table.setModel(self.model) @@ -1249,7 +1263,7 @@ class GenerateTab(QWidget): self.generate_button.clicked.connect(self.start_generate) self.stop_generate_button.clicked.connect(self.stop_generate) self.reset_generate_button.clicked.connect(self.reset_generated_result) - self.task_table.doubleClicked.connect(self.show_task_images) + self.task_table.leftDoubleClicked.connect(self.show_task_images) self.refresh_title_templates() self.refresh_cover_templates() diff --git a/docs/tasks/T-668.md b/docs/tasks/T-668.md index 420c6cb..6ecb039 100644 --- a/docs/tasks/T-668.md +++ b/docs/tasks/T-668.md @@ -1,7 +1,7 @@ --- id: T-668 title: AI生成图片画廊仅响应左键双击 -status: TODO +status: DONE phase: 7 deps: [] created: 2026-07-20 @@ -22,10 +22,10 @@ created: 2026-07-20 ## 验收标准 -- [ ] 左键双击②任务列表的非新标题编辑列时,仍打开当前行的封面图片画廊。 -- [ ] 右键双击同一行或空白区域时,不打开图片画廊、不保存或重置任何数据。 -- [ ] 新标题编辑列继续不打开图片画廊;单击选中、键盘上下切换、封面候选图保存及③更新蝦皮不回归。 -- [ ] `tests/test_gui.py` 覆盖左键双击会调用预览、右键双击不会调用预览。 +- [x] 左键双击②任务列表的非新标题编辑列时,仍打开当前行的封面图片画廊。 +- [x] 右键双击同一行或空白区域时,不打开图片画廊、不保存或重置任何数据。 +- [x] 新标题编辑列继续不打开图片画廊;单击选中、键盘上下切换、封面候选图保存及③更新蝦皮不回归。 +- [x] `tests/test_gui.py` 覆盖左键双击会调用预览、右键双击不会调用预览。 ## 验证 @@ -43,4 +43,7 @@ git diff --check ## 执行记录 -- 待实现。 +- 新增②专用 `_GenerateTaskTableView`,只为 `Qt.LeftButton` 双击发出 `leftDoubleClicked`;图片画廊改接该信号,移除通用 `doubleClicked` 绑定。 +- 保留 Qt 默认双击处理,因此新标题列的既有双击编辑不受影响;右键双击不再触发画廊。封面候选提示统一为「左键双击」。 +- 新增 GUI 回归测试,验证右键双击不打开画廊、左键双击仍打开。 +- 验证通过:`py -3.10 -m unittest discover -s tests`(620 项)、`py -3.10 -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check`。 diff --git a/tests/test_gui.py b/tests/test_gui.py index 56861bc..9ee7dae 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -3463,6 +3463,54 @@ class GuiTests(TempDirMixin, unittest.TestCase): self.assert_removed(temp_dir) + def test_generate_table_emits_preview_only_for_left_double_click(self): + with self.make_temp_dir() as temp_dir: + cfg = self.make_config(temp_dir) + db.init_db(cfg["db_path"]) + batch_id = db.create_batch(["input.xlsx"], path=cfg["db_path"]) + db.insert_tasks( + batch_id, + [ + { + "source_file_abs": os.path.join(temp_dir, "input.xlsx"), + "source_sheet": "商品", + "source_row": 2, + "account_name": "主店", + "alias": "alias-a", + "item_id": "51100639510", + } + ], + path=cfg["db_path"], + ) + tab = GenerateTab(config=cfg) + self.addCleanup(tab.close) + tab.resize(1000, 600) + tab.show() + QApplication.processEvents() + index = tab.model.index(0, 0) + rect = tab.task_table.visualRect(index) + self.assertTrue(rect.isValid()) + previewed_rows = [] + tab.task_table.leftDoubleClicked.connect( + lambda clicked_index: previewed_rows.append(clicked_index.row()) + ) + + with mock.patch( + "app.gui.tabs.generate.CoverGalleryDialog.exec", + return_value=0, + ) as execute: + QTest.mouseDClick(tab.task_table.viewport(), Qt.RightButton, Qt.NoModifier, rect.center()) + QApplication.processEvents() + self.assertEqual([], previewed_rows) + execute.assert_not_called() + + QTest.mouseDClick(tab.task_table.viewport(), Qt.LeftButton, Qt.NoModifier, rect.center()) + QApplication.processEvents() + self.assertEqual([0], previewed_rows) + execute.assert_called_once() + + self.assert_removed(temp_dir) + def test_generate_tab_opens_current_row_image_directory(self): with self.make_temp_dir() as temp_dir: cfg = self.make_config(temp_dir)