Drive Summary
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
class FrogPilotDriveSummary : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotDriveSummary(QWidget *parent = nullptr, bool random_events = false);
|
||||
|
||||
signals:
|
||||
void panelClosed();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
|
||||
private:
|
||||
QWidget *createStatBox(const QString &title, QLabel **valueLabel, QWidget *parent);
|
||||
|
||||
bool displayRandomEvents;
|
||||
|
||||
Params params;
|
||||
|
||||
QJsonObject previousStats;
|
||||
|
||||
QLabel *experimentalModeTimeValue;
|
||||
QLabel *frogPilotMetersValue;
|
||||
QLabel *engagementValue;
|
||||
QLabel *titleLabel;
|
||||
QLabel *trackedTimeValue;
|
||||
|
||||
QMap<QString, QLabel*> randomEventLabels;
|
||||
|
||||
QMap<QString, QString> randomEventsMap;
|
||||
|
||||
QVBoxLayout *eventsListLayout;
|
||||
};
|
||||
@@ -49,6 +49,7 @@ frogpilot_src = ["../../frogpilot/ui/frogpilot_ui.cc", "../../frogpilot/ui/qt/of
|
||||
"../../frogpilot/ui/qt/offroad/wheel_settings.cc", "../../frogpilot/ui/qt/onroad/frogpilot_annotated_camera.cc",
|
||||
"../../frogpilot/ui/qt/onroad/frogpilot_buttons.cc", "../../frogpilot/ui/qt/onroad/frogpilot_onroad.cc",
|
||||
"../../frogpilot/ui/qt/widgets/developer_sidebar.cc", "../../frogpilot/ui/qt/widgets/drive_stats.cc",
|
||||
"../../frogpilot/ui/qt/widgets/drive_summary.cc",
|
||||
"../../frogpilot/ui/qt/widgets/navigation_functions.cc"]
|
||||
qt_src += frogpilot_src
|
||||
|
||||
|
||||
+39
-6
@@ -10,6 +10,7 @@
|
||||
#include "selfdrive/ui/qt/widgets/prime.h"
|
||||
|
||||
#include "frogpilot/ui/qt/widgets/drive_stats.h"
|
||||
#include "frogpilot/ui/qt/widgets/drive_summary.h"
|
||||
|
||||
// HomeWindow: the container for the offroad and onroad UIs
|
||||
|
||||
@@ -170,20 +171,35 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
||||
home_layout->setContentsMargins(0, 0, 0, 0);
|
||||
home_layout->setSpacing(30);
|
||||
|
||||
// left: stack of DriveStats
|
||||
// left: stack of DriveStats / DriveSummary
|
||||
QWidget *left_widget = new QWidget(this);
|
||||
QStackedLayout *left_stack = new QStackedLayout(left_widget);
|
||||
left_stack->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
left_stack->addWidget(new DriveStats());
|
||||
FrogPilotDriveSummary *drive_summary = new FrogPilotDriveSummary(this);
|
||||
left_stack->addWidget(drive_summary);
|
||||
|
||||
QObject::connect(drive_summary, &FrogPilotDriveSummary::panelClosed, [left_stack]() {
|
||||
left_stack->setCurrentIndex(0);
|
||||
});
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, [left_stack](bool offroad) {
|
||||
static bool previouslyOnroad = false;
|
||||
if (offroad && previouslyOnroad) {
|
||||
left_stack->setCurrentIndex(1);
|
||||
}
|
||||
previouslyOnroad = !offroad;
|
||||
});
|
||||
|
||||
home_layout->addWidget(left_widget, 1);
|
||||
|
||||
// right: ExperimentalModeButton, SetupWidget
|
||||
QWidget* right_widget = new QWidget(this);
|
||||
QVBoxLayout* right_column = new QVBoxLayout(right_widget);
|
||||
right_column->setContentsMargins(0, 0, 0, 0);
|
||||
// right: ExperimentalModeButton, SetupWidget, Random Events Summary
|
||||
QStackedWidget *right_widget = new QStackedWidget(this);
|
||||
right_widget->setFixedWidth(750);
|
||||
|
||||
QWidget *default_right = new QWidget(this);
|
||||
QVBoxLayout *right_column = new QVBoxLayout(default_right);
|
||||
right_column->setContentsMargins(0, 0, 0, 0);
|
||||
right_column->setSpacing(30);
|
||||
|
||||
ExperimentalModeButton *experimental_mode = new ExperimentalModeButton(this);
|
||||
@@ -194,7 +210,24 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
||||
QObject::connect(setup_widget, &SetupWidget::openSettings, this, &OffroadHome::openSettings);
|
||||
right_column->addWidget(setup_widget, 1);
|
||||
|
||||
home_layout->addWidget(right_widget, 1);
|
||||
right_widget->addWidget(default_right);
|
||||
|
||||
FrogPilotDriveSummary *random_events_summary = new FrogPilotDriveSummary(this, true);
|
||||
right_widget->addWidget(random_events_summary);
|
||||
right_widget->setCurrentIndex(0);
|
||||
|
||||
QObject::connect(random_events_summary, &FrogPilotDriveSummary::panelClosed, [=]() {
|
||||
right_widget->setCurrentIndex(0);
|
||||
});
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, [right_widget](bool offroad) {
|
||||
static bool previouslyOnroad = false;
|
||||
if (offroad && previouslyOnroad && frogpilotUIState()->frogpilot_scene.frogpilot_toggles.value("random_events").toBool()) {
|
||||
right_widget->setCurrentIndex(1);
|
||||
}
|
||||
previouslyOnroad = !offroad;
|
||||
});
|
||||
|
||||
home_layout->addWidget(right_widget, 0);
|
||||
}
|
||||
center_layout->addWidget(home_widget);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user