diff --git a/gui/include/Visualization/VisualizationGraphRenderingDelegate.h b/gui/include/Visualization/VisualizationGraphRenderingDelegate.h index 20fb5a4..f3a2bdf 100644 --- a/gui/include/Visualization/VisualizationGraphRenderingDelegate.h +++ b/gui/include/Visualization/VisualizationGraphRenderingDelegate.h @@ -20,7 +20,7 @@ public: explicit VisualizationGraphRenderingDelegate(VisualizationGraphWidget &graphWidget); void onMouseDoubleClick(QMouseEvent *event) noexcept; - void onMouseMove(QMouseEvent *event) noexcept; + void updateTooltip(QMouseEvent *event) noexcept; /// Updates rendering when data of plot changed void onPlotUpdated() noexcept; diff --git a/gui/include/Visualization/VisualizationGraphWidget.h b/gui/include/Visualization/VisualizationGraphWidget.h index da6369f..d4adf9d 100644 --- a/gui/include/Visualization/VisualizationGraphWidget.h +++ b/gui/include/Visualization/VisualizationGraphWidget.h @@ -130,6 +130,7 @@ protected: void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; void keyReleaseEvent(QKeyEvent * event) override; void keyPressEvent(QKeyEvent * event) override; @@ -146,7 +147,7 @@ private slots: void onGraphMenuRequested(const QPoint &pos) noexcept; /// Rescale the X axe to range parameter - void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange); + // void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange); /// Slot called when a mouse double click was made void onMouseDoubleClick(QMouseEvent *event) noexcept; diff --git a/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp b/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp index 7764aa2..0c39021 100644 --- a/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp +++ b/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp @@ -233,7 +233,7 @@ void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) } } -void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept +void VisualizationGraphRenderingDelegate::updateTooltip(QMouseEvent *event) noexcept { // Cancels pending refresh impl->m_TracerTimer.disconnect(); diff --git a/gui/src/Visualization/VisualizationGraphWidget.cpp b/gui/src/Visualization/VisualizationGraphWidget.cpp index 9bbc6a3..c6decd1 100644 --- a/gui/src/Visualization/VisualizationGraphWidget.cpp +++ b/gui/src/Visualization/VisualizationGraphWidget.cpp @@ -210,6 +210,46 @@ struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { auto axisY = plot.axisRect()->axis(QCPAxis::atLeft); return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y()); } + + inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis) + { + if (axis->scaleType() == QCPAxis::stLinear) + { + auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2); + return QCPRange{axis->range().lower + diff, axis->range().upper + diff}; + } + else + { + auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2); + return QCPRange{axis->range().lower * diff, axis->range().upper * diff}; + } + } + + void setRange(const QCPRange &newRange) + { + auto graphRange = DateTimeRange{newRange.lower, newRange.upper}; + + if (m_Flags.testFlag(GraphFlag::EnableAcquisition)) + { + for (auto it = m_VariableToPlotMultiMap.begin(), + end = m_VariableToPlotMultiMap.end(); + it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first)) + { + sqpApp->variableController().asyncChangeRange(it->first, graphRange); + } + } + } + + void moveGraph(const QPoint& origin, const QPoint& destination, QCustomPlot* plot) + { + auto currentPos = destination; + auto xAxis = plot->axisRect()->rangeDragAxis(Qt::Horizontal); + auto yAxis = plot->axisRect()->rangeDragAxis(Qt::Vertical); + xAxis->setRange(_pixDistanceToRange(origin.x(), currentPos.x(), xAxis)); + yAxis->setRange(_pixDistanceToRange(origin.y(), currentPos.y(), yAxis)); + setRange(xAxis->range()); + plot->replot(QCustomPlot::rpQueuedReplot); + } }; VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent) @@ -233,6 +273,7 @@ VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget // ↓ swhitch to this ASAP, VisualizationGraphWidget should intercept all UI events this->setFocusPolicy(Qt::WheelFocus); + this->setMouseTracking(true); ui->widget->setAttribute(Qt::WA_TransparentForMouseEvents); // connect(ui->widget, &QCustomPlot::mousePress, this, // &VisualizationGraphWidget::onMousePress); connect(ui->widget, &QCustomPlot::mouseRelease, @@ -455,11 +496,10 @@ void VisualizationGraphWidget::undoZoom() void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation) { - auto oldRange = ui->widget->xAxis->range(); QCPAxis *axis = ui->widget->axisRect()->rangeZoomAxis(orientation); axis->scaleRange(factor, axis->pixelToCoord(center)); if (orientation == Qt::Horizontal) - onRangeChanged(axis->range(), oldRange); + impl->setRange(axis->range()); ui->widget->replot(QCustomPlot::rpQueuedReplot); } @@ -488,7 +528,7 @@ void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation) ui->widget->axisRect()->rangeDragAxis(orientation)->range().upper * diff); } if (orientation == Qt::Horizontal) - onRangeChanged(axis->range(), oldRange); + impl->setRange(axis->range()); ui->widget->replot(QCustomPlot::rpQueuedReplot); } @@ -709,33 +749,29 @@ void VisualizationGraphWidget::wheelEvent(QWheelEvent *event) QWidget::wheelEvent(event); } -inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis) + + +void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event) { - if (axis->scaleType() == QCPAxis::stLinear) + if (event->buttons() == Qt::LeftButton) { - auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2); - return QCPRange{axis->range().lower + diff, axis->range().upper + diff}; + impl->moveGraph(_dragLastPos, event->pos(), ui->widget); + _dragLastPos = event->pos(); } - else + else if(impl->m_DrawingZoomRect) { - auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2); - return QCPRange{axis->range().lower * diff, axis->range().upper * diff}; + QPointF pos{ui->widget->xAxis->pixelToCoord(event->pos().x()), ui->widget->yAxis->pixelToCoord(event->pos().y())}; + impl->m_DrawingZoomRect->bottomRight->setCoords(pos); } -} - -void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event) -{ - if (event->buttons() & Qt::LeftButton) { - auto currentPos = event->pos(); - auto xAxis = ui->widget->axisRect()->rangeDragAxis(Qt::Horizontal); - auto yAxis = ui->widget->axisRect()->rangeDragAxis(Qt::Vertical); - auto oldXRange = xAxis->range(); - xAxis->setRange(_pixDistanceToRange(_dragLastPos.x(), currentPos.x(), xAxis)); - yAxis->setRange(_pixDistanceToRange(_dragLastPos.y(), currentPos.y(), yAxis)); - onRangeChanged(oldXRange, xAxis->range()); - ui->widget->replot(QCustomPlot::rpQueuedReplot); - _dragLastPos = currentPos; + else if (impl->m_DrawingZone) + { + impl->m_DrawingZone->setEnd(ui->widget->xAxis->pixelToCoord(event->pos().x())); } + else + { + impl->m_RenderingDelegate->updateTooltip(event); + } + event->accept(); QWidget::mouseMoveEvent(event); } @@ -752,12 +788,21 @@ void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event) } else { setCursor(Qt::ClosedHandCursor); - this->_dragLastPos= event->pos(); + this->_dragLastPos = event->pos(); } } + else if (event->button()==Qt::RightButton) + { + onGraphMenuRequested(event->pos()); + } QWidget::mousePressEvent(event); } +void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event) +{ + impl->m_RenderingDelegate->onMouseDoubleClick(event); +} + void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event) { switch (event->key()) { @@ -921,38 +966,38 @@ void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept } } -void VisualizationGraphWidget::onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange) -{ - auto graphRange = DateTimeRange{newRange.lower, newRange.upper}; - auto oldGraphRange = DateTimeRange{oldRange.lower, oldRange.upper}; - - if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) { - for (auto it = impl->m_VariableToPlotMultiMap.begin(), - end = impl->m_VariableToPlotMultiMap.end(); - it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) { - sqpApp->variableController().asyncChangeRange(it->first, graphRange); - } - } - - // if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) - // { - // emit synchronize(graphRange, oldGraphRange); - // } - - // auto pos = mapFromGlobal(QCursor::pos()); - // auto axisPos = impl->posToAxisPos(pos, plot()); - // if (auto parentZone = parentZoneWidget()) { - // if (impl->pointIsInAxisRect(axisPos, plot())) { - // parentZone->notifyMouseMoveInGraph(pos, axisPos, this); - // } - // else { - // parentZone->notifyMouseLeaveGraph(this); - // } - // } - - // Quits calibration - // impl->m_IsCalibration = false; -} +//void VisualizationGraphWidget::onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange) +//{ +// auto graphRange = DateTimeRange{newRange.lower, newRange.upper}; +// auto oldGraphRange = DateTimeRange{oldRange.lower, oldRange.upper}; + +// if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) { +// for (auto it = impl->m_VariableToPlotMultiMap.begin(), +// end = impl->m_VariableToPlotMultiMap.end(); +// it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) { +// sqpApp->variableController().asyncChangeRange(it->first, graphRange); +// } +// } + +// // if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) +// // { +// // emit synchronize(graphRange, oldGraphRange); +// // } + +// // auto pos = mapFromGlobal(QCursor::pos()); +// // auto axisPos = impl->posToAxisPos(pos, plot()); +// // if (auto parentZone = parentZoneWidget()) { +// // if (impl->pointIsInAxisRect(axisPos, plot())) { +// // parentZone->notifyMouseMoveInGraph(pos, axisPos, this); +// // } +// // else { +// // parentZone->notifyMouseLeaveGraph(this); +// // } +// // } + +// // Quits calibration +// // impl->m_IsCalibration = false; +//} void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept { @@ -962,7 +1007,7 @@ void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept { // Handles plot rendering when mouse is moving - impl->m_RenderingDelegate->onMouseMove(event); + impl->m_RenderingDelegate->updateTooltip(event); auto axisPos = impl->posToAxisPos(event->pos(), plot()); @@ -1074,11 +1119,6 @@ void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept } } - // Allows mouse panning only in default mode - // plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode() - // == - // SqpApplication::PlotsInteractionMode::None - // && !isDragDropClick); // Allows zone edition only in selection zone mode without drag&drop impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick); diff --git a/gui/tests/simple_graph/main.cpp b/gui/tests/simple_graph/main.cpp index 8082628..2cad822 100644 --- a/gui/tests/simple_graph/main.cpp +++ b/gui/tests/simple_graph/main.cpp @@ -19,18 +19,19 @@ ALIAS_TEMPLATE_FUNCTION(isReady, static_cast(qApp)->variableController().isReady) #define A_SIMPLE_GRAPH_FIXTURE \ - VisualizationGraphWidget w;\ - PREPARE_GUI_TEST(w);\ - auto provider = std::make_shared >();\ - auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),\ - QTime(16, 00));\ - auto var = static_cast(qApp)->variableController().createVariable(\ - "V1", {{"", "scalar"}}, provider, range);\ - while (!isReady(var))\ - QCoreApplication::processEvents();\ - w.addVariable(var, range);\ - GET_CHILD_WIDGET_FOR_GUI_TESTS(w, plot, QCustomPlot, "widget");\ - auto cent = center(&w);\ + VisualizationGraphWidget w;\ + PREPARE_GUI_TEST(w);\ + auto provider = std::make_shared >();\ + auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),\ + QTime(16, 00));\ + auto var = static_cast(qApp)->variableController().createVariable(\ + "V1", {{"", "scalar"}}, provider, range);\ + while (!isReady(var))\ + QCoreApplication::processEvents();\ + w.addVariable(var, range);\ + GET_CHILD_WIDGET_FOR_GUI_TESTS(w, plot, QCustomPlot, "widget");\ + auto cent = center(&w); + class A_SimpleGraph : public QObject { @@ -39,19 +40,19 @@ public: explicit A_SimpleGraph(QObject *parent = Q_NULLPTR) : QObject(parent) {} private slots: - void scrolls_with_mouse() + void scrolls_left_with_mouse() { - A_SIMPLE_GRAPH_FIXTURE + A_SIMPLE_GRAPH_FIXTURE; - for (auto i = 0; i < 10; i++) { - QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 500); + for (auto i = 0; i < 100; i++) { + QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 1); mouseMove(&w, {cent.x() + 200, cent.y()}, Qt::LeftButton); QTest::mouseRelease(&w, Qt::LeftButton); while (!isReady(var)) QCoreApplication::processEvents(); } while (!isReady(var)) - QCoreApplication::processEvents(); + QCoreApplication::processEvents(); auto r = var->range(); /* * Scrolling to the left implies going back in time @@ -60,6 +61,28 @@ private slots: QVERIFY(r.m_TEnd < range.m_TEnd); QVERIFY(SciQLop::numeric::almost_equal(r.delta(),range.delta(),1)); } + + void scrolls_right_with_mouse() + { + A_SIMPLE_GRAPH_FIXTURE; + + for (auto i = 0; i < 100; i++) { + QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 1); + mouseMove(&w, {cent.x() - 200, cent.y()}, Qt::LeftButton); + QTest::mouseRelease(&w, Qt::LeftButton); + while (!isReady(var)) + QCoreApplication::processEvents(); + } + while (!isReady(var)) + QCoreApplication::processEvents(); + auto r = var->range(); + /* + * Scrolling to the right implies going forward in time + * Scroll only implies keeping the same delta T -> shit only transformation + */ + QVERIFY(r.m_TEnd > range.m_TEnd); + QVERIFY(SciQLop::numeric::almost_equal(r.delta(),range.delta(),1)); + } }; QT_BEGIN_NAMESPACE @@ -69,10 +92,10 @@ int main(int argc, char *argv[]) { SqpApplication app{argc, argv}; app.setAttribute(Qt::AA_Use96Dpi, true); - QTEST_DISABLE_KEYPAD_NAVIGATION - QTEST_ADD_GPU_BLACKLIST_SUPPORT + QTEST_DISABLE_KEYPAD_NAVIGATION; + QTEST_ADD_GPU_BLACKLIST_SUPPORT; A_SimpleGraph tc; - QTEST_SET_MAIN_SOURCE_PATH + QTEST_SET_MAIN_SOURCE_PATH; return QTest::qExec(&tc, argc, argv); }