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. 89
      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 content
main_layout->addSpacing(100);
main_layout->addSpacing(25);
center_layout = new QStackedLayout();
DriveStats *drive = new DriveStats;

@ -64,7 +64,7 @@ QWidget * toggles_panel() {
toggles_list->setSpacing(25);
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.",
"../assets/offroad/icon_openpilot.png"
));

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

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

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

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

@ -7,7 +7,12 @@
#include "sidebar.hpp"
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) {

@ -18,7 +18,10 @@ if __name__ == "__main__":
while True:
print("setting alert update")
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)
params.put("UpdateAvailable", "0")

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

Loading…
Cancel
Save