##// END OF EJS Templates
Partly reimplemented Graph event handling + bugfix...
jeandet -
r1362:0033c08c242f Pybind11
parent child
Show More
@@ -1,160 +1,169
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3
3
4 #include "Visualization/IVisualizationWidget.h"
4 #include "Visualization/IVisualizationWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
6
6
7 #include <QLoggingCategory>
7 #include <QLoggingCategory>
8 #include <QWidget>
8 #include <QWidget>
9 #include <QUuid>
9 #include <QUuid>
10
10
11 #include <memory>
11 #include <memory>
12
12
13 #include <Common/spimpl.h>
13 #include <Common/spimpl.h>
14
14
15 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
16
16
17 class QCPRange;
17 class QCPRange;
18 class QCustomPlot;
18 class QCustomPlot;
19 class DateTimeRange;
19 class DateTimeRange;
20 class Variable;
20 class Variable;
21 class VisualizationWidget;
21 class VisualizationWidget;
22 class VisualizationZoneWidget;
22 class VisualizationZoneWidget;
23 class VisualizationSelectionZoneItem;
23 class VisualizationSelectionZoneItem;
24
24
25 namespace Ui {
25 namespace Ui {
26 class VisualizationGraphWidget;
26 class VisualizationGraphWidget;
27 } // namespace Ui
27 } // namespace Ui
28
28
29 /// Defines options that can be associated with the graph
29 /// Defines options that can be associated with the graph
30 enum GraphFlag {
30 enum GraphFlag {
31 DisableAll = 0x0, ///< Disables acquisition and synchronization
31 DisableAll = 0x0, ///< Disables acquisition and synchronization
32 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
32 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
33 /// the acquisition of data
33 /// the acquisition of data
34 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
34 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
35 /// the call to the synchronization of the graphs contained in the
35 /// the call to the synchronization of the graphs contained in the
36 /// same zone of this graph
36 /// same zone of this graph
37 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
37 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
38 };
38 };
39
39
40 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
40 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
41 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
41 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
42
42
43 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
43 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
44 Q_OBJECT
44 Q_OBJECT
45
45
46 friend class QCustomPlotSynchronizer;
46 friend class QCustomPlotSynchronizer;
47 friend class VisualizationGraphRenderingDelegate;
47 friend class VisualizationGraphRenderingDelegate;
48
48
49 public:
49 public:
50 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
50 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
51 virtual ~VisualizationGraphWidget();
51 virtual ~VisualizationGraphWidget();
52
52
53 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
53 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
54 VisualizationZoneWidget *parentZoneWidget() const noexcept;
54 VisualizationZoneWidget *parentZoneWidget() const noexcept;
55
55
56 /// Returns the main VisualizationWidget which contains the graph or nullptr
56 /// Returns the main VisualizationWidget which contains the graph or nullptr
57 VisualizationWidget *parentVisualizationWidget() const;
57 VisualizationWidget *parentVisualizationWidget() const;
58
58
59 /// Sets graph options
59 /// Sets graph options
60 void setFlags(GraphFlags flags);
60 void setFlags(GraphFlags flags);
61
61
62 void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range);
62 void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range);
63
63
64 /// Removes a variable from the graph
64 /// Removes a variable from the graph
65 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
65 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
66
66
67 /// Returns the list of all variables used in the graph
67 /// Returns the list of all variables used in the graph
68 std::vector<std::shared_ptr<Variable> > variables() const;
68 std::vector<std::shared_ptr<Variable> > variables() const;
69
69
70 /// Sets the y-axis range based on the data of a variable
70 /// Sets the y-axis range based on the data of a variable
71 void setYRange(std::shared_ptr<Variable> variable);
71 void setYRange(std::shared_ptr<Variable> variable);
72 DateTimeRange graphRange() const noexcept;
72 DateTimeRange graphRange() const noexcept;
73 void setGraphRange(const DateTimeRange &range, bool calibration = false);
73 void setGraphRange(const DateTimeRange &range, bool calibration = false);
74 void setAutoRangeOnVariableInitialization(bool value);
74 void setAutoRangeOnVariableInitialization(bool value);
75
75
76 // Zones
76 // Zones
77 /// Returns the ranges of all the selection zones on the graph
77 /// Returns the ranges of all the selection zones on the graph
78 QVector<DateTimeRange> selectionZoneRanges() const;
78 QVector<DateTimeRange> selectionZoneRanges() const;
79 /// Adds new selection zones in the graph
79 /// Adds new selection zones in the graph
80 void addSelectionZones(const QVector<DateTimeRange> &ranges);
80 void addSelectionZones(const QVector<DateTimeRange> &ranges);
81 /// Adds a new selection zone in the graph
81 /// Adds a new selection zone in the graph
82 VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range);
82 VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range);
83 /// Removes the specified selection zone
83 /// Removes the specified selection zone
84 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
84 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
85
85
86 /// Undo the last zoom done with a zoom box
86 /// Undo the last zoom done with a zoom box
87 void undoZoom();
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 // IVisualizationWidget interface
92 // IVisualizationWidget interface
90 void accept(IVisualizationWidgetVisitor *visitor) override;
93 void accept(IVisualizationWidgetVisitor *visitor) override;
91 bool canDrop(const Variable &variable) const override;
94 bool canDrop(const Variable &variable) const override;
92 bool contains(const Variable &variable) const override;
95 bool contains(const Variable &variable) const override;
93 QString name() const override;
96 QString name() const override;
94
97
95 // VisualisationDragWidget
98 // VisualisationDragWidget
96 QMimeData *mimeData(const QPoint &position) const override;
99 QMimeData *mimeData(const QPoint &position) const override;
97 QPixmap customDragPixmap(const QPoint &dragPosition) override;
100 QPixmap customDragPixmap(const QPoint &dragPosition) override;
98 bool isDragAllowed() const override;
101 bool isDragAllowed() const override;
99 void highlightForMerge(bool highlighted) override;
102 void highlightForMerge(bool highlighted) override;
100
103
101 // Cursors
104 // Cursors
102 /// Adds or moves the vertical cursor at the specified value on the x-axis
105 /// Adds or moves the vertical cursor at the specified value on the x-axis
103 void addVerticalCursor(double time);
106 void addVerticalCursor(double time);
104 /// Adds or moves the vertical cursor at the specified value on the x-axis
107 /// Adds or moves the vertical cursor at the specified value on the x-axis
105 void addVerticalCursorAtViewportPosition(double position);
108 void addVerticalCursorAtViewportPosition(double position);
106 void removeVerticalCursor();
109 void removeVerticalCursor();
107 /// Adds or moves the vertical cursor at the specified value on the y-axis
110 /// Adds or moves the vertical cursor at the specified value on the y-axis
108 void addHorizontalCursor(double value);
111 void addHorizontalCursor(double value);
109 /// Adds or moves the vertical cursor at the specified value on the y-axis
112 /// Adds or moves the vertical cursor at the specified value on the y-axis
110 void addHorizontalCursorAtViewportPosition(double position);
113 void addHorizontalCursorAtViewportPosition(double position);
111 void removeHorizontalCursor();
114 void removeHorizontalCursor();
112
115
113 signals:
116 signals:
114 void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange);
117 void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange);
115 void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange &range);
118 void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange &range);
116
119
117 /// Signal emitted when the variable is about to be removed from the graph
120 /// Signal emitted when the variable is about to be removed from the graph
118 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
121 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
119 /// Signal emitted when the variable has been added to the graph
122 /// Signal emitted when the variable has been added to the graph
120 void variableAdded(std::shared_ptr<Variable> var);
123 void variableAdded(std::shared_ptr<Variable> var);
121
124
122 protected:
125 protected:
123 void closeEvent(QCloseEvent *event) override;
126 void closeEvent(QCloseEvent *event) override;
124 void enterEvent(QEvent *event) override;
127 void enterEvent(QEvent *event) override;
125 void leaveEvent(QEvent *event) override;
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 QCustomPlot &plot() const noexcept;
136 QCustomPlot &plot() const noexcept;
128
137
129 private:
138 private:
130 Ui::VisualizationGraphWidget *ui;
139 Ui::VisualizationGraphWidget *ui;
131
140
132 class VisualizationGraphWidgetPrivate;
141 class VisualizationGraphWidgetPrivate;
133 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
142 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
134
143 QPoint _dragLastPos;
135 private slots:
144 private slots:
136 /// Slot called when right clicking on the graph (displays a menu)
145 /// Slot called when right clicking on the graph (displays a menu)
137 void onGraphMenuRequested(const QPoint &pos) noexcept;
146 void onGraphMenuRequested(const QPoint &pos) noexcept;
138
147
139 /// Rescale the X axe to range parameter
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 /// Slot called when a mouse double click was made
151 /// Slot called when a mouse double click was made
143 void onMouseDoubleClick(QMouseEvent *event) noexcept;
152 void onMouseDoubleClick(QMouseEvent *event) noexcept;
144 /// Slot called when a mouse move was made
153 /// Slot called when a mouse move was made
145 void onMouseMove(QMouseEvent *event) noexcept;
154 void onMouseMove(QMouseEvent *event) noexcept;
146 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
155 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
147 void onMouseWheel(QWheelEvent *event) noexcept;
156 void onMouseWheel(QWheelEvent *event) noexcept;
148 /// Slot called when a mouse press was made, to activate the calibration of a graph
157 /// Slot called when a mouse press was made, to activate the calibration of a graph
149 void onMousePress(QMouseEvent *event) noexcept;
158 void onMousePress(QMouseEvent *event) noexcept;
150 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
159 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
151 void onMouseRelease(QMouseEvent *event) noexcept;
160 void onMouseRelease(QMouseEvent *event) noexcept;
152
161
153 void onDataCacheVariableUpdated();
162 void onDataCacheVariableUpdated();
154
163
155 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range);
164 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range);
156
165
157 void variableUpdated(QUuid id);
166 void variableUpdated(QUuid id);
158 };
167 };
159
168
160 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
169 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,1045 +1,1232
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
13
13
14 #include <Actions/ActionsGuiController.h>
14 #include <Actions/ActionsGuiController.h>
15 #include <Actions/FilteringAction.h>
15 #include <Actions/FilteringAction.h>
16 #include <Common/MimeTypesDef.h>
16 #include <Common/MimeTypesDef.h>
17 #include <Data/ArrayData.h>
17 #include <Data/ArrayData.h>
18 #include <Data/IDataSeries.h>
18 #include <Data/IDataSeries.h>
19 #include <Data/SpectrogramSeries.h>
19 #include <Data/SpectrogramSeries.h>
20 #include <DragAndDrop/DragDropGuiController.h>
20 #include <DragAndDrop/DragDropGuiController.h>
21 #include <Settings/SqpSettingsDefs.h>
21 #include <Settings/SqpSettingsDefs.h>
22 #include <SqpApplication.h>
22 #include <SqpApplication.h>
23 #include <Time/TimeController.h>
23 #include <Time/TimeController.h>
24 #include <Variable/Variable.h>
24 #include <Variable/Variable.h>
25 #include <Variable/VariableController2.h>
25 #include <Variable/VariableController2.h>
26
26
27 #include <unordered_map>
27 #include <unordered_map>
28
28
29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
30
30
31 namespace {
31 namespace {
32
32
33 /// Key pressed to enable drag&drop in all modes
33 /// Key pressed to enable drag&drop in all modes
34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
35
35
36 /// Key pressed to enable zoom on horizontal axis
36 /// Key pressed to enable zoom on horizontal axis
37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
38
38
39 /// Key pressed to enable zoom on vertical axis
39 /// Key pressed to enable zoom on vertical axis
40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
41
41
42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
43 const auto PAN_SPEED = 5;
43 const auto PAN_SPEED = 5;
44
44
45 /// Key pressed to enable a calibration pan
45 /// Key pressed to enable a calibration pan
46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
47
47
48 /// Key pressed to enable multi selection of selection zones
48 /// Key pressed to enable multi selection of selection zones
49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
50
50
51 /// Minimum size for the zoom box, in percentage of the axis range
51 /// Minimum size for the zoom box, in percentage of the axis range
52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
53
53
54 /// Format of the dates appearing in the label of a cursor
54 /// Format of the dates appearing in the label of a cursor
55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
56
56
57 } // namespace
57 } // namespace
58
58
59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
60
60
61 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 explicit VisualizationGraphWidgetPrivate(const QString &name)
62 : m_Name{name},
62 : m_Name{name},
63 m_Flags{GraphFlag::EnableAll},
63 m_Flags{GraphFlag::EnableAll},
64 m_IsCalibration{false},
64 m_IsCalibration{false},
65 m_RenderingDelegate{nullptr}
65 m_RenderingDelegate{nullptr}
66 {
66 {
67 }
67 }
68
68
69 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
69 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
70 const DateTimeRange &range)
70 const DateTimeRange &range)
71 {
71 {
72 VisualizationGraphHelper::updateData(plottables, variable, range);
72 VisualizationGraphHelper::updateData(plottables, variable, range);
73
73
74 // Prevents that data has changed to update rendering
74 // Prevents that data has changed to update rendering
75 m_RenderingDelegate->onPlotUpdated();
75 m_RenderingDelegate->onPlotUpdated();
76 }
76 }
77
77
78 QString m_Name;
78 QString m_Name;
79 // 1 variable -> n qcpplot
79 // 1 variable -> n qcpplot
80 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
81 GraphFlags m_Flags;
81 GraphFlags m_Flags;
82 bool m_IsCalibration;
82 bool m_IsCalibration;
83 /// Delegate used to attach rendering features to the plot
83 /// Delegate used to attach rendering features to the plot
84 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
85
85
86 QCPItemRect *m_DrawingZoomRect = nullptr;
86 QCPItemRect *m_DrawingZoomRect = nullptr;
87 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
88
88
89 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
90 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
91
91
92 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
93 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
94 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
95
95
96 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
97
97
98 bool m_VariableAutoRangeOnInit = true;
98 bool m_VariableAutoRangeOnInit = true;
99
99
100 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
100 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
101 {
101 {
102 removeDrawingRect(plot);
102 removeDrawingRect(plot);
103
103
104 auto axisPos = posToAxisPos(pos, plot);
104 auto axisPos = posToAxisPos(pos, plot);
105
105
106 m_DrawingZoomRect = new QCPItemRect{&plot};
106 m_DrawingZoomRect = new QCPItemRect{&plot};
107 QPen p;
107 QPen p;
108 p.setWidth(2);
108 p.setWidth(2);
109 m_DrawingZoomRect->setPen(p);
109 m_DrawingZoomRect->setPen(p);
110
110
111 m_DrawingZoomRect->topLeft->setCoords(axisPos);
111 m_DrawingZoomRect->topLeft->setCoords(axisPos);
112 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
112 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
113 }
113 }
114
114
115 void removeDrawingRect(QCustomPlot &plot)
115 void removeDrawingRect(QCustomPlot &plot)
116 {
116 {
117 if (m_DrawingZoomRect) {
117 if (m_DrawingZoomRect) {
118 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
118 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
119 m_DrawingZoomRect = nullptr;
119 m_DrawingZoomRect = nullptr;
120 plot.replot(QCustomPlot::rpQueuedReplot);
120 plot.replot(QCustomPlot::rpQueuedReplot);
121 }
121 }
122 }
122 }
123
123
124 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
124 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
125 {
125 {
126 endDrawingZone(graph);
126 endDrawingZone(graph);
127
127
128 auto axisPos = posToAxisPos(pos, graph->plot());
128 auto axisPos = posToAxisPos(pos, graph->plot());
129
129
130 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
130 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
131 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
131 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
132 m_DrawingZone->setEditionEnabled(false);
132 m_DrawingZone->setEditionEnabled(false);
133 }
133 }
134
134
135 void endDrawingZone(VisualizationGraphWidget *graph)
135 void endDrawingZone(VisualizationGraphWidget *graph)
136 {
136 {
137 if (m_DrawingZone) {
137 if (m_DrawingZone) {
138 auto drawingZoneRange = m_DrawingZone->range();
138 auto drawingZoneRange = m_DrawingZone->range();
139 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
139 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
140 m_DrawingZone->setEditionEnabled(true);
140 m_DrawingZone->setEditionEnabled(true);
141 addSelectionZone(m_DrawingZone);
141 addSelectionZone(m_DrawingZone);
142 }
142 }
143 else {
143 else {
144 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
144 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
145 }
145 }
146
146
147 graph->plot().replot(QCustomPlot::rpQueuedReplot);
147 graph->plot().replot(QCustomPlot::rpQueuedReplot);
148 m_DrawingZone = nullptr;
148 m_DrawingZone = nullptr;
149 }
149 }
150 }
150 }
151
151
152 void setSelectionZonesEditionEnabled(bool value)
152 void setSelectionZonesEditionEnabled(bool value)
153 {
153 {
154 for (auto s : m_SelectionZones) {
154 for (auto s : m_SelectionZones) {
155 s->setEditionEnabled(value);
155 s->setEditionEnabled(value);
156 }
156 }
157 }
157 }
158
158
159 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
159 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
160
160
161 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
161 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
162 const QCustomPlot &plot) const
162 const QCustomPlot &plot) const
163 {
163 {
164 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
164 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
165 auto minDistanceToZone = -1;
165 auto minDistanceToZone = -1;
166 for (auto zone : m_SelectionZones) {
166 for (auto zone : m_SelectionZones) {
167 auto distanceToZone = zone->selectTest(pos, false);
167 auto distanceToZone = zone->selectTest(pos, false);
168 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
168 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
169 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
169 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
170 selectionZoneItemUnderCursor = zone;
170 selectionZoneItemUnderCursor = zone;
171 }
171 }
172 }
172 }
173
173
174 return selectionZoneItemUnderCursor;
174 return selectionZoneItemUnderCursor;
175 }
175 }
176
176
177 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
177 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
178 const QCustomPlot &plot) const
178 const QCustomPlot &plot) const
179 {
179 {
180 QVector<VisualizationSelectionZoneItem *> zones;
180 QVector<VisualizationSelectionZoneItem *> zones;
181 for (auto zone : m_SelectionZones) {
181 for (auto zone : m_SelectionZones) {
182 auto distanceToZone = zone->selectTest(pos, false);
182 auto distanceToZone = zone->selectTest(pos, false);
183 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
183 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
184 zones << zone;
184 zones << zone;
185 }
185 }
186 }
186 }
187
187
188 return zones;
188 return zones;
189 }
189 }
190
190
191 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
191 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
192 {
192 {
193 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
193 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
194 zone->moveToTop();
194 zone->moveToTop();
195 m_SelectionZones.removeAll(zone);
195 m_SelectionZones.removeAll(zone);
196 m_SelectionZones.append(zone);
196 m_SelectionZones.append(zone);
197 }
197 }
198 }
198 }
199
199
200 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
200 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
201 {
201 {
202 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
202 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
203 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
203 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
204 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
204 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
205 }
205 }
206
206
207 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
207 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
208 {
208 {
209 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
209 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
210 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
210 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
211 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
211 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
212 }
212 }
213 };
213 };
214
214
215 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
215 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
216 : VisualizationDragWidget{parent},
216 : VisualizationDragWidget{parent},
217 ui{new Ui::VisualizationGraphWidget},
217 ui{new Ui::VisualizationGraphWidget},
218 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
218 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
219 {
219 {
220 ui->setupUi(this);
220 ui->setupUi(this);
221
221
222 // 'Close' options : widget is deleted when closed
222 // 'Close' options : widget is deleted when closed
223 setAttribute(Qt::WA_DeleteOnClose);
223 setAttribute(Qt::WA_DeleteOnClose);
224
224
225 // The delegate must be initialized after the ui as it uses the plot
225 // The delegate must be initialized after the ui as it uses the plot
226 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
226 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
227
227
228 // Init the cursors
228 // Init the cursors
229 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
229 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
230 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
230 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
231 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
231 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
232 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
232 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
233
233
234 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
234 // ↓ swhitch to this ASAP, VisualizationGraphWidget should intercept all UI events
235 connect(ui->widget, &QCustomPlot::mouseRelease, this,
235 this->setFocusPolicy(Qt::WheelFocus);
236 &VisualizationGraphWidget::onMouseRelease);
236 ui->widget->setAttribute(Qt::WA_TransparentForMouseEvents);
237 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
237 // connect(ui->widget, &QCustomPlot::mousePress, this,
238 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
238 // &VisualizationGraphWidget::onMousePress); connect(ui->widget, &QCustomPlot::mouseRelease,
239 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
239 // this,
240 &VisualizationGraphWidget::onMouseDoubleClick);
240 // &VisualizationGraphWidget::onMouseRelease);
241 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
241 // connect(ui->widget, &QCustomPlot::mouseMove, this,
242 &QCPAxis::rangeChanged),
242 // &VisualizationGraphWidget::onMouseMove); connect(ui->widget, &QCustomPlot::mouseWheel,
243 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
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 // Activates menu when right clicking on the graph
251 // Activates menu when right clicking on the graph
246 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
252 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
247 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
253 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
248 &VisualizationGraphWidget::onGraphMenuRequested);
254 &VisualizationGraphWidget::onGraphMenuRequested);
249
255
250 //@TODO implement this :)
256 //@TODO implement this :)
251 // connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
257 // connect(this, &VisualizationGraphWidget::requestDataLoading,
258 // &sqpApp->variableController(),
252 // &VariableController::onRequestDataLoading);
259 // &VariableController::onRequestDataLoading);
253
260
254 // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this,
261 // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this,
255 // &VisualizationGraphWidget::onUpdateVarDisplaying);
262 // &VisualizationGraphWidget::onUpdateVarDisplaying);
256
263
257 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
264 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
258 plot().setPlottingHint(QCP::phFastPolylines, true);
265 plot().setPlottingHint(QCP::phFastPolylines, true);
259 }
266 }
260
267
261
268
262 VisualizationGraphWidget::~VisualizationGraphWidget()
269 VisualizationGraphWidget::~VisualizationGraphWidget()
263 {
270 {
264 delete ui;
271 delete ui;
265 }
272 }
266
273
267 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
274 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
268 {
275 {
269 auto parent = parentWidget();
276 auto parent = parentWidget();
270 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
277 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
271 parent = parent->parentWidget();
278 parent = parent->parentWidget();
272 }
279 }
273
280
274 return qobject_cast<VisualizationZoneWidget *>(parent);
281 return qobject_cast<VisualizationZoneWidget *>(parent);
275 }
282 }
276
283
277 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
284 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
278 {
285 {
279 auto parent = parentWidget();
286 auto parent = parentWidget();
280 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
287 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
281 parent = parent->parentWidget();
288 parent = parent->parentWidget();
282 }
289 }
283
290
284 return qobject_cast<VisualizationWidget *>(parent);
291 return qobject_cast<VisualizationWidget *>(parent);
285 }
292 }
286
293
287 void VisualizationGraphWidget::setFlags(GraphFlags flags)
294 void VisualizationGraphWidget::setFlags(GraphFlags flags)
288 {
295 {
289 impl->m_Flags = std::move(flags);
296 impl->m_Flags = std::move(flags);
290 }
297 }
291
298
292 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
299 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
293 {
300 {
294 // Uses delegate to create the qcpplot components according to the variable
301 // Uses delegate to create the qcpplot components according to the variable
295 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
302 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
296
303
297 // Sets graph properties
304 // Sets graph properties
298 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
305 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
299
306
300 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
307 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
301
308
302 // If the variable already has its data loaded, load its units and its range in the graph
309 // If the variable already has its data loaded, load its units and its range in the graph
303 if (variable->dataSeries() != nullptr) {
310 if (variable->dataSeries() != nullptr) {
304 impl->m_RenderingDelegate->setAxesUnits(*variable);
311 impl->m_RenderingDelegate->setAxesUnits(*variable);
305 this->setFlags(GraphFlag::DisableAll);
312 this->setFlags(GraphFlag::DisableAll);
306 setGraphRange(range);
313 setGraphRange(range);
307 this->setFlags(GraphFlag::EnableAll);
314 this->setFlags(GraphFlag::EnableAll);
308 }
315 }
309 //@TODO this is bad! when variable is moved to another graph it still fires
316 //@TODO this is bad! when variable is moved to another graph it still fires
310 // even if this has been deleted
317 // even if this has been deleted
311 connect(variable.get(),&Variable::updated,this, &VisualizationGraphWidget::variableUpdated);
318 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
312 this->onUpdateVarDisplaying(variable,range);//My bullshit
319 this->onUpdateVarDisplaying(variable, range); // My bullshit
313 emit variableAdded(variable);
320 emit variableAdded(variable);
314 }
321 }
315
322
316 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
323 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
317 {
324 {
318 // Each component associated to the variable :
325 // Each component associated to the variable :
319 // - is removed from qcpplot (which deletes it)
326 // - is removed from qcpplot (which deletes it)
320 // - is no longer referenced in the map
327 // - is no longer referenced in the map
321 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
328 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
322 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
329 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
323 emit variableAboutToBeRemoved(variable);
330 emit variableAboutToBeRemoved(variable);
324
331
325 auto &plottablesMap = variableIt->second;
332 auto &plottablesMap = variableIt->second;
326
333
327 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
334 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
328 plottableIt != plottableEnd;) {
335 plottableIt != plottableEnd;) {
329 ui->widget->removePlottable(plottableIt->second);
336 ui->widget->removePlottable(plottableIt->second);
330 plottableIt = plottablesMap.erase(plottableIt);
337 plottableIt = plottablesMap.erase(plottableIt);
331 }
338 }
332
339
333 impl->m_VariableToPlotMultiMap.erase(variableIt);
340 impl->m_VariableToPlotMultiMap.erase(variableIt);
334 }
341 }
335
342
336 // Updates graph
343 // Updates graph
337 ui->widget->replot();
344 ui->widget->replot(QCustomPlot::rpQueuedReplot);
338 }
345 }
339
346
340 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
347 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
341 {
348 {
342 auto variables = std::vector<std::shared_ptr<Variable> >{};
349 auto variables = std::vector<std::shared_ptr<Variable> >{};
343 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
350 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
344 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
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 return variables;
355 return variables;
349 }
356 }
350
357
351 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
358 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
352 {
359 {
353 if (!variable) {
360 if (!variable) {
354 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
361 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
355 return;
362 return;
356 }
363 }
357
364
358 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
365 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
359 }
366 }
360
367
361 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
368 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
362 {
369 {
363 auto graphRange = ui->widget->xAxis->range();
370 auto graphRange = ui->widget->xAxis->range();
364 return DateTimeRange{graphRange.lower, graphRange.upper};
371 return DateTimeRange{graphRange.lower, graphRange.upper};
365 }
372 }
366
373
367 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration)
374 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration)
368 {
375 {
369 if (calibration) {
376 if (calibration) {
370 impl->m_IsCalibration = true;
377 impl->m_IsCalibration = true;
371 }
378 }
372
379
373 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
380 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
374 ui->widget->replot();
381 ui->widget->replot(QCustomPlot::rpQueuedReplot);
375
382
376 if (calibration) {
383 if (calibration) {
377 impl->m_IsCalibration = false;
384 impl->m_IsCalibration = false;
378 }
385 }
379 }
386 }
380
387
381 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
388 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
382 {
389 {
383 impl->m_VariableAutoRangeOnInit = value;
390 impl->m_VariableAutoRangeOnInit = value;
384 }
391 }
385
392
386 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
393 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
387 {
394 {
388 QVector<DateTimeRange> ranges;
395 QVector<DateTimeRange> ranges;
389 for (auto zone : impl->m_SelectionZones) {
396 for (auto zone : impl->m_SelectionZones) {
390 ranges << zone->range();
397 ranges << zone->range();
391 }
398 }
392
399
393 return ranges;
400 return ranges;
394 }
401 }
395
402
396 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
403 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
397 {
404 {
398 for (const auto &range : ranges) {
405 for (const auto &range : ranges) {
399 // note: ownership is transfered to QCustomPlot
406 // note: ownership is transfered to QCustomPlot
400 auto zone = new VisualizationSelectionZoneItem(&plot());
407 auto zone = new VisualizationSelectionZoneItem(&plot());
401 zone->setRange(range.m_TStart, range.m_TEnd);
408 zone->setRange(range.m_TStart, range.m_TEnd);
402 impl->addSelectionZone(zone);
409 impl->addSelectionZone(zone);
403 }
410 }
404
411
405 plot().replot(QCustomPlot::rpQueuedReplot);
412 plot().replot(QCustomPlot::rpQueuedReplot);
406 }
413 }
407
414
408 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
415 VisualizationSelectionZoneItem *
409 const DateTimeRange &range)
416 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
410 {
417 {
411 // note: ownership is transfered to QCustomPlot
418 // note: ownership is transfered to QCustomPlot
412 auto zone = new VisualizationSelectionZoneItem(&plot());
419 auto zone = new VisualizationSelectionZoneItem(&plot());
413 zone->setName(name);
420 zone->setName(name);
414 zone->setRange(range.m_TStart, range.m_TEnd);
421 zone->setRange(range.m_TStart, range.m_TEnd);
415 impl->addSelectionZone(zone);
422 impl->addSelectionZone(zone);
416
423
417 plot().replot(QCustomPlot::rpQueuedReplot);
424 plot().replot(QCustomPlot::rpQueuedReplot);
418
425
419 return zone;
426 return zone;
420 }
427 }
421
428
422 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
429 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
423 {
430 {
424 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
431 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
425
432
426 if (impl->m_HoveredZone == selectionZone) {
433 if (impl->m_HoveredZone == selectionZone) {
427 impl->m_HoveredZone = nullptr;
434 impl->m_HoveredZone = nullptr;
428 setCursor(Qt::ArrowCursor);
435 setCursor(Qt::ArrowCursor);
429 }
436 }
430
437
431 impl->m_SelectionZones.removeAll(selectionZone);
438 impl->m_SelectionZones.removeAll(selectionZone);
432 plot().removeItem(selectionZone);
439 plot().removeItem(selectionZone);
433 plot().replot(QCustomPlot::rpQueuedReplot);
440 plot().replot(QCustomPlot::rpQueuedReplot);
434 }
441 }
435
442
436 void VisualizationGraphWidget::undoZoom()
443 void VisualizationGraphWidget::undoZoom()
437 {
444 {
438 auto zoom = impl->m_ZoomStack.pop();
445 auto zoom = impl->m_ZoomStack.pop();
439 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
446 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
440 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
447 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
441
448
442 axisX->setRange(zoom.first);
449 axisX->setRange(zoom.first);
443 axisY->setRange(zoom.second);
450 axisY->setRange(zoom.second);
444
451
445 plot().replot(QCustomPlot::rpQueuedReplot);
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 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
495 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
449 {
496 {
450 if (visitor) {
497 if (visitor) {
451 visitor->visit(this);
498 visitor->visit(this);
452 }
499 }
453 else {
500 else {
454 qCCritical(LOG_VisualizationGraphWidget())
501 qCCritical(LOG_VisualizationGraphWidget())
455 << tr("Can't visit widget : the visitor is null");
502 << tr("Can't visit widget : the visitor is null");
456 }
503 }
457 }
504 }
458
505
459 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
506 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
460 {
507 {
461 auto isSpectrogram = [](const auto &variable) {
508 auto isSpectrogram = [](const auto &variable) {
462 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
509 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
463 };
510 };
464
511
465 // - A spectrogram series can't be dropped on graph with existing plottables
512 // - A spectrogram series can't be dropped on graph with existing plottables
466 // - No data series can be dropped on graph with existing spectrogram series
513 // - No data series can be dropped on graph with existing spectrogram series
467 return isSpectrogram(variable)
514 return isSpectrogram(variable)
468 ? impl->m_VariableToPlotMultiMap.empty()
515 ? impl->m_VariableToPlotMultiMap.empty()
469 : std::none_of(
516 : std::none_of(
470 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
517 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
471 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
518 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
472 }
519 }
473
520
474 bool VisualizationGraphWidget::contains(const Variable &variable) const
521 bool VisualizationGraphWidget::contains(const Variable &variable) const
475 {
522 {
476 // Finds the variable among the keys of the map
523 // Finds the variable among the keys of the map
477 auto variablePtr = &variable;
524 auto variablePtr = &variable;
478 auto findVariable
525 auto findVariable
479 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
526 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
480
527
481 auto end = impl->m_VariableToPlotMultiMap.cend();
528 auto end = impl->m_VariableToPlotMultiMap.cend();
482 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
529 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
483 return it != end;
530 return it != end;
484 }
531 }
485
532
486 QString VisualizationGraphWidget::name() const
533 QString VisualizationGraphWidget::name() const
487 {
534 {
488 return impl->m_Name;
535 return impl->m_Name;
489 }
536 }
490
537
491 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
538 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
492 {
539 {
493 auto mimeData = new QMimeData;
540 auto mimeData = new QMimeData;
494
541
495 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
542 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
496 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
543 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
497 && selectionZoneItemUnderCursor) {
544 && selectionZoneItemUnderCursor) {
498 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
545 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
499 selectionZoneItemUnderCursor->range()));
546 selectionZoneItemUnderCursor->range()));
500 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
547 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
501 selectionZoneItemUnderCursor->range()));
548 selectionZoneItemUnderCursor->range()));
502 }
549 }
503 else {
550 else {
504 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
551 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
505
552
506 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
553 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
507 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
554 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
508 }
555 }
509
556
510 return mimeData;
557 return mimeData;
511 }
558 }
512
559
513 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
560 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
514 {
561 {
515 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
562 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
516 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
563 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
517 && selectionZoneItemUnderCursor) {
564 && selectionZoneItemUnderCursor) {
518
565
519 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
566 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
520 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
567 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
521
568
522 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
569 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
523 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
570 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
524 .toSize();
571 .toSize();
525
572
526 auto pixmap = QPixmap(zoneSize);
573 auto pixmap = QPixmap(zoneSize);
527 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
574 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
528
575
529 return pixmap;
576 return pixmap;
530 }
577 }
531
578
532 return QPixmap();
579 return QPixmap();
533 }
580 }
534
581
535 bool VisualizationGraphWidget::isDragAllowed() const
582 bool VisualizationGraphWidget::isDragAllowed() const
536 {
583 {
537 return true;
584 return true;
538 }
585 }
539
586
540 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
587 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
541 {
588 {
542 if (highlighted) {
589 if (highlighted) {
543 plot().setBackground(QBrush(QColor("#BBD5EE")));
590 plot().setBackground(QBrush(QColor("#BBD5EE")));
544 }
591 }
545 else {
592 else {
546 plot().setBackground(QBrush(Qt::white));
593 plot().setBackground(QBrush(Qt::white));
547 }
594 }
548
595
549 plot().update();
596 plot().update();
550 }
597 }
551
598
552 void VisualizationGraphWidget::addVerticalCursor(double time)
599 void VisualizationGraphWidget::addVerticalCursor(double time)
553 {
600 {
554 impl->m_VerticalCursor->setPosition(time);
601 impl->m_VerticalCursor->setPosition(time);
555 impl->m_VerticalCursor->setVisible(true);
602 impl->m_VerticalCursor->setVisible(true);
556
603
557 auto text
604 auto text
558 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
605 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
559 impl->m_VerticalCursor->setLabelText(text);
606 impl->m_VerticalCursor->setLabelText(text);
560 }
607 }
561
608
562 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
609 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
563 {
610 {
564 impl->m_VerticalCursor->setAbsolutePosition(position);
611 impl->m_VerticalCursor->setAbsolutePosition(position);
565 impl->m_VerticalCursor->setVisible(true);
612 impl->m_VerticalCursor->setVisible(true);
566
613
567 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
614 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
568 auto text
615 auto text
569 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
616 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
570 impl->m_VerticalCursor->setLabelText(text);
617 impl->m_VerticalCursor->setLabelText(text);
571 }
618 }
572
619
573 void VisualizationGraphWidget::removeVerticalCursor()
620 void VisualizationGraphWidget::removeVerticalCursor()
574 {
621 {
575 impl->m_VerticalCursor->setVisible(false);
622 impl->m_VerticalCursor->setVisible(false);
576 plot().replot(QCustomPlot::rpQueuedReplot);
623 plot().replot(QCustomPlot::rpQueuedReplot);
577 }
624 }
578
625
579 void VisualizationGraphWidget::addHorizontalCursor(double value)
626 void VisualizationGraphWidget::addHorizontalCursor(double value)
580 {
627 {
581 impl->m_HorizontalCursor->setPosition(value);
628 impl->m_HorizontalCursor->setPosition(value);
582 impl->m_HorizontalCursor->setVisible(true);
629 impl->m_HorizontalCursor->setVisible(true);
583 impl->m_HorizontalCursor->setLabelText(QString::number(value));
630 impl->m_HorizontalCursor->setLabelText(QString::number(value));
584 }
631 }
585
632
586 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
633 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
587 {
634 {
588 impl->m_HorizontalCursor->setAbsolutePosition(position);
635 impl->m_HorizontalCursor->setAbsolutePosition(position);
589 impl->m_HorizontalCursor->setVisible(true);
636 impl->m_HorizontalCursor->setVisible(true);
590
637
591 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
638 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
592 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
639 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
593 }
640 }
594
641
595 void VisualizationGraphWidget::removeHorizontalCursor()
642 void VisualizationGraphWidget::removeHorizontalCursor()
596 {
643 {
597 impl->m_HorizontalCursor->setVisible(false);
644 impl->m_HorizontalCursor->setVisible(false);
598 plot().replot(QCustomPlot::rpQueuedReplot);
645 plot().replot(QCustomPlot::rpQueuedReplot);
599 }
646 }
600
647
601 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
648 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
602 {
649 {
603 Q_UNUSED(event);
650 Q_UNUSED(event);
604
651
605 for (auto i : impl->m_SelectionZones) {
652 for (auto i : impl->m_SelectionZones) {
606 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
653 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
607 }
654 }
608
655
609 // Prevents that all variables will be removed from graph when it will be closed
656 // Prevents that all variables will be removed from graph when it will be closed
610 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
657 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
611 emit variableAboutToBeRemoved(variableEntry.first);
658 emit variableAboutToBeRemoved(variableEntry.first);
612 }
659 }
613 }
660 }
614
661
615 void VisualizationGraphWidget::enterEvent(QEvent *event)
662 void VisualizationGraphWidget::enterEvent(QEvent *event)
616 {
663 {
617 Q_UNUSED(event);
664 Q_UNUSED(event);
618 impl->m_RenderingDelegate->showGraphOverlay(true);
665 impl->m_RenderingDelegate->showGraphOverlay(true);
619 }
666 }
620
667
621 void VisualizationGraphWidget::leaveEvent(QEvent *event)
668 void VisualizationGraphWidget::leaveEvent(QEvent *event)
622 {
669 {
623 Q_UNUSED(event);
670 Q_UNUSED(event);
624 impl->m_RenderingDelegate->showGraphOverlay(false);
671 impl->m_RenderingDelegate->showGraphOverlay(false);
625
672
626 if (auto parentZone = parentZoneWidget()) {
673 if (auto parentZone = parentZoneWidget()) {
627 parentZone->notifyMouseLeaveGraph(this);
674 parentZone->notifyMouseLeaveGraph(this);
628 }
675 }
629 else {
676 else {
630 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
677 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
631 }
678 }
632
679
633 if (impl->m_HoveredZone) {
680 if (impl->m_HoveredZone) {
634 impl->m_HoveredZone->setHovered(false);
681 impl->m_HoveredZone->setHovered(false);
635 impl->m_HoveredZone = nullptr;
682 impl->m_HoveredZone = nullptr;
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 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
827 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
640 {
828 {
641 return *ui->widget;
829 return *ui->widget;
642 }
830 }
643
831
644 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
832 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
645 {
833 {
646 QMenu graphMenu{};
834 QMenu graphMenu{};
647
835
648 // Iterates on variables (unique keys)
836 // Iterates on variables (unique keys)
649 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
837 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
650 end = impl->m_VariableToPlotMultiMap.cend();
838 end = impl->m_VariableToPlotMultiMap.cend();
651 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
839 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
652 // 'Remove variable' action
840 // 'Remove variable' action
653 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
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 if (!impl->m_ZoomStack.isEmpty()) {
845 if (!impl->m_ZoomStack.isEmpty()) {
658 if (!graphMenu.isEmpty()) {
846 if (!graphMenu.isEmpty()) {
659 graphMenu.addSeparator();
847 graphMenu.addSeparator();
660 }
848 }
661
849
662 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
850 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
663 }
851 }
664
852
665 // Selection Zone Actions
853 // Selection Zone Actions
666 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
854 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
667 if (selectionZoneItem) {
855 if (selectionZoneItem) {
668 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
856 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
669 selectedItems.removeAll(selectionZoneItem);
857 selectedItems.removeAll(selectionZoneItem);
670 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
858 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
671
859
672 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
860 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
673 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
861 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
674 graphMenu.addSeparator();
862 graphMenu.addSeparator();
675 }
863 }
676
864
677 QHash<QString, QMenu *> subMenus;
865 QHash<QString, QMenu *> subMenus;
678 QHash<QString, bool> subMenusEnabled;
866 QHash<QString, bool> subMenusEnabled;
679 QHash<QString, FilteringAction *> filteredMenu;
867 QHash<QString, FilteringAction *> filteredMenu;
680
868
681 for (auto zoneAction : zoneActions) {
869 for (auto zoneAction : zoneActions) {
682
870
683 auto isEnabled = zoneAction->isEnabled(selectedItems);
871 auto isEnabled = zoneAction->isEnabled(selectedItems);
684
872
685 auto menu = &graphMenu;
873 auto menu = &graphMenu;
686 QString menuPath;
874 QString menuPath;
687 for (auto subMenuName : zoneAction->subMenuList()) {
875 for (auto subMenuName : zoneAction->subMenuList()) {
688 menuPath += '/';
876 menuPath += '/';
689 menuPath += subMenuName;
877 menuPath += subMenuName;
690
878
691 if (!subMenus.contains(menuPath)) {
879 if (!subMenus.contains(menuPath)) {
692 menu = menu->addMenu(subMenuName);
880 menu = menu->addMenu(subMenuName);
693 subMenus[menuPath] = menu;
881 subMenus[menuPath] = menu;
694 subMenusEnabled[menuPath] = isEnabled;
882 subMenusEnabled[menuPath] = isEnabled;
695 }
883 }
696 else {
884 else {
697 menu = subMenus.value(menuPath);
885 menu = subMenus.value(menuPath);
698 if (isEnabled) {
886 if (isEnabled) {
699 // The sub menu is enabled if at least one of its actions is enabled
887 // The sub menu is enabled if at least one of its actions is enabled
700 subMenusEnabled[menuPath] = true;
888 subMenusEnabled[menuPath] = true;
701 }
889 }
702 }
890 }
703 }
891 }
704
892
705 FilteringAction *filterAction = nullptr;
893 FilteringAction *filterAction = nullptr;
706 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
894 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
707 filterAction = filteredMenu.value(menuPath);
895 filterAction = filteredMenu.value(menuPath);
708 if (!filterAction) {
896 if (!filterAction) {
709 filterAction = new FilteringAction{this};
897 filterAction = new FilteringAction{this};
710 filteredMenu[menuPath] = filterAction;
898 filteredMenu[menuPath] = filterAction;
711 menu->addAction(filterAction);
899 menu->addAction(filterAction);
712 }
900 }
713 }
901 }
714
902
715 auto action = menu->addAction(zoneAction->name());
903 auto action = menu->addAction(zoneAction->name());
716 action->setEnabled(isEnabled);
904 action->setEnabled(isEnabled);
717 action->setShortcut(zoneAction->displayedShortcut());
905 action->setShortcut(zoneAction->displayedShortcut());
718 QObject::connect(action, &QAction::triggered,
906 QObject::connect(action, &QAction::triggered,
719 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
907 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
720
908
721 if (filterAction && zoneAction->isFilteringAllowed()) {
909 if (filterAction && zoneAction->isFilteringAllowed()) {
722 filterAction->addActionToFilter(action);
910 filterAction->addActionToFilter(action);
723 }
911 }
724 }
912 }
725
913
726 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
914 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
727 it.value()->setEnabled(subMenusEnabled[it.key()]);
915 it.value()->setEnabled(subMenusEnabled[it.key()]);
728 }
916 }
729 }
917 }
730
918
731 if (!graphMenu.isEmpty()) {
919 if (!graphMenu.isEmpty()) {
732 graphMenu.exec(QCursor::pos());
920 graphMenu.exec(QCursor::pos());
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};
926 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
739 auto oldGraphRange = DateTimeRange{t2.lower, t2.upper};
927 auto oldGraphRange = DateTimeRange{oldRange.lower, oldRange.upper};
740
928
741 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
929 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
742 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
930 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
743 end = impl->m_VariableToPlotMultiMap.end();
931 end = impl->m_VariableToPlotMultiMap.end();
744 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
932 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
745 sqpApp->variableController().asyncChangeRange(it->first, graphRange);
933 sqpApp->variableController().asyncChangeRange(it->first, graphRange);
746 }
934 }
747 }
935 }
748
936
749 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration)
937 // if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration)
750 {
938 // {
751 emit synchronize(graphRange, oldGraphRange);
939 // emit synchronize(graphRange, oldGraphRange);
752 }
940 // }
753
941
754 auto pos = mapFromGlobal(QCursor::pos());
942 // auto pos = mapFromGlobal(QCursor::pos());
755 auto axisPos = impl->posToAxisPos(pos, plot());
943 // auto axisPos = impl->posToAxisPos(pos, plot());
756 if (auto parentZone = parentZoneWidget()) {
944 // if (auto parentZone = parentZoneWidget()) {
757 if (impl->pointIsInAxisRect(axisPos, plot())) {
945 // if (impl->pointIsInAxisRect(axisPos, plot())) {
758 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
946 // parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
759 }
947 // }
760 else {
948 // else {
761 parentZone->notifyMouseLeaveGraph(this);
949 // parentZone->notifyMouseLeaveGraph(this);
762 }
950 // }
763 }
951 // }
764
952
765 // Quits calibration
953 // Quits calibration
766 impl->m_IsCalibration = false;
954 // impl->m_IsCalibration = false;
767 }
955 }
768
956
769 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
957 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
770 {
958 {
771 impl->m_RenderingDelegate->onMouseDoubleClick(event);
959 impl->m_RenderingDelegate->onMouseDoubleClick(event);
772 }
960 }
773
961
774 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
962 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
775 {
963 {
776 // Handles plot rendering when mouse is moving
964 // Handles plot rendering when mouse is moving
777 impl->m_RenderingDelegate->onMouseMove(event);
965 impl->m_RenderingDelegate->onMouseMove(event);
778
966
779 auto axisPos = impl->posToAxisPos(event->pos(), plot());
967 auto axisPos = impl->posToAxisPos(event->pos(), plot());
780
968
781 // Zoom box and zone drawing
969 // Zoom box and zone drawing
782 if (impl->m_DrawingZoomRect) {
970 if (impl->m_DrawingZoomRect) {
783 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
971 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
784 }
972 }
785 else if (impl->m_DrawingZone) {
973 else if (impl->m_DrawingZone) {
786 impl->m_DrawingZone->setEnd(axisPos.x());
974 impl->m_DrawingZone->setEnd(axisPos.x());
787 }
975 }
788
976
789 // Cursor
977 // Cursor
790 if (auto parentZone = parentZoneWidget()) {
978 if (auto parentZone = parentZoneWidget()) {
791 if (impl->pointIsInAxisRect(axisPos, plot())) {
979 if (impl->pointIsInAxisRect(axisPos, plot())) {
792 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
980 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
793 }
981 }
794 else {
982 else {
795 parentZone->notifyMouseLeaveGraph(this);
983 parentZone->notifyMouseLeaveGraph(this);
796 }
984 }
797 }
985 }
798
986
799 // Search for the selection zone under the mouse
987 // Search for the selection zone under the mouse
800 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
988 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
801 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
989 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
802 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
990 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
803
991
804 // Sets the appropriate cursor shape
992 // Sets the appropriate cursor shape
805 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
993 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
806 setCursor(cursorShape);
994 setCursor(cursorShape);
807
995
808 // Manages the hovered zone
996 // Manages the hovered zone
809 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
997 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
810 if (impl->m_HoveredZone) {
998 if (impl->m_HoveredZone) {
811 impl->m_HoveredZone->setHovered(false);
999 impl->m_HoveredZone->setHovered(false);
812 }
1000 }
813 selectionZoneItemUnderCursor->setHovered(true);
1001 selectionZoneItemUnderCursor->setHovered(true);
814 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1002 impl->m_HoveredZone = selectionZoneItemUnderCursor;
815 plot().replot(QCustomPlot::rpQueuedReplot);
1003 plot().replot(QCustomPlot::rpQueuedReplot);
816 }
1004 }
817 }
1005 }
818 else {
1006 else {
819 // There is no zone under the mouse or the interaction mode is not "selection zones"
1007 // There is no zone under the mouse or the interaction mode is not "selection zones"
820 if (impl->m_HoveredZone) {
1008 if (impl->m_HoveredZone) {
821 impl->m_HoveredZone->setHovered(false);
1009 impl->m_HoveredZone->setHovered(false);
822 impl->m_HoveredZone = nullptr;
1010 impl->m_HoveredZone = nullptr;
823 }
1011 }
824
1012
825 setCursor(Qt::ArrowCursor);
1013 setCursor(Qt::ArrowCursor);
826 }
1014 }
827
1015
828 impl->m_HasMovedMouse = true;
1016 impl->m_HasMovedMouse = true;
829 VisualizationDragWidget::mouseMoveEvent(event);
1017 VisualizationDragWidget::mouseMoveEvent(event);
830 }
1018 }
831
1019
832 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1020 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
833 {
1021 {
834 // Processes event only if the wheel occurs on axis rect
1022 // Processes event only if the wheel occurs on axis rect
835 if (!dynamic_cast<QCPAxisRect *>(ui->widget->layoutElementAt(event->posF()))) {
1023 if (!dynamic_cast<QCPAxisRect *>(ui->widget->layoutElementAt(event->posF()))) {
836 return;
1024 return;
837 }
1025 }
838
1026
839 auto value = event->angleDelta().x() + event->angleDelta().y();
1027 auto value = event->angleDelta().x() + event->angleDelta().y();
840 if (value != 0) {
1028 if (value != 0) {
841
1029
842 auto direction = value > 0 ? 1.0 : -1.0;
1030 auto direction = value > 0 ? 1.0 : -1.0;
843 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1031 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
844 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1032 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
845 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1033 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
846
1034
847 auto zoomOrientations = QFlags<Qt::Orientation>{};
1035 auto zoomOrientations = QFlags<Qt::Orientation>{};
848 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1036 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
849 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1037 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
850
1038
851 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
1039 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
852
1040
853 if (!isZoomX && !isZoomY) {
1041 if (!isZoomX && !isZoomY) {
854 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1042 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
855 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1043 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
856
1044
857 axis->setRange(axis->range() + diff);
1045 axis->setRange(axis->range() + diff);
858
1046
859 if (plot().noAntialiasingOnDrag()) {
1047 if (plot().noAntialiasingOnDrag()) {
860 plot().setNotAntialiasedElements(QCP::aeAll);
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 }
867
1055
868 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1056 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
869 {
1057 {
870 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1058 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
871 auto isSelectionZoneMode
1059 auto isSelectionZoneMode
872 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1060 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
873 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1061 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
874
1062
875 if (!isDragDropClick && isLeftClick) {
1063 if (!isDragDropClick && isLeftClick) {
876 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1064 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
877 // Starts a zoom box
1065 // Starts a zoom box
878 impl->startDrawingRect(event->pos(), plot());
1066 impl->startDrawingRect(event->pos(), plot());
879 }
1067 }
880 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1068 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
881 // Starts a new selection zone
1069 // Starts a new selection zone
882 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
1070 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
883 if (!zoneAtPos) {
1071 if (!zoneAtPos) {
884 impl->startDrawingZone(event->pos(), this);
1072 impl->startDrawingZone(event->pos(), this);
885 }
1073 }
886 }
1074 }
887 }
1075 }
888
1076
889 // Allows mouse panning only in default mode
1077 // Allows mouse panning only in default mode
890 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
1078 // plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
891 == SqpApplication::PlotsInteractionMode::None
1079 // ==
892 && !isDragDropClick);
1080 // SqpApplication::PlotsInteractionMode::None
1081 // && !isDragDropClick);
893
1082
894 // Allows zone edition only in selection zone mode without drag&drop
1083 // Allows zone edition only in selection zone mode without drag&drop
895 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1084 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
896
1085
897 // Selection / Deselection
1086 // Selection / Deselection
898 if (isSelectionZoneMode) {
1087 if (isSelectionZoneMode) {
899 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1088 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
900 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
1089 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
901
1090
902
1091
903 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1092 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
904 && !isMultiSelectionClick) {
1093 && !isMultiSelectionClick) {
905 parentVisualizationWidget()->selectionZoneManager().select(
1094 parentVisualizationWidget()->selectionZoneManager().select(
906 {selectionZoneItemUnderCursor});
1095 {selectionZoneItemUnderCursor});
907 }
1096 }
908 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1097 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
909 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1098 parentVisualizationWidget()->selectionZoneManager().clearSelection();
910 }
1099 }
911 else {
1100 else {
912 // No selection change
1101 // No selection change
913 }
1102 }
914
1103
915 if (selectionZoneItemUnderCursor && isLeftClick) {
1104 if (selectionZoneItemUnderCursor && isLeftClick) {
916 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1105 selectionZoneItemUnderCursor->setAssociatedEditedZones(
917 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1106 parentVisualizationWidget()->selectionZoneManager().selectedItems());
918 }
1107 }
919 }
1108 }
920
1109
921
1110
922 impl->m_HasMovedMouse = false;
1111 impl->m_HasMovedMouse = false;
923 VisualizationDragWidget::mousePressEvent(event);
1112 VisualizationDragWidget::mousePressEvent(event);
924 }
1113 }
925
1114
926 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1115 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
927 {
1116 {
928 if (impl->m_DrawingZoomRect) {
1117 if (impl->m_DrawingZoomRect) {
929
1118
930 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1119 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
931 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1120 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
932
1121
933 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1122 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
934 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1123 impl->m_DrawingZoomRect->bottomRight->coords().x()};
935
1124
936 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1125 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
937 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1126 impl->m_DrawingZoomRect->bottomRight->coords().y()};
938
1127
939 impl->removeDrawingRect(plot());
1128 impl->removeDrawingRect(plot());
940
1129
941 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1130 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
942 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1131 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
943 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1132 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
944 axisX->setRange(newAxisXRange);
1133 axisX->setRange(newAxisXRange);
945 axisY->setRange(newAxisYRange);
1134 axisY->setRange(newAxisYRange);
946
1135
947 plot().replot(QCustomPlot::rpQueuedReplot);
1136 plot().replot(QCustomPlot::rpQueuedReplot);
948 }
1137 }
949 }
1138 }
950
1139
951 impl->endDrawingZone(this);
1140 impl->endDrawingZone(this);
952
1141
953 // Selection / Deselection
1142 // Selection / Deselection
954 auto isSelectionZoneMode
1143 auto isSelectionZoneMode
955 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1144 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
956 if (isSelectionZoneMode) {
1145 if (isSelectionZoneMode) {
957 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1146 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
958 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
1147 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
959 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1148 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
960 && !impl->m_HasMovedMouse) {
1149 && !impl->m_HasMovedMouse) {
961
1150
962 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1151 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
963 if (zonesUnderCursor.count() > 1) {
1152 if (zonesUnderCursor.count() > 1) {
964 // There are multiple zones under the mouse.
1153 // There are multiple zones under the mouse.
965 // Performs the selection with a selection dialog.
1154 // Performs the selection with a selection dialog.
966 VisualizationMultiZoneSelectionDialog dialog{this};
1155 VisualizationMultiZoneSelectionDialog dialog{this};
967 dialog.setZones(zonesUnderCursor);
1156 dialog.setZones(zonesUnderCursor);
968 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1157 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
969 dialog.activateWindow();
1158 dialog.activateWindow();
970 dialog.raise();
1159 dialog.raise();
971 if (dialog.exec() == QDialog::Accepted) {
1160 if (dialog.exec() == QDialog::Accepted) {
972 auto selection = dialog.selectedZones();
1161 auto selection = dialog.selectedZones();
973
1162
974 if (!isMultiSelectionClick) {
1163 if (!isMultiSelectionClick) {
975 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1164 parentVisualizationWidget()->selectionZoneManager().clearSelection();
976 }
1165 }
977
1166
978 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1167 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
979 auto zone = it.key();
1168 auto zone = it.key();
980 auto isSelected = it.value();
1169 auto isSelected = it.value();
981 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1170 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
982 isSelected);
1171 isSelected);
983
1172
984 if (isSelected) {
1173 if (isSelected) {
985 // Puts the zone on top of the stack so it can be moved or resized
1174 // Puts the zone on top of the stack so it can be moved or resized
986 impl->moveSelectionZoneOnTop(zone, plot());
1175 impl->moveSelectionZoneOnTop(zone, plot());
987 }
1176 }
988 }
1177 }
989 }
1178 }
990 }
1179 }
991 else {
1180 else {
992 if (!isMultiSelectionClick) {
1181 if (!isMultiSelectionClick) {
993 parentVisualizationWidget()->selectionZoneManager().select(
1182 parentVisualizationWidget()->selectionZoneManager().select(
994 {selectionZoneItemUnderCursor});
1183 {selectionZoneItemUnderCursor});
995 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1184 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
996 }
1185 }
997 else {
1186 else {
998 parentVisualizationWidget()->selectionZoneManager().setSelected(
1187 parentVisualizationWidget()->selectionZoneManager().setSelected(
999 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1188 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1000 || event->button() == Qt::RightButton);
1189 || event->button() == Qt::RightButton);
1001 }
1190 }
1002 }
1191 }
1003 }
1192 }
1004 else {
1193 else {
1005 // No selection change
1194 // No selection change
1006 }
1195 }
1007 }
1196 }
1008 }
1197 }
1009
1198
1010 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1199 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1011 {
1200 {
1012 auto graphRange = ui->widget->xAxis->range();
1201 auto graphRange = ui->widget->xAxis->range();
1013 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1202 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1014
1203
1015 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1204 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1016 auto variable = variableEntry.first;
1205 auto variable = variableEntry.first;
1017 qCDebug(LOG_VisualizationGraphWidget())
1206 qCDebug(LOG_VisualizationGraphWidget())
1018 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1207 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1019 qCDebug(LOG_VisualizationGraphWidget())
1208 qCDebug(LOG_VisualizationGraphWidget())
1020 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1209 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1021 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1210 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1022 impl->updateData(variableEntry.second, variable, variable->range());
1211 impl->updateData(variableEntry.second, variable, variable->range());
1023 }
1212 }
1024 }
1213 }
1025 }
1214 }
1026
1215
1027 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1216 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1028 const DateTimeRange &range)
1217 const DateTimeRange &range)
1029 {
1218 {
1030 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1219 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1031 if (it != impl->m_VariableToPlotMultiMap.end()) {
1220 if (it != impl->m_VariableToPlotMultiMap.end()) {
1032 impl->updateData(it->second, variable, range);
1221 impl->updateData(it->second, variable, range);
1033 }
1222 }
1034 }
1223 }
1035
1224
1036 void VisualizationGraphWidget::variableUpdated(QUuid id)
1225 void VisualizationGraphWidget::variableUpdated(QUuid id)
1037 {
1226 {
1038 for(auto& [var,plotables]:impl->m_VariableToPlotMultiMap)
1227 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1039 {
1228 if (var->ID() == id) {
1040 if(var->ID()==id)
1041 {
1042 impl->updateData(plotables,var,this->graphRange());
1229 impl->updateData(plotables, var, this->graphRange());
1043 }
1230 }
1044 }
1231 }
1045 }
1232 }
@@ -1,134 +1,148
1 #ifndef GUITESTUTILS_H
1 #ifndef GUITESTUTILS_H
2 #define GUITESTUTILS_H
2 #define GUITESTUTILS_H
3
3
4 #include <Common/cpp_utils.h>
4 #include <Common/cpp_utils.h>
5 #include <QPoint>
5 #include <QPoint>
6 #include <QCursor>
6 #include <QCursor>
7 #include <QMouseEvent>
7 #include <QMouseEvent>
8 #include <QCoreApplication>
8 #include <QCoreApplication>
9 #include <QtTest>
9 #include <QtTest>
10 #include <QDesktopWidget>
10 #include <QDesktopWidget>
11
11
12 #include <qcustomplot.h>
12 #include <qcustomplot.h>
13
13
14 template <typename T>
14 template <typename T>
15 QPoint center(T* widget)
15 QPoint center(T* widget)
16 {
16 {
17 return QPoint{widget->width()/2,widget->height()/2};
17 return QPoint{widget->width()/2,widget->height()/2};
18 }
18 }
19
19
20 HAS_METHOD(viewport)
20 HAS_METHOD(viewport)
21
21
22 template <typename T>
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 template <typename T> using viewport_type = decltype(std::declval<T>().viewport());
25 template <typename T> using viewport_type = decltype(std::declval<T>().viewport());
26
26
27 HAS_METHOD(topLevelItem)
27 HAS_METHOD(topLevelItem)
28
28
29 template<typename T>
29 template<typename T>
30 void mouseMove(T* widget, QPoint pos, Qt::MouseButton mouseModifier)
30 void mouseMove(T* widget, QPoint pos, Qt::MouseButton mouseModifier)
31 {
31 {
32 QCursor::setPos(widget->mapToGlobal(pos));
32 QCursor::setPos(widget->mapToGlobal(pos));
33 QMouseEvent event(QEvent::MouseMove, pos, Qt::NoButton, mouseModifier, Qt::NoModifier);
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 {
36 if constexpr(is_QWidgetOrDerived<viewport_type<T>>)
35 {
37 {
36 qApp->sendEvent(widget->viewport(), &event);
38 qApp->sendEvent(widget->viewport(), &event);
37 }
39 }
38 else
40 else
39 {
41 {
40 qApp->sendEvent(widget, &event);
42 qApp->sendEvent(widget, &event);
41 }
43 }
44 }
45 else
46 {
47 qApp->sendEvent(widget, &event);
48 }
42 qApp->processEvents();
49 qApp->processEvents();
43 }
50 }
44
51
45
52
46 template <typename T>
53 template <typename T>
47 void setMouseTracking(T* widget)
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>)
57 {
58 if constexpr(is_QWidgetOrDerived<viewport_type<T>>)
50 {
59 {
51 widget->viewport()->setMouseTracking(true);
60 widget->viewport()->setMouseTracking(true);
52 }
61 }
53 else
62 else
54 {
63 {
55 widget->setMouseTracking(true);
64 widget->setMouseTracking(true);
56 }
65 }
57 }
66 }
67 else
68 {
69 widget->setMouseTracking(true);
70 }
71 }
58
72
59 template <typename T, typename T2>
73 template <typename T, typename T2>
60 auto getItem(T* widget, T2 itemIndex)
74 auto getItem(T* widget, T2 itemIndex)
61 {
75 {
62 if constexpr(has_topLevelItem<T>)
76 if constexpr(has_topLevelItem<T>)
63 {
77 {
64 return widget->topLevelItem(itemIndex);
78 return widget->topLevelItem(itemIndex);
65 }
79 }
66 else
80 else
67 {
81 {
68 return widget->item(itemIndex);
82 return widget->item(itemIndex);
69 }
83 }
70 }
84 }
71
85
72 #define SELECT_ITEM(widget, itemIndex, item)\
86 #define SELECT_ITEM(widget, itemIndex, item)\
73 auto item = getItem(widget, itemIndex);\
87 auto item = getItem(widget, itemIndex);\
74 {\
88 {\
75 auto itemCenterPos = widget->visualItemRect(item).center();\
89 auto itemCenterPos = widget->visualItemRect(item).center();\
76 QTest::mouseClick(widget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos);\
90 QTest::mouseClick(widget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos);\
77 QVERIFY(widget->selectedItems().size() > 0);\
91 QVERIFY(widget->selectedItems().size() > 0);\
78 QVERIFY(widget->selectedItems().contains(item));\
92 QVERIFY(widget->selectedItems().contains(item));\
79 }
93 }
80
94
81
95
82 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName)\
96 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName)\
83 childType* child = parent.findChild<childType*>(childName); \
97 childType* child = parent.findChild<childType*>(childName); \
84 QVERIFY(child!=Q_NULLPTR); \
98 QVERIFY(child!=Q_NULLPTR); \
85 setMouseTracking(child);
99 setMouseTracking(child);
86
100
87 template<typename T1, typename T2, typename T3, typename T4=void>
101 template<typename T1, typename T2, typename T3, typename T4=void>
88 void dragnDropItem(T1* sourceWidget, T2* destWidget, T3* item, T4* destItem=Q_NULLPTR)
102 void dragnDropItem(T1* sourceWidget, T2* destWidget, T3* item, T4* destItem=Q_NULLPTR)
89 {
103 {
90 auto itemCenterPos = sourceWidget->visualItemRect(item).center();
104 auto itemCenterPos = sourceWidget->visualItemRect(item).center();
91 if constexpr(has_viewport<T1>)
105 if constexpr(has_viewport<T1>)
92 {
106 {
93 QTest::mousePress(sourceWidget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos);
107 QTest::mousePress(sourceWidget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos);
94 }
108 }
95 else
109 else
96 {
110 {
97 QTest::mousePress(sourceWidget, Qt::LeftButton, Qt::NoModifier, itemCenterPos);
111 QTest::mousePress(sourceWidget, Qt::LeftButton, Qt::NoModifier, itemCenterPos);
98 }
112 }
99 mouseMove(sourceWidget,itemCenterPos, Qt::LeftButton);
113 mouseMove(sourceWidget,itemCenterPos, Qt::LeftButton);
100 itemCenterPos+=QPoint(0,-10);
114 itemCenterPos+=QPoint(0,-10);
101 QTimer::singleShot(100,[destWidget,destItem](){
115 QTimer::singleShot(100,[destWidget,destItem](){
102 mouseMove(destWidget, destWidget->rect().center(),Qt::LeftButton);
116 mouseMove(destWidget, destWidget->rect().center(),Qt::LeftButton);
103 mouseMove(destWidget, destWidget->rect().center()+QPoint(0,-10),Qt::LeftButton);
117 mouseMove(destWidget, destWidget->rect().center()+QPoint(0,-10),Qt::LeftButton);
104 if constexpr(!std::is_same_v<void, T4>)
118 if constexpr(!std::is_same_v<void, T4>)
105 {
119 {
106 auto destItemCenterPos = destWidget->visualItemRect(destItem).center();
120 auto destItemCenterPos = destWidget->visualItemRect(destItem).center();
107 QTest::mouseRelease(destWidget, Qt::LeftButton, Qt::NoModifier, destItemCenterPos);
121 QTest::mouseRelease(destWidget, Qt::LeftButton, Qt::NoModifier, destItemCenterPos);
108 }
122 }
109 else if constexpr(has_viewport<T2>)
123 else if constexpr(has_viewport<T2>)
110 {
124 {
111 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
125 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
112 }
126 }
113 else
127 else
114 {
128 {
115 QTest::mouseRelease(destWidget, Qt::LeftButton);
129 QTest::mouseRelease(destWidget, Qt::LeftButton);
116 }
130 }
117 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
131 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
118 });
132 });
119 mouseMove(sourceWidget,itemCenterPos,Qt::LeftButton);
133 mouseMove(sourceWidget,itemCenterPos,Qt::LeftButton);
120 }
134 }
121
135
122 #define PREPARE_GUI_TEST(main_widget)\
136 #define PREPARE_GUI_TEST(main_widget)\
123 main_widget.setGeometry(QRect(QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)),\
137 main_widget.setGeometry(QRect(QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)),\
124 QSize(500, 500)));\
138 QSize(500, 500)));\
125 main_widget.show();\
139 main_widget.show();\
126 qApp->setActiveWindow(&main_widget);\
140 qApp->setActiveWindow(&main_widget);\
127 QVERIFY(QTest::qWaitForWindowActive(&main_widget))
141 QVERIFY(QTest::qWaitForWindowActive(&main_widget))
128
142
129 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName)\
143 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName)\
130 childType* child = parent.findChild<childType*>(childName); \
144 childType* child = parent.findChild<childType*>(childName); \
131 QVERIFY(child!=Q_NULLPTR); \
145 QVERIFY(child!=Q_NULLPTR); \
132 setMouseTracking(child);
146 setMouseTracking(child);
133
147
134 #endif
148 #endif
@@ -1,82 +1,79
1 #include <QtTest>
1 #include <QtTest>
2 #include <QObject>
2 #include <QObject>
3 #include <QString>
3 #include <QString>
4 #include <QScreen>
4 #include <QScreen>
5 #include <QMainWindow>
5 #include <QMainWindow>
6 #include <QWheelEvent>
6 #include <QWheelEvent>
7
7
8 #include <qcustomplot.h>
8 #include <qcustomplot.h>
9
9
10 #include <SqpApplication.h>
10 #include <SqpApplication.h>
11 #include <Variable/VariableController2.h>
11 #include <Variable/VariableController2.h>
12 #include <Common/cpp_utils.h>
12 #include <Common/cpp_utils.h>
13
13
14 #include <Visualization/VisualizationGraphWidget.h>
14 #include <Visualization/VisualizationGraphWidget.h>
15 #include <TestProviders.h>
15 #include <TestProviders.h>
16 #include <GUITestUtils.h>
16 #include <GUITestUtils.h>
17
17
18
18
19 ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication *>(qApp)->variableController().isReady)
19 ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication *>(qApp)->variableController().isReady)
20
20
21 #define A_SIMPLE_GRAPH_FIXTURE \
21 #define A_SIMPLE_GRAPH_FIXTURE \
22 VisualizationGraphWidget w;\
22 VisualizationGraphWidget w;\
23 PREPARE_GUI_TEST(w);\
23 PREPARE_GUI_TEST(w);\
24 auto provider = std::make_shared<SimpleRange<10> >();\
24 auto provider = std::make_shared<SimpleRange<10> >();\
25 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),\
25 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),\
26 QTime(16, 00));\
26 QTime(16, 00));\
27 auto var = static_cast<SqpApplication *>(qApp)->variableController().createVariable(\
27 auto var = static_cast<SqpApplication *>(qApp)->variableController().createVariable(\
28 "V1", {{"", "scalar"}}, provider, range);\
28 "V1", {{"", "scalar"}}, provider, range);\
29 while (!isReady(var))\
29 while (!isReady(var))\
30 QCoreApplication::processEvents();\
30 QCoreApplication::processEvents();\
31 w.addVariable(var, range);\
31 w.addVariable(var, range);\
32 GET_CHILD_WIDGET_FOR_GUI_TESTS(w, plot, QCustomPlot, "widget");\
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 class A_SimpleGraph : public QObject {
36 class A_SimpleGraph : public QObject {
37 Q_OBJECT
37 Q_OBJECT
38 public:
38 public:
39 explicit A_SimpleGraph(QObject *parent = Q_NULLPTR) : QObject(parent) {}
39 explicit A_SimpleGraph(QObject *parent = Q_NULLPTR) : QObject(parent) {}
40
40
41 private slots:
41 private slots:
42 void scrolls_with_mouse()
42 void scrolls_with_mouse()
43 {
43 {
44 A_SIMPLE_GRAPH_FIXTURE
44 A_SIMPLE_GRAPH_FIXTURE
45
45
46 for (auto i = 0; i < 10; i++) {
46 for (auto i = 0; i < 10; i++) {
47 QTest::mousePress(plot, Qt::LeftButton, Qt::NoModifier, cent, 5);
47 QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 500);
48 mouseMove(plot, {cent.x() + 200, cent.y()}, Qt::LeftButton);
48 mouseMove(&w, {cent.x() + 200, cent.y()}, Qt::LeftButton);
49 QTest::mouseRelease(plot, Qt::LeftButton);
49 QTest::mouseRelease(&w, Qt::LeftButton);
50 while (!isReady(var))
50 while (!isReady(var))
51 QCoreApplication::processEvents();
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 while (!isReady(var))
53 while (!isReady(var))
60 QCoreApplication::processEvents();
54 QCoreApplication::processEvents();
61 auto r = var->range();
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 QVERIFY(r.m_TEnd < range.m_TEnd);
60 QVERIFY(r.m_TEnd < range.m_TEnd);
63 // this fails :(
64 QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1));
61 QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1));
65 }
62 }
66 };
63 };
67
64
68 QT_BEGIN_NAMESPACE
65 QT_BEGIN_NAMESPACE
69 QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
66 QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
70 QT_END_NAMESPACE
67 QT_END_NAMESPACE
71 int main(int argc, char *argv[])
68 int main(int argc, char *argv[])
72 {
69 {
73 SqpApplication app{argc, argv};
70 SqpApplication app{argc, argv};
74 app.setAttribute(Qt::AA_Use96Dpi, true);
71 app.setAttribute(Qt::AA_Use96Dpi, true);
75 QTEST_DISABLE_KEYPAD_NAVIGATION
72 QTEST_DISABLE_KEYPAD_NAVIGATION
76 QTEST_ADD_GPU_BLACKLIST_SUPPORT
73 QTEST_ADD_GPU_BLACKLIST_SUPPORT
77 A_SimpleGraph tc;
74 A_SimpleGraph tc;
78 QTEST_SET_MAIN_SOURCE_PATH
75 QTEST_SET_MAIN_SOURCE_PATH
79 return QTest::qExec(&tc, argc, argv);
76 return QTest::qExec(&tc, argc, argv);
80 }
77 }
81
78
82 #include "main.moc"
79 #include "main.moc"
General Comments 0
You need to be logged in to leave comments. Login now