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