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