##// END OF EJS Templates
Basic mouse wheel interactions + mode drag
trabillard -
r1001:ebb8b3d01eee
parent child
Show More
@@ -1,53 +1,55
1 1 #include "Visualization/VisualizationDragWidget.h"
2 2 #include "Visualization/VisualizationDragDropContainer.h"
3 3
4 4 #include <QApplication>
5 5 #include <QMouseEvent>
6 6
7 #include <SqpApplication.h>
8
7 9 struct VisualizationDragWidget::VisualizationDragWidgetPrivate {
8 10
9 11 QPoint m_DragStartPosition;
10 12 bool m_DragStartPositionValid = false;
11 13
12 14 explicit VisualizationDragWidgetPrivate() {}
13 15 };
14 16
15 17 VisualizationDragWidget::VisualizationDragWidget(QWidget *parent)
16 18 : QWidget{parent}, impl{spimpl::make_unique_impl<VisualizationDragWidgetPrivate>()}
17 19 {
18 20 }
19 21
20 22 void VisualizationDragWidget::mousePressEvent(QMouseEvent *event)
21 23 {
22 24 if (event->button() == Qt::LeftButton) {
23 25 impl->m_DragStartPosition = event->pos();
24 26 }
25 27
26 28 impl->m_DragStartPositionValid = isDragAllowed();
27 29
28 30 QWidget::mousePressEvent(event);
29 31 }
30 32
31 33 void VisualizationDragWidget::mouseMoveEvent(QMouseEvent *event)
32 34 {
33 35 if (!impl->m_DragStartPositionValid || !isDragAllowed()) {
34 36 return;
35 37 }
36 38
37 39 if (!(event->buttons() & Qt::LeftButton)) {
38 40 return;
39 41 }
40 42
41 if (!event->modifiers().testFlag(Qt::AltModifier)) {
42 return;
43 }
43 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::DragAndDrop
44 || event->modifiers().testFlag(Qt::AltModifier)) {
44 45
45 46 if ((event->pos() - impl->m_DragStartPosition).manhattanLength()
46 47 < QApplication::startDragDistance()) {
47 48 return;
48 49 }
49 50
50 51 emit dragDetected(this, impl->m_DragStartPosition);
52 }
51 53
52 54 QWidget::mouseMoveEvent(event);
53 55 }
@@ -1,402 +1,416
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationDefs.h"
4 4 #include "Visualization/VisualizationGraphHelper.h"
5 5 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7 #include "ui_VisualizationGraphWidget.h"
8 8
9 9 #include <Common/MimeTypesDef.h>
10 10 #include <Data/ArrayData.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <DragAndDrop/DragDropHelper.h>
13 13 #include <Settings/SqpSettingsDefs.h>
14 14 #include <SqpApplication.h>
15 15 #include <Time/TimeController.h>
16 16 #include <Variable/Variable.h>
17 17 #include <Variable/VariableController.h>
18 18
19 19 #include <unordered_map>
20 20
21 21 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
22 22
23 23 namespace {
24 24
25 25 /// Key pressed to enable zoom on horizontal axis
26 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
26 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
27 27
28 28 /// Key pressed to enable zoom on vertical axis
29 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
29 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
30
31 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
32 const auto PAN_SPEED = 5;
33
34 /// Key pressed to enable a calibration pan
35 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
30 36
31 37 } // namespace
32 38
33 39 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
34 40
35 41 explicit VisualizationGraphWidgetPrivate(const QString &name)
36 42 : m_Name{name},
37 43 m_DoAcquisition{true},
38 44 m_IsCalibration{false},
39 45 m_RenderingDelegate{nullptr}
40 46 {
41 47 }
42 48
43 49 QString m_Name;
44 50 // 1 variable -> n qcpplot
45 51 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
46 52 bool m_DoAcquisition;
47 53 bool m_IsCalibration;
48 54 /// Delegate used to attach rendering features to the plot
49 55 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
50 56 };
51 57
52 58 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
53 59 : VisualizationDragWidget{parent},
54 60 ui{new Ui::VisualizationGraphWidget},
55 61 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
56 62 {
57 63 ui->setupUi(this);
58 64
59 65 // 'Close' options : widget is deleted when closed
60 66 setAttribute(Qt::WA_DeleteOnClose);
61 67
62 68 // Set qcpplot properties :
63 69 // - Drag (on x-axis) and zoom are enabled
64 70 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
65 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
66 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
71 ui->widget->setInteractions(QCP::iRangeZoom | QCP::iSelectItems);
67 72
68 73 // The delegate must be initialized after the ui as it uses the plot
69 74 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
70 75
71 76 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
72 77 connect(ui->widget, &QCustomPlot::mouseRelease, this,
73 78 &VisualizationGraphWidget::onMouseRelease);
74 79 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
75 80 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
76 81 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
77 82 &QCPAxis::rangeChanged),
78 83 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
79 84
80 85 // Activates menu when right clicking on the graph
81 86 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
82 87 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
83 88 &VisualizationGraphWidget::onGraphMenuRequested);
84 89
85 90 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
86 91 &VariableController::onRequestDataLoading);
87 92
88 93 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
89 94 &VisualizationGraphWidget::onUpdateVarDisplaying);
90 95 }
91 96
92 97
93 98 VisualizationGraphWidget::~VisualizationGraphWidget()
94 99 {
95 100 delete ui;
96 101 }
97 102
98 103 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
99 104 {
100 105 auto parent = parentWidget();
101 106 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
102 107 parent = parent->parentWidget();
103 108 }
104 109
105 110 return qobject_cast<VisualizationZoneWidget *>(parent);
106 111 }
107 112
108 113 void VisualizationGraphWidget::enableAcquisition(bool enable)
109 114 {
110 115 impl->m_DoAcquisition = enable;
111 116 }
112 117
113 118 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
114 119 {
115 120 // Uses delegate to create the qcpplot components according to the variable
116 121 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
117 122
118 123 if (auto dataSeries = variable->dataSeries()) {
119 124 // Set axes properties according to the units of the data series
120 125 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
121 126
122 127 // Sets rendering properties for the new plottables
123 128 // Warning: this method must be called after setAxesProperties(), as it can access to some
124 129 // axes properties that have to be initialized
125 130 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
126 131 }
127 132
128 133 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
129 134
130 135 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
131 136
132 137 this->enableAcquisition(false);
133 138 this->setGraphRange(range);
134 139 this->enableAcquisition(true);
135 140
136 141 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
137 142
138 143 emit variableAdded(variable);
139 144 }
140 145
141 146 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
142 147 {
143 148 // Each component associated to the variable :
144 149 // - is removed from qcpplot (which deletes it)
145 150 // - is no longer referenced in the map
146 151 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
147 152 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
148 153 emit variableAboutToBeRemoved(variable);
149 154
150 155 auto &plottablesMap = variableIt->second;
151 156
152 157 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
153 158 plottableIt != plottableEnd;) {
154 159 ui->widget->removePlottable(plottableIt->second);
155 160 plottableIt = plottablesMap.erase(plottableIt);
156 161 }
157 162
158 163 impl->m_VariableToPlotMultiMap.erase(variableIt);
159 164 }
160 165
161 166 // Updates graph
162 167 ui->widget->replot();
163 168 }
164 169
165 170 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
166 171 {
167 172 auto variables = QList<std::shared_ptr<Variable> >{};
168 173 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
169 174 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
170 175 variables << it->first;
171 176 }
172 177
173 178 return variables;
174 179 }
175 180
176 181 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
177 182 {
178 183 if (!variable) {
179 184 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
180 185 return;
181 186 }
182 187
183 188 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
184 189 }
185 190
186 191 SqpRange VisualizationGraphWidget::graphRange() const noexcept
187 192 {
188 193 auto graphRange = ui->widget->xAxis->range();
189 194 return SqpRange{graphRange.lower, graphRange.upper};
190 195 }
191 196
192 197 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
193 198 {
194 199 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
195 200 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
196 201 ui->widget->replot();
197 202 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
198 203 }
199 204
200 205 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
201 206 {
202 207 if (visitor) {
203 208 visitor->visit(this);
204 209 }
205 210 else {
206 211 qCCritical(LOG_VisualizationGraphWidget())
207 212 << tr("Can't visit widget : the visitor is null");
208 213 }
209 214 }
210 215
211 216 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
212 217 {
213 218 /// @todo : for the moment, a graph can always accomodate a variable
214 219 Q_UNUSED(variable);
215 220 return true;
216 221 }
217 222
218 223 bool VisualizationGraphWidget::contains(const Variable &variable) const
219 224 {
220 225 // Finds the variable among the keys of the map
221 226 auto variablePtr = &variable;
222 227 auto findVariable
223 228 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
224 229
225 230 auto end = impl->m_VariableToPlotMultiMap.cend();
226 231 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
227 232 return it != end;
228 233 }
229 234
230 235 QString VisualizationGraphWidget::name() const
231 236 {
232 237 return impl->m_Name;
233 238 }
234 239
235 240 QMimeData *VisualizationGraphWidget::mimeData() const
236 241 {
237 242 auto mimeData = new QMimeData;
238 243 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
239 244
240 245 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
241 246 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
242 247
243 248 return mimeData;
244 249 }
245 250
246 251 bool VisualizationGraphWidget::isDragAllowed() const
247 252 {
248 253 return true;
249 254 }
250 255
251 256 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
252 257 {
253 258 if (highlighted) {
254 259 plot().setBackground(QBrush(QColor("#BBD5EE")));
255 260 }
256 261 else {
257 262 plot().setBackground(QBrush(Qt::white));
258 263 }
259 264
260 265 plot().update();
261 266 }
262 267
263 268 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
264 269 {
265 270 Q_UNUSED(event);
266 271
267 272 // Prevents that all variables will be removed from graph when it will be closed
268 273 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
269 274 emit variableAboutToBeRemoved(variableEntry.first);
270 275 }
271 276 }
272 277
273 278 void VisualizationGraphWidget::enterEvent(QEvent *event)
274 279 {
275 280 Q_UNUSED(event);
276 281 impl->m_RenderingDelegate->showGraphOverlay(true);
277 282 }
278 283
279 284 void VisualizationGraphWidget::leaveEvent(QEvent *event)
280 285 {
281 286 Q_UNUSED(event);
282 287 impl->m_RenderingDelegate->showGraphOverlay(false);
283 288 }
284 289
285 290 QCustomPlot &VisualizationGraphWidget::plot() noexcept
286 291 {
287 292 return *ui->widget;
288 293 }
289 294
290 295 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
291 296 {
292 297 QMenu graphMenu{};
293 298
294 299 // Iterates on variables (unique keys)
295 300 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
296 301 end = impl->m_VariableToPlotMultiMap.cend();
297 302 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
298 303 // 'Remove variable' action
299 304 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
300 305 [ this, var = it->first ]() { removeVariable(var); });
301 306 }
302 307
303 308 if (!graphMenu.isEmpty()) {
304 309 graphMenu.exec(QCursor::pos());
305 310 }
306 311 }
307 312
308 313 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
309 314 {
310 315 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
311 316 << QThread::currentThread()->objectName() << "DoAcqui"
312 317 << impl->m_DoAcquisition;
313 318
314 319 auto graphRange = SqpRange{t1.lower, t1.upper};
315 320 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
316 321
317 322 if (impl->m_DoAcquisition) {
318 323 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
319 324
320 325 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
321 326 end = impl->m_VariableToPlotMultiMap.end();
322 327 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
323 328 variableUnderGraphVector.push_back(it->first);
324 329 }
325 330 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
326 331 !impl->m_IsCalibration);
327 332
328 333 if (!impl->m_IsCalibration) {
329 334 qCDebug(LOG_VisualizationGraphWidget())
330 335 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
331 336 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
332 337 emit synchronize(graphRange, oldGraphRange);
333 338 }
334 339 }
335 340 }
336 341
337 342 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
338 343 {
339 344 // Handles plot rendering when mouse is moving
340 345 impl->m_RenderingDelegate->onMouseMove(event);
341 346
342 347 VisualizationDragWidget::mouseMoveEvent(event);
343 348 }
344 349
345 350 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
346 351 {
347 auto zoomOrientations = QFlags<Qt::Orientation>{};
352 auto value = event->angleDelta().x() + event->angleDelta().y();
353 if (value != 0) {
348 354
349 // Lambda that enables a zoom orientation if the key modifier related to this orientation
350 // has
351 // been pressed
352 auto enableOrientation
353 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
354 auto orientationEnabled = event->modifiers().testFlag(modifier);
355 zoomOrientations.setFlag(orientation, orientationEnabled);
356 };
357 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
358 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
355 auto direction = value > 0 ? 1.0 : -1.0;
356 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
357 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
358 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
359
360 auto zoomOrientations = QFlags<Qt::Orientation>{};
361 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
362 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
359 363
360 364 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
365
366 if (!isZoomX && !isZoomY) {
367 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
368 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
369
370 axis->setRange(axis->range() + diff);
371
372 if (plot().noAntialiasingOnDrag()) {
373 plot().setNotAntialiasedElements(QCP::aeAll);
374 }
375
376 plot().replot(QCustomPlot::rpQueuedReplot);
377 }
378 }
361 379 }
362 380
363 381 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
364 382 {
365 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
366
367 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
368
369 383 VisualizationDragWidget::mousePressEvent(event);
370 384 }
371 385
372 386 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
373 387 {
374 388 impl->m_IsCalibration = false;
375 389 }
376 390
377 391 void VisualizationGraphWidget::onDataCacheVariableUpdated()
378 392 {
379 393 auto graphRange = ui->widget->xAxis->range();
380 394 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
381 395
382 396 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
383 397 auto variable = variableEntry.first;
384 398 qCDebug(LOG_VisualizationGraphWidget())
385 399 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
386 400 qCDebug(LOG_VisualizationGraphWidget())
387 401 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
388 402 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
389 403 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
390 404 variable->range());
391 405 }
392 406 }
393 407 }
394 408
395 409 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
396 410 const SqpRange &range)
397 411 {
398 412 auto it = impl->m_VariableToPlotMultiMap.find(variable);
399 413 if (it != impl->m_VariableToPlotMultiMap.end()) {
400 414 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
401 415 }
402 416 }
General Comments 1
Under Review
author

Auto status change to "Under Review"

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