diff --git a/system/ui/lib/wrapper.py b/system/ui/lib/wrapper.py new file mode 100644 index 0000000000..04247d0bcf --- /dev/null +++ b/system/ui/lib/wrapper.py @@ -0,0 +1,47 @@ +import threading +import time +from openpilot.system.ui.lib.application import gui_app + + +class Wrapper: + def __init__(self, title: str, renderer_cls, *args): + self._title = title + self._renderer_class = renderer_cls + self._args = args + self._renderer = None + self._stop_event = threading.Event() + self._thread = threading.Thread(target=self._run, args=(self._stop_event,), daemon=True) + self._thread.start() + + def _run(self, stop_event: threading.Event): + gui_app.init_window(self._title) + self._renderer = self._renderer_class(*self._args) + try: + for _ in gui_app.render(): + if stop_event.is_set(): + break + self._renderer.render() + finally: + gui_app.close() + + def __enter__(self): + return self + + def wait(self): + """wait for renderer to be initialized""" + while self._renderer is None and self._thread is not None and self._thread.is_alive(): + time.sleep(0.01) + + def close(self): + if self._thread is not None and self._thread.is_alive(): + self._stop_event.set() + self._thread.join(timeout=2.0) + if self._thread.is_alive(): + print(f"WARNING: failed to join {self._title} thread") + self._thread = None + + def __del__(self): + self.close() + + def __exit__(self, exc_type, exc_value, traceback): + self.close() diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 2e710bb2f1..50c95684a7 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -2,9 +2,9 @@ import pyray as rl import os import threading -import time from openpilot.common.basedir import BASEDIR +from openpilot.system.ui.lib.wrapper import Wrapper from openpilot.system.ui.lib.application import gui_app # Constants @@ -68,54 +68,21 @@ class Renderer: rl.Vector2(center.x - text_size.x / 2, y_pos), FONT_SIZE, 1.0, rl.WHITE) -class Spinner: +class Spinner(Wrapper): def __init__(self): - self._stop_event = threading.Event() - self._renderer: Renderer | None = None - self._thread = threading.Thread(target=self._run, args=(self._stop_event,), daemon=True) - self._thread.start() - - def _run(self, stop_event: threading.Event): - gui_app.init_window("Spinner") - self._renderer = Renderer() - try: - for _ in gui_app.render(): - if stop_event.is_set(): - break - self._renderer.render() - finally: - gui_app.close() - self._renderer = None - - def __enter__(self): - return self + super().__init__("Spinner", Renderer) def update(self, spinner_text: str): - # wait for renderer to be initialized - while self._renderer is None and self._thread is not None and self._thread.is_alive(): - time.sleep(0.01) + self.wait() if self._renderer: self._renderer.set_text(spinner_text) def update_progress(self, cur: float, total: float): self.update(str(round(100 * cur / total))) - def close(self): - if self._thread is not None and self._thread.is_alive(): - self._stop_event.set() - self._thread.join(timeout=2.0) - if self._thread.is_alive(): - print("WARNING: failed to join spinner thread") - self._thread = None - - def __del__(self): - self.close() - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - if __name__ == "__main__": + import time with Spinner() as s: s.update("Spinner text") time.sleep(5) diff --git a/system/ui/text.py b/system/ui/text.py index 97d0ebcce9..7c42ebde18 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -3,6 +3,7 @@ import pyray as rl from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel +from openpilot.system.ui.lib.wrapper import Wrapper from openpilot.system.ui.lib.application import gui_app MARGIN = 50 @@ -31,7 +32,7 @@ def wrap_text(text, font_size, max_width): return lines -class TextWindow: +class Renderer: 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) @@ -55,12 +56,12 @@ class TextWindow: return ret -def show_text_in_window(text: str): - gui_app.init_window("Text") - text_window = TextWindow(text) - for _ in gui_app.render(): - text_window.render() +class TextWindow(Wrapper): + def __init__(self, text: str): + super().__init__("Text", Renderer, text) if __name__ == "__main__": - show_text_in_window(DEMO_TEXT) + import time + with TextWindow(DEMO_TEXT): + time.sleep(5)