##// END OF EJS Templates
Corrects the problem of refreshing synchronized graphs from TimeWidget (1)...
Alexandre Leroux -
r1325:87a145505c37
parent child
Show More
@@ -1,141 +1,155
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
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 15
16 16 class QCPRange;
17 17 class QCustomPlot;
18 18 class SqpRange;
19 19 class Variable;
20 20 class VisualizationWidget;
21 21 class VisualizationZoneWidget;
22 22 class VisualizationSelectionZoneItem;
23 23
24 24 namespace Ui {
25 25 class VisualizationGraphWidget;
26 26 } // namespace Ui
27 27
28 /// Defines options that can be associated with the graph
29 enum GraphFlag {
30 DisableAll = 0x0, ///< Disables acquisition and synchronization
31 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
32 /// the acquisition of data
33 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
34 /// the call to the synchronization of the graphs contained in the
35 /// same zone of this graph
36 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
37 };
38
39 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
40 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
41
28 42 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
29 43 Q_OBJECT
30 44
31 45 friend class QCustomPlotSynchronizer;
32 46 friend class VisualizationGraphRenderingDelegate;
33 47
34 48 public:
35 49 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
36 50 virtual ~VisualizationGraphWidget();
37 51
38 52 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
39 53 VisualizationZoneWidget *parentZoneWidget() const noexcept;
40 54
41 55 /// Returns the main VisualizationWidget which contains the graph or nullptr
42 56 VisualizationWidget *parentVisualizationWidget() const;
43 57
44 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
45 void enableAcquisition(bool enable);
58 /// Sets graph options
59 void setFlags(GraphFlags flags);
46 60
47 61 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
48 62
49 63 /// Removes a variable from the graph
50 64 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
51 65
52 66 /// Returns the list of all variables used in the graph
53 67 QList<std::shared_ptr<Variable> > variables() const;
54 68
55 69 /// Sets the y-axis range based on the data of a variable
56 70 void setYRange(std::shared_ptr<Variable> variable);
57 71 SqpRange graphRange() const noexcept;
58 72 void setGraphRange(const SqpRange &range);
59 73
60 74 // Zones
61 75 /// Returns the ranges of all the selection zones on the graph
62 76 QVector<SqpRange> selectionZoneRanges() const;
63 77 /// Adds new selection zones in the graph
64 78 void addSelectionZones(const QVector<SqpRange> &ranges);
65 79 /// Removes the specified selection zone
66 80 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
67 81
68 82 /// Undo the last zoom done with a zoom box
69 83 void undoZoom();
70 84
71 85 // IVisualizationWidget interface
72 86 void accept(IVisualizationWidgetVisitor *visitor) override;
73 87 bool canDrop(const Variable &variable) const override;
74 88 bool contains(const Variable &variable) const override;
75 89 QString name() const override;
76 90
77 91 // VisualisationDragWidget
78 92 QMimeData *mimeData(const QPoint &position) const override;
79 93 QPixmap customDragPixmap(const QPoint &dragPosition) override;
80 94 bool isDragAllowed() const override;
81 95 void highlightForMerge(bool highlighted) override;
82 96
83 97 // Cursors
84 98 /// Adds or moves the vertical cursor at the specified value on the x-axis
85 99 void addVerticalCursor(double time);
86 100 /// Adds or moves the vertical cursor at the specified value on the x-axis
87 101 void addVerticalCursorAtViewportPosition(double position);
88 102 void removeVerticalCursor();
89 103 /// Adds or moves the vertical cursor at the specified value on the y-axis
90 104 void addHorizontalCursor(double value);
91 105 /// Adds or moves the vertical cursor at the specified value on the y-axis
92 106 void addHorizontalCursorAtViewportPosition(double position);
93 107 void removeHorizontalCursor();
94 108
95 109 signals:
96 110 void synchronize(const SqpRange &range, const SqpRange &oldRange);
97 111 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
98 112 bool synchronise);
99 113
100 114 /// Signal emitted when the variable is about to be removed from the graph
101 115 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
102 116 /// Signal emitted when the variable has been added to the graph
103 117 void variableAdded(std::shared_ptr<Variable> var);
104 118
105 119 protected:
106 120 void closeEvent(QCloseEvent *event) override;
107 121 void enterEvent(QEvent *event) override;
108 122 void leaveEvent(QEvent *event) override;
109 123
110 124 QCustomPlot &plot() const noexcept;
111 125
112 126 private:
113 127 Ui::VisualizationGraphWidget *ui;
114 128
115 129 class VisualizationGraphWidgetPrivate;
116 130 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
117 131
118 132 private slots:
119 133 /// Slot called when right clicking on the graph (displays a menu)
120 134 void onGraphMenuRequested(const QPoint &pos) noexcept;
121 135
122 136 /// Rescale the X axe to range parameter
123 137 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
124 138
125 139 /// Slot called when a mouse double click was made
126 140 void onMouseDoubleClick(QMouseEvent *event) noexcept;
127 141 /// Slot called when a mouse move was made
128 142 void onMouseMove(QMouseEvent *event) noexcept;
129 143 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
130 144 void onMouseWheel(QWheelEvent *event) noexcept;
131 145 /// Slot called when a mouse press was made, to activate the calibration of a graph
132 146 void onMousePress(QMouseEvent *event) noexcept;
133 147 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
134 148 void onMouseRelease(QMouseEvent *event) noexcept;
135 149
136 150 void onDataCacheVariableUpdated();
137 151
138 152 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
139 153 };
140 154
141 155 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,1003 +1,1003
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 <Common/MimeTypesDef.h>
16 16 #include <Data/ArrayData.h>
17 17 #include <Data/IDataSeries.h>
18 18 #include <Data/SpectrogramSeries.h>
19 19 #include <DragAndDrop/DragDropGuiController.h>
20 20 #include <Settings/SqpSettingsDefs.h>
21 21 #include <SqpApplication.h>
22 22 #include <Time/TimeController.h>
23 23 #include <Variable/Variable.h>
24 24 #include <Variable/VariableController.h>
25 25
26 26 #include <unordered_map>
27 27
28 28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29 29
30 30 namespace {
31 31
32 32 /// Key pressed to enable drag&drop in all modes
33 33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 34
35 35 /// Key pressed to enable zoom on horizontal axis
36 36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 37
38 38 /// Key pressed to enable zoom on vertical axis
39 39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 40
41 41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 42 const auto PAN_SPEED = 5;
43 43
44 44 /// Key pressed to enable a calibration pan
45 45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 46
47 47 /// Key pressed to enable multi selection of selection zones
48 48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 49
50 50 /// Minimum size for the zoom box, in percentage of the axis range
51 51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 52
53 53 /// Format of the dates appearing in the label of a cursor
54 54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 55
56 56 } // namespace
57 57
58 58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59 59
60 60 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 61 : m_Name{name},
62 m_DoAcquisition{true},
62 m_Flags{GraphFlag::EnableAll},
63 63 m_IsCalibration{false},
64 64 m_RenderingDelegate{nullptr}
65 65 {
66 66 }
67 67
68 68 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
69 69 const SqpRange &range)
70 70 {
71 71 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
72 72
73 73 // Prevents that data has changed to update rendering
74 74 m_RenderingDelegate->onPlotUpdated();
75 75 }
76 76
77 77 QString m_Name;
78 78 // 1 variable -> n qcpplot
79 79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 bool m_DoAcquisition;
80 GraphFlags m_Flags;
81 81 bool m_IsCalibration;
82 82 /// Delegate used to attach rendering features to the plot
83 83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84 84
85 85 QCPItemRect *m_DrawingZoomRect = nullptr;
86 86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87 87
88 88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90 90
91 91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94 94
95 95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96 96
97 97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
98 98 {
99 99 removeDrawingRect(plot);
100 100
101 101 auto axisPos = posToAxisPos(pos, plot);
102 102
103 103 m_DrawingZoomRect = new QCPItemRect{&plot};
104 104 QPen p;
105 105 p.setWidth(2);
106 106 m_DrawingZoomRect->setPen(p);
107 107
108 108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
109 109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
110 110 }
111 111
112 112 void removeDrawingRect(QCustomPlot &plot)
113 113 {
114 114 if (m_DrawingZoomRect) {
115 115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
116 116 m_DrawingZoomRect = nullptr;
117 117 plot.replot(QCustomPlot::rpQueuedReplot);
118 118 }
119 119 }
120 120
121 121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
122 122 {
123 123 endDrawingZone(graph);
124 124
125 125 auto axisPos = posToAxisPos(pos, graph->plot());
126 126
127 127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
128 128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
129 129 m_DrawingZone->setEditionEnabled(false);
130 130 }
131 131
132 132 void endDrawingZone(VisualizationGraphWidget *graph)
133 133 {
134 134 if (m_DrawingZone) {
135 135 auto drawingZoneRange = m_DrawingZone->range();
136 136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
137 137 m_DrawingZone->setEditionEnabled(true);
138 138 addSelectionZone(m_DrawingZone);
139 139 }
140 140 else {
141 141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
142 142 }
143 143
144 144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
145 145 m_DrawingZone = nullptr;
146 146 }
147 147 }
148 148
149 149 void setSelectionZonesEditionEnabled(bool value)
150 150 {
151 151 for (auto s : m_SelectionZones) {
152 152 s->setEditionEnabled(value);
153 153 }
154 154 }
155 155
156 156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
157 157
158 158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
159 159 const QCustomPlot &plot) const
160 160 {
161 161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
162 162 auto minDistanceToZone = -1;
163 163 for (auto zone : m_SelectionZones) {
164 164 auto distanceToZone = zone->selectTest(pos, false);
165 165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
166 166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
167 167 selectionZoneItemUnderCursor = zone;
168 168 }
169 169 }
170 170
171 171 return selectionZoneItemUnderCursor;
172 172 }
173 173
174 174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
175 175 const QCustomPlot &plot) const
176 176 {
177 177 QVector<VisualizationSelectionZoneItem *> zones;
178 178 for (auto zone : m_SelectionZones) {
179 179 auto distanceToZone = zone->selectTest(pos, false);
180 180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
181 181 zones << zone;
182 182 }
183 183 }
184 184
185 185 return zones;
186 186 }
187 187
188 188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
189 189 {
190 190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
191 191 zone->moveToTop();
192 192 m_SelectionZones.removeAll(zone);
193 193 m_SelectionZones.append(zone);
194 194 }
195 195 }
196 196
197 197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
198 198 {
199 199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
200 200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
201 201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
202 202 }
203 203
204 204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
205 205 {
206 206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
207 207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
208 208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
209 209 }
210 210 };
211 211
212 212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
213 213 : VisualizationDragWidget{parent},
214 214 ui{new Ui::VisualizationGraphWidget},
215 215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
216 216 {
217 217 ui->setupUi(this);
218 218
219 219 // 'Close' options : widget is deleted when closed
220 220 setAttribute(Qt::WA_DeleteOnClose);
221 221
222 222 // Set qcpplot properties :
223 223 // - zoom is enabled
224 224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
225 225 ui->widget->setInteractions(QCP::iRangeZoom);
226 226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
227 227
228 228 // The delegate must be initialized after the ui as it uses the plot
229 229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
230 230
231 231 // Init the cursors
232 232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
233 233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
234 234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
236 236
237 237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
238 238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
239 239 &VisualizationGraphWidget::onMouseRelease);
240 240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
241 241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
242 242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
243 243 &VisualizationGraphWidget::onMouseDoubleClick);
244 244 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
245 245 &QCPAxis::rangeChanged),
246 246 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
247 247
248 248 // Activates menu when right clicking on the graph
249 249 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
250 250 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
251 251 &VisualizationGraphWidget::onGraphMenuRequested);
252 252
253 253 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
254 254 &VariableController::onRequestDataLoading);
255 255
256 256 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
257 257 &VisualizationGraphWidget::onUpdateVarDisplaying);
258 258
259 259 #ifdef Q_OS_MAC
260 260 plot().setPlottingHint(QCP::phFastPolylines, true);
261 261 #endif
262 262 }
263 263
264 264
265 265 VisualizationGraphWidget::~VisualizationGraphWidget()
266 266 {
267 267 delete ui;
268 268 }
269 269
270 270 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
271 271 {
272 272 auto parent = parentWidget();
273 273 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
274 274 parent = parent->parentWidget();
275 275 }
276 276
277 277 return qobject_cast<VisualizationZoneWidget *>(parent);
278 278 }
279 279
280 280 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
281 281 {
282 282 auto parent = parentWidget();
283 283 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
284 284 parent = parent->parentWidget();
285 285 }
286 286
287 287 return qobject_cast<VisualizationWidget *>(parent);
288 288 }
289 289
290 void VisualizationGraphWidget::enableAcquisition(bool enable)
290 void VisualizationGraphWidget::setFlags(GraphFlags flags)
291 291 {
292 impl->m_DoAcquisition = enable;
292 impl->m_Flags = std::move(flags);
293 293 }
294 294
295 295 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
296 296 {
297 297 // Uses delegate to create the qcpplot components according to the variable
298 298 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
299 299
300 300 if (auto dataSeries = variable->dataSeries()) {
301 301 // Set axes properties according to the units of the data series
302 302 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
303 303
304 304 // Sets rendering properties for the new plottables
305 305 // Warning: this method must be called after setAxesProperties(), as it can access to some
306 306 // axes properties that have to be initialized
307 307 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
308 308 }
309 309
310 310 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
311 311
312 312 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
313 313
314 this->enableAcquisition(false);
314 this->setFlags(GraphFlag::DisableAll);
315 315 this->setGraphRange(range);
316 this->enableAcquisition(true);
316 this->setFlags(GraphFlag::EnableAll);
317 317
318 318 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
319 319
320 320 emit variableAdded(variable);
321 321 }
322 322
323 323 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
324 324 {
325 325 // Each component associated to the variable :
326 326 // - is removed from qcpplot (which deletes it)
327 327 // - is no longer referenced in the map
328 328 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
329 329 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
330 330 emit variableAboutToBeRemoved(variable);
331 331
332 332 auto &plottablesMap = variableIt->second;
333 333
334 334 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
335 335 plottableIt != plottableEnd;) {
336 336 ui->widget->removePlottable(plottableIt->second);
337 337 plottableIt = plottablesMap.erase(plottableIt);
338 338 }
339 339
340 340 impl->m_VariableToPlotMultiMap.erase(variableIt);
341 341 }
342 342
343 343 // Updates graph
344 344 ui->widget->replot();
345 345 }
346 346
347 347 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
348 348 {
349 349 auto variables = QList<std::shared_ptr<Variable> >{};
350 350 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
351 351 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
352 352 variables << it->first;
353 353 }
354 354
355 355 return variables;
356 356 }
357 357
358 358 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
359 359 {
360 360 if (!variable) {
361 361 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
362 362 return;
363 363 }
364 364
365 365 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
366 366 }
367 367
368 368 SqpRange VisualizationGraphWidget::graphRange() const noexcept
369 369 {
370 370 auto graphRange = ui->widget->xAxis->range();
371 371 return SqpRange{graphRange.lower, graphRange.upper};
372 372 }
373 373
374 374 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
375 375 {
376 376 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
377 377 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
378 378 ui->widget->replot();
379 379 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
380 380 }
381 381
382 382 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
383 383 {
384 384 QVector<SqpRange> ranges;
385 385 for (auto zone : impl->m_SelectionZones) {
386 386 ranges << zone->range();
387 387 }
388 388
389 389 return ranges;
390 390 }
391 391
392 392 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
393 393 {
394 394 for (const auto &range : ranges) {
395 395 // note: ownership is transfered to QCustomPlot
396 396 auto zone = new VisualizationSelectionZoneItem(&plot());
397 397 zone->setRange(range.m_TStart, range.m_TEnd);
398 398 impl->addSelectionZone(zone);
399 399 }
400 400
401 401 plot().replot(QCustomPlot::rpQueuedReplot);
402 402 }
403 403
404 404 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
405 405 {
406 406 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
407 407
408 408 if (impl->m_HoveredZone == selectionZone) {
409 409 impl->m_HoveredZone = nullptr;
410 410 setCursor(Qt::ArrowCursor);
411 411 }
412 412
413 413 impl->m_SelectionZones.removeAll(selectionZone);
414 414 plot().removeItem(selectionZone);
415 415 plot().replot(QCustomPlot::rpQueuedReplot);
416 416 }
417 417
418 418 void VisualizationGraphWidget::undoZoom()
419 419 {
420 420 auto zoom = impl->m_ZoomStack.pop();
421 421 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
422 422 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
423 423
424 424 axisX->setRange(zoom.first);
425 425 axisY->setRange(zoom.second);
426 426
427 427 plot().replot(QCustomPlot::rpQueuedReplot);
428 428 }
429 429
430 430 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
431 431 {
432 432 if (visitor) {
433 433 visitor->visit(this);
434 434 }
435 435 else {
436 436 qCCritical(LOG_VisualizationGraphWidget())
437 437 << tr("Can't visit widget : the visitor is null");
438 438 }
439 439 }
440 440
441 441 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
442 442 {
443 443 auto isSpectrogram = [](const auto &variable) {
444 444 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
445 445 };
446 446
447 447 // - A spectrogram series can't be dropped on graph with existing plottables
448 448 // - No data series can be dropped on graph with existing spectrogram series
449 449 return isSpectrogram(variable)
450 450 ? impl->m_VariableToPlotMultiMap.empty()
451 451 : std::none_of(
452 452 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
453 453 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
454 454 }
455 455
456 456 bool VisualizationGraphWidget::contains(const Variable &variable) const
457 457 {
458 458 // Finds the variable among the keys of the map
459 459 auto variablePtr = &variable;
460 460 auto findVariable
461 461 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
462 462
463 463 auto end = impl->m_VariableToPlotMultiMap.cend();
464 464 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
465 465 return it != end;
466 466 }
467 467
468 468 QString VisualizationGraphWidget::name() const
469 469 {
470 470 return impl->m_Name;
471 471 }
472 472
473 473 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
474 474 {
475 475 auto mimeData = new QMimeData;
476 476
477 477 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
478 478 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
479 479 && selectionZoneItemUnderCursor) {
480 480 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
481 481 selectionZoneItemUnderCursor->range()));
482 482 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
483 483 selectionZoneItemUnderCursor->range()));
484 484 }
485 485 else {
486 486 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
487 487
488 488 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
489 489 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
490 490 }
491 491
492 492 return mimeData;
493 493 }
494 494
495 495 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
496 496 {
497 497 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
498 498 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
499 499 && selectionZoneItemUnderCursor) {
500 500
501 501 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
502 502 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
503 503
504 504 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
505 505 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
506 506 .toSize();
507 507
508 508 auto pixmap = QPixmap(zoneSize);
509 509 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
510 510
511 511 return pixmap;
512 512 }
513 513
514 514 return QPixmap();
515 515 }
516 516
517 517 bool VisualizationGraphWidget::isDragAllowed() const
518 518 {
519 519 return true;
520 520 }
521 521
522 522 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
523 523 {
524 524 if (highlighted) {
525 525 plot().setBackground(QBrush(QColor("#BBD5EE")));
526 526 }
527 527 else {
528 528 plot().setBackground(QBrush(Qt::white));
529 529 }
530 530
531 531 plot().update();
532 532 }
533 533
534 534 void VisualizationGraphWidget::addVerticalCursor(double time)
535 535 {
536 536 impl->m_VerticalCursor->setPosition(time);
537 537 impl->m_VerticalCursor->setVisible(true);
538 538
539 539 auto text
540 540 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
541 541 impl->m_VerticalCursor->setLabelText(text);
542 542 }
543 543
544 544 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
545 545 {
546 546 impl->m_VerticalCursor->setAbsolutePosition(position);
547 547 impl->m_VerticalCursor->setVisible(true);
548 548
549 549 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
550 550 auto text
551 551 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
552 552 impl->m_VerticalCursor->setLabelText(text);
553 553 }
554 554
555 555 void VisualizationGraphWidget::removeVerticalCursor()
556 556 {
557 557 impl->m_VerticalCursor->setVisible(false);
558 558 plot().replot(QCustomPlot::rpQueuedReplot);
559 559 }
560 560
561 561 void VisualizationGraphWidget::addHorizontalCursor(double value)
562 562 {
563 563 impl->m_HorizontalCursor->setPosition(value);
564 564 impl->m_HorizontalCursor->setVisible(true);
565 565 impl->m_HorizontalCursor->setLabelText(QString::number(value));
566 566 }
567 567
568 568 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
569 569 {
570 570 impl->m_HorizontalCursor->setAbsolutePosition(position);
571 571 impl->m_HorizontalCursor->setVisible(true);
572 572
573 573 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
574 574 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
575 575 }
576 576
577 577 void VisualizationGraphWidget::removeHorizontalCursor()
578 578 {
579 579 impl->m_HorizontalCursor->setVisible(false);
580 580 plot().replot(QCustomPlot::rpQueuedReplot);
581 581 }
582 582
583 583 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
584 584 {
585 585 Q_UNUSED(event);
586 586
587 587 // Prevents that all variables will be removed from graph when it will be closed
588 588 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
589 589 emit variableAboutToBeRemoved(variableEntry.first);
590 590 }
591 591 }
592 592
593 593 void VisualizationGraphWidget::enterEvent(QEvent *event)
594 594 {
595 595 Q_UNUSED(event);
596 596 impl->m_RenderingDelegate->showGraphOverlay(true);
597 597 }
598 598
599 599 void VisualizationGraphWidget::leaveEvent(QEvent *event)
600 600 {
601 601 Q_UNUSED(event);
602 602 impl->m_RenderingDelegate->showGraphOverlay(false);
603 603
604 604 if (auto parentZone = parentZoneWidget()) {
605 605 parentZone->notifyMouseLeaveGraph(this);
606 606 }
607 607 else {
608 608 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
609 609 }
610 610
611 611 if (impl->m_HoveredZone) {
612 612 impl->m_HoveredZone->setHovered(false);
613 613 impl->m_HoveredZone = nullptr;
614 614 }
615 615 }
616 616
617 617 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
618 618 {
619 619 return *ui->widget;
620 620 }
621 621
622 622 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
623 623 {
624 624 QMenu graphMenu{};
625 625
626 626 // Iterates on variables (unique keys)
627 627 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
628 628 end = impl->m_VariableToPlotMultiMap.cend();
629 629 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
630 630 // 'Remove variable' action
631 631 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
632 632 [ this, var = it->first ]() { removeVariable(var); });
633 633 }
634 634
635 635 if (!impl->m_ZoomStack.isEmpty()) {
636 636 if (!graphMenu.isEmpty()) {
637 637 graphMenu.addSeparator();
638 638 }
639 639
640 640 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
641 641 }
642 642
643 643 // Selection Zone Actions
644 644 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
645 645 if (selectionZoneItem) {
646 646 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
647 647 selectedItems.removeAll(selectionZoneItem);
648 648 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
649 649
650 650 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
651 651 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
652 652 graphMenu.addSeparator();
653 653 }
654 654
655 655 QHash<QString, QMenu *> subMenus;
656 656 QHash<QString, bool> subMenusEnabled;
657 657
658 658 for (auto zoneAction : zoneActions) {
659 659
660 660 auto isEnabled = zoneAction->isEnabled(selectedItems);
661 661
662 662 auto menu = &graphMenu;
663 663 for (auto subMenuName : zoneAction->subMenuList()) {
664 664 if (!subMenus.contains(subMenuName)) {
665 665 menu = menu->addMenu(subMenuName);
666 666 subMenus[subMenuName] = menu;
667 667 subMenusEnabled[subMenuName] = isEnabled;
668 668 }
669 669 else {
670 670 menu = subMenus.value(subMenuName);
671 671 if (isEnabled) {
672 672 // The sub menu is enabled if at least one of its actions is enabled
673 673 subMenusEnabled[subMenuName] = true;
674 674 }
675 675 }
676 676 }
677 677
678 678 auto action = menu->addAction(zoneAction->name());
679 679 action->setEnabled(isEnabled);
680 680 action->setShortcut(zoneAction->displayedShortcut());
681 681 QObject::connect(action, &QAction::triggered,
682 682 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
683 683 }
684 684
685 685 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
686 686 it.value()->setEnabled(subMenusEnabled[it.key()]);
687 687 }
688 688 }
689 689
690 690 if (!graphMenu.isEmpty()) {
691 691 graphMenu.exec(QCursor::pos());
692 692 }
693 693 }
694 694
695 695 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
696 696 {
697 697 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
698 698 << QThread::currentThread()->objectName() << "DoAcqui"
699 << impl->m_DoAcquisition;
699 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
700 700
701 701 auto graphRange = SqpRange{t1.lower, t1.upper};
702 702 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
703 703
704 if (impl->m_DoAcquisition) {
704 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
705 705 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
706 706
707 707 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
708 708 end = impl->m_VariableToPlotMultiMap.end();
709 709 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
710 710 variableUnderGraphVector.push_back(it->first);
711 711 }
712 712 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
713 713 !impl->m_IsCalibration);
714 714
715 715 if (!impl->m_IsCalibration) {
716 716 qCDebug(LOG_VisualizationGraphWidget())
717 717 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
718 718 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
719 719 emit synchronize(graphRange, oldGraphRange);
720 720 }
721 721 }
722 722
723 723 auto pos = mapFromGlobal(QCursor::pos());
724 724 auto axisPos = impl->posToAxisPos(pos, plot());
725 725 if (auto parentZone = parentZoneWidget()) {
726 726 if (impl->pointIsInAxisRect(axisPos, plot())) {
727 727 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
728 728 }
729 729 else {
730 730 parentZone->notifyMouseLeaveGraph(this);
731 731 }
732 732 }
733 733 else {
734 734 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
735 735 }
736 736 }
737 737
738 738 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
739 739 {
740 740 impl->m_RenderingDelegate->onMouseDoubleClick(event);
741 741 }
742 742
743 743 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
744 744 {
745 745 // Handles plot rendering when mouse is moving
746 746 impl->m_RenderingDelegate->onMouseMove(event);
747 747
748 748 auto axisPos = impl->posToAxisPos(event->pos(), plot());
749 749
750 750 // Zoom box and zone drawing
751 751 if (impl->m_DrawingZoomRect) {
752 752 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
753 753 }
754 754 else if (impl->m_DrawingZone) {
755 755 impl->m_DrawingZone->setEnd(axisPos.x());
756 756 }
757 757
758 758 // Cursor
759 759 if (auto parentZone = parentZoneWidget()) {
760 760 if (impl->pointIsInAxisRect(axisPos, plot())) {
761 761 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
762 762 }
763 763 else {
764 764 parentZone->notifyMouseLeaveGraph(this);
765 765 }
766 766 }
767 767 else {
768 768 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
769 769 }
770 770
771 771 // Search for the selection zone under the mouse
772 772 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
773 773 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
774 774 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
775 775
776 776 // Sets the appropriate cursor shape
777 777 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
778 778 setCursor(cursorShape);
779 779
780 780 // Manages the hovered zone
781 781 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
782 782 if (impl->m_HoveredZone) {
783 783 impl->m_HoveredZone->setHovered(false);
784 784 }
785 785 selectionZoneItemUnderCursor->setHovered(true);
786 786 impl->m_HoveredZone = selectionZoneItemUnderCursor;
787 787 plot().replot(QCustomPlot::rpQueuedReplot);
788 788 }
789 789 }
790 790 else {
791 791 // There is no zone under the mouse or the interaction mode is not "selection zones"
792 792 if (impl->m_HoveredZone) {
793 793 impl->m_HoveredZone->setHovered(false);
794 794 impl->m_HoveredZone = nullptr;
795 795 }
796 796
797 797 setCursor(Qt::ArrowCursor);
798 798 }
799 799
800 800 impl->m_HasMovedMouse = true;
801 801 VisualizationDragWidget::mouseMoveEvent(event);
802 802 }
803 803
804 804 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
805 805 {
806 806 auto value = event->angleDelta().x() + event->angleDelta().y();
807 807 if (value != 0) {
808 808
809 809 auto direction = value > 0 ? 1.0 : -1.0;
810 810 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
811 811 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
812 812 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
813 813
814 814 auto zoomOrientations = QFlags<Qt::Orientation>{};
815 815 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
816 816 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
817 817
818 818 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
819 819
820 820 if (!isZoomX && !isZoomY) {
821 821 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
822 822 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
823 823
824 824 axis->setRange(axis->range() + diff);
825 825
826 826 if (plot().noAntialiasingOnDrag()) {
827 827 plot().setNotAntialiasedElements(QCP::aeAll);
828 828 }
829 829
830 830 plot().replot(QCustomPlot::rpQueuedReplot);
831 831 }
832 832 }
833 833 }
834 834
835 835 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
836 836 {
837 837 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
838 838 auto isSelectionZoneMode
839 839 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
840 840 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
841 841
842 842 if (!isDragDropClick && isLeftClick) {
843 843 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
844 844 // Starts a zoom box
845 845 impl->startDrawingRect(event->pos(), plot());
846 846 }
847 847 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
848 848 // Starts a new selection zone
849 849 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
850 850 if (!zoneAtPos) {
851 851 impl->startDrawingZone(event->pos(), this);
852 852 }
853 853 }
854 854 }
855 855
856 856 // Allows mouse panning only in default mode
857 857 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
858 858 == SqpApplication::PlotsInteractionMode::None
859 859 && !isDragDropClick);
860 860
861 861 // Allows zone edition only in selection zone mode without drag&drop
862 862 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
863 863
864 864 // Selection / Deselection
865 865 if (isSelectionZoneMode) {
866 866 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
867 867 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
868 868
869 869
870 870 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
871 871 && !isMultiSelectionClick) {
872 872 parentVisualizationWidget()->selectionZoneManager().select(
873 873 {selectionZoneItemUnderCursor});
874 874 }
875 875 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
876 876 parentVisualizationWidget()->selectionZoneManager().clearSelection();
877 877 }
878 878 else {
879 879 // No selection change
880 880 }
881 881
882 882 if (selectionZoneItemUnderCursor && isLeftClick) {
883 883 selectionZoneItemUnderCursor->setAssociatedEditedZones(
884 884 parentVisualizationWidget()->selectionZoneManager().selectedItems());
885 885 }
886 886 }
887 887
888 888
889 889 impl->m_HasMovedMouse = false;
890 890 VisualizationDragWidget::mousePressEvent(event);
891 891 }
892 892
893 893 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
894 894 {
895 895 if (impl->m_DrawingZoomRect) {
896 896
897 897 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
898 898 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
899 899
900 900 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
901 901 impl->m_DrawingZoomRect->bottomRight->coords().x()};
902 902
903 903 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
904 904 impl->m_DrawingZoomRect->bottomRight->coords().y()};
905 905
906 906 impl->removeDrawingRect(plot());
907 907
908 908 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
909 909 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
910 910 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
911 911 axisX->setRange(newAxisXRange);
912 912 axisY->setRange(newAxisYRange);
913 913
914 914 plot().replot(QCustomPlot::rpQueuedReplot);
915 915 }
916 916 }
917 917
918 918 impl->endDrawingZone(this);
919 919
920 920 impl->m_IsCalibration = false;
921 921
922 922 // Selection / Deselection
923 923 auto isSelectionZoneMode
924 924 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
925 925 if (isSelectionZoneMode) {
926 926 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
927 927 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
928 928 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
929 929 && !impl->m_HasMovedMouse) {
930 930
931 931 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
932 932 if (zonesUnderCursor.count() > 1) {
933 933 // There are multiple zones under the mouse.
934 934 // Performs the selection with a selection dialog.
935 935 VisualizationMultiZoneSelectionDialog dialog{this};
936 936 dialog.setZones(zonesUnderCursor);
937 937 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
938 938 dialog.activateWindow();
939 939 dialog.raise();
940 940 if (dialog.exec() == QDialog::Accepted) {
941 941 auto selection = dialog.selectedZones();
942 942
943 943 if (!isMultiSelectionClick) {
944 944 parentVisualizationWidget()->selectionZoneManager().clearSelection();
945 945 }
946 946
947 947 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
948 948 auto zone = it.key();
949 949 auto isSelected = it.value();
950 950 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
951 951 isSelected);
952 952
953 953 if (isSelected) {
954 954 // Puts the zone on top of the stack so it can be moved or resized
955 955 impl->moveSelectionZoneOnTop(zone, plot());
956 956 }
957 957 }
958 958 }
959 959 }
960 960 else {
961 961 if (!isMultiSelectionClick) {
962 962 parentVisualizationWidget()->selectionZoneManager().select(
963 963 {selectionZoneItemUnderCursor});
964 964 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
965 965 }
966 966 else {
967 967 parentVisualizationWidget()->selectionZoneManager().setSelected(
968 968 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
969 969 || event->button() == Qt::RightButton);
970 970 }
971 971 }
972 972 }
973 973 else {
974 974 // No selection change
975 975 }
976 976 }
977 977 }
978 978
979 979 void VisualizationGraphWidget::onDataCacheVariableUpdated()
980 980 {
981 981 auto graphRange = ui->widget->xAxis->range();
982 982 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
983 983
984 984 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
985 985 auto variable = variableEntry.first;
986 986 qCDebug(LOG_VisualizationGraphWidget())
987 987 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
988 988 qCDebug(LOG_VisualizationGraphWidget())
989 989 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
990 990 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
991 991 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
992 992 }
993 993 }
994 994 }
995 995
996 996 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
997 997 const SqpRange &range)
998 998 {
999 999 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1000 1000 if (it != impl->m_VariableToPlotMultiMap.end()) {
1001 1001 impl->updateData(it->second, variable->dataSeries(), range);
1002 1002 }
1003 1003 }
@@ -1,601 +1,601
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/SqpRange.h>
13 13 #include <Time/TimeController.h>
14 14 #include <Variable/Variable.h>
15 15 #include <Variable/VariableController.h>
16 16
17 17 #include <Visualization/operations/FindVariableOperation.h>
18 18
19 19 #include <DragAndDrop/DragDropGuiController.h>
20 20 #include <QUuid>
21 21 #include <SqpApplication.h>
22 22 #include <cmath>
23 23
24 24 #include <QLayout>
25 25
26 26 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
27 27
28 28 namespace {
29 29
30 30 /**
31 31 * Applies a function to all graphs of the zone represented by its layout
32 32 * @param layout the layout that contains graphs
33 33 * @param fun the function to apply to each graph
34 34 */
35 35 template <typename Fun>
36 36 void processGraphs(QLayout &layout, Fun fun)
37 37 {
38 38 for (auto i = 0; i < layout.count(); ++i) {
39 39 if (auto item = layout.itemAt(i)) {
40 40 if (auto visualizationGraphWidget
41 41 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
42 42 fun(*visualizationGraphWidget);
43 43 }
44 44 }
45 45 }
46 46 }
47 47
48 48 /// Generates a default name for a new graph, according to the number of graphs already displayed in
49 49 /// the zone
50 50 QString defaultGraphName(QLayout &layout)
51 51 {
52 52 QSet<QString> existingNames;
53 53 processGraphs(
54 54 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
55 55
56 56 int zoneNum = 1;
57 57 QString name;
58 58 do {
59 59 name = QObject::tr("Graph ").append(QString::number(zoneNum));
60 60 ++zoneNum;
61 61 } while (existingNames.contains(name));
62 62
63 63 return name;
64 64 }
65 65
66 66 } // namespace
67 67
68 68 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
69 69
70 70 explicit VisualizationZoneWidgetPrivate()
71 71 : m_SynchronisationGroupId{QUuid::createUuid()},
72 72 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
73 73 {
74 74 }
75 75 QUuid m_SynchronisationGroupId;
76 76 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
77 77
78 78 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
79 79 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
80 80 VisualizationZoneWidget *zoneWidget);
81 81 };
82 82
83 83 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
84 84 : VisualizationDragWidget{parent},
85 85 ui{new Ui::VisualizationZoneWidget},
86 86 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
87 87 {
88 88 ui->setupUi(this);
89 89
90 90 ui->zoneNameLabel->setText(name);
91 91
92 92 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
93 93 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
94 94 VisualizationDragDropContainer::DropBehavior::Inserted);
95 95 ui->dragDropContainer->setMimeType(
96 96 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
97 97 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
98 98 VisualizationDragDropContainer::DropBehavior::Merged);
99 99 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
100 100 VisualizationDragDropContainer::DropBehavior::Forbidden);
101 101 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
102 102 VisualizationDragDropContainer::DropBehavior::Forbidden);
103 103 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
104 104 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
105 105 ui->dragDropContainer);
106 106 });
107 107
108 108 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
109 109 if (!mimeData) {
110 110 return false;
111 111 }
112 112
113 113 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
114 114 auto variables = sqpApp->variableController().variablesForMimeData(
115 115 mimeData->data(MIME_TYPE_VARIABLE_LIST));
116 116
117 117 if (variables.count() != 1) {
118 118 return false;
119 119 }
120 120 auto variable = variables.first();
121 121
122 122 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
123 123 return graphWidget->canDrop(*variable);
124 124 }
125 125 }
126 126
127 127 return true;
128 128 };
129 129 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
130 130
131 131 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
132 132 &VisualizationZoneWidget::dropMimeData);
133 133 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
134 134 &VisualizationZoneWidget::dropMimeDataOnGraph);
135 135
136 136 // 'Close' options : widget is deleted when closed
137 137 setAttribute(Qt::WA_DeleteOnClose);
138 138 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
139 139 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
140 140
141 141 // Synchronisation id
142 142 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
143 143 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
144 144 }
145 145
146 146 VisualizationZoneWidget::~VisualizationZoneWidget()
147 147 {
148 148 delete ui;
149 149 }
150 150
151 151 void VisualizationZoneWidget::setZoneRange(const SqpRange &range)
152 152 {
153 153 if (auto graph = firstGraph()) {
154 154 graph->setGraphRange(range);
155 155 }
156 156 else {
157 157 qCWarning(LOG_VisualizationZoneWidget())
158 158 << tr("setZoneRange:Cannot set the range of an empty zone.");
159 159 }
160 160 }
161 161
162 162 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
163 163 {
164 164 // Synchronize new graph with others in the zone
165 165 impl->m_Synchronizer->addGraph(*graphWidget);
166 166
167 167 ui->dragDropContainer->addDragWidget(graphWidget);
168 168 }
169 169
170 170 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
171 171 {
172 172 // Synchronize new graph with others in the zone
173 173 impl->m_Synchronizer->addGraph(*graphWidget);
174 174
175 175 ui->dragDropContainer->insertDragWidget(index, graphWidget);
176 176 }
177 177
178 178 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
179 179 {
180 180 return createGraph(variable, -1);
181 181 }
182 182
183 183 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
184 184 int index)
185 185 {
186 186 auto graphWidget
187 187 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
188 188
189 189
190 190 // Set graph properties
191 191 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
192 192 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
193 193
194 194
195 195 // Lambda to synchronize zone widget
196 196 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
197 197 const SqpRange &oldGraphRange) {
198 198
199 199 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
200 200 auto frameLayout = ui->dragDropContainer->layout();
201 201 for (auto i = 0; i < frameLayout->count(); ++i) {
202 202 auto graphChild
203 203 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
204 204 if (graphChild && (graphChild != graphWidget)) {
205 205
206 206 auto graphChildRange = graphChild->graphRange();
207 207 switch (zoomType) {
208 208 case AcquisitionZoomType::ZoomIn: {
209 209 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
210 210 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
211 211 graphChildRange.m_TStart += deltaLeft;
212 212 graphChildRange.m_TEnd -= deltaRight;
213 213 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
214 214 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
215 215 << deltaLeft;
216 216 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
217 217 << deltaRight;
218 218 qCDebug(LOG_VisualizationZoneWidget())
219 219 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
220 220
221 221 break;
222 222 }
223 223
224 224 case AcquisitionZoomType::ZoomOut: {
225 225 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
226 226 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
227 227 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
228 228 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
229 229 << deltaLeft;
230 230 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
231 231 << deltaRight;
232 232 qCDebug(LOG_VisualizationZoneWidget())
233 233 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
234 234 graphChildRange.m_TStart -= deltaLeft;
235 235 graphChildRange.m_TEnd += deltaRight;
236 236 break;
237 237 }
238 238 case AcquisitionZoomType::PanRight: {
239 239 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
240 240 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
241 241 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
242 242 graphChildRange.m_TStart += deltaLeft;
243 243 graphChildRange.m_TEnd += deltaRight;
244 244 qCDebug(LOG_VisualizationZoneWidget())
245 245 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
246 246 break;
247 247 }
248 248 case AcquisitionZoomType::PanLeft: {
249 249 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
250 250 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
251 251 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
252 252 graphChildRange.m_TStart -= deltaLeft;
253 253 graphChildRange.m_TEnd -= deltaRight;
254 254 break;
255 255 }
256 256 case AcquisitionZoomType::Unknown: {
257 257 qCDebug(LOG_VisualizationZoneWidget())
258 258 << tr("Impossible to synchronize: zoom type unknown");
259 259 break;
260 260 }
261 261 default:
262 262 qCCritical(LOG_VisualizationZoneWidget())
263 263 << tr("Impossible to synchronize: zoom type not take into account");
264 264 // No action
265 265 break;
266 266 }
267 graphChild->enableAcquisition(false);
268 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
269 << graphChild->graphRange();
270 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
271 << graphChildRange;
267 graphChild->setFlags(GraphFlag::DisableAll);
268 qCDebug(LOG_VisualizationZoneWidget())
269 << tr("TORM: Range before: ") << graphChild->graphRange();
270 qCDebug(LOG_VisualizationZoneWidget())
271 << tr("TORM: Range after : ") << graphChildRange;
272 272 qCDebug(LOG_VisualizationZoneWidget())
273 273 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
274 274 graphChild->setGraphRange(graphChildRange);
275 graphChild->enableAcquisition(true);
275 graphChild->setFlags(GraphFlag::EnableAll);
276 276 }
277 277 }
278 278 };
279 279
280 280 // connection for synchronization
281 281 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
282 282 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
283 283 &VisualizationZoneWidget::onVariableAdded);
284 284 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
285 285 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
286 286
287 287 auto range = SqpRange{};
288 288 if (auto firstGraph = this->firstGraph()) {
289 289 // Case of a new graph in a existant zone
290 290 range = firstGraph->graphRange();
291 291 }
292 292 else {
293 293 // Case of a new graph as the first of the zone
294 294 range = variable->range();
295 295 }
296 296
297 297 this->insertGraph(index, graphWidget);
298 298
299 299 graphWidget->addVariable(variable, range);
300 300 graphWidget->setYRange(variable);
301 301
302 302 return graphWidget;
303 303 }
304 304
305 305 VisualizationGraphWidget *
306 306 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
307 307 {
308 308 if (variables.isEmpty()) {
309 309 return nullptr;
310 310 }
311 311
312 312 auto graphWidget = createGraph(variables.first(), index);
313 313 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
314 314 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
315 315 }
316 316
317 317 return graphWidget;
318 318 }
319 319
320 320 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
321 321 {
322 322 VisualizationGraphWidget *firstGraph = nullptr;
323 323 auto layout = ui->dragDropContainer->layout();
324 324 if (layout->count() > 0) {
325 325 if (auto visualizationGraphWidget
326 326 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
327 327 firstGraph = visualizationGraphWidget;
328 328 }
329 329 }
330 330
331 331 return firstGraph;
332 332 }
333 333
334 334 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
335 335 {
336 336 if (visitor) {
337 337 visitor->visitEnter(this);
338 338
339 339 // Apply visitor to graph children: widgets different from graphs are not visited (no
340 340 // action)
341 341 processGraphs(
342 342 *ui->dragDropContainer->layout(),
343 343 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
344 344
345 345 visitor->visitLeave(this);
346 346 }
347 347 else {
348 348 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
349 349 }
350 350 }
351 351
352 352 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
353 353 {
354 354 // A tab can always accomodate a variable
355 355 Q_UNUSED(variable);
356 356 return true;
357 357 }
358 358
359 359 bool VisualizationZoneWidget::contains(const Variable &variable) const
360 360 {
361 361 Q_UNUSED(variable);
362 362 return false;
363 363 }
364 364
365 365 QString VisualizationZoneWidget::name() const
366 366 {
367 367 return ui->zoneNameLabel->text();
368 368 }
369 369
370 370 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
371 371 {
372 372 Q_UNUSED(position);
373 373
374 374 auto mimeData = new QMimeData;
375 375 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
376 376
377 377 if (auto firstGraph = this->firstGraph()) {
378 378 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
379 379 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
380 380 }
381 381
382 382 return mimeData;
383 383 }
384 384
385 385 bool VisualizationZoneWidget::isDragAllowed() const
386 386 {
387 387 return true;
388 388 }
389 389
390 390 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
391 391 const QPointF &plotPosition,
392 392 VisualizationGraphWidget *graphWidget)
393 393 {
394 394 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
395 395 VisualizationGraphWidget &processedGraph) {
396 396
397 397 switch (sqpApp->plotsCursorMode()) {
398 398 case SqpApplication::PlotsCursorMode::Vertical:
399 399 processedGraph.removeHorizontalCursor();
400 400 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
401 401 break;
402 402 case SqpApplication::PlotsCursorMode::Temporal:
403 403 processedGraph.addVerticalCursor(plotPosition.x());
404 404 processedGraph.removeHorizontalCursor();
405 405 break;
406 406 case SqpApplication::PlotsCursorMode::Horizontal:
407 407 processedGraph.removeVerticalCursor();
408 408 if (&processedGraph == graphWidget) {
409 409 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
410 410 }
411 411 else {
412 412 processedGraph.removeHorizontalCursor();
413 413 }
414 414 break;
415 415 case SqpApplication::PlotsCursorMode::Cross:
416 416 if (&processedGraph == graphWidget) {
417 417 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
418 418 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
419 419 }
420 420 else {
421 421 processedGraph.removeHorizontalCursor();
422 422 processedGraph.removeVerticalCursor();
423 423 }
424 424 break;
425 425 case SqpApplication::PlotsCursorMode::NoCursor:
426 426 processedGraph.removeHorizontalCursor();
427 427 processedGraph.removeVerticalCursor();
428 428 break;
429 429 }
430 430
431 431
432 432 });
433 433 }
434 434
435 435 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
436 436 {
437 437 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
438 438 processedGraph.removeHorizontalCursor();
439 439 processedGraph.removeVerticalCursor();
440 440 });
441 441 }
442 442
443 443 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
444 444 {
445 445 // Closes graphs in the zone
446 446 processGraphs(*ui->dragDropContainer->layout(),
447 447 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
448 448
449 449 // Delete synchronization group from variable controller
450 450 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
451 451 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
452 452
453 453 QWidget::closeEvent(event);
454 454 }
455 455
456 456 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
457 457 {
458 458 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
459 459 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
460 460 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
461 461 }
462 462
463 463 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
464 464 {
465 465 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
466 466 Q_ARG(std::shared_ptr<Variable>, variable),
467 467 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
468 468 }
469 469
470 470 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
471 471 {
472 472 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
473 473 impl->dropGraph(index, this);
474 474 }
475 475 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
476 476 auto variables = sqpApp->variableController().variablesForMimeData(
477 477 mimeData->data(MIME_TYPE_VARIABLE_LIST));
478 478 impl->dropVariables(variables, index, this);
479 479 }
480 480 else {
481 481 qCWarning(LOG_VisualizationZoneWidget())
482 482 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
483 483 }
484 484 }
485 485
486 486 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
487 487 const QMimeData *mimeData)
488 488 {
489 489 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
490 490 if (!graphWidget) {
491 491 qCWarning(LOG_VisualizationZoneWidget())
492 492 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
493 493 "drop aborted");
494 494 Q_ASSERT(false);
495 495 return;
496 496 }
497 497
498 498 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
499 499 auto variables = sqpApp->variableController().variablesForMimeData(
500 500 mimeData->data(MIME_TYPE_VARIABLE_LIST));
501 501 for (const auto &var : variables) {
502 502 graphWidget->addVariable(var, graphWidget->graphRange());
503 503 }
504 504 }
505 505 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
506 506 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
507 507 graphWidget->setGraphRange(range);
508 508 }
509 509 else {
510 510 qCWarning(LOG_VisualizationZoneWidget())
511 511 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
512 512 }
513 513 }
514 514
515 515 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
516 516 int index, VisualizationZoneWidget *zoneWidget)
517 517 {
518 518 auto &helper = sqpApp->dragDropGuiController();
519 519
520 520 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
521 521 if (!graphWidget) {
522 522 qCWarning(LOG_VisualizationZoneWidget())
523 523 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
524 524 "found or invalid.");
525 525 Q_ASSERT(false);
526 526 return;
527 527 }
528 528
529 529 auto parentDragDropContainer
530 530 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
531 531 if (!parentDragDropContainer) {
532 532 qCWarning(LOG_VisualizationZoneWidget())
533 533 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
534 534 "the dropped graph is not found.");
535 535 Q_ASSERT(false);
536 536 return;
537 537 }
538 538
539 539 const auto &variables = graphWidget->variables();
540 540
541 541 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
542 542 // The drop didn't occur in the same zone
543 543
544 544 // Abort the requests for the variables (if any)
545 545 // Commented, because it's not sure if it's needed or not
546 546 // for (const auto& var : variables)
547 547 //{
548 548 // sqpApp->variableController().onAbortProgressRequested(var);
549 549 //}
550 550
551 551 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
552 552 auto nbGraph = parentDragDropContainer->countDragWidget();
553 553 if (nbGraph == 1) {
554 554 // This is the only graph in the previous zone, close the zone
555 555 helper.delayedCloseWidget(previousParentZoneWidget);
556 556 }
557 557 else {
558 558 // Close the graph
559 559 helper.delayedCloseWidget(graphWidget);
560 560 }
561 561
562 562 // Creates the new graph in the zone
563 563 auto newGraphWidget = zoneWidget->createGraph(variables, index);
564 564 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
565 565 }
566 566 else {
567 567 // The drop occurred in the same zone or the graph is empty
568 568 // Simple move of the graph, no variable operation associated
569 569 parentDragDropContainer->layout()->removeWidget(graphWidget);
570 570
571 571 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
572 572 // The graph is empty and dropped in a different zone.
573 573 // Take the range of the first graph in the zone (if existing).
574 574 auto layout = zoneWidget->ui->dragDropContainer->layout();
575 575 if (layout->count() > 0) {
576 576 if (auto visualizationGraphWidget
577 577 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
578 578 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
579 579 }
580 580 }
581 581 }
582 582
583 583 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
584 584 }
585 585 }
586 586
587 587 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
588 588 const QList<std::shared_ptr<Variable> > &variables, int index,
589 589 VisualizationZoneWidget *zoneWidget)
590 590 {
591 591 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
592 592 // compatible variable here
593 593 if (variables.count() > 1) {
594 594 qCWarning(LOG_VisualizationZoneWidget())
595 595 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
596 596 "aborted.");
597 597 return;
598 598 }
599 599
600 600 zoneWidget->createGraph(variables, index);
601 601 }
@@ -1,71 +1,71
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 SqpRange &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 SqpRange m_Range;
14 14 };
15 15
16 16 RescaleAxeOperation::RescaleAxeOperation(std::shared_ptr<Variable> variable, const SqpRange &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 graphWidget->enableAcquisition(false);
62 graphWidget->setFlags(GraphFlag::DisableAll);
63 63 graphWidget->setGraphRange(impl->m_Range);
64 graphWidget->enableAcquisition(true);
64 graphWidget->setFlags(GraphFlag::EnableAll);
65 65 }
66 66 }
67 67 else {
68 68 qCCritical(LOG_RescaleAxeOperation(),
69 69 "Can't visit VisualizationGraphWidget : the widget is null");
70 70 }
71 71 }
General Comments 0
You need to be logged in to leave comments. Login now