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

Auto status change to "Under Review"

Approved

Status change > Approved

Approved

Status change > Approved

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