From e06f9828eaf93fcbd9b0cdfbebc54b4197565fc5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 10 May 2021 22:09:36 -0700 Subject: [PATCH] match nvg onroad alerts (#20854) --- selfdrive/ui/qt/home.cc | 1 + selfdrive/ui/qt/onroad.cc | 157 ++++++++++++++++++------------------- selfdrive/ui/qt/onroad.h | 23 +++--- selfdrive/ui/qt/sidebar.cc | 16 ++-- selfdrive/ui/qt/util.h | 10 +++ selfdrive/ui/ui.cc | 2 +- 6 files changed, 105 insertions(+), 104 deletions(-) create mode 100644 selfdrive/ui/qt/util.h diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index 738804d5e4..18070e4e60 100644 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -46,6 +46,7 @@ void HomeWindow::offroadTransition(bool offroad) { slayout->setCurrentWidget(onroad); } sidebar->setVisible(offroad); + emit offroadTransitionSignal(offroad); } void HomeWindow::mousePressEvent(QMouseEvent* e) { diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index f457d6c3d4..cdc78a4eb5 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -5,7 +5,7 @@ #include "selfdrive/common/swaglog.h" #include "selfdrive/common/timing.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) { layout = new QStackedLayout(); @@ -17,76 +17,44 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { QObject::connect(this, &OnroadWindow::update, nvg, &NvgWindow::update); 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); + layout->addWidget(alerts); - // hack to align the onroad alerts, better way to do this? - QVBoxLayout *alerts_container = new QVBoxLayout(this); - 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); + // setup stacking order + alerts->raise(); setLayout(layout); } // ***** onroad widgets ***** -OnroadAlerts::OnroadAlerts(QWidget *parent) : QFrame(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 +OnroadAlerts::OnroadAlerts(QWidget *parent) : QWidget(parent) { for (auto &kv : sound_map) { auto path = QUrl::fromLocalFile(kv.second.first); sounds[kv.first].setSource(path); } } -void OnroadAlerts::update(const UIState &s) { +void OnroadAlerts::updateState(const UIState &s) { SubMaster &sm = *(s.sm); if (sm.updated("carState")) { // scale volume with speed volume = util::map_val(sm["carState"].getCarState().getVEgo(), 0.f, 20.f, Hardware::MIN_VOLUME, Hardware::MAX_VOLUME); } - if (sm.updated("controlsState")) { - const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState(); - updateAlert(QString::fromStdString(cs.getAlertText1()), QString::fromStdString(cs.getAlertText2()), - cs.getAlertBlinkingRate(), cs.getAlertType(), cs.getAlertSize(), cs.getAlertSound()); - } else { - // Handle controls timeout - if (s.scene.deviceState.getStarted() && (sm.frame - s.scene.started_frame) > 10 * UI_FREQ) { - const uint64_t cs_frame = sm.rcv_frame("controlsState"); - if (cs_frame < s.scene.started_frame) { + if (s.scene.deviceState.getStarted()) { + if (sm.updated("controlsState")) { + const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState(); + updateAlert(QString::fromStdString(cs.getAlertText1()), QString::fromStdString(cs.getAlertText2()), + cs.getAlertBlinkingRate(), cs.getAlertType(), cs.getAlertSize(), cs.getAlertSound()); + } else if ((sm.frame - s.scene.started_frame) > 10 * UI_FREQ) { + // Handle controls timeout + if (sm.rcv_frame("controlsState") < s.scene.started_frame) { // car is started, but controlsState hasn't been seen at all updateAlert("openpilot Unavailable", "Waiting for controls to start", 0, "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 updateAlert("TAKE CONTROL IMMEDIATELY", "Controls Unresponsive", 0, "controlsUnresponsive", cereal::ControlsState::AlertSize::FULL, AudibleAlert::CHIME_WARNING_REPEAT); @@ -97,23 +65,19 @@ void OnroadAlerts::update(const UIState &s) { } } - if (isVisible()) { - auto c = bg_colors[s.status]; - float alpha = 0.375 * cos((millis_since_boot() / 1000) * 2 * M_PI * blinking_rate) + 0.625; - bg.setRgb(c.r*255, c.g*255, c.b*255, c.a*alpha*255); - } + // TODO: add blinking back if performant + //float alpha = 0.375 * cos((millis_since_boot() / 1000) * 2 * M_PI * blinking_rate) + 0.625; + auto c = bg_colors[s.status]; + bg.setRgbF(c.r, c.g, c.b, c.a); } void OnroadAlerts::offroadTransition(bool offroad) { - stopSounds(); - setVisible(false); - alert_type = ""; + updateAlert("", "", 0, "", cereal::ControlsState::AlertSize::NONE, AudibleAlert::NONE); } -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) { - - if (alert_type.compare(type) == 0) { + if (alert_type.compare(type) == 0 && text1.compare(t1) == 0) { return; } @@ -122,28 +86,13 @@ void OnroadAlerts::updateAlert(const QString &text1, const QString &text2, float playSound(sound); } + text1 = t1; + text2 = t2; alert_type = type; + alert_size = size; 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); - repaint(); + update(); } void OnroadAlerts::playSound(AudibleAlert alert) { @@ -164,9 +113,57 @@ void OnroadAlerts::stopSounds() { void OnroadAlerts::paintEvent(QPaintEvent *event) { QPainter p(this); - p.setBrush(QBrush(bg)); + + static std::map 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.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() { diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index e81e61b61a..a3aab63d34 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -8,12 +8,13 @@ #include "cereal/gen/cpp/log.capnp.h" #include "selfdrive/hardware/hw.h" #include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/qt_window.h" typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert; // ***** onroad widgets ***** -class OnroadAlerts : public QFrame { +class OnroadAlerts : public QWidget { Q_OBJECT public: @@ -23,14 +24,11 @@ protected: void paintEvent(QPaintEvent*) override; private: - QColor bg; - QLabel *title, *msg; - QVBoxLayout *layout; - - void updateAlert(const QString &text1, const QString &text2, float blink_rate, + void stopSounds(); + void playSound(AudibleAlert alert); + void updateAlert(const QString &t1, const QString &t2, float blink_rate, const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound); - // sounds std::map> sound_map { // AudibleAlert, (file path, inf loop) {AudibleAlert::CHIME_DISENGAGE, {"../assets/sounds/disengaged.wav", false}}, @@ -42,16 +40,17 @@ private: {AudibleAlert::CHIME_ERROR, {"../assets/sounds/error.wav", false}}, {AudibleAlert::CHIME_PROMPT, {"../assets/sounds/error.wav", false}} }; + + QColor bg; float volume = Hardware::MIN_VOLUME; + std::map sounds; float blinking_rate = 0; + QString text1, text2; std::string alert_type; - std::map sounds; - - void playSound(AudibleAlert alert); - void stopSounds(); + cereal::ControlsState::AlertSize alert_size; public slots: - void update(const UIState &s); + void updateState(const UIState &s); void offroadTransition(bool offroad); }; diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 5ff0c4a56f..cf1319a70b 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -3,13 +3,7 @@ #include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/common/util.h" #include "selfdrive/hardware/hw.h" - -void configFont(QPainter &p, QString family, int size, int weight) { - QFont f(family); - f.setPixelSize(size); - f.setWeight(weight); - p.setFont(f); -} +#include "selfdrive/ui/qt/util.h" 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}; @@ -28,13 +22,13 @@ void Sidebar::drawMetric(QPainter &p, const QString &label, const QString &val, p.setPen(QColor(0xff, 0xff, 0xff)); 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()); p.drawText(r, Qt::AlignCenter, label); } else { - configFont(p, "Open Sans", 58, 500); + configFont(p, "Open Sans", 58, "Bold"); 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); } } @@ -107,7 +101,7 @@ void Sidebar::paintEvent(QPaintEvent *event) { // network 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)); const QRect r = QRect(50, 247, 100, 50); p.drawText(r, Qt::AlignCenter, network_type[net_type]); diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h new file mode 100644 index 0000000000..0ba42d3d85 --- /dev/null +++ b/selfdrive/ui/qt/util.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +inline void configFont(QPainter &p, QString family, int size, const QString &style) { + QFont f(family); + f.setPixelSize(size); + f.setStyleName(style); + p.setFont(f); +} diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 1933d8f579..0d91560f1c 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -305,7 +305,7 @@ void QUIState::update() { update_status(&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; emit offroadTransition(!ui_state.scene.started);