Source code for devkit_ui.pages.run.joystick_control_card

from collections.abc import Callable

from nicegui import ui

from devkit_ui.view_models.global_view_model import GlobalViewModel
from devkit_ui.view_models.run_view_model import RunViewModel


[docs] class JoystickControlCard(ui.card):
[docs] def __init__(self, global_store: GlobalViewModel, state: RunViewModel.Joystick, on_move: Callable[[float, float], None], on_stop: Callable[[], None], on_estop: Callable[[], None]): """ Initialize the joystick control card with its state and control callbacks. Parameters: global_store (GlobalViewModel): Global application state containing the emergency-stop status. state (RunViewModel.Joystick): Joystick state containing the displayed pose. on_move (Callable[[float, float], None]): Callback invoked with the joystick's y and x coordinates. on_stop (Callable[[], None]): Callback invoked when joystick movement ends. on_estop (Callable[[], None]): Callback invoked when the emergency-stop button is pressed. """ super().__init__() self._global_store = global_store self._state = state self.on_move = on_move self.on_stop = on_stop self.on_estop = on_estop self.classes('shrink-0') with self: ui.label('Joystick').classes('sec-label') with ui.row().classes('items-center gap-6'): ui.joystick( color='#1a7f37', size=130, on_move=lambda e: self.on_move(float(e.y), float(e.x)), on_end=lambda _: self.on_stop() ) with ui.column().classes('gap-3 items-center'): # Emergency stop self.estop_btn = ui.button( on_click=lambda _: self.on_estop() ).props('outline no-caps').classes('estop-btn') def sync_estop_state(is_active: bool) -> str: self.estop_btn.props(f'color={"negative" if is_active else "primary"}') return 'STOPPED' if is_active else 'E-Stop' self.estop_btn.bind_text_from( self._global_store, 'soft_estop_active', backward=sync_estop_state ) # Pose label ui.label().bind_text_from(self._state, 'pose_lbl').classes( 'text-xs font-mono text-[#57606a] text-center max-w-[130px] whitespace-pre-line' )