##// END OF EJS Templates
Removed old Variable impl...
jeandet -
r1422:a323cf6b7201
parent child
Show More
@@ -1,396 +1,395
1 /*------------------------------------------------------------------------------
1 /*------------------------------------------------------------------------------
2 -- This file is a part of the SciQLop Software
2 -- This file is a part of the SciQLop Software
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 --
4 --
5 -- This program is free software; you can redistribute it and/or modify
5 -- This program is free software; you can redistribute it and/or modify
6 -- it under the terms of the GNU General Public License as published by
6 -- it under the terms of the GNU General Public License as published by
7 -- the Free Software Foundation; either version 2 of the License, or
7 -- the Free Software Foundation; either version 2 of the License, or
8 -- (at your option) any later version.
8 -- (at your option) any later version.
9 --
9 --
10 -- This program is distributed in the hope that it will be useful,
10 -- This program is distributed in the hope that it will be useful,
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 -- GNU General Public License for more details.
13 -- GNU General Public License for more details.
14 --
14 --
15 -- You should have received a copy of the GNU General Public License
15 -- You should have received a copy of the GNU General Public License
16 -- along with this program; if not, write to the Free Software
16 -- along with this program; if not, write to the Free Software
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 -------------------------------------------------------------------------------*/
18 -------------------------------------------------------------------------------*/
19 /*-- Author : Alexis Jeandet
19 /*-- Author : Alexis Jeandet
20 -- Mail : alexis.jeandet@member.fsf.org
20 -- Mail : alexis.jeandet@member.fsf.org
21 ----------------------------------------------------------------------------*/
21 ----------------------------------------------------------------------------*/
22 #include "MainWindow.h"
22 #include "MainWindow.h"
23 #include "ui_MainWindow.h"
23 #include "ui_MainWindow.h"
24
24
25 #include <Catalogue/CatalogueController.h>
25 #include <Catalogue/CatalogueController.h>
26 //#include <Catalogue/CatalogueExplorer.h>
26 //#include <Catalogue/CatalogueExplorer.h>
27 #include <DataSource/DataSourceController.h>
27 #include <DataSource/DataSourceController.h>
28 #include <DataSource/DataSourceWidget.h>
28 #include <DataSource/DataSourceWidget.h>
29 #include <Settings/SqpSettingsDialog.h>
29 #include <Settings/SqpSettingsDialog.h>
30 #include <Settings/SqpSettingsGeneralWidget.h>
30 #include <Settings/SqpSettingsGeneralWidget.h>
31 #include <SidePane/SqpSidePane.h>
31 #include <SidePane/SqpSidePane.h>
32 #include <SqpApplication.h>
32 #include <SqpApplication.h>
33 #include <Time/TimeController.h>
33 #include <Time/TimeController.h>
34 #include <TimeWidget/TimeWidget.h>
34 #include <TimeWidget/TimeWidget.h>
35 #include <Variable/Variable.h>
36 #include <Visualization/VisualizationController.h>
35 #include <Visualization/VisualizationController.h>
37
36
38 #include <QAction>
37 #include <QAction>
39 #include <QCloseEvent>
38 #include <QCloseEvent>
40 #include <QDate>
39 #include <QDate>
41 #include <QDir>
40 #include <QDir>
42 #include <QFileDialog>
41 #include <QFileDialog>
43 #include <QMessageBox>
42 #include <QMessageBox>
44 #include <QToolBar>
43 #include <QToolBar>
45 #include <QToolButton>
44 #include <QToolButton>
46 #include <memory.h>
45 #include <memory.h>
47
46
48 #include "iostream"
47 #include "iostream"
49
48
50 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
49 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
51
50
52 namespace
51 namespace
53 {
52 {
54 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
53 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
55 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
54 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
56 const auto VIEWPLITTERINDEX = 2;
55 const auto VIEWPLITTERINDEX = 2;
57 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
56 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
58 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
57 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
59 }
58 }
60
59
61 class MainWindow::MainWindowPrivate
60 class MainWindow::MainWindowPrivate
62 {
61 {
63 public:
62 public:
64 explicit MainWindowPrivate(MainWindow* mainWindow)
63 explicit MainWindowPrivate(MainWindow* mainWindow)
65 : m_LastOpenLeftInspectorSize {}
64 : m_LastOpenLeftInspectorSize {}
66 , m_LastOpenRightInspectorSize {}
65 , m_LastOpenRightInspectorSize {}
67 , m_GeneralSettingsWidget { new SqpSettingsGeneralWidget { mainWindow } }
66 , m_GeneralSettingsWidget { new SqpSettingsGeneralWidget { mainWindow } }
68 , m_SettingsDialog { new SqpSettingsDialog { mainWindow } }
67 , m_SettingsDialog { new SqpSettingsDialog { mainWindow } }
69 //, m_CatalogExplorer { new CatalogueExplorer { mainWindow } }
68 //, m_CatalogExplorer { new CatalogueExplorer { mainWindow } }
70 {
69 {
71 }
70 }
72
71
73 QSize m_LastOpenLeftInspectorSize;
72 QSize m_LastOpenLeftInspectorSize;
74 QSize m_LastOpenRightInspectorSize;
73 QSize m_LastOpenRightInspectorSize;
75 /// General settings widget. MainWindow has the ownership
74 /// General settings widget. MainWindow has the ownership
76 SqpSettingsGeneralWidget* m_GeneralSettingsWidget;
75 SqpSettingsGeneralWidget* m_GeneralSettingsWidget;
77 /// Settings dialog. MainWindow has the ownership
76 /// Settings dialog. MainWindow has the ownership
78 SqpSettingsDialog* m_SettingsDialog;
77 SqpSettingsDialog* m_SettingsDialog;
79 /// Catalogue dialog. MainWindow has the ownership
78 /// Catalogue dialog. MainWindow has the ownership
80 // CatalogueExplorer* m_CatalogExplorer;
79 // CatalogueExplorer* m_CatalogExplorer;
81
80
82 bool checkDataToSave(QWidget* parentWidget);
81 bool checkDataToSave(QWidget* parentWidget);
83 };
82 };
84
83
85 MainWindow::MainWindow(QWidget* parent)
84 MainWindow::MainWindow(QWidget* parent)
86 : QMainWindow { parent }
85 : QMainWindow { parent }
87 , m_Ui { new Ui::MainWindow }
86 , m_Ui { new Ui::MainWindow }
88 , impl { spimpl::make_unique_impl<MainWindowPrivate>(this) }
87 , impl { spimpl::make_unique_impl<MainWindowPrivate>(this) }
89 {
88 {
90 m_Ui->setupUi(this);
89 m_Ui->setupUi(this);
91
90
92 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
91 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
93 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
92 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
94
93
95 // impl->m_CatalogExplorer->setVisualizationWidget(m_Ui->view);
94 // impl->m_CatalogExplorer->setVisualizationWidget(m_Ui->view);
96
95
97
96
98 auto spacerLeftTop = new QWidget {};
97 auto spacerLeftTop = new QWidget {};
99 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
98 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
100
99
101 auto spacerLeftBottom = new QWidget {};
100 auto spacerLeftBottom = new QWidget {};
102 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
101 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
103
102
104
103
105 auto spacerRightTop = new QWidget {};
104 auto spacerRightTop = new QWidget {};
106 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
105 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
107
106
108 auto spacerRightBottom = new QWidget {};
107 auto spacerRightBottom = new QWidget {};
109 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
108 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
110
109
111
110
112 auto openInspector = [this](bool checked, bool right, auto action) {
111 auto openInspector = [this](bool checked, bool right, auto action) {
113 action->setIcon(
112 action->setIcon(
114 QIcon { (checked ^ right) ? ":/icones/next.png" : ":/icones/previous.png" });
113 QIcon { (checked ^ right) ? ":/icones/next.png" : ":/icones/previous.png" });
115
114
116 auto& lastInspectorSize
115 auto& lastInspectorSize
117 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
116 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
118
117
119 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
118 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
120 : m_Ui->leftMainInspectorWidget->size();
119 : m_Ui->leftMainInspectorWidget->size();
121
120
122 // Update of the last opened geometry
121 // Update of the last opened geometry
123 if (checked)
122 if (checked)
124 {
123 {
125 lastInspectorSize = nextInspectorSize;
124 lastInspectorSize = nextInspectorSize;
126 }
125 }
127
126
128 auto startSize = lastInspectorSize;
127 auto startSize = lastInspectorSize;
129 auto endSize = startSize;
128 auto endSize = startSize;
130 endSize.setWidth(0);
129 endSize.setWidth(0);
131
130
132 auto splitterInspectorIndex
131 auto splitterInspectorIndex
133 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
132 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
134
133
135 auto currentSizes = m_Ui->splitter->sizes();
134 auto currentSizes = m_Ui->splitter->sizes();
136 if (checked)
135 if (checked)
137 {
136 {
138 // adjust sizes individually here, e.g.
137 // adjust sizes individually here, e.g.
139 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
138 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
140 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
139 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
141 m_Ui->splitter->setSizes(currentSizes);
140 m_Ui->splitter->setSizes(currentSizes);
142 }
141 }
143 else
142 else
144 {
143 {
145 // adjust sizes individually here, e.g.
144 // adjust sizes individually here, e.g.
146 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
145 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
147 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
146 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
148 m_Ui->splitter->setSizes(currentSizes);
147 m_Ui->splitter->setSizes(currentSizes);
149 }
148 }
150 };
149 };
151
150
152
151
153 // //////////////// //
152 // //////////////// //
154 // Menu and Toolbar //
153 // Menu and Toolbar //
155 // //////////////// //
154 // //////////////// //
156 this->menuBar()->addAction(tr("File"));
155 this->menuBar()->addAction(tr("File"));
157 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
156 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
158 toolsMenu->addAction(tr("Settings..."), [this]() {
157 toolsMenu->addAction(tr("Settings..."), [this]() {
159 // Loads settings
158 // Loads settings
160 impl->m_SettingsDialog->loadSettings();
159 impl->m_SettingsDialog->loadSettings();
161
160
162 // Open settings dialog and save settings if the dialog is accepted
161 // Open settings dialog and save settings if the dialog is accepted
163 if (impl->m_SettingsDialog->exec() == QDialog::Accepted)
162 if (impl->m_SettingsDialog->exec() == QDialog::Accepted)
164 {
163 {
165 impl->m_SettingsDialog->saveSettings();
164 impl->m_SettingsDialog->saveSettings();
166 }
165 }
167 });
166 });
168
167
169 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
168 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
170
169
171 auto timeWidget = new TimeWidget {};
170 auto timeWidget = new TimeWidget {};
172 mainToolBar->addWidget(timeWidget);
171 mainToolBar->addWidget(timeWidget);
173
172
174 // Interaction modes
173 // Interaction modes
175 auto actionPointerMode = new QAction { QIcon(":/icones/pointer.png"), "Move", this };
174 auto actionPointerMode = new QAction { QIcon(":/icones/pointer.png"), "Move", this };
176 actionPointerMode->setCheckable(true);
175 actionPointerMode->setCheckable(true);
177 actionPointerMode->setChecked(
176 actionPointerMode->setChecked(
178 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None);
177 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None);
179 connect(actionPointerMode, &QAction::triggered,
178 connect(actionPointerMode, &QAction::triggered,
180 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::None); });
179 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::None); });
181
180
182 auto actionZoomMode = new QAction { QIcon(":/icones/zoom.png"), "Zoom", this };
181 auto actionZoomMode = new QAction { QIcon(":/icones/zoom.png"), "Zoom", this };
183 actionZoomMode->setCheckable(true);
182 actionZoomMode->setCheckable(true);
184 actionZoomMode->setChecked(
183 actionZoomMode->setChecked(
185 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox);
184 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox);
186 connect(actionZoomMode, &QAction::triggered,
185 connect(actionZoomMode, &QAction::triggered,
187 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::ZoomBox); });
186 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::ZoomBox); });
188
187
189 auto actionOrganisationMode = new QAction { QIcon(":/icones/drag.png"), "Organize", this };
188 auto actionOrganisationMode = new QAction { QIcon(":/icones/drag.png"), "Organize", this };
190 actionOrganisationMode->setCheckable(true);
189 actionOrganisationMode->setCheckable(true);
191 actionOrganisationMode->setChecked(
190 actionOrganisationMode->setChecked(
192 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::DragAndDrop);
191 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::DragAndDrop);
193 connect(actionOrganisationMode, &QAction::triggered, []() {
192 connect(actionOrganisationMode, &QAction::triggered, []() {
194 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::DragAndDrop);
193 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::DragAndDrop);
195 });
194 });
196
195
197 auto actionZonesMode = new QAction { QIcon(":/icones/rectangle.png"), "Zones", this };
196 auto actionZonesMode = new QAction { QIcon(":/icones/rectangle.png"), "Zones", this };
198 actionZonesMode->setCheckable(true);
197 actionZonesMode->setCheckable(true);
199 actionZonesMode->setChecked(
198 actionZonesMode->setChecked(
200 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
199 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
201 connect(actionZonesMode, &QAction::triggered, []() {
200 connect(actionZonesMode, &QAction::triggered, []() {
202 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::SelectionZones);
201 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::SelectionZones);
203 });
202 });
204
203
205 auto modeActionGroup = new QActionGroup { this };
204 auto modeActionGroup = new QActionGroup { this };
206 modeActionGroup->addAction(actionZoomMode);
205 modeActionGroup->addAction(actionZoomMode);
207 modeActionGroup->addAction(actionZonesMode);
206 modeActionGroup->addAction(actionZonesMode);
208 modeActionGroup->addAction(actionOrganisationMode);
207 modeActionGroup->addAction(actionOrganisationMode);
209 modeActionGroup->addAction(actionPointerMode);
208 modeActionGroup->addAction(actionPointerMode);
210 modeActionGroup->setExclusive(true);
209 modeActionGroup->setExclusive(true);
211
210
212 mainToolBar->addSeparator();
211 mainToolBar->addSeparator();
213 mainToolBar->addAction(actionPointerMode);
212 mainToolBar->addAction(actionPointerMode);
214 mainToolBar->addAction(actionZoomMode);
213 mainToolBar->addAction(actionZoomMode);
215 mainToolBar->addAction(actionOrganisationMode);
214 mainToolBar->addAction(actionOrganisationMode);
216 mainToolBar->addAction(actionZonesMode);
215 mainToolBar->addAction(actionZonesMode);
217 mainToolBar->addSeparator();
216 mainToolBar->addSeparator();
218
217
219 // Cursors
218 // Cursors
220 auto btnCursor = new QToolButton { this };
219 auto btnCursor = new QToolButton { this };
221 btnCursor->setIcon(QIcon(":/icones/cursor.png"));
220 btnCursor->setIcon(QIcon(":/icones/cursor.png"));
222 btnCursor->setText("Cursor");
221 btnCursor->setText("Cursor");
223 btnCursor->setToolTip("Cursor");
222 btnCursor->setToolTip("Cursor");
224 btnCursor->setPopupMode(QToolButton::InstantPopup);
223 btnCursor->setPopupMode(QToolButton::InstantPopup);
225 auto cursorMenu = new QMenu("CursorMenu", this);
224 auto cursorMenu = new QMenu("CursorMenu", this);
226 btnCursor->setMenu(cursorMenu);
225 btnCursor->setMenu(cursorMenu);
227
226
228 auto noCursorAction = cursorMenu->addAction("No Cursor");
227 auto noCursorAction = cursorMenu->addAction("No Cursor");
229 noCursorAction->setCheckable(true);
228 noCursorAction->setCheckable(true);
230 noCursorAction->setChecked(
229 noCursorAction->setChecked(
231 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::NoCursor);
230 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::NoCursor);
232 connect(noCursorAction, &QAction::triggered,
231 connect(noCursorAction, &QAction::triggered,
233 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::NoCursor); });
232 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::NoCursor); });
234
233
235 cursorMenu->addSeparator();
234 cursorMenu->addSeparator();
236 auto verticalCursorAction = cursorMenu->addAction("Vertical Cursor");
235 auto verticalCursorAction = cursorMenu->addAction("Vertical Cursor");
237 verticalCursorAction->setCheckable(true);
236 verticalCursorAction->setCheckable(true);
238 verticalCursorAction->setChecked(
237 verticalCursorAction->setChecked(
239 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::Vertical);
238 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::Vertical);
240 connect(verticalCursorAction, &QAction::triggered,
239 connect(verticalCursorAction, &QAction::triggered,
241 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Vertical); });
240 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Vertical); });
242
241
243 auto temporalCursorAction = cursorMenu->addAction("Temporal Cursor");
242 auto temporalCursorAction = cursorMenu->addAction("Temporal Cursor");
244 temporalCursorAction->setCheckable(true);
243 temporalCursorAction->setCheckable(true);
245 temporalCursorAction->setChecked(
244 temporalCursorAction->setChecked(
246 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::Temporal);
245 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::Temporal);
247 connect(temporalCursorAction, &QAction::triggered,
246 connect(temporalCursorAction, &QAction::triggered,
248 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Temporal); });
247 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Temporal); });
249
248
250 auto horizontalCursorAction = cursorMenu->addAction("Horizontal Cursor");
249 auto horizontalCursorAction = cursorMenu->addAction("Horizontal Cursor");
251 horizontalCursorAction->setCheckable(true);
250 horizontalCursorAction->setCheckable(true);
252 horizontalCursorAction->setChecked(
251 horizontalCursorAction->setChecked(
253 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::Horizontal);
252 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::Horizontal);
254 connect(horizontalCursorAction, &QAction::triggered,
253 connect(horizontalCursorAction, &QAction::triggered,
255 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Horizontal); });
254 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Horizontal); });
256
255
257 auto crossCursorAction = cursorMenu->addAction("Cross Cursor");
256 auto crossCursorAction = cursorMenu->addAction("Cross Cursor");
258 crossCursorAction->setCheckable(true);
257 crossCursorAction->setCheckable(true);
259 crossCursorAction->setChecked(
258 crossCursorAction->setChecked(
260 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::Cross);
259 sqpApp->plotsCursorMode() == SqpApplication::PlotsCursorMode::Cross);
261 connect(crossCursorAction, &QAction::triggered,
260 connect(crossCursorAction, &QAction::triggered,
262 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Cross); });
261 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Cross); });
263
262
264 mainToolBar->addWidget(btnCursor);
263 mainToolBar->addWidget(btnCursor);
265
264
266 auto cursorModeActionGroup = new QActionGroup { this };
265 auto cursorModeActionGroup = new QActionGroup { this };
267 cursorModeActionGroup->setExclusive(true);
266 cursorModeActionGroup->setExclusive(true);
268 cursorModeActionGroup->addAction(noCursorAction);
267 cursorModeActionGroup->addAction(noCursorAction);
269 cursorModeActionGroup->addAction(verticalCursorAction);
268 cursorModeActionGroup->addAction(verticalCursorAction);
270 cursorModeActionGroup->addAction(temporalCursorAction);
269 cursorModeActionGroup->addAction(temporalCursorAction);
271 cursorModeActionGroup->addAction(horizontalCursorAction);
270 cursorModeActionGroup->addAction(horizontalCursorAction);
272 cursorModeActionGroup->addAction(crossCursorAction);
271 cursorModeActionGroup->addAction(crossCursorAction);
273
272
274 // Catalog
273 // Catalog
275 mainToolBar->addSeparator();
274 mainToolBar->addSeparator();
276 // mainToolBar->addAction(QIcon(":/icones/catalogue.png"), "Catalogues",
275 // mainToolBar->addAction(QIcon(":/icones/catalogue.png"), "Catalogues",
277 // [this]() { impl->m_CatalogExplorer->show(); });
276 // [this]() { impl->m_CatalogExplorer->show(); });
278
277
279 // //////// //
278 // //////// //
280 // Settings //
279 // Settings //
281 // //////// //
280 // //////// //
282
281
283 // Registers "general settings" widget to the settings dialog
282 // Registers "general settings" widget to the settings dialog
284 impl->m_SettingsDialog->registerWidget(
283 impl->m_SettingsDialog->registerWidget(
285 QStringLiteral("General"), impl->m_GeneralSettingsWidget);
284 QStringLiteral("General"), impl->m_GeneralSettingsWidget);
286
285
287 // /////////// //
286 // /////////// //
288 // Connections //
287 // Connections //
289 // /////////// //
288 // /////////// //
290
289
291 // Controllers / controllers connections
290 // Controllers / controllers connections
292 // connect(&sqpApp->timeController(), SIGNAL(timeUpdated(DateTimeRange)),
291 // connect(&sqpApp->timeController(), SIGNAL(timeUpdated(DateTimeRange)),
293 // &sqpApp->variableController(),
292 // &sqpApp->variableController(),
294 // SLOT(onDateTimeOnSelection(DateTimeRange)));
293 // SLOT(onDateTimeOnSelection(DateTimeRange)));
295
294
296 // Widgets / controllers connections
295 // Widgets / controllers connections
297
296
298 // DataSource
297 // DataSource
299 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem*)),
298 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem*)),
300 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem*)));
299 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem*)));
301
300
302 // Time
301 // Time
303 connect(timeWidget, SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->timeController(),
302 connect(timeWidget, SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->timeController(),
304 SLOT(onTimeToUpdate(DateTimeRange)));
303 SLOT(onTimeToUpdate(DateTimeRange)));
305
304
306 // Visualization
305 // Visualization
307 connect(&sqpApp->visualizationController(),
306 connect(&sqpApp->visualizationController(),
308 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
307 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
309 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
308 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
310
309
311 connect(&sqpApp->visualizationController(),
310 connect(&sqpApp->visualizationController(),
312 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange&)), m_Ui->view,
311 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange&)), m_Ui->view,
313 SLOT(onRangeChanged(std::shared_ptr<Variable>, const DateTimeRange&)));
312 SLOT(onRangeChanged(std::shared_ptr<Variable>, const DateTimeRange&)));
314
313
315 // Widgets / widgets connections
314 // Widgets / widgets connections
316
315
317 // For the following connections, we use DirectConnection to allow each widget that can
316 // For the following connections, we use DirectConnection to allow each widget that can
318 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
317 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
319 // The order of connections is also important, since it determines the order in which each
318 // The order of connections is also important, since it determines the order in which each
320 // widget will attach its menu
319 // widget will attach its menu
321 connect(m_Ui->variableInspectorWidget,
320 connect(m_Ui->variableInspectorWidget,
322 SIGNAL(tableMenuAboutToBeDisplayed(QMenu*, const QVector<std::shared_ptr<Variable>>&)),
321 SIGNAL(tableMenuAboutToBeDisplayed(QMenu*, const QVector<std::shared_ptr<Variable>>&)),
323 m_Ui->view, SLOT(attachVariableMenu(QMenu*, const QVector<std::shared_ptr<Variable>>&)),
322 m_Ui->view, SLOT(attachVariableMenu(QMenu*, const QVector<std::shared_ptr<Variable>>&)),
324 Qt::DirectConnection);
323 Qt::DirectConnection);
325 }
324 }
326
325
327 MainWindow::~MainWindow() {}
326 MainWindow::~MainWindow() {}
328
327
329 void MainWindow::changeEvent(QEvent* e)
328 void MainWindow::changeEvent(QEvent* e)
330 {
329 {
331 QMainWindow::changeEvent(e);
330 QMainWindow::changeEvent(e);
332 switch (e->type())
331 switch (e->type())
333 {
332 {
334 case QEvent::LanguageChange:
333 case QEvent::LanguageChange:
335 m_Ui->retranslateUi(this);
334 m_Ui->retranslateUi(this);
336 break;
335 break;
337 default:
336 default:
338 break;
337 break;
339 }
338 }
340 }
339 }
341
340
342 void MainWindow::closeEvent(QCloseEvent* event)
341 void MainWindow::closeEvent(QCloseEvent* event)
343 {
342 {
344 if (!impl->checkDataToSave(this))
343 if (!impl->checkDataToSave(this))
345 {
344 {
346 event->ignore();
345 event->ignore();
347 }
346 }
348 else
347 else
349 {
348 {
350 event->accept();
349 event->accept();
351 }
350 }
352 }
351 }
353
352
354 void MainWindow::keyPressEvent(QKeyEvent* event)
353 void MainWindow::keyPressEvent(QKeyEvent* event)
355 {
354 {
356 switch (event->key())
355 switch (event->key())
357 {
356 {
358 case Qt::Key_F11:
357 case Qt::Key_F11:
359 if (this->isFullScreen())
358 if (this->isFullScreen())
360 {
359 {
361 this->showNormal();
360 this->showNormal();
362 }
361 }
363 else
362 else
364 {
363 {
365 this->showFullScreen();
364 this->showFullScreen();
366 }
365 }
367 break;
366 break;
368 default:
367 default:
369 break;
368 break;
370 }
369 }
371 }
370 }
372
371
373 bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget* parentWidget)
372 bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget* parentWidget)
374 {
373 {
375 // auto hasChanges = sqpApp->catalogueController().hasChanges();
374 // auto hasChanges = sqpApp->catalogueController().hasChanges();
376 // if (hasChanges)
375 // if (hasChanges)
377 // {
376 // {
378 // // There are some unsaved changes
377 // // There are some unsaved changes
379 // switch (QMessageBox::question(parentWidget, tr("Save changes"),
378 // switch (QMessageBox::question(parentWidget, tr("Save changes"),
380 // tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"),
379 // tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"),
381 // QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel,
380 // QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel,
382 // QMessageBox::SaveAll))
381 // QMessageBox::SaveAll))
383 // {
382 // {
384 // case QMessageBox::SaveAll:
383 // case QMessageBox::SaveAll:
385 // sqpApp->catalogueController().saveAll();
384 // sqpApp->catalogueController().saveAll();
386 // break;
385 // break;
387 // case QMessageBox::Discard:
386 // case QMessageBox::Discard:
388 // break;
387 // break;
389 // case QMessageBox::Cancel:
388 // case QMessageBox::Cancel:
390 // default:
389 // default:
391 // return false;
390 // return false;
392 // }
391 // }
393 // }
392 // }
394
393
395 return true;
394 return true;
396 }
395 }
@@ -1,1 +1,1
1 Subproject commit ae48b17833443bc2730a7511f964569dc7cdb1df
1 Subproject commit cc26524fb5d10feca3820e6921c9cd3cfb1a3591
@@ -1,332 +1,331
1 #include "DragAndDrop/DragDropGuiController.h"
1 #include "DragAndDrop/DragDropGuiController.h"
2 #include "DragAndDrop/DragDropScroller.h"
2 #include "DragAndDrop/DragDropScroller.h"
3 #include "DragAndDrop/DragDropTabSwitcher.h"
3 #include "DragAndDrop/DragDropTabSwitcher.h"
4 #include "SqpApplication.h"
4 #include "SqpApplication.h"
5 #include "Visualization/VisualizationDragDropContainer.h"
5 #include "Visualization/VisualizationDragDropContainer.h"
6 #include "Visualization/VisualizationDragWidget.h"
6 #include "Visualization/VisualizationDragWidget.h"
7 #include "Visualization/VisualizationWidget.h"
7 #include "Visualization/VisualizationWidget.h"
8 #include "Visualization/operations/FindVariableOperation.h"
8 #include "Visualization/operations/FindVariableOperation.h"
9
9
10 #include "DataSource/DataSourceController.h"
10 #include "DataSource/DataSourceController.h"
11 #include "Variable/Variable.h"
12 #include "Variable/VariableController2.h"
11 #include "Variable/VariableController2.h"
13
12
14 #include "Common/MimeTypesDef.h"
13 #include "Common/MimeTypesDef.h"
15 #include "Common/VisualizationDef.h"
14 #include "Common/VisualizationDef.h"
16
15
17 #include <QDir>
16 #include <QDir>
18 #include <QLabel>
17 #include <QLabel>
19 #include <QUrl>
18 #include <QUrl>
20 #include <QVBoxLayout>
19 #include <QVBoxLayout>
21
20
22
21
23 Q_LOGGING_CATEGORY(LOG_DragDropGuiController, "DragDropGuiController")
22 Q_LOGGING_CATEGORY(LOG_DragDropGuiController, "DragDropGuiController")
24
23
25
24
26 struct DragDropGuiController::DragDropGuiControllerPrivate
25 struct DragDropGuiController::DragDropGuiControllerPrivate
27 {
26 {
28
27
29 VisualizationDragWidget* m_CurrentDragWidget = nullptr;
28 VisualizationDragWidget* m_CurrentDragWidget = nullptr;
30 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
29 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
31 QLabel* m_PlaceHolderLabel;
30 QLabel* m_PlaceHolderLabel;
32 QWidget* m_PlaceBackground;
31 QWidget* m_PlaceBackground;
33 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
32 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
34 std::unique_ptr<DragDropTabSwitcher> m_DragDropTabSwitcher = nullptr;
33 std::unique_ptr<DragDropTabSwitcher> m_DragDropTabSwitcher = nullptr;
35 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
34 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
36 // QTemporaryFile to have a name which is not generated.
35 // QTemporaryFile to have a name which is not generated.
37
36
38 VisualizationDragWidget* m_HighlightedDragWidget = nullptr;
37 VisualizationDragWidget* m_HighlightedDragWidget = nullptr;
39
38
40 QMetaObject::Connection m_DragWidgetDestroyedConnection;
39 QMetaObject::Connection m_DragWidgetDestroyedConnection;
41 QMetaObject::Connection m_HighlightedWidgetDestroyedConnection;
40 QMetaObject::Connection m_HighlightedWidgetDestroyedConnection;
42
41
43 QList<QWidget*> m_WidgetToClose;
42 QList<QWidget*> m_WidgetToClose;
44
43
45 explicit DragDropGuiControllerPrivate()
44 explicit DragDropGuiControllerPrivate()
46 : m_PlaceHolder { std::make_unique<QWidget>() }
45 : m_PlaceHolder { std::make_unique<QWidget>() }
47 , m_DragDropScroller { std::make_unique<DragDropScroller>() }
46 , m_DragDropScroller { std::make_unique<DragDropScroller>() }
48 , m_DragDropTabSwitcher { std::make_unique<DragDropTabSwitcher>() }
47 , m_DragDropTabSwitcher { std::make_unique<DragDropTabSwitcher>() }
49 {
48 {
50
49
51 auto layout = new QVBoxLayout { m_PlaceHolder.get() };
50 auto layout = new QVBoxLayout { m_PlaceHolder.get() };
52 layout->setSpacing(0);
51 layout->setSpacing(0);
53 layout->setContentsMargins(0, 0, 0, 0);
52 layout->setContentsMargins(0, 0, 0, 0);
54
53
55 m_PlaceHolderLabel = new QLabel { "", m_PlaceHolder.get() };
54 m_PlaceHolderLabel = new QLabel { "", m_PlaceHolder.get() };
56 m_PlaceHolderLabel->setMinimumHeight(25);
55 m_PlaceHolderLabel->setMinimumHeight(25);
57 layout->addWidget(m_PlaceHolderLabel);
56 layout->addWidget(m_PlaceHolderLabel);
58
57
59 m_PlaceBackground = new QWidget { m_PlaceHolder.get() };
58 m_PlaceBackground = new QWidget { m_PlaceHolder.get() };
60 m_PlaceBackground->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
59 m_PlaceBackground->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
61 layout->addWidget(m_PlaceBackground);
60 layout->addWidget(m_PlaceBackground);
62
61
63 sqpApp->installEventFilter(m_DragDropScroller.get());
62 sqpApp->installEventFilter(m_DragDropScroller.get());
64 sqpApp->installEventFilter(m_DragDropTabSwitcher.get());
63 sqpApp->installEventFilter(m_DragDropTabSwitcher.get());
65
64
66 m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png");
65 m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png");
67 }
66 }
68
67
69 void preparePlaceHolder(
68 void preparePlaceHolder(
70 DragDropGuiController::PlaceHolderType type, const QString& topLabelText) const
69 DragDropGuiController::PlaceHolderType type, const QString& topLabelText) const
71 {
70 {
72 if (m_CurrentDragWidget)
71 if (m_CurrentDragWidget)
73 {
72 {
74 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
73 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
75 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
74 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
76 }
75 }
77 else
76 else
78 {
77 {
79 // Configuration of the placeHolder when there is no dragWidget
78 // Configuration of the placeHolder when there is no dragWidget
80 // (for instance with a drag from a variable)
79 // (for instance with a drag from a variable)
81
80
82 m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
81 m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
83 m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
82 m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
84 }
83 }
85
84
86 switch (type)
85 switch (type)
87 {
86 {
88 case DragDropGuiController::PlaceHolderType::Graph:
87 case DragDropGuiController::PlaceHolderType::Graph:
89 m_PlaceBackground->setStyleSheet(
88 m_PlaceBackground->setStyleSheet(
90 "background-color: #BBD5EE; border: 1px solid #2A7FD4");
89 "background-color: #BBD5EE; border: 1px solid #2A7FD4");
91 break;
90 break;
92 case DragDropGuiController::PlaceHolderType::Zone:
91 case DragDropGuiController::PlaceHolderType::Zone:
93 case DragDropGuiController::PlaceHolderType::Default:
92 case DragDropGuiController::PlaceHolderType::Default:
94 m_PlaceBackground->setStyleSheet(
93 m_PlaceBackground->setStyleSheet(
95 "background-color: #BBD5EE; border: 2px solid #2A7FD4");
94 "background-color: #BBD5EE; border: 2px solid #2A7FD4");
96 m_PlaceHolderLabel->setStyleSheet("color: #2A7FD4");
95 m_PlaceHolderLabel->setStyleSheet("color: #2A7FD4");
97 break;
96 break;
98 }
97 }
99
98
100 m_PlaceHolderLabel->setText(topLabelText);
99 m_PlaceHolderLabel->setText(topLabelText);
101 m_PlaceHolderLabel->setVisible(!topLabelText.isEmpty());
100 m_PlaceHolderLabel->setVisible(!topLabelText.isEmpty());
102 }
101 }
103 };
102 };
104
103
105
104
106 DragDropGuiController::DragDropGuiController()
105 DragDropGuiController::DragDropGuiController()
107 : impl { spimpl::make_unique_impl<DragDropGuiControllerPrivate>() }
106 : impl { spimpl::make_unique_impl<DragDropGuiControllerPrivate>() }
108 {
107 {
109 }
108 }
110
109
111 DragDropGuiController::~DragDropGuiController()
110 DragDropGuiController::~DragDropGuiController()
112 {
111 {
113 QFile::remove(impl->m_ImageTempUrl);
112 QFile::remove(impl->m_ImageTempUrl);
114 }
113 }
115
114
116 void DragDropGuiController::resetDragAndDrop()
115 void DragDropGuiController::resetDragAndDrop()
117 {
116 {
118 setCurrentDragWidget(nullptr);
117 setCurrentDragWidget(nullptr);
119 impl->m_HighlightedDragWidget = nullptr;
118 impl->m_HighlightedDragWidget = nullptr;
120 }
119 }
121
120
122 void DragDropGuiController::setCurrentDragWidget(VisualizationDragWidget* dragWidget)
121 void DragDropGuiController::setCurrentDragWidget(VisualizationDragWidget* dragWidget)
123 {
122 {
124 if (impl->m_CurrentDragWidget)
123 if (impl->m_CurrentDragWidget)
125 {
124 {
126
125
127 QObject::disconnect(impl->m_DragWidgetDestroyedConnection);
126 QObject::disconnect(impl->m_DragWidgetDestroyedConnection);
128 }
127 }
129
128
130 if (dragWidget)
129 if (dragWidget)
131 {
130 {
132 // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed
131 // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed
133 impl->m_DragWidgetDestroyedConnection = QObject::connect(dragWidget,
132 impl->m_DragWidgetDestroyedConnection = QObject::connect(dragWidget,
134 &VisualizationDragWidget::destroyed, [this]() { impl->m_CurrentDragWidget = nullptr; });
133 &VisualizationDragWidget::destroyed, [this]() { impl->m_CurrentDragWidget = nullptr; });
135 }
134 }
136
135
137 impl->m_CurrentDragWidget = dragWidget;
136 impl->m_CurrentDragWidget = dragWidget;
138 }
137 }
139
138
140 VisualizationDragWidget* DragDropGuiController::getCurrentDragWidget() const
139 VisualizationDragWidget* DragDropGuiController::getCurrentDragWidget() const
141 {
140 {
142 return impl->m_CurrentDragWidget;
141 return impl->m_CurrentDragWidget;
143 }
142 }
144
143
145 QWidget& DragDropGuiController::placeHolder() const
144 QWidget& DragDropGuiController::placeHolder() const
146 {
145 {
147 return *impl->m_PlaceHolder;
146 return *impl->m_PlaceHolder;
148 }
147 }
149
148
150 void DragDropGuiController::insertPlaceHolder(
149 void DragDropGuiController::insertPlaceHolder(
151 QVBoxLayout* layout, int index, PlaceHolderType type, const QString& topLabelText)
150 QVBoxLayout* layout, int index, PlaceHolderType type, const QString& topLabelText)
152 {
151 {
153 removePlaceHolder();
152 removePlaceHolder();
154 impl->preparePlaceHolder(type, topLabelText);
153 impl->preparePlaceHolder(type, topLabelText);
155 layout->insertWidget(index, impl->m_PlaceHolder.get());
154 layout->insertWidget(index, impl->m_PlaceHolder.get());
156 impl->m_PlaceHolder->show();
155 impl->m_PlaceHolder->show();
157 }
156 }
158
157
159 void DragDropGuiController::removePlaceHolder()
158 void DragDropGuiController::removePlaceHolder()
160 {
159 {
161 auto parentWidget = impl->m_PlaceHolder->parentWidget();
160 auto parentWidget = impl->m_PlaceHolder->parentWidget();
162 if (parentWidget)
161 if (parentWidget)
163 {
162 {
164 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
163 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
165 impl->m_PlaceHolder->setParent(nullptr);
164 impl->m_PlaceHolder->setParent(nullptr);
166 impl->m_PlaceHolder->hide();
165 impl->m_PlaceHolder->hide();
167 }
166 }
168 }
167 }
169
168
170 bool DragDropGuiController::isPlaceHolderSet() const
169 bool DragDropGuiController::isPlaceHolderSet() const
171 {
170 {
172 return impl->m_PlaceHolder->parentWidget();
171 return impl->m_PlaceHolder->parentWidget();
173 }
172 }
174
173
175 void DragDropGuiController::addDragDropScrollArea(QScrollArea* scrollArea)
174 void DragDropGuiController::addDragDropScrollArea(QScrollArea* scrollArea)
176 {
175 {
177 impl->m_DragDropScroller->addScrollArea(scrollArea);
176 impl->m_DragDropScroller->addScrollArea(scrollArea);
178 }
177 }
179
178
180 void DragDropGuiController::removeDragDropScrollArea(QScrollArea* scrollArea)
179 void DragDropGuiController::removeDragDropScrollArea(QScrollArea* scrollArea)
181 {
180 {
182 impl->m_DragDropScroller->removeScrollArea(scrollArea);
181 impl->m_DragDropScroller->removeScrollArea(scrollArea);
183 }
182 }
184
183
185 void DragDropGuiController::addDragDropTabBar(QTabBar* tabBar)
184 void DragDropGuiController::addDragDropTabBar(QTabBar* tabBar)
186 {
185 {
187 impl->m_DragDropTabSwitcher->addTabBar(tabBar);
186 impl->m_DragDropTabSwitcher->addTabBar(tabBar);
188 }
187 }
189
188
190 void DragDropGuiController::removeDragDropTabBar(QTabBar* tabBar)
189 void DragDropGuiController::removeDragDropTabBar(QTabBar* tabBar)
191 {
190 {
192 impl->m_DragDropTabSwitcher->removeTabBar(tabBar);
191 impl->m_DragDropTabSwitcher->removeTabBar(tabBar);
193 }
192 }
194
193
195 QUrl DragDropGuiController::imageTemporaryUrl(const QImage& image) const
194 QUrl DragDropGuiController::imageTemporaryUrl(const QImage& image) const
196 {
195 {
197 image.save(impl->m_ImageTempUrl);
196 image.save(impl->m_ImageTempUrl);
198 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
197 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
199 }
198 }
200
199
201 void DragDropGuiController::setHightlightedDragWidget(VisualizationDragWidget* dragWidget)
200 void DragDropGuiController::setHightlightedDragWidget(VisualizationDragWidget* dragWidget)
202 {
201 {
203 if (impl->m_HighlightedDragWidget)
202 if (impl->m_HighlightedDragWidget)
204 {
203 {
205 impl->m_HighlightedDragWidget->highlightForMerge(false);
204 impl->m_HighlightedDragWidget->highlightForMerge(false);
206 QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection);
205 QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection);
207 }
206 }
208
207
209 if (dragWidget)
208 if (dragWidget)
210 {
209 {
211 dragWidget->highlightForMerge(true);
210 dragWidget->highlightForMerge(true);
212
211
213 // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed
212 // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed
214 impl->m_DragWidgetDestroyedConnection
213 impl->m_DragWidgetDestroyedConnection
215 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
214 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
216 [this]() { impl->m_HighlightedDragWidget = nullptr; });
215 [this]() { impl->m_HighlightedDragWidget = nullptr; });
217 }
216 }
218
217
219 impl->m_HighlightedDragWidget = dragWidget;
218 impl->m_HighlightedDragWidget = dragWidget;
220 }
219 }
221
220
222 VisualizationDragWidget* DragDropGuiController::getHightlightedDragWidget() const
221 VisualizationDragWidget* DragDropGuiController::getHightlightedDragWidget() const
223 {
222 {
224 return impl->m_HighlightedDragWidget;
223 return impl->m_HighlightedDragWidget;
225 }
224 }
226
225
227 void DragDropGuiController::delayedCloseWidget(QWidget* widget)
226 void DragDropGuiController::delayedCloseWidget(QWidget* widget)
228 {
227 {
229 widget->hide();
228 widget->hide();
230 impl->m_WidgetToClose << widget;
229 impl->m_WidgetToClose << widget;
231 }
230 }
232
231
233 void DragDropGuiController::doCloseWidgets()
232 void DragDropGuiController::doCloseWidgets()
234 {
233 {
235 for (auto widget : impl->m_WidgetToClose)
234 for (auto widget : impl->m_WidgetToClose)
236 {
235 {
237 widget->close();
236 widget->close();
238 }
237 }
239
238
240 impl->m_WidgetToClose.clear();
239 impl->m_WidgetToClose.clear();
241 }
240 }
242
241
243 bool DragDropGuiController::checkMimeDataForVisualization(
242 bool DragDropGuiController::checkMimeDataForVisualization(
244 const QMimeData* mimeData, VisualizationDragDropContainer* dropContainer)
243 const QMimeData* mimeData, VisualizationDragDropContainer* dropContainer)
245 {
244 {
246 if (!mimeData || !dropContainer)
245 if (!mimeData || !dropContainer)
247 {
246 {
248 qCWarning(LOG_DragDropGuiController()) << QObject::tr(
247 qCWarning(LOG_DragDropGuiController()) << QObject::tr(
249 "DragDropGuiController::checkMimeDataForVisualization, invalid input parameters.");
248 "DragDropGuiController::checkMimeDataForVisualization, invalid input parameters.");
250 Q_ASSERT(false);
249 Q_ASSERT(false);
251 return false;
250 return false;
252 }
251 }
253
252
254 auto result = false;
253 auto result = false;
255
254
256 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST))
255 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST))
257 {
256 {
258 auto variables = sqpApp->variableController().variables(
257 auto variables = sqpApp->variableController().variables(
259 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
258 Variable2::IDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
260
259
261 if (variables.size() == 1)
260 if (variables.size() == 1)
262 {
261 {
263
262
264 auto variable = variables[0];
263 auto variable = variables[0];
265 if (variable->data() != nullptr)
264 if (variable->data() != nullptr)
266 {
265 {
267
266
268 // Check that the variable is not already in a graph
267 // Check that the variable is not already in a graph
269
268
270 auto parent = dropContainer->parentWidget();
269 auto parent = dropContainer->parentWidget();
271 while (parent && qobject_cast<VisualizationWidget*>(parent) == nullptr)
270 while (parent && qobject_cast<VisualizationWidget*>(parent) == nullptr)
272 {
271 {
273 parent = parent->parentWidget(); // Search for the top level VisualizationWidget
272 parent = parent->parentWidget(); // Search for the top level VisualizationWidget
274 }
273 }
275
274
276 if (parent)
275 if (parent)
277 {
276 {
278 auto visualizationWidget = static_cast<VisualizationWidget*>(parent);
277 auto visualizationWidget = static_cast<VisualizationWidget*>(parent);
279
278
280 FindVariableOperation findVariableOperation { variable };
279 FindVariableOperation findVariableOperation { variable };
281 visualizationWidget->accept(&findVariableOperation);
280 visualizationWidget->accept(&findVariableOperation);
282 auto variableContainers = findVariableOperation.result();
281 auto variableContainers = findVariableOperation.result();
283 if (variableContainers.empty())
282 if (variableContainers.empty())
284 {
283 {
285 result = true;
284 result = true;
286 }
285 }
287 else
286 else
288 {
287 {
289 // result = false: the variable already exist in the visualisation
288 // result = false: the variable already exist in the visualisation
290 }
289 }
291 }
290 }
292 else
291 else
293 {
292 {
294 qCWarning(LOG_DragDropGuiController()) << QObject::tr(
293 qCWarning(LOG_DragDropGuiController()) << QObject::tr(
295 "DragDropGuiController::checkMimeDataForVisualization, the parent "
294 "DragDropGuiController::checkMimeDataForVisualization, the parent "
296 "VisualizationWidget cannot be found. Cannot check if the variable is "
295 "VisualizationWidget cannot be found. Cannot check if the variable is "
297 "already used or not.");
296 "already used or not.");
298 }
297 }
299 }
298 }
300 else
299 else
301 {
300 {
302 // result = false: the variable is not fully loaded
301 // result = false: the variable is not fully loaded
303 }
302 }
304 }
303 }
305 else
304 else
306 {
305 {
307 // result = false: cannot drop multiple variables in the visualisation
306 // result = false: cannot drop multiple variables in the visualisation
308 }
307 }
309 }
308 }
310 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST))
309 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST))
311 {
310 {
312 auto productDataList = sqpApp->dataSourceController().productsDataForMimeData(
311 auto productDataList = sqpApp->dataSourceController().productsDataForMimeData(
313 mimeData->data(MIME_TYPE_PRODUCT_LIST));
312 mimeData->data(MIME_TYPE_PRODUCT_LIST));
314 if (productDataList.count() == 1)
313 if (productDataList.count() == 1)
315 {
314 {
316 result = true;
315 result = true;
317 }
316 }
318 else
317 else
319 {
318 {
320 // result = false: cannot drop multiple products in the visualisation
319 // result = false: cannot drop multiple products in the visualisation
321 }
320 }
322 }
321 }
323 else
322 else
324 {
323 {
325 // Other MIME data
324 // Other MIME data
326 // no special rules, accepted by default
325 // no special rules, accepted by default
327 result = true;
326 result = true;
328 }
327 }
329
328
330
329
331 return result;
330 return result;
332 }
331 }
@@ -1,193 +1,192
1 #include "SqpApplication.h"
1 #include "SqpApplication.h"
2
2
3 #include <Actions/ActionsGuiController.h>
3 #include <Actions/ActionsGuiController.h>
4 #include <Catalogue/CatalogueController.h>
4 #include <Catalogue/CatalogueController.h>
5 #include <Data/IDataProvider.h>
5 #include <Data/IDataProvider.h>
6 #include <DataSource/DataSourceController.h>
6 #include <DataSource/DataSourceController.h>
7 #include <DragAndDrop/DragDropGuiController.h>
7 #include <DragAndDrop/DragDropGuiController.h>
8 #include <Network/NetworkController.h>
8 #include <Network/NetworkController.h>
9 #include <QThread>
9 #include <QThread>
10 #include <Time/TimeController.h>
10 #include <Time/TimeController.h>
11 #include <Variable/Variable.h>
12 #include <Variable/VariableController2.h>
11 #include <Variable/VariableController2.h>
13 #include <Variable/VariableModel2.h>
12 #include <Variable/VariableModel2.h>
14 #include <Visualization/VisualizationController.h>
13 #include <Visualization/VisualizationController.h>
15
14
16 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
15 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
17
16
18 class SqpApplication::SqpApplicationPrivate
17 class SqpApplication::SqpApplicationPrivate
19 {
18 {
20 public:
19 public:
21 SqpApplicationPrivate()
20 SqpApplicationPrivate()
22 : m_VariableController { std::make_shared<VariableController2>() }
21 : m_VariableController { std::make_shared<VariableController2>() }
23 , m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None)
22 , m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None)
24 , m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor)
23 , m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor)
25 {
24 {
26 // /////////////////////////////// //
25 // /////////////////////////////// //
27 // Connections between controllers //
26 // Connections between controllers //
28 // /////////////////////////////// //
27 // /////////////////////////////// //
29
28
30 // VariableController <-> DataSourceController
29 // VariableController <-> DataSourceController
31 connect(&m_DataSourceController, &DataSourceController::createVariable,
30 connect(&m_DataSourceController, &DataSourceController::createVariable,
32 [](const QString& variableName, const QVariantHash& variableMetadata,
31 [](const QString& variableName, const QVariantHash& variableMetadata,
33 std::shared_ptr<IDataProvider> variableProvider) {
32 std::shared_ptr<IDataProvider> variableProvider) {
34 sqpApp->variableController().createVariable(variableName, variableMetadata,
33 sqpApp->variableController().createVariable(variableName, variableMetadata,
35 variableProvider, sqpApp->timeController().dateTime());
34 variableProvider, sqpApp->timeController().dateTime());
36 });
35 });
37
36
38 // VariableController <-> VisualizationController
37 // VariableController <-> VisualizationController
39 // connect(m_VariableController.get(),
38 // connect(m_VariableController.get(),
40 // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
39 // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
41 // m_VisualizationController.get(),
40 // m_VisualizationController.get(),
42 // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
41 // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
43 // Qt::DirectConnection);
42 // Qt::DirectConnection);
44
43
45 // connect(m_VariableController.get(),
44 // connect(m_VariableController.get(),
46 // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)),
45 // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)),
47 // m_VisualizationController.get(),
46 // m_VisualizationController.get(),
48 // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)));
47 // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)));
49
48
50
49
51 m_DataSourceController.moveToThread(&m_DataSourceControllerThread);
50 m_DataSourceController.moveToThread(&m_DataSourceControllerThread);
52 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
51 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
53 m_NetworkController.moveToThread(&m_NetworkControllerThread);
52 m_NetworkController.moveToThread(&m_NetworkControllerThread);
54 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
53 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
55 m_VisualizationController.moveToThread(&m_VisualizationControllerThread);
54 m_VisualizationController.moveToThread(&m_VisualizationControllerThread);
56 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
55 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
57
56
58 // Additionnal init
57 // Additionnal init
59 // m_VariableController->setTimeController(m_TimeController.get());
58 // m_VariableController->setTimeController(m_TimeController.get());
60 }
59 }
61
60
62 virtual ~SqpApplicationPrivate()
61 virtual ~SqpApplicationPrivate()
63 {
62 {
64 m_DataSourceControllerThread.quit();
63 m_DataSourceControllerThread.quit();
65 m_DataSourceControllerThread.wait();
64 m_DataSourceControllerThread.wait();
66
65
67 m_NetworkControllerThread.quit();
66 m_NetworkControllerThread.quit();
68 m_NetworkControllerThread.wait();
67 m_NetworkControllerThread.wait();
69
68
70 m_VisualizationControllerThread.quit();
69 m_VisualizationControllerThread.quit();
71 m_VisualizationControllerThread.wait();
70 m_VisualizationControllerThread.wait();
72 }
71 }
73
72
74 DataSourceController m_DataSourceController;
73 DataSourceController m_DataSourceController;
75 std::shared_ptr<VariableController2> m_VariableController;
74 std::shared_ptr<VariableController2> m_VariableController;
76 TimeController m_TimeController;
75 TimeController m_TimeController;
77 NetworkController m_NetworkController;
76 NetworkController m_NetworkController;
78 VisualizationController m_VisualizationController;
77 VisualizationController m_VisualizationController;
79 CatalogueController m_CatalogueController;
78 CatalogueController m_CatalogueController;
80
79
81 QThread m_DataSourceControllerThread;
80 QThread m_DataSourceControllerThread;
82 QThread m_NetworkControllerThread;
81 QThread m_NetworkControllerThread;
83 QThread m_VisualizationControllerThread;
82 QThread m_VisualizationControllerThread;
84
83
85 DragDropGuiController m_DragDropGuiController;
84 DragDropGuiController m_DragDropGuiController;
86 ActionsGuiController m_ActionsGuiController;
85 ActionsGuiController m_ActionsGuiController;
87
86
88 SqpApplication::PlotsInteractionMode m_PlotInterractionMode;
87 SqpApplication::PlotsInteractionMode m_PlotInterractionMode;
89 SqpApplication::PlotsCursorMode m_PlotCursorMode;
88 SqpApplication::PlotsCursorMode m_PlotCursorMode;
90 };
89 };
91
90
92
91
93 SqpApplication::SqpApplication(int& argc, char** argv)
92 SqpApplication::SqpApplication(int& argc, char** argv)
94 : QApplication { argc, argv }, impl { spimpl::make_unique_impl<SqpApplicationPrivate>() }
93 : QApplication { argc, argv }, impl { spimpl::make_unique_impl<SqpApplicationPrivate>() }
95 {
94 {
96 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
95 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
97
96
98 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
97 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
99
98
100 connect(&impl->m_DataSourceControllerThread, &QThread::started, &impl->m_DataSourceController,
99 connect(&impl->m_DataSourceControllerThread, &QThread::started, &impl->m_DataSourceController,
101 &DataSourceController::initialize);
100 &DataSourceController::initialize);
102 connect(&impl->m_DataSourceControllerThread, &QThread::finished, &impl->m_DataSourceController,
101 connect(&impl->m_DataSourceControllerThread, &QThread::finished, &impl->m_DataSourceController,
103 &DataSourceController::finalize);
102 &DataSourceController::finalize);
104
103
105 connect(&impl->m_NetworkControllerThread, &QThread::started, &impl->m_NetworkController,
104 connect(&impl->m_NetworkControllerThread, &QThread::started, &impl->m_NetworkController,
106 &NetworkController::initialize);
105 &NetworkController::initialize);
107 connect(&impl->m_NetworkControllerThread, &QThread::finished, &impl->m_NetworkController,
106 connect(&impl->m_NetworkControllerThread, &QThread::finished, &impl->m_NetworkController,
108 &NetworkController::finalize);
107 &NetworkController::finalize);
109
108
110 connect(&impl->m_VisualizationControllerThread, &QThread::started,
109 connect(&impl->m_VisualizationControllerThread, &QThread::started,
111 &impl->m_VisualizationController, &VisualizationController::initialize);
110 &impl->m_VisualizationController, &VisualizationController::initialize);
112 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
111 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
113 &impl->m_VisualizationController, &VisualizationController::finalize);
112 &impl->m_VisualizationController, &VisualizationController::finalize);
114
113
115 impl->m_DataSourceControllerThread.start();
114 impl->m_DataSourceControllerThread.start();
116 impl->m_NetworkControllerThread.start();
115 impl->m_NetworkControllerThread.start();
117 impl->m_VisualizationControllerThread.start();
116 impl->m_VisualizationControllerThread.start();
118 // impl->m_CatalogueController.initialize();
117 // impl->m_CatalogueController.initialize();
119 }
118 }
120
119
121 SqpApplication::~SqpApplication() {}
120 SqpApplication::~SqpApplication() {}
122
121
123 void SqpApplication::initialize() {}
122 void SqpApplication::initialize() {}
124
123
125 DataSourceController& SqpApplication::dataSourceController() noexcept
124 DataSourceController& SqpApplication::dataSourceController() noexcept
126 {
125 {
127 return impl->m_DataSourceController;
126 return impl->m_DataSourceController;
128 }
127 }
129
128
130 NetworkController& SqpApplication::networkController() noexcept
129 NetworkController& SqpApplication::networkController() noexcept
131 {
130 {
132 return impl->m_NetworkController;
131 return impl->m_NetworkController;
133 }
132 }
134
133
135 TimeController& SqpApplication::timeController() noexcept
134 TimeController& SqpApplication::timeController() noexcept
136 {
135 {
137 return impl->m_TimeController;
136 return impl->m_TimeController;
138 }
137 }
139
138
140 VariableController2& SqpApplication::variableController() noexcept
139 VariableController2& SqpApplication::variableController() noexcept
141 {
140 {
142 return *impl->m_VariableController;
141 return *impl->m_VariableController;
143 }
142 }
144
143
145 std::shared_ptr<VariableController2> SqpApplication::variableControllerOwner() noexcept
144 std::shared_ptr<VariableController2> SqpApplication::variableControllerOwner() noexcept
146 {
145 {
147 return impl->m_VariableController;
146 return impl->m_VariableController;
148 }
147 }
149
148
150 // VariableModel2 &SqpApplication::variableModel() noexcept
149 // VariableModel2 &SqpApplication::variableModel() noexcept
151 //{
150 //{
152 // return impl->m_VariableModel;
151 // return impl->m_VariableModel;
153 //}
152 //}
154
153
155 VisualizationController& SqpApplication::visualizationController() noexcept
154 VisualizationController& SqpApplication::visualizationController() noexcept
156 {
155 {
157 return impl->m_VisualizationController;
156 return impl->m_VisualizationController;
158 }
157 }
159
158
160 CatalogueController& SqpApplication::catalogueController() noexcept
159 CatalogueController& SqpApplication::catalogueController() noexcept
161 {
160 {
162 return impl->m_CatalogueController;
161 return impl->m_CatalogueController;
163 }
162 }
164
163
165 DragDropGuiController& SqpApplication::dragDropGuiController() noexcept
164 DragDropGuiController& SqpApplication::dragDropGuiController() noexcept
166 {
165 {
167 return impl->m_DragDropGuiController;
166 return impl->m_DragDropGuiController;
168 }
167 }
169
168
170 ActionsGuiController& SqpApplication::actionsGuiController() noexcept
169 ActionsGuiController& SqpApplication::actionsGuiController() noexcept
171 {
170 {
172 return impl->m_ActionsGuiController;
171 return impl->m_ActionsGuiController;
173 }
172 }
174
173
175 SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const
174 SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const
176 {
175 {
177 return impl->m_PlotInterractionMode;
176 return impl->m_PlotInterractionMode;
178 }
177 }
179
178
180 void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode)
179 void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode)
181 {
180 {
182 impl->m_PlotInterractionMode = mode;
181 impl->m_PlotInterractionMode = mode;
183 }
182 }
184
183
185 SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const
184 SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const
186 {
185 {
187 return impl->m_PlotCursorMode;
186 return impl->m_PlotCursorMode;
188 }
187 }
189
188
190 void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode)
189 void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode)
191 {
190 {
192 impl->m_PlotCursorMode = mode;
191 impl->m_PlotCursorMode = mode;
193 }
192 }
@@ -1,266 +1,265
1 #include <DataSource/DataSourceController.h>
1 #include <DataSource/DataSourceController.h>
2 #include <Variable/RenameVariableDialog.h>
2 #include <Variable/RenameVariableDialog.h>
3 #include <Variable/Variable.h>
4 #include <Variable/VariableController2.h>
3 #include <Variable/VariableController2.h>
5 #include <Variable/VariableInspectorWidget.h>
4 #include <Variable/VariableInspectorWidget.h>
6 #include <Variable/VariableMenuHeaderWidget.h>
5 #include <Variable/VariableMenuHeaderWidget.h>
7 #include <Variable/VariableModel2.h>
6 #include <Variable/VariableModel2.h>
8
7
9 #include <ui_VariableInspectorWidget.h>
8 #include <ui_VariableInspectorWidget.h>
10
9
11 #include <QMouseEvent>
10 #include <QMouseEvent>
12 #include <QSortFilterProxyModel>
11 #include <QSortFilterProxyModel>
13 #include <QStyledItemDelegate>
12 #include <QStyledItemDelegate>
14 #include <QWidgetAction>
13 #include <QWidgetAction>
15
14
16 #include <DragAndDrop/DragDropGuiController.h>
15 #include <DragAndDrop/DragDropGuiController.h>
17 #include <SqpApplication.h>
16 #include <SqpApplication.h>
18
17
19 Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget")
18 Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget")
20
19
21
20
22 class QProgressBarItemDelegate : public QStyledItemDelegate
21 class QProgressBarItemDelegate : public QStyledItemDelegate
23 {
22 {
24
23
25 public:
24 public:
26 QProgressBarItemDelegate(QObject* parent) : QStyledItemDelegate { parent } {}
25 QProgressBarItemDelegate(QObject* parent) : QStyledItemDelegate { parent } {}
27
26
28 void paint(
27 void paint(
29 QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
28 QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
30 {
29 {
31 auto data = index.data(Qt::DisplayRole);
30 auto data = index.data(Qt::DisplayRole);
32 auto progressData = index.data(VariableRoles::ProgressRole);
31 auto progressData = index.data(VariableRoles::ProgressRole);
33 if (data.isValid() && progressData.isValid())
32 if (data.isValid() && progressData.isValid())
34 {
33 {
35 auto name = data.value<QString>();
34 auto name = data.value<QString>();
36 auto progress = progressData.value<double>();
35 auto progress = progressData.value<double>();
37 if (progress > 0)
36 if (progress > 0)
38 {
37 {
39 auto cancelButtonWidth = 20;
38 auto cancelButtonWidth = 20;
40 auto progressBarOption = QStyleOptionProgressBar {};
39 auto progressBarOption = QStyleOptionProgressBar {};
41 auto progressRect = option.rect;
40 auto progressRect = option.rect;
42 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
41 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
43 progressBarOption.rect = progressRect;
42 progressBarOption.rect = progressRect;
44 progressBarOption.minimum = 0;
43 progressBarOption.minimum = 0;
45 progressBarOption.maximum = 100;
44 progressBarOption.maximum = 100;
46 progressBarOption.progress = progress;
45 progressBarOption.progress = progress;
47 progressBarOption.text
46 progressBarOption.text
48 = QString("%1 %2").arg(name).arg(QString::number(progress, 'f', 2) + "%");
47 = QString("%1 %2").arg(name).arg(QString::number(progress, 'f', 2) + "%");
49 progressBarOption.textVisible = true;
48 progressBarOption.textVisible = true;
50 progressBarOption.textAlignment = Qt::AlignCenter;
49 progressBarOption.textAlignment = Qt::AlignCenter;
51
50
52
51
53 QApplication::style()->drawControl(
52 QApplication::style()->drawControl(
54 QStyle::CE_ProgressBar, &progressBarOption, painter);
53 QStyle::CE_ProgressBar, &progressBarOption, painter);
55
54
56 // Cancel button
55 // Cancel button
57 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
56 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
58 option.rect.height());
57 option.rect.height());
59 auto buttonOption = QStyleOptionButton {};
58 auto buttonOption = QStyleOptionButton {};
60 buttonOption.rect = buttonRect;
59 buttonOption.rect = buttonRect;
61 buttonOption.text = "X";
60 buttonOption.text = "X";
62
61
63 QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter);
62 QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter);
64 }
63 }
65 else
64 else
66 {
65 {
67 QStyledItemDelegate::paint(painter, option, index);
66 QStyledItemDelegate::paint(painter, option, index);
68 }
67 }
69 }
68 }
70 else
69 else
71 {
70 {
72 QStyledItemDelegate::paint(painter, option, index);
71 QStyledItemDelegate::paint(painter, option, index);
73 }
72 }
74 }
73 }
75
74
76 bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
75 bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
77 const QModelIndex& index)
76 const QModelIndex& index)
78 {
77 {
79 if (event->type() == QEvent::MouseButtonRelease)
78 if (event->type() == QEvent::MouseButtonRelease)
80 {
79 {
81 auto data = index.data(Qt::DisplayRole);
80 auto data = index.data(Qt::DisplayRole);
82 auto progressData = index.data(VariableRoles::ProgressRole);
81 auto progressData = index.data(VariableRoles::ProgressRole);
83 if (data.isValid() && progressData.isValid())
82 if (data.isValid() && progressData.isValid())
84 {
83 {
85 auto cancelButtonWidth = 20;
84 auto cancelButtonWidth = 20;
86 auto progressRect = option.rect;
85 auto progressRect = option.rect;
87 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
86 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
88 // Cancel button
87 // Cancel button
89 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
88 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
90 option.rect.height());
89 option.rect.height());
91
90
92 auto e = (QMouseEvent*)event;
91 auto e = (QMouseEvent*)event;
93 auto clickX = e->x();
92 auto clickX = e->x();
94 auto clickY = e->y();
93 auto clickY = e->y();
95
94
96 auto x = buttonRect.left(); // the X coordinate
95 auto x = buttonRect.left(); // the X coordinate
97 auto y = buttonRect.top(); // the Y coordinate
96 auto y = buttonRect.top(); // the Y coordinate
98 auto w = buttonRect.width(); // button width
97 auto w = buttonRect.width(); // button width
99 auto h = buttonRect.height(); // button height
98 auto h = buttonRect.height(); // button height
100
99
101 if (clickX > x && clickX < x + w)
100 if (clickX > x && clickX < x + w)
102 {
101 {
103 if (clickY > y && clickY < y + h)
102 if (clickY > y && clickY < y + h)
104 {
103 {
105 // auto& variableModel = sqpApp->variableModel();
104 // auto& variableModel = sqpApp->variableModel();
106 // variableModel->abortProgress(index);
105 // variableModel->abortProgress(index);
107 }
106 }
108 return true;
107 return true;
109 }
108 }
110 else
109 else
111 {
110 {
112 return QStyledItemDelegate::editorEvent(event, model, option, index);
111 return QStyledItemDelegate::editorEvent(event, model, option, index);
113 }
112 }
114 }
113 }
115 else
114 else
116 {
115 {
117 return QStyledItemDelegate::editorEvent(event, model, option, index);
116 return QStyledItemDelegate::editorEvent(event, model, option, index);
118 }
117 }
119 }
118 }
120 else
119 else
121 {
120 {
122 return QStyledItemDelegate::editorEvent(event, model, option, index);
121 return QStyledItemDelegate::editorEvent(event, model, option, index);
123 }
122 }
124
123
125
124
126 return QStyledItemDelegate::editorEvent(event, model, option, index);
125 return QStyledItemDelegate::editorEvent(event, model, option, index);
127 }
126 }
128 };
127 };
129
128
130 VariableInspectorWidget::VariableInspectorWidget(QWidget* parent)
129 VariableInspectorWidget::VariableInspectorWidget(QWidget* parent)
131 : QWidget { parent }
130 : QWidget { parent }
132 , ui { new Ui::VariableInspectorWidget }
131 , ui { new Ui::VariableInspectorWidget }
133 , m_ProgressBarItemDelegate { new QProgressBarItemDelegate { this } }
132 , m_ProgressBarItemDelegate { new QProgressBarItemDelegate { this } }
134 {
133 {
135 ui->setupUi(this);
134 ui->setupUi(this);
136
135
137 // Sets model for table
136 // Sets model for table
138 // auto sortFilterModel = new QSortFilterProxyModel{this};
137 // auto sortFilterModel = new QSortFilterProxyModel{this};
139 // sortFilterModel->setSourceModel(sqpApp->variableController().variableModel());
138 // sortFilterModel->setSourceModel(sqpApp->variableController().variableModel());
140
139
141 m_model = new VariableModel2();
140 m_model = new VariableModel2();
142 ui->tableView->setModel(m_model);
141 ui->tableView->setModel(m_model);
143 connect(m_model, &VariableModel2::createVariable, [](const QVariantHash& productData) {
142 connect(m_model, &VariableModel2::createVariable, [](const QVariantHash& productData) {
144 sqpApp->dataSourceController().requestVariable(productData);
143 sqpApp->dataSourceController().requestVariable(productData);
145 });
144 });
146 auto vc = &(sqpApp->variableController());
145 auto vc = &(sqpApp->variableController());
147 connect(vc, &VariableController2::variableAdded, m_model, &VariableModel2::variableAdded);
146 connect(vc, &VariableController2::variableAdded, m_model, &VariableModel2::variableAdded);
148 connect(vc, &VariableController2::variableDeleted, m_model, &VariableModel2::variableDeleted);
147 connect(vc, &VariableController2::variableDeleted, m_model, &VariableModel2::variableDeleted);
149 connect(m_model, &VariableModel2::asyncChangeRange, vc, &VariableController2::asyncChangeRange);
148 connect(m_model, &VariableModel2::asyncChangeRange, vc, &VariableController2::asyncChangeRange);
150
149
151 // Adds extra signal/slot between view and model, so the view can be updated instantly when
150 // Adds extra signal/slot between view and model, so the view can be updated instantly when
152 // there is a change of data in the model
151 // there is a change of data in the model
153 // connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
152 // connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
154 // SLOT(refresh()));
153 // SLOT(refresh()));
155
154
156 // ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel());
155 // ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel());
157 ui->tableView->setItemDelegateForColumn(0, m_ProgressBarItemDelegate);
156 ui->tableView->setItemDelegateForColumn(0, m_ProgressBarItemDelegate);
158
157
159 // Fixes column sizes
158 // Fixes column sizes
160 auto model = ui->tableView->model();
159 auto model = ui->tableView->model();
161 const auto count = model->columnCount();
160 const auto count = model->columnCount();
162 for (auto i = 0; i < count; ++i)
161 for (auto i = 0; i < count; ++i)
163 {
162 {
164 ui->tableView->setColumnWidth(
163 ui->tableView->setColumnWidth(
165 i, model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).toSize().width());
164 i, model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).toSize().width());
166 }
165 }
167
166
168 // Sets selection options
167 // Sets selection options
169 ui->tableView->setSelectionBehavior(QTableView::SelectRows);
168 ui->tableView->setSelectionBehavior(QTableView::SelectRows);
170 ui->tableView->setSelectionMode(QTableView::ExtendedSelection);
169 ui->tableView->setSelectionMode(QTableView::ExtendedSelection);
171
170
172 // Connection to show a menu when right clicking on the tree
171 // Connection to show a menu when right clicking on the tree
173 ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
172 ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
174 connect(ui->tableView, &QTableView::customContextMenuRequested, this,
173 connect(ui->tableView, &QTableView::customContextMenuRequested, this,
175 &VariableInspectorWidget::onTableMenuRequested);
174 &VariableInspectorWidget::onTableMenuRequested);
176 }
175 }
177
176
178 VariableInspectorWidget::~VariableInspectorWidget()
177 VariableInspectorWidget::~VariableInspectorWidget()
179 {
178 {
180 delete ui;
179 delete ui;
181 }
180 }
182
181
183 void VariableInspectorWidget::onTableMenuRequested(const QPoint& pos) noexcept
182 void VariableInspectorWidget::onTableMenuRequested(const QPoint& pos) noexcept
184 {
183 {
185 auto selectedRows = ui->tableView->selectionModel()->selectedRows();
184 auto selectedRows = ui->tableView->selectionModel()->selectedRows();
186 auto selectedVariables = QVector<std::shared_ptr<Variable2>> {};
185 auto selectedVariables = QVector<std::shared_ptr<Variable2>> {};
187 for (const auto& selectedRow : qAsConst(selectedRows))
186 for (const auto& selectedRow : qAsConst(selectedRows))
188 {
187 {
189 if (auto selectedVariable = this->m_model->variables()[selectedRow.row()])
188 if (auto selectedVariable = this->m_model->variables()[selectedRow.row()])
190 {
189 {
191 selectedVariables.push_back(selectedVariable);
190 selectedVariables.push_back(selectedVariable);
192 }
191 }
193 }
192 }
194
193
195 QMenu tableMenu {};
194 QMenu tableMenu {};
196
195
197 // Emits a signal so that potential receivers can populate the menu before displaying it
196 // Emits a signal so that potential receivers can populate the menu before displaying it
198 emit tableMenuAboutToBeDisplayed(&tableMenu, selectedVariables);
197 emit tableMenuAboutToBeDisplayed(&tableMenu, selectedVariables);
199
198
200 // Adds menu-specific actions
199 // Adds menu-specific actions
201 if (!selectedVariables.isEmpty())
200 if (!selectedVariables.isEmpty())
202 {
201 {
203 tableMenu.addSeparator();
202 tableMenu.addSeparator();
204
203
205 // 'Rename' and 'Duplicate' actions (only if one variable selected)
204 // 'Rename' and 'Duplicate' actions (only if one variable selected)
206 if (selectedVariables.size() == 1)
205 if (selectedVariables.size() == 1)
207 {
206 {
208 auto selectedVariable = selectedVariables.front();
207 auto selectedVariable = selectedVariables.front();
209
208
210 auto duplicateFun = [varW = std::weak_ptr<Variable2>(selectedVariable)]() {
209 auto duplicateFun = [varW = std::weak_ptr<Variable2>(selectedVariable)]() {
211 if (auto var = varW.lock())
210 if (auto var = varW.lock())
212 {
211 {
213 sqpApp->variableController().cloneVariable(var);
212 sqpApp->variableController().cloneVariable(var);
214 }
213 }
215 };
214 };
216
215
217 tableMenu.addAction(tr("Duplicate"), duplicateFun);
216 tableMenu.addAction(tr("Duplicate"), duplicateFun);
218
217
219 auto renameFun = [varW = std::weak_ptr<Variable2>(selectedVariable), this]() {
218 auto renameFun = [varW = std::weak_ptr<Variable2>(selectedVariable), this]() {
220 if (auto var = varW.lock())
219 if (auto var = varW.lock())
221 {
220 {
222 // Generates forbidden names (names associated to existing variables)
221 // Generates forbidden names (names associated to existing variables)
223 auto allVariables = sqpApp->variableController().variables();
222 auto allVariables = sqpApp->variableController().variables();
224 auto forbiddenNames = QVector<QString>(allVariables.size());
223 auto forbiddenNames = QVector<QString>(allVariables.size());
225 std::transform(allVariables.cbegin(), allVariables.cend(),
224 std::transform(allVariables.cbegin(), allVariables.cend(),
226 forbiddenNames.begin(),
225 forbiddenNames.begin(),
227 [](const auto& variable) { return variable->name(); });
226 [](const auto& variable) { return variable->name(); });
228
227
229 RenameVariableDialog dialog { var->name(), forbiddenNames, this };
228 RenameVariableDialog dialog { var->name(), forbiddenNames, this };
230 if (dialog.exec() == QDialog::Accepted)
229 if (dialog.exec() == QDialog::Accepted)
231 {
230 {
232 var->setName(dialog.name());
231 var->setName(dialog.name());
233 }
232 }
234 }
233 }
235 };
234 };
236
235
237 tableMenu.addAction(tr("Rename..."), renameFun);
236 tableMenu.addAction(tr("Rename..."), renameFun);
238 }
237 }
239
238
240 // 'Delete' action
239 // 'Delete' action
241 auto deleteFun = [&selectedVariables]() {
240 auto deleteFun = [&selectedVariables]() {
242 for (const auto& var : selectedVariables)
241 for (const auto& var : selectedVariables)
243 sqpApp->variableController().deleteVariable(var);
242 sqpApp->variableController().deleteVariable(var);
244 };
243 };
245
244
246 tableMenu.addAction(QIcon { ":/icones/delete.png" }, tr("Delete"), deleteFun);
245 tableMenu.addAction(QIcon { ":/icones/delete.png" }, tr("Delete"), deleteFun);
247 }
246 }
248
247
249 if (!tableMenu.isEmpty())
248 if (!tableMenu.isEmpty())
250 {
249 {
251 // Generates menu header (inserted before first action)
250 // Generates menu header (inserted before first action)
252 auto firstAction = tableMenu.actions().first();
251 auto firstAction = tableMenu.actions().first();
253 auto headerAction = new QWidgetAction { &tableMenu };
252 auto headerAction = new QWidgetAction { &tableMenu };
254 headerAction->setDefaultWidget(
253 headerAction->setDefaultWidget(
255 new VariableMenuHeaderWidget { selectedVariables, &tableMenu });
254 new VariableMenuHeaderWidget { selectedVariables, &tableMenu });
256 tableMenu.insertAction(firstAction, headerAction);
255 tableMenu.insertAction(firstAction, headerAction);
257
256
258 // Displays menu
257 // Displays menu
259 tableMenu.exec(QCursor::pos());
258 tableMenu.exec(QCursor::pos());
260 }
259 }
261 }
260 }
262
261
263 void VariableInspectorWidget::refresh() noexcept
262 void VariableInspectorWidget::refresh() noexcept
264 {
263 {
265 ui->tableView->viewport()->update();
264 ui->tableView->viewport()->update();
266 }
265 }
@@ -1,137 +1,133
1 #include "Visualization/PlottablesRenderingUtils.h"
1 #include "Visualization/PlottablesRenderingUtils.h"
2
2
3 #include <Common/ColorUtils.h>
3 #include <Common/ColorUtils.h>
4
4
5 #include <Data/ScalarSeries.h>
6 #include <Data/SpectrogramSeries.h>
7 #include <Data/VectorSeries.h>
8
9 #include <Variable/Variable2.h>
5 #include <Variable/Variable2.h>
10
6
11 #include <Visualization/qcustomplot.h>
7 #include <Visualization/qcustomplot.h>
12
8
13 Q_LOGGING_CATEGORY(LOG_PlottablesRenderingUtils, "PlottablesRenderingUtils")
9 Q_LOGGING_CATEGORY(LOG_PlottablesRenderingUtils, "PlottablesRenderingUtils")
14
10
15 namespace
11 namespace
16 {
12 {
17
13
18 /**
14 /**
19 * Delegate used to set plottables properties
15 * Delegate used to set plottables properties
20 */
16 */
21 template <typename T, typename Enabled = void>
17 template <typename T, typename Enabled = void>
22 struct PlottablesSetter
18 struct PlottablesSetter
23 {
19 {
24 static void setProperties(PlottablesMap&)
20 static void setProperties(PlottablesMap&)
25 {
21 {
26 // Default implementation does nothing
22 // Default implementation does nothing
27 qCCritical(LOG_PlottablesRenderingUtils())
23 qCCritical(LOG_PlottablesRenderingUtils())
28 << "Can't set plottables properties: unmanaged type of data";
24 << "Can't set plottables properties: unmanaged type of data";
29 }
25 }
30 };
26 };
31
27
32 /**
28 /**
33 * Specialization of PlottablesSetter for scalars and vectors
29 * Specialization of PlottablesSetter for scalars and vectors
34 * @sa ScalarSeries
30 * @sa ScalarSeries
35 * @sa VectorSeries
31 * @sa VectorSeries
36 */
32 */
37 template <typename T>
33 template <typename T>
38 struct PlottablesSetter<T,
34 struct PlottablesSetter<T,
39 typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value
35 typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value
40 or std::is_base_of<VectorTimeSerie, T>::value>>
36 or std::is_base_of<VectorTimeSerie, T>::value>>
41 {
37 {
42 static void setProperties(PlottablesMap& plottables)
38 static void setProperties(PlottablesMap& plottables)
43 {
39 {
44 // Finds the plottable with the highest index to determine the number of colors to generate
40 // Finds the plottable with the highest index to determine the number of colors to generate
45 auto end = plottables.cend();
41 auto end = plottables.cend();
46 auto maxPlottableIndexIt = std::max_element(plottables.cbegin(), end,
42 auto maxPlottableIndexIt = std::max_element(plottables.cbegin(), end,
47 [](const auto& it1, const auto& it2) { return it1.first < it2.first; });
43 [](const auto& it1, const auto& it2) { return it1.first < it2.first; });
48 auto componentCount = maxPlottableIndexIt != end ? maxPlottableIndexIt->first + 1 : 0;
44 auto componentCount = maxPlottableIndexIt != end ? maxPlottableIndexIt->first + 1 : 0;
49
45
50 // Generates colors for each component
46 // Generates colors for each component
51 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
47 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
52
48
53 // For each component of the data series, creates a QCPGraph to add to the plot
49 // For each component of the data series, creates a QCPGraph to add to the plot
54 for (auto i = 0; i < componentCount; ++i)
50 for (auto i = 0; i < componentCount; ++i)
55 {
51 {
56 auto graphIt = plottables.find(i);
52 auto graphIt = plottables.find(i);
57 if (graphIt != end)
53 if (graphIt != end)
58 {
54 {
59 graphIt->second->setPen(QPen { colors.at(i) });
55 graphIt->second->setPen(QPen { colors.at(i) });
60 }
56 }
61 }
57 }
62 }
58 }
63 };
59 };
64
60
65 /**
61 /**
66 * Specialization of PlottablesSetter for spectrograms
62 * Specialization of PlottablesSetter for spectrograms
67 * @sa SpectrogramSeries
63 * @sa SpectrogramSeries
68 */
64 */
69 template <typename T>
65 template <typename T>
70 struct PlottablesSetter<T,
66 struct PlottablesSetter<T,
71 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
67 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
72 {
68 {
73 static void setProperties(PlottablesMap& plottables)
69 static void setProperties(PlottablesMap& plottables)
74 {
70 {
75 // Checks that for a spectrogram there is only one plottable, that is a colormap
71 // Checks that for a spectrogram there is only one plottable, that is a colormap
76 if (plottables.size() != 1)
72 if (plottables.size() != 1)
77 {
73 {
78 return;
74 return;
79 }
75 }
80
76
81 if (auto colormap = dynamic_cast<QCPColorMap*>(plottables.begin()->second))
77 if (auto colormap = dynamic_cast<QCPColorMap*>(plottables.begin()->second))
82 {
78 {
83 colormap->setInterpolate(false); // No value interpolation
79 colormap->setInterpolate(false); // No value interpolation
84 colormap->setTightBoundary(true);
80 colormap->setTightBoundary(true);
85
81
86 // Finds color scale in the colormap's plot to associate with it
82 // Finds color scale in the colormap's plot to associate with it
87 auto plot = colormap->parentPlot();
83 auto plot = colormap->parentPlot();
88 auto plotElements = plot->plotLayout()->elements(false);
84 auto plotElements = plot->plotLayout()->elements(false);
89 for (auto plotElement : plotElements)
85 for (auto plotElement : plotElements)
90 {
86 {
91 if (auto colorScale = dynamic_cast<QCPColorScale*>(plotElement))
87 if (auto colorScale = dynamic_cast<QCPColorScale*>(plotElement))
92 {
88 {
93 colormap->setColorScale(colorScale);
89 colormap->setColorScale(colorScale);
94 }
90 }
95 }
91 }
96
92
97 colormap->rescaleDataRange();
93 colormap->rescaleDataRange();
98 }
94 }
99 else
95 else
100 {
96 {
101 qCCritical(LOG_PlottablesRenderingUtils()) << "Can't get colormap of the spectrogram";
97 qCCritical(LOG_PlottablesRenderingUtils()) << "Can't get colormap of the spectrogram";
102 }
98 }
103 }
99 }
104 };
100 };
105
101
106 /**
102 /**
107 * Default implementation of IPlottablesHelper, which takes data series to set plottables properties
103 * Default implementation of IPlottablesHelper, which takes data series to set plottables properties
108 * @tparam T the data series' type
104 * @tparam T the data series' type
109 */
105 */
110 template <typename T>
106 template <typename T>
111 struct PlottablesHelper : public IPlottablesHelper
107 struct PlottablesHelper : public IPlottablesHelper
112 {
108 {
113 void setProperties(PlottablesMap& plottables) override
109 void setProperties(PlottablesMap& plottables) override
114 {
110 {
115 PlottablesSetter<T>::setProperties(plottables);
111 PlottablesSetter<T>::setProperties(plottables);
116 }
112 }
117 };
113 };
118
114
119 } // namespace
115 } // namespace
120
116
121 std::unique_ptr<IPlottablesHelper> IPlottablesHelperFactory::create(Variable2& variable) noexcept
117 std::unique_ptr<IPlottablesHelper> IPlottablesHelperFactory::create(Variable2& variable) noexcept
122 {
118 {
123 switch (variable.type())
119 switch (variable.type())
124 {
120 {
125 case DataSeriesType::SCALAR:
121 case DataSeriesType::SCALAR:
126 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>();
122 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>();
127 case DataSeriesType::SPECTROGRAM:
123 case DataSeriesType::SPECTROGRAM:
128 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>();
124 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>();
129 case DataSeriesType::VECTOR:
125 case DataSeriesType::VECTOR:
130 return std::make_unique<PlottablesHelper<VectorTimeSerie>>();
126 return std::make_unique<PlottablesHelper<VectorTimeSerie>>();
131 default:
127 default:
132 // Returns default helper
128 // Returns default helper
133 break;
129 break;
134 }
130 }
135
131
136 return std::make_unique<PlottablesHelper<IDataSeries>>();
132 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>();
137 }
133 }
@@ -1,60 +1,63
1 #include "Visualization/SqpColorScale.h"
1 #include "Visualization/SqpColorScale.h"
2
2
3 #include <Data/DataSeriesUtils.h>
4
5 #include <Visualization/QCPColorMapIterator.h>
3 #include <Visualization/QCPColorMapIterator.h>
6
4
7 Q_LOGGING_CATEGORY(LOG_SqpColorScale, "SqpColorScale")
5 Q_LOGGING_CATEGORY(LOG_SqpColorScale, "SqpColorScale")
8
6
9 namespace {
7 namespace
8 {
10
9
11 const auto DEFAULT_GRADIENT_PRESET = QCPColorGradient::gpJet;
10 const auto DEFAULT_GRADIENT_PRESET = QCPColorGradient::gpJet;
12 const auto DEFAULT_RANGE = QCPRange{1.0e3, 1.7e7};
11 const auto DEFAULT_RANGE = QCPRange { 1.0e3, 1.7e7 };
13
12
14 } // namespace
13 } // namespace
15
14
16 std::pair<double, double> SqpColorScale::computeThresholds(const SqpColorScale &scale)
15 std::pair<double, double> SqpColorScale::computeThresholds(const SqpColorScale& scale)
17 {
16 {
18 auto qcpScale = scale.m_Scale;
17 auto qcpScale = scale.m_Scale;
19
18
20 auto colorMaps = qcpScale->colorMaps();
19 auto colorMaps = qcpScale->colorMaps();
21 if (colorMaps.size() != 1) {
20 if (colorMaps.size() != 1)
22 return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()};
21 {
22 return { std::numeric_limits<double>::quiet_NaN(),
23 std::numeric_limits<double>::quiet_NaN() };
23 }
24 }
24
25
25 // Computes thresholds
26 // Computes thresholds
26 auto isLogarithmicScale = qcpScale->dataScaleType() == QCPAxis::stLogarithmic;
27 auto isLogarithmicScale = qcpScale->dataScaleType() == QCPAxis::stLogarithmic;
27 auto colorMapData = colorMaps.first()->data();
28 auto colorMapData = colorMaps.first()->data();
28 QCPColorMapIterator begin{colorMapData, true};
29 QCPColorMapIterator begin { colorMapData, true };
29 QCPColorMapIterator end{colorMapData, false};
30 QCPColorMapIterator end { colorMapData, false };
30
31
31 return DataSeriesUtils::thresholds(begin, end, isLogarithmicScale);
32 return {}; // DataSeriesUtils::thresholds(begin, end, isLogarithmicScale);
32 }
33 }
33
34
34 SqpColorScale::SqpColorScale(QCustomPlot &plot)
35 SqpColorScale::SqpColorScale(QCustomPlot& plot)
35 : m_Scale{new QCPColorScale{&plot}},
36 : m_Scale { new QCPColorScale { &plot } }
36 m_AutomaticThreshold{false},
37 , m_AutomaticThreshold { false }
37 m_GradientPreset{DEFAULT_GRADIENT_PRESET}
38 , m_GradientPreset { DEFAULT_GRADIENT_PRESET }
38 {
39 {
39 m_Scale->setGradient(m_GradientPreset);
40 m_Scale->setGradient(m_GradientPreset);
40 m_Scale->setDataRange(DEFAULT_RANGE);
41 m_Scale->setDataRange(DEFAULT_RANGE);
41 }
42 }
42
43
43 void SqpColorScale::updateDataRange() noexcept
44 void SqpColorScale::updateDataRange() noexcept
44 {
45 {
45 // Updates data range only if mode is automatic
46 // Updates data range only if mode is automatic
46 if (!m_AutomaticThreshold) {
47 if (!m_AutomaticThreshold)
48 {
47 return;
49 return;
48 }
50 }
49
51
50 double minThreshold, maxThreshold;
52 double minThreshold, maxThreshold;
51 std::tie(minThreshold, maxThreshold) = computeThresholds(*this);
53 std::tie(minThreshold, maxThreshold) = computeThresholds(*this);
52 if (std::isnan(minThreshold) || std::isnan(maxThreshold)) {
54 if (std::isnan(minThreshold) || std::isnan(maxThreshold))
55 {
53 qCCritical(LOG_SqpColorScale())
56 qCCritical(LOG_SqpColorScale())
54 << "Can't update data range of color scale: thresholds computed are invalid";
57 << "Can't update data range of color scale: thresholds computed are invalid";
55 return;
58 return;
56 }
59 }
57
60
58 // Updates thresholds
61 // Updates thresholds
59 m_Scale->setDataRange({minThreshold, maxThreshold});
62 m_Scale->setDataRange({ minThreshold, maxThreshold });
60 }
63 }
@@ -1,443 +1,442
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Data/DataSeriesUtils.h>
5 #include <Data/ScalarTimeSerie.h>
4 #include <Data/ScalarTimeSerie.h>
6 #include <Data/SpectrogramTimeSerie.h>
5 #include <Data/SpectrogramTimeSerie.h>
7 #include <Data/VectorTimeSerie.h>
6 #include <Data/VectorTimeSerie.h>
8
7
9 #include <Variable/Variable2.h>
8 #include <Variable/Variable2.h>
10
9
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
10 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12
11
13 namespace
12 namespace
14 {
13 {
15
14
16 class SqpDataContainer : public QCPGraphDataContainer
15 class SqpDataContainer : public QCPGraphDataContainer
17 {
16 {
18 public:
17 public:
19 void appendGraphData(const QCPGraphData& data) { mData.append(data); }
18 void appendGraphData(const QCPGraphData& data) { mData.append(data); }
20 };
19 };
21
20
22 /**
21 /**
23 * Struct used to create plottables, depending on the type of the data series from which to create
22 * Struct used to create plottables, depending on the type of the data series from which to create
24 * them
23 * them
25 * @tparam T the data series' type
24 * @tparam T the data series' type
26 * @remarks Default implementation can't create plottables
25 * @remarks Default implementation can't create plottables
27 */
26 */
28 template <typename T, typename Enabled = void>
27 template <typename T, typename Enabled = void>
29 struct PlottablesCreator
28 struct PlottablesCreator
30 {
29 {
31 static PlottablesMap createPlottables(QCustomPlot&) { return {}; }
30 static PlottablesMap createPlottables(QCustomPlot&) { return {}; }
32 };
31 };
33
32
34 PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs)
33 PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs)
35 {
34 {
36 PlottablesMap result {};
35 PlottablesMap result {};
37
36
38 // Creates {nbGraphs} QCPGraph to add to the plot
37 // Creates {nbGraphs} QCPGraph to add to the plot
39 for (auto i = 0; i < nbGraphs; ++i)
38 for (auto i = 0; i < nbGraphs; ++i)
40 {
39 {
41 auto graph = plot.addGraph();
40 auto graph = plot.addGraph();
42 result.insert({ i, graph });
41 result.insert({ i, graph });
43 }
42 }
44
43
45 plot.replot();
44 plot.replot();
46
45
47 return result;
46 return result;
48 }
47 }
49
48
50 /**
49 /**
51 * Specialization of PlottablesCreator for scalars
50 * Specialization of PlottablesCreator for scalars
52 * @sa ScalarSeries
51 * @sa ScalarSeries
53 */
52 */
54 template <typename T>
53 template <typename T>
55 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
54 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
56 {
55 {
57 static PlottablesMap createPlottables(QCustomPlot& plot) { return createGraphs(plot, 1); }
56 static PlottablesMap createPlottables(QCustomPlot& plot) { return createGraphs(plot, 1); }
58 };
57 };
59
58
60 /**
59 /**
61 * Specialization of PlottablesCreator for vectors
60 * Specialization of PlottablesCreator for vectors
62 * @sa VectorSeries
61 * @sa VectorSeries
63 */
62 */
64 template <typename T>
63 template <typename T>
65 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
64 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
66 {
65 {
67 static PlottablesMap createPlottables(QCustomPlot& plot) { return createGraphs(plot, 3); }
66 static PlottablesMap createPlottables(QCustomPlot& plot) { return createGraphs(plot, 3); }
68 };
67 };
69
68
70 /**
69 /**
71 * Specialization of PlottablesCreator for spectrograms
70 * Specialization of PlottablesCreator for spectrograms
72 * @sa SpectrogramSeries
71 * @sa SpectrogramSeries
73 */
72 */
74 template <typename T>
73 template <typename T>
75 struct PlottablesCreator<T,
74 struct PlottablesCreator<T,
76 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
75 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
77 {
76 {
78 static PlottablesMap createPlottables(QCustomPlot& plot)
77 static PlottablesMap createPlottables(QCustomPlot& plot)
79 {
78 {
80 PlottablesMap result {};
79 PlottablesMap result {};
81 result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } });
80 result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } });
82
81
83 plot.replot();
82 plot.replot();
84
83
85 return result;
84 return result;
86 }
85 }
87 };
86 };
88
87
89 /**
88 /**
90 * Struct used to update plottables, depending on the type of the data series from which to update
89 * Struct used to update plottables, depending on the type of the data series from which to update
91 * them
90 * them
92 * @tparam T the data series' type
91 * @tparam T the data series' type
93 * @remarks Default implementation can't update plottables
92 * @remarks Default implementation can't update plottables
94 */
93 */
95 template <typename T, typename Enabled = void>
94 template <typename T, typename Enabled = void>
96 struct PlottablesUpdater
95 struct PlottablesUpdater
97 {
96 {
98 static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&)
97 static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&)
99 {
98 {
100 qCCritical(LOG_VisualizationGraphHelper())
99 qCCritical(LOG_VisualizationGraphHelper())
101 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
100 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
102 }
101 }
103
102
104 static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool)
103 static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool)
105 {
104 {
106 qCCritical(LOG_VisualizationGraphHelper())
105 qCCritical(LOG_VisualizationGraphHelper())
107 << QObject::tr("Can't update plottables: unmanaged data series type");
106 << QObject::tr("Can't update plottables: unmanaged data series type");
108 }
107 }
109 };
108 };
110
109
111 /**
110 /**
112 * Specialization of PlottablesUpdater for scalars and vectors
111 * Specialization of PlottablesUpdater for scalars and vectors
113 * @sa ScalarSeries
112 * @sa ScalarSeries
114 * @sa VectorSeries
113 * @sa VectorSeries
115 */
114 */
116 template <typename T>
115 template <typename T>
117 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
116 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>>
118 {
117 {
119 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
118 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
120 {
119 {
121 auto minValue = 0., maxValue = 0.;
120 auto minValue = 0., maxValue = 0.;
122 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
121 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
123 {
122 {
124 maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v();
123 maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v();
125 minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v();
124 minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v();
126 }
125 }
127 plot.yAxis->setRange(QCPRange { minValue, maxValue });
126 plot.yAxis->setRange(QCPRange { minValue, maxValue });
128 }
127 }
129
128
130 static void updatePlottables(
129 static void updatePlottables(
131 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
130 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
132 {
131 {
133
132
134 // For each plottable to update, resets its data
133 // For each plottable to update, resets its data
135 for (const auto& plottable : plottables)
134 for (const auto& plottable : plottables)
136 {
135 {
137 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
136 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
138 {
137 {
139 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
138 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
140 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
139 if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries))
141 {
140 {
142 std::for_each(
141 std::for_each(
143 std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) {
142 std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) {
144 dataContainer->appendGraphData(QCPGraphData(value.t(), value.v()));
143 dataContainer->appendGraphData(QCPGraphData(value.t(), value.v()));
145 });
144 });
146 }
145 }
147 graph->setData(dataContainer);
146 graph->setData(dataContainer);
148 }
147 }
149 }
148 }
150
149
151 if (!plottables.empty())
150 if (!plottables.empty())
152 {
151 {
153 auto plot = plottables.begin()->second->parentPlot();
152 auto plot = plottables.begin()->second->parentPlot();
154
153
155 if (rescaleAxes)
154 if (rescaleAxes)
156 {
155 {
157 plot->rescaleAxes();
156 plot->rescaleAxes();
158 }
157 }
159 }
158 }
160 }
159 }
161 };
160 };
162
161
163
162
164 template <typename T>
163 template <typename T>
165 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
164 struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>>
166 {
165 {
167 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
166 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
168 {
167 {
169 double minValue = 0., maxValue = 0.;
168 double minValue = 0., maxValue = 0.;
170 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
169 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
171 {
170 {
172 std::for_each(
171 std::for_each(
173 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
172 std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) {
174 minValue = std::min({ minValue, v.v().x, v.v().y, v.v().z });
173 minValue = std::min({ minValue, v.v().x, v.v().y, v.v().z });
175 maxValue = std::max({ maxValue, v.v().x, v.v().y, v.v().z });
174 maxValue = std::max({ maxValue, v.v().x, v.v().y, v.v().z });
176 });
175 });
177 }
176 }
178
177
179 plot.yAxis->setRange(QCPRange { minValue, maxValue });
178 plot.yAxis->setRange(QCPRange { minValue, maxValue });
180 }
179 }
181
180
182 static void updatePlottables(
181 static void updatePlottables(
183 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
182 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
184 {
183 {
185
184
186 // For each plottable to update, resets its data
185 // For each plottable to update, resets its data
187 for (const auto& plottable : plottables)
186 for (const auto& plottable : plottables)
188 {
187 {
189 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
188 if (auto graph = dynamic_cast<QCPGraph*>(plottable.second))
190 {
189 {
191 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
190 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
192 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
191 if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries))
193 {
192 {
194 switch (plottable.first)
193 switch (plottable.first)
195 {
194 {
196 case 0:
195 case 0:
197 std::for_each(std::begin(*serie), std::end(*serie),
196 std::for_each(std::begin(*serie), std::end(*serie),
198 [&dataContainer](const auto& value) {
197 [&dataContainer](const auto& value) {
199 dataContainer->appendGraphData(
198 dataContainer->appendGraphData(
200 QCPGraphData(value.t(), value.v().x));
199 QCPGraphData(value.t(), value.v().x));
201 });
200 });
202 break;
201 break;
203 case 1:
202 case 1:
204 std::for_each(std::begin(*serie), std::end(*serie),
203 std::for_each(std::begin(*serie), std::end(*serie),
205 [&dataContainer](const auto& value) {
204 [&dataContainer](const auto& value) {
206 dataContainer->appendGraphData(
205 dataContainer->appendGraphData(
207 QCPGraphData(value.t(), value.v().y));
206 QCPGraphData(value.t(), value.v().y));
208 });
207 });
209 break;
208 break;
210 case 2:
209 case 2:
211 std::for_each(std::begin(*serie), std::end(*serie),
210 std::for_each(std::begin(*serie), std::end(*serie),
212 [&dataContainer](const auto& value) {
211 [&dataContainer](const auto& value) {
213 dataContainer->appendGraphData(
212 dataContainer->appendGraphData(
214 QCPGraphData(value.t(), value.v().z));
213 QCPGraphData(value.t(), value.v().z));
215 });
214 });
216 break;
215 break;
217 default:
216 default:
218 break;
217 break;
219 }
218 }
220 }
219 }
221 graph->setData(dataContainer);
220 graph->setData(dataContainer);
222 }
221 }
223 }
222 }
224
223
225 if (!plottables.empty())
224 if (!plottables.empty())
226 {
225 {
227 auto plot = plottables.begin()->second->parentPlot();
226 auto plot = plottables.begin()->second->parentPlot();
228
227
229 if (rescaleAxes)
228 if (rescaleAxes)
230 {
229 {
231 plot->rescaleAxes();
230 plot->rescaleAxes();
232 }
231 }
233 }
232 }
234 }
233 }
235 };
234 };
236
235
237 /**
236 /**
238 * Specialization of PlottablesUpdater for spectrograms
237 * Specialization of PlottablesUpdater for spectrograms
239 * @sa SpectrogramSeries
238 * @sa SpectrogramSeries
240 */
239 */
241 template <typename T>
240 template <typename T>
242 struct PlottablesUpdater<T,
241 struct PlottablesUpdater<T,
243 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
242 typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>>
244 {
243 {
245 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
244 static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot)
246 {
245 {
247 // TODO
246 // TODO
248 // double min, max;
247 // double min, max;
249 // std::tie(min, max) = dataSeries.yBounds();
248 // std::tie(min, max) = dataSeries.yBounds();
250
249
251 // if (!std::isnan(min) && !std::isnan(max))
250 // if (!std::isnan(min) && !std::isnan(max))
252 // {
251 // {
253 // plot.yAxis->setRange(QCPRange { min, max });
252 // plot.yAxis->setRange(QCPRange { min, max });
254 // }
253 // }
255 }
254 }
256
255
257 static void updatePlottables(
256 static void updatePlottables(
258 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
257 T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes)
259 {
258 {
260 // TODO
259 // TODO
261 // if (plottables.empty())
260 // if (plottables.empty())
262 // {
261 // {
263 // qCDebug(LOG_VisualizationGraphHelper())
262 // qCDebug(LOG_VisualizationGraphHelper())
264 // << QObject::tr("Can't update spectrogram: no colormap has been
263 // << QObject::tr("Can't update spectrogram: no colormap has been
265 // associated");
264 // associated");
266 // return;
265 // return;
267 // }
266 // }
268
267
269 // // Gets the colormap to update (normally there is only one colormap)
268 // // Gets the colormap to update (normally there is only one colormap)
270 // Q_ASSERT(plottables.size() == 1);
269 // Q_ASSERT(plottables.size() == 1);
271 // auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
270 // auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0));
272 // Q_ASSERT(colormap != nullptr);
271 // Q_ASSERT(colormap != nullptr);
273
272
274 // dataSeries.lockRead();
273 // dataSeries.lockRead();
275
274
276 // // Processing spectrogram data for display in QCustomPlot
275 // // Processing spectrogram data for display in QCustomPlot
277 // auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
276 // auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
278
277
279 // // Computes logarithmic y-axis resolution for the spectrogram
278 // // Computes logarithmic y-axis resolution for the spectrogram
280 // auto yData = its.first->y();
279 // auto yData = its.first->y();
281 // auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true);
280 // auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true);
282
281
283 // // Generates mesh for colormap
282 // // Generates mesh for colormap
284 // auto mesh = DataSeriesUtils::regularMesh(its.first, its.second,
283 // auto mesh = DataSeriesUtils::regularMesh(its.first, its.second,
285 // DataSeriesUtils::Resolution { dataSeries.xResolution() }, yResolution);
284 // DataSeriesUtils::Resolution { dataSeries.xResolution() }, yResolution);
286
285
287 // dataSeries.unlock();
286 // dataSeries.unlock();
288
287
289 // colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY);
288 // colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY);
290 // if (!mesh.isEmpty())
289 // if (!mesh.isEmpty())
291 // {
290 // {
292 // colormap->data()->setRange(QCPRange { mesh.m_XMin, mesh.xMax() },
291 // colormap->data()->setRange(QCPRange { mesh.m_XMin, mesh.xMax() },
293 // // y-axis range is converted to linear values
292 // // y-axis range is converted to linear values
294 // QCPRange { std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax()) });
293 // QCPRange { std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax()) });
295
294
296 // // Sets values
295 // // Sets values
297 // auto index = 0;
296 // auto index = 0;
298 // for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it,
297 // for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it,
299 // ++index)
298 // ++index)
300 // {
299 // {
301 // auto xIndex = index % mesh.m_NbX;
300 // auto xIndex = index % mesh.m_NbX;
302 // auto yIndex = index / mesh.m_NbX;
301 // auto yIndex = index / mesh.m_NbX;
303
302
304 // colormap->data()->setCell(xIndex, yIndex, *it);
303 // colormap->data()->setCell(xIndex, yIndex, *it);
305
304
306 // // Makes the NaN values to be transparent in the colormap
305 // // Makes the NaN values to be transparent in the colormap
307 // if (std::isnan(*it))
306 // if (std::isnan(*it))
308 // {
307 // {
309 // colormap->data()->setAlpha(xIndex, yIndex, 0);
308 // colormap->data()->setAlpha(xIndex, yIndex, 0);
310 // }
309 // }
311 // }
310 // }
312 // }
311 // }
313
312
314 // // Rescales axes
313 // // Rescales axes
315 // auto plot = colormap->parentPlot();
314 // auto plot = colormap->parentPlot();
316
315
317 // if (rescaleAxes)
316 // if (rescaleAxes)
318 // {
317 // {
319 // plot->rescaleAxes();
318 // plot->rescaleAxes();
320 // }
319 // }
321 }
320 }
322 };
321 };
323
322
324 /**
323 /**
325 * Helper used to create/update plottables
324 * Helper used to create/update plottables
326 */
325 */
327 struct IPlottablesHelper
326 struct IPlottablesHelper
328 {
327 {
329 virtual ~IPlottablesHelper() noexcept = default;
328 virtual ~IPlottablesHelper() noexcept = default;
330 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
329 virtual PlottablesMap create(QCustomPlot& plot) const = 0;
331 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
330 virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0;
332 virtual void update(
331 virtual void update(
333 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
332 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0;
334 };
333 };
335
334
336 /**
335 /**
337 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
336 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
338 * @tparam T the data series' type
337 * @tparam T the data series' type
339 */
338 */
340 template <typename T>
339 template <typename T>
341 struct PlottablesHelper : public IPlottablesHelper
340 struct PlottablesHelper : public IPlottablesHelper
342 {
341 {
343 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
342 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {}
344
343
345 PlottablesMap create(QCustomPlot& plot) const override
344 PlottablesMap create(QCustomPlot& plot) const override
346 {
345 {
347 return PlottablesCreator<T>::createPlottables(plot);
346 return PlottablesCreator<T>::createPlottables(plot);
348 }
347 }
349
348
350 void update(
349 void update(
351 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
350 PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override
352 {
351 {
353 if (m_DataSeries)
352 if (m_DataSeries)
354 {
353 {
355 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
354 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
356 }
355 }
357 else
356 else
358 {
357 {
359 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
358 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
360 "between the type of data series and the "
359 "between the type of data series and the "
361 "type supposed";
360 "type supposed";
362 }
361 }
363 }
362 }
364
363
365 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
364 void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override
366 {
365 {
367 if (m_DataSeries)
366 if (m_DataSeries)
368 {
367 {
369 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
368 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
370 }
369 }
371 else
370 else
372 {
371 {
373 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
372 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
374 "between the type of data series and the "
373 "between the type of data series and the "
375 "type supposed";
374 "type supposed";
376 }
375 }
377 }
376 }
378
377
379 std::shared_ptr<T> m_DataSeries;
378 std::shared_ptr<T> m_DataSeries;
380 };
379 };
381
380
382 /// Creates IPlottablesHelper according to the type of data series a variable holds
381 /// Creates IPlottablesHelper according to the type of data series a variable holds
383 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
382 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept
384 {
383 {
385 switch (variable->type())
384 switch (variable->type())
386 {
385 {
387 case DataSeriesType::SCALAR:
386 case DataSeriesType::SCALAR:
388 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
387 return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(
389 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
388 std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data()));
390 case DataSeriesType::SPECTROGRAM:
389 case DataSeriesType::SPECTROGRAM:
391 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
390 return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(
392 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
391 std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data()));
393 case DataSeriesType::VECTOR:
392 case DataSeriesType::VECTOR:
394 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
393 return std::make_unique<PlottablesHelper<VectorTimeSerie>>(
395 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
394 std::dynamic_pointer_cast<VectorTimeSerie>(variable->data()));
396 default:
395 default:
397 // Creates default helper
396 // Creates default helper
398 break;
397 break;
399 }
398 }
400
399
401 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
400 return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr);
402 }
401 }
403
402
404 } // namespace
403 } // namespace
405
404
406 PlottablesMap VisualizationGraphHelper::create(
405 PlottablesMap VisualizationGraphHelper::create(
407 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
406 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
408 {
407 {
409 if (variable)
408 if (variable)
410 {
409 {
411 auto helper = createHelper(variable);
410 auto helper = createHelper(variable);
412 auto plottables = helper->create(plot);
411 auto plottables = helper->create(plot);
413 return plottables;
412 return plottables;
414 }
413 }
415 else
414 else
416 {
415 {
417 qCDebug(LOG_VisualizationGraphHelper())
416 qCDebug(LOG_VisualizationGraphHelper())
418 << QObject::tr("Can't create graph plottables : the variable is null");
417 << QObject::tr("Can't create graph plottables : the variable is null");
419 return PlottablesMap {};
418 return PlottablesMap {};
420 }
419 }
421 }
420 }
422
421
423 void VisualizationGraphHelper::setYAxisRange(
422 void VisualizationGraphHelper::setYAxisRange(
424 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
423 std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept
425 {
424 {
426 if (variable)
425 if (variable)
427 {
426 {
428 auto helper = createHelper(variable);
427 auto helper = createHelper(variable);
429 helper->setYAxisRange(variable->range(), plot);
428 helper->setYAxisRange(variable->range(), plot);
430 }
429 }
431 else
430 else
432 {
431 {
433 qCDebug(LOG_VisualizationGraphHelper())
432 qCDebug(LOG_VisualizationGraphHelper())
434 << QObject::tr("Can't set y-axis range of plot: the variable is null");
433 << QObject::tr("Can't set y-axis range of plot: the variable is null");
435 }
434 }
436 }
435 }
437
436
438 void VisualizationGraphHelper::updateData(
437 void VisualizationGraphHelper::updateData(
439 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
438 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime)
440 {
439 {
441 auto helper = createHelper(variable);
440 auto helper = createHelper(variable);
442 helper->update(plottables, dateTime);
441 helper->update(plottables, dateTime);
443 }
442 }
@@ -1,352 +1,349
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 #include "Visualization/AxisRenderingUtils.h"
2 #include "Visualization/AxisRenderingUtils.h"
3 #include "Visualization/ColorScaleEditor.h"
3 #include "Visualization/ColorScaleEditor.h"
4 #include "Visualization/PlottablesRenderingUtils.h"
4 #include "Visualization/PlottablesRenderingUtils.h"
5 #include "Visualization/SqpColorScale.h"
5 #include "Visualization/SqpColorScale.h"
6 #include "Visualization/VisualizationGraphWidget.h"
6 #include "Visualization/VisualizationGraphWidget.h"
7 #include "Visualization/qcustomplot.h"
7 #include "Visualization/qcustomplot.h"
8
8
9 #include <Common/DateUtils.h>
9 #include <Common/DateUtils.h>
10
10
11 #include <Data/IDataSeries.h>
12 #include <Variable/Variable.h>
13
14 #include <SqpApplication.h>
11 #include <SqpApplication.h>
15
12
16 namespace
13 namespace
17 {
14 {
18
15
19 /// Name of the axes layer in QCustomPlot
16 /// Name of the axes layer in QCustomPlot
20 const auto AXES_LAYER = QStringLiteral("axes");
17 const auto AXES_LAYER = QStringLiteral("axes");
21
18
22 /// Icon used to show x-axis properties
19 /// Icon used to show x-axis properties
23 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
20 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
24
21
25 /// Name of the overlay layer in QCustomPlot
22 /// Name of the overlay layer in QCustomPlot
26 const auto OVERLAY_LAYER = QStringLiteral("overlay");
23 const auto OVERLAY_LAYER = QStringLiteral("overlay");
27
24
28 /// Pixmap used to show x-axis properties
25 /// Pixmap used to show x-axis properties
29 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
26 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
30
27
31 /// Tooltip format for graphs
28 /// Tooltip format for graphs
32 const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
29 const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
33
30
34 /// Tooltip format for colormaps
31 /// Tooltip format for colormaps
35 const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
32 const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
36
33
37 /// Offset used to shift the tooltip of the mouse
34 /// Offset used to shift the tooltip of the mouse
38 const auto TOOLTIP_OFFSET = QPoint { 20, 20 };
35 const auto TOOLTIP_OFFSET = QPoint { 20, 20 };
39
36
40 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
37 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
41 const auto TOOLTIP_RECT = QRect { 10, 10, 10, 10 };
38 const auto TOOLTIP_RECT = QRect { 10, 10, 10, 10 };
42
39
43 /// Timeout after which the tooltip is displayed
40 /// Timeout after which the tooltip is displayed
44 const auto TOOLTIP_TIMEOUT = 500;
41 const auto TOOLTIP_TIMEOUT = 500;
45
42
46 void initPointTracerStyle(QCPItemTracer& tracer) noexcept
43 void initPointTracerStyle(QCPItemTracer& tracer) noexcept
47 {
44 {
48 tracer.setInterpolating(false);
45 tracer.setInterpolating(false);
49 tracer.setStyle(QCPItemTracer::tsCircle);
46 tracer.setStyle(QCPItemTracer::tsCircle);
50 tracer.setSize(3);
47 tracer.setSize(3);
51 tracer.setPen(QPen(Qt::black));
48 tracer.setPen(QPen(Qt::black));
52 tracer.setBrush(Qt::black);
49 tracer.setBrush(Qt::black);
53 tracer.setSelectable(false);
50 tracer.setSelectable(false);
54 }
51 }
55
52
56 QPixmap pixmap(const QString& iconPath) noexcept
53 QPixmap pixmap(const QString& iconPath) noexcept
57 {
54 {
58 return QIcon { iconPath }.pixmap(QSize { 16, 16 });
55 return QIcon { iconPath }.pixmap(QSize { 16, 16 });
59 }
56 }
60
57
61 void initClosePixmapStyle(QCPItemPixmap& pixmap) noexcept
58 void initClosePixmapStyle(QCPItemPixmap& pixmap) noexcept
62 {
59 {
63 // Icon
60 // Icon
64 pixmap.setPixmap(
61 pixmap.setPixmap(
65 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize { 16, 16 }));
62 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize { 16, 16 }));
66
63
67 // Position
64 // Position
68 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
65 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
69 pixmap.topLeft->setCoords(1, 0);
66 pixmap.topLeft->setCoords(1, 0);
70 pixmap.setClipToAxisRect(false);
67 pixmap.setClipToAxisRect(false);
71
68
72 // Can be selected
69 // Can be selected
73 pixmap.setSelectable(true);
70 pixmap.setSelectable(true);
74 }
71 }
75
72
76 void initXAxisPixmapStyle(QCPItemPixmap& itemPixmap) noexcept
73 void initXAxisPixmapStyle(QCPItemPixmap& itemPixmap) noexcept
77 {
74 {
78 // Icon
75 // Icon
79 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
76 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
80
77
81 // Position
78 // Position
82 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
79 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
83 itemPixmap.topLeft->setCoords(0, 1);
80 itemPixmap.topLeft->setCoords(0, 1);
84 itemPixmap.setClipToAxisRect(false);
81 itemPixmap.setClipToAxisRect(false);
85
82
86 // Can be selected
83 // Can be selected
87 itemPixmap.setSelectable(true);
84 itemPixmap.setSelectable(true);
88 }
85 }
89
86
90 void initTitleTextStyle(QCPItemText& text) noexcept
87 void initTitleTextStyle(QCPItemText& text) noexcept
91 {
88 {
92 // Font and background styles
89 // Font and background styles
93 text.setColor(Qt::gray);
90 text.setColor(Qt::gray);
94 text.setBrush(Qt::white);
91 text.setBrush(Qt::white);
95
92
96 // Position
93 // Position
97 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
94 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
98 text.position->setType(QCPItemPosition::ptAxisRectRatio);
95 text.position->setType(QCPItemPosition::ptAxisRectRatio);
99 text.position->setCoords(0.5, 0);
96 text.position->setCoords(0.5, 0);
100 text.setSelectable(false);
97 text.setSelectable(false);
101 }
98 }
102
99
103 /**
100 /**
104 * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
101 * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
105 * This method handles the fact that a colormap axis can be logarithmic or linear.
102 * This method handles the fact that a colormap axis can be logarithmic or linear.
106 * @param colormap the colormap for which to calculate the index
103 * @param colormap the colormap for which to calculate the index
107 * @param coord the coord to convert to cell index
104 * @param coord the coord to convert to cell index
108 * @param xCoord calculates the x index if true, calculates y index if false
105 * @param xCoord calculates the x index if true, calculates y index if false
109 * @return the cell index
106 * @return the cell index
110 */
107 */
111 int colorMapCellIndex(const QCPColorMap& colormap, double coord, bool xCoord)
108 int colorMapCellIndex(const QCPColorMap& colormap, double coord, bool xCoord)
112 {
109 {
113 // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
110 // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
114 auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
111 auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
115 == QCPAxis::stLogarithmic;
112 == QCPAxis::stLogarithmic;
116
113
117 if (isLogarithmic)
114 if (isLogarithmic)
118 {
115 {
119 // For a logarithmic axis we can't use the conversion method of colormap, so we calculate
116 // For a logarithmic axis we can't use the conversion method of colormap, so we calculate
120 // the index manually based on the position of the coordinate on the axis
117 // the index manually based on the position of the coordinate on the axis
121
118
122 // Gets the axis range and the number of values between range bounds to calculate the step
119 // Gets the axis range and the number of values between range bounds to calculate the step
123 // between each value of the range
120 // between each value of the range
124 auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
121 auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
125 auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
122 auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
126 auto valueStep
123 auto valueStep
127 = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
124 = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
128
125
129 // According to the coord position, calculates the closest index in the range
126 // According to the coord position, calculates the closest index in the range
130 return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
127 return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
131 }
128 }
132 else
129 else
133 {
130 {
134 // For a linear axis, we use the conversion method of colormap
131 // For a linear axis, we use the conversion method of colormap
135 int index;
132 int index;
136 if (xCoord)
133 if (xCoord)
137 {
134 {
138 colormap.data()->coordToCell(coord, 0., &index, nullptr);
135 colormap.data()->coordToCell(coord, 0., &index, nullptr);
139 }
136 }
140 else
137 else
141 {
138 {
142 colormap.data()->coordToCell(0., coord, nullptr, &index);
139 colormap.data()->coordToCell(0., coord, nullptr, &index);
143 }
140 }
144
141
145 return index;
142 return index;
146 }
143 }
147 }
144 }
148
145
149 } // namespace
146 } // namespace
150
147
151 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate
148 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate
152 {
149 {
153 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget& graphWidget)
150 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget& graphWidget)
154 : m_Plot { graphWidget.plot() }
151 : m_Plot { graphWidget.plot() }
155 , m_PointTracer { new QCPItemTracer { &m_Plot } }
152 , m_PointTracer { new QCPItemTracer { &m_Plot } }
156 , m_TracerTimer {}
153 , m_TracerTimer {}
157 , m_ClosePixmap { new QCPItemPixmap { &m_Plot } }
154 , m_ClosePixmap { new QCPItemPixmap { &m_Plot } }
158 , m_TitleText { new QCPItemText { &m_Plot } }
155 , m_TitleText { new QCPItemText { &m_Plot } }
159 , m_XAxisPixmap { new QCPItemPixmap { &m_Plot } }
156 , m_XAxisPixmap { new QCPItemPixmap { &m_Plot } }
160 , m_ShowXAxis { true }
157 , m_ShowXAxis { true }
161 , m_XAxisLabel {}
158 , m_XAxisLabel {}
162 , m_ColorScale { SqpColorScale { m_Plot } }
159 , m_ColorScale { SqpColorScale { m_Plot } }
163 {
160 {
164 initPointTracerStyle(*m_PointTracer);
161 initPointTracerStyle(*m_PointTracer);
165
162
166 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
163 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
167 m_TracerTimer.setSingleShot(true);
164 m_TracerTimer.setSingleShot(true);
168
165
169 // Inits "close button" in plot overlay
166 // Inits "close button" in plot overlay
170 m_ClosePixmap->setLayer(OVERLAY_LAYER);
167 m_ClosePixmap->setLayer(OVERLAY_LAYER);
171 initClosePixmapStyle(*m_ClosePixmap);
168 initClosePixmapStyle(*m_ClosePixmap);
172
169
173 // Connects pixmap selection to graph widget closing
170 // Connects pixmap selection to graph widget closing
174 QObject::connect(
171 QObject::connect(
175 &m_Plot, &QCustomPlot::itemClick, [&graphWidget, this](auto item, auto mouseEvent) {
172 &m_Plot, &QCustomPlot::itemClick, [&graphWidget, this](auto item, auto mouseEvent) {
176 if (item == m_ClosePixmap)
173 if (item == m_ClosePixmap)
177 {
174 {
178 graphWidget.close();
175 graphWidget.close();
179 }
176 }
180 });
177 });
181
178
182 // Inits graph name in plot overlay
179 // Inits graph name in plot overlay
183 m_TitleText->setLayer(OVERLAY_LAYER);
180 m_TitleText->setLayer(OVERLAY_LAYER);
184 m_TitleText->setText(graphWidget.name());
181 m_TitleText->setText(graphWidget.name());
185 initTitleTextStyle(*m_TitleText);
182 initTitleTextStyle(*m_TitleText);
186
183
187 // Inits "show x-axis button" in plot overlay
184 // Inits "show x-axis button" in plot overlay
188 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
185 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
189 initXAxisPixmapStyle(*m_XAxisPixmap);
186 initXAxisPixmapStyle(*m_XAxisPixmap);
190
187
191 // Connects pixmap selection to graph x-axis showing/hiding
188 // Connects pixmap selection to graph x-axis showing/hiding
192 QObject::connect(&m_Plot, &QCustomPlot::itemClick, [this](auto item, auto mouseEvent) {
189 QObject::connect(&m_Plot, &QCustomPlot::itemClick, [this](auto item, auto mouseEvent) {
193 if (m_XAxisPixmap == item)
190 if (m_XAxisPixmap == item)
194 {
191 {
195 // Changes the selection state and refreshes the x-axis
192 // Changes the selection state and refreshes the x-axis
196 m_ShowXAxis = !m_ShowXAxis;
193 m_ShowXAxis = !m_ShowXAxis;
197 this->updateXAxisState();
194 this->updateXAxisState();
198 m_Plot.layer(AXES_LAYER)->replot();
195 m_Plot.layer(AXES_LAYER)->replot();
199
196
200 // Deselects the x-axis pixmap and updates icon
197 // Deselects the x-axis pixmap and updates icon
201 m_XAxisPixmap->setPixmap(
198 m_XAxisPixmap->setPixmap(
202 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
199 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
203 m_Plot.layer(OVERLAY_LAYER)->replot();
200 m_Plot.layer(OVERLAY_LAYER)->replot();
204 }
201 }
205 });
202 });
206 }
203 }
207
204
208 /// Updates state of x-axis according to the current selection of x-axis pixmap
205 /// Updates state of x-axis according to the current selection of x-axis pixmap
209 /// @remarks the method doesn't call plot refresh
206 /// @remarks the method doesn't call plot refresh
210 void updateXAxisState() noexcept
207 void updateXAxisState() noexcept
211 {
208 {
212 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
209 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
213 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString {});
210 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString {});
214 }
211 }
215
212
216 QCustomPlot& m_Plot;
213 QCustomPlot& m_Plot;
217 QCPItemTracer* m_PointTracer;
214 QCPItemTracer* m_PointTracer;
218 QTimer m_TracerTimer;
215 QTimer m_TracerTimer;
219 QCPItemPixmap* m_ClosePixmap; /// Graph's close button
216 QCPItemPixmap* m_ClosePixmap; /// Graph's close button
220 QCPItemText* m_TitleText; /// Graph's title
217 QCPItemText* m_TitleText; /// Graph's title
221 QCPItemPixmap* m_XAxisPixmap;
218 QCPItemPixmap* m_XAxisPixmap;
222 bool m_ShowXAxis; /// X-axis properties are shown or hidden
219 bool m_ShowXAxis; /// X-axis properties are shown or hidden
223 QString m_XAxisLabel;
220 QString m_XAxisLabel;
224 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
221 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
225 };
222 };
226
223
227 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
224 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
228 VisualizationGraphWidget& graphWidget)
225 VisualizationGraphWidget& graphWidget)
229 : impl { spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget) }
226 : impl { spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget) }
230 {
227 {
231 }
228 }
232
229
233 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent* event) noexcept
230 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent* event) noexcept
234 {
231 {
235 // Opens color scale editor if color scale is double clicked
232 // Opens color scale editor if color scale is double clicked
236 auto colorScale = static_cast<QCPColorScale*>(impl->m_Plot.layoutElementAt(event->pos()));
233 auto colorScale = static_cast<QCPColorScale*>(impl->m_Plot.layoutElementAt(event->pos()));
237 if (impl->m_ColorScale.m_Scale == colorScale)
234 if (impl->m_ColorScale.m_Scale == colorScale)
238 {
235 {
239 if (ColorScaleEditor { impl->m_ColorScale }.exec() == QDialog::Accepted)
236 if (ColorScaleEditor { impl->m_ColorScale }.exec() == QDialog::Accepted)
240 {
237 {
241 impl->m_Plot.replot();
238 impl->m_Plot.replot();
242 }
239 }
243 }
240 }
244 }
241 }
245
242
246 void VisualizationGraphRenderingDelegate::updateTooltip(QMouseEvent* event) noexcept
243 void VisualizationGraphRenderingDelegate::updateTooltip(QMouseEvent* event) noexcept
247 {
244 {
248 // Cancels pending refresh
245 // Cancels pending refresh
249 impl->m_TracerTimer.disconnect();
246 impl->m_TracerTimer.disconnect();
250
247
251 // Reinits tracers
248 // Reinits tracers
252 impl->m_PointTracer->setGraph(nullptr);
249 impl->m_PointTracer->setGraph(nullptr);
253 impl->m_PointTracer->setVisible(false);
250 impl->m_PointTracer->setVisible(false);
254 impl->m_Plot.replot();
251 impl->m_Plot.replot();
255
252
256 QString tooltip {};
253 QString tooltip {};
257
254
258 // Gets the graph under the mouse position
255 // Gets the graph under the mouse position
259 auto eventPos = event->pos();
256 auto eventPos = event->pos();
260 if (auto graph = qobject_cast<QCPGraph*>(impl->m_Plot.plottableAt(eventPos)))
257 if (auto graph = qobject_cast<QCPGraph*>(impl->m_Plot.plottableAt(eventPos)))
261 {
258 {
262 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
259 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
263 auto graphData = graph->data();
260 auto graphData = graph->data();
264
261
265 // Gets the closest data point to the mouse
262 // Gets the closest data point to the mouse
266 auto graphDataIt = graphData->findBegin(mouseKey);
263 auto graphDataIt = graphData->findBegin(mouseKey);
267 if (graphDataIt != graphData->constEnd())
264 if (graphDataIt != graphData->constEnd())
268 {
265 {
269 // Sets tooltip
266 // Sets tooltip
270 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
267 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
271 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
268 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
272 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
269 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
273
270
274 // Displays point tracer
271 // Displays point tracer
275 impl->m_PointTracer->setGraph(graph);
272 impl->m_PointTracer->setGraph(graph);
276 impl->m_PointTracer->setGraphKey(graphDataIt->key);
273 impl->m_PointTracer->setGraphKey(graphDataIt->key);
277 impl->m_PointTracer->setLayer(
274 impl->m_PointTracer->setLayer(
278 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
275 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
279 impl->m_PointTracer->setVisible(true);
276 impl->m_PointTracer->setVisible(true);
280 impl->m_Plot.replot();
277 impl->m_Plot.replot();
281 }
278 }
282 }
279 }
283 else if (auto colorMap = qobject_cast<QCPColorMap*>(impl->m_Plot.plottableAt(eventPos)))
280 else if (auto colorMap = qobject_cast<QCPColorMap*>(impl->m_Plot.plottableAt(eventPos)))
284 {
281 {
285 // Gets x and y coords
282 // Gets x and y coords
286 auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
283 auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
287 auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
284 auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
288
285
289 // Calculates x and y cell indexes, and retrieves the underlying value
286 // Calculates x and y cell indexes, and retrieves the underlying value
290 auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
287 auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
291 auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
288 auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
292 auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
289 auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
293
290
294 // Sets tooltips
291 // Sets tooltips
295 tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
292 tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
296 formatValue(y, *colorMap->valueAxis()),
293 formatValue(y, *colorMap->valueAxis()),
297 formatValue(value, *colorMap->colorScale()->axis()));
294 formatValue(value, *colorMap->colorScale()->axis()));
298 }
295 }
299
296
300 if (!tooltip.isEmpty())
297 if (!tooltip.isEmpty())
301 {
298 {
302 // Starts timer to show tooltip after timeout
299 // Starts timer to show tooltip after timeout
303 auto showTooltip = [tooltip, eventPos, this]() {
300 auto showTooltip = [tooltip, eventPos, this]() {
304 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
301 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
305 &impl->m_Plot, TOOLTIP_RECT);
302 &impl->m_Plot, TOOLTIP_RECT);
306 };
303 };
307
304
308 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
305 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
309 impl->m_TracerTimer.start();
306 impl->m_TracerTimer.start();
310 }
307 }
311 }
308 }
312
309
313 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
310 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
314 {
311 {
315 // Updates color scale bounds
312 // Updates color scale bounds
316 impl->m_ColorScale.updateDataRange();
313 impl->m_ColorScale.updateDataRange();
317 impl->m_Plot.replot();
314 impl->m_Plot.replot();
318 }
315 }
319
316
320 void VisualizationGraphRenderingDelegate::setAxesUnits(Variable2& variable) noexcept
317 void VisualizationGraphRenderingDelegate::setAxesUnits(Variable2& variable) noexcept
321 {
318 {
322
319
323 auto axisHelper = IAxisHelperFactory::create(variable);
320 auto axisHelper = IAxisHelperFactory::create(variable);
324 axisHelper->setUnits(impl->m_Plot, impl->m_ColorScale);
321 axisHelper->setUnits(impl->m_Plot, impl->m_ColorScale);
325
322
326 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
323 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
327 impl->m_XAxisLabel = impl->m_Plot.xAxis->label();
324 impl->m_XAxisLabel = impl->m_Plot.xAxis->label();
328
325
329 // Updates x-axis state
326 // Updates x-axis state
330 impl->updateXAxisState();
327 impl->updateXAxisState();
331
328
332 impl->m_Plot.layer(AXES_LAYER)->replot();
329 impl->m_Plot.layer(AXES_LAYER)->replot();
333 }
330 }
334
331
335 void VisualizationGraphRenderingDelegate::setGraphProperties(
332 void VisualizationGraphRenderingDelegate::setGraphProperties(
336 Variable2& variable, PlottablesMap& plottables) noexcept
333 Variable2& variable, PlottablesMap& plottables) noexcept
337 {
334 {
338 // Axes' properties
335 // Axes' properties
339 auto axisHelper = IAxisHelperFactory::create(variable);
336 auto axisHelper = IAxisHelperFactory::create(variable);
340 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
337 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
341
338
342 // Plottables' properties
339 // Plottables' properties
343 auto plottablesHelper = IPlottablesHelperFactory::create(variable);
340 auto plottablesHelper = IPlottablesHelperFactory::create(variable);
344 plottablesHelper->setProperties(plottables);
341 plottablesHelper->setProperties(plottables);
345 }
342 }
346
343
347 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
344 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
348 {
345 {
349 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
346 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
350 overlay->setVisible(show);
347 overlay->setVisible(show);
351 overlay->replot();
348 overlay->replot();
352 }
349 }
@@ -1,1580 +1,1577
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>
19 #include <Data/DateTimeRangeHelper.h>
18 #include <Data/DateTimeRangeHelper.h>
20 #include <Data/IDataSeries.h>
21 #include <Data/SpectrogramSeries.h>
22 #include <DragAndDrop/DragDropGuiController.h>
19 #include <DragAndDrop/DragDropGuiController.h>
23 #include <Settings/SqpSettingsDefs.h>
20 #include <Settings/SqpSettingsDefs.h>
24 #include <SqpApplication.h>
21 #include <SqpApplication.h>
25 #include <Time/TimeController.h>
22 #include <Time/TimeController.h>
26 #include <Variable/Variable2.h>
23 #include <Variable/Variable2.h>
27 #include <Variable/VariableController2.h>
24 #include <Variable/VariableController2.h>
28
25
29 #include <unordered_map>
26 #include <unordered_map>
30
27
31 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
32
29
33 namespace
30 namespace
34 {
31 {
35
32
36 /// Key pressed to enable drag&drop in all modes
33 /// Key pressed to enable drag&drop in all modes
37 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
38
35
39 /// Key pressed to enable zoom on horizontal axis
36 /// Key pressed to enable zoom on horizontal axis
40 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
41
38
42 /// Key pressed to enable zoom on vertical axis
39 /// Key pressed to enable zoom on vertical axis
43 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
44
41
45 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
46 const auto PAN_SPEED = 5;
43 const auto PAN_SPEED = 5;
47
44
48 /// Key pressed to enable a calibration pan
45 /// Key pressed to enable a calibration pan
49 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
50
47
51 /// Key pressed to enable multi selection of selection zones
48 /// Key pressed to enable multi selection of selection zones
52 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
53
50
54 /// Minimum size for the zoom box, in percentage of the axis range
51 /// Minimum size for the zoom box, in percentage of the axis range
55 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
56
53
57 /// Format of the dates appearing in the label of a cursor
54 /// Format of the dates appearing in the label of a cursor
58 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
59
56
60 } // namespace
57 } // namespace
61
58
62 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate
59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate
63 {
60 {
64
61
65 explicit VisualizationGraphWidgetPrivate(const QString& name)
62 explicit VisualizationGraphWidgetPrivate(const QString& name)
66 : m_Name { name }
63 : m_Name { name }
67 , m_Flags { GraphFlag::EnableAll }
64 , m_Flags { GraphFlag::EnableAll }
68 , m_IsCalibration { false }
65 , m_IsCalibration { false }
69 , m_RenderingDelegate { nullptr }
66 , m_RenderingDelegate { nullptr }
70 {
67 {
71 m_plot = new QCustomPlot();
68 m_plot = new QCustomPlot();
72 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
69 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
73 m_plot->setPlottingHint(QCP::phFastPolylines, true);
70 m_plot->setPlottingHint(QCP::phFastPolylines, true);
74 }
71 }
75
72
76 void updateData(
73 void updateData(
77 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& range)
74 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& range)
78 {
75 {
79 VisualizationGraphHelper::updateData(plottables, variable, range);
76 VisualizationGraphHelper::updateData(plottables, variable, range);
80
77
81 // Prevents that data has changed to update rendering
78 // Prevents that data has changed to update rendering
82 m_RenderingDelegate->onPlotUpdated();
79 m_RenderingDelegate->onPlotUpdated();
83 }
80 }
84
81
85 QString m_Name;
82 QString m_Name;
86 // 1 variable -> n qcpplot
83 // 1 variable -> n qcpplot
87 std::map<std::shared_ptr<Variable2>, PlottablesMap> m_VariableToPlotMultiMap;
84 std::map<std::shared_ptr<Variable2>, PlottablesMap> m_VariableToPlotMultiMap;
88 GraphFlags m_Flags;
85 GraphFlags m_Flags;
89 bool m_IsCalibration;
86 bool m_IsCalibration;
90 QCustomPlot* m_plot;
87 QCustomPlot* m_plot;
91 QPoint m_lastMousePos;
88 QPoint m_lastMousePos;
92 QCPRange m_lastXRange;
89 QCPRange m_lastXRange;
93 QCPRange m_lastYRange;
90 QCPRange m_lastYRange;
94 /// Delegate used to attach rendering features to the plot
91 /// Delegate used to attach rendering features to the plot
95 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
92 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
96
93
97 QCPItemRect* m_DrawingZoomRect = nullptr;
94 QCPItemRect* m_DrawingZoomRect = nullptr;
98 QStack<QPair<QCPRange, QCPRange>> m_ZoomStack;
95 QStack<QPair<QCPRange, QCPRange>> m_ZoomStack;
99
96
100 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
97 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
101 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
102
99
103 VisualizationSelectionZoneItem* m_DrawingZone = nullptr;
100 VisualizationSelectionZoneItem* m_DrawingZone = nullptr;
104 VisualizationSelectionZoneItem* m_HoveredZone = nullptr;
101 VisualizationSelectionZoneItem* m_HoveredZone = nullptr;
105 QVector<VisualizationSelectionZoneItem*> m_SelectionZones;
102 QVector<VisualizationSelectionZoneItem*> m_SelectionZones;
106
103
107 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
104 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
108
105
109 bool m_VariableAutoRangeOnInit = true;
106 bool m_VariableAutoRangeOnInit = true;
110
107
111 inline void enterPlotDrag(const QPoint& position)
108 inline void enterPlotDrag(const QPoint& position)
112 {
109 {
113 m_lastMousePos = m_plot->mapFromParent(position);
110 m_lastMousePos = m_plot->mapFromParent(position);
114 m_lastXRange = m_plot->xAxis->range();
111 m_lastXRange = m_plot->xAxis->range();
115 m_lastYRange = m_plot->yAxis->range();
112 m_lastYRange = m_plot->yAxis->range();
116 }
113 }
117
114
118 inline bool isDrawingZoomRect() { return m_DrawingZoomRect != nullptr; }
115 inline bool isDrawingZoomRect() { return m_DrawingZoomRect != nullptr; }
119 void updateZoomRect(const QPoint& newPos)
116 void updateZoomRect(const QPoint& newPos)
120 {
117 {
121 QPointF pos { m_plot->xAxis->pixelToCoord(newPos.x()),
118 QPointF pos { m_plot->xAxis->pixelToCoord(newPos.x()),
122 m_plot->yAxis->pixelToCoord(newPos.y()) };
119 m_plot->yAxis->pixelToCoord(newPos.y()) };
123 m_DrawingZoomRect->bottomRight->setCoords(pos);
120 m_DrawingZoomRect->bottomRight->setCoords(pos);
124 m_plot->replot(QCustomPlot::rpQueuedReplot);
121 m_plot->replot(QCustomPlot::rpQueuedReplot);
125 }
122 }
126
123
127 void applyZoomRect()
124 void applyZoomRect()
128 {
125 {
129 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
126 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
130 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
127 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
131
128
132 auto newAxisXRange = QCPRange { m_DrawingZoomRect->topLeft->coords().x(),
129 auto newAxisXRange = QCPRange { m_DrawingZoomRect->topLeft->coords().x(),
133 m_DrawingZoomRect->bottomRight->coords().x() };
130 m_DrawingZoomRect->bottomRight->coords().x() };
134
131
135 auto newAxisYRange = QCPRange { m_DrawingZoomRect->topLeft->coords().y(),
132 auto newAxisYRange = QCPRange { m_DrawingZoomRect->topLeft->coords().y(),
136 m_DrawingZoomRect->bottomRight->coords().y() };
133 m_DrawingZoomRect->bottomRight->coords().y() };
137
134
138 removeDrawingRect();
135 removeDrawingRect();
139
136
140 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
137 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
141 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
138 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
142 {
139 {
143 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
140 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
144 axisX->setRange(newAxisXRange);
141 axisX->setRange(newAxisXRange);
145 axisY->setRange(newAxisYRange);
142 axisY->setRange(newAxisYRange);
146
143
147 m_plot->replot(QCustomPlot::rpQueuedReplot);
144 m_plot->replot(QCustomPlot::rpQueuedReplot);
148 }
145 }
149 }
146 }
150
147
151 inline bool isDrawingZoneRect() { return m_DrawingZone != nullptr; }
148 inline bool isDrawingZoneRect() { return m_DrawingZone != nullptr; }
152 void updateZoneRect(const QPoint& newPos)
149 void updateZoneRect(const QPoint& newPos)
153 {
150 {
154 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
151 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
155 m_plot->replot(QCustomPlot::rpQueuedReplot);
152 m_plot->replot(QCustomPlot::rpQueuedReplot);
156 }
153 }
157
154
158 void startDrawingRect(const QPoint& pos)
155 void startDrawingRect(const QPoint& pos)
159 {
156 {
160 removeDrawingRect();
157 removeDrawingRect();
161
158
162 auto axisPos = posToAxisPos(pos);
159 auto axisPos = posToAxisPos(pos);
163
160
164 m_DrawingZoomRect = new QCPItemRect { m_plot };
161 m_DrawingZoomRect = new QCPItemRect { m_plot };
165 QPen p;
162 QPen p;
166 p.setWidth(2);
163 p.setWidth(2);
167 m_DrawingZoomRect->setPen(p);
164 m_DrawingZoomRect->setPen(p);
168
165
169 m_DrawingZoomRect->topLeft->setCoords(axisPos);
166 m_DrawingZoomRect->topLeft->setCoords(axisPos);
170 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
167 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
171 }
168 }
172
169
173 void removeDrawingRect()
170 void removeDrawingRect()
174 {
171 {
175 if (m_DrawingZoomRect)
172 if (m_DrawingZoomRect)
176 {
173 {
177 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
174 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
178 m_DrawingZoomRect = nullptr;
175 m_DrawingZoomRect = nullptr;
179 m_plot->replot(QCustomPlot::rpQueuedReplot);
176 m_plot->replot(QCustomPlot::rpQueuedReplot);
180 }
177 }
181 }
178 }
182
179
183 void selectZone(const QPoint& pos)
180 void selectZone(const QPoint& pos)
184 {
181 {
185 auto zoneAtPos = selectionZoneAt(pos);
182 auto zoneAtPos = selectionZoneAt(pos);
186 setSelectionZonesEditionEnabled(
183 setSelectionZonesEditionEnabled(
187 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
184 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
188 }
185 }
189
186
190 void startDrawingZone(const QPoint& pos)
187 void startDrawingZone(const QPoint& pos)
191 {
188 {
192 endDrawingZone();
189 endDrawingZone();
193
190
194 auto axisPos = posToAxisPos(pos);
191 auto axisPos = posToAxisPos(pos);
195
192
196 m_DrawingZone = new VisualizationSelectionZoneItem { m_plot };
193 m_DrawingZone = new VisualizationSelectionZoneItem { m_plot };
197 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
194 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
198 m_DrawingZone->setEditionEnabled(false);
195 m_DrawingZone->setEditionEnabled(false);
199 }
196 }
200
197
201 void endDrawingZone()
198 void endDrawingZone()
202 {
199 {
203 if (m_DrawingZone)
200 if (m_DrawingZone)
204 {
201 {
205 auto drawingZoneRange = m_DrawingZone->range();
202 auto drawingZoneRange = m_DrawingZone->range();
206 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0)
203 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0)
207 {
204 {
208 m_DrawingZone->setEditionEnabled(true);
205 m_DrawingZone->setEditionEnabled(true);
209 addSelectionZone(m_DrawingZone);
206 addSelectionZone(m_DrawingZone);
210 }
207 }
211 else
208 else
212 {
209 {
213 m_plot->removeItem(m_DrawingZone);
210 m_plot->removeItem(m_DrawingZone);
214 }
211 }
215
212
216 m_plot->replot(QCustomPlot::rpQueuedReplot);
213 m_plot->replot(QCustomPlot::rpQueuedReplot);
217 m_DrawingZone = nullptr;
214 m_DrawingZone = nullptr;
218 }
215 }
219 }
216 }
220
217
221 void moveSelectionZone(const QPoint& destination)
218 void moveSelectionZone(const QPoint& destination)
222 {
219 {
223 /*
220 /*
224 * I give up on this for now
221 * I give up on this for now
225 * @TODO implement this, the difficulty is that selection zones have their own
222 * @TODO implement this, the difficulty is that selection zones have their own
226 * event handling code which seems to rely on QCP GUI event handling propagation
223 * event handling code which seems to rely on QCP GUI event handling propagation
227 * which was a realy bad design choice.
224 * which was a realy bad design choice.
228 */
225 */
229 }
226 }
230
227
231 void setSelectionZonesEditionEnabled(bool value)
228 void setSelectionZonesEditionEnabled(bool value)
232 {
229 {
233 for (auto s : m_SelectionZones)
230 for (auto s : m_SelectionZones)
234 {
231 {
235 s->setEditionEnabled(value);
232 s->setEditionEnabled(value);
236 }
233 }
237 }
234 }
238
235
239 void addSelectionZone(VisualizationSelectionZoneItem* zone) { m_SelectionZones << zone; }
236 void addSelectionZone(VisualizationSelectionZoneItem* zone) { m_SelectionZones << zone; }
240
237
241 VisualizationSelectionZoneItem* selectionZoneAt(const QPoint& pos) const
238 VisualizationSelectionZoneItem* selectionZoneAt(const QPoint& pos) const
242 {
239 {
243 VisualizationSelectionZoneItem* selectionZoneItemUnderCursor = nullptr;
240 VisualizationSelectionZoneItem* selectionZoneItemUnderCursor = nullptr;
244 auto minDistanceToZone = -1;
241 auto minDistanceToZone = -1;
245 for (auto zone : m_SelectionZones)
242 for (auto zone : m_SelectionZones)
246 {
243 {
247 auto distanceToZone = zone->selectTest(pos, false);
244 auto distanceToZone = zone->selectTest(pos, false);
248 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
245 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
249 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance())
246 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance())
250 {
247 {
251 selectionZoneItemUnderCursor = zone;
248 selectionZoneItemUnderCursor = zone;
252 }
249 }
253 }
250 }
254
251
255 return selectionZoneItemUnderCursor;
252 return selectionZoneItemUnderCursor;
256 }
253 }
257
254
258 QVector<VisualizationSelectionZoneItem*> selectionZonesAt(
255 QVector<VisualizationSelectionZoneItem*> selectionZonesAt(
259 const QPoint& pos, const QCustomPlot& plot) const
256 const QPoint& pos, const QCustomPlot& plot) const
260 {
257 {
261 QVector<VisualizationSelectionZoneItem*> zones;
258 QVector<VisualizationSelectionZoneItem*> zones;
262 for (auto zone : m_SelectionZones)
259 for (auto zone : m_SelectionZones)
263 {
260 {
264 auto distanceToZone = zone->selectTest(pos, false);
261 auto distanceToZone = zone->selectTest(pos, false);
265 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance())
262 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance())
266 {
263 {
267 zones << zone;
264 zones << zone;
268 }
265 }
269 }
266 }
270
267
271 return zones;
268 return zones;
272 }
269 }
273
270
274 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem* zone, QCustomPlot& plot)
271 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem* zone, QCustomPlot& plot)
275 {
272 {
276 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone)
273 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone)
277 {
274 {
278 zone->moveToTop();
275 zone->moveToTop();
279 m_SelectionZones.removeAll(zone);
276 m_SelectionZones.removeAll(zone);
280 m_SelectionZones.append(zone);
277 m_SelectionZones.append(zone);
281 }
278 }
282 }
279 }
283
280
284 QPointF posToAxisPos(const QPoint& pos) const
281 QPointF posToAxisPos(const QPoint& pos) const
285 {
282 {
286 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
283 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
287 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
284 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
288 return QPointF { axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y()) };
285 return QPointF { axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y()) };
289 }
286 }
290
287
291 bool pointIsInAxisRect(const QPointF& axisPoint, QCustomPlot& plot) const
288 bool pointIsInAxisRect(const QPointF& axisPoint, QCustomPlot& plot) const
292 {
289 {
293 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
290 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
294 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
291 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
295 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
292 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
296 }
293 }
297
294
298 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis* axis)
295 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis* axis)
299 {
296 {
300 if (axis->scaleType() == QCPAxis::stLinear)
297 if (axis->scaleType() == QCPAxis::stLinear)
301 {
298 {
302 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
299 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
303 return QCPRange { axis->range().lower + diff, axis->range().upper + diff };
300 return QCPRange { axis->range().lower + diff, axis->range().upper + diff };
304 }
301 }
305 else
302 else
306 {
303 {
307 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
304 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
308 return QCPRange { axis->range().lower * diff, axis->range().upper * diff };
305 return QCPRange { axis->range().lower * diff, axis->range().upper * diff };
309 }
306 }
310 }
307 }
311
308
312 void setRange(const DateTimeRange& newRange, bool updateVar = true)
309 void setRange(const DateTimeRange& newRange, bool updateVar = true)
313 {
310 {
314 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
311 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
315 if (updateVar)
312 if (updateVar)
316 {
313 {
317 for (auto it = m_VariableToPlotMultiMap.begin(), end = m_VariableToPlotMultiMap.end();
314 for (auto it = m_VariableToPlotMultiMap.begin(), end = m_VariableToPlotMultiMap.end();
318 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
315 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
319 {
316 {
320 sqpApp->variableController().asyncChangeRange(it->first, newRange);
317 sqpApp->variableController().asyncChangeRange(it->first, newRange);
321 }
318 }
322 }
319 }
323 m_plot->replot(QCustomPlot::rpQueuedReplot);
320 m_plot->replot(QCustomPlot::rpQueuedReplot);
324 }
321 }
325
322
326 void setRange(const QCPRange& newRange)
323 void setRange(const QCPRange& newRange)
327 {
324 {
328 auto graphRange = DateTimeRange { newRange.lower, newRange.upper };
325 auto graphRange = DateTimeRange { newRange.lower, newRange.upper };
329 setRange(graphRange);
326 setRange(graphRange);
330 }
327 }
331
328
332 void rescaleY() { m_plot->yAxis->rescale(true); }
329 void rescaleY() { m_plot->yAxis->rescale(true); }
333
330
334 std::tuple<double, double> moveGraph(const QPoint& destination)
331 std::tuple<double, double> moveGraph(const QPoint& destination)
335 {
332 {
336 auto currentPos = m_plot->mapFromParent(destination);
333 auto currentPos = m_plot->mapFromParent(destination);
337 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
334 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
338 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
335 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
339 auto oldXRange = xAxis->range();
336 auto oldXRange = xAxis->range();
340 auto oldYRange = yAxis->range();
337 auto oldYRange = yAxis->range();
341 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
338 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
342 xAxis->setRange(m_lastXRange.lower + dx, m_lastXRange.upper + dx);
339 xAxis->setRange(m_lastXRange.lower + dx, m_lastXRange.upper + dx);
343 if (yAxis->scaleType() == QCPAxis::stLinear)
340 if (yAxis->scaleType() == QCPAxis::stLinear)
344 {
341 {
345 double dy
342 double dy
346 = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
343 = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
347 yAxis->setRange(m_lastYRange.lower + dy, m_lastYRange.upper + dy);
344 yAxis->setRange(m_lastYRange.lower + dy, m_lastYRange.upper + dy);
348 }
345 }
349 else
346 else
350 {
347 {
351 double dy
348 double dy
352 = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
349 = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
353 yAxis->setRange(m_lastYRange.lower * dy, m_lastYRange.upper * dy);
350 yAxis->setRange(m_lastYRange.lower * dy, m_lastYRange.upper * dy);
354 }
351 }
355 auto newXRange = xAxis->range();
352 auto newXRange = xAxis->range();
356 auto newYRange = yAxis->range();
353 auto newYRange = yAxis->range();
357 setRange(xAxis->range());
354 setRange(xAxis->range());
358 // m_lastMousePos = currentPos;
355 // m_lastMousePos = currentPos;
359 return { newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower };
356 return { newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower };
360 }
357 }
361
358
362 void zoom(double factor, int center, Qt::Orientation orientation)
359 void zoom(double factor, int center, Qt::Orientation orientation)
363 {
360 {
364 QCPAxis* axis = m_plot->axisRect()->rangeZoomAxis(orientation);
361 QCPAxis* axis = m_plot->axisRect()->rangeZoomAxis(orientation);
365 axis->scaleRange(factor, axis->pixelToCoord(center));
362 axis->scaleRange(factor, axis->pixelToCoord(center));
366 if (orientation == Qt::Horizontal)
363 if (orientation == Qt::Horizontal)
367 setRange(axis->range());
364 setRange(axis->range());
368 m_plot->replot(QCustomPlot::rpQueuedReplot);
365 m_plot->replot(QCustomPlot::rpQueuedReplot);
369 }
366 }
370
367
371 void transform(const DateTimeRangeTransformation& tranformation)
368 void transform(const DateTimeRangeTransformation& tranformation)
372 {
369 {
373 auto graphRange = m_plot->xAxis->range();
370 auto graphRange = m_plot->xAxis->range();
374 DateTimeRange range { graphRange.lower, graphRange.upper };
371 DateTimeRange range { graphRange.lower, graphRange.upper };
375 range = range.transform(tranformation);
372 range = range.transform(tranformation);
376 setRange(range);
373 setRange(range);
377 m_plot->replot(QCustomPlot::rpQueuedReplot);
374 m_plot->replot(QCustomPlot::rpQueuedReplot);
378 }
375 }
379
376
380 void move(double dx, double dy)
377 void move(double dx, double dy)
381 {
378 {
382 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
379 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
383 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
380 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
384 xAxis->setRange(QCPRange(xAxis->range().lower + dx, xAxis->range().upper + dx));
381 xAxis->setRange(QCPRange(xAxis->range().lower + dx, xAxis->range().upper + dx));
385 yAxis->setRange(QCPRange(yAxis->range().lower + dy, yAxis->range().upper + dy));
382 yAxis->setRange(QCPRange(yAxis->range().lower + dy, yAxis->range().upper + dy));
386 setRange(xAxis->range());
383 setRange(xAxis->range());
387 m_plot->replot(QCustomPlot::rpQueuedReplot);
384 m_plot->replot(QCustomPlot::rpQueuedReplot);
388 }
385 }
389
386
390 void move(double factor, Qt::Orientation orientation)
387 void move(double factor, Qt::Orientation orientation)
391 {
388 {
392 auto oldRange = m_plot->xAxis->range();
389 auto oldRange = m_plot->xAxis->range();
393 QCPAxis* axis = m_plot->axisRect()->rangeDragAxis(orientation);
390 QCPAxis* axis = m_plot->axisRect()->rangeDragAxis(orientation);
394 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear)
391 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear)
395 {
392 {
396 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
393 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
397 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
394 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
398 }
395 }
399 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic)
396 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic)
400 {
397 {
401 int start = 0, stop = 0;
398 int start = 0, stop = 0;
402 double diff = 0.;
399 double diff = 0.;
403 if (factor > 0.0)
400 if (factor > 0.0)
404 {
401 {
405 stop = m_plot->width() * factor / 10;
402 stop = m_plot->width() * factor / 10;
406 start = 2 * m_plot->width() * factor / 10;
403 start = 2 * m_plot->width() * factor / 10;
407 }
404 }
408 if (factor < 0.0)
405 if (factor < 0.0)
409 {
406 {
410 factor *= -1.0;
407 factor *= -1.0;
411 start = m_plot->width() * factor / 10;
408 start = m_plot->width() * factor / 10;
412 stop = 2 * m_plot->width() * factor / 10;
409 stop = 2 * m_plot->width() * factor / 10;
413 }
410 }
414 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
411 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
415 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
412 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
416 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
413 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
417 }
414 }
418 if (orientation == Qt::Horizontal)
415 if (orientation == Qt::Horizontal)
419 setRange(axis->range());
416 setRange(axis->range());
420 m_plot->replot(QCustomPlot::rpQueuedReplot);
417 m_plot->replot(QCustomPlot::rpQueuedReplot);
421 }
418 }
422 };
419 };
423
420
424 VisualizationGraphWidget::VisualizationGraphWidget(const QString& name, QWidget* parent)
421 VisualizationGraphWidget::VisualizationGraphWidget(const QString& name, QWidget* parent)
425 : VisualizationDragWidget { parent }
422 : VisualizationDragWidget { parent }
426 , ui { new Ui::VisualizationGraphWidget }
423 , ui { new Ui::VisualizationGraphWidget }
427 , impl { spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name) }
424 , impl { spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name) }
428 {
425 {
429 ui->setupUi(this);
426 ui->setupUi(this);
430 this->layout()->addWidget(impl->m_plot);
427 this->layout()->addWidget(impl->m_plot);
431 // 'Close' options : widget is deleted when closed
428 // 'Close' options : widget is deleted when closed
432 setAttribute(Qt::WA_DeleteOnClose);
429 setAttribute(Qt::WA_DeleteOnClose);
433
430
434 // The delegate must be initialized after the ui as it uses the plot
431 // The delegate must be initialized after the ui as it uses the plot
435 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
432 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
436
433
437 // Init the cursors
434 // Init the cursors
438 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
435 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
439 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
436 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
440 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
437 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
441 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
438 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
442
439
443 this->setFocusPolicy(Qt::WheelFocus);
440 this->setFocusPolicy(Qt::WheelFocus);
444 this->setMouseTracking(true);
441 this->setMouseTracking(true);
445 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
442 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
446 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
443 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
447 impl->m_plot->setParent(this);
444 impl->m_plot->setParent(this);
448
445
449 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this,
446 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this,
450 &VisualizationGraphWidget::variableDeleted);
447 &VisualizationGraphWidget::variableDeleted);
451 }
448 }
452
449
453
450
454 VisualizationGraphWidget::~VisualizationGraphWidget()
451 VisualizationGraphWidget::~VisualizationGraphWidget()
455 {
452 {
456 delete ui;
453 delete ui;
457 }
454 }
458
455
459 VisualizationZoneWidget* VisualizationGraphWidget::parentZoneWidget() const noexcept
456 VisualizationZoneWidget* VisualizationGraphWidget::parentZoneWidget() const noexcept
460 {
457 {
461 auto parent = parentWidget();
458 auto parent = parentWidget();
462 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget*>(parent))
459 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget*>(parent))
463 {
460 {
464 parent = parent->parentWidget();
461 parent = parent->parentWidget();
465 }
462 }
466
463
467 return qobject_cast<VisualizationZoneWidget*>(parent);
464 return qobject_cast<VisualizationZoneWidget*>(parent);
468 }
465 }
469
466
470 VisualizationWidget* VisualizationGraphWidget::parentVisualizationWidget() const
467 VisualizationWidget* VisualizationGraphWidget::parentVisualizationWidget() const
471 {
468 {
472 auto parent = parentWidget();
469 auto parent = parentWidget();
473 while (parent != nullptr && !qobject_cast<VisualizationWidget*>(parent))
470 while (parent != nullptr && !qobject_cast<VisualizationWidget*>(parent))
474 {
471 {
475 parent = parent->parentWidget();
472 parent = parent->parentWidget();
476 }
473 }
477
474
478 return qobject_cast<VisualizationWidget*>(parent);
475 return qobject_cast<VisualizationWidget*>(parent);
479 }
476 }
480
477
481 void VisualizationGraphWidget::setFlags(GraphFlags flags)
478 void VisualizationGraphWidget::setFlags(GraphFlags flags)
482 {
479 {
483 impl->m_Flags = std::move(flags);
480 impl->m_Flags = std::move(flags);
484 }
481 }
485
482
486 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range)
483 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range)
487 {
484 {
488 // Uses delegate to create the qcpplot components according to the variable
485 // Uses delegate to create the qcpplot components according to the variable
489 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
486 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
490
487
491 // Sets graph properties
488 // Sets graph properties
492 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
489 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
493
490
494 impl->m_VariableToPlotMultiMap.insert({ variable, std::move(createdPlottables) });
491 impl->m_VariableToPlotMultiMap.insert({ variable, std::move(createdPlottables) });
495
492
496 setGraphRange(range);
493 setGraphRange(range);
497 // If the variable already has its data loaded, load its units and its range in the graph
494 // If the variable already has its data loaded, load its units and its range in the graph
498 if (variable->data() != nullptr)
495 if (variable->data() != nullptr)
499 {
496 {
500 impl->m_RenderingDelegate->setAxesUnits(*variable);
497 impl->m_RenderingDelegate->setAxesUnits(*variable);
501 }
498 }
502 else
499 else
503 {
500 {
504 auto context = new QObject { this };
501 auto context = new QObject { this };
505 connect(
502 connect(
506 variable.get(), &Variable2::updated, context, [this, variable, context, range](QUuid) {
503 variable.get(), &Variable2::updated, context, [this, variable, context, range](QUuid) {
507 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
504 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
508 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
505 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
509 delete context;
506 delete context;
510 });
507 });
511 }
508 }
512 //@TODO this is bad! when variable is moved to another graph it still fires
509 //@TODO this is bad! when variable is moved to another graph it still fires
513 // even if this has been deleted
510 // even if this has been deleted
514 connect(variable.get(), &Variable2::updated, this, &VisualizationGraphWidget::variableUpdated);
511 connect(variable.get(), &Variable2::updated, this, &VisualizationGraphWidget::variableUpdated);
515 this->onUpdateVarDisplaying(variable, range); // My bullshit
512 this->onUpdateVarDisplaying(variable, range); // My bullshit
516 emit variableAdded(variable);
513 emit variableAdded(variable);
517 }
514 }
518
515
519 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable2> variable) noexcept
516 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable2> variable) noexcept
520 {
517 {
521 // Each component associated to the variable :
518 // Each component associated to the variable :
522 // - is removed from qcpplot (which deletes it)
519 // - is removed from qcpplot (which deletes it)
523 // - is no longer referenced in the map
520 // - is no longer referenced in the map
524 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
521 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
525 if (variableIt != impl->m_VariableToPlotMultiMap.cend())
522 if (variableIt != impl->m_VariableToPlotMultiMap.cend())
526 {
523 {
527 emit variableAboutToBeRemoved(variable);
524 emit variableAboutToBeRemoved(variable);
528
525
529 auto& plottablesMap = variableIt->second;
526 auto& plottablesMap = variableIt->second;
530
527
531 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
528 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
532 plottableIt != plottableEnd;)
529 plottableIt != plottableEnd;)
533 {
530 {
534 impl->m_plot->removePlottable(plottableIt->second);
531 impl->m_plot->removePlottable(plottableIt->second);
535 plottableIt = plottablesMap.erase(plottableIt);
532 plottableIt = plottablesMap.erase(plottableIt);
536 }
533 }
537
534
538 impl->m_VariableToPlotMultiMap.erase(variableIt);
535 impl->m_VariableToPlotMultiMap.erase(variableIt);
539 }
536 }
540
537
541 // Updates graph
538 // Updates graph
542 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
539 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
543 }
540 }
544
541
545 std::vector<std::shared_ptr<Variable2>> VisualizationGraphWidget::variables() const
542 std::vector<std::shared_ptr<Variable2>> VisualizationGraphWidget::variables() const
546 {
543 {
547 auto variables = std::vector<std::shared_ptr<Variable2>> {};
544 auto variables = std::vector<std::shared_ptr<Variable2>> {};
548 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
545 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
549 it != std::cend(impl->m_VariableToPlotMultiMap); ++it)
546 it != std::cend(impl->m_VariableToPlotMultiMap); ++it)
550 {
547 {
551 variables.push_back(it->first);
548 variables.push_back(it->first);
552 }
549 }
553
550
554 return variables;
551 return variables;
555 }
552 }
556
553
557 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable2> variable)
554 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable2> variable)
558 {
555 {
559 if (!variable)
556 if (!variable)
560 {
557 {
561 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
558 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
562 return;
559 return;
563 }
560 }
564
561
565 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
562 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
566 }
563 }
567
564
568 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
565 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
569 {
566 {
570 auto graphRange = impl->m_plot->xAxis->range();
567 auto graphRange = impl->m_plot->xAxis->range();
571 return DateTimeRange { graphRange.lower, graphRange.upper };
568 return DateTimeRange { graphRange.lower, graphRange.upper };
572 }
569 }
573
570
574 void VisualizationGraphWidget::setGraphRange(
571 void VisualizationGraphWidget::setGraphRange(
575 const DateTimeRange& range, bool updateVar, bool forward)
572 const DateTimeRange& range, bool updateVar, bool forward)
576 {
573 {
577 impl->setRange(range, updateVar);
574 impl->setRange(range, updateVar);
578 if (forward)
575 if (forward)
579 {
576 {
580 emit this->setrange_sig(this->graphRange(), true, false);
577 emit this->setrange_sig(this->graphRange(), true, false);
581 }
578 }
582 }
579 }
583
580
584 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
581 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
585 {
582 {
586 impl->m_VariableAutoRangeOnInit = value;
583 impl->m_VariableAutoRangeOnInit = value;
587 }
584 }
588
585
589 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
586 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
590 {
587 {
591 QVector<DateTimeRange> ranges;
588 QVector<DateTimeRange> ranges;
592 for (auto zone : impl->m_SelectionZones)
589 for (auto zone : impl->m_SelectionZones)
593 {
590 {
594 ranges << zone->range();
591 ranges << zone->range();
595 }
592 }
596
593
597 return ranges;
594 return ranges;
598 }
595 }
599
596
600 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange>& ranges)
597 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange>& ranges)
601 {
598 {
602 for (const auto& range : ranges)
599 for (const auto& range : ranges)
603 {
600 {
604 // note: ownership is transfered to QCustomPlot
601 // note: ownership is transfered to QCustomPlot
605 auto zone = new VisualizationSelectionZoneItem(&plot());
602 auto zone = new VisualizationSelectionZoneItem(&plot());
606 zone->setRange(range.m_TStart, range.m_TEnd);
603 zone->setRange(range.m_TStart, range.m_TEnd);
607 impl->addSelectionZone(zone);
604 impl->addSelectionZone(zone);
608 }
605 }
609
606
610 plot().replot(QCustomPlot::rpQueuedReplot);
607 plot().replot(QCustomPlot::rpQueuedReplot);
611 }
608 }
612
609
613 VisualizationSelectionZoneItem* VisualizationGraphWidget::addSelectionZone(
610 VisualizationSelectionZoneItem* VisualizationGraphWidget::addSelectionZone(
614 const QString& name, const DateTimeRange& range)
611 const QString& name, const DateTimeRange& range)
615 {
612 {
616 // note: ownership is transfered to QCustomPlot
613 // note: ownership is transfered to QCustomPlot
617 auto zone = new VisualizationSelectionZoneItem(&plot());
614 auto zone = new VisualizationSelectionZoneItem(&plot());
618 zone->setName(name);
615 zone->setName(name);
619 zone->setRange(range.m_TStart, range.m_TEnd);
616 zone->setRange(range.m_TStart, range.m_TEnd);
620 impl->addSelectionZone(zone);
617 impl->addSelectionZone(zone);
621
618
622 plot().replot(QCustomPlot::rpQueuedReplot);
619 plot().replot(QCustomPlot::rpQueuedReplot);
623
620
624 return zone;
621 return zone;
625 }
622 }
626
623
627 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem* selectionZone)
624 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem* selectionZone)
628 {
625 {
629 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
626 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
630
627
631 if (impl->m_HoveredZone == selectionZone)
628 if (impl->m_HoveredZone == selectionZone)
632 {
629 {
633 impl->m_HoveredZone = nullptr;
630 impl->m_HoveredZone = nullptr;
634 setCursor(Qt::ArrowCursor);
631 setCursor(Qt::ArrowCursor);
635 }
632 }
636
633
637 impl->m_SelectionZones.removeAll(selectionZone);
634 impl->m_SelectionZones.removeAll(selectionZone);
638 plot().removeItem(selectionZone);
635 plot().removeItem(selectionZone);
639 plot().replot(QCustomPlot::rpQueuedReplot);
636 plot().replot(QCustomPlot::rpQueuedReplot);
640 }
637 }
641
638
642 void VisualizationGraphWidget::undoZoom()
639 void VisualizationGraphWidget::undoZoom()
643 {
640 {
644 auto zoom = impl->m_ZoomStack.pop();
641 auto zoom = impl->m_ZoomStack.pop();
645 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
642 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
646 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
643 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
647
644
648 axisX->setRange(zoom.first);
645 axisX->setRange(zoom.first);
649 axisY->setRange(zoom.second);
646 axisY->setRange(zoom.second);
650
647
651 plot().replot(QCustomPlot::rpQueuedReplot);
648 plot().replot(QCustomPlot::rpQueuedReplot);
652 }
649 }
653
650
654 void VisualizationGraphWidget::zoom(
651 void VisualizationGraphWidget::zoom(
655 double factor, int center, Qt::Orientation orientation, bool forward)
652 double factor, int center, Qt::Orientation orientation, bool forward)
656 {
653 {
657 impl->zoom(factor, center, orientation);
654 impl->zoom(factor, center, orientation);
658 if (forward && orientation == Qt::Horizontal)
655 if (forward && orientation == Qt::Horizontal)
659 emit this->setrange_sig(this->graphRange(), true, false);
656 emit this->setrange_sig(this->graphRange(), true, false);
660 }
657 }
661
658
662 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
659 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
663 {
660 {
664 impl->move(factor, orientation);
661 impl->move(factor, orientation);
665 if (forward)
662 if (forward)
666 emit this->setrange_sig(this->graphRange(), true, false);
663 emit this->setrange_sig(this->graphRange(), true, false);
667 }
664 }
668
665
669 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
666 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
670 {
667 {
671 impl->move(dx, dy);
668 impl->move(dx, dy);
672 if (forward)
669 if (forward)
673 emit this->setrange_sig(this->graphRange(), true, false);
670 emit this->setrange_sig(this->graphRange(), true, false);
674 }
671 }
675
672
676 void VisualizationGraphWidget::transform(
673 void VisualizationGraphWidget::transform(
677 const DateTimeRangeTransformation& tranformation, bool forward)
674 const DateTimeRangeTransformation& tranformation, bool forward)
678 {
675 {
679 impl->transform(tranformation);
676 impl->transform(tranformation);
680 if (forward)
677 if (forward)
681 emit this->setrange_sig(this->graphRange(), true, false);
678 emit this->setrange_sig(this->graphRange(), true, false);
682 }
679 }
683
680
684 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor* visitor)
681 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor* visitor)
685 {
682 {
686 if (visitor)
683 if (visitor)
687 {
684 {
688 visitor->visit(this);
685 visitor->visit(this);
689 }
686 }
690 else
687 else
691 {
688 {
692 qCCritical(LOG_VisualizationGraphWidget())
689 qCCritical(LOG_VisualizationGraphWidget())
693 << tr("Can't visit widget : the visitor is null");
690 << tr("Can't visit widget : the visitor is null");
694 }
691 }
695 }
692 }
696
693
697 bool VisualizationGraphWidget::canDrop(Variable2& variable) const
694 bool VisualizationGraphWidget::canDrop(Variable2& variable) const
698 {
695 {
699 auto isSpectrogram
696 auto isSpectrogram
700 = [](auto& variable) { return variable.type() == DataSeriesType::SPECTROGRAM; };
697 = [](auto& variable) { return variable.type() == DataSeriesType::SPECTROGRAM; };
701
698
702 // - A spectrogram series can't be dropped on graph with existing plottables
699 // - A spectrogram series can't be dropped on graph with existing plottables
703 // - No data series can be dropped on graph with existing spectrogram series
700 // - No data series can be dropped on graph with existing spectrogram series
704 return isSpectrogram(variable)
701 return isSpectrogram(variable)
705 ? impl->m_VariableToPlotMultiMap.empty()
702 ? impl->m_VariableToPlotMultiMap.empty()
706 : std::none_of(impl->m_VariableToPlotMultiMap.cbegin(),
703 : std::none_of(impl->m_VariableToPlotMultiMap.cbegin(),
707 impl->m_VariableToPlotMultiMap.cend(),
704 impl->m_VariableToPlotMultiMap.cend(),
708 [isSpectrogram](const auto& entry) { return isSpectrogram(*entry.first); });
705 [isSpectrogram](const auto& entry) { return isSpectrogram(*entry.first); });
709 }
706 }
710
707
711 bool VisualizationGraphWidget::contains(Variable2& variable) const
708 bool VisualizationGraphWidget::contains(Variable2& variable) const
712 {
709 {
713 // Finds the variable among the keys of the map
710 // Finds the variable among the keys of the map
714 auto variablePtr = &variable;
711 auto variablePtr = &variable;
715 auto findVariable
712 auto findVariable
716 = [variablePtr](const auto& entry) { return variablePtr == entry.first.get(); };
713 = [variablePtr](const auto& entry) { return variablePtr == entry.first.get(); };
717
714
718 auto end = impl->m_VariableToPlotMultiMap.cend();
715 auto end = impl->m_VariableToPlotMultiMap.cend();
719 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
716 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
720 return it != end;
717 return it != end;
721 }
718 }
722
719
723 QString VisualizationGraphWidget::name() const
720 QString VisualizationGraphWidget::name() const
724 {
721 {
725 return impl->m_Name;
722 return impl->m_Name;
726 }
723 }
727
724
728 QMimeData* VisualizationGraphWidget::mimeData(const QPoint& position) const
725 QMimeData* VisualizationGraphWidget::mimeData(const QPoint& position) const
729 {
726 {
730 auto mimeData = new QMimeData;
727 auto mimeData = new QMimeData;
731
728
732 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
729 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
733 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
730 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
734 && selectionZoneItemUnderCursor)
731 && selectionZoneItemUnderCursor)
735 {
732 {
736 mimeData->setData(MIME_TYPE_TIME_RANGE,
733 mimeData->setData(MIME_TYPE_TIME_RANGE,
737 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
734 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
738 mimeData->setData(MIME_TYPE_SELECTION_ZONE,
735 mimeData->setData(MIME_TYPE_SELECTION_ZONE,
739 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
736 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
740 }
737 }
741 else
738 else
742 {
739 {
743 mimeData->setData(MIME_TYPE_GRAPH, QByteArray {});
740 mimeData->setData(MIME_TYPE_GRAPH, QByteArray {});
744
741
745 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
742 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
746 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
743 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
747 }
744 }
748
745
749 return mimeData;
746 return mimeData;
750 }
747 }
751
748
752 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint& dragPosition)
749 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint& dragPosition)
753 {
750 {
754 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
751 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
755 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
752 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
756 && selectionZoneItemUnderCursor)
753 && selectionZoneItemUnderCursor)
757 {
754 {
758
755
759 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
756 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
760 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
757 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
761
758
762 auto zoneSize = QSizeF { qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
759 auto zoneSize = QSizeF { qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
763 qAbs(zoneBottomRight.y() - zoneTopLeft.y()) }
760 qAbs(zoneBottomRight.y() - zoneTopLeft.y()) }
764 .toSize();
761 .toSize();
765
762
766 auto pixmap = QPixmap(zoneSize);
763 auto pixmap = QPixmap(zoneSize);
767 render(&pixmap, QPoint(), QRegion { QRect { zoneTopLeft.toPoint(), zoneSize } });
764 render(&pixmap, QPoint(), QRegion { QRect { zoneTopLeft.toPoint(), zoneSize } });
768
765
769 return pixmap;
766 return pixmap;
770 }
767 }
771
768
772 return QPixmap();
769 return QPixmap();
773 }
770 }
774
771
775 bool VisualizationGraphWidget::isDragAllowed() const
772 bool VisualizationGraphWidget::isDragAllowed() const
776 {
773 {
777 return true;
774 return true;
778 }
775 }
779
776
780 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
777 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
781 {
778 {
782 if (highlighted)
779 if (highlighted)
783 {
780 {
784 plot().setBackground(QBrush(QColor("#BBD5EE")));
781 plot().setBackground(QBrush(QColor("#BBD5EE")));
785 }
782 }
786 else
783 else
787 {
784 {
788 plot().setBackground(QBrush(Qt::white));
785 plot().setBackground(QBrush(Qt::white));
789 }
786 }
790
787
791 plot().update();
788 plot().update();
792 }
789 }
793
790
794 void VisualizationGraphWidget::addVerticalCursor(double time)
791 void VisualizationGraphWidget::addVerticalCursor(double time)
795 {
792 {
796 impl->m_VerticalCursor->setPosition(time);
793 impl->m_VerticalCursor->setPosition(time);
797 impl->m_VerticalCursor->setVisible(true);
794 impl->m_VerticalCursor->setVisible(true);
798
795
799 auto text
796 auto text
800 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
797 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
801 impl->m_VerticalCursor->setLabelText(text);
798 impl->m_VerticalCursor->setLabelText(text);
802 }
799 }
803
800
804 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
801 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
805 {
802 {
806 impl->m_VerticalCursor->setAbsolutePosition(position);
803 impl->m_VerticalCursor->setAbsolutePosition(position);
807 impl->m_VerticalCursor->setVisible(true);
804 impl->m_VerticalCursor->setVisible(true);
808
805
809 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
806 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
810 auto text
807 auto text
811 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
808 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
812 impl->m_VerticalCursor->setLabelText(text);
809 impl->m_VerticalCursor->setLabelText(text);
813 }
810 }
814
811
815 void VisualizationGraphWidget::removeVerticalCursor()
812 void VisualizationGraphWidget::removeVerticalCursor()
816 {
813 {
817 impl->m_VerticalCursor->setVisible(false);
814 impl->m_VerticalCursor->setVisible(false);
818 plot().replot(QCustomPlot::rpQueuedReplot);
815 plot().replot(QCustomPlot::rpQueuedReplot);
819 }
816 }
820
817
821 void VisualizationGraphWidget::addHorizontalCursor(double value)
818 void VisualizationGraphWidget::addHorizontalCursor(double value)
822 {
819 {
823 impl->m_HorizontalCursor->setPosition(value);
820 impl->m_HorizontalCursor->setPosition(value);
824 impl->m_HorizontalCursor->setVisible(true);
821 impl->m_HorizontalCursor->setVisible(true);
825 impl->m_HorizontalCursor->setLabelText(QString::number(value));
822 impl->m_HorizontalCursor->setLabelText(QString::number(value));
826 }
823 }
827
824
828 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
825 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
829 {
826 {
830 impl->m_HorizontalCursor->setAbsolutePosition(position);
827 impl->m_HorizontalCursor->setAbsolutePosition(position);
831 impl->m_HorizontalCursor->setVisible(true);
828 impl->m_HorizontalCursor->setVisible(true);
832
829
833 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
830 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
834 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
831 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
835 }
832 }
836
833
837 void VisualizationGraphWidget::removeHorizontalCursor()
834 void VisualizationGraphWidget::removeHorizontalCursor()
838 {
835 {
839 impl->m_HorizontalCursor->setVisible(false);
836 impl->m_HorizontalCursor->setVisible(false);
840 plot().replot(QCustomPlot::rpQueuedReplot);
837 plot().replot(QCustomPlot::rpQueuedReplot);
841 }
838 }
842
839
843 void VisualizationGraphWidget::closeEvent(QCloseEvent* event)
840 void VisualizationGraphWidget::closeEvent(QCloseEvent* event)
844 {
841 {
845 Q_UNUSED(event);
842 Q_UNUSED(event);
846
843
847 for (auto i : impl->m_SelectionZones)
844 for (auto i : impl->m_SelectionZones)
848 {
845 {
849 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
846 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
850 }
847 }
851
848
852 // Prevents that all variables will be removed from graph when it will be closed
849 // Prevents that all variables will be removed from graph when it will be closed
853 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
850 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
854 {
851 {
855 emit variableAboutToBeRemoved(variableEntry.first);
852 emit variableAboutToBeRemoved(variableEntry.first);
856 }
853 }
857 }
854 }
858
855
859 void VisualizationGraphWidget::enterEvent(QEvent* event)
856 void VisualizationGraphWidget::enterEvent(QEvent* event)
860 {
857 {
861 Q_UNUSED(event);
858 Q_UNUSED(event);
862 impl->m_RenderingDelegate->showGraphOverlay(true);
859 impl->m_RenderingDelegate->showGraphOverlay(true);
863 }
860 }
864
861
865 void VisualizationGraphWidget::leaveEvent(QEvent* event)
862 void VisualizationGraphWidget::leaveEvent(QEvent* event)
866 {
863 {
867 Q_UNUSED(event);
864 Q_UNUSED(event);
868 impl->m_RenderingDelegate->showGraphOverlay(false);
865 impl->m_RenderingDelegate->showGraphOverlay(false);
869
866
870 if (auto parentZone = parentZoneWidget())
867 if (auto parentZone = parentZoneWidget())
871 {
868 {
872 parentZone->notifyMouseLeaveGraph(this);
869 parentZone->notifyMouseLeaveGraph(this);
873 }
870 }
874 else
871 else
875 {
872 {
876 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
873 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
877 }
874 }
878
875
879 if (impl->m_HoveredZone)
876 if (impl->m_HoveredZone)
880 {
877 {
881 impl->m_HoveredZone->setHovered(false);
878 impl->m_HoveredZone->setHovered(false);
882 impl->m_HoveredZone = nullptr;
879 impl->m_HoveredZone = nullptr;
883 }
880 }
884 }
881 }
885
882
886 void VisualizationGraphWidget::wheelEvent(QWheelEvent* event)
883 void VisualizationGraphWidget::wheelEvent(QWheelEvent* event)
887 {
884 {
888 double factor;
885 double factor;
889 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
886 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
890 if (event->modifiers() == Qt::ControlModifier)
887 if (event->modifiers() == Qt::ControlModifier)
891 {
888 {
892 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
889 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
893 {
890 {
894 setCursor(Qt::SizeVerCursor);
891 setCursor(Qt::SizeVerCursor);
895 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
892 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
896 zoom(factor, event->pos().y(), Qt::Vertical);
893 zoom(factor, event->pos().y(), Qt::Vertical);
897 }
894 }
898 }
895 }
899 else if (event->modifiers() == Qt::ShiftModifier)
896 else if (event->modifiers() == Qt::ShiftModifier)
900 {
897 {
901 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
898 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
902 {
899 {
903 setCursor(Qt::SizeHorCursor);
900 setCursor(Qt::SizeHorCursor);
904 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
901 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
905 zoom(factor, event->pos().x(), Qt::Horizontal);
902 zoom(factor, event->pos().x(), Qt::Horizontal);
906 }
903 }
907 }
904 }
908 else
905 else
909 {
906 {
910 move(wheelSteps, Qt::Horizontal);
907 move(wheelSteps, Qt::Horizontal);
911 }
908 }
912 event->accept();
909 event->accept();
913 }
910 }
914
911
915
912
916 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent* event)
913 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent* event)
917 {
914 {
918 if (impl->isDrawingZoomRect())
915 if (impl->isDrawingZoomRect())
919 {
916 {
920 impl->updateZoomRect(event->pos());
917 impl->updateZoomRect(event->pos());
921 }
918 }
922 else if (impl->isDrawingZoneRect())
919 else if (impl->isDrawingZoneRect())
923 {
920 {
924 impl->updateZoneRect(event->pos());
921 impl->updateZoneRect(event->pos());
925 }
922 }
926 else if (event->buttons() == Qt::LeftButton)
923 else if (event->buttons() == Qt::LeftButton)
927 {
924 {
928 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
925 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
929 {
926 {
930 auto [dx, dy] = impl->moveGraph(event->pos());
927 auto [dx, dy] = impl->moveGraph(event->pos());
931 emit this->setrange_sig(this->graphRange(), true, false);
928 emit this->setrange_sig(this->graphRange(), true, false);
932 }
929 }
933 else if (sqpApp->plotsInteractionMode()
930 else if (sqpApp->plotsInteractionMode()
934 == SqpApplication::PlotsInteractionMode::SelectionZones)
931 == SqpApplication::PlotsInteractionMode::SelectionZones)
935 {
932 {
936 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
933 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
937 if (auto item = impl->m_plot->itemAt(posInPlot))
934 if (auto item = impl->m_plot->itemAt(posInPlot))
938 {
935 {
939 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
936 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
940 {
937 {
941 QMouseEvent e { QEvent::MouseMove, posInPlot, event->button(), event->buttons(),
938 QMouseEvent e { QEvent::MouseMove, posInPlot, event->button(), event->buttons(),
942 event->modifiers() };
939 event->modifiers() };
943 sqpApp->sendEvent(this->impl->m_plot, &e);
940 sqpApp->sendEvent(this->impl->m_plot, &e);
944 this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh);
941 this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh);
945 }
942 }
946 }
943 }
947 }
944 }
948 }
945 }
949 else
946 else
950 {
947 {
951 impl->m_RenderingDelegate->updateTooltip(event);
948 impl->m_RenderingDelegate->updateTooltip(event);
952 }
949 }
953 // event->accept();
950 // event->accept();
954 QWidget::mouseMoveEvent(event);
951 QWidget::mouseMoveEvent(event);
955 }
952 }
956
953
957 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent* event)
954 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent* event)
958 {
955 {
959 if (impl->isDrawingZoomRect())
956 if (impl->isDrawingZoomRect())
960 {
957 {
961 auto oldRange = this->graphRange();
958 auto oldRange = this->graphRange();
962 impl->applyZoomRect();
959 impl->applyZoomRect();
963 auto newRange = this->graphRange();
960 auto newRange = this->graphRange();
964 if (auto tf = DateTimeRangeHelper::computeTransformation(oldRange, newRange))
961 if (auto tf = DateTimeRangeHelper::computeTransformation(oldRange, newRange))
965 emit this->transform_sig(tf.value(), false);
962 emit this->transform_sig(tf.value(), false);
966 }
963 }
967 else if (impl->isDrawingZoneRect())
964 else if (impl->isDrawingZoneRect())
968 {
965 {
969 impl->endDrawingZone();
966 impl->endDrawingZone();
970 }
967 }
971 else
968 else
972 {
969 {
973 setCursor(Qt::ArrowCursor);
970 setCursor(Qt::ArrowCursor);
974 }
971 }
975 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
972 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
976 if (auto item = impl->m_plot->itemAt(posInPlot))
973 if (auto item = impl->m_plot->itemAt(posInPlot))
977 {
974 {
978 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
975 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
979 {
976 {
980 QMouseEvent e { QEvent::MouseButtonRelease, posInPlot, event->button(),
977 QMouseEvent e { QEvent::MouseButtonRelease, posInPlot, event->button(),
981 event->buttons(), event->modifiers() };
978 event->buttons(), event->modifiers() };
982 sqpApp->sendEvent(this->impl->m_plot, &e);
979 sqpApp->sendEvent(this->impl->m_plot, &e);
983 }
980 }
984 }
981 }
985 event->accept();
982 event->accept();
986 }
983 }
987
984
988 void VisualizationGraphWidget::mousePressEvent(QMouseEvent* event)
985 void VisualizationGraphWidget::mousePressEvent(QMouseEvent* event)
989 {
986 {
990 if (event->button() == Qt::RightButton)
987 if (event->button() == Qt::RightButton)
991 {
988 {
992 onGraphMenuRequested(event->pos());
989 onGraphMenuRequested(event->pos());
993 }
990 }
994 else
991 else
995 {
992 {
996 auto selectedZone = impl->selectionZoneAt(event->pos());
993 auto selectedZone = impl->selectionZoneAt(event->pos());
997 switch (sqpApp->plotsInteractionMode())
994 switch (sqpApp->plotsInteractionMode())
998 {
995 {
999 case SqpApplication::PlotsInteractionMode::DragAndDrop:
996 case SqpApplication::PlotsInteractionMode::DragAndDrop:
1000 break;
997 break;
1001 case SqpApplication::PlotsInteractionMode::SelectionZones:
998 case SqpApplication::PlotsInteractionMode::SelectionZones:
1002 impl->setSelectionZonesEditionEnabled(true);
999 impl->setSelectionZonesEditionEnabled(true);
1003 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
1000 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
1004 {
1001 {
1005 auto alreadySelectedZones
1002 auto alreadySelectedZones
1006 = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1003 = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1007 selectedZone->setAssociatedEditedZones(alreadySelectedZones);
1004 selectedZone->setAssociatedEditedZones(alreadySelectedZones);
1008 if (SciQLop::containers::contains(alreadySelectedZones, selectedZone))
1005 if (SciQLop::containers::contains(alreadySelectedZones, selectedZone))
1009 {
1006 {
1010 alreadySelectedZones.removeOne(selectedZone);
1007 alreadySelectedZones.removeOne(selectedZone);
1011 }
1008 }
1012 else
1009 else
1013 {
1010 {
1014 alreadySelectedZones.append(selectedZone);
1011 alreadySelectedZones.append(selectedZone);
1015 }
1012 }
1016 parentVisualizationWidget()->selectionZoneManager().select(
1013 parentVisualizationWidget()->selectionZoneManager().select(
1017 alreadySelectedZones);
1014 alreadySelectedZones);
1018 }
1015 }
1019 else
1016 else
1020 {
1017 {
1021 if (!selectedZone)
1018 if (!selectedZone)
1022 {
1019 {
1023 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1020 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1024 impl->startDrawingZone(event->pos());
1021 impl->startDrawingZone(event->pos());
1025 }
1022 }
1026 else
1023 else
1027 {
1024 {
1028 parentVisualizationWidget()->selectionZoneManager().select(
1025 parentVisualizationWidget()->selectionZoneManager().select(
1029 { selectedZone });
1026 { selectedZone });
1030 }
1027 }
1031 }
1028 }
1032 {
1029 {
1033 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
1030 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
1034 if (auto item = impl->m_plot->itemAt(posInPlot))
1031 if (auto item = impl->m_plot->itemAt(posInPlot))
1035 {
1032 {
1036 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1033 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1037 {
1034 {
1038 QMouseEvent e { QEvent::MouseButtonPress, posInPlot, event->button(),
1035 QMouseEvent e { QEvent::MouseButtonPress, posInPlot, event->button(),
1039 event->buttons(), event->modifiers() };
1036 event->buttons(), event->modifiers() };
1040 sqpApp->sendEvent(this->impl->m_plot, &e);
1037 sqpApp->sendEvent(this->impl->m_plot, &e);
1041 }
1038 }
1042 }
1039 }
1043 }
1040 }
1044 break;
1041 break;
1045 case SqpApplication::PlotsInteractionMode::ZoomBox:
1042 case SqpApplication::PlotsInteractionMode::ZoomBox:
1046 impl->startDrawingRect(event->pos());
1043 impl->startDrawingRect(event->pos());
1047 break;
1044 break;
1048 default:
1045 default:
1049 if (auto item = impl->m_plot->itemAt(event->pos()))
1046 if (auto item = impl->m_plot->itemAt(event->pos()))
1050 {
1047 {
1051 emit impl->m_plot->itemClick(item, event);
1048 emit impl->m_plot->itemClick(item, event);
1052 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1049 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1053 {
1050 {
1054 setCursor(Qt::ClosedHandCursor);
1051 setCursor(Qt::ClosedHandCursor);
1055 impl->enterPlotDrag(event->pos());
1052 impl->enterPlotDrag(event->pos());
1056 }
1053 }
1057 }
1054 }
1058 else
1055 else
1059 {
1056 {
1060 setCursor(Qt::ClosedHandCursor);
1057 setCursor(Qt::ClosedHandCursor);
1061 impl->enterPlotDrag(event->pos());
1058 impl->enterPlotDrag(event->pos());
1062 }
1059 }
1063 }
1060 }
1064 }
1061 }
1065 // event->accept();
1062 // event->accept();
1066 QWidget::mousePressEvent(event);
1063 QWidget::mousePressEvent(event);
1067 }
1064 }
1068
1065
1069 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent* event)
1066 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent* event)
1070 {
1067 {
1071 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1068 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1072 }
1069 }
1073
1070
1074 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent* event)
1071 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent* event)
1075 {
1072 {
1076 switch (event->key())
1073 switch (event->key())
1077 {
1074 {
1078 case Qt::Key_Control:
1075 case Qt::Key_Control:
1079 event->accept();
1076 event->accept();
1080 break;
1077 break;
1081 case Qt::Key_Shift:
1078 case Qt::Key_Shift:
1082 event->accept();
1079 event->accept();
1083 break;
1080 break;
1084 default:
1081 default:
1085 QWidget::keyReleaseEvent(event);
1082 QWidget::keyReleaseEvent(event);
1086 break;
1083 break;
1087 }
1084 }
1088 setCursor(Qt::ArrowCursor);
1085 setCursor(Qt::ArrowCursor);
1089 // event->accept();
1086 // event->accept();
1090 }
1087 }
1091
1088
1092 void VisualizationGraphWidget::keyPressEvent(QKeyEvent* event)
1089 void VisualizationGraphWidget::keyPressEvent(QKeyEvent* event)
1093 {
1090 {
1094 switch (event->key())
1091 switch (event->key())
1095 {
1092 {
1096 case Qt::Key_Control:
1093 case Qt::Key_Control:
1097 setCursor(Qt::CrossCursor);
1094 setCursor(Qt::CrossCursor);
1098 break;
1095 break;
1099 case Qt::Key_Shift:
1096 case Qt::Key_Shift:
1100 break;
1097 break;
1101 case Qt::Key_M:
1098 case Qt::Key_M:
1102 impl->rescaleY();
1099 impl->rescaleY();
1103 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1100 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1104 break;
1101 break;
1105 case Qt::Key_Left:
1102 case Qt::Key_Left:
1106 if (event->modifiers() != Qt::ControlModifier)
1103 if (event->modifiers() != Qt::ControlModifier)
1107 {
1104 {
1108 move(-0.1, Qt::Horizontal);
1105 move(-0.1, Qt::Horizontal);
1109 }
1106 }
1110 else
1107 else
1111 {
1108 {
1112 zoom(2, this->width() / 2, Qt::Horizontal);
1109 zoom(2, this->width() / 2, Qt::Horizontal);
1113 }
1110 }
1114 break;
1111 break;
1115 case Qt::Key_Right:
1112 case Qt::Key_Right:
1116 if (event->modifiers() != Qt::ControlModifier)
1113 if (event->modifiers() != Qt::ControlModifier)
1117 {
1114 {
1118 move(0.1, Qt::Horizontal);
1115 move(0.1, Qt::Horizontal);
1119 }
1116 }
1120 else
1117 else
1121 {
1118 {
1122 zoom(0.5, this->width() / 2, Qt::Horizontal);
1119 zoom(0.5, this->width() / 2, Qt::Horizontal);
1123 }
1120 }
1124 break;
1121 break;
1125 case Qt::Key_Up:
1122 case Qt::Key_Up:
1126 if (event->modifiers() != Qt::ControlModifier)
1123 if (event->modifiers() != Qt::ControlModifier)
1127 {
1124 {
1128 move(0.1, Qt::Vertical);
1125 move(0.1, Qt::Vertical);
1129 }
1126 }
1130 else
1127 else
1131 {
1128 {
1132 zoom(0.5, this->height() / 2, Qt::Vertical);
1129 zoom(0.5, this->height() / 2, Qt::Vertical);
1133 }
1130 }
1134 break;
1131 break;
1135 case Qt::Key_Down:
1132 case Qt::Key_Down:
1136 if (event->modifiers() != Qt::ControlModifier)
1133 if (event->modifiers() != Qt::ControlModifier)
1137 {
1134 {
1138 move(-0.1, Qt::Vertical);
1135 move(-0.1, Qt::Vertical);
1139 }
1136 }
1140 else
1137 else
1141 {
1138 {
1142 zoom(2, this->height() / 2, Qt::Vertical);
1139 zoom(2, this->height() / 2, Qt::Vertical);
1143 }
1140 }
1144 break;
1141 break;
1145 default:
1142 default:
1146 QWidget::keyPressEvent(event);
1143 QWidget::keyPressEvent(event);
1147 break;
1144 break;
1148 }
1145 }
1149 }
1146 }
1150
1147
1151 QCustomPlot& VisualizationGraphWidget::plot() const noexcept
1148 QCustomPlot& VisualizationGraphWidget::plot() const noexcept
1152 {
1149 {
1153 return *impl->m_plot;
1150 return *impl->m_plot;
1154 }
1151 }
1155
1152
1156 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint& pos) noexcept
1153 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint& pos) noexcept
1157 {
1154 {
1158 QMenu graphMenu {};
1155 QMenu graphMenu {};
1159
1156
1160 // Iterates on variables (unique keys)
1157 // Iterates on variables (unique keys)
1161 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1158 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1162 end = impl->m_VariableToPlotMultiMap.cend();
1159 end = impl->m_VariableToPlotMultiMap.cend();
1163 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first))
1160 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first))
1164 {
1161 {
1165 // 'Remove variable' action
1162 // 'Remove variable' action
1166 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1163 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1167 [this, var = it->first]() { removeVariable(var); });
1164 [this, var = it->first]() { removeVariable(var); });
1168 }
1165 }
1169
1166
1170 if (!impl->m_ZoomStack.isEmpty())
1167 if (!impl->m_ZoomStack.isEmpty())
1171 {
1168 {
1172 if (!graphMenu.isEmpty())
1169 if (!graphMenu.isEmpty())
1173 {
1170 {
1174 graphMenu.addSeparator();
1171 graphMenu.addSeparator();
1175 }
1172 }
1176
1173
1177 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1174 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1178 }
1175 }
1179
1176
1180 // Selection Zone Actions
1177 // Selection Zone Actions
1181 auto selectionZoneItem = impl->selectionZoneAt(pos);
1178 auto selectionZoneItem = impl->selectionZoneAt(pos);
1182 if (selectionZoneItem)
1179 if (selectionZoneItem)
1183 {
1180 {
1184 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1181 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1185 selectedItems.removeAll(selectionZoneItem);
1182 selectedItems.removeAll(selectionZoneItem);
1186 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1183 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1187
1184
1188 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1185 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1189 if (!zoneActions.isEmpty() && !graphMenu.isEmpty())
1186 if (!zoneActions.isEmpty() && !graphMenu.isEmpty())
1190 {
1187 {
1191 graphMenu.addSeparator();
1188 graphMenu.addSeparator();
1192 }
1189 }
1193
1190
1194 QHash<QString, QMenu*> subMenus;
1191 QHash<QString, QMenu*> subMenus;
1195 QHash<QString, bool> subMenusEnabled;
1192 QHash<QString, bool> subMenusEnabled;
1196 QHash<QString, FilteringAction*> filteredMenu;
1193 QHash<QString, FilteringAction*> filteredMenu;
1197
1194
1198 for (auto zoneAction : zoneActions)
1195 for (auto zoneAction : zoneActions)
1199 {
1196 {
1200
1197
1201 auto isEnabled = zoneAction->isEnabled(selectedItems);
1198 auto isEnabled = zoneAction->isEnabled(selectedItems);
1202
1199
1203 auto menu = &graphMenu;
1200 auto menu = &graphMenu;
1204 QString menuPath;
1201 QString menuPath;
1205 for (auto subMenuName : zoneAction->subMenuList())
1202 for (auto subMenuName : zoneAction->subMenuList())
1206 {
1203 {
1207 menuPath += '/';
1204 menuPath += '/';
1208 menuPath += subMenuName;
1205 menuPath += subMenuName;
1209
1206
1210 if (!subMenus.contains(menuPath))
1207 if (!subMenus.contains(menuPath))
1211 {
1208 {
1212 menu = menu->addMenu(subMenuName);
1209 menu = menu->addMenu(subMenuName);
1213 subMenus[menuPath] = menu;
1210 subMenus[menuPath] = menu;
1214 subMenusEnabled[menuPath] = isEnabled;
1211 subMenusEnabled[menuPath] = isEnabled;
1215 }
1212 }
1216 else
1213 else
1217 {
1214 {
1218 menu = subMenus.value(menuPath);
1215 menu = subMenus.value(menuPath);
1219 if (isEnabled)
1216 if (isEnabled)
1220 {
1217 {
1221 // The sub menu is enabled if at least one of its actions is enabled
1218 // The sub menu is enabled if at least one of its actions is enabled
1222 subMenusEnabled[menuPath] = true;
1219 subMenusEnabled[menuPath] = true;
1223 }
1220 }
1224 }
1221 }
1225 }
1222 }
1226
1223
1227 FilteringAction* filterAction = nullptr;
1224 FilteringAction* filterAction = nullptr;
1228 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList()))
1225 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList()))
1229 {
1226 {
1230 filterAction = filteredMenu.value(menuPath);
1227 filterAction = filteredMenu.value(menuPath);
1231 if (!filterAction)
1228 if (!filterAction)
1232 {
1229 {
1233 filterAction = new FilteringAction { this };
1230 filterAction = new FilteringAction { this };
1234 filteredMenu[menuPath] = filterAction;
1231 filteredMenu[menuPath] = filterAction;
1235 menu->addAction(filterAction);
1232 menu->addAction(filterAction);
1236 }
1233 }
1237 }
1234 }
1238
1235
1239 auto action = menu->addAction(zoneAction->name());
1236 auto action = menu->addAction(zoneAction->name());
1240 action->setEnabled(isEnabled);
1237 action->setEnabled(isEnabled);
1241 action->setShortcut(zoneAction->displayedShortcut());
1238 action->setShortcut(zoneAction->displayedShortcut());
1242 QObject::connect(action, &QAction::triggered,
1239 QObject::connect(action, &QAction::triggered,
1243 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1240 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1244
1241
1245 if (filterAction && zoneAction->isFilteringAllowed())
1242 if (filterAction && zoneAction->isFilteringAllowed())
1246 {
1243 {
1247 filterAction->addActionToFilter(action);
1244 filterAction->addActionToFilter(action);
1248 }
1245 }
1249 }
1246 }
1250
1247
1251 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it)
1248 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it)
1252 {
1249 {
1253 it.value()->setEnabled(subMenusEnabled[it.key()]);
1250 it.value()->setEnabled(subMenusEnabled[it.key()]);
1254 }
1251 }
1255 }
1252 }
1256
1253
1257 if (!graphMenu.isEmpty())
1254 if (!graphMenu.isEmpty())
1258 {
1255 {
1259 graphMenu.exec(QCursor::pos());
1256 graphMenu.exec(QCursor::pos());
1260 }
1257 }
1261 }
1258 }
1262
1259
1263 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent* event) noexcept
1260 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent* event) noexcept
1264 {
1261 {
1265 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1262 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1266 }
1263 }
1267
1264
1268 void VisualizationGraphWidget::onMouseMove(QMouseEvent* event) noexcept
1265 void VisualizationGraphWidget::onMouseMove(QMouseEvent* event) noexcept
1269 {
1266 {
1270 // Handles plot rendering when mouse is moving
1267 // Handles plot rendering when mouse is moving
1271 impl->m_RenderingDelegate->updateTooltip(event);
1268 impl->m_RenderingDelegate->updateTooltip(event);
1272
1269
1273 auto axisPos = impl->posToAxisPos(event->pos());
1270 auto axisPos = impl->posToAxisPos(event->pos());
1274
1271
1275 // Zoom box and zone drawing
1272 // Zoom box and zone drawing
1276 if (impl->m_DrawingZoomRect)
1273 if (impl->m_DrawingZoomRect)
1277 {
1274 {
1278 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1275 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1279 }
1276 }
1280 else if (impl->m_DrawingZone)
1277 else if (impl->m_DrawingZone)
1281 {
1278 {
1282 impl->m_DrawingZone->setEnd(axisPos.x());
1279 impl->m_DrawingZone->setEnd(axisPos.x());
1283 }
1280 }
1284
1281
1285 // Cursor
1282 // Cursor
1286 if (auto parentZone = parentZoneWidget())
1283 if (auto parentZone = parentZoneWidget())
1287 {
1284 {
1288 if (impl->pointIsInAxisRect(axisPos, plot()))
1285 if (impl->pointIsInAxisRect(axisPos, plot()))
1289 {
1286 {
1290 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1287 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1291 }
1288 }
1292 else
1289 else
1293 {
1290 {
1294 parentZone->notifyMouseLeaveGraph(this);
1291 parentZone->notifyMouseLeaveGraph(this);
1295 }
1292 }
1296 }
1293 }
1297
1294
1298 // Search for the selection zone under the mouse
1295 // Search for the selection zone under the mouse
1299 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1296 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1300 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1297 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1301 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
1298 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
1302 {
1299 {
1303
1300
1304 // Sets the appropriate cursor shape
1301 // Sets the appropriate cursor shape
1305 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1302 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1306 setCursor(cursorShape);
1303 setCursor(cursorShape);
1307
1304
1308 // Manages the hovered zone
1305 // Manages the hovered zone
1309 if (selectionZoneItemUnderCursor != impl->m_HoveredZone)
1306 if (selectionZoneItemUnderCursor != impl->m_HoveredZone)
1310 {
1307 {
1311 if (impl->m_HoveredZone)
1308 if (impl->m_HoveredZone)
1312 {
1309 {
1313 impl->m_HoveredZone->setHovered(false);
1310 impl->m_HoveredZone->setHovered(false);
1314 }
1311 }
1315 selectionZoneItemUnderCursor->setHovered(true);
1312 selectionZoneItemUnderCursor->setHovered(true);
1316 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1313 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1317 plot().replot(QCustomPlot::rpQueuedReplot);
1314 plot().replot(QCustomPlot::rpQueuedReplot);
1318 }
1315 }
1319 }
1316 }
1320 else
1317 else
1321 {
1318 {
1322 // There is no zone under the mouse or the interaction mode is not "selection zones"
1319 // There is no zone under the mouse or the interaction mode is not "selection zones"
1323 if (impl->m_HoveredZone)
1320 if (impl->m_HoveredZone)
1324 {
1321 {
1325 impl->m_HoveredZone->setHovered(false);
1322 impl->m_HoveredZone->setHovered(false);
1326 impl->m_HoveredZone = nullptr;
1323 impl->m_HoveredZone = nullptr;
1327 }
1324 }
1328
1325
1329 setCursor(Qt::ArrowCursor);
1326 setCursor(Qt::ArrowCursor);
1330 }
1327 }
1331
1328
1332 impl->m_HasMovedMouse = true;
1329 impl->m_HasMovedMouse = true;
1333 VisualizationDragWidget::mouseMoveEvent(event);
1330 VisualizationDragWidget::mouseMoveEvent(event);
1334 }
1331 }
1335
1332
1336 void VisualizationGraphWidget::onMouseWheel(QWheelEvent* event) noexcept
1333 void VisualizationGraphWidget::onMouseWheel(QWheelEvent* event) noexcept
1337 {
1334 {
1338 // Processes event only if the wheel occurs on axis rect
1335 // Processes event only if the wheel occurs on axis rect
1339 if (!dynamic_cast<QCPAxisRect*>(impl->m_plot->layoutElementAt(event->posF())))
1336 if (!dynamic_cast<QCPAxisRect*>(impl->m_plot->layoutElementAt(event->posF())))
1340 {
1337 {
1341 return;
1338 return;
1342 }
1339 }
1343
1340
1344 auto value = event->angleDelta().x() + event->angleDelta().y();
1341 auto value = event->angleDelta().x() + event->angleDelta().y();
1345 if (value != 0)
1342 if (value != 0)
1346 {
1343 {
1347
1344
1348 auto direction = value > 0 ? 1.0 : -1.0;
1345 auto direction = value > 0 ? 1.0 : -1.0;
1349 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1346 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1350 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1347 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1351 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1348 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1352
1349
1353 auto zoomOrientations = QFlags<Qt::Orientation> {};
1350 auto zoomOrientations = QFlags<Qt::Orientation> {};
1354 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1351 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1355 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1352 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1356
1353
1357 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1354 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1358
1355
1359 if (!isZoomX && !isZoomY)
1356 if (!isZoomX && !isZoomY)
1360 {
1357 {
1361 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1358 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1362 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1359 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1363
1360
1364 axis->setRange(axis->range() + diff);
1361 axis->setRange(axis->range() + diff);
1365
1362
1366 if (plot().noAntialiasingOnDrag())
1363 if (plot().noAntialiasingOnDrag())
1367 {
1364 {
1368 plot().setNotAntialiasedElements(QCP::aeAll);
1365 plot().setNotAntialiasedElements(QCP::aeAll);
1369 }
1366 }
1370
1367
1371 // plot().replot(QCustomPlot::rpQueuedReplot);
1368 // plot().replot(QCustomPlot::rpQueuedReplot);
1372 }
1369 }
1373 }
1370 }
1374 }
1371 }
1375
1372
1376 void VisualizationGraphWidget::onMousePress(QMouseEvent* event) noexcept
1373 void VisualizationGraphWidget::onMousePress(QMouseEvent* event) noexcept
1377 {
1374 {
1378 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1375 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1379 auto isSelectionZoneMode
1376 auto isSelectionZoneMode
1380 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1377 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1381 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1378 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1382
1379
1383 if (!isDragDropClick && isLeftClick)
1380 if (!isDragDropClick && isLeftClick)
1384 {
1381 {
1385 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox)
1382 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox)
1386 {
1383 {
1387 // Starts a zoom box
1384 // Starts a zoom box
1388 impl->startDrawingRect(event->pos());
1385 impl->startDrawingRect(event->pos());
1389 }
1386 }
1390 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr)
1387 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr)
1391 {
1388 {
1392 // Starts a new selection zone
1389 // Starts a new selection zone
1393 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1390 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1394 if (!zoneAtPos)
1391 if (!zoneAtPos)
1395 {
1392 {
1396 impl->startDrawingZone(event->pos());
1393 impl->startDrawingZone(event->pos());
1397 }
1394 }
1398 }
1395 }
1399 }
1396 }
1400
1397
1401
1398
1402 // Allows zone edition only in selection zone mode without drag&drop
1399 // Allows zone edition only in selection zone mode without drag&drop
1403 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1400 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1404
1401
1405 // Selection / Deselection
1402 // Selection / Deselection
1406 if (isSelectionZoneMode)
1403 if (isSelectionZoneMode)
1407 {
1404 {
1408 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1405 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1409 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1406 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1410
1407
1411
1408
1412 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1409 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1413 && !isMultiSelectionClick)
1410 && !isMultiSelectionClick)
1414 {
1411 {
1415 parentVisualizationWidget()->selectionZoneManager().select(
1412 parentVisualizationWidget()->selectionZoneManager().select(
1416 { selectionZoneItemUnderCursor });
1413 { selectionZoneItemUnderCursor });
1417 }
1414 }
1418 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick)
1415 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick)
1419 {
1416 {
1420 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1417 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1421 }
1418 }
1422 else
1419 else
1423 {
1420 {
1424 // No selection change
1421 // No selection change
1425 }
1422 }
1426
1423
1427 if (selectionZoneItemUnderCursor && isLeftClick)
1424 if (selectionZoneItemUnderCursor && isLeftClick)
1428 {
1425 {
1429 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1426 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1430 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1427 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1431 }
1428 }
1432 }
1429 }
1433
1430
1434
1431
1435 impl->m_HasMovedMouse = false;
1432 impl->m_HasMovedMouse = false;
1436 VisualizationDragWidget::mousePressEvent(event);
1433 VisualizationDragWidget::mousePressEvent(event);
1437 }
1434 }
1438
1435
1439 void VisualizationGraphWidget::onMouseRelease(QMouseEvent* event) noexcept
1436 void VisualizationGraphWidget::onMouseRelease(QMouseEvent* event) noexcept
1440 {
1437 {
1441 if (impl->m_DrawingZoomRect)
1438 if (impl->m_DrawingZoomRect)
1442 {
1439 {
1443
1440
1444 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1441 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1445 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1442 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1446
1443
1447 auto newAxisXRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().x(),
1444 auto newAxisXRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().x(),
1448 impl->m_DrawingZoomRect->bottomRight->coords().x() };
1445 impl->m_DrawingZoomRect->bottomRight->coords().x() };
1449
1446
1450 auto newAxisYRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().y(),
1447 auto newAxisYRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().y(),
1451 impl->m_DrawingZoomRect->bottomRight->coords().y() };
1448 impl->m_DrawingZoomRect->bottomRight->coords().y() };
1452
1449
1453 impl->removeDrawingRect();
1450 impl->removeDrawingRect();
1454
1451
1455 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1452 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1456 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
1453 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
1457 {
1454 {
1458 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1455 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1459 axisX->setRange(newAxisXRange);
1456 axisX->setRange(newAxisXRange);
1460 axisY->setRange(newAxisYRange);
1457 axisY->setRange(newAxisYRange);
1461
1458
1462 plot().replot(QCustomPlot::rpQueuedReplot);
1459 plot().replot(QCustomPlot::rpQueuedReplot);
1463 }
1460 }
1464 }
1461 }
1465
1462
1466 impl->endDrawingZone();
1463 impl->endDrawingZone();
1467
1464
1468 // Selection / Deselection
1465 // Selection / Deselection
1469 auto isSelectionZoneMode
1466 auto isSelectionZoneMode
1470 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1467 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1471 if (isSelectionZoneMode)
1468 if (isSelectionZoneMode)
1472 {
1469 {
1473 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1470 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1474 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1471 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1475 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1472 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1476 && !impl->m_HasMovedMouse)
1473 && !impl->m_HasMovedMouse)
1477 {
1474 {
1478
1475
1479 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1476 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1480 if (zonesUnderCursor.count() > 1)
1477 if (zonesUnderCursor.count() > 1)
1481 {
1478 {
1482 // There are multiple zones under the mouse.
1479 // There are multiple zones under the mouse.
1483 // Performs the selection with a selection dialog.
1480 // Performs the selection with a selection dialog.
1484 VisualizationMultiZoneSelectionDialog dialog { this };
1481 VisualizationMultiZoneSelectionDialog dialog { this };
1485 dialog.setZones(zonesUnderCursor);
1482 dialog.setZones(zonesUnderCursor);
1486 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1483 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1487 dialog.activateWindow();
1484 dialog.activateWindow();
1488 dialog.raise();
1485 dialog.raise();
1489 if (dialog.exec() == QDialog::Accepted)
1486 if (dialog.exec() == QDialog::Accepted)
1490 {
1487 {
1491 auto selection = dialog.selectedZones();
1488 auto selection = dialog.selectedZones();
1492
1489
1493 if (!isMultiSelectionClick)
1490 if (!isMultiSelectionClick)
1494 {
1491 {
1495 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1492 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1496 }
1493 }
1497
1494
1498 for (auto it = selection.cbegin(); it != selection.cend(); ++it)
1495 for (auto it = selection.cbegin(); it != selection.cend(); ++it)
1499 {
1496 {
1500 auto zone = it.key();
1497 auto zone = it.key();
1501 auto isSelected = it.value();
1498 auto isSelected = it.value();
1502 parentVisualizationWidget()->selectionZoneManager().setSelected(
1499 parentVisualizationWidget()->selectionZoneManager().setSelected(
1503 zone, isSelected);
1500 zone, isSelected);
1504
1501
1505 if (isSelected)
1502 if (isSelected)
1506 {
1503 {
1507 // Puts the zone on top of the stack so it can be moved or resized
1504 // Puts the zone on top of the stack so it can be moved or resized
1508 impl->moveSelectionZoneOnTop(zone, plot());
1505 impl->moveSelectionZoneOnTop(zone, plot());
1509 }
1506 }
1510 }
1507 }
1511 }
1508 }
1512 }
1509 }
1513 else
1510 else
1514 {
1511 {
1515 if (!isMultiSelectionClick)
1512 if (!isMultiSelectionClick)
1516 {
1513 {
1517 parentVisualizationWidget()->selectionZoneManager().select(
1514 parentVisualizationWidget()->selectionZoneManager().select(
1518 { selectionZoneItemUnderCursor });
1515 { selectionZoneItemUnderCursor });
1519 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1516 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1520 }
1517 }
1521 else
1518 else
1522 {
1519 {
1523 parentVisualizationWidget()->selectionZoneManager().setSelected(
1520 parentVisualizationWidget()->selectionZoneManager().setSelected(
1524 selectionZoneItemUnderCursor,
1521 selectionZoneItemUnderCursor,
1525 !selectionZoneItemUnderCursor->selected()
1522 !selectionZoneItemUnderCursor->selected()
1526 || event->button() == Qt::RightButton);
1523 || event->button() == Qt::RightButton);
1527 }
1524 }
1528 }
1525 }
1529 }
1526 }
1530 else
1527 else
1531 {
1528 {
1532 // No selection change
1529 // No selection change
1533 }
1530 }
1534 }
1531 }
1535 }
1532 }
1536
1533
1537 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1534 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1538 {
1535 {
1539 auto graphRange = impl->m_plot->xAxis->range();
1536 auto graphRange = impl->m_plot->xAxis->range();
1540 auto dateTime = DateTimeRange { graphRange.lower, graphRange.upper };
1537 auto dateTime = DateTimeRange { graphRange.lower, graphRange.upper };
1541
1538
1542 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
1539 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
1543 {
1540 {
1544 auto variable = variableEntry.first;
1541 auto variable = variableEntry.first;
1545 qCDebug(LOG_VisualizationGraphWidget())
1542 qCDebug(LOG_VisualizationGraphWidget())
1546 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1543 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1547 qCDebug(LOG_VisualizationGraphWidget())
1544 qCDebug(LOG_VisualizationGraphWidget())
1548 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1545 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1549 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range()))
1546 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range()))
1550 {
1547 {
1551 impl->updateData(variableEntry.second, variable, variable->range());
1548 impl->updateData(variableEntry.second, variable, variable->range());
1552 }
1549 }
1553 }
1550 }
1554 }
1551 }
1555
1552
1556 void VisualizationGraphWidget::onUpdateVarDisplaying(
1553 void VisualizationGraphWidget::onUpdateVarDisplaying(
1557 std::shared_ptr<Variable2> variable, const DateTimeRange& range)
1554 std::shared_ptr<Variable2> variable, const DateTimeRange& range)
1558 {
1555 {
1559 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1556 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1560 if (it != impl->m_VariableToPlotMultiMap.end())
1557 if (it != impl->m_VariableToPlotMultiMap.end())
1561 {
1558 {
1562 impl->updateData(it->second, variable, range);
1559 impl->updateData(it->second, variable, range);
1563 }
1560 }
1564 }
1561 }
1565
1562
1566 void VisualizationGraphWidget::variableUpdated(QUuid id)
1563 void VisualizationGraphWidget::variableUpdated(QUuid id)
1567 {
1564 {
1568 for (auto& [var, plotables] : impl->m_VariableToPlotMultiMap)
1565 for (auto& [var, plotables] : impl->m_VariableToPlotMultiMap)
1569 {
1566 {
1570 if (var->ID() == id)
1567 if (var->ID() == id)
1571 {
1568 {
1572 impl->updateData(plotables, var, this->graphRange());
1569 impl->updateData(plotables, var, this->graphRange());
1573 }
1570 }
1574 }
1571 }
1575 }
1572 }
1576
1573
1577 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable2>& variable)
1574 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable2>& variable)
1578 {
1575 {
1579 this->removeVariable(variable);
1576 this->removeVariable(variable);
1580 }
1577 }
@@ -1,172 +1,171
1 #ifndef GUITESTUTILS_H
1 #ifndef GUITESTUTILS_H
2 #define GUITESTUTILS_H
2 #define GUITESTUTILS_H
3
3
4 #include <Common/cpp_utils.h>
4 #include <Common/cpp_utils.h>
5 #include <QCoreApplication>
5 #include <QCoreApplication>
6 #include <QCursor>
6 #include <QCursor>
7 #include <QDesktopWidget>
7 #include <QDesktopWidget>
8 #include <QMouseEvent>
8 #include <QMouseEvent>
9 #include <QPoint>
9 #include <QPoint>
10 #include <QtTest>
10 #include <QtTest>
11
11
12 #include <SqpApplication.h>
12 #include <SqpApplication.h>
13 #include <Variable/Variable.h>
14 #include <Variable/VariableController2.h>
13 #include <Variable/VariableController2.h>
15 #include <qcustomplot.h>
14 #include <qcustomplot.h>
16
15
17 template <typename T>
16 template <typename T>
18 QPoint center(T* widget)
17 QPoint center(T* widget)
19 {
18 {
20 return QPoint { widget->width() / 2, widget->height() / 2 };
19 return QPoint { widget->width() / 2, widget->height() / 2 };
21 }
20 }
22
21
23 HAS_METHOD(viewport)
22 HAS_METHOD(viewport)
24
23
25 template <typename T>
24 template <typename T>
26 static inline constexpr bool is_QWidgetOrDerived = std::is_base_of<QWidget, T>::value;
25 static inline constexpr bool is_QWidgetOrDerived = std::is_base_of<QWidget, T>::value;
27
26
28 template <typename T>
27 template <typename T>
29 using viewport_type = decltype(std::declval<T>().viewport());
28 using viewport_type = decltype(std::declval<T>().viewport());
30
29
31 HAS_METHOD(topLevelItem)
30 HAS_METHOD(topLevelItem)
32
31
33 template <typename T>
32 template <typename T>
34 void mouseMove(T* widget, QPoint pos, Qt::MouseButton mouseModifier)
33 void mouseMove(T* widget, QPoint pos, Qt::MouseButton mouseModifier)
35 {
34 {
36 QCursor::setPos(widget->mapToGlobal(pos));
35 QCursor::setPos(widget->mapToGlobal(pos));
37 QMouseEvent event(QEvent::MouseMove, pos, Qt::NoButton, mouseModifier, Qt::NoModifier);
36 QMouseEvent event(QEvent::MouseMove, pos, Qt::NoButton, mouseModifier, Qt::NoModifier);
38 if constexpr (has_viewport<T>)
37 if constexpr (has_viewport<T>)
39 {
38 {
40 if constexpr (is_QWidgetOrDerived<viewport_type<T>>)
39 if constexpr (is_QWidgetOrDerived<viewport_type<T>>)
41 {
40 {
42 qApp->sendEvent(widget->viewport(), &event);
41 qApp->sendEvent(widget->viewport(), &event);
43 }
42 }
44 else
43 else
45 {
44 {
46 qApp->sendEvent(widget, &event);
45 qApp->sendEvent(widget, &event);
47 }
46 }
48 }
47 }
49 else
48 else
50 {
49 {
51 qApp->sendEvent(widget, &event);
50 qApp->sendEvent(widget, &event);
52 }
51 }
53 qApp->processEvents();
52 qApp->processEvents();
54 }
53 }
55
54
56
55
57 template <typename T>
56 template <typename T>
58 void setMouseTracking(T* widget)
57 void setMouseTracking(T* widget)
59 {
58 {
60 if constexpr (has_viewport<T>)
59 if constexpr (has_viewport<T>)
61 {
60 {
62 if constexpr (is_QWidgetOrDerived<viewport_type<T>>)
61 if constexpr (is_QWidgetOrDerived<viewport_type<T>>)
63 {
62 {
64 widget->viewport()->setMouseTracking(true);
63 widget->viewport()->setMouseTracking(true);
65 }
64 }
66 else
65 else
67 {
66 {
68 widget->setMouseTracking(true);
67 widget->setMouseTracking(true);
69 }
68 }
70 }
69 }
71 else
70 else
72 {
71 {
73 widget->setMouseTracking(true);
72 widget->setMouseTracking(true);
74 }
73 }
75 }
74 }
76
75
77 template <typename T, typename T2>
76 template <typename T, typename T2>
78 auto getItem(T* widget, T2 itemIndex)
77 auto getItem(T* widget, T2 itemIndex)
79 {
78 {
80 if constexpr (has_topLevelItem<T>)
79 if constexpr (has_topLevelItem<T>)
81 {
80 {
82 return widget->topLevelItem(itemIndex);
81 return widget->topLevelItem(itemIndex);
83 }
82 }
84 else
83 else
85 {
84 {
86 return widget->item(itemIndex);
85 return widget->item(itemIndex);
87 }
86 }
88 }
87 }
89
88
90 #define SELECT_ITEM(widget, itemIndex, item) \
89 #define SELECT_ITEM(widget, itemIndex, item) \
91 auto item = getItem(widget, itemIndex); \
90 auto item = getItem(widget, itemIndex); \
92 { \
91 { \
93 auto itemCenterPos = widget->visualItemRect(item).center(); \
92 auto itemCenterPos = widget->visualItemRect(item).center(); \
94 QTest::mouseClick(widget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos); \
93 QTest::mouseClick(widget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos); \
95 QVERIFY(widget->selectedItems().size() > 0); \
94 QVERIFY(widget->selectedItems().size() > 0); \
96 QVERIFY(widget->selectedItems().contains(item)); \
95 QVERIFY(widget->selectedItems().contains(item)); \
97 }
96 }
98
97
99
98
100 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName) \
99 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName) \
101 childType* child = parent.findChild<childType*>(childName); \
100 childType* child = parent.findChild<childType*>(childName); \
102 QVERIFY(child != Q_NULLPTR); \
101 QVERIFY(child != Q_NULLPTR); \
103 setMouseTracking(child);
102 setMouseTracking(child);
104
103
105 template <typename T1, typename T2, typename T3, typename T4 = void>
104 template <typename T1, typename T2, typename T3, typename T4 = void>
106 void dragnDropItem(T1* sourceWidget, T2* destWidget, T3* item, T4* destItem = Q_NULLPTR)
105 void dragnDropItem(T1* sourceWidget, T2* destWidget, T3* item, T4* destItem = Q_NULLPTR)
107 {
106 {
108 auto itemCenterPos = sourceWidget->visualItemRect(item).center();
107 auto itemCenterPos = sourceWidget->visualItemRect(item).center();
109 if constexpr (has_viewport<T1>)
108 if constexpr (has_viewport<T1>)
110 {
109 {
111 QTest::mousePress(sourceWidget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos);
110 QTest::mousePress(sourceWidget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos);
112 }
111 }
113 else
112 else
114 {
113 {
115 QTest::mousePress(sourceWidget, Qt::LeftButton, Qt::NoModifier, itemCenterPos);
114 QTest::mousePress(sourceWidget, Qt::LeftButton, Qt::NoModifier, itemCenterPos);
116 }
115 }
117 mouseMove(sourceWidget, itemCenterPos, Qt::LeftButton);
116 mouseMove(sourceWidget, itemCenterPos, Qt::LeftButton);
118 itemCenterPos += QPoint(0, -10);
117 itemCenterPos += QPoint(0, -10);
119 QTimer::singleShot(100, [destWidget, destItem]() {
118 QTimer::singleShot(100, [destWidget, destItem]() {
120 mouseMove(destWidget, destWidget->rect().center(), Qt::LeftButton);
119 mouseMove(destWidget, destWidget->rect().center(), Qt::LeftButton);
121 mouseMove(destWidget, destWidget->rect().center() + QPoint(0, -10), Qt::LeftButton);
120 mouseMove(destWidget, destWidget->rect().center() + QPoint(0, -10), Qt::LeftButton);
122 if constexpr (!std::is_same_v<void, T4>)
121 if constexpr (!std::is_same_v<void, T4>)
123 {
122 {
124 auto destItemCenterPos = destWidget->visualItemRect(destItem).center();
123 auto destItemCenterPos = destWidget->visualItemRect(destItem).center();
125 QTest::mouseRelease(destWidget, Qt::LeftButton, Qt::NoModifier, destItemCenterPos);
124 QTest::mouseRelease(destWidget, Qt::LeftButton, Qt::NoModifier, destItemCenterPos);
126 }
125 }
127 else if constexpr (has_viewport<T2>)
126 else if constexpr (has_viewport<T2>)
128 {
127 {
129 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
128 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
130 }
129 }
131 else
130 else
132 {
131 {
133 QTest::mouseRelease(destWidget, Qt::LeftButton);
132 QTest::mouseRelease(destWidget, Qt::LeftButton);
134 }
133 }
135 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
134 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
136 });
135 });
137 mouseMove(sourceWidget, itemCenterPos, Qt::LeftButton);
136 mouseMove(sourceWidget, itemCenterPos, Qt::LeftButton);
138 }
137 }
139
138
140 template <typename T>
139 template <typename T>
141 void scroll_graph(T* w, int dx)
140 void scroll_graph(T* w, int dx)
142 {
141 {
143 auto cent = center(w);
142 auto cent = center(w);
144 QTest::mousePress(w, Qt::LeftButton, Qt::NoModifier, cent, 1);
143 QTest::mousePress(w, Qt::LeftButton, Qt::NoModifier, cent, 1);
145 mouseMove(w, { cent.x() + dx, cent.y() }, Qt::LeftButton);
144 mouseMove(w, { cent.x() + dx, cent.y() }, Qt::LeftButton);
146 QTest::mouseRelease(w, Qt::LeftButton);
145 QTest::mouseRelease(w, Qt::LeftButton);
147 }
146 }
148
147
149 ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication*>(qApp)->variableController().isReady)
148 ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication*>(qApp)->variableController().isReady)
150
149
151 void waitForVar(std::shared_ptr<Variable2> var)
150 void waitForVar(std::shared_ptr<Variable2> var)
152 {
151 {
153 while (!isReady(var))
152 while (!isReady(var))
154 QCoreApplication::processEvents();
153 QCoreApplication::processEvents();
155 }
154 }
156
155
157 template <typename T>
156 template <typename T>
158 bool prepare_gui_test(T* w)
157 bool prepare_gui_test(T* w)
159 {
158 {
160 w->setGeometry(QRect(
159 w->setGeometry(QRect(
161 QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)), QSize(500, 500)));
160 QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)), QSize(500, 500)));
162 w->show();
161 w->show();
163 qApp->setActiveWindow(w);
162 qApp->setActiveWindow(w);
164 return QTest::qWaitForWindowActive(w);
163 return QTest::qWaitForWindowActive(w);
165 }
164 }
166
165
167 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName) \
166 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName) \
168 childType* child = parent.findChild<childType*>(childName); \
167 childType* child = parent.findChild<childType*>(childName); \
169 QVERIFY(child != Q_NULLPTR); \
168 QVERIFY(child != Q_NULLPTR); \
170 setMouseTracking(child);
169 setMouseTracking(child);
171
170
172 #endif
171 #endif
General Comments 0
You need to be logged in to leave comments. Login now