mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-06-28 01:52:06 +08:00
raylib: wrap text for multilang (#36410)
* fix multilang dialog height * split to file * stash * Revert "stash" This reverts commit deb4239fe69f0260420fad03f2350e622e31542f. * add updater * add files * stuff * try rev * stash * works! * works! * this should be the flow? * cursor wrapping -- it missed entire sections, changed formatting, and didn't use trn properly!!!!!!!!!!!!!!!!! * update translations * learned my lesson * this should be the one thing it's good at * update trans * onroad wrap * spanish * rename * clean up * load all * Revert "load all" This reverts commit 6f2a45861c914ffb9d40a5edd15751afd798d614. * jp translations * try jp * Revert "try jp" This reverts commit d0524b10110104baafcdc1ec385c3d57bc5ef901. * remove languages we can't add rn * tr * pt and fr * ai cannot be trusted * ai cannot be trusted * missing trans * add fonts * Revert "remove languages we can't add rn" This reverts commit 73dc75fae2b9e347d867b6636dab6e2b5fe59da7. * painfully slow to startup * only load what we need * Reapply "remove languages we can't add rn" This reverts commit 52cb48f3b838520a421f9b90e5ea4409c27d4bd0. * stash! * rm * Revert "stash!" This reverts commit 31d7c361079a8e57039a0117c81d59bf84f191c7. * revert this * revert that * make this dynamic! * device * revert * firehose * stuff * revert application * back * full revert * clean up * network * more system * fix dat * fixy
This commit is contained in:
@@ -9,6 +9,7 @@ from openpilot.selfdrive.ui.widgets.prime import PrimeWidget
|
||||
from openpilot.selfdrive.ui.widgets.setup import SetupWidget
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
|
||||
from openpilot.system.ui.lib.multilang import tr, trn
|
||||
from openpilot.system.ui.widgets.label import gui_label
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
@@ -151,7 +152,7 @@ class HomeLayout(Widget):
|
||||
highlight_color = rl.Color(75, 95, 255, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(54, 77, 239, 255)
|
||||
rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color)
|
||||
|
||||
text = "UPDATE"
|
||||
text = tr("UPDATE")
|
||||
text_size = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE)
|
||||
text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_size.x) // 2
|
||||
text_y = self.update_notif_rect.y + (self.update_notif_rect.height - text_size.y) // 2
|
||||
@@ -165,7 +166,7 @@ class HomeLayout(Widget):
|
||||
highlight_color = rl.Color(255, 70, 70, 255) if self.current_state == HomeLayoutState.ALERTS else rl.Color(226, 44, 44, 255)
|
||||
rl.draw_rectangle_rounded(self.alert_notif_rect, 0.3, 10, highlight_color)
|
||||
|
||||
alert_text = f"{self.alert_count} ALERT{'S' if self.alert_count > 1 else ''}"
|
||||
alert_text = trn("{} ALERT", "{} ALERTS", self.alert_count).format(self.alert_count)
|
||||
text_size = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE)
|
||||
text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_size.x) // 2
|
||||
text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - text_size.y) // 2
|
||||
|
||||
@@ -6,6 +6,7 @@ from enum import IntEnum
|
||||
import pyray as rl
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.system.ui.lib.application import FontWeight, gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.button import Button, ButtonStyle
|
||||
from openpilot.system.ui.widgets.label import Label
|
||||
@@ -107,12 +108,12 @@ class TermsPage(Widget):
|
||||
self._on_accept = on_accept
|
||||
self._on_decline = on_decline
|
||||
|
||||
self._title = Label("Welcome to openpilot", font_size=90, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
|
||||
self._desc = Label("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing.",
|
||||
self._title = Label(tr("Welcome to openpilot"), font_size=90, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
|
||||
self._desc = Label(tr("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing."),
|
||||
font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
|
||||
|
||||
self._decline_btn = Button("Decline", click_callback=on_decline)
|
||||
self._accept_btn = Button("Agree", button_style=ButtonStyle.PRIMARY, click_callback=on_accept)
|
||||
self._decline_btn = Button(tr("Decline"), click_callback=on_decline)
|
||||
self._accept_btn = Button(tr("Agree"), button_style=ButtonStyle.PRIMARY, click_callback=on_accept)
|
||||
|
||||
def _render(self, _):
|
||||
welcome_x = self._rect.x + 165
|
||||
@@ -141,10 +142,10 @@ class TermsPage(Widget):
|
||||
class DeclinePage(Widget):
|
||||
def __init__(self, back_callback=None):
|
||||
super().__init__()
|
||||
self._text = Label("You must accept the Terms and Conditions in order to use openpilot.",
|
||||
self._text = Label(tr("You must accept the Terms and Conditions in order to use openpilot."),
|
||||
font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
|
||||
self._back_btn = Button("Back", click_callback=back_callback)
|
||||
self._uninstall_btn = Button("Decline, uninstall openpilot", button_style=ButtonStyle.DANGER,
|
||||
self._back_btn = Button(tr("Back"), click_callback=back_callback)
|
||||
self._uninstall_btn = Button(tr("Decline, uninstall openpilot"), button_style=ButtonStyle.DANGER,
|
||||
click_callback=self._on_uninstall_clicked)
|
||||
|
||||
def _on_uninstall_clicked(self):
|
||||
|
||||
@@ -6,19 +6,20 @@ from openpilot.system.ui.widgets.list_view import toggle_item
|
||||
from openpilot.system.ui.widgets.scroller import Scroller
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
|
||||
# Description constants
|
||||
DESCRIPTIONS = {
|
||||
'enable_adb': (
|
||||
'enable_adb': tr(
|
||||
"ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " +
|
||||
"See https://docs.comma.ai/how-to/connect-to-comma for more info."
|
||||
),
|
||||
'ssh_key': (
|
||||
'ssh_key': tr(
|
||||
"Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " +
|
||||
"other than your own. A comma employee will NEVER ask you to add their GitHub username."
|
||||
),
|
||||
'alpha_longitudinal': (
|
||||
'alpha_longitudinal': tr(
|
||||
"<b>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</b><br><br>" +
|
||||
"On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " +
|
||||
"Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha."
|
||||
@@ -34,7 +35,7 @@ class DeveloperLayout(Widget):
|
||||
|
||||
# Build items and keep references for callbacks/state updates
|
||||
self._adb_toggle = toggle_item(
|
||||
"Enable ADB",
|
||||
tr("Enable ADB"),
|
||||
description=DESCRIPTIONS["enable_adb"],
|
||||
initial_state=self._params.get_bool("AdbEnabled"),
|
||||
callback=self._on_enable_adb,
|
||||
@@ -43,7 +44,7 @@ class DeveloperLayout(Widget):
|
||||
|
||||
# SSH enable toggle + SSH key management
|
||||
self._ssh_toggle = toggle_item(
|
||||
"Enable SSH",
|
||||
tr("Enable SSH"),
|
||||
description="",
|
||||
initial_state=self._params.get_bool("SshEnabled"),
|
||||
callback=self._on_enable_ssh,
|
||||
@@ -51,7 +52,7 @@ class DeveloperLayout(Widget):
|
||||
self._ssh_keys = ssh_key_item("SSH Keys", description=DESCRIPTIONS["ssh_key"])
|
||||
|
||||
self._joystick_toggle = toggle_item(
|
||||
"Joystick Debug Mode",
|
||||
tr("Joystick Debug Mode"),
|
||||
description="",
|
||||
initial_state=self._params.get_bool("JoystickDebugMode"),
|
||||
callback=self._on_joystick_debug_mode,
|
||||
@@ -59,14 +60,14 @@ class DeveloperLayout(Widget):
|
||||
)
|
||||
|
||||
self._long_maneuver_toggle = toggle_item(
|
||||
"Longitudinal Maneuver Mode",
|
||||
tr("Longitudinal Maneuver Mode"),
|
||||
description="",
|
||||
initial_state=self._params.get_bool("LongitudinalManeuverMode"),
|
||||
callback=self._on_long_maneuver_mode,
|
||||
)
|
||||
|
||||
self._alpha_long_toggle = toggle_item(
|
||||
"openpilot Longitudinal Control (Alpha)",
|
||||
tr("openpilot Longitudinal Control (Alpha)"),
|
||||
description=DESCRIPTIONS["alpha_longitudinal"],
|
||||
initial_state=self._params.get_bool("AlphaLongitudinalEnabled"),
|
||||
callback=self._on_alpha_long_enabled,
|
||||
@@ -163,7 +164,7 @@ class DeveloperLayout(Widget):
|
||||
content = (f"<h1>{self._alpha_long_toggle.title}</h1><br>" +
|
||||
f"<p>{self._alpha_long_toggle.description}</p>")
|
||||
|
||||
dlg = ConfirmDialog(content, "Enable", rich=True)
|
||||
dlg = ConfirmDialog(content, tr("Enable"), rich=True)
|
||||
gui_app.set_modal_overlay(dlg, callback=confirm_callback)
|
||||
|
||||
else:
|
||||
|
||||
@@ -12,6 +12,7 @@ from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide
|
||||
from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog
|
||||
from openpilot.system.hardware import TICI
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
|
||||
from openpilot.system.ui.widgets.html_render import HtmlModal
|
||||
@@ -21,10 +22,10 @@ from openpilot.system.ui.widgets.scroller import Scroller
|
||||
|
||||
# Description constants
|
||||
DESCRIPTIONS = {
|
||||
'pair_device': "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.",
|
||||
'driver_camera': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)",
|
||||
'reset_calibration': "openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down.",
|
||||
'review_guide': "Review the rules, features, and limitations of openpilot",
|
||||
'pair_device': tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."),
|
||||
'driver_camera': tr("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)"),
|
||||
'reset_calibration': tr("openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down."),
|
||||
'review_guide': tr("Review the rules, features, and limitations of openpilot"),
|
||||
}
|
||||
|
||||
|
||||
@@ -45,27 +46,27 @@ class DeviceLayout(Widget):
|
||||
ui_state.add_offroad_transition_callback(self._offroad_transition)
|
||||
|
||||
def _initialize_items(self):
|
||||
dongle_id = self._params.get("DongleId") or "N/A"
|
||||
serial = self._params.get("HardwareSerial") or "N/A"
|
||||
dongle_id = self._params.get("DongleId") or tr("N/A")
|
||||
serial = self._params.get("HardwareSerial") or tr("N/A")
|
||||
|
||||
self._pair_device_btn = button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device)
|
||||
self._pair_device_btn = button_item(tr("Pair Device"), tr("PAIR"), DESCRIPTIONS['pair_device'], callback=self._pair_device)
|
||||
self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired())
|
||||
|
||||
self._reset_calib_btn = button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt)
|
||||
self._reset_calib_btn = button_item(tr("Reset Calibration"), tr("RESET"), DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt)
|
||||
self._reset_calib_btn.set_description_opened_callback(self._update_calib_description)
|
||||
|
||||
self._power_off_btn = dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt)
|
||||
self._power_off_btn = dual_button_item(tr("Reboot"), tr("Power Off"), left_callback=self._reboot_prompt, right_callback=self._power_off_prompt)
|
||||
|
||||
items = [
|
||||
text_item("Dongle ID", dongle_id),
|
||||
text_item("Serial", serial),
|
||||
text_item(tr("Dongle ID"), dongle_id),
|
||||
text_item(tr("Serial"), serial),
|
||||
self._pair_device_btn,
|
||||
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
|
||||
button_item(tr("Driver Camera"), tr("PREVIEW"), DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
|
||||
self._reset_calib_btn,
|
||||
button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide, enabled=ui_state.is_offroad),
|
||||
regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory, enabled=ui_state.is_offroad),
|
||||
button_item(tr("Review Training Guide"), tr("REVIEW"), DESCRIPTIONS['review_guide'], self._on_review_training_guide, enabled=ui_state.is_offroad),
|
||||
regulatory_btn := button_item(tr("Regulatory"), tr("VIEW"), callback=self._on_regulatory, enabled=ui_state.is_offroad),
|
||||
# TODO: implement multilang
|
||||
# button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad),
|
||||
# button_item(tr("Change Language"), tr("CHANGE"), callback=self._show_language_selection, enabled=ui_state.is_offroad),
|
||||
self._power_off_btn,
|
||||
]
|
||||
regulatory_btn.set_visible(TICI)
|
||||
@@ -106,7 +107,7 @@ class DeviceLayout(Widget):
|
||||
|
||||
def _reset_calibration_prompt(self):
|
||||
if ui_state.engaged:
|
||||
gui_app.set_modal_overlay(alert_dialog("Disengage to Reset Calibration"))
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reset Calibration")))
|
||||
return
|
||||
|
||||
def reset_calibration(result: int):
|
||||
@@ -122,7 +123,7 @@ class DeviceLayout(Widget):
|
||||
self._params.put_bool("OnroadCycleRequested", True)
|
||||
self._update_calib_description()
|
||||
|
||||
dialog = ConfirmDialog("Are you sure you want to reset calibration?", "Reset")
|
||||
dialog = ConfirmDialog(tr("Are you sure you want to reset calibration?"), tr("Reset"))
|
||||
gui_app.set_modal_overlay(dialog, callback=reset_calibration)
|
||||
|
||||
def _update_calib_description(self):
|
||||
@@ -136,7 +137,8 @@ class DeviceLayout(Widget):
|
||||
if calib.calStatus != log.LiveCalibrationData.Status.uncalibrated:
|
||||
pitch = math.degrees(calib.rpyCalib[1])
|
||||
yaw = math.degrees(calib.rpyCalib[2])
|
||||
desc += f" Your device is pointed {abs(pitch):.1f}° {'down' if pitch > 0 else 'up'} and {abs(yaw):.1f}° {'left' if yaw > 0 else 'right'}."
|
||||
desc += tr(" Your device is pointed {:.1f}° {} and {:.1f}° {}.").format(abs(pitch), tr("down") if pitch > 0 else tr("up"),
|
||||
abs(yaw), tr("left") if yaw > 0 else tr("right"))
|
||||
except Exception:
|
||||
cloudlog.exception("invalid CalibrationParams")
|
||||
|
||||
@@ -148,9 +150,9 @@ class DeviceLayout(Widget):
|
||||
except Exception:
|
||||
cloudlog.exception("invalid LiveDelay")
|
||||
if lag_perc < 100:
|
||||
desc += f"<br><br>Steering lag calibration is {lag_perc}% complete."
|
||||
desc += tr("<br><br>Steering lag calibration is {}% complete.").format(lag_perc)
|
||||
else:
|
||||
desc += "<br><br>Steering lag calibration is complete."
|
||||
desc += tr("<br><br>Steering lag calibration is complete.")
|
||||
|
||||
torque_bytes = self._params.get("LiveTorqueParameters")
|
||||
if torque_bytes:
|
||||
@@ -160,24 +162,24 @@ class DeviceLayout(Widget):
|
||||
if torque.useParams:
|
||||
torque_perc = torque.calPerc
|
||||
if torque_perc < 100:
|
||||
desc += f" Steering torque response calibration is {torque_perc}% complete."
|
||||
desc += tr(" Steering torque response calibration is {}% complete.").format(torque_perc)
|
||||
else:
|
||||
desc += " Steering torque response calibration is complete."
|
||||
desc += tr(" Steering torque response calibration is complete.")
|
||||
except Exception:
|
||||
cloudlog.exception("invalid LiveTorqueParameters")
|
||||
|
||||
desc += "<br><br>"
|
||||
desc += ("openpilot is continuously calibrating, resetting is rarely required. " +
|
||||
"Resetting calibration will restart openpilot if the car is powered on.")
|
||||
desc += tr("openpilot is continuously calibrating, resetting is rarely required. " +
|
||||
"Resetting calibration will restart openpilot if the car is powered on.")
|
||||
|
||||
self._reset_calib_btn.set_description(desc)
|
||||
|
||||
def _reboot_prompt(self):
|
||||
if ui_state.engaged:
|
||||
gui_app.set_modal_overlay(alert_dialog("Disengage to Reboot"))
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reboot")))
|
||||
return
|
||||
|
||||
dialog = ConfirmDialog("Are you sure you want to reboot?", "Reboot")
|
||||
dialog = ConfirmDialog(tr("Are you sure you want to reboot?"), tr("Reboot"))
|
||||
gui_app.set_modal_overlay(dialog, callback=self._perform_reboot)
|
||||
|
||||
def _perform_reboot(self, result: int):
|
||||
@@ -186,10 +188,10 @@ class DeviceLayout(Widget):
|
||||
|
||||
def _power_off_prompt(self):
|
||||
if ui_state.engaged:
|
||||
gui_app.set_modal_overlay(alert_dialog("Disengage to Power Off"))
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Power Off")))
|
||||
return
|
||||
|
||||
dialog = ConfirmDialog("Are you sure you want to power off?", "Power Off")
|
||||
dialog = ConfirmDialog(tr("Are you sure you want to power off?"), tr("Power Off"))
|
||||
gui_app.set_modal_overlay(dialog, callback=self._perform_power_off)
|
||||
|
||||
def _perform_power_off(self, result: int):
|
||||
@@ -210,5 +212,6 @@ class DeviceLayout(Widget):
|
||||
if not self._training_guide:
|
||||
def completed_callback():
|
||||
gui_app.set_modal_overlay(None)
|
||||
|
||||
self._training_guide = TrainingGuide(completed_callback=completed_callback)
|
||||
gui_app.set_modal_overlay(self._training_guide)
|
||||
|
||||
@@ -8,19 +8,20 @@ from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
|
||||
from openpilot.system.ui.lib.multilang import tr, trn
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
|
||||
from openpilot.system.ui.lib.wrap_text import wrap_text
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.selfdrive.ui.lib.api_helpers import get_token
|
||||
|
||||
TITLE = "Firehose Mode"
|
||||
DESCRIPTION = (
|
||||
TITLE = tr("Firehose Mode")
|
||||
DESCRIPTION = tr(
|
||||
"openpilot learns to drive by watching humans, like you, drive.\n\n"
|
||||
+ "Firehose Mode allows you to maximize your training data uploads to improve "
|
||||
+ "openpilot's driving models. More data means bigger models, which means better Experimental Mode."
|
||||
)
|
||||
INSTRUCTIONS = (
|
||||
INSTRUCTIONS = tr(
|
||||
"For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n"
|
||||
+ "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n\n"
|
||||
+ "Frequently Asked Questions\n\n"
|
||||
@@ -106,7 +107,8 @@ class FirehoseLayout(Widget):
|
||||
|
||||
# Contribution count (if available)
|
||||
if self.segment_count > 0:
|
||||
contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far."
|
||||
contrib_text = trn("{} segment of your driving is in the training dataset so far.",
|
||||
"{} segments of your driving is in the training dataset so far.", self.segment_count).format(self.segment_count)
|
||||
y = self._draw_wrapped_text(x, y, w, contrib_text, gui_app.font(FontWeight.BOLD), 52, rl.WHITE)
|
||||
y += 20 + 20
|
||||
|
||||
@@ -132,9 +134,9 @@ class FirehoseLayout(Widget):
|
||||
network_metered = ui_state.sm["deviceState"].networkMetered
|
||||
|
||||
if not network_metered and network_type != 0: # Not metered and connected
|
||||
return "ACTIVE", self.GREEN
|
||||
return tr("ACTIVE"), self.GREEN
|
||||
else:
|
||||
return "INACTIVE: connect to an unmetered network", self.RED
|
||||
return tr("INACTIVE: connect to an unmetered network"), self.RED
|
||||
|
||||
def _fetch_firehose_stats(self):
|
||||
try:
|
||||
|
||||
@@ -8,6 +8,7 @@ from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.lib.wifi_manager import WifiManager
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
@@ -58,12 +59,12 @@ class SettingsLayout(Widget):
|
||||
wifi_manager.set_active(False)
|
||||
|
||||
self._panels = {
|
||||
PanelType.DEVICE: PanelInfo("Device", DeviceLayout()),
|
||||
PanelType.NETWORK: PanelInfo("Network", NetworkUI(wifi_manager)),
|
||||
PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout()),
|
||||
PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout()),
|
||||
PanelType.FIREHOSE: PanelInfo("Firehose", FirehoseLayout()),
|
||||
PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout()),
|
||||
PanelType.DEVICE: PanelInfo(tr("Device"), DeviceLayout()),
|
||||
PanelType.NETWORK: PanelInfo(tr("Network"), NetworkUI(wifi_manager)),
|
||||
PanelType.TOGGLES: PanelInfo(tr("Toggles"), TogglesLayout()),
|
||||
PanelType.SOFTWARE: PanelInfo(tr("Software"), SoftwareLayout()),
|
||||
PanelType.FIREHOSE: PanelInfo(tr("Firehose"), FirehoseLayout()),
|
||||
PanelType.DEVELOPER: PanelInfo(tr("Developer"), DeveloperLayout()),
|
||||
}
|
||||
|
||||
self._font_medium = gui_app.font(FontWeight.MEDIUM)
|
||||
|
||||
@@ -4,6 +4,7 @@ import datetime
|
||||
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
|
||||
from openpilot.system.ui.lib.multilang import tr, trn
|
||||
from openpilot.system.ui.widgets import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.widgets.list_view import button_item, text_item, ListItem
|
||||
@@ -15,7 +16,7 @@ UPDATED_TIMEOUT = 10 # seconds to wait for updated to respond
|
||||
|
||||
def time_ago(date: datetime.datetime | None) -> str:
|
||||
if not date:
|
||||
return "never"
|
||||
return tr("never")
|
||||
|
||||
if not system_time_valid():
|
||||
return date.strftime("%a %b %d %Y")
|
||||
@@ -26,16 +27,16 @@ def time_ago(date: datetime.datetime | None) -> str:
|
||||
|
||||
diff_seconds = int((now - date).total_seconds())
|
||||
if diff_seconds < 60:
|
||||
return "now"
|
||||
return tr("now")
|
||||
if diff_seconds < 3600:
|
||||
m = diff_seconds // 60
|
||||
return f"{m} minute{'s' if m != 1 else ''} ago"
|
||||
return trn("{} minute ago", "{} minutes ago", m).format(m)
|
||||
if diff_seconds < 86400:
|
||||
h = diff_seconds // 3600
|
||||
return f"{h} hour{'s' if h != 1 else ''} ago"
|
||||
return trn("{} hour ago", "{} hours ago", h).format(h)
|
||||
if diff_seconds < 604800:
|
||||
d = diff_seconds // 86400
|
||||
return f"{d} day{'s' if d != 1 else ''} ago"
|
||||
return trn("{} day ago", "{} days ago", d).format(d)
|
||||
return date.strftime("%a %b %d %Y")
|
||||
|
||||
|
||||
@@ -43,12 +44,12 @@ class SoftwareLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self._onroad_label = ListItem(title="Updates are only downloaded while the car is off.")
|
||||
self._version_item = text_item("Current Version", ui_state.params.get("UpdaterCurrentDescription") or "")
|
||||
self._download_btn = button_item("Download", "CHECK", callback=self._on_download_update)
|
||||
self._onroad_label = ListItem(title=tr("Updates are only downloaded while the car is off."))
|
||||
self._version_item = text_item(tr("Current Version"), ui_state.params.get("UpdaterCurrentDescription") or "")
|
||||
self._download_btn = button_item(tr("Download"), tr("CHECK"), callback=self._on_download_update)
|
||||
|
||||
# Install button is initially hidden
|
||||
self._install_btn = button_item("Install Update", "INSTALL", callback=self._on_install_update)
|
||||
self._install_btn = button_item(tr("Install Update"), tr("INSTALL"), callback=self._on_install_update)
|
||||
self._install_btn.set_visible(False)
|
||||
|
||||
# Track waiting-for-updater transition to avoid brief re-enable while still idle
|
||||
@@ -66,7 +67,7 @@ class SoftwareLayout(Widget):
|
||||
self._install_btn,
|
||||
# TODO: implement branch switching
|
||||
# button_item("Target Branch", "SELECT", callback=self._on_select_branch),
|
||||
button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall),
|
||||
button_item("Uninstall", tr("UNINSTALL"), callback=self._on_uninstall),
|
||||
]
|
||||
return items
|
||||
|
||||
@@ -101,19 +102,19 @@ class SoftwareLayout(Widget):
|
||||
self._download_btn.action_item.set_value(updater_state)
|
||||
else:
|
||||
if failed_count > 0:
|
||||
self._download_btn.action_item.set_value("failed to check for update")
|
||||
self._download_btn.action_item.set_text("CHECK")
|
||||
self._download_btn.action_item.set_value(tr("failed to check for update"))
|
||||
self._download_btn.action_item.set_text(tr("CHECK"))
|
||||
elif fetch_available:
|
||||
self._download_btn.action_item.set_value("update available")
|
||||
self._download_btn.action_item.set_text("DOWNLOAD")
|
||||
self._download_btn.action_item.set_value(tr("update available"))
|
||||
self._download_btn.action_item.set_text(tr("DOWNLOAD"))
|
||||
else:
|
||||
last_update = ui_state.params.get("LastUpdateTime")
|
||||
if last_update:
|
||||
formatted = time_ago(last_update)
|
||||
self._download_btn.action_item.set_value(f"up to date, last checked {formatted}")
|
||||
self._download_btn.action_item.set_value(tr("up to date, last checked {}").format(formatted))
|
||||
else:
|
||||
self._download_btn.action_item.set_value("up to date, last checked never")
|
||||
self._download_btn.action_item.set_text("CHECK")
|
||||
self._download_btn.action_item.set_value(tr("up to date, last checked never"))
|
||||
self._download_btn.action_item.set_text(tr("CHECK"))
|
||||
|
||||
# If we've been waiting too long without a state change, reset state
|
||||
if self._waiting_for_updater and (time.monotonic() - self._waiting_start_ts > UPDATED_TIMEOUT):
|
||||
@@ -127,7 +128,7 @@ class SoftwareLayout(Widget):
|
||||
if update_available:
|
||||
new_desc = ui_state.params.get("UpdaterNewDescription") or ""
|
||||
new_release_notes = (ui_state.params.get("UpdaterNewReleaseNotes") or b"").decode("utf-8", "replace")
|
||||
self._install_btn.action_item.set_text("INSTALL")
|
||||
self._install_btn.action_item.set_text(tr("INSTALL"))
|
||||
self._install_btn.action_item.set_value(new_desc)
|
||||
self._install_btn.set_description(new_release_notes)
|
||||
# Enable install button for testing (like Qt showEvent)
|
||||
@@ -138,7 +139,7 @@ class SoftwareLayout(Widget):
|
||||
def _on_download_update(self):
|
||||
# Check if we should start checking or start downloading
|
||||
self._download_btn.action_item.set_enabled(False)
|
||||
if self._download_btn.action_item.text == "CHECK":
|
||||
if self._download_btn.action_item.text == tr("CHECK"):
|
||||
# Start checking for updates
|
||||
self._waiting_for_updater = True
|
||||
self._waiting_start_ts = time.monotonic()
|
||||
@@ -154,7 +155,7 @@ class SoftwareLayout(Widget):
|
||||
if result == DialogResult.CONFIRM:
|
||||
ui_state.params.put_bool("DoUninstall", True)
|
||||
|
||||
dialog = ConfirmDialog("Are you sure you want to uninstall?", "Uninstall")
|
||||
dialog = ConfirmDialog(tr("Are you sure you want to uninstall?"), tr("Uninstall"))
|
||||
gui_app.set_modal_overlay(dialog, callback=handle_uninstall_confirmation)
|
||||
|
||||
def _on_install_update(self):
|
||||
|
||||
@@ -5,6 +5,7 @@ from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_i
|
||||
from openpilot.system.ui.widgets.scroller import Scroller
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
@@ -12,24 +13,24 @@ PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants
|
||||
|
||||
# Description constants
|
||||
DESCRIPTIONS = {
|
||||
"OpenpilotEnabledToggle": (
|
||||
"OpenpilotEnabledToggle": tr(
|
||||
"Use the openpilot system for adaptive cruise control and lane keep driver assistance. " +
|
||||
"Your attention is required at all times to use this feature."
|
||||
),
|
||||
"DisengageOnAccelerator": "When enabled, pressing the accelerator pedal will disengage openpilot.",
|
||||
"LongitudinalPersonality": (
|
||||
"DisengageOnAccelerator": tr("When enabled, pressing the accelerator pedal will disengage openpilot."),
|
||||
"LongitudinalPersonality": tr(
|
||||
"Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " +
|
||||
"In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " +
|
||||
"your steering wheel distance button."
|
||||
),
|
||||
"IsLdwEnabled": (
|
||||
"IsLdwEnabled": tr(
|
||||
"Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line " +
|
||||
"without a turn signal activated while driving over 31 mph (50 km/h)."
|
||||
),
|
||||
"AlwaysOnDM": "Enable driver monitoring even when openpilot is not engaged.",
|
||||
'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.",
|
||||
"IsMetric": "Display speed in km/h instead of mph.",
|
||||
"RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.",
|
||||
"AlwaysOnDM": tr("Enable driver monitoring even when openpilot is not engaged."),
|
||||
'RecordFront': tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."),
|
||||
"IsMetric": tr("Display speed in km/h instead of mph."),
|
||||
"RecordAudio": tr("Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect."),
|
||||
}
|
||||
|
||||
|
||||
@@ -42,49 +43,49 @@ class TogglesLayout(Widget):
|
||||
# param, title, desc, icon, needs_restart
|
||||
self._toggle_defs = {
|
||||
"OpenpilotEnabledToggle": (
|
||||
"Enable openpilot",
|
||||
tr("Enable openpilot"),
|
||||
DESCRIPTIONS["OpenpilotEnabledToggle"],
|
||||
"chffr_wheel.png",
|
||||
True,
|
||||
),
|
||||
"ExperimentalMode": (
|
||||
"Experimental Mode",
|
||||
tr("Experimental Mode"),
|
||||
"",
|
||||
"experimental_white.png",
|
||||
False,
|
||||
),
|
||||
"DisengageOnAccelerator": (
|
||||
"Disengage on Accelerator Pedal",
|
||||
tr("Disengage on Accelerator Pedal"),
|
||||
DESCRIPTIONS["DisengageOnAccelerator"],
|
||||
"disengage_on_accelerator.png",
|
||||
False,
|
||||
),
|
||||
"IsLdwEnabled": (
|
||||
"Enable Lane Departure Warnings",
|
||||
tr("Enable Lane Departure Warnings"),
|
||||
DESCRIPTIONS["IsLdwEnabled"],
|
||||
"warning.png",
|
||||
False,
|
||||
),
|
||||
"AlwaysOnDM": (
|
||||
"Always-On Driver Monitoring",
|
||||
tr("Always-On Driver Monitoring"),
|
||||
DESCRIPTIONS["AlwaysOnDM"],
|
||||
"monitoring.png",
|
||||
False,
|
||||
),
|
||||
"RecordFront": (
|
||||
"Record and Upload Driver Camera",
|
||||
tr("Record and Upload Driver Camera"),
|
||||
DESCRIPTIONS["RecordFront"],
|
||||
"monitoring.png",
|
||||
True,
|
||||
),
|
||||
"RecordAudio": (
|
||||
"Record and Upload Microphone Audio",
|
||||
tr("Record and Upload Microphone Audio"),
|
||||
DESCRIPTIONS["RecordAudio"],
|
||||
"microphone.png",
|
||||
True,
|
||||
),
|
||||
"IsMetric": (
|
||||
"Use Metric System",
|
||||
tr("Use Metric System"),
|
||||
DESCRIPTIONS["IsMetric"],
|
||||
"metric.png",
|
||||
False,
|
||||
@@ -92,9 +93,9 @@ class TogglesLayout(Widget):
|
||||
}
|
||||
|
||||
self._long_personality_setting = multiple_button_item(
|
||||
"Driving Personality",
|
||||
tr("Driving Personality"),
|
||||
DESCRIPTIONS["LongitudinalPersonality"],
|
||||
buttons=["Aggressive", "Standard", "Relaxed"],
|
||||
buttons=[tr("Aggressive"), tr("Standard"), tr("Relaxed")],
|
||||
button_width=255,
|
||||
callback=self._set_longitudinal_personality,
|
||||
selected_index=self._params.get("LongitudinalPersonality", return_default=True),
|
||||
@@ -119,7 +120,7 @@ class TogglesLayout(Widget):
|
||||
toggle.action_item.set_enabled(not locked)
|
||||
|
||||
if needs_restart and not locked:
|
||||
toggle.set_description(toggle.description + " Changing this setting will restart openpilot if the car is powered on.")
|
||||
toggle.set_description(toggle.description + tr(" Changing this setting will restart openpilot if the car is powered on."))
|
||||
|
||||
# track for engaged state updates
|
||||
if locked:
|
||||
@@ -150,7 +151,7 @@ class TogglesLayout(Widget):
|
||||
def _update_toggles(self):
|
||||
ui_state.update_params()
|
||||
|
||||
e2e_description = (
|
||||
e2e_description = tr(
|
||||
"openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " +
|
||||
"Experimental features are listed below:<br>" +
|
||||
"<h4>End-to-End Longitudinal Control</h4><br>" +
|
||||
@@ -174,15 +175,15 @@ class TogglesLayout(Widget):
|
||||
self._long_personality_setting.action_item.set_enabled(False)
|
||||
self._params.remove("ExperimentalMode")
|
||||
|
||||
unavailable = "Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control."
|
||||
unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control.")
|
||||
|
||||
long_desc = unavailable + " openpilot longitudinal control may come in a future update."
|
||||
long_desc = unavailable + " " + tr("openpilot longitudinal control may come in a future update.")
|
||||
if ui_state.CP.alphaLongitudinalAvailable:
|
||||
if self._is_release:
|
||||
long_desc = unavailable + " " + ("An alpha version of openpilot longitudinal control can be tested, along with " +
|
||||
"Experimental mode, on non-release branches.")
|
||||
long_desc = unavailable + " " + tr("An alpha version of openpilot longitudinal control can be tested, along with " +
|
||||
"Experimental mode, on non-release branches.")
|
||||
else:
|
||||
long_desc = "Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode."
|
||||
long_desc = tr("Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode.")
|
||||
|
||||
self._toggles["ExperimentalMode"].set_description("<b>" + long_desc + "</b><br><br>" + e2e_description)
|
||||
else:
|
||||
@@ -221,7 +222,7 @@ class TogglesLayout(Widget):
|
||||
# show confirmation dialog
|
||||
content = (f"<h1>{self._toggles['ExperimentalMode'].title}</h1><br>" +
|
||||
f"<p>{self._toggles['ExperimentalMode'].description}</p>")
|
||||
dlg = ConfirmDialog(content, "Enable", rich=True)
|
||||
dlg = ConfirmDialog(content, tr("Enable"), rich=True)
|
||||
gui_app.set_modal_overlay(dlg, callback=confirm_callback)
|
||||
else:
|
||||
self._update_experimental_mode_icon()
|
||||
|
||||
@@ -5,6 +5,7 @@ from collections.abc import Callable
|
||||
from cereal import log
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, FONT_SCALE
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
@@ -39,13 +40,13 @@ class Colors:
|
||||
|
||||
|
||||
NETWORK_TYPES = {
|
||||
NetworkType.none: "--",
|
||||
NetworkType.wifi: "Wi-Fi",
|
||||
NetworkType.ethernet: "ETH",
|
||||
NetworkType.cell2G: "2G",
|
||||
NetworkType.cell3G: "3G",
|
||||
NetworkType.cell4G: "LTE",
|
||||
NetworkType.cell5G: "5G",
|
||||
NetworkType.none: tr("--"),
|
||||
NetworkType.wifi: tr("Wi-Fi"),
|
||||
NetworkType.ethernet: tr("ETH"),
|
||||
NetworkType.cell2G: tr("2G"),
|
||||
NetworkType.cell3G: tr("3G"),
|
||||
NetworkType.cell4G: tr("LTE"),
|
||||
NetworkType.cell5G: tr("5G"),
|
||||
}
|
||||
|
||||
|
||||
@@ -67,9 +68,9 @@ class Sidebar(Widget):
|
||||
self._net_type = NETWORK_TYPES.get(NetworkType.none)
|
||||
self._net_strength = 0
|
||||
|
||||
self._temp_status = MetricData("TEMP", "GOOD", Colors.GOOD)
|
||||
self._panda_status = MetricData("VEHICLE", "ONLINE", Colors.GOOD)
|
||||
self._connect_status = MetricData("CONNECT", "OFFLINE", Colors.WARNING)
|
||||
self._temp_status = MetricData(tr("TEMP"), tr("GOOD"), Colors.GOOD)
|
||||
self._panda_status = MetricData(tr("VEHICLE"), tr("ONLINE"), Colors.GOOD)
|
||||
self._connect_status = MetricData(tr("CONNECT"), tr("OFFLINE"), Colors.WARNING)
|
||||
self._recording_audio = False
|
||||
|
||||
self._home_img = gui_app.texture("images/button_home.png", HOME_BTN.width, HOME_BTN.height)
|
||||
@@ -113,7 +114,7 @@ class Sidebar(Widget):
|
||||
self._update_panda_status()
|
||||
|
||||
def _update_network_status(self, device_state):
|
||||
self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, "Unknown")
|
||||
self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, tr("Unknown"))
|
||||
strength = device_state.networkStrength
|
||||
self._net_strength = max(0, min(5, strength.raw + 1)) if strength > 0 else 0
|
||||
|
||||
@@ -121,26 +122,26 @@ class Sidebar(Widget):
|
||||
thermal_status = device_state.thermalStatus
|
||||
|
||||
if thermal_status == ThermalStatus.green:
|
||||
self._temp_status.update("TEMP", "GOOD", Colors.GOOD)
|
||||
self._temp_status.update(tr("TEMP"), tr("GOOD"), Colors.GOOD)
|
||||
elif thermal_status == ThermalStatus.yellow:
|
||||
self._temp_status.update("TEMP", "OK", Colors.WARNING)
|
||||
self._temp_status.update(tr("TEMP"), tr("OK"), Colors.WARNING)
|
||||
else:
|
||||
self._temp_status.update("TEMP", "HIGH", Colors.DANGER)
|
||||
self._temp_status.update(tr("TEMP"), tr("HIGH"), Colors.DANGER)
|
||||
|
||||
def _update_connection_status(self, device_state):
|
||||
last_ping = device_state.lastAthenaPingTime
|
||||
if last_ping == 0:
|
||||
self._connect_status.update("CONNECT", "OFFLINE", Colors.WARNING)
|
||||
self._connect_status.update(tr("CONNECT"), tr("OFFLINE"), Colors.WARNING)
|
||||
elif time.monotonic_ns() - last_ping < 80_000_000_000: # 80 seconds in nanoseconds
|
||||
self._connect_status.update("CONNECT", "ONLINE", Colors.GOOD)
|
||||
self._connect_status.update(tr("CONNECT"), tr("ONLINE"), Colors.GOOD)
|
||||
else:
|
||||
self._connect_status.update("CONNECT", "ERROR", Colors.DANGER)
|
||||
self._connect_status.update(tr("CONNECT"), tr("ERROR"), Colors.DANGER)
|
||||
|
||||
def _update_panda_status(self):
|
||||
if ui_state.panda_type == log.PandaState.PandaType.unknown:
|
||||
self._panda_status.update("NO", "PANDA", Colors.DANGER)
|
||||
self._panda_status.update(tr("NO"), tr("PANDA"), Colors.DANGER)
|
||||
else:
|
||||
self._panda_status.update("VEHICLE", "ONLINE", Colors.GOOD)
|
||||
self._panda_status.update(tr("VEHICLE"), tr("ONLINE"), Colors.GOOD)
|
||||
|
||||
def _handle_mouse_release(self, mouse_pos: MousePos):
|
||||
if rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN):
|
||||
|
||||
@@ -5,6 +5,7 @@ from cereal import messaging, log
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.hardware import TICI
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.label import Label
|
||||
@@ -47,22 +48,22 @@ class Alert:
|
||||
|
||||
# Pre-defined alert instances
|
||||
ALERT_STARTUP_PENDING = Alert(
|
||||
text1="openpilot Unavailable",
|
||||
text2="Waiting to start",
|
||||
text1=tr("openpilot Unavailable"),
|
||||
text2=tr("Waiting to start"),
|
||||
size=AlertSize.mid,
|
||||
status=AlertStatus.normal,
|
||||
)
|
||||
|
||||
ALERT_CRITICAL_TIMEOUT = Alert(
|
||||
text1="TAKE CONTROL IMMEDIATELY",
|
||||
text2="System Unresponsive",
|
||||
text1=tr("TAKE CONTROL IMMEDIATELY"),
|
||||
text2=tr("System Unresponsive"),
|
||||
size=AlertSize.full,
|
||||
status=AlertStatus.critical,
|
||||
)
|
||||
|
||||
ALERT_CRITICAL_REBOOT = Alert(
|
||||
text1="System Unresponsive",
|
||||
text2="Reboot Device",
|
||||
text1=tr("System Unresponsive"),
|
||||
text2=tr("Reboot Device"),
|
||||
size=AlertSize.mid,
|
||||
status=AlertStatus.normal,
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ from openpilot.selfdrive.ui.onroad.cameraview import CameraView
|
||||
from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state, device
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets.label import gui_label
|
||||
|
||||
|
||||
@@ -30,7 +31,7 @@ class DriverCameraDialog(CameraView):
|
||||
if not self.frame:
|
||||
gui_label(
|
||||
rect,
|
||||
"camera starting",
|
||||
tr("camera starting"),
|
||||
font_size=100,
|
||||
font_weight=FontWeight.BOLD,
|
||||
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
|
||||
|
||||
@@ -4,6 +4,7 @@ from openpilot.common.constants import CV
|
||||
from openpilot.selfdrive.ui.onroad.exp_button import ExpButton
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
@@ -144,7 +145,7 @@ class HudRenderer(Widget):
|
||||
elif ui_state.status == UIStatus.OVERRIDE:
|
||||
max_color = COLORS.override
|
||||
|
||||
max_text = "MAX"
|
||||
max_text = tr("MAX")
|
||||
max_text_width = measure_text_cached(self._font_semi_bold, max_text, FONT_SIZES.max_speed).x
|
||||
rl.draw_text_ex(
|
||||
self._font_semi_bold,
|
||||
@@ -173,7 +174,7 @@ class HudRenderer(Widget):
|
||||
speed_pos = rl.Vector2(rect.x + rect.width / 2 - speed_text_size.x / 2, 180 - speed_text_size.y / 2)
|
||||
rl.draw_text_ex(self._font_bold, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.white)
|
||||
|
||||
unit_text = "km/h" if ui_state.is_metric else "mph"
|
||||
unit_text = tr("km/h") if ui_state.is_metric else tr("mph")
|
||||
unit_text_size = measure_text_cached(self._font_medium, unit_text, FONT_SIZES.speed_unit)
|
||||
unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2)
|
||||
rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.white_translucent)
|
||||
|
||||
@@ -4,10 +4,8 @@ import json
|
||||
import os
|
||||
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.system.ui.lib.multilang import UI_DIR, TRANSLATIONS_DIR, LANGUAGES_FILE
|
||||
|
||||
UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui")
|
||||
TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations")
|
||||
LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json")
|
||||
TRANSLATIONS_INCLUDE_FILE = os.path.join(TRANSLATIONS_DIR, "alerts_generated.h")
|
||||
PLURAL_ONLY = ["en"] # base language, only create entries for strings with plural forms
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import pyray as rl
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
|
||||
@@ -46,7 +47,7 @@ class ExperimentalModeButton(Widget):
|
||||
rl.draw_line_ex(rl.Vector2(line_x, rect.y), rl.Vector2(line_x, rect.y + rect.height), 3, separator_color)
|
||||
|
||||
# Draw text label (left aligned)
|
||||
text = "EXPERIMENTAL MODE ON" if self.experimental_mode else "CHILL MODE ON"
|
||||
text = tr("EXPERIMENTAL MODE ON") if self.experimental_mode else tr("CHILL MODE ON")
|
||||
text_x = rect.x + self.horizontal_padding
|
||||
text_y = rect.y + rect.height / 2 - 45 * FONT_SCALE // 2 # Center vertically
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from dataclasses import dataclass
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.lib.wrap_text import wrap_text
|
||||
@@ -14,7 +15,7 @@ from openpilot.system.ui.widgets.html_render import HtmlRenderer
|
||||
from openpilot.selfdrive.selfdrived.alertmanager import OFFROAD_ALERTS
|
||||
|
||||
|
||||
NO_RELEASE_NOTES = "<h2>No release notes available.</h2>"
|
||||
NO_RELEASE_NOTES = tr("<h2>No release notes available.</h2>")
|
||||
|
||||
|
||||
class AlertColors:
|
||||
@@ -101,15 +102,15 @@ class AbstractAlert(Widget, ABC):
|
||||
if self.dismiss_callback:
|
||||
self.dismiss_callback()
|
||||
|
||||
self.dismiss_btn = ActionButton("Close")
|
||||
self.dismiss_btn = ActionButton(tr("Close"))
|
||||
|
||||
self.snooze_btn = ActionButton("Snooze Update", style=ButtonStyle.DARK)
|
||||
self.snooze_btn = ActionButton(tr("Snooze Update"), style=ButtonStyle.DARK)
|
||||
self.snooze_btn.set_click_callback(snooze_callback)
|
||||
|
||||
self.excessive_actuation_btn = ActionButton("Acknowledge Excessive Actuation", style=ButtonStyle.DARK, min_width=800)
|
||||
self.excessive_actuation_btn = ActionButton(tr("Acknowledge Excessive Actuation"), style=ButtonStyle.DARK, min_width=800)
|
||||
self.excessive_actuation_btn.set_click_callback(excessive_actuation_callback)
|
||||
|
||||
self.reboot_btn = ActionButton("Reboot and Update", min_width=600)
|
||||
self.reboot_btn = ActionButton(tr("Reboot and Update"), min_width=600)
|
||||
self.reboot_btn.set_click_callback(lambda: HARDWARE.reboot())
|
||||
|
||||
# TODO: just use a Scroller?
|
||||
|
||||
@@ -8,6 +8,7 @@ from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.lib.application import FontWeight, gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.wrap_text import wrap_text
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
@@ -99,7 +100,7 @@ class PairingDialog(Widget):
|
||||
y += close_size + 40
|
||||
|
||||
# Title
|
||||
title = "Pair your device to your comma account"
|
||||
title = tr("Pair your device to your comma account")
|
||||
title_font = gui_app.font(FontWeight.NORMAL)
|
||||
left_width = int(content_rect.width * 0.5 - 15)
|
||||
|
||||
@@ -124,9 +125,9 @@ class PairingDialog(Widget):
|
||||
|
||||
def _render_instructions(self, rect: rl.Rectangle) -> None:
|
||||
instructions = [
|
||||
"Go to https://connect.comma.ai on your phone",
|
||||
"Click \"add new device\" and scan the QR code on the right",
|
||||
"Bookmark connect.comma.ai to your home screen to use it like an app",
|
||||
tr("Go to https://connect.comma.ai on your phone"),
|
||||
tr("Click \"add new device\" and scan the QR code on the right"),
|
||||
tr("Bookmark connect.comma.ai to your home screen to use it like an app"),
|
||||
]
|
||||
|
||||
font = gui_app.font(FontWeight.BOLD)
|
||||
@@ -157,7 +158,7 @@ class PairingDialog(Widget):
|
||||
rl.draw_rectangle_rounded(rect, 0.1, 20, rl.Color(240, 240, 240, 255))
|
||||
error_font = gui_app.font(FontWeight.BOLD)
|
||||
rl.draw_text_ex(
|
||||
error_font, "QR Code Error", rl.Vector2(rect.x + 20, rect.y + rect.height // 2 - 15), 30, 0.0, rl.RED
|
||||
error_font, tr("QR Code Error"), rl.Vector2(rect.x + 20, rect.y + rect.height // 2 - 15), 30, 0.0, rl.RED
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import pyray as rl
|
||||
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.lib.wrap_text import wrap_text
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
@@ -29,21 +30,21 @@ class PrimeWidget(Widget):
|
||||
w = rect.width - 160
|
||||
|
||||
# Title
|
||||
gui_label(rl.Rectangle(x, y, w, 90), "Upgrade Now", 75, font_weight=FontWeight.BOLD)
|
||||
gui_label(rl.Rectangle(x, y, w, 90), tr("Upgrade Now"), 75, font_weight=FontWeight.BOLD)
|
||||
|
||||
# Description with wrapping
|
||||
desc_y = y + 140
|
||||
font = gui_app.font(FontWeight.LIGHT)
|
||||
wrapped_text = "\n".join(wrap_text(font, "Become a comma prime member at connect.comma.ai", 56, int(w)))
|
||||
wrapped_text = "\n".join(wrap_text(font, tr("Become a comma prime member at connect.comma.ai"), 56, int(w)))
|
||||
text_size = measure_text_cached(font, wrapped_text, 56)
|
||||
rl.draw_text_ex(font, wrapped_text, rl.Vector2(x, desc_y), 56, 0, rl.WHITE)
|
||||
|
||||
# Features section
|
||||
features_y = desc_y + text_size.y + 50
|
||||
gui_label(rl.Rectangle(x, features_y, w, 50), "PRIME FEATURES:", 41, font_weight=FontWeight.BOLD)
|
||||
gui_label(rl.Rectangle(x, features_y, w, 50), tr("PRIME FEATURES:"), 41, font_weight=FontWeight.BOLD)
|
||||
|
||||
# Feature list
|
||||
features = ["Remote access", "24/7 LTE connectivity", "1 year of drive storage", "Remote snapshots"]
|
||||
features = [tr("Remote access"), tr("24/7 LTE connectivity"), tr("1 year of drive storage"), tr("Remote snapshots")]
|
||||
for i, feature in enumerate(features):
|
||||
item_y = features_y + 80 + i * 65
|
||||
gui_label(rl.Rectangle(x, item_y, 100, 60), "✓", 50, color=rl.Color(70, 91, 234, 255))
|
||||
@@ -58,5 +59,5 @@ class PrimeWidget(Widget):
|
||||
y = rect.y + 40
|
||||
|
||||
font = gui_app.font(FontWeight.BOLD)
|
||||
rl.draw_text_ex(font, "✓ SUBSCRIBED", rl.Vector2(x, y), 41, 0, rl.Color(134, 255, 78, 255))
|
||||
rl.draw_text_ex(font, "comma prime", rl.Vector2(x, y + 61), 75, 0, rl.WHITE)
|
||||
rl.draw_text_ex(font, tr("✓ SUBSCRIBED"), rl.Vector2(x, y), 41, 0, rl.Color(134, 255, 78, 255))
|
||||
rl.draw_text_ex(font, tr("comma prime"), rl.Vector2(x, y + 61), 75, 0, rl.WHITE)
|
||||
|
||||
@@ -3,6 +3,7 @@ from openpilot.common.time_helpers import system_time_valid
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.wrap_text import wrap_text
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.confirm_dialog import alert_dialog
|
||||
@@ -15,10 +16,10 @@ class SetupWidget(Widget):
|
||||
super().__init__()
|
||||
self._open_settings_callback = None
|
||||
self._pairing_dialog: PairingDialog | None = None
|
||||
self._pair_device_btn = Button("Pair device", self._show_pairing, button_style=ButtonStyle.PRIMARY)
|
||||
self._open_settings_btn = Button("Open", lambda: self._open_settings_callback() if self._open_settings_callback else None,
|
||||
self._pair_device_btn = Button(tr("Pair device"), self._show_pairing, button_style=ButtonStyle.PRIMARY)
|
||||
self._open_settings_btn = Button(tr("Open"), lambda: self._open_settings_callback() if self._open_settings_callback else None,
|
||||
button_style=ButtonStyle.PRIMARY)
|
||||
self._firehose_label = Label("🔥 Firehose Mode 🔥", font_weight=FontWeight.MEDIUM, font_size=64)
|
||||
self._firehose_label = Label(tr("🔥 Firehose Mode 🔥"), font_weight=FontWeight.MEDIUM, font_size=64)
|
||||
|
||||
def set_open_settings_callback(self, callback):
|
||||
self._open_settings_callback = callback
|
||||
@@ -40,11 +41,11 @@ class SetupWidget(Widget):
|
||||
|
||||
# Title
|
||||
font = gui_app.font(FontWeight.BOLD)
|
||||
rl.draw_text_ex(font, "Finish Setup", rl.Vector2(x, y), 75, 0, rl.WHITE)
|
||||
rl.draw_text_ex(font, tr("Finish Setup"), rl.Vector2(x, y), 75, 0, rl.WHITE)
|
||||
y += 113 # 75 + 38 spacing
|
||||
|
||||
# Description
|
||||
desc = "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."
|
||||
desc = tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.")
|
||||
light_font = gui_app.font(FontWeight.LIGHT)
|
||||
wrapped = wrap_text(light_font, desc, 50, int(w))
|
||||
for line in wrapped:
|
||||
@@ -71,7 +72,7 @@ class SetupWidget(Widget):
|
||||
|
||||
# Description
|
||||
desc_font = gui_app.font(FontWeight.NORMAL)
|
||||
desc_text = "Maximize your training data uploads to improve openpilot's driving models."
|
||||
desc_text = tr("Maximize your training data uploads to improve openpilot's driving models.")
|
||||
wrapped_desc = wrap_text(desc_font, desc_text, 40, int(w))
|
||||
|
||||
for line in wrapped_desc:
|
||||
@@ -87,7 +88,7 @@ class SetupWidget(Widget):
|
||||
|
||||
def _show_pairing(self):
|
||||
if not system_time_valid():
|
||||
dlg = alert_dialog("Please connect to Wi-Fi to complete initial pairing")
|
||||
dlg = alert_dialog(tr("Please connect to Wi-Fi to complete initial pairing"))
|
||||
gui_app.set_modal_overlay(dlg)
|
||||
return
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from enum import Enum
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.button import Button, ButtonStyle
|
||||
@@ -25,9 +26,9 @@ VALUE_FONT_SIZE = 48
|
||||
|
||||
|
||||
class SshKeyActionState(Enum):
|
||||
LOADING = "LOADING"
|
||||
ADD = "ADD"
|
||||
REMOVE = "REMOVE"
|
||||
LOADING = tr("LOADING")
|
||||
ADD = tr("ADD")
|
||||
REMOVE = tr("REMOVE")
|
||||
|
||||
|
||||
class SshKeyAction(ItemAction):
|
||||
@@ -85,7 +86,7 @@ class SshKeyAction(ItemAction):
|
||||
def _handle_button_click(self):
|
||||
if self._state == SshKeyActionState.ADD:
|
||||
self._keyboard.reset()
|
||||
self._keyboard.set_title("Enter your GitHub username")
|
||||
self._keyboard.set_title(tr("Enter your GitHub username"))
|
||||
gui_app.set_modal_overlay(self._keyboard, callback=self._on_username_submit)
|
||||
elif self._state == SshKeyActionState.REMOVE:
|
||||
self._params.remove("GithubUsername")
|
||||
@@ -110,7 +111,7 @@ class SshKeyAction(ItemAction):
|
||||
response.raise_for_status()
|
||||
keys = response.text.strip()
|
||||
if not keys:
|
||||
raise requests.exceptions.HTTPError("No SSH keys found")
|
||||
raise requests.exceptions.HTTPError(tr("No SSH keys found"))
|
||||
|
||||
# Success - save keys
|
||||
self._params.put("GithubUsername", username)
|
||||
@@ -119,10 +120,10 @@ class SshKeyAction(ItemAction):
|
||||
self._username = username
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
self._error_message = "Request timed out"
|
||||
self._error_message = tr("Request timed out")
|
||||
self._state = SshKeyActionState.ADD
|
||||
except Exception:
|
||||
self._error_message = f"No SSH keys found for user '{username}'"
|
||||
self._error_message = tr("No SSH keys found for user '{}'").format(username)
|
||||
self._state = SshKeyActionState.ADD
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import os
|
||||
import gettext
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
|
||||
UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui")
|
||||
TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations")
|
||||
LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json")
|
||||
|
||||
tr = gettext.gettext
|
||||
trn = gettext.ngettext
|
||||
@@ -1,5 +1,6 @@
|
||||
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 DialogResult
|
||||
from openpilot.system.ui.widgets.button import ButtonStyle, Button
|
||||
from openpilot.system.ui.widgets.label import Label
|
||||
@@ -16,8 +17,10 @@ BACKGROUND_COLOR = rl.Color(27, 27, 27, 255)
|
||||
|
||||
|
||||
class ConfirmDialog(Widget):
|
||||
def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel", rich: bool = False):
|
||||
def __init__(self, text: str, confirm_text: str, cancel_text: str | None = None, rich: bool = False):
|
||||
super().__init__()
|
||||
if cancel_text is None:
|
||||
cancel_text = tr("Cancel")
|
||||
self._label = Label(text, 70, FontWeight.BOLD, text_color=rl.Color(201, 201, 201, 255))
|
||||
self._html_renderer = HtmlRenderer(text=text, text_size={ElementType.P: 50}, center_text=True)
|
||||
self._cancel_button = Button(cancel_text, self._cancel_button_callback)
|
||||
@@ -85,5 +88,7 @@ class ConfirmDialog(Widget):
|
||||
return self._dialog_result
|
||||
|
||||
|
||||
def alert_dialog(message: str, button_text: str = "Ok"):
|
||||
def alert_dialog(message: str, button_text: str | None = None):
|
||||
if button_text is None:
|
||||
button_text = tr("OK")
|
||||
return ConfirmDialog(message, button_text, cancel_text="")
|
||||
|
||||
@@ -4,6 +4,7 @@ from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
|
||||
from openpilot.system.ui.lib.wrap_text import wrap_text
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
@@ -243,7 +244,7 @@ class HtmlModal(Widget):
|
||||
super().__init__()
|
||||
self._content = HtmlRenderer(file_path=file_path, text=text)
|
||||
self._scroll_panel = GuiScrollPanel()
|
||||
self._ok_button = Button("OK", click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY)
|
||||
self._ok_button = Button(tr("OK"), click_callback=lambda: gui_app.set_modal_overlay(None), button_style=ButtonStyle.PRIMARY)
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
margin = 50
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Literal
|
||||
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.button import ButtonStyle, Button
|
||||
from openpilot.system.ui.widgets.inputbox import InputBox
|
||||
@@ -77,7 +78,7 @@ class Keyboard(Widget):
|
||||
self._backspace_last_repeat: float = 0.0
|
||||
|
||||
self._render_return_status = -1
|
||||
self._cancel_button = Button("Cancel", self._cancel_button_callback)
|
||||
self._cancel_button = Button(tr("Cancel"), self._cancel_button_callback)
|
||||
|
||||
self._eye_button = Button("", self._eye_button_callback, button_style=ButtonStyle.TRANSPARENT)
|
||||
|
||||
@@ -98,7 +99,7 @@ class Keyboard(Widget):
|
||||
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)
|
||||
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],
|
||||
|
||||
@@ -3,6 +3,7 @@ import pyray as rl
|
||||
from collections.abc import Callable
|
||||
from abc import ABC
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.button import Button, ButtonStyle
|
||||
@@ -111,7 +112,7 @@ class ButtonAction(ItemAction):
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return _resolve_value(self._text_source, "Error")
|
||||
return _resolve_value(self._text_source, tr("Error"))
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
@@ -149,7 +150,7 @@ class TextAction(ItemAction):
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return _resolve_value(self._text_source, "Error")
|
||||
return _resolve_value(self._text_source, tr("Error"))
|
||||
|
||||
def get_width_hint(self) -> float:
|
||||
text_width = measure_text_cached(self._font, self.text, ITEM_TEXT_FONT_SIZE).x
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import cast
|
||||
|
||||
import pyray as rl
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
|
||||
from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
@@ -70,7 +71,7 @@ class NetworkUI(Widget):
|
||||
self._current_panel: PanelType = PanelType.WIFI
|
||||
self._wifi_panel = WifiManagerUI(wifi_manager)
|
||||
self._advanced_panel = AdvancedNetworkSettings(wifi_manager)
|
||||
self._nav_button = NavButton("Advanced")
|
||||
self._nav_button = NavButton(tr("Advanced"))
|
||||
self._nav_button.set_click_callback(self._cycle_panel)
|
||||
|
||||
def show_event(self):
|
||||
@@ -91,11 +92,11 @@ class NetworkUI(Widget):
|
||||
content_rect = rl.Rectangle(self._rect.x, self._rect.y + self._nav_button.rect.height + 40,
|
||||
self._rect.width, self._rect.height - self._nav_button.rect.height - 40)
|
||||
if self._current_panel == PanelType.WIFI:
|
||||
self._nav_button.text = "Advanced"
|
||||
self._nav_button.text = tr("Advanced")
|
||||
self._nav_button.set_position(self._rect.x + self._rect.width - self._nav_button.rect.width, self._rect.y + 20)
|
||||
self._wifi_panel.render(content_rect)
|
||||
else:
|
||||
self._nav_button.text = "Back"
|
||||
self._nav_button.text = tr("Back")
|
||||
self._nav_button.set_position(self._rect.x, self._rect.y + 20)
|
||||
self._advanced_panel.render(content_rect)
|
||||
|
||||
@@ -116,40 +117,40 @@ class AdvancedNetworkSettings(Widget):
|
||||
|
||||
# Tethering
|
||||
self._tethering_action = ToggleAction(initial_state=False)
|
||||
tethering_btn = ListItem(title="Enable Tethering", action_item=self._tethering_action, callback=self._toggle_tethering)
|
||||
tethering_btn = ListItem(title=tr("Enable Tethering"), action_item=self._tethering_action, callback=self._toggle_tethering)
|
||||
|
||||
# Edit tethering password
|
||||
self._tethering_password_action = ButtonAction(text="EDIT")
|
||||
tethering_password_btn = ListItem(title="Tethering Password", action_item=self._tethering_password_action, callback=self._edit_tethering_password)
|
||||
self._tethering_password_action = ButtonAction(text=tr("EDIT"))
|
||||
tethering_password_btn = ListItem(title=tr("Tethering Password"), action_item=self._tethering_password_action, callback=self._edit_tethering_password)
|
||||
|
||||
# Roaming toggle
|
||||
roaming_enabled = self._params.get_bool("GsmRoaming")
|
||||
self._roaming_action = ToggleAction(initial_state=roaming_enabled)
|
||||
self._roaming_btn = ListItem(title="Enable Roaming", action_item=self._roaming_action, callback=self._toggle_roaming)
|
||||
self._roaming_btn = ListItem(title=tr("Enable Roaming"), action_item=self._roaming_action, callback=self._toggle_roaming)
|
||||
|
||||
# Cellular metered toggle
|
||||
cellular_metered = self._params.get_bool("GsmMetered")
|
||||
self._cellular_metered_action = ToggleAction(initial_state=cellular_metered)
|
||||
self._cellular_metered_btn = ListItem(title="Cellular Metered", description="Prevent large data uploads when on a metered cellular connection",
|
||||
self._cellular_metered_btn = ListItem(title=tr("Cellular Metered"), description=tr("Prevent large data uploads when on a metered cellular connection"),
|
||||
action_item=self._cellular_metered_action, callback=self._toggle_cellular_metered)
|
||||
|
||||
# APN setting
|
||||
self._apn_btn = button_item("APN Setting", "EDIT", callback=self._edit_apn)
|
||||
self._apn_btn = button_item(tr("APN Setting"), tr("EDIT"), callback=self._edit_apn)
|
||||
|
||||
# Wi-Fi metered toggle
|
||||
self._wifi_metered_action = MultipleButtonAction(["default", "metered", "unmetered"], 255, 0, callback=self._toggle_wifi_metered)
|
||||
wifi_metered_btn = ListItem(title="Wi-Fi Network Metered", description="Prevent large data uploads when on a metered Wi-Fi connection",
|
||||
self._wifi_metered_action = MultipleButtonAction([tr("default"), tr("metered"), tr("unmetered")], 255, 0, callback=self._toggle_wifi_metered)
|
||||
wifi_metered_btn = ListItem(title=tr("Wi-Fi Network Metered"), description=tr("Prevent large data uploads when on a metered Wi-Fi connection"),
|
||||
action_item=self._wifi_metered_action)
|
||||
|
||||
items: list[Widget] = [
|
||||
tethering_btn,
|
||||
tethering_password_btn,
|
||||
text_item("IP Address", lambda: self._wifi_manager.ipv4_address),
|
||||
text_item(tr("IP Address"), lambda: self._wifi_manager.ipv4_address),
|
||||
self._roaming_btn,
|
||||
self._apn_btn,
|
||||
self._cellular_metered_btn,
|
||||
wifi_metered_btn,
|
||||
button_item("Hidden Network", "CONNECT", callback=self._connect_to_hidden_network),
|
||||
button_item(tr("Hidden Network"), tr("CONNECT"), callback=self._connect_to_hidden_network),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -198,7 +199,7 @@ class AdvancedNetworkSettings(Widget):
|
||||
|
||||
current_apn = self._params.get("GsmApn") or ""
|
||||
self._keyboard.reset(min_text_size=0)
|
||||
self._keyboard.set_title("Enter APN", "leave blank for automatic configuration")
|
||||
self._keyboard.set_title(tr("Enter APN"), tr("leave blank for automatic configuration"))
|
||||
self._keyboard.set_text(current_apn)
|
||||
gui_app.set_modal_overlay(self._keyboard, update_apn)
|
||||
|
||||
@@ -231,11 +232,11 @@ class AdvancedNetworkSettings(Widget):
|
||||
self._wifi_manager.connect_to_network(ssid, password, hidden=True)
|
||||
|
||||
self._keyboard.reset(min_text_size=0)
|
||||
self._keyboard.set_title("Enter password", f"for \"{ssid}\"")
|
||||
self._keyboard.set_title(tr("Enter password"), tr("for \"{}\"").format(ssid))
|
||||
gui_app.set_modal_overlay(self._keyboard, enter_password)
|
||||
|
||||
self._keyboard.reset(min_text_size=1)
|
||||
self._keyboard.set_title("Enter SSID", "")
|
||||
self._keyboard.set_title(tr("Enter SSID"), "")
|
||||
gui_app.set_modal_overlay(self._keyboard, connect_hidden)
|
||||
|
||||
def _edit_tethering_password(self):
|
||||
@@ -248,7 +249,7 @@ class AdvancedNetworkSettings(Widget):
|
||||
self._tethering_password_action.set_enabled(False)
|
||||
|
||||
self._keyboard.reset(min_text_size=MIN_PASSWORD_LENGTH)
|
||||
self._keyboard.set_title("Enter new tethering password", "")
|
||||
self._keyboard.set_title(tr("Enter new tethering password"), "")
|
||||
self._keyboard.set_text(self._wifi_manager.tethering_password)
|
||||
gui_app.set_modal_overlay(self._keyboard, update_password)
|
||||
|
||||
@@ -281,7 +282,7 @@ class WifiManagerUI(Widget):
|
||||
self._networks: list[Network] = []
|
||||
self._networks_buttons: dict[str, Button] = {}
|
||||
self._forget_networks_buttons: dict[str, Button] = {}
|
||||
self._confirm_dialog = ConfirmDialog("", "Forget", "Cancel")
|
||||
self._confirm_dialog = ConfirmDialog("", tr("Forget"), tr("Cancel"))
|
||||
|
||||
self._wifi_manager.set_callbacks(need_auth=self._on_need_auth,
|
||||
activated=self._on_activated,
|
||||
@@ -305,15 +306,15 @@ class WifiManagerUI(Widget):
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if not self._networks:
|
||||
gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER)
|
||||
gui_label(rect, tr("Scanning Wi-Fi networks..."), 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER)
|
||||
return
|
||||
|
||||
if self.state == UIState.NEEDS_AUTH and self._state_network:
|
||||
self.keyboard.set_title("Wrong password" if self._password_retry else "Enter password", f"for {self._state_network.ssid}")
|
||||
self.keyboard.set_title(tr("Wrong password") if self._password_retry else tr("Enter password"), tr("for \"{}\"").format(self._state_network.ssid))
|
||||
self.keyboard.reset(min_text_size=MIN_PASSWORD_LENGTH)
|
||||
gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(cast(Network, self._state_network), result))
|
||||
elif self.state == UIState.SHOW_FORGET_CONFIRM and self._state_network:
|
||||
self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{self._state_network.ssid}"?')
|
||||
self._confirm_dialog.set_text(tr("Forget Wi-Fi Network \"{}\"?").format(self._state_network.ssid))
|
||||
self._confirm_dialog.reset()
|
||||
gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(self._state_network, result))
|
||||
else:
|
||||
@@ -363,11 +364,11 @@ class WifiManagerUI(Widget):
|
||||
if self.state == UIState.CONNECTING and self._state_network:
|
||||
if self._state_network.ssid == network.ssid:
|
||||
self._networks_buttons[network.ssid].set_enabled(False)
|
||||
status_text = "CONNECTING..."
|
||||
status_text = tr("CONNECTING...")
|
||||
elif self.state == UIState.FORGETTING and self._state_network:
|
||||
if self._state_network.ssid == network.ssid:
|
||||
self._networks_buttons[network.ssid].set_enabled(False)
|
||||
status_text = "FORGETTING..."
|
||||
status_text = tr("FORGETTING...")
|
||||
elif network.security_type == SecurityType.UNSUPPORTED:
|
||||
self._networks_buttons[network.ssid].set_enabled(False)
|
||||
else:
|
||||
@@ -445,7 +446,7 @@ class WifiManagerUI(Widget):
|
||||
self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55,
|
||||
text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT, button_style=ButtonStyle.TRANSPARENT_WHITE_TEXT)
|
||||
self._networks_buttons[n.ssid].set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid())
|
||||
self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI,
|
||||
self._forget_networks_buttons[n.ssid] = Button(tr("Forget"), partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI,
|
||||
font_size=45)
|
||||
self._forget_networks_buttons[n.ssid].set_touch_valid_callback(lambda: self.scroll_panel.is_touch_valid())
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pyray as rl
|
||||
from openpilot.system.ui.lib.application import FontWeight
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.button import Button, ButtonStyle
|
||||
from openpilot.system.ui.widgets.label import gui_label
|
||||
@@ -30,8 +31,8 @@ class MultiOptionDialog(Widget):
|
||||
text_padding=50) for option in options]
|
||||
self.scroller = Scroller(self.option_buttons, spacing=LIST_ITEM_SPACING)
|
||||
|
||||
self.cancel_button = Button("Cancel", click_callback=lambda: self._set_result(DialogResult.CANCEL))
|
||||
self.select_button = Button("Select", click_callback=lambda: self._set_result(DialogResult.CONFIRM), button_style=ButtonStyle.PRIMARY)
|
||||
self.cancel_button = Button(tr("Cancel"), click_callback=lambda: self._set_result(DialogResult.CANCEL))
|
||||
self.select_button = Button(tr("Select"), click_callback=lambda: self._set_result(DialogResult.CONFIRM), button_style=ButtonStyle.PRIMARY)
|
||||
|
||||
def _set_result(self, result: DialogResult):
|
||||
self._result = result
|
||||
|
||||
Reference in New Issue
Block a user