openpilot/frogpilot/ui/qt/widgets/drive_summary.cc
2025-11-01 12:00:00 -07:00

226 lines
8.1 KiB
C++

#include "selfdrive/ui/qt/widgets/scrollview.h"
#include "frogpilot/ui/qt/widgets/drive_summary.h"
FrogPilotDriveSummary::FrogPilotDriveSummary(QWidget *parent, bool randomEvents) : QFrame(parent), displayRandomEvents(randomEvents) {
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(20, 20, 20, 20);
mainLayout->setSpacing(15);
titleLabel = new QLabel(randomEvents ? tr("Random Events Summary") : tr("Drive Summary"), this);
titleLabel->setAlignment(Qt::AlignCenter);
titleLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
titleLabel->setStyleSheet(R"(
QLabel {
background-color: #444444;
border-radius: 12px;
color: #FFFFFF;
font-size: 50px;
font-weight: bold;
padding: 12px 28px;
}
)");
titleLabel->setMaximumHeight(titleLabel->sizeHint().height());
mainLayout->addWidget(titleLabel);
mainLayout->addSpacing(10);
QWidget *containerWidget = new QWidget(this);
QVBoxLayout *listLayout = new QVBoxLayout(containerWidget);
listLayout->setAlignment(Qt::AlignTop);
listLayout->setSpacing(20);
if (displayRandomEvents) {
randomEventsMap.insert("accel30", tr("UwUs"));
randomEventsMap.insert("accel35", tr("Loch Ness Encounters"));
randomEventsMap.insert("accel40", tr("Visits to 1955"));
randomEventsMap.insert("dejaVuCurve", tr("Deja Vu Moments"));
randomEventsMap.insert("firefoxSteerSaturated", tr("Internet Explorer Weeeeeeees"));
randomEventsMap.insert("hal9000", tr("HAL 9000 Denials"));
randomEventsMap.insert("openpilotCrashedRandomEvent", tr("openpilot Crashes"));
randomEventsMap.insert("thisIsFineSteerSaturated", tr("This Is Fine Moments"));
randomEventsMap.insert("toBeContinued", tr("To Be Continued Moments"));
randomEventsMap.insert("vCruise69", tr("Noices"));
randomEventsMap.insert("yourFrogTriedToKillMe", tr("Attempted Frog Murders"));
randomEventsMap.insert("youveGotMail", tr("Total Mail Received"));
} else {
listLayout->addWidget(createStatBox(tr("% of Drive With openpilot Engaged"), &engagementValue, this));
listLayout->addWidget(createStatBox(tr("Drive Distance"), &frogPilotMetersValue, this));
listLayout->addWidget(createStatBox(tr("Drive Time"), &trackedTimeValue, this));
listLayout->addWidget(createStatBox(tr("% of Drive In \"Experimental Mode\""), &experimentalModeTimeValue, this));
}
if (displayRandomEvents) {
eventsListLayout = listLayout;
mainLayout->addWidget(new ScrollView(containerWidget, this), 1);
} else {
mainLayout->addWidget(containerWidget, 1);
}
setLayout(mainLayout);
setStyleSheet(R"(
QFrame {
background-color: #333333;
}
)");
QObject::connect(device(), &Device::interactiveTimeout, [this]() {
emit panelClosed();
});
QObject::connect(uiState(), &UIState::offroadTransition, [this](bool offroad) {
if (!offroad) {
previousStats = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotStats")).toUtf8()).object();
}
});
}
void FrogPilotDriveSummary::mousePressEvent(QMouseEvent *e) {
emit panelClosed();
}
void FrogPilotDriveSummary::showEvent(QShowEvent *event) {
bool isMetric = params.getBool("IsMetric");
QJsonObject currentStats = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotStats")).toUtf8()).object();
if (displayRandomEvents) {
QJsonObject currentRandomEvents = currentStats.value("RandomEvents").toObject();
QJsonObject previousRandomEvents = previousStats.value("RandomEvents").toObject();
QList<QPair<QString, int>> eventsList;
for (QMap<QString, QString>::const_iterator it = randomEventsMap.constBegin(); it != randomEventsMap.constEnd(); ++it) {
int currentValue = currentRandomEvents.value(it.key()).toInt();
int previousValue = previousRandomEvents.value(it.key()).toInt();
int diffValue = currentValue - previousValue;
if (diffValue > 0) {
eventsList.append(qMakePair(it.value(), diffValue));
}
}
std::sort(eventsList.begin(), eventsList.end(), [](const QPair<QString, int> &a, const QPair<QString, int> &b) {
if (a.second != b.second) {
return a.second > b.second;
} else {
return a.first.localeAwareCompare(b.first) < 0;
}
});
QLayoutItem *child;
while ((child = eventsListLayout->takeAt(0)) != nullptr) {
if (QWidget *widget = child->widget()) {
widget->deleteLater();
}
delete child;
}
randomEventLabels.clear();
if (eventsList.isEmpty()) {
eventsListLayout->setAlignment(Qt::AlignCenter);
QLabel *noEventsLabel = new QLabel(tr("No Random Events Played!"), this);
noEventsLabel->setAlignment(Qt::AlignCenter);
noEventsLabel->setStyleSheet(R"(
QLabel {
font-size: 50px;
font-weight: bold;
color: #FFFFFF;
}
)");
eventsListLayout->addWidget(noEventsLabel);
} else {
eventsListLayout->setAlignment(Qt::AlignTop);
for (QList<QPair<QString, int>>::const_iterator it = eventsList.constBegin(); it != eventsList.constEnd(); ++it) {
QLabel *valueLabel = nullptr;
eventsListLayout->addWidget(createStatBox(it->first, &valueLabel, this));
randomEventLabels.insert(it->first, valueLabel);
valueLabel->setText(QLocale().toString(it->second));
}
}
} else {
std::function<double(const QString &)> diffDouble = [currentStats, this](const QString &key) -> double {
return currentStats.value(key).toDouble() - previousStats.value(key).toDouble();
};
std::function<QString(double)> formatDistance = [&](double meters) {
double value;
QString unit;
if (isMetric) {
value = meters / 1000.0;
unit = (qRound(value) == 1) ? tr(" kilometer") : tr(" kilometers");
} else {
value = meters * METER_TO_MILE;
unit = (qRound(value) == 1) ? tr(" mile") : tr(" miles");
}
return QLocale().toString(qRound(value)) + unit;
};
std::function<QString(int)> formatTime = [&](int seconds) {
static int secondsInDay = 60 * 60 * 24;
static int secondsInHour = 60 * 60;
int days = seconds / secondsInDay;
int hours = (seconds % secondsInDay) / secondsInHour;
int minutes = (seconds % secondsInHour) / 60;
QString result;
if (days > 0) result += QLocale().toString(days) + (days == 1 ? tr(" day ") : tr(" days "));
if (hours > 0 || days > 0) result += QLocale().toString(hours) + (hours == 1 ? tr(" hour ") : tr(" hours "));
result += QLocale().toString(minutes) + (minutes == 1 ? tr(" minute") : tr(" minutes"));
return result.trimmed();
};
int engagedTime = diffDouble("AOLTime") + diffDouble("LongitudinalTime");
int experimentalTime = diffDouble("ExperimentalModeTime");
int trackedTime = diffDouble("TrackedTime");
engagementValue->setText(QLocale().toString((trackedTime > 0) ? (engagedTime * 100 / trackedTime) : 0) + "%");
experimentalModeTimeValue->setText(QLocale().toString((trackedTime > 0) ? (experimentalTime * 100 / trackedTime) : 0) + "%");
frogPilotMetersValue->setText(formatDistance(diffDouble("FrogPilotMeters")));
trackedTimeValue->setText(formatTime(trackedTime));
}
}
void FrogPilotDriveSummary::hideEvent(QHideEvent *event) {
emit panelClosed();
}
QWidget *FrogPilotDriveSummary::createStatBox(const QString &title, QLabel **valueLabel, QWidget *parent) {
QWidget *box = new QWidget(parent);
QVBoxLayout *layout = new QVBoxLayout(box);
layout->setAlignment(Qt::AlignCenter);
layout->setContentsMargins(10, 10, 10, 10);
layout->setSpacing(8);
QLabel *statTitleLabel = new QLabel(title, box);
statTitleLabel->setAlignment(Qt::AlignCenter);
statTitleLabel->setStyleSheet(R"(
QLabel {
color: #AAAAAA;
font-size: 40px;
font-weight: bold;
}
)");
QLabel *value = new QLabel("-", box);
value->setAlignment(Qt::AlignCenter);
value->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
value->setStyleSheet(R"(
QLabel {
color: #FFFFFF;
font-size: 75px;
font-weight: bold;
}
)");
*valueLabel = value;
layout->addWidget(statTitleLabel);
layout->addWidget(value);
return box;
}