Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion selfdrive/ui/layouts/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ def __init__(self):
# Set callbacks
self._setup_callbacks()

gui_app.stack.push(self)
# Start onboarding if terms or training not completed
self._onboarding_window = OnboardingWindow()
if not self._onboarding_window.completed:
gui_app.set_modal_overlay(self._onboarding_window)
gui_app.stack.push(self._onboarding_window)

def _render(self, _):
self._handle_onroad_transition()
Expand Down
4 changes: 2 additions & 2 deletions selfdrive/ui/layouts/onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,11 @@ def _on_terms_accepted(self):
ui_state.params.put("HasAcceptedTerms", terms_version)
self._state = OnboardingState.ONBOARDING
if self._training_done:
gui_app.set_modal_overlay(None)
gui_app.stack.pop()

def _on_completed_training(self):
ui_state.params.put("CompletedTrainingVersion", training_version)
gui_app.set_modal_overlay(None)
gui_app.stack.pop()

def _render(self, _):
if self._training_guide is None:
Expand Down
4 changes: 1 addition & 3 deletions selfdrive/ui/layouts/settings/developer.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,7 @@ def confirm_callback(result: int):
# show confirmation dialog
content = (f"<h1>{self._alpha_long_toggle.title}</h1><br>" +
f"<p>{self._alpha_long_toggle.description}</p>")

dlg = ConfirmDialog(content, tr("Enable"), rich=True)
gui_app.set_modal_overlay(dlg, callback=confirm_callback)
gui_app.stack.push(ConfirmDialog(content, tr("Enable"), rich=True), callback=confirm_callback)

else:
self._params.put_bool("AlphaLongitudinalEnabled", False)
Expand Down
43 changes: 13 additions & 30 deletions selfdrive/ui/layouts/settings/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,14 @@ def handle_language_selection(result: int):

self._select_language_dialog = MultiOptionDialog(tr("Select a language"), multilang.languages, multilang.codes[multilang.language],
option_font_weight=FontWeight.UNIFONT)
gui_app.set_modal_overlay(self._select_language_dialog, callback=handle_language_selection)
gui_app.stack.push(self._select_language_dialog, callback=handle_language_selection)

def _show_driver_camera(self):
if not self._driver_camera:
self._driver_camera = DriverCameraDialog()

gui_app.set_modal_overlay(self._driver_camera, callback=lambda result: setattr(self, '_driver_camera', None))
gui_app.stack.push(DriverCameraDialog())

def _reset_calibration_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reset Calibration")))
gui_app.stack.push(alert_dialog(tr("Disengage to Reset Calibration")))
return

def reset_calibration(result: int):
Expand All @@ -116,8 +113,7 @@ def reset_calibration(result: int):
self._params.put_bool("OnroadCycleRequested", True)
self._update_calib_description()

dialog = ConfirmDialog(tr("Are you sure you want to reset calibration?"), tr("Reset"))
gui_app.set_modal_overlay(dialog, callback=reset_calibration)
gui_app.stack.push(ConfirmDialog(tr("Are you sure you want to reset calibration?"), tr("Reset")), callback=reset_calibration)

def _update_calib_description(self):
desc = tr(DESCRIPTIONS['reset_calibration'])
Expand Down Expand Up @@ -169,42 +165,29 @@ def _update_calib_description(self):

def _reboot_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reboot")))
return

dialog = ConfirmDialog(tr("Are you sure you want to reboot?"), tr("Reboot"))
gui_app.set_modal_overlay(dialog, callback=self._perform_reboot)
gui_app.stack.push(alert_dialog(tr("Disengage to Reboot")))
else:
gui_app.stack.push(ConfirmDialog(tr("Are you sure you want to reboot?"), tr("Reboot")), callback=self._perform_reboot)

def _perform_reboot(self, result: int):
if not ui_state.engaged and result == DialogResult.CONFIRM:
self._params.put_bool_nonblocking("DoReboot", True)

def _power_off_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Power Off")))
return

dialog = ConfirmDialog(tr("Are you sure you want to power off?"), tr("Power Off"))
gui_app.set_modal_overlay(dialog, callback=self._perform_power_off)
gui_app.stack.push(alert_dialog(tr("Disengage to Power Off")))
else:
gui_app.stack.push(ConfirmDialog(tr("Are you sure you want to power off?"), tr("Power Off")), callback=self._perform_power_off)

def _perform_power_off(self, result: int):
if not ui_state.engaged and result == DialogResult.CONFIRM:
self._params.put_bool_nonblocking("DoShutdown", True)

def _pair_device(self):
if not self._pair_device_dialog:
self._pair_device_dialog = PairingDialog()
gui_app.set_modal_overlay(self._pair_device_dialog, callback=lambda result: setattr(self, '_pair_device_dialog', None))
gui_app.stack.push(PairingDialog())

def _on_regulatory(self):
if not self._fcc_dialog:
self._fcc_dialog = HtmlModal(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html"))
gui_app.set_modal_overlay(self._fcc_dialog)
gui_app.stack.push(HtmlModal(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html")))

def _on_review_training_guide(self):
if not self._training_guide:
def completed_callback():
gui_app.set_modal_overlay(None)

self._training_guide = TrainingGuide(completed_callback=completed_callback)
gui_app.set_modal_overlay(self._training_guide)
gui_app.stack.push(TrainingGuide(completed_callback=lambda: gui_app.stack.pop()))
6 changes: 3 additions & 3 deletions selfdrive/ui/layouts/settings/software.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,7 @@ def handle_uninstall_confirmation(result):
if result == DialogResult.CONFIRM:
ui_state.params.put_bool("DoUninstall", True)

dialog = ConfirmDialog(tr("Are you sure you want to uninstall?"), tr("Uninstall"))
gui_app.set_modal_overlay(dialog, callback=handle_uninstall_confirmation)
gui_app.stack.push(ConfirmDialog(tr("Are you sure you want to uninstall?"), tr("Uninstall")), callback=handle_uninstall_confirmation)

def _on_install_update(self):
# Trigger reboot to install update
Expand All @@ -182,6 +181,7 @@ def _on_select_branch(self):
current_target = ui_state.params.get("UpdaterTargetBranch") or ""
self._branch_dialog = MultiOptionDialog(tr("Select a branch"), branches, current_target)

# TODO: better return state (should not track dialog within class like this)
def handle_selection(result):
# Confirmed selection
if result == DialogResult.CONFIRM and self._branch_dialog is not None and self._branch_dialog.selection:
Expand All @@ -191,4 +191,4 @@ def handle_selection(result):
os.system("pkill -SIGUSR1 -f system.updated.updated")
self._branch_dialog = None

gui_app.set_modal_overlay(self._branch_dialog, callback=handle_selection)
gui_app.stack.push(self._branch_dialog, callback=handle_selection)
3 changes: 1 addition & 2 deletions selfdrive/ui/layouts/settings/toggles.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,7 @@ def confirm_callback(result: int):
# show confirmation dialog
content = (f"<h1>{self._toggles['ExperimentalMode'].title}</h1><br>" +
f"<p>{self._toggles['ExperimentalMode'].description}</p>")
dlg = ConfirmDialog(content, tr("Enable"), rich=True)
gui_app.set_modal_overlay(dlg, callback=confirm_callback)
gui_app.stack.push(ConfirmDialog(content, tr("Enable"), rich=True), callback=confirm_callback)
else:
self._update_experimental_mode_icon()
self._params.put_bool("ExperimentalMode", state)
Expand Down
56 changes: 14 additions & 42 deletions selfdrive/ui/mici/layouts/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pyray as rl
from enum import IntEnum
import cereal.messaging as messaging
from openpilot.selfdrive.ui.mici.layouts.home import MiciHomeLayout
from openpilot.selfdrive.ui.mici.layouts.settings.settings import SettingsLayout
Expand All @@ -15,18 +14,12 @@
ONROAD_DELAY = 2.5 # seconds


class MainState(IntEnum):
MAIN = 0
SETTINGS = 1


class MiciMainLayout(Widget):
def __init__(self):
super().__init__()

self._pm = messaging.PubMaster(['bookmarkButton'])

self._current_mode: MainState | None = None
self._prev_onroad = False
self._prev_standstill = False
self._onroad_time_delay: float | None = None
Expand All @@ -35,11 +28,11 @@ def __init__(self):
# Initialize widgets
self._home_layout = MiciHomeLayout()
self._alerts_layout = MiciOffroadAlerts()
self._settings_layout = SettingsLayout()
self._onroad_layout = AugmentedRoadView(bookmark_callback=self._on_bookmark_clicked)
self._onboarding_window = OnboardingWindow()

# Initialize widget rects
for widget in (self._home_layout, self._settings_layout, self._alerts_layout, self._onroad_layout):
for widget in (self._home_layout, self._alerts_layout, self._onroad_layout):
# TODO: set parent rect and use it if never passed rect from render (like in Scroller)
widget.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height))

Expand All @@ -53,22 +46,16 @@ def __init__(self):
# Disable scrolling when onroad is interacting with bookmark
self._scroller.set_scrolling_enabled(lambda: not self._onroad_layout.is_swiping_left())

self._layouts = {
MainState.MAIN: self._scroller,
MainState.SETTINGS: self._settings_layout,
}

# Set callbacks
self._setup_callbacks()

# Start onboarding if terms or training not completed
self._onboarding_window = OnboardingWindow()
gui_app.stack.push(self)
if not self._onboarding_window.completed:
gui_app.set_modal_overlay(self._onboarding_window)
gui_app.stack.push(self._onboarding_window)

def _setup_callbacks(self):
self._home_layout.set_callbacks(on_settings=self._on_settings_clicked)
self._settings_layout.set_callbacks(on_close=self._on_settings_closed)
self._onroad_layout.set_click_callback(lambda: self._scroll_to(self._home_layout))
device.add_interactive_timeout_callback(self._set_mode_for_started)

Expand All @@ -77,33 +64,17 @@ def _scroll_to(self, layout: Widget):
self._scroller.scroll_to(layout_x, smooth=True)

def _render(self, _):
# Initial show event
if self._current_mode is None:
self._set_mode(MainState.MAIN)

if not self._setup:
if self._alerts_layout.active_alerts() > 0:
self._scroller.scroll_to(self._alerts_layout.rect.x)
else:
self._scroller.scroll_to(self._rect.width)
self._setup = True

# Render
if self._current_mode == MainState.MAIN:
self._scroller.render(self._rect)

elif self._current_mode == MainState.SETTINGS:
self._settings_layout.render(self._rect)
self._scroller.render(self._rect)

self._handle_transitions()

def _set_mode(self, mode: MainState):
if mode != self._current_mode:
if self._current_mode is not None:
self._layouts[self._current_mode].hide_event()
self._layouts[mode].show_event()
self._current_mode = mode

def _handle_transitions(self):
if ui_state.started != self._prev_onroad:
self._prev_onroad = ui_state.started
Expand All @@ -120,7 +91,6 @@ def _handle_transitions(self):

CS = ui_state.sm["carState"]
if not CS.standstill and self._prev_standstill:
self._set_mode(MainState.MAIN)
self._scroll_to(self._onroad_layout)
self._prev_standstill = CS.standstill

Expand All @@ -129,19 +99,21 @@ def _set_mode_for_started(self, onroad_transition: bool = False):
CS = ui_state.sm["carState"]
# Only go onroad if car starts or is not at a standstill
if not CS.standstill or onroad_transition:
self._set_mode(MainState.MAIN)
self._scroll_to(self._onroad_layout)
else:
# Stay in settings if car turns off while in settings
if not onroad_transition or self._current_mode != MainState.SETTINGS:
self._set_mode(MainState.MAIN)
if not onroad_transition or gui_app.stack.depth() == 1:
self._scroll_to(self._home_layout)

def _on_settings_clicked(self):
self._set_mode(MainState.SETTINGS)

def _on_settings_closed(self):
self._set_mode(MainState.MAIN)
settings_layout = SettingsLayout()
def callback():
gui_app.stack.pop()
self._scroll_to(self._home_layout)
settings_layout.set_back_callback(callback)

# TODO: callback isn't working here
gui_app.stack.push(settings_layout, callback=callback)

def _on_bookmark_clicked(self):
user_bookmark = messaging.new_message('bookmarkButton')
Expand Down
2 changes: 1 addition & 1 deletion selfdrive/ui/mici/layouts/onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ def _on_decline_back(self):

def close(self):
ui_state.params.put_bool("IsDriverViewEnabled", False)
gui_app.set_modal_overlay(None)
gui_app.stack.pop()

def _on_terms_accepted(self):
ui_state.params.put("HasAcceptedTerms", terms_version)
Expand Down
12 changes: 5 additions & 7 deletions selfdrive/ui/mici/layouts/settings/developer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,22 @@ def __init__(self, back_callback: Callable):
super().__init__()
self.set_back_callback(back_callback)

# TODO: bug! callback should dictate success/fail for nested dialogs
def github_username_callback(username: str):
if username:
ssh_keys = SshKeyAction()
ssh_keys._fetch_ssh_key(username)
if not ssh_keys._error_message:
self._ssh_keys_btn.set_value(username)
else:
dlg = BigDialog("", ssh_keys._error_message)
gui_app.set_modal_overlay(dlg)
gui_app.stack.push(BigDialog("", ssh_keys._error_message))

def ssh_keys_callback():
github_username = ui_state.params.get("GithubUsername") or ""
dlg = BigInputDialog("enter GitHub username", github_username, confirm_callback=github_username_callback)
if not system_time_valid():
dlg = BigDialog("Please connect to Wi-Fi to fetch your key", "")
gui_app.set_modal_overlay(dlg)
return
gui_app.set_modal_overlay(dlg)
gui_app.stack.push(BigDialog("Please connect to Wi-Fi to fetch your key", ""))
else:
gui_app.stack.push(BigInputDialog("enter GitHub username", github_username, confirm_callback=github_username_callback))

txt_ssh = gui_app.texture("icons_mici/settings/developer/ssh.png", 77, 44)
github_username = ui_state.params.get("GithubUsername") or ""
Expand Down
Loading
Loading