mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-06-22 22:42:05 +08:00
ui/onroad: split into multiple files (#32059)
This commit is contained in:
@@ -280,6 +280,8 @@ selfdrive/ui/qt/network/*.h
|
||||
selfdrive/ui/qt/offroad/*.cc
|
||||
selfdrive/ui/qt/offroad/*.h
|
||||
selfdrive/ui/qt/offroad/*.qml
|
||||
selfdrive/ui/qt/onroad/*.cc
|
||||
selfdrive/ui/qt/onroad/*.h
|
||||
selfdrive/ui/qt/widgets/*.cc
|
||||
selfdrive/ui/qt/widgets/*.h
|
||||
selfdrive/ui/qt/maps/*.cc
|
||||
|
||||
@@ -36,10 +36,12 @@ widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
|
||||
Export('widgets')
|
||||
qt_libs = [widgets, qt_util] + base_libs
|
||||
|
||||
qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc",
|
||||
qt_src = ["main.cc", "qt/sidebar.cc", "qt/body.cc",
|
||||
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
|
||||
"qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc",
|
||||
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc"]
|
||||
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc",
|
||||
"qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc",
|
||||
"qt/onroad/buttons.cc", "qt/onroad/alerts.cc"]
|
||||
|
||||
# build translation files
|
||||
with open(File("translations/languages.json").abspath) as f:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "common/params.h"
|
||||
#include "selfdrive/ui/qt/offroad/driverview.h"
|
||||
#include "selfdrive/ui/qt/body.h"
|
||||
#include "selfdrive/ui/qt/onroad.h"
|
||||
#include "selfdrive/ui/qt/onroad/onroad_home.h"
|
||||
#include "selfdrive/ui/qt/sidebar.h"
|
||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||
#include "selfdrive/ui/qt/widgets/offroad_alerts.h"
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QStackedLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "selfdrive/ui/ui.h"
|
||||
#include "selfdrive/ui/qt/widgets/cameraview.h"
|
||||
|
||||
|
||||
const int btn_size = 192;
|
||||
const int img_size = (btn_size / 4) * 3;
|
||||
|
||||
|
||||
// ***** onroad widgets *****
|
||||
class OnroadAlerts : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OnroadAlerts(QWidget *parent = 0) : QWidget(parent) {}
|
||||
void updateState(const UIState &s);
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
struct Alert {
|
||||
QString text1;
|
||||
QString text2;
|
||||
QString type;
|
||||
cereal::ControlsState::AlertSize size;
|
||||
cereal::ControlsState::AlertStatus status;
|
||||
|
||||
bool equal(const Alert &other) const {
|
||||
return text1 == other.text1 && other.text2 == other.text2 && type == other.type;
|
||||
}
|
||||
};
|
||||
|
||||
const QMap<cereal::ControlsState::AlertStatus, QColor> alert_colors = {
|
||||
{cereal::ControlsState::AlertStatus::NORMAL, QColor(0x15, 0x15, 0x15, 0xf1)},
|
||||
{cereal::ControlsState::AlertStatus::USER_PROMPT, QColor(0xDA, 0x6F, 0x25, 0xf1)},
|
||||
{cereal::ControlsState::AlertStatus::CRITICAL, QColor(0xC9, 0x22, 0x31, 0xf1)},
|
||||
};
|
||||
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
OnroadAlerts::Alert getAlert(const SubMaster &sm, uint64_t started_frame);
|
||||
|
||||
QColor bg;
|
||||
Alert alert = {};
|
||||
};
|
||||
|
||||
class ExperimentalButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExperimentalButton(QWidget *parent = 0);
|
||||
void updateState(const UIState &s);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeMode();
|
||||
|
||||
Params params;
|
||||
QPixmap engage_img;
|
||||
QPixmap experimental_img;
|
||||
bool experimental_mode;
|
||||
bool engageable;
|
||||
};
|
||||
|
||||
|
||||
class MapSettingsButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapSettingsButton(QWidget *parent = 0);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
QPixmap settings_img;
|
||||
};
|
||||
|
||||
// container window for the NVG UI
|
||||
class AnnotatedCameraWidget : public CameraWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0);
|
||||
void updateState(const UIState &s);
|
||||
|
||||
MapSettingsButton *map_settings_btn;
|
||||
|
||||
private:
|
||||
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
|
||||
|
||||
QVBoxLayout *main_layout;
|
||||
ExperimentalButton *experimental_btn;
|
||||
QPixmap dm_img;
|
||||
float speed;
|
||||
QString speedUnit;
|
||||
float setSpeed;
|
||||
float speedLimit;
|
||||
bool is_cruise_set = false;
|
||||
bool is_metric = false;
|
||||
bool dmActive = false;
|
||||
bool hideBottomIcons = false;
|
||||
bool rightHandDM = false;
|
||||
float dm_fade_state = 1.0;
|
||||
bool has_us_speed_limit = false;
|
||||
bool has_eu_speed_limit = false;
|
||||
bool v_ego_cluster_seen = false;
|
||||
int status = STATUS_DISENGAGED;
|
||||
std::unique_ptr<PubMaster> pm;
|
||||
|
||||
int skip_frame_count = 0;
|
||||
bool wide_cam_requested = false;
|
||||
|
||||
protected:
|
||||
void paintGL() override;
|
||||
void initializeGL() override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void updateFrameMat() override;
|
||||
void drawLaneLines(QPainter &painter, const UIState *s);
|
||||
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd);
|
||||
void drawHud(QPainter &p);
|
||||
void drawDriverState(QPainter &painter, const UIState *s);
|
||||
inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); }
|
||||
inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); }
|
||||
inline QColor blackColor(int alpha = 255) { return QColor(0, 0, 0, alpha); }
|
||||
|
||||
double prev_draw_t = 0;
|
||||
FirstOrderFilter fps_filter;
|
||||
};
|
||||
|
||||
// container for all onroad widgets
|
||||
class OnroadWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OnroadWindow(QWidget* parent = 0);
|
||||
bool isMapVisible() const { return map && map->isVisible(); }
|
||||
void showMapPanel(bool show) { if (map) map->setVisible(show); }
|
||||
|
||||
signals:
|
||||
void mapPanelRequested();
|
||||
|
||||
private:
|
||||
void createMapWidget();
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void mousePressEvent(QMouseEvent* e) override;
|
||||
OnroadAlerts *alerts;
|
||||
AnnotatedCameraWidget *nvg;
|
||||
QColor bg = bg_colors[STATUS_DISENGAGED];
|
||||
QWidget *map = nullptr;
|
||||
QHBoxLayout* split;
|
||||
|
||||
private slots:
|
||||
void offroadTransition(bool offroad);
|
||||
void primeChanged(bool prime);
|
||||
void updateState(const UIState &s);
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "selfdrive/ui/qt/onroad/alerts.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <map>
|
||||
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
|
||||
void OnroadAlerts::updateState(const UIState &s) {
|
||||
Alert a = getAlert(*(s.sm), s.scene.started_frame);
|
||||
if (!alert.equal(a)) {
|
||||
alert = a;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void OnroadAlerts::clear() {
|
||||
alert = {};
|
||||
update();
|
||||
}
|
||||
|
||||
OnroadAlerts::Alert OnroadAlerts::getAlert(const SubMaster &sm, uint64_t started_frame) {
|
||||
const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState();
|
||||
const uint64_t controls_frame = sm.rcv_frame("controlsState");
|
||||
|
||||
Alert a = {};
|
||||
if (controls_frame >= started_frame) { // Don't get old alert.
|
||||
a = {cs.getAlertText1().cStr(), cs.getAlertText2().cStr(),
|
||||
cs.getAlertType().cStr(), cs.getAlertSize(), cs.getAlertStatus()};
|
||||
}
|
||||
|
||||
if (!sm.updated("controlsState") && (sm.frame - started_frame) > 5 * UI_FREQ) {
|
||||
const int CONTROLS_TIMEOUT = 5;
|
||||
const int controls_missing = (nanos_since_boot() - sm.rcv_time("controlsState")) / 1e9;
|
||||
|
||||
// Handle controls timeout
|
||||
if (controls_frame < started_frame) {
|
||||
// car is started, but controlsState hasn't been seen at all
|
||||
a = {tr("openpilot Unavailable"), tr("Waiting for controls to start"),
|
||||
"controlsWaiting", cereal::ControlsState::AlertSize::MID,
|
||||
cereal::ControlsState::AlertStatus::NORMAL};
|
||||
} else if (controls_missing > CONTROLS_TIMEOUT && !Hardware::PC()) {
|
||||
// car is started, but controls is lagging or died
|
||||
if (cs.getEnabled() && (controls_missing - CONTROLS_TIMEOUT) < 10) {
|
||||
a = {tr("TAKE CONTROL IMMEDIATELY"), tr("Controls Unresponsive"),
|
||||
"controlsUnresponsive", cereal::ControlsState::AlertSize::FULL,
|
||||
cereal::ControlsState::AlertStatus::CRITICAL};
|
||||
} else {
|
||||
a = {tr("Controls Unresponsive"), tr("Reboot Device"),
|
||||
"controlsUnresponsivePermanent", cereal::ControlsState::AlertSize::MID,
|
||||
cereal::ControlsState::AlertStatus::NORMAL};
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void OnroadAlerts::paintEvent(QPaintEvent *event) {
|
||||
if (alert.size == cereal::ControlsState::AlertSize::NONE) {
|
||||
return;
|
||||
}
|
||||
static std::map<cereal::ControlsState::AlertSize, const int> alert_heights = {
|
||||
{cereal::ControlsState::AlertSize::SMALL, 271},
|
||||
{cereal::ControlsState::AlertSize::MID, 420},
|
||||
{cereal::ControlsState::AlertSize::FULL, height()},
|
||||
};
|
||||
int h = alert_heights[alert.size];
|
||||
|
||||
int margin = 40;
|
||||
int radius = 30;
|
||||
if (alert.size == cereal::ControlsState::AlertSize::FULL) {
|
||||
margin = 0;
|
||||
radius = 0;
|
||||
}
|
||||
QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2);
|
||||
|
||||
QPainter p(this);
|
||||
|
||||
// draw background + gradient
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setBrush(QBrush(alert_colors[alert.status]));
|
||||
p.drawRoundedRect(r, radius, radius);
|
||||
|
||||
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.setCompositionMode(QPainter::CompositionMode_DestinationOver);
|
||||
p.setBrush(QBrush(g));
|
||||
p.drawRoundedRect(r, radius, radius);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
// text
|
||||
const QPoint c = r.center();
|
||||
p.setPen(QColor(0xff, 0xff, 0xff));
|
||||
p.setRenderHint(QPainter::TextAntialiasing);
|
||||
if (alert.size == cereal::ControlsState::AlertSize::SMALL) {
|
||||
p.setFont(InterFont(74, QFont::DemiBold));
|
||||
p.drawText(r, Qt::AlignCenter, alert.text1);
|
||||
} else if (alert.size == cereal::ControlsState::AlertSize::MID) {
|
||||
p.setFont(InterFont(88, QFont::Bold));
|
||||
p.drawText(QRect(0, c.y() - 125, width(), 150), Qt::AlignHCenter | Qt::AlignTop, alert.text1);
|
||||
p.setFont(InterFont(66));
|
||||
p.drawText(QRect(0, c.y() + 21, width(), 90), Qt::AlignHCenter, alert.text2);
|
||||
} else if (alert.size == cereal::ControlsState::AlertSize::FULL) {
|
||||
bool l = alert.text1.length() > 15;
|
||||
p.setFont(InterFont(l ? 132 : 177, QFont::Bold));
|
||||
p.drawText(QRect(0, r.y() + (l ? 240 : 270), width(), 600), Qt::AlignHCenter | Qt::TextWordWrap, alert.text1);
|
||||
p.setFont(InterFont(88));
|
||||
p.drawText(QRect(0, r.height() - (l ? 361 : 420), width(), 300), Qt::AlignHCenter | Qt::TextWordWrap, alert.text2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
class OnroadAlerts : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OnroadAlerts(QWidget *parent = 0) : QWidget(parent) {}
|
||||
void updateState(const UIState &s);
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
struct Alert {
|
||||
QString text1;
|
||||
QString text2;
|
||||
QString type;
|
||||
cereal::ControlsState::AlertSize size;
|
||||
cereal::ControlsState::AlertStatus status;
|
||||
|
||||
bool equal(const Alert &other) const {
|
||||
return text1 == other.text1 && other.text2 == other.text2 && type == other.type;
|
||||
}
|
||||
};
|
||||
|
||||
const QMap<cereal::ControlsState::AlertStatus, QColor> alert_colors = {
|
||||
{cereal::ControlsState::AlertStatus::NORMAL, QColor(0x15, 0x15, 0x15, 0xf1)},
|
||||
{cereal::ControlsState::AlertStatus::USER_PROMPT, QColor(0xDA, 0x6F, 0x25, 0xf1)},
|
||||
{cereal::ControlsState::AlertStatus::CRITICAL, QColor(0xC9, 0x22, 0x31, 0xf1)},
|
||||
};
|
||||
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
OnroadAlerts::Alert getAlert(const SubMaster &sm, uint64_t started_frame);
|
||||
|
||||
QColor bg;
|
||||
Alert alert = {};
|
||||
};
|
||||
@@ -1,310 +1,13 @@
|
||||
#include "selfdrive/ui/qt/onroad.h"
|
||||
|
||||
#include "selfdrive/ui/qt/onroad/annotated_camera.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include "common/swaglog.h"
|
||||
#include "common/timing.h"
|
||||
#include "selfdrive/ui/qt/onroad/buttons.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
#ifdef ENABLE_MAPS
|
||||
#include "selfdrive/ui/qt/maps/map_helpers.h"
|
||||
#include "selfdrive/ui/qt/maps/map_panel.h"
|
||||
#endif
|
||||
|
||||
static void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity) {
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.setOpacity(1.0); // bg dictates opacity of ellipse
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(bg);
|
||||
p.drawEllipse(center, btn_size / 2, btn_size / 2);
|
||||
p.setOpacity(opacity);
|
||||
p.drawPixmap(center - QPoint(img.width() / 2, img.height() / 2), img);
|
||||
p.setOpacity(1.0);
|
||||
}
|
||||
|
||||
OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->setMargin(UI_BORDER_SIZE);
|
||||
QStackedLayout *stacked_layout = new QStackedLayout;
|
||||
stacked_layout->setStackingMode(QStackedLayout::StackAll);
|
||||
main_layout->addLayout(stacked_layout);
|
||||
|
||||
nvg = new AnnotatedCameraWidget(VISION_STREAM_ROAD, this);
|
||||
|
||||
QWidget * split_wrapper = new QWidget;
|
||||
split = new QHBoxLayout(split_wrapper);
|
||||
split->setContentsMargins(0, 0, 0, 0);
|
||||
split->setSpacing(0);
|
||||
split->addWidget(nvg);
|
||||
|
||||
if (getenv("DUAL_CAMERA_VIEW")) {
|
||||
CameraWidget *arCam = new CameraWidget("camerad", VISION_STREAM_ROAD, true, this);
|
||||
split->insertWidget(0, arCam);
|
||||
}
|
||||
|
||||
if (getenv("MAP_RENDER_VIEW")) {
|
||||
CameraWidget *map_render = new CameraWidget("navd", VISION_STREAM_MAP, false, this);
|
||||
split->insertWidget(0, map_render);
|
||||
}
|
||||
|
||||
stacked_layout->addWidget(split_wrapper);
|
||||
|
||||
alerts = new OnroadAlerts(this);
|
||||
alerts->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
stacked_layout->addWidget(alerts);
|
||||
|
||||
// setup stacking order
|
||||
alerts->raise();
|
||||
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState);
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
|
||||
QObject::connect(uiState(), &UIState::primeChanged, this, &OnroadWindow::primeChanged);
|
||||
}
|
||||
|
||||
void OnroadWindow::updateState(const UIState &s) {
|
||||
if (!s.scene.started) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s.scene.map_on_left) {
|
||||
split->setDirection(QBoxLayout::LeftToRight);
|
||||
} else {
|
||||
split->setDirection(QBoxLayout::RightToLeft);
|
||||
}
|
||||
|
||||
alerts->updateState(s);
|
||||
nvg->updateState(s);
|
||||
|
||||
QColor bgColor = bg_colors[s.status];
|
||||
if (bg != bgColor) {
|
||||
// repaint border
|
||||
bg = bgColor;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void OnroadWindow::mousePressEvent(QMouseEvent* e) {
|
||||
#ifdef ENABLE_MAPS
|
||||
if (map != nullptr) {
|
||||
bool sidebarVisible = geometry().x() > 0;
|
||||
bool show_map = !sidebarVisible;
|
||||
map->setVisible(show_map && !map->isVisible());
|
||||
}
|
||||
#endif
|
||||
// propagation event to parent(HomeWindow)
|
||||
QWidget::mousePressEvent(e);
|
||||
}
|
||||
|
||||
void OnroadWindow::createMapWidget() {
|
||||
#ifdef ENABLE_MAPS
|
||||
auto m = new MapPanel(get_mapbox_settings());
|
||||
map = m;
|
||||
QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindow::mapPanelRequested);
|
||||
QObject::connect(nvg->map_settings_btn, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings);
|
||||
nvg->map_settings_btn->setEnabled(true);
|
||||
|
||||
m->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
|
||||
split->insertWidget(0, m);
|
||||
// hidden by default, made visible when navRoute is published
|
||||
m->setVisible(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnroadWindow::offroadTransition(bool offroad) {
|
||||
#ifdef ENABLE_MAPS
|
||||
if (!offroad) {
|
||||
if (map == nullptr && (uiState()->hasPrime() || !MAPBOX_TOKEN.isEmpty())) {
|
||||
createMapWidget();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
alerts->clear();
|
||||
}
|
||||
|
||||
void OnroadWindow::primeChanged(bool prime) {
|
||||
#ifdef ENABLE_MAPS
|
||||
if (map && (!prime && MAPBOX_TOKEN.isEmpty())) {
|
||||
nvg->map_settings_btn->setEnabled(false);
|
||||
nvg->map_settings_btn->setVisible(false);
|
||||
map->deleteLater();
|
||||
map = nullptr;
|
||||
} else if (!map && (prime || !MAPBOX_TOKEN.isEmpty())) {
|
||||
createMapWidget();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnroadWindow::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255));
|
||||
}
|
||||
|
||||
// ***** onroad widgets *****
|
||||
|
||||
// OnroadAlerts
|
||||
|
||||
void OnroadAlerts::updateState(const UIState &s) {
|
||||
Alert a = getAlert(*(s.sm), s.scene.started_frame);
|
||||
if (!alert.equal(a)) {
|
||||
alert = a;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void OnroadAlerts::clear() {
|
||||
alert = {};
|
||||
update();
|
||||
}
|
||||
|
||||
OnroadAlerts::Alert OnroadAlerts::getAlert(const SubMaster &sm, uint64_t started_frame) {
|
||||
const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState();
|
||||
const uint64_t controls_frame = sm.rcv_frame("controlsState");
|
||||
|
||||
Alert a = {};
|
||||
if (controls_frame >= started_frame) { // Don't get old alert.
|
||||
a = {cs.getAlertText1().cStr(), cs.getAlertText2().cStr(),
|
||||
cs.getAlertType().cStr(), cs.getAlertSize(), cs.getAlertStatus()};
|
||||
}
|
||||
|
||||
if (!sm.updated("controlsState") && (sm.frame - started_frame) > 5 * UI_FREQ) {
|
||||
const int CONTROLS_TIMEOUT = 5;
|
||||
const int controls_missing = (nanos_since_boot() - sm.rcv_time("controlsState")) / 1e9;
|
||||
|
||||
// Handle controls timeout
|
||||
if (controls_frame < started_frame) {
|
||||
// car is started, but controlsState hasn't been seen at all
|
||||
a = {tr("openpilot Unavailable"), tr("Waiting for controls to start"),
|
||||
"controlsWaiting", cereal::ControlsState::AlertSize::MID,
|
||||
cereal::ControlsState::AlertStatus::NORMAL};
|
||||
} else if (controls_missing > CONTROLS_TIMEOUT && !Hardware::PC()) {
|
||||
// car is started, but controls is lagging or died
|
||||
if (cs.getEnabled() && (controls_missing - CONTROLS_TIMEOUT) < 10) {
|
||||
a = {tr("TAKE CONTROL IMMEDIATELY"), tr("Controls Unresponsive"),
|
||||
"controlsUnresponsive", cereal::ControlsState::AlertSize::FULL,
|
||||
cereal::ControlsState::AlertStatus::CRITICAL};
|
||||
} else {
|
||||
a = {tr("Controls Unresponsive"), tr("Reboot Device"),
|
||||
"controlsUnresponsivePermanent", cereal::ControlsState::AlertSize::MID,
|
||||
cereal::ControlsState::AlertStatus::NORMAL};
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void OnroadAlerts::paintEvent(QPaintEvent *event) {
|
||||
if (alert.size == cereal::ControlsState::AlertSize::NONE) {
|
||||
return;
|
||||
}
|
||||
static std::map<cereal::ControlsState::AlertSize, const int> alert_heights = {
|
||||
{cereal::ControlsState::AlertSize::SMALL, 271},
|
||||
{cereal::ControlsState::AlertSize::MID, 420},
|
||||
{cereal::ControlsState::AlertSize::FULL, height()},
|
||||
};
|
||||
int h = alert_heights[alert.size];
|
||||
|
||||
int margin = 40;
|
||||
int radius = 30;
|
||||
if (alert.size == cereal::ControlsState::AlertSize::FULL) {
|
||||
margin = 0;
|
||||
radius = 0;
|
||||
}
|
||||
QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2);
|
||||
|
||||
QPainter p(this);
|
||||
|
||||
// draw background + gradient
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setBrush(QBrush(alert_colors[alert.status]));
|
||||
p.drawRoundedRect(r, radius, radius);
|
||||
|
||||
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.setCompositionMode(QPainter::CompositionMode_DestinationOver);
|
||||
p.setBrush(QBrush(g));
|
||||
p.drawRoundedRect(r, radius, radius);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
// text
|
||||
const QPoint c = r.center();
|
||||
p.setPen(QColor(0xff, 0xff, 0xff));
|
||||
p.setRenderHint(QPainter::TextAntialiasing);
|
||||
if (alert.size == cereal::ControlsState::AlertSize::SMALL) {
|
||||
p.setFont(InterFont(74, QFont::DemiBold));
|
||||
p.drawText(r, Qt::AlignCenter, alert.text1);
|
||||
} else if (alert.size == cereal::ControlsState::AlertSize::MID) {
|
||||
p.setFont(InterFont(88, QFont::Bold));
|
||||
p.drawText(QRect(0, c.y() - 125, width(), 150), Qt::AlignHCenter | Qt::AlignTop, alert.text1);
|
||||
p.setFont(InterFont(66));
|
||||
p.drawText(QRect(0, c.y() + 21, width(), 90), Qt::AlignHCenter, alert.text2);
|
||||
} else if (alert.size == cereal::ControlsState::AlertSize::FULL) {
|
||||
bool l = alert.text1.length() > 15;
|
||||
p.setFont(InterFont(l ? 132 : 177, QFont::Bold));
|
||||
p.drawText(QRect(0, r.y() + (l ? 240 : 270), width(), 600), Qt::AlignHCenter | Qt::TextWordWrap, alert.text1);
|
||||
p.setFont(InterFont(88));
|
||||
p.drawText(QRect(0, r.height() - (l ? 361 : 420), width(), 300), Qt::AlignHCenter | Qt::TextWordWrap, alert.text2);
|
||||
}
|
||||
}
|
||||
|
||||
// ExperimentalButton
|
||||
ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(false), engageable(false), QPushButton(parent) {
|
||||
setFixedSize(btn_size, btn_size);
|
||||
|
||||
engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
|
||||
experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size, img_size});
|
||||
QObject::connect(this, &QPushButton::clicked, this, &ExperimentalButton::changeMode);
|
||||
}
|
||||
|
||||
void ExperimentalButton::changeMode() {
|
||||
const auto cp = (*uiState()->sm)["carParams"].getCarParams();
|
||||
bool can_change = hasLongitudinalControl(cp) && params.getBool("ExperimentalModeConfirmed");
|
||||
if (can_change) {
|
||||
params.putBool("ExperimentalMode", !experimental_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void ExperimentalButton::updateState(const UIState &s) {
|
||||
const auto cs = (*s.sm)["controlsState"].getControlsState();
|
||||
bool eng = cs.getEngageable() || cs.getEnabled();
|
||||
if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
|
||||
engageable = eng;
|
||||
experimental_mode = cs.getExperimentalMode();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ExperimentalButton::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
QPixmap img = experimental_mode ? experimental_img : engage_img;
|
||||
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), img, QColor(0, 0, 0, 166), (isDown() || !engageable) ? 0.6 : 1.0);
|
||||
}
|
||||
|
||||
|
||||
// MapSettingsButton
|
||||
MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) {
|
||||
setFixedSize(btn_size, btn_size);
|
||||
settings_img = loadPixmap("../assets/navigation/icon_directions_outlined.svg", {img_size, img_size});
|
||||
|
||||
// hidden by default, made visible if map is created (has prime or mapbox token)
|
||||
setVisible(false);
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
void MapSettingsButton::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0);
|
||||
}
|
||||
|
||||
|
||||
// Window that shows camera view and variety of info drawn on top
|
||||
AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent) {
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <memory>
|
||||
|
||||
#include "selfdrive/ui/qt/onroad/buttons.h"
|
||||
#include "selfdrive/ui/qt/widgets/cameraview.h"
|
||||
|
||||
class AnnotatedCameraWidget : public CameraWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0);
|
||||
void updateState(const UIState &s);
|
||||
|
||||
MapSettingsButton *map_settings_btn;
|
||||
|
||||
private:
|
||||
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
|
||||
|
||||
QVBoxLayout *main_layout;
|
||||
ExperimentalButton *experimental_btn;
|
||||
QPixmap dm_img;
|
||||
float speed;
|
||||
QString speedUnit;
|
||||
float setSpeed;
|
||||
float speedLimit;
|
||||
bool is_cruise_set = false;
|
||||
bool is_metric = false;
|
||||
bool dmActive = false;
|
||||
bool hideBottomIcons = false;
|
||||
bool rightHandDM = false;
|
||||
float dm_fade_state = 1.0;
|
||||
bool has_us_speed_limit = false;
|
||||
bool has_eu_speed_limit = false;
|
||||
bool v_ego_cluster_seen = false;
|
||||
int status = STATUS_DISENGAGED;
|
||||
std::unique_ptr<PubMaster> pm;
|
||||
|
||||
int skip_frame_count = 0;
|
||||
bool wide_cam_requested = false;
|
||||
|
||||
protected:
|
||||
void paintGL() override;
|
||||
void initializeGL() override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void updateFrameMat() override;
|
||||
void drawLaneLines(QPainter &painter, const UIState *s);
|
||||
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd);
|
||||
void drawHud(QPainter &p);
|
||||
void drawDriverState(QPainter &painter, const UIState *s);
|
||||
inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); }
|
||||
inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); }
|
||||
inline QColor blackColor(int alpha = 255) { return QColor(0, 0, 0, alpha); }
|
||||
|
||||
double prev_draw_t = 0;
|
||||
FirstOrderFilter fps_filter;
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "selfdrive/ui/qt/onroad/buttons.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
|
||||
void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity) {
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.setOpacity(1.0); // bg dictates opacity of ellipse
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(bg);
|
||||
p.drawEllipse(center, btn_size / 2, btn_size / 2);
|
||||
p.setOpacity(opacity);
|
||||
p.drawPixmap(center - QPoint(img.width() / 2, img.height() / 2), img);
|
||||
p.setOpacity(1.0);
|
||||
}
|
||||
|
||||
// ExperimentalButton
|
||||
ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(false), engageable(false), QPushButton(parent) {
|
||||
setFixedSize(btn_size, btn_size);
|
||||
|
||||
engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
|
||||
experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size, img_size});
|
||||
QObject::connect(this, &QPushButton::clicked, this, &ExperimentalButton::changeMode);
|
||||
}
|
||||
|
||||
void ExperimentalButton::changeMode() {
|
||||
const auto cp = (*uiState()->sm)["carParams"].getCarParams();
|
||||
bool can_change = hasLongitudinalControl(cp) && params.getBool("ExperimentalModeConfirmed");
|
||||
if (can_change) {
|
||||
params.putBool("ExperimentalMode", !experimental_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void ExperimentalButton::updateState(const UIState &s) {
|
||||
const auto cs = (*s.sm)["controlsState"].getControlsState();
|
||||
bool eng = cs.getEngageable() || cs.getEnabled();
|
||||
if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
|
||||
engageable = eng;
|
||||
experimental_mode = cs.getExperimentalMode();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ExperimentalButton::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
QPixmap img = experimental_mode ? experimental_img : engage_img;
|
||||
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), img, QColor(0, 0, 0, 166), (isDown() || !engageable) ? 0.6 : 1.0);
|
||||
}
|
||||
|
||||
// MapSettingsButton
|
||||
MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) {
|
||||
setFixedSize(btn_size, btn_size);
|
||||
settings_img = loadPixmap("../assets/navigation/icon_directions_outlined.svg", {img_size, img_size});
|
||||
|
||||
// hidden by default, made visible if map is created (has prime or mapbox token)
|
||||
setVisible(false);
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
void MapSettingsButton::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
const int btn_size = 192;
|
||||
const int img_size = (btn_size / 4) * 3;
|
||||
|
||||
class ExperimentalButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExperimentalButton(QWidget *parent = 0);
|
||||
void updateState(const UIState &s);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeMode();
|
||||
|
||||
Params params;
|
||||
QPixmap engage_img;
|
||||
QPixmap experimental_img;
|
||||
bool experimental_mode;
|
||||
bool engageable;
|
||||
};
|
||||
|
||||
|
||||
class MapSettingsButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapSettingsButton(QWidget *parent = 0);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
QPixmap settings_img;
|
||||
};
|
||||
|
||||
void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity);
|
||||
@@ -0,0 +1,128 @@
|
||||
#include "selfdrive/ui/qt/onroad/onroad_home.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#ifdef ENABLE_MAPS
|
||||
#include "selfdrive/ui/qt/maps/map_helpers.h"
|
||||
#include "selfdrive/ui/qt/maps/map_panel.h"
|
||||
#endif
|
||||
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
|
||||
OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->setMargin(UI_BORDER_SIZE);
|
||||
QStackedLayout *stacked_layout = new QStackedLayout;
|
||||
stacked_layout->setStackingMode(QStackedLayout::StackAll);
|
||||
main_layout->addLayout(stacked_layout);
|
||||
|
||||
nvg = new AnnotatedCameraWidget(VISION_STREAM_ROAD, this);
|
||||
|
||||
QWidget * split_wrapper = new QWidget;
|
||||
split = new QHBoxLayout(split_wrapper);
|
||||
split->setContentsMargins(0, 0, 0, 0);
|
||||
split->setSpacing(0);
|
||||
split->addWidget(nvg);
|
||||
|
||||
if (getenv("DUAL_CAMERA_VIEW")) {
|
||||
CameraWidget *arCam = new CameraWidget("camerad", VISION_STREAM_ROAD, true, this);
|
||||
split->insertWidget(0, arCam);
|
||||
}
|
||||
|
||||
if (getenv("MAP_RENDER_VIEW")) {
|
||||
CameraWidget *map_render = new CameraWidget("navd", VISION_STREAM_MAP, false, this);
|
||||
split->insertWidget(0, map_render);
|
||||
}
|
||||
|
||||
stacked_layout->addWidget(split_wrapper);
|
||||
|
||||
alerts = new OnroadAlerts(this);
|
||||
alerts->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
stacked_layout->addWidget(alerts);
|
||||
|
||||
// setup stacking order
|
||||
alerts->raise();
|
||||
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState);
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
|
||||
QObject::connect(uiState(), &UIState::primeChanged, this, &OnroadWindow::primeChanged);
|
||||
}
|
||||
|
||||
void OnroadWindow::updateState(const UIState &s) {
|
||||
if (!s.scene.started) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s.scene.map_on_left) {
|
||||
split->setDirection(QBoxLayout::LeftToRight);
|
||||
} else {
|
||||
split->setDirection(QBoxLayout::RightToLeft);
|
||||
}
|
||||
|
||||
alerts->updateState(s);
|
||||
nvg->updateState(s);
|
||||
|
||||
QColor bgColor = bg_colors[s.status];
|
||||
if (bg != bgColor) {
|
||||
// repaint border
|
||||
bg = bgColor;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void OnroadWindow::mousePressEvent(QMouseEvent* e) {
|
||||
#ifdef ENABLE_MAPS
|
||||
if (map != nullptr) {
|
||||
bool sidebarVisible = geometry().x() > 0;
|
||||
bool show_map = !sidebarVisible;
|
||||
map->setVisible(show_map && !map->isVisible());
|
||||
}
|
||||
#endif
|
||||
// propagation event to parent(HomeWindow)
|
||||
QWidget::mousePressEvent(e);
|
||||
}
|
||||
|
||||
void OnroadWindow::createMapWidget() {
|
||||
#ifdef ENABLE_MAPS
|
||||
auto m = new MapPanel(get_mapbox_settings());
|
||||
map = m;
|
||||
QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindow::mapPanelRequested);
|
||||
QObject::connect(nvg->map_settings_btn, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings);
|
||||
nvg->map_settings_btn->setEnabled(true);
|
||||
|
||||
m->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
|
||||
split->insertWidget(0, m);
|
||||
// hidden by default, made visible when navRoute is published
|
||||
m->setVisible(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnroadWindow::offroadTransition(bool offroad) {
|
||||
#ifdef ENABLE_MAPS
|
||||
if (!offroad) {
|
||||
if (map == nullptr && (uiState()->hasPrime() || !MAPBOX_TOKEN.isEmpty())) {
|
||||
createMapWidget();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
alerts->clear();
|
||||
}
|
||||
|
||||
void OnroadWindow::primeChanged(bool prime) {
|
||||
#ifdef ENABLE_MAPS
|
||||
if (map && (!prime && MAPBOX_TOKEN.isEmpty())) {
|
||||
nvg->map_settings_btn->setEnabled(false);
|
||||
nvg->map_settings_btn->setVisible(false);
|
||||
map->deleteLater();
|
||||
map = nullptr;
|
||||
} else if (!map && (prime || !MAPBOX_TOKEN.isEmpty())) {
|
||||
createMapWidget();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnroadWindow::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255));
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/ui/qt/onroad/alerts.h"
|
||||
#include "selfdrive/ui/qt/onroad/annotated_camera.h"
|
||||
|
||||
class OnroadWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OnroadWindow(QWidget* parent = 0);
|
||||
bool isMapVisible() const { return map && map->isVisible(); }
|
||||
void showMapPanel(bool show) { if (map) map->setVisible(show); }
|
||||
|
||||
signals:
|
||||
void mapPanelRequested();
|
||||
|
||||
private:
|
||||
void createMapWidget();
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void mousePressEvent(QMouseEvent* e) override;
|
||||
OnroadAlerts *alerts;
|
||||
AnnotatedCameraWidget *nvg;
|
||||
QColor bg = bg_colors[STATUS_DISENGAGED];
|
||||
QWidget *map = nullptr;
|
||||
QHBoxLayout* split;
|
||||
|
||||
private slots:
|
||||
void offroadTransition(bool offroad);
|
||||
void primeChanged(bool prime);
|
||||
void updateState(const UIState &s);
|
||||
};
|
||||
Reference in New Issue
Block a user