Keeb
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from openpilot.common.time_helpers import system_time_valid
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
@@ -76,15 +77,20 @@ class SoftwareLayout(Widget):
|
||||
self._branch_btn.action_item.set_value(ui_state.params.get("UpdaterTargetBranch") or "")
|
||||
self._branch_dialog: MultiOptionDialog | None = None
|
||||
|
||||
self._scroller = Scroller([
|
||||
self._onroad_label,
|
||||
self._version_item,
|
||||
self._auto_updates_toggle,
|
||||
self._download_btn,
|
||||
self._install_btn,
|
||||
self._branch_btn,
|
||||
button_item(lambda: tr("Uninstall"), lambda: tr("UNINSTALL"), callback=self._on_uninstall),
|
||||
], line_separator=True, spacing=0)
|
||||
self._scroller = Scroller(
|
||||
[
|
||||
self._onroad_label,
|
||||
self._version_item,
|
||||
self._auto_updates_toggle,
|
||||
self._download_btn,
|
||||
self._install_btn,
|
||||
self._branch_btn,
|
||||
button_item(lambda: tr("Uninstall"), lambda: tr("UNINSTALL"), callback=self._on_uninstall),
|
||||
button_item(lambda: tr("Error Log"), lambda: tr("VIEW"), callback=self._on_error_log),
|
||||
],
|
||||
line_separator=True,
|
||||
spacing=0,
|
||||
)
|
||||
|
||||
def show_event(self):
|
||||
self._scroller.show_event()
|
||||
@@ -170,18 +176,43 @@ class SoftwareLayout(Widget):
|
||||
# Start downloading
|
||||
self._waiting_for_updater = True
|
||||
self._waiting_start_ts = time.monotonic()
|
||||
ui_state.params_memory.put_bool("ManualUpdateInitiated", True)
|
||||
os.system("pkill -SIGHUP -f system.updated.updated")
|
||||
|
||||
def _on_auto_updates_toggle(self, enabled: bool):
|
||||
ui_state.params.put_bool("AutomaticUpdates", enabled)
|
||||
|
||||
def _on_uninstall(self):
|
||||
def handle_uninstall_confirmation(result):
|
||||
def handle_step1(result):
|
||||
if result == DialogResult.CONFIRM:
|
||||
ui_state.params.put_bool("DoUninstall", True)
|
||||
|
||||
def handle_step2(result2):
|
||||
if result2 == DialogResult.CONFIRM:
|
||||
|
||||
def handle_step3(result3):
|
||||
if result3 == DialogResult.CONFIRM:
|
||||
ui_state.params.clear_all()
|
||||
ui_state.params.put_bool("DoUninstall", True)
|
||||
|
||||
dialog = ConfirmDialog(tr("This is a complete factory reset and cannot be undone. Are you absolutely sure?"), tr("Reset"))
|
||||
gui_app.set_modal_overlay(dialog, callback=handle_step3)
|
||||
else:
|
||||
ui_state.params.put_bool("DoUninstall", True)
|
||||
|
||||
dialog = ConfirmDialog(
|
||||
tr("Do you want to perform a full factory reset? All saved assets and settings will be permanently deleted!"), tr("Factory Reset"), tr("Skip")
|
||||
)
|
||||
gui_app.set_modal_overlay(dialog, callback=handle_step2)
|
||||
|
||||
dialog = ConfirmDialog(tr("Are you sure you want to uninstall?"), tr("Uninstall"))
|
||||
gui_app.set_modal_overlay(dialog, callback=handle_uninstall_confirmation)
|
||||
gui_app.set_modal_overlay(dialog, callback=handle_step1)
|
||||
|
||||
def _on_error_log(self):
|
||||
try:
|
||||
txt = Path("/data/error_logs/error.txt").read_text(encoding='utf-8', errors='replace')
|
||||
except Exception:
|
||||
txt = tr("No error log found.")
|
||||
gui_app.set_modal_overlay(ConfirmDialog(txt, tr("OK"), on_close=lambda r: None, rich=True))
|
||||
|
||||
def _on_install_update(self):
|
||||
# Trigger reboot to install update
|
||||
|
||||
@@ -3,6 +3,7 @@ import os
|
||||
import shutil
|
||||
import threading
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
@@ -10,41 +11,63 @@ from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.system.ui.widgets.input_dialog import InputDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
|
||||
class StarPilotDataLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Manage Backups"), "panel": "backups", "icon": "toggle_icons/icon_system.png", "color": "#FA6800"},
|
||||
{"title": tr_noop("Toggle Backups"), "panel": "toggle_backups", "icon": "toggle_icons/icon_system.png", "color": "#FA6800"},
|
||||
{"title": tr_noop("Manage Storage"), "panel": "storage", "icon": "toggle_icons/icon_system.png", "color": "#FA6800"},
|
||||
{"title": tr_noop("Delete Driving Data"), "type": "hub", "on_click": self._on_delete_driving_data, "icon": "toggle_icons/icon_system.png", "color": "#FA6800"},
|
||||
{"title": tr_noop("Delete Error Logs"), "type": "hub", "on_click": self._on_delete_error_logs, "icon": "toggle_icons/icon_system.png", "color": "#FA6800"},
|
||||
{
|
||||
"title": tr_noop("Delete Driving Data"),
|
||||
"type": "hub",
|
||||
"on_click": self._on_delete_driving_data,
|
||||
"icon": "toggle_icons/icon_system.png",
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Delete Error Logs"),
|
||||
"type": "hub",
|
||||
"on_click": self._on_delete_error_logs,
|
||||
"icon": "toggle_icons/icon_system.png",
|
||||
"color": "#FA6800",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
self._sub_panels = {
|
||||
"backups": StarPilotBackupsLayout(),
|
||||
"toggle_backups": StarPilotToggleBackupsLayout(),
|
||||
"storage": StarPilotStorageLayout(),
|
||||
}
|
||||
|
||||
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back)
|
||||
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
|
||||
self._rebuild_grid()
|
||||
|
||||
def _on_delete_driving_data(self):
|
||||
def _do_delete(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
|
||||
def _task():
|
||||
drive_paths = ["/data/media/0/realdata/", "/data/media/0/realdata_HD/", "/data/media/0/realdata_konik/"]
|
||||
for path in drive_paths:
|
||||
p = Path(path)
|
||||
if p.exists():
|
||||
for entry in p.iterdir():
|
||||
if entry.is_dir(): shutil.rmtree(entry, ignore_errors=True)
|
||||
if entry.is_dir():
|
||||
shutil.rmtree(entry, ignore_errors=True)
|
||||
|
||||
threading.Thread(target=_task, daemon=True).start()
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Driving data deletion started.")))
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Delete all driving data and footage?"), tr("Delete"), on_close=_do_delete))
|
||||
|
||||
def _on_delete_error_logs(self):
|
||||
@@ -53,8 +76,10 @@ class StarPilotDataLayout(StarPilotPanel):
|
||||
shutil.rmtree("/data/error_logs", ignore_errors=True)
|
||||
os.makedirs("/data/error_logs", exist_ok=True)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Error logs deleted.")))
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Delete all error logs?"), tr("Delete"), on_close=_do_delete))
|
||||
|
||||
|
||||
class StarPilotBackupsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -67,30 +92,45 @@ class StarPilotBackupsLayout(StarPilotPanel):
|
||||
|
||||
def _get_backups(self):
|
||||
b_dir = Path("/data/backups")
|
||||
if not b_dir.exists(): return []
|
||||
if not b_dir.exists():
|
||||
return []
|
||||
return [f.name for f in b_dir.glob("*.tar.zst") if "in_progress" not in f.name]
|
||||
|
||||
def _on_create_backup(self):
|
||||
# Simplified backup logic
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Backup creation started in background.")))
|
||||
def _task():
|
||||
os.makedirs("/data/backups", exist_ok=True)
|
||||
subprocess.run(["tar", "--use-compress-program=zstd", "-cf", "/data/backups/manual_backup.tar.zst", "/data/openpilot"])
|
||||
threading.Thread(target=_task, daemon=True).start()
|
||||
def on_name(res, name):
|
||||
if res == DialogResult.CONFIRM:
|
||||
safe_name = name.replace(" ", "_") if name else f"backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
backup_path = f"/data/backups/{safe_name}.tar.zst"
|
||||
if Path(backup_path).exists():
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("A backup with this name already exists.")))
|
||||
return
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Backup creation started.")))
|
||||
|
||||
def _task():
|
||||
os.makedirs("/data/backups", exist_ok=True)
|
||||
subprocess.run(["tar", "--use-compress-program=zstd", "-cf", backup_path, "/data/openpilot"])
|
||||
|
||||
threading.Thread(target=_task, daemon=True).start()
|
||||
|
||||
gui_app.set_modal_overlay(InputDialog(tr("Name your backup"), "", on_close=on_name))
|
||||
|
||||
def _on_restore_backup(self):
|
||||
backups = self._get_backups()
|
||||
if not backups:
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("No backups found.")))
|
||||
return
|
||||
|
||||
def _on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Restoring... device will reboot.")))
|
||||
|
||||
def _task():
|
||||
subprocess.run(["rm", "-rf", "/data/openpilot/*"])
|
||||
subprocess.run(["tar", "--use-compress-program=zstd", "-xf", f"/data/backups/{val}", "-C", "/"])
|
||||
os.system("reboot")
|
||||
|
||||
threading.Thread(target=_task, daemon=True).start()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Select Backup"), backups, on_close=_on_select))
|
||||
|
||||
def _on_delete_backup(self):
|
||||
@@ -98,20 +138,96 @@ class StarPilotBackupsLayout(StarPilotPanel):
|
||||
if not backups:
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("No backups found.")))
|
||||
return
|
||||
|
||||
def _on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
os.remove(f"/data/backups/{val}")
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Delete Backup"), backups, on_close=_on_select))
|
||||
|
||||
|
||||
class StarPilotToggleBackupsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Create Toggle Backup"), "type": "hub", "on_click": self._on_create, "color": "#FA6800"},
|
||||
{"title": tr_noop("Restore Toggle Backup"), "type": "hub", "on_click": self._on_restore, "color": "#FA6800"},
|
||||
{"title": tr_noop("Delete Toggle Backup"), "type": "hub", "on_click": self._on_delete, "color": "#FA6800"},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _get_backups(self):
|
||||
b_dir = Path("/data/toggle_backups")
|
||||
if not b_dir.exists():
|
||||
return []
|
||||
return [d.name for d in b_dir.iterdir() if d.is_dir() and "in_progress" not in d.name]
|
||||
|
||||
def _on_create(self):
|
||||
def on_name(res, name):
|
||||
if res == DialogResult.CONFIRM:
|
||||
safe_name = name.replace(" ", "_") if name else f"toggle_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
backup_path = Path(f"/data/toggle_backups/{safe_name}")
|
||||
if backup_path.exists():
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("A toggle backup with this name already exists.")))
|
||||
return
|
||||
os.makedirs(backup_path, exist_ok=True)
|
||||
shutil.copytree("/data/params/d", str(backup_path), dirs_exist_ok=True)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Toggle backup created.")))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(InputDialog(tr("Name your toggle backup"), "", on_close=on_name))
|
||||
|
||||
def _on_restore(self):
|
||||
backups = self._get_backups()
|
||||
if not backups:
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("No toggle backups found.")))
|
||||
return
|
||||
|
||||
def _on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
|
||||
def on_confirm(r2):
|
||||
if r2 == DialogResult.CONFIRM:
|
||||
src = Path(f"/data/toggle_backups/{val}")
|
||||
shutil.copytree(str(src), "/data/params/d", dirs_exist_ok=True)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Toggles restored.")))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("This will overwrite your current toggles."), tr("Restore"), on_close=on_confirm))
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Select Toggle Backup"), backups, on_close=_on_select))
|
||||
|
||||
def _on_delete(self):
|
||||
backups = self._get_backups()
|
||||
if not backups:
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("No toggle backups found.")))
|
||||
return
|
||||
|
||||
def _on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
shutil.rmtree(f"/data/toggle_backups/{val}", ignore_errors=True)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Delete Toggle Backup"), backups, on_close=_on_select))
|
||||
|
||||
|
||||
class StarPilotStorageLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Driving Data"), "type": "hub", "on_click": self._show_stats, "color": "#FA6800"},
|
||||
{"title": tr_noop("Driving Data"), "type": "value", "get_value": self._get_storage, "on_click": lambda: None, "color": "#FA6800"},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _show_stats(self):
|
||||
# In a real environment we'd calculate du -sh /data
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Storage management not yet fully ported to Python.")))
|
||||
def _get_storage(self):
|
||||
paths = ["/data/media/0/osm/offline", "/data/media/0/realdata", "/data/backups"]
|
||||
total = 0
|
||||
for p in paths:
|
||||
pp = Path(p)
|
||||
if pp.exists():
|
||||
total += sum(f.stat().st_size for f in pp.rglob('*') if f.is_file())
|
||||
mb = total / (1024 * 1024)
|
||||
if mb > 1024:
|
||||
return f"{(mb / 1024):.2f} GB"
|
||||
return f"{mb:.2f} MB"
|
||||
|
||||
@@ -1,73 +1,113 @@
|
||||
from __future__ import annotations
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import SliderDialog
|
||||
|
||||
|
||||
class StarPilotDeviceLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{
|
||||
"title": tr_noop("Screen Settings"),
|
||||
"panel": "screen",
|
||||
"icon": "toggle_icons/icon_light.png",
|
||||
"color": "#FA6800"
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Device Settings"),
|
||||
"panel": "device_settings",
|
||||
"icon": "toggle_icons/icon_device.png",
|
||||
"color": "#FA6800"
|
||||
},
|
||||
{"title": tr_noop("Screen Settings"), "panel": "screen", "icon": "toggle_icons/icon_light.png", "color": "#FA6800"},
|
||||
{"title": tr_noop("Device Settings"), "panel": "device_settings", "icon": "toggle_icons/icon_device.png", "color": "#FA6800"},
|
||||
{
|
||||
"title": tr_noop("Device Shutdown"),
|
||||
"type": "value",
|
||||
"get_value": self._get_shutdown_timer,
|
||||
"on_click": self._show_shutdown_selector,
|
||||
"color": "#FA6800"
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Disable Logging"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("NoLogging"),
|
||||
"set_state": lambda s: self._params.put_bool("NoLogging", s),
|
||||
"color": "#FA6800"
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Disable Uploads"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("NoUploads"),
|
||||
"set_state": lambda s: self._params.put_bool("NoUploads", s),
|
||||
"color": "#FA6800"
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Disable Onroad Uploads"),
|
||||
"type": "toggle",
|
||||
"param": "DisableOnroadUploads",
|
||||
"get_state": lambda: self._params.get_bool("DisableOnroadUploads"),
|
||||
"set_state": lambda s: self._params.put_bool("DisableOnroadUploads", s),
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("High-Quality Recording"),
|
||||
"type": "toggle",
|
||||
"param": "HigherBitrate",
|
||||
"get_state": lambda: self._params.get_bool("HigherBitrate"),
|
||||
"set_state": lambda s: self._params.put_bool("HigherBitrate", s),
|
||||
"color": "#FA6800"
|
||||
"set_state": lambda s: self._on_higher_bitrate_toggle(s),
|
||||
"color": "#FA6800",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
self._sub_panels = {
|
||||
"screen": StarPilotScreenLayout(),
|
||||
"device_settings": StarPilotDeviceManagementLayout(),
|
||||
}
|
||||
|
||||
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back)
|
||||
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
|
||||
self._rebuild_grid()
|
||||
|
||||
def _rebuild_grid(self):
|
||||
no_uploads = self._params.get_bool("NoUploads")
|
||||
disable_onroad = self._params.get_bool("DisableOnroadUploads")
|
||||
filtered = []
|
||||
for cat in self.CATEGORIES:
|
||||
param = cat.get("param")
|
||||
if param == "DisableOnroadUploads" and not no_uploads:
|
||||
continue
|
||||
if param == "HigherBitrate" and (not no_uploads or disable_onroad):
|
||||
continue
|
||||
filtered.append(cat)
|
||||
original = self.CATEGORIES
|
||||
self.CATEGORIES = filtered
|
||||
super()._rebuild_grid()
|
||||
self.CATEGORIES = original
|
||||
|
||||
def _on_higher_bitrate_toggle(self, state):
|
||||
self._params.put_bool("HigherBitrate", state)
|
||||
cache_path = Path("/cache/use_HD")
|
||||
if state:
|
||||
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
cache_path.touch()
|
||||
else:
|
||||
if cache_path.exists():
|
||||
cache_path.unlink()
|
||||
if ui_state.started:
|
||||
gui_app.set_modal_overlay(
|
||||
ConfirmDialog(
|
||||
tr("Reboot required. Reboot now?"), tr("Reboot"), tr("Cancel"), on_close=lambda res: HARDWARE.reboot() if res == DialogResult.CONFIRM else None
|
||||
)
|
||||
)
|
||||
|
||||
def _get_shutdown_timer(self):
|
||||
v = self._params.get_int("DeviceShutdown")
|
||||
if v == 0: return tr("5 mins")
|
||||
if v <= 3: return f"{v * 15} mins"
|
||||
if v == 0:
|
||||
return tr("5 mins")
|
||||
if v <= 3:
|
||||
return f"{v * 15} mins"
|
||||
return f"{v - 3} " + (tr("hour") if v == 4 else tr("hours"))
|
||||
|
||||
def _show_shutdown_selector(self):
|
||||
@@ -75,15 +115,15 @@ class StarPilotDeviceLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int("DeviceShutdown", int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
labels = {0: tr("5 mins")}
|
||||
for i in range(1, 4): labels[i] = f"{i*15} mins"
|
||||
for i in range(4, 34): labels[i] = f"{i-3} " + (tr("hour") if i == 4 else tr("hours"))
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(
|
||||
tr("Device Shutdown"), 0, 33, 1, self._params.get_int("DeviceShutdown"),
|
||||
on_close, labels=labels, color="#FA6800"
|
||||
))
|
||||
for i in range(1, 4):
|
||||
labels[i] = f"{i * 15} mins"
|
||||
for i in range(4, 34):
|
||||
labels[i] = f"{i - 3} " + (tr("hour") if i == 4 else tr("hours"))
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr("Device Shutdown"), 0, 33, 1, self._params.get_int("DeviceShutdown"), on_close, labels=labels, color="#FA6800"))
|
||||
|
||||
|
||||
class StarPilotScreenLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
@@ -94,37 +134,45 @@ class StarPilotScreenLayout(StarPilotPanel):
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_brightness("ScreenBrightness"),
|
||||
"on_click": lambda: self._show_brightness_selector("ScreenBrightness"),
|
||||
"color": "#FA6800"
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Brightness (Onroad)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_brightness("ScreenBrightnessOnroad"),
|
||||
"on_click": lambda: self._show_brightness_selector("ScreenBrightnessOnroad"),
|
||||
"color": "#FA6800"
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Timeout (Offroad)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('ScreenTimeout')}s",
|
||||
"on_click": lambda: self._show_timeout_selector("ScreenTimeout"),
|
||||
"color": "#FA6800"
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Timeout (Onroad)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('ScreenTimeoutOnroad')}s",
|
||||
"on_click": lambda: self._show_timeout_selector("ScreenTimeoutOnroad"),
|
||||
"color": "#FA6800"
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Standby Mode"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("StandbyMode"),
|
||||
"set_state": lambda s: self._params.put_bool("StandbyMode", s),
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{"title": tr_noop("Standby Mode"), "type": "toggle", "get_state": lambda: self._params.get_bool("StandbyMode"), "set_state": lambda s: self._params.put_bool("StandbyMode", s), "color": "#FA6800"},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _get_brightness(self, key):
|
||||
v = self._params.get_int(key)
|
||||
if v == 0: return tr("Off")
|
||||
if v == 101: return tr("Auto")
|
||||
if v == 0:
|
||||
return tr("Off")
|
||||
if v == 101:
|
||||
return tr("Auto")
|
||||
return f"{v}%"
|
||||
|
||||
def _show_brightness_selector(self, key):
|
||||
@@ -134,33 +182,75 @@ class StarPilotScreenLayout(StarPilotPanel):
|
||||
self._params.put_int(key, new_v)
|
||||
HARDWARE.set_brightness(new_v)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(
|
||||
tr(key), 0, 101, 1, self._params.get_int(key),
|
||||
on_close, unit="%", labels={0: tr("Off"), 101: tr("Auto")}, color="#FA6800"
|
||||
))
|
||||
|
||||
gui_app.set_modal_overlay(
|
||||
SliderDialog(tr(key), 0, 101, 1, self._params.get_int(key), on_close, unit="%", labels={0: tr("Off"), 101: tr("Auto")}, color="#FA6800")
|
||||
)
|
||||
|
||||
def _show_timeout_selector(self, key):
|
||||
def on_close(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), 5, 60, 5, self._params.get_int(key), on_close, unit="s", color="#FA6800"))
|
||||
|
||||
|
||||
class StarPilotDeviceManagementLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Low-Voltage Cutoff"), "type": "value", "get_value": lambda: f"{self._params.get_float('LowVoltageShutdown'):.1f}V", "on_click": self._show_voltage_selector, "color": "#FA6800"},
|
||||
{"title": tr_noop("Raise Temp Limits"), "type": "toggle", "get_state": lambda: self._params.get_bool("IncreaseThermalLimits"), "set_state": lambda s: self._params.put_bool("IncreaseThermalLimits", s), "color": "#FA6800"},
|
||||
{"title": tr_noop("Use Konik Server"), "type": "toggle", "get_state": lambda: self._params.get_bool("UseKonikServer"), "set_state": lambda s: self._params.put_bool("UseKonikServer", s), "color": "#FA6800"},
|
||||
{
|
||||
"title": tr_noop("Low-Voltage Cutoff"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('LowVoltageShutdown'):.1f}V",
|
||||
"on_click": self._show_voltage_selector,
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Raise Temp Limits"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("IncreaseThermalLimits"),
|
||||
"set_state": lambda s: self._params.put_bool("IncreaseThermalLimits", s),
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Use Konik Server"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._get_konik_state(),
|
||||
"set_state": lambda s: self._on_konik_toggle(s),
|
||||
"color": "#FA6800",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _get_konik_state(self):
|
||||
if Path("/data/not_vetted").exists():
|
||||
return True
|
||||
return self._params.get_bool("UseKonikServer")
|
||||
|
||||
def _on_konik_toggle(self, state):
|
||||
self._params.put_bool("UseKonikServer", state)
|
||||
cache_path = Path("/cache/use_konik")
|
||||
if state:
|
||||
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
cache_path.touch()
|
||||
else:
|
||||
if cache_path.exists():
|
||||
cache_path.unlink()
|
||||
if ui_state.started:
|
||||
gui_app.set_modal_overlay(
|
||||
ConfirmDialog(
|
||||
tr("Reboot required. Reboot now?"), tr("Reboot"), tr("Cancel"), on_close=lambda res: HARDWARE.reboot() if res == DialogResult.CONFIRM else None
|
||||
)
|
||||
)
|
||||
|
||||
def _show_voltage_selector(self):
|
||||
def on_close(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_float("LowVoltageShutdown", float(val))
|
||||
self._rebuild_grid()
|
||||
gui_app.set_modal_overlay(SliderDialog(tr("Low-Voltage Cutoff"), 11.8, 12.5, 0.1, self._params.get_float("LowVoltageShutdown"), on_close, unit="V", color="#FA6800"))
|
||||
|
||||
gui_app.set_modal_overlay(
|
||||
SliderDialog(tr("Low-Voltage Cutoff"), 11.8, 12.5, 0.1, self._params.get_float("LowVoltageShutdown"), on_close, unit="V", color="#FA6800")
|
||||
)
|
||||
|
||||
@@ -5,9 +5,11 @@ from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.system.ui.widgets.input_dialog import InputDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import SliderDialog
|
||||
|
||||
|
||||
class StarPilotLongitudinalLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -20,18 +22,15 @@ class StarPilotLongitudinalLayout(StarPilotPanel):
|
||||
"qol": StarPilotLongitudinalQOLLayout(),
|
||||
"slc": StarPilotSpeedLimitControllerLayout(),
|
||||
"weather": StarPilotWeatherLayout(),
|
||||
|
||||
# Personality Sub-panels
|
||||
"traffic_personality": StarPilotPersonalityProfileLayout("Traffic"),
|
||||
"aggressive_personality": StarPilotPersonalityProfileLayout("Aggressive"),
|
||||
"standard_personality": StarPilotPersonalityProfileLayout("Standard"),
|
||||
"relaxed_personality": StarPilotPersonalityProfileLayout("Relaxed"),
|
||||
|
||||
# SLC Sub-panels
|
||||
"slc_offsets": StarPilotSLCOffsetsLayout(),
|
||||
"slc_qol": StarPilotSLCQOLLayout(),
|
||||
"slc_visuals": StarPilotSLCVisualsLayout(),
|
||||
|
||||
# Weather Sub-panels
|
||||
"low_visibility": StarPilotWeatherBase("LowVisibility"),
|
||||
"rain": StarPilotWeatherBase("Rain"),
|
||||
@@ -51,21 +50,80 @@ class StarPilotLongitudinalLayout(StarPilotPanel):
|
||||
]
|
||||
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back)
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
class StarPilotAdvancedLongitudinalLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("EV Tuning"), "type": "toggle", "get_state": lambda: self._params.get_bool("EVTuning"), "set_state": lambda s: self._params.put_bool("EVTuning", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Truck Tuning"), "type": "toggle", "get_state": lambda: self._params.get_bool("TruckTuning"), "set_state": lambda s: self._params.put_bool("TruckTuning", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Actuator Delay"), "type": "value", "get_value": lambda: f"{self._params.get_float('LongitudinalActuatorDelay'):.2f}s", "on_click": lambda: self._show_float_selector("LongitudinalActuatorDelay", 0.0, 1.0, 0.01, "s"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Max Acceleration"), "type": "value", "get_value": lambda: f"{self._params.get_float('MaxDesiredAcceleration'):.1f}m/s²", "on_click": lambda: self._show_float_selector("MaxDesiredAcceleration", 0.1, 4.0, 0.1, "m/s²"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Start Accel"), "type": "value", "get_value": lambda: f"{self._params.get_float('StartAccel'):.2f}m/s²", "on_click": lambda: self._show_float_selector("StartAccel", 0.0, 4.0, 0.01, "m/s²"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Stop Accel"), "type": "value", "get_value": lambda: f"{self._params.get_float('StopAccel'):.2f}m/s²", "on_click": lambda: self._show_float_selector("StopAccel", -4.0, 0.0, 0.01, "m/s²"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Stopping Rate"), "type": "value", "get_value": lambda: f"{self._params.get_float('StoppingDecelRate'):.3f}m/s²", "on_click": lambda: self._show_float_selector("StoppingDecelRate", 0.001, 1.0, 0.001, "m/s²"), "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("EV Tuning"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("EVTuning"),
|
||||
"set_state": lambda s: self._params.put_bool("EVTuning", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Truck Tuning"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("TruckTuning"),
|
||||
"set_state": lambda s: self._params.put_bool("TruckTuning", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Actuator Delay"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('LongitudinalActuatorDelay'):.2f}s",
|
||||
"on_click": lambda: self._show_float_selector("LongitudinalActuatorDelay", 0.0, 1.0, 0.01, "s"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Max Acceleration"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('MaxDesiredAcceleration'):.1f}m/s²",
|
||||
"on_click": lambda: self._show_float_selector("MaxDesiredAcceleration", 0.1, 4.0, 0.1, "m/s²"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Start Accel"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('StartAccel'):.2f}m/s²",
|
||||
"on_click": lambda: self._show_float_selector("StartAccel", 0.0, 4.0, 0.01, "m/s²"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Stop Accel"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('StopAccel'):.2f}m/s²",
|
||||
"on_click": lambda: self._show_float_selector("StopAccel", -4.0, 0.0, 0.01, "m/s²"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Stopping Rate"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('StoppingDecelRate'):.3f}m/s²",
|
||||
"on_click": lambda: self._show_float_selector("StoppingDecelRate", 0.001, 1.0, 0.001, "m/s²"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("VEgo Starting"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('VEgoStarting'):.2f}m/s",
|
||||
"on_click": lambda: self._show_float_selector("VEgoStarting", 0.01, 1.0, 0.01, "m/s"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("VEgo Stopping"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('VEgoStopping'):.2f}m/s",
|
||||
"on_click": lambda: self._show_float_selector("VEgoStopping", 0.01, 1.0, 0.01, "m/s"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -74,22 +132,108 @@ class StarPilotAdvancedLongitudinalLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_float(key, float(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, step, self._params.get_float(key), on_close, unit=unit, color="#1BA1E2"))
|
||||
|
||||
|
||||
class StarPilotConditionalExperimentalLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Conditional Experimental"), "type": "toggle", "get_state": lambda: self._params.get_bool("ConditionalExperimental"), "set_state": lambda s: self._params.put_bool("ConditionalExperimental", s), "icon": "toggle_icons/icon_conditional.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Below Speed"), "type": "value", "get_value": lambda: f"{self._params.get_int('CESpeed')} mph", "on_click": lambda: self._show_speed_selector("CESpeed"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Curves"), "type": "toggle", "get_state": lambda: self._params.get_bool("CECurves"), "set_state": lambda s: self._params.put_bool("CECurves", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Stop Lights"), "type": "toggle", "get_state": lambda: self._params.get_bool("CEStopLights"), "set_state": lambda s: self._params.put_bool("CEStopLights", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Lead Detected"), "type": "toggle", "get_state": lambda: self._params.get_bool("CELead"), "set_state": lambda s: self._params.put_bool("CELead", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Slower Lead"), "type": "toggle", "get_state": lambda: self._params.get_bool("CESlowerLead"), "set_state": lambda s: self._params.put_bool("CESlowerLead", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Stopped Lead"), "type": "toggle", "get_state": lambda: self._params.get_bool("CEStoppedLead"), "set_state": lambda s: self._params.put_bool("CEStoppedLead", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Predicted Stop"), "type": "value", "get_value": lambda: f"{self._params.get_int('CEModelStopTime')}s", "on_click": lambda: self._show_int_selector("CEModelStopTime", 0, 10, "s"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Signal Below"), "type": "value", "get_value": lambda: f"{self._params.get_int('CESignalSpeed')} mph", "on_click": lambda: self._show_speed_selector("CESignalSpeed"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Status Widget"), "type": "toggle", "get_state": lambda: self._params.get_bool("ShowCEMStatus"), "set_state": lambda s: self._params.put_bool("ShowCEMStatus", s), "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Conditional Experimental"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("ConditionalExperimental"),
|
||||
"set_state": lambda s: self._params.put_bool("ConditionalExperimental", s),
|
||||
"icon": "toggle_icons/icon_conditional.png",
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Below Speed"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('CESpeed')} mph",
|
||||
"on_click": lambda: self._show_speed_selector("CESpeed"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Curves"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("CECurves"),
|
||||
"set_state": lambda s: self._params.put_bool("CECurves", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Curves Lead"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("CECurvesLead"),
|
||||
"set_state": lambda s: self._params.put_bool("CECurvesLead", s),
|
||||
"visible": lambda: self._params.get_bool("CECurves"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Stop Lights"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("CEStopLights"),
|
||||
"set_state": lambda s: self._params.put_bool("CEStopLights", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Lead Detected"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("CELead"),
|
||||
"set_state": lambda s: self._params.put_bool("CELead", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Slower Lead"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("CESlowerLead"),
|
||||
"set_state": lambda s: self._params.put_bool("CESlowerLead", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Stopped Lead"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("CEStoppedLead"),
|
||||
"set_state": lambda s: self._params.put_bool("CEStoppedLead", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Predicted Stop"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('CEModelStopTime')}s",
|
||||
"on_click": lambda: self._show_int_selector("CEModelStopTime", 0, 10, "s"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Signal Below"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('CESignalSpeed')} mph",
|
||||
"on_click": lambda: self._show_speed_selector("CESignalSpeed"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Speed Lead"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('CESpeedLead')} mph",
|
||||
"on_click": lambda: self._show_speed_selector("CESpeedLead"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Signal Lane Detection"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("CESignalLaneDetection"),
|
||||
"set_state": lambda s: self._params.put_bool("CESignalLaneDetection", s),
|
||||
"visible": lambda: self._params.get_int("CESignalSpeed") > 0,
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Status Widget"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("ShowCEMStatus"),
|
||||
"set_state": lambda s: self._params.put_bool("ShowCEMStatus", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -98,6 +242,7 @@ class StarPilotConditionalExperimentalLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), 0, 100, 1, self._params.get_int(key), on_close, unit=" mph", color="#1BA1E2"))
|
||||
|
||||
def _show_int_selector(self, key, min_v, max_v, unit=""):
|
||||
@@ -105,17 +250,62 @@ class StarPilotConditionalExperimentalLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, 1, self._params.get_int(key), on_close, unit=unit, color="#1BA1E2"))
|
||||
|
||||
|
||||
class StarPilotCurveSpeedLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Curve Speed Controller"), "type": "toggle", "get_state": lambda: self._params.get_bool("CurveSpeedController"), "set_state": lambda s: self._params.put_bool("CurveSpeedController", s), "icon": "toggle_icons/icon_speed_map.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Status Widget"), "type": "toggle", "get_state": lambda: self._params.get_bool("ShowCSCStatus"), "set_state": lambda s: self._params.put_bool("ShowCSCStatus", s), "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Curve Speed Controller"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("CurveSpeedController"),
|
||||
"set_state": lambda s: self._params.put_bool("CurveSpeedController", s),
|
||||
"icon": "toggle_icons/icon_speed_map.png",
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Status Widget"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("ShowCSCStatus"),
|
||||
"set_state": lambda s: self._params.put_bool("ShowCSCStatus", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Calibrated Lateral Accel"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params_memory.get_float('CalibratedLateralAcceleration'):.2f} m/s²",
|
||||
"on_click": lambda: None,
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Calibration Progress"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params_memory.get_float('CalibrationProgress'):.2f}%",
|
||||
"on_click": lambda: None,
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Reset Curve Data"),
|
||||
"type": "hub",
|
||||
"on_click": lambda: self._reset_curve_data(),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _reset_curve_data(self):
|
||||
def on_close(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.remove("CalibratedLateralAcceleration")
|
||||
self._params.remove("CalibrationProgress")
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Reset Curve Data?"), tr("Confirm"), on_close=on_close))
|
||||
|
||||
|
||||
class StarPilotPersonalitiesLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -127,25 +317,88 @@ class StarPilotPersonalitiesLayout(StarPilotPanel):
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
class StarPilotPersonalityProfileLayout(StarPilotPanel):
|
||||
def __init__(self, profile: str):
|
||||
super().__init__()
|
||||
self._profile = profile
|
||||
follow_min = 1.0 if profile == "Traffic" else 0.5
|
||||
follow_max = 2.5 if profile == "Traffic" else 3.0
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Follow Distance"), "type": "value", "get_value": lambda: f"{self._params.get_float(self._profile + 'Follow'):.2f}s", "on_click": lambda: self._show_float_selector(self._profile + "Follow", 0.5, 3.0, 0.05, "s"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Accel Smoothness"), "type": "value", "get_value": lambda: f"{self._params.get_int(self._profile + 'JerkAcceleration')}%", "on_click": lambda: self._show_int_selector(self._profile + "JerkAcceleration", 25, 200, "%"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Brake Smoothness"), "type": "value", "get_value": lambda: f"{self._params.get_int(self._profile + 'JerkDeceleration')}%", "on_click": lambda: self._show_int_selector(self._profile + "JerkDeceleration", 25, 200, "%"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Safety Gap Bias"), "type": "value", "get_value": lambda: f"{self._params.get_int(self._profile + 'JerkDanger')}%", "on_click": lambda: self._show_int_selector(self._profile + "JerkDanger", 25, 200, "%"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Slowdown Response"), "type": "value", "get_value": lambda: f"{self._params.get_int(self._profile + 'JerkSpeedDecrease')}%", "on_click": lambda: self._show_int_selector(self._profile + "JerkSpeedDecrease", 25, 200, "%"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Speed-Up Response"), "type": "value", "get_value": lambda: f"{self._params.get_int(self._profile + 'JerkSpeed')}%", "on_click": lambda: self._show_int_selector(self._profile + "JerkSpeed", 25, 200, "%"), "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Follow Distance"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float(self._profile + 'Follow'):.2f}s",
|
||||
"on_click": lambda: self._show_float_selector(self._profile + "Follow", follow_min, follow_max, 0.05, "s"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Follow High"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float(self._profile + 'FollowHigh'):.2f}s",
|
||||
"on_click": lambda: self._show_float_selector(self._profile + "FollowHigh", 1.0, 3.0, 0.05, "s"),
|
||||
"visible": lambda: self._profile != "Traffic",
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Accel Smoothness"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int(self._profile + 'JerkAcceleration')}%",
|
||||
"on_click": lambda: self._show_int_selector(self._profile + "JerkAcceleration", 25, 200, "%"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Brake Smoothness"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int(self._profile + 'JerkDeceleration')}%",
|
||||
"on_click": lambda: self._show_int_selector(self._profile + "JerkDeceleration", 25, 200, "%"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Safety Gap Bias"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int(self._profile + 'JerkDanger')}%",
|
||||
"on_click": lambda: self._show_int_selector(self._profile + "JerkDanger", 25, 200, "%"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Slowdown Response"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int(self._profile + 'JerkSpeedDecrease')}%",
|
||||
"on_click": lambda: self._show_int_selector(self._profile + "JerkSpeedDecrease", 25, 200, "%"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Speed-Up Response"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int(self._profile + 'JerkSpeed')}%",
|
||||
"on_click": lambda: self._show_int_selector(self._profile + "JerkSpeed", 25, 200, "%"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Reset to Defaults"),
|
||||
"type": "hub",
|
||||
"on_click": lambda: self._reset_profile(),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _reset_profile(self):
|
||||
def on_close(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
for key in ["Follow", "FollowHigh", "JerkAcceleration", "JerkDeceleration", "JerkDanger", "JerkSpeedDecrease", "JerkSpeed"]:
|
||||
self._params.remove(self._profile + key)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Reset to Defaults?"), tr("Confirm"), on_close=on_close))
|
||||
|
||||
def _show_float_selector(self, key, min_v, max_v, step, unit=""):
|
||||
def on_close(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_float(key, float(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, step, self._params.get_float(key), on_close, unit=unit, color="#1BA1E2"))
|
||||
|
||||
def _show_int_selector(self, key, min_v, max_v, unit=""):
|
||||
@@ -153,19 +406,63 @@ class StarPilotPersonalityProfileLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, 5, self._params.get_int(key), on_close, unit=unit, color="#1BA1E2"))
|
||||
|
||||
|
||||
class StarPilotLongitudinalTuneLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Acceleration Profile"), "type": "value", "get_value": lambda: self._params.get("AccelerationProfile", encoding='utf-8') or "Standard", "on_click": lambda: self._show_selection("AccelerationProfile", ["Standard", "Eco", "Sport", "Sport+"]), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Deceleration Profile"), "type": "value", "get_value": lambda: self._params.get("DecelerationProfile", encoding='utf-8') or "Standard", "on_click": lambda: self._show_selection("DecelerationProfile", ["Standard", "Eco", "Sport"]), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Human Acceleration"), "type": "toggle", "get_state": lambda: self._params.get_bool("HumanAcceleration"), "set_state": lambda s: self._params.put_bool("HumanAcceleration", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Human Following"), "type": "toggle", "get_state": lambda: self._params.get_bool("HumanFollowing"), "set_state": lambda s: self._params.put_bool("HumanFollowing", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Human Lane Changes"), "type": "toggle", "get_state": lambda: self._params.get_bool("HumanLaneChanges"), "set_state": lambda s: self._params.put_bool("HumanLaneChanges", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Lead Detection"), "type": "value", "get_value": lambda: f"{self._params.get_int('LeadDetectionThreshold')}%", "on_click": lambda: self._show_int_selector("LeadDetectionThreshold", 25, 50, "%"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Taco Tune"), "type": "toggle", "get_state": lambda: self._params.get_bool("TacoTune"), "set_state": lambda s: self._params.put_bool("TacoTune", s), "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Acceleration Profile"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("AccelerationProfile", encoding='utf-8') or "Standard",
|
||||
"on_click": lambda: self._show_selection("AccelerationProfile", ["Standard", "Eco", "Sport", "Sport+"]),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Deceleration Profile"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("DecelerationProfile", encoding='utf-8') or "Standard",
|
||||
"on_click": lambda: self._show_selection("DecelerationProfile", ["Standard", "Eco", "Sport"]),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Human Acceleration"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("HumanAcceleration"),
|
||||
"set_state": lambda s: self._params.put_bool("HumanAcceleration", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Human Following"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("HumanFollowing"),
|
||||
"set_state": lambda s: self._params.put_bool("HumanFollowing", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Human Lane Changes"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("HumanLaneChanges"),
|
||||
"set_state": lambda s: self._params.put_bool("HumanLaneChanges", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Lead Detection"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('LeadDetectionThreshold')}%",
|
||||
"on_click": lambda: self._show_int_selector("LeadDetectionThreshold", 25, 50, "%"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Taco Tune"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("TacoTune"),
|
||||
"set_state": lambda s: self._params.put_bool("TacoTune", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -174,6 +471,7 @@ class StarPilotLongitudinalTuneLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put(key, val)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr(key), options, self._params.get(key, encoding='utf-8') or "Standard", on_close=on_select))
|
||||
|
||||
def _show_int_selector(self, key, min_v, max_v, unit=""):
|
||||
@@ -181,17 +479,79 @@ class StarPilotLongitudinalTuneLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, 1, self._params.get_int(key), on_close, unit=unit, color="#1BA1E2"))
|
||||
|
||||
|
||||
class StarPilotLongitudinalQOLLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Cruise Interval"), "type": "value", "get_value": lambda: f"{self._params.get_int('CustomCruise')} mph", "on_click": lambda: self._show_speed_selector("CustomCruise"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Reverse Cruise"), "type": "toggle", "get_state": lambda: self._params.get_bool("ReverseCruise"), "set_state": lambda s: self._params.put_bool("ReverseCruise", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Force Stops"), "type": "toggle", "get_state": lambda: self._params.get_bool("ForceStops"), "set_state": lambda s: self._params.put_bool("ForceStops", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Stopped Distance"), "type": "value", "get_value": lambda: f"{self._params.get_int('IncreasedStoppedDistance')} ft", "on_click": lambda: self._show_int_selector("IncreasedStoppedDistance", 0, 10, " ft"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Set Speed Offset"), "type": "value", "get_value": lambda: f"+{self._params.get_int('SetSpeedOffset')} mph", "on_click": lambda: self._show_int_selector("SetSpeedOffset", 0, 99, " mph"), "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Cruise Interval"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('CustomCruise')} mph",
|
||||
"on_click": lambda: self._show_speed_selector("CustomCruise"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Cruise Long"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('CustomCruiseLong')} mph",
|
||||
"on_click": lambda: self._show_speed_selector("CustomCruiseLong"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Reverse Cruise"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("ReverseCruise"),
|
||||
"set_state": lambda s: self._params.put_bool("ReverseCruise", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Force Stops"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("ForceStops"),
|
||||
"set_state": lambda s: self._params.put_bool("ForceStops", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Stopped Distance"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('IncreasedStoppedDistance')} ft",
|
||||
"on_click": lambda: self._show_int_selector("IncreasedStoppedDistance", 0, 10, " ft"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Set Speed Offset"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"+{self._params.get_int('SetSpeedOffset')} mph",
|
||||
"on_click": lambda: self._show_int_selector("SetSpeedOffset", 0, 99, " mph"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Map Gears"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("MapGears"),
|
||||
"set_state": lambda s: self._params.put_bool("MapGears", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Map Acceleration"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("MapAcceleration"),
|
||||
"set_state": lambda s: self._params.put_bool("MapAcceleration", s),
|
||||
"visible": lambda: self._params.get_bool("MapGears"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Map Deceleration"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("MapDeceleration"),
|
||||
"set_state": lambda s: self._params.put_bool("MapDeceleration", s),
|
||||
"visible": lambda: self._params.get_bool("MapGears"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -200,6 +560,7 @@ class StarPilotLongitudinalQOLLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), 0, 100, 1, self._params.get_int(key), on_close, unit=" mph", color="#1BA1E2"))
|
||||
|
||||
def _show_int_selector(self, key, min_v, max_v, unit=""):
|
||||
@@ -207,8 +568,10 @@ class StarPilotLongitudinalQOLLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, 1, self._params.get_int(key), on_close, unit=unit, color="#1BA1E2"))
|
||||
|
||||
|
||||
class StarPilotSpeedLimitControllerLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -216,40 +579,66 @@ class StarPilotSpeedLimitControllerLayout(StarPilotPanel):
|
||||
{"title": tr_noop("SLC Offsets"), "panel": "slc_offsets", "icon": "toggle_icons/icon_speed_limit.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("SLC Quality of Life"), "panel": "slc_qol", "icon": "toggle_icons/icon_speed_limit.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("SLC Visuals"), "panel": "slc_visuals", "icon": "toggle_icons/icon_speed_limit.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Fallback Speed"), "type": "value", "get_value": lambda: self._params.get("SLCFallback", encoding='utf-8') or "Set Speed", "on_click": lambda: self._show_selection("SLCFallback", ["Set Speed", "Experimental Mode", "Previous Limit"]), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Override Speed"), "type": "value", "get_value": lambda: self._params.get("SLCOverride", encoding='utf-8') or "None", "on_click": lambda: self._show_selection("SLCOverride", ["None", "Set With Gas Pedal", "Max Set Speed"]), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Source Priority"), "type": "value", "get_value": lambda: self._params.get("SLCPriority1", encoding='utf-8') or "Dashboard", "on_click": self._on_priority_clicked, "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Fallback Speed"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("SLCFallback", encoding='utf-8') or "Set Speed",
|
||||
"on_click": lambda: self._show_selection("SLCFallback", ["Set Speed", "Experimental Mode", "Previous Limit"]),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Override Speed"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("SLCOverride", encoding='utf-8') or "None",
|
||||
"on_click": lambda: self._show_selection("SLCOverride", ["None", "Set With Gas Pedal", "Max Set Speed"]),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Source Priority"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("SLCPriority1", encoding='utf-8') or "Dashboard",
|
||||
"on_click": self._on_priority_clicked,
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _on_priority_clicked(self):
|
||||
options = ["Dashboard", "Map Data", "Highest", "Lowest"]
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put("SLCPriority1", val)
|
||||
self._rebuild_grid()
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("SLC Priority"), options, self._params.get("SLCPriority1", encoding='utf-8') or "Dashboard", on_close=on_select))
|
||||
|
||||
gui_app.set_modal_overlay(
|
||||
SelectionDialog(tr("SLC Priority"), options, self._params.get("SLCPriority1", encoding='utf-8') or "Dashboard", on_close=on_select)
|
||||
)
|
||||
|
||||
def _show_selection(self, key, options):
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put(key, val)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr(key), options, self._params.get(key, encoding='utf-8') or "None", on_close=on_select))
|
||||
|
||||
|
||||
class StarPilotSLCOffsetsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = []
|
||||
for i in range(1, 8):
|
||||
key = f"Offset{i}"
|
||||
self.CATEGORIES.append({
|
||||
"title": tr_noop(f"Offset {i}"),
|
||||
"type": "value",
|
||||
"get_value": lambda k=key: f"{self._params.get_int(k)} mph",
|
||||
"on_click": lambda k=key: self._show_speed_selector(k),
|
||||
"color": "#1BA1E2"
|
||||
})
|
||||
self.CATEGORIES.append(
|
||||
{
|
||||
"title": tr_noop(f"Offset {i}"),
|
||||
"type": "value",
|
||||
"get_value": lambda k=key: f"{self._params.get_int(k)} mph",
|
||||
"on_click": lambda k=key: self._show_speed_selector(k),
|
||||
"color": "#1BA1E2",
|
||||
}
|
||||
)
|
||||
self._rebuild_grid()
|
||||
|
||||
def _show_speed_selector(self, key):
|
||||
@@ -257,17 +646,65 @@ class StarPilotSLCOffsetsLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), -99, 100, 1, self._params.get_int(key), on_close, unit=" mph", color="#1BA1E2"))
|
||||
|
||||
|
||||
class StarPilotSLCQOLLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Match Speed on Engage"), "type": "toggle", "get_state": lambda: self._params.get_bool("SetSpeedLimit"), "set_state": lambda s: self._params.put_bool("SetSpeedLimit", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Confirm New Limits"), "type": "toggle", "get_state": lambda: self._params.get_bool("SLCConfirmation"), "set_state": lambda s: self._params.put_bool("SLCConfirmation", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Higher Lookahead"), "type": "value", "get_value": lambda: f"{self._params.get_int('SLCLookaheadHigher')}s", "on_click": lambda: self._show_int_selector("SLCLookaheadHigher", 0, 30, "s"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Lower Lookahead"), "type": "value", "get_value": lambda: f"{self._params.get_int('SLCLookaheadLower')}s", "on_click": lambda: self._show_int_selector("SLCLookaheadLower", 0, 30, "s"), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Mapbox Fallback"), "type": "toggle", "get_state": lambda: self._params.get_bool("SLCMapboxFiller"), "set_state": lambda s: self._params.put_bool("SLCMapboxFiller", s), "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Match Speed on Engage"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SetSpeedLimit"),
|
||||
"set_state": lambda s: self._params.put_bool("SetSpeedLimit", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Confirm New Limits"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SLCConfirmation"),
|
||||
"set_state": lambda s: self._params.put_bool("SLCConfirmation", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Confirm Lower"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SLCConfirmationLower"),
|
||||
"set_state": lambda s: self._params.put_bool("SLCConfirmationLower", s),
|
||||
"visible": lambda: self._params.get_bool("SLCConfirmation"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Confirm Higher"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SLCConfirmationHigher"),
|
||||
"set_state": lambda s: self._params.put_bool("SLCConfirmationHigher", s),
|
||||
"visible": lambda: self._params.get_bool("SLCConfirmation"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Higher Lookahead"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('SLCLookaheadHigher')}s",
|
||||
"on_click": lambda: self._show_int_selector("SLCLookaheadHigher", 0, 30, "s"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Lower Lookahead"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('SLCLookaheadLower')}s",
|
||||
"on_click": lambda: self._show_int_selector("SLCLookaheadLower", 0, 30, "s"),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Mapbox Fallback"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SLCMapboxFiller"),
|
||||
"set_state": lambda s: self._params.put_bool("SLCMapboxFiller", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -276,17 +713,32 @@ class StarPilotSLCQOLLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, 1, self._params.get_int(key), on_close, unit=unit, color="#1BA1E2"))
|
||||
|
||||
|
||||
class StarPilotSLCVisualsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Show SLC Offset"), "type": "toggle", "get_state": lambda: self._params.get_bool("ShowSLCOffset"), "set_state": lambda s: self._params.put_bool("ShowSLCOffset", s), "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Show Sources"), "type": "toggle", "get_state": lambda: self._params.get_bool("SpeedLimitSources"), "set_state": lambda s: self._params.put_bool("SpeedLimitSources", s), "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Show SLC Offset"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("ShowSLCOffset"),
|
||||
"set_state": lambda s: self._params.put_bool("ShowSLCOffset", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Show Sources"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SpeedLimitSources"),
|
||||
"set_state": lambda s: self._params.put_bool("SpeedLimitSources", s),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
class StarPilotWeatherLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -295,18 +747,77 @@ class StarPilotWeatherLayout(StarPilotPanel):
|
||||
{"title": tr_noop("Rain"), "panel": "rain", "icon": "toggle_icons/icon_rainbow.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Rainstorms"), "panel": "rainstorm", "icon": "toggle_icons/icon_rainbow.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Snow"), "panel": "snow", "icon": "toggle_icons/icon_rainbow.png", "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Set Weather Key"),
|
||||
"type": "hub",
|
||||
"on_click": lambda: self._set_weather_key(),
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _set_weather_key(self):
|
||||
options = ["ADD", "REMOVE"]
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
if val == "ADD":
|
||||
|
||||
def on_key(res, text):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put("WeatherAPIKey", text)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(InputDialog(tr("Weather API Key"), on_close=on_key))
|
||||
elif val == "REMOVE":
|
||||
|
||||
def on_confirm(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.remove("WeatherAPIKey")
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Remove API Key?"), tr("Confirm"), on_close=on_confirm))
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Weather API Key"), options, "ADD", on_close=on_select))
|
||||
|
||||
|
||||
class StarPilotWeatherBase(StarPilotPanel):
|
||||
def __init__(self, suffix: str):
|
||||
super().__init__()
|
||||
self._suffix = suffix
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Following Distance"), "type": "value", "get_value": lambda: f"+{self._params.get_int('IncreaseFollowing' + self._suffix)}s", "on_click": lambda: self._show_value_selector("IncreaseFollowing" + self._suffix, 0, 3, 0.5, "s"), "icon": "toggle_icons/icon_longitudinal_tune.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Stopped Distance"), "type": "value", "get_value": lambda: f"+{self._params.get_int('IncreasedStoppedDistance' + self._suffix)} ft", "on_click": lambda: self._show_value_selector("IncreasedStoppedDistance" + self._suffix, 0, 10, 1, " ft"), "icon": "toggle_icons/icon_longitudinal_tune.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Reduce Accel"), "type": "value", "get_value": lambda: f"{self._params.get_int('ReduceAcceleration' + self._suffix)}%", "on_click": lambda: self._show_value_selector("ReduceAcceleration" + self._suffix, 0, 50, 5, "%"), "icon": "toggle_icons/icon_longitudinal_tune.png", "color": "#1BA1E2"},
|
||||
{"title": tr_noop("Reduce Curve Speed"), "type": "value", "get_value": lambda: f"{self._params.get_int('ReduceLateralAcceleration' + self._suffix)}%", "on_click": lambda: self._show_value_selector("ReduceLateralAcceleration" + self._suffix, 0, 50, 5, "%"), "icon": "toggle_icons/icon_longitudinal_tune.png", "color": "#1BA1E2"},
|
||||
{
|
||||
"title": tr_noop("Following Distance"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"+{self._params.get_int('IncreaseFollowing' + self._suffix)}s",
|
||||
"on_click": lambda: self._show_value_selector("IncreaseFollowing" + self._suffix, 0, 3, 0.5, "s"),
|
||||
"icon": "toggle_icons/icon_longitudinal_tune.png",
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Stopped Distance"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"+{self._params.get_int('IncreasedStoppedDistance' + self._suffix)} ft",
|
||||
"on_click": lambda: self._show_value_selector("IncreasedStoppedDistance" + self._suffix, 0, 10, 1, " ft"),
|
||||
"icon": "toggle_icons/icon_longitudinal_tune.png",
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Reduce Accel"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('ReduceAcceleration' + self._suffix)}%",
|
||||
"on_click": lambda: self._show_value_selector("ReduceAcceleration" + self._suffix, 0, 99, 1, "%"),
|
||||
"icon": "toggle_icons/icon_longitudinal_tune.png",
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Reduce Curve Speed"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_int('ReduceLateralAcceleration' + self._suffix)}%",
|
||||
"on_click": lambda: self._show_value_selector("ReduceLateralAcceleration" + self._suffix, 0, 99, 1, "%"),
|
||||
"icon": "toggle_icons/icon_longitudinal_tune.png",
|
||||
"color": "#1BA1E2",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -315,5 +826,6 @@ class StarPilotWeatherBase(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(float(val)))
|
||||
self._rebuild_grid()
|
||||
|
||||
curr = self._params.get_int(key)
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, step, curr, on_close, unit=unit, color="#1BA1E2"))
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
|
||||
from openpilot.system.ui.widgets.input_dialog import InputDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import SliderDialog
|
||||
|
||||
|
||||
class StarPilotNavigationLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
@@ -20,42 +17,118 @@ class StarPilotNavigationLayout(StarPilotPanel):
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Mapbox Credentials"), "panel": "mapbox", "icon": "toggle_icons/icon_navigate.png", "color": "#8CBF26"},
|
||||
{"title": tr_noop("Setup Instructions"), "type": "hub", "on_click": self._on_setup, "icon": "toggle_icons/icon_navigate.png", "color": "#8CBF26"},
|
||||
{"title": tr_noop("Speed Limit Filler"), "type": "toggle", "get_state": lambda: self._params.get_bool("SpeedLimitFiller"), "set_state": lambda s: self._params.put_bool("SpeedLimitFiller", s), "icon": "toggle_icons/icon_speed_limit.png", "color": "#8CBF26"},
|
||||
{
|
||||
"title": tr_noop("Speed Limit Filler"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SpeedLimitFiller"),
|
||||
"set_state": lambda s: self._params.put_bool("SpeedLimitFiller", s),
|
||||
"icon": "toggle_icons/icon_speed_limit.png",
|
||||
"color": "#8CBF26",
|
||||
},
|
||||
{"title": tr_noop("Search Destination"), "type": "hub", "on_click": self._on_search, "icon": "toggle_icons/icon_navigate.png", "color": "#8CBF26"},
|
||||
{"title": tr_noop("Home Address"), "type": "hub", "on_click": self._on_home, "icon": "toggle_icons/icon_navigate.png", "color": "#8CBF26"},
|
||||
{"title": tr_noop("Work Address"), "type": "hub", "on_click": self._on_work, "icon": "toggle_icons/icon_navigate.png", "color": "#8CBF26"},
|
||||
{
|
||||
"title": tr_noop("Home Address"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("HomeAddress", encoding='utf-8') or tr("Not set"),
|
||||
"on_click": self._on_home,
|
||||
"icon": "toggle_icons/icon_navigate.png",
|
||||
"color": "#8CBF26",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Work Address"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("WorkAddress", encoding='utf-8') or tr("Not set"),
|
||||
"on_click": self._on_work,
|
||||
"icon": "toggle_icons/icon_navigate.png",
|
||||
"color": "#8CBF26",
|
||||
},
|
||||
]
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back)
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
self._rebuild_grid()
|
||||
|
||||
def _on_setup(self):
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Mapbox Setup:\n1. Create account at mapbox.com\n2. Generate Public/Secret keys\n3. Add keys in 'Mapbox Credentials'")))
|
||||
gui_app.set_modal_overlay(
|
||||
alert_dialog(tr("Mapbox Setup:\n1. Create account at mapbox.com\n2. Generate Public/Secret keys\n3. Add keys in 'Mapbox Credentials'"))
|
||||
)
|
||||
|
||||
def _on_search(self):
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Search not yet implemented.")))
|
||||
def on_close(res, text):
|
||||
if res == DialogResult.CONFIRM and text:
|
||||
self._params.put("SearchAddress", text)
|
||||
|
||||
gui_app.set_modal_overlay(InputDialog(tr("Search Destination"), "", on_close=on_close))
|
||||
|
||||
def _on_home(self):
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Home address set.")))
|
||||
current = self._params.get("HomeAddress", encoding='utf-8') or ""
|
||||
|
||||
def on_close(res, text):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put("HomeAddress", text)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(InputDialog(tr("Home Address"), current, on_close=on_close))
|
||||
|
||||
def _on_work(self):
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Work address set.")))
|
||||
current = self._params.get("WorkAddress", encoding='utf-8') or ""
|
||||
|
||||
def on_close(res, text):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put("WorkAddress", text)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(InputDialog(tr("Work Address"), current, on_close=on_close))
|
||||
|
||||
|
||||
class StarPilotMapboxLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Public Mapbox Key"), "type": "hub", "on_click": lambda: self._on_key("MapboxPublicKey"), "color": "#8CBF26"},
|
||||
{"title": tr_noop("Secret Mapbox Key"), "type": "hub", "on_click": lambda: self._on_key("MapboxSecretKey"), "color": "#8CBF26"},
|
||||
{
|
||||
"title": tr_noop("Public Mapbox Key"),
|
||||
"type": "value",
|
||||
"get_value": self._get_key_display,
|
||||
"on_click": lambda: self._on_key("MapboxPublicKey", "pk."),
|
||||
"color": "#8CBF26",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Secret Mapbox Key"),
|
||||
"type": "value",
|
||||
"get_value": self._get_secret_display,
|
||||
"on_click": lambda: self._on_key("MapboxSecretKey", "sk."),
|
||||
"color": "#8CBF26",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _on_key(self, key):
|
||||
# Simplified keyboard entry for UI port
|
||||
def _get_key_display(self):
|
||||
v = self._params.get("MapboxPublicKey", encoding='utf-8') or ""
|
||||
return f"{v[:8]}..." if v else tr("Not set")
|
||||
|
||||
def _get_secret_display(self):
|
||||
v = self._params.get("MapboxSecretKey", encoding='utf-8') or ""
|
||||
return "********" if v else tr("Not set")
|
||||
|
||||
def _on_key(self, key, prefix):
|
||||
current = self._params.get(key, encoding='utf-8') or ""
|
||||
def on_confirm(res):
|
||||
if current:
|
||||
|
||||
def on_remove(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
# In a real build, we'd trigger a keyboard overlay
|
||||
pass
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr(f"Current Key:\n{current[:20]}..."), tr("Change"), on_close=on_confirm))
|
||||
self._params.remove(key)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr(f"Remove your {key.replace('Mapbox', '')} key?"), tr("Remove"), on_close=on_remove))
|
||||
else:
|
||||
|
||||
def on_close(res, text):
|
||||
if res == DialogResult.CONFIRM and text:
|
||||
if not text.startswith(prefix):
|
||||
text = prefix + text
|
||||
self._params.put(key, text)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(InputDialog(tr(f"Enter {key.replace('Mapbox', 'Mapbox ')}"), "", on_close=on_close))
|
||||
|
||||
@@ -9,6 +9,7 @@ from openpilot.common.params import Params
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
|
||||
class StarPilotPanelType(IntEnum):
|
||||
MAIN = 0
|
||||
SOUNDS = 1
|
||||
@@ -25,13 +26,16 @@ class StarPilotPanelType(IntEnum):
|
||||
VEHICLE = 12
|
||||
WHEEL = 13
|
||||
|
||||
|
||||
@dataclass
|
||||
class StarPilotPanelInfo:
|
||||
name: str
|
||||
instance: Widget
|
||||
|
||||
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import TileGrid, HubTile, ToggleTile, ValueTile
|
||||
|
||||
|
||||
class StarPilotPanel(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -54,13 +58,17 @@ class StarPilotPanel(Widget):
|
||||
def _rebuild_grid(self):
|
||||
if not self.CATEGORIES:
|
||||
return
|
||||
|
||||
|
||||
if self._tile_grid is None:
|
||||
self._tile_grid = TileGrid(columns=None, padding=20)
|
||||
|
||||
|
||||
self._tile_grid.clear()
|
||||
|
||||
|
||||
for cat in self.CATEGORIES:
|
||||
visible_fn = cat.get("visible")
|
||||
if visible_fn is not None and not visible_fn():
|
||||
continue
|
||||
|
||||
tile_type = cat.get("type", "hub")
|
||||
if tile_type == "hub":
|
||||
on_click = cat.get("on_click")
|
||||
@@ -73,24 +81,12 @@ class StarPilotPanel(Widget):
|
||||
icon_path=cat.get("icon"),
|
||||
on_click=on_click,
|
||||
starpilot_icon=cat.get("starpilot_icon", True),
|
||||
bg_color=cat.get("color")
|
||||
bg_color=cat.get("color"),
|
||||
)
|
||||
elif tile_type == "toggle":
|
||||
tile = ToggleTile(
|
||||
title=tr(cat["title"]),
|
||||
get_state=cat["get_state"],
|
||||
set_state=cat["set_state"],
|
||||
icon_path=cat.get("icon"),
|
||||
bg_color=cat.get("color")
|
||||
)
|
||||
tile = ToggleTile(title=tr(cat["title"]), get_state=cat["get_state"], set_state=cat["set_state"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
elif tile_type == "value":
|
||||
tile = ValueTile(
|
||||
title=tr(cat["title"]),
|
||||
get_value=cat["get_value"],
|
||||
on_click=cat["on_click"],
|
||||
icon_path=cat.get("icon"),
|
||||
bg_color=cat.get("color")
|
||||
)
|
||||
tile = ValueTile(title=tr(cat["title"]), get_value=cat["get_value"], on_click=cat["on_click"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
else:
|
||||
continue
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@ from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
|
||||
class StarPilotThemesLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
|
||||
self._sub_panels = {
|
||||
"personalize": StarPilotPersonalizeLayout(),
|
||||
}
|
||||
@@ -19,7 +20,7 @@ class StarPilotThemesLayout(StarPilotPanel):
|
||||
"panel": "personalize",
|
||||
"icon": "toggle_icons/icon_frog.png",
|
||||
"color": "#A200FF",
|
||||
"desc": tr_noop("Customize the overall look and feel.")
|
||||
"desc": tr_noop("Customize the overall look and feel."),
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Holiday Themes"),
|
||||
@@ -27,7 +28,7 @@ class StarPilotThemesLayout(StarPilotPanel):
|
||||
"get_state": lambda: self._params.get_bool("HolidayThemes"),
|
||||
"set_state": lambda s: self._params.put_bool("HolidayThemes", s),
|
||||
"icon": "toggle_icons/icon_calendar.png",
|
||||
"color": "#A200FF"
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Rainbow Path"),
|
||||
@@ -35,7 +36,7 @@ class StarPilotThemesLayout(StarPilotPanel):
|
||||
"get_state": lambda: self._params.get_bool("RainbowPath"),
|
||||
"set_state": lambda s: self._params.put_bool("RainbowPath", s),
|
||||
"icon": "toggle_icons/icon_rainbow.png",
|
||||
"color": "#A200FF"
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Random Events"),
|
||||
@@ -43,7 +44,7 @@ class StarPilotThemesLayout(StarPilotPanel):
|
||||
"get_state": lambda: self._params.get_bool("RandomEvents"),
|
||||
"set_state": lambda s: self._params.put_bool("RandomEvents", s),
|
||||
"icon": "toggle_icons/icon_random.png",
|
||||
"color": "#A200FF"
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Random Themes"),
|
||||
@@ -51,27 +52,97 @@ class StarPilotThemesLayout(StarPilotPanel):
|
||||
"get_state": lambda: self._params.get_bool("RandomThemes"),
|
||||
"set_state": lambda s: self._params.put_bool("RandomThemes", s),
|
||||
"icon": "toggle_icons/icon_random_themes.png",
|
||||
"color": "#A200FF"
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{"title": tr_noop("Startup Alert"), "type": "hub", "on_click": self._on_startup_alert, "color": "#A200FF"},
|
||||
]
|
||||
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back)
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
|
||||
self._rebuild_grid()
|
||||
|
||||
def _on_startup_alert(self):
|
||||
options = ["Stock", "FrogPilot", "Clear"]
|
||||
current_top = self._params.get("StartupMessageTop", encoding='utf-8') or ""
|
||||
if current_top == "Be ready to take over at any time":
|
||||
current = "Stock"
|
||||
elif current_top == "Hop in and buckle up!":
|
||||
current = "FrogPilot"
|
||||
else:
|
||||
current = "Clear"
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
if val == "Stock":
|
||||
self._params.put("StartupMessageTop", "Be ready to take over at any time")
|
||||
self._params.put("StartupMessageBottom", "Always keep hands on wheel and eyes on road")
|
||||
elif val == "FrogPilot":
|
||||
self._params.put("StartupMessageTop", "Hop in and buckle up!")
|
||||
self._params.put("StartupMessageBottom", "Human-tested, frog-approved")
|
||||
else:
|
||||
self._params.remove("StartupMessageTop")
|
||||
self._params.remove("StartupMessageBottom")
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Startup Alert"), options, current, on_close=on_select))
|
||||
|
||||
|
||||
class StarPilotPersonalizeLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Boot Logo"), "type": "value", "get_value": lambda: self._params.get("BootLogo", encoding='utf-8') or "Stock", "on_click": lambda: self._show_theme_selector("BootLogo"), "color": "#A200FF"},
|
||||
{"title": tr_noop("Color Scheme"), "type": "value", "get_value": lambda: self._params.get("ColorScheme", encoding='utf-8') or "Stock", "on_click": lambda: self._show_theme_selector("ColorScheme"), "color": "#A200FF"},
|
||||
{"title": tr_noop("Distance Icons"), "type": "value", "get_value": lambda: self._params.get("DistanceIconPack", encoding='utf-8') or "Stock", "on_click": lambda: self._show_theme_selector("DistanceIconPack"), "color": "#A200FF"},
|
||||
{"title": tr_noop("Icon Pack"), "type": "value", "get_value": lambda: self._params.get("IconPack", encoding='utf-8') or "Stock", "on_click": lambda: self._show_theme_selector("IconPack"), "color": "#A200FF"},
|
||||
{"title": tr_noop("Turn Signals"), "type": "value", "get_value": lambda: self._params.get("SignalAnimation", encoding='utf-8') or "Stock", "on_click": lambda: self._show_theme_selector("SignalAnimation"), "color": "#A200FF"},
|
||||
{"title": tr_noop("Sound Pack"), "type": "value", "get_value": lambda: self._params.get("SoundPack", encoding='utf-8') or "Stock", "on_click": lambda: self._show_theme_selector("SoundPack"), "color": "#A200FF"},
|
||||
{"title": tr_noop("Steering Wheel"), "type": "value", "get_value": lambda: self._params.get("WheelIcon", encoding='utf-8') or "Stock", "on_click": lambda: self._show_theme_selector("WheelIcon"), "color": "#A200FF"},
|
||||
{
|
||||
"title": tr_noop("Boot Logo"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("BootLogo", encoding='utf-8') or "Stock",
|
||||
"on_click": lambda: self._show_theme_selector("BootLogo"),
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Color Scheme"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("ColorScheme", encoding='utf-8') or "Stock",
|
||||
"on_click": lambda: self._show_theme_selector("ColorScheme"),
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Distance Icons"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("DistanceIconPack", encoding='utf-8') or "Stock",
|
||||
"on_click": lambda: self._show_theme_selector("DistanceIconPack"),
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Icon Pack"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("IconPack", encoding='utf-8') or "Stock",
|
||||
"on_click": lambda: self._show_theme_selector("IconPack"),
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Turn Signals"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("SignalAnimation", encoding='utf-8') or "Stock",
|
||||
"on_click": lambda: self._show_theme_selector("SignalAnimation"),
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Sound Pack"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("SoundPack", encoding='utf-8') or "Stock",
|
||||
"on_click": lambda: self._show_theme_selector("SoundPack"),
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Steering Wheel"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("WheelIcon", encoding='utf-8') or "Stock",
|
||||
"on_click": lambda: self._show_theme_selector("WheelIcon"),
|
||||
"color": "#A200FF",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -80,10 +151,10 @@ class StarPilotPersonalizeLayout(StarPilotPanel):
|
||||
# For now, we'll provide a simplified selection based on current param.
|
||||
themes = ["Stock", "Frog", "Cyberpunk", "Minimal"]
|
||||
current = self._params.get(key, encoding='utf-8') or "Stock"
|
||||
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put(key, val)
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr(key), themes, current, on_close=on_select))
|
||||
|
||||
@@ -1,28 +1,77 @@
|
||||
from __future__ import annotations
|
||||
import json
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.system.ui.widgets.input_dialog import InputDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
EXCLUDED_KEYS = {
|
||||
"AvailableModels",
|
||||
"AvailableModelNames",
|
||||
"FrogPilotStats",
|
||||
"GithubSshKeys",
|
||||
"GithubUsername",
|
||||
"MapBoxRequests",
|
||||
"ModelDrivesAndScores",
|
||||
"OverpassRequests",
|
||||
"SpeedLimits",
|
||||
"SpeedLimitsFiltered",
|
||||
"UpdaterAvailableBranches",
|
||||
}
|
||||
|
||||
REPORT_CATEGORIES = [
|
||||
"Acceleration feels harsh or jerky",
|
||||
"An alert was unclear and I'm not sure what it meant",
|
||||
"Braking is too sudden or uncomfortable",
|
||||
"I'm not sure if this is normal or a bug:",
|
||||
"My steering wheel buttons aren't working",
|
||||
"openpilot disengages when I don't expect it",
|
||||
"openpilot feels sluggish or slow to respond",
|
||||
"Something else (please describe)",
|
||||
]
|
||||
|
||||
|
||||
class StarPilotUtilitiesLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Debug Mode"), "type": "toggle", "get_state": lambda: self._params.get_bool("DebugMode"), "set_state": lambda s: self._params.put_bool("DebugMode", s), "color": "#FA6800"},
|
||||
{
|
||||
"title": tr_noop("Debug Mode"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("DebugMode"),
|
||||
"set_state": lambda s: self._params.put_bool("DebugMode", s),
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{"title": tr_noop("Flash Panda"), "type": "hub", "on_click": self._on_flash_panda, "color": "#FA6800"},
|
||||
{"title": tr_noop("Force Drive State"), "type": "value", "get_value": self._get_force_drive_state, "on_click": self._on_force_drive_state, "color": "#FA6800"},
|
||||
{"title": tr_noop("The Pond"), "type": "hub", "on_click": self._on_pond_clicked, "color": "#FA6800"},
|
||||
{
|
||||
"title": tr_noop("Force Drive State"),
|
||||
"type": "value",
|
||||
"get_value": self._get_force_drive_state,
|
||||
"on_click": self._on_force_drive_state,
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("The Pond"),
|
||||
"type": "value",
|
||||
"get_value": lambda: tr("Paired") if self._params.get_bool("PondPaired") else tr("Not paired"),
|
||||
"on_click": self._on_pond_clicked,
|
||||
"color": "#FA6800",
|
||||
},
|
||||
{"title": tr_noop("Report Issue"), "type": "hub", "on_click": self._on_report_issue, "color": "#FA6800"},
|
||||
{"title": tr_noop("Reset Toggles"), "type": "hub", "on_click": self._on_reset_toggles, "color": "#FA6800"},
|
||||
{"title": tr_noop("Reset to Defaults"), "type": "hub", "on_click": self._on_reset_defaults, "color": "#FA6800"},
|
||||
{"title": tr_noop("Reset to Stock"), "type": "hub", "on_click": self._on_reset_stock, "color": "#FA6800"},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _get_force_drive_state(self):
|
||||
if self._params.get_bool("ForceOnroad"): return tr("Onroad")
|
||||
if self._params.get_bool("ForceOffroad"): return tr("Offroad")
|
||||
if self._params.get_bool("ForceOnroad"):
|
||||
return tr("Onroad")
|
||||
if self._params.get_bool("ForceOffroad"):
|
||||
return tr("Offroad")
|
||||
return tr("Default")
|
||||
|
||||
def _on_flash_panda(self):
|
||||
@@ -30,10 +79,12 @@ class StarPilotUtilitiesLayout(StarPilotPanel):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params_memory.put_bool("FlashPanda", True)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Panda flashing started. Device will reboot when finished.")))
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Flash Panda firmware?"), tr("Flash"), on_close=_do_flash))
|
||||
|
||||
def _on_force_drive_state(self):
|
||||
options = [tr("Offroad"), tr("Onroad"), tr("Default")]
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
if val == tr("Offroad"):
|
||||
@@ -46,23 +97,67 @@ class StarPilotUtilitiesLayout(StarPilotPanel):
|
||||
self._params.put_bool("ForceOffroad", False)
|
||||
self._params.put_bool("ForceOnroad", False)
|
||||
self._rebuild_grid()
|
||||
|
||||
current = self._get_force_drive_state()
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Force Drive State"), options, current, on_close=on_select))
|
||||
|
||||
def _on_pond_clicked(self):
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("The Pond pairing not yet implemented in Python.")))
|
||||
paired = self._params.get_bool("PondPaired")
|
||||
if paired:
|
||||
|
||||
def on_unpair(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_bool("PondPaired", False)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Unpaired from The Pond.")))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Unpair from The Pond?"), tr("Unpair"), on_close=on_unpair))
|
||||
else:
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Visit frogpilot.com/the_pond to pair your device.")))
|
||||
|
||||
def _on_report_issue(self):
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Issue reporting not yet implemented in Python.")))
|
||||
def on_category(res, val):
|
||||
if res != DialogResult.CONFIRM:
|
||||
return
|
||||
discord_user = self._params.get("DiscordUsername", encoding='utf-8') or ""
|
||||
|
||||
def _on_reset_toggles(self):
|
||||
def on_discord(res2, username):
|
||||
if res2 == DialogResult.CONFIRM and username:
|
||||
self._params.put("DiscordUsername", username)
|
||||
report = json.dumps({"DiscordUser": username, "Issue": val})
|
||||
self._params_memory.put("IssueReported", report)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Issue reported. Thank you!")))
|
||||
|
||||
gui_app.set_modal_overlay(InputDialog(tr("Discord Username"), discord_user or "", on_close=on_discord))
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Select Issue"), REPORT_CATEGORIES, on_close=on_category))
|
||||
|
||||
def _on_reset_defaults(self):
|
||||
def _do_reset(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
# Simplified reset logic
|
||||
all_keys = self._params.all_keys()
|
||||
for k in all_keys:
|
||||
if k in EXCLUDED_KEYS:
|
||||
continue
|
||||
default = self._params.get_default_value(k)
|
||||
if default: self._params.put(k, default)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Toggles reset to default.")))
|
||||
if default is not None:
|
||||
self._params.put(k, default)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Toggles reset to defaults.")))
|
||||
self._rebuild_grid()
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Reset all toggles to default?"), tr("Reset"), on_close=_do_reset))
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Reset all toggles to defaults?"), tr("Reset"), on_close=_do_reset))
|
||||
|
||||
def _on_reset_stock(self):
|
||||
def _do_reset(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
all_keys = self._params.all_keys()
|
||||
for k in all_keys:
|
||||
if k in EXCLUDED_KEYS:
|
||||
continue
|
||||
stock = self._params.get_stock_value(k)
|
||||
if stock is not None:
|
||||
self._params.put(k, stock)
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Toggles reset to stock openpilot.")))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Reset all toggles to stock openpilot?"), tr("Reset"), on_close=_do_reset))
|
||||
|
||||
@@ -10,38 +10,64 @@ from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import SliderDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import SliderDialog, TileGrid, HubTile, ToggleTile, ValueTile
|
||||
from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state
|
||||
|
||||
MAKE_TO_FOLDER = {
|
||||
"acura": "honda", "audi": "volkswagen", "buick": "gm", "cadillac": "gm", "chevrolet": "gm",
|
||||
"chrysler": "chrysler", "cupra": "volkswagen", "dodge": "chrysler", "ford": "ford",
|
||||
"genesis": "hyundai", "gmc": "gm", "holden": "gm", "honda": "honda", "hyundai": "hyundai",
|
||||
"jeep": "chrysler", "kia": "hyundai", "lexus": "toyota", "lincoln": "ford", "man": "volkswagen",
|
||||
"mazda": "mazda", "nissan": "nissan", "peugeot": "psa", "ram": "chrysler", "rivian": "rivian",
|
||||
"seat": "volkswagen", "škoda": "volkswagen", "subaru": "subaru", "tesla": "tesla",
|
||||
"toyota": "toyota", "volkswagen": "volkswagen"
|
||||
"acura": "honda",
|
||||
"audi": "volkswagen",
|
||||
"buick": "gm",
|
||||
"cadillac": "gm",
|
||||
"chevrolet": "gm",
|
||||
"chrysler": "chrysler",
|
||||
"cupra": "volkswagen",
|
||||
"dodge": "chrysler",
|
||||
"ford": "ford",
|
||||
"genesis": "hyundai",
|
||||
"gmc": "gm",
|
||||
"holden": "gm",
|
||||
"honda": "honda",
|
||||
"hyundai": "hyundai",
|
||||
"jeep": "chrysler",
|
||||
"kia": "hyundai",
|
||||
"lexus": "toyota",
|
||||
"lincoln": "ford",
|
||||
"man": "volkswagen",
|
||||
"mazda": "mazda",
|
||||
"nissan": "nissan",
|
||||
"peugeot": "psa",
|
||||
"ram": "chrysler",
|
||||
"rivian": "rivian",
|
||||
"seat": "volkswagen",
|
||||
"škoda": "volkswagen",
|
||||
"subaru": "subaru",
|
||||
"tesla": "tesla",
|
||||
"toyota": "toyota",
|
||||
"volkswagen": "volkswagen",
|
||||
}
|
||||
|
||||
|
||||
def get_car_names(car_make: str):
|
||||
folder = MAKE_TO_FOLDER.get(car_make.lower())
|
||||
if not folder: return [], {}
|
||||
|
||||
if not folder:
|
||||
return [], {}
|
||||
|
||||
# Path to values.py in opendbc
|
||||
values_path = Path(__file__).parents[4] / "opendbc" / "car" / folder / "values.py"
|
||||
if not values_path.exists():
|
||||
return [], {}
|
||||
|
||||
|
||||
with open(values_path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
|
||||
# Clean comments
|
||||
content = re.sub(r'#.*', '', content)
|
||||
|
||||
|
||||
# Find platforms and car names
|
||||
platforms = re.findall(r'(\w+)\s*=\s*\w+\s*\(', content)
|
||||
car_models = {}
|
||||
car_names = []
|
||||
|
||||
|
||||
# This is a simplified version of the C++ regex logic
|
||||
# In values.py, CarDocs often appears as CarDocs("Name", ...)
|
||||
matches = re.finditer(r'CarDocs\w*\s*\(\s*"([^"]+)"', content)
|
||||
@@ -51,9 +77,17 @@ def get_car_names(car_make: str):
|
||||
# Find the platform name by looking backwards for the nearest platform assignment
|
||||
# For now, we'll just store the name
|
||||
car_names.append(name)
|
||||
|
||||
|
||||
return sorted(list(set(car_names))), car_models
|
||||
|
||||
|
||||
def _lock_doors_timer_labels():
|
||||
labels: dict[float, str] = {0.0: tr("Never")}
|
||||
for i in range(5, 305, 5):
|
||||
labels[float(i)] = f"{i}s"
|
||||
return labels
|
||||
|
||||
|
||||
class StarPilotVehicleSettingsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -64,46 +98,108 @@ class StarPilotVehicleSettingsLayout(StarPilotPanel):
|
||||
"toyota": StarPilotToyotaVehicleLayout(),
|
||||
"info": StarPilotVehicleInfoLayout(),
|
||||
}
|
||||
|
||||
|
||||
self.CATEGORIES = [
|
||||
{
|
||||
"title": tr_noop("Car Make"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("CarMake", encoding='utf-8') or tr("None"),
|
||||
"on_click": self._on_select_make,
|
||||
"color": "#FFC40D"
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Car Model"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._params.get("CarModelName", encoding='utf-8') or tr("None"),
|
||||
"on_click": self._on_select_model,
|
||||
"color": "#FFC40D"
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{"title": tr_noop("Disable Fingerprinting"), "type": "toggle", "get_state": lambda: self._params.get_bool("ForceFingerprint"), "set_state": lambda s: self._params.put_bool("ForceFingerprint", s), "color": "#FFC40D"},
|
||||
{"title": tr_noop("Disable openpilot Long"), "type": "toggle", "get_state": lambda: self._params.get_bool("DisableOpenpilotLongitudinal"), "set_state": self._on_disable_long, "color": "#FFC40D"},
|
||||
{"title": tr_noop("GM Settings"), "panel": "gm", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D"},
|
||||
{"title": tr_noop("HKG Settings"), "panel": "hkg", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D"},
|
||||
{"title": tr_noop("Subaru Settings"), "panel": "subaru", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D"},
|
||||
{"title": tr_noop("Toyota Settings"), "panel": "toyota", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D"},
|
||||
{
|
||||
"title": tr_noop("Disable Fingerprinting"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("ForceFingerprint"),
|
||||
"set_state": lambda s: self._params.put_bool("ForceFingerprint", s),
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Disable openpilot Long"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("DisableOpenpilotLongitudinal"),
|
||||
"set_state": self._on_disable_long,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{"title": tr_noop("GM Settings"), "panel": "gm", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D", "key": "gm"},
|
||||
{"title": tr_noop("HKG Settings"), "panel": "hkg", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D", "key": "hkg"},
|
||||
{"title": tr_noop("Subaru Settings"), "panel": "subaru", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D", "key": "subaru"},
|
||||
{"title": tr_noop("Toyota Settings"), "panel": "toyota", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D", "key": "toyota"},
|
||||
{"title": tr_noop("Vehicle Info"), "panel": "info", "icon": "toggle_icons/icon_vehicle.png", "color": "#FFC40D"},
|
||||
]
|
||||
|
||||
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back)
|
||||
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
|
||||
self._rebuild_grid()
|
||||
|
||||
def _rebuild_grid(self):
|
||||
if not self.CATEGORIES:
|
||||
return
|
||||
if self._tile_grid is None:
|
||||
self._tile_grid = TileGrid(columns=None, padding=20)
|
||||
self._tile_grid.clear()
|
||||
|
||||
cs = starpilot_state.car_state
|
||||
for cat in self.CATEGORIES:
|
||||
key = cat.get("key")
|
||||
visible = True
|
||||
|
||||
if key == "gm":
|
||||
visible = cs.isGM
|
||||
elif key == "hkg":
|
||||
visible = cs.isHKG
|
||||
elif key == "subaru":
|
||||
visible = cs.isSubaru
|
||||
elif key == "toyota":
|
||||
visible = cs.isToyota
|
||||
|
||||
if not visible:
|
||||
continue
|
||||
|
||||
tile_type = cat.get("type", "hub")
|
||||
if tile_type == "hub":
|
||||
on_click = cat.get("on_click")
|
||||
if on_click is None:
|
||||
on_click = lambda c=cat: self._navigate_to(c["panel"])
|
||||
tile = HubTile(
|
||||
title=tr(cat["title"]),
|
||||
desc=tr(cat.get("desc", "")),
|
||||
icon_path=cat.get("icon"),
|
||||
on_click=on_click,
|
||||
starpilot_icon=cat.get("starpilot_icon", True),
|
||||
bg_color=cat.get("color"),
|
||||
)
|
||||
elif tile_type == "toggle":
|
||||
tile = ToggleTile(title=tr(cat["title"]), get_state=cat["get_state"], set_state=cat["set_state"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
elif tile_type == "value":
|
||||
tile = ValueTile(title=tr(cat["title"]), get_value=cat["get_value"], on_click=cat["on_click"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
else:
|
||||
continue
|
||||
|
||||
self._tile_grid.add_tile(tile)
|
||||
|
||||
def _on_select_make(self):
|
||||
makes = sorted(list(MAKE_TO_FOLDER.keys()))
|
||||
makes = [m.capitalize() for m in makes]
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put("CarMake", val)
|
||||
self._params.remove("CarModel")
|
||||
self._params.remove("CarModelName")
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Select Make"), makes, self._params.get("CarMake", encoding='utf-8') or "", on_close=on_select))
|
||||
|
||||
def _on_select_model(self):
|
||||
@@ -111,7 +207,7 @@ class StarPilotVehicleSettingsLayout(StarPilotPanel):
|
||||
if not make:
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Please select a Car Make first!"), tr("OK"), on_close=lambda r: None))
|
||||
return
|
||||
|
||||
|
||||
models, _ = get_car_names(make)
|
||||
if not models:
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("No models found for this make."), tr("OK"), on_close=lambda r: None))
|
||||
@@ -122,71 +218,299 @@ class StarPilotVehicleSettingsLayout(StarPilotPanel):
|
||||
self._params.put("CarModelName", val)
|
||||
# In a real build we'd map name to platform code here
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Select Model"), models, self._params.get("CarModelName", encoding='utf-8') or "", on_close=on_select))
|
||||
|
||||
def _on_disable_long(self, state):
|
||||
if state:
|
||||
|
||||
def on_confirm(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_bool("DisableOpenpilotLongitudinal", True)
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
if ui_state.started: HARDWARE.reboot()
|
||||
|
||||
if ui_state.started:
|
||||
HARDWARE.reboot()
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(ConfirmDialog(tr("Disable openpilot longitudinal control?"), tr("Disable"), on_close=on_confirm))
|
||||
else:
|
||||
self._params.put_bool("DisableOpenpilotLongitudinal", False)
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
class StarPilotGMVehicleLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Pedal for Long"), "type": "toggle", "get_state": lambda: self._params.get_bool("GMPedalLongitudinal"), "set_state": lambda s: self._params.put_bool("GMPedalLongitudinal", s), "color": "#FFC40D"},
|
||||
{"title": tr_noop("Remote Start Panda"), "type": "toggle", "get_state": lambda: self._params.get_bool("RemoteStartBootsComma"), "set_state": lambda s: self._params.put_bool("RemoteStartBootsComma", s), "color": "#FFC40D"},
|
||||
{"title": tr_noop("Volt SNG Hack"), "type": "toggle", "get_state": lambda: self._params.get_bool("VoltSNG"), "set_state": lambda s: self._params.put_bool("VoltSNG", s), "color": "#FFC40D"},
|
||||
{
|
||||
"title": tr_noop("Pedal for Long"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("GMPedalLongitudinal"),
|
||||
"set_state": lambda s: self._params.put_bool("GMPedalLongitudinal", s),
|
||||
"color": "#FFC40D",
|
||||
"key": "GMPedalLongitudinal",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Remote Start Panda"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("RemoteStartBootsComma"),
|
||||
"set_state": lambda s: self._params.put_bool("RemoteStartBootsComma", s),
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Volt SNG Hack"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("VoltSNG"),
|
||||
"set_state": lambda s: self._params.put_bool("VoltSNG", s),
|
||||
"color": "#FFC40D",
|
||||
"key": "VoltSNG",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _rebuild_grid(self):
|
||||
if not self.CATEGORIES:
|
||||
return
|
||||
if self._tile_grid is None:
|
||||
self._tile_grid = TileGrid(columns=None, padding=20)
|
||||
self._tile_grid.clear()
|
||||
|
||||
cs = starpilot_state.car_state
|
||||
for cat in self.CATEGORIES:
|
||||
key = cat.get("key")
|
||||
visible = True
|
||||
|
||||
if key == "GMPedalLongitudinal":
|
||||
visible = cs.hasPedal or cs.canUsePedal
|
||||
elif key == "VoltSNG":
|
||||
visible = cs.isVolt and not cs.hasSNG
|
||||
|
||||
if not visible:
|
||||
continue
|
||||
|
||||
tile = ToggleTile(title=tr(cat["title"]), get_state=cat["get_state"], set_state=cat["set_state"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
self._tile_grid.add_tile(tile)
|
||||
|
||||
|
||||
class StarPilotHKGVehicleLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Taco Bell Torque Hack"), "type": "toggle", "get_state": lambda: self._params.get_bool("TacoTuneHacks"), "set_state": lambda s: self._params.put_bool("TacoTuneHacks", s), "color": "#FFC40D"},
|
||||
{
|
||||
"title": tr_noop("Taco Bell Torque Hack"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("TacoTuneHacks"),
|
||||
"set_state": lambda s: self._params.put_bool("TacoTuneHacks", s),
|
||||
"color": "#FFC40D",
|
||||
"key": "TacoTuneHacks",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _rebuild_grid(self):
|
||||
if not self.CATEGORIES:
|
||||
return
|
||||
if self._tile_grid is None:
|
||||
self._tile_grid = TileGrid(columns=None, padding=20)
|
||||
self._tile_grid.clear()
|
||||
|
||||
cs = starpilot_state.car_state
|
||||
for cat in self.CATEGORIES:
|
||||
key = cat.get("key")
|
||||
visible = True
|
||||
|
||||
if key == "TacoTuneHacks":
|
||||
visible = cs.isHKGCanFd
|
||||
|
||||
if not visible:
|
||||
continue
|
||||
|
||||
tile = ToggleTile(title=tr(cat["title"]), get_state=cat["get_state"], set_state=cat["set_state"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
self._tile_grid.add_tile(tile)
|
||||
|
||||
|
||||
class StarPilotSubaruVehicleLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Stop and Go"), "type": "toggle", "get_state": lambda: self._params.get_bool("SubaruSNG"), "set_state": lambda s: self._params.put_bool("SubaruSNG", s), "color": "#FFC40D"},
|
||||
{
|
||||
"title": tr_noop("Stop and Go"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SubaruSNG"),
|
||||
"set_state": lambda s: self._params.put_bool("SubaruSNG", s),
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
class StarPilotToyotaVehicleLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Auto Lock Doors"), "type": "toggle", "get_state": lambda: self._params.get_bool("LockDoors"), "set_state": lambda s: self._params.put_bool("LockDoors", s), "color": "#FFC40D"},
|
||||
{"title": tr_noop("Auto Unlock Doors"), "type": "toggle", "get_state": lambda: self._params.get_bool("UnlockDoors"), "set_state": lambda s: self._params.put_bool("UnlockDoors", s), "color": "#FFC40D"},
|
||||
{"title": tr_noop("Dashboard Speed Offset"), "type": "value", "get_value": lambda: f"{self._params.get_float('ClusterOffset'):.3f}x", "on_click": self._show_offset_selector, "color": "#FFC40D"},
|
||||
{"title": tr_noop("Stop-and-Go Hack"), "type": "toggle", "get_state": lambda: self._params.get_bool("SNGHack"), "set_state": lambda s: self._params.put_bool("SNGHack", s), "color": "#FFC40D"},
|
||||
{
|
||||
"title": tr_noop("Auto Lock Doors"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("LockDoors"),
|
||||
"set_state": lambda s: self._params.put_bool("LockDoors", s),
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Auto Unlock Doors"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("UnlockDoors"),
|
||||
"set_state": lambda s: self._params.put_bool("UnlockDoors", s),
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Lock Doors Timer"),
|
||||
"type": "value",
|
||||
"get_value": lambda: _lock_doors_timer_labels().get(self._params.get_int('LockDoorsTimer'), f"{self._params.get_int('LockDoorsTimer')}s"),
|
||||
"on_click": self._show_lock_timer_selector,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Dashboard Speed Offset"),
|
||||
"type": "value",
|
||||
"get_value": lambda: f"{self._params.get_float('ClusterOffset'):.3f}x",
|
||||
"on_click": self._show_offset_selector,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Stop-and-Go Hack"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SNGHack"),
|
||||
"set_state": lambda s: self._params.put_bool("SNGHack", s),
|
||||
"color": "#FFC40D",
|
||||
"key": "SNGHack",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("FrogsGoMoo Tweak"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("FrogsGoMoosTweak"),
|
||||
"set_state": lambda s: self._params.put_bool("FrogsGoMoosTweak", s),
|
||||
"color": "#FFC40D",
|
||||
"key": "FrogsGoMoosTweak",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _rebuild_grid(self):
|
||||
if not self.CATEGORIES:
|
||||
return
|
||||
if self._tile_grid is None:
|
||||
self._tile_grid = TileGrid(columns=None, padding=20)
|
||||
self._tile_grid.clear()
|
||||
|
||||
cs = starpilot_state.car_state
|
||||
for cat in self.CATEGORIES:
|
||||
key = cat.get("key")
|
||||
visible = True
|
||||
|
||||
if key == "SNGHack":
|
||||
visible = not cs.hasSNG
|
||||
elif key == "FrogsGoMoosTweak":
|
||||
visible = cs.hasOpenpilotLongitudinal
|
||||
|
||||
if not visible:
|
||||
continue
|
||||
|
||||
tile_type = cat.get("type", "hub")
|
||||
if tile_type == "toggle":
|
||||
tile = ToggleTile(title=tr(cat["title"]), get_state=cat["get_state"], set_state=cat["set_state"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
elif tile_type == "value":
|
||||
tile = ValueTile(title=tr(cat["title"]), get_value=cat["get_value"], on_click=cat["on_click"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
else:
|
||||
continue
|
||||
|
||||
self._tile_grid.add_tile(tile)
|
||||
|
||||
def _show_lock_timer_selector(self):
|
||||
def on_close(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int("LockDoorsTimer", int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(
|
||||
SliderDialog(tr("Lock Doors Timer"), 0, 300, 5, self._params.get_int("LockDoorsTimer"), on_close, labels=_lock_doors_timer_labels(), color="#FFC40D")
|
||||
)
|
||||
|
||||
def _show_offset_selector(self):
|
||||
def on_close(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_float("ClusterOffset", float(val))
|
||||
self._rebuild_grid()
|
||||
gui_app.set_modal_overlay(SliderDialog(tr("Dashboard Speed Offset"), 1.000, 1.050, 0.001, self._params.get_float("ClusterOffset"), on_close, unit="x", color="#FFC40D"))
|
||||
|
||||
gui_app.set_modal_overlay(
|
||||
SliderDialog(tr("Dashboard Speed Offset"), 1.000, 1.050, 0.001, self._params.get_float("ClusterOffset"), on_close, unit="x", color="#FFC40D")
|
||||
)
|
||||
|
||||
|
||||
class StarPilotVehicleInfoLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Radar Support"), "type": "value", "get_value": lambda: tr("Yes") if starpilot_state.car_state.hasRadar else tr("No"), "on_click": lambda: None, "color": "#FFC40D"},
|
||||
{"title": tr_noop("Longitudinal Support"), "type": "value", "get_value": lambda: tr("Yes") if starpilot_state.car_state.hasOpenpilotLongitudinal else tr("No"), "on_click": lambda: None, "color": "#FFC40D"},
|
||||
{"title": tr_noop("Blind Spot Support"), "type": "value", "get_value": lambda: tr("Yes") if starpilot_state.car_state.hasBSM else tr("No"), "on_click": lambda: None, "color": "#FFC40D"},
|
||||
{
|
||||
"title": tr_noop("Radar Support"),
|
||||
"type": "value",
|
||||
"get_value": lambda: tr("Yes") if starpilot_state.car_state.hasRadar else tr("No"),
|
||||
"on_click": lambda: None,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Longitudinal Support"),
|
||||
"type": "value",
|
||||
"get_value": lambda: tr("Yes") if starpilot_state.car_state.hasOpenpilotLongitudinal else tr("No"),
|
||||
"on_click": lambda: None,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Blind Spot Support"),
|
||||
"type": "value",
|
||||
"get_value": lambda: tr("Yes") if starpilot_state.car_state.hasBSM else tr("No"),
|
||||
"on_click": lambda: None,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Hardware Detected"),
|
||||
"type": "value",
|
||||
"get_value": lambda: (
|
||||
", ".join(
|
||||
filter(
|
||||
None,
|
||||
[
|
||||
tr("Pedal") if starpilot_state.car_state.canUsePedal else "",
|
||||
tr("SDSU") if starpilot_state.car_state.canUseSDSU else "",
|
||||
tr("ZSS") if starpilot_state.car_state.hasZSS else "",
|
||||
],
|
||||
)
|
||||
)
|
||||
or tr("None")
|
||||
),
|
||||
"on_click": lambda: None,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Pedal Support"),
|
||||
"type": "value",
|
||||
"get_value": lambda: tr("Yes") if starpilot_state.car_state.canUsePedal else tr("No"),
|
||||
"on_click": lambda: None,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("SDSU Support"),
|
||||
"type": "value",
|
||||
"get_value": lambda: tr("Yes") if starpilot_state.car_state.canUseSDSU else tr("No"),
|
||||
"on_click": lambda: None,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("SNG Support"),
|
||||
"type": "value",
|
||||
"get_value": lambda: tr("Yes") if starpilot_state.car_state.hasSNG else tr("No"),
|
||||
"on_click": lambda: None,
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from __future__ import annotations
|
||||
from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import SliderDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import TileGrid, ToggleTile, SliderDialog
|
||||
|
||||
|
||||
class StarPilotThemesLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
@@ -14,16 +16,47 @@ class StarPilotThemesLayout(StarPilotPanel):
|
||||
}
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Personalize openpilot"), "panel": "personalize", "icon": "toggle_icons/icon_frog.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Holiday Themes"), "type": "toggle", "get_state": lambda: self._params.get_bool("HolidayThemes"), "set_state": lambda s: self._params.put_bool("HolidayThemes", s), "icon": "toggle_icons/icon_calendar.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Rainbow Path"), "type": "toggle", "get_state": lambda: self._params.get_bool("RainbowPath"), "set_state": lambda s: self._params.put_bool("RainbowPath", s), "icon": "toggle_icons/icon_rainbow.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Random Events"), "type": "toggle", "get_state": lambda: self._params.get_bool("RandomEvents"), "set_state": lambda s: self._params.put_bool("RandomEvents", s), "icon": "toggle_icons/icon_random.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Random Themes"), "type": "toggle", "get_state": lambda: self._params.get_bool("RandomThemes"), "set_state": lambda s: self._params.put_bool("RandomThemes", s), "icon": "toggle_icons/icon_random_themes.png", "color": "#A200FF"},
|
||||
{
|
||||
"title": tr_noop("Holiday Themes"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("HolidayThemes"),
|
||||
"set_state": lambda s: self._params.put_bool("HolidayThemes", s),
|
||||
"icon": "toggle_icons/icon_calendar.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Rainbow Path"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("RainbowPath"),
|
||||
"set_state": lambda s: self._params.put_bool("RainbowPath", s),
|
||||
"icon": "toggle_icons/icon_rainbow.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Random Events"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("RandomEvents"),
|
||||
"set_state": lambda s: self._params.put_bool("RandomEvents", s),
|
||||
"icon": "toggle_icons/icon_random.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Random Themes"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("RandomThemes"),
|
||||
"set_state": lambda s: self._params.put_bool("RandomThemes", s),
|
||||
"icon": "toggle_icons/icon_random_themes.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
]
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back)
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
class StarPilotPersonalizeLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -41,12 +74,15 @@ class StarPilotPersonalizeLayout(StarPilotPanel):
|
||||
def _show_theme_selector(self, key):
|
||||
themes = ["Stock", "Frog", "Cyberpunk", "Minimal"]
|
||||
current = self._params.get(key, encoding='utf-8') or "Stock"
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put(key, val)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr(key), themes, current, on_close=on_select))
|
||||
|
||||
|
||||
class StarPilotVisualsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -65,80 +101,409 @@ class StarPilotVisualsLayout(StarPilotPanel):
|
||||
{"title": tr_noop("Quality of Life"), "panel": "qol", "icon": "toggle_icons/icon_quality_of_life.png", "color": "#A200FF"},
|
||||
]
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'): panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'): panel.set_back_callback(self._go_back)
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
class StarPilotAdvancedVisualsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Hide Speed"), "type": "toggle", "get_state": lambda: self._params.get_bool("HideSpeed"), "set_state": lambda s: self._params.put_bool("HideSpeed", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Hide Lead Marker"), "type": "toggle", "get_state": lambda: self._params.get_bool("HideLeadMarker"), "set_state": lambda s: self._params.put_bool("HideLeadMarker", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Hide Max Speed"), "type": "toggle", "get_state": lambda: self._params.get_bool("HideMaxSpeed"), "set_state": lambda s: self._params.put_bool("HideMaxSpeed", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Hide Alerts"), "type": "toggle", "get_state": lambda: self._params.get_bool("HideAlerts"), "set_state": lambda s: self._params.put_bool("HideAlerts", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Hide Speed Limit"), "type": "toggle", "get_state": lambda: self._params.get_bool("HideSpeedLimit"), "set_state": lambda s: self._params.put_bool("HideSpeedLimit", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Wheel Speed"), "type": "toggle", "get_state": lambda: self._params.get_bool("WheelSpeed"), "set_state": lambda s: self._params.put_bool("WheelSpeed", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{
|
||||
"title": tr_noop("Hide Speed"),
|
||||
"type": "toggle",
|
||||
"key": "HideSpeed",
|
||||
"get_state": lambda: self._params.get_bool("HideSpeed"),
|
||||
"set_state": lambda s: self._params.put_bool("HideSpeed", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Hide Lead Marker"),
|
||||
"type": "toggle",
|
||||
"key": "HideLeadMarker",
|
||||
"get_state": lambda: self._params.get_bool("HideLeadMarker"),
|
||||
"set_state": lambda s: self._params.put_bool("HideLeadMarker", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Hide Max Speed"),
|
||||
"type": "toggle",
|
||||
"key": "HideMaxSpeed",
|
||||
"get_state": lambda: self._params.get_bool("HideMaxSpeed"),
|
||||
"set_state": lambda s: self._params.put_bool("HideMaxSpeed", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Hide Alerts"),
|
||||
"type": "toggle",
|
||||
"key": "HideAlerts",
|
||||
"get_state": lambda: self._params.get_bool("HideAlerts"),
|
||||
"set_state": lambda s: self._params.put_bool("HideAlerts", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Hide Speed Limit"),
|
||||
"type": "toggle",
|
||||
"key": "HideSpeedLimit",
|
||||
"get_state": lambda: self._params.get_bool("HideSpeedLimit"),
|
||||
"set_state": lambda s: self._params.put_bool("HideSpeedLimit", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Wheel Speed"),
|
||||
"type": "toggle",
|
||||
"key": "WheelSpeed",
|
||||
"get_state": lambda: self._params.get_bool("WheelSpeed"),
|
||||
"set_state": lambda s: self._params.put_bool("WheelSpeed", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _rebuild_grid(self):
|
||||
if not self.CATEGORIES:
|
||||
return
|
||||
if self._tile_grid is None:
|
||||
self._tile_grid = TileGrid(columns=None, padding=20)
|
||||
self._tile_grid.clear()
|
||||
|
||||
for cat in self.CATEGORIES:
|
||||
key = cat.get("key")
|
||||
visible = True
|
||||
|
||||
if key == "HideLeadMarker":
|
||||
visible &= starpilot_state.car_state.hasOpenpilotLongitudinal
|
||||
|
||||
if not visible:
|
||||
continue
|
||||
|
||||
tile = ToggleTile(title=tr(cat["title"]), get_state=cat["get_state"], set_state=cat["set_state"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
self._tile_grid.add_tile(tile)
|
||||
|
||||
|
||||
class StarPilotVisualWidgetsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Acceleration Path"), "type": "toggle", "get_state": lambda: self._params.get_bool("AccelerationPath"), "set_state": lambda s: self._params.put_bool("AccelerationPath", s), "icon": "toggle_icons/icon_road.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Adjacent Lanes"), "type": "toggle", "get_state": lambda: self._params.get_bool("AdjacentPath"), "set_state": lambda s: self._params.put_bool("AdjacentPath", s), "icon": "toggle_icons/icon_road.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Blind Spot Path"), "type": "toggle", "get_state": lambda: self._params.get_bool("BlindSpotPath"), "set_state": lambda s: self._params.put_bool("BlindSpotPath", s), "icon": "toggle_icons/icon_road.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Compass"), "type": "toggle", "get_state": lambda: self._params.get_bool("Compass"), "set_state": lambda s: self._params.put_bool("Compass", s), "icon": "toggle_icons/icon_navigate.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Personality Button"), "type": "toggle", "get_state": lambda: self._params.get_bool("OnroadDistanceButton"), "set_state": lambda s: self._params.put_bool("OnroadDistanceButton", s), "icon": "toggle_icons/icon_personality.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Pedal Indicators"), "type": "toggle", "get_state": lambda: self._params.get_bool("PedalsOnUI"), "set_state": lambda s: self._params.put_bool("PedalsOnUI", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Rotating Wheel"), "type": "toggle", "get_state": lambda: self._params.get_bool("RotatingWheel"), "set_state": lambda s: self._params.put_bool("RotatingWheel", s), "icon": "toggle_icons/icon_steering.png", "color": "#A200FF"},
|
||||
{
|
||||
"title": tr_noop("Acceleration Path"),
|
||||
"type": "toggle",
|
||||
"key": "AccelerationPath",
|
||||
"get_state": lambda: self._params.get_bool("AccelerationPath"),
|
||||
"set_state": lambda s: self._params.put_bool("AccelerationPath", s),
|
||||
"icon": "toggle_icons/icon_road.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Adjacent Lanes"),
|
||||
"type": "toggle",
|
||||
"key": "AdjacentPath",
|
||||
"get_state": lambda: self._params.get_bool("AdjacentPath"),
|
||||
"set_state": lambda s: self._params.put_bool("AdjacentPath", s),
|
||||
"icon": "toggle_icons/icon_road.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Blind Spot Path"),
|
||||
"type": "toggle",
|
||||
"key": "BlindSpotPath",
|
||||
"get_state": lambda: self._params.get_bool("BlindSpotPath"),
|
||||
"set_state": lambda s: self._params.put_bool("BlindSpotPath", s),
|
||||
"icon": "toggle_icons/icon_road.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Compass"),
|
||||
"type": "toggle",
|
||||
"key": "Compass",
|
||||
"get_state": lambda: self._params.get_bool("Compass"),
|
||||
"set_state": lambda s: self._params.put_bool("Compass", s),
|
||||
"icon": "toggle_icons/icon_navigate.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Personality Button"),
|
||||
"type": "toggle",
|
||||
"key": "OnroadDistanceButton",
|
||||
"get_state": lambda: self._params.get_bool("OnroadDistanceButton"),
|
||||
"set_state": lambda s: self._params.put_bool("OnroadDistanceButton", s),
|
||||
"icon": "toggle_icons/icon_personality.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Pedal Indicators"),
|
||||
"type": "toggle",
|
||||
"key": "PedalsOnUI",
|
||||
"get_state": lambda: self._params.get_bool("PedalsOnUI"),
|
||||
"set_state": lambda s: self._params.put_bool("PedalsOnUI", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Dynamic Pedals"),
|
||||
"type": "toggle",
|
||||
"key": "DynamicPedalsOnUI",
|
||||
"get_state": lambda: self._params.get_bool("DynamicPedalsOnUI"),
|
||||
"set_state": lambda s: self._set_exclusive_pedal("DynamicPedalsOnUI", "StaticPedalsOnUI", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Static Pedals"),
|
||||
"type": "toggle",
|
||||
"key": "StaticPedalsOnUI",
|
||||
"get_state": lambda: self._params.get_bool("StaticPedalsOnUI"),
|
||||
"set_state": lambda s: self._set_exclusive_pedal("StaticPedalsOnUI", "DynamicPedalsOnUI", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Rotating Wheel"),
|
||||
"type": "toggle",
|
||||
"key": "RotatingWheel",
|
||||
"get_state": lambda: self._params.get_bool("RotatingWheel"),
|
||||
"set_state": lambda s: self._params.put_bool("RotatingWheel", s),
|
||||
"icon": "toggle_icons/icon_steering.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _set_exclusive_pedal(self, key, other_key, state):
|
||||
self._params.put_bool(key, state)
|
||||
if state:
|
||||
self._params.put_bool(other_key, False)
|
||||
self._rebuild_grid()
|
||||
|
||||
def _rebuild_grid(self):
|
||||
if not self.CATEGORIES:
|
||||
return
|
||||
if self._tile_grid is None:
|
||||
self._tile_grid = TileGrid(columns=None, padding=20)
|
||||
self._tile_grid.clear()
|
||||
|
||||
pedals_on_ui = self._params.get_bool("PedalsOnUI")
|
||||
|
||||
for cat in self.CATEGORIES:
|
||||
key = cat.get("key")
|
||||
visible = True
|
||||
|
||||
if key == "AccelerationPath":
|
||||
visible &= starpilot_state.car_state.hasOpenpilotLongitudinal
|
||||
elif key == "BlindSpotPath":
|
||||
visible &= starpilot_state.car_state.hasBSM
|
||||
elif key == "PedalsOnUI":
|
||||
visible &= starpilot_state.car_state.hasOpenpilotLongitudinal
|
||||
elif key == "DynamicPedalsOnUI":
|
||||
visible &= starpilot_state.car_state.hasOpenpilotLongitudinal and pedals_on_ui
|
||||
elif key == "StaticPedalsOnUI":
|
||||
visible &= starpilot_state.car_state.hasOpenpilotLongitudinal and pedals_on_ui
|
||||
|
||||
if not visible:
|
||||
continue
|
||||
|
||||
tile = ToggleTile(title=tr(cat["title"]), get_state=cat["get_state"], set_state=cat["set_state"], icon_path=cat.get("icon"), bg_color=cat.get("color"))
|
||||
self._tile_grid.add_tile(tile)
|
||||
|
||||
|
||||
class StarPilotModelUILayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Dynamic Path"), "type": "toggle", "get_state": lambda: self._params.get_bool("DynamicPathWidth"), "set_state": lambda s: self._params.put_bool("DynamicPathWidth", s), "icon": "toggle_icons/icon_road.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Lane Line Width"), "type": "value", "get_value": lambda: f"{self._params.get_int('LaneLinesWidth')}in", "on_click": lambda: self._show_int_selector("LaneLinesWidth", 0, 24, "in"), "icon": "toggle_icons/icon_road.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Path Edge Width"), "type": "value", "get_value": lambda: f"{self._params.get_int('PathEdgeWidth')}%", "on_click": lambda: self._show_int_selector("PathEdgeWidth", 0, 100, "%"), "icon": "toggle_icons/icon_road.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Path Width"), "type": "value", "get_value": lambda: f"{self._params.get_float('PathWidth'):.1f}ft", "on_click": lambda: self._show_float_selector("PathWidth", 0, 10, 0.1, "ft"), "icon": "toggle_icons/icon_road.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Road Edge Width"), "type": "value", "get_value": lambda: f"{self._params.get_int('RoadEdgesWidth')}in", "on_click": lambda: self._show_int_selector("RoadEdgesWidth", 0, 24, "in"), "icon": "toggle_icons/icon_road.png", "color": "#A200FF"},
|
||||
{
|
||||
"title": tr_noop("Dynamic Path"),
|
||||
"type": "toggle",
|
||||
"key": "DynamicPathWidth",
|
||||
"get_state": lambda: self._params.get_bool("DynamicPathWidth"),
|
||||
"set_state": lambda s: self._params.put_bool("DynamicPathWidth", s),
|
||||
"icon": "toggle_icons/icon_road.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Lane Line Width"),
|
||||
"type": "value",
|
||||
"key": "LaneLinesWidth",
|
||||
"get_value": lambda: self._get_lane_lines_display(),
|
||||
"on_click": lambda: self._show_int_selector("LaneLinesWidth", 0, 24, self._get_lane_lines_unit()),
|
||||
"icon": "toggle_icons/icon_road.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Path Edge Width"),
|
||||
"type": "value",
|
||||
"key": "PathEdgeWidth",
|
||||
"get_value": lambda: f"{self._params.get_int('PathEdgeWidth')}%",
|
||||
"on_click": lambda: self._show_int_selector("PathEdgeWidth", 0, 100, "%"),
|
||||
"icon": "toggle_icons/icon_road.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Path Width"),
|
||||
"type": "value",
|
||||
"key": "PathWidth",
|
||||
"get_value": lambda: self._get_path_width_display(),
|
||||
"on_click": lambda: self._show_path_width_selector(),
|
||||
"icon": "toggle_icons/icon_road.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Road Edge Width"),
|
||||
"type": "value",
|
||||
"key": "RoadEdgesWidth",
|
||||
"get_value": lambda: self._get_road_edges_display(),
|
||||
"on_click": lambda: self._show_int_selector("RoadEdgesWidth", 0, 24, self._get_road_edges_unit()),
|
||||
"icon": "toggle_icons/icon_road.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _get_lane_lines_unit(self):
|
||||
return "cm" if self._params.get_bool("IsMetric") else "in"
|
||||
|
||||
def _get_lane_lines_display(self):
|
||||
val = self._params.get_int('LaneLinesWidth')
|
||||
if self._params.get_bool("IsMetric"):
|
||||
return f"{int(val * 2.54)}cm"
|
||||
return f"{val}in"
|
||||
|
||||
def _get_path_width_unit(self):
|
||||
return "m" if self._params.get_bool("IsMetric") else "ft"
|
||||
|
||||
def _get_path_width_display(self):
|
||||
val = self._params.get_float('PathWidth')
|
||||
if self._params.get_bool("IsMetric"):
|
||||
return f"{val / 3.28084:.1f}m"
|
||||
return f"{val:.1f}ft"
|
||||
|
||||
def _get_road_edges_unit(self):
|
||||
return "cm" if self._params.get_bool("IsMetric") else "in"
|
||||
|
||||
def _get_road_edges_display(self):
|
||||
val = self._params.get_int('RoadEdgesWidth')
|
||||
if self._params.get_bool("IsMetric"):
|
||||
return f"{int(val * 2.54)}cm"
|
||||
return f"{val}in"
|
||||
|
||||
def _show_path_width_selector(self):
|
||||
if self._params.get_bool("IsMetric"):
|
||||
self._show_float_selector("PathWidth", 0, 10, 0.1, "m", convert=lambda v: v / 3.28084, unconvert=lambda v: v * 3.28084)
|
||||
else:
|
||||
self._show_float_selector("PathWidth", 0, 10, 0.1, "ft")
|
||||
|
||||
def _show_int_selector(self, key, min_v, max_v, unit=""):
|
||||
def on_close(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, int(val))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, 1, self._params.get_int(key), on_close, unit=unit, color="#A200FF"))
|
||||
|
||||
def _show_float_selector(self, key, min_v, max_v, step, unit=""):
|
||||
def _show_float_selector(self, key, min_v, max_v, step, unit="", convert=None, unconvert=None):
|
||||
current = self._params.get_float(key)
|
||||
if convert:
|
||||
current = convert(current)
|
||||
|
||||
def on_close(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_float(key, float(val))
|
||||
v = float(val)
|
||||
if unconvert:
|
||||
v = unconvert(v)
|
||||
self._params.put_float(key, v)
|
||||
self._rebuild_grid()
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, step, self._params.get_float(key), on_close, unit=unit, color="#A200FF"))
|
||||
|
||||
gui_app.set_modal_overlay(SliderDialog(tr(key), min_v, max_v, step, current, on_close, unit=unit, color="#A200FF"))
|
||||
|
||||
|
||||
class StarPilotNavigationVisualsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Road Name"), "type": "toggle", "get_state": lambda: self._params.get_bool("RoadNameUI"), "set_state": lambda s: self._params.put_bool("RoadNameUI", s), "icon": "toggle_icons/icon_navigate.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Speed Limits"), "type": "toggle", "get_state": lambda: self._params.get_bool("ShowSpeedLimits"), "set_state": lambda s: self._params.put_bool("ShowSpeedLimits", s), "icon": "toggle_icons/icon_speed_limit.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Mapbox Limits"), "type": "toggle", "get_state": lambda: self._params.get_bool("SLCMapboxFiller"), "set_state": lambda s: self._params.put_bool("SLCMapboxFiller", s), "icon": "toggle_icons/icon_speed_limit.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Vienna Signs"), "type": "toggle", "get_state": lambda: self._params.get_bool("UseVienna"), "set_state": lambda s: self._params.put_bool("UseVienna", s), "icon": "toggle_icons/icon_speed_limit.png", "color": "#A200FF"},
|
||||
{
|
||||
"title": tr_noop("Road Name"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("RoadNameUI"),
|
||||
"set_state": lambda s: self._params.put_bool("RoadNameUI", s),
|
||||
"icon": "toggle_icons/icon_navigate.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Speed Limits"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("ShowSpeedLimits"),
|
||||
"set_state": lambda s: self._params.put_bool("ShowSpeedLimits", s),
|
||||
"icon": "toggle_icons/icon_speed_limit.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Mapbox Limits"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("SLCMapboxFiller"),
|
||||
"set_state": lambda s: self._params.put_bool("SLCMapboxFiller", s),
|
||||
"icon": "toggle_icons/icon_speed_limit.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Vienna Signs"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("UseVienna"),
|
||||
"set_state": lambda s: self._params.put_bool("UseVienna", s),
|
||||
"icon": "toggle_icons/icon_speed_limit.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
|
||||
class StarPilotVisualQOLLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.CAMERA_VIEWS = ["Auto", "Driver", "Standard", "Wide"]
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Camera View"), "type": "toggle", "get_state": lambda: self._params.get_bool("CameraView"), "set_state": lambda s: self._params.put_bool("CameraView", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Driver Camera"), "type": "toggle", "get_state": lambda: self._params.get_bool("DriverCamera"), "set_state": lambda s: self._params.put_bool("DriverCamera", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{"title": tr_noop("Stopped Timer"), "type": "toggle", "get_state": lambda: self._params.get_bool("StoppedTimer"), "set_state": lambda s: self._params.put_bool("StoppedTimer", s), "icon": "toggle_icons/icon_display.png", "color": "#A200FF"},
|
||||
{
|
||||
"title": tr_noop("Camera View"),
|
||||
"type": "value",
|
||||
"key": "CameraView",
|
||||
"get_value": lambda: tr(self.CAMERA_VIEWS[self._params.get_int('CameraView')]),
|
||||
"on_click": lambda: self._show_camera_view_selector(),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Driver Camera"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("DriverCamera"),
|
||||
"set_state": lambda s: self._params.put_bool("DriverCamera", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Stopped Timer"),
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("StoppedTimer"),
|
||||
"set_state": lambda s: self._params.put_bool("StoppedTimer", s),
|
||||
"icon": "toggle_icons/icon_display.png",
|
||||
"color": "#A200FF",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _show_camera_view_selector(self):
|
||||
current = self._params.get_int("CameraView")
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
idx = self.CAMERA_VIEWS.index(val)
|
||||
self._params.put_int("CameraView", idx)
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr("Camera View"), self.CAMERA_VIEWS, self.CAMERA_VIEWS[current], on_close=on_select))
|
||||
|
||||
@@ -6,6 +6,10 @@ from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
ACTION_NAMES = ["No Action", "Change Personality", "Force Coast", "Pause Steering", "Pause Accel/Brake", "Toggle Experimental", "Toggle Traffic"]
|
||||
ACTION_IDS = {name: i for i, name in enumerate(ACTION_NAMES)}
|
||||
|
||||
|
||||
class StarPilotWheelLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -15,8 +19,89 @@ class StarPilotWheelLayout(StarPilotPanel):
|
||||
"type": "toggle",
|
||||
"get_state": lambda: self._params.get_bool("RemapCancelToDistance"),
|
||||
"set_state": lambda s: self._params.put_bool("RemapCancelToDistance", s),
|
||||
"icon": "toggle_icons/icon_steering.png",
|
||||
"color": "#FFC40D"
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Distance Button"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("DistanceButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("DistanceButtonControl"),
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Distance (Long Press)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("LongDistanceButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("LongDistanceButtonControl"),
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Distance (Very Long)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("VeryLongDistanceButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("VeryLongDistanceButtonControl"),
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("LKAS Button"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("LKASButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("LKASButtonControl"),
|
||||
"key": "LKASButtonControl",
|
||||
"color": "#FFC40D",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
def _get_action_name(self, key):
|
||||
idx = self._params.get_int(key)
|
||||
if 0 <= idx < len(ACTION_NAMES):
|
||||
return ACTION_NAMES[idx]
|
||||
return ACTION_NAMES[0]
|
||||
|
||||
def _get_available_actions(self):
|
||||
actions = list(ACTION_NAMES[:1]) # No Action
|
||||
cs = starpilot_state.car_state
|
||||
if cs.hasOpenpilotLongitudinal:
|
||||
actions.extend(ACTION_NAMES[1:])
|
||||
return actions
|
||||
|
||||
def _show_action_picker(self, key):
|
||||
actions = self._get_available_actions()
|
||||
current = self._get_action_name(key)
|
||||
if current not in actions:
|
||||
current = actions[0]
|
||||
|
||||
def on_select(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params.put_int(key, ACTION_IDS.get(val, 0))
|
||||
self._rebuild_grid()
|
||||
|
||||
gui_app.set_modal_overlay(SelectionDialog(tr(key), actions, current, on_close=on_select))
|
||||
|
||||
def _rebuild_grid(self):
|
||||
if not self.CATEGORIES:
|
||||
return
|
||||
if self._tile_grid is None:
|
||||
self._tile_grid = __import__('openpilot.selfdrive.ui.layouts.settings.starpilot.metro', fromlist=['TileGrid']).TileGrid(columns=None, padding=20)
|
||||
self._tile_grid.clear()
|
||||
cs = starpilot_state.car_state
|
||||
for cat in self.CATEGORIES:
|
||||
key = cat.get("key")
|
||||
visible = True
|
||||
if key == "LKASButtonControl":
|
||||
visible &= not cs.isSubaru
|
||||
if not visible:
|
||||
continue
|
||||
tile_type = cat.get("type", "hub")
|
||||
if tile_type == "toggle":
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import ToggleTile
|
||||
|
||||
tile = ToggleTile(title=tr(cat["title"]), get_state=cat["get_state"], set_state=cat["set_state"], bg_color=cat.get("color"))
|
||||
elif tile_type == "value":
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.metro import ValueTile
|
||||
|
||||
tile = ValueTile(title=tr(cat["title"]), get_value=cat["get_value"], on_click=cat["on_click"], bg_color=cat.get("color"))
|
||||
else:
|
||||
continue
|
||||
self._tile_grid.add_tile(tile)
|
||||
|
||||
@@ -225,6 +225,7 @@ class GuiApplication:
|
||||
self._modal_overlay = ModalOverlay()
|
||||
self._modal_overlay_shown = False
|
||||
self._modal_overlay_tick: Callable[[], None] | None = None
|
||||
self._nav_stack: list = []
|
||||
|
||||
self._mouse = MouseState(self._scale)
|
||||
self._mouse_events: list[MouseEvent] = []
|
||||
@@ -369,6 +370,41 @@ class GuiApplication:
|
||||
def set_modal_overlay_tick(self, tick_function: Callable | None):
|
||||
self._modal_overlay_tick = tick_function
|
||||
|
||||
def push_widget(self, widget):
|
||||
if widget in self._nav_stack:
|
||||
return
|
||||
if self._nav_stack:
|
||||
prev = self._nav_stack[-1]
|
||||
if hasattr(prev, 'set_enabled'):
|
||||
prev.set_enabled(False)
|
||||
self._nav_stack.append(widget)
|
||||
if hasattr(widget, 'show_event'):
|
||||
widget.show_event()
|
||||
if hasattr(widget, 'set_enabled'):
|
||||
widget.set_enabled(True)
|
||||
|
||||
def pop_widget(self, idx: int | None = None):
|
||||
if len(self._nav_stack) < 2:
|
||||
return
|
||||
idx_to_pop = len(self._nav_stack) - 1 if idx is None else idx
|
||||
if idx_to_pop <= 0 or idx_to_pop >= len(self._nav_stack):
|
||||
return
|
||||
if idx_to_pop == len(self._nav_stack) - 1:
|
||||
prev = self._nav_stack[idx_to_pop - 1]
|
||||
if hasattr(prev, 'set_enabled'):
|
||||
prev.set_enabled(True)
|
||||
widget = self._nav_stack.pop(idx_to_pop)
|
||||
if hasattr(widget, 'hide_event'):
|
||||
widget.hide_event()
|
||||
|
||||
def _render_nav_stack(self) -> bool:
|
||||
if not self._nav_stack:
|
||||
return False
|
||||
widget = self._nav_stack[-1]
|
||||
if hasattr(widget, 'render'):
|
||||
widget.render(rl.Rectangle(0, 0, self.width, self.height))
|
||||
return True
|
||||
|
||||
def set_should_render(self, should_render: bool):
|
||||
self._should_render = should_render
|
||||
|
||||
@@ -523,7 +559,9 @@ class GuiApplication:
|
||||
rl.clear_background(rl.BLACK)
|
||||
|
||||
# Handle modal overlay rendering and input processing
|
||||
if self._handle_modal_overlay():
|
||||
if self._render_nav_stack():
|
||||
yield False
|
||||
elif self._handle_modal_overlay():
|
||||
# Allow a Widget to still run a function while overlay is shown
|
||||
if self._modal_overlay_tick is not None:
|
||||
self._modal_overlay_tick()
|
||||
|
||||
@@ -34,6 +34,7 @@ class Widget(abc.ABC):
|
||||
self._click_callback: Callable[[], None] | None = None
|
||||
self._multi_touch = False
|
||||
self.__was_awake = True
|
||||
self._children: list = []
|
||||
|
||||
@property
|
||||
def rect(self) -> rl.Rectangle:
|
||||
@@ -180,9 +181,25 @@ class Widget(abc.ABC):
|
||||
|
||||
def show_event(self):
|
||||
"""Optionally handle show event. Parent must manually call this"""
|
||||
for child in self._children:
|
||||
child.show_event()
|
||||
|
||||
def hide_event(self):
|
||||
"""Optionally handle hide event. Parent must manually call this"""
|
||||
for child in self._children:
|
||||
child.hide_event()
|
||||
|
||||
def _child(self, widget):
|
||||
"""Register a child widget for lifecycle propagation."""
|
||||
assert widget not in self._children, f"{type(widget).__name__} already a child of {type(self).__name__}"
|
||||
self._children.append(widget)
|
||||
return widget
|
||||
|
||||
def dismiss(self, callback: Callable[[], None] | None = None):
|
||||
"""Dismiss this widget from the nav stack."""
|
||||
gui_app.pop_widget()
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
|
||||
SWIPE_AWAY_THRESHOLD = 80 # px to dismiss after releasing
|
||||
|
||||
@@ -1,118 +1,43 @@
|
||||
import pyray as rl
|
||||
from collections.abc import Callable
|
||||
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.widgets import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.button import Button, ButtonStyle
|
||||
from openpilot.system.ui.widgets.label import Label, FontWeight
|
||||
from openpilot.system.ui.widgets.keyboard import Keyboard, KeyboardLayout
|
||||
from openpilot.system.ui.widgets.keyboard import Keyboard
|
||||
|
||||
MARGIN = 50
|
||||
BUTTON_HEIGHT = 160
|
||||
OUTER_MARGIN_X = 200
|
||||
OUTER_MARGIN_Y = 150
|
||||
BACKGROUND_COLOR = rl.Color(27, 27, 27, 255)
|
||||
|
||||
class InputDialog(Widget):
|
||||
def __init__(self, title: str, default_text: str = "", hint_text: str = "", on_close: Callable[[DialogResult, str], None] | None = None):
|
||||
super().__init__()
|
||||
self._title = title
|
||||
self._text = default_text
|
||||
self._hint = hint_text
|
||||
self._default_text = default_text
|
||||
self._on_close = on_close
|
||||
|
||||
self._dialog_result = DialogResult.NO_ACTION
|
||||
|
||||
self._title_label = Label(title, 70, FontWeight.BOLD, text_color=rl.Color(201, 201, 201, 255))
|
||||
self._cancel_button = Button("Cancel", self._cancel_button_callback)
|
||||
self._confirm_button = Button("Confirm", self._confirm_button_callback, button_style=ButtonStyle.PRIMARY)
|
||||
|
||||
self._keyboard = Keyboard(self._on_key_pressed, self._on_keyboard_done, layout=KeyboardLayout.QWERTY)
|
||||
|
||||
self._font = gui_app.font(FontWeight.MEDIUM)
|
||||
|
||||
def _on_key_pressed(self, key: str):
|
||||
if key == "\b":
|
||||
self._text = self._text[:-1]
|
||||
else:
|
||||
self._text += key
|
||||
self._keyboard = Keyboard(callback=self._on_keyboard_result)
|
||||
self._keyboard.set_title(title)
|
||||
self._keyboard.set_text(default_text)
|
||||
|
||||
def _on_keyboard_done(self):
|
||||
self._confirm_button_callback()
|
||||
|
||||
def _cancel_button_callback(self):
|
||||
self._dialog_result = DialogResult.CANCEL
|
||||
def _on_keyboard_result(self, result: DialogResult):
|
||||
if self._dialog_result != DialogResult.NO_ACTION:
|
||||
return
|
||||
self._dialog_result = result
|
||||
if self._on_close:
|
||||
self._on_close(self._dialog_result, self._text)
|
||||
|
||||
def _confirm_button_callback(self):
|
||||
self._dialog_result = DialogResult.CONFIRM
|
||||
if self._on_close:
|
||||
self._on_close(self._dialog_result, self._text)
|
||||
self._on_close(result, self._keyboard.text)
|
||||
|
||||
@property
|
||||
def result(self) -> DialogResult:
|
||||
return self._dialog_result
|
||||
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
return self._text
|
||||
return self._keyboard.text
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
self._dialog_result = DialogResult.NO_ACTION
|
||||
self._keyboard.show_event()
|
||||
self._keyboard.clear()
|
||||
if self._default_text:
|
||||
self._keyboard.set_text(self._default_text)
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
# Dim background
|
||||
rl.draw_rectangle(0, 0, int(rect.width), int(rect.height), rl.Color(0, 0, 0, 200))
|
||||
|
||||
# Dialog Box
|
||||
dialog_rect = rl.Rectangle(
|
||||
rect.x + OUTER_MARGIN_X,
|
||||
rect.y + OUTER_MARGIN_Y,
|
||||
rect.width - 2 * OUTER_MARGIN_X,
|
||||
rect.height - 2 * OUTER_MARGIN_Y,
|
||||
)
|
||||
rl.draw_rectangle_rounded(dialog_rect, 0.05, 10, BACKGROUND_COLOR)
|
||||
|
||||
# Title
|
||||
title_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y + MARGIN, dialog_rect.width - 2 * MARGIN, 100)
|
||||
self._title_label.render(title_rect)
|
||||
|
||||
# Text Input Field
|
||||
input_rect = rl.Rectangle(dialog_rect.x + MARGIN, title_rect.y + title_rect.height + 40, dialog_rect.width - 2 * MARGIN, 120)
|
||||
rl.draw_rectangle_rounded(input_rect, 0.1, 10, rl.Color(40, 40, 40, 255))
|
||||
|
||||
display_text = self._text
|
||||
text_color = rl.WHITE
|
||||
if not display_text:
|
||||
display_text = self._hint
|
||||
text_color = rl.Color(128, 128, 128, 255)
|
||||
|
||||
text_size = rl.measure_text_ex(self._font, display_text, 50, 0)
|
||||
text_pos = rl.Vector2(input_rect.x + 40, input_rect.y + (input_rect.height - text_size.y) / 2)
|
||||
rl.draw_text_ex(self._font, display_text, text_pos, 50, 0, text_color)
|
||||
|
||||
# Blinking cursor
|
||||
if (rl.get_time() % 1.0) < 0.5:
|
||||
cursor_x = text_pos.x + (text_size.x if self._text else 0) + 5
|
||||
rl.draw_rectangle(int(cursor_x), int(text_pos.y), 4, 50, rl.WHITE)
|
||||
|
||||
# Keyboard
|
||||
keyboard_rect = rl.Rectangle(
|
||||
dialog_rect.x + MARGIN,
|
||||
input_rect.y + input_rect.height + 40,
|
||||
dialog_rect.width - 2 * MARGIN,
|
||||
400
|
||||
)
|
||||
self._keyboard.render(keyboard_rect)
|
||||
|
||||
# Buttons
|
||||
btn_y = dialog_rect.y + dialog_rect.height - BUTTON_HEIGHT - MARGIN
|
||||
btn_width = (dialog_rect.width - 3 * MARGIN) / 2
|
||||
|
||||
cancel_rect = rl.Rectangle(dialog_rect.x + MARGIN, btn_y, btn_width, BUTTON_HEIGHT)
|
||||
confirm_rect = rl.Rectangle(dialog_rect.x + 2 * MARGIN + btn_width, btn_y, btn_width, BUTTON_HEIGHT)
|
||||
|
||||
self._cancel_button.render(cancel_rect)
|
||||
self._confirm_button.render(confirm_rect)
|
||||
def _render(self, rect):
|
||||
self._keyboard.render(rect)
|
||||
return self._dialog_result
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from functools import partial
|
||||
import time
|
||||
from typing import Literal
|
||||
from collections.abc import Callable
|
||||
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.button import ButtonStyle, Button
|
||||
from openpilot.system.ui.widgets.inputbox import InputBox
|
||||
from openpilot.system.ui.widgets.label import Label
|
||||
@@ -58,7 +59,14 @@ KEYBOARD_LAYOUTS = {
|
||||
|
||||
|
||||
class Keyboard(Widget):
|
||||
def __init__(self, max_text_size: int = 255, min_text_size: int = 0, password_mode: bool = False, show_password_toggle: bool = False):
|
||||
def __init__(
|
||||
self,
|
||||
max_text_size: int = 255,
|
||||
min_text_size: int = 0,
|
||||
password_mode: bool = False,
|
||||
show_password_toggle: bool = False,
|
||||
callback: Callable[[DialogResult], None] | None = None,
|
||||
):
|
||||
super().__init__()
|
||||
self._layout_name: Literal["lowercase", "uppercase", "numbers", "specials"] = "lowercase"
|
||||
self._caps_lock = False
|
||||
@@ -71,6 +79,7 @@ class Keyboard(Widget):
|
||||
self._input_box = InputBox(max_text_size)
|
||||
self._password_mode = password_mode
|
||||
self._show_password_toggle = show_password_toggle
|
||||
self._callback = callback
|
||||
|
||||
# Backspace key repeat tracking
|
||||
self._backspace_pressed: bool = False
|
||||
@@ -78,6 +87,8 @@ class Keyboard(Widget):
|
||||
self._backspace_last_repeat: float = 0.0
|
||||
|
||||
self._render_return_status = -1
|
||||
self._first_render = False
|
||||
self._skip_input = False
|
||||
self._cancel_button = Button(lambda: tr("Cancel"), self._cancel_button_callback)
|
||||
|
||||
self._eye_button = Button("", self._eye_button_callback, button_style=ButtonStyle.TRANSPARENT)
|
||||
@@ -98,12 +109,18 @@ class Keyboard(Widget):
|
||||
for _, key in enumerate(keys):
|
||||
if key in self._key_icons:
|
||||
texture = self._key_icons[key]
|
||||
self._all_keys[key] = Button("", partial(self._key_callback, key), icon=texture,
|
||||
button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.KEYBOARD, multi_touch=True)
|
||||
self._all_keys[key] = Button(
|
||||
"",
|
||||
partial(self._key_callback, key),
|
||||
icon=texture,
|
||||
button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.KEYBOARD,
|
||||
multi_touch=True,
|
||||
)
|
||||
else:
|
||||
self._all_keys[key] = Button(key, partial(self._key_callback, key), button_style=ButtonStyle.KEYBOARD, font_size=85, multi_touch=True)
|
||||
self._all_keys[CAPS_LOCK_KEY] = Button("", partial(self._key_callback, CAPS_LOCK_KEY), icon=self._key_icons[CAPS_LOCK_KEY],
|
||||
button_style=ButtonStyle.KEYBOARD, multi_touch=True)
|
||||
self._all_keys[CAPS_LOCK_KEY] = Button(
|
||||
"", partial(self._key_callback, CAPS_LOCK_KEY), icon=self._key_icons[CAPS_LOCK_KEY], button_style=ButtonStyle.KEYBOARD, multi_touch=True
|
||||
)
|
||||
|
||||
def set_text(self, text: str):
|
||||
self._input_box.text = text
|
||||
@@ -122,20 +139,42 @@ class Keyboard(Widget):
|
||||
self._title.set_text(title)
|
||||
self._sub_title.set_text(sub_title)
|
||||
|
||||
def _eye_button_callback(self):
|
||||
self._password_mode = not self._password_mode
|
||||
def set_callback(self, callback: Callable[[DialogResult], None] | None):
|
||||
self._callback = callback
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
self._skip_input = True
|
||||
|
||||
def _process_mouse_events(self):
|
||||
if not self._skip_input:
|
||||
super()._process_mouse_events()
|
||||
|
||||
def _cancel_button_callback(self):
|
||||
self.clear()
|
||||
self._render_return_status = 0
|
||||
if self in gui_app._nav_stack:
|
||||
gui_app.pop_widget()
|
||||
else:
|
||||
self._render_return_status = 0
|
||||
if self._callback:
|
||||
self._callback(DialogResult.CANCEL)
|
||||
|
||||
def _eye_button_callback(self):
|
||||
self._password_mode = not self._password_mode
|
||||
|
||||
def _key_callback(self, k):
|
||||
if k == ENTER_KEY:
|
||||
self._render_return_status = 1
|
||||
if self in gui_app._nav_stack:
|
||||
gui_app.pop_widget()
|
||||
else:
|
||||
self._render_return_status = 1
|
||||
if self._callback:
|
||||
self._callback(DialogResult.CONFIRM)
|
||||
else:
|
||||
self.handle_key_press(k)
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
self._skip_input = False
|
||||
rect = rl.Rectangle(rect.x + CONTENT_MARGIN, rect.y + CONTENT_MARGIN, rect.width - 2 * CONTENT_MARGIN, rect.height - 2 * CONTENT_MARGIN)
|
||||
self._title.render(rl.Rectangle(rect.x, rect.y, rect.width, 95))
|
||||
self._sub_title.render(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60))
|
||||
|
||||
Reference in New Issue
Block a user