offroad home style (#19593)

* make drive stats look nicer

* offroad alerts style

* rest of alerts

* move that

* fix up colors

Co-authored-by: Comma Device <device@comma.ai>
pull/19598/head
Adeeb Shihadeh 4 years ago committed by GitHub
parent 2c728cc96a
commit ce48d3c91e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      selfdrive/ui/qt/home.cc
  2. 2
      selfdrive/ui/qt/offroad/settings.cc
  3. 91
      selfdrive/ui/qt/widgets/drive_stats.cc
  4. 2
      selfdrive/ui/qt/widgets/drive_stats.hpp
  5. 148
      selfdrive/ui/qt/widgets/offroad_alerts.cc
  6. 9
      selfdrive/ui/qt/widgets/offroad_alerts.hpp
  7. 7
      selfdrive/ui/sidebar.cc
  8. 5
      selfdrive/ui/tests/cycle_offroad_alerts.py
  9. 4
      selfdrive/ui/ui.hpp

@ -44,7 +44,7 @@ OffroadHome::OffroadHome(QWidget *parent) : QWidget(parent) {
main_layout->addWidget(alert_notification, 0, Qt::AlignTop | Qt::AlignRight); main_layout->addWidget(alert_notification, 0, Qt::AlignTop | Qt::AlignRight);
// main content // main content
main_layout->addSpacing(100); main_layout->addSpacing(25);
center_layout = new QStackedLayout(); center_layout = new QStackedLayout();
DriveStats *drive = new DriveStats; DriveStats *drive = new DriveStats;

@ -64,7 +64,7 @@ QWidget * toggles_panel() {
toggles_list->setSpacing(25); toggles_list->setSpacing(25);
toggles_list->addWidget(new ParamsToggle("OpenpilotEnabledToggle", toggles_list->addWidget(new ParamsToggle("OpenpilotEnabledToggle",
"Enable Openpilot", "Enable openpilot",
"Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.", "Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.",
"../assets/offroad/icon_openpilot.png" "../assets/offroad/icon_openpilot.png"
)); ));

@ -5,7 +5,6 @@
#include <QDebug> #include <QDebug>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QLabel> #include <QLabel>
#include <QLineEdit>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
@ -19,7 +18,9 @@
#include "drive_stats.hpp" #include "drive_stats.hpp"
#include "common/params.h" #include "common/params.h"
#include "common/utilpp.h" #include "common/utilpp.h"
double MILE_TO_KM = 1.60934;
constexpr double MILE_TO_KM = 1.60934;
#if defined(QCOM) || defined(QCOM2) #if defined(QCOM) || defined(QCOM2)
@ -28,7 +29,8 @@ const std::string private_key_path = "/persist/comma/id_rsa";
const std::string private_key_path = util::getenv_default("HOME", "/.comma/persist/comma/id_rsa", "/persist/comma/id_rsa"); const std::string private_key_path = util::getenv_default("HOME", "/.comma/persist/comma/id_rsa", "/persist/comma/id_rsa");
#endif #endif
QByteArray rsa_sign(QByteArray data){
QByteArray rsa_sign(QByteArray data) {
auto file = QFile(private_key_path.c_str()); auto file = QFile(private_key_path.c_str());
bool r = file.open(QIODevice::ReadOnly); bool r = file.open(QIODevice::ReadOnly);
assert(r); assert(r);
@ -56,7 +58,7 @@ QByteArray rsa_sign(QByteArray data){
return sig; return sig;
} }
QString create_jwt(QString dongle_id, int expiry=3600){ QString create_jwt(QString dongle_id, int expiry=3600) {
QJsonObject header; QJsonObject header;
header.insert("alg", "RS256"); header.insert("alg", "RS256");
header.insert("typ", "JWT"); header.insert("typ", "JWT");
@ -81,24 +83,27 @@ QString create_jwt(QString dongle_id, int expiry=3600){
return jwt; return jwt;
} }
QString bold(QString s) { QLayout *build_stat(QString name, int stat) {
return "<b>" + s + "</b>";
}
QWidget *widget(QLayout *l){
QWidget *q = new QWidget();
q->setLayout(l);
return q;
}
QWidget *build_stat(QString name, int stat){
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(new QLabel(bold(QString("%1").arg(stat))), 1, Qt::AlignCenter);
layout->addWidget(new QLabel(name),1, Qt::AlignCenter); QLabel *metric = new QLabel(QString("%1").arg(stat));
return widget(layout); metric->setStyleSheet(R"(
font-size: 72px;
font-weight: 700;
)");
layout->addWidget(metric, 0, Qt::AlignLeft);
QLabel *label = new QLabel(name);
label->setStyleSheet(R"(
font-size: 32px;
font-weight: 600;
)");
layout->addWidget(label, 0, Qt::AlignLeft);
return layout;
} }
void DriveStats::replyFinished(QNetworkReply *l){ void DriveStats::replyFinished(QNetworkReply *l) {
QString answer = l->readAll(); QString answer = l->readAll();
answer.chop(1); answer.chop(1);
@ -116,47 +121,27 @@ void DriveStats::replyFinished(QNetworkReply *l){
QGridLayout *gl = new QGridLayout(); QGridLayout *gl = new QGridLayout();
int all_distance = all["distance"].toDouble()*(metric ? MILE_TO_KM : 1); int all_distance = all["distance"].toDouble()*(metric ? MILE_TO_KM : 1);
gl->addWidget(new QLabel(bold("ALL TIME")), 0, 0, 1, 3); gl->addWidget(new QLabel("ALL TIME"), 0, 0, 1, 3);
gl->addWidget(build_stat("DRIVES", all["routes"].toDouble()), 1, 0, 3, 1); gl->addLayout(build_stat("DRIVES", all["routes"].toDouble()), 1, 0, 3, 1);
gl->addWidget(build_stat(metric ? "KM" : "MILES", all_distance), 1, 1, 3, 1); gl->addLayout(build_stat(metric ? "KM" : "MILES", all_distance), 1, 1, 3, 1);
gl->addWidget(build_stat("HOURS", all["minutes"].toDouble() / 60), 1, 2, 3, 1); gl->addLayout(build_stat("HOURS", all["minutes"].toDouble() / 60), 1, 2, 3, 1);
QFrame *lineA = new QFrame;
lineA->setFrameShape(QFrame::HLine);
lineA->setFrameShadow(QFrame::Sunken);
lineA->setProperty("class", "line");
gl->addWidget(lineA, 5, 0, 1, 3);
int week_distance = week["distance"].toDouble()*(metric ? MILE_TO_KM : 1); int week_distance = week["distance"].toDouble()*(metric ? MILE_TO_KM : 1);
gl->addWidget(new QLabel(bold("PAST WEEK")), 6, 0, 1, 3); gl->addWidget(new QLabel("PAST WEEK"), 6, 0, 1, 3);
gl->addWidget(build_stat("DRIVES", week["routes"].toDouble()), 7, 0, 3, 1); gl->addLayout(build_stat("DRIVES", week["routes"].toDouble()), 7, 0, 3, 1);
gl->addWidget(build_stat(metric ? "KM" : "MILES", week_distance), 7, 1, 3, 1); gl->addLayout(build_stat(metric ? "KM" : "MILES", week_distance), 7, 1, 3, 1);
gl->addWidget(build_stat("HOURS", week["minutes"].toDouble() / 60), 7, 2, 3, 1); gl->addLayout(build_stat("HOURS", week["minutes"].toDouble() / 60), 7, 2, 3, 1);
f->setLayout(gl); setLayout(gl);
f->setStyleSheet(R"( setStyleSheet(R"(
[class="line"] {
border: 2px solid white;
}
[class="outside"] {
border-radius: 20px;
border: 2px solid white;
padding: 10px;
}
QLabel { QLabel {
font-size: 70px; font-size: 48px;
font-weight: 200; font-weight: 600;
} }
)"); )");
} }
DriveStats::DriveStats(QWidget *parent) : QWidget(parent) {
f = new QFrame;
f->setProperty("class", "outside");
QVBoxLayout *v = new QVBoxLayout;
v->addWidget(f);
setLayout(v);
DriveStats::DriveStats(QWidget *parent) : QWidget(parent) {
QString dongle_id = QString::fromStdString(Params().get("DongleId")); QString dongle_id = QString::fromStdString(Params().get("DongleId"));
QString token = create_jwt(dongle_id); QString token = create_jwt(dongle_id);

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <QFrame>
#include <QWidget> #include <QWidget>
#include <QNetworkReply> #include <QNetworkReply>
@ -12,6 +11,5 @@ public:
explicit DriveStats(QWidget *parent = 0); explicit DriveStats(QWidget *parent = 0);
private: private:
QFrame *f;
void replyFinished(QNetworkReply *l); void replyFinished(QNetworkReply *l);
}; };

@ -1,130 +1,110 @@
#include <QLabel>
#include <QFile> #include <QFile>
#include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QVBoxLayout>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonDocument> #include <QJsonDocument>
#include <QDebug> #include <QDebug>
#include "offroad_alerts.hpp" #include "offroad_alerts.hpp"
#include "common/params.h" #include "common/params.h"
void cleanLayout(QLayout* layout) { void cleanStackedWidget(QStackedWidget* swidget) {
while (QLayoutItem* item = layout->takeAt(0)) { while(swidget->count() > 0) {
if (QWidget* widget = item->widget()) { QWidget *w = swidget->widget(0);
widget->deleteLater(); swidget->removeWidget(w);
} w->deleteLater();
if (QLayout* childLayout = item->layout()) {
cleanLayout(childLayout);
}
delete item;
} }
} }
QString vectorToQString(std::vector<char> v) {
return QString::fromStdString(std::string(v.begin(), v.end()));
}
OffroadAlert::OffroadAlert(QWidget* parent) { OffroadAlert::OffroadAlert(QWidget* parent) {
vlayout = new QVBoxLayout; QVBoxLayout *main_layout = new QVBoxLayout();
refresh(); main_layout->setMargin(25);
setLayout(vlayout);
alerts_stack = new QStackedWidget();
main_layout->addWidget(alerts_stack, 1);
// bottom footer
QVBoxLayout *footer_layout = new QVBoxLayout();
main_layout->addLayout(footer_layout);
QPushButton *dismiss_btn = new QPushButton("Dismiss");
dismiss_btn->setFixedSize(453, 125);
footer_layout->addWidget(dismiss_btn, 0, Qt::AlignLeft);
QObject::connect(dismiss_btn, SIGNAL(released()), this, SIGNAL(closeAlerts()));
setLayout(main_layout);
setStyleSheet(R"(
* {
color: white;
}
QFrame {
border-radius: 30px;
background-color: #393939;
}
QPushButton {
color: black;
font-size: 40px;
font-weight: 600;
border-radius: 20px;
background-color: white;
}
)");
} }
void OffroadAlert::refresh() { void OffroadAlert::refresh() {
cleanLayout(vlayout);
parse_alerts(); parse_alerts();
cleanStackedWidget(alerts_stack);
updateAvailable = false;
std::vector<char> bytes = Params().read_db_bytes("UpdateAvailable"); std::vector<char> bytes = Params().read_db_bytes("UpdateAvailable");
if (bytes.size() && bytes[0] == '1') { updateAvailable = bytes.size() && bytes[0] == '1';
updateAvailable = true;
}
if (updateAvailable) {
// If there is an update available, don't show alerts
alerts.clear();
QFrame *f = new QFrame(); QVBoxLayout *layout = new QVBoxLayout;
QVBoxLayout *update_layout = new QVBoxLayout; if (updateAvailable) {
update_layout->setMargin(10); QLabel *title = new QLabel("Update Available");
update_layout->setSpacing(20);
QLabel *title = new QLabel("Update available");
title->setStyleSheet(R"( title->setStyleSheet(R"(
font-size: 55px; font-size: 72px;
font-weight: bold; font-weight: 700;
)"); )");
update_layout->addWidget(title, 0, Qt::AlignTop); layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
QString release_notes = QString::fromStdString(Params().get("ReleaseNotes")); QString release_notes = QString::fromStdString(Params().get("ReleaseNotes"));
QLabel *notes_label = new QLabel(release_notes); QLabel *body = new QLabel(release_notes);
notes_label->setStyleSheet(R"(font-size: 40px;)"); body->setStyleSheet(R"(
notes_label->setWordWrap(true); font-size: 48px;
update_layout->addWidget(notes_label, 1, Qt::AlignTop); font-weight: 600;
QPushButton *update_button = new QPushButton("Reboot and Update");
update_layout->addWidget(update_button);
#ifdef __aarch64__
QObject::connect(update_button, &QPushButton::released, [=]() {std::system("sudo reboot");});
#endif
f->setLayout(update_layout);
f->setStyleSheet(R"(
.QFrame{
border-radius: 20px;
border: 2px solid white;
background-color: #114267;
}
QPushButton {
padding: 20px;
font-size: 35px;
color: white;
background-color: blue;
}
)"); )");
layout->addWidget(body, 1, Qt::AlignLeft | Qt::AlignTop);
vlayout->addWidget(f);
vlayout->addSpacing(60);
} else { } else {
vlayout->addSpacing(60); // TODO: paginate the alerts
for (const auto &alert : alerts) { for (const auto &alert : alerts) {
QLabel *l = new QLabel(alert.text); QLabel *l = new QLabel(alert.text);
l->setWordWrap(true); l->setWordWrap(true);
l->setMargin(60); l->setMargin(60);
QString style = R"( QString style = R"(
font-size: 40px; font-size: 48px;
font-weight: bold; font-weight: 600;
border-radius: 30px;
border: 2px solid;
border-color: white;
)"; )";
style.append("background-color: " + QString(alert.severity ? "#971b1c" : "#114267")); style.append("background-color: " + QString(alert.severity ? "#E22C2C" : "#292929"));
l->setStyleSheet(style); l->setStyleSheet(style);
vlayout->addWidget(l);
vlayout->addSpacing(20); layout->addWidget(l, 0, Qt::AlignTop);
} }
layout->setSpacing(20);
} }
QPushButton *hide_btn = new QPushButton(updateAvailable ? "Later" : "Hide alerts"); QWidget *w = new QWidget();
hide_btn->setStyleSheet(R"( w->setLayout(layout);
padding: 20px; alerts_stack->addWidget(w);
font-size: 35px;
color: white;
background-color: blue;
)");
vlayout->addWidget(hide_btn);
QObject::connect(hide_btn, SIGNAL(released()), this, SIGNAL(closeAlerts()));
} }
void OffroadAlert::parse_alerts() { void OffroadAlert::parse_alerts() {
alerts.clear(); alerts.clear();
// We launch in selfdrive/ui
// TODO: only read this once
QFile inFile("../controls/lib/alerts_offroad.json"); QFile inFile("../controls/lib/alerts_offroad.json");
inFile.open(QIODevice::ReadOnly | QIODevice::Text); inFile.open(QIODevice::ReadOnly | QIODevice::Text);
QByteArray data = inFile.readAll(); QByteArray data = inFile.readAll();

@ -1,14 +1,14 @@
#pragma once #pragma once
#include <QWidget> #include <QFrame>
#include <QVBoxLayout> #include <QStackedWidget>
struct Alert { struct Alert {
QString text; QString text;
int severity; int severity;
}; };
class OffroadAlert : public QWidget { class OffroadAlert : public QFrame {
Q_OBJECT Q_OBJECT
public: public:
@ -17,8 +17,7 @@ public:
bool updateAvailable; bool updateAvailable;
private: private:
QVBoxLayout *vlayout; QStackedWidget *alerts_stack;
void parse_alerts(); void parse_alerts();
signals: signals:

@ -7,7 +7,12 @@
#include "sidebar.hpp" #include "sidebar.hpp"
static void ui_draw_sidebar_background(UIState *s) { static void ui_draw_sidebar_background(UIState *s) {
ui_draw_rect(s->vg, 0, 0, sbr_w, s->fb_h, COLOR_BLACK_ALPHA(85)); #ifdef QCOM
const NVGcolor color = COLOR_BLACK_ALPHA(85);
#else
const NVGcolor color = nvgRGBA(0x39, 0x39, 0x39, 0xff);
#endif
ui_draw_rect(s->vg, 0, 0, sbr_w, s->fb_h, color);
} }
static void ui_draw_sidebar_settings_button(UIState *s) { static void ui_draw_sidebar_settings_button(UIState *s) {

@ -18,7 +18,10 @@ if __name__ == "__main__":
while True: while True:
print("setting alert update") print("setting alert update")
params.put("UpdateAvailable", "1") params.put("UpdateAvailable", "1")
params.put("ReleaseNotes", "this is a new version") r = open(os.path.join(BASEDIR, "RELEASES.md"), "r").read()
r = r[:r.find('\n\n')] # Slice latest release notes
params.put("ReleaseNotes", r + "\n")
time.sleep(t) time.sleep(t)
params.put("UpdateAvailable", "0") params.put("UpdateAvailable", "0")

@ -74,7 +74,11 @@ typedef enum UIStatus {
} UIStatus; } UIStatus;
static std::map<UIStatus, NVGcolor> bg_colors = { static std::map<UIStatus, NVGcolor> bg_colors = {
#ifdef QCOM
{STATUS_OFFROAD, nvgRGBA(0x07, 0x23, 0x39, 0xf1)}, {STATUS_OFFROAD, nvgRGBA(0x07, 0x23, 0x39, 0xf1)},
#else
{STATUS_OFFROAD, nvgRGBA(0x0, 0x0, 0x0, 0xff)},
#endif
{STATUS_DISENGAGED, nvgRGBA(0x17, 0x33, 0x49, 0xc8)}, {STATUS_DISENGAGED, nvgRGBA(0x17, 0x33, 0x49, 0xc8)},
{STATUS_ENGAGED, nvgRGBA(0x17, 0x86, 0x44, 0xf1)}, {STATUS_ENGAGED, nvgRGBA(0x17, 0x86, 0x44, 0xf1)},
{STATUS_WARNING, nvgRGBA(0xDA, 0x6F, 0x25, 0xf1)}, {STATUS_WARNING, nvgRGBA(0xDA, 0x6F, 0x25, 0xf1)},

Loading…
Cancel
Save