mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-21 08:14:00 +08:00
645 lines
19 KiB
C++
645 lines
19 KiB
C++
#pragma once
|
|
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <set>
|
|
|
|
#include <QElapsedTimer>
|
|
#include <QJsonObject>
|
|
#include <QMovie>
|
|
#include <QNetworkAccessManager>
|
|
#include <QNetworkReply>
|
|
#include <QNetworkRequest>
|
|
#include <QStyle>
|
|
|
|
#include "selfdrive/ui/qt/util.h"
|
|
#include "selfdrive/ui/qt/widgets/controls.h"
|
|
|
|
bool isFrogsGoMoo();
|
|
bool useKonikServer();
|
|
|
|
void loadGif(const QString &gifPath, QSharedPointer<QMovie> &movie, const QSize &size, QWidget *parent);
|
|
void loadImage(const QString &basePath, QPixmap &pixmap, QSharedPointer<QMovie> &movie, const QSize &size, QWidget *parent);
|
|
void openDescriptions(bool forceOpenDescriptions, std::map<QString, AbstractControl*> toggles);
|
|
void updateStarPilotToggles();
|
|
|
|
QString cleanModelName(QString modelName);
|
|
|
|
const QString buttonStyle = R"(
|
|
QPushButton {
|
|
padding: 0px 25px 0px 25px;
|
|
border-radius: 50px;
|
|
font-size: 35px;
|
|
font-weight: 500;
|
|
height: 100px;
|
|
color: #E4E4E4;
|
|
background-color: #393939;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #4a4a4a;
|
|
}
|
|
QPushButton:checked:enabled {
|
|
background-color: #33Ab4C;
|
|
}
|
|
QPushButton:disabled {
|
|
color: #33E4E4E4;
|
|
}
|
|
)";
|
|
|
|
class StarPilotConfirmationDialog : public ConfirmationDialog {
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit StarPilotConfirmationDialog(const QString &prompt_text, const QString &confirm_text,
|
|
const QString &cancel_text, const bool rich, QWidget *parent);
|
|
static bool toggleReboot(QWidget *parent);
|
|
static bool yesorno(const QString &prompt_text, QWidget *parent);
|
|
};
|
|
|
|
class StarPilotListWidget : public QWidget {
|
|
Q_OBJECT
|
|
public:
|
|
explicit StarPilotListWidget(QWidget *parent = 0) : 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
|
|
outer_layout.addStretch();
|
|
}
|
|
inline void addItem(QWidget *w, bool expanding = false) {
|
|
w->setSizePolicy(QSizePolicy::Preferred, expanding ? QSizePolicy::Expanding : QSizePolicy::Maximum);
|
|
inner_layout.addWidget(w);
|
|
}
|
|
inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); }
|
|
inline void insertItem(int index, QWidget *w, bool expanding = false) {
|
|
w->setSizePolicy(QSizePolicy::Preferred, expanding ? QSizePolicy::Expanding : QSizePolicy::Fixed);
|
|
inner_layout.insertWidget(index, w);
|
|
}
|
|
inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); }
|
|
|
|
void clear() {
|
|
while (QLayoutItem *child = inner_layout.takeAt(0)) {
|
|
if (child->widget()) {
|
|
child->widget()->deleteLater();
|
|
}
|
|
delete child;
|
|
}
|
|
outer_layout.addStretch();
|
|
}
|
|
|
|
private:
|
|
void paintEvent(QPaintEvent *) override {
|
|
QPainter p(this);
|
|
p.setPen(Qt::gray);
|
|
for (int i = 0; i < inner_layout.count() - 1; ++i) {
|
|
QWidget *widget = inner_layout.itemAt(i)->widget();
|
|
|
|
QWidget *nextWidget = nullptr;
|
|
for (int j = i + 1; j < inner_layout.count(); ++j) {
|
|
nextWidget = inner_layout.itemAt(j)->widget();
|
|
if (nextWidget != nullptr && nextWidget->isVisible()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (widget == nullptr || (widget->isVisible() && nextWidget->isVisible())) {
|
|
QRect r = inner_layout.itemAt(i)->geometry();
|
|
int bottom = r.bottom() + inner_layout.spacing() / 2;
|
|
p.drawLine(r.left() + 40, bottom, r.right() - 40, bottom);
|
|
}
|
|
}
|
|
}
|
|
QVBoxLayout outer_layout;
|
|
QVBoxLayout inner_layout;
|
|
};
|
|
|
|
class StarPilotButtonControl : public ParamControl {
|
|
Q_OBJECT
|
|
public:
|
|
StarPilotButtonControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
|
const std::vector<QString> &button_texts, bool checkable = false,
|
|
bool exclusive = false, int minimum_button_width = 225) : ParamControl(param, title, desc, icon) {
|
|
key = param.toStdString();
|
|
|
|
button_group = new QButtonGroup(this);
|
|
button_group->setExclusive(exclusive);
|
|
for (int i = 0; i < button_texts.size(); i++) {
|
|
QPushButton *button = new QPushButton(button_texts[i], this);
|
|
button->setCheckable(checkable);
|
|
button->setStyleSheet(buttonStyle);
|
|
button->setMinimumWidth(minimum_button_width);
|
|
hlayout->addWidget(button);
|
|
button_group->addButton(button, i);
|
|
}
|
|
|
|
hlayout->addWidget(&toggle);
|
|
|
|
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
|
|
emit buttonClicked(id);
|
|
});
|
|
|
|
QObject::connect(this, &ToggleControl::toggleFlipped, this, &StarPilotButtonControl::refresh);
|
|
|
|
refresh();
|
|
}
|
|
|
|
virtual void refresh() {
|
|
bool state = params.getBool(key);
|
|
if (state != toggle.on) {
|
|
toggle.togglePosition();
|
|
}
|
|
|
|
for (QAbstractButton *button : button_group->buttons()) {
|
|
button->setEnabled(state);
|
|
}
|
|
}
|
|
|
|
void clearCheckedButtons() {
|
|
bool original_exclusive = button_group->exclusive();
|
|
|
|
button_group->setExclusive(false);
|
|
|
|
for (QAbstractButton *button : button_group->buttons()) {
|
|
button->setChecked(false);
|
|
}
|
|
|
|
button_group->setExclusive(original_exclusive);
|
|
}
|
|
|
|
void setCheckedButton(int id) {
|
|
if (QAbstractButton *button = button_group->button(id)) {
|
|
button->setChecked(true);
|
|
}
|
|
}
|
|
|
|
void setEnabledButton(int id, bool enable) {
|
|
if (QAbstractButton *button = button_group->button(id)) {
|
|
button->setEnabled(enable);
|
|
}
|
|
}
|
|
|
|
void setVisibleButton(int id, bool visible) {
|
|
if (QAbstractButton *button = button_group->button(id)) {
|
|
button->setVisible(visible);
|
|
}
|
|
}
|
|
|
|
signals:
|
|
void buttonClicked(int id);
|
|
|
|
protected:
|
|
std::string key;
|
|
|
|
Params params;
|
|
|
|
QButtonGroup *button_group;
|
|
};
|
|
|
|
class StarPilotButtonsControl : public AbstractControl {
|
|
Q_OBJECT
|
|
public:
|
|
StarPilotButtonsControl(const QString &title, const QString &desc, const QString &icon,
|
|
const std::vector<QString> &button_texts, const bool &checkable = false, const bool &exclusive = true,
|
|
const int minimum_button_width = 225) : AbstractControl(title, desc, icon) {
|
|
button_group = new QButtonGroup(this);
|
|
button_group->setExclusive(exclusive);
|
|
for (int i = 0; i < button_texts.size(); i++) {
|
|
QPushButton *button = new QPushButton(button_texts[i], this);
|
|
button->installEventFilter(this);
|
|
button->setCheckable(checkable);
|
|
button->setStyleSheet(buttonStyle);
|
|
button->setMinimumWidth(minimum_button_width);
|
|
hlayout->addWidget(button);
|
|
button_group->addButton(button, i);
|
|
}
|
|
|
|
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
|
|
emit buttonClicked(id);
|
|
});
|
|
}
|
|
|
|
void clearCheckedButtons() {
|
|
bool original_exclusive = button_group->exclusive();
|
|
|
|
button_group->setExclusive(false);
|
|
|
|
for (QAbstractButton *button : button_group->buttons()) {
|
|
button->setChecked(false);
|
|
}
|
|
|
|
button_group->setExclusive(original_exclusive);
|
|
}
|
|
|
|
void setCheckedButton(int id) {
|
|
if (QAbstractButton *button = button_group->button(id)) {
|
|
button->setChecked(true);
|
|
}
|
|
}
|
|
|
|
void setEnabled(bool enable) {
|
|
for (QAbstractButton *button : button_group->buttons()) {
|
|
button->setEnabled(enable);
|
|
}
|
|
}
|
|
|
|
void setEnabledButtons(int id, bool enable) {
|
|
if (QAbstractButton *button = button_group->button(id)) {
|
|
button->setEnabled(enable);
|
|
}
|
|
}
|
|
|
|
void setText(int id, const QString &text) {
|
|
if (QAbstractButton *button = button_group->button(id)) {
|
|
button->setText(text);
|
|
}
|
|
}
|
|
|
|
void setVisibleButton(int id, bool visible) {
|
|
if (QAbstractButton *button = button_group->button(id)) {
|
|
button->setVisible(visible);
|
|
}
|
|
}
|
|
|
|
signals:
|
|
void buttonClicked(int id);
|
|
void disabledButtonClicked(int id);
|
|
|
|
protected:
|
|
bool eventFilter(QObject *obj, QEvent *event) override {
|
|
if (event->type() == QEvent::MouseButtonPress) {
|
|
QPushButton *button = qobject_cast<QPushButton *>(obj);
|
|
if (button && !button->isEnabled()) {
|
|
emit disabledButtonClicked(button_group->id(button));
|
|
}
|
|
}
|
|
return AbstractControl::eventFilter(obj, event);
|
|
}
|
|
|
|
private:
|
|
QButtonGroup *button_group;
|
|
};
|
|
|
|
class StarPilotButtonToggleControl : public StarPilotButtonControl {
|
|
Q_OBJECT
|
|
public:
|
|
StarPilotButtonToggleControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
|
const std::vector<QString> &button_params, const std::vector<QString> &button_texts,
|
|
bool exclusive = false, int minimum_button_width = 225)
|
|
: StarPilotButtonControl(param, title, desc, icon, button_texts, true, exclusive, minimum_button_width), button_params(button_params) {
|
|
|
|
for (int i = 0; i < button_texts.size(); i++) {
|
|
button_group->buttons()[i]->setChecked(params.getBool(button_params[i].toStdString()));
|
|
}
|
|
|
|
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
|
|
params.putBool(button_params[id].toStdString(), button_group->button(id)->isChecked());
|
|
emit buttonClicked(id);
|
|
});
|
|
}
|
|
|
|
void refresh() override {
|
|
StarPilotButtonControl::refresh();
|
|
|
|
for (int i = 0; i < button_group->buttons().size(); i++) {
|
|
QAbstractButton *button = button_group->button(i);
|
|
button->setChecked(params.getBool(button_params[i].toStdString()));
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::vector<QString> button_params;
|
|
};
|
|
|
|
class StarPilotManageControl : public ParamControl {
|
|
Q_OBJECT
|
|
public:
|
|
StarPilotManageControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon) : ParamControl(param, title, desc, icon) {
|
|
key = param.toStdString();
|
|
|
|
manageButton = new ButtonControl("", tr("MANAGE"), "", this);
|
|
|
|
hlayout->insertWidget(hlayout->indexOf(&toggle) - 1, manageButton);
|
|
|
|
QObject::connect(manageButton, &ButtonControl::clicked, this, &StarPilotManageControl::manageButtonClicked);
|
|
QObject::connect(this, &ToggleControl::toggleFlipped, this, &StarPilotManageControl::refresh);
|
|
}
|
|
|
|
void refresh() {
|
|
manageButton->setEnabled(params.getBool(key));
|
|
}
|
|
|
|
void setManageVisibility(bool visible) {
|
|
manageButton->setVisible(visible);
|
|
}
|
|
|
|
void showEvent(QShowEvent *event) override {
|
|
refresh();
|
|
ParamControl::showEvent(event);
|
|
}
|
|
|
|
signals:
|
|
void manageButtonClicked();
|
|
|
|
private:
|
|
std::string key;
|
|
|
|
ButtonControl *manageButton;
|
|
|
|
Params params;
|
|
};
|
|
|
|
class StarPilotParamValueControl : public AbstractControl {
|
|
Q_OBJECT
|
|
public:
|
|
StarPilotParamValueControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
|
float min_value, float max_value, const QString &label, const std::map<float, QString> &value_labels = {},
|
|
float interval = 1.0f, bool fast_increase = false, int label_width = 350)
|
|
: AbstractControl(title, desc, icon),
|
|
fast_increase(fast_increase), interval(interval), label(label), min_value(min_value), max_value(max_value), value_labels(value_labels) {
|
|
factor = std::pow(10, std::ceil(-std::log10(interval)));
|
|
key = param.toStdString();
|
|
key_type = params.getKeyType(key);
|
|
|
|
setupButton(decrement_button, "-");
|
|
setupButton(increment_button, "+");
|
|
|
|
value_label = new QLabel(this);
|
|
value_label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
value_label->setFixedSize(QSize(label_width, 100));
|
|
value_label->setStyleSheet("QLabel {color: #E0E879;}");
|
|
|
|
hlayout->addWidget(value_label);
|
|
hlayout->addWidget(&decrement_button);
|
|
hlayout->addWidget(&increment_button);
|
|
|
|
QObject::connect(&decrement_button, &QPushButton::pressed, this, &StarPilotParamValueControl::decrementPressed);
|
|
QObject::connect(&increment_button, &QPushButton::pressed, this, &StarPilotParamValueControl::incrementPressed);
|
|
|
|
last_action_timer.start();
|
|
}
|
|
|
|
void decrementPressed() {
|
|
if (display_warning && !warning_shown) {
|
|
showWarning();
|
|
}
|
|
|
|
if (last_action_timer.isValid() && last_action_timer.elapsed() > decrement_button.autoRepeatInterval() + 50) {
|
|
decrement_repeating = false;
|
|
}
|
|
|
|
float delta = decrement_repeating && fast_increase ? interval * 5 : interval;
|
|
value = std::max(value - delta, min_value);
|
|
|
|
updateValue();
|
|
|
|
if (std::lround(value / interval) % 5 == 0) {
|
|
decrement_repeating = true;
|
|
}
|
|
|
|
last_action_timer.restart();
|
|
}
|
|
|
|
void hideEvent(QHideEvent *event) override {
|
|
AbstractControl::hideEvent(event);
|
|
|
|
warning_shown = false;
|
|
|
|
updateParam();
|
|
}
|
|
|
|
void incrementPressed() {
|
|
if (display_warning && !warning_shown) {
|
|
showWarning();
|
|
}
|
|
|
|
if (last_action_timer.isValid() && last_action_timer.elapsed() > increment_button.autoRepeatInterval() + 50) {
|
|
increment_repeating = false;
|
|
}
|
|
|
|
float delta = increment_repeating && fast_increase ? interval * 5 : interval;
|
|
value = std::min(value + delta, max_value);
|
|
|
|
updateValue();
|
|
|
|
if (std::lround(value / interval) % 5 == 0) {
|
|
increment_repeating = true;
|
|
}
|
|
|
|
last_action_timer.restart();
|
|
}
|
|
|
|
void refresh() {
|
|
if (key_type == ParamKeyType::INT) {
|
|
value = std::clamp(std::round(params.getInt(key) * factor) / factor, min_value, max_value);
|
|
} else {
|
|
value = std::clamp(std::round(params.getFloat(key) * factor) / factor, min_value, max_value);
|
|
}
|
|
previous_value = value;
|
|
|
|
updateDisplay();
|
|
updateParam();
|
|
}
|
|
|
|
void setWarning(const QString &newWarning) {
|
|
display_warning = true;
|
|
|
|
warning = newWarning;
|
|
}
|
|
|
|
void setupButton(QPushButton &button, const QString &text) {
|
|
button.setAutoRepeat(true);
|
|
button.setAutoRepeatDelay(500);
|
|
button.setAutoRepeatInterval(150);
|
|
button.setFixedSize(150, 100);
|
|
button.setStyleSheet(buttonStyle);
|
|
if (text == "+" || text == "-") {
|
|
button.setStyleSheet(button.styleSheet() + " QPushButton { font-size: 50px; }");
|
|
}
|
|
button.setText(text);
|
|
}
|
|
|
|
void showEvent(QShowEvent *event) override {
|
|
refresh();
|
|
}
|
|
|
|
void showWarning() {
|
|
ConfirmationDialog::alert(warning, this);
|
|
|
|
warning_shown = true;
|
|
}
|
|
|
|
void updateControl(const float &newMinValue, const float &newMaxValue, const std::map<float, QString> &newValueLabels = {}) {
|
|
min_value = newMinValue;
|
|
max_value = newMaxValue;
|
|
|
|
value_labels = newValueLabels;
|
|
|
|
refresh();
|
|
}
|
|
|
|
void updateDisplay() {
|
|
QString displayText = QString::number(value) + label;
|
|
|
|
for (const std::pair<const float, QString> &entry : value_labels) {
|
|
if (std::lround(entry.first * factor) == std::lround(value * factor)) {
|
|
displayText = entry.second;
|
|
break;
|
|
}
|
|
}
|
|
|
|
decrement_button.setEnabled(value > min_value);
|
|
increment_button.setEnabled(value < max_value);
|
|
|
|
value_label->setText(displayText);
|
|
}
|
|
|
|
void updateParam() {
|
|
if (value == previous_value) {
|
|
return;
|
|
}
|
|
|
|
if (key_type == ParamKeyType::INT) {
|
|
params.putInt(key, value);
|
|
} else {
|
|
params.putFloat(key, value);
|
|
}
|
|
}
|
|
|
|
void updateValue() {
|
|
value = std::round(value * factor) / factor;
|
|
|
|
emit valueChanged(value);
|
|
|
|
updateDisplay();
|
|
}
|
|
|
|
signals:
|
|
void valueChanged(float value);
|
|
|
|
protected:
|
|
QLabel *value_label;
|
|
|
|
private:
|
|
bool decrement_repeating;
|
|
bool display_warning;
|
|
bool fast_increase;
|
|
bool increment_repeating;
|
|
bool warning_shown;
|
|
|
|
float interval;
|
|
float factor;
|
|
float max_value;
|
|
float min_value;
|
|
float previous_value;
|
|
float value;
|
|
|
|
std::map<float, QString> value_labels;
|
|
|
|
std::string key;
|
|
|
|
ParamKeyType key_type;
|
|
|
|
Params params;
|
|
|
|
QElapsedTimer last_action_timer;
|
|
|
|
QPushButton decrement_button;
|
|
QPushButton increment_button;
|
|
|
|
QString label;
|
|
QString warning;
|
|
};
|
|
|
|
class StarPilotParamValueButtonControl : public StarPilotParamValueControl {
|
|
Q_OBJECT
|
|
public:
|
|
StarPilotParamValueButtonControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
|
float min_value, float max_value, const QString &label, const std::map<float, QString> &value_labels,
|
|
float interval, bool fast_increase, const std::vector<QString> &button_params, const std::vector<QString> &button_texts,
|
|
bool left_button = false, bool checkable = true, int minimum_button_width = 225)
|
|
: StarPilotParamValueControl(param, title, desc, icon, min_value, max_value, label, value_labels, interval, fast_increase, 200),
|
|
button_params(button_params), checkable(checkable) {
|
|
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(checkable);
|
|
button->setChecked(checkable && params.getBool(button_params[i].toStdString()));
|
|
button->setStyleSheet(buttonStyle);
|
|
button->setMinimumWidth(minimum_button_width);
|
|
if (left_button) {
|
|
hlayout->insertWidget(hlayout->indexOf(value_label) - 1, button);
|
|
} else {
|
|
hlayout->addWidget(button);
|
|
}
|
|
button_group->addButton(button, i);
|
|
}
|
|
|
|
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
|
|
if (checkable) {
|
|
params.putBool(button_params[id].toStdString(), button_group->button(id)->isChecked());
|
|
}
|
|
emit buttonClicked(id);
|
|
});
|
|
}
|
|
|
|
void refresh() {
|
|
if (checkable) {
|
|
for (int i = 0; i < button_group->buttons().size(); i++) {
|
|
QAbstractButton *button = button_group->button(i);
|
|
button->setChecked(params.getBool(button_params[i].toStdString()));
|
|
}
|
|
}
|
|
StarPilotParamValueControl::refresh();
|
|
}
|
|
|
|
void showEvent(QShowEvent *event) override {
|
|
refresh();
|
|
StarPilotParamValueControl::showEvent(event);
|
|
}
|
|
|
|
public:
|
|
void setEnabledButtons(int id, bool enable) {
|
|
if (QAbstractButton *button = button_group->button(id)) {
|
|
button->setEnabled(enable);
|
|
}
|
|
}
|
|
|
|
signals:
|
|
void buttonClicked(int id);
|
|
|
|
private:
|
|
bool checkable;
|
|
|
|
std::vector<QString> button_params;
|
|
|
|
Params params;
|
|
|
|
QButtonGroup *button_group;
|
|
};
|
|
|
|
class StarPilotDualParamValueControl : public QFrame {
|
|
Q_OBJECT
|
|
public:
|
|
StarPilotDualParamValueControl(StarPilotParamValueControl *control1, StarPilotParamValueControl *control2, QWidget *parent = nullptr) : QFrame(parent), control1(control1), control2(control2) {
|
|
QHBoxLayout *hlayout = new QHBoxLayout(this);
|
|
hlayout->addWidget(control1);
|
|
hlayout->addWidget(control2);
|
|
}
|
|
|
|
void updateControl(const float &newMinValue, const float &newMaxValue, const std::map<float, QString> &newValueLabels = {}) {
|
|
control1->updateControl(newMinValue, newMaxValue, newValueLabels);
|
|
control2->updateControl(newMinValue, newMaxValue, newValueLabels);
|
|
}
|
|
|
|
void refresh() {
|
|
control1->refresh();
|
|
control2->refresh();
|
|
}
|
|
|
|
private:
|
|
StarPilotParamValueControl *control1;
|
|
StarPilotParamValueControl *control2;
|
|
};
|