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