First upload, 18 controller version
This commit is contained in:
118
app/ui/preview_widget.py
Normal file
118
app/ui/preview_widget.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.qt_compat import QPainter, QPointF, QRectF, Qt, Signal, QWidget, event_posf
|
||||
|
||||
from app.config.models import SegmentConfig, TileConfig
|
||||
from app.core.geometry import segment_led_positions, segment_side
|
||||
from app.core.types import PreviewFrame
|
||||
|
||||
from .preview_layout import compute_preview_layout, segment_display_rect, tile_aspect_ratio
|
||||
from .preview_modes import (
|
||||
PREVIEW_MODE_LEDS,
|
||||
PREVIEW_MODE_TECHNICAL,
|
||||
PREVIEW_MODE_TILE,
|
||||
normalize_preview_mode,
|
||||
preview_mode_flags,
|
||||
)
|
||||
from .preview_painter import paint_empty_preview, paint_preview_scene
|
||||
|
||||
|
||||
class PreviewWidget(QWidget):
|
||||
tileClicked = Signal(str)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
controller,
|
||||
preview_mode: str = PREVIEW_MODE_TILE,
|
||||
scene_role: str = "live",
|
||||
technical_preview: bool | None = None,
|
||||
parent: QWidget | None = None,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
self.scene_role = "next" if str(scene_role).strip().lower() == "next" else "live"
|
||||
if technical_preview is not None:
|
||||
preview_mode = PREVIEW_MODE_TECHNICAL if technical_preview else PREVIEW_MODE_TILE
|
||||
self.preview_mode = normalize_preview_mode(preview_mode)
|
||||
self.technical_preview = self.preview_mode == PREVIEW_MODE_TECHNICAL
|
||||
self.current_frame: PreviewFrame | None = self.controller.preview_frame_for(self.scene_role)
|
||||
self._tile_rects: dict[str, QRectF] = {}
|
||||
|
||||
self.setMinimumSize(640, 360)
|
||||
self.setMouseTracking(True)
|
||||
|
||||
if self.scene_role == "next":
|
||||
self.controller.next_frame_ready.connect(self._on_frame_ready)
|
||||
else:
|
||||
self.controller.frame_ready.connect(self._on_frame_ready)
|
||||
self.controller.config_changed.connect(self.update)
|
||||
self.controller.state_changed.connect(self.update)
|
||||
|
||||
def set_preview_mode(self, mode: str) -> None:
|
||||
self.preview_mode = normalize_preview_mode(mode)
|
||||
self.technical_preview = self.preview_mode == PREVIEW_MODE_TECHNICAL
|
||||
self.update()
|
||||
|
||||
def set_technical_preview(self, enabled: bool) -> None:
|
||||
self.set_preview_mode(PREVIEW_MODE_TECHNICAL if enabled else PREVIEW_MODE_TILE)
|
||||
|
||||
def _mode_flags(self) -> dict[str, bool]:
|
||||
return preview_mode_flags(self.preview_mode)
|
||||
|
||||
def _on_frame_ready(self, frame: PreviewFrame) -> None:
|
||||
self.current_frame = frame
|
||||
self.update()
|
||||
|
||||
def mousePressEvent(self, event) -> None:
|
||||
point = event_posf(event)
|
||||
for tile_id, rect in self._tile_rects.items():
|
||||
if rect.contains(point):
|
||||
self.tileClicked.emit(tile_id)
|
||||
break
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def paintEvent(self, event) -> None:
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing, True)
|
||||
painter.setRenderHint(QPainter.TextAntialiasing, True)
|
||||
|
||||
frame = self.current_frame
|
||||
if frame is None or not self.controller.config.tiles:
|
||||
paint_empty_preview(painter, QRectF(self.rect()))
|
||||
return
|
||||
|
||||
layout = compute_preview_layout(QRectF(self.rect()), self.controller.config)
|
||||
self._tile_rects = layout.tile_rects
|
||||
paint_preview_scene(
|
||||
painter,
|
||||
config=self.controller.config,
|
||||
frame=frame,
|
||||
preview_mode=self.preview_mode,
|
||||
selected_tile_id=self.controller.selected_tile_id,
|
||||
target_rect=QRectF(self.rect()),
|
||||
layout=layout,
|
||||
)
|
||||
|
||||
def _compute_layout(self) -> tuple[QRectF, dict[str, QRectF]]:
|
||||
layout = compute_preview_layout(QRectF(self.rect()), self.controller.config)
|
||||
return layout.canvas_rect, layout.tile_rects
|
||||
|
||||
def _tile_aspect_ratio(self) -> float:
|
||||
return tile_aspect_ratio(self.controller.config)
|
||||
|
||||
def _segment_display_rect(self, tile: TileConfig, rect: QRectF) -> QRectF:
|
||||
return segment_display_rect(tile, rect)
|
||||
|
||||
def _segment_side(self, tile: TileConfig, segment: SegmentConfig) -> str | None:
|
||||
return segment_side(tile, segment)
|
||||
|
||||
def _segment_points_for_side(self, tile: TileConfig, segment: SegmentConfig, rect: QRectF) -> list[QPointF]:
|
||||
inset = max(2.0, min(rect.width(), rect.height()) * 0.02)
|
||||
insets = (
|
||||
inset / max(1.0, rect.width()),
|
||||
inset / max(1.0, rect.height()),
|
||||
)
|
||||
return [
|
||||
QPointF(rect.left() + x_pos * rect.width(), rect.top() + y_pos * rect.height())
|
||||
for x_pos, y_pos in segment_led_positions(tile, segment, insets=insets)
|
||||
]
|
||||
Reference in New Issue
Block a user