diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 1cacaba4a2..c8e6093b86 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -29,7 +29,7 @@ cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/socketcans 'streams/routes.cc', 'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc', 'utils/export.cc', 'utils/util.cc', 'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc', - 'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) + 'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc', 'tools/routeinfo.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if GetOption('extras'): diff --git a/tools/cabana/tools/routeinfo.cc b/tools/cabana/tools/routeinfo.cc new file mode 100644 index 0000000000..77a0e065cd --- /dev/null +++ b/tools/cabana/tools/routeinfo.cc @@ -0,0 +1,40 @@ +#include "tools/cabana/tools/routeinfo.h" +#include +#include +#include +#include +#include "tools/cabana/streams/replaystream.h" + +RouteInfoDlg::RouteInfoDlg(QWidget *parent) : QDialog(parent) { + auto *replay = qobject_cast(can)->getReplay(); + setWindowTitle(tr("Route: %1").arg(QString::fromStdString(replay->route().name()))); + + auto *table = new QTableWidget(replay->route().segments().size(), 7, this); + table->setToolTip(tr("Click on a row to seek to the corresponding segment.")); + table->setEditTriggers(QAbstractItemView::NoEditTriggers); + table->setSelectionBehavior(QAbstractItemView::SelectRows); + table->setSelectionMode(QAbstractItemView::SingleSelection); + table->setHorizontalHeaderLabels({"", "rlog", "fcam", "ecam", "dcam", "qlog", "qcam"}); + table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + table->verticalHeader()->setVisible(false); + table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + int row = 0; + for (const auto &[seg_num, seg] : replay->route().segments()) { + table->setItem(row, 0, new QTableWidgetItem(QString::number(seg_num))); + table->setItem(row, 1, new QTableWidgetItem(seg.rlog.empty() ? "--" : "Yes")); + table->setItem(row, 2, new QTableWidgetItem(seg.road_cam.empty() ? "--" : "Yes")); + table->setItem(row, 3, new QTableWidgetItem(seg.wide_road_cam.empty() ? "--" : "Yes")); + table->setItem(row, 4, new QTableWidgetItem(seg.driver_cam.empty() ? "--" : "Yes")); + table->setItem(row, 5, new QTableWidgetItem(seg.qlog.empty() ? "--" : "Yes")); + table->setItem(row, 6, new QTableWidgetItem(seg.qcamera.empty() ? "--" : "Yes")); + ++row; + } + table->setMinimumWidth(table->horizontalHeader()->length() + table->verticalScrollBar()->sizeHint().width()); + table->setMinimumHeight(table->rowHeight(0) * std::min(table->rowCount(), 13) + table->horizontalHeader()->height() + table->frameWidth() * 2); + + connect(table, &QTableWidget::itemClicked, [](QTableWidgetItem *item) { can->seekTo(item->row() * 60.0); }); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(table); +} diff --git a/tools/cabana/tools/routeinfo.h b/tools/cabana/tools/routeinfo.h new file mode 100644 index 0000000000..36f32b4bf4 --- /dev/null +++ b/tools/cabana/tools/routeinfo.h @@ -0,0 +1,8 @@ +#pragma once +#include + +class RouteInfoDlg : public QDialog { + Q_OBJECT +public: + RouteInfoDlg(QWidget *parent = nullptr); +}; diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index e792f80a41..3d715b0319 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -11,6 +11,8 @@ #include #include +#include "tools/cabana/tools/routeinfo.h" + const int MIN_VIDEO_HEIGHT = 100; const int THUMBNAIL_MARGIN = 3; @@ -100,9 +102,12 @@ void VideoWidget::createPlaybackController() { if (!can->liveStreaming()) { toolbar->addAction(utils::icon("repeat"), tr("Loop playback"), this, &VideoWidget::loopPlaybackClicked); + createSpeedDropdown(toolbar); + toolbar->addSeparator(); + toolbar->addAction(utils::icon("info-circle"), tr("View route details"), this, &VideoWidget::showRouteInfo); + } else { + createSpeedDropdown(toolbar); } - - createSpeedDropdown(toolbar); } void VideoWidget::createSpeedDropdown(QToolBar *toolbar) { @@ -230,6 +235,12 @@ void VideoWidget::showThumbnail(double seconds) { slider->update(); } +void VideoWidget::showRouteInfo() { + RouteInfoDlg *route_info = new RouteInfoDlg(this); + route_info->setAttribute(Qt::WA_DeleteOnClose); + route_info->show(); +} + bool VideoWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::MouseMove) { auto [min_sec, max_sec] = can->timeRange().value_or(std::make_pair(can->minSeconds(), can->maxSeconds())); diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 1d87a5cfa0..6f756448c0 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -71,6 +71,7 @@ protected: void createSpeedDropdown(QToolBar *toolbar); void loopPlaybackClicked(); void vipcAvailableStreamsUpdated(std::set streams); + void showRouteInfo(); StreamCameraView *cam_widget; QAction *time_display_action = nullptr;