##// END OF EJS Templates
sync...
jeandet -
r1388:0dd8127294f3
parent child
Show More
@@ -1,1 +1,1
1 Subproject commit 65c558a3025bc4ab1701c51a15427ccbf9d86cd3
1 Subproject commit 139d5f0a7d64d6d6d1d20862d922cada26ed8df3
@@ -1,615 +1,615
1 #include "Catalogue/CatalogueEventsWidget.h"
1 #include "Catalogue/CatalogueEventsWidget.h"
2 #include "ui_CatalogueEventsWidget.h"
2 #include "ui_CatalogueEventsWidget.h"
3
3
4 #include <Catalogue/CatalogueController.h>
4 #include <Catalogue/CatalogueController.h>
5 #include <Catalogue/CatalogueEventsModel.h>
5 #include <Catalogue/CatalogueEventsModel.h>
6 #include <Catalogue/CatalogueExplorerHelper.h>
6 #include <Catalogue/CatalogueExplorerHelper.h>
7 #include <CatalogueDao.h>
7 #include <CatalogueDao.h>
8 #include <DBCatalogue.h>
8 #include <DBCatalogue.h>
9 #include <DBEventProduct.h>
9 #include <DBEventProduct.h>
10 #include <DataSource/DataSourceController.h>
10 #include <DataSource/DataSourceController.h>
11 #include <DataSource/DataSourceItem.h>
11 #include <DataSource/DataSourceItem.h>
12 #include <SqpApplication.h>
12 #include <SqpApplication.h>
13 #include <Variable/Variable.h>
13 #include <Variable/Variable.h>
14 #include <Variable/VariableController2.h>
14 #include <Variable/VariableController2.h>
15 #include <Visualization/VisualizationGraphWidget.h>
15 #include <Visualization/VisualizationGraphWidget.h>
16 #include <Visualization/VisualizationTabWidget.h>
16 #include <Visualization/VisualizationTabWidget.h>
17 #include <Visualization/VisualizationWidget.h>
17 #include <Visualization/VisualizationWidget.h>
18 #include <Visualization/VisualizationZoneWidget.h>
18 #include <Visualization/VisualizationZoneWidget.h>
19
19
20 #include <QActionGroup>
20 #include <QActionGroup>
21 #include <QDialog>
21 #include <QDialog>
22 #include <QDialogButtonBox>
22 #include <QDialogButtonBox>
23 #include <QKeyEvent>
23 #include <QKeyEvent>
24 #include <QListWidget>
24 #include <QListWidget>
25 #include <QMenu>
25 #include <QMenu>
26 #include <QMessageBox>
26 #include <QMessageBox>
27
27
28 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
28 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
29
29
30 /// Percentage added to the range of a event when it is displayed
30 /// Percentage added to the range of a event when it is displayed
31 const auto EVENT_RANGE_MARGE = 30; // in %
31 const auto EVENT_RANGE_MARGE = 30; // in %
32
32
33 const QString NEW_ZONE_TEXT = QStringLiteral("New Zone");
33 const QString NEW_ZONE_TEXT = QStringLiteral("New Zone");
34
34
35 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
35 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
36
36
37 CatalogueEventsModel *m_Model = nullptr;
37 CatalogueEventsModel *m_Model = nullptr;
38 QStringList m_ZonesForTimeMode;
38 QStringList m_ZonesForTimeMode;
39 QString m_ZoneForGraphMode;
39 QString m_ZoneForGraphMode;
40 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
40 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
41 bool m_AllEventDisplayed = false;
41 bool m_AllEventDisplayed = false;
42 QVector<VisualizationGraphWidget *> m_CustomGraphs;
42 QVector<VisualizationGraphWidget *> m_CustomGraphs;
43
43
44 VisualizationWidget *m_VisualizationWidget = nullptr;
44 VisualizationWidget *m_VisualizationWidget = nullptr;
45
45
46 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
46 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
47 {
47 {
48 widget->ui->treeView->setSortingEnabled(false);
48 widget->ui->treeView->setSortingEnabled(false);
49 m_Model->setSourceCatalogues(m_DisplayedCatalogues);
49 m_Model->setSourceCatalogues(m_DisplayedCatalogues);
50 m_Model->setEvents(events);
50 m_Model->setEvents(events);
51 widget->ui->treeView->setSortingEnabled(true);
51 widget->ui->treeView->setSortingEnabled(true);
52
52
53 for (auto event : events) {
53 for (auto event : events) {
54 if (sqpApp->catalogueController().eventHasChanges(event)) {
54 if (sqpApp->catalogueController().eventHasChanges(event)) {
55 auto index = m_Model->indexOf(event);
55 auto index = m_Model->indexOf(event);
56 widget->setEventChanges(event, true);
56 widget->setEventChanges(event, true);
57 }
57 }
58 }
58 }
59 }
59 }
60
60
61 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
61 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
62 {
62 {
63 treeView->setSortingEnabled(false);
63 treeView->setSortingEnabled(false);
64 m_Model->addEvent(event);
64 m_Model->addEvent(event);
65 treeView->setSortingEnabled(true);
65 treeView->setSortingEnabled(true);
66 }
66 }
67
67
68 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
68 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
69 {
69 {
70 treeView->setSortingEnabled(false);
70 treeView->setSortingEnabled(false);
71 m_Model->removeEvent(event);
71 m_Model->removeEvent(event);
72 treeView->setSortingEnabled(true);
72 treeView->setSortingEnabled(true);
73 }
73 }
74
74
75 QStringList getAvailableVisualizationZoneList() const
75 QStringList getAvailableVisualizationZoneList() const
76 {
76 {
77 if (m_VisualizationWidget) {
77 if (m_VisualizationWidget) {
78 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
78 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
79 return tab->availableZoneWidgets();
79 return tab->availableZoneWidgets();
80 }
80 }
81 }
81 }
82
82
83 return QStringList{};
83 return QStringList{};
84 }
84 }
85
85
86 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
86 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
87 bool allowMultiSelection, bool addNewZoneOption, const QPoint &location)
87 bool allowMultiSelection, bool addNewZoneOption, const QPoint &location)
88 {
88 {
89 auto availableZones = getAvailableVisualizationZoneList();
89 auto availableZones = getAvailableVisualizationZoneList();
90 if (!addNewZoneOption && availableZones.isEmpty()) {
90 if (!addNewZoneOption && availableZones.isEmpty()) {
91 return QStringList{};
91 return QStringList{};
92 }
92 }
93
93
94 QActionGroup actionGroup{parent};
94 QActionGroup actionGroup{parent};
95 actionGroup.setExclusive(!allowMultiSelection);
95 actionGroup.setExclusive(!allowMultiSelection);
96
96
97 QVector<QAction *> zoneActions;
97 QVector<QAction *> zoneActions;
98
98
99 QMenu selectionMenu{parent};
99 QMenu selectionMenu{parent};
100
100
101 if (addNewZoneOption) {
101 if (addNewZoneOption) {
102 availableZones.prepend(NEW_ZONE_TEXT);
102 availableZones.prepend(NEW_ZONE_TEXT);
103 }
103 }
104
104
105 selectionMenu.addSeparator();
105 selectionMenu.addSeparator();
106 for (auto zone : availableZones) {
106 for (auto zone : availableZones) {
107 auto zoneAction = selectionMenu.addAction(zone);
107 auto zoneAction = selectionMenu.addAction(zone);
108 zoneAction->setCheckable(true);
108 zoneAction->setCheckable(true);
109 zoneAction->setChecked(selectedZones.contains(zone));
109 zoneAction->setChecked(selectedZones.contains(zone));
110 actionGroup.addAction(zoneAction);
110 actionGroup.addAction(zoneAction);
111 zoneActions << zoneAction;
111 zoneActions << zoneAction;
112 }
112 }
113
113
114 auto resultAction = selectionMenu.exec(QCursor::pos());
114 auto resultAction = selectionMenu.exec(QCursor::pos());
115
115
116 QStringList result;
116 QStringList result;
117
117
118 if (resultAction == nullptr) {
118 if (resultAction == nullptr) {
119 result = selectedZones;
119 result = selectedZones;
120 }
120 }
121 else {
121 else {
122 for (auto zoneAction : zoneActions) {
122 for (auto zoneAction : zoneActions) {
123 if (zoneAction->isChecked()) {
123 if (zoneAction->isChecked()) {
124 result << zoneAction->text();
124 result << zoneAction->text();
125 }
125 }
126 }
126 }
127 }
127 }
128
128
129 return result;
129 return result;
130 }
130 }
131
131
132 void updateForTimeMode(QTreeView *treeView)
132 void updateForTimeMode(QTreeView *treeView)
133 {
133 {
134 auto selectedRows = treeView->selectionModel()->selectedRows();
134 auto selectedRows = treeView->selectionModel()->selectedRows();
135
135
136 if (selectedRows.count() == 1) {
136 if (selectedRows.count() == 1) {
137 auto event = m_Model->getEvent(selectedRows.first());
137 auto event = m_Model->getEvent(selectedRows.first());
138 if (event) {
138 if (event) {
139 if (m_VisualizationWidget) {
139 if (m_VisualizationWidget) {
140 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
140 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
141
141
142 for (auto zoneName : m_ZonesForTimeMode) {
142 for (auto zoneName : m_ZonesForTimeMode) {
143 if (auto zone = tab->getZoneWithName(zoneName)) {
143 if (auto zone = tab->getZoneWithName(zoneName)) {
144 DateTimeRange eventRange;
144 DateTimeRange eventRange;
145 eventRange.m_TStart = event->getTStart();
145 eventRange.m_TStart = event->getTStart();
146 eventRange.m_TEnd = event->getTEnd();
146 eventRange.m_TEnd = event->getTEnd();
147 zone->setZoneRange(eventRange);
147 zone->setZoneRange(eventRange);
148 }
148 }
149 }
149 }
150 }
150 }
151 else {
151 else {
152 qCWarning(LOG_CatalogueEventsWidget())
152 qCWarning(LOG_CatalogueEventsWidget())
153 << "updateTimeZone: no tab found in the visualization";
153 << "updateTimeZone: no tab found in the visualization";
154 }
154 }
155 }
155 }
156 else {
156 else {
157 qCWarning(LOG_CatalogueEventsWidget())
157 qCWarning(LOG_CatalogueEventsWidget())
158 << "updateTimeZone: visualization widget not found";
158 << "updateTimeZone: visualization widget not found";
159 }
159 }
160 }
160 }
161 }
161 }
162 else {
162 else {
163 qCWarning(LOG_CatalogueEventsWidget())
163 qCWarning(LOG_CatalogueEventsWidget())
164 << "updateTimeZone: not compatible with multiple events selected";
164 << "updateTimeZone: not compatible with multiple events selected";
165 }
165 }
166 }
166 }
167
167
168 QVector<DateTimeRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
168 QVector<DateTimeRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
169 {
169 {
170 // Retrieves the range of each product and the maximum size
170 // Retrieves the range of each product and the maximum size
171 QVector<DateTimeRange> graphRanges;
171 QVector<DateTimeRange> graphRanges;
172 double maxDt = 0;
172 double maxDt = 0;
173 for (auto eventProduct : event->getEventProducts()) {
173 for (auto eventProduct : event->getEventProducts()) {
174 DateTimeRange eventRange;
174 DateTimeRange eventRange;
175 eventRange.m_TStart = eventProduct.getTStart();
175 eventRange.m_TStart = eventProduct.getTStart();
176 eventRange.m_TEnd = eventProduct.getTEnd();
176 eventRange.m_TEnd = eventProduct.getTEnd();
177 graphRanges << eventRange;
177 graphRanges << eventRange;
178
178
179 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
179 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
180 if (dt > maxDt) {
180 if (dt > maxDt) {
181 maxDt = dt;
181 maxDt = dt;
182 }
182 }
183 }
183 }
184
184
185 // Adds the marge
185 // Adds the marge
186 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
186 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
187
187
188 // Corrects the graph ranges so that they all have the same size
188 // Corrects the graph ranges so that they all have the same size
189 QVector<DateTimeRange> correctedGraphRanges;
189 QVector<DateTimeRange> correctedGraphRanges;
190 for (auto range : graphRanges) {
190 for (auto range : graphRanges) {
191 auto dt = range.m_TEnd - range.m_TStart;
191 auto dt = range.m_TEnd - range.m_TStart;
192 auto diff = qAbs((maxDt - dt) / 2.0);
192 auto diff = qAbs((maxDt - dt) / 2.0);
193
193
194 DateTimeRange correctedRange;
194 DateTimeRange correctedRange;
195 correctedRange.m_TStart = range.m_TStart - diff;
195 correctedRange.m_TStart = range.m_TStart - diff;
196 correctedRange.m_TEnd = range.m_TEnd + diff;
196 correctedRange.m_TEnd = range.m_TEnd + diff;
197
197
198 correctedGraphRanges << correctedRange;
198 correctedGraphRanges << correctedRange;
199 }
199 }
200
200
201 return correctedGraphRanges;
201 return correctedGraphRanges;
202 }
202 }
203
203
204 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
204 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
205 {
205 {
206 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
206 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
207 if (selectedRows.count() != 1) {
207 if (selectedRows.count() != 1) {
208 qCWarning(LOG_CatalogueEventsWidget())
208 qCWarning(LOG_CatalogueEventsWidget())
209 << "updateGraphMode: not compatible with multiple events selected";
209 << "updateGraphMode: not compatible with multiple events selected";
210 return;
210 return;
211 }
211 }
212
212
213 if (!m_VisualizationWidget) {
213 if (!m_VisualizationWidget) {
214 qCWarning(LOG_CatalogueEventsWidget())
214 qCWarning(LOG_CatalogueEventsWidget())
215 << "updateGraphMode: visualization widget not found";
215 << "updateGraphMode: visualization widget not found";
216 return;
216 return;
217 }
217 }
218
218
219 auto event = m_Model->getEvent(selectedRows.first());
219 auto event = m_Model->getEvent(selectedRows.first());
220 if (!event) {
220 if (!event) {
221 // A event product is probably selected
221 // A event product is probably selected
222 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
222 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
223 return;
223 return;
224 }
224 }
225
225
226 auto tab = m_VisualizationWidget->currentTabWidget();
226 auto tab = m_VisualizationWidget->currentTabWidget();
227 if (!tab) {
227 if (!tab) {
228 qCWarning(LOG_CatalogueEventsWidget())
228 qCWarning(LOG_CatalogueEventsWidget())
229 << "updateGraphMode: no tab found in the visualization";
229 << "updateGraphMode: no tab found in the visualization";
230 return;
230 return;
231 }
231 }
232
232
233 auto isNewZone = m_ZoneForGraphMode == NEW_ZONE_TEXT;
233 auto isNewZone = m_ZoneForGraphMode == NEW_ZONE_TEXT;
234 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
234 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
235 if (!isNewZone && !zone) {
235 if (!isNewZone && !zone) {
236 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
236 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
237 return;
237 return;
238 }
238 }
239
239
240 // Closes the previous graph and delete the asociated variables
240 // Closes the previous graph and delete the asociated variables
241 for (auto graph : m_CustomGraphs) {
241 for (auto graph : m_CustomGraphs) {
242 graph->close();
242 graph->close();
243 auto variables = graph->variables();
243 auto variables = graph->variables();
244 for(const auto& variable:variables)
244 for(const auto& variable:variables)
245 sqpApp->variableController().deleteVariable(variable);
245 sqpApp->variableController().deleteVariable(variable);
246 }
246 }
247 m_CustomGraphs.clear();
247 m_CustomGraphs.clear();
248
248
249 // Closes the remaining graphs inside the zone
249 // Closes the remaining graphs inside the zone
250 if (zone) {
250 if (zone) {
251 zone->closeAllGraphs();
251 zone->closeAllGraphs();
252 }
252 }
253
253
254 // Creates the zone if needed
254 // Creates the zone if needed
255 if (isNewZone) {
255 if (isNewZone) {
256 zone = tab->createEmptyZone(0);
256 zone = tab->createEmptyZone(0);
257 m_ZoneForGraphMode = zone->name();
257 m_ZoneForGraphMode = zone->name();
258 }
258 }
259
259
260 // Calculates the range of each graph which will be created
260 // Calculates the range of each graph which will be created
261 auto graphRange = getGraphRanges(event);
261 auto graphRange = getGraphRanges(event);
262
262
263 // Loops through the event products and create the graph
263 // Loops through the event products and create the graph
264 auto itRange = graphRange.cbegin();
264 auto itRange = graphRange.cbegin();
265 for (auto eventProduct : event->getEventProducts()) {
265 for (auto eventProduct : event->getEventProducts()) {
266 auto productId = eventProduct.getProductId();
266 auto productId = eventProduct.getProductId();
267
267
268 auto range = *itRange;
268 auto range = *itRange;
269 ++itRange;
269 ++itRange;
270
270
271 DateTimeRange productRange;
271 DateTimeRange productRange;
272 productRange.m_TStart = eventProduct.getTStart();
272 productRange.m_TStart = eventProduct.getTStart();
273 productRange.m_TEnd = eventProduct.getTEnd();
273 productRange.m_TEnd = eventProduct.getTEnd();
274
274
275 auto context = new QObject{catalogueEventWidget};
275 auto context = new QObject{catalogueEventWidget};
276 QObject::connect(
276 QObject::connect(
277 &sqpApp->variableController(), &VariableController2::variableAdded, context,
277 &sqpApp->variableController(), &VariableController2::variableAdded, context,
278 [this, catalogueEventWidget, zone, context, event, range, productRange,
278 [this, catalogueEventWidget, zone, context, event, range, productRange,
279 productId](auto variable) {
279 productId](auto variable) {
280
280
281 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
281 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
282 == productId) {
282 == productId) {
283 auto graph = zone->createGraph(variable);
283 auto graph = zone->createGraph(variable);
284 graph->setAutoRangeOnVariableInitialization(false);
284 graph->setAutoRangeOnVariableInitialization(false);
285
285
286 auto selectionZone
286 auto selectionZone
287 = graph->addSelectionZone(event->getName(), productRange);
287 = graph->addSelectionZone(event->getName(), productRange);
288 emit catalogueEventWidget->selectionZoneAdded(event, productId,
288 emit catalogueEventWidget->selectionZoneAdded(event, productId,
289 selectionZone);
289 selectionZone);
290 m_CustomGraphs << graph;
290 m_CustomGraphs << graph;
291
291
292 graph->setGraphRange(range, true);
292 graph->setGraphRange(range, true, true);
293
293
294 // Removes the graph from the graph list if it is closed manually
294 // Removes the graph from the graph list if it is closed manually
295 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
295 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
296 [this, graph]() { m_CustomGraphs.removeAll(graph); });
296 [this, graph]() { m_CustomGraphs.removeAll(graph); });
297
297
298 delete context; // removes the connection
298 delete context; // removes the connection
299 }
299 }
300 },
300 },
301 Qt::QueuedConnection);
301 Qt::QueuedConnection);
302
302
303 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
303 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
304 "requestVariableFromProductIdKey", Qt::QueuedConnection,
304 "requestVariableFromProductIdKey", Qt::QueuedConnection,
305 Q_ARG(QString, productId));
305 Q_ARG(QString, productId));
306 }
306 }
307 }
307 }
308
308
309 void getSelectedItems(
309 void getSelectedItems(
310 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
310 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
311 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
311 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
312 {
312 {
313 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
313 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
314 auto itemType = m_Model->itemTypeOf(rowIndex);
314 auto itemType = m_Model->itemTypeOf(rowIndex);
315 if (itemType == CatalogueEventsModel::ItemType::Event) {
315 if (itemType == CatalogueEventsModel::ItemType::Event) {
316 events << m_Model->getEvent(rowIndex);
316 events << m_Model->getEvent(rowIndex);
317 }
317 }
318 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
318 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
319 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
319 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
320 m_Model->getEventProduct(rowIndex));
320 m_Model->getEventProduct(rowIndex));
321 }
321 }
322 }
322 }
323 }
323 }
324 };
324 };
325
325
326 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
326 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
327 : QWidget(parent),
327 : QWidget(parent),
328 ui(new Ui::CatalogueEventsWidget),
328 ui(new Ui::CatalogueEventsWidget),
329 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
329 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
330 {
330 {
331 ui->setupUi(this);
331 ui->setupUi(this);
332
332
333 impl->m_Model = new CatalogueEventsModel{this};
333 impl->m_Model = new CatalogueEventsModel{this};
334 ui->treeView->setModel(impl->m_Model);
334 ui->treeView->setModel(impl->m_Model);
335
335
336 ui->treeView->setSortingEnabled(true);
336 ui->treeView->setSortingEnabled(true);
337 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
337 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
338 ui->treeView->setDragEnabled(true);
338 ui->treeView->setDragEnabled(true);
339
339
340
340
341 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
341 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
342 if (checked) {
342 if (checked) {
343 ui->btnChart->setChecked(false);
343 ui->btnChart->setChecked(false);
344 impl->m_ZonesForTimeMode
344 impl->m_ZonesForTimeMode
345 = impl->selectZone(this, impl->m_ZonesForTimeMode, true, false,
345 = impl->selectZone(this, impl->m_ZonesForTimeMode, true, false,
346 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
346 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
347
347
348 impl->updateForTimeMode(ui->treeView);
348 impl->updateForTimeMode(ui->treeView);
349 }
349 }
350 });
350 });
351
351
352 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
352 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
353 if (checked) {
353 if (checked) {
354 ui->btnTime->setChecked(false);
354 ui->btnTime->setChecked(false);
355
355
356 impl->m_ZoneForGraphMode
356 impl->m_ZoneForGraphMode
357 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false, true,
357 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false, true,
358 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
358 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
359 .value(0);
359 .value(0);
360
360
361 impl->updateForGraphMode(this);
361 impl->updateForGraphMode(this);
362 }
362 }
363 });
363 });
364
364
365 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
365 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
366 QVector<std::shared_ptr<DBEvent> > events;
366 QVector<std::shared_ptr<DBEvent> > events;
367 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
367 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
368 impl->getSelectedItems(ui->treeView, events, eventProducts);
368 impl->getSelectedItems(ui->treeView, events, eventProducts);
369
369
370 if (!events.isEmpty() && eventProducts.isEmpty()) {
370 if (!events.isEmpty() && eventProducts.isEmpty()) {
371
371
372 auto canRemoveEvent
372 auto canRemoveEvent
373 = !this->isAllEventsDisplayed()
373 = !this->isAllEventsDisplayed()
374 || (QMessageBox::warning(
374 || (QMessageBox::warning(
375 this, tr("Remove Event(s)"),
375 this, tr("Remove Event(s)"),
376 tr("The selected event(s) will be permanently removed "
376 tr("The selected event(s) will be permanently removed "
377 "from the repository!\nAre you sure you want to continue?"),
377 "from the repository!\nAre you sure you want to continue?"),
378 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
378 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
379 == QMessageBox::Yes);
379 == QMessageBox::Yes);
380
380
381 if (canRemoveEvent) {
381 if (canRemoveEvent) {
382 for (auto event : events) {
382 for (auto event : events) {
383 if (this->isAllEventsDisplayed()) {
383 if (this->isAllEventsDisplayed()) {
384 sqpApp->catalogueController().removeEvent(event);
384 sqpApp->catalogueController().removeEvent(event);
385 impl->removeEvent(event, ui->treeView);
385 impl->removeEvent(event, ui->treeView);
386 }
386 }
387 else {
387 else {
388 QVector<std::shared_ptr<DBCatalogue> > modifiedCatalogues;
388 QVector<std::shared_ptr<DBCatalogue> > modifiedCatalogues;
389 for (auto catalogue : this->displayedCatalogues()) {
389 for (auto catalogue : this->displayedCatalogues()) {
390 if (catalogue->removeEvent(event->getUniqId())) {
390 if (catalogue->removeEvent(event->getUniqId())) {
391 sqpApp->catalogueController().updateCatalogue(catalogue);
391 sqpApp->catalogueController().updateCatalogue(catalogue);
392 modifiedCatalogues << catalogue;
392 modifiedCatalogues << catalogue;
393 }
393 }
394 }
394 }
395 if (!modifiedCatalogues.empty()) {
395 if (!modifiedCatalogues.empty()) {
396 emit eventCataloguesModified(modifiedCatalogues);
396 emit eventCataloguesModified(modifiedCatalogues);
397 }
397 }
398 }
398 }
399 impl->m_Model->removeEvent(event);
399 impl->m_Model->removeEvent(event);
400 }
400 }
401
401
402
402
403 emit this->eventsRemoved(events);
403 emit this->eventsRemoved(events);
404 }
404 }
405 }
405 }
406 });
406 });
407
407
408 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
408 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
409 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
409 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
410 &CatalogueEventsWidget::emitSelection);
410 &CatalogueEventsWidget::emitSelection);
411
411
412 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
412 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
413 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
413 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
414 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
414 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
415 ui->btnChart->setEnabled(isNotMultiSelection);
415 ui->btnChart->setEnabled(isNotMultiSelection);
416 ui->btnTime->setEnabled(isNotMultiSelection);
416 ui->btnTime->setEnabled(isNotMultiSelection);
417
417
418 if (isNotMultiSelection && ui->btnTime->isChecked()) {
418 if (isNotMultiSelection && ui->btnTime->isChecked()) {
419 impl->updateForTimeMode(ui->treeView);
419 impl->updateForTimeMode(ui->treeView);
420 }
420 }
421 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
421 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
422 impl->updateForGraphMode(this);
422 impl->updateForGraphMode(this);
423 }
423 }
424
424
425 QVector<std::shared_ptr<DBEvent> > events;
425 QVector<std::shared_ptr<DBEvent> > events;
426 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
426 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
427 impl->getSelectedItems(ui->treeView, events, eventProducts);
427 impl->getSelectedItems(ui->treeView, events, eventProducts);
428 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
428 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
429 });
429 });
430
430
431 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
431 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
432 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
432 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
433 QHeaderView::Stretch);
433 QHeaderView::Stretch);
434 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
434 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
435 QHeaderView::ResizeToContents);
435 QHeaderView::ResizeToContents);
436 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
436 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
437 QHeaderView::Interactive);
437 QHeaderView::Interactive);
438 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TStart,
438 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TStart,
439 QHeaderView::ResizeToContents);
439 QHeaderView::ResizeToContents);
440 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TEnd,
440 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TEnd,
441 QHeaderView::ResizeToContents);
441 QHeaderView::ResizeToContents);
442 ui->treeView->header()->setSortIndicatorShown(true);
442 ui->treeView->header()->setSortIndicatorShown(true);
443
443
444 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
444 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
445 auto allEvents = impl->m_Model->events();
445 auto allEvents = impl->m_Model->events();
446 for (auto event : allEvents) {
446 for (auto event : allEvents) {
447 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
447 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
448 }
448 }
449 });
449 });
450
450
451 populateWithAllEvents();
451 populateWithAllEvents();
452 }
452 }
453
453
454 CatalogueEventsWidget::~CatalogueEventsWidget()
454 CatalogueEventsWidget::~CatalogueEventsWidget()
455 {
455 {
456 delete ui;
456 delete ui;
457 }
457 }
458
458
459 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
459 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
460 {
460 {
461 impl->m_VisualizationWidget = visualization;
461 impl->m_VisualizationWidget = visualization;
462 }
462 }
463
463
464 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
464 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
465 {
465 {
466 impl->addEvent(event, ui->treeView);
466 impl->addEvent(event, ui->treeView);
467 }
467 }
468
468
469 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
469 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
470 {
470 {
471 impl->m_Model->refreshEvent(event);
471 impl->m_Model->refreshEvent(event);
472
472
473 auto eventIndex = impl->m_Model->indexOf(event);
473 auto eventIndex = impl->m_Model->indexOf(event);
474 auto validationIndex
474 auto validationIndex
475 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
475 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
476
476
477 if (validationIndex.isValid()) {
477 if (validationIndex.isValid()) {
478 if (hasChanges) {
478 if (hasChanges) {
479 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
479 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
480 auto widget = CatalogueExplorerHelper::buildValidationWidget(
480 auto widget = CatalogueExplorerHelper::buildValidationWidget(
481 ui->treeView,
481 ui->treeView,
482 [this, event]() {
482 [this, event]() {
483 sqpApp->catalogueController().saveEvent(event);
483 sqpApp->catalogueController().saveEvent(event);
484 setEventChanges(event, false);
484 setEventChanges(event, false);
485 },
485 },
486 [this, event]() {
486 [this, event]() {
487 bool removed = false;
487 bool removed = false;
488 sqpApp->catalogueController().discardEvent(event, removed);
488 sqpApp->catalogueController().discardEvent(event, removed);
489 if (removed) {
489 if (removed) {
490 impl->m_Model->removeEvent(event);
490 impl->m_Model->removeEvent(event);
491 }
491 }
492 else {
492 else {
493 setEventChanges(event, false);
493 setEventChanges(event, false);
494 impl->m_Model->refreshEvent(event, true);
494 impl->m_Model->refreshEvent(event, true);
495 }
495 }
496 emitSelection();
496 emitSelection();
497 });
497 });
498 ui->treeView->setIndexWidget(validationIndex, widget);
498 ui->treeView->setIndexWidget(validationIndex, widget);
499 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
499 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
500 QHeaderView::ResizeToContents);
500 QHeaderView::ResizeToContents);
501 }
501 }
502 }
502 }
503 else {
503 else {
504 // Note: the widget is destroyed
504 // Note: the widget is destroyed
505 ui->treeView->setIndexWidget(validationIndex, nullptr);
505 ui->treeView->setIndexWidget(validationIndex, nullptr);
506 }
506 }
507 }
507 }
508 else {
508 else {
509 qCWarning(LOG_CatalogueEventsWidget())
509 qCWarning(LOG_CatalogueEventsWidget())
510 << "setEventChanges: the event is not displayed in the model.";
510 << "setEventChanges: the event is not displayed in the model.";
511 }
511 }
512 }
512 }
513
513
514 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
514 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
515 {
515 {
516 return impl->m_DisplayedCatalogues;
516 return impl->m_DisplayedCatalogues;
517 }
517 }
518
518
519 bool CatalogueEventsWidget::isAllEventsDisplayed() const
519 bool CatalogueEventsWidget::isAllEventsDisplayed() const
520 {
520 {
521 return impl->m_AllEventDisplayed;
521 return impl->m_AllEventDisplayed;
522 }
522 }
523
523
524 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
524 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
525 {
525 {
526 return impl->m_Model->indexOf(event).isValid();
526 return impl->m_Model->indexOf(event).isValid();
527 }
527 }
528
528
529 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
529 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
530 {
530 {
531 impl->m_Model->refreshEvent(event, true);
531 impl->m_Model->refreshEvent(event, true);
532 }
532 }
533
533
534 void CatalogueEventsWidget::populateWithCatalogues(
534 void CatalogueEventsWidget::populateWithCatalogues(
535 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
535 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
536 {
536 {
537 impl->m_DisplayedCatalogues = catalogues;
537 impl->m_DisplayedCatalogues = catalogues;
538 impl->m_AllEventDisplayed = false;
538 impl->m_AllEventDisplayed = false;
539
539
540 QSet<QUuid> eventIds;
540 QSet<QUuid> eventIds;
541 QVector<std::shared_ptr<DBEvent> > events;
541 QVector<std::shared_ptr<DBEvent> > events;
542
542
543 for (auto catalogue : catalogues) {
543 for (auto catalogue : catalogues) {
544 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
544 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
545 for (auto event : catalogueEvents) {
545 for (auto event : catalogueEvents) {
546 if (!eventIds.contains(event->getUniqId())) {
546 if (!eventIds.contains(event->getUniqId())) {
547 events << event;
547 events << event;
548 eventIds.insert(event->getUniqId());
548 eventIds.insert(event->getUniqId());
549 }
549 }
550 }
550 }
551 }
551 }
552
552
553 impl->setEvents(events, this);
553 impl->setEvents(events, this);
554 }
554 }
555
555
556 void CatalogueEventsWidget::populateWithAllEvents()
556 void CatalogueEventsWidget::populateWithAllEvents()
557 {
557 {
558 impl->m_DisplayedCatalogues.clear();
558 impl->m_DisplayedCatalogues.clear();
559 impl->m_AllEventDisplayed = true;
559 impl->m_AllEventDisplayed = true;
560
560
561 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
561 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
562
562
563 QVector<std::shared_ptr<DBEvent> > events;
563 QVector<std::shared_ptr<DBEvent> > events;
564 for (auto event : allEvents) {
564 for (auto event : allEvents) {
565 events << event;
565 events << event;
566 }
566 }
567
567
568 impl->setEvents(events, this);
568 impl->setEvents(events, this);
569 }
569 }
570
570
571 void CatalogueEventsWidget::clear()
571 void CatalogueEventsWidget::clear()
572 {
572 {
573 impl->m_DisplayedCatalogues.clear();
573 impl->m_DisplayedCatalogues.clear();
574 impl->m_AllEventDisplayed = false;
574 impl->m_AllEventDisplayed = false;
575 impl->setEvents({}, this);
575 impl->setEvents({}, this);
576 }
576 }
577
577
578 void CatalogueEventsWidget::refresh()
578 void CatalogueEventsWidget::refresh()
579 {
579 {
580 if (isAllEventsDisplayed()) {
580 if (isAllEventsDisplayed()) {
581 populateWithAllEvents();
581 populateWithAllEvents();
582 }
582 }
583 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
583 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
584 populateWithCatalogues(impl->m_DisplayedCatalogues);
584 populateWithCatalogues(impl->m_DisplayedCatalogues);
585 }
585 }
586 }
586 }
587
587
588 void CatalogueEventsWidget::emitSelection()
588 void CatalogueEventsWidget::emitSelection()
589 {
589 {
590 QVector<std::shared_ptr<DBEvent> > events;
590 QVector<std::shared_ptr<DBEvent> > events;
591 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
591 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
592 impl->getSelectedItems(ui->treeView, events, eventProducts);
592 impl->getSelectedItems(ui->treeView, events, eventProducts);
593
593
594 if (!events.isEmpty() && eventProducts.isEmpty()) {
594 if (!events.isEmpty() && eventProducts.isEmpty()) {
595 emit eventsSelected(events);
595 emit eventsSelected(events);
596 }
596 }
597 else if (events.isEmpty() && !eventProducts.isEmpty()) {
597 else if (events.isEmpty() && !eventProducts.isEmpty()) {
598 emit eventProductsSelected(eventProducts);
598 emit eventProductsSelected(eventProducts);
599 }
599 }
600 else {
600 else {
601 emit selectionCleared();
601 emit selectionCleared();
602 }
602 }
603 }
603 }
604
604
605
605
606 void CatalogueEventsWidget::keyPressEvent(QKeyEvent *event)
606 void CatalogueEventsWidget::keyPressEvent(QKeyEvent *event)
607 {
607 {
608 switch (event->key()) {
608 switch (event->key()) {
609 case Qt::Key_Delete: {
609 case Qt::Key_Delete: {
610 ui->btnRemove->click();
610 ui->btnRemove->click();
611 }
611 }
612 default:
612 default:
613 break;
613 break;
614 }
614 }
615 }
615 }
@@ -1,1466 +1,1467
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
13
13
14 #include <Actions/ActionsGuiController.h>
14 #include <Actions/ActionsGuiController.h>
15 #include <Actions/FilteringAction.h>
15 #include <Actions/FilteringAction.h>
16 #include <Common/MimeTypesDef.h>
16 #include <Common/MimeTypesDef.h>
17 #include <Common/containers.h>
17 #include <Common/containers.h>
18 #include <Data/ArrayData.h>
18 #include <Data/ArrayData.h>
19 #include <Data/IDataSeries.h>
19 #include <Data/IDataSeries.h>
20 #include <Data/SpectrogramSeries.h>
20 #include <Data/SpectrogramSeries.h>
21 #include <DragAndDrop/DragDropGuiController.h>
21 #include <DragAndDrop/DragDropGuiController.h>
22 #include <Settings/SqpSettingsDefs.h>
22 #include <Settings/SqpSettingsDefs.h>
23 #include <SqpApplication.h>
23 #include <SqpApplication.h>
24 #include <Time/TimeController.h>
24 #include <Time/TimeController.h>
25 #include <Variable/Variable.h>
25 #include <Variable/Variable.h>
26 #include <Variable/VariableController2.h>
26 #include <Variable/VariableController2.h>
27 #include <Data/DateTimeRangeHelper.h>
27 #include <Data/DateTimeRangeHelper.h>
28
28
29 #include <unordered_map>
29 #include <unordered_map>
30
30
31 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
31 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
32
32
33 namespace {
33 namespace {
34
34
35 /// Key pressed to enable drag&drop in all modes
35 /// Key pressed to enable drag&drop in all modes
36 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
36 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
37
37
38 /// Key pressed to enable zoom on horizontal axis
38 /// Key pressed to enable zoom on horizontal axis
39 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
39 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
40
40
41 /// Key pressed to enable zoom on vertical axis
41 /// Key pressed to enable zoom on vertical axis
42 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
42 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
43
43
44 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
44 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
45 const auto PAN_SPEED = 5;
45 const auto PAN_SPEED = 5;
46
46
47 /// Key pressed to enable a calibration pan
47 /// Key pressed to enable a calibration pan
48 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
48 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
49
49
50 /// Key pressed to enable multi selection of selection zones
50 /// Key pressed to enable multi selection of selection zones
51 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
51 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
52
52
53 /// Minimum size for the zoom box, in percentage of the axis range
53 /// Minimum size for the zoom box, in percentage of the axis range
54 const auto ZOOM_BOX_MIN_SIZE = 0.8;
54 const auto ZOOM_BOX_MIN_SIZE = 0.8;
55
55
56 /// Format of the dates appearing in the label of a cursor
56 /// Format of the dates appearing in the label of a cursor
57 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
57 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
58
58
59 } // namespace
59 } // namespace
60
60
61 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
61 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
62
62
63 explicit VisualizationGraphWidgetPrivate(const QString &name)
63 explicit VisualizationGraphWidgetPrivate(const QString &name)
64 : m_Name{name},
64 : m_Name{name},
65 m_Flags{GraphFlag::EnableAll},
65 m_Flags{GraphFlag::EnableAll},
66 m_IsCalibration{false},
66 m_IsCalibration{false},
67 m_RenderingDelegate{nullptr}
67 m_RenderingDelegate{nullptr}
68 {
68 {
69 m_plot = new QCustomPlot();
69 m_plot = new QCustomPlot();
70 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
70 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
71 m_plot->setPlottingHint(QCP::phFastPolylines, true);
71 m_plot->setPlottingHint(QCP::phFastPolylines, true);
72 }
72 }
73
73
74 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
74 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
75 const DateTimeRange &range)
75 const DateTimeRange &range)
76 {
76 {
77 VisualizationGraphHelper::updateData(plottables, variable, range);
77 VisualizationGraphHelper::updateData(plottables, variable, range);
78
78
79 // Prevents that data has changed to update rendering
79 // Prevents that data has changed to update rendering
80 m_RenderingDelegate->onPlotUpdated();
80 m_RenderingDelegate->onPlotUpdated();
81 }
81 }
82
82
83 QString m_Name;
83 QString m_Name;
84 // 1 variable -> n qcpplot
84 // 1 variable -> n qcpplot
85 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
85 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
86 GraphFlags m_Flags;
86 GraphFlags m_Flags;
87 bool m_IsCalibration;
87 bool m_IsCalibration;
88 QCustomPlot* m_plot;
88 QCustomPlot* m_plot;
89 QPoint m_lastMousePos;
89 QPoint m_lastMousePos;
90 QCPRange m_lastXRange;
90 QCPRange m_lastXRange;
91 QCPRange m_lastYRange;
91 QCPRange m_lastYRange;
92 /// Delegate used to attach rendering features to the plot
92 /// Delegate used to attach rendering features to the plot
93 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
93 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
94
94
95 QCPItemRect* m_DrawingZoomRect = nullptr;
95 QCPItemRect* m_DrawingZoomRect = nullptr;
96 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
96 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
97
97
98 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
99 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
99 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
100
100
101 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
101 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
102 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
102 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
103 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
103 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
104
104
105 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
105 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
106
106
107 bool m_VariableAutoRangeOnInit = true;
107 bool m_VariableAutoRangeOnInit = true;
108
108
109 inline void enterPlotDrag(const QPoint& position)
109 inline void enterPlotDrag(const QPoint& position)
110 {
110 {
111 m_lastMousePos = m_plot->mapFromParent(position);
111 m_lastMousePos = m_plot->mapFromParent(position);
112 m_lastXRange = m_plot->xAxis->range();
112 m_lastXRange = m_plot->xAxis->range();
113 m_lastYRange = m_plot->yAxis->range();
113 m_lastYRange = m_plot->yAxis->range();
114
114
115 }
115 }
116
116
117 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
117 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
118 void updateZoomRect(const QPoint& newPos)
118 void updateZoomRect(const QPoint& newPos)
119 {
119 {
120 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
120 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
121 m_DrawingZoomRect->bottomRight->setCoords(pos);
121 m_DrawingZoomRect->bottomRight->setCoords(pos);
122 m_plot->replot(QCustomPlot::rpQueuedReplot);
122 m_plot->replot(QCustomPlot::rpQueuedReplot);
123 }
123 }
124
124
125 void applyZoomRect()
125 void applyZoomRect()
126 {
126 {
127 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
127 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
128 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
128 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
129
129
130 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
130 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
131 m_DrawingZoomRect->bottomRight->coords().x()};
131 m_DrawingZoomRect->bottomRight->coords().x()};
132
132
133 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
133 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
134 m_DrawingZoomRect->bottomRight->coords().y()};
134 m_DrawingZoomRect->bottomRight->coords().y()};
135
135
136 removeDrawingRect();
136 removeDrawingRect();
137
137
138 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
138 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
139 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
139 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
140 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
140 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
141 axisX->setRange(newAxisXRange);
141 axisX->setRange(newAxisXRange);
142 axisY->setRange(newAxisYRange);
142 axisY->setRange(newAxisYRange);
143
143
144 m_plot->replot(QCustomPlot::rpQueuedReplot);
144 m_plot->replot(QCustomPlot::rpQueuedReplot);
145 }
145 }
146 }
146 }
147
147
148 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
148 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
149 void updateZoneRect(const QPoint& newPos)
149 void updateZoneRect(const QPoint& newPos)
150 {
150 {
151 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
151 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
152 m_plot->replot(QCustomPlot::rpQueuedReplot);
152 m_plot->replot(QCustomPlot::rpQueuedReplot);
153 }
153 }
154
154
155 void startDrawingRect(const QPoint &pos)
155 void startDrawingRect(const QPoint &pos)
156 {
156 {
157 removeDrawingRect();
157 removeDrawingRect();
158
158
159 auto axisPos = posToAxisPos(pos);
159 auto axisPos = posToAxisPos(pos);
160
160
161 m_DrawingZoomRect = new QCPItemRect{m_plot};
161 m_DrawingZoomRect = new QCPItemRect{m_plot};
162 QPen p;
162 QPen p;
163 p.setWidth(2);
163 p.setWidth(2);
164 m_DrawingZoomRect->setPen(p);
164 m_DrawingZoomRect->setPen(p);
165
165
166 m_DrawingZoomRect->topLeft->setCoords(axisPos);
166 m_DrawingZoomRect->topLeft->setCoords(axisPos);
167 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
167 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
168 }
168 }
169
169
170 void removeDrawingRect()
170 void removeDrawingRect()
171 {
171 {
172 if (m_DrawingZoomRect) {
172 if (m_DrawingZoomRect) {
173 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
173 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
174 m_DrawingZoomRect = nullptr;
174 m_DrawingZoomRect = nullptr;
175 m_plot->replot(QCustomPlot::rpQueuedReplot);
175 m_plot->replot(QCustomPlot::rpQueuedReplot);
176 }
176 }
177 }
177 }
178
178
179 void selectZone(const QPoint &pos)
179 void selectZone(const QPoint &pos)
180 {
180 {
181 auto zoneAtPos = selectionZoneAt(pos);
181 auto zoneAtPos = selectionZoneAt(pos);
182 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
182 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
183 }
183 }
184
184
185 void startDrawingZone(const QPoint &pos)
185 void startDrawingZone(const QPoint &pos)
186 {
186 {
187 endDrawingZone();
187 endDrawingZone();
188
188
189 auto axisPos = posToAxisPos(pos);
189 auto axisPos = posToAxisPos(pos);
190
190
191 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
191 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
192 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
192 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
193 m_DrawingZone->setEditionEnabled(false);
193 m_DrawingZone->setEditionEnabled(false);
194 }
194 }
195
195
196 void endDrawingZone()
196 void endDrawingZone()
197 {
197 {
198 if (m_DrawingZone) {
198 if (m_DrawingZone) {
199 auto drawingZoneRange = m_DrawingZone->range();
199 auto drawingZoneRange = m_DrawingZone->range();
200 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
200 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
201 m_DrawingZone->setEditionEnabled(true);
201 m_DrawingZone->setEditionEnabled(true);
202 addSelectionZone(m_DrawingZone);
202 addSelectionZone(m_DrawingZone);
203 }
203 }
204 else {
204 else {
205 m_plot->removeItem(m_DrawingZone);
205 m_plot->removeItem(m_DrawingZone);
206 }
206 }
207
207
208 m_plot->replot(QCustomPlot::rpQueuedReplot);
208 m_plot->replot(QCustomPlot::rpQueuedReplot);
209 m_DrawingZone = nullptr;
209 m_DrawingZone = nullptr;
210 }
210 }
211 }
211 }
212
212
213 void moveSelectionZone(const QPoint& destination)
213 void moveSelectionZone(const QPoint& destination)
214 {
214 {
215 /*
215 /*
216 * I give up on this for now
216 * I give up on this for now
217 * @TODO implement this, the difficulty is that selection zones have their own
217 * @TODO implement this, the difficulty is that selection zones have their own
218 * event handling code which seems to rely on QCP GUI event handling propagation
218 * event handling code which seems to rely on QCP GUI event handling propagation
219 * which was a realy bad design choice.
219 * which was a realy bad design choice.
220 */
220 */
221 }
221 }
222
222
223 void setSelectionZonesEditionEnabled(bool value)
223 void setSelectionZonesEditionEnabled(bool value)
224 {
224 {
225 for (auto s : m_SelectionZones) {
225 for (auto s : m_SelectionZones) {
226 s->setEditionEnabled(value);
226 s->setEditionEnabled(value);
227 }
227 }
228 }
228 }
229
229
230 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
230 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
231
231
232 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
232 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
233 {
233 {
234 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
234 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
235 auto minDistanceToZone = -1;
235 auto minDistanceToZone = -1;
236 for (auto zone : m_SelectionZones) {
236 for (auto zone : m_SelectionZones) {
237 auto distanceToZone = zone->selectTest(pos, false);
237 auto distanceToZone = zone->selectTest(pos, false);
238 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
238 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
239 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
239 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
240 selectionZoneItemUnderCursor = zone;
240 selectionZoneItemUnderCursor = zone;
241 }
241 }
242 }
242 }
243
243
244 return selectionZoneItemUnderCursor;
244 return selectionZoneItemUnderCursor;
245 }
245 }
246
246
247 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
247 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
248 const QCustomPlot &plot) const
248 const QCustomPlot &plot) const
249 {
249 {
250 QVector<VisualizationSelectionZoneItem *> zones;
250 QVector<VisualizationSelectionZoneItem *> zones;
251 for (auto zone : m_SelectionZones) {
251 for (auto zone : m_SelectionZones) {
252 auto distanceToZone = zone->selectTest(pos, false);
252 auto distanceToZone = zone->selectTest(pos, false);
253 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
253 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
254 zones << zone;
254 zones << zone;
255 }
255 }
256 }
256 }
257
257
258 return zones;
258 return zones;
259 }
259 }
260
260
261 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
261 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
262 {
262 {
263 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
263 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
264 zone->moveToTop();
264 zone->moveToTop();
265 m_SelectionZones.removeAll(zone);
265 m_SelectionZones.removeAll(zone);
266 m_SelectionZones.append(zone);
266 m_SelectionZones.append(zone);
267 }
267 }
268 }
268 }
269
269
270 QPointF posToAxisPos(const QPoint &pos) const
270 QPointF posToAxisPos(const QPoint &pos) const
271 {
271 {
272 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
272 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
273 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
273 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
274 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
274 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
275 }
275 }
276
276
277 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
277 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
278 {
278 {
279 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
279 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
280 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
280 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
281 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
281 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
282 }
282 }
283
283
284 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
284 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
285 {
285 {
286 if (axis->scaleType() == QCPAxis::stLinear)
286 if (axis->scaleType() == QCPAxis::stLinear)
287 {
287 {
288 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
288 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
289 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
289 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
290 }
290 }
291 else
291 else
292 {
292 {
293 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
293 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
294 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
294 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
295 }
295 }
296 }
296 }
297
297
298 void setRange(const DateTimeRange &newRange, bool updateVar=true)
298 void setRange(const DateTimeRange &newRange, bool updateVar=true)
299 {
299 {
300 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
300 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
301 if(updateVar)
301 if(updateVar)
302 {
302 {
303 for (auto it = m_VariableToPlotMultiMap.begin(),
303 for (auto it = m_VariableToPlotMultiMap.begin(),
304 end = m_VariableToPlotMultiMap.end();
304 end = m_VariableToPlotMultiMap.end();
305 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
305 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
306 {
306 {
307 sqpApp->variableController().asyncChangeRange(it->first, newRange);
307 sqpApp->variableController().asyncChangeRange(it->first, newRange);
308 }
308 }
309 }
309 }
310 m_plot->replot(QCustomPlot::rpQueuedReplot);
310 m_plot->replot(QCustomPlot::rpQueuedReplot);
311 }
311 }
312
312
313 void setRange(const QCPRange &newRange)
313 void setRange(const QCPRange &newRange)
314 {
314 {
315 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
315 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
316 setRange(graphRange);
316 setRange(graphRange);
317 }
317 }
318
318
319 void rescaleY()
319 void rescaleY()
320 {
320 {
321 m_plot->yAxis->rescale(true);
321 m_plot->yAxis->rescale(true);
322 }
322 }
323
323
324 std::tuple<double,double> moveGraph(const QPoint& destination)
324 std::tuple<double,double> moveGraph(const QPoint& destination)
325 {
325 {
326 auto currentPos = m_plot->mapFromParent(destination);
326 auto currentPos = m_plot->mapFromParent(destination);
327 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
327 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
328 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
328 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
329 auto oldXRange = xAxis->range();
329 auto oldXRange = xAxis->range();
330 auto oldYRange = yAxis->range();
330 auto oldYRange = yAxis->range();
331 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
331 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
332 xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx);
332 xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx);
333 if(yAxis->scaleType() == QCPAxis::stLinear)
333 if(yAxis->scaleType() == QCPAxis::stLinear)
334 {
334 {
335 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
335 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
336 yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy);
336 yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy);
337 }
337 }
338 else
338 else
339 {
339 {
340 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
340 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
341 yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy);
341 yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy);
342 }
342 }
343 auto newXRange = xAxis->range();
343 auto newXRange = xAxis->range();
344 auto newYRange = yAxis->range();
344 auto newYRange = yAxis->range();
345 setRange(xAxis->range());
345 setRange(xAxis->range());
346 //m_lastMousePos = currentPos;
346 //m_lastMousePos = currentPos;
347 return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower};
347 return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower};
348 }
348 }
349
349
350 void zoom(double factor, int center, Qt::Orientation orientation)
350 void zoom(double factor, int center, Qt::Orientation orientation)
351 {
351 {
352 QCPAxis *axis = m_plot->axisRect()->rangeZoomAxis(orientation);
352 QCPAxis *axis = m_plot->axisRect()->rangeZoomAxis(orientation);
353 axis->scaleRange(factor, axis->pixelToCoord(center));
353 axis->scaleRange(factor, axis->pixelToCoord(center));
354 if (orientation == Qt::Horizontal)
354 if (orientation == Qt::Horizontal)
355 setRange(axis->range());
355 setRange(axis->range());
356 m_plot->replot(QCustomPlot::rpQueuedReplot);
356 m_plot->replot(QCustomPlot::rpQueuedReplot);
357 }
357 }
358
358
359 void transform(const DateTimeRangeTransformation &tranformation)
359 void transform(const DateTimeRangeTransformation &tranformation)
360 {
360 {
361 auto graphRange = m_plot->xAxis->range();
361 auto graphRange = m_plot->xAxis->range();
362 DateTimeRange range{graphRange.lower, graphRange.upper};
362 DateTimeRange range{graphRange.lower, graphRange.upper};
363 range = range.transform(tranformation);
363 range = range.transform(tranformation);
364 setRange(range);
364 setRange(range);
365 m_plot->replot(QCustomPlot::rpQueuedReplot);
365 m_plot->replot(QCustomPlot::rpQueuedReplot);
366 }
366 }
367
367
368 void move(double dx, double dy)
368 void move(double dx, double dy)
369 {
369 {
370 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
370 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
371 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
371 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
372 xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx));
372 xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx));
373 yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy));
373 yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy));
374 setRange(xAxis->range());
374 setRange(xAxis->range());
375 m_plot->replot(QCustomPlot::rpQueuedReplot);
375 m_plot->replot(QCustomPlot::rpQueuedReplot);
376 }
376 }
377
377
378 void move(double factor, Qt::Orientation orientation)
378 void move(double factor, Qt::Orientation orientation)
379 {
379 {
380 auto oldRange = m_plot->xAxis->range();
380 auto oldRange = m_plot->xAxis->range();
381 QCPAxis *axis = m_plot->axisRect()->rangeDragAxis(orientation);
381 QCPAxis *axis = m_plot->axisRect()->rangeDragAxis(orientation);
382 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
382 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
383 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
383 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
384 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
384 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
385 }
385 }
386 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
386 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
387 int start = 0, stop = 0;
387 int start = 0, stop = 0;
388 double diff = 0.;
388 double diff = 0.;
389 if (factor > 0.0) {
389 if (factor > 0.0) {
390 stop = m_plot->width() * factor / 10;
390 stop = m_plot->width() * factor / 10;
391 start = 2 * m_plot->width() * factor / 10;
391 start = 2 * m_plot->width() * factor / 10;
392 }
392 }
393 if (factor < 0.0) {
393 if (factor < 0.0) {
394 factor *= -1.0;
394 factor *= -1.0;
395 start = m_plot->width() * factor / 10;
395 start = m_plot->width() * factor / 10;
396 stop = 2 * m_plot->width() * factor / 10;
396 stop = 2 * m_plot->width() * factor / 10;
397 }
397 }
398 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
398 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
399 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
399 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
400 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
400 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
401 }
401 }
402 if (orientation == Qt::Horizontal)
402 if (orientation == Qt::Horizontal)
403 setRange(axis->range());
403 setRange(axis->range());
404 m_plot->replot(QCustomPlot::rpQueuedReplot);
404 m_plot->replot(QCustomPlot::rpQueuedReplot);
405 }
405 }
406 };
406 };
407
407
408 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
408 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
409 : VisualizationDragWidget{parent},
409 : VisualizationDragWidget{parent},
410 ui{new Ui::VisualizationGraphWidget},
410 ui{new Ui::VisualizationGraphWidget},
411 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
411 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
412 {
412 {
413 ui->setupUi(this);
413 ui->setupUi(this);
414 this->layout()->addWidget(impl->m_plot);
414 this->layout()->addWidget(impl->m_plot);
415 // 'Close' options : widget is deleted when closed
415 // 'Close' options : widget is deleted when closed
416 setAttribute(Qt::WA_DeleteOnClose);
416 setAttribute(Qt::WA_DeleteOnClose);
417
417
418 // The delegate must be initialized after the ui as it uses the plot
418 // The delegate must be initialized after the ui as it uses the plot
419 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
419 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
420
420
421 // Init the cursors
421 // Init the cursors
422 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
422 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
423 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
423 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
424 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
424 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
425 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
425 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
426
426
427 this->setFocusPolicy(Qt::WheelFocus);
427 this->setFocusPolicy(Qt::WheelFocus);
428 this->setMouseTracking(true);
428 this->setMouseTracking(true);
429 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
429 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
430 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
430 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
431 impl->m_plot->setParent(this);
431 impl->m_plot->setParent(this);
432
432
433 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, &VisualizationGraphWidget::variableDeleted);
433 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, &VisualizationGraphWidget::variableDeleted);
434 }
434 }
435
435
436
436
437 VisualizationGraphWidget::~VisualizationGraphWidget()
437 VisualizationGraphWidget::~VisualizationGraphWidget()
438 {
438 {
439 delete ui;
439 delete ui;
440 }
440 }
441
441
442 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
442 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
443 {
443 {
444 auto parent = parentWidget();
444 auto parent = parentWidget();
445 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
445 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
446 parent = parent->parentWidget();
446 parent = parent->parentWidget();
447 }
447 }
448
448
449 return qobject_cast<VisualizationZoneWidget *>(parent);
449 return qobject_cast<VisualizationZoneWidget *>(parent);
450 }
450 }
451
451
452 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
452 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
453 {
453 {
454 auto parent = parentWidget();
454 auto parent = parentWidget();
455 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
455 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
456 parent = parent->parentWidget();
456 parent = parent->parentWidget();
457 }
457 }
458
458
459 return qobject_cast<VisualizationWidget *>(parent);
459 return qobject_cast<VisualizationWidget *>(parent);
460 }
460 }
461
461
462 void VisualizationGraphWidget::setFlags(GraphFlags flags)
462 void VisualizationGraphWidget::setFlags(GraphFlags flags)
463 {
463 {
464 impl->m_Flags = std::move(flags);
464 impl->m_Flags = std::move(flags);
465 }
465 }
466
466
467 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
467 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
468 {
468 {
469 // Uses delegate to create the qcpplot components according to the variable
469 // Uses delegate to create the qcpplot components according to the variable
470 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
470 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
471
471
472 // Sets graph properties
472 // Sets graph properties
473 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
473 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
474
474
475 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
475 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
476
476
477 setGraphRange(range);
477 setGraphRange(range);
478 // If the variable already has its data loaded, load its units and its range in the graph
478 // If the variable already has its data loaded, load its units and its range in the graph
479 if (variable->dataSeries() != nullptr) {
479 if (variable->dataSeries() != nullptr) {
480 impl->m_RenderingDelegate->setAxesUnits(*variable);
480 impl->m_RenderingDelegate->setAxesUnits(*variable);
481 }
481 }
482 else
482 else
483 {
483 {
484 auto context = new QObject{this};
484 auto context = new QObject{this};
485 connect(variable.get(), &Variable::updated, context,
485 connect(variable.get(), &Variable::updated, context,
486 [this, variable, context, range](QUuid)
486 [this, variable, context, range](QUuid)
487 {
487 {
488 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
488 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
489 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
489 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
490 delete context;
490 delete context;
491 }
491 }
492 );
492 );
493 }
493 }
494 //@TODO this is bad! when variable is moved to another graph it still fires
494 //@TODO this is bad! when variable is moved to another graph it still fires
495 // even if this has been deleted
495 // even if this has been deleted
496 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
496 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
497 this->onUpdateVarDisplaying(variable, range); // My bullshit
497 this->onUpdateVarDisplaying(variable, range); // My bullshit
498 emit variableAdded(variable);
498 emit variableAdded(variable);
499 }
499 }
500
500
501 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
501 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
502 {
502 {
503 // Each component associated to the variable :
503 // Each component associated to the variable :
504 // - is removed from qcpplot (which deletes it)
504 // - is removed from qcpplot (which deletes it)
505 // - is no longer referenced in the map
505 // - is no longer referenced in the map
506 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
506 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
507 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
507 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
508 emit variableAboutToBeRemoved(variable);
508 emit variableAboutToBeRemoved(variable);
509
509
510 auto &plottablesMap = variableIt->second;
510 auto &plottablesMap = variableIt->second;
511
511
512 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
512 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
513 plottableIt != plottableEnd;) {
513 plottableIt != plottableEnd;) {
514 impl->m_plot->removePlottable(plottableIt->second);
514 impl->m_plot->removePlottable(plottableIt->second);
515 plottableIt = plottablesMap.erase(plottableIt);
515 plottableIt = plottablesMap.erase(plottableIt);
516 }
516 }
517
517
518 impl->m_VariableToPlotMultiMap.erase(variableIt);
518 impl->m_VariableToPlotMultiMap.erase(variableIt);
519 }
519 }
520
520
521 // Updates graph
521 // Updates graph
522 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
522 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
523 }
523 }
524
524
525 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
525 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
526 {
526 {
527 auto variables = std::vector<std::shared_ptr<Variable> >{};
527 auto variables = std::vector<std::shared_ptr<Variable> >{};
528 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
528 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
529 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
529 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
530 variables.push_back(it->first);
530 variables.push_back(it->first);
531 }
531 }
532
532
533 return variables;
533 return variables;
534 }
534 }
535
535
536 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
536 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
537 {
537 {
538 if (!variable) {
538 if (!variable) {
539 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
539 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
540 return;
540 return;
541 }
541 }
542
542
543 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
543 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
544 }
544 }
545
545
546 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
546 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
547 {
547 {
548 auto graphRange = impl->m_plot->xAxis->range();
548 auto graphRange = impl->m_plot->xAxis->range();
549 return DateTimeRange{graphRange.lower, graphRange.upper};
549 return DateTimeRange{graphRange.lower, graphRange.upper};
550 }
550 }
551
551
552 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool updateVar, bool forward)
552 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool updateVar, bool forward)
553 {
553 {
554 impl->setRange(range, updateVar);
554 impl->setRange(range, updateVar);
555 if(forward)
555 if(forward)
556 {
556 {
557 emit this->setrange_sig(this->graphRange(), true, false);
557 emit this->setrange_sig(this->graphRange(), true, false);
558 }
558 }
559
559
560 }
560 }
561
561
562 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
562 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
563 {
563 {
564 impl->m_VariableAutoRangeOnInit = value;
564 impl->m_VariableAutoRangeOnInit = value;
565 }
565 }
566
566
567 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
567 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
568 {
568 {
569 QVector<DateTimeRange> ranges;
569 QVector<DateTimeRange> ranges;
570 for (auto zone : impl->m_SelectionZones) {
570 for (auto zone : impl->m_SelectionZones) {
571 ranges << zone->range();
571 ranges << zone->range();
572 }
572 }
573
573
574 return ranges;
574 return ranges;
575 }
575 }
576
576
577 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
577 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
578 {
578 {
579 for (const auto &range : ranges) {
579 for (const auto &range : ranges) {
580 // note: ownership is transfered to QCustomPlot
580 // note: ownership is transfered to QCustomPlot
581 auto zone = new VisualizationSelectionZoneItem(&plot());
581 auto zone = new VisualizationSelectionZoneItem(&plot());
582 zone->setRange(range.m_TStart, range.m_TEnd);
582 zone->setRange(range.m_TStart, range.m_TEnd);
583 impl->addSelectionZone(zone);
583 impl->addSelectionZone(zone);
584 }
584 }
585
585
586 plot().replot(QCustomPlot::rpQueuedReplot);
586 plot().replot(QCustomPlot::rpQueuedReplot);
587 }
587 }
588
588
589 VisualizationSelectionZoneItem *
589 VisualizationSelectionZoneItem *
590 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
590 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
591 {
591 {
592 // note: ownership is transfered to QCustomPlot
592 // note: ownership is transfered to QCustomPlot
593 auto zone = new VisualizationSelectionZoneItem(&plot());
593 auto zone = new VisualizationSelectionZoneItem(&plot());
594 zone->setName(name);
594 zone->setName(name);
595 zone->setRange(range.m_TStart, range.m_TEnd);
595 zone->setRange(range.m_TStart, range.m_TEnd);
596 impl->addSelectionZone(zone);
596 impl->addSelectionZone(zone);
597
597
598 plot().replot(QCustomPlot::rpQueuedReplot);
598 plot().replot(QCustomPlot::rpQueuedReplot);
599
599
600 return zone;
600 return zone;
601 }
601 }
602
602
603 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
603 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
604 {
604 {
605 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
605 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
606
606
607 if (impl->m_HoveredZone == selectionZone) {
607 if (impl->m_HoveredZone == selectionZone) {
608 impl->m_HoveredZone = nullptr;
608 impl->m_HoveredZone = nullptr;
609 setCursor(Qt::ArrowCursor);
609 setCursor(Qt::ArrowCursor);
610 }
610 }
611
611
612 impl->m_SelectionZones.removeAll(selectionZone);
612 impl->m_SelectionZones.removeAll(selectionZone);
613 plot().removeItem(selectionZone);
613 plot().removeItem(selectionZone);
614 plot().replot(QCustomPlot::rpQueuedReplot);
614 plot().replot(QCustomPlot::rpQueuedReplot);
615 }
615 }
616
616
617 void VisualizationGraphWidget::undoZoom()
617 void VisualizationGraphWidget::undoZoom()
618 {
618 {
619 auto zoom = impl->m_ZoomStack.pop();
619 auto zoom = impl->m_ZoomStack.pop();
620 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
620 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
621 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
621 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
622
622
623 axisX->setRange(zoom.first);
623 axisX->setRange(zoom.first);
624 axisY->setRange(zoom.second);
624 axisY->setRange(zoom.second);
625
625
626 plot().replot(QCustomPlot::rpQueuedReplot);
626 plot().replot(QCustomPlot::rpQueuedReplot);
627 }
627 }
628
628
629 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward)
629 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward)
630 {
630 {
631 impl->zoom(factor, center, orientation);
631 impl->zoom(factor, center, orientation);
632 if(forward && orientation==Qt::Horizontal)
632 if(forward && orientation==Qt::Horizontal)
633 emit this->setrange_sig(this->graphRange(), true, false);
633 emit this->setrange_sig(this->graphRange(), true, false);
634 }
634 }
635
635
636 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
636 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
637 {
637 {
638 impl->move(factor, orientation);
638 impl->move(factor, orientation);
639 if(forward)
639 if(forward)
640 emit this->setrange_sig(this->graphRange(), true, false);
640 emit this->setrange_sig(this->graphRange(), true, false);
641 }
641 }
642
642
643 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
643 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
644 {
644 {
645 impl->move(dx, dy);
645 impl->move(dx, dy);
646 if(forward)
646 if(forward)
647 emit this->setrange_sig(this->graphRange(), true, false);
647 emit this->setrange_sig(this->graphRange(), true, false);
648 }
648 }
649
649
650 void VisualizationGraphWidget::transform(const DateTimeRangeTransformation &tranformation, bool forward)
650 void VisualizationGraphWidget::transform(const DateTimeRangeTransformation &tranformation, bool forward)
651 {
651 {
652 impl->transform(tranformation);
652 impl->transform(tranformation);
653 if(forward)
653 if(forward)
654 emit this->setrange_sig(this->graphRange(), true, false);
654 emit this->setrange_sig(this->graphRange(), true, false);
655 }
655 }
656
656
657 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
657 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
658 {
658 {
659 if (visitor) {
659 if (visitor) {
660 visitor->visit(this);
660 visitor->visit(this);
661 }
661 }
662 else {
662 else {
663 qCCritical(LOG_VisualizationGraphWidget())
663 qCCritical(LOG_VisualizationGraphWidget())
664 << tr("Can't visit widget : the visitor is null");
664 << tr("Can't visit widget : the visitor is null");
665 }
665 }
666 }
666 }
667
667
668 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
668 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
669 {
669 {
670 auto isSpectrogram = [](const auto &variable) {
670 auto isSpectrogram = [](const auto &variable) {
671 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
671 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
672 };
672 };
673
673
674 // - A spectrogram series can't be dropped on graph with existing plottables
674 // - A spectrogram series can't be dropped on graph with existing plottables
675 // - No data series can be dropped on graph with existing spectrogram series
675 // - No data series can be dropped on graph with existing spectrogram series
676 return isSpectrogram(variable)
676 return isSpectrogram(variable)
677 ? impl->m_VariableToPlotMultiMap.empty()
677 ? impl->m_VariableToPlotMultiMap.empty()
678 : std::none_of(
678 : std::none_of(
679 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
679 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
680 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
680 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
681 }
681 }
682
682
683 bool VisualizationGraphWidget::contains(const Variable &variable) const
683 bool VisualizationGraphWidget::contains(const Variable &variable) const
684 {
684 {
685 // Finds the variable among the keys of the map
685 // Finds the variable among the keys of the map
686 auto variablePtr = &variable;
686 auto variablePtr = &variable;
687 auto findVariable
687 auto findVariable
688 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
688 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
689
689
690 auto end = impl->m_VariableToPlotMultiMap.cend();
690 auto end = impl->m_VariableToPlotMultiMap.cend();
691 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
691 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
692 return it != end;
692 return it != end;
693 }
693 }
694
694
695 QString VisualizationGraphWidget::name() const
695 QString VisualizationGraphWidget::name() const
696 {
696 {
697 return impl->m_Name;
697 return impl->m_Name;
698 }
698 }
699
699
700 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
700 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
701 {
701 {
702 auto mimeData = new QMimeData;
702 auto mimeData = new QMimeData;
703
703
704 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
704 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
705 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
705 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
706 && selectionZoneItemUnderCursor) {
706 && selectionZoneItemUnderCursor) {
707 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
707 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
708 selectionZoneItemUnderCursor->range()));
708 selectionZoneItemUnderCursor->range()));
709 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
709 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
710 selectionZoneItemUnderCursor->range()));
710 selectionZoneItemUnderCursor->range()));
711 }
711 }
712 else {
712 else {
713 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
713 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
714
714
715 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
715 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
716 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
716 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
717 }
717 }
718
718
719 return mimeData;
719 return mimeData;
720 }
720 }
721
721
722 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
722 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
723 {
723 {
724 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
724 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
725 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
725 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
726 && selectionZoneItemUnderCursor) {
726 && selectionZoneItemUnderCursor) {
727
727
728 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
728 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
729 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
729 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
730
730
731 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
731 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
732 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
732 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
733 .toSize();
733 .toSize();
734
734
735 auto pixmap = QPixmap(zoneSize);
735 auto pixmap = QPixmap(zoneSize);
736 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
736 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
737
737
738 return pixmap;
738 return pixmap;
739 }
739 }
740
740
741 return QPixmap();
741 return QPixmap();
742 }
742 }
743
743
744 bool VisualizationGraphWidget::isDragAllowed() const
744 bool VisualizationGraphWidget::isDragAllowed() const
745 {
745 {
746 return true;
746 return true;
747 }
747 }
748
748
749 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
749 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
750 {
750 {
751 if (highlighted) {
751 if (highlighted) {
752 plot().setBackground(QBrush(QColor("#BBD5EE")));
752 plot().setBackground(QBrush(QColor("#BBD5EE")));
753 }
753 }
754 else {
754 else {
755 plot().setBackground(QBrush(Qt::white));
755 plot().setBackground(QBrush(Qt::white));
756 }
756 }
757
757
758 plot().update();
758 plot().update();
759 }
759 }
760
760
761 void VisualizationGraphWidget::addVerticalCursor(double time)
761 void VisualizationGraphWidget::addVerticalCursor(double time)
762 {
762 {
763 impl->m_VerticalCursor->setPosition(time);
763 impl->m_VerticalCursor->setPosition(time);
764 impl->m_VerticalCursor->setVisible(true);
764 impl->m_VerticalCursor->setVisible(true);
765
765
766 auto text
766 auto text
767 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
767 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
768 impl->m_VerticalCursor->setLabelText(text);
768 impl->m_VerticalCursor->setLabelText(text);
769 }
769 }
770
770
771 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
771 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
772 {
772 {
773 impl->m_VerticalCursor->setAbsolutePosition(position);
773 impl->m_VerticalCursor->setAbsolutePosition(position);
774 impl->m_VerticalCursor->setVisible(true);
774 impl->m_VerticalCursor->setVisible(true);
775
775
776 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
776 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
777 auto text
777 auto text
778 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
778 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
779 impl->m_VerticalCursor->setLabelText(text);
779 impl->m_VerticalCursor->setLabelText(text);
780 }
780 }
781
781
782 void VisualizationGraphWidget::removeVerticalCursor()
782 void VisualizationGraphWidget::removeVerticalCursor()
783 {
783 {
784 impl->m_VerticalCursor->setVisible(false);
784 impl->m_VerticalCursor->setVisible(false);
785 plot().replot(QCustomPlot::rpQueuedReplot);
785 plot().replot(QCustomPlot::rpQueuedReplot);
786 }
786 }
787
787
788 void VisualizationGraphWidget::addHorizontalCursor(double value)
788 void VisualizationGraphWidget::addHorizontalCursor(double value)
789 {
789 {
790 impl->m_HorizontalCursor->setPosition(value);
790 impl->m_HorizontalCursor->setPosition(value);
791 impl->m_HorizontalCursor->setVisible(true);
791 impl->m_HorizontalCursor->setVisible(true);
792 impl->m_HorizontalCursor->setLabelText(QString::number(value));
792 impl->m_HorizontalCursor->setLabelText(QString::number(value));
793 }
793 }
794
794
795 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
795 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
796 {
796 {
797 impl->m_HorizontalCursor->setAbsolutePosition(position);
797 impl->m_HorizontalCursor->setAbsolutePosition(position);
798 impl->m_HorizontalCursor->setVisible(true);
798 impl->m_HorizontalCursor->setVisible(true);
799
799
800 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
800 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
801 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
801 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
802 }
802 }
803
803
804 void VisualizationGraphWidget::removeHorizontalCursor()
804 void VisualizationGraphWidget::removeHorizontalCursor()
805 {
805 {
806 impl->m_HorizontalCursor->setVisible(false);
806 impl->m_HorizontalCursor->setVisible(false);
807 plot().replot(QCustomPlot::rpQueuedReplot);
807 plot().replot(QCustomPlot::rpQueuedReplot);
808 }
808 }
809
809
810 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
810 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
811 {
811 {
812 Q_UNUSED(event);
812 Q_UNUSED(event);
813
813
814 for (auto i : impl->m_SelectionZones) {
814 for (auto i : impl->m_SelectionZones) {
815 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
815 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
816 }
816 }
817
817
818 // Prevents that all variables will be removed from graph when it will be closed
818 // Prevents that all variables will be removed from graph when it will be closed
819 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
819 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
820 emit variableAboutToBeRemoved(variableEntry.first);
820 emit variableAboutToBeRemoved(variableEntry.first);
821 }
821 }
822 }
822 }
823
823
824 void VisualizationGraphWidget::enterEvent(QEvent *event)
824 void VisualizationGraphWidget::enterEvent(QEvent *event)
825 {
825 {
826 Q_UNUSED(event);
826 Q_UNUSED(event);
827 impl->m_RenderingDelegate->showGraphOverlay(true);
827 impl->m_RenderingDelegate->showGraphOverlay(true);
828 }
828 }
829
829
830 void VisualizationGraphWidget::leaveEvent(QEvent *event)
830 void VisualizationGraphWidget::leaveEvent(QEvent *event)
831 {
831 {
832 Q_UNUSED(event);
832 Q_UNUSED(event);
833 impl->m_RenderingDelegate->showGraphOverlay(false);
833 impl->m_RenderingDelegate->showGraphOverlay(false);
834
834
835 if (auto parentZone = parentZoneWidget()) {
835 if (auto parentZone = parentZoneWidget()) {
836 parentZone->notifyMouseLeaveGraph(this);
836 parentZone->notifyMouseLeaveGraph(this);
837 }
837 }
838 else {
838 else {
839 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
839 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
840 }
840 }
841
841
842 if (impl->m_HoveredZone) {
842 if (impl->m_HoveredZone) {
843 impl->m_HoveredZone->setHovered(false);
843 impl->m_HoveredZone->setHovered(false);
844 impl->m_HoveredZone = nullptr;
844 impl->m_HoveredZone = nullptr;
845 }
845 }
846 }
846 }
847
847
848 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
848 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
849 {
849 {
850 double factor;
850 double factor;
851 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
851 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
852 if (event->modifiers() == Qt::ControlModifier) {
852 if (event->modifiers() == Qt::ControlModifier) {
853 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
853 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
854 {
854 {
855 setCursor(Qt::SizeVerCursor);
855 setCursor(Qt::SizeVerCursor);
856 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
856 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
857 zoom(factor, event->pos().y(), Qt::Vertical);
857 zoom(factor, event->pos().y(), Qt::Vertical);
858 }
858 }
859 }
859 }
860 else if (event->modifiers() == Qt::ShiftModifier) {
860 else if (event->modifiers() == Qt::ShiftModifier) {
861 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
861 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
862 {
862 {
863 setCursor(Qt::SizeHorCursor);
863 setCursor(Qt::SizeHorCursor);
864 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
864 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
865 zoom(factor, event->pos().x(), Qt::Horizontal);
865 zoom(factor, event->pos().x(), Qt::Horizontal);
866 }
866 }
867 }
867 }
868 else {
868 else {
869 move(wheelSteps, Qt::Horizontal);
869 move(wheelSteps, Qt::Horizontal);
870 }
870 }
871 event->accept();
871 event->accept();
872 }
872 }
873
873
874
874
875
875
876 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
876 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
877 {
877 {
878 if(impl->isDrawingZoomRect())
878 if(impl->isDrawingZoomRect())
879 {
879 {
880 impl->updateZoomRect(event->pos());
880 impl->updateZoomRect(event->pos());
881 }
881 }
882 else if (impl->isDrawingZoneRect())
882 else if (impl->isDrawingZoneRect())
883 {
883 {
884 impl->updateZoneRect(event->pos());
884 impl->updateZoneRect(event->pos());
885 }
885 }
886 else if (event->buttons() == Qt::LeftButton)
886 else if (event->buttons() == Qt::LeftButton)
887 {
887 {
888 if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
888 if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
889 {
889 {
890 auto [dx,dy] = impl->moveGraph(event->pos());
890 auto [dx,dy] = impl->moveGraph(event->pos());
891 emit this->setrange_sig(this->graphRange(), true, false);
891 emit this->setrange_sig(this->graphRange(), true, false);
892 }
892 }
893 else if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
893 else if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
894 {
894 {
895 if(auto item = impl->m_plot->itemAt(event->pos()))
895 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
896 if(auto item = impl->m_plot->itemAt(posInPlot))
896 {
897 {
897 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
898 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
898 {
899 {
899 QMouseEvent e{QEvent::MouseMove,this->impl->m_plot->mapFromParent(event->pos()),event->button(),event->buttons(),event->modifiers()};
900 QMouseEvent e{QEvent::MouseMove, posInPlot, event->button(),event->buttons(),event->modifiers()};
900 sqpApp->sendEvent(this->impl->m_plot, &e);
901 sqpApp->sendEvent(this->impl->m_plot, &e);
901 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
902 this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh);
902 }
903 }
903 }
904 }
904
905
905 }
906 }
906 }
907 }
907 else
908 else
908 {
909 {
909 impl->m_RenderingDelegate->updateTooltip(event);
910 impl->m_RenderingDelegate->updateTooltip(event);
910 }
911 }
911 //event->accept();
912 //event->accept();
912 QWidget::mouseMoveEvent(event);
913 QWidget::mouseMoveEvent(event);
913 }
914 }
914
915
915 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
916 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
916 {
917 {
917 if(impl->isDrawingZoomRect())
918 if(impl->isDrawingZoomRect())
918 {
919 {
919 auto oldRange = this->graphRange();
920 auto oldRange = this->graphRange();
920 impl->applyZoomRect();
921 impl->applyZoomRect();
921 auto newRange = this->graphRange();
922 auto newRange = this->graphRange();
922 if(auto tf = DateTimeRangeHelper::computeTransformation(oldRange,newRange))
923 if(auto tf = DateTimeRangeHelper::computeTransformation(oldRange,newRange))
923 emit this->transform_sig(tf.value(), false);
924 emit this->transform_sig(tf.value(), false);
924 }
925 }
925 else if(impl->isDrawingZoneRect())
926 else if(impl->isDrawingZoneRect())
926 {
927 {
927 impl->endDrawingZone();
928 impl->endDrawingZone();
928 }
929 }
929 else
930 else
930 {
931 {
931 setCursor(Qt::ArrowCursor);
932 setCursor(Qt::ArrowCursor);
932 }
933 }
933 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
934 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
934 if(auto item = impl->m_plot->itemAt(posInPlot))
935 if(auto item = impl->m_plot->itemAt(posInPlot))
935 {
936 {
936 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
937 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
937 {
938 {
938 QMouseEvent e{QEvent::MouseButtonRelease, posInPlot, event->button(), event->buttons(), event->modifiers()};
939 QMouseEvent e{QEvent::MouseButtonRelease, posInPlot, event->button(), event->buttons(), event->modifiers()};
939 sqpApp->sendEvent(this->impl->m_plot, &e);
940 sqpApp->sendEvent(this->impl->m_plot, &e);
940 }
941 }
941 }
942 }
942 event->accept();
943 event->accept();
943 }
944 }
944
945
945 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
946 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
946 {
947 {
947 if (event->button()==Qt::RightButton)
948 if (event->button()==Qt::RightButton)
948 {
949 {
949 onGraphMenuRequested(event->pos());
950 onGraphMenuRequested(event->pos());
950 }
951 }
951 else
952 else
952 {
953 {
953 auto selectedZone = impl->selectionZoneAt(event->pos());
954 auto selectedZone = impl->selectionZoneAt(event->pos());
954 switch (sqpApp->plotsInteractionMode())
955 switch (sqpApp->plotsInteractionMode())
955 {
956 {
956 case SqpApplication::PlotsInteractionMode::DragAndDrop :
957 case SqpApplication::PlotsInteractionMode::DragAndDrop :
957 break;
958 break;
958 case SqpApplication::PlotsInteractionMode::SelectionZones :
959 case SqpApplication::PlotsInteractionMode::SelectionZones :
959 impl->setSelectionZonesEditionEnabled(true);
960 impl->setSelectionZonesEditionEnabled(true);
960 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
961 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
961 {
962 {
962 auto alreadySelectedZones = parentVisualizationWidget()->selectionZoneManager().selectedItems();
963 auto alreadySelectedZones = parentVisualizationWidget()->selectionZoneManager().selectedItems();
963 selectedZone->setAssociatedEditedZones(alreadySelectedZones);
964 selectedZone->setAssociatedEditedZones(alreadySelectedZones);
964 if(SciQLop::containers::contains(alreadySelectedZones, selectedZone))
965 if(SciQLop::containers::contains(alreadySelectedZones, selectedZone))
965 {
966 {
966 alreadySelectedZones.removeOne(selectedZone);
967 alreadySelectedZones.removeOne(selectedZone);
967 }
968 }
968 else
969 else
969 {
970 {
970 alreadySelectedZones.append(selectedZone);
971 alreadySelectedZones.append(selectedZone);
971 }
972 }
972 parentVisualizationWidget()->selectionZoneManager().select(alreadySelectedZones);
973 parentVisualizationWidget()->selectionZoneManager().select(alreadySelectedZones);
973 }
974 }
974 else
975 else
975 {
976 {
976 if (!selectedZone)
977 if (!selectedZone)
977 {
978 {
978 parentVisualizationWidget()->selectionZoneManager().clearSelection();
979 parentVisualizationWidget()->selectionZoneManager().clearSelection();
979 impl->startDrawingZone(event->pos());
980 impl->startDrawingZone(event->pos());
980 }
981 }
981 else
982 else
982 {
983 {
983 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
984 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
984 }
985 }
985 }
986 }
986 {
987 {
987 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
988 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
988 if(auto item = impl->m_plot->itemAt(posInPlot))
989 if(auto item = impl->m_plot->itemAt(posInPlot))
989 {
990 {
990 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
991 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
991 {
992 {
992 QMouseEvent e{QEvent::MouseButtonPress, posInPlot, event->button(), event->buttons(), event->modifiers()};
993 QMouseEvent e{QEvent::MouseButtonPress, posInPlot, event->button(), event->buttons(), event->modifiers()};
993 sqpApp->sendEvent(this->impl->m_plot, &e);
994 sqpApp->sendEvent(this->impl->m_plot, &e);
994 }
995 }
995 }
996 }
996 }
997 }
997 break;
998 break;
998 case SqpApplication::PlotsInteractionMode::ZoomBox :
999 case SqpApplication::PlotsInteractionMode::ZoomBox :
999 impl->startDrawingRect(event->pos());
1000 impl->startDrawingRect(event->pos());
1000 break;
1001 break;
1001 default:
1002 default:
1002 if(auto item = impl->m_plot->itemAt(event->pos()))
1003 if(auto item = impl->m_plot->itemAt(event->pos()))
1003 {
1004 {
1004 emit impl->m_plot->itemClick(item,event);
1005 emit impl->m_plot->itemClick(item,event);
1005 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
1006 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
1006 {
1007 {
1007 setCursor(Qt::ClosedHandCursor);
1008 setCursor(Qt::ClosedHandCursor);
1008 impl->enterPlotDrag(event->pos());
1009 impl->enterPlotDrag(event->pos());
1009 }
1010 }
1010 }
1011 }
1011 else
1012 else
1012 {
1013 {
1013 setCursor(Qt::ClosedHandCursor);
1014 setCursor(Qt::ClosedHandCursor);
1014 impl->enterPlotDrag(event->pos());
1015 impl->enterPlotDrag(event->pos());
1015 }
1016 }
1016 }
1017 }
1017 }
1018 }
1018 //event->accept();
1019 //event->accept();
1019 QWidget::mousePressEvent(event);
1020 QWidget::mousePressEvent(event);
1020 }
1021 }
1021
1022
1022 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
1023 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
1023 {
1024 {
1024 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1025 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1025 }
1026 }
1026
1027
1027 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
1028 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
1028 {
1029 {
1029 switch (event->key()) {
1030 switch (event->key()) {
1030 case Qt::Key_Control:
1031 case Qt::Key_Control:
1031 event->accept();
1032 event->accept();
1032 break;
1033 break;
1033 case Qt::Key_Shift:
1034 case Qt::Key_Shift:
1034 event->accept();
1035 event->accept();
1035 break;
1036 break;
1036 default:
1037 default:
1037 QWidget::keyReleaseEvent(event);
1038 QWidget::keyReleaseEvent(event);
1038 break;
1039 break;
1039 }
1040 }
1040 setCursor(Qt::ArrowCursor);
1041 setCursor(Qt::ArrowCursor);
1041 //event->accept();
1042 //event->accept();
1042 }
1043 }
1043
1044
1044 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
1045 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
1045 {
1046 {
1046 switch (event->key()) {
1047 switch (event->key()) {
1047 case Qt::Key_Control:
1048 case Qt::Key_Control:
1048 setCursor(Qt::CrossCursor);
1049 setCursor(Qt::CrossCursor);
1049 break;
1050 break;
1050 case Qt::Key_Shift:
1051 case Qt::Key_Shift:
1051 break;
1052 break;
1052 case Qt::Key_M:
1053 case Qt::Key_M:
1053 impl->rescaleY();
1054 impl->rescaleY();
1054 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1055 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1055 break;
1056 break;
1056 case Qt::Key_Left:
1057 case Qt::Key_Left:
1057 if (event->modifiers() != Qt::ControlModifier) {
1058 if (event->modifiers() != Qt::ControlModifier) {
1058 move(-0.1, Qt::Horizontal);
1059 move(-0.1, Qt::Horizontal);
1059 }
1060 }
1060 else {
1061 else {
1061 zoom(2, this->width() / 2, Qt::Horizontal);
1062 zoom(2, this->width() / 2, Qt::Horizontal);
1062 }
1063 }
1063 break;
1064 break;
1064 case Qt::Key_Right:
1065 case Qt::Key_Right:
1065 if (event->modifiers() != Qt::ControlModifier) {
1066 if (event->modifiers() != Qt::ControlModifier) {
1066 move(0.1, Qt::Horizontal);
1067 move(0.1, Qt::Horizontal);
1067 }
1068 }
1068 else {
1069 else {
1069 zoom(0.5, this->width() / 2, Qt::Horizontal);
1070 zoom(0.5, this->width() / 2, Qt::Horizontal);
1070 }
1071 }
1071 break;
1072 break;
1072 case Qt::Key_Up:
1073 case Qt::Key_Up:
1073 if (event->modifiers() != Qt::ControlModifier) {
1074 if (event->modifiers() != Qt::ControlModifier) {
1074 move(0.1, Qt::Vertical);
1075 move(0.1, Qt::Vertical);
1075 }
1076 }
1076 else {
1077 else {
1077 zoom(0.5, this->height() / 2, Qt::Vertical);
1078 zoom(0.5, this->height() / 2, Qt::Vertical);
1078 }
1079 }
1079 break;
1080 break;
1080 case Qt::Key_Down:
1081 case Qt::Key_Down:
1081 if (event->modifiers() != Qt::ControlModifier) {
1082 if (event->modifiers() != Qt::ControlModifier) {
1082 move(-0.1, Qt::Vertical);
1083 move(-0.1, Qt::Vertical);
1083 }
1084 }
1084 else {
1085 else {
1085 zoom(2, this->height() / 2, Qt::Vertical);
1086 zoom(2, this->height() / 2, Qt::Vertical);
1086 }
1087 }
1087 break;
1088 break;
1088 default:
1089 default:
1089 QWidget::keyPressEvent(event);
1090 QWidget::keyPressEvent(event);
1090 break;
1091 break;
1091 }
1092 }
1092 }
1093 }
1093
1094
1094 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
1095 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
1095 {
1096 {
1096 return *impl->m_plot;
1097 return *impl->m_plot;
1097 }
1098 }
1098
1099
1099 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
1100 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
1100 {
1101 {
1101 QMenu graphMenu{};
1102 QMenu graphMenu{};
1102
1103
1103 // Iterates on variables (unique keys)
1104 // Iterates on variables (unique keys)
1104 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1105 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1105 end = impl->m_VariableToPlotMultiMap.cend();
1106 end = impl->m_VariableToPlotMultiMap.cend();
1106 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
1107 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
1107 // 'Remove variable' action
1108 // 'Remove variable' action
1108 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1109 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1109 [this, var = it->first]() { removeVariable(var); });
1110 [this, var = it->first]() { removeVariable(var); });
1110 }
1111 }
1111
1112
1112 if (!impl->m_ZoomStack.isEmpty()) {
1113 if (!impl->m_ZoomStack.isEmpty()) {
1113 if (!graphMenu.isEmpty()) {
1114 if (!graphMenu.isEmpty()) {
1114 graphMenu.addSeparator();
1115 graphMenu.addSeparator();
1115 }
1116 }
1116
1117
1117 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1118 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1118 }
1119 }
1119
1120
1120 // Selection Zone Actions
1121 // Selection Zone Actions
1121 auto selectionZoneItem = impl->selectionZoneAt(pos);
1122 auto selectionZoneItem = impl->selectionZoneAt(pos);
1122 if (selectionZoneItem) {
1123 if (selectionZoneItem) {
1123 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1124 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1124 selectedItems.removeAll(selectionZoneItem);
1125 selectedItems.removeAll(selectionZoneItem);
1125 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1126 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1126
1127
1127 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1128 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1128 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
1129 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
1129 graphMenu.addSeparator();
1130 graphMenu.addSeparator();
1130 }
1131 }
1131
1132
1132 QHash<QString, QMenu *> subMenus;
1133 QHash<QString, QMenu *> subMenus;
1133 QHash<QString, bool> subMenusEnabled;
1134 QHash<QString, bool> subMenusEnabled;
1134 QHash<QString, FilteringAction *> filteredMenu;
1135 QHash<QString, FilteringAction *> filteredMenu;
1135
1136
1136 for (auto zoneAction : zoneActions) {
1137 for (auto zoneAction : zoneActions) {
1137
1138
1138 auto isEnabled = zoneAction->isEnabled(selectedItems);
1139 auto isEnabled = zoneAction->isEnabled(selectedItems);
1139
1140
1140 auto menu = &graphMenu;
1141 auto menu = &graphMenu;
1141 QString menuPath;
1142 QString menuPath;
1142 for (auto subMenuName : zoneAction->subMenuList()) {
1143 for (auto subMenuName : zoneAction->subMenuList()) {
1143 menuPath += '/';
1144 menuPath += '/';
1144 menuPath += subMenuName;
1145 menuPath += subMenuName;
1145
1146
1146 if (!subMenus.contains(menuPath)) {
1147 if (!subMenus.contains(menuPath)) {
1147 menu = menu->addMenu(subMenuName);
1148 menu = menu->addMenu(subMenuName);
1148 subMenus[menuPath] = menu;
1149 subMenus[menuPath] = menu;
1149 subMenusEnabled[menuPath] = isEnabled;
1150 subMenusEnabled[menuPath] = isEnabled;
1150 }
1151 }
1151 else {
1152 else {
1152 menu = subMenus.value(menuPath);
1153 menu = subMenus.value(menuPath);
1153 if (isEnabled) {
1154 if (isEnabled) {
1154 // The sub menu is enabled if at least one of its actions is enabled
1155 // The sub menu is enabled if at least one of its actions is enabled
1155 subMenusEnabled[menuPath] = true;
1156 subMenusEnabled[menuPath] = true;
1156 }
1157 }
1157 }
1158 }
1158 }
1159 }
1159
1160
1160 FilteringAction *filterAction = nullptr;
1161 FilteringAction *filterAction = nullptr;
1161 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1162 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1162 filterAction = filteredMenu.value(menuPath);
1163 filterAction = filteredMenu.value(menuPath);
1163 if (!filterAction) {
1164 if (!filterAction) {
1164 filterAction = new FilteringAction{this};
1165 filterAction = new FilteringAction{this};
1165 filteredMenu[menuPath] = filterAction;
1166 filteredMenu[menuPath] = filterAction;
1166 menu->addAction(filterAction);
1167 menu->addAction(filterAction);
1167 }
1168 }
1168 }
1169 }
1169
1170
1170 auto action = menu->addAction(zoneAction->name());
1171 auto action = menu->addAction(zoneAction->name());
1171 action->setEnabled(isEnabled);
1172 action->setEnabled(isEnabled);
1172 action->setShortcut(zoneAction->displayedShortcut());
1173 action->setShortcut(zoneAction->displayedShortcut());
1173 QObject::connect(action, &QAction::triggered,
1174 QObject::connect(action, &QAction::triggered,
1174 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1175 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1175
1176
1176 if (filterAction && zoneAction->isFilteringAllowed()) {
1177 if (filterAction && zoneAction->isFilteringAllowed()) {
1177 filterAction->addActionToFilter(action);
1178 filterAction->addActionToFilter(action);
1178 }
1179 }
1179 }
1180 }
1180
1181
1181 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1182 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1182 it.value()->setEnabled(subMenusEnabled[it.key()]);
1183 it.value()->setEnabled(subMenusEnabled[it.key()]);
1183 }
1184 }
1184 }
1185 }
1185
1186
1186 if (!graphMenu.isEmpty()) {
1187 if (!graphMenu.isEmpty()) {
1187 graphMenu.exec(QCursor::pos());
1188 graphMenu.exec(QCursor::pos());
1188 }
1189 }
1189 }
1190 }
1190
1191
1191 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1192 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1192 {
1193 {
1193 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1194 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1194 }
1195 }
1195
1196
1196 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1197 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1197 {
1198 {
1198 // Handles plot rendering when mouse is moving
1199 // Handles plot rendering when mouse is moving
1199 impl->m_RenderingDelegate->updateTooltip(event);
1200 impl->m_RenderingDelegate->updateTooltip(event);
1200
1201
1201 auto axisPos = impl->posToAxisPos(event->pos());
1202 auto axisPos = impl->posToAxisPos(event->pos());
1202
1203
1203 // Zoom box and zone drawing
1204 // Zoom box and zone drawing
1204 if (impl->m_DrawingZoomRect) {
1205 if (impl->m_DrawingZoomRect) {
1205 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1206 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1206 }
1207 }
1207 else if (impl->m_DrawingZone) {
1208 else if (impl->m_DrawingZone) {
1208 impl->m_DrawingZone->setEnd(axisPos.x());
1209 impl->m_DrawingZone->setEnd(axisPos.x());
1209 }
1210 }
1210
1211
1211 // Cursor
1212 // Cursor
1212 if (auto parentZone = parentZoneWidget()) {
1213 if (auto parentZone = parentZoneWidget()) {
1213 if (impl->pointIsInAxisRect(axisPos, plot())) {
1214 if (impl->pointIsInAxisRect(axisPos, plot())) {
1214 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1215 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1215 }
1216 }
1216 else {
1217 else {
1217 parentZone->notifyMouseLeaveGraph(this);
1218 parentZone->notifyMouseLeaveGraph(this);
1218 }
1219 }
1219 }
1220 }
1220
1221
1221 // Search for the selection zone under the mouse
1222 // Search for the selection zone under the mouse
1222 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1223 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1223 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1224 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1224 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1225 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1225
1226
1226 // Sets the appropriate cursor shape
1227 // Sets the appropriate cursor shape
1227 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1228 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1228 setCursor(cursorShape);
1229 setCursor(cursorShape);
1229
1230
1230 // Manages the hovered zone
1231 // Manages the hovered zone
1231 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1232 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1232 if (impl->m_HoveredZone) {
1233 if (impl->m_HoveredZone) {
1233 impl->m_HoveredZone->setHovered(false);
1234 impl->m_HoveredZone->setHovered(false);
1234 }
1235 }
1235 selectionZoneItemUnderCursor->setHovered(true);
1236 selectionZoneItemUnderCursor->setHovered(true);
1236 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1237 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1237 plot().replot(QCustomPlot::rpQueuedReplot);
1238 plot().replot(QCustomPlot::rpQueuedReplot);
1238 }
1239 }
1239 }
1240 }
1240 else {
1241 else {
1241 // There is no zone under the mouse or the interaction mode is not "selection zones"
1242 // There is no zone under the mouse or the interaction mode is not "selection zones"
1242 if (impl->m_HoveredZone) {
1243 if (impl->m_HoveredZone) {
1243 impl->m_HoveredZone->setHovered(false);
1244 impl->m_HoveredZone->setHovered(false);
1244 impl->m_HoveredZone = nullptr;
1245 impl->m_HoveredZone = nullptr;
1245 }
1246 }
1246
1247
1247 setCursor(Qt::ArrowCursor);
1248 setCursor(Qt::ArrowCursor);
1248 }
1249 }
1249
1250
1250 impl->m_HasMovedMouse = true;
1251 impl->m_HasMovedMouse = true;
1251 VisualizationDragWidget::mouseMoveEvent(event);
1252 VisualizationDragWidget::mouseMoveEvent(event);
1252 }
1253 }
1253
1254
1254 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1255 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1255 {
1256 {
1256 // Processes event only if the wheel occurs on axis rect
1257 // Processes event only if the wheel occurs on axis rect
1257 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1258 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1258 return;
1259 return;
1259 }
1260 }
1260
1261
1261 auto value = event->angleDelta().x() + event->angleDelta().y();
1262 auto value = event->angleDelta().x() + event->angleDelta().y();
1262 if (value != 0) {
1263 if (value != 0) {
1263
1264
1264 auto direction = value > 0 ? 1.0 : -1.0;
1265 auto direction = value > 0 ? 1.0 : -1.0;
1265 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1266 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1266 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1267 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1267 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1268 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1268
1269
1269 auto zoomOrientations = QFlags<Qt::Orientation>{};
1270 auto zoomOrientations = QFlags<Qt::Orientation>{};
1270 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1271 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1271 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1272 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1272
1273
1273 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1274 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1274
1275
1275 if (!isZoomX && !isZoomY) {
1276 if (!isZoomX && !isZoomY) {
1276 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1277 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1277 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1278 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1278
1279
1279 axis->setRange(axis->range() + diff);
1280 axis->setRange(axis->range() + diff);
1280
1281
1281 if (plot().noAntialiasingOnDrag()) {
1282 if (plot().noAntialiasingOnDrag()) {
1282 plot().setNotAntialiasedElements(QCP::aeAll);
1283 plot().setNotAntialiasedElements(QCP::aeAll);
1283 }
1284 }
1284
1285
1285 // plot().replot(QCustomPlot::rpQueuedReplot);
1286 // plot().replot(QCustomPlot::rpQueuedReplot);
1286 }
1287 }
1287 }
1288 }
1288 }
1289 }
1289
1290
1290 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1291 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1291 {
1292 {
1292 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1293 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1293 auto isSelectionZoneMode
1294 auto isSelectionZoneMode
1294 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1295 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1295 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1296 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1296
1297
1297 if (!isDragDropClick && isLeftClick) {
1298 if (!isDragDropClick && isLeftClick) {
1298 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1299 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1299 // Starts a zoom box
1300 // Starts a zoom box
1300 impl->startDrawingRect(event->pos());
1301 impl->startDrawingRect(event->pos());
1301 }
1302 }
1302 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1303 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1303 // Starts a new selection zone
1304 // Starts a new selection zone
1304 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1305 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1305 if (!zoneAtPos) {
1306 if (!zoneAtPos) {
1306 impl->startDrawingZone(event->pos());
1307 impl->startDrawingZone(event->pos());
1307 }
1308 }
1308 }
1309 }
1309 }
1310 }
1310
1311
1311
1312
1312 // Allows zone edition only in selection zone mode without drag&drop
1313 // Allows zone edition only in selection zone mode without drag&drop
1313 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1314 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1314
1315
1315 // Selection / Deselection
1316 // Selection / Deselection
1316 if (isSelectionZoneMode) {
1317 if (isSelectionZoneMode) {
1317 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1318 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1318 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1319 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1319
1320
1320
1321
1321 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1322 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1322 && !isMultiSelectionClick) {
1323 && !isMultiSelectionClick) {
1323 parentVisualizationWidget()->selectionZoneManager().select(
1324 parentVisualizationWidget()->selectionZoneManager().select(
1324 {selectionZoneItemUnderCursor});
1325 {selectionZoneItemUnderCursor});
1325 }
1326 }
1326 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1327 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1327 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1328 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1328 }
1329 }
1329 else {
1330 else {
1330 // No selection change
1331 // No selection change
1331 }
1332 }
1332
1333
1333 if (selectionZoneItemUnderCursor && isLeftClick) {
1334 if (selectionZoneItemUnderCursor && isLeftClick) {
1334 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1335 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1335 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1336 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1336 }
1337 }
1337 }
1338 }
1338
1339
1339
1340
1340 impl->m_HasMovedMouse = false;
1341 impl->m_HasMovedMouse = false;
1341 VisualizationDragWidget::mousePressEvent(event);
1342 VisualizationDragWidget::mousePressEvent(event);
1342 }
1343 }
1343
1344
1344 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1345 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1345 {
1346 {
1346 if (impl->m_DrawingZoomRect) {
1347 if (impl->m_DrawingZoomRect) {
1347
1348
1348 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1349 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1349 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1350 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1350
1351
1351 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1352 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1352 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1353 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1353
1354
1354 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1355 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1355 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1356 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1356
1357
1357 impl->removeDrawingRect();
1358 impl->removeDrawingRect();
1358
1359
1359 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1360 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1360 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1361 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1361 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1362 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1362 axisX->setRange(newAxisXRange);
1363 axisX->setRange(newAxisXRange);
1363 axisY->setRange(newAxisYRange);
1364 axisY->setRange(newAxisYRange);
1364
1365
1365 plot().replot(QCustomPlot::rpQueuedReplot);
1366 plot().replot(QCustomPlot::rpQueuedReplot);
1366 }
1367 }
1367 }
1368 }
1368
1369
1369 impl->endDrawingZone();
1370 impl->endDrawingZone();
1370
1371
1371 // Selection / Deselection
1372 // Selection / Deselection
1372 auto isSelectionZoneMode
1373 auto isSelectionZoneMode
1373 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1374 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1374 if (isSelectionZoneMode) {
1375 if (isSelectionZoneMode) {
1375 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1376 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1376 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1377 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1377 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1378 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1378 && !impl->m_HasMovedMouse) {
1379 && !impl->m_HasMovedMouse) {
1379
1380
1380 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1381 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1381 if (zonesUnderCursor.count() > 1) {
1382 if (zonesUnderCursor.count() > 1) {
1382 // There are multiple zones under the mouse.
1383 // There are multiple zones under the mouse.
1383 // Performs the selection with a selection dialog.
1384 // Performs the selection with a selection dialog.
1384 VisualizationMultiZoneSelectionDialog dialog{this};
1385 VisualizationMultiZoneSelectionDialog dialog{this};
1385 dialog.setZones(zonesUnderCursor);
1386 dialog.setZones(zonesUnderCursor);
1386 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1387 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1387 dialog.activateWindow();
1388 dialog.activateWindow();
1388 dialog.raise();
1389 dialog.raise();
1389 if (dialog.exec() == QDialog::Accepted) {
1390 if (dialog.exec() == QDialog::Accepted) {
1390 auto selection = dialog.selectedZones();
1391 auto selection = dialog.selectedZones();
1391
1392
1392 if (!isMultiSelectionClick) {
1393 if (!isMultiSelectionClick) {
1393 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1394 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1394 }
1395 }
1395
1396
1396 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1397 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1397 auto zone = it.key();
1398 auto zone = it.key();
1398 auto isSelected = it.value();
1399 auto isSelected = it.value();
1399 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1400 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1400 isSelected);
1401 isSelected);
1401
1402
1402 if (isSelected) {
1403 if (isSelected) {
1403 // Puts the zone on top of the stack so it can be moved or resized
1404 // Puts the zone on top of the stack so it can be moved or resized
1404 impl->moveSelectionZoneOnTop(zone, plot());
1405 impl->moveSelectionZoneOnTop(zone, plot());
1405 }
1406 }
1406 }
1407 }
1407 }
1408 }
1408 }
1409 }
1409 else {
1410 else {
1410 if (!isMultiSelectionClick) {
1411 if (!isMultiSelectionClick) {
1411 parentVisualizationWidget()->selectionZoneManager().select(
1412 parentVisualizationWidget()->selectionZoneManager().select(
1412 {selectionZoneItemUnderCursor});
1413 {selectionZoneItemUnderCursor});
1413 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1414 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1414 }
1415 }
1415 else {
1416 else {
1416 parentVisualizationWidget()->selectionZoneManager().setSelected(
1417 parentVisualizationWidget()->selectionZoneManager().setSelected(
1417 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1418 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1418 || event->button() == Qt::RightButton);
1419 || event->button() == Qt::RightButton);
1419 }
1420 }
1420 }
1421 }
1421 }
1422 }
1422 else {
1423 else {
1423 // No selection change
1424 // No selection change
1424 }
1425 }
1425 }
1426 }
1426 }
1427 }
1427
1428
1428 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1429 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1429 {
1430 {
1430 auto graphRange = impl->m_plot->xAxis->range();
1431 auto graphRange = impl->m_plot->xAxis->range();
1431 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1432 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1432
1433
1433 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1434 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1434 auto variable = variableEntry.first;
1435 auto variable = variableEntry.first;
1435 qCDebug(LOG_VisualizationGraphWidget())
1436 qCDebug(LOG_VisualizationGraphWidget())
1436 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1437 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1437 qCDebug(LOG_VisualizationGraphWidget())
1438 qCDebug(LOG_VisualizationGraphWidget())
1438 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1439 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1439 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1440 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1440 impl->updateData(variableEntry.second, variable, variable->range());
1441 impl->updateData(variableEntry.second, variable, variable->range());
1441 }
1442 }
1442 }
1443 }
1443 }
1444 }
1444
1445
1445 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1446 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1446 const DateTimeRange &range)
1447 const DateTimeRange &range)
1447 {
1448 {
1448 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1449 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1449 if (it != impl->m_VariableToPlotMultiMap.end()) {
1450 if (it != impl->m_VariableToPlotMultiMap.end()) {
1450 impl->updateData(it->second, variable, range);
1451 impl->updateData(it->second, variable, range);
1451 }
1452 }
1452 }
1453 }
1453
1454
1454 void VisualizationGraphWidget::variableUpdated(QUuid id)
1455 void VisualizationGraphWidget::variableUpdated(QUuid id)
1455 {
1456 {
1456 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1457 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1457 if (var->ID() == id) {
1458 if (var->ID() == id) {
1458 impl->updateData(plotables, var, this->graphRange());
1459 impl->updateData(plotables, var, this->graphRange());
1459 }
1460 }
1460 }
1461 }
1461 }
1462 }
1462
1463
1463 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable> & variable)
1464 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable> & variable)
1464 {
1465 {
1465 this->removeVariable(variable);
1466 this->removeVariable(variable);
1466 }
1467 }
@@ -1,677 +1,662
1 #include "Visualization/VisualizationZoneWidget.h"
1 #include "Visualization/VisualizationZoneWidget.h"
2
2
3 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 #include "Visualization/QCustomPlotSynchronizer.h"
4 #include "Visualization/QCustomPlotSynchronizer.h"
5 #include "Visualization/VisualizationGraphWidget.h"
5 #include "Visualization/VisualizationGraphWidget.h"
6 #include "Visualization/VisualizationWidget.h"
6 #include "Visualization/VisualizationWidget.h"
7 #include "ui_VisualizationZoneWidget.h"
7 #include "ui_VisualizationZoneWidget.h"
8
8
9 #include "Common/MimeTypesDef.h"
9 #include "Common/MimeTypesDef.h"
10 #include "Common/VisualizationDef.h"
10 #include "Common/VisualizationDef.h"
11
11
12 #include <Data/DateTimeRange.h>
12 #include <Data/DateTimeRange.h>
13 #include <Data/DateTimeRangeHelper.h>
13 #include <Data/DateTimeRangeHelper.h>
14 #include <DataSource/DataSourceController.h>
14 #include <DataSource/DataSourceController.h>
15 #include <Time/TimeController.h>
15 #include <Time/TimeController.h>
16 #include <Variable/Variable.h>
16 #include <Variable/Variable.h>
17 #include <Variable/VariableController2.h>
17 #include <Variable/VariableController2.h>
18
18
19 #include <Visualization/operations/FindVariableOperation.h>
19 #include <Visualization/operations/FindVariableOperation.h>
20
20
21 #include <DragAndDrop/DragDropGuiController.h>
21 #include <DragAndDrop/DragDropGuiController.h>
22 #include <QUuid>
22 #include <QUuid>
23 #include <SqpApplication.h>
23 #include <SqpApplication.h>
24 #include <cmath>
24 #include <cmath>
25
25
26 #include <QLayout>
26 #include <QLayout>
27 #include <QStyle>
27 #include <QStyle>
28
28
29 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
29 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
30
30
31 namespace {
31 namespace {
32
32
33 /**
33 /**
34 * Applies a function to all graphs of the zone represented by its layout
34 * Applies a function to all graphs of the zone represented by its layout
35 * @param layout the layout that contains graphs
35 * @param layout the layout that contains graphs
36 * @param fun the function to apply to each graph
36 * @param fun the function to apply to each graph
37 */
37 */
38 template <typename Fun>
38 template <typename Fun>
39 void processGraphs(QLayout &layout, Fun fun)
39 void processGraphs(QLayout &layout, Fun fun)
40 {
40 {
41 for (auto i = 0; i < layout.count(); ++i) {
41 for (auto i = 0; i < layout.count(); ++i) {
42 if (auto item = layout.itemAt(i)) {
42 if (auto item = layout.itemAt(i)) {
43 if (auto visualizationGraphWidget
43 if (auto visualizationGraphWidget
44 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
44 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
45 fun(*visualizationGraphWidget);
45 fun(*visualizationGraphWidget);
46 }
46 }
47 }
47 }
48 }
48 }
49 }
49 }
50
50
51 /// Generates a default name for a new graph, according to the number of graphs already displayed in
51 /// Generates a default name for a new graph, according to the number of graphs already displayed in
52 /// the zone
52 /// the zone
53 QString defaultGraphName(QLayout &layout)
53 QString defaultGraphName(QLayout &layout)
54 {
54 {
55 QSet<QString> existingNames;
55 QSet<QString> existingNames;
56 processGraphs(
56 processGraphs(
57 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
57 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
58
58
59 int zoneNum = 1;
59 int zoneNum = 1;
60 QString name;
60 QString name;
61 do {
61 do {
62 name = QObject::tr("Graph ").append(QString::number(zoneNum));
62 name = QObject::tr("Graph ").append(QString::number(zoneNum));
63 ++zoneNum;
63 ++zoneNum;
64 } while (existingNames.contains(name));
64 } while (existingNames.contains(name));
65
65
66 return name;
66 return name;
67 }
67 }
68
68
69 } // namespace
69 } // namespace
70
70
71 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
71 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
72
72
73 explicit VisualizationZoneWidgetPrivate()
73 explicit VisualizationZoneWidgetPrivate()
74 : m_SynchronisationGroupId{QUuid::createUuid()},
74 : m_SynchronisationGroupId{QUuid::createUuid()},
75 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
75 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
76 {
76 {
77 }
77 }
78 QUuid m_SynchronisationGroupId;
78 QUuid m_SynchronisationGroupId;
79 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
79 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
80
80
81 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
81 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
82 void dropVariables(const std::vector<std::shared_ptr<Variable> > &variables, int index,
82 void dropVariables(const std::vector<std::shared_ptr<Variable> > &variables, int index,
83 VisualizationZoneWidget *zoneWidget);
83 VisualizationZoneWidget *zoneWidget);
84 void dropProducts(const QVariantList &productsData, int index,
84 void dropProducts(const QVariantList &productsData, int index,
85 VisualizationZoneWidget *zoneWidget);
85 VisualizationZoneWidget *zoneWidget);
86 };
86 };
87
87
88 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
88 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
89 : VisualizationDragWidget{parent},
89 : VisualizationDragWidget{parent},
90 ui{new Ui::VisualizationZoneWidget},
90 ui{new Ui::VisualizationZoneWidget},
91 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
91 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
92 {
92 {
93 ui->setupUi(this);
93 ui->setupUi(this);
94
94
95 ui->zoneNameLabel->setText(name);
95 ui->zoneNameLabel->setText(name);
96
96
97 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
97 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
98 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
98 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
99 VisualizationDragDropContainer::DropBehavior::Inserted);
99 VisualizationDragDropContainer::DropBehavior::Inserted);
100 ui->dragDropContainer->setMimeType(
100 ui->dragDropContainer->setMimeType(
101 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
101 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
102 ui->dragDropContainer->setMimeType(
102 ui->dragDropContainer->setMimeType(
103 MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
103 MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
104 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
104 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
105 VisualizationDragDropContainer::DropBehavior::Merged);
105 VisualizationDragDropContainer::DropBehavior::Merged);
106 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
106 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
107 VisualizationDragDropContainer::DropBehavior::Forbidden);
107 VisualizationDragDropContainer::DropBehavior::Forbidden);
108 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
108 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
109 VisualizationDragDropContainer::DropBehavior::Forbidden);
109 VisualizationDragDropContainer::DropBehavior::Forbidden);
110 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
110 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
111 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
111 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
112 ui->dragDropContainer);
112 ui->dragDropContainer);
113 });
113 });
114
114
115 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
115 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
116 if (!mimeData) {
116 if (!mimeData) {
117 return false;
117 return false;
118 }
118 }
119
119
120 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
120 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
121 auto variables = sqpApp->variableController().variables(
121 auto variables = sqpApp->variableController().variables(
122 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
122 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
123
123
124 if (variables.size() != 1) {
124 if (variables.size() != 1) {
125 return false;
125 return false;
126 }
126 }
127 auto variable = variables.front();
127 auto variable = variables.front();
128
128
129 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
129 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
130 return graphWidget->canDrop(*variable);
130 return graphWidget->canDrop(*variable);
131 }
131 }
132 }
132 }
133
133
134 return true;
134 return true;
135 };
135 };
136 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
136 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
137
137
138 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
138 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
139 &VisualizationZoneWidget::dropMimeData);
139 &VisualizationZoneWidget::dropMimeData);
140 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
140 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
141 &VisualizationZoneWidget::dropMimeDataOnGraph);
141 &VisualizationZoneWidget::dropMimeDataOnGraph);
142
142
143 // 'Close' options : widget is deleted when closed
143 // 'Close' options : widget is deleted when closed
144 setAttribute(Qt::WA_DeleteOnClose);
144 setAttribute(Qt::WA_DeleteOnClose);
145 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
145 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
146 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
146 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
147
147
148 // Synchronisation id
148 // Synchronisation id
149 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
149 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
150 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
150 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
151 }
151 }
152
152
153 VisualizationZoneWidget::~VisualizationZoneWidget()
153 VisualizationZoneWidget::~VisualizationZoneWidget()
154 {
154 {
155 delete ui;
155 delete ui;
156 }
156 }
157
157
158 void VisualizationZoneWidget::setZoneRange(const DateTimeRange &range)
158 void VisualizationZoneWidget::setZoneRange(const DateTimeRange &range)
159 {
159 {
160 if (auto graph = firstGraph()) {
160 if (auto graph = firstGraph()) {
161 graph->setGraphRange(range);
161 graph->setGraphRange(range);
162 }
162 }
163 else {
163 else {
164 qCWarning(LOG_VisualizationZoneWidget())
164 qCWarning(LOG_VisualizationZoneWidget())
165 << tr("setZoneRange:Cannot set the range of an empty zone.");
165 << tr("setZoneRange:Cannot set the range of an empty zone.");
166 }
166 }
167 }
167 }
168
168
169 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
169 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
170 {
170 {
171 // Synchronize new graph with others in the zone
171 // Synchronize new graph with others in the zone
172 // impl->m_Synchronizer->addGraph(*graphWidget);
172 // impl->m_Synchronizer->addGraph(*graphWidget);
173
173
174 // ui->dragDropContainer->addDragWidget(graphWidget);
174 // ui->dragDropContainer->addDragWidget(graphWidget);
175 insertGraph(0,graphWidget);
175 insertGraph(0,graphWidget);
176
176
177 }
177 }
178
178
179 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
179 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
180 {
180 {
181 DEPRECATE(
181 DEPRECATE(
182 auto layout = ui->dragDropContainer->layout();
182 auto layout = ui->dragDropContainer->layout();
183 for(int i=0;i<layout->count();i++)
183 for(int i=0;i<layout->count();i++)
184 {
184 {
185 auto graph = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(i)->widget());
185 auto graph = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(i)->widget());
186 connect(graphWidget, &VisualizationGraphWidget::setrange_sig, graph, &VisualizationGraphWidget::setGraphRange);
186 connect(graphWidget, &VisualizationGraphWidget::setrange_sig, graph, &VisualizationGraphWidget::setGraphRange);
187 connect(graph, &VisualizationGraphWidget::setrange_sig, graphWidget, &VisualizationGraphWidget::setGraphRange);
187 connect(graph, &VisualizationGraphWidget::setrange_sig, graphWidget, &VisualizationGraphWidget::setGraphRange);
188 // connect(graphWidget, &VisualizationGraphWidget::zoom_sig, graph, &VisualizationGraphWidget::zoom);
188 }
189 // connect(graphWidget, &VisualizationGraphWidget::transform_sig, graph, &VisualizationGraphWidget::transform);
189 if(auto graph = firstGraph())
190
190 {
191 // connect(graphWidget, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move_sig),
191 graphWidget->setGraphRange(graph->graphRange(), true);
192 // graph, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move));
192 }
193 // connect(graphWidget, qOverload<double,double,bool>(&VisualizationGraphWidget::move_sig),
194 // graph, qOverload<double,double,bool>(&VisualizationGraphWidget::move));
195
196 // connect(graph, &VisualizationGraphWidget::zoom_sig, graphWidget, &VisualizationGraphWidget::zoom);
197 // connect(graph, &VisualizationGraphWidget::transform_sig, graphWidget, &VisualizationGraphWidget::transform);
198
199 // connect(graph, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move_sig),
200 // graphWidget, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move));
201 // connect(graph, qOverload<double,double,bool>(&VisualizationGraphWidget::move_sig),
202 // graphWidget, qOverload<double,double,bool>(&VisualizationGraphWidget::move));
203 }
204 if(auto graph = firstGraph())
205 {
206 graphWidget->setGraphRange(graph->graphRange(), true);
207 }
208 )
193 )
209
194
210 // Synchronize new graph with others in the zone
195 // Synchronize new graph with others in the zone
211 impl->m_Synchronizer->addGraph(*graphWidget);
196 impl->m_Synchronizer->addGraph(*graphWidget);
212
197
213 ui->dragDropContainer->insertDragWidget(index, graphWidget);
198 ui->dragDropContainer->insertDragWidget(index, graphWidget);
214
199
215 }
200 }
216
201
217 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
202 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
218 {
203 {
219 return createGraph(variable, -1);
204 return createGraph(variable, -1);
220 }
205 }
221
206
222 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
207 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
223 int index)
208 int index)
224 {
209 {
225 auto graphWidget
210 auto graphWidget
226 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
211 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
227
212
228
213
229 // Set graph properties
214 // Set graph properties
230 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
215 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
231 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
216 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
232
217
233
218
234 // Lambda to synchronize zone widget
219 // Lambda to synchronize zone widget
235 auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange,
220 // auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange,
236 const DateTimeRange &oldGraphRange) {
221 // const DateTimeRange &oldGraphRange) {
237
222
238 auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange);
223 // auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange);
239 auto frameLayout = ui->dragDropContainer->layout();
224 // auto frameLayout = ui->dragDropContainer->layout();
240 for (auto i = 0; i < frameLayout->count(); ++i) {
225 // for (auto i = 0; i < frameLayout->count(); ++i) {
241 auto graphChild
226 // auto graphChild
242 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
227 // = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
243 if (graphChild && (graphChild != graphWidget)) {
228 // if (graphChild && (graphChild != graphWidget)) {
244
229
245 auto graphChildRange = graphChild->graphRange();
230 // auto graphChildRange = graphChild->graphRange();
246 switch (zoomType) {
231 // switch (zoomType) {
247 case TransformationType::ZoomIn: {
232 // case TransformationType::ZoomIn: {
248 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
233 // auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
249 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
234 // auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
250 graphChildRange.m_TStart += deltaLeft;
235 // graphChildRange.m_TStart += deltaLeft;
251 graphChildRange.m_TEnd -= deltaRight;
236 // graphChildRange.m_TEnd -= deltaRight;
252 break;
237 // break;
253 }
238 // }
254
239
255 case TransformationType::ZoomOut: {
240 // case TransformationType::ZoomOut: {
256 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
241 // auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
257 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
242 // auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
258 graphChildRange.m_TStart -= deltaLeft;
243 // graphChildRange.m_TStart -= deltaLeft;
259 graphChildRange.m_TEnd += deltaRight;
244 // graphChildRange.m_TEnd += deltaRight;
260 break;
245 // break;
261 }
246 // }
262 case TransformationType::PanRight: {
247 // case TransformationType::PanRight: {
263 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
248 // auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
264 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
249 // auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
265 graphChildRange.m_TStart += deltaLeft;
250 // graphChildRange.m_TStart += deltaLeft;
266 graphChildRange.m_TEnd += deltaRight;
251 // graphChildRange.m_TEnd += deltaRight;
267 break;
252 // break;
268 }
253 // }
269 case TransformationType::PanLeft: {
254 // case TransformationType::PanLeft: {
270 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
255 // auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
271 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
256 // auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
272 graphChildRange.m_TStart -= deltaLeft;
257 // graphChildRange.m_TStart -= deltaLeft;
273 graphChildRange.m_TEnd -= deltaRight;
258 // graphChildRange.m_TEnd -= deltaRight;
274 break;
259 // break;
275 }
260 // }
276 case TransformationType::Unknown: {
261 // case TransformationType::Unknown: {
277 break;
262 // break;
278 }
263 // }
279 default:
264 // default:
280 qCCritical(LOG_VisualizationZoneWidget())
265 // qCCritical(LOG_VisualizationZoneWidget())
281 << tr("Impossible to synchronize: zoom type not take into account");
266 // << tr("Impossible to synchronize: zoom type not take into account");
282 // No action
267 // // No action
283 break;
268 // break;
284 }
269 // }
285 graphChild->setFlags(GraphFlag::DisableAll);
270 // graphChild->setFlags(GraphFlag::DisableAll);
286 graphChild->setGraphRange(graphChildRange);
271 // graphChild->setGraphRange(graphChildRange, true);
287 graphChild->setFlags(GraphFlag::EnableAll);
272 // graphChild->setFlags(GraphFlag::EnableAll);
288 }
273 // }
289 }
274 // }
290 };
275 // };
291
276
292 // connection for synchronization
277 // connection for synchronization
293 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
278 //connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
294 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
279 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
295 &VisualizationZoneWidget::onVariableAdded);
280 &VisualizationZoneWidget::onVariableAdded);
296 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
281 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
297 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
282 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
298
283
299 auto range = DateTimeRange{};
284 auto range = DateTimeRange{};
300 if (auto firstGraph = this->firstGraph()) {
285 if (auto firstGraph = this->firstGraph()) {
301 // Case of a new graph in a existant zone
286 // Case of a new graph in a existant zone
302 range = firstGraph->graphRange();
287 range = firstGraph->graphRange();
303 }
288 }
304 else {
289 else {
305 // Case of a new graph as the first of the zone
290 // Case of a new graph as the first of the zone
306 range = variable->range();
291 range = variable->range();
307 }
292 }
308
293
309 this->insertGraph(index, graphWidget);
294 this->insertGraph(index, graphWidget);
310
295
311 graphWidget->addVariable(variable, range);
296 graphWidget->addVariable(variable, range);
312 graphWidget->setYRange(variable);
297 graphWidget->setYRange(variable);
313
298
314 return graphWidget;
299 return graphWidget;
315 }
300 }
316
301
317 VisualizationGraphWidget *
302 VisualizationGraphWidget *
318 VisualizationZoneWidget::createGraph(const std::vector<std::shared_ptr<Variable> > variables, int index)
303 VisualizationZoneWidget::createGraph(const std::vector<std::shared_ptr<Variable> > variables, int index)
319 {
304 {
320 if (variables.empty()) {
305 if (variables.empty()) {
321 return nullptr;
306 return nullptr;
322 }
307 }
323
308
324 auto graphWidget = createGraph(variables.front(), index);
309 auto graphWidget = createGraph(variables.front(), index);
325 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
310 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
326 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
311 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
327 }
312 }
328
313
329 return graphWidget;
314 return graphWidget;
330 }
315 }
331
316
332 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
317 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
333 {
318 {
334 VisualizationGraphWidget *firstGraph = nullptr;
319 VisualizationGraphWidget *firstGraph = nullptr;
335 auto layout = ui->dragDropContainer->layout();
320 auto layout = ui->dragDropContainer->layout();
336 if (layout->count() > 0) {
321 if (layout->count() > 0) {
337 if (auto visualizationGraphWidget
322 if (auto visualizationGraphWidget
338 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
323 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
339 firstGraph = visualizationGraphWidget;
324 firstGraph = visualizationGraphWidget;
340 }
325 }
341 }
326 }
342
327
343 return firstGraph;
328 return firstGraph;
344 }
329 }
345
330
346 void VisualizationZoneWidget::closeAllGraphs()
331 void VisualizationZoneWidget::closeAllGraphs()
347 {
332 {
348 processGraphs(*ui->dragDropContainer->layout(),
333 processGraphs(*ui->dragDropContainer->layout(),
349 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
334 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
350 }
335 }
351
336
352 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
337 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
353 {
338 {
354 if (visitor) {
339 if (visitor) {
355 visitor->visitEnter(this);
340 visitor->visitEnter(this);
356
341
357 // Apply visitor to graph children: widgets different from graphs are not visited (no
342 // Apply visitor to graph children: widgets different from graphs are not visited (no
358 // action)
343 // action)
359 processGraphs(
344 processGraphs(
360 *ui->dragDropContainer->layout(),
345 *ui->dragDropContainer->layout(),
361 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
346 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
362
347
363 visitor->visitLeave(this);
348 visitor->visitLeave(this);
364 }
349 }
365 else {
350 else {
366 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
351 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
367 }
352 }
368 }
353 }
369
354
370 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
355 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
371 {
356 {
372 // A tab can always accomodate a variable
357 // A tab can always accomodate a variable
373 Q_UNUSED(variable);
358 Q_UNUSED(variable);
374 return true;
359 return true;
375 }
360 }
376
361
377 bool VisualizationZoneWidget::contains(const Variable &variable) const
362 bool VisualizationZoneWidget::contains(const Variable &variable) const
378 {
363 {
379 Q_UNUSED(variable);
364 Q_UNUSED(variable);
380 return false;
365 return false;
381 }
366 }
382
367
383 QString VisualizationZoneWidget::name() const
368 QString VisualizationZoneWidget::name() const
384 {
369 {
385 return ui->zoneNameLabel->text();
370 return ui->zoneNameLabel->text();
386 }
371 }
387
372
388 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
373 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
389 {
374 {
390 Q_UNUSED(position);
375 Q_UNUSED(position);
391
376
392 auto mimeData = new QMimeData;
377 auto mimeData = new QMimeData;
393 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
378 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
394
379
395 if (auto firstGraph = this->firstGraph()) {
380 if (auto firstGraph = this->firstGraph()) {
396 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
381 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
397 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
382 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
398 }
383 }
399
384
400 return mimeData;
385 return mimeData;
401 }
386 }
402
387
403 bool VisualizationZoneWidget::isDragAllowed() const
388 bool VisualizationZoneWidget::isDragAllowed() const
404 {
389 {
405 return true;
390 return true;
406 }
391 }
407
392
408 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
393 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
409 const QPointF &plotPosition,
394 const QPointF &plotPosition,
410 VisualizationGraphWidget *graphWidget)
395 VisualizationGraphWidget *graphWidget)
411 {
396 {
412 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
397 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
413 VisualizationGraphWidget &processedGraph) {
398 VisualizationGraphWidget &processedGraph) {
414
399
415 switch (sqpApp->plotsCursorMode()) {
400 switch (sqpApp->plotsCursorMode()) {
416 case SqpApplication::PlotsCursorMode::Vertical:
401 case SqpApplication::PlotsCursorMode::Vertical:
417 processedGraph.removeHorizontalCursor();
402 processedGraph.removeHorizontalCursor();
418 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
403 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
419 break;
404 break;
420 case SqpApplication::PlotsCursorMode::Temporal:
405 case SqpApplication::PlotsCursorMode::Temporal:
421 processedGraph.addVerticalCursor(plotPosition.x());
406 processedGraph.addVerticalCursor(plotPosition.x());
422 processedGraph.removeHorizontalCursor();
407 processedGraph.removeHorizontalCursor();
423 break;
408 break;
424 case SqpApplication::PlotsCursorMode::Horizontal:
409 case SqpApplication::PlotsCursorMode::Horizontal:
425 processedGraph.removeVerticalCursor();
410 processedGraph.removeVerticalCursor();
426 if (&processedGraph == graphWidget) {
411 if (&processedGraph == graphWidget) {
427 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
412 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
428 }
413 }
429 else {
414 else {
430 processedGraph.removeHorizontalCursor();
415 processedGraph.removeHorizontalCursor();
431 }
416 }
432 break;
417 break;
433 case SqpApplication::PlotsCursorMode::Cross:
418 case SqpApplication::PlotsCursorMode::Cross:
434 if (&processedGraph == graphWidget) {
419 if (&processedGraph == graphWidget) {
435 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
420 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
436 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
421 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
437 }
422 }
438 else {
423 else {
439 processedGraph.removeHorizontalCursor();
424 processedGraph.removeHorizontalCursor();
440 processedGraph.removeVerticalCursor();
425 processedGraph.removeVerticalCursor();
441 }
426 }
442 break;
427 break;
443 case SqpApplication::PlotsCursorMode::NoCursor:
428 case SqpApplication::PlotsCursorMode::NoCursor:
444 processedGraph.removeHorizontalCursor();
429 processedGraph.removeHorizontalCursor();
445 processedGraph.removeVerticalCursor();
430 processedGraph.removeVerticalCursor();
446 break;
431 break;
447 }
432 }
448
433
449
434
450 });
435 });
451 }
436 }
452
437
453 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
438 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
454 {
439 {
455 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
440 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
456 processedGraph.removeHorizontalCursor();
441 processedGraph.removeHorizontalCursor();
457 processedGraph.removeVerticalCursor();
442 processedGraph.removeVerticalCursor();
458 });
443 });
459 }
444 }
460
445
461 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
446 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
462 {
447 {
463 // Closes graphs in the zone
448 // Closes graphs in the zone
464 processGraphs(*ui->dragDropContainer->layout(),
449 processGraphs(*ui->dragDropContainer->layout(),
465 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
450 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
466
451
467 // Delete synchronization group from variable controller
452 // Delete synchronization group from variable controller
468 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
453 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
469 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
454 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
470
455
471 QWidget::closeEvent(event);
456 QWidget::closeEvent(event);
472 }
457 }
473
458
474 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
459 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
475 {
460 {
476 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
461 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
477 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
462 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
478 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
463 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
479 }
464 }
480
465
481 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
466 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
482 {
467 {
483 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
468 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
484 Q_ARG(std::shared_ptr<Variable>, variable),
469 Q_ARG(std::shared_ptr<Variable>, variable),
485 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
470 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
486 }
471 }
487
472
488 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
473 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
489 {
474 {
490 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
475 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
491 impl->dropGraph(index, this);
476 impl->dropGraph(index, this);
492 }
477 }
493 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
478 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
494 auto variables = sqpApp->variableController().variables(
479 auto variables = sqpApp->variableController().variables(
495 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
480 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
496 impl->dropVariables(variables, index, this);
481 impl->dropVariables(variables, index, this);
497 }
482 }
498 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
483 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
499 auto products = sqpApp->dataSourceController().productsDataForMimeData(
484 auto products = sqpApp->dataSourceController().productsDataForMimeData(
500 mimeData->data(MIME_TYPE_PRODUCT_LIST));
485 mimeData->data(MIME_TYPE_PRODUCT_LIST));
501 impl->dropProducts(products, index, this);
486 impl->dropProducts(products, index, this);
502 }
487 }
503 else {
488 else {
504 qCWarning(LOG_VisualizationZoneWidget())
489 qCWarning(LOG_VisualizationZoneWidget())
505 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
490 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
506 }
491 }
507 }
492 }
508
493
509 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
494 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
510 const QMimeData *mimeData)
495 const QMimeData *mimeData)
511 {
496 {
512 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
497 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
513 if (!graphWidget) {
498 if (!graphWidget) {
514 qCWarning(LOG_VisualizationZoneWidget())
499 qCWarning(LOG_VisualizationZoneWidget())
515 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
500 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
516 "drop aborted");
501 "drop aborted");
517 Q_ASSERT(false);
502 Q_ASSERT(false);
518 return;
503 return;
519 }
504 }
520
505
521 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
506 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
522 auto variables = sqpApp->variableController().variables(
507 auto variables = sqpApp->variableController().variables(
523 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
508 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
524 for (const auto &var : variables) {
509 for (const auto &var : variables) {
525 graphWidget->addVariable(var, graphWidget->graphRange());
510 graphWidget->addVariable(var, graphWidget->graphRange());
526 }
511 }
527 }
512 }
528 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
513 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
529 auto products = sqpApp->dataSourceController().productsDataForMimeData(
514 auto products = sqpApp->dataSourceController().productsDataForMimeData(
530 mimeData->data(MIME_TYPE_PRODUCT_LIST));
515 mimeData->data(MIME_TYPE_PRODUCT_LIST));
531
516
532 auto context = new QObject{this};
517 auto context = new QObject{this};
533 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
518 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
534 // BTW this is really dangerous, this assumes the next created variable will be this one...
519 // BTW this is really dangerous, this assumes the next created variable will be this one...
535 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
520 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
536 [this, graphWidget, context, range](auto variable) {
521 [this, graphWidget, context, range](auto variable) {
537 if(sqpApp->variableController().isReady(variable))
522 if(sqpApp->variableController().isReady(variable))
538 {
523 {
539 graphWidget->addVariable(variable, range);
524 graphWidget->addVariable(variable, range);
540 delete context;
525 delete context;
541 }
526 }
542 else
527 else
543 {
528 {
544 // -> this is pure insanity! this is a workaround to make a bad design work
529 // -> this is pure insanity! this is a workaround to make a bad design work
545 QObject::connect(variable.get(), &Variable::updated,context,
530 QObject::connect(variable.get(), &Variable::updated,context,
546 [graphWidget, context, range, variable]()
531 [graphWidget, context, range, variable]()
547 {
532 {
548 graphWidget->addVariable(variable, range);
533 graphWidget->addVariable(variable, range);
549 delete context;
534 delete context;
550 });
535 });
551 }
536 }
552
537
553 },
538 },
554 Qt::QueuedConnection);
539 Qt::QueuedConnection);
555
540
556 auto productData = products.first().toHash();
541 auto productData = products.first().toHash();
557 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
542 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
558 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
543 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
559 }
544 }
560 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
545 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
561 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
546 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
562 graphWidget->setGraphRange(range, true, true);
547 graphWidget->setGraphRange(range, true, true);
563 }
548 }
564 else {
549 else {
565 qCWarning(LOG_VisualizationZoneWidget())
550 qCWarning(LOG_VisualizationZoneWidget())
566 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
551 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
567 }
552 }
568 }
553 }
569
554
570 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
555 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
571 int index, VisualizationZoneWidget *zoneWidget)
556 int index, VisualizationZoneWidget *zoneWidget)
572 {
557 {
573 auto &helper = sqpApp->dragDropGuiController();
558 auto &helper = sqpApp->dragDropGuiController();
574
559
575 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
560 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
576 if (!graphWidget) {
561 if (!graphWidget) {
577 qCWarning(LOG_VisualizationZoneWidget())
562 qCWarning(LOG_VisualizationZoneWidget())
578 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
563 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
579 "found or invalid.");
564 "found or invalid.");
580 Q_ASSERT(false);
565 Q_ASSERT(false);
581 return;
566 return;
582 }
567 }
583
568
584 auto parentDragDropContainer
569 auto parentDragDropContainer
585 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
570 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
586 if (!parentDragDropContainer) {
571 if (!parentDragDropContainer) {
587 qCWarning(LOG_VisualizationZoneWidget())
572 qCWarning(LOG_VisualizationZoneWidget())
588 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
573 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
589 "the dropped graph is not found.");
574 "the dropped graph is not found.");
590 Q_ASSERT(false);
575 Q_ASSERT(false);
591 return;
576 return;
592 }
577 }
593
578
594 const auto &variables = graphWidget->variables();
579 const auto &variables = graphWidget->variables();
595
580
596 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) {
581 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) {
597 // The drop didn't occur in the same zone
582 // The drop didn't occur in the same zone
598
583
599 // Abort the requests for the variables (if any)
584 // Abort the requests for the variables (if any)
600 // Commented, because it's not sure if it's needed or not
585 // Commented, because it's not sure if it's needed or not
601 // for (const auto& var : variables)
586 // for (const auto& var : variables)
602 //{
587 //{
603 // sqpApp->variableController().onAbortProgressRequested(var);
588 // sqpApp->variableController().onAbortProgressRequested(var);
604 //}
589 //}
605
590
606 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
591 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
607 auto nbGraph = parentDragDropContainer->countDragWidget();
592 auto nbGraph = parentDragDropContainer->countDragWidget();
608 if (nbGraph == 1) {
593 if (nbGraph == 1) {
609 // This is the only graph in the previous zone, close the zone
594 // This is the only graph in the previous zone, close the zone
610 helper.delayedCloseWidget(previousParentZoneWidget);
595 helper.delayedCloseWidget(previousParentZoneWidget);
611 }
596 }
612 else {
597 else {
613 // Close the graph
598 // Close the graph
614 helper.delayedCloseWidget(graphWidget);
599 helper.delayedCloseWidget(graphWidget);
615 }
600 }
616
601
617 // Creates the new graph in the zone
602 // Creates the new graph in the zone
618 auto newGraphWidget = zoneWidget->createGraph(variables, index);
603 auto newGraphWidget = zoneWidget->createGraph(variables, index);
619 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
604 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
620 }
605 }
621 else {
606 else {
622 // The drop occurred in the same zone or the graph is empty
607 // The drop occurred in the same zone or the graph is empty
623 // Simple move of the graph, no variable operation associated
608 // Simple move of the graph, no variable operation associated
624 parentDragDropContainer->layout()->removeWidget(graphWidget);
609 parentDragDropContainer->layout()->removeWidget(graphWidget);
625
610
626 if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
611 if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
627 // The graph is empty and dropped in a different zone.
612 // The graph is empty and dropped in a different zone.
628 // Take the range of the first graph in the zone (if existing).
613 // Take the range of the first graph in the zone (if existing).
629 auto layout = zoneWidget->ui->dragDropContainer->layout();
614 auto layout = zoneWidget->ui->dragDropContainer->layout();
630 if (layout->count() > 0) {
615 if (layout->count() > 0) {
631 if (auto visualizationGraphWidget
616 if (auto visualizationGraphWidget
632 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
617 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
633 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
618 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
634 }
619 }
635 }
620 }
636 }
621 }
637
622
638 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
623 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
639 }
624 }
640 }
625 }
641
626
642 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
627 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
643 const std::vector<std::shared_ptr<Variable> > &variables, int index,
628 const std::vector<std::shared_ptr<Variable> > &variables, int index,
644 VisualizationZoneWidget *zoneWidget)
629 VisualizationZoneWidget *zoneWidget)
645 {
630 {
646 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
631 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
647 // compatible variable here
632 // compatible variable here
648 if (variables.size() > 1) {
633 if (variables.size() > 1) {
649 return;
634 return;
650 }
635 }
651 zoneWidget->createGraph(variables, index);
636 zoneWidget->createGraph(variables, index);
652 }
637 }
653
638
654 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
639 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
655 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
640 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
656 {
641 {
657 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
642 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
658 // compatible variable here
643 // compatible variable here
659 if (productsData.count() != 1) {
644 if (productsData.count() != 1) {
660 qCWarning(LOG_VisualizationZoneWidget())
645 qCWarning(LOG_VisualizationZoneWidget())
661 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
646 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
662 "aborted.");
647 "aborted.");
663 return;
648 return;
664 }
649 }
665
650
666 auto context = new QObject{zoneWidget};
651 auto context = new QObject{zoneWidget};
667 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
652 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
668 [this, index, zoneWidget, context](auto variable) {
653 [this, index, zoneWidget, context](auto variable) {
669 zoneWidget->createGraph(variable, index);
654 zoneWidget->createGraph(variable, index);
670 delete context; // removes the connection
655 delete context; // removes the connection
671 },
656 },
672 Qt::QueuedConnection);
657 Qt::QueuedConnection);
673
658
674 auto productData = productsData.first().toHash();
659 auto productData = productsData.first().toHash();
675 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
660 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
676 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
661 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
677 }
662 }
General Comments 0
You need to be logged in to leave comments. Login now