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