libstp.ui.step ============== .. py:module:: libstp.ui.step .. autoapi-nested-parse:: UIStep - Base class for Steps that show UI screens. The Step orchestrates the flow, screens handle their own UI/events. Attributes ---------- .. autoapisummary:: libstp.ui.step.T Classes ------- .. autoapisummary:: libstp.ui.step.UIStep Module Contents --------------- .. py:data:: T .. py:class:: UIStep Bases: :py:obj:`libstp.step.base.Step`, :py:obj:`abc.ABC` Base class for Steps that show UI screens. The Step is responsible for orchestrating the flow by showing screens and collecting their results. Each screen handles its own events and state. .. rubric:: Example class MyCalibration(UIStep): async def _execute_step(self, robot): # Show first screen, wait for result off_value = await self.show(MeasureScreen(is_on=False)) # Show second screen on_value = await self.show(MeasureScreen(is_on=True)) # Show confirmation result = await self.show(ConfirmScreen(off_value, on_value)) if not result.confirmed: # Retry return await self._execute_step(robot) self.result = result .. py:method:: show(screen: libstp.ui.screen.UIScreen[T]) -> T :async: Show a screen and wait for it to close. The screen's event handlers will be called as the user interacts. When the screen calls close(result), this method returns that result. :param screen: The UIScreen instance to display :returns: Whatever the screen passed to close() .. py:method:: close_ui() -> None :async: Close any active screen. .. py:method:: run_step(robot: libstp.robot.api.GenericRobot) -> None :async: Run the step with automatic UI cleanup. Ensures the UI is closed even if the step fails or is cancelled. .. py:method:: display(screen: libstp.ui.screen.UIScreen) -> None :async: Show a screen without waiting for it to close. Use this when you want to run background logic while the UI is visible. Events will still be dispatched to the screen's handlers. .. rubric:: Example await self.display(StatusScreen("Processing...")) for i in range(100): await self.update_progress(i) await asyncio.sleep(0.1) await self.close_ui() .. py:method:: pump_events(timeout: float = 0) -> None :async: Process pending UI events without blocking. Call this periodically in your background loop to handle user input. .. rubric:: Example await self.display(MyScreen()) while running: await self.pump_events() # ... do background work ... await asyncio.sleep(0.05) .. py:method:: showing(screen: libstp.ui.screen.UIScreen[T]) :async: Context manager for displaying a screen while running code. Events are automatically pumped. Screen closes when exiting context. .. rubric:: Example async with self.showing(ProgressScreen()) as ctx: for i in range(100): ctx.screen.progress = i await ctx.screen.refresh() await asyncio.sleep(0.1) .. py:method:: run_with_ui(screen: libstp.ui.screen.UIScreen, task: Union[Callable[[], Awaitable[Any]], Awaitable[Any]], poll_interval: float = 0.05) -> Any :async: Run a background task while showing a screen. Events are pumped automatically. Returns when task completes. :param screen: The screen to display while task runs :param task: Either a coroutine or a callable that returns a coroutine :param poll_interval: How often to pump UI events (seconds) .. rubric:: Example # With callable: result = await self.run_with_ui( LoadingScreen("Calibrating..."), self.do_calibration ) # With coroutine directly: result = await self.run_with_ui( LoadingScreen("Calibrating..."), self.do_calibration() ) .. py:method:: message(text: str, title: str = 'Info', button_label: str = 'OK') -> None :async: Show a simple message and wait for acknowledgment. .. rubric:: Example await self.message("Calibration complete!") await self.message("Error occurred", title="Error") .. py:method:: confirm(text: str, title: str = 'Confirm', yes_label: str = 'Yes', no_label: str = 'No') -> bool :async: Show a yes/no confirmation dialog. .. rubric:: Example if await self.confirm("Start calibration?"): await self.do_calibration() .. py:method:: input_number(prompt: str, title: str = 'Input', default: float = 0, unit: str = '', min_value: Optional[float] = None, max_value: Optional[float] = None) -> Optional[float] :async: Get a numeric value from the user. Returns None if cancelled. .. rubric:: Example distance = await self.input_number("Enter distance:", unit="cm") speed = await self.input_number("Speed:", min_value=0, max_value=100) .. py:method:: choose(prompt: str, options: List[str], title: str = 'Choose') -> Optional[str] :async: Let user choose from a list of options. Returns None if cancelled. .. rubric:: Example mode = await self.choose("Select mode:", ["Fast", "Normal", "Precise"]) .. py:method:: wait_for_button(text: str = 'Press button to continue', title: str = 'Ready') -> None :async: Wait for physical button press. .. rubric:: Example await self.wait_for_button("Position robot and press button") .. py:method:: input_text(prompt: str, title: str = 'Input', default: str = '', placeholder: str = '') -> Optional[str] :async: Get text input from the user. Returns None if cancelled. .. rubric:: Example name = await self.input_text("Enter robot name:") .. py:method:: input_slider(prompt: str, min: float, max: float, title: str = 'Input', default: float = None) -> Optional[float] :async: Get a value using a slider. Returns None if cancelled. .. rubric:: Example speed = await self.input_slider("Select speed:", min=0, max=100)