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. 84
      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 <QFutureSynchronizer>
#include <QGraphicsLayout> #include <QGraphicsLayout>
#include <QLineEdit> #include <QLineEdit>
#include <QMenu>
#include <QRubberBand> #include <QRubberBand>
#include <QToolBar> #include <QToolBar>
#include <QToolButton> #include <QToolButton>
@ -178,6 +179,7 @@ void ChartsWidget::settingChanged() {
range_slider->setRange(1, settings.max_cached_minutes * 60); range_slider->setRange(1, settings.max_cached_minutes * 60);
for (auto c : charts) { for (auto c : charts) {
c->setFixedHeight(settings.chart_height); 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::ChartView(QWidget *parent) : QChartView(nullptr, parent) { ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
series_type = settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter;
QChart *chart = new QChart(); QChart *chart = new QChart();
chart->setBackgroundRoundness(0); chart->setBackgroundRoundness(0);
axis_x = new QValueAxis(this); axis_x = new QValueAxis(this);
@ -317,9 +321,20 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
close_btn_proxy->setZValue(chart->zValue() + 11); close_btn_proxy->setZValue(chart->zValue() + 11);
QToolButton *manage_btn = new QToolButton(); QToolButton *manage_btn = new QToolButton();
manage_btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
manage_btn->setIcon(utils::icon("gear")); manage_btn->setIcon(utils::icon("gear"));
manage_btn->setAutoRaise(true); 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 = new QGraphicsProxyWidget(chart);
manage_btn_proxy->setWidget(manage_btn); manage_btn_proxy->setWidget(manage_btn);
manage_btn_proxy->setZValue(chart->zValue() + 11); 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::msgRemoved, this, &ChartView::msgRemoved);
QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated); QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated);
QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove);
QObject::connect(manage_btn, &QToolButton::clicked, this, &ChartView::manageSeries);
} }
qreal ChartView::getYAsixLabelWidth() const { qreal ChartView::getYAsixLabelWidth() const {
@ -354,8 +368,7 @@ void ChartView::setPlotAreaLeftPosition(int pos) {
} }
void ChartView::addSeries(const QString &msg_id, const Signal *sig) { void ChartView::addSeries(const QString &msg_id, const Signal *sig) {
QLineSeries *series = new QLineSeries(this); QXYSeries *series = createSeries(series_type);
chart()->addSeries(series); chart()->addSeries(series);
series->attachAxis(axis_x); series->attachAxis(axis_x);
series->attachAxis(axis_y); series->attachAxis(axis_y);
@ -478,22 +491,26 @@ void ChartView::updatePlot(double cur, double min, double max) {
int num_points = std::max<int>(end - begin, 1); int num_points = std::max<int>(end - begin, 1);
int pixels_per_point = width() / num_points; int pixels_per_point = width() / num_points;
s.series->setPointsVisible(pixels_per_point > 20); 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. // TODO: On MacOS QChartWidget doesn't work with the OpenGL settings that CameraWidget needs.
#ifndef __APPLE #ifndef __APPLE
// OpenGL mode lacks certain features (such as showing points), only use when drawing many points // OpenGL mode lacks certain features (such as showing points), only use when drawing many points
bool use_opengl = pixels_per_point < 1; bool use_opengl = pixels_per_point < 1;
s.series->setUseOpenGL(use_opengl); s.series->setUseOpenGL(use_opengl);
// Qt doesn't properly apply device pixel ratio in OpenGL mode // 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; float scale = use_opengl ? application->devicePixelRatio() : 1.0;
QPen pen = s.series->pen(); QPen pen = s.series->pen();
pen.setWidth(2.0 * scale); pen.setWidth(2.0 * scale);
s.series->setPen(pen); s.series->setPen(pen);
#endif #endif
}
} }
} }
@ -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::SeriesSelector(QWidget *parent) { SeriesSelector::SeriesSelector(QWidget *parent) {

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

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

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

Loading…
Cancel
Save