#include "selfdrive/ui/qt/home.h" #include #include #include #include #include "selfdrive/ui/qt/offroad/experimental_mode.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/widgets/drive_stats.h" #include "system/hardware/hw.h" #ifdef ENABLE_MAPS #include "selfdrive/ui/qt/maps/map_settings.h" #endif #include // pybind11 header to control Python embedding namespace py = pybind11; // HomeWindow: the container for the offroad and onroad UIs HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) { // CLEARPILOT Sidebar set to invisible in drive view. params.putBool("Sidebar", false); QHBoxLayout *main_layout = new QHBoxLayout(this); main_layout->setMargin(0); main_layout->setSpacing(0); sidebar = new Sidebar(this); main_layout->addWidget(sidebar); QObject::connect(sidebar, &Sidebar::openSettings, this, &HomeWindow::openSettings); slayout = new QStackedLayout(); main_layout->addLayout(slayout); home = new OffroadHome(this); QObject::connect(home, &OffroadHome::openSettings, this, &HomeWindow::openSettings); slayout->addWidget(home); onroad = new OnroadWindow(this); QObject::connect(onroad, &OnroadWindow::mapPanelRequested, this, [=] { sidebar->hide(); }); slayout->addWidget(onroad); body = new BodyWindow(this); slayout->addWidget(body); // CLEARPILOT ready = new ReadyWindow(this); slayout->addWidget(ready); driver_view = new DriverViewWindow(this); connect(driver_view, &DriverViewWindow::done, [=] { showDriverView(false); }); slayout->addWidget(driver_view); setAttribute(Qt::WA_NoSystemBackground); QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState); QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition); QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition); } 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); // CLEARPILOT // switch to the generic robot UI // if (onroad->isVisible() && !body->isEnabled() && sm["carParams"].getCarParams().getNotCar()) { // body->setEnabled(true); // slayout->setCurrentWidget(body); // } if (s.scene.started) { showDriverView(s.scene.driver_camera_timer >= 10, true); } } void HomeWindow::offroadTransition(bool offroad) { body->setEnabled(false); if (offroad) { sidebar->setVisible(false); slayout->setCurrentWidget(ready); // this->showDriverView(true, true); // Temp } else { sidebar->setVisible(false); slayout->setCurrentWidget(onroad); uiState()->scene.map_open = onroad->isMapVisible(); } } void HomeWindow::showDriverView(bool show, bool started) { if (show) { emit closeSettings(); slayout->setCurrentWidget(driver_view); sidebar->setVisible(show == false); } else { if (started) { slayout->setCurrentWidget(onroad); sidebar->setVisible(params.getBool("Sidebar")); } else { slayout->setCurrentWidget(home); sidebar->setVisible(show == false); } } } void HomeWindow::mousePressEvent(QMouseEvent* e) { // CLEARPILOT todo - tap on main goes straight to settings // Unless we click a debug widget. // Handle sidebar collapsing // if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) { // sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible()); // uiState()->scene.map_open = onroad->isMapVisible(); // params.putBool("Sidebar", sidebar->isVisible()); // } // CLEARPILOT - click ready shows home if (!onroad->isVisible() && ready->isVisible()) { sidebar->setVisible(true); slayout->setCurrentWidget(home); } // Todo: widgets if (onroad->isVisible()) { emit openSettings(); } } void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) { HomeWindow::mousePressEvent(e); const SubMaster &sm = *(uiState()->sm); if (sm["carParams"].getCarParams().getNotCar()) { if (onroad->isVisible()) { slayout->setCurrentWidget(body); } else if (body->isVisible()) { slayout->setCurrentWidget(onroad); } showSidebar(false); } } // OffroadHome: the offroad home page OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { QVBoxLayout* main_layout = new QVBoxLayout(this); main_layout->setContentsMargins(40, 40, 40, 40); // top header QHBoxLayout* header_layout = new QHBoxLayout(); header_layout->setContentsMargins(0, 0, 0, 0); header_layout->setSpacing(16); update_notif = new QPushButton(tr("UPDATE")); update_notif->setVisible(false); update_notif->setStyleSheet("background-color: #364DEF;"); QObject::connect(update_notif, &QPushButton::clicked, [=]() { center_layout->setCurrentIndex(1); }); header_layout->addWidget(update_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); alert_notif = new QPushButton(); alert_notif->setVisible(false); alert_notif->setStyleSheet("background-color: #E22C2C;"); QObject::connect(alert_notif, &QPushButton::clicked, [=] { center_layout->setCurrentIndex(2); }); header_layout->addWidget(alert_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); date = new ElidedLabel(); header_layout->addWidget(date, 0, Qt::AlignHCenter | Qt::AlignLeft); version = new ElidedLabel(); header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight); main_layout->addLayout(header_layout); // main content main_layout->addSpacing(25); center_layout = new QStackedLayout(); QWidget *home_widget = new QWidget(this); { QHBoxLayout *home_layout = new QHBoxLayout(home_widget); home_layout->setContentsMargins(0, 0, 0, 0); home_layout->setSpacing(30); // Clearpilot: removed home ui widgets py::scoped_interpreter guard{}; // Start the Python interpreter auto python_module = py::module_::import("/data/openpilot/webviewtest.py"); auto WebViewWidget = python_module.attr("WebViewWidget"); auto python_widget_instance = WebViewWidget(); auto* python_widget_embedded = python_widget_instance.cast(); home_layout->addWidget(python_widget_embedded); } center_layout->addWidget(home_widget); // add update & alerts widgets update_widget = new UpdateAlert(); QObject::connect(update_widget, &UpdateAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); center_layout->addWidget(update_widget); alerts_widget = new OffroadAlert(); QObject::connect(alerts_widget, &OffroadAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); center_layout->addWidget(alerts_widget); main_layout->addLayout(center_layout, 1); // set up refresh timer timer = new QTimer(this); timer->callOnTimeout(this, &OffroadHome::refresh); setStyleSheet(R"( * { color: white; } OffroadHome { background-color: black; } OffroadHome > QPushButton { padding: 15px 30px; border-radius: 5px; font-size: 40px; font-weight: 500; } OffroadHome > QLabel { font-size: 55px; } )"); } /* Refresh data on screen every 5 seconds. */ void OffroadHome::showEvent(QShowEvent *event) { refresh(); timer->start(5 * 1000); } void OffroadHome::hideEvent(QHideEvent *event) { timer->stop(); } void OffroadHome::refresh() { QString model = QString::fromStdString(params.get("ModelName")); date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d")); version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + model); bool updateAvailable = update_widget->refresh(); int alerts = alerts_widget->refresh(); if (alerts > 0 && !alerts_widget->isVisible()) { alerts_widget->setVisible(true); } else if (alerts == 0 && alerts_widget->isVisible()) { alerts_widget->setVisible(false); } // pop-up new notification // CLEARPILOT temp disabled update notifications int idx = center_layout->currentIndex(); if (!updateAvailable && !alerts && false) { idx = 0; } else if (updateAvailable && (!update_notif->isVisible() || (!alerts && idx == 2))) { idx = 1; } else if (alerts && (!alert_notif->isVisible() || (!updateAvailable && idx == 1))) { idx = 2; } center_layout->setCurrentIndex(idx); // CLEARPILOT temp disabled update notifications // update_notif->setVisible(updateAvailable); // alert_notif->setVisible(alerts); update_notif->setVisible(false); alert_notif->setVisible(false); if (alerts) { alert_notif->setText(QString::number(alerts) + (alerts > 1 ? tr(" ALERTS") : tr(" ALERT"))); } }