ui: replace qt text window with raylib (#35064)

* remove qt text window

* use wrapper, render text window in thread

* add wait_for_exit method

* update imports
pull/35070/head
Cameron Clough 3 days ago committed by GitHub
parent 7eb1c31d72
commit 362ddfc0c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      .gitattributes
  2. 63
      common/text_window.py
  3. 2
      selfdrive/ui/.gitignore
  4. 3
      selfdrive/ui/SConscript
  5. 64
      selfdrive/ui/qt/text.cc
  6. 3
      selfdrive/ui/qt/text_larch64
  7. 7
      selfdrive/ui/text
  8. 2
      system/manager/build.py
  9. 2
      system/manager/manager.py
  10. 54
      system/ui/text.py

1
.gitattributes vendored

@ -11,7 +11,6 @@
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text
selfdrive/ui/qt/text_larch64 filter=lfs diff=lfs merge=lfs -text
third_party/**/*.a filter=lfs diff=lfs merge=lfs -text third_party/**/*.a filter=lfs diff=lfs merge=lfs -text
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text

@ -1,63 +0,0 @@
#!/usr/bin/env python3
import os
import time
import subprocess
from openpilot.common.basedir import BASEDIR
class TextWindow:
def __init__(self, text):
try:
self.text_proc = subprocess.Popen(["./text", text],
stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "selfdrive", "ui"),
close_fds=True)
except OSError:
self.text_proc = None
def get_status(self):
if self.text_proc is not None:
self.text_proc.poll()
return self.text_proc.returncode
return None
def __enter__(self):
return self
def close(self):
if self.text_proc is not None:
self.text_proc.terminate()
self.text_proc = None
def wait_for_exit(self):
if self.text_proc is not None:
while True:
if self.get_status() == 1:
return
time.sleep(0.1)
def __del__(self):
self.close()
def __exit__(self, exc_type, exc_value, traceback):
self.close()
if __name__ == "__main__":
text = """Traceback (most recent call last):
File "./controlsd.py", line 608, in <module>
main()
File "./controlsd.py", line 604, in main
controlsd_thread(sm, pm, logcan)
File "./controlsd.py", line 455, in controlsd_thread
1/0
ZeroDivisionError: division by zero"""
print(text)
with TextWindow(text) as s:
for _ in range(100):
if s.get_status() == 1:
print("Got exit button")
break
time.sleep(0.1)
print("gone")

@ -3,8 +3,6 @@ moc_*
translations/main_test_en.* translations/main_test_en.*
_text
ui ui
mui mui
watch3 watch3

@ -66,9 +66,6 @@ if GetOption('extras'):
qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs) qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs)
# text window
qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs)
# setup and factory resetter # setup and factory resetter
qt_env.Program("qt/setup/reset", ["qt/setup/reset.cc"], LIBS=qt_libs) qt_env.Program("qt/setup/reset", ["qt/setup/reset.cc"], LIBS=qt_libs)
qt_env.Program("qt/setup/setup", ["qt/setup/setup.cc", asset_obj], qt_env.Program("qt/setup/setup", ["qt/setup/setup.cc", asset_obj],

@ -1,64 +0,0 @@
#include <QApplication>
#include <QLabel>
#include <QPushButton>
#include <QScrollBar>
#include <QVBoxLayout>
#include <QWidget>
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
int main(int argc, char *argv[]) {
initApp(argc, argv);
QApplication a(argc, argv);
QWidget window;
setMainWindow(&window);
QGridLayout *main_layout = new QGridLayout(&window);
main_layout->setMargin(50);
QLabel *label = new QLabel(argv[1]);
label->setWordWrap(true);
label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
ScrollView *scroll = new ScrollView(label);
scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
main_layout->addWidget(scroll, 0, 0, Qt::AlignTop);
// Scroll to the bottom
QObject::connect(scroll->verticalScrollBar(), &QAbstractSlider::rangeChanged, [=]() {
scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum());
});
QPushButton *btn = new QPushButton();
#ifdef __aarch64__
btn->setText(QObject::tr("Reboot"));
QObject::connect(btn, &QPushButton::clicked, [=]() {
Hardware::reboot();
});
#else
btn->setText(QObject::tr("Exit"));
QObject::connect(btn, &QPushButton::clicked, &a, &QApplication::quit);
#endif
main_layout->addWidget(btn, 0, 0, Qt::AlignRight | Qt::AlignBottom);
window.setStyleSheet(R"(
* {
outline: none;
color: white;
background-color: black;
font-size: 60px;
}
QPushButton {
padding: 50px;
padding-right: 100px;
padding-left: 100px;
border: 2px solid white;
border-radius: 20px;
margin-right: 40px;
}
)");
return a.exec();
}

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:61f539845ebfc9568c8d28867f1e5642e882f52ead8862c9b2224b7139f4a552
size 3787480

@ -1,7 +0,0 @@
#!/bin/sh
if [ -f /TICI ] && [ ! -f _text ]; then
cp qt/text_larch64 _text
fi
exec ./_text "$1"

@ -5,10 +5,10 @@ from pathlib import Path
# NOTE: Do NOT import anything here that needs be built (e.g. params) # NOTE: Do NOT import anything here that needs be built (e.g. params)
from openpilot.common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from openpilot.common.text_window import TextWindow
from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.system.hardware import HARDWARE, AGNOS from openpilot.system.hardware import HARDWARE, AGNOS
from openpilot.system.ui.spinner import Spinner from openpilot.system.ui.spinner import Spinner
from openpilot.system.ui.text import TextWindow
from openpilot.system.version import get_build_metadata from openpilot.system.version import get_build_metadata
MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9 MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9

@ -9,7 +9,6 @@ from cereal import log
import cereal.messaging as messaging import cereal.messaging as messaging
import openpilot.system.sentry as sentry import openpilot.system.sentry as sentry
from openpilot.common.params import Params, ParamKeyType from openpilot.common.params import Params, ParamKeyType
from openpilot.common.text_window import TextWindow
from openpilot.system.hardware import HARDWARE from openpilot.system.hardware import HARDWARE
from openpilot.system.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog from openpilot.system.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog
from openpilot.system.manager.process import ensure_running from openpilot.system.manager.process import ensure_running
@ -18,6 +17,7 @@ from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_I
from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.system.version import get_build_metadata, terms_version, training_version from openpilot.system.version import get_build_metadata, terms_version, training_version
from openpilot.system.hardware.hw import Paths from openpilot.system.hardware.hw import Paths
from openpilot.system.ui.text import TextWindow
def manager_init() -> None: def manager_init() -> None:

@ -1,5 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import re import re
import threading
import time
import pyray as rl import pyray as rl
from openpilot.system.hardware import HARDWARE, PC from openpilot.system.hardware import HARDWARE, PC
from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.button import gui_button, ButtonStyle
@ -42,7 +45,8 @@ def wrap_text(text, font_size, max_width):
return lines return lines
class TextWindow:
class TextWindowRenderer:
def __init__(self, text: str): def __init__(self, text: str):
self._textarea_rect = rl.Rectangle(MARGIN, MARGIN, gui_app.width - MARGIN * 2, gui_app.height - MARGIN * 2) 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._wrapped_lines = wrap_text(text, FONT_SIZE, self._textarea_rect.width - 20)
@ -70,13 +74,53 @@ class TextWindow:
return ret return ret
def show_text_in_window(text: str): class TextWindow:
def __init__(self, text: str):
self._text = text
self._renderer: TextWindowRenderer | 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 wait_for_exit(self):
while self._thread.is_alive():
time.sleep(0.01)
def _run(self):
if os.getenv("CI") is not None:
return
gui_app.init_window("Text") gui_app.init_window("Text")
text_window = TextWindow(text) self._renderer = renderer = TextWindowRenderer(self._text)
try:
for _ in gui_app.render(): for _ in gui_app.render():
text_window.render() if self._stop_event.is_set():
break
renderer.render()
finally:
gui_app.close() 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 text window thread")
def __del__(self):
self.close()
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
if __name__ == "__main__": if __name__ == "__main__":
show_text_in_window(DEMO_TEXT) with TextWindow(DEMO_TEXT):
time.sleep(5)

Loading…
Cancel
Save