ui/onroad: split into multiple files (#32059)
parent
70cdcc51a9
commit
c1b059de1e
12 changed files with 484 additions and 466 deletions
@ -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); |
||||
}; |
Loading…
Reference in new issue