feat: persist template/batch-mode, per-aspect resets, de-dup template actions
§17.1 Preferences persistence: - Wire the previously-unused config_service; remember and restore the last-selected template and last batch mode. main_window loads config, distributes initial values, then persists on change (save-on-change). - template_panel: template_changed signal, select_template, current_template_state. queue_panel: set_batch_mode + batch_mode_changed. §17.2 Independent resets: - Replace the combined reset and 归零 with per-section 位置/尺寸/旋转 reset buttons (quiet ghost styling) that restore only their own aspect to the selected template. Size reset changes width/height only, leaving X/Y. Button de-dup: - Remove the export panel's 应用为模板 / 重置为模板 — literal duplicates of the template panel's 另存为 / 重置为模板. Template actions now live only in the template area; the export panel handles output only. Docs: PRD (6.6/6.9/9/10), architecture (4.12), UI design (7.x/10), development-rules (6, single-home actions + consistent naming), tasks.md (17). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+78
-6
@@ -14,6 +14,8 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from version import APP_NAME, APP_VERSION
|
||||
from core.models import BatchMode, TransformState
|
||||
from services.config_service import load_config, save_config
|
||||
from app.widgets.export_panel import ExportPanel
|
||||
from app.widgets.image_canvas import ImageCanvas
|
||||
from app.widgets.image_list_panel import ImageListPanel
|
||||
@@ -66,6 +68,7 @@ class MainWindow(QMainWindow):
|
||||
self.setCentralWidget(root)
|
||||
self._apply_stylesheet()
|
||||
self._connect_signals()
|
||||
self._restore_preferences()
|
||||
|
||||
def _create_header(self):
|
||||
"""Top header: app name, version label, settings button."""
|
||||
@@ -270,8 +273,10 @@ class MainWindow(QMainWindow):
|
||||
self.image_canvas.transform_changed.connect(self._on_transform_changed)
|
||||
self.transform_panel.transform_changed.connect(self._on_transform_changed)
|
||||
|
||||
# Reset position/size/angle back to the selected template
|
||||
self.transform_panel.reset_to_template.connect(self.template_panel._reset_to_template)
|
||||
# Per-section reset: restore only that aspect to the selected template
|
||||
self.transform_panel.reset_position.connect(lambda: self._reset_aspect("pos"))
|
||||
self.transform_panel.reset_size.connect(lambda: self._reset_aspect("size"))
|
||||
self.transform_panel.reset_rotation.connect(lambda: self._reset_aspect("rot"))
|
||||
|
||||
# ── Template panel ─────────────────────────────────────────────────
|
||||
# Template selection updates canvas, param panel, and queue template
|
||||
@@ -280,10 +285,6 @@ class MainWindow(QMainWindow):
|
||||
self.template_panel.template_applied.connect(self.queue_panel.set_transform)
|
||||
self.template_panel.template_applied.connect(self.export_panel.set_transform)
|
||||
|
||||
# ── Export panel secondary buttons → template panel ────────────────
|
||||
self.export_panel.apply_as_template.connect(self.template_panel._save_as_template)
|
||||
self.export_panel.reset_to_template.connect(self.template_panel._reset_to_template)
|
||||
|
||||
def _load_print_preview(self, path):
|
||||
"""Load a print into the canvas and sync its native size to the template panel."""
|
||||
self.image_canvas.load_print(path)
|
||||
@@ -300,6 +301,7 @@ class MainWindow(QMainWindow):
|
||||
self._active_queue_item = None # leave queue mode
|
||||
if not asset:
|
||||
self.export_panel.set_garment(None)
|
||||
self._update_reset_availability()
|
||||
return
|
||||
path = str(asset.path)
|
||||
self.image_canvas.load_garment(path)
|
||||
@@ -316,6 +318,7 @@ class MainWindow(QMainWindow):
|
||||
if self._current_print_path:
|
||||
self._load_print_preview(self._current_print_path)
|
||||
self.template_panel.apply_current()
|
||||
self._update_reset_availability()
|
||||
|
||||
def _on_print_preview(self, asset):
|
||||
"""Load a print from the asset list into the canvas for direct preview."""
|
||||
@@ -323,12 +326,14 @@ class MainWindow(QMainWindow):
|
||||
if not asset:
|
||||
self._current_print_path = None
|
||||
self.export_panel.set_print(None)
|
||||
self._update_reset_availability()
|
||||
return
|
||||
self._current_print_path = str(asset.path)
|
||||
self._load_print_preview(self._current_print_path)
|
||||
# Apply the selected template so the print lands per the chosen layout,
|
||||
# not the canvas's hard-coded centred default.
|
||||
self.template_panel.apply_current()
|
||||
self._update_reset_availability()
|
||||
|
||||
def _on_queue_item_activated(self, item):
|
||||
"""Load a queue item into the canvas and arm fine-tune tracking."""
|
||||
@@ -356,6 +361,7 @@ class MainWindow(QMainWindow):
|
||||
pass
|
||||
finally:
|
||||
self._loading_queue_item = False
|
||||
self._update_reset_availability()
|
||||
|
||||
def _on_transform_changed(self, state):
|
||||
"""Route transform changes to panels and mark the active queue item fine-tuned."""
|
||||
@@ -367,6 +373,72 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
self.queue_panel.mark_item_fine_tuned(self._active_queue_item, state)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Per-aspect reset (position / size / rotation → selected template)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _update_reset_availability(self):
|
||||
"""Enable the section resets only when a print is on the canvas."""
|
||||
has_print = self.image_canvas.get_transform() is not None
|
||||
self.transform_panel.set_resets_enabled(has_print)
|
||||
|
||||
def _reset_aspect(self, aspect):
|
||||
"""Restore only one aspect of the current transform to the selected template."""
|
||||
tpl = self.template_panel.current_template_state()
|
||||
cur = self.image_canvas.get_transform()
|
||||
if tpl is None or cur is None:
|
||||
return
|
||||
new = TransformState(
|
||||
x=cur.x, y=cur.y, width=cur.width, height=cur.height,
|
||||
rotation=cur.rotation, keep_aspect_ratio=cur.keep_aspect_ratio,
|
||||
)
|
||||
if aspect == "pos":
|
||||
# Centre the current-sized print on the template's centre
|
||||
new.x = (tpl.x + tpl.width / 2.0) - cur.width / 2.0
|
||||
new.y = (tpl.y + tpl.height / 2.0) - cur.height / 2.0
|
||||
elif aspect == "size":
|
||||
# Only the size; leave position (x/y) untouched so each reset is independent
|
||||
new.width, new.height = tpl.width, tpl.height
|
||||
elif aspect == "rot":
|
||||
new.rotation = tpl.rotation
|
||||
self._apply_preview_transform(new)
|
||||
|
||||
def _apply_preview_transform(self, state):
|
||||
"""Push a transform to the canvas + panels as if it were a user edit."""
|
||||
self.image_canvas.set_transform(state)
|
||||
self.transform_panel.set_transform(state)
|
||||
self._on_transform_changed(state)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Preference persistence (last template / last batch mode)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _restore_preferences(self):
|
||||
"""Load saved preferences, apply them, then start persisting changes."""
|
||||
self._config = load_config()
|
||||
|
||||
name = self._config.get("last_template", "")
|
||||
if name:
|
||||
self.template_panel.select_template(name)
|
||||
|
||||
raw_mode = self._config.get("last_batch_mode", "")
|
||||
try:
|
||||
self.queue_panel.set_batch_mode(BatchMode(raw_mode))
|
||||
except ValueError:
|
||||
logger.warning("Unknown last_batch_mode %r, keeping default", raw_mode)
|
||||
|
||||
# Persist future changes (connected after restore to avoid echo writes)
|
||||
self.template_panel.template_changed.connect(self._on_template_persist)
|
||||
self.queue_panel.batch_mode_changed.connect(self._on_batch_mode_persist)
|
||||
|
||||
def _on_template_persist(self, name):
|
||||
self._config["last_template"] = name
|
||||
save_config(self._config)
|
||||
|
||||
def _on_batch_mode_persist(self, mode):
|
||||
self._config["last_batch_mode"] = getattr(mode, "value", str(mode))
|
||||
save_config(self._config)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Style (ref: docs/07-ui-design.md §10)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user