Skip to content

Commit e61f5cb

Browse files
incognitojamtwilsonco
authored andcommitted
ui: tap experimental mode icon to toggle (commaai#27064)
* ui: tap experimental mode icon to toggle After experimental mode has been enabled for the first time (confirmed), it can be toggled by tapping the engage-ability/experimental mode icon in the upper right. Closes commaai#27002 * replace with QPushButton * fixes * cleanup
1 parent af9fbd9 commit e61f5cb

File tree

2 files changed

+81
-20
lines changed

2 files changed

+81
-20
lines changed

selfdrive/ui/qt/onroad.cc

+59-13
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,62 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
175175
}
176176

177177

178+
ExperimentalButton::ExperimentalButton(QWidget *parent) : QPushButton(parent) {
179+
setVisible(false);
180+
setFixedSize(btn_size, btn_size);
181+
setCheckable(true);
182+
183+
params = Params();
184+
engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
185+
experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size, img_size});
186+
187+
QObject::connect(this, &QPushButton::toggled, [=](bool checked) {
188+
params.putBool("ExperimentalMode", checked);
189+
});
190+
}
191+
192+
void ExperimentalButton::updateState(const UIState &s) {
193+
const SubMaster &sm = *(s.sm);
194+
195+
// button is "visible" if engageable or enabled
196+
const auto cs = sm["controlsState"].getControlsState();
197+
setVisible(cs.getEngageable() || cs.getEnabled());
198+
199+
// button is "checked" if experimental mode is enabled
200+
setChecked(sm["controlsState"].getControlsState().getExperimentalMode());
201+
202+
// disable button when experimental mode is not available, or has not been confirmed for the first time
203+
const auto cp = sm["carParams"].getCarParams();
204+
const bool experimental_mode_available = cp.getExperimentalLongitudinalAvailable() ? params.getBool("ExperimentalLongitudinalEnabled") : cp.getOpenpilotLongitudinalControl();
205+
setEnabled(params.getBool("ExperimentalModeConfirmed") && experimental_mode_available);
206+
}
207+
208+
void ExperimentalButton::paintEvent(QPaintEvent *event) {
209+
QPainter p(this);
210+
p.setRenderHint(QPainter::Antialiasing);
211+
212+
QPoint center(btn_size / 2, btn_size / 2);
213+
QPixmap img = isChecked() ? experimental_img : engage_img;
214+
215+
p.setOpacity(1.0);
216+
p.setPen(Qt::NoPen);
217+
p.setBrush(QColor(0, 0, 0, 166));
218+
p.drawEllipse(center, btn_size / 2, btn_size / 2);
219+
p.setOpacity(isDown() ? 0.8 : 1.0);
220+
p.drawPixmap((btn_size - img_size) / 2, (btn_size - img_size) / 2, img);
221+
}
222+
223+
178224
AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent) {
179225
pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"uiDebug"});
180226

181-
engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
182-
experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size - 5, img_size - 5});
227+
QVBoxLayout *main_layout = new QVBoxLayout(this);
228+
main_layout->setMargin(bdr_s);
229+
main_layout->setSpacing(0);
230+
231+
experimental_btn = new ExperimentalButton(this);
232+
main_layout->addWidget(experimental_btn, 0, Qt::AlignTop | Qt::AlignRight);
233+
183234
dm_img = loadPixmap("../assets/img_driver_face.png", {img_size, img_size});
184235
}
185236

@@ -227,9 +278,11 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
227278
setProperty("hideDM", cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE);
228279
setProperty("status", s.status);
229280

230-
// update engageability and DM icons at 2Hz
281+
// update engageability/experimental mode button
282+
experimental_btn->updateState(s);
283+
284+
// update DM icons at 2Hz
231285
if (sm.frame % (UI_FREQ / 2) == 0) {
232-
setProperty("engageable", cs.getEngageable() || cs.getEnabled());
233286
setProperty("dmActive", sm["driverMonitoringState"].getDriverMonitoringState().getIsActiveMode());
234287
setProperty("rightHandDM", sm["driverMonitoringState"].getDriverMonitoringState().getIsRHD());
235288
}
@@ -382,16 +435,9 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
382435
configFont(p, "Inter", 66, "Regular");
383436
drawText(p, rect().center().x(), 290, speedUnit, 200);
384437

385-
// engage-ability icon
386-
if (engageable) {
387-
SubMaster &sm = *(uiState()->sm);
388-
drawIcon(p, rect().right() - radius / 2 - bdr_s * 2, radius / 2 + int(bdr_s * 1.5),
389-
sm["controlsState"].getControlsState().getExperimentalMode() ? experimental_img : engage_img, blackColor(166), 1.0);
390-
}
391-
392438
// dm icon
393439
if (!hideDM) {
394-
int dm_icon_x = rightHandDM ? rect().right() - radius / 2 - (bdr_s * 2) : radius / 2 + (bdr_s * 2);
440+
int dm_icon_x = rightHandDM ? rect().right() - btn_size / 2 - (bdr_s * 2) : btn_size / 2 + (bdr_s * 2);
395441
drawIcon(p, dm_icon_x, rect().bottom() - footer_h / 2,
396442
dm_img, blackColor(70), dmActive ? 1.0 : 0.2);
397443
}
@@ -414,7 +460,7 @@ void AnnotatedCameraWidget::drawIcon(QPainter &p, int x, int y, QPixmap &img, QB
414460
p.setOpacity(1.0); // bg dictates opacity of ellipse
415461
p.setPen(Qt::NoPen);
416462
p.setBrush(bg);
417-
p.drawEllipse(x - radius / 2, y - radius / 2, radius, radius);
463+
p.drawEllipse(x - btn_size / 2, y - btn_size / 2, btn_size, btn_size);
418464
p.setOpacity(opacity);
419465
p.drawPixmap(x - img.size().width() / 2, y - img.size().height() / 2, img);
420466
}

selfdrive/ui/qt/onroad.h

+22-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
#pragma once
22

3+
#include <QPushButton>
34
#include <QStackedLayout>
45
#include <QWidget>
56

67
#include "common/util.h"
7-
#include "selfdrive/ui/qt/widgets/cameraview.h"
88
#include "selfdrive/ui/ui.h"
9+
#include "selfdrive/ui/qt/widgets/cameraview.h"
10+
11+
12+
const int btn_size = 192;
13+
const int img_size = (btn_size / 4) * 3;
914

1015

1116
// ***** onroad widgets *****
@@ -24,6 +29,21 @@ class OnroadAlerts : public QWidget {
2429
Alert alert = {};
2530
};
2631

32+
class ExperimentalButton : public QPushButton {
33+
Q_OBJECT
34+
35+
public:
36+
explicit ExperimentalButton(QWidget *parent = 0);
37+
void updateState(const UIState &s);
38+
39+
private:
40+
void paintEvent(QPaintEvent *event) override;
41+
42+
Params params;
43+
QPixmap engage_img;
44+
QPixmap experimental_img;
45+
};
46+
2747
// container window for the NVG UI
2848
class AnnotatedCameraWidget : public CameraWidget {
2949
Q_OBJECT
@@ -36,7 +56,6 @@ class AnnotatedCameraWidget : public CameraWidget {
3656
Q_PROPERTY(bool has_us_speed_limit MEMBER has_us_speed_limit);
3757
Q_PROPERTY(bool is_metric MEMBER is_metric);
3858

39-
Q_PROPERTY(bool engageable MEMBER engageable);
4059
Q_PROPERTY(bool dmActive MEMBER dmActive);
4160
Q_PROPERTY(bool hideDM MEMBER hideDM);
4261
Q_PROPERTY(bool rightHandDM MEMBER rightHandDM);
@@ -50,18 +69,14 @@ class AnnotatedCameraWidget : public CameraWidget {
5069
void drawIcon(QPainter &p, int x, int y, QPixmap &img, QBrush bg, float opacity);
5170
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
5271

53-
QPixmap engage_img;
54-
QPixmap experimental_img;
72+
ExperimentalButton *experimental_btn;
5573
QPixmap dm_img;
56-
const int radius = 192;
57-
const int img_size = (radius / 2) * 1.5;
5874
float speed;
5975
QString speedUnit;
6076
float setSpeed;
6177
float speedLimit;
6278
bool is_cruise_set = false;
6379
bool is_metric = false;
64-
bool engageable = false;
6580
bool dmActive = false;
6681
bool hideDM = false;
6782
bool rightHandDM = false;

0 commit comments

Comments
 (0)