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"
|
||||
|
||||
# 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
|
||||
pkill -9 -f "selfdrive/ui/text" 2>/dev/null
|
||||
pkill -9 -f 'launch_openpilot.sh' 2>/dev/null
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
// 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; }
|
||||
)");
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user