openpilot/selfdrive/ui/layouts/settings/settings.py
mouxangithub 2270c6d7f1 feat(monitoring): 添加驾驶员分心检测灵敏度设置功能
新增 `DistractionDetectionLevel` 参数以控制驾驶员分心检测的灵敏度等级,并在 `dmonitoringd.py` 和 `helpers.py` 中实现不同等级对应的时间阈值配置。同时更新了相关逻辑以支持动态调整该参数。

fix(toyota): 支持 Toyota Wildlander PHEV 车型接入与控制

增加对 Toyota Wildlander PHEV 的指纹识别、车辆规格定义及接口适配,确保其在 TSS2 平台下的正常运行,并修正部分雷达ACC判断条件。

feat(ui): 优化 Dragonpilot 设置界面选项显示语言一致性

将 Dragonpilot 设置页面中的多个下拉选项文本进行国际化处理,统一使用翻译函数包裹,提升多语言兼容性。

chore(config): 更新 launch 脚本 API 地址并切换 shell 解释器

修改 `launch_openpilot.sh` 使用 `/usr/bin/bash` 作为解释器,并设置自定义 API 与 Athena 服务地址。

refactor(key): 实现 ECU 秘钥提取脚本并写入参数存储

创建 `key.py` 脚本用于通过 UDS 协议从 ECU 提取 SecOC 密钥,并将其保存至系统参数中供后续使用。

docs(vscode): 移除不再使用的终端配置项

清理 `.vscode/settings.json` 文件中过时的 terminal 配置内容。

feat(fonts): 新增中文字体资源文件

添加 `china.ttf` 字体文件以增强 UI 在中文环境下的渲染效果。

build(payload): 添加二进制负载文件

引入新的二进制 payload 文件用于辅助密钥提取流程。
2025-11-14 16:00:25 +08:00

170 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pyray as rl
from dataclasses import dataclass
from enum import IntEnum
from collections.abc import Callable
from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout
from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout
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, tr_noop
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
from openpilot.system.ui.widgets.network import NetworkUI
from dragonpilot.selfdrive.ui.layouts.settings.dragonpilot import DragonpilotLayout
# Settings close button
SETTINGS_CLOSE_TEXT = "×"
SETTINGS_CLOSE_TEXT_Y_OFFSET = 8 # The '×' character isn't quite vertically centered in the font so we need to offset it a bit to fully center it
# Constants
SIDEBAR_WIDTH = 500
CLOSE_BTN_SIZE = 200
NAV_BTN_HEIGHT = 110
PANEL_MARGIN = 50
# Colors
SIDEBAR_COLOR = rl.BLACK
PANEL_COLOR = rl.Color(41, 41, 41, 255)
CLOSE_BTN_COLOR = rl.Color(41, 41, 41, 255)
CLOSE_BTN_PRESSED = rl.Color(59, 59, 59, 255)
TEXT_NORMAL = rl.Color(128, 128, 128, 255)
TEXT_SELECTED = rl.WHITE
class PanelType(IntEnum):
DEVICE = 0
NETWORK = 1
TOGGLES = 2
SOFTWARE = 3
FIREHOSE = 4
DEVELOPER = 5
DRAGONPILOT = 6
@dataclass
class PanelInfo:
name: str
instance: Widget
button_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0)
class SettingsLayout(Widget):
def __init__(self):
super().__init__()
self._current_panel = PanelType.DEVICE
# Panel configuration
wifi_manager = WifiManager()
wifi_manager.set_active(False)
self._panels = {
PanelType.DEVICE: PanelInfo(tr_noop("Device"), DeviceLayout()),
PanelType.NETWORK: PanelInfo(tr_noop("Network"), NetworkUI(wifi_manager)),
PanelType.TOGGLES: PanelInfo(tr_noop("Toggles"), TogglesLayout()),
PanelType.SOFTWARE: PanelInfo(tr_noop("Software"), SoftwareLayout()),
PanelType.DEVELOPER: PanelInfo(tr_noop("Developer"), DeveloperLayout()),
PanelType.DRAGONPILOT: PanelInfo(tr_noop("Dp"), DragonpilotLayout()),
}
self._font_medium = gui_app.font(FontWeight.MEDIUM)
# Callbacks
self._close_callback: Callable | None = None
def set_callbacks(self, on_close: Callable):
self._close_callback = on_close
def _render(self, rect: rl.Rectangle):
# Calculate layout
sidebar_rect = rl.Rectangle(rect.x, rect.y, SIDEBAR_WIDTH, rect.height)
panel_rect = rl.Rectangle(rect.x + SIDEBAR_WIDTH, rect.y, rect.width - SIDEBAR_WIDTH, rect.height)
# Draw components
self._draw_sidebar(sidebar_rect)
self._draw_current_panel(panel_rect)
def _draw_sidebar(self, rect: rl.Rectangle):
rl.draw_rectangle_rec(rect, SIDEBAR_COLOR)
# Close button
close_btn_rect = rl.Rectangle(
rect.x + (rect.width - CLOSE_BTN_SIZE) / 2, rect.y + 60, CLOSE_BTN_SIZE, CLOSE_BTN_SIZE
)
pressed = (rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and
rl.check_collision_point_rec(rl.get_mouse_position(), close_btn_rect))
close_color = CLOSE_BTN_PRESSED if pressed else CLOSE_BTN_COLOR
rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color)
close_text_size = measure_text_cached(self._font_medium, SETTINGS_CLOSE_TEXT, 140)
close_text_pos = rl.Vector2(
close_btn_rect.x + (close_btn_rect.width - close_text_size.x) / 2,
close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2 - SETTINGS_CLOSE_TEXT_Y_OFFSET,
)
rl.draw_text_ex(self._font_medium, SETTINGS_CLOSE_TEXT, close_text_pos, 140, 0, TEXT_SELECTED)
# Store close button rect for click detection
self._close_btn_rect = close_btn_rect
# Navigation buttons
y = rect.y + 300
for panel_type, panel_info in self._panels.items():
button_rect = rl.Rectangle(rect.x + 50, y, rect.width - 150, NAV_BTN_HEIGHT)
# Button styling
is_selected = panel_type == self._current_panel
text_color = TEXT_SELECTED if is_selected else TEXT_NORMAL
# Draw button text (right-aligned)
panel_name = tr(panel_info.name)
text_size = measure_text_cached(self._font_medium, panel_name, 65)
text_pos = rl.Vector2(
button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2
)
rl.draw_text_ex(self._font_medium, panel_name, text_pos, 65, 0, text_color)
# Store button rect for click detection
panel_info.button_rect = button_rect
y += NAV_BTN_HEIGHT
def _draw_current_panel(self, rect: rl.Rectangle):
rl.draw_rectangle_rounded(
rl.Rectangle(rect.x + 10, rect.y + 10, rect.width - 20, rect.height - 20), 0.04, 30, PANEL_COLOR
)
content_rect = rl.Rectangle(rect.x + PANEL_MARGIN, rect.y + 25, rect.width - (PANEL_MARGIN * 2), rect.height - 50)
# rl.draw_rectangle_rounded(content_rect, 0.03, 30, PANEL_COLOR)
panel = self._panels[self._current_panel]
if panel.instance:
panel.instance.render(content_rect)
def _handle_mouse_release(self, mouse_pos: MousePos) -> bool:
# Check close button
if rl.check_collision_point_rec(mouse_pos, self._close_btn_rect):
if self._close_callback:
self._close_callback()
return True
# Check navigation buttons
for panel_type, panel_info in self._panels.items():
if rl.check_collision_point_rec(mouse_pos, panel_info.button_rect):
self.set_current_panel(panel_type)
return True
return False
def set_current_panel(self, panel_type: PanelType):
if panel_type != self._current_panel:
self._panels[self._current_panel].instance.hide_event()
self._current_panel = panel_type
self._panels[self._current_panel].instance.show_event()
def show_event(self):
super().show_event()
self._panels[self._current_panel].instance.show_event()
def hide_event(self):
super().hide_event()
self._panels[self._current_panel].instance.hide_event()