##// END OF EJS Templates
Partly reimplemented Graph event handling + bugfix...
jeandet -
r1362:0033c08c242f Pybind11
parent child
Show More
@@ -86,6 +86,9 public:
86 86 /// Undo the last zoom done with a zoom box
87 87 void undoZoom();
88 88
89 void zoom(double factor, int center, Qt::Orientation orientation);
90 void move(double factor, Qt::Orientation orientation);
91
89 92 // IVisualizationWidget interface
90 93 void accept(IVisualizationWidgetVisitor *visitor) override;
91 94 bool canDrop(const Variable &variable) const override;
@@ -123,6 +126,12 protected:
123 126 void closeEvent(QCloseEvent *event) override;
124 127 void enterEvent(QEvent *event) override;
125 128 void leaveEvent(QEvent *event) override;
129 void wheelEvent(QWheelEvent * event) override;
130 void mouseMoveEvent(QMouseEvent *event) override;
131 void mouseReleaseEvent(QMouseEvent *event) override;
132 void mousePressEvent(QMouseEvent *event) override;
133 void keyReleaseEvent(QKeyEvent * event) override;
134 void keyPressEvent(QKeyEvent * event) override;
126 135
127 136 QCustomPlot &plot() const noexcept;
128 137
@@ -131,13 +140,13 private:
131 140
132 141 class VisualizationGraphWidgetPrivate;
133 142 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
134
143 QPoint _dragLastPos;
135 144 private slots:
136 145 /// Slot called when right clicking on the graph (displays a menu)
137 146 void onGraphMenuRequested(const QPoint &pos) noexcept;
138 147
139 148 /// Rescale the X axe to range parameter
140 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
149 void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
141 150
142 151 /// Slot called when a mouse double click was made
143 152 void onMouseDoubleClick(QMouseEvent *event) noexcept;
@@ -231,16 +231,22 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget
231 231 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
232 232 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
233 233
234 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
235 connect(ui->widget, &QCustomPlot::mouseRelease, this,
236 &VisualizationGraphWidget::onMouseRelease);
237 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
238 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
239 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
240 &VisualizationGraphWidget::onMouseDoubleClick);
241 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
242 &QCPAxis::rangeChanged),
243 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
234 // ↓ swhitch to this ASAP, VisualizationGraphWidget should intercept all UI events
235 this->setFocusPolicy(Qt::WheelFocus);
236 ui->widget->setAttribute(Qt::WA_TransparentForMouseEvents);
237 // connect(ui->widget, &QCustomPlot::mousePress, this,
238 // &VisualizationGraphWidget::onMousePress); connect(ui->widget, &QCustomPlot::mouseRelease,
239 // this,
240 // &VisualizationGraphWidget::onMouseRelease);
241 // connect(ui->widget, &QCustomPlot::mouseMove, this,
242 // &VisualizationGraphWidget::onMouseMove); connect(ui->widget, &QCustomPlot::mouseWheel,
243 // this, &VisualizationGraphWidget::onMouseWheel); connect(ui->widget,
244 // &QCustomPlot::mouseDoubleClick, this,
245 // &VisualizationGraphWidget::onMouseDoubleClick);
246 // connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange
247 // &)>(
248 // &QCPAxis::rangeChanged),
249 // this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
244 250
245 251 // Activates menu when right clicking on the graph
246 252 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
@@ -248,11 +254,12 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget
248 254 &VisualizationGraphWidget::onGraphMenuRequested);
249 255
250 256 //@TODO implement this :)
251 // connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
252 // &VariableController::onRequestDataLoading);
257 // connect(this, &VisualizationGraphWidget::requestDataLoading,
258 // &sqpApp->variableController(),
259 // &VariableController::onRequestDataLoading);
253 260
254 // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this,
255 // &VisualizationGraphWidget::onUpdateVarDisplaying);
261 // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this,
262 // &VisualizationGraphWidget::onUpdateVarDisplaying);
256 263
257 264 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
258 265 plot().setPlottingHint(QCP::phFastPolylines, true);
@@ -308,8 +315,8 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, D
308 315 }
309 316 //@TODO this is bad! when variable is moved to another graph it still fires
310 317 // even if this has been deleted
311 connect(variable.get(),&Variable::updated,this, &VisualizationGraphWidget::variableUpdated);
312 this->onUpdateVarDisplaying(variable,range);//My bullshit
318 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
319 this->onUpdateVarDisplaying(variable, range); // My bullshit
313 320 emit variableAdded(variable);
314 321 }
315 322
@@ -334,7 +341,7 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable
334 341 }
335 342
336 343 // Updates graph
337 ui->widget->replot();
344 ui->widget->replot(QCustomPlot::rpQueuedReplot);
338 345 }
339 346
340 347 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
@@ -342,7 +349,7 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() co
342 349 auto variables = std::vector<std::shared_ptr<Variable> >{};
343 350 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
344 351 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
345 variables.push_back (it->first);
352 variables.push_back(it->first);
346 353 }
347 354
348 355 return variables;
@@ -371,7 +378,7 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool ca
371 378 }
372 379
373 380 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
374 ui->widget->replot();
381 ui->widget->replot(QCustomPlot::rpQueuedReplot);
375 382
376 383 if (calibration) {
377 384 impl->m_IsCalibration = false;
@@ -405,8 +412,8 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &r
405 412 plot().replot(QCustomPlot::rpQueuedReplot);
406 413 }
407 414
408 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
409 const DateTimeRange &range)
415 VisualizationSelectionZoneItem *
416 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
410 417 {
411 418 // note: ownership is transfered to QCustomPlot
412 419 auto zone = new VisualizationSelectionZoneItem(&plot());
@@ -445,6 +452,46 void VisualizationGraphWidget::undoZoom()
445 452 plot().replot(QCustomPlot::rpQueuedReplot);
446 453 }
447 454
455 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation)
456
457 {
458 auto oldRange = ui->widget->xAxis->range();
459 QCPAxis *axis = ui->widget->axisRect()->rangeZoomAxis(orientation);
460 axis->scaleRange(factor, axis->pixelToCoord(center));
461 if (orientation == Qt::Horizontal)
462 onRangeChanged(axis->range(), oldRange);
463 ui->widget->replot(QCustomPlot::rpQueuedReplot);
464 }
465
466 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation)
467 {
468 auto oldRange = ui->widget->xAxis->range();
469 QCPAxis *axis = ui->widget->axisRect()->rangeDragAxis(orientation);
470 if (ui->widget->xAxis->scaleType() == QCPAxis::stLinear) {
471 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
472 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
473 }
474 else if (ui->widget->xAxis->scaleType() == QCPAxis::stLogarithmic) {
475 int start = 0, stop = 0;
476 double diff = 0.;
477 if (factor > 0.0) {
478 stop = this->width() * factor / 10;
479 start = 2 * this->width() * factor / 10;
480 }
481 if (factor < 0.0) {
482 factor *= -1.0;
483 start = this->width() * factor / 10;
484 stop = 2 * this->width() * factor / 10;
485 }
486 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
487 axis->setRange(ui->widget->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
488 ui->widget->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
489 }
490 if (orientation == Qt::Horizontal)
491 onRangeChanged(axis->range(), oldRange);
492 ui->widget->replot(QCustomPlot::rpQueuedReplot);
493 }
494
448 495 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
449 496 {
450 497 if (visitor) {
@@ -636,6 +683,147 void VisualizationGraphWidget::leaveEvent(QEvent *event)
636 683 }
637 684 }
638 685
686 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
687 {
688 double factor;
689 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
690 if (event->modifiers() == Qt::ControlModifier) {
691 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
692 {
693 setCursor(Qt::SizeVerCursor);
694 factor = pow(ui->widget->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
695 zoom(factor, event->pos().y(), Qt::Vertical);
696 }
697 }
698 else if (event->modifiers() == Qt::ShiftModifier) {
699 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
700 {
701 setCursor(Qt::SizeHorCursor);
702 factor = pow(ui->widget->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
703 zoom(factor, event->pos().x(), Qt::Horizontal);
704 }
705 }
706 else {
707 move(wheelSteps, Qt::Horizontal);
708 }
709 QWidget::wheelEvent(event);
710 }
711
712 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
713 {
714 if (axis->scaleType() == QCPAxis::stLinear)
715 {
716 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
717 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
718 }
719 else
720 {
721 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
722 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
723 }
724 }
725
726 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
727 {
728 if (event->buttons() & Qt::LeftButton) {
729 auto currentPos = event->pos();
730 auto xAxis = ui->widget->axisRect()->rangeDragAxis(Qt::Horizontal);
731 auto yAxis = ui->widget->axisRect()->rangeDragAxis(Qt::Vertical);
732 auto oldXRange = xAxis->range();
733 xAxis->setRange(_pixDistanceToRange(_dragLastPos.x(), currentPos.x(), xAxis));
734 yAxis->setRange(_pixDistanceToRange(_dragLastPos.y(), currentPos.y(), yAxis));
735 onRangeChanged(oldXRange, xAxis->range());
736 ui->widget->replot(QCustomPlot::rpQueuedReplot);
737 _dragLastPos = currentPos;
738 }
739 QWidget::mouseMoveEvent(event);
740 }
741
742 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
743 {
744 setCursor(Qt::ArrowCursor);
745 QWidget::mouseReleaseEvent(event);
746 }
747
748 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
749 {
750 if (event->button() == Qt::LeftButton) {
751 if (event->modifiers() == Qt::ControlModifier) {
752 }
753 else {
754 setCursor(Qt::ClosedHandCursor);
755 this->_dragLastPos= event->pos();
756 }
757 }
758 QWidget::mousePressEvent(event);
759 }
760
761 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
762 {
763 switch (event->key()) {
764 case Qt::Key_Control:
765 event->accept();
766 break;
767 case Qt::Key_Shift:
768 event->accept();
769 break;
770 default:
771 QWidget::keyReleaseEvent(event);
772 break;
773 }
774 setCursor(Qt::ArrowCursor);
775 }
776
777 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
778 {
779 switch (event->key()) {
780 case Qt::Key_Control:
781 setCursor(Qt::CrossCursor);
782 break;
783 case Qt::Key_Shift:
784 break;
785 case Qt::Key_M:
786 ui->widget->rescaleAxes();
787 ui->widget->replot(QCustomPlot::rpQueuedReplot);
788 break;
789 case Qt::Key_Left:
790 if (event->modifiers() != Qt::ControlModifier) {
791 move(-0.1, Qt::Horizontal);
792 }
793 else {
794 zoom(2, this->width() / 2, Qt::Horizontal);
795 }
796 break;
797 case Qt::Key_Right:
798 if (event->modifiers() != Qt::ControlModifier) {
799 move(0.1, Qt::Horizontal);
800 }
801 else {
802 zoom(0.5, this->width() / 2, Qt::Horizontal);
803 }
804 break;
805 case Qt::Key_Up:
806 if (event->modifiers() != Qt::ControlModifier) {
807 move(0.1, Qt::Vertical);
808 }
809 else {
810 zoom(0.5, this->height() / 2, Qt::Vertical);
811 }
812 break;
813 case Qt::Key_Down:
814 if (event->modifiers() != Qt::ControlModifier) {
815 move(-0.1, Qt::Vertical);
816 }
817 else {
818 zoom(2, this->height() / 2, Qt::Vertical);
819 }
820 break;
821 default:
822 QWidget::keyPressEvent(event);
823 break;
824 }
825 }
826
639 827 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
640 828 {
641 829 return *ui->widget;
@@ -651,7 +839,7 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
651 839 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
652 840 // 'Remove variable' action
653 841 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
654 [ this, var = it->first ]() { removeVariable(var); });
842 [this, var = it->first]() { removeVariable(var); });
655 843 }
656 844
657 845 if (!impl->m_ZoomStack.isEmpty()) {
@@ -733,10 +921,10 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
733 921 }
734 922 }
735 923
736 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
924 void VisualizationGraphWidget::onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange)
737 925 {
738 auto graphRange = DateTimeRange{t1.lower, t1.upper};
739 auto oldGraphRange = DateTimeRange{t2.lower, t2.upper};
926 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
927 auto oldGraphRange = DateTimeRange{oldRange.lower, oldRange.upper};
740 928
741 929 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
742 930 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
@@ -746,24 +934,24 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange
746 934 }
747 935 }
748 936
749 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration)
750 {
751 emit synchronize(graphRange, oldGraphRange);
752 }
753
754 auto pos = mapFromGlobal(QCursor::pos());
755 auto axisPos = impl->posToAxisPos(pos, plot());
756 if (auto parentZone = parentZoneWidget()) {
757 if (impl->pointIsInAxisRect(axisPos, plot())) {
758 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
759 }
760 else {
761 parentZone->notifyMouseLeaveGraph(this);
762 }
763 }
937 // if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration)
938 // {
939 // emit synchronize(graphRange, oldGraphRange);
940 // }
941
942 // auto pos = mapFromGlobal(QCursor::pos());
943 // auto axisPos = impl->posToAxisPos(pos, plot());
944 // if (auto parentZone = parentZoneWidget()) {
945 // if (impl->pointIsInAxisRect(axisPos, plot())) {
946 // parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
947 // }
948 // else {
949 // parentZone->notifyMouseLeaveGraph(this);
950 // }
951 // }
764 952
765 953 // Quits calibration
766 impl->m_IsCalibration = false;
954 // impl->m_IsCalibration = false;
767 955 }
768 956
769 957 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
@@ -860,7 +1048,7 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
860 1048 plot().setNotAntialiasedElements(QCP::aeAll);
861 1049 }
862 1050
863 //plot().replot(QCustomPlot::rpQueuedReplot);
1051 // plot().replot(QCustomPlot::rpQueuedReplot);
864 1052 }
865 1053 }
866 1054 }
@@ -887,9 +1075,10 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
887 1075 }
888 1076
889 1077 // Allows mouse panning only in default mode
890 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
891 == SqpApplication::PlotsInteractionMode::None
892 && !isDragDropClick);
1078 // plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
1079 // ==
1080 // SqpApplication::PlotsInteractionMode::None
1081 // && !isDragDropClick);
893 1082
894 1083 // Allows zone edition only in selection zone mode without drag&drop
895 1084 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
@@ -1035,11 +1224,9 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> v
1035 1224
1036 1225 void VisualizationGraphWidget::variableUpdated(QUuid id)
1037 1226 {
1038 for(auto& [var,plotables]:impl->m_VariableToPlotMultiMap)
1039 {
1040 if(var->ID()==id)
1041 {
1042 impl->updateData(plotables,var,this->graphRange());
1227 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1228 if (var->ID() == id) {
1229 impl->updateData(plotables, var, this->graphRange());
1043 1230 }
1044 1231 }
1045 1232 }
@@ -20,7 +20,7 QPoint center(T* widget)
20 20 HAS_METHOD(viewport)
21 21
22 22 template <typename T>
23 using is_QWidgetOrDerived = std::is_base_of<QWidget,T>;
23 static inline constexpr bool is_QWidgetOrDerived = std::is_base_of<QWidget,T>::value;
24 24
25 25 template <typename T> using viewport_type = decltype(std::declval<T>().viewport());
26 26
@@ -31,9 +31,16 void mouseMove(T* widget, QPoint pos, Qt::MouseButton mouseModifier)
31 31 {
32 32 QCursor::setPos(widget->mapToGlobal(pos));
33 33 QMouseEvent event(QEvent::MouseMove, pos, Qt::NoButton, mouseModifier, Qt::NoModifier);
34 if constexpr(has_viewport<T> && is_QWidgetOrDerived<viewport_type<T>>::value )
34 if constexpr(has_viewport<T>)
35 35 {
36 qApp->sendEvent(widget->viewport(), &event);
36 if constexpr(is_QWidgetOrDerived<viewport_type<T>>)
37 {
38 qApp->sendEvent(widget->viewport(), &event);
39 }
40 else
41 {
42 qApp->sendEvent(widget, &event);
43 }
37 44 }
38 45 else
39 46 {
@@ -46,9 +53,16 void mouseMove(T* widget, QPoint pos, Qt::MouseButton mouseModifier)
46 53 template <typename T>
47 54 void setMouseTracking(T* widget)
48 55 {
49 if constexpr(has_viewport<T> && is_QWidgetOrDerived<viewport_type<T>>::value)
56 if constexpr(has_viewport<T>)
50 57 {
51 widget->viewport()->setMouseTracking(true);
58 if constexpr(is_QWidgetOrDerived<viewport_type<T>>)
59 {
60 widget->viewport()->setMouseTracking(true);
61 }
62 else
63 {
64 widget->setMouseTracking(true);
65 }
52 66 }
53 67 else
54 68 {
@@ -30,7 +30,7 ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication *>(qApp)->variableCo
30 30 QCoreApplication::processEvents();\
31 31 w.addVariable(var, range);\
32 32 GET_CHILD_WIDGET_FOR_GUI_TESTS(w, plot, QCustomPlot, "widget");\
33 auto cent = center(plot);\
33 auto cent = center(&w);\
34 34
35 35
36 36 class A_SimpleGraph : public QObject {
@@ -44,23 +44,20 private slots:
44 44 A_SIMPLE_GRAPH_FIXTURE
45 45
46 46 for (auto i = 0; i < 10; i++) {
47 QTest::mousePress(plot, Qt::LeftButton, Qt::NoModifier, cent, 5);
48 mouseMove(plot, {cent.x() + 200, cent.y()}, Qt::LeftButton);
49 QTest::mouseRelease(plot, Qt::LeftButton);
47 QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 500);
48 mouseMove(&w, {cent.x() + 200, cent.y()}, Qt::LeftButton);
49 QTest::mouseRelease(&w, Qt::LeftButton);
50 50 while (!isReady(var))
51 51 QCoreApplication::processEvents();
52 /*
53 * Just for visual inspection while running tests
54 */
55 plot->rescaleAxes();
56 plot->replot();
57 QCoreApplication::processEvents();
58 52 }
59 53 while (!isReady(var))
60 54 QCoreApplication::processEvents();
61 55 auto r = var->range();
56 /*
57 * Scrolling to the left implies going back in time
58 * Scroll only implies keeping the same delta T -> shit only transformation
59 */
62 60 QVERIFY(r.m_TEnd < range.m_TEnd);
63 // this fails :(
64 61 QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1));
65 62 }
66 63 };
General Comments 0
You need to be logged in to leave comments. Login now