FrogPilot setup - Toggle panels
After Width: | Height: | Size: 9.5 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_conditional.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_device.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_green_light.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_lane.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_lateral_tune.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_light.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 36 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_mute.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_personality.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_rotate.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_speed_map.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/icon_vtc.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
selfdrive/frogpilot/assets/toggle_icons/quality_of_life.png
Normal file
After Width: | Height: | Size: 42 KiB |
1216
selfdrive/frogpilot/ui/qt/offroad/control_settings.cc
Normal file
83
selfdrive/frogpilot/ui/qt/offroad/control_settings.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||||
|
#include "selfdrive/ui/ui.h"
|
||||||
|
|
||||||
|
class FrogPilotControlsPanel : public FrogPilotListWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FrogPilotControlsPanel(SettingsWindow *parent);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void openParentToggle();
|
||||||
|
void openSubParentToggle();
|
||||||
|
void openSubSubParentToggle();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void hideSubToggles();
|
||||||
|
void hideSubSubToggles();
|
||||||
|
void hideToggles();
|
||||||
|
void showEvent(QShowEvent *event, const UIState &s);
|
||||||
|
void updateCarToggles();
|
||||||
|
void updateMetric();
|
||||||
|
void updateState(const UIState &s);
|
||||||
|
|
||||||
|
ButtonControl *deleteModelBtn;
|
||||||
|
ButtonControl *downloadModelBtn;
|
||||||
|
ButtonControl *personalitiesInfoBtn;
|
||||||
|
ButtonControl *selectModelBtn;
|
||||||
|
|
||||||
|
FrogPilotDualParamControl *conditionalSpeedsImperial;
|
||||||
|
FrogPilotDualParamControl *conditionalSpeedsMetric;
|
||||||
|
|
||||||
|
FrogPilotParamManageControl *modelManagerToggle;
|
||||||
|
|
||||||
|
FrogPilotParamValueToggleControl *steerRatioToggle;
|
||||||
|
|
||||||
|
std::set<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkSpeed", "ResetAggressivePersonality"};
|
||||||
|
std::set<QString> aolKeys = {"AlwaysOnLateralMain", "HideAOLStatusBar", "PauseAOLOnBrake"};
|
||||||
|
std::set<QString> conditionalExperimentalKeys = {"CECurves", "CECurvesLead", "CENavigation", "CESignal", "CESlowerLead", "CEStopLights", "HideCEMStatusBar"};
|
||||||
|
std::set<QString> deviceManagementKeys = {"DeviceShutdown", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads", "OfflineMode"};
|
||||||
|
std::set<QString> customdrivingPersonalityKeys = {"TrafficPersonalityProfile", "AggressivePersonalityProfile", "StandardPersonalityProfile", "RelaxedPersonalityProfile"};
|
||||||
|
std::set<QString> drivingPersonalityKeys = {"CustomPersonalities", "OnroadDistanceButton"};
|
||||||
|
std::set<QString> experimentalModeActivationKeys = {"ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", "ExperimentalModeViaTap"};
|
||||||
|
std::set<QString> laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"};
|
||||||
|
std::set<QString> lateralTuneKeys = {"ForceAutoTune", "NNFF", "NNFFLite", "SteerRatio", "TacoTune", "TurnDesires"};
|
||||||
|
std::set<QString> longitudinalTuneKeys = {"AccelerationProfile", "AggressiveAcceleration", "DecelerationProfile", "LeadDetectionThreshold", "SmoothBraking", "StoppingDistance", "TrafficMode"};
|
||||||
|
std::set<QString> mtscKeys = {"DisableMTSCSmoothing", "MTSCAggressiveness", "MTSCCurvatureCheck"};
|
||||||
|
std::set<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "MapGears", "PauseLateralSpeed", "ReverseCruise", "SetSpeedOffset"};
|
||||||
|
std::set<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkSpeed", "ResetRelaxedPersonality"};
|
||||||
|
std::set<QString> speedLimitControllerKeys = {"SLCControls", "SLCQOL", "SLCVisuals"};
|
||||||
|
std::set<QString> speedLimitControllerControlsKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "SLCFallback", "SLCOverride", "SLCPriority"};
|
||||||
|
std::set<QString> speedLimitControllerQOLKeys = {"ForceMPHDashboard", "SetSpeedLimit", "SLCConfirmation", "SLCLookaheadHigher", "SLCLookaheadLower"};
|
||||||
|
std::set<QString> speedLimitControllerVisualsKeys = {"ShowSLCOffset", "SpeedLimitChangedAlert", "UseVienna"};
|
||||||
|
std::set<QString> standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkSpeed", "ResetStandardPersonality"};
|
||||||
|
std::set<QString> trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkSpeed", "ResetTrafficPersonality"};
|
||||||
|
std::set<QString> tuningKeys = {"kiV1", "kiV2", "kiV3", "kiV4", "kpV1", "kpV2", "kpV3", "kpV4"};
|
||||||
|
std::set<QString> visionTurnControlKeys = {"CurveSensitivity", "DisableVTSCSmoothing", "TurnAggressiveness"};
|
||||||
|
|
||||||
|
std::map<std::string, AbstractControl*> toggles;
|
||||||
|
|
||||||
|
Params params;
|
||||||
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
|
|
||||||
|
bool customPersonalitiesOpen;
|
||||||
|
bool hasAutoTune;
|
||||||
|
bool hasCommaNNFFSupport;
|
||||||
|
bool hasNNFFLog;
|
||||||
|
bool hasOpenpilotLongitudinal;
|
||||||
|
bool hasPCMCruise;
|
||||||
|
bool hasDashSpeedLimits;
|
||||||
|
bool isGM;
|
||||||
|
bool isHKGCanFd;
|
||||||
|
bool isMetric = params.getBool("IsMetric");
|
||||||
|
bool isRelease;
|
||||||
|
bool isToyota;
|
||||||
|
bool slcOpen;
|
||||||
|
bool started;
|
||||||
|
|
||||||
|
float steerRatioStock;
|
||||||
|
};
|
284
selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
#include <QDir>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||||
|
|
||||||
|
QStringList getCarNames(const QString &carMake) {
|
||||||
|
QMap<QString, QString> makeMap;
|
||||||
|
makeMap["acura"] = "honda";
|
||||||
|
makeMap["audi"] = "volkswagen";
|
||||||
|
makeMap["buick"] = "gm";
|
||||||
|
makeMap["cadillac"] = "gm";
|
||||||
|
makeMap["chevrolet"] = "gm";
|
||||||
|
makeMap["chrysler"] = "chrysler";
|
||||||
|
makeMap["dodge"] = "chrysler";
|
||||||
|
makeMap["ford"] = "ford";
|
||||||
|
makeMap["gm"] = "gm";
|
||||||
|
makeMap["gmc"] = "gm";
|
||||||
|
makeMap["genesis"] = "hyundai";
|
||||||
|
makeMap["honda"] = "honda";
|
||||||
|
makeMap["hyundai"] = "hyundai";
|
||||||
|
makeMap["infiniti"] = "nissan";
|
||||||
|
makeMap["jeep"] = "chrysler";
|
||||||
|
makeMap["kia"] = "hyundai";
|
||||||
|
makeMap["lexus"] = "toyota";
|
||||||
|
makeMap["lincoln"] = "ford";
|
||||||
|
makeMap["man"] = "volkswagen";
|
||||||
|
makeMap["mazda"] = "mazda";
|
||||||
|
makeMap["nissan"] = "nissan";
|
||||||
|
makeMap["ram"] = "chrysler";
|
||||||
|
makeMap["seat"] = "volkswagen";
|
||||||
|
makeMap["škoda"] = "volkswagen";
|
||||||
|
makeMap["subaru"] = "subaru";
|
||||||
|
makeMap["tesla"] = "tesla";
|
||||||
|
makeMap["toyota"] = "toyota";
|
||||||
|
makeMap["volkswagen"] = "volkswagen";
|
||||||
|
|
||||||
|
QString dirPath = "../car";
|
||||||
|
QDir dir(dirPath);
|
||||||
|
QString targetFolder = makeMap.value(carMake, carMake);
|
||||||
|
QStringList names;
|
||||||
|
|
||||||
|
QString filePath = dir.absoluteFilePath(targetFolder + "/values.py");
|
||||||
|
QFile file(filePath);
|
||||||
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
QTextStream in(&file);
|
||||||
|
QRegularExpression regex(R"delimiter(\w+\s*=\s*\w+PlatformConfig\(\s*"([^"]+)",)delimiter");
|
||||||
|
QRegularExpressionMatchIterator it = regex.globalMatch(in.readAll());
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = it.next();
|
||||||
|
names << match.captured(1);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(names.begin(), names.end());
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(SettingsWindow *parent) : FrogPilotListWidget(parent) {
|
||||||
|
selectMakeButton = new ButtonControl(tr("Select Make"), tr("SELECT"));
|
||||||
|
QObject::connect(selectMakeButton, &ButtonControl::clicked, [this]() {
|
||||||
|
QStringList makes = {
|
||||||
|
"Acura", "Audi", "BMW", "Buick", "Cadillac", "Chevrolet", "Chrysler", "Dodge", "Ford", "GM", "GMC",
|
||||||
|
"Genesis", "Honda", "Hyundai", "Infiniti", "Jeep", "Kia", "Lexus", "Lincoln", "MAN", "Mazda",
|
||||||
|
"Mercedes", "Nissan", "Ram", "SEAT", "Škoda", "Subaru", "Tesla", "Toyota", "Volkswagen", "Volvo",
|
||||||
|
};
|
||||||
|
|
||||||
|
QString newMakeSelection = MultiOptionDialog::getSelection(tr("Select a Make"), makes, "", this);
|
||||||
|
if (!newMakeSelection.isEmpty()) {
|
||||||
|
carMake = newMakeSelection;
|
||||||
|
params.putNonBlocking("CarMake", carMake.toStdString());
|
||||||
|
selectMakeButton->setValue(newMakeSelection);
|
||||||
|
setModels();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addItem(selectMakeButton);
|
||||||
|
|
||||||
|
selectModelButton = new ButtonControl(tr("Select Model"), tr("SELECT"));
|
||||||
|
QObject::connect(selectModelButton, &ButtonControl::clicked, [this]() {
|
||||||
|
QString newModelSelection = MultiOptionDialog::getSelection(tr("Select a Model"), models, "", this);
|
||||||
|
if (!newModelSelection.isEmpty()) {
|
||||||
|
carModel = newModelSelection;
|
||||||
|
params.putNonBlocking("CarModel", newModelSelection.toStdString());
|
||||||
|
selectModelButton->setValue(newModelSelection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addItem(selectModelButton);
|
||||||
|
selectModelButton->setVisible(false);
|
||||||
|
|
||||||
|
ParamControl *forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("Forces the selected fingerprint and prevents it from ever changing."), "", this);
|
||||||
|
addItem(forceFingerprint);
|
||||||
|
|
||||||
|
bool disableOpenpilotLongState = params.getBool("DisableOpenpilotLongitudinal");
|
||||||
|
disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disable openpilot longitudinal control and use stock ACC instead."), "", disableOpenpilotLongState);
|
||||||
|
QObject::connect(disableOpenpilotLong, &ToggleControl::toggleFlipped, [=](bool state) {
|
||||||
|
if (state) {
|
||||||
|
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely disable openpilot longitudinal control?"), this)) {
|
||||||
|
params.putBoolNonBlocking("DisableOpenpilotLongitudinal", state);
|
||||||
|
if (started) {
|
||||||
|
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) {
|
||||||
|
Hardware::reboot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params.putBoolNonBlocking("DisableOpenpilotLongitudinal", state);
|
||||||
|
}
|
||||||
|
updateCarToggles();
|
||||||
|
});
|
||||||
|
addItem(disableOpenpilotLong);
|
||||||
|
|
||||||
|
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
|
||||||
|
{"LongPitch", tr("Long Pitch Compensation"), tr("Smoothen out the gas and pedal controls."), ""},
|
||||||
|
{"GasRegenCmd", tr("Truck Tune"), tr("Increase the acceleration and smoothen out the brake control when coming to a stop. For use on Silverado/Sierra only."), ""},
|
||||||
|
|
||||||
|
{"CrosstrekTorque", tr("Subaru Crosstrek Torque Increase"), tr("Increases the maximum allowed torque for the Subaru Crosstrek."), ""},
|
||||||
|
|
||||||
|
{"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("Automatically lock the doors when in drive and unlock when in park."), ""},
|
||||||
|
{"ClusterOffset", tr("Cluster Offset"), tr("Set the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""},
|
||||||
|
{"SNGHack", tr("Stop and Go Hack"), tr("Enable the 'Stop and Go' hack for vehicles without stock stop and go functionality."), ""},
|
||||||
|
{"ToyotaTune", tr("Toyota Tune"), tr("Use a custom Toyota longitudinal tune.\n\nCydia = More focused on TSS-P vehicles but works for all Toyotas\n\nDragonPilot = Focused on TSS2 vehicles\n\nFrogPilot = Takes the best of both worlds with some personal tweaks focused around FrogsGoMoo's 2019 Lexus ES 350"), ""},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &[param, title, desc, icon] : vehicleToggles) {
|
||||||
|
AbstractControl *toggle;
|
||||||
|
|
||||||
|
if (param == "ToyotaDoors") {
|
||||||
|
std::vector<QString> lockToggles{"LockDoors", "UnlockDoors"};
|
||||||
|
std::vector<QString> lockToggleNames{tr("Lock"), tr("Unlock")};
|
||||||
|
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, lockToggles, lockToggleNames);
|
||||||
|
|
||||||
|
} else if (param == "ClusterOffset") {
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 1.000, 1.050, std::map<int, QString>(), this, false, "x", 1, 0.001);
|
||||||
|
|
||||||
|
} else if (param == "ToyotaTune") {
|
||||||
|
std::vector<std::pair<QString, QString>> tuneOptions{
|
||||||
|
{"StockTune", tr("Stock")},
|
||||||
|
{"CydiaTune", tr("Cydia")},
|
||||||
|
{"DragonPilotTune", tr("DragonPilot")},
|
||||||
|
{"FrogsGoMooTune", tr("FrogPilot")},
|
||||||
|
};
|
||||||
|
toggle = new FrogPilotButtonsParamControl(param, title, desc, icon, tuneOptions);
|
||||||
|
|
||||||
|
QObject::connect(static_cast<FrogPilotButtonsParamControl*>(toggle), &FrogPilotButtonsParamControl::buttonClicked, [this]() {
|
||||||
|
if (started) {
|
||||||
|
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) {
|
||||||
|
Hardware::reboot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
toggle = new ParamControl(param, title, desc, icon, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle->setVisible(false);
|
||||||
|
addItem(toggle);
|
||||||
|
toggles[param.toStdString()] = toggle;
|
||||||
|
|
||||||
|
QObject::connect(static_cast<ToggleControl*>(toggle), &ToggleControl::toggleFlipped, &updateFrogPilotToggles);
|
||||||
|
|
||||||
|
QObject::connect(toggle, &AbstractControl::showDescriptionEvent, [this]() {
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<QString> rebootKeys = {"CrosstrekTorque", "GasRegenCmd"};
|
||||||
|
for (const QString &key : rebootKeys) {
|
||||||
|
QObject::connect(static_cast<ToggleControl*>(toggles[key.toStdString().c_str()]), &ToggleControl::toggleFlipped, [this]() {
|
||||||
|
if (started) {
|
||||||
|
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) {
|
||||||
|
Hardware::reboot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject::connect(uiState(), &UIState::offroadTransition, [this](bool offroad) {
|
||||||
|
std::thread([this]() {
|
||||||
|
while (carMake.isEmpty()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
carMake = QString::fromStdString(params.get("CarMake"));
|
||||||
|
carModel = QString::fromStdString(params.get("CarModel"));
|
||||||
|
}
|
||||||
|
setModels();
|
||||||
|
updateCarToggles();
|
||||||
|
}).detach();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVehiclesPanel::updateState);
|
||||||
|
|
||||||
|
carMake = QString::fromStdString(params.get("CarMake"));
|
||||||
|
carModel = QString::fromStdString(params.get("CarModel"));
|
||||||
|
|
||||||
|
if (!carMake.isEmpty()) {
|
||||||
|
setModels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVehiclesPanel::updateState(const UIState &s) {
|
||||||
|
if (!isVisible()) return;
|
||||||
|
|
||||||
|
started = s.scene.started;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVehiclesPanel::updateCarToggles() {
|
||||||
|
auto carParams = params.get("CarParamsPersistent");
|
||||||
|
if (!carParams.empty()) {
|
||||||
|
AlignedBuffer aligned_buf;
|
||||||
|
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(carParams.data(), carParams.size()));
|
||||||
|
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
|
||||||
|
|
||||||
|
auto carFingerprint = CP.getCarFingerprint();
|
||||||
|
|
||||||
|
hasExperimentalOpenpilotLongitudinal = CP.getExperimentalLongitudinalAvailable();
|
||||||
|
hasOpenpilotLongitudinal = CP.getOpenpilotLongitudinalControl();
|
||||||
|
hasSNG = CP.getMinEnableSpeed() <= 0;
|
||||||
|
isGMTruck = carFingerprint == "CHEVROLET SILVERADO 1500 2020";
|
||||||
|
isImpreza = carFingerprint == "SUBARU IMPREZA LIMITED 2019";
|
||||||
|
} else {
|
||||||
|
hasExperimentalOpenpilotLongitudinal = false;
|
||||||
|
hasOpenpilotLongitudinal = true;
|
||||||
|
hasSNG = false;
|
||||||
|
isGMTruck = true;
|
||||||
|
isImpreza = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hideToggles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVehiclesPanel::setModels() {
|
||||||
|
models = getCarNames(carMake.toLower());
|
||||||
|
hideToggles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVehiclesPanel::hideToggles() {
|
||||||
|
disableOpenpilotLong->setVisible(hasOpenpilotLongitudinal && !hasExperimentalOpenpilotLongitudinal && !params.getBool("HideDisableOpenpilotLongitudinal"));
|
||||||
|
|
||||||
|
selectMakeButton->setValue(carMake);
|
||||||
|
selectModelButton->setValue(carModel);
|
||||||
|
selectModelButton->setVisible(!carMake.isEmpty());
|
||||||
|
|
||||||
|
bool gm = carMake == "Buick" || carMake == "Cadillac" || carMake == "Chevrolet" || carMake == "GM" || carMake == "GMC";
|
||||||
|
bool subaru = carMake == "Subaru";
|
||||||
|
bool toyota = carMake == "Lexus" || carMake == "Toyota";
|
||||||
|
|
||||||
|
std::set<QString> gmTruckKeys = {"GasRegenCmd"};
|
||||||
|
std::set<QString> imprezaKeys = {"CrosstrekTorque"};
|
||||||
|
std::set<QString> longitudinalKeys = {"GasRegenCmd", "ToyotaTune", "LongPitch", "SNGHack"};
|
||||||
|
std::set<QString> sngKeys = {"SNGHack"};
|
||||||
|
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
if (toggle) {
|
||||||
|
toggle->setVisible(false);
|
||||||
|
|
||||||
|
if ((!hasOpenpilotLongitudinal || params.getBool("DisableOpenpilotLongitudinal")) && longitudinalKeys.find(key.c_str()) != longitudinalKeys.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasSNG && sngKeys.find(key.c_str()) != sngKeys.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isGMTruck && gmTruckKeys.find(key.c_str()) != gmTruckKeys.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isImpreza && imprezaKeys.find(key.c_str()) != imprezaKeys.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gm) {
|
||||||
|
toggle->setVisible(gmKeys.find(key.c_str()) != gmKeys.end());
|
||||||
|
} else if (subaru) {
|
||||||
|
toggle->setVisible(subaruKeys.find(key.c_str()) != subaruKeys.end());
|
||||||
|
} else if (toyota) {
|
||||||
|
toggle->setVisible(toyotaKeys.find(key.c_str()) != toyotaKeys.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
46
selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||||
|
#include "selfdrive/ui/ui.h"
|
||||||
|
|
||||||
|
class FrogPilotVehiclesPanel : public FrogPilotListWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FrogPilotVehiclesPanel(SettingsWindow *parent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void hideToggles();
|
||||||
|
void setModels();
|
||||||
|
void updateCarToggles();
|
||||||
|
void updateState(const UIState &s);
|
||||||
|
|
||||||
|
ButtonControl *selectMakeButton;
|
||||||
|
ButtonControl *selectModelButton;
|
||||||
|
|
||||||
|
ToggleControl *disableOpenpilotLong;
|
||||||
|
|
||||||
|
QString carMake;
|
||||||
|
QString carModel;
|
||||||
|
|
||||||
|
QStringList models;
|
||||||
|
|
||||||
|
std::set<QString> gmKeys = {"GasRegenCmd", "LongPitch"};
|
||||||
|
std::set<QString> subaruKeys = {"CrosstrekTorque"};
|
||||||
|
std::set<QString> toyotaKeys = {"ClusterOffset", "ToyotaTune", "SNGHack", "ToyotaDoors"};
|
||||||
|
|
||||||
|
std::map<std::string, AbstractControl*> toggles;
|
||||||
|
|
||||||
|
Params params;
|
||||||
|
|
||||||
|
bool hasExperimentalOpenpilotLongitudinal;
|
||||||
|
bool hasOpenpilotLongitudinal;
|
||||||
|
bool hasSNG;
|
||||||
|
bool isGMTruck;
|
||||||
|
bool isImpreza;
|
||||||
|
bool started;
|
||||||
|
};
|
428
selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
|
||||||
|
|
||||||
|
FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilotListWidget(parent) {
|
||||||
|
std::string branch = params.get("GitBranch");
|
||||||
|
isRelease = branch == "FrogPilot";
|
||||||
|
|
||||||
|
const std::vector<std::tuple<QString, QString, QString, QString>> visualToggles {
|
||||||
|
{"AlertVolumeControl", tr("Alert Volume Controller"), tr("Control the volume level for each individual sound in openpilot."), "../frogpilot/assets/toggle_icons/icon_mute.png"},
|
||||||
|
{"DisengageVolume", tr("Disengage Volume"), tr("Related alerts:\n\nAdaptive Cruise Disabled\nParking Brake Engaged\nBrake Pedal Pressed\nSpeed too Low"), ""},
|
||||||
|
{"EngageVolume", tr("Engage Volume"), tr("Related alerts:\n\nNNFF Torque Controller loaded\nopenpilot engaged"), ""},
|
||||||
|
{"PromptVolume", tr("Prompt Volume"), tr("Related alerts:\n\nCar Detected in Blindspot\nSpeed too Low\nSteer Unavailable Below 'X'\nTake Control, Turn Exceeds Steering Limit"), ""},
|
||||||
|
{"PromptDistractedVolume", tr("Prompt Distracted Volume"), tr("Related alerts:\n\nPay Attention, Driver Distracted\nTouch Steering Wheel, Driver Unresponsive"), ""},
|
||||||
|
{"RefuseVolume", tr("Refuse Volume"), tr("Related alerts:\n\nopenpilot Unavailable"), ""},
|
||||||
|
{"WarningSoftVolume", tr("Warning Soft Volume"), tr("Related alerts:\n\nBRAKE!, Risk of Collision\nTAKE CONTROL IMMEDIATELY"), ""},
|
||||||
|
{"WarningImmediateVolume", tr("Warning Immediate Volume"), tr("Related alerts:\n\nDISENGAGE IMMEDIATELY, Driver Distracted\nDISENGAGE IMMEDIATELY, Driver Unresponsive"), ""},
|
||||||
|
|
||||||
|
{"CustomAlerts", tr("Custom Alerts"), tr("Enable custom alerts for openpilot events."), "../frogpilot/assets/toggle_icons/icon_green_light.png"},
|
||||||
|
{"GreenLightAlert", tr("Green Light Alert"), tr("Get an alert when a traffic light changes from red to green."), ""},
|
||||||
|
{"LeadDepartingAlert", tr("Lead Departing Alert"), tr("Get an alert when the lead vehicle starts departing when at a standstill."), ""},
|
||||||
|
{"LoudBlindspotAlert", tr("Loud Blindspot Alert"), tr("Enable a louder alert for when a vehicle is detected in the blindspot when attempting to change lanes."), ""},
|
||||||
|
|
||||||
|
{"CustomUI", tr("Custom Onroad UI"), tr("Customize the Onroad UI."), "../assets/offroad/icon_road.png"},
|
||||||
|
{"Compass", tr("Compass"), tr("Add a compass to the onroad UI."), ""},
|
||||||
|
{"CustomPaths", tr("Paths"), tr("Show your projected acceleration on the driving path, detected adjacent lanes, or when a vehicle is detected in your blindspot."), ""},
|
||||||
|
{"PedalsOnUI", tr("Pedals Being Pressed"), tr("Display the brake and gas pedals on the onroad UI below the steering wheel icon."), ""},
|
||||||
|
{"RoadNameUI", tr("Road Name"), tr("Display the current road's name at the bottom of the screen. Sourced from OpenStreetMap."), ""},
|
||||||
|
{"WheelIcon", tr("Steering Wheel Icon"), tr("Replace the default steering wheel icon with a custom icon."), ""},
|
||||||
|
|
||||||
|
{"CustomTheme", tr("Custom Themes"), tr("Enable the ability to use custom themes."), "../frogpilot/assets/wheel_images/frog.png"},
|
||||||
|
{"CustomColors", tr("Color Theme"), tr("Switch out the standard openpilot color scheme with themed colors.\n\nWant to submit your own color scheme? Post it in the 'feature-request' channel in the FrogPilot Discord!"), ""},
|
||||||
|
{"CustomIcons", tr("Icon Pack"), tr("Switch out the standard openpilot icons with a set of themed icons.\n\nWant to submit your own icon pack? Post it in the 'feature-request' channel in the FrogPilot Discord!"), ""},
|
||||||
|
{"CustomSounds", tr("Sound Pack"), tr("Switch out the standard openpilot sounds with a set of themed sounds.\n\nWant to submit your own sound pack? Post it in the 'feature-request' channel in the FrogPilot Discord!"), ""},
|
||||||
|
{"CustomSignals", tr("Turn Signals"), tr("Add themed animation for your turn signals.\n\nWant to submit your own turn signal animation? Post it in the 'feature-request' channel in the FrogPilot Discord!"), ""},
|
||||||
|
{"HolidayThemes", tr("Holiday Themes"), tr("The openpilot theme changes according to the current/upcoming holiday. Minor holidays last a day, while major holidays (Easter, Christmas, Halloween, etc.) last a week."), ""},
|
||||||
|
{"RandomEvents", tr("Random Events"), tr("Enjoy a bit of unpredictability with random events that can occur during certain driving conditions. This is purely cosmetic and has no impact on driving controls!"), ""},
|
||||||
|
|
||||||
|
{"DeveloperUI", tr("Developer UI"), tr("Get various detailed information of what openpilot is doing behind the scenes."), "../frogpilot/assets/toggle_icons/icon_device.png"},
|
||||||
|
{"BorderMetrics", tr("Border Metrics"), tr("Display metrics in onroad UI border."), ""},
|
||||||
|
{"FPSCounter", tr("FPS Counter"), tr("Display the 'Frames Per Second' (FPS) of your onroad UI for monitoring system performance."), ""},
|
||||||
|
{"LateralMetrics", tr("Lateral Metrics"), tr("Display various metrics related to the lateral performance of openpilot."), ""},
|
||||||
|
{"LongitudinalMetrics", tr("Longitudinal Metrics"), tr("Display various metrics related to the longitudinal performance of openpilot."), ""},
|
||||||
|
{"NumericalTemp", tr("Numerical Temperature Gauge"), tr("Replace the 'GOOD', 'OK', and 'HIGH' temperature statuses with a numerical temperature gauge based on the highest temperature between the memory, CPU, and GPU."), ""},
|
||||||
|
{"SidebarMetrics", tr("Sidebar"), tr("Display various custom metrics on the sidebar for the CPU, GPU, RAM, IP, and storage used/left."), ""},
|
||||||
|
{"UseSI", tr("Use International System of Units"), tr("Display relevant metrics in the SI format."), ""},
|
||||||
|
|
||||||
|
{"ModelUI", tr("Model UI"), tr("Customize the model visualizations on the screen."), "../assets/offroad/icon_calibration.png"},
|
||||||
|
{"DynamicPathWidth", tr("Dynamic Path Width"), tr("Have the path width dynamically adjust based on the current engagement state of openpilot."), ""},
|
||||||
|
{"HideLeadMarker", tr("Hide Lead Marker"), tr("Hide the lead marker from the onroad UI."), ""},
|
||||||
|
{"LaneLinesWidth", tr("Lane Lines"), tr("Adjust the visual thickness of lane lines on your display.\n\nDefault matches the MUTCD average of 4 inches."), ""},
|
||||||
|
{"PathEdgeWidth", tr("Path Edges"), tr("Adjust the width of the path edges shown on your UI to represent different driving modes and statuses.\n\nDefault is 20% of the total path.\n\nBlue = Navigation\nLight Blue = 'Always On Lateral'\nGreen = Default\nOrange = 'Experimental Mode'\nRed = 'Traffic Mode'\nYellow = 'Conditional Experimental Mode' Overriden"), ""},
|
||||||
|
{"PathWidth", tr("Path Width"), tr("Customize the width of the driving path shown on your UI.\n\nDefault matches the width of a 2019 Lexus ES 350."), ""},
|
||||||
|
{"RoadEdgesWidth", tr("Road Edges"), tr("Adjust the visual thickness of road edges on your display.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches."), ""},
|
||||||
|
{"UnlimitedLength", tr("'Unlimited' Road UI Length"), tr("Extend the display of the path, lane lines, and road edges out as far as the model can see."), ""},
|
||||||
|
|
||||||
|
{"QOLVisuals", tr("Quality of Life"), tr("Miscellaneous quality of life changes to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"},
|
||||||
|
{"BigMap", tr("Big Map"), tr("Increase the size of the map in the onroad UI."), ""},
|
||||||
|
{"CameraView", tr("Camera View"), tr("Choose your preferred camera view for the onroad UI. This is purely a visual change and doesn't impact how openpilot drives."), ""},
|
||||||
|
{"DriverCamera", tr("Driver Camera On Reverse"), tr("Show the driver camera feed when in reverse."), ""},
|
||||||
|
{"HideSpeed", tr("Hide Speed"), tr("Hide the speed indicator in the onroad UI. Additional toggle allows it to be hidden/shown via tapping the speed itself."), ""},
|
||||||
|
{"MapStyle", tr("Map Style"), tr("Select a map style to use with navigation."), ""},
|
||||||
|
{"WheelSpeed", tr("Use Wheel Speed"), tr("Use the wheel speed instead of the cluster speed in the onroad UI."), ""},
|
||||||
|
|
||||||
|
{"ScreenManagement", tr("Screen Management"), tr("Manage your screen's brightness, timeout settings, and hide onroad UI elements."), "../frogpilot/assets/toggle_icons/icon_light.png"},
|
||||||
|
{"HideUIElements", tr("Hide UI Elements"), tr("Hide the selected UI elements from the onroad screen."), ""},
|
||||||
|
{"ScreenBrightness", tr("Screen Brightness"), tr("Customize your screen brightness when offroad."), ""},
|
||||||
|
{"ScreenBrightnessOnroad", tr("Screen Brightness (Onroad)"), tr("Customize your screen brightness when onroad."), ""},
|
||||||
|
{"ScreenRecorder", tr("Screen Recorder"), tr("Enable the ability to record the screen while onroad."), ""},
|
||||||
|
{"ScreenTimeout", tr("Screen Timeout"), tr("Customize how long it takes for your screen to turn off."), ""},
|
||||||
|
{"ScreenTimeoutOnroad", tr("Screen Timeout (Onroad)"), tr("Customize how long it takes for your screen to turn off when onroad."), ""},
|
||||||
|
{"StandbyMode", tr("Standby Mode"), tr("Turn the screen off after your screen times out when onroad, but wake it back up when engagement state changes or important alerts are triggered."), ""},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &[param, title, desc, icon] : visualToggles) {
|
||||||
|
AbstractControl *toggle;
|
||||||
|
|
||||||
|
if (param == "AlertVolumeControl") {
|
||||||
|
FrogPilotParamManageControl *alertVolumeControlToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
|
||||||
|
QObject::connect(alertVolumeControlToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
|
||||||
|
openParentToggle();
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
toggle->setVisible(alertVolumeControlKeys.find(key.c_str()) != alertVolumeControlKeys.end());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle = alertVolumeControlToggle;
|
||||||
|
} else if (alertVolumeControlKeys.find(param) != alertVolumeControlKeys.end()) {
|
||||||
|
if (param == "WarningImmediateVolume") {
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 25, 100, std::map<int, QString>(), this, false, "%");
|
||||||
|
} else {
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map<int, QString>(), this, false, "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (param == "CustomAlerts") {
|
||||||
|
FrogPilotParamManageControl *customAlertsToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
|
||||||
|
QObject::connect(customAlertsToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
|
||||||
|
openParentToggle();
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
std::set<QString> modifiedCustomAlertsKeys = customAlertsKeys;
|
||||||
|
|
||||||
|
if (!hasBSM) {
|
||||||
|
modifiedCustomAlertsKeys.erase("LoudBlindspotAlert");
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle->setVisible(modifiedCustomAlertsKeys.find(key.c_str()) != modifiedCustomAlertsKeys.end());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle = customAlertsToggle;
|
||||||
|
|
||||||
|
} else if (param == "CustomTheme") {
|
||||||
|
FrogPilotParamManageControl *customThemeToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
|
||||||
|
QObject::connect(customThemeToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
|
||||||
|
openParentToggle();
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
toggle->setVisible(customThemeKeys.find(key.c_str()) != customThemeKeys.end());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle = customThemeToggle;
|
||||||
|
} else if (param == "CustomColors" || param == "CustomIcons" || param == "CustomSignals" || param == "CustomSounds") {
|
||||||
|
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(tr("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]() {
|
||||||
|
openParentToggle();
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
std::set<QString> modifiedCustomOnroadUIKeys = customOnroadUIKeys;
|
||||||
|
|
||||||
|
if (!hasOpenpilotLongitudinal && !hasAutoTune) {
|
||||||
|
modifiedCustomOnroadUIKeys.erase("DeveloperUI");
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle->setVisible(modifiedCustomOnroadUIKeys.find(key.c_str()) != modifiedCustomOnroadUIKeys.end());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle = customUIToggle;
|
||||||
|
} else if (param == "CustomPaths") {
|
||||||
|
std::vector<QString> pathToggles{"AccelerationPath", "AdjacentPath", "BlindSpotPath", "AdjacentPathMetrics"};
|
||||||
|
std::vector<QString> pathToggleNames{tr("Acceleration"), tr("Adjacent"), tr("Blind Spot"), tr("Metrics")};
|
||||||
|
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, pathToggles, pathToggleNames);
|
||||||
|
} else if (param == "PedalsOnUI") {
|
||||||
|
std::vector<QString> pedalsToggles{"DynamicPedalsOnUI", "StaticPedalsOnUI"};
|
||||||
|
std::vector<QString> pedalsToggleNames{tr("Dynamic"), tr("Static")};
|
||||||
|
FrogPilotParamToggleControl *pedalsToggle = new FrogPilotParamToggleControl(param, title, desc, icon, pedalsToggles, pedalsToggleNames);
|
||||||
|
QObject::connect(pedalsToggle, &FrogPilotParamToggleControl::buttonTypeClicked, this, [this, pedalsToggle](int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
params.putBool("StaticPedalsOnUI", false);
|
||||||
|
} else if (index == 1) {
|
||||||
|
params.putBool("DynamicPedalsOnUI", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pedalsToggle->updateButtonStates();
|
||||||
|
});
|
||||||
|
toggle = pedalsToggle;
|
||||||
|
|
||||||
|
} else if (param == "WheelIcon") {
|
||||||
|
std::vector<QString> wheelToggles{"RotatingWheel"};
|
||||||
|
std::vector<QString> wheelToggleNames{"Live Rotation"};
|
||||||
|
std::map<int, QString> steeringWheelLabels = {{-1, tr("None")}, {0, tr("Stock")}, {1, tr("Lexus")}, {2, tr("Toyota")}, {3, tr("Frog")}, {4, tr("Rocket")}, {5, tr("Hyundai")}, {6, tr("Stalin")}};
|
||||||
|
toggle = new FrogPilotParamValueToggleControl(param, title, desc, icon, -1, 6, steeringWheelLabels, this, true, "", 1, 1, wheelToggles, wheelToggleNames);
|
||||||
|
|
||||||
|
} else if (param == "DeveloperUI") {
|
||||||
|
FrogPilotParamManageControl *developerUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
|
||||||
|
QObject::connect(developerUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
|
||||||
|
openParentToggle();
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
std::set<QString> modifiedDeveloperUIKeys = developerUIKeys ;
|
||||||
|
|
||||||
|
toggle->setVisible(modifiedDeveloperUIKeys.find(key.c_str()) != modifiedDeveloperUIKeys.end());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle = developerUIToggle;
|
||||||
|
} else if (param == "BorderMetrics") {
|
||||||
|
std::vector<QString> borderToggles{"BlindSpotMetrics", "ShowSteering", "SignalMetrics"};
|
||||||
|
std::vector<QString> borderToggleNames{tr("Blind Spot"), tr("Steering Torque"), tr("Turn Signal"), };
|
||||||
|
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, borderToggles, borderToggleNames);
|
||||||
|
} else if (param == "NumericalTemp") {
|
||||||
|
std::vector<QString> temperatureToggles{"Fahrenheit"};
|
||||||
|
std::vector<QString> temperatureToggleNames{tr("Fahrenheit")};
|
||||||
|
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, temperatureToggles, temperatureToggleNames);
|
||||||
|
} else if (param == "SidebarMetrics") {
|
||||||
|
std::vector<QString> sidebarMetricsToggles{"ShowCPU", "ShowGPU", "ShowIP", "ShowMemoryUsage", "ShowStorageLeft", "ShowStorageUsed"};
|
||||||
|
std::vector<QString> sidebarMetricsToggleNames{tr("CPU"), tr("GPU"), tr("IP"), tr("RAM"), tr("SSD Left"), tr("SSD Used")};
|
||||||
|
FrogPilotParamToggleControl *sidebarMetricsToggle = new FrogPilotParamToggleControl(param, title, desc, icon, sidebarMetricsToggles, sidebarMetricsToggleNames, this, 125);
|
||||||
|
QObject::connect(sidebarMetricsToggle, &FrogPilotParamToggleControl::buttonTypeClicked, this, [this, sidebarMetricsToggle](int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
params.putBool("ShowGPU", false);
|
||||||
|
} else if (index == 1) {
|
||||||
|
params.putBool("ShowCPU", false);
|
||||||
|
} else if (index == 3) {
|
||||||
|
params.putBool("ShowStorageLeft", false);
|
||||||
|
params.putBool("ShowStorageUsed", false);
|
||||||
|
} else if (index == 4) {
|
||||||
|
params.putBool("ShowMemoryUsage", false);
|
||||||
|
params.putBool("ShowStorageUsed", false);
|
||||||
|
} else if (index == 5) {
|
||||||
|
params.putBool("ShowMemoryUsage", false);
|
||||||
|
params.putBool("ShowStorageLeft", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
sidebarMetricsToggle->updateButtonStates();
|
||||||
|
});
|
||||||
|
toggle = sidebarMetricsToggle;
|
||||||
|
|
||||||
|
} else if (param == "ModelUI") {
|
||||||
|
FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
|
||||||
|
QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
|
||||||
|
openParentToggle();
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
std::set<QString> modifiedModelUIKeysKeys = modelUIKeys;
|
||||||
|
|
||||||
|
if (!hasOpenpilotLongitudinal) {
|
||||||
|
modifiedModelUIKeysKeys.erase("HideLeadMarker");
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle->setVisible(modifiedModelUIKeysKeys.find(key.c_str()) != modifiedModelUIKeysKeys.end());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle = modelUIToggle;
|
||||||
|
} else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") {
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, std::map<int, QString>(), this, false, tr(" inches"));
|
||||||
|
} else if (param == "PathEdgeWidth") {
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map<int, QString>(), this, false, tr("%"));
|
||||||
|
} else if (param == "PathWidth") {
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map<int, QString>(), this, false, tr(" feet"), 10);
|
||||||
|
|
||||||
|
} else if (param == "QOLVisuals") {
|
||||||
|
FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
|
||||||
|
QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
|
||||||
|
openParentToggle();
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
toggle->setVisible(qolKeys.find(key.c_str()) != qolKeys.end());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle = qolToggle;
|
||||||
|
} else if (param == "CameraView") {
|
||||||
|
std::vector<QString> cameraOptions{tr("Auto"), tr("Driver"), tr("Standard"), tr("Wide")};
|
||||||
|
FrogPilotButtonParamControl *preferredCamera = new FrogPilotButtonParamControl(param, title, desc, icon, cameraOptions);
|
||||||
|
toggle = preferredCamera;
|
||||||
|
} else if (param == "BigMap") {
|
||||||
|
std::vector<QString> mapToggles{"FullMap"};
|
||||||
|
std::vector<QString> mapToggleNames{tr("Full Map")};
|
||||||
|
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, mapToggles, mapToggleNames);
|
||||||
|
} else if (param == "HideSpeed") {
|
||||||
|
std::vector<QString> hideSpeedToggles{"HideSpeedUI"};
|
||||||
|
std::vector<QString> hideSpeedToggleNames{tr("Control Via UI")};
|
||||||
|
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, hideSpeedToggles, hideSpeedToggleNames);
|
||||||
|
} else if (param == "MapStyle") {
|
||||||
|
QMap<int, QString> styleMap = {
|
||||||
|
{0, tr("Stock openpilot")},
|
||||||
|
{1, tr("Mapbox Streets")},
|
||||||
|
{2, tr("Mapbox Outdoors")},
|
||||||
|
{3, tr("Mapbox Light")},
|
||||||
|
{4, tr("Mapbox Dark")},
|
||||||
|
{5, tr("Mapbox Satellite")},
|
||||||
|
{6, tr("Mapbox Satellite Streets")},
|
||||||
|
{7, tr("Mapbox Navigation Day")},
|
||||||
|
{8, tr("Mapbox Navigation Night")},
|
||||||
|
{9, tr("Mapbox Traffic Night")},
|
||||||
|
{10, tr("mike854's (Satellite hybrid)")},
|
||||||
|
};
|
||||||
|
|
||||||
|
QStringList styles = styleMap.values();
|
||||||
|
ButtonControl *mapStyleButton = new ButtonControl(title, tr("SELECT"), desc);
|
||||||
|
QObject::connect(mapStyleButton, &ButtonControl::clicked, [=]() {
|
||||||
|
QStringList styles = styleMap.values();
|
||||||
|
QString selection = MultiOptionDialog::getSelection(tr("Select a map style"), styles, "", this);
|
||||||
|
if (!selection.isEmpty()) {
|
||||||
|
int selectedStyle = styleMap.key(selection);
|
||||||
|
params.putIntNonBlocking("MapStyle", selectedStyle);
|
||||||
|
mapStyleButton->setValue(selection);
|
||||||
|
updateFrogPilotToggles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
int currentStyle = params.getInt("MapStyle");
|
||||||
|
mapStyleButton->setValue(styleMap[currentStyle]);
|
||||||
|
|
||||||
|
toggle = mapStyleButton;
|
||||||
|
|
||||||
|
} else if (param == "ScreenManagement") {
|
||||||
|
FrogPilotParamManageControl *screenToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
|
||||||
|
QObject::connect(screenToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
|
||||||
|
openParentToggle();
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
toggle->setVisible(screenKeys.find(key.c_str()) != screenKeys.end());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toggle = screenToggle;
|
||||||
|
} else if (param == "HideUIElements") {
|
||||||
|
std::vector<QString> uiElementsToggles{"HideAlerts", "HideMapIcon", "HideMaxSpeed"};
|
||||||
|
std::vector<QString> uiElementsToggleNames{tr("Alerts"), tr("Map Icon"), tr("Max Speed")};
|
||||||
|
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, uiElementsToggles, uiElementsToggleNames);
|
||||||
|
} else if (param == "ScreenBrightness" || param == "ScreenBrightnessOnroad") {
|
||||||
|
std::map<int, QString> brightnessLabels;
|
||||||
|
if (param == "ScreenBrightnessOnroad") {
|
||||||
|
for (int i = 0; i <= 101; i++) {
|
||||||
|
brightnessLabels[i] = (i == 0) ? tr("Screen Off") : (i == 101) ? tr("Auto") : QString::number(i) + "%";
|
||||||
|
}
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 101, brightnessLabels, this, false);
|
||||||
|
} else {
|
||||||
|
for (int i = 1; i <= 101; i++) {
|
||||||
|
brightnessLabels[i] = (i == 101) ? tr("Auto") : QString::number(i) + "%";
|
||||||
|
}
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 101, brightnessLabels, this, false);
|
||||||
|
}
|
||||||
|
} else if (param == "ScreenTimeout" || param == "ScreenTimeoutOnroad") {
|
||||||
|
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 5, 60, std::map<int, QString>(), this, false, tr(" seconds"));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
toggle = new ParamControl(param, title, desc, icon, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
addItem(toggle);
|
||||||
|
toggles[param.toStdString()] = toggle;
|
||||||
|
|
||||||
|
QObject::connect(static_cast<ToggleControl*>(toggle), &ToggleControl::toggleFlipped, &updateFrogPilotToggles);
|
||||||
|
QObject::connect(static_cast<FrogPilotParamValueControl*>(toggle), &FrogPilotParamValueControl::valueChanged, &updateFrogPilotToggles);
|
||||||
|
|
||||||
|
QObject::connect(toggle, &AbstractControl::showDescriptionEvent, [this]() {
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(static_cast<FrogPilotParamManageControl*>(toggle), &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject::connect(parent, &SettingsWindow::closeParentToggle, this, &FrogPilotVisualsPanel::hideToggles);
|
||||||
|
QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotVisualsPanel::updateMetric);
|
||||||
|
QObject::connect(uiState(), &UIState::offroadTransition, this, &FrogPilotVisualsPanel::updateCarToggles);
|
||||||
|
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVisualsPanel::updateState);
|
||||||
|
|
||||||
|
updateMetric();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVisualsPanel::showEvent(QShowEvent *event) {
|
||||||
|
hasOpenpilotLongitudinal = hasOpenpilotLongitudinal && !params.getBool("DisableOpenpilotLongitudinal");
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVisualsPanel::updateState(const UIState &s) {
|
||||||
|
if (!isVisible()) return;
|
||||||
|
|
||||||
|
started = s.scene.started;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVisualsPanel::updateCarToggles() {
|
||||||
|
auto carParams = params.get("CarParamsPersistent");
|
||||||
|
if (!carParams.empty()) {
|
||||||
|
AlignedBuffer aligned_buf;
|
||||||
|
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(carParams.data(), carParams.size()));
|
||||||
|
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
|
||||||
|
auto carName = CP.getCarName();
|
||||||
|
|
||||||
|
hasAutoTune = (carName == "hyundai" || carName == "toyota") && CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::TORQUE;
|
||||||
|
hasBSM = CP.getEnableBsm();
|
||||||
|
hasOpenpilotLongitudinal = CP.getOpenpilotLongitudinalControl() && !params.getBool("DisableOpenpilotLongitudinal");
|
||||||
|
} else {
|
||||||
|
hasBSM = true;
|
||||||
|
hasOpenpilotLongitudinal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hideToggles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVisualsPanel::updateMetric() {
|
||||||
|
bool previousIsMetric = isMetric;
|
||||||
|
isMetric = params.getBool("IsMetric");
|
||||||
|
|
||||||
|
if (isMetric != previousIsMetric) {
|
||||||
|
double distanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH;
|
||||||
|
double speedConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
|
||||||
|
params.putIntNonBlocking("LaneLinesWidth", std::nearbyint(params.getInt("LaneLinesWidth") * distanceConversion));
|
||||||
|
params.putIntNonBlocking("RoadEdgesWidth", std::nearbyint(params.getInt("RoadEdgesWidth") * distanceConversion));
|
||||||
|
params.putIntNonBlocking("PathWidth", std::nearbyint(params.getInt("PathWidth") * speedConversion));
|
||||||
|
}
|
||||||
|
|
||||||
|
FrogPilotParamValueControl *laneLinesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["LaneLinesWidth"]);
|
||||||
|
FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["RoadEdgesWidth"]);
|
||||||
|
FrogPilotParamValueControl *pathWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["PathWidth"]);
|
||||||
|
|
||||||
|
if (isMetric) {
|
||||||
|
laneLinesWidthToggle->setDescription(tr("Customize the lane line width.\n\nDefault matches the Vienna average of 10 centimeters."));
|
||||||
|
roadEdgesWidthToggle->setDescription(tr("Customize the road edges width.\n\nDefault is 1/2 of the Vienna average lane line width of 10 centimeters."));
|
||||||
|
|
||||||
|
laneLinesWidthToggle->updateControl(0, 60, tr(" centimeters"));
|
||||||
|
roadEdgesWidthToggle->updateControl(0, 60, tr(" centimeters"));
|
||||||
|
pathWidthToggle->updateControl(0, 30, tr(" meters"), 10);
|
||||||
|
} else {
|
||||||
|
laneLinesWidthToggle->setDescription(tr("Customize the lane line width.\n\nDefault matches the MUTCD average of 4 inches."));
|
||||||
|
roadEdgesWidthToggle->setDescription(tr("Customize the road edges width.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches."));
|
||||||
|
|
||||||
|
laneLinesWidthToggle->updateControl(0, 24, tr(" inches"));
|
||||||
|
roadEdgesWidthToggle->updateControl(0, 24, tr(" inches"));
|
||||||
|
pathWidthToggle->updateControl(0, 100, tr(" feet"), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
laneLinesWidthToggle->refresh();
|
||||||
|
roadEdgesWidthToggle->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrogPilotVisualsPanel::hideToggles() {
|
||||||
|
for (auto &[key, toggle] : toggles) {
|
||||||
|
bool subToggles = alertVolumeControlKeys.find(key.c_str()) != alertVolumeControlKeys.end() ||
|
||||||
|
customAlertsKeys.find(key.c_str()) != customAlertsKeys.end() ||
|
||||||
|
customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end() ||
|
||||||
|
customThemeKeys.find(key.c_str()) != customThemeKeys.end() ||
|
||||||
|
developerUIKeys.find(key.c_str()) != developerUIKeys.end() ||
|
||||||
|
modelUIKeys.find(key.c_str()) != modelUIKeys.end() ||
|
||||||
|
qolKeys.find(key.c_str()) != qolKeys.end() ||
|
||||||
|
screenKeys.find(key.c_str()) != screenKeys.end();
|
||||||
|
toggle->setVisible(!subToggles);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
43
selfdrive/frogpilot/ui/qt/offroad/visual_settings.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||||
|
#include "selfdrive/ui/ui.h"
|
||||||
|
|
||||||
|
class FrogPilotVisualsPanel : public FrogPilotListWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FrogPilotVisualsPanel(SettingsWindow *parent);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void openParentToggle();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void hideToggles();
|
||||||
|
void showEvent(QShowEvent *event);
|
||||||
|
void updateCarToggles();
|
||||||
|
void updateMetric();
|
||||||
|
void updateState(const UIState &s);
|
||||||
|
|
||||||
|
std::set<QString> alertVolumeControlKeys = {"DisengageVolume", "EngageVolume", "PromptDistractedVolume", "PromptVolume", "RefuseVolume", "WarningImmediateVolume", "WarningSoftVolume"};
|
||||||
|
std::set<QString> customAlertsKeys = {"GreenLightAlert", "LeadDepartingAlert", "LoudBlindspotAlert"};
|
||||||
|
std::set<QString> customOnroadUIKeys = {"Compass", "CustomPaths", "PedalsOnUI", "RoadNameUI", "WheelIcon"};
|
||||||
|
std::set<QString> customThemeKeys = {"CustomColors", "CustomIcons", "CustomSignals", "CustomSounds", "HolidayThemes", "RandomEvents"};
|
||||||
|
std::set<QString> developerUIKeys = {"BorderMetrics", "FPSCounter", "LateralMetrics", "LongitudinalMetrics", "NumericalTemp", "SidebarMetrics", "UseSI"};
|
||||||
|
std::set<QString> modelUIKeys = {"DynamicPathWidth", "HideLeadMarker", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"};
|
||||||
|
std::set<QString> qolKeys = {"BigMap", "CameraView", "DriverCamera", "FullMap", "HideSpeed", "MapStyle", "WheelSpeed"};
|
||||||
|
std::set<QString> screenKeys = {"HideUIElements", "ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", "ScreenTimeout", "ScreenTimeoutOnroad", "StandbyMode"};
|
||||||
|
|
||||||
|
std::map<std::string, AbstractControl*> toggles;
|
||||||
|
|
||||||
|
Params params;
|
||||||
|
|
||||||
|
bool hasAutoTune;
|
||||||
|
bool hasBSM;
|
||||||
|
bool hasOpenpilotLongitudinal;
|
||||||
|
bool isMetric = params.getBool("IsMetric");
|
||||||
|
bool isRelease;
|
||||||
|
bool started;
|
||||||
|
};
|
@ -1,3 +1,5 @@
|
|||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "selfdrive/ui/ui.h"
|
#include "selfdrive/ui/ui.h"
|
||||||
|
|
||||||
Params paramsMemory{"/dev/shm/params"};
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
@ -9,3 +11,41 @@ void updateFrogPilotToggles() {
|
|||||||
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
|
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
|
||||||
}).detach();
|
}).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FrogPilotConfirmationDialog::toggle(const QString &prompt_text, const QString &confirm_text, QWidget *parent) {
|
||||||
|
ConfirmationDialog d = ConfirmationDialog(prompt_text, confirm_text, tr("Reboot Later"), false, parent);
|
||||||
|
return d.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FrogPilotConfirmationDialog::toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent) {
|
||||||
|
ConfirmationDialog d = ConfirmationDialog(prompt_text, button_text, "", false, parent);
|
||||||
|
return d.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FrogPilotConfirmationDialog::yesorno(const QString &prompt_text, QWidget *parent) {
|
||||||
|
ConfirmationDialog d = ConfirmationDialog(prompt_text, tr("Yes"), tr("No"), false, parent);
|
||||||
|
return d.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
FrogPilotButtonIconControl::FrogPilotButtonIconControl(const QString &title, const QString &text, const QString &desc, const QString &icon, QWidget *parent) : AbstractControl(title, desc, icon, parent) {
|
||||||
|
btn.setText(text);
|
||||||
|
btn.setStyleSheet(R"(
|
||||||
|
QPushButton {
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 50px;
|
||||||
|
font-size: 35px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #E4E4E4;
|
||||||
|
background-color: #393939;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
color: #33E4E4E4;
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
btn.setFixedSize(250, 100);
|
||||||
|
QObject::connect(&btn, &QPushButton::clicked, this, &FrogPilotButtonIconControl::clicked);
|
||||||
|
hlayout->addWidget(&btn);
|
||||||
|
}
|
||||||
|
@ -1,3 +1,708 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||||
|
|
||||||
void updateFrogPilotToggles();
|
void updateFrogPilotToggles();
|
||||||
|
|
||||||
|
class FrogPilotConfirmationDialog : public ConfirmationDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FrogPilotConfirmationDialog(const QString &prompt_text, const QString &confirm_text,
|
||||||
|
const QString &cancel_text, const bool rich, QWidget *parent);
|
||||||
|
static bool toggle(const QString &prompt_text, const QString &confirm_text, QWidget *parent);
|
||||||
|
static bool toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent);
|
||||||
|
static bool yesorno(const QString &prompt_text, QWidget *parent);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotListWidget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit FrogPilotListWidget(QWidget *parent = nullptr) : QWidget(parent), outer_layout(this) {
|
||||||
|
outer_layout.setMargin(0);
|
||||||
|
outer_layout.setSpacing(0);
|
||||||
|
outer_layout.addLayout(&inner_layout);
|
||||||
|
inner_layout.setMargin(0);
|
||||||
|
inner_layout.setSpacing(25); // default spacing is 25
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void addItem(QWidget *w) {
|
||||||
|
inner_layout.addWidget(w);
|
||||||
|
adjustStretch();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void addItem(QLayout *layout) {
|
||||||
|
inner_layout.addLayout(layout);
|
||||||
|
adjustStretch();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setSpacing(int spacing) {
|
||||||
|
inner_layout.setSpacing(spacing);
|
||||||
|
adjustStretch();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void adjustStretch() {
|
||||||
|
if (inner_layout.stretch(inner_layout.count() - 1) > 0) {
|
||||||
|
inner_layout.setStretch(inner_layout.count() - 1, 0);
|
||||||
|
}
|
||||||
|
if (inner_layout.count() > 3) {
|
||||||
|
outer_layout.addStretch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *event) override {
|
||||||
|
QPainter p(this);
|
||||||
|
p.setPen(Qt::gray);
|
||||||
|
|
||||||
|
int visibleWidgetCount = 0;
|
||||||
|
std::vector<QRect> visibleRects;
|
||||||
|
|
||||||
|
for (int i = 0; i < inner_layout.count(); ++i) {
|
||||||
|
QWidget *widget = inner_layout.itemAt(i)->widget();
|
||||||
|
if (widget && widget->isVisible()) {
|
||||||
|
visibleWidgetCount++;
|
||||||
|
visibleRects.push_back(inner_layout.itemAt(i)->geometry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < visibleWidgetCount - 1; ++i) {
|
||||||
|
int bottom = visibleRects[i].bottom() + inner_layout.spacing() / 2;
|
||||||
|
p.drawLine(visibleRects[i].left() + 40, bottom, visibleRects[i].right() - 40, bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVBoxLayout outer_layout;
|
||||||
|
QVBoxLayout inner_layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotDualParamControl : public QFrame {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrogPilotDualParamControl(ParamControl *control1, ParamControl *control2, QWidget *parent = nullptr, bool split=false)
|
||||||
|
: QFrame(parent) {
|
||||||
|
QHBoxLayout *hlayout = new QHBoxLayout(this);
|
||||||
|
|
||||||
|
control1->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||||
|
control1->setMaximumWidth(split ? 850 : 700);
|
||||||
|
|
||||||
|
control2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||||
|
control2->setMaximumWidth(split ? 700 : 850);
|
||||||
|
|
||||||
|
hlayout->addWidget(control1);
|
||||||
|
hlayout->addWidget(control2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotButtonControl : public AbstractControl {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrogPilotButtonControl(const QString &title, const QString &text, const QString &desc = "", QWidget *parent = nullptr);
|
||||||
|
inline void setText(const QString &text) { btn.setText(text); }
|
||||||
|
inline QString text() const { return btn.text(); }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clicked();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setEnabled(bool enabled) { btn.setEnabled(enabled); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPushButton btn;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotButtonIconControl : public AbstractControl {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrogPilotButtonIconControl(const QString &title, const QString &text, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
|
||||||
|
inline void setText(const QString &text) { btn.setText(text); }
|
||||||
|
inline QString text() const { return btn.text(); }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clicked();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setEnabled(bool enabled) { btn.setEnabled(enabled); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPushButton btn;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotButtonParamControl : public ParamControl {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
FrogPilotButtonParamControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
||||||
|
const std::vector<QString> &button_texts, const int minimum_button_width = 225)
|
||||||
|
: ParamControl(param, title, desc, icon) {
|
||||||
|
const QString style = R"(
|
||||||
|
QPushButton {
|
||||||
|
border-radius: 50px;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: 500;
|
||||||
|
height:100px;
|
||||||
|
padding: 0 25 0 25;
|
||||||
|
color: #E4E4E4;
|
||||||
|
background-color: #393939;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
}
|
||||||
|
QPushButton:checked:enabled {
|
||||||
|
background-color: #33Ab4C;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
color: #33E4E4E4;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
key = param.toStdString();
|
||||||
|
int value = atoi(params.get(key).c_str());
|
||||||
|
|
||||||
|
button_group = new QButtonGroup(this);
|
||||||
|
button_group->setExclusive(true);
|
||||||
|
for (size_t i = 0; i < button_texts.size(); i++) {
|
||||||
|
QPushButton *button = new QPushButton(button_texts[i], this);
|
||||||
|
button->setCheckable(true);
|
||||||
|
button->setChecked(i == value);
|
||||||
|
button->setStyleSheet(style);
|
||||||
|
button->setMinimumWidth(minimum_button_width);
|
||||||
|
hlayout->addWidget(button);
|
||||||
|
button_group->addButton(button, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject::connect(button_group, QOverload<int, bool>::of(&QButtonGroup::buttonToggled), [=](int id, bool checked) {
|
||||||
|
if (checked) {
|
||||||
|
params.put(key, std::to_string(id));
|
||||||
|
refresh();
|
||||||
|
emit buttonClicked(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
toggle.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled(bool enable) {
|
||||||
|
for (auto btn : button_group->buttons()) {
|
||||||
|
btn->setEnabled(enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void buttonClicked(int id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string key;
|
||||||
|
Params params;
|
||||||
|
QButtonGroup *button_group;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotButtonsControl : public ParamControl {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
FrogPilotButtonsControl(const QString &title, const QString &desc, const QString &icon,
|
||||||
|
const std::vector<QString> &button_texts, const int minimum_button_width = 225)
|
||||||
|
: ParamControl("", title, desc, icon) {
|
||||||
|
const QString style = R"(
|
||||||
|
QPushButton {
|
||||||
|
border-radius: 50px;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: 500;
|
||||||
|
height: 100px;
|
||||||
|
padding: 0 25px 0 25px;
|
||||||
|
color: #E4E4E4;
|
||||||
|
background-color: #393939;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #33Ab4C;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
color: #33E4E4E4;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
button_group = new QButtonGroup(this);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < button_texts.size(); i++) {
|
||||||
|
QPushButton *button = new QPushButton(button_texts[i], this);
|
||||||
|
button->setStyleSheet(style);
|
||||||
|
button->setMinimumWidth(minimum_button_width);
|
||||||
|
hlayout->addWidget(button);
|
||||||
|
button_group->addButton(button, static_cast<int>(i));
|
||||||
|
|
||||||
|
connect(button, &QPushButton::clicked, this, [this, i]() {
|
||||||
|
emit buttonClicked(static_cast<int>(i));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void buttonClicked(int id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QButtonGroup *button_group;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotButtonsParamControl : public ParamControl {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
FrogPilotButtonsParamControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
||||||
|
const std::vector<std::pair<QString, QString>> &button_params)
|
||||||
|
: ParamControl(param, title, desc, icon) {
|
||||||
|
const QString style = R"(
|
||||||
|
QPushButton {
|
||||||
|
border-radius: 50px;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: 500;
|
||||||
|
height:100px;
|
||||||
|
padding: 0 25 0 25;
|
||||||
|
color: #E4E4E4;
|
||||||
|
background-color: #393939;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
}
|
||||||
|
QPushButton:checked:enabled {
|
||||||
|
background-color: #33Ab4C;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
color: #33E4E4E4;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
button_group = new QButtonGroup(this);
|
||||||
|
button_group->setExclusive(true);
|
||||||
|
|
||||||
|
for (const auto ¶m_pair : button_params) {
|
||||||
|
const QString ¶m_toggle = param_pair.first;
|
||||||
|
const QString &button_text = param_pair.second;
|
||||||
|
|
||||||
|
QPushButton *button = new QPushButton(button_text, this);
|
||||||
|
button->setCheckable(true);
|
||||||
|
|
||||||
|
bool value = params.getBool(param_toggle.toStdString());
|
||||||
|
button->setChecked(value);
|
||||||
|
button->setStyleSheet(style);
|
||||||
|
button->setMinimumWidth(225);
|
||||||
|
hlayout->addWidget(button);
|
||||||
|
|
||||||
|
QObject::connect(button, &QPushButton::toggled, this, [=](bool checked) {
|
||||||
|
if (checked) {
|
||||||
|
for (const auto &inner_param_pair : button_params) {
|
||||||
|
const QString &inner_param = inner_param_pair.first;
|
||||||
|
params.putBool(inner_param.toStdString(), inner_param == param_toggle);
|
||||||
|
}
|
||||||
|
refresh();
|
||||||
|
emit buttonClicked();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
button_group->addButton(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled(bool enable) {
|
||||||
|
for (auto btn : button_group->buttons()) {
|
||||||
|
btn->setEnabled(enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void buttonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Params params;
|
||||||
|
QButtonGroup *button_group;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotParamManageControl : public ParamControl {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrogPilotParamManageControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr, bool hideToggle = false)
|
||||||
|
: ParamControl(param, title, desc, icon, parent),
|
||||||
|
hideToggle(hideToggle),
|
||||||
|
key(param.toStdString()),
|
||||||
|
manageButton(new ButtonControl(tr(""), tr("MANAGE"), tr(""))) {
|
||||||
|
hlayout->insertWidget(hlayout->indexOf(&toggle) - 1, manageButton);
|
||||||
|
|
||||||
|
connect(this, &ToggleControl::toggleFlipped, this, [this](bool state) {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(manageButton, &ButtonControl::clicked, this, &FrogPilotParamManageControl::manageButtonClicked);
|
||||||
|
|
||||||
|
if (hideToggle) {
|
||||||
|
toggle.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
ParamControl::refresh();
|
||||||
|
manageButton->setVisible(params.getBool(key) || hideToggle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled(bool enabled) {
|
||||||
|
manageButton->setEnabled(enabled);
|
||||||
|
toggle.setEnabled(enabled);
|
||||||
|
toggle.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void showEvent(QShowEvent *event) override {
|
||||||
|
ParamControl::showEvent(event);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void manageButtonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool hideToggle;
|
||||||
|
std::string key;
|
||||||
|
Params params;
|
||||||
|
ButtonControl *manageButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotParamToggleControl : public ParamControl {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
FrogPilotParamToggleControl(const QString ¶m, const QString &title, const QString &desc,
|
||||||
|
const QString &icon, const std::vector<QString> &button_params,
|
||||||
|
const std::vector<QString> &button_texts, QWidget *parent = nullptr,
|
||||||
|
const int minimum_button_width = 225)
|
||||||
|
: ParamControl(param, title, desc, icon, parent) {
|
||||||
|
|
||||||
|
key = param.toStdString();
|
||||||
|
|
||||||
|
connect(this, &ToggleControl::toggleFlipped, this, [this](bool state) {
|
||||||
|
refreshButtons(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
const QString style = R"(
|
||||||
|
QPushButton {
|
||||||
|
border-radius: 50px;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: 500;
|
||||||
|
height:100px;
|
||||||
|
padding: 0 25 0 25;
|
||||||
|
color: #E4E4E4;
|
||||||
|
background-color: #393939;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
}
|
||||||
|
QPushButton:checked:enabled {
|
||||||
|
background-color: #33Ab4C;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
color: #33E4E4E4;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
button_group = new QButtonGroup(this);
|
||||||
|
button_group->setExclusive(false);
|
||||||
|
this->button_params = button_params;
|
||||||
|
|
||||||
|
for (int i = 0; i < button_texts.size(); ++i) {
|
||||||
|
QPushButton *button = new QPushButton(button_texts[i], this);
|
||||||
|
button->setCheckable(true);
|
||||||
|
button->setStyleSheet(style);
|
||||||
|
button->setMinimumWidth(minimum_button_width);
|
||||||
|
button_group->addButton(button, i);
|
||||||
|
|
||||||
|
connect(button, &QPushButton::clicked, [this, i](bool checked) {
|
||||||
|
params.putBool(this->button_params[i].toStdString(), checked);
|
||||||
|
button_group->button(i)->setChecked(checked);
|
||||||
|
emit buttonClicked(checked);
|
||||||
|
emit buttonTypeClicked(i);
|
||||||
|
});
|
||||||
|
|
||||||
|
hlayout->insertWidget(hlayout->indexOf(&toggle) - 1, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
bool state = params.getBool(key);
|
||||||
|
if (state != toggle.on) {
|
||||||
|
toggle.togglePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshButtons(state);
|
||||||
|
updateButtonStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshButtons(bool state) {
|
||||||
|
for (QAbstractButton *button : button_group->buttons()) {
|
||||||
|
button->setVisible(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateButtonStates() {
|
||||||
|
for (int i = 0; i < button_group->buttons().size(); ++i) {
|
||||||
|
bool checked = params.getBool(button_params[i].toStdString());
|
||||||
|
QAbstractButton *button = button_group->button(i);
|
||||||
|
if (button) {
|
||||||
|
button->setChecked(checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showEvent(QShowEvent *event) override {
|
||||||
|
refresh();
|
||||||
|
QWidget::showEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void buttonClicked(const bool checked);
|
||||||
|
void buttonTypeClicked(int i);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string key;
|
||||||
|
Params params;
|
||||||
|
QButtonGroup *button_group;
|
||||||
|
std::vector<QString> button_params;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotParamValueControl : public ParamControl {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrogPilotParamValueControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
||||||
|
const float &minValue, const float &maxValue, const std::map<int, QString> &valueLabels,
|
||||||
|
QWidget *parent = nullptr, const bool &loop = true, const QString &label = "",
|
||||||
|
const float &division = 1.0f, const float &interval = 1.0f)
|
||||||
|
: ParamControl(param, title, desc, icon, parent),
|
||||||
|
minValue(minValue), maxValue(maxValue), valueLabelMappings(valueLabels), loop(loop), labelText(label),
|
||||||
|
division(division), interval(interval), previousValue(0.0f), value(0.0f) {
|
||||||
|
key = param.toStdString();
|
||||||
|
|
||||||
|
valueLabel = new QLabel(this);
|
||||||
|
hlayout->addWidget(valueLabel);
|
||||||
|
|
||||||
|
QPushButton *decrementButton = createButton("-", this);
|
||||||
|
QPushButton *incrementButton = createButton("+", this);
|
||||||
|
|
||||||
|
hlayout->addWidget(decrementButton);
|
||||||
|
hlayout->addWidget(incrementButton);
|
||||||
|
|
||||||
|
countdownTimer = new QTimer(this);
|
||||||
|
countdownTimer->setInterval(150);
|
||||||
|
countdownTimer->setSingleShot(true);
|
||||||
|
|
||||||
|
connect(countdownTimer, &QTimer::timeout, this, &FrogPilotParamValueControl::handleTimeout);
|
||||||
|
|
||||||
|
connect(decrementButton, &QPushButton::pressed, this, [=]() { updateValue(-interval); });
|
||||||
|
connect(incrementButton, &QPushButton::pressed, this, [=]() { updateValue(interval); });
|
||||||
|
|
||||||
|
connect(decrementButton, &QPushButton::released, this, &FrogPilotParamValueControl::restartTimer);
|
||||||
|
connect(incrementButton, &QPushButton::released, this, &FrogPilotParamValueControl::restartTimer);
|
||||||
|
|
||||||
|
toggle.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void restartTimer() {
|
||||||
|
countdownTimer->stop();
|
||||||
|
countdownTimer->start();
|
||||||
|
|
||||||
|
emit valueChanged(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleTimeout() {
|
||||||
|
previousValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateValue(float intervalChange) {
|
||||||
|
int previousValueAdjusted = round(previousValue * 100) / 100 / intervalChange;
|
||||||
|
int valueAdjusted = round(value * 100) / 100 / intervalChange;
|
||||||
|
|
||||||
|
if (std::fabs(previousValueAdjusted - valueAdjusted) > 5 && std::fmod(valueAdjusted, 5) == 0) {
|
||||||
|
intervalChange *= 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
value += intervalChange;
|
||||||
|
|
||||||
|
if (loop) {
|
||||||
|
if (value < minValue) {
|
||||||
|
value = maxValue;
|
||||||
|
} else if (value > maxValue) {
|
||||||
|
value = minValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = std::max(minValue, std::min(maxValue, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
params.putFloat(key, value);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
value = params.getFloat(key);
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
auto it = valueLabelMappings.find(value);
|
||||||
|
int decimals = interval < 1.0f ? static_cast<int>(-std::log10(interval)) : 2;
|
||||||
|
|
||||||
|
if (division > 1.0f) {
|
||||||
|
text = QString::number(value / division, 'g', division >= 10.0f ? 4 : 3);
|
||||||
|
} else {
|
||||||
|
if (it != valueLabelMappings.end()) {
|
||||||
|
text = it->second;
|
||||||
|
} else {
|
||||||
|
if (value >= 100.0f) {
|
||||||
|
text = QString::number(value, 'f', 0);
|
||||||
|
} else {
|
||||||
|
text = QString::number(value, interval < 1.0f ? 'f' : 'g', decimals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!labelText.isEmpty()) {
|
||||||
|
text += labelText;
|
||||||
|
}
|
||||||
|
|
||||||
|
valueLabel->setText(text);
|
||||||
|
valueLabel->setStyleSheet("QLabel { color: #E0E879; }");
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateControl(float newMinValue, float newMaxValue, const QString &newLabel, float newDivision = 1.0f) {
|
||||||
|
minValue = newMinValue;
|
||||||
|
maxValue = newMaxValue;
|
||||||
|
labelText = newLabel;
|
||||||
|
division = newDivision;
|
||||||
|
}
|
||||||
|
|
||||||
|
void showEvent(QShowEvent *event) override {
|
||||||
|
refresh();
|
||||||
|
previousValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void valueChanged(float value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Params params;
|
||||||
|
|
||||||
|
bool loop;
|
||||||
|
|
||||||
|
float division;
|
||||||
|
float interval;
|
||||||
|
float maxValue;
|
||||||
|
float minValue;
|
||||||
|
float previousValue;
|
||||||
|
float value;
|
||||||
|
|
||||||
|
QLabel *valueLabel;
|
||||||
|
QString labelText;
|
||||||
|
|
||||||
|
std::map<int, QString> valueLabelMappings;
|
||||||
|
std::string key;
|
||||||
|
|
||||||
|
QTimer *countdownTimer;
|
||||||
|
|
||||||
|
QPushButton *createButton(const QString &text, QWidget *parent) {
|
||||||
|
QPushButton *button = new QPushButton(text, parent);
|
||||||
|
button->setFixedSize(150, 100);
|
||||||
|
button->setAutoRepeat(true);
|
||||||
|
button->setAutoRepeatInterval(150);
|
||||||
|
button->setAutoRepeatDelay(500);
|
||||||
|
button->setStyleSheet(R"(
|
||||||
|
QPushButton {
|
||||||
|
border-radius: 50px;
|
||||||
|
font-size: 50px;
|
||||||
|
font-weight: 500;
|
||||||
|
height: 100px;
|
||||||
|
padding: 0 25 0 25;
|
||||||
|
color: #E4E4E4;
|
||||||
|
background-color: #393939;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrogPilotParamValueToggleControl : public FrogPilotParamValueControl {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrogPilotParamValueToggleControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
||||||
|
const float &minValue, const float &maxValue, const std::map<int, QString> &valueLabels,
|
||||||
|
QWidget *parent = nullptr, const bool &loop = true, const QString &label = "",
|
||||||
|
const float &division = 1.0f, const float &interval = 1.0f,
|
||||||
|
const std::vector<QString> &button_params = std::vector<QString>(), const std::vector<QString> &button_texts = std::vector<QString>(),
|
||||||
|
const int minimum_button_width = 225)
|
||||||
|
: FrogPilotParamValueControl(param, title, desc, icon, minValue, maxValue, valueLabels, parent, loop, label, division, interval) {
|
||||||
|
|
||||||
|
const QString style = R"(
|
||||||
|
QPushButton {
|
||||||
|
border-radius: 50px;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: 500;
|
||||||
|
height: 100px;
|
||||||
|
padding: 0 25 0 25;
|
||||||
|
color: #E4E4E4;
|
||||||
|
background-color: #393939;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
}
|
||||||
|
QPushButton:checked:enabled {
|
||||||
|
background-color: #33Ab4C;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
color: #33E4E4E4;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
button_group = new QButtonGroup(this);
|
||||||
|
button_group->setExclusive(false);
|
||||||
|
|
||||||
|
for (int i = 0; i < button_texts.size(); ++i) {
|
||||||
|
QPushButton *button = new QPushButton(button_texts[i], this);
|
||||||
|
button->setCheckable(true);
|
||||||
|
button->setChecked(params.getBool(button_params[i].toStdString()));
|
||||||
|
button->setStyleSheet(style);
|
||||||
|
button->setMinimumWidth(minimum_button_width);
|
||||||
|
button_group->addButton(button, i);
|
||||||
|
|
||||||
|
connect(button, &QPushButton::clicked, [this, button_params, i](bool checked) {
|
||||||
|
params.putBool(button_params[i].toStdString(), checked);
|
||||||
|
emit buttonClicked();
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
buttons[button_params[i]] = button;
|
||||||
|
hlayout->insertWidget(3, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
FrogPilotParamValueControl::refresh();
|
||||||
|
|
||||||
|
auto keys = buttons.keys();
|
||||||
|
for (const QString ¶m : keys) {
|
||||||
|
QPushButton *button = buttons.value(param);
|
||||||
|
button->setChecked(params.getBool(param.toStdString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void buttonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Params params;
|
||||||
|
QButtonGroup *button_group;
|
||||||
|
QMap<QString, QPushButton*> buttons;
|
||||||
|
};
|
||||||
|
@ -23,7 +23,10 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc",
|
|||||||
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
|
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
|
||||||
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
|
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
|
||||||
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
|
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
|
||||||
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"]
|
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc",
|
||||||
|
"../frogpilot/ui/qt/widgets/frogpilot_controls.cc",
|
||||||
|
"../frogpilot/ui/qt/offroad/control_settings.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc",
|
||||||
|
"../frogpilot/ui/qt/offroad/visual_settings.cc"]
|
||||||
|
|
||||||
qt_env['CPPDEFINES'] = []
|
qt_env['CPPDEFINES'] = []
|
||||||
if maps:
|
if maps:
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/network/networking.h"
|
#include "selfdrive/ui/qt/network/networking.h"
|
||||||
|
|
||||||
@ -23,6 +24,10 @@
|
|||||||
#include "selfdrive/ui/qt/util.h"
|
#include "selfdrive/ui/qt/util.h"
|
||||||
#include "selfdrive/ui/qt/qt_window.h"
|
#include "selfdrive/ui/qt/qt_window.h"
|
||||||
|
|
||||||
|
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
|
||||||
|
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||||
|
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
|
||||||
|
|
||||||
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||||
// param, title, desc, icon
|
// param, title, desc, icon
|
||||||
std::vector<std::tuple<QString, QString, QString, QString>> toggle_defs{
|
std::vector<std::tuple<QString, QString, QString, QString>> toggle_defs{
|
||||||
@ -122,6 +127,10 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
connect(toggles["ExperimentalLongitudinalEnabled"], &ToggleControl::toggleFlipped, [=]() {
|
connect(toggles["ExperimentalLongitudinalEnabled"], &ToggleControl::toggleFlipped, [=]() {
|
||||||
updateToggles();
|
updateToggles();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(toggles["IsMetric"], &ToggleControl::toggleFlipped, [=]() {
|
||||||
|
updateMetric();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TogglesPanel::updateState(const UIState &s) {
|
void TogglesPanel::updateState(const UIState &s) {
|
||||||
@ -345,6 +354,16 @@ void DevicePanel::poweroff() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsWindow::hideEvent(QHideEvent *event) {
|
||||||
|
closeParentToggle();
|
||||||
|
|
||||||
|
parentToggleOpen = false;
|
||||||
|
subParentToggleOpen = false;
|
||||||
|
subSubParentToggleOpen = false;
|
||||||
|
|
||||||
|
previousScrollPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsWindow::showEvent(QShowEvent *event) {
|
void SettingsWindow::showEvent(QShowEvent *event) {
|
||||||
setCurrentPanel(0);
|
setCurrentPanel(0);
|
||||||
}
|
}
|
||||||
@ -381,7 +400,20 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
|||||||
close_btn->setFixedSize(200, 200);
|
close_btn->setFixedSize(200, 200);
|
||||||
sidebar_layout->addSpacing(45);
|
sidebar_layout->addSpacing(45);
|
||||||
sidebar_layout->addWidget(close_btn, 0, Qt::AlignCenter);
|
sidebar_layout->addWidget(close_btn, 0, Qt::AlignCenter);
|
||||||
QObject::connect(close_btn, &QPushButton::clicked, this, &SettingsWindow::closeSettings);
|
QObject::connect(close_btn, &QPushButton::clicked, [this]() {
|
||||||
|
if (subSubParentToggleOpen) {
|
||||||
|
closeSubSubParentToggle();
|
||||||
|
subSubParentToggleOpen = false;
|
||||||
|
} else if (subParentToggleOpen) {
|
||||||
|
closeSubParentToggle();
|
||||||
|
subParentToggleOpen = false;
|
||||||
|
} else if (parentToggleOpen) {
|
||||||
|
closeParentToggle();
|
||||||
|
parentToggleOpen = false;
|
||||||
|
} else {
|
||||||
|
closeSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// setup panels
|
// setup panels
|
||||||
DevicePanel *device = new DevicePanel(this);
|
DevicePanel *device = new DevicePanel(this);
|
||||||
@ -390,12 +422,24 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
|||||||
|
|
||||||
TogglesPanel *toggles = new TogglesPanel(this);
|
TogglesPanel *toggles = new TogglesPanel(this);
|
||||||
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
|
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
|
||||||
|
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
|
||||||
|
|
||||||
|
FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
|
||||||
|
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {parentToggleOpen=true;});
|
||||||
|
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openSubParentToggle, this, [this]() {subParentToggleOpen=true;});
|
||||||
|
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openSubSubParentToggle, this, [this]() {subSubParentToggleOpen=true;});
|
||||||
|
|
||||||
|
FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this);
|
||||||
|
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {parentToggleOpen=true;});
|
||||||
|
|
||||||
QList<QPair<QString, QWidget *>> panels = {
|
QList<QPair<QString, QWidget *>> panels = {
|
||||||
{tr("Device"), device},
|
{tr("Device"), device},
|
||||||
{tr("Network"), new Networking(this)},
|
{tr("Network"), new Networking(this)},
|
||||||
{tr("Toggles"), toggles},
|
{tr("Toggles"), toggles},
|
||||||
{tr("Software"), new SoftwarePanel(this)},
|
{tr("Software"), new SoftwarePanel(this)},
|
||||||
|
{tr("Controls"), frogpilotControls},
|
||||||
|
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
|
||||||
|
{tr("Visuals"), frogpilotVisuals},
|
||||||
};
|
};
|
||||||
|
|
||||||
nav_btns = new QButtonGroup(this);
|
nav_btns = new QButtonGroup(this);
|
||||||
@ -428,7 +472,25 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
|||||||
ScrollView *panel_frame = new ScrollView(panel, this);
|
ScrollView *panel_frame = new ScrollView(panel, this);
|
||||||
panel_widget->addWidget(panel_frame);
|
panel_widget->addWidget(panel_frame);
|
||||||
|
|
||||||
|
if (name == tr("Controls") || name == tr("Visuals")) {
|
||||||
|
QScrollBar *scrollbar = panel_frame->verticalScrollBar();
|
||||||
|
|
||||||
|
QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) {
|
||||||
|
if (!parentToggleOpen) {
|
||||||
|
previousScrollPosition = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() {
|
||||||
|
if (!parentToggleOpen) {
|
||||||
|
panel_frame->restorePosition(previousScrollPosition);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
|
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
|
||||||
|
closeParentToggle();
|
||||||
|
previousScrollPosition = 0;
|
||||||
btn->setChecked(true);
|
btn->setChecked(true);
|
||||||
panel_widget->setCurrentWidget(w);
|
panel_widget->setCurrentWidget(w);
|
||||||
});
|
});
|
||||||
|
@ -26,17 +26,33 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
// FrogPilot widgets
|
||||||
|
void hideEvent(QHideEvent *event) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void closeSettings();
|
void closeSettings();
|
||||||
void reviewTrainingGuide();
|
void reviewTrainingGuide();
|
||||||
void showDriverView();
|
void showDriverView();
|
||||||
void expandToggleDescription(const QString ¶m);
|
void expandToggleDescription(const QString ¶m);
|
||||||
|
|
||||||
|
// FrogPilot signals
|
||||||
|
void closeParentToggle();
|
||||||
|
void closeSubParentToggle();
|
||||||
|
void closeSubSubParentToggle();
|
||||||
|
void updateMetric();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPushButton *sidebar_alert_widget;
|
QPushButton *sidebar_alert_widget;
|
||||||
QWidget *sidebar_widget;
|
QWidget *sidebar_widget;
|
||||||
QButtonGroup *nav_btns;
|
QButtonGroup *nav_btns;
|
||||||
QStackedWidget *panel_widget;
|
QStackedWidget *panel_widget;
|
||||||
|
|
||||||
|
// FrogPilot variables
|
||||||
|
bool parentToggleOpen;
|
||||||
|
bool subParentToggleOpen;
|
||||||
|
bool subSubParentToggleOpen;
|
||||||
|
|
||||||
|
int previousScrollPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DevicePanel : public ListWidget {
|
class DevicePanel : public ListWidget {
|
||||||
@ -62,6 +78,10 @@ public:
|
|||||||
explicit TogglesPanel(SettingsWindow *parent);
|
explicit TogglesPanel(SettingsWindow *parent);
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// FrogPilot signals
|
||||||
|
void updateMetric();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void expandToggleDescription(const QString ¶m);
|
void expandToggleDescription(const QString ¶m);
|
||||||
|
|
||||||
|
@ -132,6 +132,10 @@ public:
|
|||||||
toggle.update();
|
toggle.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
toggle.togglePosition();
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void toggleFlipped(bool state);
|
void toggleFlipped(bool state);
|
||||||
|
|
||||||
|
@ -44,6 +44,10 @@ ScrollView::ScrollView(QWidget *w, QWidget *parent) : QScrollArea(parent) {
|
|||||||
scroller->setScrollerProperties(sp);
|
scroller->setScrollerProperties(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScrollView::restorePosition(int previousScrollPosition) {
|
||||||
|
verticalScrollBar()->setValue(previousScrollPosition);
|
||||||
|
}
|
||||||
|
|
||||||
void ScrollView::hideEvent(QHideEvent *e) {
|
void ScrollView::hideEvent(QHideEvent *e) {
|
||||||
verticalScrollBar()->setValue(0);
|
verticalScrollBar()->setValue(0);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@ class ScrollView : public QScrollArea {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ScrollView(QWidget *w = nullptr, QWidget *parent = nullptr);
|
explicit ScrollView(QWidget *w = nullptr, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
// FrogPilot functions
|
||||||
|
void restorePosition(int previousScrollPosition);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void hideEvent(QHideEvent *e) override;
|
void hideEvent(QHideEvent *e) override;
|
||||||
};
|
};
|
||||||
|
@ -218,6 +218,7 @@ static void update_state(UIState *s) {
|
|||||||
}
|
}
|
||||||
if (sm.updated("deviceState")) {
|
if (sm.updated("deviceState")) {
|
||||||
auto deviceState = sm["deviceState"].getDeviceState();
|
auto deviceState = sm["deviceState"].getDeviceState();
|
||||||
|
scene.online = deviceState.getNetworkType() != cereal::DeviceState::NetworkType::NONE;
|
||||||
}
|
}
|
||||||
if (sm.updated("frogpilotCarControl")) {
|
if (sm.updated("frogpilotCarControl")) {
|
||||||
auto frogpilotCarControl = sm["frogpilotCarControl"].getFrogpilotCarControl();
|
auto frogpilotCarControl = sm["frogpilotCarControl"].getFrogpilotCarControl();
|
||||||
|