ClearPilot menu: General/Network/Dashcam/Debug panels
Replace Home panel with General: status, reset calibration, shutdown timer, reboot/soft reboot/power off. Add Network tab (stock networking minus APN). Remove system settings access — ClearPilot menu is now the only settings interface. build_only.sh now auto-chowns before build. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,9 @@
|
|||||||
|
|
||||||
BASEDIR="/data/openpilot"
|
BASEDIR="/data/openpilot"
|
||||||
|
|
||||||
|
# Fix ownership — we edit as root, openpilot builds/runs as comma
|
||||||
|
sudo chown -R comma:comma "$BASEDIR"
|
||||||
|
|
||||||
# Kill stale error displays and any running manager/launch/managed processes
|
# Kill stale error displays and any running manager/launch/managed processes
|
||||||
pkill -9 -f "selfdrive/ui/text" 2>/dev/null
|
pkill -9 -f "selfdrive/ui/text" 2>/dev/null
|
||||||
pkill -9 -f 'launch_openpilot.sh' 2>/dev/null
|
pkill -9 -f 'launch_openpilot.sh' 2>/dev/null
|
||||||
|
|||||||
@@ -6,8 +6,14 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "common/swaglog.h"
|
#include "common/swaglog.h"
|
||||||
|
#include "common/params.h"
|
||||||
|
#include "system/hardware/hw.h"
|
||||||
#include "selfdrive/ui/qt/util.h"
|
#include "selfdrive/ui/qt/util.h"
|
||||||
|
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||||
|
#include "selfdrive/ui/qt/widgets/input.h"
|
||||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||||
|
#include "selfdrive/ui/qt/network/networking.h"
|
||||||
|
#include "cereal/messaging/messaging.h"
|
||||||
|
|
||||||
// HomeWindow: the container for the offroad and onroad UIs
|
// HomeWindow: the container for the offroad and onroad UIs
|
||||||
|
|
||||||
@@ -46,7 +52,7 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
|
|||||||
// CLEARPILOT
|
// CLEARPILOT
|
||||||
ready = new ReadyWindow(this);
|
ready = new ReadyWindow(this);
|
||||||
slayout->addWidget(ready);
|
slayout->addWidget(ready);
|
||||||
|
|
||||||
driver_view = new DriverViewWindow(this);
|
driver_view = new DriverViewWindow(this);
|
||||||
connect(driver_view, &DriverViewWindow::done, [=] {
|
connect(driver_view, &DriverViewWindow::done, [=] {
|
||||||
showDriverView(false);
|
showDriverView(false);
|
||||||
@@ -160,6 +166,27 @@ static const char *clpSidebarBtnStyle = R"(
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
static const char *clpActionBtnStyle = R"(
|
||||||
|
QPushButton {
|
||||||
|
background-color: #393939;
|
||||||
|
color: white;
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: 50px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
QPushButton:pressed { background-color: #4a4a4a; }
|
||||||
|
)";
|
||||||
|
|
||||||
|
// Shutdown timer: param value -> display label
|
||||||
|
static QString shutdownLabel(int val) {
|
||||||
|
if (val == 0) return "5 mins";
|
||||||
|
if (val <= 3) return QString::number(val * 15) + " mins";
|
||||||
|
int hours = val - 3;
|
||||||
|
return QString::number(hours) + (hours == 1 ? " hour" : " hours");
|
||||||
|
}
|
||||||
|
|
||||||
ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
||||||
// Sidebar
|
// Sidebar
|
||||||
QWidget *sidebar_widget = new QWidget;
|
QWidget *sidebar_widget = new QWidget;
|
||||||
@@ -188,39 +215,137 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
|||||||
// Panel content area
|
// Panel content area
|
||||||
panel_widget = new QStackedWidget();
|
panel_widget = new QStackedWidget();
|
||||||
|
|
||||||
// Define panels: sidebar label -> content widget
|
// ── General panel ──
|
||||||
// Home panel: buttons for Status and System Settings
|
ListWidget *general_panel = new ListWidget(this);
|
||||||
QWidget *home_panel = new QWidget(this);
|
general_panel->setContentsMargins(50, 25, 50, 25);
|
||||||
QVBoxLayout *home_layout = new QVBoxLayout(home_panel);
|
|
||||||
home_layout->setContentsMargins(50, 25, 50, 25);
|
|
||||||
home_layout->setSpacing(20);
|
|
||||||
|
|
||||||
|
// Status button
|
||||||
QPushButton *status_btn = new QPushButton("Status");
|
QPushButton *status_btn = new QPushButton("Status");
|
||||||
status_btn->setFixedHeight(120);
|
status_btn->setFixedHeight(120);
|
||||||
status_btn->setStyleSheet(R"(
|
status_btn->setStyleSheet(clpActionBtnStyle);
|
||||||
QPushButton {
|
|
||||||
background-color: #393939;
|
|
||||||
color: white;
|
|
||||||
border-radius: 15px;
|
|
||||||
font-size: 50px;
|
|
||||||
font-weight: 500;
|
|
||||||
text-align: left;
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
QPushButton:pressed { background-color: #4a4a4a; }
|
|
||||||
)");
|
|
||||||
QObject::connect(status_btn, &QPushButton::clicked, [=]() { emit openStatus(); });
|
QObject::connect(status_btn, &QPushButton::clicked, [=]() { emit openStatus(); });
|
||||||
home_layout->addWidget(status_btn);
|
general_panel->addItem(status_btn);
|
||||||
|
|
||||||
QPushButton *sysset_btn = new QPushButton("System Settings");
|
// Reset Calibration
|
||||||
sysset_btn->setFixedHeight(120);
|
auto resetCalibBtn = new ButtonControl("Reset Calibration", "RESET", "");
|
||||||
sysset_btn->setStyleSheet(status_btn->styleSheet());
|
connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, [=]() {
|
||||||
QObject::connect(sysset_btn, &QPushButton::clicked, [=]() { emit openSettings(); });
|
QString desc = "openpilot requires the device to be mounted within 4° left or right and "
|
||||||
home_layout->addWidget(sysset_btn);
|
"within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required.";
|
||||||
|
std::string calib_bytes = Params().get("CalibrationParams");
|
||||||
|
if (!calib_bytes.empty()) {
|
||||||
|
try {
|
||||||
|
AlignedBuffer aligned_buf;
|
||||||
|
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size()));
|
||||||
|
auto calib = cmsg.getRoot<cereal::Event>().getLiveCalibration();
|
||||||
|
if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) {
|
||||||
|
double pitch = calib.getRpyCalib()[1] * (180 / M_PI);
|
||||||
|
double yaw = calib.getRpyCalib()[2] * (180 / M_PI);
|
||||||
|
desc += QString(" Your device is pointed %1° %2 and %3° %4.")
|
||||||
|
.arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? "down" : "up",
|
||||||
|
QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? "left" : "right");
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
qInfo() << "invalid CalibrationParams";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qobject_cast<ButtonControl *>(sender())->setDescription(desc);
|
||||||
|
});
|
||||||
|
connect(resetCalibBtn, &ButtonControl::clicked, [=]() {
|
||||||
|
if (ConfirmationDialog::confirm("Are you sure you want to reset calibration?", "Reset", this)) {
|
||||||
|
Params().remove("CalibrationParams");
|
||||||
|
Params().remove("LiveTorqueParameters");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
general_panel->addItem(resetCalibBtn);
|
||||||
|
|
||||||
home_layout->addStretch();
|
// Shutdown Timer
|
||||||
|
int cur_shutdown = Params().getInt("DeviceShutdown");
|
||||||
|
auto shutdownBtn = new ButtonControl("Shutdown Timer", shutdownLabel(cur_shutdown),
|
||||||
|
"How long the device stays on after the car is turned off.");
|
||||||
|
connect(shutdownBtn, &ButtonControl::clicked, [=]() {
|
||||||
|
QStringList options;
|
||||||
|
for (int i = 0; i <= 33; i++) {
|
||||||
|
options << shutdownLabel(i);
|
||||||
|
}
|
||||||
|
int current = Params().getInt("DeviceShutdown");
|
||||||
|
QString sel = MultiOptionDialog::getSelection("Shutdown Timer", options, shutdownLabel(current), this);
|
||||||
|
if (!sel.isEmpty()) {
|
||||||
|
int idx = options.indexOf(sel);
|
||||||
|
if (idx >= 0) {
|
||||||
|
Params().putInt("DeviceShutdown", idx);
|
||||||
|
shutdownBtn->setValue(shutdownLabel(idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
general_panel->addItem(shutdownBtn);
|
||||||
|
|
||||||
// Dashcam panel: placeholder
|
// Power buttons
|
||||||
|
QHBoxLayout *power_layout = new QHBoxLayout();
|
||||||
|
power_layout->setSpacing(30);
|
||||||
|
|
||||||
|
QPushButton *reboot_btn = new QPushButton("Reboot");
|
||||||
|
reboot_btn->setObjectName("reboot_btn");
|
||||||
|
power_layout->addWidget(reboot_btn);
|
||||||
|
|
||||||
|
QPushButton *softreboot_btn = new QPushButton("Soft Reboot");
|
||||||
|
softreboot_btn->setObjectName("softreboot_btn");
|
||||||
|
power_layout->addWidget(softreboot_btn);
|
||||||
|
|
||||||
|
QPushButton *poweroff_btn = new QPushButton("Power Off");
|
||||||
|
poweroff_btn->setObjectName("poweroff_btn");
|
||||||
|
power_layout->addWidget(poweroff_btn);
|
||||||
|
|
||||||
|
QObject::connect(reboot_btn, &QPushButton::clicked, [=]() {
|
||||||
|
if (!uiState()->engaged()) {
|
||||||
|
if (ConfirmationDialog::confirm("Are you sure you want to reboot?", "Reboot", this)) {
|
||||||
|
if (!uiState()->engaged()) {
|
||||||
|
Params().putBool("DoReboot", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ConfirmationDialog::alert("Disengage to Reboot", this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(softreboot_btn, &QPushButton::clicked, [=]() {
|
||||||
|
if (!uiState()->engaged()) {
|
||||||
|
if (ConfirmationDialog::confirm("Are you sure you want to soft reboot?", "Soft Reboot", this)) {
|
||||||
|
if (!uiState()->engaged()) {
|
||||||
|
Params().putBool("DoSoftReboot", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ConfirmationDialog::alert("Disengage to Soft Reboot", this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(poweroff_btn, &QPushButton::clicked, [=]() {
|
||||||
|
if (!uiState()->engaged()) {
|
||||||
|
if (ConfirmationDialog::confirm("Are you sure you want to power off?", "Power Off", this)) {
|
||||||
|
if (!uiState()->engaged()) {
|
||||||
|
Params().putBool("DoShutdown", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ConfirmationDialog::alert("Disengage to Power Off", this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
general_panel->addItem(power_layout);
|
||||||
|
|
||||||
|
// ── Network panel ──
|
||||||
|
Networking *network_panel = new Networking(this);
|
||||||
|
// Hide APN button — find by searching QPushButton labels inside AdvancedNetworking
|
||||||
|
for (auto *btn : network_panel->findChildren<QPushButton *>()) {
|
||||||
|
if (btn->text().contains("APN", Qt::CaseInsensitive)) {
|
||||||
|
// Hide the parent AbstractControl frame, not just the button
|
||||||
|
if (auto *frame = qobject_cast<QFrame *>(btn->parentWidget())) {
|
||||||
|
frame->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Dashcam panel ──
|
||||||
QWidget *dashcam_panel = new QWidget(this);
|
QWidget *dashcam_panel = new QWidget(this);
|
||||||
QVBoxLayout *dash_layout = new QVBoxLayout(dashcam_panel);
|
QVBoxLayout *dash_layout = new QVBoxLayout(dashcam_panel);
|
||||||
dash_layout->setContentsMargins(50, 25, 50, 25);
|
dash_layout->setContentsMargins(50, 25, 50, 25);
|
||||||
@@ -230,7 +355,7 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
|||||||
dash_layout->addWidget(dash_label);
|
dash_layout->addWidget(dash_label);
|
||||||
dash_layout->addStretch();
|
dash_layout->addStretch();
|
||||||
|
|
||||||
// Debug panel
|
// ── Debug panel ──
|
||||||
ListWidget *debug_panel = new ListWidget(this);
|
ListWidget *debug_panel = new ListWidget(this);
|
||||||
debug_panel->setContentsMargins(50, 25, 50, 25);
|
debug_panel->setContentsMargins(50, 25, 50, 25);
|
||||||
|
|
||||||
@@ -239,9 +364,10 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
|||||||
"Captures only changed values for efficiency.", "", this);
|
"Captures only changed values for efficiency.", "", this);
|
||||||
debug_panel->addItem(telemetry_toggle);
|
debug_panel->addItem(telemetry_toggle);
|
||||||
|
|
||||||
// Register panels with sidebar buttons
|
// ── Register panels with sidebar buttons ──
|
||||||
QList<QPair<QString, QWidget *>> panels = {
|
QList<QPair<QString, QWidget *>> panels = {
|
||||||
{"Home", home_panel},
|
{"General", general_panel},
|
||||||
|
{"Network", network_panel},
|
||||||
{"Dashcam", dashcam_panel},
|
{"Dashcam", dashcam_panel},
|
||||||
{"Debug", debug_panel},
|
{"Debug", debug_panel},
|
||||||
};
|
};
|
||||||
@@ -253,19 +379,24 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
|||||||
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||||
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
|
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
|
||||||
|
|
||||||
ScrollView *panel_frame = new ScrollView(panel, this);
|
// Network panel handles its own scrolling/margins
|
||||||
panel_widget->addWidget(panel_frame);
|
if (name == "Network") {
|
||||||
|
panel_widget->addWidget(panel);
|
||||||
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
|
QObject::connect(btn, &QPushButton::clicked, [=, w = panel]() {
|
||||||
btn->setChecked(true);
|
btn->setChecked(true);
|
||||||
panel_widget->setCurrentWidget(w);
|
panel_widget->setCurrentWidget(w);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
ScrollView *panel_frame = new ScrollView(panel, this);
|
||||||
|
panel_widget->addWidget(panel_frame);
|
||||||
|
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
|
||||||
|
btn->setChecked(true);
|
||||||
|
panel_widget->setCurrentWidget(w);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select Home by default
|
// Select General by default
|
||||||
if (auto *first_btn = sidebar_widget->findChild<QPushButton *>(QString(), Qt::FindDirectChildrenOnly)) {
|
|
||||||
// Skip close_btn, find first sidebar btn
|
|
||||||
}
|
|
||||||
panel_widget->setCurrentIndex(0);
|
panel_widget->setCurrentIndex(0);
|
||||||
|
|
||||||
// Main layout: sidebar + panels
|
// Main layout: sidebar + panels
|
||||||
@@ -282,9 +413,15 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
|||||||
ClearPilotPanel {
|
ClearPilotPanel {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
QStackedWidget, ScrollView {
|
QStackedWidget, ScrollView, Networking {
|
||||||
background-color: #292929;
|
background-color: #292929;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
}
|
}
|
||||||
|
#softreboot_btn { height: 120px; border-radius: 15px; background-color: #e2e22c; }
|
||||||
|
#softreboot_btn:pressed { background-color: #ffe224; }
|
||||||
|
#reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; }
|
||||||
|
#reboot_btn:pressed { background-color: #4a4a4a; }
|
||||||
|
#poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; }
|
||||||
|
#poweroff_btn:pressed { background-color: #FF2424; }
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user