openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
7.4 KiB

#pragma once
#include <string>
#include <vector>
#include <QButtonGroup>
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QPushButton>
#include "common/params.h"
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/toggle.h"
class ElidedLabel : public QLabel {
Q_OBJECT
public:
explicit ElidedLabel(QWidget *parent = 0);
explicit ElidedLabel(const QString &text, QWidget *parent = 0);
signals:
void clicked();
protected:
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent* event) override;
void mouseReleaseEvent(QMouseEvent *event) override {
if (rect().contains(event->pos())) {
emit clicked();
}
}
QString lastText_, elidedText_;
};
class AbstractControl : public QFrame {
Q_OBJECT
public:
void setDescription(const QString &desc) {
if (description) description->setText(desc);
}
void setTitle(const QString &title) {
title_label->setText(title);
}
void setValue(const QString &val) {
value->setText(val);
}
const QString getDescription() {
return description->text();
}
QLabel *icon_label;
QPixmap icon_pixmap;
public slots:
void showDescription() {
description->setVisible(true);
}
signals:
void showDescriptionEvent();
protected:
AbstractControl(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
void hideEvent(QHideEvent *e) override;
QHBoxLayout *hlayout;
QPushButton *title_label;
private:
ElidedLabel *value;
QLabel *description = nullptr;
};
// widget to display a value
class LabelControl : public AbstractControl {
Q_OBJECT
public:
LabelControl(const QString &title, const QString &text = "", const QString &desc = "", QWidget *parent = nullptr) : AbstractControl(title, desc, "", parent) {
label.setText(text);
label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
hlayout->addWidget(&label);
}
void setText(const QString &text) { label.setText(text); }
private:
ElidedLabel label;
};
// widget for a button with a label
class ButtonControl : public AbstractControl {
Q_OBJECT
public:
ButtonControl(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 ToggleControl : public AbstractControl {
Q_OBJECT
public:
ToggleControl(const QString &title, const QString &desc = "", const QString &icon = "", const bool state = false, QWidget *parent = nullptr) : AbstractControl(title, desc, icon, parent) {
toggle.setFixedSize(150, 100);
if (state) {
toggle.togglePosition();
}
hlayout->addWidget(&toggle);
QObject::connect(&toggle, &Toggle::stateChanged, this, &ToggleControl::toggleFlipped);
}
void setEnabled(bool enabled) {
toggle.setEnabled(enabled);
toggle.update();
}
signals:
void toggleFlipped(bool state);
protected:
Toggle toggle;
};
// widget to toggle params
class ParamControl : public ToggleControl {
Q_OBJECT
public:
ParamControl(const QString &param, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr);
void setConfirmation(bool _confirm, bool _store_confirm) {
confirm = _confirm;
store_confirm = _store_confirm;
}
void setActiveIcon(const QString &icon) {
active_icon_pixmap = QPixmap(icon).scaledToWidth(80, Qt::SmoothTransformation);
}
void refresh() {
bool state = params.getBool(key);
if (state != toggle.on) {
toggle.togglePosition();
setIcon(state);
}
}
void showEvent(QShowEvent *event) override {
refresh();
}
private:
void toggleClicked(bool state);
void setIcon(bool state) {
if (state && !active_icon_pixmap.isNull()) {
icon_label->setPixmap(active_icon_pixmap);
} else if (!icon_pixmap.isNull()) {
icon_label->setPixmap(icon_pixmap);
}
}
std::string key;
Params params;
QPixmap active_icon_pixmap;
bool confirm = false;
bool store_confirm = false;
};
class ButtonParamControl : public AbstractControl {
Q_OBJECT
public:
ButtonParamControl(const QString &param, const QString &title, const QString &desc, const QString &icon,
const std::vector<QString> &button_texts, const int minimum_button_width = 225) : AbstractControl(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 (int 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>::of(&QButtonGroup::buttonClicked), [=](int id) {
params.put(key, std::to_string(id));
});
}
void setEnabled(bool enable) {
for (auto btn : button_group->buttons()) {
btn->setEnabled(enable);
}
}
longitudinal personality: change via steering wheel distance button (#31792) * start at param * start by sending personality * change to personality * POC: button changes personality * what's wrong with this? * fix * not really possible but fuzzy test catches this * there's always a typo * dang, we're dropping messages * clean up * no comment * bump * rename * not all cars yet * works but at what cost * clean up * inside settings * write param so we save the distance button changes * setChecked activates buttonToggled and already writes param! * don't need this, we update from longitudinalPlan on changes * some clean up * more * ui * allow some time for ui to receive and write param * plannerd: only track changes in case no ui * Revert "plannerd: only track changes in case no ui" This reverts commit 2b081aa6ceb92c67a621b74592b2292756d29871. * write in plannerd as well, I assume this is atomic? * don't write when setting checked (only user clicks) * better nane * more * Update selfdrive/controls/lib/longitudinal_planner.py Co-authored-by: Cameron Clough <cameronjclough@gmail.com> * doesn't write param now * ParamWatcher is nice * no debug * Update translations * fix * odd drain sock proc replay behavior * vanish * Revert "odd drain sock proc replay behavior" This reverts commit 29b70b39413e1852bb512155af6b6a94a5bd9454. * add GM * only if OP long * move personality to controlsState, since eventually it won't be exclusive to long planner more bump * diff without translations * fix * put nonblocking * CS should start at up to date personality always (no ui flicker) * update toggle on cereal message change * fix * fix that * ubmp * mypy doesn't know this is an int :( * update translations * fix the tests --------- Co-authored-by: Cameron Clough <cameronjclough@gmail.com> old-commit-hash: 3d63c7093afec7ba24f447a44f02f7bea1f7282b
1 year ago
void setCheckedButton(int id) {
button_group->button(id)->setChecked(true);
}
void refresh() {
int value = atoi(params.get(key).c_str());
button_group->button(value)->setChecked(true);
}
void showEvent(QShowEvent *event) override {
refresh();
}
private:
std::string key;
Params params;
QButtonGroup *button_group;
};
class ListWidget : public QWidget {
Q_OBJECT
public:
explicit ListWidget(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(1);
}
inline void addItem(QWidget *w) { inner_layout.addWidget(w); }
inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); }
inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); }
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();
if (widget == nullptr || widget->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;
};
// convenience class for wrapping layouts
class LayoutWidget : public QWidget {
Q_OBJECT
public:
LayoutWidget(QLayout *l, QWidget *parent = nullptr) : QWidget(parent) {
setLayout(l);
}
};