Commit Graph
77 Commits
Author SHA1 Message Date
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
admin 511fcb1fc5 docs: add low contrast screening requirement 2026-06-16 10:39:31 +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
admin a6a8504ea8 fix: align resource and runtime paths 2026-06-15 17:13:36 +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
admin b8801ec024 fix: support isolated Python startup 2026-06-15 16:44:33 +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 7d941a03aa docs: add agent development guide 2026-06-15 16:26:55 +08:00
QiuSW a3e61bc602 docs: add development task plan 2026-06-15 16:09:32 +08:00
QiuSW e91207357b chore: scaffold PySide6 application 2026-06-15 15:53:01 +08:00
QiuSW 5613f08213 docs: add project planning documentation 2026-06-15 15:37:33 +08:00
ila 73dddf3691 Initial commit 2026-06-15 15:19:30 +08:00