python ui: refactor spinner&text (#34897)

refactor spinner&text
pull/34908/head
Dean Lee 1 month ago committed by GitHub
parent e785026a98
commit 6d12ade231
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      system/ui/lib/application.py
  2. 61
      system/ui/spinner.py
  3. 44
      system/ui/text.py

@ -64,11 +64,16 @@ class GuiApplication:
return texture
def close(self):
if not rl.is_window_ready():
return
for texture in self._textures:
rl.unload_texture(texture)
self._textures = []
for font in self._fonts.values():
rl.unload_font(font)
self._fonts = []
rl.close_window()
@ -120,7 +125,6 @@ class GuiApplication:
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_SIZE, DEFAULT_TEXT_SIZE)
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.BACKGROUND_COLOR, rl.color_to_int(rl.BLACK))
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_COLOR_NORMAL, rl.color_to_int(DEFAULT_TEXT_COLOR))
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.BACKGROUND_COLOR, rl.color_to_int(rl.Color(30, 30, 30, 255)))
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(rl.Color(50, 50, 50, 255)))
def _monitor_fps(self):

@ -1,8 +1,7 @@
#!/usr/bin/env python3
import pyray as rl
import os
import select
import sys
import threading
from openpilot.common.basedir import BASEDIR
from openpilot.system.ui.lib.application import gui_app
@ -20,57 +19,57 @@ def clamp(value, min_value, max_value):
return max(min(value, max_value), min_value)
def check_input_non_blocking():
if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
return sys.stdin.readline().strip()
return ""
class Spinner:
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)
self._rotation = 0.0
self._text: str = ""
self._lock = threading.Lock()
def set_text(self, text: str) -> None:
with self._lock:
self._text = text
def main():
gui_app.init_window("Spinner")
# Load textures
comma_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_comma.png"), TEXTURE_SIZE, TEXTURE_SIZE)
spinner_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_track.png"), TEXTURE_SIZE, TEXTURE_SIZE)
# Initial values
rotation = 0.0
user_input = ""
def render(self):
center = rl.Vector2(gui_app.width / 2.0, gui_app.height / 2.0)
spinner_origin = rl.Vector2(TEXTURE_SIZE / 2.0, TEXTURE_SIZE / 2.0)
comma_position = rl.Vector2(center.x - TEXTURE_SIZE / 2.0, center.y - TEXTURE_SIZE / 2.0)
for _ in gui_app.render():
fps = rl.get_fps()
if fps > 0:
degrees_per_frame = 360.0 / (ROTATION_TIME_SECONDS * fps)
rotation = (rotation + degrees_per_frame) % 360.0
self._rotation = (self._rotation + degrees_per_frame) % 360.0
# Draw rotating spinner and static comma logo
rl.draw_texture_pro(spinner_texture, rl.Rectangle(0, 0, TEXTURE_SIZE, TEXTURE_SIZE),
rl.draw_texture_pro(self._spinner_texture, rl.Rectangle(0, 0, TEXTURE_SIZE, TEXTURE_SIZE),
rl.Rectangle(center.x, center.y, TEXTURE_SIZE, TEXTURE_SIZE),
spinner_origin, rotation, rl.WHITE)
rl.draw_texture_v(comma_texture, comma_position, rl.WHITE)
# Read user input
if input_str := check_input_non_blocking():
user_input = input_str
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
if user_input:
text = None
with self._lock:
text = self._text
if text:
y_pos = rl.get_screen_height() - MARGIN - PROGRESS_BAR_HEIGHT
if user_input.isdigit():
progress = clamp(int(user_input), 0, 100)
if text.isdigit():
progress = clamp(int(text), 0, 100)
bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT)
rl.draw_rectangle_rounded(bar, 0.5, 10, rl.GRAY)
bar.width *= progress / 100.0
rl.draw_rectangle_rounded(bar, 0.5, 10, rl.WHITE)
else:
text_size = rl.measure_text_ex(gui_app.font(), user_input, FONT_SIZE, 1.0)
rl.draw_text_ex(gui_app.font(), user_input,
text_size = rl.measure_text_ex(gui_app.font(), text, FONT_SIZE, 1.0)
rl.draw_text_ex(gui_app.font(), text,
rl.Vector2(center.x - text_size.x / 2, y_pos), FONT_SIZE, 1.0, rl.WHITE)
if __name__ == "__main__":
main()
gui_app.init_window("Spinner")
spinner = Spinner()
spinner.set_text("Spinner text")
for _ in gui_app.render():
spinner.render()

@ -1,7 +1,5 @@
#!/usr/bin/env python3
import sys
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
@ -33,32 +31,36 @@ def wrap_text(text, font_size, max_width):
return lines
class TextWindow:
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)
self._content_rect = rl.Rectangle(0, 0, self._textarea_rect.width - 20, len(self._wrapped_lines) * LINE_HEIGHT)
self._scroll_panel = GuiScrollPanel(show_vertical_scroll_bar=True)
def main():
gui_app.init_window("Text")
text_content = sys.argv[1] if len(sys.argv) > 1 else DEMO_TEXT
textarea_rect = rl.Rectangle(MARGIN, MARGIN, gui_app.width - MARGIN * 2, gui_app.height - MARGIN * 2)
wrapped_lines = wrap_text(text_content, FONT_SIZE, textarea_rect.width - 20)
content_rect = rl.Rectangle(0, 0, textarea_rect.width - 20, len(wrapped_lines) * LINE_HEIGHT)
scroll_panel = GuiScrollPanel(show_vertical_scroll_bar=True)
for _ in gui_app.render():
scroll = scroll_panel.handle_scroll(textarea_rect, content_rect)
rl.begin_scissor_mode(int(textarea_rect.x), int(textarea_rect.y), int(textarea_rect.width), int(textarea_rect.height))
for i, line in enumerate(wrapped_lines):
position = rl.Vector2(textarea_rect.x + scroll.x, textarea_rect.y + scroll.y + i * LINE_HEIGHT)
if position.y + LINE_HEIGHT < textarea_rect.y or position.y > textarea_rect.y + textarea_rect.height:
def render(self):
scroll = self._scroll_panel.handle_scroll(self._textarea_rect, self._content_rect)
rl.begin_scissor_mode(int(self._textarea_rect.x), int(self._textarea_rect.y), int(self._textarea_rect.width), int(self._textarea_rect.height))
for i, line in enumerate(self._wrapped_lines):
position = rl.Vector2(self._textarea_rect.x + scroll.x, self._textarea_rect.y + scroll.y + i * LINE_HEIGHT)
if position.y + LINE_HEIGHT < self._textarea_rect.y or position.y > self._textarea_rect.y + self._textarea_rect.height:
continue
rl.draw_text_ex(gui_app.font(), line.strip(), position, FONT_SIZE, 0, rl.WHITE)
rl.end_scissor_mode()
button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y)
if gui_button(button_bounds, "Reboot", button_style=ButtonStyle.TRANSPARENT):
ret = gui_button(button_bounds, "Reboot", button_style=ButtonStyle.TRANSPARENT)
if ret:
HARDWARE.reboot()
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()
if __name__ == "__main__":
main()
show_text_in_window(DEMO_TEXT)

Loading…
Cancel
Save