From e5b7832be63226fadabf9ee04b07c6d3ee72c205 Mon Sep 17 00:00:00 2001 From: James <91348155+FrogAi@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:00:00 -0700 Subject: [PATCH] Driving Personality Button --- frogpilot/ui/qt/onroad/frogpilot_buttons.cc | 75 +++++++++++++++++++++ frogpilot/ui/qt/onroad/frogpilot_buttons.h | 27 ++++++++ opendbc_repo/opendbc/car/interfaces.py | 7 +- selfdrive/ui/qt/onroad/annotated_camera.cc | 11 +++ selfdrive/ui/qt/onroad/annotated_camera.h | 1 + selfdrive/ui/qt/onroad/driver_monitoring.cc | 8 +++ selfdrive/ui/qt/onroad/driver_monitoring.h | 2 + 7 files changed, 129 insertions(+), 2 deletions(-) diff --git a/frogpilot/ui/qt/onroad/frogpilot_buttons.cc b/frogpilot/ui/qt/onroad/frogpilot_buttons.cc index dcf07008d..c6e49b939 100644 --- a/frogpilot/ui/qt/onroad/frogpilot_buttons.cc +++ b/frogpilot/ui/qt/onroad/frogpilot_buttons.cc @@ -1 +1,76 @@ #include "frogpilot/ui/qt/onroad/frogpilot_buttons.h" + +DrivingPersonalityButton::DrivingPersonalityButton(QWidget *parent) : QPushButton(parent) { + setFixedSize(btn_size + UI_BORDER_SIZE, btn_size); + + QObject::connect(frogpilotUIState(), &FrogPilotUIState::themeUpdated, this, &DrivingPersonalityButton::updateTheme); + QObject::connect(this, &QPushButton::pressed, [this] {params_memory.putBool("OnroadDistanceButtonPressed", true);}); + QObject::connect(this, &QPushButton::released, [this] {params_memory.putBool("OnroadDistanceButtonPressed", false);}); +} + +void DrivingPersonalityButton::showEvent(QShowEvent *event) { + updateTheme(); +} + +void DrivingPersonalityButton::updateTheme() { + for (QMap>>::iterator it = icon_map.begin(); it != icon_map.end(); ++it) { + QSharedPointer &movie = it.value().second; + if (!movie.isNull()) { + QObject::disconnect(movie.data(), nullptr, this, nullptr); + movie->stop(); + } + } + + icon_map.clear(); + + QPixmap traffic_img, aggressive_img, standard_img, relaxed_img; + QSharedPointer traffic_gif, aggressive_gif, standard_gif, relaxed_gif; + + loadImage("../../frogpilot/assets/active_theme/distance_icons/traffic", traffic_img, traffic_gif, QSize(btn_size, btn_size), this); + loadImage("../../frogpilot/assets/active_theme/distance_icons/aggressive", aggressive_img, aggressive_gif, QSize(btn_size, btn_size), this); + loadImage("../../frogpilot/assets/active_theme/distance_icons/standard", standard_img, standard_gif, QSize(btn_size, btn_size), this); + loadImage("../../frogpilot/assets/active_theme/distance_icons/relaxed", relaxed_img, relaxed_gif, QSize(btn_size, btn_size), this); + + icon_map.insert(0, qMakePair(traffic_img, traffic_gif)); + icon_map.insert(1, qMakePair(aggressive_img, aggressive_gif)); + icon_map.insert(2, qMakePair(standard_img, standard_gif)); + icon_map.insert(3, qMakePair(relaxed_img, relaxed_gif)); + + theme_updated = true; +} + +void DrivingPersonalityButton::updateState(const UIState &s, const FrogPilotUIState &fs) { + const UIScene &scene = s.scene; + + const SubMaster &fpsm = *(fs.sm); + + const cereal::FrogPilotCarState::Reader &frogpilotCarState = fpsm["frogpilotCarState"].getFrogpilotCarState(); + + bool new_traffic_mode_active = frogpilotCarState.getTrafficModeEnabled(); + + int new_personality = static_cast(scene.personality) + 1; + + bool state_changed = (traffic_mode_active != new_traffic_mode_active) || + (personality != new_personality && !new_traffic_mode_active); + + if (!state_changed && !theme_updated) { + return; + } + + traffic_mode_active = new_traffic_mode_active; + + personality = new_personality; + + theme_updated = false; + + QPair> icon = icon_map.value(traffic_mode_active ? 0 : personality); + currentImg = icon.first; + currentGif = icon.second.data(); +} + +void DrivingPersonalityButton::paintEvent(QPaintEvent *event) { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + drawIcon(p, rect().center() + QPoint(UI_BORDER_SIZE / 2, 0), currentGif ? currentGif->currentPixmap() : currentImg, Qt::transparent, 1.0); +} diff --git a/frogpilot/ui/qt/onroad/frogpilot_buttons.h b/frogpilot/ui/qt/onroad/frogpilot_buttons.h index d427996fe..39ca243a7 100644 --- a/frogpilot/ui/qt/onroad/frogpilot_buttons.h +++ b/frogpilot/ui/qt/onroad/frogpilot_buttons.h @@ -1,3 +1,30 @@ #pragma once #include "selfdrive/ui/qt/onroad/buttons.h" + +class DrivingPersonalityButton : public QPushButton { + Q_OBJECT + +public: + explicit DrivingPersonalityButton(QWidget *parent = 0); + + void updateState(const UIState &s, const FrogPilotUIState &fs); + +private: + void paintEvent(QPaintEvent *event) override; + void showEvent(QShowEvent *event) override; + void updateTheme(); + + bool theme_updated; + bool traffic_mode_active; + + int personality; + + Params params_memory{"", true}; + + QMap>> icon_map; + + QMovie *currentGif; + + QPixmap currentImg; +}; diff --git a/opendbc_repo/opendbc/car/interfaces.py b/opendbc_repo/opendbc/car/interfaces.py index 4c1def340..2b764a154 100644 --- a/opendbc_repo/opendbc/car/interfaces.py +++ b/opendbc_repo/opendbc/car/interfaces.py @@ -319,9 +319,12 @@ class CarInterfaceBase(ABC): self.CS.out = ret # FrogPilot variables - self.distance_button = bool(self.CS.distance_button) + prev_distance_button = self.distance_button + self.distance_button = self.params_memory.get_bool("OnroadDistanceButtonPressed") + if self.distance_button != prev_distance_button: + ret.buttonEvents = create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}) - fp_ret.distancePressed = self.distance_button + fp_ret.distancePressed = self.distance_button or bool(self.CS.distance_button) fp_ret.ecoGear |= ret.gearShifter == GearShifter.eco fp_ret.sportGear |= ret.gearShifter == GearShifter.sport diff --git a/selfdrive/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc index 0e00f9a4a..9b915fc30 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.cc +++ b/selfdrive/ui/qt/onroad/annotated_camera.cc @@ -21,6 +21,8 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget *par main_layout->addWidget(experimental_btn, 0, Qt::AlignTop | Qt::AlignRight); // FrogPilot variables + personality_btn = new DrivingPersonalityButton(this); + personality_btn->setVisible(false); } void AnnotatedCameraWidget::updateState(const UIState &s, const FrogPilotUIState &fs) { @@ -34,6 +36,15 @@ void AnnotatedCameraWidget::updateState(const UIState &s, const FrogPilotUIState const cereal::CarState::Reader &carState = sm["carState"].getCarState(); frogpilot_nvg->experimentalButtonPosition = QPoint(experimental_btn->x(), experimental_btn->y()); + + bool onroad_distance_btn_enabled = frogpilot_nvg->dmIconPosition != QPoint(0, 0) && !frogpilot_nvg->hideBottomIcons && frogpilot_toggles.value("onroad_distance_button").toBool(); + personality_btn->setVisible(onroad_distance_btn_enabled); + if (onroad_distance_btn_enabled) { + personality_btn->move(frogpilot_nvg->rightHandDM ? width() - UI_BORDER_SIZE - personality_btn->width() - (UI_BORDER_SIZE / 2) : UI_BORDER_SIZE, frogpilot_nvg->dmIconPosition.y() - personality_btn->height() / 2); + personality_btn->updateState(s, fs); + } + + dmon.onroad_distance_btn_enabled = onroad_distance_btn_enabled; } void AnnotatedCameraWidget::initializeGL() { diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h index 85017a547..fe73363c7 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/qt/onroad/annotated_camera.h @@ -38,6 +38,7 @@ private: bool wide_cam_requested = false; // FrogPilot variables + DrivingPersonalityButton *personality_btn; protected: void paintGL() override; diff --git a/selfdrive/ui/qt/onroad/driver_monitoring.cc b/selfdrive/ui/qt/onroad/driver_monitoring.cc index c63520ac3..1f36c1007 100644 --- a/selfdrive/ui/qt/onroad/driver_monitoring.cc +++ b/selfdrive/ui/qt/onroad/driver_monitoring.cc @@ -74,6 +74,14 @@ void DriverMonitorRenderer::draw(QPainter &painter, const QRect &surface_rect) { float opacity = is_active ? 0.65f : 0.2f; // FrogPilot variables + if (onroad_distance_btn_enabled) { + if (is_rhd) { + x -= UI_BORDER_SIZE + (btn_size + UI_BORDER_SIZE) + UI_BORDER_SIZE; + } else { + x += UI_BORDER_SIZE + (btn_size + UI_BORDER_SIZE) + UI_BORDER_SIZE; + } + } + if (frogpilot_toggles.value("road_name_ui").toBool()) { y -= UI_BORDER_SIZE; } diff --git a/selfdrive/ui/qt/onroad/driver_monitoring.h b/selfdrive/ui/qt/onroad/driver_monitoring.h index 67e60f864..8ecdbdb5f 100644 --- a/selfdrive/ui/qt/onroad/driver_monitoring.h +++ b/selfdrive/ui/qt/onroad/driver_monitoring.h @@ -15,6 +15,8 @@ public: // FrogPilot variables FrogPilotAnnotatedCameraWidget *frogpilot_nvg; + bool onroad_distance_btn_enabled; + QJsonObject frogpilot_toggles; private: