##// END OF EJS Templates
Closes all graphs of the selected zone in graph mode
trabillard -
r1307:47a361805173
parent child
Show More
@@ -1,103 +1,106
1 1 #ifndef SCIQLOP_VISUALIZATIONZONEWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONZONEWIDGET_H
3 3
4 4 #include "Data/SqpRange.h"
5 5 #include "Visualization/IVisualizationWidget.h"
6 6 #include "Visualization/VisualizationDragWidget.h"
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QWidget>
10 10
11 11 #include <memory>
12 12
13 13 #include <Common/spimpl.h>
14 14
15 15 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationZoneWidget)
16 16
17 17 namespace Ui {
18 18 class VisualizationZoneWidget;
19 19 } // namespace Ui
20 20
21 21 class Variable;
22 22 class VisualizationGraphWidget;
23 23
24 24 class VisualizationZoneWidget : public VisualizationDragWidget, public IVisualizationWidget {
25 25 Q_OBJECT
26 26
27 27 public:
28 28 explicit VisualizationZoneWidget(const QString &name = {}, QWidget *parent = 0);
29 29 virtual ~VisualizationZoneWidget();
30 30
31 31 /// Sets the range of the zone, only works if there is at least one graph in the zone
32 32 /// Note: calibrations between graphs are lost.
33 33 void setZoneRange(const SqpRange &range);
34 34
35 35 /// Adds a graph widget
36 36 void addGraph(VisualizationGraphWidget *graphWidget);
37 37
38 38 /// Inserts a graph widget
39 39 void insertGraph(int index, VisualizationGraphWidget *graphWidget);
40 40
41 41 /**
42 42 * Creates a graph using a variable. The variable will be displayed in the new graph.
43 43 * The graph is added at the end.
44 44 * @param variable the variable for which to create the graph
45 45 * @return the pointer to the created graph
46 46 */
47 47 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable);
48 48
49 49 /**
50 50 * Creates a graph using a variable. The variable will be displayed in the new graph.
51 51 * The graph is inserted at the specified index.
52 52 * @param variable the variable for which to create the graph
53 53 * @param index The index where the graph should be inserted in the layout
54 54 * @return the pointer to the created graph
55 55 */
56 56 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable, int index);
57 57
58 58 /**
59 59 * Creates a graph using a list of variables. The variables will be displayed in the new graph.
60 60 * The graph is inserted at the specified index.
61 61 * @param variables List of variables to be added to the graph
62 62 * @param index The index where the graph should be inserted in the layout
63 63 * @return the pointer to the created graph
64 64 */
65 65 VisualizationGraphWidget *createGraph(const QList<std::shared_ptr<Variable> > variables,
66 66 int index);
67 67
68 68 /// Returns the first graph in the zone or nullptr if there is no graph inside
69 69 VisualizationGraphWidget *firstGraph() const;
70 70
71 /// Closes all graphes inside the zone
72 void closeAllGraphs();
73
71 74 // IVisualizationWidget interface
72 75 void accept(IVisualizationWidgetVisitor *visitor) override;
73 76 bool canDrop(const Variable &variable) const override;
74 77 bool contains(const Variable &variable) const override;
75 78 QString name() const override;
76 79
77 80 // VisualisationDragWidget
78 81 QMimeData *mimeData(const QPoint &position) const override;
79 82 bool isDragAllowed() const override;
80 83
81 84 void notifyMouseMoveInGraph(const QPointF &graphPosition, const QPointF &plotPosition,
82 85 VisualizationGraphWidget *graphWidget);
83 86 void notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget);
84 87
85 88 protected:
86 89 void closeEvent(QCloseEvent *event) override;
87 90
88 91 private:
89 92 Ui::VisualizationZoneWidget *ui;
90 93
91 94 class VisualizationZoneWidgetPrivate;
92 95 spimpl::unique_impl_ptr<VisualizationZoneWidgetPrivate> impl;
93 96
94 97 private slots:
95 98 void onVariableAdded(std::shared_ptr<Variable> variable);
96 99 /// Slot called when a variable is about to be removed from a graph contained in the zone
97 100 void onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable);
98 101
99 102 void dropMimeData(int index, const QMimeData *mimeData);
100 103 void dropMimeDataOnGraph(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
101 104 };
102 105
103 106 #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H
@@ -1,590 +1,593
1 1 #include "Catalogue/CatalogueEventsWidget.h"
2 2 #include "ui_CatalogueEventsWidget.h"
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Catalogue/CatalogueEventsModel.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <CatalogueDao.h>
8 8 #include <DBCatalogue.h>
9 9 #include <DBEventProduct.h>
10 10 #include <DataSource/DataSourceController.h>
11 11 #include <DataSource/DataSourceItem.h>
12 12 #include <SqpApplication.h>
13 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15 #include <Visualization/VisualizationGraphWidget.h>
16 16 #include <Visualization/VisualizationTabWidget.h>
17 17 #include <Visualization/VisualizationWidget.h>
18 18 #include <Visualization/VisualizationZoneWidget.h>
19 19
20 20 #include <QDialog>
21 21 #include <QDialogButtonBox>
22 22 #include <QListWidget>
23 23 #include <QMessageBox>
24 24
25 25 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
26 26
27 27 /// Fixed size of the validation column
28 28 const auto VALIDATION_COLUMN_SIZE = 35;
29 29
30 30 /// Percentage added to the range of a event when it is displayed
31 31 const auto EVENT_RANGE_MARGE = 30; // in %
32 32
33 33 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
34 34
35 35 CatalogueEventsModel *m_Model = nullptr;
36 36 QStringList m_ZonesForTimeMode;
37 37 QString m_ZoneForGraphMode;
38 38 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
39 39 bool m_AllEventDisplayed = false;
40 40 QVector<VisualizationGraphWidget *> m_CustomGraphs;
41 41
42 42 VisualizationWidget *m_VisualizationWidget = nullptr;
43 43
44 44 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
45 45 {
46 46 widget->ui->treeView->setSortingEnabled(false);
47 47 m_Model->setEvents(events);
48 48 widget->ui->treeView->setSortingEnabled(true);
49 49
50 50 for (auto event : events) {
51 51 if (sqpApp->catalogueController().eventHasChanges(event)) {
52 52 auto index = m_Model->indexOf(event);
53 53 widget->setEventChanges(event, true);
54 54 }
55 55 }
56 56 }
57 57
58 58 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
59 59 {
60 60 treeView->setSortingEnabled(false);
61 61 m_Model->addEvent(event);
62 62 treeView->setSortingEnabled(true);
63 63 }
64 64
65 65 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
66 66 {
67 67 treeView->setSortingEnabled(false);
68 68 m_Model->removeEvent(event);
69 69 treeView->setSortingEnabled(true);
70 70 }
71 71
72 72 QStringList getAvailableVisualizationZoneList() const
73 73 {
74 74 if (m_VisualizationWidget) {
75 75 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
76 76 return tab->availableZoneWidgets();
77 77 }
78 78 }
79 79
80 80 return QStringList{};
81 81 }
82 82
83 83 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
84 84 bool allowMultiSelection, const QPoint &location)
85 85 {
86 86 auto availableZones = getAvailableVisualizationZoneList();
87 87 if (availableZones.isEmpty()) {
88 88 return QStringList{};
89 89 }
90 90
91 91 QDialog d(parent, Qt::Tool);
92 92 d.setWindowTitle("Choose a zone");
93 93 auto layout = new QVBoxLayout{&d};
94 94 layout->setContentsMargins(0, 0, 0, 0);
95 95 auto listWidget = new QListWidget{&d};
96 96 layout->addWidget(listWidget);
97 97
98 98 QSet<QListWidgetItem *> checkedItems;
99 99 for (auto zone : availableZones) {
100 100 auto item = new QListWidgetItem{zone};
101 101 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
102 102 if (selectedZones.contains(zone)) {
103 103 item->setCheckState(Qt::Checked);
104 104 checkedItems << item;
105 105 }
106 106 else {
107 107 item->setCheckState(Qt::Unchecked);
108 108 }
109 109
110 110 listWidget->addItem(item);
111 111 }
112 112
113 113 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
114 114 layout->addWidget(buttonBox);
115 115
116 116 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
117 117 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
118 118
119 119 QObject::connect(listWidget, &QListWidget::itemChanged,
120 120 [&checkedItems, allowMultiSelection, listWidget](auto item) {
121 121 if (item->checkState() == Qt::Checked) {
122 122 if (!allowMultiSelection) {
123 123 for (auto checkedItem : checkedItems) {
124 124 listWidget->blockSignals(true);
125 125 checkedItem->setCheckState(Qt::Unchecked);
126 126 listWidget->blockSignals(false);
127 127 }
128 128
129 129 checkedItems.clear();
130 130 }
131 131 checkedItems << item;
132 132 }
133 133 else {
134 134 checkedItems.remove(item);
135 135 }
136 136 });
137 137
138 138 QStringList result;
139 139
140 140 d.setMinimumWidth(120);
141 141 d.resize(d.minimumSizeHint());
142 142 d.move(location);
143 143 if (d.exec() == QDialog::Accepted) {
144 144 for (auto item : checkedItems) {
145 145 result += item->text();
146 146 }
147 147 }
148 148 else {
149 149 result = selectedZones;
150 150 }
151 151
152 152 return result;
153 153 }
154 154
155 155 void updateForTimeMode(QTreeView *treeView)
156 156 {
157 157 auto selectedRows = treeView->selectionModel()->selectedRows();
158 158
159 159 if (selectedRows.count() == 1) {
160 160 auto event = m_Model->getEvent(selectedRows.first());
161 161 if (event) {
162 162 if (m_VisualizationWidget) {
163 163 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
164 164
165 165 for (auto zoneName : m_ZonesForTimeMode) {
166 166 if (auto zone = tab->getZoneWithName(zoneName)) {
167 167 SqpRange eventRange;
168 168 eventRange.m_TStart = event->getTStart();
169 169 eventRange.m_TEnd = event->getTEnd();
170 170 zone->setZoneRange(eventRange);
171 171 }
172 172 }
173 173 }
174 174 else {
175 175 qCWarning(LOG_CatalogueEventsWidget())
176 176 << "updateTimeZone: no tab found in the visualization";
177 177 }
178 178 }
179 179 else {
180 180 qCWarning(LOG_CatalogueEventsWidget())
181 181 << "updateTimeZone: visualization widget not found";
182 182 }
183 183 }
184 184 }
185 185 else {
186 186 qCWarning(LOG_CatalogueEventsWidget())
187 187 << "updateTimeZone: not compatible with multiple events selected";
188 188 }
189 189 }
190 190
191 191 QVector<SqpRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
192 192 {
193 193 // Retrieves the range of each product and the maximum size
194 194 QVector<SqpRange> graphRanges;
195 195 double maxDt = 0;
196 196 for (auto eventProduct : event->getEventProducts()) {
197 197 SqpRange eventRange;
198 198 eventRange.m_TStart = eventProduct.getTStart();
199 199 eventRange.m_TEnd = eventProduct.getTEnd();
200 200 graphRanges << eventRange;
201 201
202 202 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
203 203 if (dt > maxDt) {
204 204 maxDt = dt;
205 205 }
206 206 }
207 207
208 208 // Adds the marge
209 209 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
210 210
211 211 // Corrects the graph ranges so that they all have the same size
212 212 QVector<SqpRange> correctedGraphRanges;
213 213 for (auto range : graphRanges) {
214 214 auto dt = range.m_TEnd - range.m_TStart;
215 215 auto diff = qAbs((maxDt - dt) / 2.0);
216 216
217 217 SqpRange correctedRange;
218 218 correctedRange.m_TStart = range.m_TStart - diff;
219 219 correctedRange.m_TEnd = range.m_TEnd + diff;
220 220
221 221 correctedGraphRanges << correctedRange;
222 222 }
223 223
224 224 return correctedGraphRanges;
225 225 }
226 226
227 227 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
228 228 {
229 229 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
230 230 if (selectedRows.count() != 1) {
231 231 qCWarning(LOG_CatalogueEventsWidget())
232 232 << "updateGraphMode: not compatible with multiple events selected";
233 233 return;
234 234 }
235 235
236 236 if (!m_VisualizationWidget) {
237 237 qCWarning(LOG_CatalogueEventsWidget())
238 238 << "updateGraphMode: visualization widget not found";
239 239 return;
240 240 }
241 241
242 242 auto event = m_Model->getEvent(selectedRows.first());
243 243 if (!event) {
244 244 // A event product is probably selected
245 245 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
246 246 return;
247 247 }
248 248
249 249 auto tab = m_VisualizationWidget->currentTabWidget();
250 250 if (!tab) {
251 251 qCWarning(LOG_CatalogueEventsWidget())
252 252 << "updateGraphMode: no tab found in the visualization";
253 253 return;
254 254 }
255 255
256 256 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
257 257 if (!zone) {
258 258 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
259 259 return;
260 260 }
261 261
262 // Close the previous graph and delete the asociated variables
262 // Closes the previous graph and delete the asociated variables
263 263 for (auto graph : m_CustomGraphs) {
264 264 graph->close();
265 265 auto variables = graph->variables().toVector();
266 266
267 267 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
268 268 Qt::QueuedConnection,
269 269 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
270 270 }
271 271 m_CustomGraphs.clear();
272 272
273 // Closes the remaining graphs inside the zone
274 zone->closeAllGraphs();
275
273 276 // Calculates the range of each graph which will be created
274 277 auto graphRange = getGraphRanges(event);
275 278
276 279 // Loops through the event products and create the graph
277 280 auto itRange = graphRange.cbegin();
278 281 for (auto eventProduct : event->getEventProducts()) {
279 282 auto productId = eventProduct.getProductId();
280 283
281 284 auto range = *itRange;
282 285 ++itRange;
283 286
284 287 SqpRange productRange;
285 288 productRange.m_TStart = eventProduct.getTStart();
286 289 productRange.m_TEnd = eventProduct.getTEnd();
287 290
288 291 auto context = new QObject{catalogueEventWidget};
289 292 QObject::connect(
290 293 &sqpApp->variableController(), &VariableController::variableAdded, context,
291 294 [this, catalogueEventWidget, zone, context, event, range, productRange,
292 295 productId](auto variable) {
293 296
294 297 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
295 298 == productId) {
296 299 auto graph = zone->createGraph(variable);
297 300 graph->setAutoRangeOnVariableInitialization(false);
298 301
299 302 auto selectionZone
300 303 = graph->addSelectionZone(event->getName(), productRange);
301 304 emit catalogueEventWidget->selectionZoneAdded(event, productId,
302 305 selectionZone);
303 306 m_CustomGraphs << graph;
304 307
305 308 graph->setGraphRange(range, true);
306 309
307 310 // Removes the graph from the graph list if it is closed manually
308 311 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
309 312 [this, graph]() { m_CustomGraphs.removeAll(graph); });
310 313
311 314 delete context; // removes the connection
312 315 }
313 316 },
314 317 Qt::QueuedConnection);
315 318
316 319 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
317 320 "requestVariableFromProductIdKey", Qt::QueuedConnection,
318 321 Q_ARG(QString, productId));
319 322 }
320 323 }
321 324
322 325 void getSelectedItems(
323 326 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
324 327 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
325 328 {
326 329 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
327 330 auto itemType = m_Model->itemTypeOf(rowIndex);
328 331 if (itemType == CatalogueEventsModel::ItemType::Event) {
329 332 events << m_Model->getEvent(rowIndex);
330 333 }
331 334 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
332 335 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
333 336 m_Model->getEventProduct(rowIndex));
334 337 }
335 338 }
336 339 }
337 340 };
338 341
339 342 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
340 343 : QWidget(parent),
341 344 ui(new Ui::CatalogueEventsWidget),
342 345 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
343 346 {
344 347 ui->setupUi(this);
345 348
346 349 impl->m_Model = new CatalogueEventsModel{this};
347 350 ui->treeView->setModel(impl->m_Model);
348 351
349 352 ui->treeView->setSortingEnabled(true);
350 353 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
351 354 ui->treeView->setDragEnabled(true);
352 355
353 356 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
354 357 if (checked) {
355 358 ui->btnChart->setChecked(false);
356 359 impl->m_ZonesForTimeMode
357 360 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
358 361 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
359 362
360 363 impl->updateForTimeMode(ui->treeView);
361 364 }
362 365 });
363 366
364 367 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
365 368 if (checked) {
366 369 ui->btnTime->setChecked(false);
367 370 impl->m_ZoneForGraphMode
368 371 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
369 372 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
370 373 .value(0);
371 374
372 375 impl->updateForGraphMode(this);
373 376 }
374 377 });
375 378
376 379 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
377 380 QVector<std::shared_ptr<DBEvent> > events;
378 381 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
379 382 impl->getSelectedItems(ui->treeView, events, eventProducts);
380 383
381 384 if (!events.isEmpty() && eventProducts.isEmpty()) {
382 385
383 386 if (QMessageBox::warning(this, tr("Remove Event(s)"),
384 387 tr("The selected event(s) will be permanently removed "
385 388 "from the repository!\nAre you sure you want to continue?"),
386 389 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
387 390 == QMessageBox::Yes) {
388 391
389 392 for (auto event : events) {
390 393 sqpApp->catalogueController().removeEvent(event);
391 394 impl->removeEvent(event, ui->treeView);
392 395 }
393 396
394 397 emit this->eventsRemoved(events);
395 398 }
396 399 }
397 400 });
398 401
399 402 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
400 403 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
401 404 &CatalogueEventsWidget::emitSelection);
402 405
403 406 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
404 407 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
405 408 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
406 409 ui->btnChart->setEnabled(isNotMultiSelection);
407 410 ui->btnTime->setEnabled(isNotMultiSelection);
408 411
409 412 if (isNotMultiSelection && ui->btnTime->isChecked()) {
410 413 impl->updateForTimeMode(ui->treeView);
411 414 }
412 415 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
413 416 impl->updateForGraphMode(this);
414 417 }
415 418
416 419 QVector<std::shared_ptr<DBEvent> > events;
417 420 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
418 421 impl->getSelectedItems(ui->treeView, events, eventProducts);
419 422 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
420 423 });
421 424
422 425 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
423 426 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
424 427 QHeaderView::Stretch);
425 428 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
426 429 QHeaderView::Fixed);
427 430 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
428 431 QHeaderView::Interactive);
429 432 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
430 433 VALIDATION_COLUMN_SIZE);
431 434 ui->treeView->header()->setSortIndicatorShown(true);
432 435
433 436 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
434 437 auto allEvents = impl->m_Model->events();
435 438 for (auto event : allEvents) {
436 439 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
437 440 }
438 441 });
439 442
440 443 populateWithAllEvents();
441 444 }
442 445
443 446 CatalogueEventsWidget::~CatalogueEventsWidget()
444 447 {
445 448 delete ui;
446 449 }
447 450
448 451 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
449 452 {
450 453 impl->m_VisualizationWidget = visualization;
451 454 }
452 455
453 456 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
454 457 {
455 458 impl->addEvent(event, ui->treeView);
456 459 }
457 460
458 461 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
459 462 {
460 463 impl->m_Model->refreshEvent(event);
461 464
462 465 auto eventIndex = impl->m_Model->indexOf(event);
463 466 auto validationIndex
464 467 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
465 468
466 469 if (validationIndex.isValid()) {
467 470 if (hasChanges) {
468 471 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
469 472 auto widget = CatalogueExplorerHelper::buildValidationWidget(
470 473 ui->treeView,
471 474 [this, event]() {
472 475 sqpApp->catalogueController().saveEvent(event);
473 476 setEventChanges(event, false);
474 477 },
475 478 [this, event]() {
476 479 bool removed = false;
477 480 sqpApp->catalogueController().discardEvent(event, removed);
478 481 if (removed) {
479 482 impl->m_Model->removeEvent(event);
480 483 }
481 484 else {
482 485 setEventChanges(event, false);
483 486 impl->m_Model->refreshEvent(event, true);
484 487 }
485 488 emitSelection();
486 489 });
487 490 ui->treeView->setIndexWidget(validationIndex, widget);
488 491 }
489 492 }
490 493 else {
491 494 // Note: the widget is destroyed
492 495 ui->treeView->setIndexWidget(validationIndex, nullptr);
493 496 }
494 497 }
495 498 else {
496 499 qCWarning(LOG_CatalogueEventsWidget())
497 500 << "setEventChanges: the event is not displayed in the model.";
498 501 }
499 502 }
500 503
501 504 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
502 505 {
503 506 return impl->m_DisplayedCatalogues;
504 507 }
505 508
506 509 bool CatalogueEventsWidget::isAllEventsDisplayed() const
507 510 {
508 511 return impl->m_AllEventDisplayed;
509 512 }
510 513
511 514 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
512 515 {
513 516 return impl->m_Model->indexOf(event).isValid();
514 517 }
515 518
516 519 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
517 520 {
518 521 impl->m_Model->refreshEvent(event, true);
519 522 }
520 523
521 524 void CatalogueEventsWidget::populateWithCatalogues(
522 525 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
523 526 {
524 527 impl->m_DisplayedCatalogues = catalogues;
525 528 impl->m_AllEventDisplayed = false;
526 529
527 530 QSet<QUuid> eventIds;
528 531 QVector<std::shared_ptr<DBEvent> > events;
529 532
530 533 for (auto catalogue : catalogues) {
531 534 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
532 535 for (auto event : catalogueEvents) {
533 536 if (!eventIds.contains(event->getUniqId())) {
534 537 events << event;
535 538 eventIds.insert(event->getUniqId());
536 539 }
537 540 }
538 541 }
539 542
540 543 impl->setEvents(events, this);
541 544 }
542 545
543 546 void CatalogueEventsWidget::populateWithAllEvents()
544 547 {
545 548 impl->m_DisplayedCatalogues.clear();
546 549 impl->m_AllEventDisplayed = true;
547 550
548 551 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
549 552
550 553 QVector<std::shared_ptr<DBEvent> > events;
551 554 for (auto event : allEvents) {
552 555 events << event;
553 556 }
554 557
555 558 impl->setEvents(events, this);
556 559 }
557 560
558 561 void CatalogueEventsWidget::clear()
559 562 {
560 563 impl->m_DisplayedCatalogues.clear();
561 564 impl->m_AllEventDisplayed = false;
562 565 impl->setEvents({}, this);
563 566 }
564 567
565 568 void CatalogueEventsWidget::refresh()
566 569 {
567 570 if (isAllEventsDisplayed()) {
568 571 populateWithAllEvents();
569 572 }
570 573 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
571 574 populateWithCatalogues(impl->m_DisplayedCatalogues);
572 575 }
573 576 }
574 577
575 578 void CatalogueEventsWidget::emitSelection()
576 579 {
577 580 QVector<std::shared_ptr<DBEvent> > events;
578 581 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
579 582 impl->getSelectedItems(ui->treeView, events, eventProducts);
580 583
581 584 if (!events.isEmpty() && eventProducts.isEmpty()) {
582 585 emit eventsSelected(events);
583 586 }
584 587 else if (events.isEmpty() && !eventProducts.isEmpty()) {
585 588 emit eventProductsSelected(eventProducts);
586 589 }
587 590 else {
588 591 emit selectionCleared();
589 592 }
590 593 }
@@ -1,652 +1,658
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 <DataSource/DataSourceController.h>
14 14 #include <Time/TimeController.h>
15 15 #include <Variable/Variable.h>
16 16 #include <Variable/VariableController.h>
17 17
18 18 #include <Visualization/operations/FindVariableOperation.h>
19 19
20 20 #include <DragAndDrop/DragDropGuiController.h>
21 21 #include <QUuid>
22 22 #include <SqpApplication.h>
23 23 #include <cmath>
24 24
25 25 #include <QLayout>
26 26
27 27 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
28 28
29 29 namespace {
30 30
31 31 /**
32 32 * Applies a function to all graphs of the zone represented by its layout
33 33 * @param layout the layout that contains graphs
34 34 * @param fun the function to apply to each graph
35 35 */
36 36 template <typename Fun>
37 37 void processGraphs(QLayout &layout, Fun fun)
38 38 {
39 39 for (auto i = 0; i < layout.count(); ++i) {
40 40 if (auto item = layout.itemAt(i)) {
41 41 if (auto visualizationGraphWidget
42 42 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
43 43 fun(*visualizationGraphWidget);
44 44 }
45 45 }
46 46 }
47 47 }
48 48
49 49 /// Generates a default name for a new graph, according to the number of graphs already displayed in
50 50 /// the zone
51 51 QString defaultGraphName(QLayout &layout)
52 52 {
53 53 QSet<QString> existingNames;
54 54 processGraphs(
55 55 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
56 56
57 57 int zoneNum = 1;
58 58 QString name;
59 59 do {
60 60 name = QObject::tr("Graph ").append(QString::number(zoneNum));
61 61 ++zoneNum;
62 62 } while (existingNames.contains(name));
63 63
64 64 return name;
65 65 }
66 66
67 67 } // namespace
68 68
69 69 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
70 70
71 71 explicit VisualizationZoneWidgetPrivate()
72 72 : m_SynchronisationGroupId{QUuid::createUuid()},
73 73 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
74 74 {
75 75 }
76 76 QUuid m_SynchronisationGroupId;
77 77 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
78 78
79 79 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
80 80 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
81 81 VisualizationZoneWidget *zoneWidget);
82 82 void dropProducts(const QVariantList &productsData, int index,
83 83 VisualizationZoneWidget *zoneWidget);
84 84 };
85 85
86 86 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
87 87 : VisualizationDragWidget{parent},
88 88 ui{new Ui::VisualizationZoneWidget},
89 89 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
90 90 {
91 91 ui->setupUi(this);
92 92
93 93 ui->zoneNameLabel->setText(name);
94 94
95 95 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
96 96 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
97 97 VisualizationDragDropContainer::DropBehavior::Inserted);
98 98 ui->dragDropContainer->setMimeType(
99 99 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
100 100 ui->dragDropContainer->setMimeType(
101 101 MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
102 102 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
103 103 VisualizationDragDropContainer::DropBehavior::Merged);
104 104 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
105 105 VisualizationDragDropContainer::DropBehavior::Forbidden);
106 106 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
107 107 VisualizationDragDropContainer::DropBehavior::Forbidden);
108 108 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
109 109 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
110 110 ui->dragDropContainer);
111 111 });
112 112
113 113 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
114 114 if (!mimeData) {
115 115 return false;
116 116 }
117 117
118 118 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
119 119 auto variables = sqpApp->variableController().variablesForMimeData(
120 120 mimeData->data(MIME_TYPE_VARIABLE_LIST));
121 121
122 122 if (variables.count() != 1) {
123 123 return false;
124 124 }
125 125 auto variable = variables.first();
126 126
127 127 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
128 128 return graphWidget->canDrop(*variable);
129 129 }
130 130 }
131 131
132 132 return true;
133 133 };
134 134 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
135 135
136 136 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
137 137 &VisualizationZoneWidget::dropMimeData);
138 138 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
139 139 &VisualizationZoneWidget::dropMimeDataOnGraph);
140 140
141 141 // 'Close' options : widget is deleted when closed
142 142 setAttribute(Qt::WA_DeleteOnClose);
143 143 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
144 144 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
145 145
146 146 // Synchronisation id
147 147 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
148 148 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
149 149 }
150 150
151 151 VisualizationZoneWidget::~VisualizationZoneWidget()
152 152 {
153 153 delete ui;
154 154 }
155 155
156 156 void VisualizationZoneWidget::setZoneRange(const SqpRange &range)
157 157 {
158 158 if (auto graph = firstGraph()) {
159 159 graph->setGraphRange(range);
160 160 }
161 161 else {
162 162 qCWarning(LOG_VisualizationZoneWidget())
163 163 << tr("setZoneRange:Cannot set the range of an empty zone.");
164 164 }
165 165 }
166 166
167 167 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
168 168 {
169 169 // Synchronize new graph with others in the zone
170 170 impl->m_Synchronizer->addGraph(*graphWidget);
171 171
172 172 ui->dragDropContainer->addDragWidget(graphWidget);
173 173 }
174 174
175 175 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
176 176 {
177 177 // Synchronize new graph with others in the zone
178 178 impl->m_Synchronizer->addGraph(*graphWidget);
179 179
180 180 ui->dragDropContainer->insertDragWidget(index, graphWidget);
181 181 }
182 182
183 183 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
184 184 {
185 185 return createGraph(variable, -1);
186 186 }
187 187
188 188 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
189 189 int index)
190 190 {
191 191 auto graphWidget
192 192 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
193 193
194 194
195 195 // Set graph properties
196 196 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
197 197 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
198 198
199 199
200 200 // Lambda to synchronize zone widget
201 201 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
202 202 const SqpRange &oldGraphRange) {
203 203
204 204 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
205 205 auto frameLayout = ui->dragDropContainer->layout();
206 206 for (auto i = 0; i < frameLayout->count(); ++i) {
207 207 auto graphChild
208 208 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
209 209 if (graphChild && (graphChild != graphWidget)) {
210 210
211 211 auto graphChildRange = graphChild->graphRange();
212 212 switch (zoomType) {
213 213 case AcquisitionZoomType::ZoomIn: {
214 214 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
215 215 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
216 216 graphChildRange.m_TStart += deltaLeft;
217 217 graphChildRange.m_TEnd -= deltaRight;
218 218 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
219 219 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
220 220 << deltaLeft;
221 221 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
222 222 << deltaRight;
223 223 qCDebug(LOG_VisualizationZoneWidget())
224 224 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
225 225
226 226 break;
227 227 }
228 228
229 229 case AcquisitionZoomType::ZoomOut: {
230 230 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
231 231 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
232 232 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
233 233 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
234 234 << deltaLeft;
235 235 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
236 236 << deltaRight;
237 237 qCDebug(LOG_VisualizationZoneWidget())
238 238 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
239 239 graphChildRange.m_TStart -= deltaLeft;
240 240 graphChildRange.m_TEnd += deltaRight;
241 241 break;
242 242 }
243 243 case AcquisitionZoomType::PanRight: {
244 244 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
245 245 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
246 246 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
247 247 graphChildRange.m_TStart += deltaLeft;
248 248 graphChildRange.m_TEnd += deltaRight;
249 249 qCDebug(LOG_VisualizationZoneWidget())
250 250 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
251 251 break;
252 252 }
253 253 case AcquisitionZoomType::PanLeft: {
254 254 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
255 255 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
256 256 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
257 257 graphChildRange.m_TStart -= deltaLeft;
258 258 graphChildRange.m_TEnd -= deltaRight;
259 259 break;
260 260 }
261 261 case AcquisitionZoomType::Unknown: {
262 262 qCDebug(LOG_VisualizationZoneWidget())
263 263 << tr("Impossible to synchronize: zoom type unknown");
264 264 break;
265 265 }
266 266 default:
267 267 qCCritical(LOG_VisualizationZoneWidget())
268 268 << tr("Impossible to synchronize: zoom type not take into account");
269 269 // No action
270 270 break;
271 271 }
272 272 graphChild->setFlags(GraphFlag::DisableAll);
273 273 qCDebug(LOG_VisualizationZoneWidget())
274 274 << tr("TORM: Range before: ") << graphChild->graphRange();
275 275 qCDebug(LOG_VisualizationZoneWidget())
276 276 << tr("TORM: Range after : ") << graphChildRange;
277 277 qCDebug(LOG_VisualizationZoneWidget())
278 278 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
279 279 graphChild->setGraphRange(graphChildRange);
280 280 graphChild->setFlags(GraphFlag::EnableAll);
281 281 }
282 282 }
283 283 };
284 284
285 285 // connection for synchronization
286 286 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
287 287 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
288 288 &VisualizationZoneWidget::onVariableAdded);
289 289 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
290 290 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
291 291
292 292 auto range = SqpRange{};
293 293 if (auto firstGraph = this->firstGraph()) {
294 294 // Case of a new graph in a existant zone
295 295 range = firstGraph->graphRange();
296 296 }
297 297 else {
298 298 // Case of a new graph as the first of the zone
299 299 range = variable->range();
300 300 }
301 301
302 302 this->insertGraph(index, graphWidget);
303 303
304 304 graphWidget->addVariable(variable, range);
305 305 graphWidget->setYRange(variable);
306 306
307 307 return graphWidget;
308 308 }
309 309
310 310 VisualizationGraphWidget *
311 311 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
312 312 {
313 313 if (variables.isEmpty()) {
314 314 return nullptr;
315 315 }
316 316
317 317 auto graphWidget = createGraph(variables.first(), index);
318 318 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
319 319 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
320 320 }
321 321
322 322 return graphWidget;
323 323 }
324 324
325 325 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
326 326 {
327 327 VisualizationGraphWidget *firstGraph = nullptr;
328 328 auto layout = ui->dragDropContainer->layout();
329 329 if (layout->count() > 0) {
330 330 if (auto visualizationGraphWidget
331 331 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
332 332 firstGraph = visualizationGraphWidget;
333 333 }
334 334 }
335 335
336 336 return firstGraph;
337 337 }
338 338
339 void VisualizationZoneWidget::closeAllGraphs()
340 {
341 processGraphs(*ui->dragDropContainer->layout(),
342 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
343 }
344
339 345 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
340 346 {
341 347 if (visitor) {
342 348 visitor->visitEnter(this);
343 349
344 350 // Apply visitor to graph children: widgets different from graphs are not visited (no
345 351 // action)
346 352 processGraphs(
347 353 *ui->dragDropContainer->layout(),
348 354 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
349 355
350 356 visitor->visitLeave(this);
351 357 }
352 358 else {
353 359 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
354 360 }
355 361 }
356 362
357 363 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
358 364 {
359 365 // A tab can always accomodate a variable
360 366 Q_UNUSED(variable);
361 367 return true;
362 368 }
363 369
364 370 bool VisualizationZoneWidget::contains(const Variable &variable) const
365 371 {
366 372 Q_UNUSED(variable);
367 373 return false;
368 374 }
369 375
370 376 QString VisualizationZoneWidget::name() const
371 377 {
372 378 return ui->zoneNameLabel->text();
373 379 }
374 380
375 381 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
376 382 {
377 383 Q_UNUSED(position);
378 384
379 385 auto mimeData = new QMimeData;
380 386 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
381 387
382 388 if (auto firstGraph = this->firstGraph()) {
383 389 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
384 390 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
385 391 }
386 392
387 393 return mimeData;
388 394 }
389 395
390 396 bool VisualizationZoneWidget::isDragAllowed() const
391 397 {
392 398 return true;
393 399 }
394 400
395 401 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
396 402 const QPointF &plotPosition,
397 403 VisualizationGraphWidget *graphWidget)
398 404 {
399 405 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
400 406 VisualizationGraphWidget &processedGraph) {
401 407
402 408 switch (sqpApp->plotsCursorMode()) {
403 409 case SqpApplication::PlotsCursorMode::Vertical:
404 410 processedGraph.removeHorizontalCursor();
405 411 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
406 412 break;
407 413 case SqpApplication::PlotsCursorMode::Temporal:
408 414 processedGraph.addVerticalCursor(plotPosition.x());
409 415 processedGraph.removeHorizontalCursor();
410 416 break;
411 417 case SqpApplication::PlotsCursorMode::Horizontal:
412 418 processedGraph.removeVerticalCursor();
413 419 if (&processedGraph == graphWidget) {
414 420 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
415 421 }
416 422 else {
417 423 processedGraph.removeHorizontalCursor();
418 424 }
419 425 break;
420 426 case SqpApplication::PlotsCursorMode::Cross:
421 427 if (&processedGraph == graphWidget) {
422 428 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
423 429 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
424 430 }
425 431 else {
426 432 processedGraph.removeHorizontalCursor();
427 433 processedGraph.removeVerticalCursor();
428 434 }
429 435 break;
430 436 case SqpApplication::PlotsCursorMode::NoCursor:
431 437 processedGraph.removeHorizontalCursor();
432 438 processedGraph.removeVerticalCursor();
433 439 break;
434 440 }
435 441
436 442
437 443 });
438 444 }
439 445
440 446 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
441 447 {
442 448 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
443 449 processedGraph.removeHorizontalCursor();
444 450 processedGraph.removeVerticalCursor();
445 451 });
446 452 }
447 453
448 454 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
449 455 {
450 456 // Closes graphs in the zone
451 457 processGraphs(*ui->dragDropContainer->layout(),
452 458 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
453 459
454 460 // Delete synchronization group from variable controller
455 461 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
456 462 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
457 463
458 464 QWidget::closeEvent(event);
459 465 }
460 466
461 467 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
462 468 {
463 469 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
464 470 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
465 471 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
466 472 }
467 473
468 474 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
469 475 {
470 476 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
471 477 Q_ARG(std::shared_ptr<Variable>, variable),
472 478 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
473 479 }
474 480
475 481 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
476 482 {
477 483 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
478 484 impl->dropGraph(index, this);
479 485 }
480 486 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
481 487 auto variables = sqpApp->variableController().variablesForMimeData(
482 488 mimeData->data(MIME_TYPE_VARIABLE_LIST));
483 489 impl->dropVariables(variables, index, this);
484 490 }
485 491 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
486 492 auto products = sqpApp->dataSourceController().productsDataForMimeData(
487 493 mimeData->data(MIME_TYPE_PRODUCT_LIST));
488 494 impl->dropProducts(products, index, this);
489 495 }
490 496 else {
491 497 qCWarning(LOG_VisualizationZoneWidget())
492 498 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
493 499 }
494 500 }
495 501
496 502 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
497 503 const QMimeData *mimeData)
498 504 {
499 505 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
500 506 if (!graphWidget) {
501 507 qCWarning(LOG_VisualizationZoneWidget())
502 508 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
503 509 "drop aborted");
504 510 Q_ASSERT(false);
505 511 return;
506 512 }
507 513
508 514 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
509 515 auto variables = sqpApp->variableController().variablesForMimeData(
510 516 mimeData->data(MIME_TYPE_VARIABLE_LIST));
511 517 for (const auto &var : variables) {
512 518 graphWidget->addVariable(var, graphWidget->graphRange());
513 519 }
514 520 }
515 521 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
516 522 auto products = sqpApp->dataSourceController().productsDataForMimeData(
517 523 mimeData->data(MIME_TYPE_PRODUCT_LIST));
518 524
519 525 auto context = new QObject{this};
520 526 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
521 527 [this, graphWidget, context](auto variable) {
522 528 graphWidget->addVariable(variable, graphWidget->graphRange());
523 529 delete context; // removes the connection
524 530 },
525 531 Qt::QueuedConnection);
526 532
527 533 auto productData = products.first().toHash();
528 534 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
529 535 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
530 536 }
531 537 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
532 538 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
533 539 graphWidget->setGraphRange(range);
534 540 }
535 541 else {
536 542 qCWarning(LOG_VisualizationZoneWidget())
537 543 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
538 544 }
539 545 }
540 546
541 547 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
542 548 int index, VisualizationZoneWidget *zoneWidget)
543 549 {
544 550 auto &helper = sqpApp->dragDropGuiController();
545 551
546 552 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
547 553 if (!graphWidget) {
548 554 qCWarning(LOG_VisualizationZoneWidget())
549 555 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
550 556 "found or invalid.");
551 557 Q_ASSERT(false);
552 558 return;
553 559 }
554 560
555 561 auto parentDragDropContainer
556 562 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
557 563 if (!parentDragDropContainer) {
558 564 qCWarning(LOG_VisualizationZoneWidget())
559 565 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
560 566 "the dropped graph is not found.");
561 567 Q_ASSERT(false);
562 568 return;
563 569 }
564 570
565 571 const auto &variables = graphWidget->variables();
566 572
567 573 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
568 574 // The drop didn't occur in the same zone
569 575
570 576 // Abort the requests for the variables (if any)
571 577 // Commented, because it's not sure if it's needed or not
572 578 // for (const auto& var : variables)
573 579 //{
574 580 // sqpApp->variableController().onAbortProgressRequested(var);
575 581 //}
576 582
577 583 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
578 584 auto nbGraph = parentDragDropContainer->countDragWidget();
579 585 if (nbGraph == 1) {
580 586 // This is the only graph in the previous zone, close the zone
581 587 helper.delayedCloseWidget(previousParentZoneWidget);
582 588 }
583 589 else {
584 590 // Close the graph
585 591 helper.delayedCloseWidget(graphWidget);
586 592 }
587 593
588 594 // Creates the new graph in the zone
589 595 auto newGraphWidget = zoneWidget->createGraph(variables, index);
590 596 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
591 597 }
592 598 else {
593 599 // The drop occurred in the same zone or the graph is empty
594 600 // Simple move of the graph, no variable operation associated
595 601 parentDragDropContainer->layout()->removeWidget(graphWidget);
596 602
597 603 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
598 604 // The graph is empty and dropped in a different zone.
599 605 // Take the range of the first graph in the zone (if existing).
600 606 auto layout = zoneWidget->ui->dragDropContainer->layout();
601 607 if (layout->count() > 0) {
602 608 if (auto visualizationGraphWidget
603 609 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
604 610 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
605 611 }
606 612 }
607 613 }
608 614
609 615 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
610 616 }
611 617 }
612 618
613 619 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
614 620 const QList<std::shared_ptr<Variable> > &variables, int index,
615 621 VisualizationZoneWidget *zoneWidget)
616 622 {
617 623 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
618 624 // compatible variable here
619 625 if (variables.count() > 1) {
620 626 qCWarning(LOG_VisualizationZoneWidget())
621 627 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
622 628 "aborted.");
623 629 return;
624 630 }
625 631
626 632 zoneWidget->createGraph(variables, index);
627 633 }
628 634
629 635 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
630 636 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
631 637 {
632 638 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
633 639 // compatible variable here
634 640 if (productsData.count() != 1) {
635 641 qCWarning(LOG_VisualizationZoneWidget())
636 642 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
637 643 "aborted.");
638 644 return;
639 645 }
640 646
641 647 auto context = new QObject{zoneWidget};
642 648 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
643 649 [this, index, zoneWidget, context](auto variable) {
644 650 zoneWidget->createGraph(variable, index);
645 651 delete context; // removes the connection
646 652 },
647 653 Qt::QueuedConnection);
648 654
649 655 auto productData = productsData.first().toHash();
650 656 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
651 657 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
652 658 }
General Comments 0
You need to be logged in to leave comments. Login now