cabana: add capability for switching between line and scatter plots (#27169)

* add capability for switching between line and scatter plots

* Update tools/cabana/chartswidget.cc

Co-authored-by: Willem Melching <willem.melching@gmail.com>

* setUserOpengl in createSeries

* update series title

* set marker size by pixels_per_point

* sync menu state

* cleanup

* set default series type in settings dlg

* remove qdebug

---------

Co-authored-by: Willem Melching <willem.melching@gmail.com>
old-commit-hash: 53396076df
beeps
Dean Lee 2 years ago committed by GitHub
parent 4081c51c73
commit b4ba7f91a5
  1. 64
      tools/cabana/chartswidget.cc
  2. 8
      tools/cabana/chartswidget.h
  3. 8
      tools/cabana/settings.cc
  4. 2
      tools/cabana/settings.h

@ -7,6 +7,7 @@
#include <QFutureSynchronizer>
#include <QGraphicsLayout>
#include <QLineEdit>
#include <QMenu>
#include <QRubberBand>
#include <QToolBar>
#include <QToolButton>
@ -178,6 +179,7 @@ void ChartsWidget::settingChanged() {
range_slider->setRange(1, settings.max_cached_minutes * 60);
for (auto c : charts) {
c->setFixedHeight(settings.chart_height);
c->setSeriesType(settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter);
}
}
@ -297,6 +299,8 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) {
// ChartView
ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
series_type = settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter;
QChart *chart = new QChart();
chart->setBackgroundRoundness(0);
axis_x = new QValueAxis(this);
@ -317,9 +321,20 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
close_btn_proxy->setZValue(chart->zValue() + 11);
QToolButton *manage_btn = new QToolButton();
manage_btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
manage_btn->setIcon(utils::icon("gear"));
manage_btn->setAutoRaise(true);
manage_btn->setToolTip(tr("Manage series"));
QMenu *menu = new QMenu(this);
line_series_action = menu->addAction(tr("Line"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeLine); });
line_series_action->setCheckable(true);
line_series_action->setChecked(series_type == QAbstractSeries::SeriesTypeLine);
scatter_series_action = menu->addAction(tr("Scatter"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeScatter); });
scatter_series_action->setCheckable(true);
scatter_series_action->setChecked(series_type == QAbstractSeries::SeriesTypeScatter);
menu->addSeparator();
menu->addAction(tr("Manage series"), this, &ChartView::manageSeries);
manage_btn->setMenu(menu);
manage_btn->setPopupMode(QToolButton::InstantPopup);
manage_btn_proxy = new QGraphicsProxyWidget(chart);
manage_btn_proxy->setWidget(manage_btn);
manage_btn_proxy->setZValue(chart->zValue() + 11);
@ -334,7 +349,6 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved);
QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated);
QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove);
QObject::connect(manage_btn, &QToolButton::clicked, this, &ChartView::manageSeries);
}
qreal ChartView::getYAsixLabelWidth() const {
@ -354,8 +368,7 @@ void ChartView::setPlotAreaLeftPosition(int pos) {
}
void ChartView::addSeries(const QString &msg_id, const Signal *sig) {
QLineSeries *series = new QLineSeries(this);
QXYSeries *series = createSeries(series_type);
chart()->addSeries(series);
series->attachAxis(axis_x);
series->attachAxis(axis_y);
@ -478,6 +491,9 @@ void ChartView::updatePlot(double cur, double min, double max) {
int num_points = std::max<int>(end - begin, 1);
int pixels_per_point = width() / num_points;
if (series_type == QAbstractSeries::SeriesTypeScatter) {
((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 1, 8));
} else {
s.series->setPointsVisible(pixels_per_point > 20);
// TODO: On MacOS QChartWidget doesn't work with the OpenGL settings that CameraWidget needs.
@ -487,7 +503,7 @@ void ChartView::updatePlot(double cur, double min, double max) {
s.series->setUseOpenGL(use_opengl);
// Qt doesn't properly apply device pixel ratio in OpenGL mode
QApplication* application = static_cast<QApplication *>(QApplication::instance());
QApplication *application = static_cast<QApplication *>(QApplication::instance());
float scale = use_opengl ? application->devicePixelRatio() : 1.0;
QPen pen = s.series->pen();
@ -496,6 +512,7 @@ void ChartView::updatePlot(double cur, double min, double max) {
#endif
}
}
}
scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
}
@ -733,6 +750,43 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) {
}
}
QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type) {
QXYSeries *series = nullptr;
if (type == QAbstractSeries::SeriesTypeLine) {
series = new QLineSeries(this);
} else {
series = new QScatterSeries(this);
}
// TODO: Due to a bug in CameraWidget the camera frames
// are drawn instead of the graphs on MacOS. Re-enable OpenGL when fixed
#ifndef __APPLE__
series->setUseOpenGL(true);
#endif
return series;
}
void ChartView::setSeriesType(QAbstractSeries::SeriesType type) {
if (type != series_type) {
series_type = type;
line_series_action->setChecked(type == QAbstractSeries::SeriesTypeLine);
scatter_series_action->setChecked(type == QAbstractSeries::SeriesTypeScatter);
for (auto &s : sigs) {
chart()->removeSeries(s.series);
s.series->deleteLater();
}
for (auto &s : sigs) {
auto series = createSeries(series_type);
chart()->addSeries(series);
series->attachAxis(axis_x);
series->attachAxis(axis_y);
series->replace(s.vals);
s.series = series;
}
updateTitle();
}
}
// SeriesSelector
SeriesSelector::SeriesSelector(QWidget *parent) {

@ -10,6 +10,7 @@
#include <QTimer>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QScatterSeries>
#include <QtCharts/QValueAxis>
#include "tools/cabana/dbcmanager.h"
@ -30,13 +31,14 @@ public:
void updatePlot(double cur, double min, double max);
void setPlotAreaLeftPosition(int pos);
qreal getYAsixLabelWidth() const;
void setSeriesType(QAbstractSeries::SeriesType type);
struct SigItem {
QString msg_id;
uint8_t source = 0;
uint32_t address = 0;
const Signal *sig = nullptr;
QLineSeries *series = nullptr;
QXYSeries *series = nullptr;
QVector<QPointF> vals;
uint64_t last_value_mono_time = 0;
};
@ -70,6 +72,7 @@ private:
void drawForeground(QPainter *painter, const QRectF &rect) override;
void applyNiceNumbers(qreal min, qreal max);
qreal niceNumber(qreal x, bool ceiling);
QXYSeries *createSeries(QAbstractSeries::SeriesType type);
QValueAxis *axis_x;
QValueAxis *axis_y;
@ -79,6 +82,9 @@ private:
QList<SigItem> sigs;
double cur_sec = 0;
const QString mime_type = "application/x-cabanachartview";
QAbstractSeries::SeriesType series_type = QAbstractSeries::SeriesTypeLine;
QAction *line_series_action;
QAction *scatter_series_action;
};
class ChartsWidget : public QWidget {

@ -25,6 +25,7 @@ void Settings::save() {
s.setValue("video_splitter_state", video_splitter_state);
s.setValue("recent_files", recent_files);
s.setValue("message_header_state", message_header_state);
s.setValue("chart_series_type", chart_series_type);
}
void Settings::load() {
@ -40,6 +41,7 @@ void Settings::load() {
video_splitter_state = s.value("video_splitter_state").toByteArray();
recent_files = s.value("recent_files").toStringList();
message_header_state = s.value("message_header_state").toByteArray();
chart_series_type = s.value("chart_series_type", 0).toInt();
}
// SettingsDlg
@ -60,6 +62,11 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
cached_minutes->setValue(settings.max_cached_minutes);
form_layout->addRow(tr("Max Cached Minutes"), cached_minutes);
chart_series_type = new QComboBox(this);
chart_series_type->addItems({tr("Line"), tr("Scatter")});
chart_series_type->setCurrentIndex(settings.chart_series_type);
form_layout->addRow(tr("Chart Default Series Type"), chart_series_type);
chart_height = new QSpinBox(this);
chart_height->setRange(100, 500);
chart_height->setSingleStep(10);
@ -77,6 +84,7 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
void SettingsDlg::save() {
settings.fps = fps->value();
settings.max_cached_minutes = cached_minutes->value();
settings.chart_series_type = chart_series_type->currentIndex();
settings.chart_height = chart_height->value();
settings.save();
accept();

@ -18,6 +18,7 @@ public:
int chart_height = 200;
int chart_column_count = 1;
int chart_range = 3 * 60; // e minutes
int chart_series_type = 0;
QString last_dir;
QByteArray geometry;
QByteArray video_splitter_state;
@ -38,6 +39,7 @@ public:
QSpinBox *fps;
QSpinBox *cached_minutes;
QSpinBox *chart_height;
QComboBox *chart_series_type;
};
extern Settings settings;

Loading…
Cancel
Save