Custom themes

Added toggles for the "Custom Themes" configuration. Colors, icons, sounds, and turn signal animations are all individually toggleable.
This commit is contained in:
FrogAi
2024-02-27 16:34:47 -07:00
parent 50cc95341d
commit 786e52880d
62 changed files with 276 additions and 25 deletions

Binary file not shown.

View File

@@ -778,7 +778,7 @@ class Controls:
good_speed = CS.vEgo > 5
max_torque = abs(self.last_actuators.steer) > 0.99
if undershooting and turning and good_speed and max_torque:
lac_log.active and self.events.add(EventName.steerSaturated)
lac_log.active and self.events.add(EventName.frogSteerSaturated if self.goat_scream else EventName.steerSaturated)
elif lac_log.saturated:
# TODO probably should not use dpath_points but curvature
dpath_points = model_v2.position.y
@@ -1018,6 +1018,11 @@ class Controls:
custom_alerts = self.params.get_bool("CustomAlerts")
custom_theme = self.params.get_bool("CustomTheme")
custom_sounds = self.params.get_int("CustomSounds") if custom_theme else 0
frog_sounds = custom_sounds == 1
self.goat_scream = frog_sounds and self.params.get_bool("GoatScream")
lateral_tune = self.params.get_bool("LateralTune")
longitudinal_tune = self.params.get_bool("LongitudinalTune")

View File

@@ -958,6 +958,13 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
},
# FrogPilot Events
EventName.frogSteerSaturated: {
ET.WARNING: Alert(
"Turn Exceeds Steering Limit",
"JESUS TAKE THE WHEEL!!",
AlertStatus.userPrompt, AlertSize.mid,
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.warningSoft, 2.),
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

View File

@@ -2,6 +2,12 @@
FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilotListWidget(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> visualToggles {
{"CustomTheme", "Custom Themes", "Enable the ability to use custom themes.", "../frogpilot/assets/wheel_images/frog.png"},
{"CustomColors", "Color Theme", "Switch out the standard openpilot color scheme with a custom color scheme.\n\nWant to submit your own color scheme? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomIcons", "Icon Pack", "Switch out the standard openpilot icons with a set of custom icons.\n\nWant to submit your own icon pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomSignals", "Turn Signals", "Add custom animations for your turn signals for a personal touch!\n\nWant to submit your own turn signal animation? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomSounds", "Sound Pack", "Switch out the standard openpilot sounds with a set of custom sounds.\n\nWant to submit your own sound pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"AlertVolumeControl", "Alert Volume Control", "Control the volume level for each individual sound in openpilot.", "../frogpilot/assets/toggle_icons/icon_mute.png"},
{"DisengageVolume", "Disengage Volume", "Related alerts:\n\nAdaptive Cruise Disabled\nParking Brake Engaged\nPedal Pressed\nSpeed too Low", ""},
{"EngageVolume", "Engage Volume", "Related alerts:\n\nNNFF Torque Controller loaded", ""},
@@ -57,6 +63,32 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilot
});
toggle = customAlertsToggle;
} else if (param == "CustomTheme") {
FrogPilotParamManageControl *customThemeToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(customThemeToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(customThemeKeys.find(key.c_str()) != customThemeKeys.end());
}
});
toggle = customThemeToggle;
} else if (customThemeKeys.find(param) != customThemeKeys.end()) {
std::vector<QString> themeOptions{tr("Stock"), tr("Frog"), tr("Tesla"), tr("Stalin")};
FrogPilotButtonParamControl *themeSelection = new FrogPilotButtonParamControl(param, title, desc, icon, themeOptions);
toggle = themeSelection;
if (param == "CustomSounds") {
QObject::connect(static_cast<FrogPilotButtonParamControl*>(toggle), &FrogPilotButtonParamControl::buttonClicked, [this](int id) {
if (id == 1) {
if (FrogPilotConfirmationDialog::yesorno("Do you want to enable the bonus 'Goat' sound effect?", this)) {
params.putBoolNonBlocking("GoatScream", true);
} else {
params.putBoolNonBlocking("GoatScream", false);
}
}
});
}
} else if (param == "CustomUI") {
FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {

View File

@@ -31,7 +31,7 @@ private:
std::set<QString> alertVolumeControlKeys = {"EngageVolume", "DisengageVolume", "RefuseVolume", "PromptVolume", "PromptDistractedVolume", "WarningSoftVolume", "WarningImmediateVolume"};
std::set<QString> customAlertsKeys = {};
std::set<QString> customOnroadUIKeys = {"AccelerationPath", "BlindSpotPath"};
std::set<QString> customThemeKeys = {};
std::set<QString> customThemeKeys = {"CustomColors", "CustomIcons", "CustomSignals", "CustomSounds"};
std::set<QString> modelUIKeys = {};
std::set<QString> qolKeys = {"DriveStats"};

View File

@@ -327,7 +327,7 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA);
is_metric = s.scene.is_metric;
speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE);
hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals && (turnSignalLeft || turnSignalRight));
status = s.status;
// update engageability/experimental mode button
@@ -489,13 +489,21 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
// lanelines
for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7)));
if (customColors != 0) {
painter.setBrush(std::get<3>(themeConfiguration[customColors]).begin()->second);
} else {
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7)));
}
painter.drawPolygon(scene.lane_line_vertices[i]);
}
// road edges
for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0)));
if (customColors != 0) {
painter.setBrush(std::get<3>(themeConfiguration[customColors]).begin()->second);
} else {
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0)));
}
painter.drawPolygon(scene.road_edge_vertices[i]);
}
@@ -504,8 +512,14 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
if (sm["controlsState"].getControlsState().getExperimentalMode() || scene.acceleration_path) {
// The first half of track_vertices are the points for the right side of the path
// and the indices match the positions of accel from uiPlan
const auto &acceleration = sm["uiPlan"].getUiPlan().getAccel();
const int max_len = std::min<int>(scene.track_vertices.length() / 2, acceleration.size());
const auto &acceleration_const = sm["uiPlan"].getUiPlan().getAccel();
const int max_len = std::min<int>(scene.track_vertices.length() / 2, acceleration_const.size());
// Copy of the acceleration vector
std::vector<float> acceleration;
for (int i = 0; i < acceleration_const.size(); i++) {
acceleration.push_back(acceleration_const[i]);
}
for (int i = 0; i < max_len; ++i) {
// Some points are out of frame
@@ -514,20 +528,33 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
// Flip so 0 is bottom of frame
float lin_grad_point = (height() - scene.track_vertices[i].y()) / height();
// speed up: 120, slow down: 0
float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0);
// FIXME: painter.drawPolygon can be slow if hue is not rounded
path_hue = int(path_hue * 100 + 0.5) / 100;
// If acceleration is between -0.2 and 0.2, resort to the theme color
if (std::abs(acceleration[i]) < 0.2 && (customColors != 0)) {
const auto &colorMap = std::get<3>(themeConfiguration[customColors]);
for (const auto &[position, brush] : colorMap) {
bg.setColorAt(position, brush.color());
}
} else {
// speed up: 120, slow down: 0
float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0);
// FIXME: painter.drawPolygon can be slow if hue is not rounded
path_hue = int(path_hue * 100 + 0.5) / 100;
float saturation = fmin(fabs(acceleration[i] * 1.5), 1);
float lightness = util::map_val(saturation, 0.0f, 1.0f, 0.95f, 0.62f); // lighter when grey
float alpha = util::map_val(lin_grad_point, 0.75f / 2.f, 0.75f, 0.4f, 0.0f); // matches previous alpha fade
bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha));
float saturation = fmin(fabs(acceleration[i] * 1.5), 1);
float lightness = util::map_val(saturation, 0.0f, 1.0f, 0.95f, 0.62f); // lighter when grey
float alpha = util::map_val(lin_grad_point, 0.75f / 2.f, 0.75f, 0.4f, 0.0f); // matches previous alpha fade
bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha));
// Skip a point, unless next is last
i += (i + 2) < max_len ? 1 : 0;
// Skip a point, unless next is last
i += (i + 2) < max_len ? 1 : 0;
}
}
} else if (customColors != 0) {
const auto &colorMap = std::get<3>(themeConfiguration[customColors]);
for (const auto &[position, brush] : colorMap) {
bg.setColorAt(position, brush.color());
}
} else {
bg.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 0.4));
bg.setColorAt(0.5, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.35));
@@ -601,8 +628,8 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s)
void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd) {
painter.save();
const float speedBuff = 10.;
const float leadBuff = 40.;
const float speedBuff = customColors ? 25. : 10.; // Make the center of the chevron appear sooner if a custom theme is active
const float leadBuff = customColors ? 100. : 40.; // Make the center of the chevron appear sooner if a custom theme is active
const float d_rel = lead_data.getDRel();
const float v_rel = lead_data.getVRel();
@@ -628,7 +655,11 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState
// chevron
QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}};
painter.setBrush(redColor(fillAlpha));
if (customColors != 0) {
painter.setBrush(std::get<3>(themeConfiguration[customColors]).begin()->second);
} else {
painter.setBrush(redColor(fillAlpha));
}
painter.drawPolygon(chevron, std::size(chevron));
painter.restore();
@@ -751,6 +782,25 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
bottom_layout->addWidget(map_settings_btn_bottom);
main_layout->addLayout(bottom_layout);
// Custom themes configuration
themeConfiguration = {
{1, {"frog_theme", 4, QColor(23, 134, 68, 242), {{0.0, QBrush(QColor::fromHslF(144 / 360., 0.71, 0.31, 0.9))},
{0.5, QBrush(QColor::fromHslF(144 / 360., 0.71, 0.31, 0.5))},
{1.0, QBrush(QColor::fromHslF(144 / 360., 0.71, 0.31, 0.1))}}}},
{2, {"tesla_theme", 4, QColor(0, 72, 255, 255), {{0.0, QBrush(QColor::fromHslF(223 / 360., 1.0, 0.5, 0.9))},
{0.5, QBrush(QColor::fromHslF(223 / 360., 1.0, 0.5, 0.5))},
{1.0, QBrush(QColor::fromHslF(223 / 360., 1.0, 0.5, 0.1))}}}},
{3, {"stalin_theme", 6, QColor(255, 0, 0, 255), {{0.0, QBrush(QColor::fromHslF(0 / 360., 1.0, 0.5, 0.9))},
{0.5, QBrush(QColor::fromHslF(0 / 360., 1.0, 0.5, 0.5))},
{1.0, QBrush(QColor::fromHslF(0 / 360., 1.0, 0.5, 0.1))}}}}
};
// Initialize the timer for the turn signal animation
animationTimer = new QTimer(this);
connect(animationTimer, &QTimer::timeout, this, [this] {
animationFrameIndex = (animationFrameIndex + 1) % totalFrames;
});
}
void AnnotatedCameraWidget::updateFrogPilotWidgets(QPainter &p) {
@@ -767,17 +817,59 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets(QPainter &p) {
conditionalSpeedLead = scene.conditional_speed_lead;
conditionalStatus = scene.conditional_status;
customColors = scene.custom_colors;
experimentalMode = scene.experimental_mode;
turnSignalLeft = scene.turn_signal_left;
turnSignalRight = scene.turn_signal_right;
if (alwaysOnLateral || conditionalExperimental) {
drawStatusBar(p);
}
if (customSignals && (turnSignalLeft || turnSignalRight)) {
if (!animationTimer->isActive()) {
animationTimer->start(totalFrames * 11); // 440 milliseconds per loop; syncs up perfectly with my 2019 Lexus ES 350 turn signal clicks
}
drawTurnSignals(p);
} else if (animationTimer->isActive()) {
animationTimer->stop();
}
map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled());
if (map_settings_btn_bottom->isEnabled()) {
map_settings_btn_bottom->setVisible(!hideBottomIcons);
bottom_layout->setAlignment(map_settings_btn_bottom, rightHandDM ? Qt::AlignLeft : Qt::AlignRight);
}
// Update the turn signal animation images upon toggle change
if (customSignals != scene.custom_signals) {
customSignals = scene.custom_signals;
QString theme_path = QString("../frogpilot/assets/custom_themes/%1/images").arg(themeConfiguration.find(customSignals) != themeConfiguration.end() ?
std::get<0>(themeConfiguration[customSignals]) : "");
QStringList imagePaths;
int availableImages = std::get<1>(themeConfiguration[customSignals]);
for (int i = 1; i <= totalFrames; ++i) {
int imageIndex = ((i - 1) % availableImages) + 1;
QString imagePath = theme_path + QString("/turn_signal_%1.png").arg(imageIndex);
imagePaths.push_back(imagePath);
}
signalImgVector.clear();
signalImgVector.reserve(2 * imagePaths.size()); // Reserve space for both regular and flipped images
for (const QString &imagePath : imagePaths) {
QPixmap pixmap(imagePath);
signalImgVector.push_back(pixmap); // Regular image
signalImgVector.push_back(pixmap.transformed(QTransform().scale(-1, 1))); // Flipped image
}
signalImgVector.push_back(QPixmap(theme_path + "/turn_signal_1_red.png")); // Regular blindspot image
signalImgVector.push_back(QPixmap(theme_path + "/turn_signal_1_red.png").transformed(QTransform().scale(-1, 1))); // Flipped blindspot image
}
}
void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
@@ -854,3 +946,35 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
p.restore();
}
void AnnotatedCameraWidget::drawTurnSignals(QPainter &p) {
// Declare the turn signal size
constexpr int signalHeight = 480;
constexpr int signalWidth = 360;
// Enable Antialiasing
p.setRenderHint(QPainter::Antialiasing);
// Calculate the vertical position for the turn signals
int baseYPosition = (height() - signalHeight) / 2 + (alwaysOnLateral || conditionalExperimental || roadNameUI ? 225 : 300);
// Calculate the x-coordinates for the turn signals
int leftSignalXPosition = 75 + width() - signalWidth - 300 * (blindSpotLeft ? 0 : animationFrameIndex);
int rightSignalXPosition = -75 + 300 * (blindSpotRight ? 0 : animationFrameIndex);
// Draw the turn signals
if (animationFrameIndex < signalImgVector.size()) {
auto drawSignal = [&](bool signalActivated, int xPosition, bool flip, bool blindspot) {
if (signalActivated) {
// Get the appropriate image from the signalImgVector
int uniqueImages = signalImgVector.size() / 4; // Each image has a regular, flipped, and two blindspot versions
int index = (blindspot ? 2 * uniqueImages : 2 * animationFrameIndex % totalFrames) + (flip ? 1 : 0);
QPixmap &signal = signalImgVector[index];
p.drawPixmap(xPosition, baseYPosition, signalWidth, signalHeight, signal);
}
};
// Display the animation based on which signal is activated
drawSignal(turnSignalLeft, leftSignalXPosition, false, blindSpotLeft);
drawSignal(turnSignalRight, rightSignalXPosition, true, blindSpotRight);
}
}

View File

@@ -110,6 +110,7 @@ private:
void updateFrogPilotWidgets(QPainter &p);
void drawStatusBar(QPainter &p);
void drawTurnSignals(QPainter &p);
// FrogPilot variables
Params paramsMemory{"/dev/shm/params"};
@@ -124,11 +125,23 @@ private:
bool blindSpotRight;
bool conditionalExperimental;
bool experimentalMode;
bool turnSignalLeft;
bool turnSignalRight;
int cameraView;
int conditionalSpeed;
int conditionalSpeedLead;
int conditionalStatus;
int customColors;
int customSignals;
int totalFrames = 8;
size_t animationFrameIndex;
std::unordered_map<int, std::tuple<QString, int, QColor, std::map<double, QBrush>>> themeConfiguration;
std::vector<QPixmap> signalImgVector;
QTimer *animationTimer;
protected:
void paintGL() override;

View File

@@ -40,6 +40,28 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"userFlag"});
// FrogPilot variables
themeConfiguration = {
{0, {"stock", {QColor(255, 255, 255)}}},
{1, {"frog_theme", {QColor(23, 134, 68)}}},
{2, {"tesla_theme", {QColor(0, 72, 255)}}},
{3, {"stalin_theme", {QColor(255, 0, 0)}}}
};
for (auto &[key, themeData] : themeConfiguration) {
QString &themeName = themeData.first;
QString base = themeName == "stock" ? "../assets/images" : QString("../frogpilot/assets/custom_themes/%1/images").arg(themeName);
std::vector<QString> paths = {base + "/button_home.png", base + "/button_flag.png", base + "/button_settings.png"};
home_imgs[key] = loadPixmap(paths[0], home_btn.size());
flag_imgs[key] = loadPixmap(paths[1], home_btn.size());
settings_imgs[key] = loadPixmap(paths[2], settings_btn.size(), Qt::IgnoreAspectRatio);
}
home_img = home_imgs[scene.custom_icons];
flag_img = flag_imgs[scene.custom_icons];
settings_img = settings_imgs[scene.custom_icons];
currentColors = themeConfiguration[scene.custom_colors].second;
}
void Sidebar::mousePressEvent(QMouseEvent *event) {
@@ -82,15 +104,23 @@ void Sidebar::updateState(const UIState &s) {
setProperty("netStrength", strength > 0 ? strength + 1 : 0);
// FrogPilot properties
home_img = home_imgs[scene.custom_icons];
flag_img = flag_imgs[scene.custom_icons];
settings_img = settings_imgs[scene.custom_icons];
currentColors = themeConfiguration[scene.custom_colors].second;
auto frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState();
QColor theme_color = currentColors[0];
ItemStatus connectStatus;
auto last_ping = deviceState.getLastAthenaPingTime();
if (last_ping == 0) {
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
} else {
connectStatus = nanos_since_boot() - last_ping < 80e9
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, good_color}
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, theme_color}
: ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color};
}
setProperty("connectStatus", QVariant::fromValue(connectStatus));
@@ -98,13 +128,13 @@ void Sidebar::updateState(const UIState &s) {
ItemStatus tempStatus = {{tr("TEMP"), tr("HIGH")}, danger_color};
auto ts = deviceState.getThermalStatus();
if (ts == cereal::DeviceState::ThermalStatus::GREEN) {
tempStatus = {{tr("TEMP"), tr("GOOD")}, good_color};
tempStatus = {{tr("TEMP"), tr("GOOD")}, theme_color};
} else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) {
tempStatus = {{tr("TEMP"), tr("OK")}, warning_color};
}
setProperty("tempStatus", QVariant::fromValue(tempStatus));
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, good_color};
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, theme_color};
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
} else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) {

View File

@@ -65,4 +65,10 @@ private:
// FrogPilot variables
Params params;
UIScene &scene;
std::unordered_map<int, std::pair<QString, std::vector<QColor>>> themeConfiguration;
std::unordered_map<int, QPixmap> flag_imgs;
std::unordered_map<int, QPixmap> home_imgs;
std::unordered_map<int, QPixmap> settings_imgs;
std::vector<QColor> currentColors;
};

View File

@@ -77,7 +77,7 @@ class Soundd:
for sound in sound_list:
filename, play_count, volume = sound_list[sound]
wavefile = wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r')
wavefile = wave.open(self.sound_directory + filename, 'r')
assert wavefile.getnchannels() == 1
assert wavefile.getsampwidth() == 2
@@ -186,6 +186,19 @@ class Soundd:
AudibleAlert.warningImmediate: self.params.get_int("WarningImmediateVolume")
}
custom_theme = self.params.get_bool("CustomTheme")
custom_sounds = self.params.get_int("CustomSounds") if custom_theme else 0
theme_configuration = {
0: "stock",
1: "frog_theme",
2: "tesla_theme",
3: "stalin_theme"
}
theme_name = theme_configuration.get(custom_sounds, "stock")
self.sound_directory = BASEDIR + ("/selfdrive/frogpilot/assets/custom_themes/" + theme_name + "/sounds/" if custom_sounds else "/selfdrive/assets/sounds/")
self.load_sounds()
def main():

View File

@@ -207,10 +207,14 @@ static void update_state(UIState *s) {
}
if (sm.updated("carState")) {
auto carState = sm["carState"].getCarState();
if (scene.blind_spot_path) {
if (scene.blind_spot_path || scene.custom_signals) {
scene.blind_spot_left = carState.getLeftBlindspot();
scene.blind_spot_right = carState.getRightBlindspot();
}
if (scene.custom_signals) {
scene.turn_signal_left = carState.getLeftBlinker();
scene.turn_signal_right = carState.getRightBlinker();
}
}
if (sm.updated("controlsState")) {
auto controlsState = sm["controlsState"].getControlsState();
@@ -270,6 +274,11 @@ void ui_update_frogpilot_params(UIState *s) {
scene.acceleration_path = custom_onroad_ui && params.getBool("AccelerationPath");
scene.blind_spot_path = custom_onroad_ui && params.getBool("BlindSpotPath");
bool custom_theme = params.getBool("CustomTheme");
scene.custom_colors = custom_theme ? params.getInt("CustomColors") : 0;
scene.custom_icons = custom_theme ? params.getInt("CustomIcons") : 0;
scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0;
bool quality_of_life_controls = params.getBool("QOLControls");
bool quality_of_life_visuals = params.getBool("QOLVisuals");

View File

@@ -178,6 +178,8 @@ typedef struct UIScene {
bool conditional_experimental;
bool enabled;
bool experimental_mode;
bool turn_signal_left;
bool turn_signal_right;
float lane_width_left;
float lane_width_right;
@@ -186,6 +188,9 @@ typedef struct UIScene {
int conditional_speed;
int conditional_speed_lead;
int conditional_status;
int custom_colors;
int custom_icons;
int custom_signals;
QPolygonF track_adjacent_vertices[6];