##// END OF EJS Templates
Adds selection zone actions in the context menu
trabillard -
r1112:77b0e391c46a
parent child
Show More
@@ -1,876 +1,893
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationSelectionZoneItem.h"
7 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneManager.h"
8 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationWidget.h"
9 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationZoneWidget.h"
10 #include "Visualization/VisualizationZoneWidget.h"
11 #include "ui_VisualizationGraphWidget.h"
11 #include "ui_VisualizationGraphWidget.h"
12
12
13 #include <Actions/ActionsGuiController.h>
13 #include <Common/MimeTypesDef.h>
14 #include <Common/MimeTypesDef.h>
14 #include <Data/ArrayData.h>
15 #include <Data/ArrayData.h>
15 #include <Data/IDataSeries.h>
16 #include <Data/IDataSeries.h>
16 #include <Data/SpectrogramSeries.h>
17 #include <Data/SpectrogramSeries.h>
17 #include <DragAndDrop/DragDropGuiController.h>
18 #include <DragAndDrop/DragDropGuiController.h>
18 #include <Settings/SqpSettingsDefs.h>
19 #include <Settings/SqpSettingsDefs.h>
19 #include <SqpApplication.h>
20 #include <SqpApplication.h>
20 #include <Time/TimeController.h>
21 #include <Time/TimeController.h>
21 #include <Variable/Variable.h>
22 #include <Variable/Variable.h>
22 #include <Variable/VariableController.h>
23 #include <Variable/VariableController.h>
23
24
24 #include <unordered_map>
25 #include <unordered_map>
25
26
26 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
27 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
27
28
28 namespace {
29 namespace {
29
30
30 /// Key pressed to enable drag&drop in all modes
31 /// Key pressed to enable drag&drop in all modes
31 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
32 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
32
33
33 /// Key pressed to enable zoom on horizontal axis
34 /// Key pressed to enable zoom on horizontal axis
34 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
35 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
35
36
36 /// Key pressed to enable zoom on vertical axis
37 /// Key pressed to enable zoom on vertical axis
37 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
38 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
38
39
39 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
40 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
40 const auto PAN_SPEED = 5;
41 const auto PAN_SPEED = 5;
41
42
42 /// Key pressed to enable a calibration pan
43 /// Key pressed to enable a calibration pan
43 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
44 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
44
45
45 /// Key pressed to enable multi selection of selection zones
46 /// Key pressed to enable multi selection of selection zones
46 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
47 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
47
48
48 /// Minimum size for the zoom box, in percentage of the axis range
49 /// Minimum size for the zoom box, in percentage of the axis range
49 const auto ZOOM_BOX_MIN_SIZE = 0.8;
50 const auto ZOOM_BOX_MIN_SIZE = 0.8;
50
51
51 /// Format of the dates appearing in the label of a cursor
52 /// Format of the dates appearing in the label of a cursor
52 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
53 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
53
54
54 } // namespace
55 } // namespace
55
56
56 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
57 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
57
58
58 explicit VisualizationGraphWidgetPrivate(const QString &name)
59 explicit VisualizationGraphWidgetPrivate(const QString &name)
59 : m_Name{name},
60 : m_Name{name},
60 m_DoAcquisition{true},
61 m_DoAcquisition{true},
61 m_IsCalibration{false},
62 m_IsCalibration{false},
62 m_RenderingDelegate{nullptr}
63 m_RenderingDelegate{nullptr}
63 {
64 {
64 }
65 }
65
66
66 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
67 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
67 const SqpRange &range)
68 const SqpRange &range)
68 {
69 {
69 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
70 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
70
71
71 // Prevents that data has changed to update rendering
72 // Prevents that data has changed to update rendering
72 m_RenderingDelegate->onPlotUpdated();
73 m_RenderingDelegate->onPlotUpdated();
73 }
74 }
74
75
75 QString m_Name;
76 QString m_Name;
76 // 1 variable -> n qcpplot
77 // 1 variable -> n qcpplot
77 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
78 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
78 bool m_DoAcquisition;
79 bool m_DoAcquisition;
79 bool m_IsCalibration;
80 bool m_IsCalibration;
80 /// Delegate used to attach rendering features to the plot
81 /// Delegate used to attach rendering features to the plot
81 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
82 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
82
83
83 QCPItemRect *m_DrawingZoomRect = nullptr;
84 QCPItemRect *m_DrawingZoomRect = nullptr;
84 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
85 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
85
86
86 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
87 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
87 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
88 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
88
89
89 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
90 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
90 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
91 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
91 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
92 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
92
93
93 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
94 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
94
95
95 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
96 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
96 {
97 {
97 removeDrawingRect(plot);
98 removeDrawingRect(plot);
98
99
99 auto axisPos = posToAxisPos(pos, plot);
100 auto axisPos = posToAxisPos(pos, plot);
100
101
101 m_DrawingZoomRect = new QCPItemRect{&plot};
102 m_DrawingZoomRect = new QCPItemRect{&plot};
102 QPen p;
103 QPen p;
103 p.setWidth(2);
104 p.setWidth(2);
104 m_DrawingZoomRect->setPen(p);
105 m_DrawingZoomRect->setPen(p);
105
106
106 m_DrawingZoomRect->topLeft->setCoords(axisPos);
107 m_DrawingZoomRect->topLeft->setCoords(axisPos);
107 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
108 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
108 }
109 }
109
110
110 void removeDrawingRect(QCustomPlot &plot)
111 void removeDrawingRect(QCustomPlot &plot)
111 {
112 {
112 if (m_DrawingZoomRect) {
113 if (m_DrawingZoomRect) {
113 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
114 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
114 m_DrawingZoomRect = nullptr;
115 m_DrawingZoomRect = nullptr;
115 plot.replot(QCustomPlot::rpQueuedReplot);
116 plot.replot(QCustomPlot::rpQueuedReplot);
116 }
117 }
117 }
118 }
118
119
119 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
120 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
120 {
121 {
121 endDrawingZone(graph);
122 endDrawingZone(graph);
122
123
123 auto axisPos = posToAxisPos(pos, graph->plot());
124 auto axisPos = posToAxisPos(pos, graph->plot());
124
125
125 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
126 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
126 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
127 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
127 m_DrawingZone->setEditionEnabled(false);
128 m_DrawingZone->setEditionEnabled(false);
128 }
129 }
129
130
130 void endDrawingZone(VisualizationGraphWidget *graph)
131 void endDrawingZone(VisualizationGraphWidget *graph)
131 {
132 {
132 if (m_DrawingZone) {
133 if (m_DrawingZone) {
133 auto drawingZoneRange = m_DrawingZone->range();
134 auto drawingZoneRange = m_DrawingZone->range();
134 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
135 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
135 m_DrawingZone->setEditionEnabled(true);
136 m_DrawingZone->setEditionEnabled(true);
136 addSelectionZone(m_DrawingZone);
137 addSelectionZone(m_DrawingZone);
137 }
138 }
138 else {
139 else {
139 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
140 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
140 }
141 }
141
142
142 graph->plot().replot(QCustomPlot::rpQueuedReplot);
143 graph->plot().replot(QCustomPlot::rpQueuedReplot);
143 m_DrawingZone = nullptr;
144 m_DrawingZone = nullptr;
144 }
145 }
145 }
146 }
146
147
147 void setSelectionZonesEditionEnabled(bool value)
148 void setSelectionZonesEditionEnabled(bool value)
148 {
149 {
149 for (auto s : m_SelectionZones) {
150 for (auto s : m_SelectionZones) {
150 s->setEditionEnabled(value);
151 s->setEditionEnabled(value);
151 }
152 }
152 }
153 }
153
154
154 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
155 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
155
156
156 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
157 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
157 const QCustomPlot &plot) const
158 const QCustomPlot &plot) const
158 {
159 {
159 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
160 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
160 auto minDistanceToZone = -1;
161 auto minDistanceToZone = -1;
161 for (auto zone : m_SelectionZones) {
162 for (auto zone : m_SelectionZones) {
162 auto distanceToZone = zone->selectTest(pos, false);
163 auto distanceToZone = zone->selectTest(pos, false);
163 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
164 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
164 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
165 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
165 selectionZoneItemUnderCursor = zone;
166 selectionZoneItemUnderCursor = zone;
166 }
167 }
167 }
168 }
168
169
169 return selectionZoneItemUnderCursor;
170 return selectionZoneItemUnderCursor;
170 }
171 }
171
172
172 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
173 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
173 {
174 {
174 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
175 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
175 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
176 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
176 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
177 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
177 }
178 }
178
179
179 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
180 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
180 {
181 {
181 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
182 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
182 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
183 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
183 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
184 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
184 }
185 }
185 };
186 };
186
187
187 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
188 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
188 : VisualizationDragWidget{parent},
189 : VisualizationDragWidget{parent},
189 ui{new Ui::VisualizationGraphWidget},
190 ui{new Ui::VisualizationGraphWidget},
190 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
191 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
191 {
192 {
192 ui->setupUi(this);
193 ui->setupUi(this);
193
194
194 // 'Close' options : widget is deleted when closed
195 // 'Close' options : widget is deleted when closed
195 setAttribute(Qt::WA_DeleteOnClose);
196 setAttribute(Qt::WA_DeleteOnClose);
196
197
197 // Set qcpplot properties :
198 // Set qcpplot properties :
198 // - zoom is enabled
199 // - zoom is enabled
199 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
200 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
200 ui->widget->setInteractions(QCP::iRangeZoom);
201 ui->widget->setInteractions(QCP::iRangeZoom);
201 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
202 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
202
203
203 // The delegate must be initialized after the ui as it uses the plot
204 // The delegate must be initialized after the ui as it uses the plot
204 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
205 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
205
206
206 // Init the cursors
207 // Init the cursors
207 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
208 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
208 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
209 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
209 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
210 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
210 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
211 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
211
212
212 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
213 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
213 connect(ui->widget, &QCustomPlot::mouseRelease, this,
214 connect(ui->widget, &QCustomPlot::mouseRelease, this,
214 &VisualizationGraphWidget::onMouseRelease);
215 &VisualizationGraphWidget::onMouseRelease);
215 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
216 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
216 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
217 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
217 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
218 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
218 &VisualizationGraphWidget::onMouseDoubleClick);
219 &VisualizationGraphWidget::onMouseDoubleClick);
219 connect(
220 connect(
220 ui->widget->xAxis,
221 ui->widget->xAxis,
221 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
222 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
222 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
223 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
223
224
224 // Activates menu when right clicking on the graph
225 // Activates menu when right clicking on the graph
225 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
226 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
226 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
227 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
227 &VisualizationGraphWidget::onGraphMenuRequested);
228 &VisualizationGraphWidget::onGraphMenuRequested);
228
229
229 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
230 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
230 &VariableController::onRequestDataLoading);
231 &VariableController::onRequestDataLoading);
231
232
232 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
233 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
233 &VisualizationGraphWidget::onUpdateVarDisplaying);
234 &VisualizationGraphWidget::onUpdateVarDisplaying);
234
235
235 #ifdef Q_OS_MAC
236 #ifdef Q_OS_MAC
236 plot().setPlottingHint(QCP::phFastPolylines, true);
237 plot().setPlottingHint(QCP::phFastPolylines, true);
237 #endif
238 #endif
238 }
239 }
239
240
240
241
241 VisualizationGraphWidget::~VisualizationGraphWidget()
242 VisualizationGraphWidget::~VisualizationGraphWidget()
242 {
243 {
243 delete ui;
244 delete ui;
244 }
245 }
245
246
246 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
247 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
247 {
248 {
248 auto parent = parentWidget();
249 auto parent = parentWidget();
249 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
250 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
250 parent = parent->parentWidget();
251 parent = parent->parentWidget();
251 }
252 }
252
253
253 return qobject_cast<VisualizationZoneWidget *>(parent);
254 return qobject_cast<VisualizationZoneWidget *>(parent);
254 }
255 }
255
256
256 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
257 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
257 {
258 {
258 auto parent = parentWidget();
259 auto parent = parentWidget();
259 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
260 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
260 parent = parent->parentWidget();
261 parent = parent->parentWidget();
261 }
262 }
262
263
263 return qobject_cast<VisualizationWidget *>(parent);
264 return qobject_cast<VisualizationWidget *>(parent);
264 }
265 }
265
266
266 void VisualizationGraphWidget::enableAcquisition(bool enable)
267 void VisualizationGraphWidget::enableAcquisition(bool enable)
267 {
268 {
268 impl->m_DoAcquisition = enable;
269 impl->m_DoAcquisition = enable;
269 }
270 }
270
271
271 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
272 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
272 {
273 {
273 // Uses delegate to create the qcpplot components according to the variable
274 // Uses delegate to create the qcpplot components according to the variable
274 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
275 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
275
276
276 if (auto dataSeries = variable->dataSeries()) {
277 if (auto dataSeries = variable->dataSeries()) {
277 // Set axes properties according to the units of the data series
278 // Set axes properties according to the units of the data series
278 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
279 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
279
280
280 // Sets rendering properties for the new plottables
281 // Sets rendering properties for the new plottables
281 // Warning: this method must be called after setAxesProperties(), as it can access to some
282 // Warning: this method must be called after setAxesProperties(), as it can access to some
282 // axes properties that have to be initialized
283 // axes properties that have to be initialized
283 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
284 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
284 }
285 }
285
286
286 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
287 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
287
288
288 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
289 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
289
290
290 this->enableAcquisition(false);
291 this->enableAcquisition(false);
291 this->setGraphRange(range);
292 this->setGraphRange(range);
292 this->enableAcquisition(true);
293 this->enableAcquisition(true);
293
294
294 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
295 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
295
296
296 emit variableAdded(variable);
297 emit variableAdded(variable);
297 }
298 }
298
299
299 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
300 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
300 {
301 {
301 // Each component associated to the variable :
302 // Each component associated to the variable :
302 // - is removed from qcpplot (which deletes it)
303 // - is removed from qcpplot (which deletes it)
303 // - is no longer referenced in the map
304 // - is no longer referenced in the map
304 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
305 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
305 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
306 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
306 emit variableAboutToBeRemoved(variable);
307 emit variableAboutToBeRemoved(variable);
307
308
308 auto &plottablesMap = variableIt->second;
309 auto &plottablesMap = variableIt->second;
309
310
310 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
311 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
311 plottableIt != plottableEnd;) {
312 plottableIt != plottableEnd;) {
312 ui->widget->removePlottable(plottableIt->second);
313 ui->widget->removePlottable(plottableIt->second);
313 plottableIt = plottablesMap.erase(plottableIt);
314 plottableIt = plottablesMap.erase(plottableIt);
314 }
315 }
315
316
316 impl->m_VariableToPlotMultiMap.erase(variableIt);
317 impl->m_VariableToPlotMultiMap.erase(variableIt);
317 }
318 }
318
319
319 // Updates graph
320 // Updates graph
320 ui->widget->replot();
321 ui->widget->replot();
321 }
322 }
322
323
323 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
324 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
324 {
325 {
325 auto variables = QList<std::shared_ptr<Variable> >{};
326 auto variables = QList<std::shared_ptr<Variable> >{};
326 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
327 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
327 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
328 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
328 variables << it->first;
329 variables << it->first;
329 }
330 }
330
331
331 return variables;
332 return variables;
332 }
333 }
333
334
334 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
335 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
335 {
336 {
336 if (!variable) {
337 if (!variable) {
337 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
338 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
338 return;
339 return;
339 }
340 }
340
341
341 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
342 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
342 }
343 }
343
344
344 SqpRange VisualizationGraphWidget::graphRange() const noexcept
345 SqpRange VisualizationGraphWidget::graphRange() const noexcept
345 {
346 {
346 auto graphRange = ui->widget->xAxis->range();
347 auto graphRange = ui->widget->xAxis->range();
347 return SqpRange{graphRange.lower, graphRange.upper};
348 return SqpRange{graphRange.lower, graphRange.upper};
348 }
349 }
349
350
350 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
351 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
351 {
352 {
352 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
353 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
353 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
354 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
354 ui->widget->replot();
355 ui->widget->replot();
355 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
356 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
356 }
357 }
357
358
358 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
359 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
359 {
360 {
360 QVector<SqpRange> ranges;
361 QVector<SqpRange> ranges;
361 for (auto zone : impl->m_SelectionZones) {
362 for (auto zone : impl->m_SelectionZones) {
362 ranges << zone->range();
363 ranges << zone->range();
363 }
364 }
364
365
365 return ranges;
366 return ranges;
366 }
367 }
367
368
368 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
369 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
369 {
370 {
370 for (const auto &range : ranges) {
371 for (const auto &range : ranges) {
371 // note: ownership is transfered to QCustomPlot
372 // note: ownership is transfered to QCustomPlot
372 auto zone = new VisualizationSelectionZoneItem(&plot());
373 auto zone = new VisualizationSelectionZoneItem(&plot());
373 zone->setRange(range.m_TStart, range.m_TEnd);
374 zone->setRange(range.m_TStart, range.m_TEnd);
374 impl->addSelectionZone(zone);
375 impl->addSelectionZone(zone);
375 }
376 }
376
377
377 plot().replot(QCustomPlot::rpQueuedReplot);
378 plot().replot(QCustomPlot::rpQueuedReplot);
378 }
379 }
379
380
380 void VisualizationGraphWidget::undoZoom()
381 void VisualizationGraphWidget::undoZoom()
381 {
382 {
382 auto zoom = impl->m_ZoomStack.pop();
383 auto zoom = impl->m_ZoomStack.pop();
383 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
384 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
384 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
385 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
385
386
386 axisX->setRange(zoom.first);
387 axisX->setRange(zoom.first);
387 axisY->setRange(zoom.second);
388 axisY->setRange(zoom.second);
388
389
389 plot().replot(QCustomPlot::rpQueuedReplot);
390 plot().replot(QCustomPlot::rpQueuedReplot);
390 }
391 }
391
392
392 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
393 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
393 {
394 {
394 if (visitor) {
395 if (visitor) {
395 visitor->visit(this);
396 visitor->visit(this);
396 }
397 }
397 else {
398 else {
398 qCCritical(LOG_VisualizationGraphWidget())
399 qCCritical(LOG_VisualizationGraphWidget())
399 << tr("Can't visit widget : the visitor is null");
400 << tr("Can't visit widget : the visitor is null");
400 }
401 }
401 }
402 }
402
403
403 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
404 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
404 {
405 {
405 auto isSpectrogram = [](const auto &variable) {
406 auto isSpectrogram = [](const auto &variable) {
406 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
407 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
407 };
408 };
408
409
409 // - A spectrogram series can't be dropped on graph with existing plottables
410 // - A spectrogram series can't be dropped on graph with existing plottables
410 // - No data series can be dropped on graph with existing spectrogram series
411 // - No data series can be dropped on graph with existing spectrogram series
411 return isSpectrogram(variable)
412 return isSpectrogram(variable)
412 ? impl->m_VariableToPlotMultiMap.empty()
413 ? impl->m_VariableToPlotMultiMap.empty()
413 : std::none_of(
414 : std::none_of(
414 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
415 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
415 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
416 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
416 }
417 }
417
418
418 bool VisualizationGraphWidget::contains(const Variable &variable) const
419 bool VisualizationGraphWidget::contains(const Variable &variable) const
419 {
420 {
420 // Finds the variable among the keys of the map
421 // Finds the variable among the keys of the map
421 auto variablePtr = &variable;
422 auto variablePtr = &variable;
422 auto findVariable
423 auto findVariable
423 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
424 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
424
425
425 auto end = impl->m_VariableToPlotMultiMap.cend();
426 auto end = impl->m_VariableToPlotMultiMap.cend();
426 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
427 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
427 return it != end;
428 return it != end;
428 }
429 }
429
430
430 QString VisualizationGraphWidget::name() const
431 QString VisualizationGraphWidget::name() const
431 {
432 {
432 return impl->m_Name;
433 return impl->m_Name;
433 }
434 }
434
435
435 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
436 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
436 {
437 {
437 auto mimeData = new QMimeData;
438 auto mimeData = new QMimeData;
438
439
439 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
440 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
440 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
441 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
441 && selectionZoneItemUnderCursor) {
442 && selectionZoneItemUnderCursor) {
442 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
443 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
443 selectionZoneItemUnderCursor->range()));
444 selectionZoneItemUnderCursor->range()));
444 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
445 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
445 selectionZoneItemUnderCursor->range()));
446 selectionZoneItemUnderCursor->range()));
446 }
447 }
447 else {
448 else {
448 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
449 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
449
450
450 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
451 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
451 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
452 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
452 }
453 }
453
454
454 return mimeData;
455 return mimeData;
455 }
456 }
456
457
457 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
458 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
458 {
459 {
459 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
460 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
460 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
461 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
461 && selectionZoneItemUnderCursor) {
462 && selectionZoneItemUnderCursor) {
462
463
463 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
464 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
464 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
465 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
465
466
466 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
467 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
467 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
468 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
468 .toSize();
469 .toSize();
469
470
470 auto pixmap = QPixmap(zoneSize);
471 auto pixmap = QPixmap(zoneSize);
471 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
472 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
472
473
473 return pixmap;
474 return pixmap;
474 }
475 }
475
476
476 return QPixmap();
477 return QPixmap();
477 }
478 }
478
479
479 bool VisualizationGraphWidget::isDragAllowed() const
480 bool VisualizationGraphWidget::isDragAllowed() const
480 {
481 {
481 return true;
482 return true;
482 }
483 }
483
484
484 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
485 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
485 {
486 {
486 if (highlighted) {
487 if (highlighted) {
487 plot().setBackground(QBrush(QColor("#BBD5EE")));
488 plot().setBackground(QBrush(QColor("#BBD5EE")));
488 }
489 }
489 else {
490 else {
490 plot().setBackground(QBrush(Qt::white));
491 plot().setBackground(QBrush(Qt::white));
491 }
492 }
492
493
493 plot().update();
494 plot().update();
494 }
495 }
495
496
496 void VisualizationGraphWidget::addVerticalCursor(double time)
497 void VisualizationGraphWidget::addVerticalCursor(double time)
497 {
498 {
498 impl->m_VerticalCursor->setPosition(time);
499 impl->m_VerticalCursor->setPosition(time);
499 impl->m_VerticalCursor->setVisible(true);
500 impl->m_VerticalCursor->setVisible(true);
500
501
501 auto text
502 auto text
502 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
503 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
503 impl->m_VerticalCursor->setLabelText(text);
504 impl->m_VerticalCursor->setLabelText(text);
504 }
505 }
505
506
506 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
507 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
507 {
508 {
508 impl->m_VerticalCursor->setAbsolutePosition(position);
509 impl->m_VerticalCursor->setAbsolutePosition(position);
509 impl->m_VerticalCursor->setVisible(true);
510 impl->m_VerticalCursor->setVisible(true);
510
511
511 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
512 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
512 auto text
513 auto text
513 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
514 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
514 impl->m_VerticalCursor->setLabelText(text);
515 impl->m_VerticalCursor->setLabelText(text);
515 }
516 }
516
517
517 void VisualizationGraphWidget::removeVerticalCursor()
518 void VisualizationGraphWidget::removeVerticalCursor()
518 {
519 {
519 impl->m_VerticalCursor->setVisible(false);
520 impl->m_VerticalCursor->setVisible(false);
520 plot().replot(QCustomPlot::rpQueuedReplot);
521 plot().replot(QCustomPlot::rpQueuedReplot);
521 }
522 }
522
523
523 void VisualizationGraphWidget::addHorizontalCursor(double value)
524 void VisualizationGraphWidget::addHorizontalCursor(double value)
524 {
525 {
525 impl->m_HorizontalCursor->setPosition(value);
526 impl->m_HorizontalCursor->setPosition(value);
526 impl->m_HorizontalCursor->setVisible(true);
527 impl->m_HorizontalCursor->setVisible(true);
527 impl->m_HorizontalCursor->setLabelText(QString::number(value));
528 impl->m_HorizontalCursor->setLabelText(QString::number(value));
528 }
529 }
529
530
530 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
531 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
531 {
532 {
532 impl->m_HorizontalCursor->setAbsolutePosition(position);
533 impl->m_HorizontalCursor->setAbsolutePosition(position);
533 impl->m_HorizontalCursor->setVisible(true);
534 impl->m_HorizontalCursor->setVisible(true);
534
535
535 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
536 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
536 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
537 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
537 }
538 }
538
539
539 void VisualizationGraphWidget::removeHorizontalCursor()
540 void VisualizationGraphWidget::removeHorizontalCursor()
540 {
541 {
541 impl->m_HorizontalCursor->setVisible(false);
542 impl->m_HorizontalCursor->setVisible(false);
542 plot().replot(QCustomPlot::rpQueuedReplot);
543 plot().replot(QCustomPlot::rpQueuedReplot);
543 }
544 }
544
545
545 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
546 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
546 {
547 {
547 Q_UNUSED(event);
548 Q_UNUSED(event);
548
549
549 // Prevents that all variables will be removed from graph when it will be closed
550 // Prevents that all variables will be removed from graph when it will be closed
550 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
551 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
551 emit variableAboutToBeRemoved(variableEntry.first);
552 emit variableAboutToBeRemoved(variableEntry.first);
552 }
553 }
553 }
554 }
554
555
555 void VisualizationGraphWidget::enterEvent(QEvent *event)
556 void VisualizationGraphWidget::enterEvent(QEvent *event)
556 {
557 {
557 Q_UNUSED(event);
558 Q_UNUSED(event);
558 impl->m_RenderingDelegate->showGraphOverlay(true);
559 impl->m_RenderingDelegate->showGraphOverlay(true);
559 }
560 }
560
561
561 void VisualizationGraphWidget::leaveEvent(QEvent *event)
562 void VisualizationGraphWidget::leaveEvent(QEvent *event)
562 {
563 {
563 Q_UNUSED(event);
564 Q_UNUSED(event);
564 impl->m_RenderingDelegate->showGraphOverlay(false);
565 impl->m_RenderingDelegate->showGraphOverlay(false);
565
566
566 if (auto parentZone = parentZoneWidget()) {
567 if (auto parentZone = parentZoneWidget()) {
567 parentZone->notifyMouseLeaveGraph(this);
568 parentZone->notifyMouseLeaveGraph(this);
568 }
569 }
569 else {
570 else {
570 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
571 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
571 }
572 }
572
573
573 if (impl->m_HoveredZone) {
574 if (impl->m_HoveredZone) {
574 impl->m_HoveredZone->setHovered(false);
575 impl->m_HoveredZone->setHovered(false);
575 impl->m_HoveredZone = nullptr;
576 impl->m_HoveredZone = nullptr;
576 }
577 }
577 }
578 }
578
579
579 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
580 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
580 {
581 {
581 return *ui->widget;
582 return *ui->widget;
582 }
583 }
583
584
584 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
585 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
585 {
586 {
586 QMenu graphMenu{};
587 QMenu graphMenu{};
587
588
588 // Iterates on variables (unique keys)
589 // Iterates on variables (unique keys)
589 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
590 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
590 end = impl->m_VariableToPlotMultiMap.cend();
591 end = impl->m_VariableToPlotMultiMap.cend();
591 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
592 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
592 // 'Remove variable' action
593 // 'Remove variable' action
593 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
594 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
594 [ this, var = it->first ]() { removeVariable(var); });
595 [ this, var = it->first ]() { removeVariable(var); });
595 }
596 }
596
597
597 if (!impl->m_ZoomStack.isEmpty()) {
598 if (!impl->m_ZoomStack.isEmpty()) {
598 if (!graphMenu.isEmpty()) {
599 if (!graphMenu.isEmpty()) {
599 graphMenu.addSeparator();
600 graphMenu.addSeparator();
600 }
601 }
601
602
602 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
603 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
603 }
604 }
604
605
606 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
607 if (selectionZoneItem) {
608 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
609 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
610 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
611 graphMenu.addSeparator();
612 }
613
614 for (auto zoneAction : zoneActions) {
615 auto action = graphMenu.addAction(zoneAction->name());
616 QObject::connect(action, &QAction::triggered, [zoneAction, &selectedItems]() {
617 zoneAction->execute(selectedItems);
618 });
619 }
620 }
621
605 if (!graphMenu.isEmpty()) {
622 if (!graphMenu.isEmpty()) {
606 graphMenu.exec(QCursor::pos());
623 graphMenu.exec(QCursor::pos());
607 }
624 }
608 }
625 }
609
626
610 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
627 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
611 {
628 {
612 qCDebug(LOG_VisualizationGraphWidget())
629 qCDebug(LOG_VisualizationGraphWidget())
613 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
630 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
614 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
631 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
615
632
616 auto graphRange = SqpRange{t1.lower, t1.upper};
633 auto graphRange = SqpRange{t1.lower, t1.upper};
617 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
634 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
618
635
619 if (impl->m_DoAcquisition) {
636 if (impl->m_DoAcquisition) {
620 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
637 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
621
638
622 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
639 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
623 end = impl->m_VariableToPlotMultiMap.end();
640 end = impl->m_VariableToPlotMultiMap.end();
624 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
641 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
625 variableUnderGraphVector.push_back(it->first);
642 variableUnderGraphVector.push_back(it->first);
626 }
643 }
627 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
644 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
628 !impl->m_IsCalibration);
645 !impl->m_IsCalibration);
629
646
630 if (!impl->m_IsCalibration) {
647 if (!impl->m_IsCalibration) {
631 qCDebug(LOG_VisualizationGraphWidget())
648 qCDebug(LOG_VisualizationGraphWidget())
632 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
649 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
633 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
650 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
634 emit synchronize(graphRange, oldGraphRange);
651 emit synchronize(graphRange, oldGraphRange);
635 }
652 }
636 }
653 }
637
654
638 auto pos = mapFromGlobal(QCursor::pos());
655 auto pos = mapFromGlobal(QCursor::pos());
639 auto axisPos = impl->posToAxisPos(pos, plot());
656 auto axisPos = impl->posToAxisPos(pos, plot());
640 if (auto parentZone = parentZoneWidget()) {
657 if (auto parentZone = parentZoneWidget()) {
641 if (impl->pointIsInAxisRect(axisPos, plot())) {
658 if (impl->pointIsInAxisRect(axisPos, plot())) {
642 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
659 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
643 }
660 }
644 else {
661 else {
645 parentZone->notifyMouseLeaveGraph(this);
662 parentZone->notifyMouseLeaveGraph(this);
646 }
663 }
647 }
664 }
648 else {
665 else {
649 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
666 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
650 }
667 }
651 }
668 }
652
669
653 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
670 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
654 {
671 {
655 impl->m_RenderingDelegate->onMouseDoubleClick(event);
672 impl->m_RenderingDelegate->onMouseDoubleClick(event);
656 }
673 }
657
674
658 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
675 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
659 {
676 {
660 // Handles plot rendering when mouse is moving
677 // Handles plot rendering when mouse is moving
661 impl->m_RenderingDelegate->onMouseMove(event);
678 impl->m_RenderingDelegate->onMouseMove(event);
662
679
663 auto axisPos = impl->posToAxisPos(event->pos(), plot());
680 auto axisPos = impl->posToAxisPos(event->pos(), plot());
664
681
665 // Zoom box and zone drawing
682 // Zoom box and zone drawing
666 if (impl->m_DrawingZoomRect) {
683 if (impl->m_DrawingZoomRect) {
667 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
684 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
668 }
685 }
669 else if (impl->m_DrawingZone) {
686 else if (impl->m_DrawingZone) {
670 impl->m_DrawingZone->setEnd(axisPos.x());
687 impl->m_DrawingZone->setEnd(axisPos.x());
671 }
688 }
672
689
673 // Cursor
690 // Cursor
674 if (auto parentZone = parentZoneWidget()) {
691 if (auto parentZone = parentZoneWidget()) {
675 if (impl->pointIsInAxisRect(axisPos, plot())) {
692 if (impl->pointIsInAxisRect(axisPos, plot())) {
676 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
693 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
677 }
694 }
678 else {
695 else {
679 parentZone->notifyMouseLeaveGraph(this);
696 parentZone->notifyMouseLeaveGraph(this);
680 }
697 }
681 }
698 }
682 else {
699 else {
683 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
700 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
684 }
701 }
685
702
686 // Search for the selection zone under the mouse
703 // Search for the selection zone under the mouse
687 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
704 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
688 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
705 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
689 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
706 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
690
707
691 // Sets the appropriate cursor shape
708 // Sets the appropriate cursor shape
692 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
709 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
693 setCursor(cursorShape);
710 setCursor(cursorShape);
694
711
695 // Manages the hovered zone
712 // Manages the hovered zone
696 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
713 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
697 if (impl->m_HoveredZone) {
714 if (impl->m_HoveredZone) {
698 impl->m_HoveredZone->setHovered(false);
715 impl->m_HoveredZone->setHovered(false);
699 }
716 }
700 selectionZoneItemUnderCursor->setHovered(true);
717 selectionZoneItemUnderCursor->setHovered(true);
701 impl->m_HoveredZone = selectionZoneItemUnderCursor;
718 impl->m_HoveredZone = selectionZoneItemUnderCursor;
702 plot().replot(QCustomPlot::rpQueuedReplot);
719 plot().replot(QCustomPlot::rpQueuedReplot);
703 }
720 }
704 }
721 }
705 else {
722 else {
706 // There is no zone under the mouse or the interaction mode is not "selection zones"
723 // There is no zone under the mouse or the interaction mode is not "selection zones"
707 if (impl->m_HoveredZone) {
724 if (impl->m_HoveredZone) {
708 impl->m_HoveredZone->setHovered(false);
725 impl->m_HoveredZone->setHovered(false);
709 impl->m_HoveredZone = nullptr;
726 impl->m_HoveredZone = nullptr;
710 }
727 }
711
728
712 setCursor(Qt::ArrowCursor);
729 setCursor(Qt::ArrowCursor);
713 }
730 }
714
731
715 impl->m_HasMovedMouse = true;
732 impl->m_HasMovedMouse = true;
716 VisualizationDragWidget::mouseMoveEvent(event);
733 VisualizationDragWidget::mouseMoveEvent(event);
717 }
734 }
718
735
719 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
736 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
720 {
737 {
721 auto value = event->angleDelta().x() + event->angleDelta().y();
738 auto value = event->angleDelta().x() + event->angleDelta().y();
722 if (value != 0) {
739 if (value != 0) {
723
740
724 auto direction = value > 0 ? 1.0 : -1.0;
741 auto direction = value > 0 ? 1.0 : -1.0;
725 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
742 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
726 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
743 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
727 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
744 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
728
745
729 auto zoomOrientations = QFlags<Qt::Orientation>{};
746 auto zoomOrientations = QFlags<Qt::Orientation>{};
730 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
747 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
731 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
748 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
732
749
733 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
750 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
734
751
735 if (!isZoomX && !isZoomY) {
752 if (!isZoomX && !isZoomY) {
736 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
753 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
737 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
754 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
738
755
739 axis->setRange(axis->range() + diff);
756 axis->setRange(axis->range() + diff);
740
757
741 if (plot().noAntialiasingOnDrag()) {
758 if (plot().noAntialiasingOnDrag()) {
742 plot().setNotAntialiasedElements(QCP::aeAll);
759 plot().setNotAntialiasedElements(QCP::aeAll);
743 }
760 }
744
761
745 plot().replot(QCustomPlot::rpQueuedReplot);
762 plot().replot(QCustomPlot::rpQueuedReplot);
746 }
763 }
747 }
764 }
748 }
765 }
749
766
750 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
767 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
751 {
768 {
752 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
769 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
753 auto isSelectionZoneMode
770 auto isSelectionZoneMode
754 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
771 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
755 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
772 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
756
773
757 if (!isDragDropClick && isLeftClick) {
774 if (!isDragDropClick && isLeftClick) {
758 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
775 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
759 // Starts a zoom box
776 // Starts a zoom box
760 impl->startDrawingRect(event->pos(), plot());
777 impl->startDrawingRect(event->pos(), plot());
761 }
778 }
762 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
779 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
763 // Starts a new selection zone
780 // Starts a new selection zone
764 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
781 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
765 if (!zoneAtPos) {
782 if (!zoneAtPos) {
766 impl->startDrawingZone(event->pos(), this);
783 impl->startDrawingZone(event->pos(), this);
767 }
784 }
768 }
785 }
769 }
786 }
770
787
771 // Allows mouse panning only in default mode
788 // Allows mouse panning only in default mode
772 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
789 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
773 == SqpApplication::PlotsInteractionMode::None
790 == SqpApplication::PlotsInteractionMode::None
774 && !isDragDropClick);
791 && !isDragDropClick);
775
792
776 // Allows zone edition only in selection zone mode without drag&drop
793 // Allows zone edition only in selection zone mode without drag&drop
777 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
794 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
778
795
779 // Selection / Deselection
796 // Selection / Deselection
780 if (isSelectionZoneMode) {
797 if (isSelectionZoneMode) {
781 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
798 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
782 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
799 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
783 if (selectionZoneItemUnderCursor && isLeftClick) {
800 if (selectionZoneItemUnderCursor && isLeftClick) {
784 selectionZoneItemUnderCursor->setAssociatedEditedZones(
801 selectionZoneItemUnderCursor->setAssociatedEditedZones(
785 parentVisualizationWidget()->selectionZoneManager().selectedItems());
802 parentVisualizationWidget()->selectionZoneManager().selectedItems());
786 }
803 }
787 else if (!isMultiSelectionClick && isLeftClick) {
804 else if (!isMultiSelectionClick && isLeftClick) {
788 parentVisualizationWidget()->selectionZoneManager().clearSelection();
805 parentVisualizationWidget()->selectionZoneManager().clearSelection();
789 }
806 }
790 else {
807 else {
791 // No selection change
808 // No selection change
792 }
809 }
793 }
810 }
794
811
795
812
796 impl->m_HasMovedMouse = false;
813 impl->m_HasMovedMouse = false;
797 VisualizationDragWidget::mousePressEvent(event);
814 VisualizationDragWidget::mousePressEvent(event);
798 }
815 }
799
816
800 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
817 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
801 {
818 {
802 if (impl->m_DrawingZoomRect) {
819 if (impl->m_DrawingZoomRect) {
803
820
804 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
821 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
805 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
822 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
806
823
807 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
824 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
808 impl->m_DrawingZoomRect->bottomRight->coords().x()};
825 impl->m_DrawingZoomRect->bottomRight->coords().x()};
809
826
810 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
827 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
811 impl->m_DrawingZoomRect->bottomRight->coords().y()};
828 impl->m_DrawingZoomRect->bottomRight->coords().y()};
812
829
813 impl->removeDrawingRect(plot());
830 impl->removeDrawingRect(plot());
814
831
815 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
832 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
816 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
833 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
817 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
834 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
818 axisX->setRange(newAxisXRange);
835 axisX->setRange(newAxisXRange);
819 axisY->setRange(newAxisYRange);
836 axisY->setRange(newAxisYRange);
820
837
821 plot().replot(QCustomPlot::rpQueuedReplot);
838 plot().replot(QCustomPlot::rpQueuedReplot);
822 }
839 }
823 }
840 }
824
841
825 impl->endDrawingZone(this);
842 impl->endDrawingZone(this);
826
843
827 impl->m_IsCalibration = false;
844 impl->m_IsCalibration = false;
828
845
829 // Selection / Deselection
846 // Selection / Deselection
830 auto isSelectionZoneMode
847 auto isSelectionZoneMode
831 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
848 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
832 if (isSelectionZoneMode) {
849 if (isSelectionZoneMode) {
833 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
850 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
834 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
851 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
835 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
852 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
836 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
853 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
837 parentVisualizationWidget()->selectionZoneManager().select(
854 parentVisualizationWidget()->selectionZoneManager().select(
838 {selectionZoneItemUnderCursor});
855 {selectionZoneItemUnderCursor});
839 }
856 }
840 else if (!impl->m_HasMovedMouse) {
857 else if (!impl->m_HasMovedMouse) {
841 parentVisualizationWidget()->selectionZoneManager().setSelected(
858 parentVisualizationWidget()->selectionZoneManager().setSelected(
842 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
859 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
843 || event->button() == Qt::RightButton);
860 || event->button() == Qt::RightButton);
844 }
861 }
845 }
862 }
846 else {
863 else {
847 // No selection change
864 // No selection change
848 }
865 }
849 }
866 }
850 }
867 }
851
868
852 void VisualizationGraphWidget::onDataCacheVariableUpdated()
869 void VisualizationGraphWidget::onDataCacheVariableUpdated()
853 {
870 {
854 auto graphRange = ui->widget->xAxis->range();
871 auto graphRange = ui->widget->xAxis->range();
855 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
872 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
856
873
857 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
874 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
858 auto variable = variableEntry.first;
875 auto variable = variableEntry.first;
859 qCDebug(LOG_VisualizationGraphWidget())
876 qCDebug(LOG_VisualizationGraphWidget())
860 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
877 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
861 qCDebug(LOG_VisualizationGraphWidget())
878 qCDebug(LOG_VisualizationGraphWidget())
862 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
879 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
863 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
880 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
864 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
881 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
865 }
882 }
866 }
883 }
867 }
884 }
868
885
869 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
886 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
870 const SqpRange &range)
887 const SqpRange &range)
871 {
888 {
872 auto it = impl->m_VariableToPlotMultiMap.find(variable);
889 auto it = impl->m_VariableToPlotMultiMap.find(variable);
873 if (it != impl->m_VariableToPlotMultiMap.end()) {
890 if (it != impl->m_VariableToPlotMultiMap.end()) {
874 impl->updateData(it->second, variable->dataSeries(), range);
891 impl->updateData(it->second, variable->dataSeries(), range);
875 }
892 }
876 }
893 }
General Comments 4
Under Review
author

Auto status change to "Under Review"

Approved

Status change > Approved

Approved

Status change > Approved

You need to be logged in to leave comments. Login now