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