##// END OF EJS Templates
Improves display of selected zones
trabillard -
r1051:ea6e09e4a905
parent child
Show More
@@ -1,878 +1,878
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 bool m_HasMovedMouse = false; //Indicates if the mouse moved in a releaseMouse even
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 752 bool isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
753 753 auto isSelectionZoneMode
754 754 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
755 755
756 756 if (!isDragDropClick) {
757 757 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
758 758 // Starts a zoom box
759 759 impl->startDrawingRect(event->pos(), plot());
760 760 }
761 761 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
762 762 // Starts a new selection zone
763 763 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
764 764 if (!zoneAtPos) {
765 765 impl->startDrawingZone(event->pos(), this);
766 766 }
767 767 }
768 768 }
769 769 else if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None) {
770 770 plot().setInteraction(QCP::iRangeDrag, true);
771 771 }
772 772
773 773 // Allows mouse panning only in default mode
774 774 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
775 775 == SqpApplication::PlotsInteractionMode::None
776 776 && !isDragDropClick);
777 777
778 778 // Allows zone edition only in selection zone mode without drag&drop
779 779 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
780 780
781 // Selection / Deselection
781 // Selection / Deselection
782 782 if (isSelectionZoneMode) {
783 783 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
784 784 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
785 785 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
786 786 selectionZoneItemUnderCursor->setAssociatedEditedZones(
787 787 parentVisualizationWidget()->selectionZoneManager().selectedItems());
788 788 }
789 789 else if (!isMultiSelectionClick && event->button() == Qt::LeftButton) {
790 790 parentVisualizationWidget()->selectionZoneManager().clearSelection();
791 791 }
792 792 else {
793 793 // No selection change
794 794 }
795 795 }
796 796
797 797
798 798 impl->m_HasMovedMouse = false;
799 799 VisualizationDragWidget::mousePressEvent(event);
800 800 }
801 801
802 802 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
803 803 {
804 804 if (impl->m_DrawingZoomRect) {
805 805
806 806 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
807 807 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
808 808
809 809 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
810 810 impl->m_DrawingZoomRect->bottomRight->coords().x()};
811 811
812 812 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
813 813 impl->m_DrawingZoomRect->bottomRight->coords().y()};
814 814
815 815 impl->removeDrawingRect(plot());
816 816
817 817 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
818 818 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
819 819 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
820 820 axisX->setRange(newAxisXRange);
821 821 axisY->setRange(newAxisYRange);
822 822
823 823 plot().replot(QCustomPlot::rpQueuedReplot);
824 824 }
825 825 }
826 826
827 827 impl->endDrawingZone(this);
828 828
829 829 impl->m_IsCalibration = false;
830 830
831 831 // Selection / Deselection
832 832 auto isSelectionZoneMode
833 833 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
834 834 if (isSelectionZoneMode) {
835 835 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
836 836 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
837 837 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
838 838 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
839 839 parentVisualizationWidget()->selectionZoneManager().select(
840 840 {selectionZoneItemUnderCursor});
841 841 }
842 842 else if (!impl->m_HasMovedMouse) {
843 843 parentVisualizationWidget()->selectionZoneManager().setSelected(
844 844 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
845 845 || event->button() == Qt::RightButton);
846 846 }
847 847 }
848 848 else {
849 849 // No selection change
850 850 }
851 851 }
852 852 }
853 853
854 854 void VisualizationGraphWidget::onDataCacheVariableUpdated()
855 855 {
856 856 auto graphRange = ui->widget->xAxis->range();
857 857 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
858 858
859 859 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
860 860 auto variable = variableEntry.first;
861 861 qCDebug(LOG_VisualizationGraphWidget())
862 862 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
863 863 qCDebug(LOG_VisualizationGraphWidget())
864 864 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
865 865 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
866 866 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
867 867 }
868 868 }
869 869 }
870 870
871 871 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
872 872 const SqpRange &range)
873 873 {
874 874 auto it = impl->m_VariableToPlotMultiMap.find(variable);
875 875 if (it != impl->m_VariableToPlotMultiMap.end()) {
876 876 impl->updateData(it->second, variable->dataSeries(), range);
877 877 }
878 878 }
@@ -1,337 +1,343
1 1 #include "Visualization/VisualizationSelectionZoneItem.h"
2 2
3 3 const QString &DEFAULT_COLOR = QStringLiteral("#E79D41");
4 4
5 5 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
6 6
7 7 QCustomPlot *m_Plot;
8 8 double m_T1 = 0;
9 9 double m_T2 = 0;
10 10 QColor m_Color;
11 11
12 12 bool m_IsEditionEnabled = true;
13 13 double m_MovedOrinalT1 = 0;
14 14 double m_MovedOrinalT2 = 0;
15 15
16 16 QCPItemStraightLine *m_LeftLine;
17 17 QCPItemStraightLine *m_RightLine;
18 18 QCPItemText *m_NameLabelItem = nullptr;
19 19
20 20 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
21 21 EditionMode m_CurrentEditionMode;
22 22
23 23 QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones;
24 24
25 25 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
26 26 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
27 27 {
28 28 }
29 29
30 30 void updatePosition(VisualizationSelectionZoneItem *item)
31 31 {
32 32 item->topLeft->setCoords(m_T1, 0);
33 33 item->bottomRight->setCoords(m_T2, 1);
34 34 }
35 35
36 36 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
37 37 {
38 38 auto distanceLeft = m_LeftLine->selectTest(pos, false);
39 39 auto distanceRight = m_RightLine->selectTest(pos, false);
40 40 auto distance = zoneItem->selectTest(pos, false);
41 41
42 42 if (distanceRight <= distance) {
43 43 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
44 44 }
45 45 else if (distanceLeft <= distance) {
46 46 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
47 47 }
48 48
49 49 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
50 50 }
51 51
52 52 double pixelSizeToAxisXSize(double pixels)
53 53 {
54 54 auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
55 55 return axis->pixelToCoord(pixels) - axis->pixelToCoord(0);
56 56 }
57 57 };
58 58
59 59 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
60 60 : QCPItemRect(plot),
61 61 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
62 62 {
63 63 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
64 64 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
65 65 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
66 66 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
67 67 setSelectable(false);
68 68
69 69 impl->m_RightLine = new QCPItemStraightLine(plot);
70 70 impl->m_RightLine->point1->setParentAnchor(topRight);
71 71 impl->m_RightLine->point2->setParentAnchor(bottomRight);
72 72 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
73 73 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
74 74 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
75 75 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
76 76 impl->m_RightLine->setSelectable(false);
77 77
78 78 impl->m_LeftLine = new QCPItemStraightLine(plot);
79 79 impl->m_LeftLine->point1->setParentAnchor(topLeft);
80 80 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
81 81 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
82 82 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
83 83 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
84 84 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
85 85 impl->m_LeftLine->setSelectable(false);
86 86
87 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
88 &QCPItemStraightLine::setSelected);
89 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
90 &QCPItemStraightLine::setSelected);
91
87 92 setColor(QColor(DEFAULT_COLOR));
88 93 }
89 94
90 95 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem()
91 96 {
92 97 impl->m_Plot->removeItem(impl->m_RightLine);
93 98 impl->m_Plot->removeItem(impl->m_LeftLine);
94 99 }
95 100
96 101 void VisualizationSelectionZoneItem::setName(const QString &name)
97 102 {
98 103 if (name.isEmpty() && impl->m_NameLabelItem) {
99 104 impl->m_Plot->removeItem(impl->m_NameLabelItem);
100 105 impl->m_NameLabelItem = nullptr;
101 106 }
102 107 else if (!impl->m_NameLabelItem) {
103 108 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
104 109 impl->m_NameLabelItem->setText(name);
105 110 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
106 111 impl->m_NameLabelItem->setColor(impl->m_Color);
107 112 impl->m_NameLabelItem->position->setParentAnchor(top);
108 113 }
109 114 }
110 115
111 116 QString VisualizationSelectionZoneItem::name() const
112 117 {
113 118 if (!impl->m_NameLabelItem) {
114 119 return QString();
115 120 }
116 121
117 122 return impl->m_NameLabelItem->text();
118 123 }
119 124
120 125 SqpRange VisualizationSelectionZoneItem::range() const
121 126 {
122 127 SqpRange range;
123 128 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
124 129 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
125 130 return range;
126 131 }
127 132
128 133 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
129 134 {
130 135 impl->m_T1 = tstart;
131 136 impl->m_T2 = tend;
132 137 impl->updatePosition(this);
133 138 }
134 139
135 140 void VisualizationSelectionZoneItem::setStart(double tstart)
136 141 {
137 142 impl->m_T1 = tstart;
138 143 impl->updatePosition(this);
139 144 }
140 145
141 146 void VisualizationSelectionZoneItem::setEnd(double tend)
142 147 {
143 148 impl->m_T2 = tend;
144 149 impl->updatePosition(this);
145 150 }
146 151
147 152 void VisualizationSelectionZoneItem::setColor(const QColor &color)
148 153 {
149 154 impl->m_Color = color;
150 155
151 156 auto brushColor = color;
152 brushColor.setAlpha(40);
157 brushColor.setAlpha(80);
153 158 setBrush(QBrush(brushColor));
154 159 setPen(QPen(Qt::NoPen));
155 160
156 161 auto selectedBrushColor = brushColor;
157 selectedBrushColor.setAlpha(65);
162 selectedBrushColor.setAlpha(150);
158 163 setSelectedBrush(QBrush(selectedBrushColor));
159 164 setSelectedPen(QPen(Qt::NoPen));
160 165
161 166 auto linePen = QPen(color);
162 167 linePen.setStyle(Qt::SolidLine);
163 linePen.setWidth(2);
168 linePen.setWidth(4);
164 169
165 170 auto selectedLinePen = linePen;
166 selectedLinePen.setColor(color.darker(30));
171 selectedLinePen.setColor(color.darker(120));
172 selectedLinePen.setWidth(4);
167 173
168 174 impl->m_LeftLine->setPen(linePen);
169 175 impl->m_RightLine->setPen(linePen);
170 176
171 177 impl->m_LeftLine->setSelectedPen(selectedLinePen);
172 178 impl->m_RightLine->setSelectedPen(selectedLinePen);
173 179 }
174 180
175 181 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
176 182 {
177 183 impl->m_IsEditionEnabled = value;
178 184 setSelectable(value);
179 185 if (!value) {
180 186 setSelected(false);
181 187 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
182 188 }
183 189 }
184 190
185 191 bool VisualizationSelectionZoneItem::isEditionEnabled() const
186 192 {
187 193 return impl->m_IsEditionEnabled;
188 194 }
189 195
190 196 Qt::CursorShape
191 197 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
192 198 {
193 199 auto mode = impl->m_CurrentEditionMode
194 200 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
195 201 ? impl->getEditionMode(position, this)
196 202 : impl->m_CurrentEditionMode;
197 203 switch (mode) {
198 204 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
199 205 return Qt::SizeAllCursor;
200 206 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
201 207 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
202 208 return Qt::SizeHorCursor;
203 209 default:
204 210 return Qt::ArrowCursor;
205 211 }
206 212 }
207 213
208 214 void VisualizationSelectionZoneItem::setHovered(bool value)
209 215 {
210 216 if (value) {
211 217 auto linePen = impl->m_LeftLine->pen();
212 218 linePen.setStyle(Qt::DotLine);
213 219 linePen.setWidth(3);
214 220
215 221 auto selectedLinePen = impl->m_LeftLine->selectedPen();
216 222 ;
217 223 selectedLinePen.setStyle(Qt::DotLine);
218 224 selectedLinePen.setWidth(3);
219 225
220 226 impl->m_LeftLine->setPen(linePen);
221 227 impl->m_RightLine->setPen(linePen);
222 228
223 229 impl->m_LeftLine->setSelectedPen(selectedLinePen);
224 230 impl->m_RightLine->setSelectedPen(selectedLinePen);
225 231 }
226 232 else {
227 233 setColor(impl->m_Color);
228 234 }
229 235 }
230 236
231 237 void VisualizationSelectionZoneItem::setAssociatedEditedZones(
232 238 const QVector<VisualizationSelectionZoneItem *> &associatedZones)
233 239 {
234 240 impl->m_AssociatedEditedZones = associatedZones;
235 241 impl->m_AssociatedEditedZones.removeAll(this);
236 242 }
237 243
238 244 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
239 245 {
240 246 if (isEditionEnabled() && event->button() == Qt::LeftButton) {
241 247 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
242 248
243 249 impl->m_MovedOrinalT1 = impl->m_T1;
244 250 impl->m_MovedOrinalT2 = impl->m_T2;
245 251 for (auto associatedZone : impl->m_AssociatedEditedZones) {
246 252 associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1;
247 253 associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2;
248 254 }
249 255 }
250 256 else {
251 257 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
252 258 event->ignore();
253 259 }
254 260 }
255 261
256 262 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
257 263 {
258 264 if (isEditionEnabled()) {
259 265 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
260 266 auto pixelDiff = event->pos().x() - startPos.x();
261 267 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
262 268
263 269 switch (impl->m_CurrentEditionMode) {
264 270 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
265 271 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
266 272 for (auto associatedZone : impl->m_AssociatedEditedZones) {
267 273 associatedZone->move(pixelDiff);
268 274 }
269 275 break;
270 276 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
271 277 setStart(impl->m_MovedOrinalT1 + diff);
272 278 for (auto associatedZone : impl->m_AssociatedEditedZones) {
273 279 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
274 280 ? associatedZone->resizeLeft(pixelDiff)
275 281 : associatedZone->resizeRight(pixelDiff);
276 282 }
277 283 break;
278 284 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
279 285 setEnd(impl->m_MovedOrinalT2 + diff);
280 286 for (auto associatedZone : impl->m_AssociatedEditedZones) {
281 287 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
282 288 ? associatedZone->resizeRight(pixelDiff)
283 289 : associatedZone->resizeLeft(pixelDiff);
284 290 }
285 291 break;
286 292 default:
287 293 break;
288 294 }
289 295
290 296 for (auto associatedZone : impl->m_AssociatedEditedZones) {
291 297 associatedZone->parentPlot()->replot();
292 298 }
293 299 }
294 300 else {
295 301 event->ignore();
296 302 }
297 303 }
298 304
299 305 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
300 306 {
301 307 if (isEditionEnabled()) {
302 308 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
303 309 }
304 310 else {
305 311 event->ignore();
306 312 }
307 313
308 314 impl->m_AssociatedEditedZones.clear();
309 315 }
310 316
311 317 void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff)
312 318 {
313 319 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
314 320 if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) {
315 321 setStart(impl->m_MovedOrinalT1 + diff);
316 322 }
317 323 else {
318 324 setEnd(impl->m_MovedOrinalT2 + diff);
319 325 }
320 326 }
321 327
322 328 void VisualizationSelectionZoneItem::resizeRight(double pixelDiff)
323 329 {
324 330 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
325 331 if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) {
326 332 setStart(impl->m_MovedOrinalT1 + diff);
327 333 }
328 334 else {
329 335 setEnd(impl->m_MovedOrinalT2 + diff);
330 336 }
331 337 }
332 338
333 339 void VisualizationSelectionZoneItem::move(double pixelDiff)
334 340 {
335 341 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
336 342 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
337 343 }
General Comments 0
You need to be logged in to leave comments. Login now