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:
2026-04-13 05:38:00 +00:00
parent 86bd2e25b9
commit 0aca33791c
3 changed files with 182 additions and 42 deletions

View File

@@ -6,8 +6,14 @@
#include <QVBoxLayout>
#include "common/swaglog.h"
#include "common/params.h"
#include "system/hardware/hw.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/network/networking.h"
#include "cereal/messaging/messaging.h"
// HomeWindow: the container for the offroad and onroad UIs
@@ -46,7 +52,7 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
// CLEARPILOT
ready = new ReadyWindow(this);
slayout->addWidget(ready);
driver_view = new DriverViewWindow(this);
connect(driver_view, &DriverViewWindow::done, [=] {
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) {
// Sidebar
QWidget *sidebar_widget = new QWidget;
@@ -188,39 +215,137 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
// Panel content area
panel_widget = new QStackedWidget();
// Define panels: sidebar label -> content widget
// Home panel: buttons for Status and System Settings
QWidget *home_panel = new QWidget(this);
QVBoxLayout *home_layout = new QVBoxLayout(home_panel);
home_layout->setContentsMargins(50, 25, 50, 25);
home_layout->setSpacing(20);
// ── General panel ──
ListWidget *general_panel = new ListWidget(this);
general_panel->setContentsMargins(50, 25, 50, 25);
// Status button
QPushButton *status_btn = new QPushButton("Status");
status_btn->setFixedHeight(120);
status_btn->setStyleSheet(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; }
)");
status_btn->setStyleSheet(clpActionBtnStyle);
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");
sysset_btn->setFixedHeight(120);
sysset_btn->setStyleSheet(status_btn->styleSheet());
QObject::connect(sysset_btn, &QPushButton::clicked, [=]() { emit openSettings(); });
home_layout->addWidget(sysset_btn);
// Reset Calibration
auto resetCalibBtn = new ButtonControl("Reset Calibration", "RESET", "");
connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, [=]() {
QString desc = "openpilot requires the device to be mounted within 4° left or right and "
"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);
QVBoxLayout *dash_layout = new QVBoxLayout(dashcam_panel);
dash_layout->setContentsMargins(50, 25, 50, 25);
@@ -230,7 +355,7 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
dash_layout->addWidget(dash_label);
dash_layout->addStretch();
// Debug panel
// ── Debug panel ──
ListWidget *debug_panel = new ListWidget(this);
debug_panel->setContentsMargins(50, 25, 50, 25);
@@ -239,9 +364,10 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
"Captures only changed values for efficiency.", "", this);
debug_panel->addItem(telemetry_toggle);
// Register panels with sidebar buttons
// ── Register panels with sidebar buttons ──
QList<QPair<QString, QWidget *>> panels = {
{"Home", home_panel},
{"General", general_panel},
{"Network", network_panel},
{"Dashcam", dashcam_panel},
{"Debug", debug_panel},
};
@@ -253,19 +379,24 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
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);
});
// Network panel handles its own scrolling/margins
if (name == "Network") {
panel_widget->addWidget(panel);
QObject::connect(btn, &QPushButton::clicked, [=, w = panel]() {
btn->setChecked(true);
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
if (auto *first_btn = sidebar_widget->findChild<QPushButton *>(QString(), Qt::FindDirectChildrenOnly)) {
// Skip close_btn, find first sidebar btn
}
// Select General by default
panel_widget->setCurrentIndex(0);
// Main layout: sidebar + panels
@@ -282,9 +413,15 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
ClearPilotPanel {
background-color: black;
}
QStackedWidget, ScrollView {
QStackedWidget, ScrollView, Networking {
background-color: #292929;
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; }
)");
}