diff --git a/selfdrive/assets/icons/blinkL_off.png b/selfdrive/assets/icons/blinkL_off.png new file mode 100644 index 0000000000..1a36ecb6a4 Binary files /dev/null and b/selfdrive/assets/icons/blinkL_off.png differ diff --git a/selfdrive/assets/icons/blinkL_on.png b/selfdrive/assets/icons/blinkL_on.png new file mode 100644 index 0000000000..437a8e39c5 Binary files /dev/null and b/selfdrive/assets/icons/blinkL_on.png differ diff --git a/selfdrive/assets/icons/blinkR_off.png b/selfdrive/assets/icons/blinkR_off.png new file mode 100644 index 0000000000..2cce35764f Binary files /dev/null and b/selfdrive/assets/icons/blinkR_off.png differ diff --git a/selfdrive/assets/icons/blinkR_on.png b/selfdrive/assets/icons/blinkR_on.png new file mode 100644 index 0000000000..d019dcc6d7 Binary files /dev/null and b/selfdrive/assets/icons/blinkR_on.png differ diff --git a/selfdrive/assets/icons/indicatorC_alert.png b/selfdrive/assets/icons/indicatorC_alert.png new file mode 100644 index 0000000000..d36f17a558 Binary files /dev/null and b/selfdrive/assets/icons/indicatorC_alert.png differ diff --git a/selfdrive/assets/icons/indicatorC_off.png b/selfdrive/assets/icons/indicatorC_off.png new file mode 100644 index 0000000000..4528c62591 Binary files /dev/null and b/selfdrive/assets/icons/indicatorC_off.png differ diff --git a/selfdrive/assets/icons/indicatorC_on.png b/selfdrive/assets/icons/indicatorC_on.png new file mode 100644 index 0000000000..1c5c164301 Binary files /dev/null and b/selfdrive/assets/icons/indicatorC_on.png differ diff --git a/selfdrive/assets/icons/indicatorC_warn.png b/selfdrive/assets/icons/indicatorC_warn.png new file mode 100644 index 0000000000..03bc07157b Binary files /dev/null and b/selfdrive/assets/icons/indicatorC_warn.png differ diff --git a/selfdrive/assets/icons/indicator_off.png b/selfdrive/assets/icons/indicator_off.png new file mode 100644 index 0000000000..6dea0f81a4 Binary files /dev/null and b/selfdrive/assets/icons/indicator_off.png differ diff --git a/selfdrive/assets/icons/indicator_on.png b/selfdrive/assets/icons/indicator_on.png new file mode 100644 index 0000000000..ef021fb7aa Binary files /dev/null and b/selfdrive/assets/icons/indicator_on.png differ diff --git a/selfdrive/assets/images/arrow.svg b/selfdrive/assets/images/arrow.svg new file mode 100644 index 0000000000..659fe5d516 --- /dev/null +++ b/selfdrive/assets/images/arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 1695e60cd5..8109a793c0 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -27,11 +27,12 @@ Export('widgets') qt_libs = [widgets, qt_util] + base_libs qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc", - "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", + "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/customGUI.cc", "qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc", - "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] + "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc", + "qt/offroad/custom_widgets/acceleration.cc", "qt/offroad/custom_widgets/steering.cc", "qt/offroad/custom_widgets/status.cc"] # build translation files with open(File("translations/languages.json").abspath) as f: diff --git a/selfdrive/ui/qt/offroad/customGUI.cc b/selfdrive/ui/qt/offroad/customGUI.cc new file mode 100644 index 0000000000..1b654922c8 --- /dev/null +++ b/selfdrive/ui/qt/offroad/customGUI.cc @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include + +#include + +#include "common/watchdog.h" +#include "common/util.h" +#include "selfdrive/ui/qt/network/networking.h" +#include "selfdrive/ui/qt/offroad/settings.h" +#include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/ui/qt/widgets/prime.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/qt/offroad/developer_panel.h" +#include "selfdrive/ui/qt/offroad/customGUI.h" +#include "cereal/gen/cpp/car.capnp.h" + +// Bring custom window to the front +void CustomWindow::showEvent(QShowEvent *event) { + setCurrentPanel(0); +} + +void CustomWindow::setCurrentPanel(int index, const QString ¶m) {} + +CustomWindow::CustomWindow(QWidget *parent) : QFrame(parent) { + // Connect custom GUI update to parent uiState update function + QObject::connect(uiState(), &UIState::uiUpdate, this, &CustomWindow::updateState); + + // Close btn returns to settings page + QPushButton *close_btn = new QPushButton(tr("×")); + close_btn->setStyleSheet(R"( + QPushButton { + font-size: 140px; + padding-bottom: 20px; + border-radius: 75px; + background-color: #555555; + font-weight: 400; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + close_btn->setFixedSize(150, 150); + + // Status control buttons to hide/show indicator groups + QPushButton *blink_btn = new QPushButton(tr("Blinker")); + blink_btn->setStyleSheet(R"( + QPushButton { + font-size: 25px; + padding-bottom: 20px; + border-radius: 25px; + background-color: #555555; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + blink_btn->setFixedSize(125, 125); + + QPushButton *car_btn = new QPushButton(tr("Car")); + car_btn->setStyleSheet(R"( + QPushButton { + font-size: 30px; + padding-bottom: 20px; + border-radius: 25px; + background-color: #555555; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + car_btn->setFixedSize(125, 125); + + QPushButton *steer_btn = new QPushButton(tr("Steer")); + steer_btn->setStyleSheet(R"( + QPushButton { + font-size: 30px; + padding-bottom: 20px; + border-radius: 25px; + background-color: #555555; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + steer_btn->setFixedSize(125, 125); + + QPushButton *drive_btn = new QPushButton(tr("Drive")); + drive_btn->setStyleSheet(R"( + QPushButton { + font-size: 30px; + padding-bottom: 20px; + border-radius: 25px; + background-color: #555555; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + drive_btn->setFixedSize(125, 125); + + // main settings layout, sidebar + main panel + QHBoxLayout *main_layout = new QHBoxLayout(this); + main_layout->setSpacing(40); + QVBoxLayout *sidebar = new QVBoxLayout(); + QVBoxLayout *primary = new QVBoxLayout(); + QVBoxLayout *speed_bar = new QVBoxLayout(); + + sidebar->addWidget(close_btn); + sidebar->addWidget(blink_btn); + sidebar->addWidget(car_btn); + sidebar->addWidget(steer_btn); + sidebar->addWidget(drive_btn); + + ss = new SteeringSlider(this); + lW = new BlinkerStatus(this); + + carStat = new CarStatus(this); + steerStat = new SteerStatus(this); + driveStat = new DriveStatus(this); + + primary->addWidget(lW); + primary->addWidget(carStat); + primary->addWidget(steerStat); + primary->addWidget(driveStat); + primary->addWidget(ss); + + accW = new AccelerationW(this); + spW = new SpeedStatus(this, 0); + speed_bar->addWidget(spW); + speed_bar->addWidget(accW); + + // Connect buttons to visibility functions + settings close + QObject::connect(blink_btn, &QPushButton::clicked, [&](){ + lW->setVisible(!lW->isVisible()); + }); + QObject::connect(steer_btn, &QPushButton::clicked, [&](){ + steerStat->setVisible(!steerStat->isVisible()); + }); + QObject::connect(car_btn, &QPushButton::clicked, [&](){ + carStat->setVisible(!carStat->isVisible()); + }); + QObject::connect(drive_btn, &QPushButton::clicked, [&](){ + driveStat->setVisible(!driveStat->isVisible()); + }); + QObject::connect(close_btn, &QPushButton::clicked, this, &CustomWindow::closeCustom); + main_layout->addLayout(sidebar); + main_layout->addLayout(primary); + main_layout->addLayout(speed_bar); + setStyleSheet(R"( + CustomWindow { + background-color: black; + } + )"); +} + +// Update contents of custom window +// Inherits from ui +void CustomWindow::updateState(const UIState &s) { + const SubMaster &sm = *(s.sm); + lW->update(sm); + spW->update(sm); + carStat->update(sm); + steerStat->update(sm); + driveStat->update(sm); +} + + diff --git a/selfdrive/ui/qt/offroad/customGUI.h b/selfdrive/ui/qt/offroad/customGUI.h new file mode 100644 index 0000000000..41b43b97f8 --- /dev/null +++ b/selfdrive/ui/qt/offroad/customGUI.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/offroad/custom_widgets/acceleration.h" +#include "selfdrive/ui/qt/offroad/custom_widgets/steering.h" +#include "selfdrive/ui/qt/offroad/custom_widgets/status.h" + +// ********** custom window ********** +class CustomWindow : public QFrame { + Q_OBJECT + +public: + explicit CustomWindow(QWidget *parent = 0); + void setCurrentPanel(int index, const QString ¶m = ""); + void updateState(const UIState &s); + +protected: + void showEvent(QShowEvent *event) override; + +signals: + void closeCustom(); + +private: +// All widgets present in custom window + SteeringSlider *ss; + BlinkerStatus *lW; + AccelerationW *accW; + SpeedStatus *spW; + SteerStatus *steerStat; + DriveStatus *driveStat; + CarStatus *carStat; +}; diff --git a/selfdrive/ui/qt/offroad/custom_widgets/acceleration.cc b/selfdrive/ui/qt/offroad/custom_widgets/acceleration.cc new file mode 100644 index 0000000000..0af0972b82 --- /dev/null +++ b/selfdrive/ui/qt/offroad/custom_widgets/acceleration.cc @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +#include + +#include "common/watchdog.h" +#include "common/util.h" +#include "selfdrive/ui/qt/network/networking.h" +#include "selfdrive/ui/qt/offroad/settings.h" +#include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/ui/qt/widgets/prime.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/qt/offroad/developer_panel.h" +#include "selfdrive/ui/qt/offroad/customGUI.h" +#include "cereal/gen/cpp/car.capnp.h" +#include "selfdrive/ui/qt/offroad/custom_widgets/acceleration.h" + +AccelerationW::AccelerationW(QWidget* parent) : QWidget(parent) { + // Create title and control buttons + QVBoxLayout *main = new QVBoxLayout(this); + QLabel *title2 = new QLabel("Acceleration"); + title2->setStyleSheet(R"( + QLabel { + font-size: 40px; + font-weight: bold; + color: #a9a9a9; + } + )"); + + QPushButton *incBtn = new QPushButton(tr("^")); + incBtn->setFixedSize(200, 350); + + QPushButton *decBtn = new QPushButton(tr("V")); + decBtn->setFixedSize(200, 350); + + // Add widgets to layout + main->addWidget(title2); + main->addWidget(incBtn); + main->addWidget(decBtn); + + setStyleSheet(R"( + QPushButton { + font-size: 140px; + padding-bottom: 20px; + background-color: #DDDDDD; + font-weight: 400; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + + main->setAlignment(Qt::AlignRight); +} diff --git a/selfdrive/ui/qt/offroad/custom_widgets/acceleration.h b/selfdrive/ui/qt/offroad/custom_widgets/acceleration.h new file mode 100644 index 0000000000..0e50f411df --- /dev/null +++ b/selfdrive/ui/qt/offroad/custom_widgets/acceleration.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/util.h" + +// Creates gas / brake controls +class AccelerationW : public QWidget { + Q_OBJECT +public: + explicit AccelerationW(QWidget *parent); +}; diff --git a/selfdrive/ui/qt/offroad/custom_widgets/status.cc b/selfdrive/ui/qt/offroad/custom_widgets/status.cc new file mode 100644 index 0000000000..f658fabb8e --- /dev/null +++ b/selfdrive/ui/qt/offroad/custom_widgets/status.cc @@ -0,0 +1,473 @@ + +#include +#include +#include +#include +#include + +#include + +#include "common/watchdog.h" +#include "common/util.h" +#include "selfdrive/ui/qt/network/networking.h" +#include "selfdrive/ui/qt/offroad/settings.h" +#include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/ui/qt/widgets/prime.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/qt/offroad/developer_panel.h" +#include "selfdrive/ui/qt/offroad/customGUI.h" +#include "selfdrive/ui/qt/offroad/custom_widgets/status.h" + +// General status widget outline +StatusWidget::StatusWidget(QWidget* parent) : QWidget(parent) { + main = new QVBoxLayout(this); + statusValue = new QLabel(); + + setStyleSheet(R"( + QLabel { + color: #CCCCCC; + font-size: 30px; + } + )"); +} + +void StatusWidget::update(const SubMaster &sm) { + statusValue->setText("Update"); +} + +SpeedStatus::SpeedStatus(QWidget* parent, int uSel) : QWidget(parent) { + // Set up pixmap array and conversion values + convFactor = 1; + QLabel *units; + QPixmap indOff = QPixmap("../assets/icons/indicatorC_off"); + QPixmap indOn = QPixmap("../assets/icons/indicatorC_alert"); + iconMap[0] = indOff; + iconMap[1] = indOn; + + if(uSel == 0){ + convFactor = 2.2369; + units = new QLabel("MPH"); + } + else if(uSel == 1){ + convFactor = 3.6; + units = new QLabel("KMH"); + } + + // Create labels and value objects + main = new QVBoxLayout(this); + speed_value = new QLabel("0"); + QLabel *standstill = new QLabel("Standstill"); + ss_value = new QLabel(); + ss_value->setScaledContents(true); + ss_value->setPixmap(iconMap[0]); + ss_value->setStyleSheet(R"( + QLabel { + max-height: 35px; + max-width: 35px; + min-height: 35px; + min-width: 35px; + } + )"); + standstill->setStyleSheet(R"( + QLabel { + font-size: 35px; + } + )"); + + // Add labels and values to layouts + QHBoxLayout *i = new QHBoxLayout(); + QHBoxLayout *i2 = new QHBoxLayout(); + i2->setAlignment(Qt::AlignCenter); + i2->setSpacing(35); + + i->addWidget(speed_value); + i->addWidget(units); + + i2->addWidget(standstill); + i2->addWidget(ss_value); + + main->addLayout(i); + main->addLayout(i2); + setStyleSheet(R"( + QLabel { + font-size: 75px; + font-weight: bold; + color: #CCCCCC; + } + )"); +} + +// Update speed and standstill state +void SpeedStatus::update(const SubMaster &sm){ + auto cs = sm["carState"].getCarState(); + speed_value->setText(QString::number((static_cast(convFactor*cs.getVEgoCluster())))); + + if(cs.getStandstill() == 1){ + // Car is stationary + ss_value->setPixmap(iconMap[1]); + } + else{ + ss_value->setPixmap(iconMap[0]); + } +} + +BlinkerStatus::BlinkerStatus(QWidget *parent) : QWidget(parent) { + // Create icon map array, labels, and values + main = new QVBoxLayout(this); + QHBoxLayout *main2 = new QHBoxLayout(); + QLabel *title = new QLabel("Turn Signals"); + + QPixmap indL_on = QPixmap("../assets/icons/blinkL_on"); + QPixmap indL_off = QPixmap("../assets/icons/blinkL_off"); + QPixmap indR_on = QPixmap("../assets/icons/blinkR_on"); + QPixmap indR_off = QPixmap("../assets/icons/blinkR_off"); + + iconMap[0] = indL_off; + iconMap[1] = indL_on; + iconMap[2] = indR_off; + iconMap[3] = indR_on; + leftInd = new QLabel(); + leftInd->setScaledContents(true); + rightInd = new QLabel(); + rightInd->setScaledContents(true); + + leftInd->setPixmap(iconMap[0]); + rightInd->setPixmap(iconMap[2]); + + leftInd->setStyleSheet(R"( + QLabel { + max-height: 150px; + max-width: 150px; + min-height: 150px; + min-width: 150px; + } + )"); + rightInd->setStyleSheet(R"( + QLabel { + max-height: 150px; + max-width: 150px; + min-height: 150px; + min-width: 150px; + } + )"); + + // Add widgets to layout + main2->setSpacing(25); + main2->setAlignment(Qt::AlignCenter); + main2->addWidget(leftInd); + main2->addWidget(title); + main2->addWidget(rightInd); + main->addLayout(main2); + setStyleSheet(R"( + QLabel { + color: #CCCCCC; + font-size: 60px; + font-weight: bold; + } + )"); +} + +void BlinkerStatus::update(const SubMaster &sm){ + if(sm["carState"].getCarState().getLeftBlinker() == true){ + // Left turn signal is on + leftInd->setPixmap(iconMap[1]); + } + else{ + leftInd->setPixmap(iconMap[0]); + } + + if(sm["carState"].getCarState().getRightBlinker() == true){ + // right turn signal is on + rightInd->setPixmap(iconMap[3]); + } + else{ + rightInd->setPixmap(iconMap[2]); + } +} + +CarStatus::CarStatus(QWidget *parent) : QWidget(parent) { + // Create icon map, labels, and value objects + main = new QVBoxLayout(this); + QPixmap indOff = QPixmap("../assets/icons/indicatorC_off"); + QPixmap indAlert = QPixmap("../assets/icons/indicatorC_alert"); + QPixmap indWarn = QPixmap("../assets/icons/indicatorC_warn"); + QPixmap indOn = QPixmap("../assets/icons/indicatorC_on"); + iconMap[0] = indOff; + iconMap[1] = indAlert; + iconMap[2] = indWarn; + iconMap[3] = indOn; + + QHBoxLayout *driverStatus = new QHBoxLayout(); + driverStatus->setSpacing(30); + driverStatus->setAlignment(Qt::AlignLeft); + + QLabel *door_status = new QLabel("Door Open"); + QLabel *seatbelt_stat = new QLabel("Seatbelt Unbuckled"); + QLabel *espEnabled = new QLabel("ESP Disabled"); + QLabel *cruiseStat = new QLabel("Cruise Control"); + + // Set up indicator objects and fix image size + door_value = new QLabel(); + door_value->setScaledContents(true); + door_value->setPixmap(iconMap[0]); + door_value->setStyleSheet(R"( + QLabel { + max-height: 60px; + max-width: 60px; + min-height: 60px; + min-width: 60px; } )"); + seatbelt_value = new QLabel(); + seatbelt_value->setPixmap(iconMap[0]); + seatbelt_value->setScaledContents(true); + seatbelt_value->setStyleSheet(R"( + QLabel { + max-height: 60px; + max-width: 60px; + min-height: 60px; + min-width: 60px; } )"); + + esp_value = new QLabel(); + esp_value->setPixmap(iconMap[0]); + esp_value->setScaledContents(true); + esp_value->setStyleSheet(R"( + QLabel { + max-height: 60px; + max-width: 60px; + min-height: 60px; + min-width: 60px; } )"); + cruise_enabled = new QLabel(); + cruise_enabled->setScaledContents(true); + cruise_enabled->setPixmap(iconMap[0]); + cruise_enabled->setStyleSheet(R"( + QLabel { + max-height: 60px; + max-width: 60px; + min-height: 60px; + min-width: 60px; } )"); + + // Add widgets to layout + driverStatus->addWidget(door_status); + driverStatus->addWidget(door_value); + driverStatus->addStretch(); + driverStatus->addWidget(seatbelt_stat); + driverStatus->addWidget(seatbelt_value); + main->addLayout(driverStatus); + + QHBoxLayout *temp = new QHBoxLayout(); + temp->setAlignment(Qt::AlignLeft); + temp->setSpacing(30); + temp->addWidget(espEnabled); + temp->addWidget(esp_value); + temp->addStretch(); + temp->addWidget(cruiseStat); + temp->addWidget(cruise_enabled); + + main->addLayout(temp); + setStyleSheet(R"( + QLabel { + color: #CCCCCC; + font-size: 55px; + font-weight: bold; + } + )"); +} + +void CarStatus::update(const SubMaster &sm){ + auto cs = sm["carState"].getCarState(); + if(cs.getDoorOpen() == 1){ + // Door is open in car, turn on warning light + door_value->setPixmap(iconMap[2]); + } + else{ + door_value->setPixmap(iconMap[0]); + } + + if(cs.getSeatbeltUnlatched() == 1){ + // Seatbelt unbuckled + seatbelt_value->setPixmap(iconMap[2]); + } + else{ + seatbelt_value->setPixmap(iconMap[0]); + } + + if(cs.getEspDisabled() == 0){ + // ESP is off + esp_value->setPixmap(iconMap[1]); + } + else{ + esp_value->setPixmap(iconMap[0]); + } + + if(cs.getCruiseState().getEnabled() == 1){ + // Cruise control is turned on + cruise_enabled->setPixmap(iconMap[3]); + } + else{ + cruise_enabled->setPixmap(iconMap[0]); + } +} + +DriveStatus::DriveStatus(QWidget *parent) : QWidget(parent) { + // Create icon layout, labels and value objects + main = new QVBoxLayout(this); + QPixmap indOff = QPixmap("../assets/icons/indicatorC_off"); + QPixmap indOn = QPixmap("../assets/icons/indicatorC_on"); + iconMap[0] = indOff; + iconMap[1] = indOn; + QHBoxLayout *hLay = new QHBoxLayout(); + hLay->setSpacing(35); + hLay->setAlignment(Qt::AlignLeft); + + QLabel *fuel_name = new QLabel("Gas Pedal %"); + QLabel *gas_pressed = new QLabel("Gas Engaged"); + QLabel *brake_pressed = new QLabel("Brake Engaged"); + QLabel *gearShift = new QLabel("Gear: "); + gear_value = new QLabel(); + + // Create indicator objects + gas_engaged = new QLabel(); + gas_engaged->setScaledContents(true); + gas_engaged->setPixmap(iconMap[0]); + gas_engaged->setStyleSheet(R"( + QLabel { + max-height: 60px; + max-width: 60px; + min-height: 60px; + min-width: 60px; } )"); + gas_value = new QLabel(); + brake_engaged = new QLabel(); + brake_engaged->setScaledContents(true); + brake_engaged->setPixmap(iconMap[0]); + brake_engaged->setStyleSheet(R"( + QLabel { + max-height: 60px; + max-width: 60px; + min-height: 60px; + min-width: 60px; } )"); + + // Add widgets to layout + hLay->addWidget(gas_pressed); + hLay->addWidget(gas_engaged); + hLay->addStretch(); + hLay->addWidget(brake_pressed); + hLay->addWidget(brake_engaged); + + QHBoxLayout *carStat = new QHBoxLayout(); + carStat->setSpacing(35); + carStat->setAlignment(Qt::AlignLeft); + carStat->addWidget(fuel_name); + carStat->addWidget(gas_value); + carStat->addStretch(); + carStat->addWidget(gearShift); + carStat->addWidget(gear_value); + + main->addLayout(hLay); + main->addLayout(carStat); +setStyleSheet(R"( + QLabel { + color: #CCCCCC; + font-size: 55px; + font-weight: bold; + } + )"); +} + +void DriveStatus::update(const SubMaster &sm){ + auto cs = sm["carState"].getCarState(); + + if(cs.getGasPressed() == 1){ + // Gas pedal is pressed + gas_engaged->setPixmap(iconMap[1]); + } + else{ + gas_engaged->setPixmap(iconMap[0]); + } + + if(cs.getBrakePressed() == 1){ + // Brake pedal is pressed + brake_engaged->setPixmap(iconMap[1]); + } + else{ + brake_engaged->setPixmap(iconMap[0]); + } + + // Set text based on current gear position + if(static_cast(cs.getGearShifter()) == 1){ + gear_value->setText("Park"); + } + else if(static_cast(cs.getGearShifter()) == 2){ + gear_value->setText("Drive"); + } + else if(static_cast(cs.getGearShifter()) == 4){ + gear_value->setText("Reverse"); + } + else{ + gear_value->setText("Unknown"); + } + + // Set % of gas pedal down, estimates 250 as maximum + gas_value->setText(QString::number(static_cast(cs.getGas() / 250))); +} + +SteerStatus::SteerStatus(QWidget *parent) : QWidget(parent) { + // Creates icon map, labels, and value objects + main = new QVBoxLayout(this); + QHBoxLayout *steeringStatus = new QHBoxLayout(); + steeringStatus->setSpacing(35); + QLabel *steeringPressed = new QLabel("Steering Engaged"); + QLabel *steeringVal = new QLabel("Wheel Angle %"); + steer_enabled = new QLabel(); + steer_value = new QLabel("NULL"); + steer_dir = new QLabel("NULL"); + + QPixmap indOff = QPixmap("../assets/icons/indicatorC_off"); + QPixmap indOn = QPixmap("../assets/icons/indicatorC_on"); + iconMap[0] = indOff; + iconMap[1] = indOn; + + // Create indicator objects + steer_enabled->setScaledContents(true); + steer_enabled->setPixmap(iconMap[0]); + steer_enabled->setStyleSheet(R"( + QLabel { + max-height: 60px; + max-width: 60px; + min-height: 60px; + min-width: 60px; } )"); + steeringStatus->addWidget(steeringPressed); + steeringStatus->addWidget(steer_enabled); + steeringStatus->addStretch(); + steeringStatus->addWidget(steeringVal); + steeringStatus->addWidget(steer_value); + steeringStatus->addWidget(steer_dir); + + main->addLayout(steeringStatus); + setStyleSheet(R"( + QLabel { + color: #CCCCCC; + font-size: 55px; + font-weight: bold; + } + )"); +} + +void SteerStatus::update(const SubMaster &sm){ + auto cs = sm["carState"].getCarState(); + if(cs.getSteeringPressed() == 1){ + // Currently adjusting steering wheel + steer_enabled->setPixmap(iconMap[1]); + } + else{ + steer_enabled->setPixmap(iconMap[0]); + } + + // Set labels for how far the wheel is turned, estimates 480 as maximum + float val = cs.getSteeringAngleDeg(); + steer_value->setText(QString::number(static_cast(std::abs(val) / 480))); + if(val < 0){ + steer_dir->setText("Right"); + } + else{ + steer_dir->setText("Left"); + } +} diff --git a/selfdrive/ui/qt/offroad/custom_widgets/status.h b/selfdrive/ui/qt/offroad/custom_widgets/status.h new file mode 100644 index 0000000000..bab7fa95f0 --- /dev/null +++ b/selfdrive/ui/qt/offroad/custom_widgets/status.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/controls.h" + +// General status widget outline +class StatusWidget : public QWidget { + Q_OBJECT + public: + explicit StatusWidget(QWidget *parent); + void update(const SubMaster &sm); + + private: + QVBoxLayout *main; + QLabel *statusValue; + }; + +// Displays speed of vehicle +class SpeedStatus : public QWidget { + Q_OBJECT + public: + explicit SpeedStatus(QWidget *parent, int uSel); + void update(const SubMaster &sm); + + private: // Current speed of car, standstill indicator + QVBoxLayout *main; + QLabel *speed_value; + QLabel *unit; + QLabel *ss_value; + QPixmap iconMap[2]; + float convFactor; +}; + +// Displays status of turn signals +class BlinkerStatus : public QWidget { + Q_OBJECT + public: + explicit BlinkerStatus(QWidget *parent); + void update(const SubMaster &sm); + + private: + QVBoxLayout *main; + QPixmap iconMap[4]; + QLabel *leftInd; + QLabel *rightInd; +}; + +// General car status +class CarStatus : public QWidget { + Q_OBJECT + public: + explicit CarStatus(QWidget *parent); + void update(const SubMaster &sm); + + private: + QVBoxLayout *main; + QLabel *door_value; + QLabel *seatbelt_value; + QLabel *cruise_enabled; + QLabel *esp_value; + QPixmap iconMap[4]; +}; + +// Active driving status +class DriveStatus : public QWidget { + Q_OBJECT + public: + explicit DriveStatus(QWidget *parent); + void update(const SubMaster &sm); + + private: + QVBoxLayout *main; + QLabel *gas_engaged; + QLabel *gas_value; + QLabel *brake_engaged; + QLabel *gear_value; + QPixmap iconMap[2]; +}; + +// Steering wheel status +class SteerStatus : public QWidget { + Q_OBJECT + public: + explicit SteerStatus(QWidget *parent); + void update(const SubMaster &sm); + + private: + QVBoxLayout *main; + QLabel *steer_enabled; + QLabel *steer_value; + QLabel *steer_dir; + QPixmap iconMap[2]; +}; diff --git a/selfdrive/ui/qt/offroad/custom_widgets/steering.cc b/selfdrive/ui/qt/offroad/custom_widgets/steering.cc new file mode 100644 index 0000000000..c9784ff1b4 --- /dev/null +++ b/selfdrive/ui/qt/offroad/custom_widgets/steering.cc @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include + +#include + +#include "common/watchdog.h" +#include "common/util.h" +#include "selfdrive/ui/qt/network/networking.h" +#include "selfdrive/ui/qt/offroad/settings.h" +#include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/qt/offroad/customGUI.h" +#include "cereal/gen/cpp/car.capnp.h" +#include "selfdrive/ui/qt/offroad/custom_widgets/steering.h" + +// Create steering control slider widget +SteeringSlider::SteeringSlider(QWidget* parent) : QWidget(parent) { + QVBoxLayout *main = new QVBoxLayout(this); + main->addStretch(); + main->setAlignment(Qt::AlignHCenter); + + // Create and add labels + QLabel *title = new QLabel("Wheel Control"); + turnAngle = new QSpinBox(); + turnAngle->setPrefix("Angle: "); + title->setStyleSheet(R"( + QLabel { + font-size: 40px; + font-weight: bold; + color: #CCCCCC; + } + )"); + + QHBoxLayout *temp = new QHBoxLayout(); + temp->setSpacing(35); + temp->setAlignment(Qt::AlignCenter); + temp->addWidget(title); + temp->addWidget(turnAngle); + main->addLayout(temp); + main->setSpacing(20); + + // Create new slider control + // Set style of slider + steering_slider = new SliderControl(Qt::Horizontal); + connect(steering_slider, &SliderControl::valueChanged, turnAngle, &QSpinBox::setValue); + steering_slider->setStyleSheet(R"( + QSlider { + min-height: 200px; + max-height: 200px; + background: #000000; + color: black; + } + QSlider::groove:horizontal { + border: 1px solid #262626; + height: 150px; + background: #333333; + margin: 0 75px; + } + QSlider::handle:horizontal { + background: #CCCCCC; + width: 100px; + height: 200px; + margin: -100px -50px; + } + )"); + + turnAngle->setStyleSheet(R"( + QSpinBox { + background: #000000; + color: #CCCCCC; + font-size: 40px; + font-weight: bold; + })"); + + // Slider initialization + turnAngle->setReadOnly(true); + steering_slider->setMinimum(-100); + steering_slider->setMaximum(100); + turnAngle->setRange(-100, 100); + steering_slider->setValue(0); + main->addWidget(steering_slider); +} diff --git a/selfdrive/ui/qt/offroad/custom_widgets/steering.h b/selfdrive/ui/qt/offroad/custom_widgets/steering.h new file mode 100644 index 0000000000..2b242845c6 --- /dev/null +++ b/selfdrive/ui/qt/offroad/custom_widgets/steering.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/controls.h" + +// Creates steering slider widget +class SteeringSlider : public QWidget { + Q_OBJECT +public: + explicit SteeringSlider(QWidget *parent); + +private: + SliderControl *steering_slider; + QSpinBox *turnAngle; +}; diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index f68379242e..b4c349c83c 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -15,6 +15,15 @@ #include "selfdrive/ui/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/offroad/developer_panel.h" +// Adds a new panel to the settings +// Contains settings relevant to custom page +CustomPanel::CustomPanel(QWidget* parent) : ListWidget(parent) { + // Enable GUI button to switch to the custom window + enableGui = new ButtonControl(tr("Drive-by-wire GUI"), tr("ENABLE")); + connect(enableGui, &ButtonControl::clicked, [=]() { emit openCustom(); }); + addItem(enableGui); +} + TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { // param, title, desc, icon std::vector> toggle_defs{ @@ -378,12 +387,16 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { auto networking = new Networking(this); QObject::connect(uiState()->prime_state, &PrimeState::changed, networking, &Networking::setPrimeType); + CustomPanel *customGui = new CustomPanel(this); + QObject::connect(customGui, &CustomPanel::openCustom, this, &SettingsWindow::openCustom); + QList> panels = { {tr("Device"), device}, {tr("Network"), networking}, {tr("Toggles"), toggles}, {tr("Software"), new SoftwarePanel(this)}, {tr("Developer"), new DeveloperPanel(this)}, + {tr("Custom"), customGui}, }; nav_btns = new QButtonGroup(this); @@ -396,7 +409,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { color: grey; border: none; background: none; - font-size: 65px; + font-size: 50px; font-weight: 500; } QPushButton:checked { diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index 68ba0d1898..114ec2584b 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -27,6 +27,7 @@ class SettingsWindow : public QFrame { signals: void closeSettings(); + void openCustom(); void reviewTrainingGuide(); void showDriverView(); void expandToggleDescription(const QString ¶m); @@ -98,3 +99,13 @@ class SoftwarePanel : public ListWidget { Params params; ParamWatcher *fs_watch; }; + +class CustomPanel : public ListWidget { + Q_OBJECT +public: + explicit CustomPanel(QWidget* parent = nullptr); +signals: + void openCustom(); +private: + ButtonControl *enableGui; +}; diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 966396edc2..8b6fa8a9b3 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -77,7 +77,7 @@ void Sidebar::updateState(const UIState &s) { networking = networking ? networking : window()->findChild(""); bool tethering_on = networking && networking->wifi->tethering_on; auto deviceState = sm["deviceState"].getDeviceState(); - setProperty("netType", tethering_on ? "Hotspot": network_type[deviceState.getNetworkType()]); + setProperty("netType", tethering_on ? "Hotspot": "Testing"); int strength = tethering_on ? 4 : (int)deviceState.getNetworkStrength(); setProperty("netStrength", strength > 0 ? strength + 1 : 0); @@ -123,19 +123,13 @@ void Sidebar::paintEvent(QPaintEvent *event) { p.setOpacity(1.0); // network - int x = 58; - const QColor gray(0x54, 0x54, 0x54); - for (int i = 0; i < 5; ++i) { - p.setBrush(i < net_strength ? Qt::white : gray); - p.drawEllipse(x, 196, 27, 27); - x += 37; - } - - p.setFont(InterFont(35)); - p.setPen(QColor(0xff, 0xff, 0xff)); - const QRect r = QRect(58, 247, width() - 100, 50); - p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, net_type); - + int x = 58; + const QColor gray(0x54, 0x54, 0x54); + for (int i = 0; i < 5; ++i) { + p.setBrush(i < net_strength ? Qt::white : gray); + p.drawEllipse(x, 196, 27, 27); + x += 37; + } // metrics drawMetric(p, temp_status.first, temp_status.second, 338); drawMetric(p, panda_status.first, panda_status.second, 496); diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h index 2091418e52..9915ebdfd7 100644 --- a/selfdrive/ui/qt/sidebar.h +++ b/selfdrive/ui/qt/sidebar.h @@ -50,6 +50,7 @@ public slots: const QRect home_btn = QRect(60, 860, 180, 180); const QRect settings_btn = QRect(50, 35, 200, 117); + const QRect custom_btn = QRect(50,196,200,117); const QColor good_color = QColor(255, 255, 255); const QColor warning_color = QColor(218, 202, 37); const QColor danger_color = QColor(201, 34, 49); diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc index 40dda971f5..60d87f1f2a 100644 --- a/selfdrive/ui/qt/widgets/controls.cc +++ b/selfdrive/ui/qt/widgets/controls.cc @@ -139,3 +139,13 @@ void ParamControl::toggleClicked(bool state) { toggle.togglePosition(); } } + +// Create a slider component for control +SliderControl::SliderControl(Qt::Orientation orientation, QWidget *parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setMargin(0); + + slider.setOrientation(orientation); + QObject::connect(&slider, &QAbstractSlider::valueChanged, this, &SliderControl::valueChanged); + main_layout->addWidget(&slider); +} diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index aebf934f2a..b78b859eab 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "common/params.h" #include "selfdrive/ui/qt/widgets/input.h" @@ -294,3 +295,21 @@ class LayoutWidget : public QWidget { setLayout(l); } }; + +// slider widget +class SliderControl : public QFrame { + Q_OBJECT + +public: + SliderControl(Qt::Orientation orientation, QWidget *parent = nullptr); + inline void setMaximum(int value) {slider.setMaximum(value); } + inline void setMinimum(int value) { slider.setMinimum(value); } + inline void setTickStyle(QSlider::TickPosition position) { slider.setTickPosition(position); } + inline void setValue(int value) {slider.setValue(value); } + inline void setTickInterval(int value) { slider.setTickInterval(value); } + +signals: + void valueChanged(int value); +private: + QSlider slider; +}; diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc index 6b579fcc5d..296db1d090 100644 --- a/selfdrive/ui/qt/window.cc +++ b/selfdrive/ui/qt/window.cc @@ -33,6 +33,11 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { main_layout->setCurrentWidget(onboardingWindow); } + customWindow = new CustomWindow(this); + main_layout->addWidget(customWindow); + QObject::connect(customWindow, &CustomWindow::closeCustom, this, &MainWindow::closeCustom); + QObject::connect(settingsWindow, &SettingsWindow::openCustom, this, &MainWindow::openCustom); + QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { if (!offroad) { closeSettings(); @@ -70,6 +75,10 @@ void MainWindow::openSettings(int index, const QString ¶m) { settingsWindow->setCurrentPanel(index, param); } +void MainWindow::openCustom() { + main_layout->setCurrentWidget(customWindow); +} + void MainWindow::closeSettings() { main_layout->setCurrentWidget(homeWindow); @@ -78,6 +87,10 @@ void MainWindow::closeSettings() { } } +void MainWindow::closeCustom() { + main_layout->setCurrentWidget(settingsWindow); +} + bool MainWindow::eventFilter(QObject *obj, QEvent *event) { bool ignore = false; switch (event->type()) { diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h index 05b61e1f76..163d3a843a 100644 --- a/selfdrive/ui/qt/window.h +++ b/selfdrive/ui/qt/window.h @@ -6,6 +6,7 @@ #include "selfdrive/ui/qt/home.h" #include "selfdrive/ui/qt/offroad/onboarding.h" #include "selfdrive/ui/qt/offroad/settings.h" +#include "selfdrive/ui/qt/offroad/customGUI.h" class MainWindow : public QWidget { Q_OBJECT @@ -17,9 +18,12 @@ class MainWindow : public QWidget { bool eventFilter(QObject *obj, QEvent *event) override; void openSettings(int index = 0, const QString ¶m = ""); void closeSettings(); + void openCustom(); + void closeCustom(); QStackedLayout *main_layout; HomeWindow *homeWindow; SettingsWindow *settingsWindow; OnboardingWindow *onboardingWindow; + CustomWindow *customWindow; };