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>
old-commit-hash: ce48d3c91e
This commit is contained in:
Adeeb Shihadeh 2020-12-25 01:02:47 -08:00 committed by GitHub
parent 30adcbc95e
commit 7dd876805f
9 changed files with 121 additions and 147 deletions

View File

@ -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;

View File

@ -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"
));

View File

@ -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>";
}
QWidget *widget(QLayout *l){
QWidget *q = new QWidget();
q->setLayout(l);
return q;
}
QWidget *build_stat(QString name, int stat){
QLayout *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 *metric = new QLabel(QString("%1").arg(stat));
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();
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);
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);
f->setLayout(gl);
f->setStyleSheet(R"(
[class="line"] {
border: 2px solid white;
}
[class="outside"] {
border-radius: 20px;
border: 2px solid white;
padding: 10px;
}
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);

View File

@ -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);
};

View File

@ -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;
}
updateAvailable = bytes.size() && bytes[0] == '1';
QVBoxLayout *layout = new QVBoxLayout;
if (updateAvailable) {
// If there is an update available, don't show alerts
alerts.clear();
QFrame *f = new QFrame();
QVBoxLayout *update_layout = new QVBoxLayout;
update_layout->setMargin(10);
update_layout->setSpacing(20);
QLabel *title = new QLabel("Update available");
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();

View File

@ -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:

View File

@ -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) {

View File

@ -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")

View File

@ -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)},