mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-01 19:42:07 +08:00
cabana: display signal value in all charts on mouse hover (#27749)
* display all values * cleanup * cleanup * emit hoverd if tip is visible old-commit-hash: 15a4b60ee6e78d16646db1535c5840cc4fd56932
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include <QOpenGLWidget>
|
||||
#include <QPushButton>
|
||||
#include <QRubberBand>
|
||||
#include <QStylePainter>
|
||||
#include <QToolBar>
|
||||
#include <QToolTip>
|
||||
#include <QtConcurrent>
|
||||
@@ -157,6 +158,12 @@ void ChartsWidget::zoomUndo() {
|
||||
}
|
||||
}
|
||||
|
||||
void ChartsWidget::showValueTip(double sec) {
|
||||
for (auto c : charts) {
|
||||
sec >= 0 ? c->showTip(sec) : c->hideTip();
|
||||
}
|
||||
}
|
||||
|
||||
void ChartsWidget::updateState() {
|
||||
if (charts.isEmpty()) return;
|
||||
|
||||
@@ -226,6 +233,7 @@ ChartView *ChartsWidget::createChart() {
|
||||
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();
|
||||
return chart;
|
||||
@@ -346,7 +354,7 @@ bool ChartsWidget::event(QEvent *event) {
|
||||
|
||||
// ChartView
|
||||
|
||||
ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
|
||||
ChartView::ChartView(QWidget *parent) : tip_label(this), QChartView(nullptr, parent) {
|
||||
series_type = (SeriesType)settings.chart_series_type;
|
||||
QChart *chart = new QChart();
|
||||
chart->setBackgroundVisible(false);
|
||||
@@ -542,7 +550,11 @@ void ChartView::updateSeriesPoints() {
|
||||
double pixels_per_point = (chart()->mapToPosition(right_pt).x() - chart()->mapToPosition(*begin).x()) / num_points;
|
||||
|
||||
if (series_type == SeriesType::Scatter) {
|
||||
((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 2.0, 2.0, 8.0) * devicePixelRatioF());
|
||||
qreal size = std::clamp(pixels_per_point / 2.0, 2.0, 8.0);
|
||||
if (s.series->useOpenGL()) {
|
||||
size *= devicePixelRatioF();
|
||||
}
|
||||
((QScatterSeries *)s.series)->setMarkerSize(size);
|
||||
} else {
|
||||
s.series->setPointsVisible(pixels_per_point > 20);
|
||||
}
|
||||
@@ -672,8 +684,9 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) {
|
||||
}
|
||||
|
||||
void ChartView::leaveEvent(QEvent *event) {
|
||||
clearTrackPoints();
|
||||
scene()->update();
|
||||
if (tip_label.isVisible()) {
|
||||
emit hovered(-1);
|
||||
}
|
||||
QChartView::leaveEvent(event);
|
||||
}
|
||||
|
||||
@@ -745,44 +758,17 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
|
||||
if (plot_area.contains(ev->pos())) {
|
||||
can->seekTo(std::clamp(chart()->mapToValue(ev->pos()).x(), 0., can->totalSeconds()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto rubber = findChild<QRubberBand *>();
|
||||
bool is_zooming = rubber && rubber->isVisible();
|
||||
is_scrubbing = false;
|
||||
clearTrackPoints();
|
||||
|
||||
if (!is_zooming && plot_area.contains(ev->pos())) {
|
||||
QStringList text_list;
|
||||
const double sec = chart()->mapToValue(ev->pos()).x();
|
||||
qreal x = -1;
|
||||
for (auto &s : sigs) {
|
||||
if (!s.series->isVisible()) continue;
|
||||
|
||||
// use reverse iterator to find last item <= sec.
|
||||
double value = 0;
|
||||
auto it = std::lower_bound(s.vals.rbegin(), s.vals.rend(), sec, [](auto &p, double x) { return p.x() > x; });
|
||||
if (it != s.vals.rend() && it->x() >= axis_x->min()) {
|
||||
value = it->y();
|
||||
s.track_pt = chart()->mapToPosition(*it);
|
||||
x = std::max(x, s.track_pt.x());
|
||||
}
|
||||
text_list.push_back(QString("<span style=\"color:%1;\">■ </span>%2: <b>%3</b> (%4 - %5)")
|
||||
.arg(s.series->color().name(), s.sig->name,
|
||||
s.track_pt.isNull() ? "--" : QString::number(value),
|
||||
QString::number(s.min), QString::number(s.max)));
|
||||
|
||||
}
|
||||
if (x < 0) {
|
||||
x = ev->pos().x();
|
||||
}
|
||||
text_list.push_front(QString::number(chart()->mapToValue({x, 0}).x(), 'f', 3));
|
||||
QPointF tooltip_pt(x + 12, plot_area.top() - 20);
|
||||
QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), "<p style='white-space:pre'>" + text_list.join("<br />"), this, plot_area.toRect());
|
||||
scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
|
||||
} else {
|
||||
QToolTip::hideText();
|
||||
emit hovered(sec);
|
||||
} else if (tip_label.isVisible()) {
|
||||
emit hovered(-1);
|
||||
}
|
||||
|
||||
QChartView::mouseMoveEvent(ev);
|
||||
@@ -797,6 +783,35 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
|
||||
}
|
||||
}
|
||||
|
||||
void ChartView::showTip(double sec) {
|
||||
qreal x = chart()->mapToPosition({sec, 0}).x();
|
||||
QStringList text_list(QString::number(chart()->mapToValue({x, 0}).x(), 'f', 3));
|
||||
for (auto &s : sigs) {
|
||||
if (s.series->isVisible()) {
|
||||
QString value = "--";
|
||||
// use reverse iterator to find last item <= sec.
|
||||
auto it = std::lower_bound(s.vals.rbegin(), s.vals.rend(), sec, [](auto &p, double x) { return p.x() > x; });
|
||||
if (it != s.vals.rend() && it->x() >= axis_x->min()) {
|
||||
value = QString::number(it->y());
|
||||
s.track_pt = chart()->mapToPosition(*it);
|
||||
x = std::max(x, s.track_pt.x());
|
||||
}
|
||||
text_list << QString("<span style=\"color:%1;\">■ </span>%2: <b>%3</b> (%4 - %5)")
|
||||
.arg(s.series->color().name(), s.sig->name, value, QString::number(s.min), QString::number(s.max));
|
||||
}
|
||||
}
|
||||
QPointF tooltip_pt(x, chart()->plotArea().top());
|
||||
int plot_right = mapToGlobal(chart()->plotArea().topRight().toPoint()).x();
|
||||
tip_label.showText(mapToGlobal(tooltip_pt.toPoint()), "<p style='white-space:pre'>" + text_list.join("<br />") + "</p>", plot_right);
|
||||
scene()->update();
|
||||
}
|
||||
|
||||
void ChartView::hideTip() {
|
||||
clearTrackPoints();
|
||||
tip_label.hide();
|
||||
scene()->update();
|
||||
}
|
||||
|
||||
void ChartView::dragMoveEvent(QDragMoveEvent *event) {
|
||||
if (event->mimeData()->hasFormat(mime_type)) {
|
||||
event->setDropAction(event->source() == this ? Qt::MoveAction : Qt::CopyAction);
|
||||
@@ -1039,3 +1054,39 @@ QList<SeriesSelector::ListItem *> SeriesSelector::seletedItems() {
|
||||
for (int i = 0; i < selected_list->count(); ++i) ret.push_back((ListItem *)selected_list->item(i));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ValueTipLabel
|
||||
|
||||
ValueTipLabel::ValueTipLabel(QWidget *parent) : QLabel(parent, Qt::Tool | Qt::FramelessWindowHint) {
|
||||
setForegroundRole(QPalette::ToolTipText);
|
||||
setBackgroundRole(QPalette::ToolTipBase);
|
||||
setPalette(QToolTip::palette());
|
||||
ensurePolished();
|
||||
setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, nullptr, this));
|
||||
setAttribute(Qt::WA_ShowWithoutActivating);
|
||||
setTextFormat(Qt::RichText);
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
void ValueTipLabel::showText(const QPoint &pt, const QString &text, int right_edge) {
|
||||
setText(text);
|
||||
if (!text.isEmpty()) {
|
||||
QSize extra(1, 1);
|
||||
resize(sizeHint() + extra);
|
||||
QPoint tip_pos(pt.x() + 12, pt.y());
|
||||
if (tip_pos.x() + size().width() >= right_edge) {
|
||||
tip_pos.rx() = pt.x() - size().width() - 12;
|
||||
}
|
||||
move(tip_pos);
|
||||
}
|
||||
setVisible(!text.isEmpty());
|
||||
}
|
||||
|
||||
void ValueTipLabel::paintEvent(QPaintEvent *ev) {
|
||||
QStylePainter p(this);
|
||||
QStyleOptionFrame opt;
|
||||
opt.init(this);
|
||||
p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
|
||||
p.end();
|
||||
QLabel::paintEvent(ev);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,13 @@ enum class SeriesType {
|
||||
Scatter
|
||||
};
|
||||
|
||||
class ValueTipLabel : public QLabel {
|
||||
public:
|
||||
ValueTipLabel(QWidget *parent = nullptr);
|
||||
void showText(const QPoint &pt, const QString &sec, int right_edge);
|
||||
void paintEvent(QPaintEvent *ev) override;
|
||||
};
|
||||
|
||||
class ChartView : public QChartView {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -36,6 +43,8 @@ public:
|
||||
void updatePlot(double cur, double min, double max);
|
||||
void setSeriesType(SeriesType type);
|
||||
void updatePlotArea(int left);
|
||||
void showTip(double sec);
|
||||
void hideTip();
|
||||
|
||||
struct SigItem {
|
||||
MessageId msg_id;
|
||||
@@ -57,6 +66,7 @@ signals:
|
||||
void zoomUndo();
|
||||
void remove();
|
||||
void axisYLabelWidthChanged(int w);
|
||||
void hovered(double sec);
|
||||
|
||||
private slots:
|
||||
void signalUpdated(const cabana::Signal *sig);
|
||||
@@ -94,6 +104,7 @@ private:
|
||||
QGraphicsProxyWidget *close_btn_proxy;
|
||||
QGraphicsProxyWidget *manage_btn_proxy;
|
||||
QGraphicsRectItem *background;
|
||||
ValueTipLabel tip_label;
|
||||
QList<SigItem> sigs;
|
||||
double cur_sec = 0;
|
||||
const QString mime_type = "application/x-cabanachartview";
|
||||
@@ -137,6 +148,7 @@ private:
|
||||
void setMaxChartRange(int value);
|
||||
void updateLayout();
|
||||
void settingChanged();
|
||||
void showValueTip(double sec);
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
ChartView *findChart(const MessageId &id, const cabana::Signal *sig);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user