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

Auto status change to "Under Review"

Approved

Status change > Approved

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