This commit is contained in:
firestar5683
2026-04-14 17:16:17 -05:00
parent 2e20cd061f
commit 1ce03e79f0
3 changed files with 119 additions and 46 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ from openpilot.system.ui.widgets import Widget
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
CONNECTION_RETRY_INTERVAL = 0.2 # seconds between connection attempts
MICI_FORCE_TEXTURE_CAMERA = os.getenv("MICI_FORCE_TEXTURE_CAMERA", "1") == "1"
MICI_FORCE_TEXTURE_CAMERA = os.getenv("MICI_FORCE_TEXTURE_CAMERA", "0") == "1"
VERSION = """
#version 300 es
+6 -2
View File
@@ -22,7 +22,8 @@ from openpilot.system.hardware import HARDWARE, PC
from openpilot.system.ui.lib.multilang import multilang
from openpilot.common.realtime import Ratekeeper
_DEFAULT_FPS = int(os.getenv("FPS", {'tizi': 20}.get(HARDWARE.get_device_type(), 60)))
DEVICE_TYPE = HARDWARE.get_device_type()
_DEFAULT_FPS = int(os.getenv("FPS", {'tizi': 20}.get(DEVICE_TYPE, 60)))
FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops
FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning
FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions
@@ -32,6 +33,7 @@ TOUCH_HISTORY_TIMEOUT = 3.0 # Seconds before touch points fade out
BIG_UI = os.getenv("BIG", "0") == "1"
ENABLE_VSYNC = os.getenv("ENABLE_VSYNC", "0") == "1"
MICI_FORCE_RENDER_TEXTURE = os.getenv("MICI_FORCE_RENDER_TEXTURE", "1" if DEVICE_TYPE == "mici" else "0") == "1"
SHOW_FPS = os.getenv("SHOW_FPS") == "1"
SHOW_TOUCHES = os.getenv("SHOW_TOUCHES") == "1"
STRICT_MODE = os.getenv("STRICT_MODE") == "1"
@@ -279,10 +281,12 @@ class GuiApplication:
rl.init_window(self._scaled_width, self._scaled_height, title)
needs_render_texture = self._scale != 1.0 or BURN_IN_MODE or RECORD
needs_render_texture = self._scale != 1.0 or BURN_IN_MODE or RECORD or MICI_FORCE_RENDER_TEXTURE
if self._scale != 1.0:
rl.set_mouse_scale(1 / self._scale, 1 / self._scale)
if needs_render_texture:
if MICI_FORCE_RENDER_TEXTURE:
cloudlog.warning("Forcing render texture path for mici UI")
self._render_texture = rl.load_render_texture(self._scaled_width, self._scaled_height)
rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR)
+112 -43
View File
@@ -1,30 +1,55 @@
#!/usr/bin/env python3
from dataclasses import dataclass
import pyray as rl
import select
import sys
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.application import FontWeight, gui_app
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.text import wrap_text
from openpilot.system.ui.widgets import Widget
# Constants
if gui_app.big_ui():
PROGRESS_BAR_WIDTH = 1000
PROGRESS_BAR_HEIGHT = 20
TEXTURE_SIZE = 360
WRAPPED_SPACING = 50
CENTERED_SPACING = 150
else:
PROGRESS_BAR_WIDTH = 268
PROGRESS_BAR_HEIGHT = 10
TEXTURE_SIZE = 140
WRAPPED_SPACING = 10
CENTERED_SPACING = 20
@dataclass(frozen=True)
class SpinnerTheme:
progress_bar_width: int
progress_bar_height: int
texture_size: int
wrapped_spacing: int
centered_spacing: int
text_font_size: int
line_height: int
horizontal_margin: int
progress_label_font_size: int | None = None
BIG_THEME = SpinnerTheme(
progress_bar_width=1000,
progress_bar_height=20,
texture_size=360,
wrapped_spacing=50,
centered_spacing=150,
text_font_size=96,
line_height=104,
horizontal_margin=100,
)
SMALL_THEME = SpinnerTheme(
progress_bar_width=320,
progress_bar_height=12,
texture_size=92,
wrapped_spacing=14,
centered_spacing=26,
text_font_size=30,
line_height=36,
horizontal_margin=24,
progress_label_font_size=24,
)
DEGREES_PER_SECOND = 360.0 # one full rotation per second
MARGIN_H = 100
FONT_SIZE = 96
LINE_HEIGHT = 104
SMALL_MAX_WRAPPED_LINES = 4
PROGRESS_LABEL_SPACING = 12
DARKGRAY = (55, 55, 55, 255)
@@ -35,11 +60,14 @@ def clamp(value, min_value, max_value):
class Spinner(Widget):
def __init__(self):
super().__init__()
self._comma_texture = gui_app.texture("images/spinner_comma.png", TEXTURE_SIZE, TEXTURE_SIZE)
self._spinner_texture = gui_app.texture("images/spinner_track.png", TEXTURE_SIZE, TEXTURE_SIZE, alpha_premultiply=True)
self._theme = BIG_THEME if gui_app.big_ui() else SMALL_THEME
self._comma_texture = gui_app.texture("images/spinner_comma.png", self._theme.texture_size, self._theme.texture_size)
self._spinner_texture = gui_app.texture("images/spinner_track.png", self._theme.texture_size, self._theme.texture_size, alpha_premultiply=True)
self._rotation = 0.0
self._progress: int | None = None
self._wrapped_lines: list[str] = []
self._text_font_size = self._theme.text_font_size
self._line_height = self._theme.line_height
def set_text(self, text: str) -> None:
if text.isdigit():
@@ -47,45 +75,86 @@ class Spinner(Widget):
self._wrapped_lines = []
else:
self._progress = None
self._wrapped_lines = wrap_text(text, FONT_SIZE, gui_app.width - MARGIN_H)
self._wrapped_lines, self._text_font_size = self._wrap_text(text)
scale = self._text_font_size / self._theme.text_font_size
self._line_height = round(self._theme.line_height * scale)
def _wrap_text(self, text: str) -> tuple[list[str], int]:
max_width = gui_app.width - self._theme.horizontal_margin * 2
font_sizes = [self._theme.text_font_size]
if not gui_app.big_ui():
font_sizes.extend([28, 26, 24])
for font_size in font_sizes:
wrapped_lines = wrap_text(text, font_size, max_width)
if gui_app.big_ui() or len(wrapped_lines) <= SMALL_MAX_WRAPPED_LINES:
return wrapped_lines, font_size
return wrapped_lines, font_sizes[-1]
def _content_height(self) -> float:
if self._progress is not None:
height = self._theme.texture_size + self._theme.centered_spacing + self._theme.progress_bar_height
if self._theme.progress_label_font_size is not None:
height += PROGRESS_LABEL_SPACING + self._theme.progress_label_font_size
return height
if self._wrapped_lines:
return self._theme.texture_size + self._theme.wrapped_spacing + len(self._wrapped_lines) * self._line_height
return self._theme.texture_size
def _draw_spinner(self, center_x: float, center_y: float) -> None:
spinner_origin = rl.Vector2(self._theme.texture_size / 2.0, self._theme.texture_size / 2.0)
comma_position = rl.Vector2(center_x - self._theme.texture_size / 2.0, center_y - self._theme.texture_size / 2.0)
rl.draw_texture_pro(
self._spinner_texture,
rl.Rectangle(0, 0, self._theme.texture_size, self._theme.texture_size),
rl.Rectangle(center_x, center_y, self._theme.texture_size, self._theme.texture_size),
spinner_origin,
self._rotation,
rl.WHITE,
)
rl.draw_texture_v(self._comma_texture, comma_position, rl.WHITE)
def _render(self, rect: rl.Rectangle):
if self._wrapped_lines:
# Calculate total height required for spinner and text
spacing = WRAPPED_SPACING
total_height = TEXTURE_SIZE + spacing + len(self._wrapped_lines) * LINE_HEIGHT
center_y = (rect.height - total_height) / 2.0 + TEXTURE_SIZE / 2.0
else:
# Center spinner vertically
spacing = CENTERED_SPACING
center_y = rect.height / 2.0
y_pos = center_y + TEXTURE_SIZE / 2.0 + spacing
total_height = self._content_height()
top_y = rect.y + (rect.height - total_height) / 2.0
center_x = rect.x + rect.width / 2.0
center_y = top_y + self._theme.texture_size / 2.0
center = rl.Vector2(rect.width / 2.0, center_y)
spinner_origin = rl.Vector2(TEXTURE_SIZE / 2.0, TEXTURE_SIZE / 2.0)
comma_position = rl.Vector2(center.x - TEXTURE_SIZE / 2.0, center.y - TEXTURE_SIZE / 2.0)
y_pos = top_y + self._theme.texture_size
y_pos += self._theme.wrapped_spacing if self._wrapped_lines else self._theme.centered_spacing
delta_time = rl.get_frame_time()
self._rotation = (self._rotation + DEGREES_PER_SECOND * delta_time) % 360.0
# Draw rotating spinner and static comma logo
rl.draw_texture_pro(self._spinner_texture, rl.Rectangle(0, 0, TEXTURE_SIZE, TEXTURE_SIZE),
rl.Rectangle(center.x, center.y, TEXTURE_SIZE, TEXTURE_SIZE),
spinner_origin, self._rotation, rl.WHITE)
rl.draw_texture_v(self._comma_texture, comma_position, rl.WHITE)
self._draw_spinner(center_x, center_y)
# Display the progress bar or text based on user input
if self._progress is not None:
bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT)
bar = rl.Rectangle(
center_x - self._theme.progress_bar_width / 2.0,
y_pos,
self._theme.progress_bar_width,
self._theme.progress_bar_height,
)
rl.draw_rectangle_rounded(bar, 1, 10, DARKGRAY)
bar.width *= self._progress / 100.0
rl.draw_rectangle_rounded(bar, 1, 10, rl.WHITE)
if self._theme.progress_label_font_size is not None:
progress_text = f"{self._progress}%"
font = gui_app.font(FontWeight.BOLD)
text_size = measure_text_cached(font, progress_text, self._theme.progress_label_font_size)
text_pos = rl.Vector2(center_x - text_size.x / 2.0, y_pos + bar.height + PROGRESS_LABEL_SPACING)
rl.draw_text_ex(font, progress_text, text_pos, self._theme.progress_label_font_size, 0.0, rl.Color(255, 255, 255, 180))
elif self._wrapped_lines:
font = gui_app.font(FontWeight.BOLD if not gui_app.big_ui() else FontWeight.NORMAL)
for i, line in enumerate(self._wrapped_lines):
text_size = measure_text_cached(gui_app.font(), line, FONT_SIZE)
rl.draw_text_ex(gui_app.font(), line, rl.Vector2(center.x - text_size.x / 2, y_pos + i * LINE_HEIGHT),
FONT_SIZE, 0.0, rl.WHITE)
text_size = measure_text_cached(font, line, self._text_font_size)
line_y = y_pos + i * self._line_height
rl.draw_text_ex(font, line, rl.Vector2(center_x - text_size.x / 2.0, line_y), self._text_font_size, 0.0, rl.WHITE)
def _read_stdin():