From fd4dc109e1ab01b2d1ea6aaf3e99126dbaddc63b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 20 Jan 2023 05:51:55 +0800 Subject: [PATCH] cabana: add support for multiple columns charts (#27000) --- tools/cabana/chartswidget.cc | 68 +++++++++++++++++++++++++++++------- tools/cabana/chartswidget.h | 14 ++++++-- tools/cabana/settings.cc | 2 ++ tools/cabana/settings.h | 1 + 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 69229baa64..412365a15b 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -22,9 +22,19 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { // toolbar QToolBar *toolbar = new QToolBar(tr("Charts"), this); toolbar->setIconSize({16, 16}); - title_label = new QLabel(); - title_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - toolbar->addWidget(title_label); + + toolbar->addWidget(title_label = new QLabel()); + title_label->setContentsMargins(0 ,0, 12, 0); + columns_cb = new QComboBox(this); + columns_cb->addItems({"1", "2", "3", "4"}); + columns_cb->setCurrentIndex(std::clamp(settings.chart_column_count - 1, 0, 3)); + toolbar->addWidget(new QLabel(tr("Columns:"))); + toolbar->addWidget(columns_cb); + + QLabel *stretch_label = new QLabel(this); + stretch_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + toolbar->addWidget(stretch_label); + show_all_values_btn = toolbar->addAction(""); toolbar->addWidget(range_label = new QLabel()); reset_zoom_btn = toolbar->addAction(bootstrapPixmap("arrow-counterclockwise"), ""); @@ -36,8 +46,11 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { // charts QWidget *charts_container = new QWidget(this); - charts_layout = new QVBoxLayout(charts_container); - charts_layout->addStretch(); + QVBoxLayout *charts_main_layout = new QVBoxLayout(charts_container); + charts_main_layout->setContentsMargins(0, 0, 0, 0); + charts_layout = new QGridLayout(charts_container); + charts_main_layout->addLayout(charts_layout); + charts_main_layout->addStretch(0); QScrollArea *charts_scroll = new QScrollArea(this); charts_scroll->setWidgetResizable(true); @@ -53,6 +66,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { align_charts_timer = new QTimer(this); align_charts_timer->setSingleShot(true); align_charts_timer->callOnTimeout(this, &ChartsWidget::alignCharts); + column_count = settings.chart_column_count; QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); QObject::connect(can, &CANMessages::eventsMerged, this, &ChartsWidget::eventsMerged); @@ -60,6 +74,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(show_all_values_btn, &QAction::triggered, this, &ChartsWidget::showAllData); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); QObject::connect(reset_zoom_btn, &QAction::triggered, this, &ChartsWidget::zoomReset); + QObject::connect(columns_cb, SIGNAL(activated(int)), SLOT(setColumnCount(int))); + QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged); QObject::connect(dock_btn, &QAction::triggered, [this]() { emit dock(!docking); docking = !docking; @@ -144,6 +160,14 @@ void ChartsWidget::updateToolBar() { dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); } +void ChartsWidget::settingChanged() { + for (auto c : charts) { + c->setFixedHeight(settings.chart_height); + columns_cb->setCurrentIndex(std::clamp(settings.chart_column_count - 1, 0, columns_cb->count() - 1)); + setColumnCount(settings.chart_column_count); + } +} + ChartView *ChartsWidget::findChart(const QString &id, const Signal *sig) { for (auto c : charts) if (c->hasSeries(id, sig)) return c; @@ -157,6 +181,9 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo chart = merge && charts.size() > 0 ? charts.back() : nullptr; if (!chart) { chart = new ChartView(this); + chart->setFixedHeight(settings.chart_height); + chart->setMinimumWidth(CHART_MIN_WIDTH); + chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); chart->setEventsRange(display_range); auto range = is_zoomed ? zoomed_range : display_range; @@ -167,8 +194,8 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::axisYUpdated, [this]() { align_charts_timer->start(100); }); - charts_layout->insertWidget(0, chart); charts.push_back(chart); + updateLayout(); } chart->addSeries(id, sig); } else if (!show && chart) { @@ -178,6 +205,29 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo setUpdatesEnabled(true); } +void ChartsWidget::setColumnCount(int n) { + n = std::clamp(n + 1, 1, columns_cb->count()); + if (column_count != n) { + column_count = n; + updateLayout(); + } +} + +void ChartsWidget::updateLayout() { + int n = column_count; + for (; n >= 1; --n) { + if ((n * (CHART_MIN_WIDTH + charts_layout->spacing())) < rect().width()) break; + } + for (int i = 0; i < charts.size(); ++i) { + charts_layout->addWidget(charts[i], i / n, i % n); + } +} + +void ChartsWidget::resizeEvent(QResizeEvent *event) { + QWidget::resizeEvent(event); + updateLayout(); +} + void ChartsWidget::removeChart(ChartView *chart) { charts.removeOne(chart); chart->deleteLater(); @@ -244,13 +294,11 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { setChart(chart); setRenderHint(QPainter::Antialiasing); setRubberBand(QChartView::HorizontalRubberBand); - updateFromSettings(); QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved); QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated); QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved); QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated); - QObject::connect(&settings, &Settings::changed, this, &ChartView::updateFromSettings); QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); QObject::connect(manage_btn, &QToolButton::clicked, this, &ChartView::manageSeries); } @@ -386,10 +434,6 @@ void ChartView::updateTitle() { } } -void ChartView::updateFromSettings() { - setFixedHeight(settings.chart_height); -} - void ChartView::setEventsRange(const std::pair &range) { if (range != events_range) { events_range = range; diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index ebce8ceda6..54c84ad2db 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -3,11 +3,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -68,7 +68,6 @@ private: void resizeEvent(QResizeEvent *event) override; void updateAxisY(); void updateTitle(); - void updateFromSettings(); void drawForeground(QPainter *painter, const QRectF &rect) override; void applyNiceNumbers(qreal min, qreal max); qreal niceNumber(qreal x, bool ceiling); @@ -92,12 +91,16 @@ public: void showChart(const QString &id, const Signal *sig, bool show, bool merge); inline bool hasSignal(const QString &id, const Signal *sig) { return findChart(id, sig) != nullptr; } +public slots: + void setColumnCount(int n); + signals: void dock(bool floating); void rangeChanged(double min, double max, bool is_zommed); void seriesChanged(); private: + void resizeEvent(QResizeEvent *event) override; void alignCharts(); void removeChart(ChartView *chart); void eventsMerged(); @@ -108,6 +111,8 @@ private: void updateToolBar(); void removeAll(); void showAllData(); + void updateLayout(); + void settingChanged(); bool eventFilter(QObject *obj, QEvent *event) override; ChartView *findChart(const QString &id, const Signal *sig); @@ -119,7 +124,7 @@ private: QAction *reset_zoom_btn; QAction *remove_all_btn; QTimer *align_charts_timer; - QVBoxLayout *charts_layout; + QGridLayout *charts_layout; QList charts; uint32_t max_chart_range = 0; bool is_zoomed = false; @@ -127,6 +132,9 @@ private: std::pair display_range; std::pair zoomed_range; bool use_dark_theme = false; + QComboBox *columns_cb; + int column_count = 1; + const int CHART_MIN_WIDTH = 300; }; class SeriesSelector : public QDialog { diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index a5b490e9fb..0b0610edc9 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -18,6 +18,7 @@ void Settings::save() { s.setValue("cached_segment", cached_segment_limit); s.setValue("chart_height", chart_height); s.setValue("max_chart_x_range", max_chart_x_range); + s.setValue("chart_column_count", chart_column_count); s.setValue("last_dir", last_dir); s.setValue("window_state", window_state); s.setValue("geometry", geometry); @@ -30,6 +31,7 @@ void Settings::load() { cached_segment_limit = s.value("cached_segment", 3).toInt(); chart_height = s.value("chart_height", 200).toInt(); max_chart_x_range = s.value("max_chart_x_range", 3 * 60).toInt(); + chart_column_count = s.value("chart_column_count", 1).toInt(); last_dir = s.value("last_dir", QDir::homePath()).toString(); window_state = s.value("window_state").toByteArray(); geometry = s.value("geometry").toByteArray(); diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 76be2f1487..924021eb94 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -16,6 +16,7 @@ public: int fps = 10; int cached_segment_limit = 3; int chart_height = 200; + int chart_column_count = 1; int max_chart_x_range = 3 * 60; // 3 minutes QString last_dir; QByteArray geometry;