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