cabana: improve chart drag and drop (#27820)

* improve drag/drop

* change alpha to 180

* cleanup

* cleanup

* draw drop indicator
old-commit-hash: 23e420448b
beeps
Dean Lee 2 years ago committed by GitHub
parent 1b2cb4e7a5
commit 1db81fb496
  1. 79
      tools/cabana/chartswidget.cc
  2. 13
      tools/cabana/chartswidget.h

@ -12,6 +12,7 @@
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QPushButton> #include <QPushButton>
#include <QRubberBand> #include <QRubberBand>
#include <QScrollBar>
#include <QStylePainter> #include <QStylePainter>
#include <QToolBar> #include <QToolBar>
#include <QToolTip> #include <QToolTip>
@ -22,7 +23,7 @@ static inline bool xLessThan(const QPointF &p, float x) { return p.x() < x; }
// ChartsWidget // ChartsWidget
ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent) { ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_timer(this), QFrame(parent) {
setFrameStyle(QFrame::StyledPanel | QFrame::Plain); setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
QVBoxLayout *main_layout = new QVBoxLayout(this); QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(0, 0, 0, 0); main_layout->setContentsMargins(0, 0, 0, 0);
@ -99,6 +100,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent)
align_timer.setSingleShot(true); align_timer.setSingleShot(true);
QObject::connect(&align_timer, &QTimer::timeout, this, &ChartsWidget::alignCharts); QObject::connect(&align_timer, &QTimer::timeout, this, &ChartsWidget::alignCharts);
QObject::connect(&auto_scroll_timer, &QTimer::timeout, this, &ChartsWidget::doAutoScroll);
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll);
QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged); QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged);
QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState); QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState);
@ -270,6 +272,36 @@ void ChartsWidget::updateLayout() {
} }
} }
void ChartsWidget::startAutoScroll() {
auto_scroll_timer.start(50);
}
void ChartsWidget::stopAutoScroll() {
auto_scroll_timer.stop();
auto_scroll_count = 0;
}
void ChartsWidget::doAutoScroll() {
QScrollBar *scroll = charts_scroll->verticalScrollBar();
if (auto_scroll_count < scroll->pageStep()) {
++auto_scroll_count;
}
int value = scroll->value();
QPoint pos = charts_scroll->viewport()->mapFromGlobal(QCursor::pos());
QRect area = charts_scroll->viewport()->rect();
if (pos.y() - area.top() < settings.chart_height / 2) {
scroll->setValue(value - auto_scroll_count);
} else if (area.bottom() - pos.y() < settings.chart_height / 2) {
scroll->setValue(value + auto_scroll_count);
}
bool vertical_unchanged = value == scroll->value();
if (vertical_unchanged) {
stopAutoScroll();
}
}
void ChartsWidget::resizeEvent(QResizeEvent *event) { void ChartsWidget::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event); QWidget::resizeEvent(event);
updateLayout(); updateLayout();
@ -335,7 +367,6 @@ bool ChartsWidget::event(QEvent *event) {
back_button = ev->button() == Qt::BackButton; back_button = ev->button() == Qt::BackButton;
break; break;
} }
case QEvent::NativeGesture: { case QEvent::NativeGesture: {
QNativeGestureEvent *ev = static_cast<QNativeGestureEvent *>(event); QNativeGestureEvent *ev = static_cast<QNativeGestureEvent *>(event);
back_button = (ev->value() == 180); back_button = (ev->value() == 180);
@ -361,7 +392,7 @@ bool ChartsWidget::event(QEvent *event) {
// ChartView // ChartView
ChartView::ChartView(const std::pair<double, double> &x_range, QWidget *parent) : tip_label(this), QChartView(nullptr, parent) { ChartView::ChartView(const std::pair<double, double> &x_range, ChartsWidget *parent) : charts_widget(parent), tip_label(this), QChartView(nullptr, parent) {
series_type = (SeriesType)settings.chart_series_type; series_type = (SeriesType)settings.chart_series_type;
QChart *chart = new QChart(); QChart *chart = new QChart();
chart->setBackgroundVisible(false); chart->setBackgroundVisible(false);
@ -514,7 +545,7 @@ void ChartView::updatePlotArea(int left_pos, bool force) {
qreal left, top, right, bottom; qreal left, top, right, bottom;
chart()->layout()->getContentsMargins(&left, &top, &right, &bottom); chart()->layout()->getContentsMargins(&left, &top, &right, &bottom);
QSizeF x_label_size = QFontMetrics(axis_x->labelsFont()).size(Qt::TextSingleLine, QString::number(axis_x->max(), 'f', 2)); QSizeF x_label_size = QFontMetrics(axis_x->labelsFont()).size(Qt::TextSingleLine, QString::number(axis_x->max(), 'f', 2));
x_label_size += QSizeF{5 * devicePixelRatioF(), 5 * devicePixelRatioF()}; x_label_size += QSizeF{5, 5};
int adjust_top = chart()->legend()->geometry().height() + style()->pixelMetric(QStyle::PM_LayoutTopMargin); int adjust_top = chart()->legend()->geometry().height() + style()->pixelMetric(QStyle::PM_LayoutTopMargin);
chart()->setPlotArea(rect().adjusted(align_to + left, adjust_top + top, -x_label_size.width() / 2 - right, -x_label_size.height() - bottom)); chart()->setPlotArea(rect().adjusted(align_to + left, adjust_top + top, -x_label_size.width() / 2 - right, -x_label_size.height() - bottom));
chart()->layout()->invalidate(); chart()->layout()->invalidate();
@ -704,11 +735,18 @@ void ChartView::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton && move_icon->sceneBoundingRect().contains(event->pos())) { if (event->button() == Qt::LeftButton && move_icon->sceneBoundingRect().contains(event->pos())) {
QMimeData *mimeData = new QMimeData; QMimeData *mimeData = new QMimeData;
mimeData->setData(mime_type, QByteArray::number((qulonglong)this)); mimeData->setData(mime_type, QByteArray::number((qulonglong)this));
QPixmap pm = grab();
QPainter p(&pm);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.fillRect(pm.rect(), QColor(0, 0, 0, 180));
p.end();
QDrag *drag = new QDrag(this); QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData); drag->setMimeData(mimeData);
drag->setPixmap(grab()); drag->setPixmap(pm);
drag->setHotSpot(event->pos()); drag->setHotSpot(event->pos());
drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::MoveAction); drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::MoveAction);
charts_widget->stopAutoScroll();
} else if (event->button() == Qt::LeftButton && QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) { } else if (event->button() == Qt::LeftButton && QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) {
if (!can->liveStreaming()) { if (!can->liveStreaming()) {
// Save current playback state when scrubbing // Save current playback state when scrubbing
@ -735,13 +773,11 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) {
min = std::clamp(min, 0., can->totalSeconds()); min = std::clamp(min, 0., can->totalSeconds());
max = std::clamp(max, 0., can->totalSeconds()); max = std::clamp(max, 0., can->totalSeconds());
double min_rounded = std::floor(min * 10.0) / 10.0;
double max_rounded = std::floor(max * 10.0) / 10.0;
if (rubber->width() <= 0) { if (rubber->width() <= 0) {
// no rubber dragged, seek to mouse position // no rubber dragged, seek to mouse position
can->seekTo(min); can->seekTo(min);
} else if (rubber->width() > 10) { } else if (rubber->width() > 10) {
emit zoomIn(min_rounded, max_rounded); emit zoomIn(min, max);
} else { } else {
viewport()->update(); viewport()->update();
} }
@ -827,6 +863,17 @@ void ChartView::hideTip() {
viewport()->update(); viewport()->update();
} }
void ChartView::dragEnterEvent(QDragEnterEvent *event) {
can_drop = event->source() != this;
viewport()->update();
QChartView::dragEnterEvent(event);
}
void ChartView::dragLeaveEvent(QDragLeaveEvent *event) {
can_drop = false;
viewport()->update();
QChartView::dragLeaveEvent(event);
}
void ChartView::dragMoveEvent(QDragMoveEvent *event) { void ChartView::dragMoveEvent(QDragMoveEvent *event) {
if (event->mimeData()->hasFormat(mime_type)) { if (event->mimeData()->hasFormat(mime_type)) {
event->setDropAction(event->source() == this ? Qt::MoveAction : Qt::CopyAction); event->setDropAction(event->source() == this ? Qt::MoveAction : Qt::CopyAction);
@ -834,6 +881,7 @@ void ChartView::dragMoveEvent(QDragMoveEvent *event) {
} else { } else {
event->ignore(); event->ignore();
} }
charts_widget->startAutoScroll();
} }
void ChartView::dropEvent(QDropEvent *event) { void ChartView::dropEvent(QDropEvent *event) {
@ -844,11 +892,20 @@ void ChartView::dropEvent(QDropEvent *event) {
} else { } else {
ChartView *source_chart = (ChartView *)event->source(); ChartView *source_chart = (ChartView *)event->source();
for (auto &s : source_chart->sigs) { for (auto &s : source_chart->sigs) {
addSeries(s.msg_id, s.sig); source_chart->chart()->removeSeries(s.series);
chart()->addSeries(s.series);
s.series->attachAxis(axis_x);
s.series->attachAxis(axis_y);
} }
sigs.append(source_chart->sigs);
updateAxisY();
updateTitle();
source_chart->sigs.clear();
emit source_chart->remove(); emit source_chart->remove();
event->acceptProposedAction(); event->acceptProposedAction();
} }
can_drop = false;
} else { } else {
event->ignore(); event->ignore();
} }
@ -875,6 +932,10 @@ void ChartView::paintEvent(QPaintEvent *event) {
QPainter painter(viewport()); QPainter painter(viewport());
painter.setRenderHints(QPainter::Antialiasing); painter.setRenderHints(QPainter::Antialiasing);
painter.drawPixmap(QPoint(), chart_pixmap); painter.drawPixmap(QPoint(), chart_pixmap);
if (can_drop) {
painter.setPen(QPen(palette().color(QPalette::Highlight), 4));
painter.drawRect(viewport()->rect());
}
QRectF exposed_rect = mapToScene(event->region().boundingRect()).boundingRect(); QRectF exposed_rect = mapToScene(event->region().boundingRect()).boundingRect();
drawForeground(&painter, exposed_rect); drawForeground(&painter, exposed_rect);
} else { } else {

@ -33,11 +33,12 @@ public:
void paintEvent(QPaintEvent *ev) override; void paintEvent(QPaintEvent *ev) override;
}; };
class ChartsWidget;
class ChartView : public QChartView { class ChartView : public QChartView {
Q_OBJECT Q_OBJECT
public: public:
ChartView(const std::pair<double, double> &x_range, QWidget *parent = nullptr); ChartView(const std::pair<double, double> &x_range, ChartsWidget *parent = nullptr);
void addSeries(const MessageId &msg_id, const cabana::Signal *sig); void addSeries(const MessageId &msg_id, const cabana::Signal *sig);
bool hasSeries(const MessageId &msg_id, const cabana::Signal *sig) const; bool hasSeries(const MessageId &msg_id, const cabana::Signal *sig) const;
void updateSeries(const cabana::Signal *sig = nullptr); void updateSeries(const cabana::Signal *sig = nullptr);
@ -82,6 +83,8 @@ private:
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *ev) override; void mouseMoveEvent(QMouseEvent *ev) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override; void dropEvent(QDropEvent *event) override;
void leaveEvent(QEvent *event) override; void leaveEvent(QEvent *event) override;
@ -115,7 +118,9 @@ private:
bool is_scrubbing = false; bool is_scrubbing = false;
bool resume_after_scrub = false; bool resume_after_scrub = false;
QPixmap chart_pixmap; QPixmap chart_pixmap;
bool can_drop = false;
double tooltip_x = -1; double tooltip_x = -1;
ChartsWidget *charts_widget;
friend class ChartsWidget; friend class ChartsWidget;
}; };
@ -148,6 +153,9 @@ private:
void updateState(); void updateState();
void zoomIn(double min, double max); void zoomIn(double min, double max);
void zoomReset(); void zoomReset();
void startAutoScroll();
void stopAutoScroll();
void doAutoScroll();
void updateToolBar(); void updateToolBar();
void setMaxChartRange(int value); void setMaxChartRange(int value);
void updateLayout(); void updateLayout();
@ -181,8 +189,11 @@ private:
QAction *columns_action; QAction *columns_action;
int column_count = 1; int column_count = 1;
int current_column_count = 0; int current_column_count = 0;
int auto_scroll_count = 0;
QTimer auto_scroll_timer;
QTimer align_timer; QTimer align_timer;
friend class ZoomCommand; friend class ZoomCommand;
friend class ChartView;
}; };
class ZoomCommand : public QUndoCommand { class ZoomCommand : public QUndoCommand {

Loading…
Cancel
Save