##// END OF EJS Templates
Removed Y autoscale on each variable update...
jeandet -
r1385:27de5ac17fca
parent child
Show More
@@ -1,1427 +1,1426
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 #include <Data/DateTimeRangeHelper.h>
26 #include <Data/DateTimeRangeHelper.h>
27
27
28 #include <unordered_map>
28 #include <unordered_map>
29
29
30 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
30 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
31
31
32 namespace {
32 namespace {
33
33
34 /// Key pressed to enable drag&drop in all modes
34 /// Key pressed to enable drag&drop in all modes
35 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
35 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
36
36
37 /// Key pressed to enable zoom on horizontal axis
37 /// Key pressed to enable zoom on horizontal axis
38 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
38 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
39
39
40 /// Key pressed to enable zoom on vertical axis
40 /// Key pressed to enable zoom on vertical axis
41 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
41 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
42
42
43 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
43 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
44 const auto PAN_SPEED = 5;
44 const auto PAN_SPEED = 5;
45
45
46 /// Key pressed to enable a calibration pan
46 /// Key pressed to enable a calibration pan
47 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
47 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
48
48
49 /// Key pressed to enable multi selection of selection zones
49 /// Key pressed to enable multi selection of selection zones
50 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
50 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
51
51
52 /// Minimum size for the zoom box, in percentage of the axis range
52 /// Minimum size for the zoom box, in percentage of the axis range
53 const auto ZOOM_BOX_MIN_SIZE = 0.8;
53 const auto ZOOM_BOX_MIN_SIZE = 0.8;
54
54
55 /// Format of the dates appearing in the label of a cursor
55 /// Format of the dates appearing in the label of a cursor
56 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
56 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
57
57
58 } // namespace
58 } // namespace
59
59
60 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
60 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
61
61
62 explicit VisualizationGraphWidgetPrivate(const QString &name)
62 explicit VisualizationGraphWidgetPrivate(const QString &name)
63 : m_Name{name},
63 : m_Name{name},
64 m_Flags{GraphFlag::EnableAll},
64 m_Flags{GraphFlag::EnableAll},
65 m_IsCalibration{false},
65 m_IsCalibration{false},
66 m_RenderingDelegate{nullptr}
66 m_RenderingDelegate{nullptr}
67 {
67 {
68 m_plot = new QCustomPlot();
68 m_plot = new QCustomPlot();
69 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
69 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
70 m_plot->setPlottingHint(QCP::phFastPolylines, true);
70 m_plot->setPlottingHint(QCP::phFastPolylines, true);
71 }
71 }
72
72
73 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
73 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
74 const DateTimeRange &range)
74 const DateTimeRange &range)
75 {
75 {
76 VisualizationGraphHelper::updateData(plottables, variable, range);
76 VisualizationGraphHelper::updateData(plottables, variable, range);
77
77
78 // Prevents that data has changed to update rendering
78 // Prevents that data has changed to update rendering
79 m_RenderingDelegate->onPlotUpdated();
79 m_RenderingDelegate->onPlotUpdated();
80 }
80 }
81
81
82 QString m_Name;
82 QString m_Name;
83 // 1 variable -> n qcpplot
83 // 1 variable -> n qcpplot
84 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
84 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
85 GraphFlags m_Flags;
85 GraphFlags m_Flags;
86 bool m_IsCalibration;
86 bool m_IsCalibration;
87 QCustomPlot* m_plot;
87 QCustomPlot* m_plot;
88 QPoint m_lastMousePos;
88 QPoint m_lastMousePos;
89 QCPRange m_lastXRange;
89 QCPRange m_lastXRange;
90 QCPRange m_lastYRange;
90 QCPRange m_lastYRange;
91 /// Delegate used to attach rendering features to the plot
91 /// Delegate used to attach rendering features to the plot
92 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
92 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
93
93
94 QCPItemRect* m_DrawingZoomRect = nullptr;
94 QCPItemRect* m_DrawingZoomRect = nullptr;
95 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
95 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
96
96
97 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
97 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
99
99
100 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
100 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
101 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
101 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
102 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
102 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
103
103
104 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
104 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
105
105
106 bool m_VariableAutoRangeOnInit = true;
106 bool m_VariableAutoRangeOnInit = true;
107
107
108 inline void enterPlotDrag(const QPoint& position)
108 inline void enterPlotDrag(const QPoint& position)
109 {
109 {
110 m_lastMousePos = m_plot->mapFromParent(position);
110 m_lastMousePos = m_plot->mapFromParent(position);
111 m_lastXRange = m_plot->xAxis->range();
111 m_lastXRange = m_plot->xAxis->range();
112 m_lastYRange = m_plot->yAxis->range();
112 m_lastYRange = m_plot->yAxis->range();
113
113
114 }
114 }
115
115
116 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
116 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
117 void updateZoomRect(const QPoint& newPos)
117 void updateZoomRect(const QPoint& newPos)
118 {
118 {
119 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
119 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
120 m_DrawingZoomRect->bottomRight->setCoords(pos);
120 m_DrawingZoomRect->bottomRight->setCoords(pos);
121 m_plot->replot(QCustomPlot::rpQueuedReplot);
121 m_plot->replot(QCustomPlot::rpQueuedReplot);
122 }
122 }
123
123
124 void applyZoomRect()
124 void applyZoomRect()
125 {
125 {
126 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
126 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
127 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
127 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
128
128
129 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
129 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
130 m_DrawingZoomRect->bottomRight->coords().x()};
130 m_DrawingZoomRect->bottomRight->coords().x()};
131
131
132 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
132 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
133 m_DrawingZoomRect->bottomRight->coords().y()};
133 m_DrawingZoomRect->bottomRight->coords().y()};
134
134
135 removeDrawingRect();
135 removeDrawingRect();
136
136
137 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
137 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
138 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
138 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
139 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
139 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
140 axisX->setRange(newAxisXRange);
140 axisX->setRange(newAxisXRange);
141 axisY->setRange(newAxisYRange);
141 axisY->setRange(newAxisYRange);
142
142
143 m_plot->replot(QCustomPlot::rpQueuedReplot);
143 m_plot->replot(QCustomPlot::rpQueuedReplot);
144 }
144 }
145 }
145 }
146
146
147 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
147 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
148 void updateZoneRect(const QPoint& newPos)
148 void updateZoneRect(const QPoint& newPos)
149 {
149 {
150 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
150 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
151 m_plot->replot(QCustomPlot::rpQueuedReplot);
151 m_plot->replot(QCustomPlot::rpQueuedReplot);
152 }
152 }
153
153
154 void startDrawingRect(const QPoint &pos)
154 void startDrawingRect(const QPoint &pos)
155 {
155 {
156 removeDrawingRect();
156 removeDrawingRect();
157
157
158 auto axisPos = posToAxisPos(pos);
158 auto axisPos = posToAxisPos(pos);
159
159
160 m_DrawingZoomRect = new QCPItemRect{m_plot};
160 m_DrawingZoomRect = new QCPItemRect{m_plot};
161 QPen p;
161 QPen p;
162 p.setWidth(2);
162 p.setWidth(2);
163 m_DrawingZoomRect->setPen(p);
163 m_DrawingZoomRect->setPen(p);
164
164
165 m_DrawingZoomRect->topLeft->setCoords(axisPos);
165 m_DrawingZoomRect->topLeft->setCoords(axisPos);
166 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
166 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
167 }
167 }
168
168
169 void removeDrawingRect()
169 void removeDrawingRect()
170 {
170 {
171 if (m_DrawingZoomRect) {
171 if (m_DrawingZoomRect) {
172 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
172 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
173 m_DrawingZoomRect = nullptr;
173 m_DrawingZoomRect = nullptr;
174 m_plot->replot(QCustomPlot::rpQueuedReplot);
174 m_plot->replot(QCustomPlot::rpQueuedReplot);
175 }
175 }
176 }
176 }
177
177
178 void selectZone(const QPoint &pos)
178 void selectZone(const QPoint &pos)
179 {
179 {
180 auto zoneAtPos = selectionZoneAt(pos);
180 auto zoneAtPos = selectionZoneAt(pos);
181 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
181 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
182 }
182 }
183
183
184 void startDrawingZone(const QPoint &pos)
184 void startDrawingZone(const QPoint &pos)
185 {
185 {
186 endDrawingZone();
186 endDrawingZone();
187
187
188 auto axisPos = posToAxisPos(pos);
188 auto axisPos = posToAxisPos(pos);
189
189
190 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
190 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
191 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
191 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
192 m_DrawingZone->setEditionEnabled(false);
192 m_DrawingZone->setEditionEnabled(false);
193 }
193 }
194
194
195 void endDrawingZone()
195 void endDrawingZone()
196 {
196 {
197 if (m_DrawingZone) {
197 if (m_DrawingZone) {
198 auto drawingZoneRange = m_DrawingZone->range();
198 auto drawingZoneRange = m_DrawingZone->range();
199 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
199 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
200 m_DrawingZone->setEditionEnabled(true);
200 m_DrawingZone->setEditionEnabled(true);
201 addSelectionZone(m_DrawingZone);
201 addSelectionZone(m_DrawingZone);
202 }
202 }
203 else {
203 else {
204 m_plot->removeItem(m_DrawingZone);
204 m_plot->removeItem(m_DrawingZone);
205 }
205 }
206
206
207 m_plot->replot(QCustomPlot::rpQueuedReplot);
207 m_plot->replot(QCustomPlot::rpQueuedReplot);
208 m_DrawingZone = nullptr;
208 m_DrawingZone = nullptr;
209 }
209 }
210 }
210 }
211
211
212 void moveSelectionZone(const QPoint& destination)
212 void moveSelectionZone(const QPoint& destination)
213 {
213 {
214 /*
214 /*
215 * I give up on this for now
215 * I give up on this for now
216 * @TODO implement this, the difficulty is that selection zones have their own
216 * @TODO implement this, the difficulty is that selection zones have their own
217 * event handling code which seems to rely on QCP GUI event handling propagation
217 * event handling code which seems to rely on QCP GUI event handling propagation
218 * which was a realy bad design choice.
218 * which was a realy bad design choice.
219 */
219 */
220 }
220 }
221
221
222 void setSelectionZonesEditionEnabled(bool value)
222 void setSelectionZonesEditionEnabled(bool value)
223 {
223 {
224 for (auto s : m_SelectionZones) {
224 for (auto s : m_SelectionZones) {
225 s->setEditionEnabled(value);
225 s->setEditionEnabled(value);
226 }
226 }
227 }
227 }
228
228
229 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
229 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
230
230
231 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
231 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
232 {
232 {
233 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
233 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
234 auto minDistanceToZone = -1;
234 auto minDistanceToZone = -1;
235 for (auto zone : m_SelectionZones) {
235 for (auto zone : m_SelectionZones) {
236 auto distanceToZone = zone->selectTest(pos, false);
236 auto distanceToZone = zone->selectTest(pos, false);
237 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
237 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
238 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
238 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
239 selectionZoneItemUnderCursor = zone;
239 selectionZoneItemUnderCursor = zone;
240 }
240 }
241 }
241 }
242
242
243 return selectionZoneItemUnderCursor;
243 return selectionZoneItemUnderCursor;
244 }
244 }
245
245
246 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
246 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
247 const QCustomPlot &plot) const
247 const QCustomPlot &plot) const
248 {
248 {
249 QVector<VisualizationSelectionZoneItem *> zones;
249 QVector<VisualizationSelectionZoneItem *> zones;
250 for (auto zone : m_SelectionZones) {
250 for (auto zone : m_SelectionZones) {
251 auto distanceToZone = zone->selectTest(pos, false);
251 auto distanceToZone = zone->selectTest(pos, false);
252 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
252 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
253 zones << zone;
253 zones << zone;
254 }
254 }
255 }
255 }
256
256
257 return zones;
257 return zones;
258 }
258 }
259
259
260 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
260 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
261 {
261 {
262 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
262 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
263 zone->moveToTop();
263 zone->moveToTop();
264 m_SelectionZones.removeAll(zone);
264 m_SelectionZones.removeAll(zone);
265 m_SelectionZones.append(zone);
265 m_SelectionZones.append(zone);
266 }
266 }
267 }
267 }
268
268
269 QPointF posToAxisPos(const QPoint &pos) const
269 QPointF posToAxisPos(const QPoint &pos) const
270 {
270 {
271 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
271 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
272 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
272 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
273 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
273 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
274 }
274 }
275
275
276 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
276 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
277 {
277 {
278 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
278 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
279 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
279 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
280 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
280 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
281 }
281 }
282
282
283 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
283 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
284 {
284 {
285 if (axis->scaleType() == QCPAxis::stLinear)
285 if (axis->scaleType() == QCPAxis::stLinear)
286 {
286 {
287 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
287 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
288 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
288 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
289 }
289 }
290 else
290 else
291 {
291 {
292 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
292 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
293 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
293 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
294 }
294 }
295 }
295 }
296
296
297 void setRange(const DateTimeRange &newRange, bool updateVar=true)
297 void setRange(const DateTimeRange &newRange, bool updateVar=true)
298 {
298 {
299 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
299 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
300 if(updateVar)
300 if(updateVar)
301 {
301 {
302 for (auto it = m_VariableToPlotMultiMap.begin(),
302 for (auto it = m_VariableToPlotMultiMap.begin(),
303 end = m_VariableToPlotMultiMap.end();
303 end = m_VariableToPlotMultiMap.end();
304 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
304 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
305 {
305 {
306 sqpApp->variableController().asyncChangeRange(it->first, newRange);
306 sqpApp->variableController().asyncChangeRange(it->first, newRange);
307 }
307 }
308 }
308 }
309 m_plot->replot(QCustomPlot::rpQueuedReplot);
309 m_plot->replot(QCustomPlot::rpQueuedReplot);
310 }
310 }
311
311
312 void setRange(const QCPRange &newRange)
312 void setRange(const QCPRange &newRange)
313 {
313 {
314 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
314 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
315 setRange(graphRange);
315 setRange(graphRange);
316 }
316 }
317
317
318 void rescaleY()
318 void rescaleY()
319 {
319 {
320 m_plot->yAxis->rescale(true);
320 m_plot->yAxis->rescale(true);
321 }
321 }
322
322
323 std::tuple<double,double> moveGraph(const QPoint& destination)
323 std::tuple<double,double> moveGraph(const QPoint& destination)
324 {
324 {
325 auto currentPos = m_plot->mapFromParent(destination);
325 auto currentPos = m_plot->mapFromParent(destination);
326 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
326 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
327 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
327 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
328 auto oldXRange = xAxis->range();
328 auto oldXRange = xAxis->range();
329 auto oldYRange = yAxis->range();
329 auto oldYRange = yAxis->range();
330 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
330 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
331 xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx);
331 xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx);
332 if(yAxis->scaleType() == QCPAxis::stLinear)
332 if(yAxis->scaleType() == QCPAxis::stLinear)
333 {
333 {
334 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
334 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
335 yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy);
335 yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy);
336 }
336 }
337 else
337 else
338 {
338 {
339 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
339 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
340 yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy);
340 yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy);
341 }
341 }
342 auto newXRange = xAxis->range();
342 auto newXRange = xAxis->range();
343 auto newYRange = yAxis->range();
343 auto newYRange = yAxis->range();
344 setRange(xAxis->range());
344 setRange(xAxis->range());
345 //m_lastMousePos = currentPos;
345 //m_lastMousePos = currentPos;
346 return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower};
346 return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower};
347 }
347 }
348
348
349 void zoom(double factor, int center, Qt::Orientation orientation)
349 void zoom(double factor, int center, Qt::Orientation orientation)
350 {
350 {
351 QCPAxis *axis = m_plot->axisRect()->rangeZoomAxis(orientation);
351 QCPAxis *axis = m_plot->axisRect()->rangeZoomAxis(orientation);
352 axis->scaleRange(factor, axis->pixelToCoord(center));
352 axis->scaleRange(factor, axis->pixelToCoord(center));
353 if (orientation == Qt::Horizontal)
353 if (orientation == Qt::Horizontal)
354 setRange(axis->range());
354 setRange(axis->range());
355 m_plot->replot(QCustomPlot::rpQueuedReplot);
355 m_plot->replot(QCustomPlot::rpQueuedReplot);
356 }
356 }
357
357
358 void transform(const DateTimeRangeTransformation &tranformation)
358 void transform(const DateTimeRangeTransformation &tranformation)
359 {
359 {
360 auto graphRange = m_plot->xAxis->range();
360 auto graphRange = m_plot->xAxis->range();
361 DateTimeRange range{graphRange.lower, graphRange.upper};
361 DateTimeRange range{graphRange.lower, graphRange.upper};
362 range = range.transform(tranformation);
362 range = range.transform(tranformation);
363 setRange(range);
363 setRange(range);
364 m_plot->replot(QCustomPlot::rpQueuedReplot);
364 m_plot->replot(QCustomPlot::rpQueuedReplot);
365 }
365 }
366
366
367 void move(double dx, double dy)
367 void move(double dx, double dy)
368 {
368 {
369 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
369 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
370 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
370 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
371 xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx));
371 xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx));
372 yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy));
372 yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy));
373 setRange(xAxis->range());
373 setRange(xAxis->range());
374 m_plot->replot(QCustomPlot::rpQueuedReplot);
374 m_plot->replot(QCustomPlot::rpQueuedReplot);
375 }
375 }
376
376
377 void move(double factor, Qt::Orientation orientation)
377 void move(double factor, Qt::Orientation orientation)
378 {
378 {
379 auto oldRange = m_plot->xAxis->range();
379 auto oldRange = m_plot->xAxis->range();
380 QCPAxis *axis = m_plot->axisRect()->rangeDragAxis(orientation);
380 QCPAxis *axis = m_plot->axisRect()->rangeDragAxis(orientation);
381 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
381 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
382 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
382 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
383 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
383 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
384 }
384 }
385 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
385 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
386 int start = 0, stop = 0;
386 int start = 0, stop = 0;
387 double diff = 0.;
387 double diff = 0.;
388 if (factor > 0.0) {
388 if (factor > 0.0) {
389 stop = m_plot->width() * factor / 10;
389 stop = m_plot->width() * factor / 10;
390 start = 2 * m_plot->width() * factor / 10;
390 start = 2 * m_plot->width() * factor / 10;
391 }
391 }
392 if (factor < 0.0) {
392 if (factor < 0.0) {
393 factor *= -1.0;
393 factor *= -1.0;
394 start = m_plot->width() * factor / 10;
394 start = m_plot->width() * factor / 10;
395 stop = 2 * m_plot->width() * factor / 10;
395 stop = 2 * m_plot->width() * factor / 10;
396 }
396 }
397 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
397 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
398 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
398 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
399 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
399 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
400 }
400 }
401 if (orientation == Qt::Horizontal)
401 if (orientation == Qt::Horizontal)
402 setRange(axis->range());
402 setRange(axis->range());
403 m_plot->replot(QCustomPlot::rpQueuedReplot);
403 m_plot->replot(QCustomPlot::rpQueuedReplot);
404 }
404 }
405 };
405 };
406
406
407 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
407 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
408 : VisualizationDragWidget{parent},
408 : VisualizationDragWidget{parent},
409 ui{new Ui::VisualizationGraphWidget},
409 ui{new Ui::VisualizationGraphWidget},
410 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
410 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
411 {
411 {
412 ui->setupUi(this);
412 ui->setupUi(this);
413 this->layout()->addWidget(impl->m_plot);
413 this->layout()->addWidget(impl->m_plot);
414 // 'Close' options : widget is deleted when closed
414 // 'Close' options : widget is deleted when closed
415 setAttribute(Qt::WA_DeleteOnClose);
415 setAttribute(Qt::WA_DeleteOnClose);
416
416
417 // The delegate must be initialized after the ui as it uses the plot
417 // The delegate must be initialized after the ui as it uses the plot
418 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
418 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
419
419
420 // Init the cursors
420 // Init the cursors
421 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
421 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
422 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
422 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
423 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
423 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
424 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
424 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
425
425
426 this->setFocusPolicy(Qt::WheelFocus);
426 this->setFocusPolicy(Qt::WheelFocus);
427 this->setMouseTracking(true);
427 this->setMouseTracking(true);
428 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
428 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
429 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
429 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
430 impl->m_plot->setParent(this);
430 impl->m_plot->setParent(this);
431
431
432 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, &VisualizationGraphWidget::variableDeleted);
432 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, &VisualizationGraphWidget::variableDeleted);
433 }
433 }
434
434
435
435
436 VisualizationGraphWidget::~VisualizationGraphWidget()
436 VisualizationGraphWidget::~VisualizationGraphWidget()
437 {
437 {
438 delete ui;
438 delete ui;
439 }
439 }
440
440
441 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
441 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
442 {
442 {
443 auto parent = parentWidget();
443 auto parent = parentWidget();
444 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
444 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
445 parent = parent->parentWidget();
445 parent = parent->parentWidget();
446 }
446 }
447
447
448 return qobject_cast<VisualizationZoneWidget *>(parent);
448 return qobject_cast<VisualizationZoneWidget *>(parent);
449 }
449 }
450
450
451 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
451 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
452 {
452 {
453 auto parent = parentWidget();
453 auto parent = parentWidget();
454 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
454 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
455 parent = parent->parentWidget();
455 parent = parent->parentWidget();
456 }
456 }
457
457
458 return qobject_cast<VisualizationWidget *>(parent);
458 return qobject_cast<VisualizationWidget *>(parent);
459 }
459 }
460
460
461 void VisualizationGraphWidget::setFlags(GraphFlags flags)
461 void VisualizationGraphWidget::setFlags(GraphFlags flags)
462 {
462 {
463 impl->m_Flags = std::move(flags);
463 impl->m_Flags = std::move(flags);
464 }
464 }
465
465
466 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
466 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
467 {
467 {
468 // Uses delegate to create the qcpplot components according to the variable
468 // Uses delegate to create the qcpplot components according to the variable
469 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
469 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
470
470
471 // Sets graph properties
471 // Sets graph properties
472 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
472 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
473
473
474 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
474 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
475
475
476 setGraphRange(range);
476 setGraphRange(range);
477 // If the variable already has its data loaded, load its units and its range in the graph
477 // If the variable already has its data loaded, load its units and its range in the graph
478 if (variable->dataSeries() != nullptr) {
478 if (variable->dataSeries() != nullptr) {
479 impl->m_RenderingDelegate->setAxesUnits(*variable);
479 impl->m_RenderingDelegate->setAxesUnits(*variable);
480 }
480 }
481 else
481 else
482 {
482 {
483 auto context = new QObject{this};
483 auto context = new QObject{this};
484 connect(variable.get(), &Variable::updated, context,
484 connect(variable.get(), &Variable::updated, context,
485 [this, variable, context, range](QUuid)
485 [this, variable, context, range](QUuid)
486 {
486 {
487 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
487 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
488 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
488 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
489 delete context;
489 delete context;
490 }
490 }
491 );
491 );
492 }
492 }
493 //@TODO this is bad! when variable is moved to another graph it still fires
493 //@TODO this is bad! when variable is moved to another graph it still fires
494 // even if this has been deleted
494 // even if this has been deleted
495 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
495 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
496 this->onUpdateVarDisplaying(variable, range); // My bullshit
496 this->onUpdateVarDisplaying(variable, range); // My bullshit
497 emit variableAdded(variable);
497 emit variableAdded(variable);
498 }
498 }
499
499
500 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
500 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
501 {
501 {
502 // Each component associated to the variable :
502 // Each component associated to the variable :
503 // - is removed from qcpplot (which deletes it)
503 // - is removed from qcpplot (which deletes it)
504 // - is no longer referenced in the map
504 // - is no longer referenced in the map
505 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
505 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
506 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
506 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
507 emit variableAboutToBeRemoved(variable);
507 emit variableAboutToBeRemoved(variable);
508
508
509 auto &plottablesMap = variableIt->second;
509 auto &plottablesMap = variableIt->second;
510
510
511 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
511 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
512 plottableIt != plottableEnd;) {
512 plottableIt != plottableEnd;) {
513 impl->m_plot->removePlottable(plottableIt->second);
513 impl->m_plot->removePlottable(plottableIt->second);
514 plottableIt = plottablesMap.erase(plottableIt);
514 plottableIt = plottablesMap.erase(plottableIt);
515 }
515 }
516
516
517 impl->m_VariableToPlotMultiMap.erase(variableIt);
517 impl->m_VariableToPlotMultiMap.erase(variableIt);
518 }
518 }
519
519
520 // Updates graph
520 // Updates graph
521 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
521 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
522 }
522 }
523
523
524 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
524 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
525 {
525 {
526 auto variables = std::vector<std::shared_ptr<Variable> >{};
526 auto variables = std::vector<std::shared_ptr<Variable> >{};
527 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
527 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
528 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
528 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
529 variables.push_back(it->first);
529 variables.push_back(it->first);
530 }
530 }
531
531
532 return variables;
532 return variables;
533 }
533 }
534
534
535 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
535 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
536 {
536 {
537 if (!variable) {
537 if (!variable) {
538 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
538 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
539 return;
539 return;
540 }
540 }
541
541
542 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
542 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
543 }
543 }
544
544
545 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
545 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
546 {
546 {
547 auto graphRange = impl->m_plot->xAxis->range();
547 auto graphRange = impl->m_plot->xAxis->range();
548 return DateTimeRange{graphRange.lower, graphRange.upper};
548 return DateTimeRange{graphRange.lower, graphRange.upper};
549 }
549 }
550
550
551 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool updateVar, bool forward)
551 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool updateVar, bool forward)
552 {
552 {
553 impl->setRange(range, updateVar);
553 impl->setRange(range, updateVar);
554 if(forward)
554 if(forward)
555 {
555 {
556 emit this->setrange_sig(this->graphRange(), true, false);
556 emit this->setrange_sig(this->graphRange(), true, false);
557 }
557 }
558
558
559 }
559 }
560
560
561 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
561 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
562 {
562 {
563 impl->m_VariableAutoRangeOnInit = value;
563 impl->m_VariableAutoRangeOnInit = value;
564 }
564 }
565
565
566 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
566 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
567 {
567 {
568 QVector<DateTimeRange> ranges;
568 QVector<DateTimeRange> ranges;
569 for (auto zone : impl->m_SelectionZones) {
569 for (auto zone : impl->m_SelectionZones) {
570 ranges << zone->range();
570 ranges << zone->range();
571 }
571 }
572
572
573 return ranges;
573 return ranges;
574 }
574 }
575
575
576 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
576 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
577 {
577 {
578 for (const auto &range : ranges) {
578 for (const auto &range : ranges) {
579 // note: ownership is transfered to QCustomPlot
579 // note: ownership is transfered to QCustomPlot
580 auto zone = new VisualizationSelectionZoneItem(&plot());
580 auto zone = new VisualizationSelectionZoneItem(&plot());
581 zone->setRange(range.m_TStart, range.m_TEnd);
581 zone->setRange(range.m_TStart, range.m_TEnd);
582 impl->addSelectionZone(zone);
582 impl->addSelectionZone(zone);
583 }
583 }
584
584
585 plot().replot(QCustomPlot::rpQueuedReplot);
585 plot().replot(QCustomPlot::rpQueuedReplot);
586 }
586 }
587
587
588 VisualizationSelectionZoneItem *
588 VisualizationSelectionZoneItem *
589 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
589 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
590 {
590 {
591 // note: ownership is transfered to QCustomPlot
591 // note: ownership is transfered to QCustomPlot
592 auto zone = new VisualizationSelectionZoneItem(&plot());
592 auto zone = new VisualizationSelectionZoneItem(&plot());
593 zone->setName(name);
593 zone->setName(name);
594 zone->setRange(range.m_TStart, range.m_TEnd);
594 zone->setRange(range.m_TStart, range.m_TEnd);
595 impl->addSelectionZone(zone);
595 impl->addSelectionZone(zone);
596
596
597 plot().replot(QCustomPlot::rpQueuedReplot);
597 plot().replot(QCustomPlot::rpQueuedReplot);
598
598
599 return zone;
599 return zone;
600 }
600 }
601
601
602 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
602 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
603 {
603 {
604 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
604 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
605
605
606 if (impl->m_HoveredZone == selectionZone) {
606 if (impl->m_HoveredZone == selectionZone) {
607 impl->m_HoveredZone = nullptr;
607 impl->m_HoveredZone = nullptr;
608 setCursor(Qt::ArrowCursor);
608 setCursor(Qt::ArrowCursor);
609 }
609 }
610
610
611 impl->m_SelectionZones.removeAll(selectionZone);
611 impl->m_SelectionZones.removeAll(selectionZone);
612 plot().removeItem(selectionZone);
612 plot().removeItem(selectionZone);
613 plot().replot(QCustomPlot::rpQueuedReplot);
613 plot().replot(QCustomPlot::rpQueuedReplot);
614 }
614 }
615
615
616 void VisualizationGraphWidget::undoZoom()
616 void VisualizationGraphWidget::undoZoom()
617 {
617 {
618 auto zoom = impl->m_ZoomStack.pop();
618 auto zoom = impl->m_ZoomStack.pop();
619 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
619 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
620 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
620 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
621
621
622 axisX->setRange(zoom.first);
622 axisX->setRange(zoom.first);
623 axisY->setRange(zoom.second);
623 axisY->setRange(zoom.second);
624
624
625 plot().replot(QCustomPlot::rpQueuedReplot);
625 plot().replot(QCustomPlot::rpQueuedReplot);
626 }
626 }
627
627
628 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward)
628 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward)
629 {
629 {
630 impl->zoom(factor, center, orientation);
630 impl->zoom(factor, center, orientation);
631 if(forward && orientation==Qt::Horizontal)
631 if(forward && orientation==Qt::Horizontal)
632 emit this->setrange_sig(this->graphRange(), true, false);
632 emit this->setrange_sig(this->graphRange(), true, false);
633 }
633 }
634
634
635 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
635 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
636 {
636 {
637 impl->move(factor, orientation);
637 impl->move(factor, orientation);
638 if(forward)
638 if(forward)
639 emit this->setrange_sig(this->graphRange(), true, false);
639 emit this->setrange_sig(this->graphRange(), true, false);
640 }
640 }
641
641
642 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
642 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
643 {
643 {
644 impl->move(dx, dy);
644 impl->move(dx, dy);
645 if(forward)
645 if(forward)
646 emit this->setrange_sig(this->graphRange(), true, false);
646 emit this->setrange_sig(this->graphRange(), true, false);
647 }
647 }
648
648
649 void VisualizationGraphWidget::transform(const DateTimeRangeTransformation &tranformation, bool forward)
649 void VisualizationGraphWidget::transform(const DateTimeRangeTransformation &tranformation, bool forward)
650 {
650 {
651 impl->transform(tranformation);
651 impl->transform(tranformation);
652 if(forward)
652 if(forward)
653 emit this->setrange_sig(this->graphRange(), true, false);
653 emit this->setrange_sig(this->graphRange(), true, false);
654 }
654 }
655
655
656 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
656 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
657 {
657 {
658 if (visitor) {
658 if (visitor) {
659 visitor->visit(this);
659 visitor->visit(this);
660 }
660 }
661 else {
661 else {
662 qCCritical(LOG_VisualizationGraphWidget())
662 qCCritical(LOG_VisualizationGraphWidget())
663 << tr("Can't visit widget : the visitor is null");
663 << tr("Can't visit widget : the visitor is null");
664 }
664 }
665 }
665 }
666
666
667 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
667 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
668 {
668 {
669 auto isSpectrogram = [](const auto &variable) {
669 auto isSpectrogram = [](const auto &variable) {
670 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
670 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
671 };
671 };
672
672
673 // - A spectrogram series can't be dropped on graph with existing plottables
673 // - A spectrogram series can't be dropped on graph with existing plottables
674 // - No data series can be dropped on graph with existing spectrogram series
674 // - No data series can be dropped on graph with existing spectrogram series
675 return isSpectrogram(variable)
675 return isSpectrogram(variable)
676 ? impl->m_VariableToPlotMultiMap.empty()
676 ? impl->m_VariableToPlotMultiMap.empty()
677 : std::none_of(
677 : std::none_of(
678 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
678 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
679 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
679 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
680 }
680 }
681
681
682 bool VisualizationGraphWidget::contains(const Variable &variable) const
682 bool VisualizationGraphWidget::contains(const Variable &variable) const
683 {
683 {
684 // Finds the variable among the keys of the map
684 // Finds the variable among the keys of the map
685 auto variablePtr = &variable;
685 auto variablePtr = &variable;
686 auto findVariable
686 auto findVariable
687 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
687 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
688
688
689 auto end = impl->m_VariableToPlotMultiMap.cend();
689 auto end = impl->m_VariableToPlotMultiMap.cend();
690 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
690 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
691 return it != end;
691 return it != end;
692 }
692 }
693
693
694 QString VisualizationGraphWidget::name() const
694 QString VisualizationGraphWidget::name() const
695 {
695 {
696 return impl->m_Name;
696 return impl->m_Name;
697 }
697 }
698
698
699 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
699 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
700 {
700 {
701 auto mimeData = new QMimeData;
701 auto mimeData = new QMimeData;
702
702
703 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
703 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
704 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
704 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
705 && selectionZoneItemUnderCursor) {
705 && selectionZoneItemUnderCursor) {
706 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
706 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
707 selectionZoneItemUnderCursor->range()));
707 selectionZoneItemUnderCursor->range()));
708 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
708 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
709 selectionZoneItemUnderCursor->range()));
709 selectionZoneItemUnderCursor->range()));
710 }
710 }
711 else {
711 else {
712 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
712 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
713
713
714 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
714 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
715 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
715 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
716 }
716 }
717
717
718 return mimeData;
718 return mimeData;
719 }
719 }
720
720
721 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
721 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
722 {
722 {
723 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
723 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
724 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
724 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
725 && selectionZoneItemUnderCursor) {
725 && selectionZoneItemUnderCursor) {
726
726
727 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
727 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
728 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
728 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
729
729
730 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
730 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
731 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
731 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
732 .toSize();
732 .toSize();
733
733
734 auto pixmap = QPixmap(zoneSize);
734 auto pixmap = QPixmap(zoneSize);
735 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
735 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
736
736
737 return pixmap;
737 return pixmap;
738 }
738 }
739
739
740 return QPixmap();
740 return QPixmap();
741 }
741 }
742
742
743 bool VisualizationGraphWidget::isDragAllowed() const
743 bool VisualizationGraphWidget::isDragAllowed() const
744 {
744 {
745 return true;
745 return true;
746 }
746 }
747
747
748 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
748 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
749 {
749 {
750 if (highlighted) {
750 if (highlighted) {
751 plot().setBackground(QBrush(QColor("#BBD5EE")));
751 plot().setBackground(QBrush(QColor("#BBD5EE")));
752 }
752 }
753 else {
753 else {
754 plot().setBackground(QBrush(Qt::white));
754 plot().setBackground(QBrush(Qt::white));
755 }
755 }
756
756
757 plot().update();
757 plot().update();
758 }
758 }
759
759
760 void VisualizationGraphWidget::addVerticalCursor(double time)
760 void VisualizationGraphWidget::addVerticalCursor(double time)
761 {
761 {
762 impl->m_VerticalCursor->setPosition(time);
762 impl->m_VerticalCursor->setPosition(time);
763 impl->m_VerticalCursor->setVisible(true);
763 impl->m_VerticalCursor->setVisible(true);
764
764
765 auto text
765 auto text
766 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
766 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
767 impl->m_VerticalCursor->setLabelText(text);
767 impl->m_VerticalCursor->setLabelText(text);
768 }
768 }
769
769
770 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
770 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
771 {
771 {
772 impl->m_VerticalCursor->setAbsolutePosition(position);
772 impl->m_VerticalCursor->setAbsolutePosition(position);
773 impl->m_VerticalCursor->setVisible(true);
773 impl->m_VerticalCursor->setVisible(true);
774
774
775 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
775 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
776 auto text
776 auto text
777 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
777 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
778 impl->m_VerticalCursor->setLabelText(text);
778 impl->m_VerticalCursor->setLabelText(text);
779 }
779 }
780
780
781 void VisualizationGraphWidget::removeVerticalCursor()
781 void VisualizationGraphWidget::removeVerticalCursor()
782 {
782 {
783 impl->m_VerticalCursor->setVisible(false);
783 impl->m_VerticalCursor->setVisible(false);
784 plot().replot(QCustomPlot::rpQueuedReplot);
784 plot().replot(QCustomPlot::rpQueuedReplot);
785 }
785 }
786
786
787 void VisualizationGraphWidget::addHorizontalCursor(double value)
787 void VisualizationGraphWidget::addHorizontalCursor(double value)
788 {
788 {
789 impl->m_HorizontalCursor->setPosition(value);
789 impl->m_HorizontalCursor->setPosition(value);
790 impl->m_HorizontalCursor->setVisible(true);
790 impl->m_HorizontalCursor->setVisible(true);
791 impl->m_HorizontalCursor->setLabelText(QString::number(value));
791 impl->m_HorizontalCursor->setLabelText(QString::number(value));
792 }
792 }
793
793
794 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
794 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
795 {
795 {
796 impl->m_HorizontalCursor->setAbsolutePosition(position);
796 impl->m_HorizontalCursor->setAbsolutePosition(position);
797 impl->m_HorizontalCursor->setVisible(true);
797 impl->m_HorizontalCursor->setVisible(true);
798
798
799 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
799 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
800 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
800 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
801 }
801 }
802
802
803 void VisualizationGraphWidget::removeHorizontalCursor()
803 void VisualizationGraphWidget::removeHorizontalCursor()
804 {
804 {
805 impl->m_HorizontalCursor->setVisible(false);
805 impl->m_HorizontalCursor->setVisible(false);
806 plot().replot(QCustomPlot::rpQueuedReplot);
806 plot().replot(QCustomPlot::rpQueuedReplot);
807 }
807 }
808
808
809 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
809 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
810 {
810 {
811 Q_UNUSED(event);
811 Q_UNUSED(event);
812
812
813 for (auto i : impl->m_SelectionZones) {
813 for (auto i : impl->m_SelectionZones) {
814 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
814 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
815 }
815 }
816
816
817 // Prevents that all variables will be removed from graph when it will be closed
817 // Prevents that all variables will be removed from graph when it will be closed
818 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
818 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
819 emit variableAboutToBeRemoved(variableEntry.first);
819 emit variableAboutToBeRemoved(variableEntry.first);
820 }
820 }
821 }
821 }
822
822
823 void VisualizationGraphWidget::enterEvent(QEvent *event)
823 void VisualizationGraphWidget::enterEvent(QEvent *event)
824 {
824 {
825 Q_UNUSED(event);
825 Q_UNUSED(event);
826 impl->m_RenderingDelegate->showGraphOverlay(true);
826 impl->m_RenderingDelegate->showGraphOverlay(true);
827 }
827 }
828
828
829 void VisualizationGraphWidget::leaveEvent(QEvent *event)
829 void VisualizationGraphWidget::leaveEvent(QEvent *event)
830 {
830 {
831 Q_UNUSED(event);
831 Q_UNUSED(event);
832 impl->m_RenderingDelegate->showGraphOverlay(false);
832 impl->m_RenderingDelegate->showGraphOverlay(false);
833
833
834 if (auto parentZone = parentZoneWidget()) {
834 if (auto parentZone = parentZoneWidget()) {
835 parentZone->notifyMouseLeaveGraph(this);
835 parentZone->notifyMouseLeaveGraph(this);
836 }
836 }
837 else {
837 else {
838 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
838 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
839 }
839 }
840
840
841 if (impl->m_HoveredZone) {
841 if (impl->m_HoveredZone) {
842 impl->m_HoveredZone->setHovered(false);
842 impl->m_HoveredZone->setHovered(false);
843 impl->m_HoveredZone = nullptr;
843 impl->m_HoveredZone = nullptr;
844 }
844 }
845 }
845 }
846
846
847 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
847 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
848 {
848 {
849 double factor;
849 double factor;
850 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
850 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
851 if (event->modifiers() == Qt::ControlModifier) {
851 if (event->modifiers() == Qt::ControlModifier) {
852 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
852 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
853 {
853 {
854 setCursor(Qt::SizeVerCursor);
854 setCursor(Qt::SizeVerCursor);
855 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
855 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
856 zoom(factor, event->pos().y(), Qt::Vertical);
856 zoom(factor, event->pos().y(), Qt::Vertical);
857 }
857 }
858 }
858 }
859 else if (event->modifiers() == Qt::ShiftModifier) {
859 else if (event->modifiers() == Qt::ShiftModifier) {
860 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
860 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
861 {
861 {
862 setCursor(Qt::SizeHorCursor);
862 setCursor(Qt::SizeHorCursor);
863 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
863 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
864 zoom(factor, event->pos().x(), Qt::Horizontal);
864 zoom(factor, event->pos().x(), Qt::Horizontal);
865 }
865 }
866 }
866 }
867 else {
867 else {
868 move(wheelSteps, Qt::Horizontal);
868 move(wheelSteps, Qt::Horizontal);
869 }
869 }
870 event->accept();
870 event->accept();
871 }
871 }
872
872
873
873
874
874
875 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
875 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
876 {
876 {
877 if(impl->isDrawingZoomRect())
877 if(impl->isDrawingZoomRect())
878 {
878 {
879 impl->updateZoomRect(event->pos());
879 impl->updateZoomRect(event->pos());
880 }
880 }
881 else if (impl->isDrawingZoneRect())
881 else if (impl->isDrawingZoneRect())
882 {
882 {
883 impl->updateZoneRect(event->pos());
883 impl->updateZoneRect(event->pos());
884 }
884 }
885 else if (event->buttons() == Qt::LeftButton)
885 else if (event->buttons() == Qt::LeftButton)
886 {
886 {
887 if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
887 if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
888 {
888 {
889 auto [dx,dy] = impl->moveGraph(event->pos());
889 auto [dx,dy] = impl->moveGraph(event->pos());
890 emit this->setrange_sig(this->graphRange(), true, false);
890 emit this->setrange_sig(this->graphRange(), true, false);
891 }
891 }
892 else if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
892 else if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
893 {
893 {
894
894
895 }
895 }
896 }
896 }
897 else
897 else
898 {
898 {
899 impl->m_RenderingDelegate->updateTooltip(event);
899 impl->m_RenderingDelegate->updateTooltip(event);
900 }
900 }
901 //event->accept();
901 //event->accept();
902 QWidget::mouseMoveEvent(event);
902 QWidget::mouseMoveEvent(event);
903 }
903 }
904
904
905 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
905 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
906 {
906 {
907 if(impl->isDrawingZoomRect())
907 if(impl->isDrawingZoomRect())
908 {
908 {
909 auto oldRange = this->graphRange();
909 auto oldRange = this->graphRange();
910 impl->applyZoomRect();
910 impl->applyZoomRect();
911 auto newRange = this->graphRange();
911 auto newRange = this->graphRange();
912 if(auto tf = DateTimeRangeHelper::computeTransformation(oldRange,newRange))
912 if(auto tf = DateTimeRangeHelper::computeTransformation(oldRange,newRange))
913 emit this->transform_sig(tf.value(), false);
913 emit this->transform_sig(tf.value(), false);
914 }
914 }
915 else if(impl->isDrawingZoneRect())
915 else if(impl->isDrawingZoneRect())
916 {
916 {
917 impl->endDrawingZone();
917 impl->endDrawingZone();
918 }
918 }
919 else
919 else
920 {
920 {
921 setCursor(Qt::ArrowCursor);
921 setCursor(Qt::ArrowCursor);
922 }
922 }
923 event->accept();
923 event->accept();
924 }
924 }
925
925
926 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
926 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
927 {
927 {
928 if (event->button()==Qt::RightButton)
928 if (event->button()==Qt::RightButton)
929 {
929 {
930 onGraphMenuRequested(event->pos());
930 onGraphMenuRequested(event->pos());
931 }
931 }
932 else
932 else
933 {
933 {
934 auto selectedZone = impl->selectionZoneAt(event->pos());
934 auto selectedZone = impl->selectionZoneAt(event->pos());
935 switch (sqpApp->plotsInteractionMode())
935 switch (sqpApp->plotsInteractionMode())
936 {
936 {
937 case SqpApplication::PlotsInteractionMode::DragAndDrop :
937 case SqpApplication::PlotsInteractionMode::DragAndDrop :
938 break;
938 break;
939 case SqpApplication::PlotsInteractionMode::SelectionZones :
939 case SqpApplication::PlotsInteractionMode::SelectionZones :
940 impl->setSelectionZonesEditionEnabled(true);
940 impl->setSelectionZonesEditionEnabled(true);
941 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
941 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
942 {
942 {
943 selectedZone->setAssociatedEditedZones(parentVisualizationWidget()->selectionZoneManager().selectedItems());
943 selectedZone->setAssociatedEditedZones(parentVisualizationWidget()->selectionZoneManager().selectedItems());
944 }
944 }
945 else
945 else
946 {
946 {
947 if (!selectedZone)
947 if (!selectedZone)
948 {
948 {
949 parentVisualizationWidget()->selectionZoneManager().clearSelection();
949 parentVisualizationWidget()->selectionZoneManager().clearSelection();
950 impl->startDrawingZone(event->pos());
950 impl->startDrawingZone(event->pos());
951 }
951 }
952 else
952 else
953 {
953 {
954 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
954 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
955 }
955 }
956 }
956 }
957 break;
957 break;
958 case SqpApplication::PlotsInteractionMode::ZoomBox :
958 case SqpApplication::PlotsInteractionMode::ZoomBox :
959 impl->startDrawingRect(event->pos());
959 impl->startDrawingRect(event->pos());
960 break;
960 break;
961 default:
961 default:
962 if(auto item = impl->m_plot->itemAt(event->pos()))
962 if(auto item = impl->m_plot->itemAt(event->pos()))
963 {
963 {
964 emit impl->m_plot->itemClick(item,event);
964 emit impl->m_plot->itemClick(item,event);
965 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
965 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
966 {
966 {
967 setCursor(Qt::ClosedHandCursor);
967 setCursor(Qt::ClosedHandCursor);
968 impl->enterPlotDrag(event->pos());
968 impl->enterPlotDrag(event->pos());
969 }
969 }
970 }
970 }
971 else
971 else
972 {
972 {
973 setCursor(Qt::ClosedHandCursor);
973 setCursor(Qt::ClosedHandCursor);
974 impl->enterPlotDrag(event->pos());
974 impl->enterPlotDrag(event->pos());
975 }
975 }
976 }
976 }
977 }
977 }
978 //event->accept();
978 //event->accept();
979 QWidget::mousePressEvent(event);
979 QWidget::mousePressEvent(event);
980 }
980 }
981
981
982 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
982 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
983 {
983 {
984 impl->m_RenderingDelegate->onMouseDoubleClick(event);
984 impl->m_RenderingDelegate->onMouseDoubleClick(event);
985 }
985 }
986
986
987 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
987 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
988 {
988 {
989 switch (event->key()) {
989 switch (event->key()) {
990 case Qt::Key_Control:
990 case Qt::Key_Control:
991 event->accept();
991 event->accept();
992 break;
992 break;
993 case Qt::Key_Shift:
993 case Qt::Key_Shift:
994 event->accept();
994 event->accept();
995 break;
995 break;
996 default:
996 default:
997 QWidget::keyReleaseEvent(event);
997 QWidget::keyReleaseEvent(event);
998 break;
998 break;
999 }
999 }
1000 setCursor(Qt::ArrowCursor);
1000 setCursor(Qt::ArrowCursor);
1001 //event->accept();
1001 //event->accept();
1002 }
1002 }
1003
1003
1004 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
1004 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
1005 {
1005 {
1006 switch (event->key()) {
1006 switch (event->key()) {
1007 case Qt::Key_Control:
1007 case Qt::Key_Control:
1008 setCursor(Qt::CrossCursor);
1008 setCursor(Qt::CrossCursor);
1009 break;
1009 break;
1010 case Qt::Key_Shift:
1010 case Qt::Key_Shift:
1011 break;
1011 break;
1012 case Qt::Key_M:
1012 case Qt::Key_M:
1013 impl->rescaleY();
1013 impl->rescaleY();
1014 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1014 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1015 break;
1015 break;
1016 case Qt::Key_Left:
1016 case Qt::Key_Left:
1017 if (event->modifiers() != Qt::ControlModifier) {
1017 if (event->modifiers() != Qt::ControlModifier) {
1018 move(-0.1, Qt::Horizontal);
1018 move(-0.1, Qt::Horizontal);
1019 }
1019 }
1020 else {
1020 else {
1021 zoom(2, this->width() / 2, Qt::Horizontal);
1021 zoom(2, this->width() / 2, Qt::Horizontal);
1022 }
1022 }
1023 break;
1023 break;
1024 case Qt::Key_Right:
1024 case Qt::Key_Right:
1025 if (event->modifiers() != Qt::ControlModifier) {
1025 if (event->modifiers() != Qt::ControlModifier) {
1026 move(0.1, Qt::Horizontal);
1026 move(0.1, Qt::Horizontal);
1027 }
1027 }
1028 else {
1028 else {
1029 zoom(0.5, this->width() / 2, Qt::Horizontal);
1029 zoom(0.5, this->width() / 2, Qt::Horizontal);
1030 }
1030 }
1031 break;
1031 break;
1032 case Qt::Key_Up:
1032 case Qt::Key_Up:
1033 if (event->modifiers() != Qt::ControlModifier) {
1033 if (event->modifiers() != Qt::ControlModifier) {
1034 move(0.1, Qt::Vertical);
1034 move(0.1, Qt::Vertical);
1035 }
1035 }
1036 else {
1036 else {
1037 zoom(0.5, this->height() / 2, Qt::Vertical);
1037 zoom(0.5, this->height() / 2, Qt::Vertical);
1038 }
1038 }
1039 break;
1039 break;
1040 case Qt::Key_Down:
1040 case Qt::Key_Down:
1041 if (event->modifiers() != Qt::ControlModifier) {
1041 if (event->modifiers() != Qt::ControlModifier) {
1042 move(-0.1, Qt::Vertical);
1042 move(-0.1, Qt::Vertical);
1043 }
1043 }
1044 else {
1044 else {
1045 zoom(2, this->height() / 2, Qt::Vertical);
1045 zoom(2, this->height() / 2, Qt::Vertical);
1046 }
1046 }
1047 break;
1047 break;
1048 default:
1048 default:
1049 QWidget::keyPressEvent(event);
1049 QWidget::keyPressEvent(event);
1050 break;
1050 break;
1051 }
1051 }
1052 }
1052 }
1053
1053
1054 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
1054 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
1055 {
1055 {
1056 return *impl->m_plot;
1056 return *impl->m_plot;
1057 }
1057 }
1058
1058
1059 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
1059 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
1060 {
1060 {
1061 QMenu graphMenu{};
1061 QMenu graphMenu{};
1062
1062
1063 // Iterates on variables (unique keys)
1063 // Iterates on variables (unique keys)
1064 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1064 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1065 end = impl->m_VariableToPlotMultiMap.cend();
1065 end = impl->m_VariableToPlotMultiMap.cend();
1066 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
1066 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
1067 // 'Remove variable' action
1067 // 'Remove variable' action
1068 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1068 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1069 [this, var = it->first]() { removeVariable(var); });
1069 [this, var = it->first]() { removeVariable(var); });
1070 }
1070 }
1071
1071
1072 if (!impl->m_ZoomStack.isEmpty()) {
1072 if (!impl->m_ZoomStack.isEmpty()) {
1073 if (!graphMenu.isEmpty()) {
1073 if (!graphMenu.isEmpty()) {
1074 graphMenu.addSeparator();
1074 graphMenu.addSeparator();
1075 }
1075 }
1076
1076
1077 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1077 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1078 }
1078 }
1079
1079
1080 // Selection Zone Actions
1080 // Selection Zone Actions
1081 auto selectionZoneItem = impl->selectionZoneAt(pos);
1081 auto selectionZoneItem = impl->selectionZoneAt(pos);
1082 if (selectionZoneItem) {
1082 if (selectionZoneItem) {
1083 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1083 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1084 selectedItems.removeAll(selectionZoneItem);
1084 selectedItems.removeAll(selectionZoneItem);
1085 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1085 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1086
1086
1087 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1087 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1088 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
1088 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
1089 graphMenu.addSeparator();
1089 graphMenu.addSeparator();
1090 }
1090 }
1091
1091
1092 QHash<QString, QMenu *> subMenus;
1092 QHash<QString, QMenu *> subMenus;
1093 QHash<QString, bool> subMenusEnabled;
1093 QHash<QString, bool> subMenusEnabled;
1094 QHash<QString, FilteringAction *> filteredMenu;
1094 QHash<QString, FilteringAction *> filteredMenu;
1095
1095
1096 for (auto zoneAction : zoneActions) {
1096 for (auto zoneAction : zoneActions) {
1097
1097
1098 auto isEnabled = zoneAction->isEnabled(selectedItems);
1098 auto isEnabled = zoneAction->isEnabled(selectedItems);
1099
1099
1100 auto menu = &graphMenu;
1100 auto menu = &graphMenu;
1101 QString menuPath;
1101 QString menuPath;
1102 for (auto subMenuName : zoneAction->subMenuList()) {
1102 for (auto subMenuName : zoneAction->subMenuList()) {
1103 menuPath += '/';
1103 menuPath += '/';
1104 menuPath += subMenuName;
1104 menuPath += subMenuName;
1105
1105
1106 if (!subMenus.contains(menuPath)) {
1106 if (!subMenus.contains(menuPath)) {
1107 menu = menu->addMenu(subMenuName);
1107 menu = menu->addMenu(subMenuName);
1108 subMenus[menuPath] = menu;
1108 subMenus[menuPath] = menu;
1109 subMenusEnabled[menuPath] = isEnabled;
1109 subMenusEnabled[menuPath] = isEnabled;
1110 }
1110 }
1111 else {
1111 else {
1112 menu = subMenus.value(menuPath);
1112 menu = subMenus.value(menuPath);
1113 if (isEnabled) {
1113 if (isEnabled) {
1114 // The sub menu is enabled if at least one of its actions is enabled
1114 // The sub menu is enabled if at least one of its actions is enabled
1115 subMenusEnabled[menuPath] = true;
1115 subMenusEnabled[menuPath] = true;
1116 }
1116 }
1117 }
1117 }
1118 }
1118 }
1119
1119
1120 FilteringAction *filterAction = nullptr;
1120 FilteringAction *filterAction = nullptr;
1121 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1121 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1122 filterAction = filteredMenu.value(menuPath);
1122 filterAction = filteredMenu.value(menuPath);
1123 if (!filterAction) {
1123 if (!filterAction) {
1124 filterAction = new FilteringAction{this};
1124 filterAction = new FilteringAction{this};
1125 filteredMenu[menuPath] = filterAction;
1125 filteredMenu[menuPath] = filterAction;
1126 menu->addAction(filterAction);
1126 menu->addAction(filterAction);
1127 }
1127 }
1128 }
1128 }
1129
1129
1130 auto action = menu->addAction(zoneAction->name());
1130 auto action = menu->addAction(zoneAction->name());
1131 action->setEnabled(isEnabled);
1131 action->setEnabled(isEnabled);
1132 action->setShortcut(zoneAction->displayedShortcut());
1132 action->setShortcut(zoneAction->displayedShortcut());
1133 QObject::connect(action, &QAction::triggered,
1133 QObject::connect(action, &QAction::triggered,
1134 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1134 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1135
1135
1136 if (filterAction && zoneAction->isFilteringAllowed()) {
1136 if (filterAction && zoneAction->isFilteringAllowed()) {
1137 filterAction->addActionToFilter(action);
1137 filterAction->addActionToFilter(action);
1138 }
1138 }
1139 }
1139 }
1140
1140
1141 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1141 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1142 it.value()->setEnabled(subMenusEnabled[it.key()]);
1142 it.value()->setEnabled(subMenusEnabled[it.key()]);
1143 }
1143 }
1144 }
1144 }
1145
1145
1146 if (!graphMenu.isEmpty()) {
1146 if (!graphMenu.isEmpty()) {
1147 graphMenu.exec(QCursor::pos());
1147 graphMenu.exec(QCursor::pos());
1148 }
1148 }
1149 }
1149 }
1150
1150
1151 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1151 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1152 {
1152 {
1153 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1153 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1154 }
1154 }
1155
1155
1156 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1156 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1157 {
1157 {
1158 // Handles plot rendering when mouse is moving
1158 // Handles plot rendering when mouse is moving
1159 impl->m_RenderingDelegate->updateTooltip(event);
1159 impl->m_RenderingDelegate->updateTooltip(event);
1160
1160
1161 auto axisPos = impl->posToAxisPos(event->pos());
1161 auto axisPos = impl->posToAxisPos(event->pos());
1162
1162
1163 // Zoom box and zone drawing
1163 // Zoom box and zone drawing
1164 if (impl->m_DrawingZoomRect) {
1164 if (impl->m_DrawingZoomRect) {
1165 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1165 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1166 }
1166 }
1167 else if (impl->m_DrawingZone) {
1167 else if (impl->m_DrawingZone) {
1168 impl->m_DrawingZone->setEnd(axisPos.x());
1168 impl->m_DrawingZone->setEnd(axisPos.x());
1169 }
1169 }
1170
1170
1171 // Cursor
1171 // Cursor
1172 if (auto parentZone = parentZoneWidget()) {
1172 if (auto parentZone = parentZoneWidget()) {
1173 if (impl->pointIsInAxisRect(axisPos, plot())) {
1173 if (impl->pointIsInAxisRect(axisPos, plot())) {
1174 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1174 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1175 }
1175 }
1176 else {
1176 else {
1177 parentZone->notifyMouseLeaveGraph(this);
1177 parentZone->notifyMouseLeaveGraph(this);
1178 }
1178 }
1179 }
1179 }
1180
1180
1181 // Search for the selection zone under the mouse
1181 // Search for the selection zone under the mouse
1182 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1182 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1183 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1183 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1184 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1184 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1185
1185
1186 // Sets the appropriate cursor shape
1186 // Sets the appropriate cursor shape
1187 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1187 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1188 setCursor(cursorShape);
1188 setCursor(cursorShape);
1189
1189
1190 // Manages the hovered zone
1190 // Manages the hovered zone
1191 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1191 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1192 if (impl->m_HoveredZone) {
1192 if (impl->m_HoveredZone) {
1193 impl->m_HoveredZone->setHovered(false);
1193 impl->m_HoveredZone->setHovered(false);
1194 }
1194 }
1195 selectionZoneItemUnderCursor->setHovered(true);
1195 selectionZoneItemUnderCursor->setHovered(true);
1196 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1196 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1197 plot().replot(QCustomPlot::rpQueuedReplot);
1197 plot().replot(QCustomPlot::rpQueuedReplot);
1198 }
1198 }
1199 }
1199 }
1200 else {
1200 else {
1201 // There is no zone under the mouse or the interaction mode is not "selection zones"
1201 // There is no zone under the mouse or the interaction mode is not "selection zones"
1202 if (impl->m_HoveredZone) {
1202 if (impl->m_HoveredZone) {
1203 impl->m_HoveredZone->setHovered(false);
1203 impl->m_HoveredZone->setHovered(false);
1204 impl->m_HoveredZone = nullptr;
1204 impl->m_HoveredZone = nullptr;
1205 }
1205 }
1206
1206
1207 setCursor(Qt::ArrowCursor);
1207 setCursor(Qt::ArrowCursor);
1208 }
1208 }
1209
1209
1210 impl->m_HasMovedMouse = true;
1210 impl->m_HasMovedMouse = true;
1211 VisualizationDragWidget::mouseMoveEvent(event);
1211 VisualizationDragWidget::mouseMoveEvent(event);
1212 }
1212 }
1213
1213
1214 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1214 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1215 {
1215 {
1216 // Processes event only if the wheel occurs on axis rect
1216 // Processes event only if the wheel occurs on axis rect
1217 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1217 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1218 return;
1218 return;
1219 }
1219 }
1220
1220
1221 auto value = event->angleDelta().x() + event->angleDelta().y();
1221 auto value = event->angleDelta().x() + event->angleDelta().y();
1222 if (value != 0) {
1222 if (value != 0) {
1223
1223
1224 auto direction = value > 0 ? 1.0 : -1.0;
1224 auto direction = value > 0 ? 1.0 : -1.0;
1225 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1225 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1226 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1226 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1227 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1227 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1228
1228
1229 auto zoomOrientations = QFlags<Qt::Orientation>{};
1229 auto zoomOrientations = QFlags<Qt::Orientation>{};
1230 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1230 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1231 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1231 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1232
1232
1233 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1233 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1234
1234
1235 if (!isZoomX && !isZoomY) {
1235 if (!isZoomX && !isZoomY) {
1236 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1236 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1237 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1237 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1238
1238
1239 axis->setRange(axis->range() + diff);
1239 axis->setRange(axis->range() + diff);
1240
1240
1241 if (plot().noAntialiasingOnDrag()) {
1241 if (plot().noAntialiasingOnDrag()) {
1242 plot().setNotAntialiasedElements(QCP::aeAll);
1242 plot().setNotAntialiasedElements(QCP::aeAll);
1243 }
1243 }
1244
1244
1245 // plot().replot(QCustomPlot::rpQueuedReplot);
1245 // plot().replot(QCustomPlot::rpQueuedReplot);
1246 }
1246 }
1247 }
1247 }
1248 }
1248 }
1249
1249
1250 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1250 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1251 {
1251 {
1252 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1252 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1253 auto isSelectionZoneMode
1253 auto isSelectionZoneMode
1254 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1254 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1255 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1255 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1256
1256
1257 if (!isDragDropClick && isLeftClick) {
1257 if (!isDragDropClick && isLeftClick) {
1258 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1258 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1259 // Starts a zoom box
1259 // Starts a zoom box
1260 impl->startDrawingRect(event->pos());
1260 impl->startDrawingRect(event->pos());
1261 }
1261 }
1262 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1262 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1263 // Starts a new selection zone
1263 // Starts a new selection zone
1264 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1264 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1265 if (!zoneAtPos) {
1265 if (!zoneAtPos) {
1266 impl->startDrawingZone(event->pos());
1266 impl->startDrawingZone(event->pos());
1267 }
1267 }
1268 }
1268 }
1269 }
1269 }
1270
1270
1271
1271
1272 // Allows zone edition only in selection zone mode without drag&drop
1272 // Allows zone edition only in selection zone mode without drag&drop
1273 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1273 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1274
1274
1275 // Selection / Deselection
1275 // Selection / Deselection
1276 if (isSelectionZoneMode) {
1276 if (isSelectionZoneMode) {
1277 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1277 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1278 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1278 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1279
1279
1280
1280
1281 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1281 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1282 && !isMultiSelectionClick) {
1282 && !isMultiSelectionClick) {
1283 parentVisualizationWidget()->selectionZoneManager().select(
1283 parentVisualizationWidget()->selectionZoneManager().select(
1284 {selectionZoneItemUnderCursor});
1284 {selectionZoneItemUnderCursor});
1285 }
1285 }
1286 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1286 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1287 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1287 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1288 }
1288 }
1289 else {
1289 else {
1290 // No selection change
1290 // No selection change
1291 }
1291 }
1292
1292
1293 if (selectionZoneItemUnderCursor && isLeftClick) {
1293 if (selectionZoneItemUnderCursor && isLeftClick) {
1294 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1294 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1295 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1295 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1296 }
1296 }
1297 }
1297 }
1298
1298
1299
1299
1300 impl->m_HasMovedMouse = false;
1300 impl->m_HasMovedMouse = false;
1301 VisualizationDragWidget::mousePressEvent(event);
1301 VisualizationDragWidget::mousePressEvent(event);
1302 }
1302 }
1303
1303
1304 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1304 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1305 {
1305 {
1306 if (impl->m_DrawingZoomRect) {
1306 if (impl->m_DrawingZoomRect) {
1307
1307
1308 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1308 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1309 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1309 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1310
1310
1311 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1311 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1312 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1312 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1313
1313
1314 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1314 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1315 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1315 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1316
1316
1317 impl->removeDrawingRect();
1317 impl->removeDrawingRect();
1318
1318
1319 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1319 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1320 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1320 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1321 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1321 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1322 axisX->setRange(newAxisXRange);
1322 axisX->setRange(newAxisXRange);
1323 axisY->setRange(newAxisYRange);
1323 axisY->setRange(newAxisYRange);
1324
1324
1325 plot().replot(QCustomPlot::rpQueuedReplot);
1325 plot().replot(QCustomPlot::rpQueuedReplot);
1326 }
1326 }
1327 }
1327 }
1328
1328
1329 impl->endDrawingZone();
1329 impl->endDrawingZone();
1330
1330
1331 // Selection / Deselection
1331 // Selection / Deselection
1332 auto isSelectionZoneMode
1332 auto isSelectionZoneMode
1333 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1333 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1334 if (isSelectionZoneMode) {
1334 if (isSelectionZoneMode) {
1335 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1335 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1336 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1336 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1337 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1337 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1338 && !impl->m_HasMovedMouse) {
1338 && !impl->m_HasMovedMouse) {
1339
1339
1340 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1340 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1341 if (zonesUnderCursor.count() > 1) {
1341 if (zonesUnderCursor.count() > 1) {
1342 // There are multiple zones under the mouse.
1342 // There are multiple zones under the mouse.
1343 // Performs the selection with a selection dialog.
1343 // Performs the selection with a selection dialog.
1344 VisualizationMultiZoneSelectionDialog dialog{this};
1344 VisualizationMultiZoneSelectionDialog dialog{this};
1345 dialog.setZones(zonesUnderCursor);
1345 dialog.setZones(zonesUnderCursor);
1346 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1346 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1347 dialog.activateWindow();
1347 dialog.activateWindow();
1348 dialog.raise();
1348 dialog.raise();
1349 if (dialog.exec() == QDialog::Accepted) {
1349 if (dialog.exec() == QDialog::Accepted) {
1350 auto selection = dialog.selectedZones();
1350 auto selection = dialog.selectedZones();
1351
1351
1352 if (!isMultiSelectionClick) {
1352 if (!isMultiSelectionClick) {
1353 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1353 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1354 }
1354 }
1355
1355
1356 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1356 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1357 auto zone = it.key();
1357 auto zone = it.key();
1358 auto isSelected = it.value();
1358 auto isSelected = it.value();
1359 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1359 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1360 isSelected);
1360 isSelected);
1361
1361
1362 if (isSelected) {
1362 if (isSelected) {
1363 // Puts the zone on top of the stack so it can be moved or resized
1363 // Puts the zone on top of the stack so it can be moved or resized
1364 impl->moveSelectionZoneOnTop(zone, plot());
1364 impl->moveSelectionZoneOnTop(zone, plot());
1365 }
1365 }
1366 }
1366 }
1367 }
1367 }
1368 }
1368 }
1369 else {
1369 else {
1370 if (!isMultiSelectionClick) {
1370 if (!isMultiSelectionClick) {
1371 parentVisualizationWidget()->selectionZoneManager().select(
1371 parentVisualizationWidget()->selectionZoneManager().select(
1372 {selectionZoneItemUnderCursor});
1372 {selectionZoneItemUnderCursor});
1373 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1373 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1374 }
1374 }
1375 else {
1375 else {
1376 parentVisualizationWidget()->selectionZoneManager().setSelected(
1376 parentVisualizationWidget()->selectionZoneManager().setSelected(
1377 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1377 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1378 || event->button() == Qt::RightButton);
1378 || event->button() == Qt::RightButton);
1379 }
1379 }
1380 }
1380 }
1381 }
1381 }
1382 else {
1382 else {
1383 // No selection change
1383 // No selection change
1384 }
1384 }
1385 }
1385 }
1386 }
1386 }
1387
1387
1388 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1388 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1389 {
1389 {
1390 auto graphRange = impl->m_plot->xAxis->range();
1390 auto graphRange = impl->m_plot->xAxis->range();
1391 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1391 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1392
1392
1393 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1393 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1394 auto variable = variableEntry.first;
1394 auto variable = variableEntry.first;
1395 qCDebug(LOG_VisualizationGraphWidget())
1395 qCDebug(LOG_VisualizationGraphWidget())
1396 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1396 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1397 qCDebug(LOG_VisualizationGraphWidget())
1397 qCDebug(LOG_VisualizationGraphWidget())
1398 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1398 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1399 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1399 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1400 impl->updateData(variableEntry.second, variable, variable->range());
1400 impl->updateData(variableEntry.second, variable, variable->range());
1401 }
1401 }
1402 }
1402 }
1403 }
1403 }
1404
1404
1405 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1405 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1406 const DateTimeRange &range)
1406 const DateTimeRange &range)
1407 {
1407 {
1408 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1408 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1409 if (it != impl->m_VariableToPlotMultiMap.end()) {
1409 if (it != impl->m_VariableToPlotMultiMap.end()) {
1410 impl->updateData(it->second, variable, range);
1410 impl->updateData(it->second, variable, range);
1411 }
1411 }
1412 }
1412 }
1413
1413
1414 void VisualizationGraphWidget::variableUpdated(QUuid id)
1414 void VisualizationGraphWidget::variableUpdated(QUuid id)
1415 {
1415 {
1416 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1416 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1417 if (var->ID() == id) {
1417 if (var->ID() == id) {
1418 impl->updateData(plotables, var, this->graphRange());
1418 impl->updateData(plotables, var, this->graphRange());
1419 }
1419 }
1420 }
1420 }
1421 this->impl->rescaleY();
1422 }
1421 }
1423
1422
1424 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable> & variable)
1423 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable> & variable)
1425 {
1424 {
1426 this->removeVariable(variable);
1425 this->removeVariable(variable);
1427 }
1426 }
General Comments 0
You need to be logged in to leave comments. Login now