mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-02-18 14:13:53 +08:00
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
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -11,7 +11,6 @@
|
||||
|
||||
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
|
||||
selfdrive/ui/qt/text_larch64 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
|
||||
|
||||
@@ -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")
|
||||
2
selfdrive/ui/.gitignore
vendored
2
selfdrive/ui/.gitignore
vendored
@@ -3,8 +3,6 @@ moc_*
|
||||
|
||||
translations/main_test_en.*
|
||||
|
||||
_text
|
||||
|
||||
ui
|
||||
mui
|
||||
watch3
|
||||
|
||||
@@ -66,9 +66,6 @@ if GetOption('extras'):
|
||||
|
||||
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
|
||||
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],
|
||||
|
||||
@@ -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)
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.text_window import TextWindow
|
||||
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.ui.text import TextWindow
|
||||
from openpilot.system.version import get_build_metadata
|
||||
|
||||
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 openpilot.system.sentry as sentry
|
||||
from openpilot.common.params import Params, ParamKeyType
|
||||
from openpilot.common.text_window import TextWindow
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.system.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog
|
||||
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.system.version import get_build_metadata, terms_version, training_version
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.ui.text import TextWindow
|
||||
|
||||
|
||||
def manager_init() -> None:
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
import pyray as rl
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.system.ui.lib.button import gui_button, ButtonStyle
|
||||
@@ -42,7 +45,8 @@ def wrap_text(text, font_size, max_width):
|
||||
|
||||
return lines
|
||||
|
||||
class TextWindow:
|
||||
|
||||
class TextWindowRenderer:
|
||||
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)
|
||||
@@ -70,13 +74,53 @@ 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()
|
||||
gui_app.close()
|
||||
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")
|
||||
self._renderer = renderer = TextWindowRenderer(self._text)
|
||||
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 text window thread")
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
show_text_in_window(DEMO_TEXT)
|
||||
with TextWindow(DEMO_TEXT):
|
||||
time.sleep(5)
|
||||
|
||||
Reference in New Issue
Block a user