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