GUI for finger v1
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
Finger dashboard layout — edit this file to define the panel hierarchy and sizes.
|
||||
|
||||
Structure:
|
||||
|
||||
┌─ toolbar ─────────────────────────────────────────────┐
|
||||
├─ TOP (stretch: top_stretch) ──────────────────────────┤
|
||||
│ ┌ mesh ─┬─ arrows ─┬─ charts ─┐ (left → right) │
|
||||
│ └───────┴───────────┴─────────┘ │
|
||||
├─ LOG (stretch: log_stretch) ──────────────────────────┤
|
||||
└───────────────────────────────────────────────────────┘
|
||||
|
||||
Each panel uses explicit width_px and height_px.
|
||||
top_stretch / log_stretch control how extra vertical space is shared on resize.
|
||||
|
||||
Edit TOP_ROW.slots to reorder or resize panels.
|
||||
Edit PANEL_SETUP to change top vs log proportions.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
PanelId = Literal["mesh", "arrows", "charts", "log"]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PanelSlot:
|
||||
"""One panel in the dashboard."""
|
||||
|
||||
id: PanelId
|
||||
title: str
|
||||
width_px: int
|
||||
height_px: int
|
||||
stretch: int = 0
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PanelRow:
|
||||
"""Horizontal row of panels (the top visual strip)."""
|
||||
|
||||
slots: tuple[PanelSlot, ...]
|
||||
collapsible: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PanelSetup:
|
||||
"""Full panel hierarchy: top row + bottom log."""
|
||||
|
||||
top_row: PanelRow
|
||||
log_panel: PanelSlot
|
||||
top_stretch: int = 5
|
||||
log_stretch: int = 1
|
||||
|
||||
|
||||
# --- Top strip: mesh | arrows | charts (edit order and sizes here) ---
|
||||
TOP_ROW = PanelRow(
|
||||
slots=(
|
||||
PanelSlot("mesh", "3D mesh", width_px=500, height_px=500),
|
||||
PanelSlot("arrows", "UV Unrolled", width_px=500, height_px=500),
|
||||
PanelSlot("charts", "Force charts", width_px=500, height_px=500),
|
||||
),
|
||||
)
|
||||
|
||||
LOG_PANEL = PanelSlot("log", "Log", width_px=0, height_px=100)
|
||||
|
||||
PANEL_SETUP = PanelSetup(
|
||||
top_row=TOP_ROW,
|
||||
log_panel=LOG_PANEL,
|
||||
top_stretch=5,
|
||||
log_stretch=1,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DashboardLayout:
|
||||
"""Window chrome, chart styling, and panel hierarchy."""
|
||||
|
||||
panel_setup: PanelSetup = PANEL_SETUP
|
||||
|
||||
window_title: str = "Orisys Finger Dashboard"
|
||||
window_width: int = 1500
|
||||
window_height: int = 600
|
||||
|
||||
root_margin_px: int = 6
|
||||
root_spacing_px: int = 6
|
||||
toolbar_spacing_px: int = 8
|
||||
|
||||
plot_buffer_points: int = 120
|
||||
plot_sample_rate_hz: float = 30.0
|
||||
chart_title_font_pt: int = 11
|
||||
chart_axis_label_font_pt: int = 9
|
||||
chart_tick_font_pt: int = 9
|
||||
y_axis_min_span: float = 20000.0
|
||||
y_range_smooth_alpha: float = 0.5
|
||||
curve_color: str = "#00E5FF"
|
||||
|
||||
# UV arrow panel: min flow magnitude to draw an arrow (display only; not dd02-ov.json contact.flow_threshold)
|
||||
arrow_flow_threshold: float = 1.0
|
||||
|
||||
timer_interval_ms: int = 10
|
||||
|
||||
def slot(self, panel_id: PanelId) -> PanelSlot | None:
|
||||
if self.panel_setup.log_panel.id == panel_id:
|
||||
return self.panel_setup.log_panel
|
||||
for slot in self.panel_setup.top_row.slots:
|
||||
if slot.id == panel_id:
|
||||
return slot
|
||||
return None
|
||||
|
||||
def top_slot(self, panel_id: PanelId) -> PanelSlot:
|
||||
for slot in self.panel_setup.top_row.slots:
|
||||
if slot.id == panel_id:
|
||||
return slot
|
||||
raise KeyError(f"panel {panel_id!r} not in top row")
|
||||
|
||||
@property
|
||||
def mesh_panel_title(self) -> str:
|
||||
return self.top_slot("mesh").title
|
||||
|
||||
@property
|
||||
def arrows_panel_title(self) -> str:
|
||||
return self.top_slot("arrows").title
|
||||
|
||||
|
||||
LAYOUT = DashboardLayout()
|
||||
|
||||
|
||||
def top_row_splitter_sizes(layout: DashboardLayout = LAYOUT) -> tuple[int, ...]:
|
||||
"""Horizontal splitter widths from panel setup."""
|
||||
return tuple(slot.width_px for slot in layout.panel_setup.top_row.slots if slot.enabled)
|
||||
|
||||
|
||||
def top_row_slot_index(panel_id: PanelId, layout: DashboardLayout = LAYOUT) -> int:
|
||||
"""Index of a panel in the horizontal top splitter."""
|
||||
for i, slot in enumerate(layout.panel_setup.top_row.slots):
|
||||
if slot.id == panel_id:
|
||||
return i
|
||||
raise KeyError(f"panel {panel_id!r} not in top row")
|
||||
Reference in New Issue
Block a user