ui: sunnypilot offroad UI (#512)

* Revert "Revert "ui: sunnypilot offroad UI" (#511)"

This reverts commit 0e264e1b05.

* Revert "move files to sp dir"

This reverts commit c72d732259d35872fca92b90289785e7b4086140.

* remove drive stats for now

* update translation

* update onboarding

* remove sp onboarding for now

* rearrange

* remove more

* shorter license
This commit is contained in:
Jason Wen
2025-01-03 15:04:09 -05:00
committed by GitHub
parent 0e264e1b05
commit 3966599e9d
66 changed files with 2118 additions and 50 deletions
+1
View File
@@ -0,0 +1 @@
Wen
+10
View File
@@ -74,6 +74,12 @@ AddOption('--minimal',
default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS)
help='the minimum build to run openpilot. no tests, tools, etc.')
AddOption('--stock-ui',
action='store_true',
dest='stock_ui',
default=False,
help='Build stock openpilot UI instead of sunnypilot UI')
## Architecture name breakdown (arch)
## - larch64: linux tici aarch64
## - aarch64: linux pc aarch64
@@ -172,6 +178,10 @@ else:
if arch != "Darwin":
ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"]
if not GetOption('stock_ui'):
cflags += ["-DSUNNYPILOT"]
cxxflags += ["-DSUNNYPILOT"]
ccflags_option = GetOption('ccflags')
if ccflags_option:
ccflags += ccflags_option.split(' ')
+1 -1
View File
@@ -57,7 +57,7 @@ function run_tests() {
if [[ -z "$FAST" ]]; then
run "mypy" mypy $PYTHON_FILES
run "codespell" codespell $ALL_FILES
run "codespell" codespell $ALL_FILES --ignore-words=$ROOT/.codespellignore
fi
return $FAILED
+8 -2
View File
@@ -12,6 +12,12 @@ if arch == "Darwin":
del base_libs[base_libs.index('OpenCL')]
qt_env['FRAMEWORKS'] += ['OpenCL']
sp_widgets_src = []
sp_qt_src = []
if not GetOption('stock_ui'):
SConscript(['sunnypilot/SConscript'])
Import('sp_widgets_src', 'sp_qt_src')
# FIXME: remove this once we're on 5.15 (24.04)
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
@@ -20,7 +26,7 @@ widgets_src = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc",
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"]
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + sp_widgets_src
widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
Export('widgets')
@@ -31,7 +37,7 @@ qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.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"] + sp_qt_src
# build translation files
with open(File("translations/languages.json").abspath) as f:
+7 -1
View File
@@ -4,10 +4,16 @@
#include <QTranslator>
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/window.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/window.h"
#define MainWindow MainWindowSP
#else
#include "selfdrive/ui/qt/qt_window.h"
#endif
int main(int argc, char *argv[]) {
setpriority(PRIO_PROCESS, 0, -20);
+6
View File
@@ -5,7 +5,13 @@
#include <QPushButton>
#include "common/util.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#define UIState UIStateSP
#else
#include "selfdrive/ui/ui.h"
#endif
class RecordButton : public QPushButton {
Q_OBJECT
+16 -6
View File
@@ -9,12 +9,23 @@
#include "common/params.h"
#include "selfdrive/ui/qt/body.h"
#include "selfdrive/ui/qt/onroad/onroad_home.h"
#include "selfdrive/ui/qt/sidebar.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/offroad_alerts.h"
#include "selfdrive/ui/ui.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h"
#include "selfdrive/ui/sunnypilot/qt/sidebar.h"
#define OnroadWindow OnroadWindowSP
#define LayoutWidget LayoutWidgetSP
#define Sidebar SidebarSP
#define ElidedLabel ElidedLabelSP
#else
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/onroad/onroad_home.h"
#include "selfdrive/ui/qt/sidebar.h"
#endif
class OffroadHome : public QFrame {
Q_OBJECT
@@ -58,13 +69,12 @@ protected:
void mousePressEvent(QMouseEvent* e) override;
void mouseDoubleClickEvent(QMouseEvent* e) override;
private:
Sidebar *sidebar;
OffroadHome *home;
OnroadWindow *onroad;
BodyWindow *body;
QStackedLayout *slayout;
private slots:
void updateState(const UIState &s);
protected slots:
virtual void updateState(const UIState &s);
};
+6
View File
@@ -8,8 +8,14 @@
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
#else
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
#endif
static const int ICON_WIDTH = 49;
+11
View File
@@ -8,6 +8,17 @@
#include "selfdrive/ui/qt/widgets/ssh_keys.h"
#include "selfdrive/ui/qt/widgets/toggle.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
#define ButtonControl ButtonControlSP
#define ElidedLabel ElidedLabelSP
#define LabelControl LabelControlSP
#define ListWidget ListWidgetSP
#define ToggleControl ToggleControlSP
#else
#include "selfdrive/ui/qt/widgets/controls.h"
#endif
class WifiItem : public QWidget {
Q_OBJECT
public:
+6 -1
View File
@@ -2,9 +2,14 @@
#include "selfdrive/ui/qt/offroad/developer_panel.h"
#include "selfdrive/ui/qt/widgets/ssh_keys.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "common/util.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
#else
#include "selfdrive/ui/qt/widgets/controls.h"
#endif
DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) {
// SSH keys
addItem(new SshToggle());
@@ -1,6 +1,10 @@
#pragma once
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#else
#include "selfdrive/ui/qt/offroad/settings.h"
#endif
class DeveloperPanel : public ListWidget {
Q_OBJECT
+4 -3
View File
@@ -71,6 +71,7 @@ public slots:
private:
void showEvent(QShowEvent *event) override;
protected:
QPushButton *accept_btn;
signals:
@@ -97,10 +98,10 @@ class OnboardingWindow : public QStackedWidget {
public:
explicit OnboardingWindow(QWidget *parent = 0);
inline void showTrainingGuide() { setCurrentIndex(1); }
inline bool completed() const { return accepted_terms && training_done; }
virtual inline bool completed() const { return accepted_terms && training_done; }
private:
void updateActiveScreen();
protected:
virtual void updateActiveScreen();
Params params;
bool accepted_terms = false, training_done = false;
+3
View File
@@ -100,7 +100,9 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
}
// Toggles with confirmation dialogs
#ifndef SUNNYPILOT
toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg");
#endif
toggles["ExperimentalMode"]->setConfirmation(true, true);
toggles["ExperimentalLongitudinalEnabled"]->setConfirmation(true, false);
@@ -354,6 +356,7 @@ void SettingsWindow::setCurrentPanel(int index, const QString &param) {
}
SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
RETURN_IF_SUNNYPILOT
// setup two main layouts
sidebar_widget = new QWidget;
+21 -9
View File
@@ -10,9 +10,21 @@
#include <QStackedWidget>
#include <QWidget>
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
#define ListWidget ListWidgetSP
#define ParamControl ParamControlSP
#define ButtonControl ButtonControlSP
#define ButtonParamControl ButtonParamControlSP
#define ToggleControl ToggleControlSP
#define LabelControl LabelControlSP
#else
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#endif
// ********** settings window + top-level panels **********
class SettingsWindow : public QFrame {
@@ -30,7 +42,7 @@ signals:
void reviewTrainingGuide();
void expandToggleDescription(const QString &param);
private:
protected:
QPushButton *sidebar_alert_widget;
QWidget *sidebar_widget;
QButtonGroup *nav_btns;
@@ -45,12 +57,12 @@ public:
signals:
void reviewTrainingGuide();
private slots:
protected slots:
void poweroff();
void reboot();
void updateCalibDescription();
private:
protected:
Params params;
ButtonControl *pair_device;
};
@@ -64,15 +76,15 @@ public:
public slots:
void expandToggleDescription(const QString &param);
private slots:
void updateState(const UIState &s);
protected slots:
virtual void updateState(const UIState &s);
private:
protected:
Params params;
std::map<std::string, ParamControl*> toggles;
ButtonParamControl *long_personality_setting;
void updateToggles();
virtual void updateToggles();
};
class SoftwarePanel : public ListWidget {
@@ -80,7 +92,7 @@ class SoftwarePanel : public ListWidget {
public:
explicit SoftwarePanel(QWidget* parent = nullptr);
private:
protected:
void showEvent(QShowEvent *event) override;
void updateLabels();
void checkForUpdates();
+4
View File
@@ -2,7 +2,11 @@
#include <QPushButton>
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#else
#include "selfdrive/ui/ui.h"
#endif
const int btn_size = 192;
const int img_size = (btn_size / 4) * 3;
+4
View File
@@ -3,7 +3,11 @@
#include <QPainter>
#include <QPolygonF>
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#else
#include "selfdrive/ui/ui.h"
#endif
class ModelRenderer {
public:
+4
View File
@@ -35,8 +35,12 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
alerts->raise();
setAttribute(Qt::WA_OpaquePaintEvent);
// We handle the connection of the signals on the derived class
#ifndef SUNNYPILOT
QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState);
QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
#endif
}
void OnroadWindow::updateState(const UIState &s) {
+8 -4
View File
@@ -3,20 +3,24 @@
#include "selfdrive/ui/qt/onroad/alerts.h"
#include "selfdrive/ui/qt/onroad/annotated_camera.h"
#ifdef SUNNYPILOT
#define UIState UIStateSP
#endif
class OnroadWindow : public QWidget {
Q_OBJECT
public:
OnroadWindow(QWidget* parent = 0);
private:
protected:
void paintEvent(QPaintEvent *event);
OnroadAlerts *alerts;
AnnotatedCameraWidget *nvg;
QColor bg = bg_colors[STATUS_DISENGAGED];
QHBoxLayout* split;
private slots:
void offroadTransition(bool offroad);
void updateState(const UIState &s);
protected slots:
virtual void offroadTransition(bool offroad);
virtual void updateState(const UIState &s);
};
+5
View File
@@ -2,7 +2,12 @@
#include "common/util.h"
#include "selfdrive/ui/qt/api.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#else
#include "selfdrive/ui/ui.h"
#endif
class RequestRepeater : public HttpRequest {
public:
+4
View File
@@ -110,6 +110,10 @@ void Sidebar::updateState(const UIState &s) {
void Sidebar::paintEvent(QPaintEvent *event) {
QPainter p(this);
paintSidebar(p);
}
void Sidebar::paintSidebar(QPainter &p) {
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::Antialiasing);
+7 -1
View File
@@ -5,9 +5,14 @@
#include <QFrame>
#include <QMap>
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/network/networking.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#else
#include "selfdrive/ui/ui.h"
#endif
typedef QPair<QPair<QString, QString>, QColor> ItemStatus;
Q_DECLARE_METATYPE(ItemStatus);
@@ -35,6 +40,7 @@ protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void drawMetric(QPainter &p, const QPair<QString, QString> &label, QColor c, int y);
virtual void paintSidebar(QPainter &p);
QPixmap home_img, flag_img, settings_img;
bool onroad, flag_pressed, settings_pressed;
+6
View File
@@ -13,6 +13,12 @@
#include "cereal/gen/cpp/car.capnp.h"
#include "common/params.h"
#ifdef SUNNYPILOT
#define RETURN_IF_SUNNYPILOT return;
#else
#define RETURN_IF_SUNNYPILOT // Do nothing
#endif
QString getVersion();
QString getBrand();
QString getUserAgent();
+5
View File
@@ -23,7 +23,12 @@
#endif
#include "msgq/visionipc/visionipc_client.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#else
#include "selfdrive/ui/ui.h"
#endif
const int FRAME_BUFFER_SIZE = 5;
+3
View File
@@ -3,7 +3,10 @@
#include <QPainter>
#include <QStyleOption>
#include "selfdrive/ui/qt/util.h"
AbstractControl::AbstractControl(const QString &title, const QString &desc, const QString &icon, QWidget *parent) : QFrame(parent) {
RETURN_IF_SUNNYPILOT
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setMargin(0);
+3 -3
View File
@@ -40,7 +40,7 @@ class AbstractControl : public QFrame {
Q_OBJECT
public:
void setDescription(const QString &desc) {
virtual void setDescription(const QString &desc) {
if (description) description->setText(desc);
}
@@ -52,7 +52,7 @@ public:
value->setText(val);
}
const QString getDescription() {
virtual const QString getDescription() {
return description->text();
}
@@ -60,7 +60,7 @@ public:
QPixmap icon_pixmap;
public slots:
void showDescription() {
virtual void showDescription() {
description->setVisible(true);
}
+7
View File
@@ -3,7 +3,14 @@
#include <QPushButton>
#include "system/hardware/hw.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
#define ButtonControl ButtonControlSP
#define ToggleControl ToggleControlSP
#else
#include "selfdrive/ui/qt/widgets/controls.h"
#endif
// SSH enable toggle
class SshToggle : public ToggleControl {
+1 -2
View File
@@ -23,14 +23,13 @@ public:
update();
}
bool getEnabled();
void setEnabled(bool value);
virtual void setEnabled(bool value);
protected:
void paintEvent(QPaintEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void enterEvent(QEvent*) override;
private:
QColor circleColor;
QColor green;
bool enabled = true;
+5
View File
@@ -4,7 +4,12 @@
#include <QStackedLayout>
#include <QWidget>
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#define UIState UIStateSP
#else
#include "selfdrive/ui/ui.h"
#endif
class WiFiPromptWidget : public QFrame {
Q_OBJECT
+6 -3
View File
@@ -4,16 +4,19 @@
#include "system/hardware/hw.h"
MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
// We have this constructor so that we can provide custom implementations of the windows. By default (stock_ui) would receive them as nullptr, so they'll be instantiated with stock. Otherwise they'd be SP instances
MainWindow::MainWindow(QWidget *parent, HomeWindow *hw, SettingsWindow *sw) :
QWidget(parent),
homeWindow(hw ? hw : new HomeWindow(this)),
settingsWindow(sw ? sw : new SettingsWindow(this)) {
main_layout = new QStackedLayout(this);
main_layout->setMargin(0);
homeWindow = new HomeWindow(this);
main_layout->addWidget(homeWindow);
QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings);
QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings);
settingsWindow = new SettingsWindow(this);
main_layout->addWidget(settingsWindow);
QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings);
QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() {
+7 -4
View File
@@ -11,15 +11,18 @@ class MainWindow : public QWidget {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
explicit MainWindow(QWidget *parent = 0) : MainWindow(parent, nullptr, nullptr) {}
protected:
explicit MainWindow(QWidget *parent, HomeWindow *hw = nullptr, SettingsWindow *sw = nullptr);
HomeWindow *homeWindow;
SettingsWindow *settingsWindow;
virtual void closeSettings();
private:
bool eventFilter(QObject *obj, QEvent *event) override;
void openSettings(int index = 0, const QString &param = "");
void closeSettings();
QStackedLayout *main_layout;
HomeWindow *homeWindow;
SettingsWindow *settingsWindow;
OnboardingWindow *onboardingWindow;
};
+19
View File
@@ -0,0 +1,19 @@
widgets_src = [
"sunnypilot/qt/widgets/toggle.cc",
"sunnypilot/qt/widgets/controls.cc",
"sunnypilot/qt/widgets/scrollview.cc",
]
qt_src = [
"sunnypilot/ui.cc",
"sunnypilot/qt/sidebar.cc",
"sunnypilot/qt/window.cc",
"sunnypilot/qt/home.cc",
"sunnypilot/qt/offroad/settings/settings.cc",
"sunnypilot/qt/onroad/onroad_home.cc",
]
sp_widgets_src = widgets_src
sp_qt_src = qt_src
Export('sp_widgets_src', 'sp_qt_src')
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/home.h"
HomeWindowSP::HomeWindowSP(QWidget *parent) : HomeWindow(parent) {
}
void HomeWindowSP::updateState(const UIState &s) {
HomeWindow::updateState(s);
}
void HomeWindowSP::mousePressEvent(QMouseEvent *e) {
HomeWindow::mousePressEvent(e);
}
+38
View File
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include <QFrame>
#include <QWidget>
#include "common/params.h"
#include "selfdrive/ui/qt/body.h"
#include "selfdrive/ui/qt/widgets/offroad_alerts.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/qt/home.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/sidebar.h"
#define OnroadWindow OnroadWindowSP
#else
#include "selfdrive/ui/qt/sidebar.h"
#include "selfdrive/ui/qt/onroad/onroad_home.h"
#endif
class HomeWindowSP : public HomeWindow {
Q_OBJECT
public:
explicit HomeWindowSP(QWidget *parent = 0);
protected:
void mousePressEvent(QMouseEvent *e) override;
private slots:
void updateState(const UIState &s) override;
};
@@ -0,0 +1,146 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/qt/network/networking.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
#include "selfdrive/ui/qt/offroad/developer_panel.h"
TogglesPanelSP::TogglesPanelSP(SettingsWindow *parent) : TogglesPanel(parent) {
QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &TogglesPanelSP::updateState);
}
void TogglesPanelSP::updateState(const UIStateSP &s) {
TogglesPanel::updateState(s);
}
SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) {
// setup two main layouts
sidebar_widget = new QWidget;
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
panel_widget = new QStackedWidget();
// setup layout for close button
QVBoxLayout *close_btn_layout = new QVBoxLayout;
close_btn_layout->setContentsMargins(0, 0, 0, 20);
// close button
QPushButton *close_btn = new QPushButton(tr("×"));
close_btn->setStyleSheet(R"(
QPushButton {
font-size: 140px;
padding-bottom: 20px;
border-radius: 76px;
background-color: #292929;
font-weight: 400;
}
QPushButton:pressed {
background-color: #3B3B3B;
}
)");
close_btn->setFixedSize(152, 152);
close_btn_layout->addWidget(close_btn, 0, Qt::AlignLeft);
QObject::connect(close_btn, &QPushButton::clicked, this, &SettingsWindow::closeSettings);
// setup buttons widget
QWidget *buttons_widget = new QWidget;
QVBoxLayout *buttons_layout = new QVBoxLayout(buttons_widget);
buttons_layout->setMargin(0);
buttons_layout->addSpacing(10);
// setup panels
DevicePanel *device = new DevicePanel(this);
QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide);
TogglesPanelSP *toggles = new TogglesPanelSP(this);
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
auto networking = new Networking(this);
QObject::connect(uiState()->prime_state, &PrimeState::changed, networking, &Networking::setPrimeType);
QList<PanelInfo> panels = {
PanelInfo(" " + tr("Device"), device, "../../sunnypilot/selfdrive/assets/offroad/icon_home.svg"),
PanelInfo(" " + tr("Network"), networking, "../assets/offroad/icon_network.png"),
PanelInfo(" " + tr("Toggles"), toggles, "../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"),
PanelInfo(" " + tr("Software"), new SoftwarePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"),
PanelInfo(" " + tr("Developer"), new DeveloperPanel(this), "../assets/offroad/icon_shell.png"),
};
nav_btns = new QButtonGroup(this);
for (auto &[name, panel, icon] : panels) {
QPushButton *btn = new QPushButton(name);
btn->setCheckable(true);
btn->setChecked(nav_btns->buttons().size() == 0);
btn->setIcon(QIcon(QPixmap(icon)));
btn->setIconSize(QSize(70, 70));
btn->setStyleSheet(R"(
QPushButton {
border-radius: 20px;
width: 400px;
height: 98px;
color: #bdbdbd;
border: none;
background: none;
font-size: 50px;
font-weight: 500;
text-align: left;
padding-left: 22px;
}
QPushButton:checked {
background-color: #696868;
color: white;
}
QPushButton:pressed {
color: #ADADAD;
}
)");
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
nav_btns->addButton(btn);
buttons_layout->addWidget(btn, 0, Qt::AlignLeft | Qt::AlignBottom);
const int lr_margin = (name != (" " + tr("Network")) || (name != (" " + tr("sunnypilot")))) ? 50 : 0; // Network and sunnypilot panel handles its own margins
panel->setContentsMargins(lr_margin, 25, lr_margin, 25);
ScrollViewSP *panel_frame = new ScrollViewSP(panel, this);
panel_widget->addWidget(panel_frame);
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
btn->setChecked(true);
panel_widget->setCurrentWidget(w);
});
}
sidebar_layout->setContentsMargins(50, 50, 25, 50);
// main settings layout, sidebar + main panel
QHBoxLayout *main_layout = new QHBoxLayout(this);
// add layout for close button
sidebar_layout->addLayout(close_btn_layout);
// add layout for buttons scrolling
ScrollViewSP *buttons_scrollview = new ScrollViewSP(buttons_widget, this);
sidebar_layout->addWidget(buttons_scrollview);
sidebar_widget->setFixedWidth(500);
main_layout->addWidget(sidebar_widget);
main_layout->addWidget(panel_widget);
setStyleSheet(R"(
* {
color: white;
font-size: 50px;
}
SettingsWindow {
background-color: black;
}
QStackedWidget, ScrollViewSP {
background-color: black;
border-radius: 30px;
}
)");
}
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include <map>
#include <string>
#include "selfdrive/ui/qt/offroad/settings.h"
class SettingsWindowSP : public SettingsWindow {
Q_OBJECT
public:
explicit SettingsWindowSP(QWidget *parent = nullptr);
protected:
struct PanelInfo {
QString name;
QWidget *widget;
QString icon;
PanelInfo(const QString &name, QWidget *widget, const QString &icon) : name(name), widget(widget), icon(icon) {}
};
};
class TogglesPanelSP : public TogglesPanel {
Q_OBJECT
public:
explicit TogglesPanelSP(SettingsWindow *parent);
private slots:
void updateState(const UIStateSP &s);
};
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/onroad/onroad_home.h"
#include "common/swaglog.h"
#include "selfdrive/ui/qt/util.h"
OnroadWindowSP::OnroadWindowSP(QWidget *parent) : OnroadWindow(parent) {
QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &OnroadWindowSP::updateState);
QObject::connect(uiStateSP(), &UIStateSP::offroadTransition, this, &OnroadWindowSP::offroadTransition);
}
void OnroadWindowSP::updateState(const UIStateSP &s) {
if (!s.scene.started) {
return;
}
OnroadWindow::updateState(s);
}
void OnroadWindowSP::mousePressEvent(QMouseEvent *e) {
OnroadWindow::mousePressEvent(e);
}
void OnroadWindowSP::offroadTransition(bool offroad) {
OnroadWindow::offroadTransition(offroad);
}
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/qt/onroad/onroad_home.h"
class OnroadWindowSP : public OnroadWindow {
Q_OBJECT
public:
OnroadWindowSP(QWidget *parent = 0);
private:
void mousePressEvent(QMouseEvent *e) override;
protected slots:
void offroadTransition(bool offroad) override;
void updateState(const UIStateSP &s) override;
};
+27
View File
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/sidebar.h"
#include <cmath>
#include <QMouseEvent>
#include "selfdrive/ui/qt/util.h"
SidebarSP::SidebarSP(QWidget *parent) : Sidebar(parent) {
QObject::disconnect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &SidebarSP::updateState);
}
void SidebarSP::updateState(const UIStateSP &s) {
if (!isVisible()) return;
Sidebar::updateState(s);
}
void SidebarSP::paintSidebar(QPainter &p) {
Sidebar::paintSidebar(p);
}
+27
View File
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include <memory>
#include "selfdrive/ui/qt/sidebar.h"
#include "selfdrive/ui/sunnypilot/ui.h"
class SidebarSP : public Sidebar {
Q_OBJECT
public slots:
void updateState(const UIStateSP &s);
public:
explicit SidebarSP(QWidget *parent = 0);
private:
void paintSidebar(QPainter &p) override;
};
@@ -0,0 +1,254 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
#include <QPainter>
#include <QStyleOption>
QFrame *horizontal_line(QWidget *parent) {
QFrame *line = new QFrame(parent);
line->setFrameShape(QFrame::StyledPanel);
line->setStyleSheet(R"(
border-width: 2px;
border-bottom-style: solid;
border-color: gray;
)");
line->setFixedHeight(10);
return line;
}
// AbstractControlSP
AbstractControlSP::AbstractControlSP(const QString &title, const QString &desc, const QString &icon, QWidget *parent)
: AbstractControl(title, desc, icon, parent) {
main_layout = new QVBoxLayout(this);
main_layout->setMargin(0);
hlayout = new QHBoxLayout;
hlayout->setMargin(0);
hlayout->setSpacing(20);
// title
title_label = new QPushButton(title);
title_label->setFixedHeight(120);
title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;");
hlayout->addWidget(title_label, 1);
// value next to control button
value = new ElidedLabelSP();
value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
value->setStyleSheet("color: #aaaaaa");
hlayout->addWidget(value);
main_layout->addLayout(hlayout);
// description
description = new QLabel(desc);
description->setContentsMargins(40, 20, 40, 20);
description->setStyleSheet("font-size: 40px; color: grey");
description->setWordWrap(true);
description->setVisible(false);
main_layout->addWidget(description);
connect(title_label, &QPushButton::clicked, [=]() {
if (!description->isVisible()) {
emit showDescriptionEvent();
}
if (!description->text().isEmpty()) {
description->setVisible(!description->isVisible());
}
});
main_layout->addStretch();
}
void AbstractControlSP::hideEvent(QHideEvent *e) {
if (description != nullptr) {
description->hide();
}
}
AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, const QString &desc, const QString &icon, QWidget *parent)
: AbstractControlSP(title, desc, icon, parent) {
if (title_label != nullptr) {
delete title_label;
title_label = nullptr;
}
if (description != nullptr) {
delete description;
description = nullptr;
}
if (value != nullptr) {
ReplaceWidget(value, new QWidget());
value = nullptr;
}
QLayoutItem *item;
while ((item = main_layout->takeAt(0)) != nullptr) {
if (item->widget()) {
delete item->widget();
}
delete item;
}
main_layout->setMargin(0);
hlayout = new QHBoxLayout;
hlayout->setMargin(0);
hlayout->setSpacing(0);
// title
if (!title.isEmpty()) {
title_label = new QPushButton(title);
title_label->setFixedHeight(120);
title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 20 0 0 0");
main_layout->addWidget(title_label, 1);
connect(title_label, &QPushButton::clicked, [=]() {
if (!description->isVisible()) {
emit showDescriptionEvent();
}
if (!description->text().isEmpty()) {
bool isVisible = !description->isVisible();
description->setVisible(isVisible);
if (isVisible && spacingItem) {
main_layout->removeItem(spacingItem);
delete spacingItem;
spacingItem = nullptr;
} else if (!isVisible && spacingItem == nullptr) {
spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed);
main_layout->insertItem(main_layout->indexOf(description), spacingItem);
}
}
});
} else {
main_layout->addSpacing(20);
}
main_layout->addLayout(hlayout);
if (!desc.isEmpty() && spacingItem == nullptr) {
spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed);
main_layout->insertItem(main_layout->count(), spacingItem);
}
// description
description = new QLabel(desc);
description->setContentsMargins(0, 20, 40, 20);
description->setStyleSheet("font-size: 40px; color: grey");
description->setWordWrap(true);
description->setVisible(false);
main_layout->addWidget(description);
main_layout->addStretch();
}
void AbstractControlSP_SELECTOR::hideEvent(QHideEvent *e) {
if (description != nullptr) {
description->hide();
}
if (spacingItem == nullptr) {
spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed);
main_layout->insertItem(main_layout->indexOf(description), spacingItem);
}
}
// controls
ButtonControlSP::ButtonControlSP(const QString &title, const QString &text, const QString &desc, QWidget *parent)
: AbstractControlSP(title, desc, "", parent) {
btn.setText(text);
btn.setStyleSheet(R"(
QPushButton {
padding: 0;
border-radius: 50px;
font-size: 35px;
font-weight: 500;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)");
btn.setFixedSize(250, 100);
QObject::connect(&btn, &QPushButton::clicked, this, &ButtonControlSP::clicked);
hlayout->addWidget(&btn);
}
// ElidedLabelSP
ElidedLabelSP::ElidedLabelSP(QWidget *parent) : ElidedLabelSP({}, parent) {
}
ElidedLabelSP::ElidedLabelSP(const QString &text, QWidget *parent) : QLabel(text.trimmed(), parent) {
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
setMinimumWidth(1);
}
void ElidedLabelSP::resizeEvent(QResizeEvent *event) {
QLabel::resizeEvent(event);
lastText_ = elidedText_ = "";
}
void ElidedLabelSP::paintEvent(QPaintEvent *event) {
const QString curText = text();
if (curText != lastText_) {
elidedText_ = fontMetrics().elidedText(curText, Qt::ElideRight, contentsRect().width());
lastText_ = curText;
}
QPainter painter(this);
drawFrame(&painter);
QStyleOption opt;
opt.initFrom(this);
style()->drawItemText(&painter, contentsRect(), alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole());
}
// ParamControlSP
ParamControlSP::ParamControlSP(const QString &param, const QString &title, const QString &desc, const QString &icon, QWidget *parent)
: ToggleControlSP(title, desc, icon, false, parent) {
key = param.toStdString();
QObject::connect(this, &ParamControlSP::toggleFlipped, this, &ParamControlSP::toggleClicked);
hlayout->removeWidget(&toggle);
hlayout->insertWidget(0, &toggle);
hlayout->removeWidget(this->icon_label);
hlayout->insertWidget(1, this->icon_label);
}
void ParamControlSP::toggleClicked(bool state) {
auto do_confirm = [this]() {
QString content("<body><h2 style=\"text-align: center;\">" + title_label->text() + "</h2><br>"
"<p style=\"text-align: center; margin: 0 128px; font-size: 50px;\">" + getDescription() + "</p></body>");
return ConfirmationDialog(content, tr("Enable"), tr("Cancel"), true, this).exec();
};
bool confirmed = store_confirm && params.getBool(key + "Confirmed");
if (!confirm || confirmed || !state || do_confirm()) {
if (store_confirm && state) params.putBool(key + "Confirmed", true);
params.putBool(key, state);
setIcon(state);
} else {
toggle.togglePosition();
}
}
@@ -0,0 +1,588 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include <map>
#include <optional>
#include <string>
#include <vector>
#include "common/params.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/toggle.h"
QFrame *horizontal_line(QWidget *parent = nullptr);
inline void ReplaceWidget(QWidget *old_widget, QWidget *new_widget) {
if (old_widget && old_widget->parentWidget() && old_widget->parentWidget()->layout()) {
old_widget->parentWidget()->layout()->replaceWidget(old_widget, new_widget);
old_widget->hide();
old_widget->deleteLater();
}
}
class ElidedLabelSP : public QLabel {
Q_OBJECT
public:
explicit ElidedLabelSP(QWidget *parent = 0);
explicit ElidedLabelSP(const QString &text, QWidget *parent = 0);
void setColor(const QString &color) {
setStyleSheet("QLabel { color : " + color + "; }");
}
signals:
void clicked();
protected:
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override {
if (rect().contains(event->pos())) {
emit clicked();
}
}
QString lastText_, elidedText_;
};
class AbstractControlSP : public AbstractControl {
Q_OBJECT
public:
void setDescription(const QString &desc) override {
if (description) description->setText(desc);
}
void setValue(const QString &val, std::optional<QString> color = std::nullopt) {
value->setText(val);
if (color.has_value()) {
value->setColor(color.value());
}
}
const QString getDescription() override {
return description->text();
}
void hideDescription() {
description->setVisible(false);
}
public slots:
void showDescription() override {
description->setVisible(true);
}
protected:
AbstractControlSP(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
void hideEvent(QHideEvent *e) override;
QVBoxLayout *main_layout;
ElidedLabelSP *value;
QLabel *description = nullptr;
};
// AbstractControlSP_SELECTOR
class AbstractControlSP_SELECTOR : public AbstractControlSP {
Q_OBJECT
protected:
AbstractControlSP_SELECTOR(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
void hideEvent(QHideEvent *e) override;
private:
QSpacerItem *spacingItem = nullptr;
};
// widget to display a value
class LabelControlSP : public AbstractControlSP {
Q_OBJECT
public:
LabelControlSP(const QString &title, const QString &text = "", const QString &desc = "", QWidget *parent = nullptr) : AbstractControlSP(title, desc, "", parent) {
label.setText(text);
label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
hlayout->addWidget(&label);
}
void setText(const QString &text) { label.setText(text); }
private:
ElidedLabelSP label;
};
// widget for a button with a label
class ButtonControlSP : public AbstractControlSP {
Q_OBJECT
public:
ButtonControlSP(const QString &title, const QString &text, const QString &desc = "", QWidget *parent = nullptr);
inline void setText(const QString &text) { btn.setText(text); }
inline QString text() const { return btn.text(); }
inline void click() { btn.click(); }
signals:
void clicked();
public slots:
void setEnabled(bool enabled) { btn.setEnabled(enabled); }
private:
QPushButton btn;
};
class ToggleControlSP : public AbstractControlSP {
Q_OBJECT
public:
ToggleControlSP(const QString &title, const QString &desc = "", const QString &icon = "", const bool state = false, QWidget *parent = nullptr) : AbstractControlSP(title, desc, icon, parent) {
// space between toggle and title
icon_label = new QLabel(this);
hlayout->addWidget(icon_label);
toggle.setFixedSize(150, 100);
if (state) {
toggle.togglePosition();
}
hlayout->insertWidget(0, &toggle);
hlayout->insertWidget(1, this->icon_label);
QObject::connect(&toggle, &ToggleSP::stateChanged, this, &ToggleControlSP::toggleFlipped);
}
void setEnabled(bool enabled) {
toggle.setEnabled(enabled);
toggle.update();
}
signals:
void toggleFlipped(bool state);
protected:
ToggleSP toggle;
};
// widget to toggle params
class ParamControlSP : public ToggleControlSP {
Q_OBJECT
public:
ParamControlSP(const QString &param, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr);
void setConfirmation(bool _confirm, bool _store_confirm) {
confirm = _confirm;
store_confirm = _store_confirm;
}
void setActiveIcon(const QString &icon) {
active_icon_pixmap = QPixmap(icon).scaledToWidth(80, Qt::SmoothTransformation);
}
void refresh() {
bool state = params.getBool(key);
if (state != toggle.on) {
toggle.togglePosition();
setIcon(state);
}
}
void showEvent(QShowEvent *event) override {
refresh();
}
bool isToggled() { return params.getBool(key); }
private:
void toggleClicked(bool state);
void setIcon(bool state) {
if (state && !active_icon_pixmap.isNull()) {
icon_label->setPixmap(active_icon_pixmap);
} else if (!icon_pixmap.isNull()) {
icon_label->setPixmap(icon_pixmap);
}
}
std::string key;
Params params;
QPixmap active_icon_pixmap;
bool confirm = false;
bool store_confirm = false;
};
class ButtonParamControlSP : public AbstractControlSP_SELECTOR {
Q_OBJECT
public:
ButtonParamControlSP(const QString &param, const QString &title, const QString &desc, const QString &icon,
const std::vector<QString> &button_texts, const int minimum_button_width = 300) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts) {
const QString style = R"(
QPushButton {
border-radius: 20px;
font-size: 50px;
font-weight: 450;
height:150px;
padding: 0 25 0 25;
color: #FFFFFF;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
QPushButton:checked:enabled {
background-color: #696868;
}
QPushButton:disabled {
color: #33FFFFFF;
}
QPushButton:checked:disabled {
background-color: #121212;
color: #33FFFFFF;
}
)";
key = param.toStdString();
int value = atoi(params.get(key).c_str());
button_group = new QButtonGroup(this);
button_group->setExclusive(true);
for (int i = 0; i < button_texts.size(); i++) {
QPushButton *button = new QPushButton(button_texts[i], this);
button->setCheckable(true);
button->setChecked(i == value);
button->setStyleSheet(style);
button->setMinimumWidth(minimum_button_width);
if (i == 0) hlayout->addSpacing(2);
hlayout->addWidget(button);
button_group->addButton(button, i);
}
hlayout->setAlignment(Qt::AlignLeft);
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
params.put(key, std::to_string(id));
emit buttonToggled(id);
});
}
void setEnabled(bool enable) {
for (auto btn: button_group->buttons()) {
btn->setEnabled(enable);
}
button_group_enabled = enable;
update();
}
void setCheckedButton(int id) {
button_group->button(id)->setChecked(true);
}
void refresh() {
int value = atoi(params.get(key).c_str());
if (value >= button_texts.size()) {
value = button_texts.size() - 1;
}
if (value < 0) {
value = 0;
}
button_group->button(value)->setChecked(true);
}
void showEvent(QShowEvent *event) override {
refresh();
}
void setButton(QString param) {
key = param.toStdString();
int value = atoi(params.get(key).c_str());
for (int i = 0; i < button_group->buttons().size(); i++) {
button_group->buttons()[i]->setChecked(i == value);
}
}
void setDisabledSelectedButton(std::string val) {
int value = atoi(val.c_str());
for (int i = 0; i < button_group->buttons().size(); i++) {
button_group->buttons()[i]->setEnabled(i != value);
}
}
protected:
void paintEvent(QPaintEvent *event) override {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
// Calculate the total width and height for the background rectangle
int w = 0;
int h = 150;
for (int i = 0; i < hlayout->count(); ++i) {
QPushButton *button = qobject_cast<QPushButton *>(hlayout->itemAt(i)->widget());
if (button) {
w += button->width();
}
}
// Draw the rectangle
#ifdef __APPLE__
QRect rect(0 + 2, h - 16, w, h);
#else
QRect rect(0 + 2, h - 24, w, h);
#endif
p.setPen(QPen(QColor(button_group_enabled ? "#696868" : "#121212"), 3));
p.drawRoundedRect(rect, 20, 20);
}
signals:
void buttonToggled(int btn_id);
private:
std::string key;
Params params;
QButtonGroup *button_group;
std::vector<QString> button_texts;
bool button_group_enabled = true;
};
class ListWidgetSP : public QWidget {
Q_OBJECT
public:
explicit ListWidgetSP(QWidget *parent = 0, const bool split_line = true) : QWidget(parent), _split_line(split_line), outer_layout(this) {
outer_layout.setMargin(0);
outer_layout.setSpacing(0);
outer_layout.addLayout(&inner_layout);
inner_layout.setMargin(0);
inner_layout.setSpacing(25); // default spacing is 25
outer_layout.addStretch();
}
inline void addItem(QWidget *w) { inner_layout.addWidget(w); }
inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); }
inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); }
inline void AddWidgetAt(const int index, QWidget *new_widget) { inner_layout.insertWidget(index, new_widget); }
inline void RemoveWidgetAt(const int index) {
if (QLayoutItem *item; (item = inner_layout.takeAt(index)) != nullptr) {
if (item->widget()) delete item->widget();
delete item;
}
}
inline void ReplaceOrAddWidget(QWidget *old_widget, QWidget *new_widget) {
if (const int index = inner_layout.indexOf(old_widget); index != -1) {
RemoveWidgetAt(index);
AddWidgetAt(index, new_widget);
} else {
addItem(new_widget);
}
}
private:
void paintEvent(QPaintEvent *) override {
QPainter p(this);
p.setPen(Qt::gray);
for (int i = 0; i < inner_layout.count() - 1; ++i) {
QWidget *widget = inner_layout.itemAt(i)->widget();
if ((widget == nullptr || widget->isVisible()) && _split_line) {
QRect r = inner_layout.itemAt(i)->geometry();
int bottom = r.bottom() + inner_layout.spacing() / 2;
p.drawLine(r.left(), bottom, r.right(), bottom);
}
}
}
QVBoxLayout outer_layout;
QVBoxLayout inner_layout;
bool _split_line;
};
// convenience class for wrapping layouts
class LayoutWidgetSP : public QWidget {
Q_OBJECT
public:
LayoutWidgetSP(QLayout *l, QWidget *parent = nullptr) : QWidget(parent) {
setLayout(l);
}
};
class OptionControlSP : public AbstractControlSP_SELECTOR {
Q_OBJECT
private:
struct MinMaxValue {
int min_value;
int max_value;
};
public:
OptionControlSP(const QString &param, const QString &title, const QString &desc, const QString &icon,
const MinMaxValue &range, const int per_value_change = 1) : _title(title), AbstractControlSP_SELECTOR(title, desc, icon) {
const QString style = R"(
QPushButton {
border-radius: 20px;
font-size: 60px;
font-weight: 500;
width: 150px;
height: 150px;
padding: -3 25 3 25;
color: #FFFFFF;
font-weight: bold;
}
QPushButton:pressed {
color: #5C5C5C;
}
QPushButton:disabled {
color: #5C5C5C;
}
)";
label.setStyleSheet(label_enabled_style);
label.setFixedWidth(300);
label.setAlignment(Qt::AlignCenter);
const std::vector<QString> button_texts{"", ""};
key = param.toStdString();
value = atoi(params.get(key).c_str());
button_group = new QButtonGroup(this);
button_group->setExclusive(true);
for (int i = 0; i < button_texts.size(); i++) {
QPushButton *button = new QPushButton(button_texts[i], this);
button->setStyleSheet(style + ((i == 0) ? "QPushButton { text-align: left; }" :
"QPushButton { text-align: right; }"));
hlayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter);
if (i == 0) {
hlayout->addWidget(&label, 0, Qt::AlignCenter);
}
button_group->addButton(button, i);
QObject::connect(button, &QPushButton::clicked, [=]() {
int change_value = (i == 0) ? -per_value_change : per_value_change;
key = param.toStdString();
value = atoi(params.get(key).c_str());
value += change_value;
value = std::clamp(value, range.min_value, range.max_value);
params.put(key, QString::number(value).toStdString());
button_group->button(0)->setEnabled(!(value <= range.min_value));
button_group->button(1)->setEnabled(!(value >= range.max_value));
updateLabels();
if (request_update) {
emit updateOtherToggles();
}
});
}
hlayout->setAlignment(Qt::AlignLeft);
}
void setUpdateOtherToggles(bool _update) {
request_update = _update;
}
inline void setLabel(const QString &text) { label.setText(text); }
void setEnabled(bool enabled) {
for (auto btn: button_group->buttons()) {
btn->setEnabled(enabled);
}
label.setEnabled(enabled);
label.setStyleSheet(enabled ? label_enabled_style : label_disabled_style);
button_enabled = enabled;
update();
}
protected:
void paintEvent(QPaintEvent *event) override {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
// Calculate the total width and height for the background rectangle
int w = 0;
int h = 150;
for (int i = 0; i < hlayout->count(); ++i) {
QWidget *widget = qobject_cast<QWidget *>(hlayout->itemAt(i)->widget());
if (widget) {
w += widget->width();
}
}
// Draw the rectangle
QRect rect(0, !_title.isEmpty() ? (h - 24) : 20, w, h);
p.setBrush(QColor(button_enabled ? "#b24a4a4a" : "#121212")); // Background color
p.setPen(QPen(Qt::NoPen));
p.drawRoundedRect(rect, 20, 20);
}
signals:
void updateLabels();
void updateOtherToggles();
private:
std::string key;
int value;
QButtonGroup *button_group;
QLabel label;
Params params;
std::map<QString, QString> option_label = {};
bool request_update = false;
QString _title = "";
const QString label_enabled_style = "font-size: 50px; font-weight: 450; color: #FFFFFF;";
const QString label_disabled_style = "font-size: 50px; font-weight: 450; color: #5C5C5C;";
bool button_enabled = true;
};
class SubPanelButton : public QPushButton {
Q_OBJECT
public:
SubPanelButton(const QString &text, const int minimum_button_width = 800, QWidget *parent = nullptr) : QPushButton(text, parent) {
const QString buttonStyle = R"(
QPushButton {
border-radius: 20px;
font-size: 50px;
font-weight: 450;
height: 150px;
padding: 0 25px 0 25px;
color: #FFFFFF;
}
QPushButton:enabled {
background-color: #393939;
}
QPushButton:pressed {
background-color: #4A4A4A;
}
QPushButton:disabled {
background-color: #121212;
color: #5C5C5C;
}
)";
setStyleSheet(buttonStyle);
setFixedWidth(minimum_button_width);
}
};
class PanelBackButton : public QPushButton {
Q_OBJECT
public:
PanelBackButton(const QString &label = "Back", QWidget *parent = nullptr) : QPushButton(label, parent) {
setObjectName("back_btn");
setFixedSize(400, 100);
}
};
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
#include <QScrollBar>
void ScrollViewSP::setLastScrollPosition() {
lastScrollPosition = verticalScrollBar()->value();
}
void ScrollViewSP::restoreScrollPosition() {
verticalScrollBar()->setValue(lastScrollPosition);
}
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/qt/widgets/scrollview.h"
class ScrollViewSP : public ScrollView {
Q_OBJECT
public:
explicit ScrollViewSP(QWidget *w = nullptr, QWidget *parent = nullptr) : ScrollView(w, parent) {}
public slots:
void setLastScrollPosition();
void restoreScrollPosition();
private:
int lastScrollPosition = 0;
};
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/widgets/toggle.h"
#include <QPainter>
ToggleSP::ToggleSP(QWidget *parent) : Toggle(parent) {
_height_rect = 80;
}
void ToggleSP::paintEvent(QPaintEvent *e) {
this->setFixedHeight(100);
QPainter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::Antialiasing, true);
// Draw toggle background
enabled ? green.setRgb(0x1e79e8) : green.setRgb(0x125db8);
p.setBrush(on ? green : QColor(0x292929));
p.drawRoundedRect(QRect(0, 10, width(), _height_rect), _height_rect / 2, _height_rect / 2);
// Draw toggle circle
p.setBrush(circleColor);
p.drawEllipse(QRectF(_x_circle - _radius + 6, 16, 68, 68));
}
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/qt/widgets/toggle.h"
class ToggleSP : public Toggle {
Q_OBJECT
public:
explicit ToggleSP(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *) override;
};
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/window.h"
MainWindowSP::MainWindowSP(QWidget *parent)
: MainWindow(parent, new HomeWindowSP(parent), new SettingsWindowSP(parent)) {
homeWindow = dynamic_cast<HomeWindowSP *>(MainWindow::homeWindow);
settingsWindow = dynamic_cast<SettingsWindowSP *>(MainWindow::settingsWindow);
}
void MainWindowSP::closeSettings() {
MainWindow::closeSettings();
}
+24
View File
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/qt/window.h"
#include "selfdrive/ui/sunnypilot/qt/home.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
class MainWindowSP : public MainWindow {
Q_OBJECT
public:
explicit MainWindowSP(QWidget *parent = 0);
private:
HomeWindowSP *homeWindow;
SettingsWindowSP *settingsWindow;
void closeSettings() override;
};
+53
View File
@@ -0,0 +1,53 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/ui.h"
#include "common/watchdog.h"
void UIStateSP::updateStatus() {
UIState::updateStatus();
}
UIStateSP::UIStateSP(QObject *parent) : UIState(parent) {
sm = std::make_unique<SubMaster>(std::vector<const char*>{
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
"pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2",
"wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan",
});
// update timer
timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &UIStateSP::update);
timer->start(1000 / UI_FREQ);
}
// This method overrides completely the update method from the parent class intentionally.
void UIStateSP::update() {
update_sockets(this);
update_state(this);
updateStatus();
if (sm->frame % UI_FREQ == 0) {
watchdog_kick(nanos_since_boot());
}
emit uiUpdate(*this);
}
DeviceSP::DeviceSP(QObject *parent) : Device(parent) {
QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &DeviceSP::update);
}
UIStateSP *uiStateSP() {
static UIStateSP ui_state;
return &ui_state;
}
DeviceSP *deviceSP() {
static DeviceSP _device;
return &_device;
}
+38
View File
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/ui.h"
class UIStateSP : public UIState {
Q_OBJECT
public:
UIStateSP(QObject *parent = 0);
void updateStatus() override;
signals:
void uiUpdate(const UIStateSP &s);
private slots:
void update() override;
};
UIStateSP *uiStateSP();
inline UIStateSP *uiState() { return uiStateSP(); };
// device management class
class DeviceSP : public Device {
Q_OBJECT
public:
DeviceSP(QObject *parent = 0);
};
DeviceSP *deviceSP();
inline DeviceSP *device() { return deviceSP(); }
+2 -2
View File
@@ -160,7 +160,7 @@ def setup_offroad_alert(click, pm: PubMaster):
# Toggle between settings and home to refresh the offroad alert widget
setup_settings_device(click, pm)
click(240, 216)
click(100, 100)
def setup_update_available(click, pm: PubMaster):
Params().put_bool("UpdateAvailable", True)
@@ -170,7 +170,7 @@ def setup_update_available(click, pm: PubMaster):
Params().put("UpdaterNewReleaseNotes", release_notes + "\n")
setup_settings_device(click, pm)
click(240, 216)
click(100, 100)
def setup_pair_device(click, pm: PubMaster):
click(1950, 435)
+42
View File
@@ -480,6 +480,17 @@
<translation>إلغاء</translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Enable</source>
<translation>تمكين</translation>
</message>
<message>
<source>Cancel</source>
<translation>إلغاء</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -642,6 +653,37 @@ This may take up to a minute.</source>
<translation>المطور</translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation>الجهاز</translation>
</message>
<message>
<source>Network</source>
<translation>الشبكة</translation>
</message>
<message>
<source>Toggles</source>
<translation>المثبتتات</translation>
</message>
<message>
<source>Software</source>
<translation>البرنامج</translation>
</message>
<message>
<source>Developer</source>
<translation>المطور</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -475,6 +475,17 @@
<translation>Aktivieren</translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Cancel</source>
<translation>Abbrechen</translation>
</message>
<message>
<source>Enable</source>
<translation>Aktivieren</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -624,6 +635,37 @@ This may take up to a minute.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>x</translation>
</message>
<message>
<source>Device</source>
<translation>Gerät</translation>
</message>
<message>
<source>Network</source>
<translation>Netzwerk</translation>
</message>
<message>
<source>Toggles</source>
<translation>Schalter</translation>
</message>
<message>
<source>Software</source>
<translation>Software</translation>
</message>
<message>
<source>Developer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -476,6 +476,17 @@
<translation>Cancelar</translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Enable</source>
<translation>Activar</translation>
</message>
<message>
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -626,6 +637,37 @@ Esto puede tardar un minuto.</translation>
<translation>Desarrollador</translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation>Dispositivo</translation>
</message>
<message>
<source>Network</source>
<translation>Red</translation>
</message>
<message>
<source>Toggles</source>
<translation>Ajustes</translation>
</message>
<message>
<source>Software</source>
<translation>Software</translation>
</message>
<message>
<source>Developer</source>
<translation>Desarrollador</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -476,6 +476,17 @@
<translation>Annuler</translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Enable</source>
<translation>Activer</translation>
</message>
<message>
<source>Cancel</source>
<translation>Annuler</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -626,6 +637,37 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation>Appareil</translation>
</message>
<message>
<source>Network</source>
<translation>Réseau</translation>
</message>
<message>
<source>Toggles</source>
<translation>Options</translation>
</message>
<message>
<source>Software</source>
<translation>Logiciel</translation>
</message>
<message>
<source>Developer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -474,6 +474,17 @@
<translation></translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<source>Enable</source>
<translation></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -620,6 +631,37 @@ This may take up to a minute.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation></translation>
</message>
<message>
<source>Network</source>
<translation></translation>
</message>
<message>
<source>Toggles</source>
<translation></translation>
</message>
<message>
<source>Software</source>
<translation>ソ</translation>
</message>
<message>
<source>Developer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -475,6 +475,17 @@
<translation></translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<source>Enable</source>
<translation></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -622,6 +633,37 @@ This may take up to a minute.</source>
<translation></translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation></translation>
</message>
<message>
<source>Network</source>
<translation></translation>
</message>
<message>
<source>Toggles</source>
<translation></translation>
</message>
<message>
<source>Software</source>
<translation></translation>
</message>
<message>
<source>Developer</source>
<translation></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -476,6 +476,17 @@
<translation>Ativar</translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
<message>
<source>Enable</source>
<translation>Ativar</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -626,6 +637,37 @@ Isso pode levar até um minuto.</translation>
<translation>Desenvdor</translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation>Dispositivo</translation>
</message>
<message>
<source>Network</source>
<translation>Rede</translation>
</message>
<message>
<source>Toggles</source>
<translation>Ajustes</translation>
</message>
<message>
<source>Software</source>
<translation>Software</translation>
</message>
<message>
<source>Developer</source>
<translation>Desenvdor</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -475,6 +475,17 @@
<translation></translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Enable</source>
<translation></translation>
</message>
<message>
<source>Cancel</source>
<translation></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -622,6 +633,37 @@ This may take up to a minute.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation></translation>
</message>
<message>
<source>Network</source>
<translation></translation>
</message>
<message>
<source>Toggles</source>
<translation></translation>
</message>
<message>
<source>Software</source>
<translation></translation>
</message>
<message>
<source>Developer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -474,6 +474,17 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Enable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -620,6 +631,37 @@ This may take up to a minute.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>x</translation>
</message>
<message>
<source>Device</source>
<translation>Cihaz</translation>
</message>
<message>
<source>Network</source>
<translation></translation>
</message>
<message>
<source>Toggles</source>
<translation>Değiştirme</translation>
</message>
<message>
<source>Software</source>
<translation>Yazılım</translation>
</message>
<message>
<source>Developer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -475,6 +475,17 @@
<translation></translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<source>Enable</source>
<translation></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -622,6 +633,37 @@ This may take up to a minute.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation></translation>
</message>
<message>
<source>Network</source>
<translation></translation>
</message>
<message>
<source>Toggles</source>
<translation></translation>
</message>
<message>
<source>Software</source>
<translation></translation>
</message>
<message>
<source>Developer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+42
View File
@@ -475,6 +475,17 @@
<translation></translation>
</message>
</context>
<context>
<name>ParamControlSP</name>
<message>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<source>Enable</source>
<translation></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
@@ -622,6 +633,37 @@ This may take up to a minute.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsWindowSP</name>
<message>
<source>×</source>
<translation>×</translation>
</message>
<message>
<source>Device</source>
<translation></translation>
</message>
<message>
<source>Network</source>
<translation></translation>
</message>
<message>
<source>Toggles</source>
<translation></translation>
</message>
<message>
<source>Software</source>
<translation></translation>
</message>
<message>
<source>Developer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
</message>
</context>
<context>
<name>Setup</name>
<message>
+9 -3
View File
@@ -9,16 +9,17 @@
#include "common/swaglog.h"
#include "common/util.h"
#include "common/watchdog.h"
#include "qt/util.h"
#include "system/hardware/hw.h"
#define BACKLIGHT_DT 0.05
#define BACKLIGHT_TS 10.00
static void update_sockets(UIState *s) {
void update_sockets(UIState *s) {
s->sm->update(0);
}
static void update_state(UIState *s) {
void update_state(UIState *s) {
SubMaster &sm = *(s->sm);
UIScene &scene = s->scene;
@@ -98,6 +99,7 @@ UIState::UIState(QObject *parent) : QObject(parent) {
prime_state = new PrimeState(this);
language = QString::fromStdString(Params().get("LanguageSetting"));
RETURN_IF_SUNNYPILOT
// update timer
timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &UIState::update);
@@ -105,6 +107,7 @@ UIState::UIState(QObject *parent) : QObject(parent) {
}
void UIState::update() {
RETURN_IF_SUNNYPILOT
update_sockets(this);
update_state(this);
updateStatus();
@@ -118,8 +121,9 @@ void UIState::update() {
Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) {
setAwake(true);
resetInteractiveTimeout();
#ifndef SUNNYPILOT
QObject::connect(uiState(), &UIState::uiUpdate, this, &Device::update);
#endif
}
void Device::update(const UIState &s) {
@@ -185,6 +189,7 @@ void Device::updateWakefulness(const UIState &s) {
setAwake(s.scene.ignition || interactive_timeout > 0);
}
#ifndef SUNNYPILOT
UIState *uiState() {
static UIState ui_state;
return &ui_state;
@@ -194,3 +199,4 @@ Device *device() {
static Device _device;
return &_device;
}
#endif
+14 -5
View File
@@ -67,7 +67,7 @@ class UIState : public QObject {
public:
UIState(QObject* parent = 0);
void updateStatus();
virtual void updateStatus();
inline bool engaged() const {
return scene.started && (*sm)["selfdriveState"].getSelfdriveState().getEnabled();
}
@@ -82,15 +82,19 @@ signals:
void uiUpdate(const UIState &s);
void offroadTransition(bool offroad);
private slots:
void update();
protected slots:
virtual void update();
protected:
QTimer *timer;
private:
QTimer *timer;
bool started_prev = false;
};
#ifndef SUNNYPILOT
UIState *uiState();
#endif
// device management class
class Device : public QObject {
@@ -103,7 +107,7 @@ public:
offroad_brightness = std::clamp(brightness, 0, 100);
}
private:
protected:
bool awake = false;
int interactive_timeout = 0;
bool ignition_on = false;
@@ -126,5 +130,10 @@ public slots:
void update(const UIState &s);
};
#ifndef SUNNYPILOT
Device *device();
#endif
void ui_update_params(UIState *s);
void update_state(UIState *s);
void update_sockets(UIState *s);
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1673f8d46251a05787b60346193852991739345506dc7e9b106dfb370d3611ed
size 489
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:01fd38c0d5b05c3ce4ed07bfe4cd47a3ea61f68f24e6cc3058190d90d8200f9b
size 4785
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11336d2f65100bc0dc2a53e09efae41ae85817b27e1a5bf134cc995e631c5b52
size 4073