feat: auto-preview, timestamped/grouped output, folder memory; fix batch format
- Auto-preview the first image after a folder loads (both panels), so the canvas isn't empty (§17.8). - Group exports as <output_dir>/<timestamp>/<print_stem>/<garment_stem>.<ext>; one timestamp per export run, shared across the batch (§17.9). - Remember the last garment/print folders and reopen the picker there; persisted via config, wired through main_window (§17.10). - Shrink the 3 box templates further and lower them (chest placement). - Fix: batch export ignored the chosen format/quality/output dir because the queue never received ExportOptions. Export panel now emits export_options_changed → queue.set_export_options (+ initial sync) (§17.11). Docs: PRD 6.1/6.8/9, architecture 4.12, UI design 5.1, tasks.md 17.8–17.11. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
@@ -102,20 +103,32 @@ def scan_image_folder(folder) -> List:
|
||||
# 安全输出文件名
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def timestamped_run_dir(base_dir):
|
||||
"""Return base_dir/<YYYYmmdd_HHMMSS> for grouping one export run.
|
||||
|
||||
Compute this once per export run (a batch, or a single export) and pass the
|
||||
result as the output_dir to make_safe_output_path, so repeated runs land in
|
||||
separate timestamped folders instead of mixing together.
|
||||
"""
|
||||
return Path(base_dir) / datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
|
||||
def make_safe_output_path(output_dir, garment_path, print_path, output_format="PNG"):
|
||||
"""Return an output Path that will not overwrite an existing file.
|
||||
|
||||
Filename pattern : <garment_stem>_<print_stem>.<ext>
|
||||
Outputs are grouped by print into a subfolder named after the print file:
|
||||
<output_dir>/<print_stem>/<garment_stem>.<ext>
|
||||
If that path exists, appends _1, _2, ... until a free slot is found.
|
||||
(The composer creates the subfolder when saving.)
|
||||
"""
|
||||
output_dir = Path(output_dir)
|
||||
ext = ".png" if output_format.upper() == "PNG" else ".jpg"
|
||||
stem = "{}_{}".format(Path(garment_path).stem, Path(print_path).stem)
|
||||
target_dir = Path(output_dir) / Path(print_path).stem
|
||||
stem = Path(garment_path).stem
|
||||
|
||||
candidate = output_dir / (stem + ext)
|
||||
candidate = target_dir / (stem + ext)
|
||||
counter = 1
|
||||
while candidate.exists():
|
||||
candidate = output_dir / ("{}_{}{}".format(stem, counter, ext))
|
||||
candidate = target_dir / ("{}_{}{}".format(stem, counter, ext))
|
||||
counter += 1
|
||||
|
||||
return candidate
|
||||
|
||||
@@ -15,25 +15,25 @@ _TEMPLATES_FILENAME = "templates.json"
|
||||
BUILTIN_TEMPLATES: List[Template] = [
|
||||
Template(
|
||||
name="正方形模板",
|
||||
x_ratio=0.34, y_ratio=0.22,
|
||||
width_ratio=0.32, height_ratio=0.32,
|
||||
x_ratio=0.40, y_ratio=0.40,
|
||||
width_ratio=0.25, height_ratio=0.25,
|
||||
rotation=0.0, type="builtin",
|
||||
),
|
||||
Template(
|
||||
name="纵向长方形模板",
|
||||
x_ratio=0.35, y_ratio=0.16,
|
||||
width_ratio=0.30, height_ratio=0.46,
|
||||
x_ratio=0.435, y_ratio=0.385,
|
||||
width_ratio=0.20, height_ratio=0.30,
|
||||
rotation=0.0, type="builtin",
|
||||
),
|
||||
Template(
|
||||
name="横向长方形模板",
|
||||
x_ratio=0.27, y_ratio=0.26,
|
||||
width_ratio=0.46, height_ratio=0.30,
|
||||
x_ratio=0.385, y_ratio=0.435,
|
||||
width_ratio=0.30, height_ratio=0.20,
|
||||
rotation=0.0, type="builtin",
|
||||
),
|
||||
Template(
|
||||
name="左胸小号模板",
|
||||
x_ratio=0.50, y_ratio=0.22,
|
||||
x_ratio=0.55, y_ratio=0.30,
|
||||
width_ratio=0.16, height_ratio=0.16,
|
||||
rotation=0.0, type="builtin",
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user