| """Startup mode menu.""" |
|
|
| from __future__ import annotations |
|
|
| import cv2 |
| import numpy as np |
|
|
| import ui |
|
|
| WINDOW = "Hand Tracking Drawing" |
| ITEMS = ( |
| ("2d", "2D Drawing"), |
| ("3d", "3D Drawing"), |
| ("poses", "Poses"), |
| ("translater", "Translater"), |
| ) |
| W, H = 720, 480 |
| TOP = 150 |
| ROW_H = 56 |
| PAD = 60 |
|
|
|
|
| class Menu: |
| """Mode selection screen.""" |
|
|
| def __init__(self) -> None: |
| self.index = 0 |
| self.click = -1 |
| self.note = "" |
|
|
| def on_mouse(self, event: int, x: int, y: int, flags: int, param) -> None: |
| """Track hover and clicks.""" |
| row = (y - TOP) // ROW_H |
| if not 0 <= row < len(ITEMS) or not PAD <= x <= W - PAD: |
| return |
| self.index = int(row) |
| if event == cv2.EVENT_LBUTTONDOWN: |
| self.click = int(row) |
|
|
| def frame(self) -> np.ndarray: |
| """Render menu image.""" |
| img = np.zeros((H, W, 3), dtype=np.uint8) |
| ui.put_text(img, "HAND TRACKING DRAWING", (PAD, 54), 26, (255, 255, 255)) |
| ui.put_text(img, "Select a mode", (PAD, 96), 17, (150, 150, 150)) |
| for i, (_, label) in enumerate(ITEMS): |
| y = TOP + i * ROW_H |
| box = (PAD, y, W - PAD, y + ROW_H - 12) |
| if i == self.index: |
| cv2.rectangle(img, box[:2], box[2:], (45, 45, 45), -1) |
| cv2.rectangle(img, box[:2], box[2:], (200, 200, 200), 1, cv2.LINE_AA) |
| color = (255, 255, 255) |
| else: |
| cv2.rectangle(img, box[:2], box[2:], (70, 70, 70), 1, cv2.LINE_AA) |
| color = (185, 185, 185) |
| ui.put_text(img, f"{i + 1}) {label}", (PAD + 20, y + 12), 19, color) |
| ui.put_text(img, "1..4 select W S move ENTER start Q quit", |
| (PAD, H - 70), 16, (140, 140, 140)) |
| if self.note: |
| ui.put_text(img, self.note, (PAD, H - 42), 16, (120, 120, 230)) |
| return img |
|
|
| def run(self) -> str | None: |
| """Show menu and return mode.""" |
| cv2.namedWindow(WINDOW, cv2.WINDOW_NORMAL) |
| cv2.resizeWindow(WINDOW, W, H) |
| cv2.setMouseCallback(WINDOW, self.on_mouse) |
| while True: |
| cv2.imshow(WINDOW, self.frame()) |
| key = cv2.waitKey(20) & 0xFF |
| if cv2.getWindowProperty(WINDOW, cv2.WND_PROP_VISIBLE) < 1: |
| return None |
|
|
| chosen = -1 |
| if self.click >= 0: |
| chosen, self.click = self.click, -1 |
| elif key in (ord("q"), 27): |
| cv2.destroyWindow(WINDOW) |
| cv2.waitKey(1) |
| return None |
| elif key in (ord("w"), 82): |
| self.index = (self.index - 1) % len(ITEMS) |
| elif key in (ord("s"), 84): |
| self.index = (self.index + 1) % len(ITEMS) |
| elif key in (13, 10, 32): |
| chosen = self.index |
| elif ord("1") <= key <= ord("0") + len(ITEMS): |
| chosen = key - ord("1") |
|
|
| if chosen < 0: |
| continue |
| self.index = chosen |
| name = ITEMS[chosen][0] |
| if name in ("2d", "3d", "poses"): |
| cv2.destroyWindow(WINDOW) |
| cv2.waitKey(1) |
| return name |
| self.note = f"{ITEMS[chosen][1]} is not available yet" |
|
|