FrogPilot setup - Toggle panels
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,100 @@
|
||||
#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:
|
||||
QString processModelName(const QString &modelName);
|
||||
|
||||
void hideSubToggles();
|
||||
void hideSubSubToggles();
|
||||
void hideToggles();
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void startDownloadAllModels();
|
||||
void updateCalibrationDescription();
|
||||
void updateCarToggles();
|
||||
void updateMetric();
|
||||
void updateModelLabels();
|
||||
void updateState(const UIState &s);
|
||||
|
||||
ButtonControl *deleteModelBtn;
|
||||
ButtonControl *downloadAllModelsBtn;
|
||||
ButtonControl *downloadModelBtn;
|
||||
ButtonControl *selectModelBtn;
|
||||
|
||||
FrogPilotParamValueToggleControl *steerRatioToggle;
|
||||
|
||||
std::set<QString> aggressivePersonalityKeys = {"PersonalityInfo", "AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "ResetAggressivePersonality"};
|
||||
std::set<QString> aolKeys = {"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "HideAOLStatusBar", "PauseAOLOnBrake"};
|
||||
std::set<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CENavigation", "CESignal", "CEStopLights", "HideCEMStatusBar"};
|
||||
std::set<QString> customdrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"};
|
||||
std::set<QString> deviceManagementKeys = {"DeviceShutdown", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads", "OfflineMode"};
|
||||
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> modelManagementKeys = {"AutomaticallyUpdateModels", "ModelRandomizer", "DeleteModel", "DownloadModel", "DownloadAllModels", "SelectModel", "ResetCalibrations"};
|
||||
std::set<QString> modelRandomizerKeys = {"ManageBlacklistedModels", "ResetScores", "ReviewScores"};
|
||||
std::set<QString> mtscKeys = {"DisableMTSCSmoothing", "MTSCAggressiveness", "MTSCCurvatureCheck"};
|
||||
std::set<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStandstill", "MapGears", "PauseLateralSpeed", "ReverseCruise", "SetSpeedOffset"};
|
||||
std::set<QString> relaxedPersonalityKeys = {"PersonalityInfo", "RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDanger", "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 = {"PersonalityInfo", "StandardFollow", "StandardJerkAcceleration", "StandardJerkDanger", "StandardJerkSpeed", "ResetStandardPersonality"};
|
||||
std::set<QString> trafficPersonalityKeys = {"PersonalityInfo", "TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "ResetTrafficPersonality"};
|
||||
std::set<QString> visionTurnControlKeys = {"CurveSensitivity", "DisableVTSCSmoothing", "TurnAggressiveness"};
|
||||
|
||||
std::map<std::string, AbstractControl*> toggles;
|
||||
|
||||
QList<LabelControl*> labelControls;
|
||||
|
||||
QDir modelDir{"/data/models/"};
|
||||
|
||||
Params params;
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
Params paramsStorage{"/persist/params"};
|
||||
|
||||
bool customPersonalitiesOpen;
|
||||
bool disableOpenpilotLongitudinal;
|
||||
bool hasAutoTune;
|
||||
bool hasCommaNNFFSupport;
|
||||
bool hasNNFFLog;
|
||||
bool hasOpenpilotLongitudinal;
|
||||
bool hasPCMCruise;
|
||||
bool hasDashSpeedLimits;
|
||||
bool haveModelsDownloaded;
|
||||
bool isGM;
|
||||
bool isHKGCanFd;
|
||||
bool isMetric = params.getBool("IsMetric");
|
||||
bool isRelease;
|
||||
bool isToyota;
|
||||
bool modelDeleting;
|
||||
bool modelDownloading;
|
||||
bool modelManagementOpen;
|
||||
bool modelRandomizer;
|
||||
bool modelsDownloaded;
|
||||
bool slcOpen;
|
||||
bool started;
|
||||
|
||||
float steerRatioStock;
|
||||
|
||||
QStringList availableModelNames;
|
||||
QStringList availableModels;
|
||||
QStringList experimentalModels;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
Following Distance:
|
||||
Represents the desired time-based distance from the lead vehicle in seconds. This value adjusts how closely openpilot follows the car in front. It is not an exact value but a more representative value.
|
||||
|
||||
Real-World Example: If this value is set to 1.5 seconds, and you’re driving at 50 mph (80 kph), openpilot aims to keep a distance of about 110 feet (33.5 meters) from the car in front. It means if the car in front suddenly stops, you have 1.5 seconds to react and stop safely.
|
||||
|
||||
Acceleration Jerk:
|
||||
Controls how quickly the car speeds up or slows down.
|
||||
|
||||
Real-World Example: If you like your rides to be smooth and gentle, you can set this value higher. So, if the car in front speeds up or slows down suddenly, openpilot will change its speed more slowly to avoid jerky movements.
|
||||
|
||||
Danger Zone Jerk:
|
||||
How careful the car is when it gets close to other cars or obstacles.
|
||||
|
||||
Real-World Example: If there’s a much slower car ahead of you, setting a higher danger zone will make openpilot more cautious. It will brake sooner and harder if the speed difference is big or the distance is small, making sure you don’t get too close too quickly.
|
||||
|
||||
Speed Control Jerk:
|
||||
How smoothly the car changes its speed overall.
|
||||
|
||||
Real-World Example: If you set this value higher, openpilot will change the car's speed more gradually to adjust to higher/lower speed limits and other speed change conditions where there's no lead. So, when you need to speed up to match the set cruise speed or slow down because of traffic, openpilot will do it gently, making the ride smoother.
|
||||
|
||||
In Summary:
|
||||
|
||||
Following Distance: Sets how far to stay behind the car in front, based on time.
|
||||
Acceleration Jerk: Controls how smoothly the car changes speed.
|
||||
Danger Zone Jerk: Adjusts how cautious the car is when getting close to other cars or obstacles.
|
||||
Speed Control Jerk: Controls the overall smoothness of speed changes when there's no lead in front.
|
||||
@@ -0,0 +1,313 @@
|
||||
#include <QDir>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||
|
||||
QStringList getCarNames(const QString &carMake, QMap<QString, QString> &carModels) {
|
||||
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["genesis"] = "hyundai";
|
||||
makeMap["gmc"] = "gm";
|
||||
makeMap["holden"] = "gm";
|
||||
makeMap["honda"] = "honda";
|
||||
makeMap["hyundai"] = "hyundai";
|
||||
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 targetFolder = makeMap.value(carMake, carMake);
|
||||
QFile file(QString("../car/%1/values.py").arg(targetFolder));
|
||||
QStringList names;
|
||||
QSet<QString> uniqueNames;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return names;
|
||||
|
||||
QTextStream in(&file);
|
||||
QString fileContent = in.readAll();
|
||||
file.close();
|
||||
|
||||
fileContent.remove(QRegularExpression("#[^\n]*"));
|
||||
|
||||
QRegularExpression carModelRegex(R"delimiter((\w+)\s*=\s*\w+\s*\(\s*\[([\s\S]*?)\]\s*,)delimiter");
|
||||
QRegularExpression carDocsRegex(R"delimiter(CarDocs\(\s*"([^"]+)"[^)]*\))delimiter");
|
||||
|
||||
QRegularExpressionMatchIterator carModelIt = carModelRegex.globalMatch(fileContent);
|
||||
while (carModelIt.hasNext()) {
|
||||
QRegularExpressionMatch carModelMatch = carModelIt.next();
|
||||
QString platform = carModelMatch.captured(1);
|
||||
QString platformSection = carModelMatch.captured(2);
|
||||
|
||||
QRegularExpressionMatchIterator carDocsIt = carDocsRegex.globalMatch(platformSection);
|
||||
while (carDocsIt.hasNext()) {
|
||||
QRegularExpressionMatch match = carDocsIt.next();
|
||||
QString carName = match.captured(1);
|
||||
|
||||
if (carName.contains(QRegularExpression("^[A-Za-z0-9 Š.()-]+$")) && carName.count(" ") >= 1) {
|
||||
QStringList nameParts = carName.split(" ");
|
||||
if (nameParts.contains(carMake, Qt::CaseInsensitive)) {
|
||||
if (!uniqueNames.contains(carName)) {
|
||||
names << carName;
|
||||
carModels[carName] = platform;
|
||||
uniqueNames.insert(carName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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", "Buick", "Cadillac", "Chevrolet", "Chrysler", "Dodge", "Ford", "Genesis",
|
||||
"GMC", "Holden", "Honda", "Hyundai", "Jeep", "Kia", "Lexus", "Lincoln", "MAN", "Mazda",
|
||||
"Nissan", "Ram", "SEAT", "Škoda", "Subaru", "Tesla", "Toyota", "Volkswagen",
|
||||
};
|
||||
|
||||
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;
|
||||
QString modelIdentifier = carModels.value(newModelSelection);
|
||||
params.putNonBlocking("CarModel", modelIdentifier.toStdString());
|
||||
params.putNonBlocking("CarModelName", 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, [this](bool state) {
|
||||
if (state) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely disable openpilot longitudinal control?"), this)) {
|
||||
params.putBool("DisableOpenpilotLongitudinal", state);
|
||||
if (started) {
|
||||
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
disableOpenpilotLong->refresh();
|
||||
}
|
||||
} else {
|
||||
params.putBool("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."), ""},
|
||||
{"VoltSNG", tr("2017 Volt SNG"), tr("Enable the 'Stop and Go' hack for 2017 Chevy Volts."), ""},
|
||||
|
||||
{"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 *vehicleToggle;
|
||||
|
||||
if (param == "ToyotaDoors") {
|
||||
std::vector<QString> lockToggles{"LockDoors", "UnlockDoors"};
|
||||
std::vector<QString> lockToggleNames{tr("Lock"), tr("Unlock")};
|
||||
vehicleToggle = new FrogPilotParamToggleControl(param, title, desc, icon, lockToggles, lockToggleNames);
|
||||
|
||||
} else if (param == "ClusterOffset") {
|
||||
vehicleToggle = 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")},
|
||||
{"FrogsGoMooTune", tr("FrogPilot")},
|
||||
};
|
||||
|
||||
FrogPilotButtonsParamControl *toyotaTuneToggle = new FrogPilotButtonsParamControl(param, title, desc, icon, tuneOptions);
|
||||
vehicleToggle = toyotaTuneToggle;
|
||||
|
||||
QObject::connect(toyotaTuneToggle, &FrogPilotButtonsParamControl::buttonClicked, [this]() {
|
||||
if (started) {
|
||||
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
vehicleToggle = new ParamControl(param, title, desc, icon, this);
|
||||
}
|
||||
|
||||
vehicleToggle->setVisible(false);
|
||||
addItem(vehicleToggle);
|
||||
toggles[param.toStdString()] = vehicleToggle;
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(vehicleToggle), &ToggleControl::toggleFlipped, &updateFrogPilotToggles);
|
||||
QObject::connect(static_cast<FrogPilotParamToggleControl*>(vehicleToggle), &FrogPilotParamToggleControl::buttonTypeClicked, &updateFrogPilotToggles);
|
||||
|
||||
QObject::connect(vehicleToggle, &AbstractControl::showDescriptionEvent, [this]() {
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
std::set<QString> rebootKeys = {"CrosstrekTorque"};
|
||||
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]() {
|
||||
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(params.get("CarModelName").empty() ? "CarModel" : "CarModelName"));
|
||||
}
|
||||
setModels();
|
||||
updateCarToggles();
|
||||
}).detach();
|
||||
});
|
||||
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVehiclesPanel::updateState);
|
||||
|
||||
carMake = QString::fromStdString(params.get("CarMake"));
|
||||
carModel = QString::fromStdString(params.get(params.get("CarModelName").empty() ? "CarModel" : "CarModelName"));
|
||||
|
||||
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 = hasLongitudinalControl(CP);
|
||||
hasSNG = CP.getMinEnableSpeed() <= 0;
|
||||
isGMPCMCruise = CP.getCarName() == "gm" && CP.getPcmCruise();
|
||||
isImpreza = carFingerprint == "SUBARU_IMPREZA";
|
||||
isVolt = carFingerprint == "CHEVROLET_VOLT";
|
||||
} else {
|
||||
hasExperimentalOpenpilotLongitudinal = false;
|
||||
hasOpenpilotLongitudinal = true;
|
||||
hasSNG = false;
|
||||
isImpreza = true;
|
||||
isVolt = true;
|
||||
}
|
||||
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotVehiclesPanel::setModels() {
|
||||
models = getCarNames(carMake.toLower(), carModels);
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotVehiclesPanel::hideToggles() {
|
||||
disableOpenpilotLong->setVisible(hasOpenpilotLongitudinal && !hasExperimentalOpenpilotLongitudinal && !isGMPCMCruise || params.getBool("DisableOpenpilotLongitudinal"));
|
||||
|
||||
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> imprezaKeys = {"CrosstrekTorque"};
|
||||
std::set<QString> longitudinalKeys = {"ToyotaTune", "LongPitch", "SNGHack"};
|
||||
std::set<QString> sngKeys = {"SNGHack"};
|
||||
std::set<QString> voltKeys = {"VoltSNG"};
|
||||
|
||||
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 (!isImpreza && imprezaKeys.find(key.c_str()) != imprezaKeys.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isVolt && voltKeys.find(key.c_str()) != voltKeys.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();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#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;
|
||||
|
||||
QMap<QString, QString> carModels;
|
||||
|
||||
std::set<QString> gmKeys = {"LongPitch", "VoltSNG"};
|
||||
std::set<QString> subaruKeys = {"CrosstrekTorque"};
|
||||
std::set<QString> toyotaKeys = {"ClusterOffset", "SNGHack", "ToyotaDoors", "ToyotaTune"};
|
||||
|
||||
std::map<std::string, AbstractControl*> toggles;
|
||||
|
||||
Params params;
|
||||
|
||||
bool hasExperimentalOpenpilotLongitudinal;
|
||||
bool hasOpenpilotLongitudinal;
|
||||
bool hasSNG;
|
||||
bool isGMPCMCruise;
|
||||
bool isImpreza;
|
||||
bool isVolt;
|
||||
bool started;
|
||||
};
|
||||
@@ -0,0 +1,449 @@
|
||||
#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."), ""},
|
||||
{"ShowStoppingPoint", tr("Stopping Points"), tr("Display the point where openpilot wants to stop for red lights/stop signs."), ""},
|
||||
|
||||
{"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' Overridden"), ""},
|
||||
{"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."), ""},
|
||||
{"StoppedTimer", tr("Stopped Timer"), tr("Display a timer in the onroad UI that indicates how long you've been stopped for."), ""},
|
||||
{"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 *visualToggle;
|
||||
|
||||
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());
|
||||
}
|
||||
});
|
||||
visualToggle = alertVolumeControlToggle;
|
||||
} else if (alertVolumeControlKeys.find(param) != alertVolumeControlKeys.end()) {
|
||||
if (param == "WarningImmediateVolume") {
|
||||
visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 25, 100, std::map<int, QString>(), this, false, "%");
|
||||
} else {
|
||||
visualToggle = 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());
|
||||
}
|
||||
});
|
||||
visualToggle = 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());
|
||||
}
|
||||
});
|
||||
visualToggle = 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);
|
||||
visualToggle = themeSelection;
|
||||
|
||||
if (param == "CustomSounds") {
|
||||
QObject::connect(themeSelection, &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) {
|
||||
toggle->setVisible(customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end());
|
||||
}
|
||||
});
|
||||
visualToggle = 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")};
|
||||
visualToggle = 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();
|
||||
});
|
||||
visualToggle = 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")}};
|
||||
visualToggle = new FrogPilotParamValueToggleControl(param, title, desc, icon, -1, 6, steeringWheelLabels, this, true, "", 1, 1, wheelToggles, wheelToggleNames);
|
||||
} else if (param == "ShowStoppingPoint") {
|
||||
std::vector<QString> stoppingPointToggles{"ShowStoppingPointMetrics"};
|
||||
std::vector<QString> stoppingPointToggleNames{tr("Show Distance")};
|
||||
visualToggle = new FrogPilotParamToggleControl(param, title, desc, icon, stoppingPointToggles, stoppingPointToggleNames);
|
||||
|
||||
} 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());
|
||||
}
|
||||
});
|
||||
visualToggle = 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")};
|
||||
visualToggle = new FrogPilotParamToggleControl(param, title, desc, icon, borderToggles, borderToggleNames);
|
||||
} else if (param == "LateralMetrics") {
|
||||
std::vector<QString> lateralToggles{"TuningInfo"};
|
||||
std::vector<QString> lateralToggleNames{tr("Auto Tune")};
|
||||
visualToggle = new FrogPilotParamToggleControl(param, title, desc, icon, lateralToggles, lateralToggleNames);
|
||||
} else if (param == "LongitudinalMetrics") {
|
||||
std::vector<QString> longitudinalToggles{"LeadInfo", "JerkInfo"};
|
||||
std::vector<QString> longitudinalToggleNames{tr("Lead Info"), tr("Longitudinal Jerk")};
|
||||
visualToggle = new FrogPilotParamToggleControl(param, title, desc, icon, longitudinalToggles, longitudinalToggleNames);
|
||||
} else if (param == "NumericalTemp") {
|
||||
std::vector<QString> temperatureToggles{"Fahrenheit"};
|
||||
std::vector<QString> temperatureToggleNames{tr("Fahrenheit")};
|
||||
visualToggle = 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();
|
||||
});
|
||||
visualToggle = 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 || disableOpenpilotLongitudinal) {
|
||||
modifiedModelUIKeysKeys.erase("HideLeadMarker");
|
||||
}
|
||||
|
||||
toggle->setVisible(modifiedModelUIKeysKeys.find(key.c_str()) != modifiedModelUIKeysKeys.end());
|
||||
}
|
||||
});
|
||||
visualToggle = modelUIToggle;
|
||||
} else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") {
|
||||
visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, std::map<int, QString>(), this, false, tr(" inches"));
|
||||
} else if (param == "PathEdgeWidth") {
|
||||
visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map<int, QString>(), this, false, tr("%"));
|
||||
} else if (param == "PathWidth") {
|
||||
visualToggle = 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());
|
||||
}
|
||||
});
|
||||
visualToggle = 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);
|
||||
visualToggle = preferredCamera;
|
||||
} else if (param == "BigMap") {
|
||||
std::vector<QString> mapToggles{"FullMap"};
|
||||
std::vector<QString> mapToggleNames{tr("Full Map")};
|
||||
visualToggle = 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")};
|
||||
visualToggle = 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]);
|
||||
|
||||
visualToggle = 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());
|
||||
}
|
||||
});
|
||||
visualToggle = screenToggle;
|
||||
} else if (param == "HideUIElements") {
|
||||
std::vector<QString> uiElementsToggles{"HideAlerts", "HideMapIcon", "HideMaxSpeed"};
|
||||
std::vector<QString> uiElementsToggleNames{tr("Alerts"), tr("Map Icon"), tr("Max Speed")};
|
||||
visualToggle = 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) + "%";
|
||||
}
|
||||
visualToggle = 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) + "%";
|
||||
}
|
||||
visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 101, brightnessLabels, this, false);
|
||||
}
|
||||
} else if (param == "ScreenTimeout" || param == "ScreenTimeoutOnroad") {
|
||||
visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 5, 60, std::map<int, QString>(), this, false, tr(" seconds"));
|
||||
|
||||
} else {
|
||||
visualToggle = new ParamControl(param, title, desc, icon, this);
|
||||
}
|
||||
|
||||
addItem(visualToggle);
|
||||
toggles[param.toStdString()] = visualToggle;
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(visualToggle), &ToggleControl::toggleFlipped, &updateFrogPilotToggles);
|
||||
QObject::connect(static_cast<FrogPilotParamToggleControl*>(visualToggle), &FrogPilotParamToggleControl::buttonTypeClicked, &updateFrogPilotToggles);
|
||||
QObject::connect(static_cast<FrogPilotParamValueControl*>(visualToggle), &FrogPilotParamValueControl::valueChanged, [this]() {
|
||||
bool screen_management = params.getBool("ScreenManagement");
|
||||
if (!started) {
|
||||
uiState()->scene.screen_brightness = screen_management ? params.getInt("ScreenBrightness") : 101;
|
||||
} else {
|
||||
uiState()->scene.screen_brightness_onroad = screen_management ? params.getInt("ScreenBrightnessOnroad") : 101;
|
||||
}
|
||||
updateFrogPilotToggles();
|
||||
});
|
||||
|
||||
QObject::connect(visualToggle, &AbstractControl::showDescriptionEvent, [this]() {
|
||||
update();
|
||||
});
|
||||
|
||||
QObject::connect(static_cast<FrogPilotParamManageControl*>(visualToggle), &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) {
|
||||
disableOpenpilotLongitudinal = 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 = hasLongitudinalControl(CP);
|
||||
} else {
|
||||
hasAutoTune = true;
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#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) override;
|
||||
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", "ShowStoppingPoint", "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", "StoppedTimer", "WheelSpeed"};
|
||||
std::set<QString> screenKeys = {"HideUIElements", "ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", "ScreenTimeout", "ScreenTimeoutOnroad", "StandbyMode"};
|
||||
|
||||
std::map<std::string, AbstractControl*> toggles;
|
||||
|
||||
Params params;
|
||||
|
||||
bool disableOpenpilotLongitudinal;
|
||||
bool hasAutoTune;
|
||||
bool hasBSM;
|
||||
bool hasOpenpilotLongitudinal;
|
||||
bool isMetric = params.getBool("IsMetric");
|
||||
bool isRelease;
|
||||
bool started;
|
||||
};
|
||||
@@ -14,3 +14,41 @@ void updateFrogPilotToggles() {
|
||||
}
|
||||
}).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,731 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||
|
||||
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 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 bool checkable = false, 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:checked {
|
||||
background-color: #33Ab4C;
|
||||
}
|
||||
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->setCheckable(checkable);
|
||||
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();
|
||||
}
|
||||
|
||||
void updateButtonStyles(int id) {
|
||||
for (auto button : button_group->buttons()) {
|
||||
button->setChecked(button_group->id(button) == id);
|
||||
}
|
||||
}
|
||||
|
||||
void setEnabled(bool enable) {
|
||||
for (auto btn : button_group->buttons()) {
|
||||
btn->setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class FrogPilotDualParamControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FrogPilotDualParamControl(FrogPilotParamValueControl *control1, FrogPilotParamValueControl *control2, QWidget *parent = nullptr)
|
||||
: QFrame(parent), control1(control1), control2(control2) {
|
||||
QHBoxLayout *hlayout = new QHBoxLayout(this);
|
||||
hlayout->addWidget(control1);
|
||||
hlayout->addWidget(control2);
|
||||
}
|
||||
|
||||
void updateControl(float newMinValue, float newMaxValue, const QString &newLabel, float newDivision = 1.0f) {
|
||||
control1->updateControl(newMinValue, newMaxValue, newLabel, newDivision);
|
||||
control2->updateControl(newMinValue, newMaxValue, newLabel, newDivision);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
control1->refresh();
|
||||
control2->refresh();
|
||||
}
|
||||
|
||||
private:
|
||||
FrogPilotParamValueControl *control1;
|
||||
FrogPilotParamValueControl *control2;
|
||||
};
|
||||
|
||||
@@ -22,7 +22,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/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/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'] = []
|
||||
if maps:
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "common/watchdog.h"
|
||||
#include "common/util.h"
|
||||
@@ -15,6 +16,10 @@
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
#include "selfdrive/ui/qt/widgets/ssh_keys.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) {
|
||||
// param, title, desc, icon
|
||||
std::vector<std::tuple<QString, QString, QString, QString>> toggle_defs{
|
||||
@@ -114,6 +119,11 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
connect(toggles["ExperimentalLongitudinalEnabled"], &ToggleControl::toggleFlipped, [=]() {
|
||||
updateToggles();
|
||||
});
|
||||
|
||||
// FrogPilot signals
|
||||
connect(toggles["IsMetric"], &ToggleControl::toggleFlipped, [=]() {
|
||||
updateMetric();
|
||||
});
|
||||
}
|
||||
|
||||
void TogglesPanel::updateState(const UIState &s) {
|
||||
@@ -348,6 +358,16 @@ void DevicePanel::showEvent(QShowEvent *event) {
|
||||
ListWidget::showEvent(event);
|
||||
}
|
||||
|
||||
void SettingsWindow::hideEvent(QHideEvent *event) {
|
||||
closeParentToggle();
|
||||
|
||||
parentToggleOpen = false;
|
||||
subParentToggleOpen = false;
|
||||
subSubParentToggleOpen = false;
|
||||
|
||||
previousScrollPosition = 0;
|
||||
}
|
||||
|
||||
void SettingsWindow::showEvent(QShowEvent *event) {
|
||||
setCurrentPanel(0);
|
||||
}
|
||||
@@ -384,7 +404,20 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
close_btn->setFixedSize(200, 200);
|
||||
sidebar_layout->addSpacing(45);
|
||||
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
|
||||
DevicePanel *device = new DevicePanel(this);
|
||||
@@ -393,12 +426,24 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
|
||||
TogglesPanel *toggles = new TogglesPanel(this);
|
||||
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 = {
|
||||
{tr("Device"), device},
|
||||
{tr("Network"), new Networking(this)},
|
||||
{tr("Toggles"), toggles},
|
||||
{tr("Software"), new SoftwarePanel(this)},
|
||||
{tr("Controls"), frogpilotControls},
|
||||
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
|
||||
{tr("Visuals"), frogpilotVisuals},
|
||||
};
|
||||
|
||||
nav_btns = new QButtonGroup(this);
|
||||
@@ -431,7 +476,25 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
ScrollView *panel_frame = new ScrollView(panel, this);
|
||||
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]() {
|
||||
closeParentToggle();
|
||||
previousScrollPosition = 0;
|
||||
btn->setChecked(true);
|
||||
panel_widget->setCurrentWidget(w);
|
||||
});
|
||||
|
||||
@@ -25,17 +25,33 @@ public:
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
// FrogPilot widgets
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
|
||||
signals:
|
||||
void closeSettings();
|
||||
void reviewTrainingGuide();
|
||||
void showDriverView();
|
||||
void expandToggleDescription(const QString ¶m);
|
||||
|
||||
// FrogPilot signals
|
||||
void closeParentToggle();
|
||||
void closeSubParentToggle();
|
||||
void closeSubSubParentToggle();
|
||||
void updateMetric();
|
||||
|
||||
private:
|
||||
QPushButton *sidebar_alert_widget;
|
||||
QWidget *sidebar_widget;
|
||||
QButtonGroup *nav_btns;
|
||||
QStackedWidget *panel_widget;
|
||||
|
||||
// FrogPilot variables
|
||||
bool parentToggleOpen;
|
||||
bool subParentToggleOpen;
|
||||
bool subSubParentToggleOpen;
|
||||
|
||||
int previousScrollPosition;
|
||||
};
|
||||
|
||||
class DevicePanel : public ListWidget {
|
||||
@@ -64,6 +80,10 @@ public:
|
||||
explicit TogglesPanel(SettingsWindow *parent);
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
signals:
|
||||
// FrogPilot signals
|
||||
void updateMetric();
|
||||
|
||||
public slots:
|
||||
void expandToggleDescription(const QString ¶m);
|
||||
|
||||
|
||||
@@ -132,6 +132,10 @@ public:
|
||||
toggle.update();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
toggle.togglePosition();
|
||||
}
|
||||
|
||||
signals:
|
||||
void toggleFlipped(bool state);
|
||||
|
||||
|
||||
@@ -44,6 +44,10 @@ ScrollView::ScrollView(QWidget *w, QWidget *parent) : QScrollArea(parent) {
|
||||
scroller->setScrollerProperties(sp);
|
||||
}
|
||||
|
||||
void ScrollView::restorePosition(int previousScrollPosition) {
|
||||
verticalScrollBar()->setValue(previousScrollPosition);
|
||||
}
|
||||
|
||||
void ScrollView::hideEvent(QHideEvent *e) {
|
||||
verticalScrollBar()->setValue(0);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ class ScrollView : public QScrollArea {
|
||||
|
||||
public:
|
||||
explicit ScrollView(QWidget *w = nullptr, QWidget *parent = nullptr);
|
||||
|
||||
// FrogPilot functions
|
||||
void restorePosition(int previousScrollPosition);
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent *e) override;
|
||||
};
|
||||
|
||||
@@ -216,6 +216,7 @@ static void update_state(UIState *s) {
|
||||
}
|
||||
if (sm.updated("deviceState")) {
|
||||
auto deviceState = sm["deviceState"].getDeviceState();
|
||||
scene.online = deviceState.getNetworkType() != cereal::DeviceState::NetworkType::NONE;
|
||||
}
|
||||
if (sm.updated("frogpilotCarControl")) {
|
||||
auto frogpilotCarControl = sm["frogpilotCarControl"].getFrogpilotCarControl();
|
||||
|
||||
@@ -117,6 +117,7 @@ typedef struct UIScene {
|
||||
bool enabled;
|
||||
bool experimental_mode;
|
||||
bool map_open;
|
||||
bool online;
|
||||
bool right_hand_drive;
|
||||
|
||||
int alert_size;
|
||||
|
||||