Commit Graph
25 Commits
Author SHA1 Message Date
adminandClaude Opus 4.8 edcd5d1e18 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>
2026-06-17 15:13:01 +08:00
adminandClaude Opus 4.8 0cb1544654 refactor: drop the duplicate in-app title bar; use the OS window title
The in-content header strip showed the app name + version, duplicating the
OS window title (setWindowTitle). With the 设置 button already gone it had no
other purpose, so remove it (and its styles / unused _HEADER_HEIGHT). Name and
version stay visible via the OS title bar; the work area gains ~40px.

Docs: UI design 3 (layout) and 4.1 (title bar), tasks.md 17.7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 09:05:47 +08:00
adminandClaude Opus 4.8 7fe770e80d feat: template delete, whole-reset relocation, and compact param/export rows
- Template delete: add a 删除 button (reuses template_service.delete_template)
  with a confirm dialog; hidden for built-ins like 保存; reloads and re-selects
  the first built-in after deletion, persisting the new selection.
- Whole reset: move the template-wide reset out of the template area to the
  bottom of the transform panel and rename it to 位置尺寸旋转重置为当前模板, so it
  sits with the params it resets, beside the three per-section resets.
- Compact layout: pair X/Y and 宽/高 onto single rows (short labels), and put
  the export 格式/质量 selectors on one row. Sync logic unchanged.

Docs: PRD 6.6, UI design 7.1/7.2/7.3/7.4/7.5/10, tasks.md 17.4/17.5/17.6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 17:45:09 +08:00
adminandClaude Opus 4.8 f767ec2660 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>
2026-06-16 17:01:58 +08:00
admin df9145abdb chore: add packaging build script 2026-06-16 11:49:25 +08:00
admin c94b600bb6 feat: add low contrast visibility analysis 2026-06-16 11:21:21 +08:00
admin 43a096366f docs: add low contrast screening task 2026-06-16 10:53:19 +08:00
adminandClaude Sonnet 4.6 d02da738c6 test: add composer and template test suites (43 tests, all passing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:38:06 +08:00
adminandClaude Sonnet 4.6 597da6744d feat: implement fine-tune state and complete inter-panel signal wiring
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:31:16 +08:00
adminandClaude Sonnet 4.6 aeb6b59ca1 feat: implement queue panel with table, batch modes, stats, and export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:25:45 +08:00
adminandClaude Sonnet 4.6 1054ab6236 feat: implement export panel UI with output settings and single export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:18:39 +08:00
adminandClaude Sonnet 4.6 abc92cbdcb feat: implement template panel UI with picker, save, save-as, reset
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:15:33 +08:00
adminandClaude Sonnet 4.6 8bb6a8e53a feat: implement transform panel UI with position, size, rotation controls
TransformPanel provides the right-panel parameter editing section:

- Position: X/Y QDoubleSpinBox (range ±99999 px, 0.1 step)
- Size: Width/Height QDoubleSpinBox (min 10 px) + 锁定宽高比例 checkbox
  - When locked and width changes, height is auto-updated to maintain ratio
  - When locked and height changes, width is auto-updated to maintain ratio
  - Locking captures the current ratio at that moment
- Rotation: angle QDoubleSpinBox + 左旋 90° / 右旋 90° buttons

Signal-loop prevention:
- set_transform() sets self._updating = True and blockSignals on all inputs
  before updating values, preventing valueChanged from re-firing _emit()
- _on_width_changed/_on_height_changed also blockSignals on the companion
  spinbox when adjusting for aspect ratio to avoid double emission

Public API:
- set_transform(TransformState): programmatic update, no signal emitted
- get_transform() -> TransformState: read current inputs
- transform_changed signal: emitted whenever user edits any input

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:06:24 +08:00
adminandClaude Sonnet 4.6 3e3e06b7a1 feat: implement image canvas with drag/scale/rotate and preview zoom
Replace placeholder ImageCanvas with full QGraphicsView/QGraphicsScene editor:

Layout: ImageCanvas(QWidget) = top bar + _CanvasView(QGraphicsView) + hint bar

Top bar: garment/print filenames, mode buttons (移动/缩放/旋转, context-aware),
zoom controls (−/% label/+, scroll wheel).

_CanvasView:
- drawBackground: checkerboard in garment area, gray outside
- wheelEvent: zoom in/out anchored at cursor
- mousePressEvent/mouseMoveEvent/mouseReleaseEvent: delegate to ImageCanvas

Scene (1:1 with garment pixel coords):
- garment QGraphicsPixmapItem at (0,0), not interactive
- print QGraphicsPixmapItem positioned/rotated from TransformState
- cosmetic selection rect (blue, 1.5px)
- 4 corner handles (white circle, blue border) for scaling
- rotation arm (dashed) + handle (blue circle) above top-center

Interaction:
- Hit test: rotation handle > corner handles > print body (priority order)
- Move: delta in scene coords added to TransformState.x/y
- Scale: _scene_to_local_xy() maps mouse to item local coords, computes
  new size keeping opposite corner fixed (_do_scale); supports aspect
  ratio lock via Shift or keep_aspect_ratio flag; correct with rotation
- Rotate: atan2(mouse - center) + angle offset at press

Coordinate helpers (static):
- _local_to_scene_xy(lx, ly, state): item-local → scene
- _scene_to_local_xy(sx, sy, state): scene → item-local (inverse rotation)
Both use TransformState snapshot, not live item state, to avoid stale-pixmap issues.

transform_changed signal fires on mouse release with final TransformState.
Preview zoom changes only QGraphicsView transform; TransformState is unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:01:15 +08:00
adminandClaude Sonnet 4.6 94d30ab183 feat: implement image list panel UI for garment and print assets
Implement ImageListPanel with two _AssetPanel sub-widgets (garment/print)
separated by a vertical QSplitter:

- _AssetItemWidget: checkbox (batch select) + 52px thumbnail + subfolder
  label + filename; WA_StyledBackground enables preview highlight (#cce4f7)
- _AssetPanel: open-folder button → scan_image_folder(); stats row with
  tristate 全选 checkbox + "含子文件夹·共N 已选M" label + sort selector
  (文件夹+名称 / 名称); QListWidget with NoSelection mode so preview
  highlight and batch checkbox are visually distinct
- ImageListPanel: exposes garment_preview_changed, print_preview_changed,
  garments_changed, prints_changed signals for downstream panels

File scanning delegates to services/file_service.scan_image_folder();
no scanning logic duplicated in UI layer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 08:44:17 +08:00
adminandClaude Sonnet 4.6 0887cb35aa feat: implement main window layout with panels and style
Expand MainWindow from placeholder to full structural layout:
- Header bar: app name, version label, settings button
- Flow tab bar (QTabBar): tabs 2/3 disabled, click redirects to status bar message
- Work area (QSplitter): ImageListPanel (left 374px) | ImageCanvas (center, stretch) | right scroll with TemplatePanel/TransformPanel/ExportPanel (318px)
- Queue panel (188px, collapsible ▲/▼ toggle)
- QStatusBar with Windows-style styling

Applies color/font spec from docs/07-ui-design.md §10.
No business logic in main_window.py; panel stubs remain for tasks 8–13.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 18:04:29 +08:00
admin 29523f9ac2 feat: implement batch composition core 2026-06-15 17:55:55 +08:00
adminandClaude Sonnet 4.6 4b008a0dc8 feat: implement image composer with Pillow
- compose(): scales print to TransformState.width/height, rotates
  clockwise by TransformState.rotation (negated for Pillow), pastes
  onto a transparent layer via alpha_composite, saves PNG or JPG
- Rotation centre fixed at print bounding-box centre; paste_x/y
  computed from centre - rotated_size/2 per design doc §13.3
- JPG: flattens RGBA onto white RGB background before saving
- Out-of-bounds print clips silently via PIL paste; width/height<=0
  returns ComposeResult(success=False); file errors caught, never raise

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:28:02 +08:00
adminandClaude Sonnet 4.6 519bb9a6d2 feat: implement template service with builtin and custom templates
- BUILTIN_TEMPLATES: 4 presets (正方形/纵向/横向长方形/左胸小号)
- load_custom_templates(): reads templates.json, skips corrupt entries
  individually so one bad record never blocks the rest
- get_all_templates(): builtin first, then custom
- add_template(): upsert by name, forces type="custom"
- rename_template() / delete_template(): raise ValueError if not found
- _save_custom_templates(): auto-creates config/ dir, logs on failure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:01:40 +08:00
adminandClaude Sonnet 4.6 2d9121e3e0 feat: add image folder scanning and safe output filename
- scan_image_folder(): recursively scans a directory for supported
  images (png/jpg/jpeg/webp, case-insensitive), returns List[ImageAsset],
  skips and logs unsupported files, raises OSError on bad path
- make_safe_output_path(): builds <garment>_<print>.<ext> filename,
  appends _1/_2/... counter if destination already exists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:58:44 +08:00
adminandClaude Sonnet 4.6 b692e3aa42 feat: complete core data models
Add Template, ExportOptions, BatchMode, BatchOptions, ComposeResult,
BatchResult to models.py. Keep existing ImageAsset and TransformState,
adding center() helper to TransformState.

Template uses ratio-based fields with to_transform_state() to convert
to pixel coordinates for any garment size.
BatchMode enum covers all four batch modes from PRD.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:56:36 +08:00
adminandClaude Sonnet 4.6 134896fcd2 feat: implement config service with JSON read/write and safe fallback
- DEFAULT_CONFIG defines output_format, output_quality, output_dir,
  last_garment_dir, last_print_dir
- load_config(): merges file values with defaults; never raises on
  missing, damaged, or non-object JSON root
- save_config(): creates config/ dir as needed; logs error on failure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:51:46 +08:00
adminandClaude Sonnet 4.6 f7d52e8cb0 feat: add path helper functions to file_service
- get_app_dir(): project root in dev, exe dir in PyInstaller bundle
- get_resource_path/get_config_path: paths under app dir
- get_log_dir/get_output_dir: auto-create and return logs/ and output/
- Refactor log_service to use get_log_dir() instead of own __file__ calc

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:48:37 +08:00
adminandClaude Sonnet 4.6 24d43db434 feat: implement logging service with file output
- setup_logging() creates logs/ dir and writes timestamped log file
- format: asctime name levelname message
- falls back to console-only if log dir is not writable
- main.py now calls setup_logging() at startup and logs app name/version

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:33:33 +08:00
QiuSW a3e61bc602 docs: add development task plan 2026-06-15 16:09:32 +08:00