mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-07-18 17:02:06 +08:00
non mici
This commit is contained in:
@@ -34,7 +34,11 @@ class MainLayout(Widget):
|
||||
# Initialize layouts
|
||||
self._home_layout = HomeLayout()
|
||||
self._home_body_layout = BodyLayout()
|
||||
self._layouts = {MainState.HOME: self._home_layout, MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()}
|
||||
self._layouts = {
|
||||
MainState.HOME: self._home_layout,
|
||||
MainState.SETTINGS: SettingsLayout(),
|
||||
MainState.ONROAD: AugmentedRoadView(radar_tracks_settings_callback=lambda: self.open_settings(PanelType.TOGGLES)),
|
||||
}
|
||||
|
||||
self._sidebar_rect = rl.Rectangle(0, 0, 0, 0)
|
||||
self._content_rect = rl.Rectangle(0, 0, 0, 0)
|
||||
|
||||
@@ -106,7 +106,18 @@ class TogglesLayout(Widget):
|
||||
icon="speed_limit.png"
|
||||
)
|
||||
|
||||
self._radar_tracks_setting = None
|
||||
self._toggles = {}
|
||||
if gui_app.sunnypilot_ui():
|
||||
self._radar_tracks_setting = multiple_button_item(
|
||||
lambda: tr("Radar Tracks"),
|
||||
"",
|
||||
buttons=[lambda: tr("Off"), lambda: tr("Lead Only"), lambda: tr("Full Radar")],
|
||||
button_width=250,
|
||||
callback=self._set_radar_tracks,
|
||||
selected_index=self._params.get("RadarTracks", return_default=True),
|
||||
)
|
||||
self._toggles["RadarTracks"] = self._radar_tracks_setting
|
||||
self._locked_toggles = set()
|
||||
for param, (title, desc, icon, needs_restart) in self._toggle_defs.items():
|
||||
toggle = toggle_item(
|
||||
@@ -203,6 +214,8 @@ class TogglesLayout(Widget):
|
||||
# refresh toggles from params to mirror external changes
|
||||
for param in self._toggle_defs:
|
||||
self._toggles[param].action_item.set_state(self._params.get_bool(param))
|
||||
if self._radar_tracks_setting is not None:
|
||||
self._radar_tracks_setting.action_item.set_selected_button(self._params.get("RadarTracks", return_default=True))
|
||||
|
||||
# these toggles need restart, block while engaged
|
||||
for toggle_def in self._toggle_defs:
|
||||
@@ -247,3 +260,6 @@ class TogglesLayout(Widget):
|
||||
|
||||
def _set_longitudinal_personality(self, button_index: int):
|
||||
self._params.put("LongitudinalPersonality", button_index, block=True)
|
||||
|
||||
def _set_radar_tracks(self, button_index: int):
|
||||
self._params.put("RadarTracks", button_index, block=True)
|
||||
|
||||
@@ -10,7 +10,7 @@ from openpilot.selfdrive.ui.mici.onroad.hud_renderer import HudRenderer
|
||||
from openpilot.selfdrive.ui.mici.onroad.model_renderer import ModelRenderer
|
||||
from openpilot.selfdrive.ui.mici.onroad.confidence_ball import ConfidenceBall
|
||||
from openpilot.selfdrive.ui.mici.onroad.cameraview import CameraView
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.radar_tracks import format_radar_tracks_onroad_columns
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.radar_tracks import RadarTracksStatus
|
||||
from openpilot.system.ui.lib.application import FontWeight, gui_app, MousePos, MouseEvent
|
||||
from openpilot.system.ui.widgets.label import UnifiedLabel
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
@@ -138,9 +138,7 @@ class AugmentedRoadView(CameraView):
|
||||
radar_tracks_settings_callback=None):
|
||||
super().__init__("camerad", stream_type)
|
||||
self._bookmark_callback = bookmark_callback
|
||||
self._radar_tracks_settings_callback = radar_tracks_settings_callback
|
||||
self._radar_tracks_settings_prompt = False
|
||||
self._radar_status_rect = rl.Rectangle()
|
||||
self._radar_tracks_status = RadarTracksStatus(radar_tracks_settings_callback)
|
||||
self._set_placeholder_color(rl.BLACK)
|
||||
|
||||
self.device_camera: DeviceCameraConfig | None = None
|
||||
@@ -164,22 +162,6 @@ class AugmentedRoadView(CameraView):
|
||||
text_color=rl.Color(255, 255, 255, int(255 * 0.9)),
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||||
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE)
|
||||
radar_text_args = {
|
||||
"font_size": 26,
|
||||
"font_weight": FontWeight.SEMI_BOLD,
|
||||
"text_color": rl.Color(0, 255, 64, 255),
|
||||
"alignment": rl.GuiTextAlignment.TEXT_ALIGN_RIGHT,
|
||||
"alignment_vertical": rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP,
|
||||
"wrap_text": False,
|
||||
}
|
||||
self._radar_ranges_label = UnifiedLabel("", **radar_text_args)
|
||||
self._radar_counts_label = UnifiedLabel("none", **radar_text_args)
|
||||
self._radar_status = ("", "none")
|
||||
self._radar_layout_key: tuple[str, str, int] | None = None
|
||||
self._radar_range_width = 0
|
||||
self._radar_count_width = 36
|
||||
self._radar_status_width = 52
|
||||
self._radar_status_height = 42
|
||||
|
||||
self._fade_texture = gui_app.texture("icons_mici/onroad/onroad_fade.png")
|
||||
|
||||
@@ -191,16 +173,9 @@ class AugmentedRoadView(CameraView):
|
||||
super()._update_state()
|
||||
|
||||
if ui_state.sm.updated["liveTracks"]:
|
||||
live_tracks = ui_state.sm["liveTracks"]
|
||||
self._radar_tracks_settings_prompt = bool(live_tracks.radarTracksAvailable and ui_state.radar_tracks != 2)
|
||||
if self._radar_tracks_settings_prompt:
|
||||
status = ("", "radar detected\ntap to enable")
|
||||
else:
|
||||
status = format_radar_tracks_onroad_columns(live_tracks) if ui_state.sm.valid["liveTracks"] else ("", "none")
|
||||
self._set_radar_status(status)
|
||||
self._radar_tracks_status.update(ui_state.sm["liveTracks"], ui_state.sm.valid["liveTracks"], ui_state.radar_tracks)
|
||||
elif not ui_state.sm.alive["liveTracks"]:
|
||||
self._radar_tracks_settings_prompt = False
|
||||
self._set_radar_status(("", "none"))
|
||||
self._radar_tracks_status.reset()
|
||||
|
||||
# update offroad label
|
||||
if ui_state.panda_type == log.PandaState.PandaType.unknown:
|
||||
@@ -210,41 +185,8 @@ class AugmentedRoadView(CameraView):
|
||||
else:
|
||||
self._offroad_label.set_text("start the car to\nuse sunnypilot")
|
||||
|
||||
def _set_radar_status(self, status: tuple[str, str]) -> None:
|
||||
if status == self._radar_status:
|
||||
return
|
||||
|
||||
self._radar_status = status
|
||||
self._radar_ranges_label.set_text(status[0])
|
||||
self._radar_counts_label.set_text(status[1])
|
||||
self._radar_layout_key = None
|
||||
|
||||
def _update_radar_status_layout(self) -> None:
|
||||
radar_horizontal_padding = 8
|
||||
radar_column_gap = 8
|
||||
radar_max_inner_width = int(self._content_rect.width - 40)
|
||||
layout_key = (*self._radar_status, radar_max_inner_width)
|
||||
if layout_key == self._radar_layout_key:
|
||||
return
|
||||
|
||||
radar_range_max_width = radar_max_inner_width - 36 - radar_column_gap
|
||||
self._radar_ranges_label.get_content_height(radar_range_max_width)
|
||||
self._radar_counts_label.get_content_height(radar_max_inner_width)
|
||||
self._radar_range_width = int(np.ceil(self._radar_ranges_label.text_width))
|
||||
self._radar_count_width = max(36, int(np.ceil(self._radar_counts_label.text_width)))
|
||||
radar_status_inner_width = self._radar_range_width + self._radar_count_width + (radar_column_gap if self._radar_range_width else 0)
|
||||
self._radar_status_width = radar_status_inner_width + radar_horizontal_padding * 2
|
||||
self._radar_status_height = max(
|
||||
42,
|
||||
self._radar_ranges_label.get_content_height(max(self._radar_range_width, 1)) + 10,
|
||||
self._radar_counts_label.get_content_height(self._radar_count_width) + 10,
|
||||
)
|
||||
self._radar_layout_key = layout_key
|
||||
|
||||
def _handle_mouse_release(self, mouse_pos: MousePos):
|
||||
if (self._radar_tracks_settings_callback is not None and
|
||||
rl.check_collision_point_rec(mouse_pos, self._radar_status_rect)):
|
||||
self._radar_tracks_settings_callback()
|
||||
if self._radar_tracks_status.handle_mouse(mouse_pos):
|
||||
return
|
||||
|
||||
# Don't trigger click callback if bookmark was triggered
|
||||
@@ -289,29 +231,7 @@ class AugmentedRoadView(CameraView):
|
||||
# Fade out bottom of overlays for looks
|
||||
rl.draw_texture_ex(self._fade_texture, rl.Vector2(self._content_rect.x, self._content_rect.y), 0.0, 1.0, rl.WHITE)
|
||||
|
||||
radar_horizontal_padding = 8
|
||||
radar_column_gap = 8
|
||||
self._update_radar_status_layout()
|
||||
self._radar_status_rect = rl.Rectangle(
|
||||
self._content_rect.x + self._content_rect.width - self._radar_status_width - 12,
|
||||
self._content_rect.y + 8,
|
||||
self._radar_status_width,
|
||||
self._radar_status_height,
|
||||
)
|
||||
radar_status_rect = self._radar_status_rect
|
||||
rl.draw_rectangle_rounded(radar_status_rect, 0.5, 8, rl.Color(0, 0, 0, 170))
|
||||
self._radar_ranges_label.render(rl.Rectangle(
|
||||
radar_status_rect.x + radar_horizontal_padding,
|
||||
radar_status_rect.y + 5,
|
||||
self._radar_range_width,
|
||||
radar_status_rect.height - 10,
|
||||
))
|
||||
self._radar_counts_label.render(rl.Rectangle(
|
||||
radar_status_rect.x + radar_horizontal_padding + self._radar_range_width + (radar_column_gap if self._radar_range_width else 0),
|
||||
radar_status_rect.y + 5,
|
||||
self._radar_count_width,
|
||||
radar_status_rect.height - 10,
|
||||
))
|
||||
self._radar_tracks_status.render(self._content_rect)
|
||||
|
||||
alert_to_render, not_animating_out = self._alert_renderer.will_render()
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ if gui_app.sunnypilot_ui():
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.augmented_road_view import BORDER_COLORS_SP, AugmentedRoadViewSP
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.driver_state import DriverStateRendererSP as DriverStateRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.hud_renderer import HudRendererSP as HudRenderer
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.radar_tracks import RadarTracksStatus
|
||||
from openpilot.selfdrive.ui.sunnypilot.ui_state import OnroadTimerStatus
|
||||
|
||||
OpState = log.SelfdriveState.OpenpilotState
|
||||
@@ -39,7 +40,7 @@ INF_POINT = np.array([1000.0, 0.0, 0.0])
|
||||
|
||||
|
||||
class AugmentedRoadView(CameraView, AugmentedRoadViewSP):
|
||||
def __init__(self, stream_type: VisionStreamType = VisionStreamType.VISION_STREAM_ROAD):
|
||||
def __init__(self, stream_type: VisionStreamType = VisionStreamType.VISION_STREAM_ROAD, radar_tracks_settings_callback=None):
|
||||
CameraView.__init__(self, "camerad", stream_type)
|
||||
AugmentedRoadViewSP.__init__(self)
|
||||
self._set_placeholder_color(BORDER_COLORS[UIStatus.DISENGAGED])
|
||||
@@ -51,12 +52,22 @@ class AugmentedRoadView(CameraView, AugmentedRoadViewSP):
|
||||
self._matrix_cache_key = (0, 0.0, 0.0, stream_type)
|
||||
self._cached_matrix: np.ndarray | None = None
|
||||
self._content_rect = rl.Rectangle()
|
||||
self._radar_tracks_status = RadarTracksStatus(radar_tracks_settings_callback, right_margin=240) if gui_app.sunnypilot_ui() else None
|
||||
|
||||
self.model_renderer = ModelRenderer()
|
||||
self._hud_renderer = HudRenderer()
|
||||
self.alert_renderer = AlertRenderer()
|
||||
self.driver_state_renderer = DriverStateRenderer()
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
if self._radar_tracks_status is None:
|
||||
return
|
||||
if ui_state.sm.updated["liveTracks"]:
|
||||
self._radar_tracks_status.update(ui_state.sm["liveTracks"], ui_state.sm.valid["liveTracks"], ui_state.radar_tracks)
|
||||
elif not ui_state.sm.alive["liveTracks"]:
|
||||
self._radar_tracks_status.reset()
|
||||
|
||||
def _render(self, rect):
|
||||
# Only render when system is started to avoid invalid data access
|
||||
if not ui_state.started:
|
||||
@@ -90,6 +101,8 @@ class AugmentedRoadView(CameraView, AugmentedRoadViewSP):
|
||||
# Draw all UI overlays
|
||||
self.model_renderer.render(self._content_rect)
|
||||
AugmentedRoadViewSP.update_fade_out_bottom_overlay(self, self._content_rect)
|
||||
if self._radar_tracks_status is not None:
|
||||
self._radar_tracks_status.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)
|
||||
@@ -103,7 +116,9 @@ class AugmentedRoadView(CameraView, AugmentedRoadViewSP):
|
||||
# Draw colored border based on driving state
|
||||
self._draw_border(rect)
|
||||
|
||||
def _handle_mouse_press(self, _):
|
||||
def _handle_mouse_press(self, mouse_pos):
|
||||
if self._radar_tracks_status is not None and self._radar_tracks_status.handle_mouse(mouse_pos):
|
||||
return
|
||||
if not self._hud_renderer.user_interacting() and self._click_callback is not None:
|
||||
self._click_callback()
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
import math
|
||||
import pyray as rl
|
||||
from openpilot.system.ui.lib.application import FontWeight
|
||||
from openpilot.system.ui.widgets.label import UnifiedLabel
|
||||
|
||||
RELATIVE_SPEED_MOVING_THRESHOLD = 0.5 # m/s relative speed deadband
|
||||
STATIONARY_SPEED_THRESHOLD = 1.0 # m/s estimated ground speed
|
||||
@@ -33,6 +35,98 @@ def format_radar_tracks_onroad_columns(live_tracks) -> tuple[str, str]:
|
||||
return range_text, count_text
|
||||
|
||||
|
||||
class RadarTracksStatus:
|
||||
HORIZONTAL_PADDING = 8
|
||||
COLUMN_GAP = 8
|
||||
|
||||
def __init__(self, settings_callback=None, right_margin: int = 12):
|
||||
self._settings_callback = settings_callback
|
||||
self._right_margin = right_margin
|
||||
self._rect = rl.Rectangle()
|
||||
text_args = {
|
||||
"font_size": 26,
|
||||
"font_weight": FontWeight.SEMI_BOLD,
|
||||
"text_color": rl.Color(0, 255, 64, 255),
|
||||
"alignment": rl.GuiTextAlignment.TEXT_ALIGN_RIGHT,
|
||||
"alignment_vertical": rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP,
|
||||
"wrap_text": False,
|
||||
}
|
||||
self._ranges_label = UnifiedLabel("", **text_args)
|
||||
self._counts_label = UnifiedLabel("none", **text_args)
|
||||
self._status = ("", "none")
|
||||
self._layout_key: tuple[str, str, int] | None = None
|
||||
self._range_width = 0
|
||||
self._count_width = 36
|
||||
self._width = 52
|
||||
self._height = 42
|
||||
|
||||
def update(self, live_tracks, valid: bool, radar_mode: int) -> None:
|
||||
if live_tracks.radarTracksAvailable and radar_mode != 2:
|
||||
status = ("", "radar detected\ntap to enable")
|
||||
else:
|
||||
status = format_radar_tracks_onroad_columns(live_tracks) if valid else ("", "none")
|
||||
self._set_status(status)
|
||||
|
||||
def reset(self) -> None:
|
||||
self._set_status(("", "none"))
|
||||
|
||||
def handle_mouse(self, mouse_pos) -> bool:
|
||||
if self._settings_callback is None or not rl.check_collision_point_rec(mouse_pos, self._rect):
|
||||
return False
|
||||
|
||||
self._settings_callback()
|
||||
return True
|
||||
|
||||
def render(self, content_rect: rl.Rectangle) -> None:
|
||||
self._update_layout(int(content_rect.width - 40))
|
||||
self._rect = rl.Rectangle(
|
||||
content_rect.x + content_rect.width - self._width - self._right_margin,
|
||||
content_rect.y + 8,
|
||||
self._width,
|
||||
self._height,
|
||||
)
|
||||
rl.draw_rectangle_rounded(self._rect, 0.5, 8, rl.Color(0, 0, 0, 170))
|
||||
self._ranges_label.render(rl.Rectangle(
|
||||
self._rect.x + self.HORIZONTAL_PADDING,
|
||||
self._rect.y + 5,
|
||||
self._range_width,
|
||||
self._rect.height - 10,
|
||||
))
|
||||
self._counts_label.render(rl.Rectangle(
|
||||
self._rect.x + self.HORIZONTAL_PADDING + self._range_width + (self.COLUMN_GAP if self._range_width else 0),
|
||||
self._rect.y + 5,
|
||||
self._count_width,
|
||||
self._rect.height - 10,
|
||||
))
|
||||
|
||||
def _set_status(self, status: tuple[str, str]) -> None:
|
||||
if status == self._status:
|
||||
return
|
||||
|
||||
self._status = status
|
||||
self._ranges_label.set_text(status[0])
|
||||
self._counts_label.set_text(status[1])
|
||||
self._layout_key = None
|
||||
|
||||
def _update_layout(self, max_inner_width: int) -> None:
|
||||
layout_key = (*self._status, max_inner_width)
|
||||
if layout_key == self._layout_key:
|
||||
return
|
||||
|
||||
self._ranges_label.get_content_height(max_inner_width - 36 - self.COLUMN_GAP)
|
||||
self._counts_label.get_content_height(max_inner_width)
|
||||
self._range_width = math.ceil(self._ranges_label.text_width)
|
||||
self._count_width = max(36, math.ceil(self._counts_label.text_width))
|
||||
inner_width = self._range_width + self._count_width + (self.COLUMN_GAP if self._range_width else 0)
|
||||
self._width = inner_width + self.HORIZONTAL_PADDING * 2
|
||||
self._height = max(
|
||||
42,
|
||||
self._ranges_label.get_content_height(max(self._range_width, 1)) + 10,
|
||||
self._counts_label.get_content_height(self._count_width) + 10,
|
||||
)
|
||||
self._layout_key = layout_key
|
||||
|
||||
|
||||
class RadarTracks:
|
||||
def draw_radar_tracks(self, live_tracks, map_to_screen, path_offset_z, track_size=6, screen_offset=(0, 0), v_ego=0.0):
|
||||
for track in live_tracks.points:
|
||||
|
||||
Reference in New Issue
Block a user