T-581 AI生成打开图片文件夹
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
"""Helpers for opening local folders from the GUI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def open_in_file_manager(path: str) -> str:
|
||||
"""Open an existing directory with the platform file manager.
|
||||
|
||||
Returns the absolute path that was opened. The helper never creates
|
||||
directories; callers should present FileNotFoundError as a user-facing
|
||||
warning.
|
||||
"""
|
||||
|
||||
target = os.path.abspath(str(path or ""))
|
||||
if not os.path.isdir(target):
|
||||
raise FileNotFoundError(f"目录不存在:{target}")
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
startfile = getattr(os, "startfile", None)
|
||||
if startfile is None:
|
||||
raise OSError("当前系统不支持打开文件夹")
|
||||
startfile(target)
|
||||
return target
|
||||
|
||||
command = ["open", target] if sys.platform == "darwin" else ["xdg-open", target]
|
||||
subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
shell=False,
|
||||
)
|
||||
return target
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
from PySide6.QtCore import QEvent, QRectF, QSize
|
||||
from PySide6.QtGui import QColor, QPainter
|
||||
|
||||
from .. import file_manager
|
||||
from ..models import GenerateTaskTableModel
|
||||
from ..widgets import *
|
||||
from ..workers import GenerateWorker as _RealGenerateWorker
|
||||
@@ -882,6 +883,11 @@ class GenerateTab(QWidget):
|
||||
for label, value in self.STATUS_FILTERS:
|
||||
self.status_filter.addItem(label, value)
|
||||
self.refresh_button = QPushButton("刷新")
|
||||
self.open_image_dir_button = QPushButton("打开图片文件夹")
|
||||
self.open_image_dir_button.setObjectName("openImageDirButton")
|
||||
self.open_image_dir_button.setToolTip(
|
||||
"选中某行→打开该账号图片文件夹;未选中→打开当前批次文件夹;全部批次→打开图片根目录"
|
||||
)
|
||||
|
||||
filter_layout = QHBoxLayout()
|
||||
filter_layout.addWidget(QLabel("批次"))
|
||||
@@ -893,6 +899,7 @@ class GenerateTab(QWidget):
|
||||
filter_layout.addWidget(QLabel("状态"))
|
||||
filter_layout.addWidget(self.status_filter, 1)
|
||||
filter_layout.addWidget(self.refresh_button)
|
||||
filter_layout.addWidget(self.open_image_dir_button)
|
||||
|
||||
self.summary_label = QLabel("任务 0 条")
|
||||
self.batch_progress_label = _build_batch_progress_overview("generateBatchProgressOverview")
|
||||
@@ -973,6 +980,7 @@ class GenerateTab(QWidget):
|
||||
self.item_filter.textChanged.connect(self.refresh_tasks)
|
||||
self.status_filter.currentIndexChanged.connect(self.refresh_tasks)
|
||||
self.refresh_button.clicked.connect(self.refresh_tasks)
|
||||
self.open_image_dir_button.clicked.connect(self.open_image_directory)
|
||||
self.save_title_button.clicked.connect(self.save_title_prompt)
|
||||
self.insert_old_title_button.clicked.connect(self.insert_old_title_placeholder)
|
||||
self.cover_template_combo.currentIndexChanged.connect(self.load_selected_cover_template)
|
||||
@@ -1824,6 +1832,84 @@ class GenerateTab(QWidget):
|
||||
failed=payload.get("failed", 0),
|
||||
)
|
||||
|
||||
def open_image_directory(self):
|
||||
target_dir, missing_message = self._resolve_image_directory_target()
|
||||
if not target_dir:
|
||||
message = missing_message or "无法定位图片文件夹"
|
||||
QMessageBox.warning(self, "打开图片文件夹", message)
|
||||
self._set_status(message, level="warning")
|
||||
return
|
||||
if not os.path.isdir(target_dir):
|
||||
message = missing_message or "该批次/账号还没有图片"
|
||||
QMessageBox.warning(self, "打开图片文件夹", message)
|
||||
self._set_status(message, level="warning")
|
||||
return
|
||||
try:
|
||||
opened_dir = file_manager.open_in_file_manager(target_dir)
|
||||
except Exception as exc:
|
||||
message = f"打开图片文件夹失败:{exc}"
|
||||
QMessageBox.warning(self, "打开图片文件夹", message)
|
||||
self._set_status(message, level="danger")
|
||||
return
|
||||
self._set_status(f"已打开图片文件夹:{opened_dir}", level="success")
|
||||
|
||||
def _resolve_image_directory_target(self):
|
||||
image_root = appconfig.image_dir(self.config)
|
||||
row = self._selected_row_for_image_directory()
|
||||
if row is not None:
|
||||
task = self.model.task_at(row)
|
||||
if task is None:
|
||||
return None, "没有选中可打开图片文件夹的任务"
|
||||
existing_dir = self._existing_cover_parent_dir(task)
|
||||
if existing_dir:
|
||||
return existing_dir, "该账号还没有图片"
|
||||
account = self._account_for_task(task)
|
||||
if account is None:
|
||||
return None, "选中任务没有匹配账号,无法定位图片文件夹"
|
||||
target = os.path.dirname(
|
||||
image_paths.task_image_path(
|
||||
image_root,
|
||||
task,
|
||||
account=account,
|
||||
suffix="new",
|
||||
ext=".jpg",
|
||||
)
|
||||
)
|
||||
return target, "该账号还没有图片"
|
||||
|
||||
selected_batch = self.batch_filter.currentData()
|
||||
if selected_batch not in (None, "", "all"):
|
||||
return os.path.abspath(os.path.join(image_root, str(selected_batch))), "该批次还没有图片"
|
||||
return os.path.abspath(image_root), "图片根目录不存在,请先采集或生成图片"
|
||||
|
||||
def _selected_row_for_image_directory(self):
|
||||
selection = self.task_table.selectionModel()
|
||||
selected_rows = []
|
||||
if selection is not None:
|
||||
selected_rows = sorted({index.row() for index in selection.selectedRows()})
|
||||
if not selected_rows:
|
||||
return None
|
||||
current = self.task_table.currentIndex()
|
||||
if current.isValid() and current.row() in selected_rows:
|
||||
return current.row()
|
||||
return selected_rows[0]
|
||||
|
||||
def _existing_cover_parent_dir(self, task):
|
||||
for attr in ("new_cover_path", "old_cover_path"):
|
||||
value = str(getattr(task, attr, "") or "").strip()
|
||||
if not value:
|
||||
continue
|
||||
path = os.path.abspath(value)
|
||||
if os.path.isfile(path):
|
||||
return os.path.dirname(path)
|
||||
return None
|
||||
|
||||
def _account_for_task(self, task):
|
||||
alias = str(getattr(task, "alias", "") or "").strip()
|
||||
if not alias:
|
||||
return None
|
||||
return self.model.account_by_alias.get(alias)
|
||||
|
||||
def _selected_task(self):
|
||||
index = self.task_table.currentIndex()
|
||||
if index.isValid():
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
- **封面提示词**:模板下拉(读 `data/prompts/cover/*.txt`)+ 图标工具栏(新建/保存/另存为/重命名/删除)+ 多行输入 + 「插入标题」(插 `{新标题}`)/「预览」(变量替换后查看)。
|
||||
- 变量:标题提示词本阶段只支持 `{旧标题}`;封面提示词支持 `{旧标题}`/`{新标题}`/`{商品id}`/`{店铺}`,生成前按任务替换。
|
||||
- 右上:按导入批次 / 店铺 / 商品ID / 状态筛选任务;商品ID输入框按包含匹配 `item_id`,清空表示全部。
|
||||
- 筛选行提供「打开图片文件夹」按钮,用于只读打开本地图片目录:选中某行时打开该商品所在账号图片文件夹(优先打开已有新/旧封面文件的真实父目录,缺失时回退到规范账号目录);未选行且选择具体批次时打开该批次图片文件夹;未选行且为全部批次时打开图片根目录。目录不存在只中文提示,不自动创建目录,不修改任务状态。
|
||||
- 右下:任务列表(店铺名、商品id、旧标题、新标题、标题状态、图片状态)+ AI生成运行日志;标题/图片状态由 `new_title`、`new_cover_path`、`stage/status` 和失败步骤推导,帮助用户区分“标题未生成 / 图片未生成 / 标题成功但图片失败”。商品ID列按原等分宽度约 50% 显示;标题状态和图片状态列在 T-554 基础上再缩到约 33%,缩出的宽度平均给旧标题和新标题。已生成、未提交线上、非运行中的任务可双击「新标题」列本地微调,写回 `tasks.new_title`,清空 `last_error` 并回到可更新;双击其他列弹窗展示旧封面、新封面和历史候选图。
|
||||
- 底部**单个「开始生成」+「停止」**,并增加「生成内容」下拉:默认只生成标题,可选只生成封面或生成标题和封面;只生成封面要求任务已有新标题。标题/图片两条进度条右侧分别显示同宽用时标签(`生标题用时 N 秒` / `生图用时 N 秒`),运行中每秒递增,完成/停止后冻结;原图片进度条右侧的失败数和 cmhub 余额不再占用该位置。cmhub 模式会把用户设置的图片并发内部限制到最大 5,并用同样最大 5 的独立下载线程池拉取 `image_url`,不新增用户可见下载并发配置;运行日志显示用户设置并发和实际并发。下拉状态持久化到 `config.json` 的 `ai.generate_mode`,并继续写回旧兼容 `ai.generate_cover`。
|
||||
- 生成参数(标题/图片并发数、失败重试、分辨率、jpg 质量、cmhub 网关/Key/别名)在 **⑤ 设置**;②只暴露本轮生成标题/封面/图文的内容模式。⑤ 不新增“下载并发”控件;cmhub 图片下载并发由程序按实际生图并发自动计算,最大 5。
|
||||
|
||||
+11
-2
@@ -3,7 +3,7 @@ id: T-581
|
||||
title: ②AI生成增加「打开图片文件夹」按钮(级联:选中行账号图 / 批次 / 全部批次根)
|
||||
phase: 7
|
||||
deps: [T-566]
|
||||
status: TODO
|
||||
status: DONE
|
||||
created: 2026-07-10
|
||||
---
|
||||
|
||||
@@ -79,4 +79,13 @@ created: 2026-07-10
|
||||
|
||||
## 执行记录
|
||||
|
||||
(做完在这里写:改了什么文件、跑了什么验证命令及结果、遇到的阻塞、关键决策。)
|
||||
- 2026-07-10:已实现②「打开图片文件夹」按钮,位于 AI生成筛选行刷新按钮右侧;按钮按选中行、具体批次、全部批次三级规则定位本地图片目录,运行中不禁用,不修改任何任务/批次状态。
|
||||
- 2026-07-10:新增 `app/gui/file_manager.py`,封装跨平台打开目录逻辑;Windows 使用 `os.startfile`,macOS/Linux 使用 `subprocess.Popen([...], shell=False)`,helper 不创建目录。
|
||||
- 2026-07-10:选中行优先打开已有 `new_cover_path` / `old_cover_path` 的真实父目录;没有真实图片文件时回退到 `image_paths.task_image_path(..., "new")` 的账号目录;账号无法匹配时中文提示,不猜 slug。
|
||||
- 2026-07-10:同步 `docs/routes.md` ②AI生成说明;补 `tests/test_gui.py` 覆盖选中行当前焦点优先、规范账号目录回退、具体批次目录、图片根目录、目录缺失不创建、账号未匹配提示、平台打开 helper 分支。
|
||||
- 验证通过:
|
||||
- `py -3.10 -m unittest tests.test_gui.GuiTests.test_generate_tab_opens_current_row_image_directory tests.test_gui.GuiTests.test_generate_tab_open_image_directory_falls_back_to_canonical_account_dir tests.test_gui.GuiTests.test_generate_tab_opens_batch_or_root_directory_without_selected_row tests.test_gui.GuiTests.test_generate_tab_open_image_directory_warns_without_creating_missing_dir tests.test_gui.GuiTests.test_generate_tab_open_image_directory_warns_when_selected_account_unmatched tests.test_gui.GuiTests.test_open_in_file_manager_uses_platform_file_manager`
|
||||
- `python -m ruff check app tests main.py`
|
||||
- `py -3.10 -m compileall app main.py`
|
||||
- `git diff --check`(仅输出当前工作区既有 CRLF 提示,无空白错误)
|
||||
- 当前工作区直接运行 `py -3.10 -m unittest tests.test_gui` 失败 2 项,原因是本任务开始前已有未提交改动把默认封面提示词从 `papa1` 改成「默认」,导致两个旧断言仍期望 `papa1`;该提示词改名不属于 T-581,本次未处理、未提交。
|
||||
|
||||
+232
-1
@@ -17,7 +17,7 @@ from app import accounts, ai, appconfig, db, image_paths, prompts, update_check
|
||||
if gui.QT_IMPORT_ERROR is not None:
|
||||
raise unittest.SkipTest("PySide6 未安装")
|
||||
|
||||
from PySide6.QtCore import QItemSelectionModel, QRect
|
||||
from PySide6.QtCore import QItemSelectionModel, QModelIndex, QRect
|
||||
from PySide6.QtGui import QImage, QKeyEvent, QTextCursor
|
||||
from PySide6.QtWidgets import QApplication, QComboBox, QLineEdit, QPlainTextEdit, QProgressBar, QTableView
|
||||
|
||||
@@ -39,6 +39,7 @@ from app.gui import (
|
||||
TAB_TITLES,
|
||||
WriteBackWorker,
|
||||
)
|
||||
from app.gui import file_manager
|
||||
from app.gui.tabs.generate import CoverGalleryDialog, OriginalImageDialog
|
||||
from app.gui.main_window import _fit_and_center_window
|
||||
|
||||
@@ -1810,6 +1811,9 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
self.assertEqual("开始生成", tab.generate_button.text())
|
||||
self.assertEqual("停止", tab.stop_generate_button.text())
|
||||
self.assertEqual("重置生成结果", tab.reset_generate_button.text())
|
||||
self.assertEqual("打开图片文件夹", tab.open_image_dir_button.text())
|
||||
self.assertEqual("openImageDirButton", tab.open_image_dir_button.objectName())
|
||||
self.assertIn("当前批次文件夹", tab.open_image_dir_button.toolTip())
|
||||
self.assertEqual(
|
||||
["只生成标题", "只生成封面", "生成标题和封面"],
|
||||
[tab.generate_mode_combo.itemText(index) for index in range(tab.generate_mode_combo.count())],
|
||||
@@ -1975,6 +1979,233 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
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)
|
||||
accounts.create_account("主店", "alias-a", debug_port=9222, config=cfg)
|
||||
accounts.create_account("副店", "alias-b", debug_port=9223, config=cfg)
|
||||
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": "Excel主店",
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639510",
|
||||
},
|
||||
{
|
||||
"source_file_abs": os.path.join(temp_dir, "input.xlsx"),
|
||||
"source_sheet": "商品",
|
||||
"source_row": 3,
|
||||
"account_name": "Excel副店",
|
||||
"alias": "alias-b",
|
||||
"item_id": "51100639511",
|
||||
},
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tasks = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])
|
||||
first_dir = os.path.join(temp_dir, "legacy-a")
|
||||
second_dir = os.path.join(temp_dir, "legacy-b")
|
||||
first_cover = self.write_test_image(os.path.join(first_dir, "old.jpg"))
|
||||
second_cover = self.write_test_image(os.path.join(second_dir, "new.jpg"))
|
||||
db.set_collected(tasks[0].id, "旧标题A", first_cover, path=cfg["db_path"])
|
||||
db.set_collected(tasks[1].id, "旧标题B", "missing-old.jpg", path=cfg["db_path"])
|
||||
db.set_generated(tasks[1].id, "新标题B", second_cover, path=cfg["db_path"])
|
||||
|
||||
tab = GenerateTab(config=cfg)
|
||||
self.addCleanup(tab.close)
|
||||
selection = tab.task_table.selectionModel()
|
||||
selection.select(tab.model.index(0, 0), QItemSelectionModel.Select | QItemSelectionModel.Rows)
|
||||
selection.select(tab.model.index(1, 0), QItemSelectionModel.Select | QItemSelectionModel.Rows)
|
||||
tab.task_table.setCurrentIndex(tab.model.index(1, 0))
|
||||
|
||||
with mock.patch(
|
||||
"app.gui.tabs.generate.file_manager.open_in_file_manager",
|
||||
return_value=second_dir,
|
||||
) as open_dir:
|
||||
tab.open_image_directory()
|
||||
|
||||
open_dir.assert_called_once_with(os.path.abspath(second_dir))
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_generate_tab_open_image_directory_falls_back_to_canonical_account_dir(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
account = accounts.create_account("主店", "alias-a", debug_port=9222, config=cfg)
|
||||
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": "Excel主店",
|
||||
"alias": "alias-a",
|
||||
"item_id": "51100639510",
|
||||
},
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
task = db.list_tasks(batch_id=batch_id, path=cfg["db_path"])[0]
|
||||
canonical_dir = os.path.dirname(
|
||||
image_paths.task_image_path(cfg["image_dir"], task, account=account, suffix="new")
|
||||
)
|
||||
os.makedirs(canonical_dir, exist_ok=True)
|
||||
|
||||
tab = GenerateTab(config=cfg)
|
||||
self.addCleanup(tab.close)
|
||||
tab.task_table.selectRow(0)
|
||||
tab.task_table.setCurrentIndex(tab.model.index(0, 0))
|
||||
|
||||
with mock.patch(
|
||||
"app.gui.tabs.generate.file_manager.open_in_file_manager",
|
||||
return_value=canonical_dir,
|
||||
) as open_dir:
|
||||
tab.open_image_directory()
|
||||
|
||||
open_dir.assert_called_once_with(os.path.abspath(canonical_dir))
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_generate_tab_opens_batch_or_root_directory_without_selected_row(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"])
|
||||
image_root = os.path.abspath(cfg["image_dir"])
|
||||
batch_dir = os.path.join(image_root, batch_id)
|
||||
os.makedirs(batch_dir, exist_ok=True)
|
||||
tab = GenerateTab(config=cfg)
|
||||
self.addCleanup(tab.close)
|
||||
tab.batch_filter.setCurrentIndex(tab.batch_filter.findData(batch_id))
|
||||
tab.task_table.clearSelection()
|
||||
tab.task_table.setCurrentIndex(QModelIndex())
|
||||
|
||||
with mock.patch(
|
||||
"app.gui.tabs.generate.file_manager.open_in_file_manager",
|
||||
return_value=batch_dir,
|
||||
) as open_dir:
|
||||
tab.open_image_directory()
|
||||
|
||||
open_dir.assert_called_once_with(os.path.abspath(batch_dir))
|
||||
|
||||
tab.batch_filter.setCurrentIndex(tab.batch_filter.findData(None))
|
||||
tab.task_table.clearSelection()
|
||||
tab.task_table.setCurrentIndex(QModelIndex())
|
||||
with mock.patch(
|
||||
"app.gui.tabs.generate.file_manager.open_in_file_manager",
|
||||
return_value=image_root,
|
||||
) as open_dir:
|
||||
tab.open_image_directory()
|
||||
|
||||
open_dir.assert_called_once_with(image_root)
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_generate_tab_open_image_directory_warns_without_creating_missing_dir(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"])
|
||||
batch_dir = os.path.join(os.path.abspath(cfg["image_dir"]), batch_id)
|
||||
tab = GenerateTab(config=cfg)
|
||||
self.addCleanup(tab.close)
|
||||
tab.batch_filter.setCurrentIndex(tab.batch_filter.findData(batch_id))
|
||||
tab.task_table.clearSelection()
|
||||
tab.task_table.setCurrentIndex(QModelIndex())
|
||||
|
||||
with mock.patch("app.gui.tabs.generate.QMessageBox.warning") as warning, mock.patch(
|
||||
"app.gui.tabs.generate.file_manager.open_in_file_manager"
|
||||
) as open_dir:
|
||||
tab.open_image_directory()
|
||||
|
||||
warning.assert_called_once()
|
||||
self.assertIn("该批次还没有图片", warning.call_args[0][2])
|
||||
open_dir.assert_not_called()
|
||||
self.assertFalse(os.path.exists(batch_dir))
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_generate_tab_open_image_directory_warns_when_selected_account_unmatched(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": "Excel主店",
|
||||
"alias": "missing-alias",
|
||||
"item_id": "51100639510",
|
||||
},
|
||||
],
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tab = GenerateTab(config=cfg)
|
||||
self.addCleanup(tab.close)
|
||||
tab.task_table.selectRow(0)
|
||||
tab.task_table.setCurrentIndex(tab.model.index(0, 0))
|
||||
|
||||
with mock.patch("app.gui.tabs.generate.QMessageBox.warning") as warning, mock.patch(
|
||||
"app.gui.tabs.generate.file_manager.open_in_file_manager"
|
||||
) as open_dir:
|
||||
tab.open_image_directory()
|
||||
|
||||
warning.assert_called_once()
|
||||
self.assertIn("选中任务没有匹配账号", warning.call_args[0][2])
|
||||
open_dir.assert_not_called()
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_open_in_file_manager_uses_platform_file_manager(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
target = os.path.join(temp_dir, "images")
|
||||
os.makedirs(target)
|
||||
|
||||
with mock.patch("app.gui.file_manager.sys.platform", "win32"), mock.patch(
|
||||
"app.gui.file_manager.os.startfile",
|
||||
create=True,
|
||||
) as startfile:
|
||||
self.assertEqual(os.path.abspath(target), file_manager.open_in_file_manager(target))
|
||||
startfile.assert_called_once_with(os.path.abspath(target))
|
||||
|
||||
with mock.patch("app.gui.file_manager.sys.platform", "darwin"), mock.patch(
|
||||
"app.gui.file_manager.subprocess.Popen"
|
||||
) as popen:
|
||||
self.assertEqual(os.path.abspath(target), file_manager.open_in_file_manager(target))
|
||||
popen.assert_called_once_with(
|
||||
["open", os.path.abspath(target)],
|
||||
stdout=file_manager.subprocess.DEVNULL,
|
||||
stderr=file_manager.subprocess.DEVNULL,
|
||||
shell=False,
|
||||
)
|
||||
|
||||
with mock.patch("app.gui.file_manager.sys.platform", "linux"), mock.patch(
|
||||
"app.gui.file_manager.subprocess.Popen"
|
||||
) as popen:
|
||||
self.assertEqual(os.path.abspath(target), file_manager.open_in_file_manager(target))
|
||||
popen.assert_called_once_with(
|
||||
["xdg-open", os.path.abspath(target)],
|
||||
stdout=file_manager.subprocess.DEVNULL,
|
||||
stderr=file_manager.subprocess.DEVNULL,
|
||||
shell=False,
|
||||
)
|
||||
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
file_manager.open_in_file_manager(os.path.join(temp_dir, "missing"))
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_generate_elapsed_timer_tracks_title_then_cover(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
tab = GenerateTab(config=self.make_config(temp_dir))
|
||||
|
||||
Reference in New Issue
Block a user