Cabana: display the (x,y) values while MouseMove on the chart (#26064)

pull/25859/head^2
Dean Lee 3 years ago committed by GitHub
parent 5f7d9a519e
commit cc6dd18cf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 122
      tools/cabana/chartswidget.cc
  2. 41
      tools/cabana/chartswidget.h

@ -3,7 +3,6 @@
#include <QGraphicsLayout> #include <QGraphicsLayout>
#include <QLabel> #include <QLabel>
#include <QRubberBand> #include <QRubberBand>
#include <QStackedLayout>
#include <QtCharts/QLineSeries> #include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis> #include <QtCharts/QValueAxis>
@ -136,8 +135,7 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) {
// ChartWidget // ChartWidget
ChartWidget::ChartWidget(const QString &id, const QString &sig_name, QWidget *parent) : id(id), sig_name(sig_name), QWidget(parent) { ChartWidget::ChartWidget(const QString &id, const QString &sig_name, QWidget *parent) : id(id), sig_name(sig_name), QWidget(parent) {
QStackedLayout *stacked = new QStackedLayout(this); QVBoxLayout *main_layout = new QVBoxLayout(this);
stacked->setStackingMode(QStackedLayout::StackAll);
QWidget *chart_widget = new QWidget(this); QWidget *chart_widget = new QWidget(this);
QVBoxLayout *chart_layout = new QVBoxLayout(chart_widget); QVBoxLayout *chart_layout = new QVBoxLayout(chart_widget);
@ -159,9 +157,23 @@ ChartWidget::ChartWidget(const QString &id, const QString &sig_name, QWidget *pa
header_layout->addWidget(remove_btn); header_layout->addWidget(remove_btn);
chart_layout->addWidget(header); chart_layout->addWidget(header);
chart_view = new ChartView(id, sig_name, this);
chart_view->setFixedHeight(300);
chart_layout->addWidget(chart_view);
chart_layout->addStretch();
main_layout->addWidget(chart_widget);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
}
// ChartView
ChartView::ChartView(const QString &id, const QString &sig_name, QWidget *parent)
: id(id), sig_name(sig_name), QChartView(nullptr, parent) {
QLineSeries *series = new QLineSeries(); QLineSeries *series = new QLineSeries();
series->setUseOpenGL(true); series->setUseOpenGL(true);
auto chart = new QChart(); QChart *chart = new QChart();
chart->setTitle(sig_name); chart->setTitle(sig_name);
chart->addSeries(series); chart->addSeries(series);
chart->createDefaultAxes(); chart->createDefaultAxes();
@ -172,28 +184,26 @@ ChartWidget::ChartWidget(const QString &id, const QString &sig_name, QWidget *pa
chart->setMargins({0, 0, 0, 0}); chart->setMargins({0, 0, 0, 0});
chart->layout()->setContentsMargins(0, 0, 0, 0); chart->layout()->setContentsMargins(0, 0, 0, 0);
chart_view = new ChartView(chart); track_line = new QGraphicsLineItem(chart);
chart_view->setFixedHeight(300); track_line->setPen(QPen(Qt::gray, 1, Qt::DashLine));
chart_view->setRenderHint(QPainter::Antialiasing); value_text = new QGraphicsSimpleTextItem(chart);
chart_view->setRubberBand(QChartView::HorizontalRubberBand); value_text->setBrush(Qt::gray);
if (auto rubber = chart_view->findChild<QRubberBand *>()) { line_marker = new QGraphicsLineItem(chart);
line_marker->setPen(QPen(Qt::black, 2));
setChart(chart);
setRenderHint(QPainter::Antialiasing);
setRubberBand(QChartView::HorizontalRubberBand);
if (auto rubber = findChild<QRubberBand *>()) {
QPalette pal; QPalette pal;
pal.setBrush(QPalette::Base, QColor(0, 0, 0, 80)); pal.setBrush(QPalette::Base, QColor(0, 0, 0, 80));
rubber->setPalette(pal); rubber->setPalette(pal);
} }
chart_layout->addWidget(chart_view);
chart_layout->addStretch();
stacked->addWidget(chart_widget); QObject::connect(can, &CANMessages::updated, this, &ChartView::updateState);
line_marker = new LineMarker(this); QObject::connect(can, &CANMessages::rangeChanged, this, &ChartView::rangeChanged);
stacked->addWidget(line_marker); QObject::connect(can, &CANMessages::eventsMerged, this, &ChartView::updateSeries);
line_marker->setAttribute(Qt::WA_TransparentForMouseEvents, true);
line_marker->raise();
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QObject::connect(can, &CANMessages::updated, this, &ChartWidget::updateState);
QObject::connect(can, &CANMessages::rangeChanged, this, &ChartWidget::rangeChanged);
QObject::connect(can, &CANMessages::eventsMerged, this, &ChartWidget::updateSeries);
QObject::connect(dynamic_cast<QValueAxis *>(chart->axisX()), &QValueAxis::rangeChanged, can, &CANMessages::setRange); QObject::connect(dynamic_cast<QValueAxis *>(chart->axisX()), &QValueAxis::rangeChanged, can, &CANMessages::setRange);
QObject::connect(dbc(), &DBCManager::signalUpdated, [this](const QString &msg_id, const QString &sig_name) { QObject::connect(dbc(), &DBCManager::signalUpdated, [this](const QString &msg_id, const QString &sig_name) {
if (this->id == msg_id && this->sig_name == sig_name) if (this->id == msg_id && this->sig_name == sig_name)
@ -202,15 +212,13 @@ ChartWidget::ChartWidget(const QString &id, const QString &sig_name, QWidget *pa
updateSeries(); updateSeries();
} }
void ChartWidget::updateState() { void ChartView::updateState() {
auto chart = chart_view->chart(); auto axis_x = dynamic_cast<QValueAxis *>(chart()->axisX());
auto axis_x = dynamic_cast<QValueAxis *>(chart->axisX()); int x = chart()->plotArea().left() + chart()->plotArea().width() * (can->currentSec() - axis_x->min()) / (axis_x->max() - axis_x->min());
line_marker->setLine(x, 0, x, height());
int x = chart->plotArea().left() + chart->plotArea().width() * (can->currentSec() - axis_x->min()) / (axis_x->max() - axis_x->min());
line_marker->setX(x);
} }
void ChartWidget::updateSeries() { void ChartView::updateSeries() {
const Signal *sig = dbc()->signal(id, sig_name); const Signal *sig = dbc()->signal(id, sig_name);
auto events = can->events(); auto events = can->events();
if (!sig || !events) return; if (!sig || !events) return;
@ -234,15 +242,16 @@ void ChartWidget::updateSeries() {
} }
} }
} }
QLineSeries *series = (QLineSeries *)chart_view->chart()->series()[0]; QLineSeries *series = (QLineSeries *)chart()->series()[0];
series->replace(vals); series->replace(vals);
series->setPointLabelsColor(Qt::black);
auto [begin, end] = can->range(); auto [begin, end] = can->range();
chart_view->chart()->axisX()->setRange(begin, end); chart()->axisX()->setRange(begin, end);
updateAxisY(); updateAxisY();
} }
void ChartWidget::rangeChanged(qreal min, qreal max) { void ChartView::rangeChanged(qreal min, qreal max) {
auto axis_x = dynamic_cast<QValueAxis *>(chart_view->chart()->axisX()); auto axis_x = dynamic_cast<QValueAxis *>(chart()->axisX());
if (axis_x->min() != min || axis_x->max() != max) { if (axis_x->min() != min || axis_x->max() != max) {
axis_x->setRange(min, max); axis_x->setRange(min, max);
} }
@ -250,9 +259,9 @@ void ChartWidget::rangeChanged(qreal min, qreal max) {
} }
// auto zoom on yaxis // auto zoom on yaxis
void ChartWidget::updateAxisY() { void ChartView::updateAxisY() {
const auto axis_x = dynamic_cast<QValueAxis *>(chart_view->chart()->axisX()); const auto axis_x = dynamic_cast<QValueAxis *>(chart()->axisX());
const auto axis_y = dynamic_cast<QValueAxis *>(chart_view->chart()->axisY()); const auto axis_y = dynamic_cast<QValueAxis *>(chart()->axisY());
// vals is a sorted list // vals is a sorted list
auto begin = std::lower_bound(vals.begin(), vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); auto begin = std::lower_bound(vals.begin(), vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; });
if (begin == vals.end()) if (begin == vals.end())
@ -271,7 +280,17 @@ void ChartWidget::updateAxisY() {
} }
} }
// ChartView void ChartView::enterEvent(QEvent *event) {
track_line->setVisible(true);
value_text->setVisible(true);
QChartView::enterEvent(event);
}
void ChartView::leaveEvent(QEvent *event) {
track_line->setVisible(false);
value_text->setVisible(false);
QChartView::leaveEvent(event);
}
void ChartView::mouseReleaseEvent(QMouseEvent *event) { void ChartView::mouseReleaseEvent(QMouseEvent *event) {
auto rubber = findChild<QRubberBand *>(); auto rubber = findChild<QRubberBand *>();
@ -289,20 +308,31 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) {
} }
// TODO: right-click to reset zoom // TODO: right-click to reset zoom
QChartView::mouseReleaseEvent(event); QChartView::mouseReleaseEvent(event);
line_marker->setVisible(true);
} }
void ChartView::mouseMoveEvent(QMouseEvent *ev) {
auto rubber = findChild<QRubberBand *>();
bool show = !(rubber && rubber->isVisible());
// LineMarker if (show) {
const auto plot_area = chart()->plotArea();
float x = std::clamp((float)ev->pos().x(), (float)plot_area.left(), (float)plot_area.right());
track_line->setLine(x, plot_area.top(), x, plot_area.bottom());
void LineMarker::setX(double x) { auto [begin, end] = can->range();
if (x != x_pos) { double sec = begin + ((x - plot_area.x()) / plot_area.width()) * (end - begin);
x_pos = x; auto value = std::lower_bound(vals.begin(), vals.end(), sec, [](auto &p, double x) { return p.x() < x; });
update(); value_text->setPos(x + 6, plot_area.bottom() - 25);
if (value != vals.end()) {
value_text->setText(QString("(%1, %2)").arg(value->x(), 0, 'f', 3).arg(value->y()));
} else {
value_text->setText("(--, --)");
}
} }
}
void LineMarker::paintEvent(QPaintEvent *event) { value_text->setVisible(show);
QPainter p(this); track_line->setVisible(show);
p.setPen(QPen(Qt::black, 2)); line_marker->setVisible(show);
p.drawLine(QPointF{x_pos, 50.}, QPointF{x_pos, (qreal)height() - 11}); QChartView::mouseMoveEvent(ev);
} }

@ -3,6 +3,8 @@
#include <map> #include <map>
#include <QLabel> #include <QLabel>
#include <QGraphicsLineItem>
#include <QGraphicsSimpleTextItem>
#include <QPushButton> #include <QPushButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QWidget> #include <QWidget>
@ -14,24 +16,29 @@
using namespace QtCharts; using namespace QtCharts;
class LineMarker : public QWidget { class ChartView : public QChartView {
Q_OBJECT Q_OBJECT
public: public:
LineMarker(QWidget *parent) : QWidget(parent) {} ChartView(const QString &id, const QString &sig_name, QWidget *parent = nullptr);
void setX(double x);
private: private:
void paintEvent(QPaintEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
double x_pos = -1; void mouseMoveEvent(QMouseEvent *ev) override;
}; void enterEvent(QEvent *event) override;
void leaveEvent(QEvent *event) override;
class ChartView : public QChartView { void updateSeries();
Q_OBJECT void rangeChanged(qreal min, qreal max);
void updateAxisY();
void updateState();
public: QGraphicsLineItem *track_line;
ChartView(QChart *chart, QWidget *parent = nullptr) : QChartView(chart, parent) {} QGraphicsSimpleTextItem *value_text;
void mouseReleaseEvent(QMouseEvent *event) override; QGraphicsLineItem *line_marker;
QList<QPointF> vals;
QString id;
QString sig_name;
}; };
class ChartWidget : public QWidget { class ChartWidget : public QWidget {
@ -44,18 +51,10 @@ public:
signals: signals:
void remove(); void remove();
private: protected:
void updateState();
void addData(const CanData &can_data, const Signal &sig);
void updateSeries();
void rangeChanged(qreal min, qreal max);
void updateAxisY();
QString id; QString id;
QString sig_name; QString sig_name;
ChartView *chart_view = nullptr; ChartView *chart_view = nullptr;
LineMarker *line_marker = nullptr;
QList<QPointF> vals;
}; };
class ChartsWidget : public QWidget { class ChartsWidget : public QWidget {

Loading…
Cancel
Save