match nvg onroad alerts (#20854)

old-commit-hash: e06f9828ea
commatwo_master
Adeeb Shihadeh 4 years ago committed by GitHub
parent 3a38582efb
commit eb79a7214e
  1. 1
      selfdrive/ui/qt/home.cc
  2. 157
      selfdrive/ui/qt/onroad.cc
  3. 23
      selfdrive/ui/qt/onroad.h
  4. 16
      selfdrive/ui/qt/sidebar.cc
  5. 10
      selfdrive/ui/qt/util.h
  6. 2
      selfdrive/ui/ui.cc

@ -46,6 +46,7 @@ void HomeWindow::offroadTransition(bool offroad) {
slayout->setCurrentWidget(onroad); slayout->setCurrentWidget(onroad);
} }
sidebar->setVisible(offroad); sidebar->setVisible(offroad);
emit offroadTransitionSignal(offroad);
} }
void HomeWindow::mousePressEvent(QMouseEvent* e) { void HomeWindow::mousePressEvent(QMouseEvent* e) {

@ -5,7 +5,7 @@
#include "selfdrive/common/swaglog.h" #include "selfdrive/common/swaglog.h"
#include "selfdrive/common/timing.h" #include "selfdrive/common/timing.h"
#include "selfdrive/ui/paint.h" #include "selfdrive/ui/paint.h"
#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/util.h"
OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
layout = new QStackedLayout(); layout = new QStackedLayout();
@ -17,76 +17,44 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
QObject::connect(this, &OnroadWindow::update, nvg, &NvgWindow::update); QObject::connect(this, &OnroadWindow::update, nvg, &NvgWindow::update);
alerts = new OnroadAlerts(this); alerts = new OnroadAlerts(this);
QObject::connect(this, &OnroadWindow::update, alerts, &OnroadAlerts::update); QObject::connect(this, &OnroadWindow::update, alerts, &OnroadAlerts::updateState);
QObject::connect(this, &OnroadWindow::offroadTransition, alerts, &OnroadAlerts::offroadTransition); QObject::connect(this, &OnroadWindow::offroadTransition, alerts, &OnroadAlerts::offroadTransition);
layout->addWidget(alerts);
// hack to align the onroad alerts, better way to do this? // setup stacking order
QVBoxLayout *alerts_container = new QVBoxLayout(this); alerts->raise();
alerts_container->setMargin(0);
alerts_container->addStretch(1);
alerts_container->addWidget(alerts, 0, Qt::AlignBottom);
QWidget *w = new QWidget(this);
w->setLayout(alerts_container);
layout->addWidget(w);
// alerts on top
layout->setCurrentWidget(w);
setLayout(layout); setLayout(layout);
} }
// ***** onroad widgets ***** // ***** onroad widgets *****
OnroadAlerts::OnroadAlerts(QWidget *parent) : QFrame(parent) { OnroadAlerts::OnroadAlerts(QWidget *parent) : QWidget(parent) {
layout = new QVBoxLayout(this);
layout->setSpacing(40);
layout->setMargin(20);
title = new QLabel();
title->setWordWrap(true);
title->setAlignment(Qt::AlignCenter);
layout->addWidget(title);
msg = new QLabel();
msg->setWordWrap(true);
msg->setAlignment(Qt::AlignCenter);
layout->addWidget(msg);
layout->addStretch(1);
layout->insertStretch(0, 1);
setLayout(layout);
setStyleSheet("color: white;");
setVisible(false);
// setup sounds
for (auto &kv : sound_map) { for (auto &kv : sound_map) {
auto path = QUrl::fromLocalFile(kv.second.first); auto path = QUrl::fromLocalFile(kv.second.first);
sounds[kv.first].setSource(path); sounds[kv.first].setSource(path);
} }
} }
void OnroadAlerts::update(const UIState &s) { void OnroadAlerts::updateState(const UIState &s) {
SubMaster &sm = *(s.sm); SubMaster &sm = *(s.sm);
if (sm.updated("carState")) { if (sm.updated("carState")) {
// scale volume with speed // scale volume with speed
volume = util::map_val(sm["carState"].getCarState().getVEgo(), 0.f, 20.f, volume = util::map_val(sm["carState"].getCarState().getVEgo(), 0.f, 20.f,
Hardware::MIN_VOLUME, Hardware::MAX_VOLUME); Hardware::MIN_VOLUME, Hardware::MAX_VOLUME);
} }
if (sm.updated("controlsState")) { if (s.scene.deviceState.getStarted()) {
const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState(); if (sm.updated("controlsState")) {
updateAlert(QString::fromStdString(cs.getAlertText1()), QString::fromStdString(cs.getAlertText2()), const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState();
cs.getAlertBlinkingRate(), cs.getAlertType(), cs.getAlertSize(), cs.getAlertSound()); updateAlert(QString::fromStdString(cs.getAlertText1()), QString::fromStdString(cs.getAlertText2()),
} else { cs.getAlertBlinkingRate(), cs.getAlertType(), cs.getAlertSize(), cs.getAlertSound());
// Handle controls timeout } else if ((sm.frame - s.scene.started_frame) > 10 * UI_FREQ) {
if (s.scene.deviceState.getStarted() && (sm.frame - s.scene.started_frame) > 10 * UI_FREQ) { // Handle controls timeout
const uint64_t cs_frame = sm.rcv_frame("controlsState"); if (sm.rcv_frame("controlsState") < s.scene.started_frame) {
if (cs_frame < s.scene.started_frame) {
// car is started, but controlsState hasn't been seen at all // car is started, but controlsState hasn't been seen at all
updateAlert("openpilot Unavailable", "Waiting for controls to start", 0, updateAlert("openpilot Unavailable", "Waiting for controls to start", 0,
"controlsWaiting", cereal::ControlsState::AlertSize::MID, AudibleAlert::NONE); "controlsWaiting", cereal::ControlsState::AlertSize::MID, AudibleAlert::NONE);
} else if ((sm.frame - cs_frame) > 5 * UI_FREQ) { } else if ((sm.frame - sm.rcv_frame("controlsState")) > 5 * UI_FREQ) {
// car is started, but controls is lagging or died // car is started, but controls is lagging or died
updateAlert("TAKE CONTROL IMMEDIATELY", "Controls Unresponsive", 0, updateAlert("TAKE CONTROL IMMEDIATELY", "Controls Unresponsive", 0,
"controlsUnresponsive", cereal::ControlsState::AlertSize::FULL, AudibleAlert::CHIME_WARNING_REPEAT); "controlsUnresponsive", cereal::ControlsState::AlertSize::FULL, AudibleAlert::CHIME_WARNING_REPEAT);
@ -97,23 +65,19 @@ void OnroadAlerts::update(const UIState &s) {
} }
} }
if (isVisible()) { // TODO: add blinking back if performant
auto c = bg_colors[s.status]; //float alpha = 0.375 * cos((millis_since_boot() / 1000) * 2 * M_PI * blinking_rate) + 0.625;
float alpha = 0.375 * cos((millis_since_boot() / 1000) * 2 * M_PI * blinking_rate) + 0.625; auto c = bg_colors[s.status];
bg.setRgb(c.r*255, c.g*255, c.b*255, c.a*alpha*255); bg.setRgbF(c.r, c.g, c.b, c.a);
}
} }
void OnroadAlerts::offroadTransition(bool offroad) { void OnroadAlerts::offroadTransition(bool offroad) {
stopSounds(); updateAlert("", "", 0, "", cereal::ControlsState::AlertSize::NONE, AudibleAlert::NONE);
setVisible(false);
alert_type = "";
} }
void OnroadAlerts::updateAlert(const QString &text1, const QString &text2, float blink_rate, void OnroadAlerts::updateAlert(const QString &t1, const QString &t2, float blink_rate,
const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound) { const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound) {
if (alert_type.compare(type) == 0 && text1.compare(t1) == 0) {
if (alert_type.compare(type) == 0) {
return; return;
} }
@ -122,28 +86,13 @@ void OnroadAlerts::updateAlert(const QString &text1, const QString &text2, float
playSound(sound); playSound(sound);
} }
text1 = t1;
text2 = t2;
alert_type = type; alert_type = type;
alert_size = size;
blinking_rate = blink_rate; blinking_rate = blink_rate;
title->setText(text1);
msg->setText(text2);
msg->setVisible(!msg->text().isEmpty());
if (size == cereal::ControlsState::AlertSize::SMALL) {
setFixedHeight(241);
title->setStyleSheet("font-size: 70px; font-weight: 500;");
} else if (size == cereal::ControlsState::AlertSize::MID) {
setFixedHeight(390);
msg->setStyleSheet("font-size: 65px; font-weight: 400;");
title->setStyleSheet("font-size: 80px; font-weight: 500;");
} else if (size == cereal::ControlsState::AlertSize::FULL) {
setFixedHeight(vwp_h);
int title_size = (title->text().size() > 15) ? 130 : 110;
title->setStyleSheet(QString("font-size: %1px; font-weight: 500;").arg(title_size));
msg->setStyleSheet("font-size: 90px; font-weight: 400;");
}
setVisible(size != cereal::ControlsState::AlertSize::NONE); update();
repaint();
} }
void OnroadAlerts::playSound(AudibleAlert alert) { void OnroadAlerts::playSound(AudibleAlert alert) {
@ -164,9 +113,57 @@ void OnroadAlerts::stopSounds() {
void OnroadAlerts::paintEvent(QPaintEvent *event) { void OnroadAlerts::paintEvent(QPaintEvent *event) {
QPainter p(this); QPainter p(this);
p.setBrush(QBrush(bg));
static std::map<cereal::ControlsState::AlertSize, const int> alert_sizes = {
{cereal::ControlsState::AlertSize::NONE, 0},
{cereal::ControlsState::AlertSize::SMALL, 271},
{cereal::ControlsState::AlertSize::MID, 420},
{cereal::ControlsState::AlertSize::FULL, height()},
};
int h = alert_sizes[alert_size];
if (h == 0) {
return;
}
QRect r = QRect(0, height() - h, width(), h);
// draw background + gradient
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.drawRect(rect()); p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
p.setBrush(QBrush(bg));
p.drawRect(r);
QLinearGradient g(0, r.y(), 0, r.bottom());
g.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.05));
g.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0.35));
p.setBrush(QBrush(g));
p.fillRect(r, g);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
// remove bottom border
r = QRect(0, height() - h, width(), h - 30);
// text
const QPoint c = r.center();
p.setPen(QColor(0xff, 0xff, 0xff));
p.setRenderHint(QPainter::TextAntialiasing);
if (alert_size == cereal::ControlsState::AlertSize::SMALL) {
configFont(p, "Open Sans", 74, "SemiBold");
p.drawText(r, Qt::AlignCenter, text1);
} else if (alert_size == cereal::ControlsState::AlertSize::MID) {
configFont(p, "Open Sans", 88, "Bold");
p.drawText(QRect(0, c.y() - 125, width(), 150), Qt::AlignHCenter | Qt::AlignTop, text1);
configFont(p, "Open Sans", 66, "Regular");
p.drawText(QRect(0, c.y() + 21, width(), 90), Qt::AlignHCenter, text2);
} else if (alert_size == cereal::ControlsState::AlertSize::FULL) {
// TODO: offset from center to match old NVG UI, but why was it this way?
bool l = text1.length() > 15;
configFont(p, "Open Sans", l ? 132 : 177, "Bold");
p.drawText(QRect(0, r.y() + (l ? 240 : 270), width() - 60, 350), Qt::AlignHCenter | Qt::TextWordWrap, text1);
configFont(p, "Open Sans", 88, "Regular");
p.drawText(QRect(0, r.height() - (l ? 361 : 420), width() - 60, 300), Qt::AlignHCenter | Qt::TextWordWrap, text2);
}
} }
NvgWindow::~NvgWindow() { NvgWindow::~NvgWindow() {

@ -8,12 +8,13 @@
#include "cereal/gen/cpp/log.capnp.h" #include "cereal/gen/cpp/log.capnp.h"
#include "selfdrive/hardware/hw.h" #include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/qt_window.h"
typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert; typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert;
// ***** onroad widgets ***** // ***** onroad widgets *****
class OnroadAlerts : public QFrame { class OnroadAlerts : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
@ -23,14 +24,11 @@ protected:
void paintEvent(QPaintEvent*) override; void paintEvent(QPaintEvent*) override;
private: private:
QColor bg; void stopSounds();
QLabel *title, *msg; void playSound(AudibleAlert alert);
QVBoxLayout *layout; void updateAlert(const QString &t1, const QString &t2, float blink_rate,
void updateAlert(const QString &text1, const QString &text2, float blink_rate,
const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound); const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound);
// sounds
std::map<AudibleAlert, std::pair<QString, bool>> sound_map { std::map<AudibleAlert, std::pair<QString, bool>> sound_map {
// AudibleAlert, (file path, inf loop) // AudibleAlert, (file path, inf loop)
{AudibleAlert::CHIME_DISENGAGE, {"../assets/sounds/disengaged.wav", false}}, {AudibleAlert::CHIME_DISENGAGE, {"../assets/sounds/disengaged.wav", false}},
@ -42,16 +40,17 @@ private:
{AudibleAlert::CHIME_ERROR, {"../assets/sounds/error.wav", false}}, {AudibleAlert::CHIME_ERROR, {"../assets/sounds/error.wav", false}},
{AudibleAlert::CHIME_PROMPT, {"../assets/sounds/error.wav", false}} {AudibleAlert::CHIME_PROMPT, {"../assets/sounds/error.wav", false}}
}; };
QColor bg;
float volume = Hardware::MIN_VOLUME; float volume = Hardware::MIN_VOLUME;
std::map<AudibleAlert, QSoundEffect> sounds;
float blinking_rate = 0; float blinking_rate = 0;
QString text1, text2;
std::string alert_type; std::string alert_type;
std::map<AudibleAlert, QSoundEffect> sounds; cereal::ControlsState::AlertSize alert_size;
void playSound(AudibleAlert alert);
void stopSounds();
public slots: public slots:
void update(const UIState &s); void updateState(const UIState &s);
void offroadTransition(bool offroad); void offroadTransition(bool offroad);
}; };

@ -3,13 +3,7 @@
#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/common/util.h" #include "selfdrive/common/util.h"
#include "selfdrive/hardware/hw.h" #include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/util.h"
void configFont(QPainter &p, QString family, int size, int weight) {
QFont f(family);
f.setPixelSize(size);
f.setWeight(weight);
p.setFont(f);
}
void Sidebar::drawMetric(QPainter &p, const QString &label, const QString &val, QColor c, int y) { void Sidebar::drawMetric(QPainter &p, const QString &label, const QString &val, QColor c, int y) {
const QRect rect = {30, y, 240, val.isEmpty() ? (label.contains("\n") ? 124 : 100) : 148}; const QRect rect = {30, y, 240, val.isEmpty() ? (label.contains("\n") ? 124 : 100) : 148};
@ -28,13 +22,13 @@ void Sidebar::drawMetric(QPainter &p, const QString &label, const QString &val,
p.setPen(QColor(0xff, 0xff, 0xff)); p.setPen(QColor(0xff, 0xff, 0xff));
if (val.isEmpty()) { if (val.isEmpty()) {
configFont(p, "Open Sans", 35, 500); configFont(p, "Open Sans", 35, "Bold");
const QRect r = QRect(rect.x() + 35, rect.y(), rect.width() - 50, rect.height()); const QRect r = QRect(rect.x() + 35, rect.y(), rect.width() - 50, rect.height());
p.drawText(r, Qt::AlignCenter, label); p.drawText(r, Qt::AlignCenter, label);
} else { } else {
configFont(p, "Open Sans", 58, 500); configFont(p, "Open Sans", 58, "Bold");
p.drawText(rect.x() + 50, rect.y() + 71, val); p.drawText(rect.x() + 50, rect.y() + 71, val);
configFont(p, "Open Sans", 35, 400); configFont(p, "Open Sans", 35, "Regular");
p.drawText(rect.x() + 50, rect.y() + 50 + 77, label); p.drawText(rect.x() + 50, rect.y() + 50 + 77, label);
} }
} }
@ -107,7 +101,7 @@ void Sidebar::paintEvent(QPaintEvent *event) {
// network // network
p.drawImage(58, 196, signal_imgs[strength]); p.drawImage(58, 196, signal_imgs[strength]);
configFont(p, "Open Sans", 35, 400); configFont(p, "Open Sans", 35, "Regular");
p.setPen(QColor(0xff, 0xff, 0xff)); p.setPen(QColor(0xff, 0xff, 0xff));
const QRect r = QRect(50, 247, 100, 50); const QRect r = QRect(50, 247, 100, 50);
p.drawText(r, Qt::AlignCenter, network_type[net_type]); p.drawText(r, Qt::AlignCenter, network_type[net_type]);

@ -0,0 +1,10 @@
#pragma once
#include <QtWidgets>
inline void configFont(QPainter &p, QString family, int size, const QString &style) {
QFont f(family);
f.setPixelSize(size);
f.setStyleName(style);
p.setFont(f);
}

@ -305,7 +305,7 @@ void QUIState::update() {
update_status(&ui_state); update_status(&ui_state);
update_vision(&ui_state); update_vision(&ui_state);
if (ui_state.scene.started != started_prev) { if (ui_state.scene.started != started_prev || ui_state.sm->frame == 1) {
started_prev = ui_state.scene.started; started_prev = ui_state.scene.started;
emit offroadTransition(!ui_state.scene.started); emit offroadTransition(!ui_state.scene.started);

Loading…
Cancel
Save