Phase 0 of expert feedback (#1~#11): infrastructure + design + 1차 fixes
Implementations (즉시 동작): - #1 crash logging: harness/crash_logger.py (sys.excepthook + threading + faulthandler, 회전 파일 logs/scanvas.log). main 진입점 통합. - #2 smooth curves (1차): gate_3d_builder ogee profile를 arc-length parametric CubicSpline로 4× densify (8pt→32pt, 36→132 cells, 60 FPS 안전). - #3 TIN colormap: matplotlib "terrain"의 파란색 범위 제거 → 짙은갈색→황토→ 모래→능선 LinearSegmentedColormap. 9 사이트 교체. 회귀 테스트 추가. - #5 uv: pyproject.toml + UV_GUIDE.md. base/[py313]/[dev]/[build] extras + hatchling. - #6,#7,#8 dev cycle infra: .pre-commit-config.yaml (ruff+secrets+위생), .gitea/workflows/ci.yml (Py3.11+3.13 matrix), tests/test_regressions.py (18 회귀 테스트, iter=1~7 fix 박제), CONTRIBUTING.md (Red→Green 알고리즘). Design docs (다음 세션 마이그레이션 청사진): - #4 UI/UX 전면 수정: UI_REDESIGN_PLAN.md (12 popup→1 inspector, vtkTkRenderWidget embedding 게이트, 4 phase × 7 sessions). - #10 Core/Plugin: ARCHITECTURE_PLAN.md (Core 14 / Plugin 7 구조물 + 2 렌더 + 1 QA, STRUCTURE_REGISTRY 확장, manifest 기반 디스커버리). - #11 perf hotspots: PERFORMANCE_BASELINE.md (19 핫스팟, P1: 타일 직렬DL 5~30s, 캡처 직렬 4.5~15s, numpy 벡터화 가능 Python loops, 텍스처 4회 반복read). Behavior preservation: ruff 0 errors, pytest 17 passed/1 skipped(bpy), import 33/33 OK on Py3.13.13. Item #2 P2/P3 곡선, #4 UI 마이그레이션, #10 Phase 1 추출, #11 P1 최적화는 차기 세션. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
374
UI_REDESIGN_PLAN.md
Normal file
374
UI_REDESIGN_PLAN.md
Normal file
@@ -0,0 +1,374 @@
|
||||
# S-CANVAS UI/UX 전면 재설계 (Phase 0 — 디자인)
|
||||
|
||||
> **사용자 피드백 #4 인용**
|
||||
> "느리고, 조작이 어렵게 느껴지므로, UI/UX를 전면 수정할 필요가 있음(기존 구조에 로그는 백엔드로 빼고, 프로세스를 클릭할 때마다 새로운 창이 뜨는 것이 아니라 한 화면에서 바로 구동되게끔 적용)"
|
||||
>
|
||||
> 요지: (1) 인라인 로그 패널 제거 → 백엔드 파일 (2) 단계마다 새 팝업창 → 단일 창 인스펙터.
|
||||
|
||||
본 문서는 **Phase 0(읽기 전용 분석 + 청사진)**. 이번 라운드에서는 코드 한 줄도 수정하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 현재 UI 진단
|
||||
|
||||
### 1.1 메인 셸 구조 (`scanvas_maker.py`)
|
||||
- **클래스**: `SCanvasApp(ctk.CTk)` — 단일 창. line 160.
|
||||
- **창 크기**: 1200x900, light theme, blue color theme. line 165–167.
|
||||
- **메인 레이아웃**: 2-column grid.
|
||||
- **Left (col 0)**: `sidebar_container` (270px 고정) + `sidebar_frame` (CTkScrollableFrame 250px). line 274–283.
|
||||
- 섹션: 로고 → SETTINGS (위성 소스/Vworld 키/AI 엔진/GCP/Vertex Loc/CRS) → WORKFLOW (Step1, 1.5, 2, 3, 4, 구조물 빌드, 상세 치수, 3D 다시 열기) → OPTIONS (와이어프레임, 뷰 버퍼 %, DEM 확장 m, 테마) → SAMAN 푸터.
|
||||
- **Right (col 1)**: `main_frame` (corner_radius=15, transparent). line 558–562.
|
||||
- row 0 weight=3 → `map_frame` (TkinterMapView, 위성 지도 미리보기).
|
||||
- row 1 weight=1 → `textbox` (CTkTextbox, **height=120, 인라인 로그 패널**). line 582–583.
|
||||
- row 2 → `status_bar` (28px, ● READY 인디케이터 + status_text). line 586–593.
|
||||
|
||||
### 1.2 인라인 로그 (제거 대상)
|
||||
- 표면화 위치: `main_frame.row=1`, `self.textbox` 한 위젯.
|
||||
- 호출지점: `self.log(message)` — **180회**. line 691–696.
|
||||
- 동작: `datetime` timestamp prefix → `textbox.insert("end", ...)` → auto-scroll.
|
||||
- 별도로 `self._diag(...)` (구조물 분류 진단)와 `harness.logger.setup_logging(log_file=harness_log_path())`는 **이미 백엔드 파일**로 흘러가고 있음 (line 228, 698–710). 즉 인프라 절반은 이미 존재.
|
||||
- 결론: `harness_log_path()` (`%LOCALAPPDATA%\S-CANVAS\scanvas_harness.log`) 표준에 `self.log()`도 포함시키면 됨. 새 파일 만들 필요 없음.
|
||||
|
||||
### 1.3 팝업 다이얼로그 카탈로그 (제거/이식 대상)
|
||||
**`CTkToplevel(...)` 12개** + **`messagebox.*` 63회** + **`filedialog.*` 다수**.
|
||||
|
||||
| # | 위치 (line) | 트리거 | 제목 | 크기 | 이식 우선순위 |
|
||||
|---|---|---|---|---|---|
|
||||
| T1 | 766 | Step 1 DXF 로드 후 자동 | `DXF 레이어 분류` | 900×650 | **HIGH** — 첫 인상 |
|
||||
| T2 | 1419 | 사이드바 `구조물 상세 3D 빌드` | `구조물 상세 3D 빌드 (템플릿)` | 1100×650 | HIGH |
|
||||
| T3 | 1596 | T2 내부 `상세 빌드` 버튼 | 빌드 진행 다이얼로그 | — | MED |
|
||||
| T4 | 1889 | T3 내부 옵션 | `렌더 옵션` (서브) | — | LOW (T3 흡수) |
|
||||
| T5 | 2044 | T3 내부 VLM 결과 | AI 검증 결과 | — | LOW (인라인 토스트) |
|
||||
| T6 | 2366 | 사이드바 `간단 치수 추가` | 상세도면 업로드 | — | MED |
|
||||
| T7 | 2486 | T6 후속 | 치수 확인/편집 | 650×500 | MED |
|
||||
| T8 | 2723 | Step 1 후속 | `계획선 고도 설정` | 1280×560 | HIGH — 워크플로 핵심 |
|
||||
| T9 | 4624 | 사이드바 `🎯 TIN 이용 범위` | TIN core 선택 (matplotlib 내장) | 1100×920 | **CRITICAL** — interactive canvas |
|
||||
| T10 | 6537 | Step 4 시작 | `렌더링 옵션` (시간대/화질) | 380×360 | HIGH |
|
||||
| T11 | 6897 | Blender 렌더 결과 | 결과 이미지 뷰어 | 동적 | MED |
|
||||
| T12 | 6970 | AI 렌더 결과 | 결과 이미지 뷰어 | 동적 | MED |
|
||||
|
||||
**`messagebox` 63회 분포**: 거의 모두 (a) 모듈 미설치 경고, (b) 전제조건 안내("먼저 Step N을 수행하세요"), (c) 최종 완료/실패 메시지. 진짜 **위험한 결정**(askyesno) 은 line 2191/2213/2223/2238 (구조물 빌드 시 덮어쓰기 확인) — 4건.
|
||||
|
||||
### 1.4 PyVista 3D 뷰포트 — 가장 큰 함정
|
||||
**현재**: `pv.Plotter(title=...).show()` 를 호출 → **별도 OS 윈도우** 가 뜸 (VTK render window). line 1769, 5463, 5567, 5888, 6228 등 6개 호출지.
|
||||
- 메인 CustomTkinter 창과 **물리적으로 다른 창**. 사용자가 "한 화면에서" 보길 원하는 핵심 위반.
|
||||
- `pyvistaqt`/`QtInteractor` 임포트는 코드베이스에 **없음** (전체 검색 0건).
|
||||
- Tk + VTK 임베딩 표준 경로: `vtkmodules.tk.vtkTkRenderWidget` (Python 3.13 + PyVista 0.43+ 환경에서 동작 확인 필요) **OR** PyQt 의존성 추가 + `pyvistaqt.QtInteractor`.
|
||||
|
||||
### 1.5 사용자 워크플로 막힘 지점 (5분 룰 위반)
|
||||
1. **첫 화면이 위성지도** — 사용자는 DXF 작업이 목적인데 첫 화면에 빈 한국 지도가 떠 있음. 다음 단계가 사이드바 `1. TIN 생성 (DXF)` 임이 아이콘/색으로 강조되지 않음.
|
||||
2. **각 Step 후 새 창 강제** — Step 1 → T1 팝업(레이어 분류) → T8 팝업(고도 설정) → 닫힘 → 사이드바 클릭 → Step 2 → 위성 지도 갱신 → Step 3 → T9-likely 팝업 → Step 4 → T10 팝업. **창 4–5개를 ALT-TAB** 으로 오가야 한다.
|
||||
3. **3D 뷰가 별도 창** — 위성지도(메인)와 3D(VTK 별창)가 분리. 사용자는 두 창 사이에서 컨텍스트 잃음.
|
||||
4. **로그 패널 차지 면적** — 메인 영역 25% (`main_frame` row 1 weight=1, weight=3 vs 1) 가 로그. 사용자가 보지도 않는 텍스트가 차지.
|
||||
5. **상태바가 정보 부족** — `● READY` + 짧은 텍스트 1줄. 진행률 게이지 없음 (단순 set_status 호출).
|
||||
6. **에러 = messagebox 63회** — 매 실패마다 모달 창. 사용자 흐름 차단.
|
||||
|
||||
---
|
||||
|
||||
## 2. 새 레이아웃 (text mockup)
|
||||
|
||||
### 2.1 메인 셸 (단일 창)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ S-CANVAS ─ □ ✕ │
|
||||
├────────┬─────────────────────────────────────────┬───────────────────┤
|
||||
│ Sidebar│ Main Canvas │ Inspector │
|
||||
│ (240) │ (flex, weight=1) │ (340) │
|
||||
│ │ │ │
|
||||
│ ┌────┐ │ ┌───────────────────────────────────┐ │ ┌───────────────┐ │
|
||||
│ │ S │ │ │ │ │ │ Step 1: DXF │ │
|
||||
│ │CANVAS│ │ │ │ │ │ 로드 │ │
|
||||
│ └────┘ │ │ [PyVista 3D Viewport] │ │ ├───────────────┤ │
|
||||
│ │ │ (or fallback │ │ │ │ │
|
||||
│ Pipeline│ │ TkinterMapView when no │ │ │ DXF 파일: │ │
|
||||
│ │ │ TIN yet) │ │ │ [filepath...] │ │
|
||||
│ ① DXF │ │ │ │ │ [📂 찾아보기] │ │
|
||||
│ ② GeoR │ │ │ │ │ │ │
|
||||
│ ③ TIN │ │ │ │ │ CRS: ▼ EPSG.. │ │
|
||||
│ ④ Strct│ │ │ │ │ │ │
|
||||
│ ⑤ Rndr │ │ │ │ │ ┌──────────┐ │ │
|
||||
│ │ └───────────────────────────────────┘ │ │ │ 시작 → │ │ │
|
||||
│ ── │ ┌─Tab strip──────────────────────────┐ │ │ └──────────┘ │ │
|
||||
│ Settings│ │ [3D] [위성지도] [DXF 미리보기] │ │ │ │ │
|
||||
│ • Theme│ └───────────────────────────────────┘ │ │ ── 도움말 ─── │ │
|
||||
│ • API │ │ │ Step 1은 ... │ │
|
||||
│ • CRS │ │ └───────────────┘ │
|
||||
├────────┴─────────────────────────────────────────┴───────────────────┤
|
||||
│ ● Ready Step 1/5: DXF 로드 대기 [▮▮▮▮▮▯▯▯ 50%] [📋 log] │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 영역 정의
|
||||
|
||||
| 영역 | 폭 | 책임 | 구현 위젯 |
|
||||
|---|---|---|---|
|
||||
| **Sidebar** (left) | 240px 고정 | 네비게이션 (5단계 rail) + 글로벌 settings 토글 | `CTkScrollableFrame` 분리: 위 = pipeline rail, 아래 = settings (collapsed by default) |
|
||||
| **Main Canvas** (center) | flex | 항상 켜진 3D 뷰포트 + 보조 탭 (지도/DXF) | top: VTK 임베디드 위젯, bottom: 탭 strip |
|
||||
| **Inspector** (right) | 340px | **활성 step 의 폼**. step 바뀌면 내용만 swap (창 X) | `CTkFrame` + step 별 `_build_inspector_step{N}()` 메서드 |
|
||||
| **Status Bar** (bottom) | 32px | ● 인디케이터 + 현재 단계 + 진행률 + log 버튼 | 기존 `status_bar` 확장 |
|
||||
|
||||
### 2.3 사이드바 — 5단계 rail (텍스트만)
|
||||
|
||||
```
|
||||
PIPELINE
|
||||
─────────
|
||||
✓ ① DXF 로드 ← 완료 (체크, 녹)
|
||||
▶ ② GeoRef ← 활성 (▶, 청록 강조)
|
||||
○ ③ TIN + DEM ← 미진행 (회색)
|
||||
○ ④ Structures
|
||||
○ ⑤ Render (AI)
|
||||
─────────
|
||||
SETTINGS ▼ (collapsible)
|
||||
• API Key
|
||||
• CRS
|
||||
• Theme
|
||||
─────────
|
||||
SAMAN © Footer
|
||||
```
|
||||
|
||||
- 클릭 시: inspector 내용을 해당 step 폼으로 swap. 창 안 띄움.
|
||||
- 진행 상태 아이콘: ✓ (완료) / ▶ (활성) / ○ (대기) / ✕ (실패) — 일관된 4가지.
|
||||
|
||||
### 2.4 Inspector — step 별 폼
|
||||
|
||||
각 step 의 inspector 는 다음 표준을 따른다 (5분 룰 강제):
|
||||
|
||||
```
|
||||
┌───────────────────────┐
|
||||
│ ① DXF 로드 │ ← h1 (18pt bold)
|
||||
├───────────────────────┤
|
||||
│ │
|
||||
│ [현재 step 의 입력] │ ← 결정 ≤ 3개
|
||||
│ │
|
||||
│ ┌─ 시작 → ──────┐ │ ← primary button (#16A085, height=40)
|
||||
│ └────────────────┘ │
|
||||
│ │
|
||||
│ ── 도움말 ── │ ← caption (10pt gray)
|
||||
│ "DXF 파일을 골라 │
|
||||
│ CRS 를 확인하세요" │
|
||||
└───────────────────────┘
|
||||
```
|
||||
|
||||
**크리티컬 케이스**: step 9 (TIN core 선택) 의 matplotlib interactive canvas — 이건 inspector 가 아니라 **메인 canvas 영역에 임시 오버레이** 로 띄워야 함 (창 신설 금지). PyVista 뷰포트 위에 matplotlib FigureCanvasTkAgg 를 잠시 덮고, 확정 시 다시 PyVista 로 복귀.
|
||||
|
||||
### 2.5 Status Bar — 미니 로그 인디케이터
|
||||
|
||||
```
|
||||
● Running Step 3/5: 제어맵 추출 중 [▮▮▮▮▮▮▯▯ 70%] 📋 log (3 new)
|
||||
```
|
||||
|
||||
- ● 색: ready=녹(#2ECC71), running=청(#3498DB), warn=주황(#D35400), error=빨(#E74C3C).
|
||||
- 진행률: 8칸 ASCII bar, 단계별 percent.
|
||||
- `📋 log (N new)` 버튼 클릭 → **log drawer** (창 아님, 메인 canvas 위로 슬라이드 다운, 350px) 열림. 닫기 ✕ 로 즉시 사라짐. 내용은 `harness_log_path()` 의 tail 200줄.
|
||||
|
||||
---
|
||||
|
||||
## 3. 컴포넌트 매핑 (현재 → 신규)
|
||||
|
||||
| 현재 (popup) | 신규 (single-window) | 처리 방식 |
|
||||
|---|---|---|
|
||||
| T1 `DXF 레이어 분류` (900×650) | Inspector — Step 1 의 sub-form (스크롤) | 탭 또는 expandable section |
|
||||
| T2 `구조물 상세 3D 빌드` (1100×650) | Inspector — Step 4 main form | sidebar `④ Structures` 클릭 시 swap |
|
||||
| T3 빌드 다이얼로그 | Inspector 내부 진행 영역 + status bar 진행률 | inline progress |
|
||||
| T4 옵션 서브창 | T3 폼의 expandable "고급" 섹션 | 같은 inspector 안 |
|
||||
| T5 AI 검증 결과 | **inline toast** (canvas 우상단 8s) | 비모달 |
|
||||
| T6 상세도면 업로드 | Step 4 sub-form 내 파일 슬롯 | Drag&drop or browse |
|
||||
| T7 치수 확인 (650×500) | Step 4 sub-form 내 confirm panel | 같은 inspector 폼 안 |
|
||||
| T8 `계획선 고도 설정` (1280×560) | Inspector — Step 3 sub-form (Step 2.5 스타일) | scrollable form |
|
||||
| T9 TIN core 선택 (interactive) | **Main canvas overlay** (matplotlib) | 임시 mode swap, ESC=취소 |
|
||||
| T10 렌더링 옵션 (380×360) | Inspector — Step 5 main form | sidebar `⑤ Render` 클릭 |
|
||||
| T11/T12 결과 이미지 뷰어 | Main canvas 의 신규 탭 `[결과]` | tab swap, 우측에 메타 |
|
||||
|
||||
| 현재 (messagebox) | 신규 |
|
||||
|---|---|
|
||||
| `showerror` (≈30회) | inline toast (빨, 8s) + status bar 빨 |
|
||||
| `showwarning` (≈18회) | inline toast (주황, 6s) |
|
||||
| `showinfo` (≈12회) | inline toast (청, 4s) |
|
||||
| `askyesno` 4건 (덮어쓰기 확인) | **유지** (모달, 진짜 데이터 손실 위험 시만) |
|
||||
|
||||
| 현재 (log) | 신규 |
|
||||
|---|---|
|
||||
| `self.log()` 180회 → CTkTextbox | `self.log()` API 유지 + 내부에서 `logging.getLogger("scanvas").info(...)` 로 routing → `harness_log_path()` |
|
||||
| `main_frame` row 1 (CTkTextbox) | **삭제**. 그 공간을 main canvas 가 차지 |
|
||||
| status bar 의 `📋 log` 버튼 | log drawer (slide-down panel) — 개발자만 클릭 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 마이그레이션 단계 (4 phase)
|
||||
|
||||
### Phase A — Backend log 일원화 (1 session, 코드 ≈ 60 lines)
|
||||
- **목표**: `self.log()` 가 textbox 가 아니라 logger 로 흐르게. UI 구조는 그대로.
|
||||
- **변경**:
|
||||
1. `self.log(msg)` 내부 → `logging.getLogger("scanvas").info(msg)` 호출 추가 (textbox 도 유지 — 안전망).
|
||||
2. `setup_logging(log_file=harness_log_path())` 가 이미 line 228 에 있음. 이걸 `HARNESS_AVAILABLE=False` 분기에서도 fallback 으로 작동하게 보강.
|
||||
3. status bar 의 `● READY` 옆에 `📋 log` 버튼 prototype 추가 (클릭 → 별도 창으로 textbox 띄움 — Phase D 에서 drawer 로 교체).
|
||||
- **위험**: 거의 없음. log drawer 가 Phase D 에 있어서 백엔드 라우팅만 먼저.
|
||||
- **결과 게이트**: `harness_log_path()` 에 모든 메시지가 쌓이는지 grep 으로 확인.
|
||||
|
||||
### Phase B — Single-window shell (2 sessions, 코드 ≈ 350 lines)
|
||||
- **목표**: 3-column grid (sidebar | main canvas | inspector) + status bar 골격. 기능은 placeholder.
|
||||
- **변경**:
|
||||
1. `__init__` 의 grid 를 `grid_columnconfigure(0, weight=0); (1, weight=1); (2, weight=0)` 3-column 으로.
|
||||
2. 신규 `self.inspector_frame` (340px, 우측). 현재 사이드바 의 WORKFLOW 섹션을 inspector 의 step1 placeholder 로 옮김.
|
||||
3. 사이드바를 **rail** 모드로 단순화: 5단계 rail + collapsible SETTINGS.
|
||||
4. `main_frame` 에서 textbox 삭제 → 그 자리에 PyVista placeholder (지금은 빈 frame, Phase C 에서 채움).
|
||||
5. `_show_inspector(step_id)` 메서드 추가: step_id 로 해당 폼 swap.
|
||||
- **위험**: window resize 깨짐 — `grid_rowconfigure(0, weight=1)` 와 `weight=0` 분리 정확히 해야. inspector 폭 340 고정 + sidebar 240 고정 + canvas weight=1.
|
||||
- **결과 게이트**: 빈 inspector 가 step 클릭에 따라 라벨만 바꾸는 데모. 기능 미작동.
|
||||
|
||||
### Phase C — Step inspector forms 이식 (3 sessions, 코드 ≈ 800 lines)
|
||||
- **목표**: T1, T2, T8, T10 (HIGH 4개) 의 내용을 inspector 폼으로 옮김. 팝업은 아직 코드에 남겨둠 (병행 동작).
|
||||
- **변경**:
|
||||
1. **Step 1 inspector** ← T1 `DXF 레이어 분류` 의 scroll_frame + dropdown 을 inspector 폼으로 복제.
|
||||
2. **Step 3 inspector** ← T8 `계획선 고도 설정` 의 10-column scroll 을 inspector 폼으로. 폭이 좁으니 (340 < 1280) 한 row 당 2-line 으로 wrap.
|
||||
3. **Step 4 inspector** ← T2 `구조물 상세 3D 빌드` 의 row-per-structure 를 inspector accordion 으로.
|
||||
4. **Step 5 inspector** ← T10 `렌더링 옵션` (시간대/화질).
|
||||
5. inline toast 위젯 (CTkFrame 우상단 절대 배치, after(N, destroy)) — messagebox 대체용.
|
||||
- **위험**: T8 의 10-column 이 340px 에 안 들어감 → row 별 expandable section 으로 디자인 변경 필요. T9 (interactive matplotlib) 는 Phase D 까지 팝업 유지.
|
||||
- **결과 게이트**: 사이드바 5단계 모두 클릭 가능 + inspector 가 step 마다 정확한 폼 표시 + Step 1~5 회귀 테스트 통과.
|
||||
|
||||
### Phase D — 팝업 제거 + log drawer + 회귀 (1 session, 코드 ≈ 200 lines)
|
||||
- **목표**: 모든 `CTkToplevel(...)` 12개 제거 (T9 main canvas overlay 로 교체) + log drawer 완성.
|
||||
- **변경**:
|
||||
1. T1, T2, T8, T10 의 `CTkToplevel` 호출 코드 삭제 (Phase C 에서 inspector 가 이미 동작 중).
|
||||
2. T3, T4, T5, T6, T7 의 호출지 → inspector 의 sub-state 로 바뀜.
|
||||
3. T9 → main canvas 위에 matplotlib FigureCanvasTkAgg 를 띄우고 ESC/확정 시 사라짐. PyVista 모드 ↔ matplotlib 모드 swap.
|
||||
4. T11, T12 → main canvas 의 신규 `[결과]` 탭 으로.
|
||||
5. log drawer: status bar `📋 log` 클릭 → 메인 canvas 위에 slide-down (높이 350) panel + 닫기 ✕.
|
||||
6. messagebox 63회 → inline toast (showerror/warning/info) + askyesno 4건만 유지.
|
||||
- **위험**: T9 의 matplotlib pick event 가 main canvas 에서 동작 안 할 수 있음 — pyvista 와 matplotlib 가 같은 Tk frame 안에서 mode swap 시 grab 충돌.
|
||||
- **결과 게이트**: `Grep CTkToplevel` 결과 0개, `Grep "messagebox.show"` 결과 4개 미만 (askyesno 만), full Step 1~5 시나리오 manual 테스트.
|
||||
|
||||
### 누적 일정
|
||||
- Phase A: 1 session
|
||||
- Phase B: 2 sessions
|
||||
- Phase C: 3 sessions
|
||||
- Phase D: 1 session
|
||||
- **총 7 sessions**.
|
||||
|
||||
---
|
||||
|
||||
## 5. 5축 / Cities / Workflow 영향
|
||||
|
||||
### 5.1 5축 비전 영향
|
||||
- **축 1 (AI 물리 조감도)**: ✅ 영향 중립. AI 렌더 트랙(D003) 손대지 않음 — Step 5 inspector 는 기존 T10 폼의 lift-and-shift.
|
||||
- **축 2 (Cities-Skylines like, 5분 룰)**: ✅✅ 강한 개선. 단일 창에서 사이드바 → inspector → canvas 가 한눈에. 새 사용자가 ALT-TAB 안 해도 됨.
|
||||
- **축 3 (심미성)**: ✅ 일관성 향상. 12개 팝업의 제각각 폰트/패딩이 1개 inspector 표준으로 통일. 8px 그리드 강제.
|
||||
- **축 4 (라이브러리)**: ⚠️ 중립. STRUCTURE_REGISTRY 패턴 유지. 단, inspector 폼이 새 구조물 타입 추가 시 자동 확장돼야 함 (data-driven, 코드 수정 X).
|
||||
- **축 5 (성능)**: ✅ 팝업 생성/파괴 오버헤드 제거 (window create/destroy 는 비용). 단, **PyVista 임베딩이 Tk 에서 60FPS 유지될지 검증 필요** — 이게 Phase B 의 게이트.
|
||||
|
||||
### 5.2 두 워크플로 호환
|
||||
- **Workflow A (Engineering, DXF 기반)**: ✅ Step 1~5 이 그대로 사이드바 rail. 기존 사용자 학습곡선 단발성.
|
||||
- **Workflow B (Cities, 도면 없이)**: ✅ `cities_placement_widget.py` 가 별도 위젯 — Step 1 inspector 의 toggle 또는 사이드바 별도 모드 버튼으로 attach. 메인 canvas 공유 가능.
|
||||
|
||||
---
|
||||
|
||||
## 6. 위험 / 트레이드오프
|
||||
|
||||
| 위험 | 영향 | 완화 |
|
||||
|---|---|---|
|
||||
| **PyVista Tk 임베딩 미검증** | 치명. Phase B 의존. | Phase B 진입 전 5-line 프로토타입으로 `vtkTkRenderWidget` 동작 확인. 실패 시 Plan B = 메인 canvas 는 BackgroundPlotter 별창 유지하되 status bar 위에 thumbnail 동기화. |
|
||||
| **사용자 학습 곡선** | 중. 12개 팝업 다 사라짐 → "어디로 갔지?" | 첫 실행 시 5초 onboarding tooltip ("기존 팝업이 우측 inspector 로 이동했습니다"). |
|
||||
| **window resize 깨짐** | 중. CustomTkinter 약점. | `grid_columnconfigure(1, weight=1)` 만 flex, 0/2 는 fixed. 최소 폭 900px (사이드바 240+canvas 320+inspector 340). |
|
||||
| **백엔드 log 의 디버깅 불편** | 저. inline log 가 사라져 개발자 불편. | log drawer (status bar 버튼 1클릭, 350px) 가 즉시 표시. 파일 위치 status bar 우클릭 메뉴에 노출. |
|
||||
| **T9 matplotlib mode swap 복잡도** | 중. PyVista ↔ matplotlib 같은 frame 안 swap. | Phase D 까지 T9 만 팝업 유지하는 안전 fallback. 사용자에게 "1회만 별창" 양해. |
|
||||
| **Phase B-C 사이 코드 중복** | 저. 팝업 + inspector 병존. | Phase D 끝에 일괄 삭제. 중간 git tag 유지. |
|
||||
|
||||
---
|
||||
|
||||
## 7. 디자인 토큰 (ux-designer.md 기준 강제)
|
||||
|
||||
```python
|
||||
# 색상 (현재 사용 중 + 신규 0개)
|
||||
COLOR_PRIMARY = "#16A085" # rail 활성, primary button
|
||||
COLOR_PRIMARY_HOVER = "#117A65"
|
||||
COLOR_SUCCESS = "#2ECC71" # ● ready, ✓ 완료
|
||||
COLOR_INFO = "#3498DB" # ● running
|
||||
COLOR_WARN = "#D35400" # toast warning
|
||||
COLOR_DANGER = "#E74C3C" # ● error, ✕ 실패
|
||||
COLOR_NEUTRAL_LIGHT = "#F4F6F7" # canvas bg (light)
|
||||
COLOR_NEUTRAL_MID = "#95A5A6" # caption text
|
||||
COLOR_NEUTRAL_DARK = "#2C3E50" # body text
|
||||
|
||||
# 폰트 (5단계만)
|
||||
FONT_H1 = ctk.CTkFont(size=18, weight="bold") # inspector 제목
|
||||
FONT_H2 = ctk.CTkFont(size=14, weight="bold") # inspector 섹션 헤더, sidebar PIPELINE 헤더
|
||||
FONT_BODY = ctk.CTkFont(size=12) # form 라벨, status text
|
||||
FONT_BUTTON = ctk.CTkFont(size=11, weight="bold") # buttons
|
||||
FONT_MICRO = ctk.CTkFont(size=10) # caption, log timestamp
|
||||
|
||||
# 8px 그리드
|
||||
PAD_XS = 8
|
||||
PAD_SM = 16
|
||||
PAD_MD = 24
|
||||
PAD_LG = 32
|
||||
|
||||
# 폭/높이
|
||||
SIDEBAR_W = 240
|
||||
INSPECTOR_W = 340
|
||||
STATUS_BAR_H = 32
|
||||
LOG_DRAWER_H = 350
|
||||
MAIN_MIN_W = 320 # window 최소 폭 = 240+320+340 = 900
|
||||
|
||||
# CustomTkinter 한계 인지 → 하지 말 것
|
||||
# (1) 부드러운 애니메이션 (slide-down log drawer 는 즉각 show/hide)
|
||||
# (2) 그라디언트
|
||||
# (3) drop shadow (border 색 변화로 흉내)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 영향 받는 파일 (Phase 별 예상)
|
||||
|
||||
### Phase A (백엔드 log)
|
||||
- `D:\2026\PROGRAM\1_S-CANVAS\scanvas_maker.py` (line 691–696, 228, 595)
|
||||
- `D:\2026\PROGRAM\1_S-CANVAS\harness\logger.py` (이미 작성됨, 변경 없음 검증만)
|
||||
|
||||
### Phase B (single-window shell)
|
||||
- `D:\2026\PROGRAM\1_S-CANVAS\scanvas_maker.py` (line 268–600 layout 전면)
|
||||
- 신규 메서드 `_build_sidebar_rail()`, `_build_main_canvas()`, `_build_inspector()`, `_build_status_bar()`, `_show_inspector(step_id)`
|
||||
- PyVista 임베딩 프로토타입 (별도 5-line 파일에서 검증 후 본 코드 통합)
|
||||
|
||||
### Phase C (inspector forms)
|
||||
- `D:\2026\PROGRAM\1_S-CANVAS\scanvas_maker.py` (T1/T2/T8/T10 본문을 `_inspector_step{N}` 로 이전)
|
||||
- 신규: `_inline_toast(level, msg)` 헬퍼 (≈ 30 lines)
|
||||
|
||||
### Phase D (팝업 제거)
|
||||
- `D:\2026\PROGRAM\1_S-CANVAS\scanvas_maker.py` (line 766, 1419, 1596, 1889, 2044, 2366, 2486, 2723, 4624, 6537, 6897, 6970 의 `CTkToplevel(self)` 12 개소 정리)
|
||||
- 신규: `_log_drawer.py` (slide-down panel) — 또는 inline class
|
||||
|
||||
### 무영향 (보호 영역)
|
||||
- `harness/`, `gemini_renderer.py`, `blender_renderer.py` — AI 렌더 백엔드 (D003)
|
||||
- `dem_extender.py`, `geo_referencing.py`, `dxf_geometry.py` — 데이터 레이어
|
||||
- `structure_templates.py`, `structure_placement.py` — 라이브러리 (축 4)
|
||||
- `cities_placement_widget.py` — Workflow B (별도 트랙)
|
||||
|
||||
---
|
||||
|
||||
## 9. 결정 게이트 (사용자/검수자 확인 요청 항목)
|
||||
|
||||
다음은 **사용자 결정** 이 필요한 사항. 코드 작성 전 확정:
|
||||
|
||||
1. **PyVista 임베딩**: `vtkmodules.tk.vtkTkRenderWidget` (Tk 네이티브, 외부 의존성 없음) vs `pyvistaqt.QtInteractor` (Qt 의존성 추가) — 어느 쪽?
|
||||
- 권장: **vtkTkRenderWidget** (D001 결정 정신: 외부 의존성 최소).
|
||||
2. **inspector 폭 340px**: 1280×560 폭의 T8 폼이 row 당 2-line wrap 으로 들어가는 게 OK 인가, 아니면 inspector 폭을 480px 까지 늘릴 것인가?
|
||||
3. **사이드바 SETTINGS 접기**: 기본 collapsed 인가 expanded 인가? (5분 룰 = collapsed 권장 — 첫 사용자에게 옵션 폭격 X)
|
||||
4. **Workflow B 통합**: cities_placement_widget 을 사이드바 별도 toggle 로 둘 건지, 아니면 Step 1 inspector 의 sub-tab 인지?
|
||||
|
||||
---
|
||||
|
||||
## 10. 결론
|
||||
|
||||
**디자인 권장**: 진행 가능. 7 sessions 분량.
|
||||
|
||||
**핵심 가치 제안**:
|
||||
- 팝업 12개 → inspector 1개 (사용자가 보는 창의 수: 5+ → 1).
|
||||
- 인라인 로그 패널 차지 면적 (메인 25%) → log drawer (필요 시만, 0%).
|
||||
- messagebox 63회 → inline toast (비모달, 흐름 끊김 X).
|
||||
|
||||
**가장 큰 위험**: PyVista 임베딩의 기술적 미검증. Phase B 의 게이트로 5-line 프로토타입 먼저.
|
||||
|
||||
**가장 큰 보상**: 5분 룰 합격. 사용자가 ALT-TAB 없이 한 화면에서 워크플로 완주.
|
||||
|
||||
— Phase 0 끝. 구현은 사용자 GO 사인 후 Phase A 부터.
|
||||
Reference in New Issue
Block a user