Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc

# Conflicts:
#	common/params_keys.h
#	opendbc_repo
This commit is contained in:
Jason Wen
2025-06-06 18:46:00 -04:00
22 changed files with 345 additions and 18 deletions
+9 -1
View File
@@ -1,3 +1,11 @@
* @sunnypilot/dev-internal
/.github/ @devtekve @sunnyhaibin
/release/ci/ @devtekve @sunnyhaibin
/release/ci/ @devtekve @sunnyhaibin
/tinygrad_repo @devtekve @Discountchubbs
/tinygrad/ @devtekve @Discountchubbs
/selfdrive/controls/lib/longitudinal_planner.py @devtekve @Discountchubbs
/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @devtekve @Discountchubbs
/selfdrive/modeld/ @devtekve @Discountchubbs
/sunnypilot/model* @devtekve @Discountchubbs
/sunnypilot/sunnylink/ @devtekve
/system/athena/ @devtekve
+6 -1
View File
@@ -71,7 +71,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"LastPowerDropDetected", CLEAR_ON_MANAGER_START},
{"LastUpdateException", CLEAR_ON_MANAGER_START},
{"LastUpdateTime", PERSISTENT},
{"LiveDelay", PERSISTENT},
{"LiveDelay", PERSISTENT | BACKUP},
{"LiveParameters", PERSISTENT},
{"LiveParametersV2", PERSISTENT},
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
@@ -133,6 +133,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"CarPlatformBundle", PERSISTENT},
{"EnableGithubRunner", PERSISTENT | BACKUP},
{"MaxTimeOffroad", PERSISTENT | BACKUP},
{"Brightness", PERSISTENT | BACKUP},
{"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION},
{"OffroadMode", CLEAR_ON_MANAGER_START},
{"OffroadMode_Status", CLEAR_ON_MANAGER_START},
@@ -170,6 +171,10 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"HyundaiLongitudinalTuning", PERSISTENT},
{"DynamicExperimentalControl", PERSISTENT},
{"BlindSpot", PERSISTENT | BACKUP},
// model panel params
{"LagdToggle", PERSISTENT | BACKUP},
// mapd
{"MapdVersion", PERSISTENT},
+1
View File
@@ -24,6 +24,7 @@ qt_src = [
"sunnypilot/qt/offroad/settings/lateral_panel.cc",
"sunnypilot/qt/offroad/settings/longitudinal_panel.cc",
"sunnypilot/qt/offroad/settings/max_time_offroad.cc",
"sunnypilot/qt/offroad/settings/brightness.cc",
"sunnypilot/qt/offroad/settings/models_panel.cc",
"sunnypilot/qt/offroad/settings/osm_panel.cc",
"sunnypilot/qt/offroad/settings/settings.cc",
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.h"
// Map of Brightness Options
const QMap<QString, QString> Brightness::brightness_options = {
{"0", "1"}, // Auto (Dark)
{"1", "0"}, // Auto
{"2", "10"},
{"3", "20"},
{"4", "30"},
{"5", "40"},
{"6", "50"},
{"7", "60"},
{"8", "70"},
{"9", "80"},
{"10", "90"},
{"11", "100"}
};
Brightness::Brightness() : OptionControlSP(
"Brightness",
tr("Brightness"),
tr("Overrides the brightness of the device."),
"../assets/offroad/icon_blank.png",
{0, 11}, 1, true, &brightness_options) {
refresh();
}
void Brightness::refresh() {
const int brightness = QString::fromStdString(params.get("Brightness")).toInt();
QString label;
if (brightness == 1) {
label = tr("Auto (Dark)");
} else if (brightness == 0) {
label = tr("Auto");
} else {
const int value = brightness;
label = QString("%1").arg(value);
}
setLabel(label);
}
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class Brightness : public OptionControlSP {
Q_OBJECT
public:
static const QMap<QString, QString> brightness_options;
Brightness();
void refresh();
private:
Params params;
};
@@ -80,6 +80,11 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) {
connect(maxTimeOffroad, &OptionControlSP::updateLabels, maxTimeOffroad, &MaxTimeOffroad::refresh);
addItem(maxTimeOffroad);
// Brightness
brightness = new Brightness();
connect(brightness, &OptionControlSP::updateLabels, brightness, &Brightness::refresh);
addItem(brightness);
addItem(device_grid_layout);
// offroad mode and power buttons
@@ -8,6 +8,7 @@
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/max_time_offroad.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
@@ -25,6 +26,7 @@ private:
std::map<QString, PushButtonSP*> buttons;
PushButtonSP *offroadBtn;
MaxTimeOffroad *maxTimeOffroad;
Brightness *brightness;
const QString alwaysOffroadStyle = R"(
PushButtonSP {
@@ -24,13 +24,30 @@ ModelsPanel::ModelsPanel(QWidget *parent) : QWidget(parent) {
currentModelLblBtn = new ButtonControlSP(tr("Current Model"), tr("SELECT"), current_model);
currentModelLblBtn->setValue(current_model);
connect(currentModelLblBtn, &ButtonControlSP::clicked, this, &ModelsPanel::handleCurrentModelLblBtnClicked);
connect(currentModelLblBtn, &ButtonControlSP::clicked, [=]() {
InputDialog d(tr("Search Model"), this, tr("Enter search keywords, or leave blank to list all models."), false);
d.setMinLength(0);
const int ret = d.exec();
if (ret) {
handleCurrentModelLblBtnClicked(d.text());
}
});
connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
is_onroad = !offroad;
updateLabels();
});
connect(uiStateSP(), &UIStateSP::uiUpdate, this, &ModelsPanel::updateLabels);
list->addItem(currentModelLblBtn);
// LiveDelay toggle
list->addItem(new ParamControlSP("LagdToggle",
tr("Live Learning Steer Delay"),
tr("Enable this for the car to learn and adapt its steering response time. "
"Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience."),
"../assets/offroad/icon_shell.png"));
}
@@ -127,7 +144,7 @@ void ModelsPanel::updateModelManagerState() {
* @brief Handles the model bundle selection button click
* Displays available bundles, allows selection, and initiates download
*/
void ModelsPanel::handleCurrentModelLblBtnClicked() {
void ModelsPanel::handleCurrentModelLblBtnClicked(const QString &query) {
currentModelLblBtn->setEnabled(false);
currentModelLblBtn->setValue(tr("Fetching models..."));
@@ -135,13 +152,14 @@ void ModelsPanel::handleCurrentModelLblBtnClicked() {
QMap<uint32_t, QString> index_to_bundle;
const auto bundles = model_manager.getAvailableBundles();
for (const auto &bundle: bundles) {
index_to_bundle.insert(bundle.getIndex(), QString::fromStdString(bundle.getDisplayName()));
std::string bundleStr = std::string(bundle.getDisplayName()) + " $SNAME$ " + std::string(bundle.getInternalName());
index_to_bundle.insert(bundle.getIndex(), QString::fromStdString(bundleStr));
}
// Sort bundles by index in descending order
QStringList bundleNames;
// Add "Default" as the first option
bundleNames.append(tr("Use Default"));
bundleNames.append(DEFAULT_MODEL);
auto indices = index_to_bundle.keys();
std::sort(indices.begin(), indices.end(), std::greater<uint32_t>());
@@ -149,17 +167,31 @@ void ModelsPanel::handleCurrentModelLblBtnClicked() {
bundleNames.append(index_to_bundle[index]);
}
QStringList filteredBundleNames = searchFromList(query, bundleNames);
currentModelLblBtn->setValue(GetActiveModelName());
if (filteredBundleNames.isEmpty()) {
ConfirmationDialog::alert(tr("No model found for keywords: %1").arg(query), this);
return;
}
for (const QString &bundleName: filteredBundleNames) {
int index = bundleName.indexOf("$SNAME$");
if (index != -1) {
filteredBundleNames.replace(filteredBundleNames.indexOf(bundleName), bundleName.left(index).trimmed());
}
}
const QString selectedBundleName = MultiOptionDialog::getSelection(
tr("Select a Model"), bundleNames, GetActiveModelName(), this);
tr("Select a Model"), filteredBundleNames, GetActiveModelName(), this);
if (selectedBundleName.isEmpty() || !canContinueOnMeteredDialog()) {
return;
}
// Handle "Stock" selection differently
if (selectedBundleName == tr("Use Default")) {
if (selectedBundleName == DEFAULT_MODEL) {
params.remove("ModelManager_ActiveBundle");
currentModelLblBtn->setValue(tr("Default"));
showResetParamsDialog();
@@ -7,6 +7,7 @@
#pragma once
#include "selfdrive/ui/sunnypilot/qt/util.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
class ModelsPanel : public QWidget {
@@ -30,7 +31,7 @@ private:
// UI update related methods
void updateLabels();
void handleCurrentModelLblBtnClicked();
void handleCurrentModelLblBtnClicked(const QString &query);
void handleBundleDownloadProgress();
void showResetParamsDialog();
cereal::ModelManagerSP::Reader model_manager;
@@ -8,5 +8,63 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h"
VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
param_watcher = new ParamWatcher(this);
connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
paramsRefresh();
});
main_layout = new QStackedLayout(this);
ListWidgetSP *list = new ListWidgetSP(this, false);
sunnypilotScreen = new QWidget(this);
QVBoxLayout* vlayout = new QVBoxLayout(sunnypilotScreen);
vlayout->setContentsMargins(50, 20, 50, 20);
std::vector<std::tuple<QString, QString, QString, QString, bool> > toggle_defs{
{
"BlindSpot",
tr("Show Blind Spot Warnings"),
tr("Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported."),
"../assets/offroad/icon_monitoring.png",
false,
},
};
for (auto &[param, title, desc, icon, needs_restart] : toggle_defs) {
auto toggle = new ParamControlSP(param, title, desc, icon, this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
if (needs_restart && !locked) {
toggle->setDescription(toggle->getDescription() + tr(" Changing this setting will restart openpilot if the car is powered on."));
QObject::connect(uiState(), &UIState::engagedChanged, [toggle](bool engaged) {
toggle->setEnabled(!engaged);
});
QObject::connect(toggle, &ParamControlSP::toggleFlipped, [=](bool state) {
params.putBool("OnroadCycleRequested", true);
});
}
list->addItem(toggle);
toggles[param.toStdString()] = toggle;
param_watcher->addParam(param);
}
sunnypilotScroller = new ScrollViewSP(list, this);
vlayout->addWidget(sunnypilotScroller);
main_layout->addWidget(sunnypilotScreen);
}
void VisualsPanel::paramsRefresh() {
if (!isVisible()) {
return;
}
for (auto toggle : toggles) {
toggle.second->refresh();
}
}
@@ -8,6 +8,9 @@
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
class ScrollViewSP;
class VisualsPanel : public QWidget {
Q_OBJECT
@@ -15,4 +18,13 @@ class VisualsPanel : public QWidget {
public:
explicit VisualsPanel(QWidget *parent = nullptr);
void paramsRefresh();
protected:
QStackedLayout* main_layout = nullptr;
QWidget* sunnypilotScreen = nullptr;
ScrollViewSP *sunnypilotScroller = nullptr;
Params params;
std::map<std::string, ParamControlSP*> toggles;
ParamWatcher * param_watcher;
};
@@ -6,3 +6,47 @@
*/
#include "selfdrive/ui/sunnypilot/qt/onroad/model.h"
void ModelRendererSP::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) {
ModelRenderer::update_model(model, lead);
const auto &model_position = model.getPosition();
const auto &lane_lines = model.getLaneLines();
float max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
int max_idx = get_path_length_idx(lane_lines[0], max_distance);
// update blindspot vertices
float max_distance_barrier = 100;
int max_idx_barrier = std::min(max_idx, get_path_length_idx(lane_lines[0], max_distance_barrier));
mapLineToPolygon(model.getLaneLines()[1], 0.2, -0.05, &left_blindspot_vertices, max_idx_barrier);
mapLineToPolygon(model.getLaneLines()[2], 0.2, -0.05, &right_blindspot_vertices, max_idx_barrier);
}
void ModelRendererSP::drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, const QRect &surface_rect) {
auto *s = uiState();
auto &sm = *(s->sm);
bool blindspot = Params().getBool("BlindSpot");
if (blindspot) {
bool left_blindspot = sm["carState"].getCarState().getLeftBlindspot();
bool right_blindspot = sm["carState"].getCarState().getRightBlindspot();
//painter.setBrush(QColor::fromRgbF(1.0, 0.0, 0.0, 0.4)); // Red with alpha for blind spot
if (left_blindspot && !left_blindspot_vertices.isEmpty()) {
QLinearGradient gradient(0, 0, surface_rect.width(), 0); // Horizontal gradient from left to right
gradient.setColorAt(0.0, QColor(255, 165, 0, 102)); // Orange with alpha
gradient.setColorAt(1.0, QColor(255, 255, 0, 102)); // Yellow with alpha
painter.setBrush(gradient);
painter.drawPolygon(left_blindspot_vertices);
}
if (right_blindspot && !right_blindspot_vertices.isEmpty()) {
QLinearGradient gradient(surface_rect.width(), 0, 0, 0); // Horizontal gradient from right to left
gradient.setColorAt(0.0, QColor(255, 165, 0, 102)); // Orange with alpha
gradient.setColorAt(1.0, QColor(255, 255, 0, 102)); // Yellow with alpha
painter.setBrush(gradient);
painter.drawPolygon(right_blindspot_vertices);
}
}
ModelRenderer::drawPath(painter, model, surface_rect.height());
}
@@ -12,4 +12,11 @@
class ModelRendererSP : public ModelRenderer {
public:
ModelRendererSP() = default;
private:
void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) override;
void drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, const QRect &rect) override;
QPolygonF left_blindspot_vertices;
QPolygonF right_blindspot_vertices;
};
+19 -5
View File
@@ -80,8 +80,12 @@ void UIState::updateStatus() {
status = STATUS_OVERRIDE;
} else {
if (mads.getAvailable()) {
if (mads.getEnabled()) {
status = ss.getEnabled() ? STATUS_ENGAGED : STATUS_LAT_ONLY;
if (mads.getEnabled() && ss.getEnabled()) {
status = STATUS_ENGAGED;
} else if (mads.getEnabled()) {
status = STATUS_LAT_ONLY;
} else if (ss.getEnabled()) {
status = STATUS_LONG_ONLY;
} else {
status = STATUS_DISENGAGED;
}
@@ -167,6 +171,8 @@ void Device::resetInteractiveTimeout(int timeout) {
}
void Device::updateBrightness(const UIState &s) {
int brightness;
int brightness_override = QString::fromStdString(Params().get("Brightness")).toInt();
float clipped_brightness = offroad_brightness;
if (s.scene.started && s.scene.light_sensor >= 0) {
clipped_brightness = s.scene.light_sensor;
@@ -178,11 +184,19 @@ void Device::updateBrightness(const UIState &s) {
clipped_brightness = std::pow((clipped_brightness + 16.0) / 116.0, 3.0);
}
// Scale back to 10% to 100%
clipped_brightness = std::clamp(100.0f * clipped_brightness, 10.0f, 100.0f);
if (brightness_override == 1) {
clipped_brightness = std::clamp(100.0f * clipped_brightness, 1.0f, 100.0f); // Scale back to 1% to 100%
} else if (brightness_override == 0) {
clipped_brightness = std::clamp(100.0f * clipped_brightness, 10.0f, 100.0f); // Scale back to 10% to 100%
}
}
if (brightness_override == 0 || brightness_override == 1) {
brightness = brightness_filter.update(clipped_brightness);
} else {
brightness = brightness_override;
}
int brightness = brightness_filter.update(clipped_brightness);
if (!awake) {
brightness = 0;
}
+5 -1
View File
@@ -25,6 +25,7 @@ from openpilot.sunnypilot.modeld.parse_model_outputs import Parser
from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.sunnypilot.modeld.constants import ModelConstants, Plan
from openpilot.sunnypilot.models.helpers import get_active_bundle, get_model_path, load_metadata, prepare_inputs, load_meta_constants
from openpilot.sunnypilot.models.modeld_lagd import ModeldLagd
from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext
@@ -201,8 +202,9 @@ def main(demo=False):
cloudlog.info("modeld got CarParams: %s", CP.brand)
modeld_lagd = ModeldLagd()
# Enable lagd support for sunnypilot modeld
steer_delay = sm["liveDelay"].lateralDelay + model.LAT_SMOOTH_SECONDS
long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS
prev_action = log.ModelDataV2.Action()
@@ -246,6 +248,8 @@ def main(demo=False):
v_ego = sm["carState"].vEgo
is_rhd = sm["driverMonitoringState"].isRHD
frame_id = sm["roadCameraState"].frameId
steer_delay = modeld_lagd.lagd_main(CP, sm, model)
if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']:
device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32)
dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))]
+5 -1
View File
@@ -23,6 +23,7 @@ from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFr
from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants
from openpilot.sunnypilot.models.helpers import get_active_bundle
from openpilot.sunnypilot.models.modeld_lagd import ModeldLagd
from openpilot.sunnypilot.models.runners.helpers import get_model_runner
PROCESS_NAME = "selfdrive.modeld.modeld"
@@ -238,6 +239,9 @@ def main(demo=False):
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)
cloudlog.info("modeld got CarParams: %s", CP.brand)
modeld_lagd = ModeldLagd()
# TODO Move smooth seconds to action function
long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS
prev_action = log.ModelDataV2.Action()
@@ -282,7 +286,7 @@ def main(demo=False):
is_rhd = sm["driverMonitoringState"].isRHD
frame_id = sm["roadCameraState"].frameId
v_ego = max(sm["carState"].vEgo, 0.)
steer_delay = sm["liveDelay"].lateralDelay + model.LAT_SMOOTH_SECONDS
steer_delay = modeld_lagd.lagd_main(CP, sm, model)
if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']:
device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32)
dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))]
+26
View File
@@ -0,0 +1,26 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
class ModeldLagd:
def __init__(self):
self.params = Params()
def lagd_main(self, CP, sm, model):
if self.params.get_bool("LagdToggle"):
lateral_delay = sm["liveDelay"].lateralDelay
lat_smooth = model.LAT_SMOOTH_SECONDS
result = lateral_delay + lat_smooth
cloudlog.debug(f"LAGD USING LIVE DELAY: {lateral_delay:.3f} + {lat_smooth:.3f} = {result:.3f}")
return result
steer_actuator_delay = CP.steerActuatorDelay
lat_smooth = model.LAT_SMOOTH_SECONDS
result = (steer_actuator_delay + 0.2) + lat_smooth
cloudlog.debug(f"LAGD USING STEER ACTUATOR: {steer_actuator_delay:.3f} + 0.2 + {lat_smooth:.3f} = {result:.3f}")
return result
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9dbacb55581a5ea8cbcd5cea0560ec21d96ac7185463a40fff0f81bebf044ffa
size 23187
+3
View File
@@ -46,16 +46,19 @@ def manager_init() -> None:
sunnypilot_default_params: list[tuple[str, str | bytes]] = [
("AutoLaneChangeTimer", "0"),
("AutoLaneChangeBsmDelay", "0"),
("BlindSpot", "0"),
("BlinkerMinLateralControlSpeed", "20"), # MPH or km/h
("BlinkerPauseLateralControl", "0"),
("DynamicExperimentalControl", "0"),
("HyundaiLongitudinalTuning", "0"),
("LagdToggle", "1"),
("Mads", "1"),
("MadsMainCruiseAllowed", "1"),
("MadsSteeringMode", "0"),
("MadsUnifiedEngagementMode", "1"),
("MapdVersion", f"{VERSION}"),
("MaxTimeOffroad", "1800"),
("Brightness", "0"),
("ModelManager_LastSyncTime", "0"),
("ModelManager_ModelsCache", ""),
("NeuralNetworkLateralControl", "0"),
+3 -1
View File
@@ -8,6 +8,8 @@ from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.lib.window import BaseWindow
from openpilot.system.ui.text import wrap_text
from openpilot.system.ui.sunnypilot.lib.application import gui_app_sp
# Constants
PROGRESS_BAR_WIDTH = 1000
PROGRESS_BAR_HEIGHT = 20
@@ -25,7 +27,7 @@ def clamp(value, min_value, max_value):
class SpinnerRenderer:
def __init__(self):
self._comma_texture = gui_app.texture("images/spinner_comma.png", TEXTURE_SIZE, TEXTURE_SIZE)
self._comma_texture = gui_app_sp.sp_texture("images/spinner_sunnypilot.png", TEXTURE_SIZE, TEXTURE_SIZE)
self._spinner_texture = gui_app.texture("images/spinner_track.png", TEXTURE_SIZE, TEXTURE_SIZE, alpha_premultiply=True)
self._rotation = 0.0
self._progress: int | None = None
+21
View File
@@ -0,0 +1,21 @@
from openpilot.system.ui.lib.application import GuiApplication
from importlib.resources import as_file, files
ASSETS_DIR_SP = files("openpilot.sunnypilot.selfdrive").joinpath("assets")
class GuiApplicationSP(GuiApplication):
def __init__(self, width: int, height: int):
super().__init__(width, height)
def sp_texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True):
cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}"
if cache_key in self._textures:
return self._textures[cache_key]
with as_file(ASSETS_DIR_SP.joinpath(asset_path)) as fspath:
texture_obj = self._load_texture_from_image(fspath.as_posix(), width, height, alpha_premultiply, keep_aspect_ratio)
self._textures[cache_key] = texture_obj
return texture_obj
gui_app_sp = GuiApplicationSP(2160, 1080)