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