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