This commit is contained in:
Your Name
2024-05-17 11:14:45 -05:00
parent 54a65f1697
commit eb05d8bfd3
38 changed files with 101 additions and 3691 deletions

View File

@@ -8,15 +8,10 @@
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/drive_stats.h"
#include "selfdrive/ui/qt/widgets/prime.h"
#include "system/hardware/hw.h"
#include <QWebEngineView>
#ifdef ENABLE_MAPS
#include "selfdrive/ui/qt/maps/map_settings.h"
#endif
// HomeWindow: the container for the offroad and onroad UIs
HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
@@ -40,7 +35,6 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
slayout->addWidget(home);
onroad = new OnroadWindow(this);
QObject::connect(onroad, &OnroadWindow::mapPanelRequested, this, [=] { sidebar->hide(); });
slayout->addWidget(onroad);
// CLEARPILOT
@@ -71,10 +65,6 @@ void HomeWindow::showSidebar(bool show) {
sidebar->setVisible(show);
}
void HomeWindow::showMapPanel(bool show) {
onroad->showMapPanel(show);
}
void HomeWindow::updateState(const UIState &s) {
// const SubMaster &sm = *(s.sm);
if (s.scene.started) {
@@ -89,7 +79,6 @@ void HomeWindow::offroadTransition(bool offroad) {
} else {
sidebar->setVisible(false);
slayout->setCurrentWidget(onroad);
uiState()->scene.map_open = onroad->isMapVisible();
}
}

View File

@@ -60,7 +60,6 @@ public slots:
void showDriverView(bool show, bool started=false);
void showOnroad();
void showSidebar(bool show);
void showMapPanel(bool show);
protected:
void mousePressEvent(QMouseEvent* e) override;

View File

@@ -8,7 +8,6 @@
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/drive_stats.h"
#include "selfdrive/ui/qt/widgets/prime.h"
#include "system/hardware/hw.h"
#ifdef ENABLE_MAPS

View File

@@ -1,469 +0,0 @@
#include "selfdrive/ui/qt/maps/map.h"
#include <algorithm>
#include <eigen3/Eigen/Dense>
#include <QDebug>
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/ui.h"
const int INTERACTION_TIMEOUT = 100;
const float MAX_ZOOM = 17;
const float MIN_ZOOM = 14;
const float MAX_PITCH = 50;
const float MIN_PITCH = 0;
const float MAP_SCALE = 2;
MapWindow::MapWindow(const QMapLibre::Settings &settings) : m_settings(settings), velocity_filter(0, 10, 0.05, false) {
QObject::connect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState);
map_overlay = new QWidget (this);
map_overlay->setAttribute(Qt::WA_TranslucentBackground, true);
QVBoxLayout *overlay_layout = new QVBoxLayout(map_overlay);
overlay_layout->setContentsMargins(0, 0, 0, 0);
// Instructions
map_instructions = new MapInstructions(this);
map_instructions->setVisible(false);
map_eta = new MapETA(this);
map_eta->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
map_eta->setFixedHeight(120);
error = new QLabel(this);
error->setStyleSheet(R"(color:white;padding:50px 11px;font-size: 90px; background-color:rgba(0, 0, 0, 150);)");
error->setAlignment(Qt::AlignCenter);
overlay_layout->addWidget(error);
overlay_layout->addWidget(map_instructions);
overlay_layout->addStretch(1);
overlay_layout->addWidget(map_eta);
last_position = coordinate_from_param("LastGPSPosition");
grabGesture(Qt::GestureType::PinchGesture);
qDebug() << "MapWindow initialized";
}
MapWindow::~MapWindow() {
makeCurrent();
}
void MapWindow::initLayers() {
// This doesn't work from initializeGL
if (!m_map->layerExists("modelPathLayer")) {
qDebug() << "Initializing modelPathLayer";
QVariantMap modelPath;
//modelPath["id"] = "modelPathLayer";
modelPath["type"] = "line";
modelPath["source"] = "modelPathSource";
m_map->addLayer("modelPathLayer", modelPath);
m_map->setPaintProperty("modelPathLayer", "line-color", QColor("red"));
m_map->setPaintProperty("modelPathLayer", "line-width", 5.0);
m_map->setLayoutProperty("modelPathLayer", "line-cap", "round");
}
if (!m_map->layerExists("navLayer")) {
qDebug() << "Initializing navLayer";
QVariantMap nav;
nav["type"] = "line";
nav["source"] = "navSource";
m_map->addLayer("navLayer", nav, "road-intersection");
QVariantMap transition;
transition["duration"] = 400; // ms
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(uiState()->scene.navigate_on_openpilot));
m_map->setPaintProperty("navLayer", "line-color-transition", transition);
m_map->setPaintProperty("navLayer", "line-width", 7.5);
m_map->setLayoutProperty("navLayer", "line-cap", "round");
}
if (!m_map->layerExists("pinLayer")) {
qDebug() << "Initializing pinLayer";
m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg"));
QVariantMap pin;
pin["type"] = "symbol";
pin["source"] = "pinSource";
m_map->addLayer("pinLayer", pin);
m_map->setLayoutProperty("pinLayer", "icon-pitch-alignment", "viewport");
m_map->setLayoutProperty("pinLayer", "icon-image", "default_marker");
m_map->setLayoutProperty("pinLayer", "icon-ignore-placement", true);
m_map->setLayoutProperty("pinLayer", "icon-allow-overlap", true);
m_map->setLayoutProperty("pinLayer", "symbol-sort-key", 0);
m_map->setLayoutProperty("pinLayer", "icon-anchor", "bottom");
}
if (!m_map->layerExists("carPosLayer")) {
qDebug() << "Initializing carPosLayer";
m_map->addImage("label-arrow", QImage("../assets/images/triangle.svg"));
QVariantMap carPos;
carPos["type"] = "symbol";
carPos["source"] = "carPosSource";
m_map->addLayer("carPosLayer", carPos);
m_map->setLayoutProperty("carPosLayer", "icon-pitch-alignment", "map");
m_map->setLayoutProperty("carPosLayer", "icon-image", "label-arrow");
m_map->setLayoutProperty("carPosLayer", "icon-size", 0.5);
m_map->setLayoutProperty("carPosLayer", "icon-ignore-placement", true);
m_map->setLayoutProperty("carPosLayer", "icon-allow-overlap", true);
// TODO: remove, symbol-sort-key does not seem to matter outside of each layer
m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0);
}
if (!m_map->layerExists("buildingsLayer")) {
qDebug() << "Initializing buildingsLayer";
QVariantMap buildings;
buildings["id"] = "buildingsLayer";
buildings["source"] = "composite";
buildings["source-layer"] = "building";
buildings["type"] = "fill-extrusion";
buildings["minzoom"] = 15;
m_map->addLayer("buildingsLayer", buildings);
m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"}));
QVariantList fillExtrusionHight = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.05, QVariantList{"get", "height"}
};
QVariantList fillExtrusionBase = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.05, QVariantList{"get", "min_height"}
};
QVariantList fillExtrusionOpacity = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.5, .6,
17, .6,
20, 0
};
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-color", QColor("grey"));
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-opacity", fillExtrusionOpacity);
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-height", fillExtrusionHight);
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-base", fillExtrusionBase);
m_map->setLayoutProperty("buildingsLayer", "visibility", "visible");
}
}
void MapWindow::updateState(const UIState &s) {
if (!uiState()->scene.started) {
return;
}
const SubMaster &sm = *(s.sm);
update();
if (sm.updated("modelV2")) {
// set path color on change, and show map on rising edge of navigate on openpilot
bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() &&
(sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral());
if (nav_enabled != uiState()->scene.navigate_on_openpilot) {
if (loaded_once) {
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled));
}
if (nav_enabled) {
emit requestVisible(true);
}
}
uiState()->scene.navigate_on_openpilot = nav_enabled;
}
if (sm.updated("liveLocationKalman")) {
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
auto locationd_pos = locationd_location.getPositionGeodetic();
auto locationd_orientation = locationd_location.getCalibratedOrientationNED();
auto locationd_velocity = locationd_location.getVelocityCalibrated();
// Check std norm
auto pos_ecef_std = locationd_location.getPositionECEF().getStd();
bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100;
locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough);
if (locationd_valid) {
last_position = QMapLibre::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]);
last_bearing = RAD2DEG(locationd_orientation.getValue()[2]);
velocity_filter.update(std::max(10.0, locationd_velocity.getValue()[0]));
}
}
// Credit to jakethesnake420
if (loaded_once && (sm.rcv_frame("uiPlan") != model_rcv_frame)) {
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
auto model_path = model_to_collection(locationd_location.getCalibratedOrientationECEF(), locationd_location.getPositionECEF(), sm["uiPlan"].getUiPlan().getPosition());
QMapLibre::Feature model_path_feature(QMapLibre::Feature::LineStringType, model_path, {}, {});
QVariantMap modelV2Path;
modelV2Path["type"] = "geojson";
modelV2Path["data"] = QVariant::fromValue<QMapLibre::Feature>(model_path_feature);
m_map->updateSource("modelPathSource", modelV2Path);
model_rcv_frame = sm.rcv_frame("uiPlan");
}
if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) {
auto nav_dest = coordinate_from_param("NavDestination");
bool allow_open = std::exchange(last_valid_nav_dest, nav_dest) != nav_dest &&
nav_dest && !isVisible();
qWarning() << "Got new navRoute from navd. Opening map:" << allow_open;
// Show map on destination set/change
if (allow_open) {
emit requestSettings(false);
emit requestVisible(true);
}
}
loaded_once = loaded_once || (m_map && m_map->isFullyLoaded());
if (!loaded_once) {
setError(tr("Map Loading"));
return;
}
initLayers();
if (!locationd_valid) {
setError(tr("Waiting for GPS"));
} else if (routing_problem) {
setError(tr("Waiting for route"));
} else {
setError("");
}
if (locationd_valid) {
// Update current location marker
auto point = coordinate_to_collection(*last_position);
QMapLibre::Feature feature1(QMapLibre::Feature::PointType, point, {}, {});
QVariantMap carPosSource;
carPosSource["type"] = "geojson";
carPosSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature1);
m_map->updateSource("carPosSource", carPosSource);
// Map bearing isn't updated when interacting, keep location marker up to date
if (last_bearing) {
m_map->setLayoutProperty("carPosLayer", "icon-rotate", *last_bearing - m_map->bearing());
}
}
if (interaction_counter == 0) {
if (last_position) m_map->setCoordinate(*last_position);
if (last_bearing) m_map->setBearing(*last_bearing);
m_map->setZoom(util::map_val<float>(velocity_filter.x(), 0, 30, MAX_ZOOM, MIN_ZOOM));
} else {
interaction_counter--;
}
if (sm.updated("navInstruction")) {
// an invalid navInstruction packet with a nav destination is only possible if:
// - API exception/no internet
// - route response is empty
// - any time navd is waiting for recompute_countdown
routing_problem = !sm.valid("navInstruction") && coordinate_from_param("NavDestination").has_value();
if (sm.valid("navInstruction")) {
auto i = sm["navInstruction"].getNavInstruction();
map_eta->updateETA(i.getTimeRemaining(), i.getTimeRemainingTypical(), i.getDistanceRemaining());
if (locationd_valid) {
m_map->setPitch(MAX_PITCH); // TODO: smooth pitching based on maneuver distance
map_instructions->updateInstructions(i);
}
} else {
clearRoute();
}
}
if (sm.rcv_frame("navRoute") != route_rcv_frame) {
qWarning() << "Updating navLayer with new route";
auto route = sm["navRoute"].getNavRoute();
auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates());
QMapLibre::Feature feature(QMapLibre::Feature::LineStringType, route_points, {}, {});
QVariantMap navSource;
navSource["type"] = "geojson";
navSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
m_map->updateSource("navSource", navSource);
m_map->setLayoutProperty("navLayer", "visibility", "visible");
route_rcv_frame = sm.rcv_frame("navRoute");
updateDestinationMarker();
}
// Map Styling - Credit goes to OPKR!
int map_style = uiState()->scene.map_style;
if (map_style != previous_map_style) {
std::unordered_map<int, std::string> styleUrls = {
{0, "mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj"}, // Stock openpilot
{1, "mapbox://styles/mapbox/streets-v11"}, // Mapbox Streets
{2, "mapbox://styles/mapbox/outdoors-v11"}, // Mapbox Outdoors
{3, "mapbox://styles/mapbox/light-v10"}, // Mapbox Light
{4, "mapbox://styles/mapbox/dark-v10"}, // Mapbox Dark
{5, "mapbox://styles/mapbox/satellite-v9"}, // Mapbox Satellite
{6, "mapbox://styles/mapbox/satellite-streets-v11"}, // Mapbox Satellite Streets
{7, "mapbox://styles/mapbox/navigation-day-v1"}, // Mapbox Navigation Day
{8, "mapbox://styles/mapbox/navigation-night-v1"}, // Mapbox Navigation Night
{9, "mapbox://styles/mapbox/traffic-night-v2"}, // Mapbox Traffic Night
{10, "mapbox://styles/mike854/clt0hm8mw01ok01p4blkr27jp"}, // mike854's (Satellite hybrid)
};
std::unordered_map<int, std::string>::iterator it = styleUrls.find(map_style);
m_map->setStyleUrl(QString::fromStdString(it->second));
}
previous_map_style = map_style;
}
void MapWindow::setError(const QString &err_str) {
if (err_str != error->text()) {
error->setText(err_str);
error->setVisible(!err_str.isEmpty());
if (!err_str.isEmpty()) map_instructions->setVisible(false);
}
}
void MapWindow::resizeGL(int w, int h) {
m_map->resize(size() / MAP_SCALE);
map_overlay->setFixedSize(width(), height());
}
void MapWindow::initializeGL() {
m_map.reset(new QMapLibre::Map(this, m_settings, size(), 1));
if (last_position) {
m_map->setCoordinateZoom(*last_position, MAX_ZOOM);
} else {
m_map->setCoordinateZoom(QMapLibre::Coordinate(64.31990695292795, -149.79038934046247), MIN_ZOOM);
}
m_map->setMargins({0, 350, 0, 50});
m_map->setPitch(MIN_PITCH);
m_map->setStyleUrl("mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj");
QObject::connect(m_map.data(), &QMapLibre::Map::mapChanged, [=](QMapLibre::Map::MapChange change) {
// set global animation duration to 0 ms so visibility changes are instant
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingStyle) {
m_map->setTransitionOptions(0, 0);
}
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingMap) {
loaded_once = true;
}
});
}
void MapWindow::paintGL() {
if (!isVisible() || m_map.isNull()) return;
m_map->render();
}
void MapWindow::clearRoute() {
if (!m_map.isNull()) {
m_map->setLayoutProperty("navLayer", "visibility", "none");
m_map->setPitch(MIN_PITCH);
updateDestinationMarker();
}
map_instructions->setVisible(false);
map_eta->setVisible(false);
last_valid_nav_dest = std::nullopt;
}
void MapWindow::mousePressEvent(QMouseEvent *ev) {
m_lastPos = ev->localPos();
ev->accept();
}
void MapWindow::mouseDoubleClickEvent(QMouseEvent *ev) {
if (last_position) m_map->setCoordinate(*last_position);
if (last_bearing) m_map->setBearing(*last_bearing);
m_map->setZoom(util::map_val<float>(velocity_filter.x(), 0, 30, MAX_ZOOM, MIN_ZOOM));
update();
interaction_counter = 0;
}
void MapWindow::mouseMoveEvent(QMouseEvent *ev) {
QPointF delta = ev->localPos() - m_lastPos;
if (!delta.isNull()) {
interaction_counter = INTERACTION_TIMEOUT;
m_map->moveBy(delta / MAP_SCALE);
update();
}
m_lastPos = ev->localPos();
ev->accept();
}
void MapWindow::wheelEvent(QWheelEvent *ev) {
if (ev->orientation() == Qt::Horizontal) {
return;
}
float factor = ev->delta() / 1200.;
if (ev->delta() < 0) {
factor = factor > -1 ? factor : 1 / factor;
}
m_map->scaleBy(1 + factor, ev->pos() / MAP_SCALE);
update();
interaction_counter = INTERACTION_TIMEOUT;
ev->accept();
}
bool MapWindow::event(QEvent *event) {
if (event->type() == QEvent::Gesture) {
return gestureEvent(static_cast<QGestureEvent*>(event));
}
return QWidget::event(event);
}
bool MapWindow::gestureEvent(QGestureEvent *event) {
if (QGesture *pinch = event->gesture(Qt::PinchGesture)) {
pinchTriggered(static_cast<QPinchGesture *>(pinch));
}
return true;
}
void MapWindow::pinchTriggered(QPinchGesture *gesture) {
QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
if (changeFlags & QPinchGesture::ScaleFactorChanged) {
// TODO: figure out why gesture centerPoint doesn't work
m_map->scaleBy(gesture->scaleFactor(), {width() / 2.0 / MAP_SCALE, height() / 2.0 / MAP_SCALE});
update();
interaction_counter = INTERACTION_TIMEOUT;
}
}
void MapWindow::offroadTransition(bool offroad) {
if (offroad) {
clearRoute();
uiState()->scene.navigate_on_openpilot = false;
routing_problem = false;
} else {
auto dest = coordinate_from_param("NavDestination");
emit requestVisible(dest.has_value());
}
last_bearing = {};
}
void MapWindow::updateDestinationMarker() {
auto nav_dest = coordinate_from_param("NavDestination");
if (nav_dest.has_value()) {
auto point = coordinate_to_collection(*nav_dest);
QMapLibre::Feature feature(QMapLibre::Feature::PointType, point, {}, {});
QVariantMap pinSource;
pinSource["type"] = "geojson";
pinSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
m_map->updateSource("pinSource", pinSource);
m_map->setPaintProperty("pinLayer", "visibility", "visible");
} else {
m_map->setPaintProperty("pinLayer", "visibility", "none");
}
}

View File

@@ -1,97 +0,0 @@
#pragma once
#include <optional>
#include <QGeoCoordinate>
#include <QGestureEvent>
#include <QLabel>
#include <QMap>
#include <QMapLibre/Map>
#include <QMapLibre/Settings>
#include <QMouseEvent>
#include <QOpenGLWidget>
#include <QPixmap>
#include <QPushButton>
#include <QScopedPointer>
#include <QString>
#include <QVBoxLayout>
#include <QWheelEvent>
#include "cereal/messaging/messaging.h"
#include "common/params.h"
#include "common/util.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/maps/map_eta.h"
#include "selfdrive/ui/qt/maps/map_instructions.h"
class MapWindow : public QOpenGLWidget {
Q_OBJECT
public:
MapWindow(const QMapLibre::Settings &);
~MapWindow();
private:
void initializeGL() final;
void paintGL() final;
void resizeGL(int w, int h) override;
QMapLibre::Settings m_settings;
QScopedPointer<QMapLibre::Map> m_map;
void initLayers();
void mousePressEvent(QMouseEvent *ev) final;
void mouseDoubleClickEvent(QMouseEvent *ev) final;
void mouseMoveEvent(QMouseEvent *ev) final;
void wheelEvent(QWheelEvent *ev) final;
bool event(QEvent *event) final;
bool gestureEvent(QGestureEvent *event);
void pinchTriggered(QPinchGesture *gesture);
void setError(const QString &err_str);
bool loaded_once = false;
// Panning
QPointF m_lastPos;
int interaction_counter = 0;
// Position
std::optional<QMapLibre::Coordinate> last_valid_nav_dest;
std::optional<QMapLibre::Coordinate> last_position;
std::optional<float> last_bearing;
FirstOrderFilter velocity_filter;
bool locationd_valid = false;
bool routing_problem = false;
QWidget *map_overlay;
QLabel *error;
MapInstructions* map_instructions;
MapETA* map_eta;
// Blue with normal nav, green when nav is input into the model
QColor getNavPathColor(bool nav_enabled) {
return nav_enabled ? QColor("#31ee73") : QColor("#31a1ee");
}
void clearRoute();
void updateDestinationMarker();
uint64_t route_rcv_frame = 0;
// FrogPilot variables
Params params;
int previous_map_style;
uint64_t model_rcv_frame = 0;
private slots:
void updateState(const UIState &s);
public slots:
void offroadTransition(bool offroad);
signals:
void requestVisible(bool visible);
void requestSettings(bool settings);
};

View File

@@ -1,56 +0,0 @@
#include "selfdrive/ui/qt/maps/map_eta.h"
#include <QDateTime>
#include <QPainter>
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include "selfdrive/ui/ui.h"
const float MANEUVER_TRANSITION_THRESHOLD = 10;
MapETA::MapETA(QWidget *parent) : QWidget(parent) {
setVisible(false);
setAttribute(Qt::WA_TranslucentBackground);
eta_doc.setUndoRedoEnabled(false);
eta_doc.setDefaultStyleSheet("body {font-family:Inter;font-size:70px;color:white;} b{font-weight:600;} td{padding:0 3px;}");
}
void MapETA::paintEvent(QPaintEvent *event) {
if (!eta_doc.isEmpty()) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setBrush(QColor(0, 0, 0, 255));
QSizeF txt_size = eta_doc.size();
p.drawRoundedRect((width() - txt_size.width()) / 2 - UI_BORDER_SIZE, 0, txt_size.width() + UI_BORDER_SIZE * 2, height() + 25, 25, 25);
p.translate((width() - txt_size.width()) / 2, (height() - txt_size.height()) / 2);
eta_doc.drawContents(&p);
}
}
void MapETA::updateETA(float s, float s_typical, float d) {
// ETA
auto eta_t = QDateTime::currentDateTime().addSecs(s).time();
auto eta = format_24h ? std::pair{eta_t.toString("HH:mm"), tr("eta")}
: std::pair{eta_t.toString("h:mm a").split(' ')[0], eta_t.toString("a")};
// Remaining time
auto remaining = s < 3600 ? std::pair{QString::number(int(s / 60)), tr("min")}
: std::pair{QString("%1:%2").arg((int)s / 3600).arg(((int)s % 3600) / 60, 2, 10, QLatin1Char('0')), tr("hr")};
QString color = "#25DA6E";
if (s / s_typical > 1.5)
color = "#DA3025";
else if (s / s_typical > 1.2)
color = "#DAA725";
// Distance
auto distance = map_format_distance(d, uiState()->scene.is_metric);
eta_doc.setHtml(QString(R"(<body><table><tr style="vertical-align:bottom;"><td><b>%1</b></td><td>%2</td>
<td style="padding-left:40px;color:%3;"><b>%4</b></td><td style="padding-right:40px;color:%3;">%5</td>
<td><b>%6</b></td><td>%7</td></tr></body>)")
.arg(eta.first, eta.second, color, remaining.first, remaining.second, distance.first, distance.second));
setVisible(d >= MANEUVER_TRANSITION_THRESHOLD);
update();
}

View File

@@ -1,23 +0,0 @@
#pragma once
#include <QPaintEvent>
#include <QTextDocument>
#include <QWidget>
#include "common/params.h"
class MapETA : public QWidget {
Q_OBJECT
public:
MapETA(QWidget * parent=nullptr);
void updateETA(float seconds, float seconds_typical, float distance);
private:
void paintEvent(QPaintEvent *event) override;
void showEvent(QShowEvent *event) override { format_24h = param.getBool("NavSettingTime24h"); }
bool format_24h = false;
QTextDocument eta_doc;
Params param;
};

View File

@@ -1,153 +0,0 @@
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include <algorithm>
#include <string>
#include <utility>
#include <QJsonDocument>
#include <QJsonObject>
#include "common/params.h"
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/api.h"
QString get_mapbox_token() {
// Valid for 4 weeks since we can't swap tokens on the fly
return MAPBOX_TOKEN.isEmpty() ? CommaApi::create_jwt({}, 4 * 7 * 24 * 3600) : MAPBOX_TOKEN;
}
QMapLibre::Settings get_mapbox_settings() {
QMapLibre::Settings settings;
settings.setProviderTemplate(QMapLibre::Settings::ProviderTemplate::MapboxProvider);
if (!Hardware::PC()) {
settings.setCacheDatabasePath(MAPS_CACHE_PATH);
settings.setCacheDatabaseMaximumSize(100 * 1024 * 1024);
}
settings.setApiBaseUrl(MAPS_HOST);
settings.setApiKey(get_mapbox_token());
return settings;
}
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in) {
return QGeoCoordinate(in.first, in.second);
}
QMapLibre::CoordinatesCollections model_to_collection(
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
const cereal::XYZTData::Reader &line){
Eigen::Vector3d ecef(positionECEF.getValue()[0], positionECEF.getValue()[1], positionECEF.getValue()[2]);
Eigen::Vector3d orient(calibratedOrientationECEF.getValue()[0], calibratedOrientationECEF.getValue()[1], calibratedOrientationECEF.getValue()[2]);
Eigen::Matrix3d ecef_from_local = euler2rot(orient);
QMapLibre::Coordinates coordinates;
auto x = line.getX();
auto y = line.getY();
auto z = line.getZ();
for (int i = 0; i < x.size(); i++) {
Eigen::Vector3d point_ecef = ecef_from_local * Eigen::Vector3d(x[i], y[i], z[i]) + ecef;
Geodetic point_geodetic = ecef2geodetic((ECEF){.x = point_ecef[0], .y = point_ecef[1], .z = point_ecef[2]});
coordinates.push_back({point_geodetic.lat, point_geodetic.lon});
}
return {QMapLibre::CoordinatesCollection{coordinates}};
}
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c) {
QMapLibre::Coordinates coordinates{c};
return {QMapLibre::CoordinatesCollection{coordinates}};
}
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) {
QMapLibre::Coordinates coordinates;
for (auto const &c : coordinate_list) {
coordinates.push_back({c.getLatitude(), c.getLongitude()});
}
return {QMapLibre::CoordinatesCollection{coordinates}};
}
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list) {
QMapLibre::Coordinates coordinates;
for (auto &c : coordinate_list) {
coordinates.push_back({c.latitude(), c.longitude()});
}
return {QMapLibre::CoordinatesCollection{coordinates}};
}
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString) {
QList<QGeoCoordinate> path;
if (polylineString.isEmpty())
return path;
QByteArray data = polylineString.toLatin1();
bool parsingLatitude = true;
int shift = 0;
int value = 0;
QGeoCoordinate coord(0, 0);
for (int i = 0; i < data.length(); ++i) {
unsigned char c = data.at(i) - 63;
value |= (c & 0x1f) << shift;
shift += 5;
// another chunk
if (c & 0x20)
continue;
int diff = (value & 1) ? ~(value >> 1) : (value >> 1);
if (parsingLatitude) {
coord.setLatitude(coord.latitude() + (double)diff/1e6);
} else {
coord.setLongitude(coord.longitude() + (double)diff/1e6);
path.append(coord);
}
parsingLatitude = !parsingLatitude;
value = 0;
shift = 0;
}
return path;
}
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string &param) {
QString json_str = QString::fromStdString(Params().get(param));
if (json_str.isEmpty()) return {};
QJsonDocument doc = QJsonDocument::fromJson(json_str.toUtf8());
if (doc.isNull()) return {};
QJsonObject json = doc.object();
if (json["latitude"].isDouble() && json["longitude"].isDouble()) {
QMapLibre::Coordinate coord(json["latitude"].toDouble(), json["longitude"].toDouble());
return coord;
} else {
return {};
}
}
// return {distance, unit}
std::pair<QString, QString> map_format_distance(float d, bool is_metric) {
auto round_distance = [](float d) -> float {
return (d > 10) ? std::nearbyint(d) : std::nearbyint(d * 10) / 10.0;
};
d = std::max(d, 0.0f);
if (is_metric) {
return (d > 500) ? std::pair{QString::number(round_distance(d / 1000)), QObject::tr("km")}
: std::pair{QString::number(50 * std::nearbyint(d / 50)), QObject::tr("m")};
} else {
float feet = d * METER_TO_FOOT;
return (feet > 500) ? std::pair{QString::number(round_distance(d * METER_TO_MILE)), QObject::tr("mi")}
: std::pair{QString::number(50 * std::nearbyint(d / 50)), QObject::tr("ft")};
}
}

View File

@@ -1,33 +0,0 @@
#pragma once
#include <optional>
#include <string>
#include <utility>
#include <QMapLibre/Map>
#include <QMapLibre/Settings>
#include <eigen3/Eigen/Dense>
#include <QGeoCoordinate>
#include "common/params.h"
#include "common/util.h"
#include "common/transformations/coordinates.hpp"
#include "common/transformations/orientation.hpp"
#include "cereal/messaging/messaging.h"
const QString MAPBOX_TOKEN = QString::fromStdString(Params().get("MapboxPublicKey"));
const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "https://maps.comma.ai" : "https://api.mapbox.com").c_str();
const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db";
QString get_mapbox_token();
QMapLibre::Settings get_mapbox_settings();
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in);
QMapLibre::CoordinatesCollections model_to_collection(
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
const cereal::XYZTData::Reader &line);
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c);
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader &coordinate_list);
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list);
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString);
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string &param);
std::pair<QString, QString> map_format_distance(float d, bool is_metric);

View File

@@ -1,144 +0,0 @@
#include "selfdrive/ui/qt/maps/map_instructions.h"
#include <QDir>
#include <QVBoxLayout>
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include "selfdrive/ui/ui.h"
const QString ICON_SUFFIX = ".png";
MapInstructions::MapInstructions(QWidget *parent) : QWidget(parent) {
is_rhd = Params().getBool("IsRhdDetected");
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(11, UI_BORDER_SIZE, 11, 20);
QHBoxLayout *top_layout = new QHBoxLayout;
top_layout->addWidget(icon_01 = new QLabel, 0, Qt::AlignTop);
QVBoxLayout *right_layout = new QVBoxLayout;
right_layout->setContentsMargins(9, 9, 9, 0);
right_layout->addWidget(distance = new QLabel);
distance->setStyleSheet(R"(font-size: 90px;)");
right_layout->addWidget(primary = new QLabel);
primary->setStyleSheet(R"(font-size: 60px;)");
primary->setWordWrap(true);
right_layout->addWidget(secondary = new QLabel);
secondary->setStyleSheet(R"(font-size: 50px;)");
secondary->setWordWrap(true);
top_layout->addLayout(right_layout);
main_layout->addLayout(top_layout);
main_layout->addLayout(lane_layout = new QHBoxLayout);
lane_layout->setAlignment(Qt::AlignHCenter);
lane_layout->setSpacing(10);
setStyleSheet("color:white");
QPalette pal = palette();
pal.setColor(QPalette::Background, QColor(0, 0, 0, 150));
setAutoFillBackground(true);
setPalette(pal);
buildPixmapCache();
}
void MapInstructions::buildPixmapCache() {
QDir dir("../assets/navigation");
for (QString fn : dir.entryList({"*" + ICON_SUFFIX}, QDir::Files)) {
QPixmap pm(dir.filePath(fn));
QString key = fn.left(fn.size() - ICON_SUFFIX.length());
pm = pm.scaledToWidth(200, Qt::SmoothTransformation);
// Maneuver icons
pixmap_cache[key] = pm;
// lane direction icons
if (key.contains("turn_")) {
pixmap_cache["lane_" + key] = pm.scaled({125, 125}, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
// for rhd, reflect direction and then flip
if (key.contains("_left")) {
pixmap_cache["rhd_" + key.replace("_left", "_right")] = pm.transformed(QTransform().scale(-1, 1));
} else if (key.contains("_right")) {
pixmap_cache["rhd_" + key.replace("_right", "_left")] = pm.transformed(QTransform().scale(-1, 1));
}
}
}
void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruction) {
setUpdatesEnabled(false);
// Show instruction text
QString primary_str = QString::fromStdString(instruction.getManeuverPrimaryText());
QString secondary_str = QString::fromStdString(instruction.getManeuverSecondaryText());
primary->setText(primary_str);
secondary->setVisible(secondary_str.length() > 0);
secondary->setText(secondary_str);
auto distance_str_pair = map_format_distance(instruction.getManeuverDistance(), uiState()->scene.is_metric);
distance->setText(QString("%1 %2").arg(distance_str_pair.first, distance_str_pair.second));
// Show arrow with direction
QString type = QString::fromStdString(instruction.getManeuverType());
QString modifier = QString::fromStdString(instruction.getManeuverModifier());
if (!type.isEmpty()) {
QString fn = "direction_" + type;
if (!modifier.isEmpty()) {
fn += "_" + modifier;
}
fn = fn.replace(' ', '_');
bool rhd = is_rhd && (fn.contains("_left") || fn.contains("_right"));
icon_01->setPixmap(pixmap_cache[!rhd ? fn : "rhd_" + fn]);
icon_01->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
icon_01->setVisible(true);
} else {
icon_01->setVisible(false);
}
// Hide distance after arrival
distance->setVisible(type != "arrive" || instruction.getManeuverDistance() > 0);
// Show lanes
auto lanes = instruction.getLanes();
for (int i = 0; i < lanes.size(); ++i) {
bool active = lanes[i].getActive();
const auto active_direction = lanes[i].getActiveDirection();
// TODO: Make more images based on active direction and combined directions
QString fn = "lane_direction_";
// active direction has precedence
if (active && active_direction != cereal::NavInstruction::Direction::NONE) {
fn += "turn_" + DIRECTIONS[active_direction];
} else {
for (auto const &direction : lanes[i].getDirections()) {
if (direction != cereal::NavInstruction::Direction::NONE) {
fn += "turn_" + DIRECTIONS[direction];
break;
}
}
}
if (!active) {
fn += "_inactive";
}
QLabel *label = (i < lane_labels.size()) ? lane_labels[i] : lane_labels.emplace_back(new QLabel);
if (!label->parentWidget()) {
lane_layout->addWidget(label);
}
label->setPixmap(pixmap_cache[fn]);
label->setVisible(true);
}
for (int i = lanes.size(); i < lane_labels.size(); ++i) {
lane_labels[i]->setVisible(false);
}
setUpdatesEnabled(true);
setVisible(true);
}

View File

@@ -1,38 +0,0 @@
#pragma once
#include <map>
#include <vector>
#include <QHash>
#include <QHBoxLayout>
#include <QLabel>
#include "cereal/gen/cpp/log.capnp.h"
static std::map<cereal::NavInstruction::Direction, QString> DIRECTIONS = {
{cereal::NavInstruction::Direction::NONE, "none"},
{cereal::NavInstruction::Direction::LEFT, "left"},
{cereal::NavInstruction::Direction::RIGHT, "right"},
{cereal::NavInstruction::Direction::STRAIGHT, "straight"},
{cereal::NavInstruction::Direction::SLIGHT_LEFT, "slight_left"},
{cereal::NavInstruction::Direction::SLIGHT_RIGHT, "slight_right"},
};
class MapInstructions : public QWidget {
Q_OBJECT
private:
QLabel *distance;
QLabel *primary;
QLabel *secondary;
QLabel *icon_01;
QHBoxLayout *lane_layout;
bool is_rhd = false;
std::vector<QLabel *> lane_labels;
QHash<QString, QPixmap> pixmap_cache;
public:
MapInstructions(QWidget * parent=nullptr);
void buildPixmapCache();
void updateInstructions(cereal::NavInstruction::Reader instruction);
};

View File

@@ -1,43 +0,0 @@
#include "selfdrive/ui/qt/maps/map_panel.h"
#include <QHBoxLayout>
#include <QWidget>
#include "selfdrive/ui/qt/maps/map.h"
#include "selfdrive/ui/qt/maps/map_settings.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/ui.h"
MapPanel::MapPanel(const QMapLibre::Settings &mapboxSettings, QWidget *parent) : QFrame(parent) {
content_stack = new QStackedLayout(this);
content_stack->setContentsMargins(0, 0, 0, 0);
auto map = new MapWindow(mapboxSettings);
QObject::connect(uiState(), &UIState::offroadTransition, map, &MapWindow::offroadTransition);
QObject::connect(device(), &Device::interactiveTimeout, this, [=]() {
content_stack->setCurrentIndex(0);
});
QObject::connect(map, &MapWindow::requestVisible, this, [=](bool visible) {
// when we show the map for a new route, signal HomeWindow to hide the sidebar
if (visible) { emit mapPanelRequested(); }
setVisible(visible);
});
QObject::connect(map, &MapWindow::requestSettings, this, [=](bool settings) {
content_stack->setCurrentIndex(settings ? 1 : 0);
});
content_stack->addWidget(map);
auto settings = new MapSettings(true, parent);
QObject::connect(settings, &MapSettings::closeSettings, this, [=]() {
content_stack->setCurrentIndex(0);
});
content_stack->addWidget(settings);
}
void MapPanel::toggleMapSettings() {
// show settings if not visible, then toggle between map and settings
int new_index = isVisible() ? (1 - content_stack->currentIndex()) : 1;
content_stack->setCurrentIndex(new_index);
emit mapPanelRequested();
show();
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include <QFrame>
#include <QMapLibre/Settings>
#include <QStackedLayout>
class MapPanel : public QFrame {
Q_OBJECT
public:
explicit MapPanel(const QMapLibre::Settings &settings, QWidget *parent = nullptr);
signals:
void mapPanelRequested();
public slots:
void toggleMapSettings();
private:
QStackedLayout *content_stack;
};

View File

@@ -1,393 +0,0 @@
#include "selfdrive/ui/qt/maps/map_settings.h"
#include <utility>
#include <QApplication>
#include <QDebug>
#include "common/util.h"
#include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
static void swap(QJsonValueRef v1, QJsonValueRef v2) { std::swap(v1, v2); }
static bool locationEqual(const QJsonValue &v1, const QJsonValue &v2) {
return v1["latitude"] == v2["latitude"] && v1["longitude"] == v2["longitude"];
}
static qint64 convertTimestampToEpoch(const QString &timestamp) {
QDateTime dt = QDateTime::fromString(timestamp, Qt::ISODate);
return dt.isValid() ? dt.toSecsSinceEpoch() : 0;
}
MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
setContentsMargins(0, 0, 0, 0);
setAttribute(Qt::WA_NoMousePropagation);
auto *frame = new QVBoxLayout(this);
frame->setContentsMargins(40, 40, 40, 0);
frame->setSpacing(0);
auto *heading_frame = new QHBoxLayout;
heading_frame->setContentsMargins(0, 0, 0, 0);
heading_frame->setSpacing(32);
{
if (closeable) {
auto *close_btn = new QPushButton("");
close_btn->setStyleSheet(R"(
QPushButton {
color: #FFFFFF;
font-size: 100px;
padding-bottom: 8px;
border 1px grey solid;
border-radius: 70px;
background-color: #292929;
font-weight: 500;
}
QPushButton:pressed {
background-color: #3B3B3B;
}
)");
close_btn->setFixedSize(140, 140);
QObject::connect(close_btn, &QPushButton::clicked, [=]() { emit closeSettings(); });
// TODO: read map_on_left from ui state
heading_frame->addWidget(close_btn);
}
auto *heading = new QVBoxLayout;
heading->setContentsMargins(0, 0, 0, 0);
heading->setSpacing(16);
{
auto *title = new QLabel(tr("NAVIGATION"), this);
title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;");
heading->addWidget(title);
subtitle = new QLabel(tr("Manage at connect.comma.ai"), this);
subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;");
heading->addWidget(subtitle);
}
heading_frame->addLayout(heading, 1);
}
frame->addLayout(heading_frame);
frame->addSpacing(32);
current_widget = new DestinationWidget(this);
QObject::connect(current_widget, &DestinationWidget::actionClicked,
[]() { NavManager::instance()->setCurrentDestination({}); });
frame->addWidget(current_widget);
frame->addSpacing(32);
QWidget *destinations_container = new QWidget(this);
destinations_layout = new QVBoxLayout(destinations_container);
destinations_layout->setContentsMargins(0, 32, 0, 32);
destinations_layout->setSpacing(20);
destinations_layout->addWidget(home_widget = new DestinationWidget(this));
destinations_layout->addWidget(work_widget = new DestinationWidget(this));
QObject::connect(home_widget, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
QObject::connect(work_widget, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
destinations_layout->addStretch();
ScrollView *destinations_scroller = new ScrollView(destinations_container, this);
destinations_scroller->setFrameShape(QFrame::NoFrame);
frame->addWidget(destinations_scroller);
setStyleSheet("MapSettings { background-color: #333333; }");
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
wifi = new WifiManager(this);
}
void MapSettings::showEvent(QShowEvent *event) {
refresh();
}
void MapSettings::refresh() {
if (!isVisible()) return;
setUpdatesEnabled(false);
auto get_w = [this](int i) {
auto w = i < widgets.size() ? widgets[i] : widgets.emplace_back(new DestinationWidget);
if (!w->parentWidget()) {
destinations_layout->insertWidget(destinations_layout->count() - 1, w);
QObject::connect(w, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
}
return w;
};
const auto current_dest = NavManager::instance()->currentDestination();
if (!current_dest.isEmpty()) {
current_widget->set(current_dest, true);
} else {
current_widget->unset("", true);
}
home_widget->unset(NAV_FAVORITE_LABEL_HOME);
work_widget->unset(NAV_FAVORITE_LABEL_WORK);
int n = 0;
for (auto location : NavManager::instance()->currentLocations()) {
DestinationWidget *w = nullptr;
auto dest = location.toObject();
if (dest["save_type"].toString() == NAV_TYPE_FAVORITE) {
auto label = dest["label"].toString();
if (label == NAV_FAVORITE_LABEL_HOME) w = home_widget;
if (label == NAV_FAVORITE_LABEL_WORK) w = work_widget;
}
w = w ? w : get_w(n++);
w->set(dest, false);
w->setVisible(!locationEqual(dest, current_dest));
}
for (; n < widgets.size(); ++n) widgets[n]->setVisible(false);
setUpdatesEnabled(true);
// Use IP for NOO without Prime
if (!uiState()->hasPrime()) {
QString ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
subtitle->setText(tr("Manage at %1").arg(ipAddress));
}
}
void MapSettings::navigateTo(const QJsonObject &place) {
NavManager::instance()->setCurrentDestination(place);
emit closeSettings();
}
DestinationWidget::DestinationWidget(QWidget *parent) : QPushButton(parent) {
setContentsMargins(0, 0, 0, 0);
auto *frame = new QHBoxLayout(this);
frame->setContentsMargins(32, 24, 32, 24);
frame->setSpacing(32);
icon = new QLabel(this);
icon->setAlignment(Qt::AlignCenter);
icon->setFixedSize(96, 96);
icon->setObjectName("icon");
frame->addWidget(icon);
auto *inner_frame = new QVBoxLayout;
inner_frame->setContentsMargins(0, 0, 0, 0);
inner_frame->setSpacing(0);
{
title = new ElidedLabel(this);
title->setAttribute(Qt::WA_TransparentForMouseEvents);
inner_frame->addWidget(title);
subtitle = new ElidedLabel(this);
subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
subtitle->setObjectName("subtitle");
inner_frame->addWidget(subtitle);
}
frame->addLayout(inner_frame, 1);
action = new QPushButton(this);
action->setFixedSize(96, 96);
action->setObjectName("action");
action->setStyleSheet("font-size: 65px; font-weight: 600;");
QObject::connect(action, &QPushButton::clicked, this, &QPushButton::clicked);
QObject::connect(action, &QPushButton::clicked, this, &DestinationWidget::actionClicked);
frame->addWidget(action);
setFixedHeight(164);
setStyleSheet(R"(
DestinationWidget { background-color: #202123; border-radius: 10px; }
QLabel { color: #FFFFFF; font-size: 48px; font-weight: 400; }
#icon { background-color: #3B4356; border-radius: 48px; }
#subtitle { color: #9BA0A5; }
#action { border: none; border-radius: 48px; color: #FFFFFF; padding-bottom: 4px; }
/* current destination */
[current="true"] { background-color: #E8E8E8; }
[current="true"] QLabel { color: #000000; }
[current="true"] #icon { background-color: #42906B; }
[current="true"] #subtitle { color: #333333; }
[current="true"] #action { color: #202123; }
/* no saved destination */
[set="false"] QLabel { color: #9BA0A5; }
[current="true"][set="false"] QLabel { color: #A0000000; }
/* pressed */
[current="false"]:pressed { background-color: #18191B; }
[current="true"] #action:pressed { background-color: #D6D6D6; }
)");
QObject::connect(this, &QPushButton::clicked, [this]() { if (!dest.isEmpty()) emit navigateTo(dest); });
}
void DestinationWidget::set(const QJsonObject &destination, bool current) {
if (dest == destination) return;
dest = destination;
setProperty("current", current);
setProperty("set", true);
auto icon_pixmap = current ? icons().directions : icons().recent;
auto title_text = destination["place_name"].toString();
auto subtitle_text = destination["place_details"].toString();
if (destination["save_type"] == NAV_TYPE_FAVORITE) {
if (destination["label"] == NAV_FAVORITE_LABEL_HOME) {
icon_pixmap = icons().home;
subtitle_text = title_text + ", " + subtitle_text;
title_text = tr("Home");
} else if (destination["label"] == NAV_FAVORITE_LABEL_WORK) {
icon_pixmap = icons().work;
subtitle_text = title_text + ", " + subtitle_text;
title_text = tr("Work");
} else {
icon_pixmap = icons().favorite;
}
}
icon->setPixmap(icon_pixmap);
title->setText(title_text);
subtitle->setText(subtitle_text);
subtitle->setVisible(true);
// TODO: use pixmap
action->setAttribute(Qt::WA_TransparentForMouseEvents, !current);
action->setText(current ? "×" : "");
action->setVisible(true);
setStyleSheet(styleSheet());
}
void DestinationWidget::unset(const QString &label, bool current) {
dest = {};
setProperty("current", current);
setProperty("set", false);
if (label.isEmpty()) {
icon->setPixmap(icons().directions);
title->setText(tr("No destination set"));
} else {
QString title_text = label == NAV_FAVORITE_LABEL_HOME ? tr("home") : tr("work");
icon->setPixmap(label == NAV_FAVORITE_LABEL_HOME ? icons().home : icons().work);
title->setText(tr("No %1 location set").arg(title_text));
}
subtitle->setVisible(false);
action->setVisible(false);
setStyleSheet(styleSheet());
setVisible(true);
}
// singleton NavManager
NavManager *NavManager::instance() {
static NavManager *request = new NavManager(qApp);
return request;
}
NavManager::NavManager(QObject *parent) : QObject(parent) {
locations = QJsonDocument::fromJson(params.get("NavPastDestinations").c_str()).array();
current_dest = QJsonDocument::fromJson(params.get("NavDestination").c_str()).object();
if (auto dongle_id = getDongleId()) {
{
// Fetch favorite and recent locations
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/locations";
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_NavDestinations", 30, true);
QObject::connect(repeater, &RequestRepeater::requestDone, this, &NavManager::parseLocationsResponse);
}
{
auto param_watcher = new ParamWatcher(this);
QObject::connect(param_watcher, &ParamWatcher::paramChanged, this, &NavManager::updated);
// Destination set while offline
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/next";
HttpRequest *deleter = new HttpRequest(this);
RequestRepeater *repeater = new RequestRepeater(this, url, "", 10, true);
QObject::connect(repeater, &RequestRepeater::requestDone, [=](const QString &resp, bool success) {
if (success && resp != "null") {
if (params.get("NavDestination").empty()) {
qWarning() << "Setting NavDestination from /next" << resp;
params.put("NavDestination", resp.toStdString());
} else {
qWarning() << "Got location from /next, but NavDestination already set";
}
// Send DELETE to clear destination server side
deleter->sendRequest(url, HttpRequest::Method::DELETE);
}
// athena can set destination at any time
param_watcher->addParam("NavDestination");
current_dest = QJsonDocument::fromJson(params.get("NavDestination").c_str()).object();
emit updated();
});
}
}
}
void NavManager::parseLocationsResponse(const QString &response, bool success) {
if (!success || response == prev_response) return;
prev_response = response;
QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8());
if (doc.isNull()) {
qWarning() << "JSON Parse failed on navigation locations" << response;
return;
}
// set last activity time.
auto remote_locations = doc.array();
for (QJsonValueRef loc : remote_locations) {
auto obj = loc.toObject();
auto serverTime = convertTimestampToEpoch(obj["modified"].toString());
obj.insert("time", qMax(serverTime, getLastActivity(obj)));
loc = obj;
}
locations = remote_locations;
sortLocations();
emit updated();
}
void NavManager::sortLocations() {
// Sort: alphabetical FAVORITES, and then most recent.
// We don't need to care about the ordering of HOME and WORK. DestinationWidget always displays them at the top.
std::stable_sort(locations.begin(), locations.end(), [](const QJsonValue &a, const QJsonValue &b) {
if (a["save_type"] == NAV_TYPE_FAVORITE || b["save_type"] == NAV_TYPE_FAVORITE) {
return (std::tuple(a["save_type"].toString(), a["place_name"].toString()) <
std::tuple(b["save_type"].toString(), b["place_name"].toString()));
} else {
return a["time"].toVariant().toLongLong() > b["time"].toVariant().toLongLong();
}
});
write_param_future = std::async(std::launch::async, [destinations = QJsonArray(locations)]() {
Params().put("NavPastDestinations", QJsonDocument(destinations).toJson().toStdString());
});
}
qint64 NavManager::getLastActivity(const QJsonObject &loc) const {
qint64 last_activity = 0;
auto it = std::find_if(locations.begin(), locations.end(),
[&loc](const QJsonValue &l) { return locationEqual(loc, l); });
if (it != locations.end()) {
auto tm = it->toObject().value("time");
if (!tm.isUndefined() && !tm.isNull()) {
last_activity = tm.toVariant().toLongLong();
}
}
return last_activity;
}
void NavManager::setCurrentDestination(const QJsonObject &loc) {
current_dest = loc;
if (!current_dest.isEmpty()) {
current_dest["time"] = QDateTime::currentSecsSinceEpoch();
auto it = std::find_if(locations.begin(), locations.end(),
[&loc](const QJsonValue &l) { return locationEqual(loc, l); });
if (it != locations.end()) {
*it = current_dest;
sortLocations();
}
params.put("NavDestination", QJsonDocument(current_dest).toJson().toStdString());
} else {
params.remove("NavDestination");
}
emit updated();
}

View File

@@ -1,108 +0,0 @@
#pragma once
#include <future>
#include <vector>
#include <QFrame>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include "common/params.h"
#include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
const QString NAV_TYPE_FAVORITE = "favorite";
const QString NAV_TYPE_RECENT = "recent";
const QString NAV_FAVORITE_LABEL_HOME = "home";
const QString NAV_FAVORITE_LABEL_WORK = "work";
class DestinationWidget;
class NavManager : public QObject {
Q_OBJECT
public:
static NavManager *instance();
QJsonArray currentLocations() const { return locations; }
QJsonObject currentDestination() const { return current_dest; }
void setCurrentDestination(const QJsonObject &loc);
qint64 getLastActivity(const QJsonObject &loc) const;
signals:
void updated();
private:
NavManager(QObject *parent);
void parseLocationsResponse(const QString &response, bool success);
void sortLocations();
Params params;
QString prev_response;
QJsonArray locations;
QJsonObject current_dest;
std::future<void> write_param_future;
};
class MapSettings : public QFrame {
Q_OBJECT
public:
explicit MapSettings(bool closeable = false, QWidget *parent = nullptr);
void navigateTo(const QJsonObject &place);
private:
void showEvent(QShowEvent *event) override;
void refresh();
QVBoxLayout *destinations_layout;
DestinationWidget *current_widget;
DestinationWidget *home_widget;
DestinationWidget *work_widget;
std::vector<DestinationWidget *> widgets;
// FrogPilot variables
QLabel *subtitle;
WifiManager *wifi;
signals:
void closeSettings();
};
class DestinationWidget : public QPushButton {
Q_OBJECT
public:
explicit DestinationWidget(QWidget *parent = nullptr);
void set(const QJsonObject &location, bool current = false);
void unset(const QString &label, bool current = false);
signals:
void actionClicked();
void navigateTo(const QJsonObject &destination);
private:
struct NavIcons {
QPixmap home, work, favorite, recent, directions;
};
static NavIcons icons() {
static NavIcons nav_icons {
loadPixmap("../assets/navigation/icon_home.svg", {48, 48}),
loadPixmap("../assets/navigation/icon_work.svg", {48, 48}),
loadPixmap("../assets/navigation/icon_favorite.svg", {48, 48}),
loadPixmap("../assets/navigation/icon_recent.svg", {48, 48}),
loadPixmap("../assets/navigation/icon_directions.svg", {48, 48}),
};
return nav_icons;
}
private:
QLabel *icon, *title, *subtitle;
QPushButton *action;
QJsonObject dest;
};

View File

@@ -205,16 +205,6 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
});
list->addItem(hiddenNetworkButton);
// Set initial config
wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn")), metered);
connect(uiState(), &UIState::primeTypeChanged, this, [=](PrimeType prime_type) {
bool gsmVisible = prime_type == PrimeType::NONE || prime_type == PrimeType::LITE;
roamingToggle->setVisible(gsmVisible);
editApnButton->setVisible(gsmVisible);
meteredToggle->setVisible(gsmVisible);
});
main_layout->addWidget(new ScrollView(list, this));
main_layout->addStretch(1);
}

View File

@@ -1,7 +1,6 @@
#include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/widgets/prime.h"
#include "common/params.h"
#include "common/swaglog.h"
@@ -432,10 +431,8 @@ void WifiManager::addTetheringConnection() {
}
void WifiManager::tetheringActivated(QDBusPendingCallWatcher *call) {
int prime_type = uiState()->primeType();
int ipv4_forward = (prime_type == PrimeType::NONE || prime_type == PrimeType::LITE);
if (!ipv4_forward) {
// Todo: this is false if not prime. Override?
if (!ipv4_forward && false) {
QTimer::singleShot(5000, this, [=] {
qWarning() << "net.ipv4.ip_forward = 0";
std::system("sudo sysctl net.ipv4.ip_forward=0");

View File

@@ -26,7 +26,6 @@
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
@@ -79,20 +78,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
tr("Display speed in km/h instead of mph."),
"../assets/offroad/icon_metric.png",
},
#ifdef ENABLE_MAPS
{
"NavSettingTime24h",
tr("Show ETA in 24h Format"),
tr("Use 24h format instead of am/pm"),
"../assets/offroad/icon_metric.png",
},
{
"NavSettingLeftSide",
tr("Show Map on Left Side of UI"),
tr("Show map on left side when in split screen view."),
"../assets/offroad/icon_road.png",
},
#endif
};
@@ -161,8 +146,6 @@ void TogglesPanel::updateToggles() {
disengage_on_accelerator_toggle->setVisible(!params.getBool("AlwaysOnLateral"));
auto driver_camera_toggle = toggles["RecordFront"];
driver_camera_toggle->setVisible(!(params.getBool("DeviceManagement") && params.getBool("NoLogging") && params.getBool("NoUploads")));
auto nav_settings_left_toggle = toggles["NavSettingLeftSide"];
nav_settings_left_toggle->setVisible(!params.getBool("FullMap"));
auto experimental_mode_toggle = toggles["ExperimentalMode"];
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
@@ -178,10 +161,6 @@ void TogglesPanel::updateToggles() {
.arg(tr("Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. "
"Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; "
"mistakes should be expected."))
.arg(tr("Navigate on openpilot"))
.arg(tr("When navigation has a destination, openpilot will input the map information into the model. This provides useful context for the model and allows openpilot to keep left or right "
"appropriately at forks/exits. Lane change behavior is unchanged and still activated by the driver. This is an alpha quality feature; mistakes should be expected, particularly around "
"exits and forks. These mistakes can include unintended laneline crossings, late exit taking, driving towards dividing barriers in the gore areas, etc."))
.arg(tr("New Driving Visualization"))
.arg(tr("The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. "
"When a navigation destination is set and the driving model is using it as input, the driving path on the map will turn green."));
@@ -651,7 +630,6 @@ void SettingsWindow::showEvent(QShowEvent *event) {
void SettingsWindow::setCurrentPanel(int index, const QString &param) {
panel_widget->setCurrentIndex(index);
nav_btns->buttons()[index]->setChecked(true);
if (!param.isEmpty()) {
emit expandToggleDescription(param);
}
@@ -715,16 +693,13 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
{tr("Toggles"), toggles},
{tr("Software"), new SoftwarePanel(this)},
{tr("Controls"), frogpilotControls},
{tr("Navigation"), new FrogPilotNavigationPanel(this)},
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
{tr("Visuals"), frogpilotVisuals},
};
nav_btns = new QButtonGroup(this);
for (auto &[name, panel] : panels) {
QPushButton *btn = new QPushButton(name);
btn->setCheckable(true);
btn->setChecked(nav_btns->buttons().size() == 0);
btn->setStyleSheet(R"(
QPushButton {
color: grey;
@@ -741,7 +716,6 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
}
)");
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
nav_btns->addButton(btn);
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins

View File

@@ -43,7 +43,6 @@ signals:
private:
QPushButton *sidebar_alert_widget;
QWidget *sidebar_widget;
QButtonGroup *nav_btns;
QStackedWidget *panel_widget;
// FrogPilot variables

View File

@@ -13,10 +13,6 @@
#include "common/swaglog.h"
#include "common/timing.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 &center, const QPixmap &img, const QBrush &bg, float opacity, const int angle = 0) {
p.setRenderHint(QPainter::Antialiasing);
@@ -60,16 +56,6 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->
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);
@@ -82,7 +68,6 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->
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);
QObject::connect(&clickTimer, &QTimer::timeout, this, [this]() {
clickTimer.stop();
@@ -99,12 +84,6 @@ void OnroadWindow::updateState(const UIState &s) {
Alert alert = Alert::get(*(s.sm), s.scene.started_frame);
alerts->updateAlert(alert);
if (s.scene.map_on_left || scene.full_map) {
split->setDirection(QBoxLayout::LeftToRight);
} else {
split->setDirection(QBoxLayout::RightToLeft);
}
nvg->updateState(s);
QColor bgColor = bg_colors[s.status];
@@ -194,19 +173,6 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) {
// widgetClicked = true;
// }
#ifdef ENABLE_MAPS
if (map != nullptr && !widgetClicked) {
// Switch between map and sidebar when using navigate on openpilot
bool sidebarVisible = geometry().x() > 0;
bool show_map = uiState()->scene.navigate_on_openpilot ? sidebarVisible : !sidebarVisible;
map->setVisible(show_map && !map->isVisible());
if (scene.big_map) {
map->setFixedWidth(width());
} else {
map->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
}
}
#endif
// propagation event to parent(HomeWindow)
if (!widgetClicked) {
QWidget::mousePressEvent(e);
@@ -214,39 +180,10 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) {
}
void OnroadWindow::offroadTransition(bool offroad) {
#ifdef ENABLE_MAPS
if (!offroad) {
if (map == nullptr && (uiState()->hasPrime() || !MAPBOX_TOKEN.isEmpty())) {
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);
QObject::connect(nvg->map_settings_btn_bottom, &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
alerts->updateAlert({});
}
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;
}
#endif
}
void OnroadWindow::paintEvent(QPaintEvent *event) {
QPainter p(this);
@@ -448,100 +385,85 @@ void ExperimentalButton::changeMode() {
}
void ExperimentalButton::updateState(const UIState &s, bool leadInfo) {
const auto cs = (*s.sm)["controlsState"].getControlsState();
bool eng = cs.getEngageable() || cs.getEnabled() || scene.always_on_lateral_active;
if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
engageable = eng;
experimental_mode = cs.getExperimentalMode();
update();
}
// const auto cs = (*s.sm)["controlsState"].getControlsState();
// bool eng = cs.getEngageable() || cs.getEnabled() || scene.always_on_lateral_active;
// if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
// engageable = eng;
// experimental_mode = cs.getExperimentalMode();
// update();
// }
// FrogPilot variables
int randomEvent = scene.current_random_event;
// // FrogPilot variables
// int randomEvent = scene.current_random_event;
rotatingWheel = scene.rotating_wheel;
wheelIcon = scene.wheel_icon;
wheelIconGif = 0;
// rotatingWheel = scene.rotating_wheel;
// wheelIcon = scene.wheel_icon;
// wheelIconGif = 0;
y_offset = leadInfo ? 10 : 0;
// y_offset = leadInfo ? 10 : 0;
if (randomEvent == 0 && gifLabel) {
delete gifLabel;
gifLabel = nullptr;
} else if (randomEvent == 1) {
static int rotationDegree = 0;
rotationDegree = (rotationDegree + 36) % 360;
steeringAngleDeg = rotationDegree;
wheelIcon = 7;
update();
// if (randomEvent == 0 && gifLabel) {
// delete gifLabel;
// gifLabel = nullptr;
// } else if (randomEvent == 1) {
// static int rotationDegree = 0;
// rotationDegree = (rotationDegree + 36) % 360;
// steeringAngleDeg = rotationDegree;
// wheelIcon = 7;
// update();
} else if (randomEvent == 2 || randomEvent == 3 || randomEvent == 4) {
if (!gifLabel) {
gifLabel = new QLabel(this);
QMovie *movie = wheelImagesGif[randomEvent - 1];
if (movie) {
gifLabel->setMovie(movie);
gifLabel->setFixedSize(img_size, img_size);
gifLabel->move((width() - gifLabel->width()) / 2, (height() - gifLabel->height()) / 2 + y_offset);
gifLabel->movie()->start();
}
}
gifLabel->show();
wheelIconGif = randomEvent - 1;
update();
// } else if (randomEvent == 2 || randomEvent == 3 || randomEvent == 4) {
// if (!gifLabel) {
// gifLabel = new QLabel(this);
// QMovie *movie = wheelImagesGif[randomEvent - 1];
// if (movie) {
// gifLabel->setMovie(movie);
// gifLabel->setFixedSize(img_size, img_size);
// gifLabel->move((width() - gifLabel->width()) / 2, (height() - gifLabel->height()) / 2 + y_offset);
// gifLabel->movie()->start();
// }
// }
// gifLabel->show();
// wheelIconGif = randomEvent - 1;
// update();
} else if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) {
steeringAngleDeg = scene.steering_angle_deg;
update();
steeringAngleDeg = scene.steering_angle_deg;
} else if (!rotatingWheel) {
steeringAngleDeg = 0;
}
// } else if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) {
// steeringAngleDeg = scene.steering_angle_deg;
// update();
// steeringAngleDeg = scene.steering_angle_deg;
// } else if (!rotatingWheel) {
// steeringAngleDeg = 0;
// }
}
void ExperimentalButton::paintEvent(QPaintEvent *event) {
if (wheelIcon < 0) {
return;
}
// if (wheelIcon < 0) {
// return;
// }
QPainter p(this);
engage_img = wheelImages[wheelIcon];
QPixmap img = wheelIcon != 0 ? engage_img : (experimental_mode ? experimental_img : engage_img);
QMovie *gif = wheelImagesGif[wheelIconGif];
// QPainter p(this);
// engage_img = wheelImages[wheelIcon];
// QPixmap img = wheelIcon != 0 ? engage_img : (experimental_mode ? experimental_img : engage_img);
// QMovie *gif = wheelImagesGif[wheelIconGif];
QColor background_color = wheelIcon != 0 && !isDown() && engageable ?
(scene.always_on_lateral_active ? QColor(10, 186, 181, 255) :
(scene.conditional_status == 1 || scene.conditional_status == 3 || scene.conditional_status == 5 ? QColor(255, 246, 0, 255) :
(experimental_mode ? QColor(218, 111, 37, 241) :
(scene.traffic_mode_active ? QColor(201, 34, 49, 255) :
(scene.navigate_on_openpilot ? QColor(49, 161, 238, 255) : QColor(0, 0, 0, 166)))))) :
QColor(0, 0, 0, 166);
// QColor background_color = wheelIcon != 0 && !isDown() && engageable ?
// (scene.always_on_lateral_active ? QColor(10, 186, 181, 255) :
// (scene.conditional_status == 1 || scene.conditional_status == 3 || scene.conditional_status == 5 ? QColor(255, 246, 0, 255) :
// (experimental_mode ? QColor(218, 111, 37, 241) :
// (scene.traffic_mode_active ? QColor(201, 34, 49, 255) :
// (scene.navigate_on_openpilot ? QColor(49, 161, 238, 255) : QColor(0, 0, 0, 166)))))) :
// QColor(0, 0, 0, 166);
if (!(scene.map_open && scene.big_map)) {
if (wheelIconGif != 0) {
drawIconGif(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), *gif, background_color, 1.0);
} else {
drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || !engageable) ? 0.6 : 1.0, steeringAngleDeg);
}
}
// if (!(scene.map_open && scene.big_map)) {
// if (wheelIconGif != 0) {
// drawIconGif(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), *gif, background_color, 1.0);
// } else {
// drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || !engageable) ? 0.6 : 1.0, steeringAngleDeg);
// }
// }
}
// MapSettingsButton
MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) {
setFixedSize(btn_size, btn_size + 20);
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), scene(uiState()->scene) {
@@ -572,9 +494,6 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par
main_layout->addLayout(top_right_layout, 0);
main_layout->setAlignment(top_right_layout, Qt::AlignTop | Qt::AlignRight);
map_settings_btn = new MapSettingsButton(this);
main_layout->addWidget(map_settings_btn, 0, Qt::AlignBottom | Qt::AlignRight);
dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5});
// Initialize FrogPilot widgets
@@ -586,10 +505,8 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
const SubMaster &sm = *(s.sm);
const bool cs_alive = sm.alive("controlsState");
const bool nav_alive = sm.alive("navInstruction") && sm["navInstruction"].getValid();
const auto cs = sm["controlsState"].getControlsState();
const auto car_state = sm["carState"].getCarState();
const auto nav_instruction = sm["navInstruction"].getNavInstruction();
// Handle older routes where vCruiseCluster is not set
float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster();
@@ -605,19 +522,19 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
speed = cs_alive ? std::max<float>(0.0, v_ego) : 0.0;
speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH;
auto speed_limit_sign = nav_instruction.getSpeedLimitSign();
speedLimit = slcOverridden ? scene.speed_limit_overridden_speed : speedLimitController ? scene.speed_limit : nav_alive ? nav_instruction.getSpeedLimit() : 0.0;
speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH);
if (speedLimitController && !slcOverridden) {
speedLimit = speedLimit - (showSLCOffset ? slcSpeedLimitOffset : 0);
}
// auto speed_limit_sign = nav_instruction.getSpeedLimitSign();
// speedLimit = slcOverridden ? scene.speed_limit_overridden_speed : speedLimitController ? scene.speed_limit : nav_alive ? nav_instruction.getSpeedLimit() : 0.0;
// speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH);
// if (speedLimitController && !slcOverridden) {
// speedLimit = speedLimit - (showSLCOffset ? slcSpeedLimitOffset : 0);
// }
has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || (speedLimitController && !useViennaSLCSign);
has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign);
is_metric = s.scene.is_metric;
speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals != 0 && (turnSignalLeft || turnSignalRight) || bigMapOpen);
status = s.status;
// has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || (speedLimitController && !useViennaSLCSign);
// has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign);
// is_metric = s.scene.is_metric;
// speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
// hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals != 0 && (turnSignalLeft || turnSignalRight));
// status = s.status;
// update engageability/experimental mode button
experimental_btn->updateState(s, leadInfo);
@@ -630,12 +547,6 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
rightHandDM = dm_state.getIsRHD();
// DM icon transition
dm_fade_state = std::clamp(dm_fade_state+0.2*(0.5-dmActive), 0.0, 1.0);
// hide map settings button for alerts and flip for right hand DM
if (map_settings_btn->isEnabled()) {
map_settings_btn->setVisible(!hideBottomIcons && compass && !scene.hide_map_icon);
main_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignTop);
}
updateFrogPilotWidgets();
}
@@ -777,7 +688,7 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
}
// current speed
if (!(scene.hide_speed || bigMapOpen)) {
if (!(scene.hide_speed)) {
// CLEARPILOT changes to 120 from ~176
// Maybe we want to hide this?
p.setFont(InterFont(140, QFont::Bold));
@@ -1193,9 +1104,6 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
compass_img = new Compass(this);
// bottom_layout->addWidget(compass_img);
map_settings_btn_bottom = new MapSettingsButton(this);
bottom_layout->addWidget(map_settings_btn_bottom);
main_layout->addLayout(bottom_layout);
themeConfiguration = {
@@ -1293,9 +1201,6 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
obstacleDistance = scene.obstacle_distance;
obstacleDistanceStock = scene.obstacle_distance_stock;
mapOpen = scene.map_open;
bigMapOpen = mapOpen && scene.big_map;
onroadDistanceButton = scene.onroad_distance_button;
roadNameUI = scene.road_name_ui;
@@ -1350,7 +1255,7 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
}
void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
if ((showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI) && !bigMapOpen) {
if ((showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI)) {
drawStatusBar(p);
}
@@ -1363,7 +1268,7 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
// animationTimer->stop();
// }
if (leadInfo && !bigMapOpen) {
if (leadInfo) {
drawLeadInfo(p);
}
@@ -1391,12 +1296,6 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
// pedal_icons->updateState();
// }
map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled());
if (map_settings_btn_bottom->isEnabled()) {
map_settings_btn_bottom->setVisible(!hideBottomIcons && !compass && !scene.hide_map_icon);
bottom_layout->setAlignment(map_settings_btn_bottom, rightHandDM ? Qt::AlignLeft : Qt::AlignRight);
}
// recorder_btn->setVisible(scene.screen_recorder && !mapOpen);
recorder_btn->setVisible(false);
}
@@ -1581,7 +1480,7 @@ void AnnotatedCameraWidget::drawLeadInfo(QPainter &p) {
constexpr int maxAccelDuration = 5000;
QString accelerationUnit = tr(" m/s²");
leadDistanceUnit = tr(mapOpen ? "m" : "meters");
leadDistanceUnit = tr("meters");
leadSpeedUnit = tr("m/s");
float accelerationConversion = 1.0f;
@@ -1635,15 +1534,15 @@ void AnnotatedCameraWidget::drawLeadInfo(QPainter &p) {
.arg(accelerationUnit);
QString maxAccSuffix;
if (!mapOpen) {
maxAccSuffix = tr(" - Max: %1%2")
.arg(maxAcceleration * accelerationConversion, 0, 'f', 2)
.arg(accelerationUnit);
}
QString obstacleText = createText(mapOpen ? tr(" | Obstacle: ") : tr(" | Obstacle Factor: "), obstacleDistance);
QString stopText = createText(mapOpen ? tr(" - Stop: ") : tr(" - Stop Factor: "), scene.stopped_equivalence);
QString followText = " = " + createText(mapOpen ? tr("Follow: ") : tr("Follow Distance: "), scene.desired_follow);
QString obstacleText = createText(tr(" | Obstacle Factor: "), obstacleDistance);
QString stopText = createText(tr(" - Stop Factor: "), scene.stopped_equivalence);
QString followText = " = " + createText(tr("Follow Distance: "), scene.desired_follow);
auto createDiffText = [&](const double data, const double stockData) {
double difference = std::round((data - stockData) * distanceConversion);
@@ -1784,21 +1683,21 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
{4, tr("Experimental Mode manually activated")},
{5, tr("Conditional Experimental overridden")},
{6, tr("Experimental Mode manually activated")},
{7, tr("Experimental Mode activated for") + (mapOpen ? tr(" intersection") : tr(" upcoming intersection"))},
{8, tr("Experimental Mode activated for") + (mapOpen ? tr(" turn") : tr(" upcoming turn"))},
{9, tr("Experimental Mode activated due to") + (mapOpen ? tr(" SLC") : tr(" no speed limit set"))},
{10, tr("Experimental Mode activated due to") + (mapOpen ? tr(" speed") : tr(" speed being less than ") + QString::number(scene.conditional_speed_lead) + (is_metric ? tr(" kph") : tr(" mph")))},
{11, tr("Experimental Mode activated due to") + (mapOpen ? tr(" speed") : tr(" speed being less than ") + QString::number(scene.conditional_speed) + (is_metric ? tr(" kph") : tr(" mph")))},
{7, tr("Experimental Mode activated for") + (tr(" upcoming intersection"))},
{8, tr("Experimental Mode activated for") + (tr(" upcoming turn"))},
{9, tr("Experimental Mode activated due to") + (tr(" no speed limit set"))},
{10, tr("Experimental Mode activated due to") + (tr(" speed being less than ") + QString::number(scene.conditional_speed_lead) + (is_metric ? tr(" kph") : tr(" mph")))},
{11, tr("Experimental Mode activated due to") + (tr(" speed being less than ") + QString::number(scene.conditional_speed) + (is_metric ? tr(" kph") : tr(" mph")))},
{12, tr("Experimental Mode activated for slower lead")},
{13, tr("Experimental Mode activated for turn") + (mapOpen ? "" : tr(" / lane change"))},
{13, tr("Experimental Mode activated for turn") + (tr(" / lane change"))},
{14, tr("Experimental Mode activated for curve")},
{15, tr("Experimental Mode activated for stop") + (mapOpen ? "" : tr(" sign / stop light"))},
{15, tr("Experimental Mode activated for stop") + (tr(" sign / stop light"))},
};
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
if (alwaysOnLateralActive && showAlwaysOnLateralStatusBar) {
newStatus = tr("Always On Lateral active") + (mapOpen ? "" : tr(". Press the \"Cruise Control\" button to disable"));
newStatus = tr("Always On Lateral active") + (tr(". Press the \"Cruise Control\" button to disable"));
} else if (showConditionalExperimentalStatusBar) {
newStatus = conditionalStatusMap[status != STATUS_DISENGAGED ? conditionalStatus : 0];
}
@@ -1807,7 +1706,7 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
QString lkasSuffix = tr(". Double press the \"LKAS\" button to revert");
QString screenSuffix = tr(". Double tap the screen to revert");
if (!alwaysOnLateralActive && !mapOpen && status != STATUS_DISENGAGED && !newStatus.isEmpty()) {
if (!alwaysOnLateralActive && status != STATUS_DISENGAGED && !newStatus.isEmpty()) {
if (conditionalStatus == 1 || conditionalStatus == 2) {
newStatus += distanceSuffix;
} else if (conditionalStatus == 3 || conditionalStatus == 4) {

View File

@@ -127,19 +127,6 @@ private:
int y_offset;
};
class MapSettingsButton : public QPushButton {
Q_OBJECT
public:
explicit MapSettingsButton(QWidget *parent = 0);
private:
void paintEvent(QPaintEvent *event) override;
QPixmap settings_img;
};
class PedalIcons : public QWidget {
Q_OBJECT
@@ -170,9 +157,6 @@ public:
void updateState(const UIState &s);
void updateLaneEdgeColor(QColor &bgColor);
MapSettingsButton *map_settings_btn;
MapSettingsButton *map_settings_btn_bottom;
private:
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
@@ -221,13 +205,11 @@ private:
QHBoxLayout *bottom_layout;
bool alwaysOnLateralActive;
bool bigMapOpen;
bool blindSpotLeft;
bool blindSpotRight;
bool compass;
bool experimentalMode;
bool leadInfo;
bool mapOpen;
bool onroadDistanceButton;
bool roadNameUI;
bool showAlwaysOnLateralStatusBar;
@@ -297,11 +279,8 @@ class OnroadWindow : public QWidget {
public:
OnroadWindow(QWidget* parent = 0);
bool isMapVisible() const { return map && map->isVisible(); }
void showMapPanel(bool show) { if (map) map->setVisible(show); }
signals:
void mapPanelRequested();
// signals:
private:
void paintEvent(QPaintEvent *event);
@@ -322,6 +301,5 @@ private:
private slots:
void offroadTransition(bool offroad);
void primeChanged(bool prime);
void updateState(const UIState &s);
};

View File

@@ -1,283 +0,0 @@
#include "selfdrive/ui/qt/widgets/prime.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QTimer>
#include <QVBoxLayout>
#include <QrCode.hpp>
#include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/widgets/wifi.h"
using qrcodegen::QrCode;
PairingQRWidget::PairingQRWidget(QWidget* parent) : QWidget(parent) {
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &PairingQRWidget::refresh);
}
void PairingQRWidget::showEvent(QShowEvent *event) {
refresh();
timer->start(5 * 60 * 1000);
device()->setOffroadBrightness(100);
}
void PairingQRWidget::hideEvent(QHideEvent *event) {
timer->stop();
device()->setOffroadBrightness(BACKLIGHT_OFFROAD);
}
void PairingQRWidget::refresh() {
QString pairToken = CommaApi::create_jwt({{"pair", true}});
QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
this->updateQrCode(qrString);
update();
}
void PairingQRWidget::updateQrCode(const QString &text) {
QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW);
qint32 sz = qr.getSize();
QImage im(sz, sz, QImage::Format_RGB32);
QRgb black = qRgb(0, 0, 0);
QRgb white = qRgb(255, 255, 255);
for (int y = 0; y < sz; y++) {
for (int x = 0; x < sz; x++) {
im.setPixel(x, y, qr.getModule(x, y) ? black : white);
}
}
// Integer division to prevent anti-aliasing
int final_sz = ((width() / sz) - 1) * sz;
img = QPixmap::fromImage(im.scaled(final_sz, final_sz, Qt::KeepAspectRatio), Qt::MonoOnly);
}
void PairingQRWidget::paintEvent(QPaintEvent *e) {
QPainter p(this);
p.fillRect(rect(), Qt::white);
QSize s = (size() - img.size()) / 2;
p.drawPixmap(s.width(), s.height(), img);
}
PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) {
QHBoxLayout *hlayout = new QHBoxLayout(this);
hlayout->setContentsMargins(0, 0, 0, 0);
hlayout->setSpacing(0);
setStyleSheet("PairingPopup { background-color: #E0E0E0; }");
// text
QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->setContentsMargins(85, 70, 50, 70);
vlayout->setSpacing(50);
hlayout->addLayout(vlayout, 1);
{
QPushButton *close = new QPushButton(QIcon(":/icons/close.svg"), "", this);
close->setIconSize(QSize(80, 80));
close->setStyleSheet("border: none;");
vlayout->addWidget(close, 0, Qt::AlignLeft);
QObject::connect(close, &QPushButton::clicked, this, &QDialog::reject);
vlayout->addSpacing(30);
QLabel *title = new QLabel(tr("Pair your device to your comma account"), this);
title->setStyleSheet("font-size: 75px; color: black;");
title->setWordWrap(true);
vlayout->addWidget(title);
QLabel *instructions = new QLabel(QString(R"(
<ol type='1' style='margin-left: 15px;'>
<li style='margin-bottom: 50px;'>%1</li>
<li style='margin-bottom: 50px;'>%2</li>
<li style='margin-bottom: 50px;'>%3</li>
</ol>
)").arg(tr("Go to https://connect.comma.ai on your phone"))
.arg(tr("Click \"add new device\" and scan the QR code on the right"))
.arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this);
instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
instructions->setWordWrap(true);
vlayout->addWidget(instructions);
vlayout->addStretch();
}
// QR code
PairingQRWidget *qr = new PairingQRWidget(this);
hlayout->addWidget(qr, 1);
}
PrimeUserWidget::PrimeUserWidget(QWidget *parent) : QFrame(parent) {
setObjectName("primeWidget");
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(56, 40, 56, 40);
mainLayout->setSpacing(20);
QLabel *subscribed = new QLabel(tr("✓ SUBSCRIBED"));
subscribed->setStyleSheet("font-size: 41px; font-weight: bold; color: #86FF4E;");
mainLayout->addWidget(subscribed);
QLabel *commaPrime = new QLabel(tr("comma prime"));
commaPrime->setStyleSheet("font-size: 75px; font-weight: bold;");
mainLayout->addWidget(commaPrime);
}
PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(80, 90, 80, 60);
main_layout->setSpacing(0);
QLabel *upgrade = new QLabel(tr("Upgrade Now"));
upgrade->setStyleSheet("font-size: 75px; font-weight: bold;");
main_layout->addWidget(upgrade, 0, Qt::AlignTop);
main_layout->addSpacing(50);
QLabel *description = new QLabel(tr("Become a comma prime member at connect.comma.ai"));
description->setStyleSheet("font-size: 56px; font-weight: light; color: white;");
description->setWordWrap(true);
main_layout->addWidget(description, 0, Qt::AlignTop);
main_layout->addStretch();
QLabel *features = new QLabel(tr("PRIME FEATURES:"));
features->setStyleSheet("font-size: 41px; font-weight: bold; color: #E5E5E5;");
main_layout->addWidget(features, 0, Qt::AlignBottom);
main_layout->addSpacing(30);
QVector<QString> bullets = {tr("Remote access"), tr("24/7 LTE connectivity"), tr("1 year of drive storage"), tr("Turn-by-turn navigation")};
for (auto &b : bullets) {
const QString check = "<b><font color='#465BEA'>✓</font></b> ";
QLabel *l = new QLabel(check + b);
l->setAlignment(Qt::AlignLeft);
l->setStyleSheet("font-size: 50px; margin-bottom: 15px;");
main_layout->addWidget(l, 0, Qt::AlignBottom);
}
setStyleSheet(R"(
PrimeAdWidget {
border-radius: 10px;
background-color: #333333;
}
)");
}
SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
mainLayout = new QStackedWidget;
// Unpaired, registration prompt layout
QFrame* finishRegistration = new QFrame;
finishRegistration->setObjectName("primeWidget");
QVBoxLayout* finishRegistationLayout = new QVBoxLayout(finishRegistration);
finishRegistationLayout->setSpacing(38);
finishRegistationLayout->setContentsMargins(64, 48, 64, 48);
QLabel* registrationTitle = new QLabel(tr("Finish Setup"));
registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold;");
finishRegistationLayout->addWidget(registrationTitle);
QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
registrationDescription->setWordWrap(true);
registrationDescription->setStyleSheet("font-size: 50px; font-weight: light;");
finishRegistationLayout->addWidget(registrationDescription);
finishRegistationLayout->addStretch();
QPushButton* pair = new QPushButton(tr("Pair device"));
pair->setStyleSheet(R"(
QPushButton {
font-size: 55px;
font-weight: 500;
border-radius: 10px;
background-color: #465BEA;
padding: 64px;
}
QPushButton:pressed {
background-color: #3049F4;
}
)");
finishRegistationLayout->addWidget(pair);
popup = new PairingPopup(this);
QObject::connect(pair, &QPushButton::clicked, popup, &PairingPopup::exec);
mainLayout->addWidget(finishRegistration);
// build stacked layout
QVBoxLayout *outer_layout = new QVBoxLayout(this);
outer_layout->setContentsMargins(0, 0, 0, 0);
outer_layout->addWidget(mainLayout);
QWidget *content = new QWidget;
QVBoxLayout *content_layout = new QVBoxLayout(content);
content_layout->setContentsMargins(0, 0, 0, 0);
content_layout->setSpacing(30);
primeUser = new PrimeUserWidget;
content_layout->addWidget(primeUser);
WiFiPromptWidget *wifi_prompt = new WiFiPromptWidget;
QObject::connect(wifi_prompt, &WiFiPromptWidget::openSettings, this, &SetupWidget::openSettings);
content_layout->addWidget(wifi_prompt);
content_layout->addStretch();
mainLayout->addWidget(content);
primeUser->setVisible(uiState()->primeType());
mainLayout->setCurrentIndex(1);
setStyleSheet(R"(
#primeWidget {
border-radius: 10px;
background-color: #333333;
}
)");
// Retain size while hidden
QSizePolicy sp_retain = sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
setSizePolicy(sp_retain);
// set up API requests
if (auto dongleId = getDongleId()) {
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/";
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);
QObject::connect(repeater, &RequestRepeater::requestDone, this, &SetupWidget::replyFinished);
}
}
void SetupWidget::replyFinished(const QString &response, bool success) {
if (!success) return;
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting pairing and prime status";
return;
}
QJsonObject json = doc.object();
PrimeType prime_type = static_cast<PrimeType>(json["prime_type"].toInt());
uiState()->setPrimeType(prime_type);
if (!json["is_paired"].toBool()) {
mainLayout->setCurrentIndex(0);
} else {
popup->reject();
primeUser->setVisible(prime_type);
mainLayout->setCurrentIndex(1);
}
}

View File

@@ -1,73 +0,0 @@
#pragma once
#include <QLabel>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QWidget>
#include "selfdrive/ui/qt/widgets/input.h"
// pairing QR code
class PairingQRWidget : public QWidget {
Q_OBJECT
public:
explicit PairingQRWidget(QWidget* parent = 0);
void paintEvent(QPaintEvent*) override;
private:
QPixmap img;
QTimer *timer;
void updateQrCode(const QString &text);
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
private slots:
void refresh();
};
// pairing popup widget
class PairingPopup : public DialogBase {
Q_OBJECT
public:
explicit PairingPopup(QWidget* parent);
};
// widget for paired users with prime
class PrimeUserWidget : public QFrame {
Q_OBJECT
public:
explicit PrimeUserWidget(QWidget* parent = 0);
};
// widget for paired users without prime
class PrimeAdWidget : public QFrame {
Q_OBJECT
public:
explicit PrimeAdWidget(QWidget* parent = 0);
};
// container widget
class SetupWidget : public QFrame {
Q_OBJECT
public:
explicit SetupWidget(QWidget* parent = 0);
signals:
void openSettings(int index = 0, const QString &param = "");
private:
PairingPopup *popup;
QStackedWidget *mainLayout;
PrimeUserWidget *primeUser;
private slots:
void replyFinished(const QString &response, bool success);
};

View File

@@ -74,12 +74,7 @@ void MainWindow::closeSettings() {
main_layout->setCurrentWidget(homeWindow);
if (uiState()->scene.started) {
// Map is always shown when using navigate on openpilot
if (uiState()->scene.navigate_on_openpilot) {
homeWindow->showMapPanel(true);
} else {
homeWindow->showSidebar(params.getBool("Sidebar"));
}
homeWindow->showSidebar(params.getBool("Sidebar"));
}
}