ui: replace qt spinner with raylib (#35059)

* use raylib spinner

* remove qt spinner

* use wrapper, render spinner in thread

* english

* fix?

* match Qt font size more closely
This commit is contained in:
Cameron Clough
2025-04-24 21:16:38 +01:00
committed by GitHub
parent 7c223e5586
commit 3ee2882093
11 changed files with 64 additions and 239 deletions
+1 -1
View File
@@ -7,10 +7,10 @@ from pathlib import Path
from datetime import datetime, timedelta, UTC
from openpilot.common.api import api_get
from openpilot.common.params import Params
from openpilot.common.spinner import Spinner
from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
from openpilot.system.hardware import HARDWARE, PC
from openpilot.system.hardware.hw import Paths
from openpilot.system.ui.spinner import Spinner
from openpilot.common.swaglog import cloudlog
+6 -6
View File
@@ -5,10 +5,10 @@ from pathlib import Path
# NOTE: Do NOT import anything here that needs be built (e.g. params)
from openpilot.common.basedir import BASEDIR
from openpilot.common.spinner import Spinner
from openpilot.common.text_window import TextWindow
from openpilot.system.hardware import HARDWARE, AGNOS
from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.system.hardware import HARDWARE, AGNOS
from openpilot.system.ui.spinner import Spinner
from openpilot.system.version import get_build_metadata
MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9
@@ -88,7 +88,7 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None:
if __name__ == "__main__":
spinner = Spinner()
spinner.update_progress(0, 100)
build_metadata = get_build_metadata()
build(spinner, build_metadata.openpilot.is_dirty, minimal = AGNOS)
with Spinner() as spinner:
spinner.update_progress(0, 100)
build_metadata = get_build_metadata()
build(spinner, build_metadata.openpilot.is_dirty, minimal = AGNOS)
+56 -9
View File
@@ -2,6 +2,7 @@
import pyray as rl
import os
import threading
import time
from openpilot.common.basedir import BASEDIR
from openpilot.system.ui.lib.application import gui_app
@@ -13,8 +14,8 @@ PROGRESS_BAR_HEIGHT = 20
DEGREES_PER_SECOND = 360.0 # one full rotation per second
MARGIN_H = 100
TEXTURE_SIZE = 360
FONT_SIZE = 88
LINE_HEIGHT = 96
FONT_SIZE = 96
LINE_HEIGHT = 104
DARKGRAY = (55, 55, 55, 255)
@@ -22,7 +23,7 @@ def clamp(value, min_value, max_value):
return max(min(value, max_value), min_value)
class Spinner:
class SpinnerRenderer:
def __init__(self):
self._comma_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_comma.png"), TEXTURE_SIZE, TEXTURE_SIZE)
self._spinner_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_track.png"), TEXTURE_SIZE, TEXTURE_SIZE,
@@ -70,7 +71,7 @@ class Spinner:
spinner_origin, self._rotation, rl.WHITE)
rl.draw_texture_v(self._comma_texture, comma_position, rl.WHITE)
# Display progress bar or text based on user input
# Display the progress bar or text based on user input
if progress is not None:
bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT)
rl.draw_rectangle_rounded(bar, 1, 10, DARKGRAY)
@@ -84,9 +85,55 @@ class Spinner:
FONT_SIZE, 0.0, rl.WHITE)
class Spinner:
def __init__(self):
self._renderer: SpinnerRenderer | None = None
self._stop_event = threading.Event()
self._thread = threading.Thread(target=self._run)
self._thread.start()
# wait for the renderer to be initialized
while self._renderer is None and self._thread.is_alive():
time.sleep(0.01)
def update(self, spinner_text: str):
if self._renderer is not None:
self._renderer.set_text(spinner_text)
def update_progress(self, cur: float, total: float):
self.update(str(round(100 * cur / total)))
def _run(self):
if os.getenv("CI") is not None:
return
gui_app.init_window("Spinner")
self._renderer = renderer = SpinnerRenderer()
try:
for _ in gui_app.render():
if self._stop_event.is_set():
break
renderer.render()
finally:
gui_app.close()
def __enter__(self):
return self
def close(self):
if 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")
def __del__(self):
self.close()
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
if __name__ == "__main__":
gui_app.init_window("Spinner")
spinner = Spinner()
spinner.set_text("Spinner text")
for _ in gui_app.render():
spinner.render()
with Spinner() as s:
s.update("Spinner text")
time.sleep(5)