From c5f5a3b5f2063be36497c81526c3a4c42d84beb7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 5 Apr 2023 01:46:36 +0800 Subject: [PATCH] cabana: support undo/redo zoom (#27799) * support updo/redo zoom * cleanup eventsMerged * init axis_x when create chart * update tool bar after create chart * align charts after updatelayout --- tools/cabana/chartswidget.cc | 62 +++++++++++++++--------------------- tools/cabana/chartswidget.h | 26 ++++++++++++--- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index c072f7d257..20a115ccb6 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -57,10 +57,16 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent) range_slider->setPageStep(60); // 1 min range_slider_action = toolbar->addWidget(range_slider); - undo_zoom_action = toolbar->addAction(utils::icon("arrow-counterclockwise"), tr("Previous zoom")); - qobject_cast(toolbar->widgetForAction(undo_zoom_action))->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - reset_zoom_action = toolbar->addAction(utils::icon("zoom-out"), tr("Reset Zoom")); + // zoom controls + zoom_undo_stack = new QUndoStack(this); + undo_zoom_action = zoom_undo_stack->createUndoAction(this); + undo_zoom_action->setIcon(utils::icon("arrow-counterclockwise")); + toolbar->addAction(undo_zoom_action); + redo_zoom_action = zoom_undo_stack->createRedoAction(this); + redo_zoom_action->setIcon(utils::icon("arrow-clockwise")); + toolbar->addAction(redo_zoom_action); + reset_zoom_action = toolbar->addAction(utils::icon("zoom-out"), ""); + reset_zoom_action->setToolTip(tr("Reset zoom")); qobject_cast(toolbar->widgetForAction(reset_zoom_action))->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); remove_all_btn = toolbar->addAction(utils::icon("x"), tr("Remove all charts")); @@ -99,7 +105,6 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent) QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange); QObject::connect(new_plot_btn, &QAction::triggered, this, &ChartsWidget::newChart); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); - QObject::connect(undo_zoom_action, &QAction::triggered, this, &ChartsWidget::zoomUndo); QObject::connect(reset_zoom_action, &QAction::triggered, this, &ChartsWidget::zoomReset); QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged); QObject::connect(dock_btn, &QAction::triggered, [this]() { @@ -115,14 +120,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent) } void ChartsWidget::eventsMerged() { - { - QFutureSynchronizer future_synchronizer; - for (auto c : charts) { - future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr)); - } - } - if (can->isPaused()) { - updateState(); + QFutureSynchronizer future_synchronizer; + for (auto c : charts) { + future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr)); } } @@ -135,25 +135,12 @@ void ChartsWidget::setZoom(double min, double max) { } void ChartsWidget::zoomIn(double min, double max) { - // Save previous zoom on undo stack - if (is_zoomed) { - zoom_stack.push({zoomed_range.first, zoomed_range.second}); - } - setZoom(min, max); + zoom_undo_stack->push(new ZoomCommand(this, {min, max})); } void ChartsWidget::zoomReset() { setZoom(display_range.first, display_range.second); - zoom_stack.clear(); -} - -void ChartsWidget::zoomUndo() { - if (!zoom_stack.isEmpty()) { - auto r = zoom_stack.pop(); - setZoom(r.first, r.second); - } else { - zoomReset(); - } + zoom_undo_stack->clear(); } void ChartsWidget::showValueTip(double sec) { @@ -199,12 +186,13 @@ void ChartsWidget::setMaxChartRange(int value) { void ChartsWidget::updateToolBar() { title_label->setText(tr("Charts: %1").arg(charts.size())); columns_action->setText(tr("Column: %1").arg(column_count)); - range_lb->setText(QString("Range: %1 ").arg(utils::formatSeconds(max_chart_range))); + range_lb->setText(utils::formatSeconds(max_chart_range)); range_lb_action->setVisible(!is_zoomed); range_slider_action->setVisible(!is_zoomed); undo_zoom_action->setVisible(is_zoomed); + redo_zoom_action->setVisible(is_zoomed); reset_zoom_action->setVisible(is_zoomed); - reset_zoom_action->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : ""); + reset_zoom_action->setText(is_zoomed ? tr("%1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : ""); remove_all_btn->setEnabled(!charts.isEmpty()); dock_btn->setIcon(utils::icon(docking ? "arrow-up-right-square" : "arrow-down-left-square")); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); @@ -225,20 +213,21 @@ ChartView *ChartsWidget::findChart(const MessageId &id, const cabana::Signal *si } ChartView *ChartsWidget::createChart() { - auto chart = new ChartView(this); + auto chart = new ChartView(is_zoomed ? zoomed_range : display_range, this); chart->setFixedHeight(settings.chart_height); chart->setMinimumWidth(CHART_MIN_WIDTH); chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); chart->chart()->setTheme(settings.theme == 2 ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); }); QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn); - QObject::connect(chart, &ChartView::zoomUndo, this, &ChartsWidget::zoomUndo); + QObject::connect(chart, &ChartView::zoomUndo, undo_zoom_action, &QAction::trigger); QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::axisYLabelWidthChanged, &align_timer, qOverload<>(&QTimer::start)); QObject::connect(chart, &ChartView::hovered, this, &ChartsWidget::showValueTip); charts.push_back(chart); updateLayout(); + updateToolBar(); return chart; } @@ -247,11 +236,9 @@ void ChartsWidget::showChart(const MessageId &id, const cabana::Signal *sig, boo if (show && !chart) { chart = merge && charts.size() > 0 ? charts.back() : createChart(); chart->addSeries(id, sig); - updateState(); } else if (!show && chart) { chart->removeIf([&](auto &s) { return s.msg_id == id && s.sig == sig; }); } - updateToolBar(); } void ChartsWidget::setColumnCount(int n) { @@ -305,8 +292,8 @@ void ChartsWidget::removeChart(ChartView *chart) { charts.removeOne(chart); chart->deleteLater(); updateToolBar(); - alignCharts(); updateLayout(); + alignCharts(); emit seriesChanged(); } @@ -366,7 +353,7 @@ bool ChartsWidget::event(QEvent *event) { } if (back_button) { - zoomUndo(); + emit undo_zoom_action->triggered(); return true; } return QFrame::event(event); @@ -374,7 +361,7 @@ bool ChartsWidget::event(QEvent *event) { // ChartView -ChartView::ChartView(QWidget *parent) : tip_label(this), QChartView(nullptr, parent) { +ChartView::ChartView(const std::pair &x_range, QWidget *parent) : tip_label(this), QChartView(nullptr, parent) { series_type = (SeriesType)settings.chart_series_type; QChart *chart = new QChart(); chart->setBackgroundVisible(false); @@ -386,6 +373,7 @@ ChartView::ChartView(QWidget *parent) : tip_label(this), QChartView(nullptr, par chart->legend()->setShowToolTips(true); chart->setMargins({0, 0, 0, 0}); + axis_x->setRange(x_range.first, x_range.second); setChart(chart); createToolButtons(); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 771230e9df..0a2f76202e 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -5,8 +5,9 @@ #include #include #include -#include #include +#include +#include #include #include #include @@ -36,7 +37,7 @@ class ChartView : public QChartView { Q_OBJECT public: - ChartView(QWidget *parent = nullptr); + ChartView(const std::pair &x_range, QWidget *parent = nullptr); void addSeries(const MessageId &msg_id, const cabana::Signal *sig); bool hasSeries(const MessageId &msg_id, const cabana::Signal *sig) const; void updateSeries(const cabana::Signal *sig = nullptr); @@ -129,6 +130,7 @@ public: public slots: void setColumnCount(int n); void removeAll(); + void setZoom(double min, double max); signals: void dock(bool floating); @@ -146,8 +148,6 @@ private: void updateState(); void zoomIn(double min, double max); void zoomReset(); - void zoomUndo(); - void setZoom(double min, double max); void updateToolBar(); void setMaxChartRange(int value); void updateLayout(); @@ -163,8 +163,12 @@ private: QAction *range_slider_action; bool docking = true; QAction *dock_btn; + QAction *undo_zoom_action; + QAction *redo_zoom_action; QAction *reset_zoom_action; + QUndoStack *zoom_undo_stack; + QAction *remove_all_btn; QGridLayout *charts_layout; QList charts; @@ -174,11 +178,23 @@ private: bool is_zoomed = false; std::pair display_range; std::pair zoomed_range; - QStack> zoom_stack; QAction *columns_action; int column_count = 1; int current_column_count = 0; QTimer align_timer; + friend class ZoomCommand; +}; + +class ZoomCommand : public QUndoCommand { +public: + ZoomCommand(ChartsWidget *charts, std::pair range) : charts(charts), range(range), QUndoCommand() { + prev_range = charts->is_zoomed ? charts->zoomed_range : charts->display_range; + setText(QObject::tr("Zoom to %1-%2").arg(range.first, 0, 'f', 1).arg(range.second, 0, 'f', 1)); + } + void undo() override { charts->setZoom(prev_range.first, prev_range.second); } + void redo() override { charts->setZoom(range.first, range.second); } + ChartsWidget *charts; + std::pair prev_range, range; }; class SeriesSelector : public QDialog {