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