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