From 3ce87d0ac9049a362fcad50b07ad0d6e70694d14 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 6 Jun 2025 19:32:03 -0700 Subject: [PATCH] raylib: base widget class (#35484) * use some widgets * consistent name draw -> render * more * rest --- selfdrive/ui/layouts/home.py | 4 ++-- selfdrive/ui/layouts/main.py | 3 ++- selfdrive/ui/layouts/network.py | 3 ++- selfdrive/ui/layouts/settings/developer.py | 3 ++- selfdrive/ui/layouts/settings/device.py | 3 ++- selfdrive/ui/layouts/settings/settings.py | 4 ++-- selfdrive/ui/layouts/settings/software.py | 3 ++- selfdrive/ui/layouts/settings/toggles.py | 3 ++- selfdrive/ui/layouts/sidebar.py | 4 ++-- selfdrive/ui/onroad/alert_renderer.py | 8 ++++---- selfdrive/ui/onroad/augmented_road_view.py | 8 ++++---- selfdrive/ui/onroad/cameraview.py | 4 ++-- selfdrive/ui/onroad/driver_camera_view.py | 13 ++++++------- selfdrive/ui/onroad/driver_state.py | 10 +++++----- selfdrive/ui/onroad/hud_renderer.py | 8 ++++---- selfdrive/ui/onroad/model_renderer.py | 8 +++++--- selfdrive/ui/widgets/offroad_alerts.py | 4 ++-- system/ui/lib/application.py | 7 +++++++ system/ui/lib/inputbox.py | 4 ++-- system/ui/lib/list_view.py | 19 +++++++------------ system/ui/lib/toggle.py | 3 ++- system/ui/reset.py | 4 ++-- system/ui/setup.py | 4 ++-- system/ui/spinner.py | 6 +++--- system/ui/text.py | 6 +++--- system/ui/updater.py | 6 +++--- system/ui/widgets/keyboard.py | 4 ++-- system/ui/widgets/network.py | 4 ++-- system/ui/widgets/option_dialog.py | 3 ++- 29 files changed, 87 insertions(+), 76 deletions(-) diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index a3d5a0d7e5..11d87cd112 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -6,7 +6,7 @@ from openpilot.common.params import Params from openpilot.selfdrive.ui.widgets.offroad_alerts import UpdateAlert, OffroadAlert from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.label import gui_label -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR, Widget HEADER_HEIGHT = 80 HEAD_BUTTON_FONT_SIZE = 40 @@ -24,7 +24,7 @@ class HomeLayoutState(IntEnum): ALERTS = 2 -class HomeLayout: +class HomeLayout(Widget): def __init__(self): self.params = Params() diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 08ce50407d..a6beabe671 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -5,6 +5,7 @@ from openpilot.selfdrive.ui.layouts.home import HomeLayout from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView +from openpilot.system.ui.lib.application import Widget class MainState(IntEnum): @@ -13,7 +14,7 @@ class MainState(IntEnum): ONROAD = 2 -class MainLayout: +class MainLayout(Widget): def __init__(self): self._sidebar = Sidebar() self._sidebar_visible = True diff --git a/selfdrive/ui/layouts/network.py b/selfdrive/ui/layouts/network.py index 834234c47d..cc28b7cff5 100644 --- a/selfdrive/ui/layouts/network.py +++ b/selfdrive/ui/layouts/network.py @@ -1,9 +1,10 @@ import pyray as rl +from openpilot.system.ui.lib.application import Widget from openpilot.system.ui.lib.wifi_manager import WifiManagerWrapper from openpilot.system.ui.widgets.network import WifiManagerUI -class NetworkLayout: +class NetworkLayout(Widget): def __init__(self): self.wifi_manager = WifiManagerWrapper() self.wifi_ui = WifiManagerUI(self.wifi_manager) diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py index 0b40cbbb60..ba2c21bc38 100644 --- a/selfdrive/ui/layouts/settings/developer.py +++ b/selfdrive/ui/layouts/settings/developer.py @@ -1,3 +1,4 @@ +from openpilot.system.ui.lib.application import Widget from openpilot.system.ui.lib.list_view import ListView, toggle_item from openpilot.common.params import Params @@ -11,7 +12,7 @@ DESCRIPTIONS = { } -class DeveloperLayout: +class DeveloperLayout(Widget): def __init__(self): self._params = Params() items = [ diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 5051dd6ba6..25b275e863 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -1,3 +1,4 @@ +from openpilot.system.ui.lib.application import Widget from openpilot.system.ui.lib.list_view import ListView, text_item, button_item from openpilot.common.params import Params from openpilot.system.hardware import TICI @@ -14,7 +15,7 @@ DESCRIPTIONS = { } -class DeviceLayout: +class DeviceLayout(Widget): def __init__(self): params = Params() dongle_id = params.get("DongleId", encoding="utf-8") or "N/A" diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index 1afa4f8f4a..9611d96a2b 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -7,7 +7,7 @@ from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget from openpilot.system.ui.lib.label import gui_text_box from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.selfdrive.ui.layouts.network import NetworkLayout @@ -48,7 +48,7 @@ class PanelInfo: button_rect: rl.Rectangle -class SettingsLayout: +class SettingsLayout(Widget): def __init__(self): self._params = Params() self._current_panel = PanelType.DEVICE diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 586b311424..b50526332d 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -1,7 +1,8 @@ +from openpilot.system.ui.lib.application import Widget from openpilot.system.ui.lib.list_view import ListView, button_item, text_item -class SoftwareLayout: +class SoftwareLayout(Widget): def __init__(self): items = [ text_item("Current Version", ""), diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index e774c6f2f5..04c858c97a 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -1,3 +1,4 @@ +from openpilot.system.ui.lib.application import Widget from openpilot.system.ui.lib.list_view import ListView, toggle_item from openpilot.common.params import Params @@ -18,7 +19,7 @@ DESCRIPTIONS = { } -class TogglesLayout: +class TogglesLayout(Widget): def __init__(self): self._params = Params() items = [ diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 5f92f0eb20..637cd12217 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from collections.abc import Callable from cereal import log from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget from openpilot.system.ui.lib.text_measure import measure_text_cached SIDEBAR_WIDTH = 300 @@ -60,7 +60,7 @@ class MetricData: self.color = color -class Sidebar: +class Sidebar(Widget): def __init__(self): self._net_type = NETWORK_TYPES.get(NetworkType.none) self._net_strength = 0 diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 64a5f0d1a1..7802959e73 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -3,7 +3,7 @@ import pyray as rl from dataclasses import dataclass from cereal import messaging, log from openpilot.system.hardware import TICI -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_FPS +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_FPS, Widget from openpilot.system.ui.lib.label import gui_text_box from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.selfdrive.ui.ui_state import ui_state @@ -59,7 +59,7 @@ ALERT_CRITICAL_REBOOT = Alert( ) -class AlertRenderer: +class AlertRenderer(Widget): def __init__(self): self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL) self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD) @@ -91,8 +91,8 @@ class AlertRenderer: # Return current alert return Alert(text1=ss.alertText1, text2=ss.alertText2, size=ss.alertSize, status=ss.alertStatus) - def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster) -> None: - alert = self.get_alert(sm) + def render(self, rect: rl.Rectangle) -> None: + alert = self.get_alert(ui_state.sm) if not alert: return diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 131ba3dd79..2a7fe3c67f 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -78,10 +78,10 @@ class AugmentedRoadView(CameraView): super().render(rect) # Draw all UI overlays - self.model_renderer.draw(self._content_rect, ui_state.sm) - self._hud_renderer.draw(self._content_rect, ui_state.sm) - self.alert_renderer.draw(self._content_rect, ui_state.sm) - self.driver_state_renderer.draw(self._content_rect, ui_state.sm) + self.model_renderer.render(self._content_rect) + self._hud_renderer.render(self._content_rect) + self.alert_renderer.render(self._content_rect) + self.driver_state_renderer.render(self._content_rect, ui_state.sm) # Custom UI extension point - add custom overlays here # Use self._content_rect for positioning within camera bounds diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index 753c5f5556..46aa829c1a 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -4,7 +4,7 @@ import pyray as rl from openpilot.system.hardware import TICI from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf from openpilot.common.swaglog import cloudlog -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, Widget from openpilot.system.ui.lib.egl import init_egl, create_egl_image, destroy_egl_image, bind_egl_image_to_texture, EGLImage CONNECTION_RETRY_INTERVAL = 0.2 # seconds between connection attempts @@ -55,7 +55,7 @@ else: """ -class CameraView: +class CameraView(Widget): def __init__(self, name: str, stream_type: VisionStreamType): self._name = name # Primary stream diff --git a/selfdrive/ui/onroad/driver_camera_view.py b/selfdrive/ui/onroad/driver_camera_view.py index 2f9ff2ca7a..66ede91232 100644 --- a/selfdrive/ui/onroad/driver_camera_view.py +++ b/selfdrive/ui/onroad/driver_camera_view.py @@ -1,9 +1,9 @@ import numpy as np import pyray as rl -from cereal import messaging from msgq.visionipc import VisionStreamType from openpilot.selfdrive.ui.onroad.cameraview import CameraView from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer +from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.label import gui_label @@ -13,7 +13,7 @@ class DriverCameraView(CameraView): super().__init__("camerad", stream_type) self.driver_state_renderer = DriverStateRenderer() - def render(self, rect, sm): + def render(self, rect): super().render(rect) if not self.frame: @@ -26,8 +26,8 @@ class DriverCameraView(CameraView): ) return - self._draw_face_detection(rect, sm) - self.driver_state_renderer.draw(rect, sm) + self._draw_face_detection(rect, ui_state.sm) + self.driver_state_renderer.draw(rect, ui_state.sm) def _draw_face_detection(self, rect: rl.Rectangle, sm) -> None: driver_state = sm["driverStateV2"] @@ -83,12 +83,11 @@ class DriverCameraView(CameraView): if __name__ == "__main__": gui_app.init_window("Driver Camera View") - sm = messaging.SubMaster(["selfdriveState", "driverStateV2", "driverMonitoringState"]) driver_camera_view = DriverCameraView(VisionStreamType.VISION_STREAM_DRIVER) try: for _ in gui_app.render(): - sm.update() - driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height), sm) + ui_state.update() + driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) finally: driver_camera_view.close() diff --git a/selfdrive/ui/onroad/driver_state.py b/selfdrive/ui/onroad/driver_state.py index ce0a4b748c..60eaf25582 100644 --- a/selfdrive/ui/onroad/driver_state.py +++ b/selfdrive/ui/onroad/driver_state.py @@ -2,7 +2,7 @@ import numpy as np import pyray as rl from dataclasses import dataclass from openpilot.selfdrive.ui.ui_state import ui_state, UI_BORDER_SIZE -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, Widget # Default 3D coordinates for face keypoints as a NumPy array DEFAULT_FACE_KPTS_3D = np.array([ @@ -41,7 +41,7 @@ class ArcData: thickness: float -class DriverStateRenderer: +class DriverStateRenderer(Widget): def __init__(self): # Initial state with NumPy arrays self.face_kpts_draw = DEFAULT_FACE_KPTS_3D.copy() @@ -74,11 +74,11 @@ class DriverStateRenderer: self.engaged_color = rl.Color(26, 242, 66, 255) self.disengaged_color = rl.Color(139, 139, 139, 255) - def draw(self, rect, sm): - if not self._is_visible(sm): + def render(self, rect): + if not self._is_visible(ui_state.sm): return - self._update_state(sm, rect) + self._update_state(ui_state.sm, rect) if not self.state_updated: return diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index 0e893acfe9..6904bf4d3f 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -2,7 +2,7 @@ import pyray as rl from dataclasses import dataclass from cereal.messaging import SubMaster from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.common.conversions import Conversions as CV @@ -54,7 +54,7 @@ FONT_SIZES = FontSizes() COLORS = Colors() -class HudRenderer: +class HudRenderer(Widget): def __init__(self): """Initialize the HUD renderer.""" self.is_cruise_set: bool = False @@ -94,9 +94,9 @@ class HudRenderer: speed_conversion = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH self.speed = max(0.0, v_ego * speed_conversion) - def draw(self, rect: rl.Rectangle, sm: SubMaster) -> None: + def render(self, rect: rl.Rectangle) -> None: """Render HUD elements to the screen.""" - self._update_state(sm) + self._update_state(ui_state.sm) rl.draw_rectangle_gradient_v( int(rect.x), int(rect.y), diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index ab439312cb..1c52676a69 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -5,7 +5,7 @@ from cereal import messaging, car from dataclasses import dataclass, field from openpilot.common.params import Params from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import DEFAULT_FPS +from openpilot.system.ui.lib.application import DEFAULT_FPS, Widget from openpilot.system.ui.lib.shader_polygon import draw_polygon from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT @@ -43,7 +43,7 @@ class LeadVehicle: fill_alpha: int = 0 -class ModelRenderer: +class ModelRenderer(Widget): def __init__(self): self._longitudinal_control = False self._experimental_mode = False @@ -86,7 +86,9 @@ class ModelRenderer: self._car_space_transform = transform.astype(np.float32) self._transform_dirty = True - def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster): + def render(self, rect: rl.Rectangle): + sm = ui_state.sm + # Check if data is up-to-date if (sm.recv_frame["liveCalibration"] < ui_state.started_frame or sm.recv_frame["modelV2"] < ui_state.started_frame): diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py index 630c934090..9948db5b85 100644 --- a/selfdrive/ui/widgets/offroad_alerts.py +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -8,7 +8,7 @@ from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget class AlertColors: @@ -41,7 +41,7 @@ class AlertData: visible: bool = False -class AbstractAlert(ABC): +class AbstractAlert(Widget, ABC): def __init__(self, has_reboot_btn: bool = False): self.params = Params() self.has_reboot_btn = has_reboot_btn diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index c4ee2f6f26..b63e6a9335 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -1,3 +1,4 @@ +import abc import atexit import os import time @@ -26,6 +27,12 @@ ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") +class Widget(abc.ABC): + @abc.abstractmethod + def render(self, rect: rl.Rectangle) -> bool | None: + """Render the widget within the given rectangle.""" + + class FontWeight(IntEnum): THIN = 0 EXTRA_LIGHT = 1 diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index b53b22dd9e..4269869006 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -1,13 +1,13 @@ import pyray as rl import time -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, Widget from openpilot.system.ui.lib.text_measure import measure_text_cached PASSWORD_MASK_CHAR = "•" PASSWORD_MASK_DELAY = 1.5 # Seconds to show character before masking -class InputBox: +class InputBox(Widget): def __init__(self, max_text_size=255, password_mode=False): self._max_text_size = max_text_size self._input_text = "" diff --git a/system/ui/lib/list_view.py b/system/ui/lib/list_view.py index 221f4983a0..3eae33045b 100644 --- a/system/ui/lib/list_view.py +++ b/system/ui/lib/list_view.py @@ -4,12 +4,11 @@ from dataclasses import dataclass from collections.abc import Callable from abc import ABC, abstractmethod from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.lib.button import gui_button -from openpilot.system.ui.lib.toggle import Toggle -from openpilot.system.ui.lib.toggle import WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT +from openpilot.system.ui.lib.toggle import Toggle, WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT LINE_PADDING = 40 LINE_COLOR = rl.GRAY @@ -31,15 +30,11 @@ BUTTON_FONT_WEIGHT = FontWeight.MEDIUM # Abstract base class for right-side items -class RightItem(ABC): +class RightItem(Widget, ABC): def __init__(self, width: int = 100): self.width = width self.enabled = True - @abstractmethod - def draw(self, rect: rl.Rectangle) -> bool: - pass - @abstractmethod def get_width(self) -> int: pass @@ -52,7 +47,7 @@ class ToggleRightItem(RightItem): self.state = initial_state self.enabled = True - def draw(self, rect: rl.Rectangle) -> bool: + def render(self, rect: rl.Rectangle) -> bool: if self.toggle.render(rl.Rectangle(rect.x, rect.y + (rect.height - TOGGLE_HEIGHT) / 2, self.width, TOGGLE_HEIGHT)): self.state = not self.state return True @@ -78,7 +73,7 @@ class ButtonRightItem(RightItem): self.text = text self.enabled = True - def draw(self, rect: rl.Rectangle) -> bool: + def render(self, rect: rl.Rectangle) -> bool: return ( gui_button( rl.Rectangle(rect.x, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT), @@ -108,7 +103,7 @@ class TextRightItem(RightItem): text_width = measure_text_cached(font, text, font_size).x super().__init__(int(text_width + 20)) - def draw(self, rect: rl.Rectangle) -> bool: + def render(self, rect: rl.Rectangle) -> bool: font = gui_app.font(FontWeight.NORMAL) text_size = measure_text_cached(font, self.text, self.font_size) @@ -269,7 +264,7 @@ class ListView: right_rect = item.get_right_item_rect(rect) # Adjust for scroll offset right_rect.y = right_rect.y - if item.right_item.draw(right_rect): + if item.right_item.render(right_rect): # Right item was clicked/activated if item.callback: item.callback() diff --git a/system/ui/lib/toggle.py b/system/ui/lib/toggle.py index 5b3fbf277c..b1ea36408c 100644 --- a/system/ui/lib/toggle.py +++ b/system/ui/lib/toggle.py @@ -1,4 +1,5 @@ import pyray as rl +from openpilot.system.ui.lib.application import Widget ON_COLOR = rl.Color(51, 171, 76, 255) OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255) @@ -11,7 +12,7 @@ BG_HEIGHT = 60 ANIMATION_SPEED = 8.0 -class Toggle: +class Toggle(Widget): def __init__(self, initial_state=False): self._state = initial_state self._enabled = True diff --git a/system/ui/reset.py b/system/ui/reset.py index 6d2a0f9974..1da3db4c64 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -6,7 +6,7 @@ import threading from enum import IntEnum from openpilot.system.hardware import PC -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_label, gui_text_box @@ -27,7 +27,7 @@ class ResetState(IntEnum): FAILED = 3 -class Reset: +class Reset(Widget): def __init__(self, mode): self.mode = mode self.reset_state = ResetState.NONE diff --git a/system/ui/setup.py b/system/ui/setup.py index 73573c950a..6bdfd311e8 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -9,7 +9,7 @@ import pyray as rl from cereal import log from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_label, gui_text_box from openpilot.system.ui.widgets.network import WifiManagerUI, WifiManagerWrapper @@ -39,7 +39,7 @@ class SetupState(IntEnum): DOWNLOAD_FAILED = 6 -class Setup: +class Setup(Widget): def __init__(self): self.state = SetupState.GETTING_STARTED self.network_check_thread = None diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 5408592151..543d03c08b 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -3,7 +3,7 @@ import pyray as rl import select import sys -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, Widget from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.text import wrap_text @@ -22,7 +22,7 @@ def clamp(value, min_value, max_value): return max(min(value, max_value), min_value) -class Spinner: +class Spinner(Widget): def __init__(self): 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) @@ -38,7 +38,7 @@ class Spinner: self._progress = None self._wrapped_lines = wrap_text(text, FONT_SIZE, gui_app.width - MARGIN_H) - def render(self): + def render(self, _: rl.Rectangle): if self._wrapped_lines: # Calculate total height required for spinner and text spacing = 50 diff --git a/system/ui/text.py b/system/ui/text.py index 2a99c56a01..d434cbe43d 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -6,7 +6,7 @@ from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, Widget MARGIN = 50 SPACING = 40 @@ -46,7 +46,7 @@ def wrap_text(text, font_size, max_width): return lines -class TextWindow: +class TextWindow(Widget): def __init__(self, text: str): self._textarea_rect = rl.Rectangle(MARGIN, MARGIN, gui_app.width - MARGIN * 2, gui_app.height - MARGIN * 2) self._wrapped_lines = wrap_text(text, FONT_SIZE, self._textarea_rect.width - 20) @@ -54,7 +54,7 @@ class TextWindow: self._scroll_panel = GuiScrollPanel(show_vertical_scroll_bar=True) self._scroll_panel._offset.y = -max(self._content_rect.height - self._textarea_rect.height, 0) - def render(self): + def render(self, _: rl.Rectangle = None): scroll = self._scroll_panel.handle_scroll(self._textarea_rect, self._content_rect) rl.begin_scissor_mode(int(self._textarea_rect.x), int(self._textarea_rect.y), int(self._textarea_rect.width), int(self._textarea_rect.height)) for i, line in enumerate(self._wrapped_lines): diff --git a/system/ui/updater.py b/system/ui/updater.py index 9eaedd9e99..b7e36a6204 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -6,7 +6,7 @@ import pyray as rl from enum import IntEnum from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_text_box, gui_label from openpilot.system.ui.lib.wifi_manager import WifiManagerWrapper @@ -30,7 +30,7 @@ class Screen(IntEnum): PROGRESS = 2 -class Updater: +class Updater(Widget): def __init__(self, updater_path, manifest_path): self.updater = updater_path self.manifest = manifest_path @@ -137,7 +137,7 @@ class Updater: HARDWARE.reboot() return - def render(self): + def render(self, _: rl.Rectangle = None): if self.current_screen == Screen.PROMPT: self.render_prompt_screen() elif self.current_screen == Screen.WIFI: diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index c8dfff3dfa..94f3f93734 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,7 +1,7 @@ import time from typing import Literal import pyray as rl -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, Widget from openpilot.system.ui.lib.button import ButtonStyle, gui_button from openpilot.system.ui.lib.inputbox import InputBox from openpilot.system.ui.lib.label import gui_label @@ -52,7 +52,7 @@ KEYBOARD_LAYOUTS = { } -class Keyboard: +class Keyboard(Widget): def __init__(self, max_text_size: int = 255, min_text_size: int = 0, password_mode: bool = False, show_password_toggle: bool = False): self._layout_name: Literal["lowercase", "uppercase", "numbers", "specials"] = "lowercase" self._caps_lock = False diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 8f59631668..979dbb9a31 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -3,7 +3,7 @@ from threading import Lock from typing import Literal import pyray as rl -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, Widget from openpilot.system.ui.lib.button import ButtonStyle, gui_button from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel @@ -57,7 +57,7 @@ class StateForgetting: UIState = StateIdle | StateConnecting | StateNeedsAuth | StateShowForgetConfirm | StateForgetting -class WifiManagerUI: +class WifiManagerUI(Widget): def __init__(self, wifi_manager: WifiManagerWrapper): self.state: UIState = StateIdle() self.btn_width: int = 200 diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py index c25c5c6e6c..8b829d4945 100644 --- a/system/ui/widgets/option_dialog.py +++ b/system/ui/widgets/option_dialog.py @@ -1,11 +1,12 @@ import pyray as rl +from openpilot.system.ui.lib.application import Widget from openpilot.system.ui.lib.button import gui_button, ButtonStyle, TextAlignment from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel -class MultiOptionDialog: +class MultiOptionDialog(Widget): def __init__(self, title, options, current=""): self._title = title self._options = options