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