##// END OF EJS Templates
Activate the drop of a variable on an existing graph.
trabillard -
r858:fe864ba63490
parent child
Show More
@@ -1,484 +1,485
1 1 #include "Visualization/VisualizationZoneWidget.h"
2 2
3 3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 4 #include "Visualization/QCustomPlotSynchronizer.h"
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationWidget.h"
7 7 #include "ui_VisualizationZoneWidget.h"
8 8
9 9 #include "Common/MimeTypesDef.h"
10 10 #include "Common/VisualizationDef.h"
11 11
12 12 #include <Data/SqpRange.h>
13 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15
16 16 #include <Visualization/operations/FindVariableOperation.h>
17 17
18 18 #include <DragDropHelper.h>
19 19 #include <QUuid>
20 20 #include <SqpApplication.h>
21 21 #include <cmath>
22 22
23 23 #include <QLayout>
24 24
25 25 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
26 26
27 27 namespace {
28 28
29 29
30 30 /// Generates a default name for a new graph, according to the number of graphs already displayed in
31 31 /// the zone
32 32 QString defaultGraphName(const QLayout &layout)
33 33 {
34 34 auto count = 0;
35 35 for (auto i = 0; i < layout.count(); ++i) {
36 36 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
37 37 count++;
38 38 }
39 39 }
40 40
41 41 return QObject::tr("Graph %1").arg(count + 1);
42 42 }
43 43
44 44 /**
45 45 * Applies a function to all graphs of the zone represented by its layout
46 46 * @param layout the layout that contains graphs
47 47 * @param fun the function to apply to each graph
48 48 */
49 49 template <typename Fun>
50 50 void processGraphs(QLayout &layout, Fun fun)
51 51 {
52 52 for (auto i = 0; i < layout.count(); ++i) {
53 53 if (auto item = layout.itemAt(i)) {
54 54 if (auto visualizationGraphWidget
55 55 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
56 56 fun(*visualizationGraphWidget);
57 57 }
58 58 }
59 59 }
60 60 }
61 61
62 62 } // namespace
63 63
64 64 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
65 65
66 66 explicit VisualizationZoneWidgetPrivate()
67 67 : m_SynchronisationGroupId{QUuid::createUuid()},
68 68 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
69 69 {
70 70 }
71 71 QUuid m_SynchronisationGroupId;
72 72 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
73 73
74 74 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
75 75 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
76 76 VisualizationZoneWidget *zoneWidget);
77 77 };
78 78
79 79 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
80 80 : VisualizationDragWidget{parent},
81 81 ui{new Ui::VisualizationZoneWidget},
82 82 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
83 83 {
84 84 ui->setupUi(this);
85 85
86 86 ui->zoneNameLabel->setText(name);
87 87
88 88 ui->dragDropContainer->setAcceptedMimeTypes({MIME_TYPE_GRAPH, MIME_TYPE_VARIABLE_LIST});
89 ui->dragDropContainer->setMergeAllowedMimeTypes({MIME_TYPE_VARIABLE_LIST});
89 90 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
90 91 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
91 92 ui->dragDropContainer);
92 93 });
93 94 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this,
94 95 &VisualizationZoneWidget::dropMimeData);
95 96
96 97 // 'Close' options : widget is deleted when closed
97 98 setAttribute(Qt::WA_DeleteOnClose);
98 99 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
99 100 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
100 101
101 102 // Synchronisation id
102 103 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
103 104 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
104 105 }
105 106
106 107 VisualizationZoneWidget::~VisualizationZoneWidget()
107 108 {
108 109 delete ui;
109 110 }
110 111
111 112 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
112 113 {
113 114 // Synchronize new graph with others in the zone
114 115 impl->m_Synchronizer->addGraph(*graphWidget);
115 116
116 117 ui->dragDropContainer->addDragWidget(graphWidget);
117 118 }
118 119
119 120 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
120 121 {
121 122 // Synchronize new graph with others in the zone
122 123 impl->m_Synchronizer->addGraph(*graphWidget);
123 124
124 125 ui->dragDropContainer->insertDragWidget(index, graphWidget);
125 126 }
126 127
127 128 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
128 129 {
129 130 return createGraph(variable, -1);
130 131 }
131 132
132 133 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
133 134 int index)
134 135 {
135 136 auto graphWidget
136 137 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
137 138
138 139
139 140 // Set graph properties
140 141 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
141 142 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
142 143
143 144
144 145 // Lambda to synchronize zone widget
145 146 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
146 147 const SqpRange &oldGraphRange) {
147 148
148 149 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
149 150 auto frameLayout = ui->dragDropContainer->layout();
150 151 for (auto i = 0; i < frameLayout->count(); ++i) {
151 152 auto graphChild
152 153 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
153 154 if (graphChild && (graphChild != graphWidget)) {
154 155
155 156 auto graphChildRange = graphChild->graphRange();
156 157 switch (zoomType) {
157 158 case AcquisitionZoomType::ZoomIn: {
158 159 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
159 160 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
160 161 graphChildRange.m_TStart += deltaLeft;
161 162 graphChildRange.m_TEnd -= deltaRight;
162 163 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
163 164 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
164 165 << deltaLeft;
165 166 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
166 167 << deltaRight;
167 168 qCDebug(LOG_VisualizationZoneWidget())
168 169 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
169 170
170 171 break;
171 172 }
172 173
173 174 case AcquisitionZoomType::ZoomOut: {
174 175 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
175 176 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
176 177 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
177 178 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
178 179 << deltaLeft;
179 180 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
180 181 << deltaRight;
181 182 qCDebug(LOG_VisualizationZoneWidget())
182 183 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
183 184 graphChildRange.m_TStart -= deltaLeft;
184 185 graphChildRange.m_TEnd += deltaRight;
185 186 break;
186 187 }
187 188 case AcquisitionZoomType::PanRight: {
188 189 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
189 190 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
190 191 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
191 192 graphChildRange.m_TStart += deltaLeft;
192 193 graphChildRange.m_TEnd += deltaRight;
193 194 qCDebug(LOG_VisualizationZoneWidget())
194 195 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
195 196 break;
196 197 }
197 198 case AcquisitionZoomType::PanLeft: {
198 199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
199 200 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
200 201 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
201 202 graphChildRange.m_TStart -= deltaLeft;
202 203 graphChildRange.m_TEnd -= deltaRight;
203 204 break;
204 205 }
205 206 case AcquisitionZoomType::Unknown: {
206 207 qCDebug(LOG_VisualizationZoneWidget())
207 208 << tr("Impossible to synchronize: zoom type unknown");
208 209 break;
209 210 }
210 211 default:
211 212 qCCritical(LOG_VisualizationZoneWidget())
212 213 << tr("Impossible to synchronize: zoom type not take into account");
213 214 // No action
214 215 break;
215 216 }
216 217 graphChild->enableAcquisition(false);
217 218 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
218 219 << graphChild->graphRange();
219 220 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
220 221 << graphChildRange;
221 222 qCDebug(LOG_VisualizationZoneWidget())
222 223 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
223 224 graphChild->setGraphRange(graphChildRange);
224 225 graphChild->enableAcquisition(true);
225 226 }
226 227 }
227 228 };
228 229
229 230 // connection for synchronization
230 231 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
231 232 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
232 233 &VisualizationZoneWidget::onVariableAdded);
233 234 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
234 235 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
235 236
236 237 auto range = SqpRange{};
237 238
238 239 // Apply visitor to graph children
239 240 auto layout = ui->dragDropContainer->layout();
240 241 if (layout->count() > 0) {
241 242 // Case of a new graph in a existant zone
242 243 if (auto visualizationGraphWidget
243 244 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
244 245 range = visualizationGraphWidget->graphRange();
245 246 }
246 247 }
247 248 else {
248 249 // Case of a new graph as the first of the zone
249 250 range = variable->range();
250 251 }
251 252
252 253 this->insertGraph(index, graphWidget);
253 254
254 255 graphWidget->addVariable(variable, range);
255 256
256 257 // get y using variable range
257 258 if (auto dataSeries = variable->dataSeries()) {
258 259 dataSeries->lockRead();
259 260 auto valuesBounds
260 261 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
261 262 auto end = dataSeries->cend();
262 263 if (valuesBounds.first != end && valuesBounds.second != end) {
263 264 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
264 265
265 266 auto minValue = rangeValue(valuesBounds.first->minValue());
266 267 auto maxValue = rangeValue(valuesBounds.second->maxValue());
267 268
268 269 graphWidget->setYRange(SqpRange{minValue, maxValue});
269 270 }
270 271 dataSeries->unlock();
271 272 }
272 273
273 274 return graphWidget;
274 275 }
275 276
276 277 VisualizationGraphWidget *
277 278 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
278 279 {
279 280 if (variables.isEmpty()) {
280 281 return nullptr;
281 282 }
282 283
283 284 auto graphWidget = createGraph(variables.first(), index);
284 285 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
285 286 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
286 287 }
287 288
288 289 return graphWidget;
289 290 }
290 291
291 292 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
292 293 {
293 294 if (visitor) {
294 295 visitor->visitEnter(this);
295 296
296 297 // Apply visitor to graph children: widgets different from graphs are not visited (no
297 298 // action)
298 299 processGraphs(
299 300 *ui->dragDropContainer->layout(),
300 301 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
301 302
302 303 visitor->visitLeave(this);
303 304 }
304 305 else {
305 306 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
306 307 }
307 308 }
308 309
309 310 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
310 311 {
311 312 // A tab can always accomodate a variable
312 313 Q_UNUSED(variable);
313 314 return true;
314 315 }
315 316
316 317 bool VisualizationZoneWidget::contains(const Variable &variable) const
317 318 {
318 319 Q_UNUSED(variable);
319 320 return false;
320 321 }
321 322
322 323 QString VisualizationZoneWidget::name() const
323 324 {
324 325 return ui->zoneNameLabel->text();
325 326 }
326 327
327 328 QMimeData *VisualizationZoneWidget::mimeData() const
328 329 {
329 330 auto mimeData = new QMimeData;
330 331 mimeData->setData(MIME_TYPE_ZONE, QByteArray());
331 332
332 333 return mimeData;
333 334 }
334 335
335 336 bool VisualizationZoneWidget::isDragAllowed() const
336 337 {
337 338 return true;
338 339 }
339 340
340 341 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
341 342 {
342 343 // Closes graphs in the zone
343 344 processGraphs(*ui->dragDropContainer->layout(),
344 345 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
345 346
346 347 // Delete synchronization group from variable controller
347 348 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
348 349 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
349 350
350 351 QWidget::closeEvent(event);
351 352 }
352 353
353 354 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
354 355 {
355 356 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
356 357 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
357 358 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
358 359 }
359 360
360 361 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
361 362 {
362 363 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
363 364 Q_ARG(std::shared_ptr<Variable>, variable),
364 365 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
365 366 }
366 367
367 368 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
368 369 {
369 370 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
370 371 impl->dropGraph(index, this);
371 372 }
372 373 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
373 374 auto variables = sqpApp->variableController().variablesForMimeData(
374 375 mimeData->data(MIME_TYPE_VARIABLE_LIST));
375 376 impl->dropVariables(variables, index, this);
376 377 }
377 378 else {
378 379 qCWarning(LOG_VisualizationZoneWidget())
379 380 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
380 381 }
381 382 }
382 383
383 384 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
384 385 int index, VisualizationZoneWidget *zoneWidget)
385 386 {
386 387 auto &helper = sqpApp->dragDropHelper();
387 388
388 389 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
389 390 if (!graphWidget) {
390 391 qCWarning(LOG_VisualizationZoneWidget())
391 392 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
392 393 "found or invalid.");
393 394 Q_ASSERT(false);
394 395 return;
395 396 }
396 397
397 398 auto parentDragDropContainer
398 399 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
399 400 if (!parentDragDropContainer) {
400 401 qCWarning(LOG_VisualizationZoneWidget())
401 402 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
402 403 "the dropped graph is not found.");
403 404 Q_ASSERT(false);
404 405 return;
405 406 }
406 407
407 408 const auto &variables = graphWidget->variables();
408 409
409 410 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
410 411 // The drop didn't occur in the same zone
411 412
412 413 // Abort the requests for the variables (if any)
413 414 // Commented, because it's not sure if it's needed or not
414 415 // for (const auto& var : variables)
415 416 //{
416 417 // sqpApp->variableController().onAbortProgressRequested(var);
417 418 //}
418 419
419 420 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
420 421 auto nbGraph = parentDragDropContainer->countDragWidget();
421 422 if (nbGraph == 1) {
422 423 // This is the only graph in the previous zone, close the zone
423 424 previousParentZoneWidget->close();
424 425 }
425 426 else {
426 427 // Close the graph
427 428 graphWidget->close();
428 429 }
429 430
430 431 // Creates the new graph in the zone
431 432 zoneWidget->createGraph(variables, index);
432 433 }
433 434 else {
434 435 // The drop occurred in the same zone or the graph is empty
435 436 // Simple move of the graph, no variable operation associated
436 437 parentDragDropContainer->layout()->removeWidget(graphWidget);
437 438
438 439 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
439 440 // The graph is empty and dropped in a different zone.
440 441 // Take the range of the first graph in the zone (if existing).
441 442 auto layout = zoneWidget->ui->dragDropContainer->layout();
442 443 if (layout->count() > 0) {
443 444 if (auto visualizationGraphWidget
444 445 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
445 446 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
446 447 }
447 448 }
448 449 }
449 450
450 451 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
451 452 }
452 453 }
453 454
454 455 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
455 456 const QList<std::shared_ptr<Variable> > &variables, int index,
456 457 VisualizationZoneWidget *zoneWidget)
457 458 {
458 459 // Search for the top level VisualizationWidget
459 460 auto parent = zoneWidget->parentWidget();
460 461 while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) {
461 462 parent = parent->parentWidget();
462 463 }
463 464
464 465 if (!parent) {
465 466 qCWarning(LOG_VisualizationZoneWidget())
466 467 << tr("VisualizationZoneWidget::dropVariables, drop aborted, the parent "
467 468 "VisualizationWidget cannot be found.");
468 469 Q_ASSERT(false);
469 470 return;
470 471 }
471 472
472 473 auto visualizationWidget = static_cast<VisualizationWidget *>(parent);
473 474
474 475 // Search for the first variable which can be dropped
475 476 for (auto variable : variables) {
476 477 FindVariableOperation findVariableOperation{variable};
477 478 visualizationWidget->accept(&findVariableOperation);
478 479 auto variableContainers = findVariableOperation.result();
479 480 if (variableContainers.empty()) {
480 481 zoneWidget->createGraph(variable, index);
481 482 break;
482 483 }
483 484 }
484 485 }
General Comments 0
You need to be logged in to leave comments. Login now