##// END OF EJS Templates
All the codebase is modified to build with new Variable Controller...
jeandet -
r1348:ea7d1a66f4ab
parent child
Show More
@@ -1,54 +1,60
1 1 cmake_minimum_required(VERSION 3.6)
2 2 project(SciQLOP CXX)
3 3
4 4 include(GNUInstallDirs)
5 5
6 6 SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake")
7 7
8 8 OPTION (CPPCHECK "Analyzes the source code with cppcheck" OFF)
9 9 OPTION (CLANG_TIDY "Analyzes the source code with Clang Tidy" OFF)
10 10 OPTION (IWYU "Analyzes the source code with Include What You Use" OFF)
11 11
12 12 set(CMAKE_CXX_STANDARD 17)
13 13
14 14 set(CMAKE_AUTOMOC ON)
15 15 #https://gitlab.kitware.com/cmake/cmake/issues/15227
16 16 #set(CMAKE_AUTOUIC ON)
17 17 if(POLICY CMP0071)
18 18 cmake_policy(SET CMP0071 OLD)
19 19 endif()
20 20 set(CMAKE_AUTORCC ON)
21 21 set(CMAKE_INCLUDE_CURRENT_DIR ON)
22 22
23 23 if(NOT DEFINED CMAKE_INSTALL_RPATH_USE_LINK_PATH)
24 24 set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
25 25 endif()
26 26 if(NOT DEFINED CMAKE_MACOSX_RPATH)
27 27 set(CMAKE_MACOSX_RPATH TRUE)
28 28 endif()
29 29
30 30 if(NOT CMAKE_BUILD_TYPE)
31 31 set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
32 32 endif()
33 33
34 34 find_package(Qt5 COMPONENTS Core Widgets Network PrintSupport Svg Test REQUIRED)
35 35
36 36 IF(CPPCHECK)
37 37 set(CMAKE_CXX_CPPCHECK "cppcheck;--enable=warning,style")
38 38 ENDIF(CPPCHECK)
39 39
40 40 IF(CLANG_TIDY)
41 41 set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-style=file;-checks=*")
42 42 ENDIF(CLANG_TIDY)
43 43
44 44 IF(IWYU)
45 45 set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "include-what-you-use")
46 46 ENDIF(IWYU)
47 47
48 48 enable_testing()
49 49
50 add_subdirectory(core)
50 find_package(core CONFIG QUIET)
51 if (NOT sciqlopcore_FOUND)
52 execute_process(COMMAND git submodule init core WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
53 execute_process(COMMAND git submodule update core WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
54 add_subdirectory(core)
55 endif()
56
51 57 add_subdirectory(gui)
52 58 add_subdirectory(app)
53 59 add_subdirectory(plugins)
54 60 add_subdirectory(docs)
@@ -1,405 +1,405
1 1 /*------------------------------------------------------------------------------
2 2 -- This file is a part of the SciQLop Software
3 3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 4 --
5 5 -- This program is free software; you can redistribute it and/or modify
6 6 -- it under the terms of the GNU General Public License as published by
7 7 -- the Free Software Foundation; either version 2 of the License, or
8 8 -- (at your option) any later version.
9 9 --
10 10 -- This program is distributed in the hope that it will be useful,
11 11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 -- GNU General Public License for more details.
14 14 --
15 15 -- You should have received a copy of the GNU General Public License
16 16 -- along with this program; if not, write to the Free Software
17 17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 18 -------------------------------------------------------------------------------*/
19 19 /*-- Author : Alexis Jeandet
20 20 -- Mail : alexis.jeandet@member.fsf.org
21 21 ----------------------------------------------------------------------------*/
22 22 #include "MainWindow.h"
23 23 #include "ui_MainWindow.h"
24 24
25 25 #include <Catalogue/CatalogueController.h>
26 26 #include <Catalogue/CatalogueExplorer.h>
27 27 #include <DataSource/DataSourceController.h>
28 28 #include <DataSource/DataSourceWidget.h>
29 29 #include <Settings/SqpSettingsDialog.h>
30 30 #include <Settings/SqpSettingsGeneralWidget.h>
31 31 #include <SidePane/SqpSidePane.h>
32 32 #include <SqpApplication.h>
33 33 #include <Time/TimeController.h>
34 34 #include <TimeWidget/TimeWidget.h>
35 35 #include <Variable/Variable.h>
36 36 #include <Variable/VariableController.h>
37 37 #include <Visualization/VisualizationController.h>
38 38
39 39 #include <QAction>
40 40 #include <QCloseEvent>
41 41 #include <QDate>
42 42 #include <QDir>
43 43 #include <QFileDialog>
44 44 #include <QMessageBox>
45 45 #include <QToolBar>
46 46 #include <QToolButton>
47 47 #include <memory.h>
48 48
49 49 #include "iostream"
50 50
51 51 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
52 52
53 53 namespace {
54 54 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
55 55 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
56 56 const auto VIEWPLITTERINDEX = 2;
57 57 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
58 58 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
59 59 }
60 60
61 61 class MainWindow::MainWindowPrivate {
62 62 public:
63 63 explicit MainWindowPrivate(MainWindow *mainWindow)
64 64 : m_LastOpenLeftInspectorSize{},
65 65 m_LastOpenRightInspectorSize{},
66 66 m_GeneralSettingsWidget{new SqpSettingsGeneralWidget{mainWindow}},
67 67 m_SettingsDialog{new SqpSettingsDialog{mainWindow}},
68 68 m_CatalogExplorer{new CatalogueExplorer{mainWindow}}
69 69 {
70 70 }
71 71
72 72 QSize m_LastOpenLeftInspectorSize;
73 73 QSize m_LastOpenRightInspectorSize;
74 74 /// General settings widget. MainWindow has the ownership
75 75 SqpSettingsGeneralWidget *m_GeneralSettingsWidget;
76 76 /// Settings dialog. MainWindow has the ownership
77 77 SqpSettingsDialog *m_SettingsDialog;
78 78 /// Catalogue dialog. MainWindow has the ownership
79 79 CatalogueExplorer *m_CatalogExplorer;
80 80
81 81 bool checkDataToSave(QWidget *parentWidget);
82 82 };
83 83
84 84 MainWindow::MainWindow(QWidget *parent)
85 85 : QMainWindow{parent},
86 86 m_Ui{new Ui::MainWindow},
87 87 impl{spimpl::make_unique_impl<MainWindowPrivate>(this)}
88 88 {
89 89 m_Ui->setupUi(this);
90 90
91 91 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
92 92 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
93 93
94 94 impl->m_CatalogExplorer->setVisualizationWidget(m_Ui->view);
95 95
96 96
97 97 auto leftSidePane = m_Ui->leftInspectorSidePane->sidePane();
98 98 auto openLeftInspectorAction = new QAction{QIcon{
99 99 ":/icones/previous.png",
100 100 },
101 101 tr("Show/hide the left inspector"), this};
102 102
103 103
104 104 auto spacerLeftTop = new QWidget{};
105 105 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
106 106
107 107 auto spacerLeftBottom = new QWidget{};
108 108 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
109 109
110 110 leftSidePane->addWidget(spacerLeftTop);
111 111 leftSidePane->addAction(openLeftInspectorAction);
112 112 leftSidePane->addWidget(spacerLeftBottom);
113 113
114 114
115 115 auto rightSidePane = m_Ui->rightInspectorSidePane->sidePane();
116 116 auto openRightInspectorAction = new QAction{QIcon{
117 117 ":/icones/next.png",
118 118 },
119 119 tr("Show/hide the right inspector"), this};
120 120
121 121 auto spacerRightTop = new QWidget{};
122 122 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
123 123
124 124 auto spacerRightBottom = new QWidget{};
125 125 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
126 126
127 127 rightSidePane->addWidget(spacerRightTop);
128 128 rightSidePane->addAction(openRightInspectorAction);
129 129 rightSidePane->addWidget(spacerRightBottom);
130 130
131 131 openLeftInspectorAction->setCheckable(true);
132 132 openRightInspectorAction->setCheckable(true);
133 133
134 134 auto openInspector = [this](bool checked, bool right, auto action) {
135 135
136 136 action->setIcon(QIcon{(checked ^ right) ? ":/icones/next.png" : ":/icones/previous.png"});
137 137
138 138 auto &lastInspectorSize
139 139 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
140 140
141 141 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
142 142 : m_Ui->leftMainInspectorWidget->size();
143 143
144 144 // Update of the last opened geometry
145 145 if (checked) {
146 146 lastInspectorSize = nextInspectorSize;
147 147 }
148 148
149 149 auto startSize = lastInspectorSize;
150 150 auto endSize = startSize;
151 151 endSize.setWidth(0);
152 152
153 153 auto splitterInspectorIndex
154 154 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
155 155
156 156 auto currentSizes = m_Ui->splitter->sizes();
157 157 if (checked) {
158 158 // adjust sizes individually here, e.g.
159 159 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
160 160 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
161 161 m_Ui->splitter->setSizes(currentSizes);
162 162 }
163 163 else {
164 164 // adjust sizes individually here, e.g.
165 165 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
166 166 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
167 167 m_Ui->splitter->setSizes(currentSizes);
168 168 }
169 169
170 170 };
171 171
172 172
173 173 connect(openLeftInspectorAction, &QAction::triggered,
174 174 [openInspector, openLeftInspectorAction](bool checked) {
175 175 openInspector(checked, false, openLeftInspectorAction);
176 176 });
177 177 connect(openRightInspectorAction, &QAction::triggered,
178 178 [openInspector, openRightInspectorAction](bool checked) {
179 179 openInspector(checked, true, openRightInspectorAction);
180 180 });
181 181
182 182 // //////////////// //
183 183 // Menu and Toolbar //
184 184 // //////////////// //
185 185 this->menuBar()->addAction(tr("File"));
186 186 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
187 187 toolsMenu->addAction(tr("Settings..."), [this]() {
188 188 // Loads settings
189 189 impl->m_SettingsDialog->loadSettings();
190 190
191 191 // Open settings dialog and save settings if the dialog is accepted
192 192 if (impl->m_SettingsDialog->exec() == QDialog::Accepted) {
193 193 impl->m_SettingsDialog->saveSettings();
194 194 }
195 195
196 196 });
197 197
198 198 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
199 199
200 200 auto timeWidget = new TimeWidget{};
201 201 mainToolBar->addWidget(timeWidget);
202 202
203 203 // Interaction modes
204 204 auto actionPointerMode = new QAction{QIcon(":/icones/pointer.png"), "Move", this};
205 205 actionPointerMode->setCheckable(true);
206 206 actionPointerMode->setChecked(sqpApp->plotsInteractionMode()
207 207 == SqpApplication::PlotsInteractionMode::None);
208 208 connect(actionPointerMode, &QAction::triggered,
209 209 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::None); });
210 210
211 211 auto actionZoomMode = new QAction{QIcon(":/icones/zoom.png"), "Zoom", this};
212 212 actionZoomMode->setCheckable(true);
213 213 actionZoomMode->setChecked(sqpApp->plotsInteractionMode()
214 214 == SqpApplication::PlotsInteractionMode::ZoomBox);
215 215 connect(actionZoomMode, &QAction::triggered, []() {
216 216 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::ZoomBox);
217 217 });
218 218
219 219 auto actionOrganisationMode = new QAction{QIcon(":/icones/drag.png"), "Organize", this};
220 220 actionOrganisationMode->setCheckable(true);
221 221 actionOrganisationMode->setChecked(sqpApp->plotsInteractionMode()
222 222 == SqpApplication::PlotsInteractionMode::DragAndDrop);
223 223 connect(actionOrganisationMode, &QAction::triggered, []() {
224 224 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::DragAndDrop);
225 225 });
226 226
227 227 auto actionZonesMode = new QAction{QIcon(":/icones/rectangle.png"), "Zones", this};
228 228 actionZonesMode->setCheckable(true);
229 229 actionZonesMode->setChecked(sqpApp->plotsInteractionMode()
230 230 == SqpApplication::PlotsInteractionMode::SelectionZones);
231 231 connect(actionZonesMode, &QAction::triggered, []() {
232 232 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::SelectionZones);
233 233 });
234 234
235 235 auto modeActionGroup = new QActionGroup{this};
236 236 modeActionGroup->addAction(actionZoomMode);
237 237 modeActionGroup->addAction(actionZonesMode);
238 238 modeActionGroup->addAction(actionOrganisationMode);
239 239 modeActionGroup->addAction(actionPointerMode);
240 240 modeActionGroup->setExclusive(true);
241 241
242 242 mainToolBar->addSeparator();
243 243 mainToolBar->addAction(actionPointerMode);
244 244 mainToolBar->addAction(actionZoomMode);
245 245 mainToolBar->addAction(actionOrganisationMode);
246 246 mainToolBar->addAction(actionZonesMode);
247 247 mainToolBar->addSeparator();
248 248
249 249 // Cursors
250 250 auto btnCursor = new QToolButton{this};
251 251 btnCursor->setIcon(QIcon(":/icones/cursor.png"));
252 252 btnCursor->setText("Cursor");
253 253 btnCursor->setToolTip("Cursor");
254 254 btnCursor->setPopupMode(QToolButton::InstantPopup);
255 255 auto cursorMenu = new QMenu("CursorMenu", this);
256 256 btnCursor->setMenu(cursorMenu);
257 257
258 258 auto noCursorAction = cursorMenu->addAction("No Cursor");
259 259 noCursorAction->setCheckable(true);
260 260 noCursorAction->setChecked(sqpApp->plotsCursorMode()
261 261 == SqpApplication::PlotsCursorMode::NoCursor);
262 262 connect(noCursorAction, &QAction::triggered,
263 263 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::NoCursor); });
264 264
265 265 cursorMenu->addSeparator();
266 266 auto verticalCursorAction = cursorMenu->addAction("Vertical Cursor");
267 267 verticalCursorAction->setCheckable(true);
268 268 verticalCursorAction->setChecked(sqpApp->plotsCursorMode()
269 269 == SqpApplication::PlotsCursorMode::Vertical);
270 270 connect(verticalCursorAction, &QAction::triggered,
271 271 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Vertical); });
272 272
273 273 auto temporalCursorAction = cursorMenu->addAction("Temporal Cursor");
274 274 temporalCursorAction->setCheckable(true);
275 275 temporalCursorAction->setChecked(sqpApp->plotsCursorMode()
276 276 == SqpApplication::PlotsCursorMode::Temporal);
277 277 connect(temporalCursorAction, &QAction::triggered,
278 278 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Temporal); });
279 279
280 280 auto horizontalCursorAction = cursorMenu->addAction("Horizontal Cursor");
281 281 horizontalCursorAction->setCheckable(true);
282 282 horizontalCursorAction->setChecked(sqpApp->plotsCursorMode()
283 283 == SqpApplication::PlotsCursorMode::Horizontal);
284 284 connect(horizontalCursorAction, &QAction::triggered,
285 285 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Horizontal); });
286 286
287 287 auto crossCursorAction = cursorMenu->addAction("Cross Cursor");
288 288 crossCursorAction->setCheckable(true);
289 289 crossCursorAction->setChecked(sqpApp->plotsCursorMode()
290 290 == SqpApplication::PlotsCursorMode::Cross);
291 291 connect(crossCursorAction, &QAction::triggered,
292 292 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Cross); });
293 293
294 294 mainToolBar->addWidget(btnCursor);
295 295
296 296 auto cursorModeActionGroup = new QActionGroup{this};
297 297 cursorModeActionGroup->setExclusive(true);
298 298 cursorModeActionGroup->addAction(noCursorAction);
299 299 cursorModeActionGroup->addAction(verticalCursorAction);
300 300 cursorModeActionGroup->addAction(temporalCursorAction);
301 301 cursorModeActionGroup->addAction(horizontalCursorAction);
302 302 cursorModeActionGroup->addAction(crossCursorAction);
303 303
304 304 // Catalog
305 305 mainToolBar->addSeparator();
306 306 mainToolBar->addAction(QIcon(":/icones/catalogue.png"), "Catalogues",
307 307 [this]() { impl->m_CatalogExplorer->show(); });
308 308
309 309 // //////// //
310 310 // Settings //
311 311 // //////// //
312 312
313 313 // Registers "general settings" widget to the settings dialog
314 314 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
315 315 impl->m_GeneralSettingsWidget);
316 316
317 317 // /////////// //
318 318 // Connections //
319 319 // /////////// //
320 320
321 321 // Controllers / controllers connections
322 connect(&sqpApp->timeController(), SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->variableController(),
323 SLOT(onDateTimeOnSelection(DateTimeRange)));
322 // connect(&sqpApp->timeController(), SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->variableController(),
323 // SLOT(onDateTimeOnSelection(DateTimeRange)));
324 324
325 325 // Widgets / controllers connections
326 326
327 327 // DataSource
328 328 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
329 329 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
330 330
331 331 // Time
332 332 connect(timeWidget, SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->timeController(),
333 333 SLOT(onTimeToUpdate(DateTimeRange)));
334 334
335 335 // Visualization
336 336 connect(&sqpApp->visualizationController(),
337 337 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
338 338 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
339 339
340 340 connect(&sqpApp->visualizationController(),
341 341 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)), m_Ui->view,
342 342 SLOT(onRangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)));
343 343
344 344 // Widgets / widgets connections
345 345
346 346 // For the following connections, we use DirectConnection to allow each widget that can
347 347 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
348 348 // The order of connections is also important, since it determines the order in which each
349 349 // widget will attach its menu
350 350 connect(
351 351 m_Ui->variableInspectorWidget,
352 352 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
353 353 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
354 354 Qt::DirectConnection);
355 355 }
356 356
357 357 MainWindow::~MainWindow()
358 358 {
359 359 }
360 360
361 361 void MainWindow::changeEvent(QEvent *e)
362 362 {
363 363 QMainWindow::changeEvent(e);
364 364 switch (e->type()) {
365 365 case QEvent::LanguageChange:
366 366 m_Ui->retranslateUi(this);
367 367 break;
368 368 default:
369 369 break;
370 370 }
371 371 }
372 372
373 373 void MainWindow::closeEvent(QCloseEvent *event)
374 374 {
375 375 if (!impl->checkDataToSave(this)) {
376 376 event->ignore();
377 377 }
378 378 else {
379 379 event->accept();
380 380 }
381 381 }
382 382
383 383 bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget *parentWidget)
384 384 {
385 385 auto hasChanges = sqpApp->catalogueController().hasChanges();
386 386 if (hasChanges) {
387 387 // There are some unsaved changes
388 388 switch (QMessageBox::question(
389 389 parentWidget, tr("Save changes"),
390 390 tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"),
391 391 QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel,
392 392 QMessageBox::SaveAll)) {
393 393 case QMessageBox::SaveAll:
394 394 sqpApp->catalogueController().saveAll();
395 395 break;
396 396 case QMessageBox::Discard:
397 397 break;
398 398 case QMessageBox::Cancel:
399 399 default:
400 400 return false;
401 401 }
402 402 }
403 403
404 404 return true;
405 405 }
@@ -1,1 +1,1
1 Subproject commit 5a50815a763fb421298a06a6d0380b8f5364e7cd
1 Subproject commit 5f4f9560990ba00394322fa1d06d4a30feaf7075
@@ -1,71 +1,76
1 1 #ifndef SCIQLOP_SQPAPPLICATION_H
2 2 #define SCIQLOP_SQPAPPLICATION_H
3 3
4 4 #include "SqpApplication.h"
5 5
6 6 #include <QApplication>
7 7 #include <QLoggingCategory>
8 8
9 9 #include <Common/spimpl.h>
10 10
11 11 Q_DECLARE_LOGGING_CATEGORY(LOG_SqpApplication)
12 12
13 13 #if defined(sqpApp)
14 14 #undef sqpApp
15 15 #endif
16 16 #define sqpApp (static_cast<SqpApplication *>(QCoreApplication::instance()))
17 17
18 18 class DataSourceController;
19 19 class NetworkController;
20 20 class TimeController;
21 21 class VariableController;
22 class VariableController2;
23 class VariableModel2;
22 24 class VisualizationController;
23 25 class DragDropGuiController;
24 26 class ActionsGuiController;
25 27 class CatalogueController;
26 28
27 29 /**
28 30 * @brief The SqpApplication class aims to make the link between SciQlop
29 31 * and its plugins. This is the intermediate class that SciQlop has to use
30 32 * in the way to connect a data source. Please first use load method to initialize
31 33 * a plugin specified by its metadata name (JSON plugin source) then others specifics
32 34 * method will be able to access it.
33 35 * You can load a data source driver plugin then create a data source.
34 36 */
35 37
36 38 class SqpApplication : public QApplication {
37 39 Q_OBJECT
38 40 public:
39 41 explicit SqpApplication(int &argc, char **argv);
40 virtual ~SqpApplication();
42 ~SqpApplication() override;
41 43 void initialize();
42 44
43 45 /// Accessors for the differents sciqlop controllers
44 46 DataSourceController &dataSourceController() noexcept;
45 47 NetworkController &networkController() noexcept;
46 48 TimeController &timeController() noexcept;
47 VariableController &variableController() noexcept;
49 VariableController2 &variableController() noexcept;
50 std::shared_ptr<VariableController2> variableControllerOwner() noexcept;
51 //@TODO there should not be any global model it's just GUI impl detail
52 // VariableModel2 &variableModel() noexcept;
48 53 VisualizationController &visualizationController() noexcept;
49 54 CatalogueController &catalogueController() noexcept;
50 55
51 56 /// Accessors for the differents sciqlop helpers, these helpers classes are like controllers but
52 57 /// doesn't live in a thread and access gui
53 58 DragDropGuiController &dragDropGuiController() noexcept;
54 59 ActionsGuiController &actionsGuiController() noexcept;
55 60
56 61 enum class PlotsInteractionMode { None, ZoomBox, DragAndDrop, SelectionZones };
57 62
58 63 enum class PlotsCursorMode { NoCursor, Vertical, Temporal, Horizontal, Cross };
59 64
60 65 PlotsInteractionMode plotsInteractionMode() const;
61 66 void setPlotsInteractionMode(PlotsInteractionMode mode);
62 67
63 68 PlotsCursorMode plotsCursorMode() const;
64 69 void setPlotsCursorMode(PlotsCursorMode mode);
65 70
66 71 private:
67 72 class SqpApplicationPrivate;
68 73 spimpl::unique_impl_ptr<SqpApplicationPrivate> impl;
69 74 };
70 75
71 76 #endif // SCIQLOP_SQPAPPLICATION_H
@@ -1,56 +1,59
1 1 #ifndef SCIQLOP_VARIABLEINSPECTORWIDGET_H
2 2 #define SCIQLOP_VARIABLEINSPECTORWIDGET_H
3 3
4 4 #include <QLoggingCategory>
5 5 #include <QMenu>
6 6 #include <QWidget>
7 7
8 8 #include <memory>
9 9
10 #include <Variable/VariableModel2.h>
11
10 12 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableInspectorWidget)
11 13
12 14 class Variable;
13 15
14 16 class QProgressBarItemDelegate;
15 17
16 18 namespace Ui {
17 19 class VariableInspectorWidget;
18 20 } // Ui
19 21
20 22 /**
21 23 * @brief The VariableInspectorWidget class representes represents the variable inspector, from
22 24 * which it is possible to view the loaded variables, handle them or trigger their display in
23 25 * visualization
24 26 */
25 27 class VariableInspectorWidget : public QWidget {
26 28 Q_OBJECT
27 29
28 30 public:
29 31 explicit VariableInspectorWidget(QWidget *parent = 0);
30 32 virtual ~VariableInspectorWidget();
31 33
32 34 signals:
33 35 /**
34 36 * Signal emitted before a menu concerning variables is displayed. It is used for other widgets
35 37 * to complete the menu.
36 38 * @param tableMenu the menu to be completed
37 39 * @param variables the variables concerned by the menu
38 40 * @remarks To make the dynamic addition of menus work, the connections to this signal must be
39 41 * in Qt :: DirectConnection
40 42 */
41 43 void tableMenuAboutToBeDisplayed(QMenu *tableMenu,
42 44 const QVector<std::shared_ptr<Variable> > &variables);
43 45
44 46 private:
45 47 Ui::VariableInspectorWidget *ui;
46 48
47 49 QProgressBarItemDelegate *m_ProgressBarItemDelegate;
50 VariableModel2* m_model;
48 51
49 52 private slots:
50 53 /// Slot called when right clicking on an variable in the table (displays a menu)
51 54 void onTableMenuRequested(const QPoint &pos) noexcept;
52 55 /// Refreshes instantly the variable view
53 56 void refresh() noexcept;
54 57 };
55 58
56 59 #endif // SCIQLOP_VARIABLEINSPECTORWIDGET_H
@@ -1,158 +1,158
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 15
16 16 class QCPRange;
17 17 class QCustomPlot;
18 18 class DateTimeRange;
19 19 class Variable;
20 20 class VisualizationWidget;
21 21 class VisualizationZoneWidget;
22 22 class VisualizationSelectionZoneItem;
23 23
24 24 namespace Ui {
25 25 class VisualizationGraphWidget;
26 26 } // namespace Ui
27 27
28 28 /// Defines options that can be associated with the graph
29 29 enum GraphFlag {
30 30 DisableAll = 0x0, ///< Disables acquisition and synchronization
31 31 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
32 32 /// the acquisition of data
33 33 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
34 34 /// the call to the synchronization of the graphs contained in the
35 35 /// same zone of this graph
36 36 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
37 37 };
38 38
39 39 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
40 40 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
41 41
42 42 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
43 43 Q_OBJECT
44 44
45 45 friend class QCustomPlotSynchronizer;
46 46 friend class VisualizationGraphRenderingDelegate;
47 47
48 48 public:
49 49 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
50 50 virtual ~VisualizationGraphWidget();
51 51
52 52 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
53 53 VisualizationZoneWidget *parentZoneWidget() const noexcept;
54 54
55 55 /// Returns the main VisualizationWidget which contains the graph or nullptr
56 56 VisualizationWidget *parentVisualizationWidget() const;
57 57
58 58 /// Sets graph options
59 59 void setFlags(GraphFlags flags);
60 60
61 61 void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range);
62 62
63 63 /// Removes a variable from the graph
64 64 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
65 65
66 66 /// Returns the list of all variables used in the graph
67 QList<std::shared_ptr<Variable> > variables() const;
67 std::vector<std::shared_ptr<Variable> > variables() const;
68 68
69 69 /// Sets the y-axis range based on the data of a variable
70 70 void setYRange(std::shared_ptr<Variable> variable);
71 71 DateTimeRange graphRange() const noexcept;
72 72 void setGraphRange(const DateTimeRange &range, bool calibration = false);
73 73 void setAutoRangeOnVariableInitialization(bool value);
74 74
75 75 // Zones
76 76 /// Returns the ranges of all the selection zones on the graph
77 77 QVector<DateTimeRange> selectionZoneRanges() const;
78 78 /// Adds new selection zones in the graph
79 79 void addSelectionZones(const QVector<DateTimeRange> &ranges);
80 80 /// Adds a new selection zone in the graph
81 81 VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range);
82 82 /// Removes the specified selection zone
83 83 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
84 84
85 85 /// Undo the last zoom done with a zoom box
86 86 void undoZoom();
87 87
88 88 // IVisualizationWidget interface
89 89 void accept(IVisualizationWidgetVisitor *visitor) override;
90 90 bool canDrop(const Variable &variable) const override;
91 91 bool contains(const Variable &variable) const override;
92 92 QString name() const override;
93 93
94 94 // VisualisationDragWidget
95 95 QMimeData *mimeData(const QPoint &position) const override;
96 96 QPixmap customDragPixmap(const QPoint &dragPosition) override;
97 97 bool isDragAllowed() const override;
98 98 void highlightForMerge(bool highlighted) override;
99 99
100 100 // Cursors
101 101 /// Adds or moves the vertical cursor at the specified value on the x-axis
102 102 void addVerticalCursor(double time);
103 103 /// Adds or moves the vertical cursor at the specified value on the x-axis
104 104 void addVerticalCursorAtViewportPosition(double position);
105 105 void removeVerticalCursor();
106 106 /// Adds or moves the vertical cursor at the specified value on the y-axis
107 107 void addHorizontalCursor(double value);
108 108 /// Adds or moves the vertical cursor at the specified value on the y-axis
109 109 void addHorizontalCursorAtViewportPosition(double position);
110 110 void removeHorizontalCursor();
111 111
112 112 signals:
113 113 void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange);
114 114 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const DateTimeRange &range,
115 115 bool synchronise);
116 116
117 117 /// Signal emitted when the variable is about to be removed from the graph
118 118 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
119 119 /// Signal emitted when the variable has been added to the graph
120 120 void variableAdded(std::shared_ptr<Variable> var);
121 121
122 122 protected:
123 123 void closeEvent(QCloseEvent *event) override;
124 124 void enterEvent(QEvent *event) override;
125 125 void leaveEvent(QEvent *event) override;
126 126
127 127 QCustomPlot &plot() const noexcept;
128 128
129 129 private:
130 130 Ui::VisualizationGraphWidget *ui;
131 131
132 132 class VisualizationGraphWidgetPrivate;
133 133 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
134 134
135 135 private slots:
136 136 /// Slot called when right clicking on the graph (displays a menu)
137 137 void onGraphMenuRequested(const QPoint &pos) noexcept;
138 138
139 139 /// Rescale the X axe to range parameter
140 140 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
141 141
142 142 /// Slot called when a mouse double click was made
143 143 void onMouseDoubleClick(QMouseEvent *event) noexcept;
144 144 /// Slot called when a mouse move was made
145 145 void onMouseMove(QMouseEvent *event) noexcept;
146 146 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
147 147 void onMouseWheel(QWheelEvent *event) noexcept;
148 148 /// Slot called when a mouse press was made, to activate the calibration of a graph
149 149 void onMousePress(QMouseEvent *event) noexcept;
150 150 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
151 151 void onMouseRelease(QMouseEvent *event) noexcept;
152 152
153 153 void onDataCacheVariableUpdated();
154 154
155 155 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range);
156 156 };
157 157
158 158 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,88 +1,88
1 1 #ifndef SCIQLOP_VISUALIZATIONTABWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONTABWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5
6 6 #include <Common/spimpl.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QMimeData>
10 10 #include <QWidget>
11 11
12 12 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationTabWidget)
13 13
14 14 class Variable;
15 15 class VisualizationZoneWidget;
16 16
17 17 namespace Ui {
18 18 class VisualizationTabWidget;
19 19 } // namespace Ui
20 20
21 21 class VisualizationTabWidget : public QWidget, public IVisualizationWidget {
22 22 Q_OBJECT
23 23
24 24 public:
25 25 explicit VisualizationTabWidget(const QString &name = {}, QWidget *parent = 0);
26 26 virtual ~VisualizationTabWidget();
27 27
28 28 /// Adds a zone widget
29 29 void addZone(VisualizationZoneWidget *zoneWidget);
30 30
31 31 /// Inserts a zone widget at the specified position
32 32 void insertZone(int index, VisualizationZoneWidget *zoneWidget);
33 33
34 34 /// Returns the list of zone widget names in the order they are displayed
35 35 QStringList availableZoneWidgets() const;
36 36
37 37 /// Returns the zone with the specified name.
38 38 /// If multiple zone with the same name exist, the first one is returned.
39 39 VisualizationZoneWidget *getZoneWithName(const QString &zoneName);
40 40
41 41 /**
42 42 * Creates a zone using a variable. The variable will be displayed in a new graph of the new
43 43 * zone. The zone is added at the end.
44 44 * @param variable the variable for which to create the zone
45 45 * @return the pointer to the created zone
46 46 */
47 47 VisualizationZoneWidget *createZone(std::shared_ptr<Variable> variable);
48 48
49 49 /**
50 50 * Creates a zone using a list of variables. The variables will be displayed in a new graph of
51 51 * the new zone. The zone is inserted at the specified index.
52 52 * @param variables the variables for which to create the zone
53 53 * @param index The index where the zone should be inserted in the layout
54 54 * @return the pointer to the created zone
55 55 */
56 VisualizationZoneWidget *createZone(const QList<std::shared_ptr<Variable> > &variables,
56 VisualizationZoneWidget *createZone(const std::vector<std::shared_ptr<Variable> > &variables,
57 57 int index);
58 58
59 59 /**
60 60 * Creates a zone which is empty (no variables). The zone is inserted at the specified index.
61 61 * @param index The index where the zone should be inserted in the layout
62 62 * @return the pointer to the created zone
63 63 */
64 64 VisualizationZoneWidget *createEmptyZone(int index);
65 65
66 66 // IVisualizationWidget interface
67 67 void accept(IVisualizationWidgetVisitor *visitor) override;
68 68 bool canDrop(const Variable &variable) const override;
69 69 bool contains(const Variable &variable) const override;
70 70 QString name() const override;
71 71
72 72 protected:
73 73 void closeEvent(QCloseEvent *event) override;
74 74
75 75 private:
76 76 /// @return the layout of tab in which zones are added
77 77 QLayout &tabLayout() const noexcept;
78 78
79 79 Ui::VisualizationTabWidget *ui;
80 80
81 81 class VisualizationTabWidgetPrivate;
82 82 spimpl::unique_impl_ptr<VisualizationTabWidgetPrivate> impl;
83 83
84 84 private slots:
85 85 void dropMimeData(int index, const QMimeData *mimeData);
86 86 };
87 87
88 88 #endif // SCIQLOP_VISUALIZATIONTABWIDGET_H
@@ -1,106 +1,106
1 1 #ifndef SCIQLOP_VISUALIZATIONZONEWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONZONEWIDGET_H
3 3
4 4 #include "Data/DateTimeRange.h"
5 5 #include "Visualization/IVisualizationWidget.h"
6 6 #include "Visualization/VisualizationDragWidget.h"
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QWidget>
10 10
11 11 #include <memory>
12 12
13 13 #include <Common/spimpl.h>
14 14
15 15 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationZoneWidget)
16 16
17 17 namespace Ui {
18 18 class VisualizationZoneWidget;
19 19 } // namespace Ui
20 20
21 21 class Variable;
22 22 class VisualizationGraphWidget;
23 23
24 24 class VisualizationZoneWidget : public VisualizationDragWidget, public IVisualizationWidget {
25 25 Q_OBJECT
26 26
27 27 public:
28 28 explicit VisualizationZoneWidget(const QString &name = {}, QWidget *parent = 0);
29 29 virtual ~VisualizationZoneWidget();
30 30
31 31 /// Sets the range of the zone, only works if there is at least one graph in the zone
32 32 /// Note: calibrations between graphs are lost.
33 33 void setZoneRange(const DateTimeRange &range);
34 34
35 35 /// Adds a graph widget
36 36 void addGraph(VisualizationGraphWidget *graphWidget);
37 37
38 38 /// Inserts a graph widget
39 39 void insertGraph(int index, VisualizationGraphWidget *graphWidget);
40 40
41 41 /**
42 42 * Creates a graph using a variable. The variable will be displayed in the new graph.
43 43 * The graph is added at the end.
44 44 * @param variable the variable for which to create the graph
45 45 * @return the pointer to the created graph
46 46 */
47 47 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable);
48 48
49 49 /**
50 50 * Creates a graph using a variable. The variable will be displayed in the new graph.
51 51 * The graph is inserted at the specified index.
52 52 * @param variable the variable for which to create the graph
53 53 * @param index The index where the graph should be inserted in the layout
54 54 * @return the pointer to the created graph
55 55 */
56 56 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable, int index);
57 57
58 58 /**
59 59 * Creates a graph using a list of variables. The variables will be displayed in the new graph.
60 60 * The graph is inserted at the specified index.
61 61 * @param variables List of variables to be added to the graph
62 62 * @param index The index where the graph should be inserted in the layout
63 63 * @return the pointer to the created graph
64 64 */
65 VisualizationGraphWidget *createGraph(const QList<std::shared_ptr<Variable> > variables,
65 VisualizationGraphWidget *createGraph( std::vector<std::shared_ptr<Variable> > variables,
66 66 int index);
67 67
68 68 /// Returns the first graph in the zone or nullptr if there is no graph inside
69 69 VisualizationGraphWidget *firstGraph() const;
70 70
71 71 /// Closes all graphes inside the zone
72 72 void closeAllGraphs();
73 73
74 74 // IVisualizationWidget interface
75 75 void accept(IVisualizationWidgetVisitor *visitor) override;
76 76 bool canDrop(const Variable &variable) const override;
77 77 bool contains(const Variable &variable) const override;
78 78 QString name() const override;
79 79
80 80 // VisualisationDragWidget
81 81 QMimeData *mimeData(const QPoint &position) const override;
82 82 bool isDragAllowed() const override;
83 83
84 84 void notifyMouseMoveInGraph(const QPointF &graphPosition, const QPointF &plotPosition,
85 85 VisualizationGraphWidget *graphWidget);
86 86 void notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget);
87 87
88 88 protected:
89 89 void closeEvent(QCloseEvent *event) override;
90 90
91 91 private:
92 92 Ui::VisualizationZoneWidget *ui;
93 93
94 94 class VisualizationZoneWidgetPrivate;
95 95 spimpl::unique_impl_ptr<VisualizationZoneWidgetPrivate> impl;
96 96
97 97 private slots:
98 98 void onVariableAdded(std::shared_ptr<Variable> variable);
99 99 /// Slot called when a variable is about to be removed from a graph contained in the zone
100 100 void onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable);
101 101
102 102 void dropMimeData(int index, const QMimeData *mimeData);
103 103 void dropMimeDataOnGraph(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
104 104 };
105 105
106 106 #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H
@@ -1,617 +1,616
1 1 #include "Catalogue/CatalogueEventsWidget.h"
2 2 #include "ui_CatalogueEventsWidget.h"
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Catalogue/CatalogueEventsModel.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <CatalogueDao.h>
8 8 #include <DBCatalogue.h>
9 9 #include <DBEventProduct.h>
10 10 #include <DataSource/DataSourceController.h>
11 11 #include <DataSource/DataSourceItem.h>
12 12 #include <SqpApplication.h>
13 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 #include <Variable/VariableController2.h>
15 16 #include <Visualization/VisualizationGraphWidget.h>
16 17 #include <Visualization/VisualizationTabWidget.h>
17 18 #include <Visualization/VisualizationWidget.h>
18 19 #include <Visualization/VisualizationZoneWidget.h>
19 20
20 21 #include <QActionGroup>
21 22 #include <QDialog>
22 23 #include <QDialogButtonBox>
23 24 #include <QKeyEvent>
24 25 #include <QListWidget>
25 26 #include <QMenu>
26 27 #include <QMessageBox>
27 28
28 29 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
29 30
30 31 /// Percentage added to the range of a event when it is displayed
31 32 const auto EVENT_RANGE_MARGE = 30; // in %
32 33
33 34 const QString NEW_ZONE_TEXT = QStringLiteral("New Zone");
34 35
35 36 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
36 37
37 38 CatalogueEventsModel *m_Model = nullptr;
38 39 QStringList m_ZonesForTimeMode;
39 40 QString m_ZoneForGraphMode;
40 41 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
41 42 bool m_AllEventDisplayed = false;
42 43 QVector<VisualizationGraphWidget *> m_CustomGraphs;
43 44
44 45 VisualizationWidget *m_VisualizationWidget = nullptr;
45 46
46 47 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
47 48 {
48 49 widget->ui->treeView->setSortingEnabled(false);
49 50 m_Model->setSourceCatalogues(m_DisplayedCatalogues);
50 51 m_Model->setEvents(events);
51 52 widget->ui->treeView->setSortingEnabled(true);
52 53
53 54 for (auto event : events) {
54 55 if (sqpApp->catalogueController().eventHasChanges(event)) {
55 56 auto index = m_Model->indexOf(event);
56 57 widget->setEventChanges(event, true);
57 58 }
58 59 }
59 60 }
60 61
61 62 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
62 63 {
63 64 treeView->setSortingEnabled(false);
64 65 m_Model->addEvent(event);
65 66 treeView->setSortingEnabled(true);
66 67 }
67 68
68 69 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
69 70 {
70 71 treeView->setSortingEnabled(false);
71 72 m_Model->removeEvent(event);
72 73 treeView->setSortingEnabled(true);
73 74 }
74 75
75 76 QStringList getAvailableVisualizationZoneList() const
76 77 {
77 78 if (m_VisualizationWidget) {
78 79 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
79 80 return tab->availableZoneWidgets();
80 81 }
81 82 }
82 83
83 84 return QStringList{};
84 85 }
85 86
86 87 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
87 88 bool allowMultiSelection, bool addNewZoneOption, const QPoint &location)
88 89 {
89 90 auto availableZones = getAvailableVisualizationZoneList();
90 91 if (!addNewZoneOption && availableZones.isEmpty()) {
91 92 return QStringList{};
92 93 }
93 94
94 95 QActionGroup actionGroup{parent};
95 96 actionGroup.setExclusive(!allowMultiSelection);
96 97
97 98 QVector<QAction *> zoneActions;
98 99
99 100 QMenu selectionMenu{parent};
100 101
101 102 if (addNewZoneOption) {
102 103 availableZones.prepend(NEW_ZONE_TEXT);
103 104 }
104 105
105 106 selectionMenu.addSeparator();
106 107 for (auto zone : availableZones) {
107 108 auto zoneAction = selectionMenu.addAction(zone);
108 109 zoneAction->setCheckable(true);
109 110 zoneAction->setChecked(selectedZones.contains(zone));
110 111 actionGroup.addAction(zoneAction);
111 112 zoneActions << zoneAction;
112 113 }
113 114
114 115 auto resultAction = selectionMenu.exec(QCursor::pos());
115 116
116 117 QStringList result;
117 118
118 119 if (resultAction == nullptr) {
119 120 result = selectedZones;
120 121 }
121 122 else {
122 123 for (auto zoneAction : zoneActions) {
123 124 if (zoneAction->isChecked()) {
124 125 result << zoneAction->text();
125 126 }
126 127 }
127 128 }
128 129
129 130 return result;
130 131 }
131 132
132 133 void updateForTimeMode(QTreeView *treeView)
133 134 {
134 135 auto selectedRows = treeView->selectionModel()->selectedRows();
135 136
136 137 if (selectedRows.count() == 1) {
137 138 auto event = m_Model->getEvent(selectedRows.first());
138 139 if (event) {
139 140 if (m_VisualizationWidget) {
140 141 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
141 142
142 143 for (auto zoneName : m_ZonesForTimeMode) {
143 144 if (auto zone = tab->getZoneWithName(zoneName)) {
144 145 DateTimeRange eventRange;
145 146 eventRange.m_TStart = event->getTStart();
146 147 eventRange.m_TEnd = event->getTEnd();
147 148 zone->setZoneRange(eventRange);
148 149 }
149 150 }
150 151 }
151 152 else {
152 153 qCWarning(LOG_CatalogueEventsWidget())
153 154 << "updateTimeZone: no tab found in the visualization";
154 155 }
155 156 }
156 157 else {
157 158 qCWarning(LOG_CatalogueEventsWidget())
158 159 << "updateTimeZone: visualization widget not found";
159 160 }
160 161 }
161 162 }
162 163 else {
163 164 qCWarning(LOG_CatalogueEventsWidget())
164 165 << "updateTimeZone: not compatible with multiple events selected";
165 166 }
166 167 }
167 168
168 169 QVector<DateTimeRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
169 170 {
170 171 // Retrieves the range of each product and the maximum size
171 172 QVector<DateTimeRange> graphRanges;
172 173 double maxDt = 0;
173 174 for (auto eventProduct : event->getEventProducts()) {
174 175 DateTimeRange eventRange;
175 176 eventRange.m_TStart = eventProduct.getTStart();
176 177 eventRange.m_TEnd = eventProduct.getTEnd();
177 178 graphRanges << eventRange;
178 179
179 180 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
180 181 if (dt > maxDt) {
181 182 maxDt = dt;
182 183 }
183 184 }
184 185
185 186 // Adds the marge
186 187 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
187 188
188 189 // Corrects the graph ranges so that they all have the same size
189 190 QVector<DateTimeRange> correctedGraphRanges;
190 191 for (auto range : graphRanges) {
191 192 auto dt = range.m_TEnd - range.m_TStart;
192 193 auto diff = qAbs((maxDt - dt) / 2.0);
193 194
194 195 DateTimeRange correctedRange;
195 196 correctedRange.m_TStart = range.m_TStart - diff;
196 197 correctedRange.m_TEnd = range.m_TEnd + diff;
197 198
198 199 correctedGraphRanges << correctedRange;
199 200 }
200 201
201 202 return correctedGraphRanges;
202 203 }
203 204
204 205 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
205 206 {
206 207 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
207 208 if (selectedRows.count() != 1) {
208 209 qCWarning(LOG_CatalogueEventsWidget())
209 210 << "updateGraphMode: not compatible with multiple events selected";
210 211 return;
211 212 }
212 213
213 214 if (!m_VisualizationWidget) {
214 215 qCWarning(LOG_CatalogueEventsWidget())
215 216 << "updateGraphMode: visualization widget not found";
216 217 return;
217 218 }
218 219
219 220 auto event = m_Model->getEvent(selectedRows.first());
220 221 if (!event) {
221 222 // A event product is probably selected
222 223 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
223 224 return;
224 225 }
225 226
226 227 auto tab = m_VisualizationWidget->currentTabWidget();
227 228 if (!tab) {
228 229 qCWarning(LOG_CatalogueEventsWidget())
229 230 << "updateGraphMode: no tab found in the visualization";
230 231 return;
231 232 }
232 233
233 234 auto isNewZone = m_ZoneForGraphMode == NEW_ZONE_TEXT;
234 235 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
235 236 if (!isNewZone && !zone) {
236 237 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
237 238 return;
238 239 }
239 240
240 241 // Closes the previous graph and delete the asociated variables
241 242 for (auto graph : m_CustomGraphs) {
242 243 graph->close();
243 auto variables = graph->variables().toVector();
244
245 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
246 Qt::QueuedConnection,
247 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
244 auto variables = graph->variables();
245 for(const auto& variable:variables)
246 sqpApp->variableController().deleteVariable(variable);
248 247 }
249 248 m_CustomGraphs.clear();
250 249
251 250 // Closes the remaining graphs inside the zone
252 251 if (zone) {
253 252 zone->closeAllGraphs();
254 253 }
255 254
256 255 // Creates the zone if needed
257 256 if (isNewZone) {
258 257 zone = tab->createEmptyZone(0);
259 258 m_ZoneForGraphMode = zone->name();
260 259 }
261 260
262 261 // Calculates the range of each graph which will be created
263 262 auto graphRange = getGraphRanges(event);
264 263
265 264 // Loops through the event products and create the graph
266 265 auto itRange = graphRange.cbegin();
267 266 for (auto eventProduct : event->getEventProducts()) {
268 267 auto productId = eventProduct.getProductId();
269 268
270 269 auto range = *itRange;
271 270 ++itRange;
272 271
273 272 DateTimeRange productRange;
274 273 productRange.m_TStart = eventProduct.getTStart();
275 274 productRange.m_TEnd = eventProduct.getTEnd();
276 275
277 276 auto context = new QObject{catalogueEventWidget};
278 277 QObject::connect(
279 &sqpApp->variableController(), &VariableController::variableAdded, context,
278 &sqpApp->variableController(), &VariableController2::variableAdded, context,
280 279 [this, catalogueEventWidget, zone, context, event, range, productRange,
281 280 productId](auto variable) {
282 281
283 282 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
284 283 == productId) {
285 284 auto graph = zone->createGraph(variable);
286 285 graph->setAutoRangeOnVariableInitialization(false);
287 286
288 287 auto selectionZone
289 288 = graph->addSelectionZone(event->getName(), productRange);
290 289 emit catalogueEventWidget->selectionZoneAdded(event, productId,
291 290 selectionZone);
292 291 m_CustomGraphs << graph;
293 292
294 293 graph->setGraphRange(range, true);
295 294
296 295 // Removes the graph from the graph list if it is closed manually
297 296 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
298 297 [this, graph]() { m_CustomGraphs.removeAll(graph); });
299 298
300 299 delete context; // removes the connection
301 300 }
302 301 },
303 302 Qt::QueuedConnection);
304 303
305 304 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
306 305 "requestVariableFromProductIdKey", Qt::QueuedConnection,
307 306 Q_ARG(QString, productId));
308 307 }
309 308 }
310 309
311 310 void getSelectedItems(
312 311 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
313 312 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
314 313 {
315 314 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
316 315 auto itemType = m_Model->itemTypeOf(rowIndex);
317 316 if (itemType == CatalogueEventsModel::ItemType::Event) {
318 317 events << m_Model->getEvent(rowIndex);
319 318 }
320 319 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
321 320 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
322 321 m_Model->getEventProduct(rowIndex));
323 322 }
324 323 }
325 324 }
326 325 };
327 326
328 327 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
329 328 : QWidget(parent),
330 329 ui(new Ui::CatalogueEventsWidget),
331 330 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
332 331 {
333 332 ui->setupUi(this);
334 333
335 334 impl->m_Model = new CatalogueEventsModel{this};
336 335 ui->treeView->setModel(impl->m_Model);
337 336
338 337 ui->treeView->setSortingEnabled(true);
339 338 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
340 339 ui->treeView->setDragEnabled(true);
341 340
342 341
343 342 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
344 343 if (checked) {
345 344 ui->btnChart->setChecked(false);
346 345 impl->m_ZonesForTimeMode
347 346 = impl->selectZone(this, impl->m_ZonesForTimeMode, true, false,
348 347 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
349 348
350 349 impl->updateForTimeMode(ui->treeView);
351 350 }
352 351 });
353 352
354 353 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
355 354 if (checked) {
356 355 ui->btnTime->setChecked(false);
357 356
358 357 impl->m_ZoneForGraphMode
359 358 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false, true,
360 359 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
361 360 .value(0);
362 361
363 362 impl->updateForGraphMode(this);
364 363 }
365 364 });
366 365
367 366 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
368 367 QVector<std::shared_ptr<DBEvent> > events;
369 368 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
370 369 impl->getSelectedItems(ui->treeView, events, eventProducts);
371 370
372 371 if (!events.isEmpty() && eventProducts.isEmpty()) {
373 372
374 373 auto canRemoveEvent
375 374 = !this->isAllEventsDisplayed()
376 375 || (QMessageBox::warning(
377 376 this, tr("Remove Event(s)"),
378 377 tr("The selected event(s) will be permanently removed "
379 378 "from the repository!\nAre you sure you want to continue?"),
380 379 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
381 380 == QMessageBox::Yes);
382 381
383 382 if (canRemoveEvent) {
384 383 for (auto event : events) {
385 384 if (this->isAllEventsDisplayed()) {
386 385 sqpApp->catalogueController().removeEvent(event);
387 386 impl->removeEvent(event, ui->treeView);
388 387 }
389 388 else {
390 389 QVector<std::shared_ptr<DBCatalogue> > modifiedCatalogues;
391 390 for (auto catalogue : this->displayedCatalogues()) {
392 391 if (catalogue->removeEvent(event->getUniqId())) {
393 392 sqpApp->catalogueController().updateCatalogue(catalogue);
394 393 modifiedCatalogues << catalogue;
395 394 }
396 395 }
397 396 if (!modifiedCatalogues.empty()) {
398 397 emit eventCataloguesModified(modifiedCatalogues);
399 398 }
400 399 }
401 400 impl->m_Model->removeEvent(event);
402 401 }
403 402
404 403
405 404 emit this->eventsRemoved(events);
406 405 }
407 406 }
408 407 });
409 408
410 409 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
411 410 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
412 411 &CatalogueEventsWidget::emitSelection);
413 412
414 413 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
415 414 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
416 415 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
417 416 ui->btnChart->setEnabled(isNotMultiSelection);
418 417 ui->btnTime->setEnabled(isNotMultiSelection);
419 418
420 419 if (isNotMultiSelection && ui->btnTime->isChecked()) {
421 420 impl->updateForTimeMode(ui->treeView);
422 421 }
423 422 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
424 423 impl->updateForGraphMode(this);
425 424 }
426 425
427 426 QVector<std::shared_ptr<DBEvent> > events;
428 427 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
429 428 impl->getSelectedItems(ui->treeView, events, eventProducts);
430 429 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
431 430 });
432 431
433 432 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
434 433 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
435 434 QHeaderView::Stretch);
436 435 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
437 436 QHeaderView::ResizeToContents);
438 437 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
439 438 QHeaderView::Interactive);
440 439 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TStart,
441 440 QHeaderView::ResizeToContents);
442 441 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TEnd,
443 442 QHeaderView::ResizeToContents);
444 443 ui->treeView->header()->setSortIndicatorShown(true);
445 444
446 445 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
447 446 auto allEvents = impl->m_Model->events();
448 447 for (auto event : allEvents) {
449 448 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
450 449 }
451 450 });
452 451
453 452 populateWithAllEvents();
454 453 }
455 454
456 455 CatalogueEventsWidget::~CatalogueEventsWidget()
457 456 {
458 457 delete ui;
459 458 }
460 459
461 460 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
462 461 {
463 462 impl->m_VisualizationWidget = visualization;
464 463 }
465 464
466 465 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
467 466 {
468 467 impl->addEvent(event, ui->treeView);
469 468 }
470 469
471 470 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
472 471 {
473 472 impl->m_Model->refreshEvent(event);
474 473
475 474 auto eventIndex = impl->m_Model->indexOf(event);
476 475 auto validationIndex
477 476 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
478 477
479 478 if (validationIndex.isValid()) {
480 479 if (hasChanges) {
481 480 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
482 481 auto widget = CatalogueExplorerHelper::buildValidationWidget(
483 482 ui->treeView,
484 483 [this, event]() {
485 484 sqpApp->catalogueController().saveEvent(event);
486 485 setEventChanges(event, false);
487 486 },
488 487 [this, event]() {
489 488 bool removed = false;
490 489 sqpApp->catalogueController().discardEvent(event, removed);
491 490 if (removed) {
492 491 impl->m_Model->removeEvent(event);
493 492 }
494 493 else {
495 494 setEventChanges(event, false);
496 495 impl->m_Model->refreshEvent(event, true);
497 496 }
498 497 emitSelection();
499 498 });
500 499 ui->treeView->setIndexWidget(validationIndex, widget);
501 500 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
502 501 QHeaderView::ResizeToContents);
503 502 }
504 503 }
505 504 else {
506 505 // Note: the widget is destroyed
507 506 ui->treeView->setIndexWidget(validationIndex, nullptr);
508 507 }
509 508 }
510 509 else {
511 510 qCWarning(LOG_CatalogueEventsWidget())
512 511 << "setEventChanges: the event is not displayed in the model.";
513 512 }
514 513 }
515 514
516 515 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
517 516 {
518 517 return impl->m_DisplayedCatalogues;
519 518 }
520 519
521 520 bool CatalogueEventsWidget::isAllEventsDisplayed() const
522 521 {
523 522 return impl->m_AllEventDisplayed;
524 523 }
525 524
526 525 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
527 526 {
528 527 return impl->m_Model->indexOf(event).isValid();
529 528 }
530 529
531 530 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
532 531 {
533 532 impl->m_Model->refreshEvent(event, true);
534 533 }
535 534
536 535 void CatalogueEventsWidget::populateWithCatalogues(
537 536 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
538 537 {
539 538 impl->m_DisplayedCatalogues = catalogues;
540 539 impl->m_AllEventDisplayed = false;
541 540
542 541 QSet<QUuid> eventIds;
543 542 QVector<std::shared_ptr<DBEvent> > events;
544 543
545 544 for (auto catalogue : catalogues) {
546 545 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
547 546 for (auto event : catalogueEvents) {
548 547 if (!eventIds.contains(event->getUniqId())) {
549 548 events << event;
550 549 eventIds.insert(event->getUniqId());
551 550 }
552 551 }
553 552 }
554 553
555 554 impl->setEvents(events, this);
556 555 }
557 556
558 557 void CatalogueEventsWidget::populateWithAllEvents()
559 558 {
560 559 impl->m_DisplayedCatalogues.clear();
561 560 impl->m_AllEventDisplayed = true;
562 561
563 562 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
564 563
565 564 QVector<std::shared_ptr<DBEvent> > events;
566 565 for (auto event : allEvents) {
567 566 events << event;
568 567 }
569 568
570 569 impl->setEvents(events, this);
571 570 }
572 571
573 572 void CatalogueEventsWidget::clear()
574 573 {
575 574 impl->m_DisplayedCatalogues.clear();
576 575 impl->m_AllEventDisplayed = false;
577 576 impl->setEvents({}, this);
578 577 }
579 578
580 579 void CatalogueEventsWidget::refresh()
581 580 {
582 581 if (isAllEventsDisplayed()) {
583 582 populateWithAllEvents();
584 583 }
585 584 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
586 585 populateWithCatalogues(impl->m_DisplayedCatalogues);
587 586 }
588 587 }
589 588
590 589 void CatalogueEventsWidget::emitSelection()
591 590 {
592 591 QVector<std::shared_ptr<DBEvent> > events;
593 592 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
594 593 impl->getSelectedItems(ui->treeView, events, eventProducts);
595 594
596 595 if (!events.isEmpty() && eventProducts.isEmpty()) {
597 596 emit eventsSelected(events);
598 597 }
599 598 else if (events.isEmpty() && !eventProducts.isEmpty()) {
600 599 emit eventProductsSelected(eventProducts);
601 600 }
602 601 else {
603 602 emit selectionCleared();
604 603 }
605 604 }
606 605
607 606
608 607 void CatalogueEventsWidget::keyPressEvent(QKeyEvent *event)
609 608 {
610 609 switch (event->key()) {
611 610 case Qt::Key_Delete: {
612 611 ui->btnRemove->click();
613 612 }
614 613 default:
615 614 break;
616 615 }
617 616 }
@@ -1,308 +1,308
1 1 #include "DragAndDrop/DragDropGuiController.h"
2 2 #include "DragAndDrop/DragDropScroller.h"
3 3 #include "DragAndDrop/DragDropTabSwitcher.h"
4 4 #include "SqpApplication.h"
5 5 #include "Visualization/VisualizationDragDropContainer.h"
6 6 #include "Visualization/VisualizationDragWidget.h"
7 7 #include "Visualization/VisualizationWidget.h"
8 8 #include "Visualization/operations/FindVariableOperation.h"
9 9
10 10 #include "DataSource/DataSourceController.h"
11 11 #include "Variable/Variable.h"
12 #include "Variable/VariableController.h"
12 #include "Variable/VariableController2.h"
13 13
14 14 #include "Common/MimeTypesDef.h"
15 15 #include "Common/VisualizationDef.h"
16 16
17 17 #include <QDir>
18 18 #include <QLabel>
19 19 #include <QUrl>
20 20 #include <QVBoxLayout>
21 21
22 22
23 23 Q_LOGGING_CATEGORY(LOG_DragDropGuiController, "DragDropGuiController")
24 24
25 25
26 26 struct DragDropGuiController::DragDropGuiControllerPrivate {
27 27
28 28 VisualizationDragWidget *m_CurrentDragWidget = nullptr;
29 29 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
30 30 QLabel *m_PlaceHolderLabel;
31 31 QWidget *m_PlaceBackground;
32 32 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
33 33 std::unique_ptr<DragDropTabSwitcher> m_DragDropTabSwitcher = nullptr;
34 34 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
35 35 // QTemporaryFile to have a name which is not generated.
36 36
37 37 VisualizationDragWidget *m_HighlightedDragWidget = nullptr;
38 38
39 39 QMetaObject::Connection m_DragWidgetDestroyedConnection;
40 40 QMetaObject::Connection m_HighlightedWidgetDestroyedConnection;
41 41
42 42 QList<QWidget *> m_WidgetToClose;
43 43
44 44 explicit DragDropGuiControllerPrivate()
45 45 : m_PlaceHolder{std::make_unique<QWidget>()},
46 46 m_DragDropScroller{std::make_unique<DragDropScroller>()},
47 47 m_DragDropTabSwitcher{std::make_unique<DragDropTabSwitcher>()}
48 48 {
49 49
50 50 auto layout = new QVBoxLayout{m_PlaceHolder.get()};
51 51 layout->setSpacing(0);
52 52 layout->setContentsMargins(0, 0, 0, 0);
53 53
54 54 m_PlaceHolderLabel = new QLabel{"", m_PlaceHolder.get()};
55 55 m_PlaceHolderLabel->setMinimumHeight(25);
56 56 layout->addWidget(m_PlaceHolderLabel);
57 57
58 58 m_PlaceBackground = new QWidget{m_PlaceHolder.get()};
59 59 m_PlaceBackground->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
60 60 layout->addWidget(m_PlaceBackground);
61 61
62 62 sqpApp->installEventFilter(m_DragDropScroller.get());
63 63 sqpApp->installEventFilter(m_DragDropTabSwitcher.get());
64 64
65 65 m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png");
66 66 }
67 67
68 68 void preparePlaceHolder(DragDropGuiController::PlaceHolderType type,
69 69 const QString &topLabelText) const
70 70 {
71 71 if (m_CurrentDragWidget) {
72 72 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
73 73 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
74 74 }
75 75 else {
76 76 // Configuration of the placeHolder when there is no dragWidget
77 77 // (for instance with a drag from a variable)
78 78
79 79 m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
80 80 m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
81 81 }
82 82
83 83 switch (type) {
84 84 case DragDropGuiController::PlaceHolderType::Graph:
85 85 m_PlaceBackground->setStyleSheet(
86 86 "background-color: #BBD5EE; border: 1px solid #2A7FD4");
87 87 break;
88 88 case DragDropGuiController::PlaceHolderType::Zone:
89 89 case DragDropGuiController::PlaceHolderType::Default:
90 90 m_PlaceBackground->setStyleSheet(
91 91 "background-color: #BBD5EE; border: 2px solid #2A7FD4");
92 92 m_PlaceHolderLabel->setStyleSheet("color: #2A7FD4");
93 93 break;
94 94 }
95 95
96 96 m_PlaceHolderLabel->setText(topLabelText);
97 97 m_PlaceHolderLabel->setVisible(!topLabelText.isEmpty());
98 98 }
99 99 };
100 100
101 101
102 102 DragDropGuiController::DragDropGuiController()
103 103 : impl{spimpl::make_unique_impl<DragDropGuiControllerPrivate>()}
104 104 {
105 105 }
106 106
107 107 DragDropGuiController::~DragDropGuiController()
108 108 {
109 109 QFile::remove(impl->m_ImageTempUrl);
110 110 }
111 111
112 112 void DragDropGuiController::resetDragAndDrop()
113 113 {
114 114 setCurrentDragWidget(nullptr);
115 115 impl->m_HighlightedDragWidget = nullptr;
116 116 }
117 117
118 118 void DragDropGuiController::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
119 119 {
120 120 if (impl->m_CurrentDragWidget) {
121 121
122 122 QObject::disconnect(impl->m_DragWidgetDestroyedConnection);
123 123 }
124 124
125 125 if (dragWidget) {
126 126 // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed
127 127 impl->m_DragWidgetDestroyedConnection
128 128 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
129 129 [this]() { impl->m_CurrentDragWidget = nullptr; });
130 130 }
131 131
132 132 impl->m_CurrentDragWidget = dragWidget;
133 133 }
134 134
135 135 VisualizationDragWidget *DragDropGuiController::getCurrentDragWidget() const
136 136 {
137 137 return impl->m_CurrentDragWidget;
138 138 }
139 139
140 140 QWidget &DragDropGuiController::placeHolder() const
141 141 {
142 142 return *impl->m_PlaceHolder;
143 143 }
144 144
145 145 void DragDropGuiController::insertPlaceHolder(QVBoxLayout *layout, int index, PlaceHolderType type,
146 146 const QString &topLabelText)
147 147 {
148 148 removePlaceHolder();
149 149 impl->preparePlaceHolder(type, topLabelText);
150 150 layout->insertWidget(index, impl->m_PlaceHolder.get());
151 151 impl->m_PlaceHolder->show();
152 152 }
153 153
154 154 void DragDropGuiController::removePlaceHolder()
155 155 {
156 156 auto parentWidget = impl->m_PlaceHolder->parentWidget();
157 157 if (parentWidget) {
158 158 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
159 159 impl->m_PlaceHolder->setParent(nullptr);
160 160 impl->m_PlaceHolder->hide();
161 161 }
162 162 }
163 163
164 164 bool DragDropGuiController::isPlaceHolderSet() const
165 165 {
166 166 return impl->m_PlaceHolder->parentWidget();
167 167 }
168 168
169 169 void DragDropGuiController::addDragDropScrollArea(QScrollArea *scrollArea)
170 170 {
171 171 impl->m_DragDropScroller->addScrollArea(scrollArea);
172 172 }
173 173
174 174 void DragDropGuiController::removeDragDropScrollArea(QScrollArea *scrollArea)
175 175 {
176 176 impl->m_DragDropScroller->removeScrollArea(scrollArea);
177 177 }
178 178
179 179 void DragDropGuiController::addDragDropTabBar(QTabBar *tabBar)
180 180 {
181 181 impl->m_DragDropTabSwitcher->addTabBar(tabBar);
182 182 }
183 183
184 184 void DragDropGuiController::removeDragDropTabBar(QTabBar *tabBar)
185 185 {
186 186 impl->m_DragDropTabSwitcher->removeTabBar(tabBar);
187 187 }
188 188
189 189 QUrl DragDropGuiController::imageTemporaryUrl(const QImage &image) const
190 190 {
191 191 image.save(impl->m_ImageTempUrl);
192 192 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
193 193 }
194 194
195 195 void DragDropGuiController::setHightlightedDragWidget(VisualizationDragWidget *dragWidget)
196 196 {
197 197 if (impl->m_HighlightedDragWidget) {
198 198 impl->m_HighlightedDragWidget->highlightForMerge(false);
199 199 QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection);
200 200 }
201 201
202 202 if (dragWidget) {
203 203 dragWidget->highlightForMerge(true);
204 204
205 205 // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed
206 206 impl->m_DragWidgetDestroyedConnection
207 207 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
208 208 [this]() { impl->m_HighlightedDragWidget = nullptr; });
209 209 }
210 210
211 211 impl->m_HighlightedDragWidget = dragWidget;
212 212 }
213 213
214 214 VisualizationDragWidget *DragDropGuiController::getHightlightedDragWidget() const
215 215 {
216 216 return impl->m_HighlightedDragWidget;
217 217 }
218 218
219 219 void DragDropGuiController::delayedCloseWidget(QWidget *widget)
220 220 {
221 221 widget->hide();
222 222 impl->m_WidgetToClose << widget;
223 223 }
224 224
225 225 void DragDropGuiController::doCloseWidgets()
226 226 {
227 227 for (auto widget : impl->m_WidgetToClose) {
228 228 widget->close();
229 229 }
230 230
231 231 impl->m_WidgetToClose.clear();
232 232 }
233 233
234 234 bool DragDropGuiController::checkMimeDataForVisualization(
235 235 const QMimeData *mimeData, VisualizationDragDropContainer *dropContainer)
236 236 {
237 237 if (!mimeData || !dropContainer) {
238 238 qCWarning(LOG_DragDropGuiController()) << QObject::tr(
239 239 "DragDropGuiController::checkMimeDataForVisualization, invalid input parameters.");
240 240 Q_ASSERT(false);
241 241 return false;
242 242 }
243 243
244 244 auto result = false;
245 245
246 246 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
247 auto variables = sqpApp->variableController().variablesForMimeData(
247 auto variables = sqpApp->variableController().variables(
248 248 mimeData->data(MIME_TYPE_VARIABLE_LIST));
249 249
250 if (variables.count() == 1) {
250 if (variables.size() == 1) {
251 251
252 auto variable = variables.first();
252 auto variable = variables[0];
253 253 if (variable->dataSeries() != nullptr) {
254 254
255 255 // Check that the variable is not already in a graph
256 256
257 257 auto parent = dropContainer->parentWidget();
258 258 while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) {
259 259 parent = parent->parentWidget(); // Search for the top level VisualizationWidget
260 260 }
261 261
262 262 if (parent) {
263 263 auto visualizationWidget = static_cast<VisualizationWidget *>(parent);
264 264
265 265 FindVariableOperation findVariableOperation{variable};
266 266 visualizationWidget->accept(&findVariableOperation);
267 267 auto variableContainers = findVariableOperation.result();
268 268 if (variableContainers.empty()) {
269 269 result = true;
270 270 }
271 271 else {
272 272 // result = false: the variable already exist in the visualisation
273 273 }
274 274 }
275 275 else {
276 276 qCWarning(LOG_DragDropGuiController()) << QObject::tr(
277 277 "DragDropGuiController::checkMimeDataForVisualization, the parent "
278 278 "VisualizationWidget cannot be found. Cannot check if the variable is "
279 279 "already used or not.");
280 280 }
281 281 }
282 282 else {
283 283 // result = false: the variable is not fully loaded
284 284 }
285 285 }
286 286 else {
287 287 // result = false: cannot drop multiple variables in the visualisation
288 288 }
289 289 }
290 290 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
291 291 auto productDataList = sqpApp->dataSourceController().productsDataForMimeData(
292 292 mimeData->data(MIME_TYPE_PRODUCT_LIST));
293 293 if (productDataList.count() == 1) {
294 294 result = true;
295 295 }
296 296 else {
297 297 // result = false: cannot drop multiple products in the visualisation
298 298 }
299 299 }
300 300 else {
301 301 // Other MIME data
302 302 // no special rules, accepted by default
303 303 result = true;
304 304 }
305 305
306 306
307 307 return result;
308 308 }
@@ -1,207 +1,202
1 1 #include "SqpApplication.h"
2 2
3 3 #include <Actions/ActionsGuiController.h>
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Data/IDataProvider.h>
6 6 #include <DataSource/DataSourceController.h>
7 7 #include <DragAndDrop/DragDropGuiController.h>
8 8 #include <Network/NetworkController.h>
9 9 #include <QThread>
10 10 #include <Time/TimeController.h>
11 11 #include <Variable/Variable.h>
12 12 #include <Variable/VariableController.h>
13 #include <Variable/VariableController2.h>
14 #include <Variable/VariableModel2.h>
13 15 #include <Variable/VariableModel.h>
14 16 #include <Visualization/VisualizationController.h>
15 17
16 18 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
17 19
18 20 class SqpApplication::SqpApplicationPrivate {
19 21 public:
20 22 SqpApplicationPrivate()
21 : m_DataSourceController{std::make_unique<DataSourceController>()},
22 m_VariableController{std::make_unique<VariableController>()},
23 m_TimeController{std::make_unique<TimeController>()},
24 m_NetworkController{std::make_unique<NetworkController>()},
25 m_VisualizationController{std::make_unique<VisualizationController>()},
26 m_DragDropGuiController{std::make_unique<DragDropGuiController>()},
27 m_CatalogueController{std::make_unique<CatalogueController>()},
28 m_ActionsGuiController{std::make_unique<ActionsGuiController>()},
23 : m_VariableController{std::make_shared<VariableController2>()},
24 m_VariableModel{m_VariableController},
29 25 m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None),
30 26 m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor)
31 27 {
32 28 // /////////////////////////////// //
33 29 // Connections between controllers //
34 30 // /////////////////////////////// //
35 31
36 32 // VariableController <-> DataSourceController
37 connect(m_DataSourceController.get(),
33 connect(&m_DataSourceController,
38 34 SIGNAL(variableCreationRequested(const QString &, const QVariantHash &,
39 35 std::shared_ptr<IDataProvider>)),
40 36 m_VariableController.get(),
41 37 SLOT(createVariable(const QString &, const QVariantHash &,
42 38 std::shared_ptr<IDataProvider>)));
43 39
44 connect(m_VariableController->variableModel(), &VariableModel::requestVariable,
45 m_DataSourceController.get(), &DataSourceController::requestVariable);
40 // connect(m_VariableController->variableModel(), &VariableModel::requestVariable,
41 // m_DataSourceController.get(), &DataSourceController::requestVariable);
46 42
47 43 // VariableController <-> VisualizationController
48 connect(m_VariableController.get(),
49 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
50 m_VisualizationController.get(),
51 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
44 // connect(m_VariableController.get(),
45 // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
46 // m_VisualizationController.get(),
47 // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
52 48
53 connect(m_VariableController.get(),
54 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)),
55 m_VisualizationController.get(),
56 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)));
49 // connect(m_VariableController.get(),
50 // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)),
51 // m_VisualizationController.get(),
52 // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)));
57 53
58 54
59 m_DataSourceController->moveToThread(&m_DataSourceControllerThread);
55 m_DataSourceController.moveToThread(&m_DataSourceControllerThread);
60 56 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
61 m_NetworkController->moveToThread(&m_NetworkControllerThread);
57 m_NetworkController.moveToThread(&m_NetworkControllerThread);
62 58 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
63 m_VariableController->moveToThread(&m_VariableControllerThread);
64 m_VariableControllerThread.setObjectName("VariableControllerThread");
65 m_VisualizationController->moveToThread(&m_VisualizationControllerThread);
59 m_VisualizationController.moveToThread(&m_VisualizationControllerThread);
66 60 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
67 61
68 62 // Additionnal init
69 63 //m_VariableController->setTimeController(m_TimeController.get());
70 64 }
71 65
72 66 virtual ~SqpApplicationPrivate()
73 67 {
74 68 m_DataSourceControllerThread.quit();
75 69 m_DataSourceControllerThread.wait();
76 70
77 71 m_NetworkControllerThread.quit();
78 72 m_NetworkControllerThread.wait();
79 73
80 m_VariableControllerThread.quit();
81 m_VariableControllerThread.wait();
82
83 74 m_VisualizationControllerThread.quit();
84 75 m_VisualizationControllerThread.wait();
85 76 }
86 77
87 std::unique_ptr<DataSourceController> m_DataSourceController;
88 std::unique_ptr<VariableController> m_VariableController;
89 std::unique_ptr<TimeController> m_TimeController;
90 std::unique_ptr<NetworkController> m_NetworkController;
91 std::unique_ptr<VisualizationController> m_VisualizationController;
92 std::unique_ptr<CatalogueController> m_CatalogueController;
78 DataSourceController m_DataSourceController;
79 std::shared_ptr<VariableController2> m_VariableController;
80 TimeController m_TimeController;
81 NetworkController m_NetworkController;
82 VisualizationController m_VisualizationController;
83 CatalogueController m_CatalogueController;
84 VariableModel2 m_VariableModel;
93 85
94 86 QThread m_DataSourceControllerThread;
95 87 QThread m_NetworkControllerThread;
96 QThread m_VariableControllerThread;
97 88 QThread m_VisualizationControllerThread;
98 89
99 std::unique_ptr<DragDropGuiController> m_DragDropGuiController;
100 std::unique_ptr<ActionsGuiController> m_ActionsGuiController;
90 DragDropGuiController m_DragDropGuiController;
91 ActionsGuiController m_ActionsGuiController;
101 92
102 93 SqpApplication::PlotsInteractionMode m_PlotInterractionMode;
103 94 SqpApplication::PlotsCursorMode m_PlotCursorMode;
104 95 };
105 96
106 97
107 98 SqpApplication::SqpApplication(int &argc, char **argv)
108 99 : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()}
109 100 {
110 101 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
111 102
112 103 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
113 104
114 105 connect(&impl->m_DataSourceControllerThread, &QThread::started,
115 impl->m_DataSourceController.get(), &DataSourceController::initialize);
106 &impl->m_DataSourceController, &DataSourceController::initialize);
116 107 connect(&impl->m_DataSourceControllerThread, &QThread::finished,
117 impl->m_DataSourceController.get(), &DataSourceController::finalize);
108 &impl->m_DataSourceController, &DataSourceController::finalize);
118 109
119 connect(&impl->m_NetworkControllerThread, &QThread::started, impl->m_NetworkController.get(),
110 connect(&impl->m_NetworkControllerThread, &QThread::started, &impl->m_NetworkController,
120 111 &NetworkController::initialize);
121 connect(&impl->m_NetworkControllerThread, &QThread::finished, impl->m_NetworkController.get(),
112 connect(&impl->m_NetworkControllerThread, &QThread::finished, &impl->m_NetworkController,
122 113 &NetworkController::finalize);
123 114
124 connect(&impl->m_VariableControllerThread, &QThread::started, impl->m_VariableController.get(),
125 &VariableController::initialize);
126 connect(&impl->m_VariableControllerThread, &QThread::finished, impl->m_VariableController.get(),
127 &VariableController::finalize);
128
129 115 connect(&impl->m_VisualizationControllerThread, &QThread::started,
130 impl->m_VisualizationController.get(), &VisualizationController::initialize);
116 &impl->m_VisualizationController, &VisualizationController::initialize);
131 117 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
132 impl->m_VisualizationController.get(), &VisualizationController::finalize);
118 &impl->m_VisualizationController, &VisualizationController::finalize);
133 119
134 120 impl->m_DataSourceControllerThread.start();
135 121 impl->m_NetworkControllerThread.start();
136 impl->m_VariableControllerThread.start();
137 122 impl->m_VisualizationControllerThread.start();
138 impl->m_CatalogueController->initialize();
123 impl->m_CatalogueController.initialize();
139 124 }
140 125
141 126 SqpApplication::~SqpApplication()
142 127 {
143 128 }
144 129
145 130 void SqpApplication::initialize()
146 131 {
147 132 }
148 133
149 134 DataSourceController &SqpApplication::dataSourceController() noexcept
150 135 {
151 return *impl->m_DataSourceController;
136 return impl->m_DataSourceController;
152 137 }
153 138
154 139 NetworkController &SqpApplication::networkController() noexcept
155 140 {
156 return *impl->m_NetworkController;
141 return impl->m_NetworkController;
157 142 }
158 143
159 144 TimeController &SqpApplication::timeController() noexcept
160 145 {
161 return *impl->m_TimeController;
146 return impl->m_TimeController;
162 147 }
163 148
164 VariableController &SqpApplication::variableController() noexcept
149 VariableController2 &SqpApplication::variableController() noexcept
165 150 {
166 151 return *impl->m_VariableController;
167 152 }
168 153
154 std::shared_ptr<VariableController2> SqpApplication::variableControllerOwner() noexcept
155 {
156 return impl->m_VariableController;
157 }
158
159 //VariableModel2 &SqpApplication::variableModel() noexcept
160 //{
161 // return impl->m_VariableModel;
162 //}
163
169 164 VisualizationController &SqpApplication::visualizationController() noexcept
170 165 {
171 return *impl->m_VisualizationController;
166 return impl->m_VisualizationController;
172 167 }
173 168
174 169 CatalogueController &SqpApplication::catalogueController() noexcept
175 170 {
176 return *impl->m_CatalogueController;
171 return impl->m_CatalogueController;
177 172 }
178 173
179 174 DragDropGuiController &SqpApplication::dragDropGuiController() noexcept
180 175 {
181 return *impl->m_DragDropGuiController;
176 return impl->m_DragDropGuiController;
182 177 }
183 178
184 179 ActionsGuiController &SqpApplication::actionsGuiController() noexcept
185 180 {
186 return *impl->m_ActionsGuiController;
181 return impl->m_ActionsGuiController;
187 182 }
188 183
189 184 SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const
190 185 {
191 186 return impl->m_PlotInterractionMode;
192 187 }
193 188
194 189 void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode)
195 190 {
196 191 impl->m_PlotInterractionMode = mode;
197 192 }
198 193
199 194 SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const
200 195 {
201 196 return impl->m_PlotCursorMode;
202 197 }
203 198
204 199 void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode)
205 200 {
206 201 impl->m_PlotCursorMode = mode;
207 202 }
@@ -1,240 +1,241
1 1 #include <Variable/RenameVariableDialog.h>
2 2 #include <Variable/Variable.h>
3 #include <Variable/VariableController.h>
3 #include <Variable/VariableController2.h>
4 4 #include <Variable/VariableInspectorWidget.h>
5 5 #include <Variable/VariableMenuHeaderWidget.h>
6 #include <Variable/VariableModel.h>
6 #include <Variable/VariableModel2.h>
7 7
8 8 #include <ui_VariableInspectorWidget.h>
9 9
10 10 #include <QMouseEvent>
11 11 #include <QSortFilterProxyModel>
12 12 #include <QStyledItemDelegate>
13 13 #include <QWidgetAction>
14 14
15 15 #include <DragAndDrop/DragDropGuiController.h>
16 16 #include <SqpApplication.h>
17 17
18 18 Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget")
19 19
20 20
21 21 class QProgressBarItemDelegate : public QStyledItemDelegate {
22 22
23 23 public:
24 24 QProgressBarItemDelegate(QObject *parent) : QStyledItemDelegate{parent} {}
25 25
26 26 void paint(QPainter *painter, const QStyleOptionViewItem &option,
27 27 const QModelIndex &index) const
28 28 {
29 29 auto data = index.data(Qt::DisplayRole);
30 30 auto progressData = index.data(VariableRoles::ProgressRole);
31 31 if (data.isValid() && progressData.isValid()) {
32 32 auto name = data.value<QString>();
33 33 auto progress = progressData.value<double>();
34 34 if (progress > 0) {
35 35 auto cancelButtonWidth = 20;
36 36 auto progressBarOption = QStyleOptionProgressBar{};
37 37 auto progressRect = option.rect;
38 38 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
39 39 progressBarOption.rect = progressRect;
40 40 progressBarOption.minimum = 0;
41 41 progressBarOption.maximum = 100;
42 42 progressBarOption.progress = progress;
43 43 progressBarOption.text
44 44 = QString("%1 %2").arg(name).arg(QString::number(progress, 'f', 2) + "%");
45 45 progressBarOption.textVisible = true;
46 46 progressBarOption.textAlignment = Qt::AlignCenter;
47 47
48 48
49 49 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption,
50 50 painter);
51 51
52 52 // Cancel button
53 53 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
54 54 option.rect.height());
55 55 auto buttonOption = QStyleOptionButton{};
56 56 buttonOption.rect = buttonRect;
57 57 buttonOption.text = "X";
58 58
59 59 QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter);
60 60 }
61 61 else {
62 62 QStyledItemDelegate::paint(painter, option, index);
63 63 }
64 64 }
65 65 else {
66 66 QStyledItemDelegate::paint(painter, option, index);
67 67 }
68 68 }
69 69
70 70 bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
71 71 const QModelIndex &index)
72 72 {
73 73 if (event->type() == QEvent::MouseButtonRelease) {
74 74 auto data = index.data(Qt::DisplayRole);
75 75 auto progressData = index.data(VariableRoles::ProgressRole);
76 76 if (data.isValid() && progressData.isValid()) {
77 77 auto cancelButtonWidth = 20;
78 78 auto progressRect = option.rect;
79 79 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
80 80 // Cancel button
81 81 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
82 82 option.rect.height());
83 83
84 84 auto e = (QMouseEvent *)event;
85 85 auto clickX = e->x();
86 86 auto clickY = e->y();
87 87
88 88 auto x = buttonRect.left(); // the X coordinate
89 89 auto y = buttonRect.top(); // the Y coordinate
90 90 auto w = buttonRect.width(); // button width
91 91 auto h = buttonRect.height(); // button height
92 92
93 93 if (clickX > x && clickX < x + w) {
94 94 if (clickY > y && clickY < y + h) {
95 auto variableModel = sqpApp->variableController().variableModel();
96 variableModel->abortProgress(index);
95 //auto& variableModel = sqpApp->variableModel();
96 //variableModel->abortProgress(index);
97 97 }
98 98 return true;
99 99 }
100 100 else {
101 101 return QStyledItemDelegate::editorEvent(event, model, option, index);
102 102 }
103 103 }
104 104 else {
105 105 return QStyledItemDelegate::editorEvent(event, model, option, index);
106 106 }
107 107 }
108 108 else {
109 109 return QStyledItemDelegate::editorEvent(event, model, option, index);
110 110 }
111 111
112 112
113 113 return QStyledItemDelegate::editorEvent(event, model, option, index);
114 114 }
115 115 };
116 116
117 117 VariableInspectorWidget::VariableInspectorWidget(QWidget *parent)
118 118 : QWidget{parent},
119 119 ui{new Ui::VariableInspectorWidget},
120 120 m_ProgressBarItemDelegate{new QProgressBarItemDelegate{this}}
121 121 {
122 122 ui->setupUi(this);
123 123
124 124 // Sets model for table
125 125 // auto sortFilterModel = new QSortFilterProxyModel{this};
126 126 // sortFilterModel->setSourceModel(sqpApp->variableController().variableModel());
127 127
128 auto variableModel = sqpApp->variableController().variableModel();
129 ui->tableView->setModel(variableModel);
128 m_model = new VariableModel2(sqpApp->variableControllerOwner());
129 ui->tableView->setModel(m_model);
130 130
131 131 // Adds extra signal/slot between view and model, so the view can be updated instantly when
132 132 // there is a change of data in the model
133 connect(variableModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
133 connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
134 134 SLOT(refresh()));
135 135
136 ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel());
136 //ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel());
137 137 ui->tableView->setItemDelegateForColumn(0, m_ProgressBarItemDelegate);
138 138
139 139 // Fixes column sizes
140 140 auto model = ui->tableView->model();
141 141 const auto count = model->columnCount();
142 142 for (auto i = 0; i < count; ++i) {
143 143 ui->tableView->setColumnWidth(
144 144 i, model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).toSize().width());
145 145 }
146 146
147 147 // Sets selection options
148 148 ui->tableView->setSelectionBehavior(QTableView::SelectRows);
149 149 ui->tableView->setSelectionMode(QTableView::ExtendedSelection);
150 150
151 151 // Connection to show a menu when right clicking on the tree
152 152 ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
153 153 connect(ui->tableView, &QTableView::customContextMenuRequested, this,
154 154 &VariableInspectorWidget::onTableMenuRequested);
155 155 }
156 156
157 157 VariableInspectorWidget::~VariableInspectorWidget()
158 158 {
159 159 delete ui;
160 160 }
161 161
162 162 void VariableInspectorWidget::onTableMenuRequested(const QPoint &pos) noexcept
163 163 {
164 164 auto selectedRows = ui->tableView->selectionModel()->selectedRows();
165 165
166 166 // Gets the model to retrieve the underlying selected variables
167 auto model = sqpApp->variableController().variableModel();
167 auto& vc = sqpApp->variableController();
168 168 auto selectedVariables = QVector<std::shared_ptr<Variable> >{};
169 169 for (const auto &selectedRow : qAsConst(selectedRows)) {
170 if (auto selectedVariable = model->variable(selectedRow.row())) {
170 if (auto selectedVariable = vc[selectedRow.row()]) {
171 171 selectedVariables.push_back(selectedVariable);
172 172 }
173 173 }
174 174
175 175 QMenu tableMenu{};
176 176
177 177 // Emits a signal so that potential receivers can populate the menu before displaying it
178 178 emit tableMenuAboutToBeDisplayed(&tableMenu, selectedVariables);
179 179
180 180 // Adds menu-specific actions
181 181 if (!selectedVariables.isEmpty()) {
182 182 tableMenu.addSeparator();
183 183
184 184 // 'Rename' and 'Duplicate' actions (only if one variable selected)
185 185 if (selectedVariables.size() == 1) {
186 186 auto selectedVariable = selectedVariables.front();
187 187
188 188 auto duplicateFun = [varW = std::weak_ptr<Variable>(selectedVariable)]()
189 189 {
190 190 if (auto var = varW.lock()) {
191 191 sqpApp->variableController().cloneVariable(var);
192 192 }
193 193 };
194 194
195 195 tableMenu.addAction(tr("Duplicate"), duplicateFun);
196 196
197 auto renameFun = [ varW = std::weak_ptr<Variable>(selectedVariable), &model, this ]()
197 auto renameFun = [ varW = std::weak_ptr<Variable>(selectedVariable), this ]()
198 198 {
199 199 if (auto var = varW.lock()) {
200 200 // Generates forbidden names (names associated to existing variables)
201 auto allVariables = model->variables();
201 auto allVariables = sqpApp->variableController().variables();
202 202 auto forbiddenNames = QVector<QString>(allVariables.size());
203 203 std::transform(allVariables.cbegin(), allVariables.cend(),
204 204 forbiddenNames.begin(),
205 205 [](const auto &variable) { return variable->name(); });
206 206
207 207 RenameVariableDialog dialog{var->name(), forbiddenNames, this};
208 208 if (dialog.exec() == QDialog::Accepted) {
209 209 var->setName(dialog.name());
210 210 }
211 211 }
212 212 };
213 213
214 214 tableMenu.addAction(tr("Rename..."), renameFun);
215 215 }
216 216
217 217 // 'Delete' action
218 218 auto deleteFun = [&selectedVariables]() {
219 sqpApp->variableController().deleteVariables(selectedVariables);
219 for(const auto& var:selectedVariables)
220 sqpApp->variableController().deleteVariable(var);
220 221 };
221 222
222 223 tableMenu.addAction(QIcon{":/icones/delete.png"}, tr("Delete"), deleteFun);
223 224 }
224 225
225 226 if (!tableMenu.isEmpty()) {
226 227 // Generates menu header (inserted before first action)
227 228 auto firstAction = tableMenu.actions().first();
228 229 auto headerAction = new QWidgetAction{&tableMenu};
229 230 headerAction->setDefaultWidget(new VariableMenuHeaderWidget{selectedVariables, &tableMenu});
230 231 tableMenu.insertAction(firstAction, headerAction);
231 232
232 233 // Displays menu
233 234 tableMenu.exec(QCursor::pos());
234 235 }
235 236 }
236 237
237 238 void VariableInspectorWidget::refresh() noexcept
238 239 {
239 240 ui->tableView->viewport()->update();
240 241 }
@@ -1,1082 +1,1083
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 10 #include "Visualization/VisualizationWidget.h"
11 11 #include "Visualization/VisualizationZoneWidget.h"
12 12 #include "ui_VisualizationGraphWidget.h"
13 13
14 14 #include <Actions/ActionsGuiController.h>
15 15 #include <Actions/FilteringAction.h>
16 16 #include <Common/MimeTypesDef.h>
17 17 #include <Data/ArrayData.h>
18 18 #include <Data/IDataSeries.h>
19 19 #include <Data/SpectrogramSeries.h>
20 20 #include <DragAndDrop/DragDropGuiController.h>
21 21 #include <Settings/SqpSettingsDefs.h>
22 22 #include <SqpApplication.h>
23 23 #include <Time/TimeController.h>
24 24 #include <Variable/Variable.h>
25 25 #include <Variable/VariableController.h>
26 26
27 27 #include <unordered_map>
28 28
29 29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
30 30
31 31 namespace {
32 32
33 33 /// Key pressed to enable drag&drop in all modes
34 34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
35 35
36 36 /// Key pressed to enable zoom on horizontal axis
37 37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
38 38
39 39 /// Key pressed to enable zoom on vertical axis
40 40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
41 41
42 42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
43 43 const auto PAN_SPEED = 5;
44 44
45 45 /// Key pressed to enable a calibration pan
46 46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
47 47
48 48 /// Key pressed to enable multi selection of selection zones
49 49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
50 50
51 51 /// Minimum size for the zoom box, in percentage of the axis range
52 52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
53 53
54 54 /// Format of the dates appearing in the label of a cursor
55 55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
56 56
57 57 } // namespace
58 58
59 59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
60 60
61 61 explicit VisualizationGraphWidgetPrivate(const QString &name)
62 62 : m_Name{name},
63 63 m_Flags{GraphFlag::EnableAll},
64 64 m_IsCalibration{false},
65 65 m_RenderingDelegate{nullptr}
66 66 {
67 67 }
68 68
69 69 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
70 70 const DateTimeRange &range)
71 71 {
72 72 VisualizationGraphHelper::updateData(plottables, variable, range);
73 73
74 74 // Prevents that data has changed to update rendering
75 75 m_RenderingDelegate->onPlotUpdated();
76 76 }
77 77
78 78 QString m_Name;
79 79 // 1 variable -> n qcpplot
80 80 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
81 81 GraphFlags m_Flags;
82 82 bool m_IsCalibration;
83 83 /// Delegate used to attach rendering features to the plot
84 84 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
85 85
86 86 QCPItemRect *m_DrawingZoomRect = nullptr;
87 87 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
88 88
89 89 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
90 90 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
91 91
92 92 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
93 93 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
94 94 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
95 95
96 96 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
97 97
98 98 bool m_VariableAutoRangeOnInit = true;
99 99
100 100 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
101 101 {
102 102 removeDrawingRect(plot);
103 103
104 104 auto axisPos = posToAxisPos(pos, plot);
105 105
106 106 m_DrawingZoomRect = new QCPItemRect{&plot};
107 107 QPen p;
108 108 p.setWidth(2);
109 109 m_DrawingZoomRect->setPen(p);
110 110
111 111 m_DrawingZoomRect->topLeft->setCoords(axisPos);
112 112 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
113 113 }
114 114
115 115 void removeDrawingRect(QCustomPlot &plot)
116 116 {
117 117 if (m_DrawingZoomRect) {
118 118 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
119 119 m_DrawingZoomRect = nullptr;
120 120 plot.replot(QCustomPlot::rpQueuedReplot);
121 121 }
122 122 }
123 123
124 124 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
125 125 {
126 126 endDrawingZone(graph);
127 127
128 128 auto axisPos = posToAxisPos(pos, graph->plot());
129 129
130 130 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
131 131 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
132 132 m_DrawingZone->setEditionEnabled(false);
133 133 }
134 134
135 135 void endDrawingZone(VisualizationGraphWidget *graph)
136 136 {
137 137 if (m_DrawingZone) {
138 138 auto drawingZoneRange = m_DrawingZone->range();
139 139 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
140 140 m_DrawingZone->setEditionEnabled(true);
141 141 addSelectionZone(m_DrawingZone);
142 142 }
143 143 else {
144 144 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
145 145 }
146 146
147 147 graph->plot().replot(QCustomPlot::rpQueuedReplot);
148 148 m_DrawingZone = nullptr;
149 149 }
150 150 }
151 151
152 152 void setSelectionZonesEditionEnabled(bool value)
153 153 {
154 154 for (auto s : m_SelectionZones) {
155 155 s->setEditionEnabled(value);
156 156 }
157 157 }
158 158
159 159 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
160 160
161 161 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
162 162 const QCustomPlot &plot) const
163 163 {
164 164 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
165 165 auto minDistanceToZone = -1;
166 166 for (auto zone : m_SelectionZones) {
167 167 auto distanceToZone = zone->selectTest(pos, false);
168 168 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
169 169 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
170 170 selectionZoneItemUnderCursor = zone;
171 171 }
172 172 }
173 173
174 174 return selectionZoneItemUnderCursor;
175 175 }
176 176
177 177 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
178 178 const QCustomPlot &plot) const
179 179 {
180 180 QVector<VisualizationSelectionZoneItem *> zones;
181 181 for (auto zone : m_SelectionZones) {
182 182 auto distanceToZone = zone->selectTest(pos, false);
183 183 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
184 184 zones << zone;
185 185 }
186 186 }
187 187
188 188 return zones;
189 189 }
190 190
191 191 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
192 192 {
193 193 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
194 194 zone->moveToTop();
195 195 m_SelectionZones.removeAll(zone);
196 196 m_SelectionZones.append(zone);
197 197 }
198 198 }
199 199
200 200 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
201 201 {
202 202 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
203 203 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
204 204 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
205 205 }
206 206
207 207 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
208 208 {
209 209 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
210 210 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
211 211 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
212 212 }
213 213 };
214 214
215 215 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
216 216 : VisualizationDragWidget{parent},
217 217 ui{new Ui::VisualizationGraphWidget},
218 218 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
219 219 {
220 220 ui->setupUi(this);
221 221
222 222 // 'Close' options : widget is deleted when closed
223 223 setAttribute(Qt::WA_DeleteOnClose);
224 224
225 225 // Set qcpplot properties :
226 226 // - zoom is enabled
227 227 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
228 228 ui->widget->setInteractions(QCP::iRangeZoom);
229 229 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
230 230
231 231 // The delegate must be initialized after the ui as it uses the plot
232 232 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
233 233
234 234 // Init the cursors
235 235 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
236 236 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
237 237 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
238 238 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
239 239
240 240 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
241 241 connect(ui->widget, &QCustomPlot::mouseRelease, this,
242 242 &VisualizationGraphWidget::onMouseRelease);
243 243 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
244 244 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
245 245 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
246 246 &VisualizationGraphWidget::onMouseDoubleClick);
247 247 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
248 248 &QCPAxis::rangeChanged),
249 249 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
250 250
251 251 // Activates menu when right clicking on the graph
252 252 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
253 253 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
254 254 &VisualizationGraphWidget::onGraphMenuRequested);
255 255
256 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
257 &VariableController::onRequestDataLoading);
256 //@TODO implement this :)
257 // connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
258 // &VariableController::onRequestDataLoading);
258 259
259 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
260 &VisualizationGraphWidget::onUpdateVarDisplaying);
260 // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this,
261 // &VisualizationGraphWidget::onUpdateVarDisplaying);
261 262
262 263 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
263 264 plot().setPlottingHint(QCP::phFastPolylines, true);
264 265 }
265 266
266 267
267 268 VisualizationGraphWidget::~VisualizationGraphWidget()
268 269 {
269 270 delete ui;
270 271 }
271 272
272 273 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
273 274 {
274 275 auto parent = parentWidget();
275 276 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
276 277 parent = parent->parentWidget();
277 278 }
278 279
279 280 return qobject_cast<VisualizationZoneWidget *>(parent);
280 281 }
281 282
282 283 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
283 284 {
284 285 auto parent = parentWidget();
285 286 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
286 287 parent = parent->parentWidget();
287 288 }
288 289
289 290 return qobject_cast<VisualizationWidget *>(parent);
290 291 }
291 292
292 293 void VisualizationGraphWidget::setFlags(GraphFlags flags)
293 294 {
294 295 impl->m_Flags = std::move(flags);
295 296 }
296 297
297 298 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
298 299 {
299 300 /// Lambda used to set graph's units and range according to the variable passed in parameter
300 301 auto loadRange = [this](std::shared_ptr<Variable> variable, const DateTimeRange &range) {
301 302 impl->m_RenderingDelegate->setAxesUnits(*variable);
302 303
303 304 this->setFlags(GraphFlag::DisableAll);
304 305 setGraphRange(range);
305 306 this->setFlags(GraphFlag::EnableAll);
306 307 emit requestDataLoading({variable}, range, false);
307 308 };
308 309
309 310 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
310 311
311 312 // Calls update of graph's range and units when the data of the variable have been initialized.
312 313 // Note: we use QueuedConnection here as the update event must be called in the UI thread
313 314 connect(variable.get(), &Variable::dataInitialized, this,
314 315 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
315 316 if (auto var = varW.lock()) {
316 317 // If the variable is the first added in the graph, we load its range
317 318 auto firstVariableInGraph = range == INVALID_RANGE;
318 319 auto loadedRange = graphRange();
319 320 if (impl->m_VariableAutoRangeOnInit) {
320 321 loadedRange = firstVariableInGraph ? var->range() : range;
321 322 }
322 323 loadRange(var, loadedRange);
323 324 setYRange(var);
324 325 }
325 326 },
326 327 Qt::QueuedConnection);
327 328
328 329 // Uses delegate to create the qcpplot components according to the variable
329 330 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
330 331
331 332 // Sets graph properties
332 333 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
333 334
334 335 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
335 336
336 337 // If the variable already has its data loaded, load its units and its range in the graph
337 338 if (variable->dataSeries() != nullptr) {
338 339 loadRange(variable, range);
339 340 }
340 341
341 342 emit variableAdded(variable);
342 343 }
343 344
344 345 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
345 346 {
346 347 // Each component associated to the variable :
347 348 // - is removed from qcpplot (which deletes it)
348 349 // - is no longer referenced in the map
349 350 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
350 351 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
351 352 emit variableAboutToBeRemoved(variable);
352 353
353 354 auto &plottablesMap = variableIt->second;
354 355
355 356 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
356 357 plottableIt != plottableEnd;) {
357 358 ui->widget->removePlottable(plottableIt->second);
358 359 plottableIt = plottablesMap.erase(plottableIt);
359 360 }
360 361
361 362 impl->m_VariableToPlotMultiMap.erase(variableIt);
362 363 }
363 364
364 365 // Updates graph
365 366 ui->widget->replot();
366 367 }
367 368
368 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
369 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
369 370 {
370 auto variables = QList<std::shared_ptr<Variable> >{};
371 auto variables = std::vector<std::shared_ptr<Variable> >{};
371 372 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
372 373 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
373 variables << it->first;
374 variables.push_back (it->first);
374 375 }
375 376
376 377 return variables;
377 378 }
378 379
379 380 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
380 381 {
381 382 if (!variable) {
382 383 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
383 384 return;
384 385 }
385 386
386 387 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
387 388 }
388 389
389 390 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
390 391 {
391 392 auto graphRange = ui->widget->xAxis->range();
392 393 return DateTimeRange{graphRange.lower, graphRange.upper};
393 394 }
394 395
395 396 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration)
396 397 {
397 398 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
398 399
399 400 if (calibration) {
400 401 impl->m_IsCalibration = true;
401 402 }
402 403
403 404 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
404 405 ui->widget->replot();
405 406
406 407 if (calibration) {
407 408 impl->m_IsCalibration = false;
408 409 }
409 410
410 411 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
411 412 }
412 413
413 414 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
414 415 {
415 416 impl->m_VariableAutoRangeOnInit = value;
416 417 }
417 418
418 419 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
419 420 {
420 421 QVector<DateTimeRange> ranges;
421 422 for (auto zone : impl->m_SelectionZones) {
422 423 ranges << zone->range();
423 424 }
424 425
425 426 return ranges;
426 427 }
427 428
428 429 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
429 430 {
430 431 for (const auto &range : ranges) {
431 432 // note: ownership is transfered to QCustomPlot
432 433 auto zone = new VisualizationSelectionZoneItem(&plot());
433 434 zone->setRange(range.m_TStart, range.m_TEnd);
434 435 impl->addSelectionZone(zone);
435 436 }
436 437
437 438 plot().replot(QCustomPlot::rpQueuedReplot);
438 439 }
439 440
440 441 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
441 442 const DateTimeRange &range)
442 443 {
443 444 // note: ownership is transfered to QCustomPlot
444 445 auto zone = new VisualizationSelectionZoneItem(&plot());
445 446 zone->setName(name);
446 447 zone->setRange(range.m_TStart, range.m_TEnd);
447 448 impl->addSelectionZone(zone);
448 449
449 450 plot().replot(QCustomPlot::rpQueuedReplot);
450 451
451 452 return zone;
452 453 }
453 454
454 455 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
455 456 {
456 457 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
457 458
458 459 if (impl->m_HoveredZone == selectionZone) {
459 460 impl->m_HoveredZone = nullptr;
460 461 setCursor(Qt::ArrowCursor);
461 462 }
462 463
463 464 impl->m_SelectionZones.removeAll(selectionZone);
464 465 plot().removeItem(selectionZone);
465 466 plot().replot(QCustomPlot::rpQueuedReplot);
466 467 }
467 468
468 469 void VisualizationGraphWidget::undoZoom()
469 470 {
470 471 auto zoom = impl->m_ZoomStack.pop();
471 472 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
472 473 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
473 474
474 475 axisX->setRange(zoom.first);
475 476 axisY->setRange(zoom.second);
476 477
477 478 plot().replot(QCustomPlot::rpQueuedReplot);
478 479 }
479 480
480 481 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
481 482 {
482 483 if (visitor) {
483 484 visitor->visit(this);
484 485 }
485 486 else {
486 487 qCCritical(LOG_VisualizationGraphWidget())
487 488 << tr("Can't visit widget : the visitor is null");
488 489 }
489 490 }
490 491
491 492 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
492 493 {
493 494 auto isSpectrogram = [](const auto &variable) {
494 495 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
495 496 };
496 497
497 498 // - A spectrogram series can't be dropped on graph with existing plottables
498 499 // - No data series can be dropped on graph with existing spectrogram series
499 500 return isSpectrogram(variable)
500 501 ? impl->m_VariableToPlotMultiMap.empty()
501 502 : std::none_of(
502 503 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
503 504 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
504 505 }
505 506
506 507 bool VisualizationGraphWidget::contains(const Variable &variable) const
507 508 {
508 509 // Finds the variable among the keys of the map
509 510 auto variablePtr = &variable;
510 511 auto findVariable
511 512 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
512 513
513 514 auto end = impl->m_VariableToPlotMultiMap.cend();
514 515 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
515 516 return it != end;
516 517 }
517 518
518 519 QString VisualizationGraphWidget::name() const
519 520 {
520 521 return impl->m_Name;
521 522 }
522 523
523 524 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
524 525 {
525 526 auto mimeData = new QMimeData;
526 527
527 528 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
528 529 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
529 530 && selectionZoneItemUnderCursor) {
530 531 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
531 532 selectionZoneItemUnderCursor->range()));
532 533 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
533 534 selectionZoneItemUnderCursor->range()));
534 535 }
535 536 else {
536 537 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
537 538
538 539 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
539 540 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
540 541 }
541 542
542 543 return mimeData;
543 544 }
544 545
545 546 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
546 547 {
547 548 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
548 549 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
549 550 && selectionZoneItemUnderCursor) {
550 551
551 552 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
552 553 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
553 554
554 555 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
555 556 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
556 557 .toSize();
557 558
558 559 auto pixmap = QPixmap(zoneSize);
559 560 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
560 561
561 562 return pixmap;
562 563 }
563 564
564 565 return QPixmap();
565 566 }
566 567
567 568 bool VisualizationGraphWidget::isDragAllowed() const
568 569 {
569 570 return true;
570 571 }
571 572
572 573 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
573 574 {
574 575 if (highlighted) {
575 576 plot().setBackground(QBrush(QColor("#BBD5EE")));
576 577 }
577 578 else {
578 579 plot().setBackground(QBrush(Qt::white));
579 580 }
580 581
581 582 plot().update();
582 583 }
583 584
584 585 void VisualizationGraphWidget::addVerticalCursor(double time)
585 586 {
586 587 impl->m_VerticalCursor->setPosition(time);
587 588 impl->m_VerticalCursor->setVisible(true);
588 589
589 590 auto text
590 591 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
591 592 impl->m_VerticalCursor->setLabelText(text);
592 593 }
593 594
594 595 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
595 596 {
596 597 impl->m_VerticalCursor->setAbsolutePosition(position);
597 598 impl->m_VerticalCursor->setVisible(true);
598 599
599 600 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
600 601 auto text
601 602 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
602 603 impl->m_VerticalCursor->setLabelText(text);
603 604 }
604 605
605 606 void VisualizationGraphWidget::removeVerticalCursor()
606 607 {
607 608 impl->m_VerticalCursor->setVisible(false);
608 609 plot().replot(QCustomPlot::rpQueuedReplot);
609 610 }
610 611
611 612 void VisualizationGraphWidget::addHorizontalCursor(double value)
612 613 {
613 614 impl->m_HorizontalCursor->setPosition(value);
614 615 impl->m_HorizontalCursor->setVisible(true);
615 616 impl->m_HorizontalCursor->setLabelText(QString::number(value));
616 617 }
617 618
618 619 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
619 620 {
620 621 impl->m_HorizontalCursor->setAbsolutePosition(position);
621 622 impl->m_HorizontalCursor->setVisible(true);
622 623
623 624 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
624 625 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
625 626 }
626 627
627 628 void VisualizationGraphWidget::removeHorizontalCursor()
628 629 {
629 630 impl->m_HorizontalCursor->setVisible(false);
630 631 plot().replot(QCustomPlot::rpQueuedReplot);
631 632 }
632 633
633 634 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
634 635 {
635 636 Q_UNUSED(event);
636 637
637 638 for (auto i : impl->m_SelectionZones) {
638 639 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
639 640 }
640 641
641 642 // Prevents that all variables will be removed from graph when it will be closed
642 643 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
643 644 emit variableAboutToBeRemoved(variableEntry.first);
644 645 }
645 646 }
646 647
647 648 void VisualizationGraphWidget::enterEvent(QEvent *event)
648 649 {
649 650 Q_UNUSED(event);
650 651 impl->m_RenderingDelegate->showGraphOverlay(true);
651 652 }
652 653
653 654 void VisualizationGraphWidget::leaveEvent(QEvent *event)
654 655 {
655 656 Q_UNUSED(event);
656 657 impl->m_RenderingDelegate->showGraphOverlay(false);
657 658
658 659 if (auto parentZone = parentZoneWidget()) {
659 660 parentZone->notifyMouseLeaveGraph(this);
660 661 }
661 662 else {
662 663 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
663 664 }
664 665
665 666 if (impl->m_HoveredZone) {
666 667 impl->m_HoveredZone->setHovered(false);
667 668 impl->m_HoveredZone = nullptr;
668 669 }
669 670 }
670 671
671 672 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
672 673 {
673 674 return *ui->widget;
674 675 }
675 676
676 677 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
677 678 {
678 679 QMenu graphMenu{};
679 680
680 681 // Iterates on variables (unique keys)
681 682 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
682 683 end = impl->m_VariableToPlotMultiMap.cend();
683 684 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
684 685 // 'Remove variable' action
685 686 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
686 687 [ this, var = it->first ]() { removeVariable(var); });
687 688 }
688 689
689 690 if (!impl->m_ZoomStack.isEmpty()) {
690 691 if (!graphMenu.isEmpty()) {
691 692 graphMenu.addSeparator();
692 693 }
693 694
694 695 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
695 696 }
696 697
697 698 // Selection Zone Actions
698 699 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
699 700 if (selectionZoneItem) {
700 701 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
701 702 selectedItems.removeAll(selectionZoneItem);
702 703 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
703 704
704 705 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
705 706 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
706 707 graphMenu.addSeparator();
707 708 }
708 709
709 710 QHash<QString, QMenu *> subMenus;
710 711 QHash<QString, bool> subMenusEnabled;
711 712 QHash<QString, FilteringAction *> filteredMenu;
712 713
713 714 for (auto zoneAction : zoneActions) {
714 715
715 716 auto isEnabled = zoneAction->isEnabled(selectedItems);
716 717
717 718 auto menu = &graphMenu;
718 719 QString menuPath;
719 720 for (auto subMenuName : zoneAction->subMenuList()) {
720 721 menuPath += '/';
721 722 menuPath += subMenuName;
722 723
723 724 if (!subMenus.contains(menuPath)) {
724 725 menu = menu->addMenu(subMenuName);
725 726 subMenus[menuPath] = menu;
726 727 subMenusEnabled[menuPath] = isEnabled;
727 728 }
728 729 else {
729 730 menu = subMenus.value(menuPath);
730 731 if (isEnabled) {
731 732 // The sub menu is enabled if at least one of its actions is enabled
732 733 subMenusEnabled[menuPath] = true;
733 734 }
734 735 }
735 736 }
736 737
737 738 FilteringAction *filterAction = nullptr;
738 739 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
739 740 filterAction = filteredMenu.value(menuPath);
740 741 if (!filterAction) {
741 742 filterAction = new FilteringAction{this};
742 743 filteredMenu[menuPath] = filterAction;
743 744 menu->addAction(filterAction);
744 745 }
745 746 }
746 747
747 748 auto action = menu->addAction(zoneAction->name());
748 749 action->setEnabled(isEnabled);
749 750 action->setShortcut(zoneAction->displayedShortcut());
750 751 QObject::connect(action, &QAction::triggered,
751 752 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
752 753
753 754 if (filterAction && zoneAction->isFilteringAllowed()) {
754 755 filterAction->addActionToFilter(action);
755 756 }
756 757 }
757 758
758 759 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
759 760 it.value()->setEnabled(subMenusEnabled[it.key()]);
760 761 }
761 762 }
762 763
763 764 if (!graphMenu.isEmpty()) {
764 765 graphMenu.exec(QCursor::pos());
765 766 }
766 767 }
767 768
768 769 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
769 770 {
770 771 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
771 772 << QThread::currentThread()->objectName() << "DoAcqui"
772 773 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
773 774
774 775 auto graphRange = DateTimeRange{t1.lower, t1.upper};
775 776 auto oldGraphRange = DateTimeRange{t2.lower, t2.upper};
776 777
777 778 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
778 779 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
779 780
780 781 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
781 782 end = impl->m_VariableToPlotMultiMap.end();
782 783 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
783 784 variableUnderGraphVector.push_back(it->first);
784 785 }
785 786 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
786 787 !impl->m_IsCalibration);
787 788 }
788 789
789 790 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
790 791 qCDebug(LOG_VisualizationGraphWidget())
791 792 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
792 793 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
793 794 emit synchronize(graphRange, oldGraphRange);
794 795 }
795 796
796 797 auto pos = mapFromGlobal(QCursor::pos());
797 798 auto axisPos = impl->posToAxisPos(pos, plot());
798 799 if (auto parentZone = parentZoneWidget()) {
799 800 if (impl->pointIsInAxisRect(axisPos, plot())) {
800 801 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
801 802 }
802 803 else {
803 804 parentZone->notifyMouseLeaveGraph(this);
804 805 }
805 806 }
806 807 else {
807 808 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
808 809 }
809 810
810 811 // Quits calibration
811 812 impl->m_IsCalibration = false;
812 813 }
813 814
814 815 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
815 816 {
816 817 impl->m_RenderingDelegate->onMouseDoubleClick(event);
817 818 }
818 819
819 820 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
820 821 {
821 822 // Handles plot rendering when mouse is moving
822 823 impl->m_RenderingDelegate->onMouseMove(event);
823 824
824 825 auto axisPos = impl->posToAxisPos(event->pos(), plot());
825 826
826 827 // Zoom box and zone drawing
827 828 if (impl->m_DrawingZoomRect) {
828 829 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
829 830 }
830 831 else if (impl->m_DrawingZone) {
831 832 impl->m_DrawingZone->setEnd(axisPos.x());
832 833 }
833 834
834 835 // Cursor
835 836 if (auto parentZone = parentZoneWidget()) {
836 837 if (impl->pointIsInAxisRect(axisPos, plot())) {
837 838 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
838 839 }
839 840 else {
840 841 parentZone->notifyMouseLeaveGraph(this);
841 842 }
842 843 }
843 844 else {
844 845 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
845 846 }
846 847
847 848 // Search for the selection zone under the mouse
848 849 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
849 850 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
850 851 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
851 852
852 853 // Sets the appropriate cursor shape
853 854 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
854 855 setCursor(cursorShape);
855 856
856 857 // Manages the hovered zone
857 858 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
858 859 if (impl->m_HoveredZone) {
859 860 impl->m_HoveredZone->setHovered(false);
860 861 }
861 862 selectionZoneItemUnderCursor->setHovered(true);
862 863 impl->m_HoveredZone = selectionZoneItemUnderCursor;
863 864 plot().replot(QCustomPlot::rpQueuedReplot);
864 865 }
865 866 }
866 867 else {
867 868 // There is no zone under the mouse or the interaction mode is not "selection zones"
868 869 if (impl->m_HoveredZone) {
869 870 impl->m_HoveredZone->setHovered(false);
870 871 impl->m_HoveredZone = nullptr;
871 872 }
872 873
873 874 setCursor(Qt::ArrowCursor);
874 875 }
875 876
876 877 impl->m_HasMovedMouse = true;
877 878 VisualizationDragWidget::mouseMoveEvent(event);
878 879 }
879 880
880 881 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
881 882 {
882 883 // Processes event only if the wheel occurs on axis rect
883 884 if (!dynamic_cast<QCPAxisRect *>(ui->widget->layoutElementAt(event->posF()))) {
884 885 return;
885 886 }
886 887
887 888 auto value = event->angleDelta().x() + event->angleDelta().y();
888 889 if (value != 0) {
889 890
890 891 auto direction = value > 0 ? 1.0 : -1.0;
891 892 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
892 893 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
893 894 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
894 895
895 896 auto zoomOrientations = QFlags<Qt::Orientation>{};
896 897 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
897 898 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
898 899
899 900 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
900 901
901 902 if (!isZoomX && !isZoomY) {
902 903 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
903 904 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
904 905
905 906 axis->setRange(axis->range() + diff);
906 907
907 908 if (plot().noAntialiasingOnDrag()) {
908 909 plot().setNotAntialiasedElements(QCP::aeAll);
909 910 }
910 911
911 912 plot().replot(QCustomPlot::rpQueuedReplot);
912 913 }
913 914 }
914 915 }
915 916
916 917 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
917 918 {
918 919 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
919 920 auto isSelectionZoneMode
920 921 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
921 922 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
922 923
923 924 if (!isDragDropClick && isLeftClick) {
924 925 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
925 926 // Starts a zoom box
926 927 impl->startDrawingRect(event->pos(), plot());
927 928 }
928 929 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
929 930 // Starts a new selection zone
930 931 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
931 932 if (!zoneAtPos) {
932 933 impl->startDrawingZone(event->pos(), this);
933 934 }
934 935 }
935 936 }
936 937
937 938 // Allows mouse panning only in default mode
938 939 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
939 940 == SqpApplication::PlotsInteractionMode::None
940 941 && !isDragDropClick);
941 942
942 943 // Allows zone edition only in selection zone mode without drag&drop
943 944 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
944 945
945 946 // Selection / Deselection
946 947 if (isSelectionZoneMode) {
947 948 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
948 949 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
949 950
950 951
951 952 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
952 953 && !isMultiSelectionClick) {
953 954 parentVisualizationWidget()->selectionZoneManager().select(
954 955 {selectionZoneItemUnderCursor});
955 956 }
956 957 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
957 958 parentVisualizationWidget()->selectionZoneManager().clearSelection();
958 959 }
959 960 else {
960 961 // No selection change
961 962 }
962 963
963 964 if (selectionZoneItemUnderCursor && isLeftClick) {
964 965 selectionZoneItemUnderCursor->setAssociatedEditedZones(
965 966 parentVisualizationWidget()->selectionZoneManager().selectedItems());
966 967 }
967 968 }
968 969
969 970
970 971 impl->m_HasMovedMouse = false;
971 972 VisualizationDragWidget::mousePressEvent(event);
972 973 }
973 974
974 975 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
975 976 {
976 977 if (impl->m_DrawingZoomRect) {
977 978
978 979 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
979 980 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
980 981
981 982 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
982 983 impl->m_DrawingZoomRect->bottomRight->coords().x()};
983 984
984 985 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
985 986 impl->m_DrawingZoomRect->bottomRight->coords().y()};
986 987
987 988 impl->removeDrawingRect(plot());
988 989
989 990 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
990 991 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
991 992 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
992 993 axisX->setRange(newAxisXRange);
993 994 axisY->setRange(newAxisYRange);
994 995
995 996 plot().replot(QCustomPlot::rpQueuedReplot);
996 997 }
997 998 }
998 999
999 1000 impl->endDrawingZone(this);
1000 1001
1001 1002 // Selection / Deselection
1002 1003 auto isSelectionZoneMode
1003 1004 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1004 1005 if (isSelectionZoneMode) {
1005 1006 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1006 1007 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
1007 1008 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1008 1009 && !impl->m_HasMovedMouse) {
1009 1010
1010 1011 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1011 1012 if (zonesUnderCursor.count() > 1) {
1012 1013 // There are multiple zones under the mouse.
1013 1014 // Performs the selection with a selection dialog.
1014 1015 VisualizationMultiZoneSelectionDialog dialog{this};
1015 1016 dialog.setZones(zonesUnderCursor);
1016 1017 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1017 1018 dialog.activateWindow();
1018 1019 dialog.raise();
1019 1020 if (dialog.exec() == QDialog::Accepted) {
1020 1021 auto selection = dialog.selectedZones();
1021 1022
1022 1023 if (!isMultiSelectionClick) {
1023 1024 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1024 1025 }
1025 1026
1026 1027 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1027 1028 auto zone = it.key();
1028 1029 auto isSelected = it.value();
1029 1030 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1030 1031 isSelected);
1031 1032
1032 1033 if (isSelected) {
1033 1034 // Puts the zone on top of the stack so it can be moved or resized
1034 1035 impl->moveSelectionZoneOnTop(zone, plot());
1035 1036 }
1036 1037 }
1037 1038 }
1038 1039 }
1039 1040 else {
1040 1041 if (!isMultiSelectionClick) {
1041 1042 parentVisualizationWidget()->selectionZoneManager().select(
1042 1043 {selectionZoneItemUnderCursor});
1043 1044 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1044 1045 }
1045 1046 else {
1046 1047 parentVisualizationWidget()->selectionZoneManager().setSelected(
1047 1048 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1048 1049 || event->button() == Qt::RightButton);
1049 1050 }
1050 1051 }
1051 1052 }
1052 1053 else {
1053 1054 // No selection change
1054 1055 }
1055 1056 }
1056 1057 }
1057 1058
1058 1059 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1059 1060 {
1060 1061 auto graphRange = ui->widget->xAxis->range();
1061 1062 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1062 1063
1063 1064 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1064 1065 auto variable = variableEntry.first;
1065 1066 qCDebug(LOG_VisualizationGraphWidget())
1066 1067 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1067 1068 qCDebug(LOG_VisualizationGraphWidget())
1068 1069 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1069 1070 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1070 1071 impl->updateData(variableEntry.second, variable, variable->range());
1071 1072 }
1072 1073 }
1073 1074 }
1074 1075
1075 1076 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1076 1077 const DateTimeRange &range)
1077 1078 {
1078 1079 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1079 1080 if (it != impl->m_VariableToPlotMultiMap.end()) {
1080 1081 impl->updateData(it->second, variable, range);
1081 1082 }
1082 1083 }
@@ -1,389 +1,389
1 1 #include "Visualization/VisualizationTabWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "ui_VisualizationTabWidget.h"
4 4
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7
8 8 #include "Visualization/MacScrollBarStyle.h"
9 9
10 10 #include "DataSource/DataSourceController.h"
11 #include "Variable/VariableController.h"
11 #include "Variable/VariableController2.h"
12 12
13 13 #include "Common/MimeTypesDef.h"
14 14
15 15 #include "DragAndDrop/DragDropGuiController.h"
16 16 #include "SqpApplication.h"
17 17
18 18 Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget")
19 19
20 20 namespace {
21 21
22 22 /**
23 23 * Applies a function to all zones of the tab represented by its layout
24 24 * @param layout the layout that contains zones
25 25 * @param fun the function to apply to each zone
26 26 */
27 27 template <typename Fun>
28 28 void processZones(QLayout &layout, Fun fun)
29 29 {
30 30 for (auto i = 0; i < layout.count(); ++i) {
31 31 if (auto item = layout.itemAt(i)) {
32 32 if (auto visualizationZoneWidget
33 33 = qobject_cast<VisualizationZoneWidget *>(item->widget())) {
34 34 fun(*visualizationZoneWidget);
35 35 }
36 36 }
37 37 }
38 38 }
39 39
40 40 /// Generates a default name for a new zone, according to the number of zones already displayed in
41 41 /// the tab
42 42 QString defaultZoneName(QLayout &layout)
43 43 {
44 44 QSet<QString> existingNames;
45 45 processZones(layout,
46 46 [&existingNames](auto &zoneWidget) { existingNames.insert(zoneWidget.name()); });
47 47
48 48 int zoneNum = 1;
49 49 QString name;
50 50 do {
51 51 name = QObject::tr("Zone ").append(QString::number(zoneNum));
52 52 ++zoneNum;
53 53 } while (existingNames.contains(name));
54 54
55 55 return name;
56 56 }
57 57
58 58 } // namespace
59 59
60 60 struct VisualizationTabWidget::VisualizationTabWidgetPrivate {
61 61 explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {}
62 62
63 63 QString m_Name;
64 64
65 65 #ifdef Q_OS_MAC
66 66 std::unique_ptr<MacScrollBarStyle> m_MacScrollBarStyle = std::make_unique<MacScrollBarStyle>();
67 67 #endif
68 68
69 69 void dropGraph(int index, VisualizationTabWidget *tabWidget);
70 70 void dropZone(int index, VisualizationTabWidget *tabWidget);
71 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
71 void dropVariables(const std::vector<std::shared_ptr<Variable> > &variables, int index,
72 72 VisualizationTabWidget *tabWidget);
73 73 void dropProducts(const QVariantList &productsMetaData, int index,
74 74 VisualizationTabWidget *tabWidget);
75 75 };
76 76
77 77 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent)
78 78 : QWidget{parent},
79 79 ui{new Ui::VisualizationTabWidget},
80 80 impl{spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name)}
81 81 {
82 82 ui->setupUi(this);
83 83
84 84 #ifdef Q_OS_MAC
85 85 impl->m_MacScrollBarStyle->selfInstallOn(ui->scrollArea, true);
86 86 #endif
87 87
88 88 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Zone, "Zone");
89 89 ui->dragDropContainer->layout()->setContentsMargins(0, 0, 0, 12);
90 90 ui->dragDropContainer->layout()->setSpacing(0);
91 91 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
92 92 VisualizationDragDropContainer::DropBehavior::Inserted);
93 93 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
94 94 VisualizationDragDropContainer::DropBehavior::Inserted);
95 95 ui->dragDropContainer->setMimeType(MIME_TYPE_VARIABLE_LIST,
96 96 VisualizationDragDropContainer::DropBehavior::Inserted);
97 97 ui->dragDropContainer->setMimeType(MIME_TYPE_PRODUCT_LIST,
98 98 VisualizationDragDropContainer::DropBehavior::Inserted);
99 99
100 100 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
101 101 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
102 102 ui->dragDropContainer);
103 103 });
104 104
105 105 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
106 106 &VisualizationTabWidget::dropMimeData);
107 107
108 108 sqpApp->dragDropGuiController().addDragDropScrollArea(ui->scrollArea);
109 109
110 110 // Widget is deleted when closed
111 111 setAttribute(Qt::WA_DeleteOnClose);
112 112 }
113 113
114 114 VisualizationTabWidget::~VisualizationTabWidget()
115 115 {
116 116 sqpApp->dragDropGuiController().removeDragDropScrollArea(ui->scrollArea);
117 117 delete ui;
118 118 }
119 119
120 120 void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget)
121 121 {
122 122 ui->dragDropContainer->addDragWidget(zoneWidget);
123 123 }
124 124
125 125 void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget)
126 126 {
127 127 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
128 128 }
129 129
130 130 QStringList VisualizationTabWidget::availableZoneWidgets() const
131 131 {
132 132 QStringList zones;
133 133 processZones(tabLayout(),
134 134 [&zones](VisualizationZoneWidget &zoneWidget) { zones << zoneWidget.name(); });
135 135
136 136 return zones;
137 137 }
138 138
139 139 VisualizationZoneWidget *VisualizationTabWidget::getZoneWithName(const QString &zoneName)
140 140 {
141 141 VisualizationZoneWidget *result = nullptr;
142 142 processZones(tabLayout(), [&zoneName, &result](VisualizationZoneWidget &zoneWidget) {
143 143 if (!result && zoneWidget.name() == zoneName) {
144 144 result = &zoneWidget;
145 145 }
146 146 });
147 147
148 148 return result;
149 149 }
150 150
151 151 VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr<Variable> variable)
152 152 {
153 153 return createZone({variable}, -1);
154 154 }
155 155
156 156 VisualizationZoneWidget *
157 VisualizationTabWidget::createZone(const QList<std::shared_ptr<Variable> > &variables, int index)
157 VisualizationTabWidget::createZone(const std::vector<std::shared_ptr<Variable> > &variables, int index)
158 158 {
159 159 auto zoneWidget = createEmptyZone(index);
160 160
161 161 // Creates a new graph into the zone
162 162 zoneWidget->createGraph(variables, index);
163 163
164 164 return zoneWidget;
165 165 }
166 166
167 167 VisualizationZoneWidget *VisualizationTabWidget::createEmptyZone(int index)
168 168 {
169 169 auto zoneWidget
170 170 = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this};
171 171 this->insertZone(index, zoneWidget);
172 172
173 173 return zoneWidget;
174 174 }
175 175
176 176 void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor)
177 177 {
178 178 if (visitor) {
179 179 visitor->visitEnter(this);
180 180
181 181 // Apply visitor to zone children: widgets different from zones are not visited (no action)
182 182 processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) {
183 183 zoneWidget.accept(visitor);
184 184 });
185 185
186 186 visitor->visitLeave(this);
187 187 }
188 188 else {
189 189 qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null");
190 190 }
191 191 }
192 192
193 193 bool VisualizationTabWidget::canDrop(const Variable &variable) const
194 194 {
195 195 // A tab can always accomodate a variable
196 196 Q_UNUSED(variable);
197 197 return true;
198 198 }
199 199
200 200 bool VisualizationTabWidget::contains(const Variable &variable) const
201 201 {
202 202 Q_UNUSED(variable);
203 203 return false;
204 204 }
205 205
206 206 QString VisualizationTabWidget::name() const
207 207 {
208 208 return impl->m_Name;
209 209 }
210 210
211 211 void VisualizationTabWidget::closeEvent(QCloseEvent *event)
212 212 {
213 213 // Closes zones in the tab
214 214 processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); });
215 215
216 216 QWidget::closeEvent(event);
217 217 }
218 218
219 219 QLayout &VisualizationTabWidget::tabLayout() const noexcept
220 220 {
221 221 return *ui->dragDropContainer->layout();
222 222 }
223 223
224 224 void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData)
225 225 {
226 226 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
227 227 impl->dropGraph(index, this);
228 228 }
229 229 else if (mimeData->hasFormat(MIME_TYPE_ZONE)) {
230 230 impl->dropZone(index, this);
231 231 }
232 232 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
233 auto variables = sqpApp->variableController().variablesForMimeData(
233 auto variables = sqpApp->variableController().variables(
234 234 mimeData->data(MIME_TYPE_VARIABLE_LIST));
235 235 impl->dropVariables(variables, index, this);
236 236 }
237 237 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
238 238 auto productsData = sqpApp->dataSourceController().productsDataForMimeData(
239 239 mimeData->data(MIME_TYPE_PRODUCT_LIST));
240 240 impl->dropProducts(productsData, index, this);
241 241 }
242 242 else {
243 243 qCWarning(LOG_VisualizationZoneWidget())
244 244 << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received.");
245 245 }
246 246 }
247 247
248 248 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph(
249 249 int index, VisualizationTabWidget *tabWidget)
250 250 {
251 251 auto &helper = sqpApp->dragDropGuiController();
252 252
253 253 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
254 254 if (!graphWidget) {
255 255 qCWarning(LOG_VisualizationZoneWidget())
256 256 << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not "
257 257 "found or invalid.");
258 258 Q_ASSERT(false);
259 259 return;
260 260 }
261 261
262 262 auto parentDragDropContainer
263 263 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
264 264 if (!parentDragDropContainer) {
265 265 qCWarning(LOG_VisualizationZoneWidget())
266 266 << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of "
267 267 "the dropped graph is not found.");
268 268 Q_ASSERT(false);
269 269 return;
270 270 }
271 271
272 272 auto nbGraph = parentDragDropContainer->countDragWidget();
273 273
274 274 const auto &variables = graphWidget->variables();
275 275
276 if (!variables.isEmpty()) {
276 if (!variables.empty()) {
277 277 // Abort the requests for the variables (if any)
278 278 // Commented, because it's not sure if it's needed or not
279 279 // for (const auto& var : variables)
280 280 //{
281 281 // sqpApp->variableController().onAbortProgressRequested(var);
282 282 //}
283 283
284 284 if (nbGraph == 1) {
285 285 // This is the only graph in the previous zone, close the zone
286 286 helper.delayedCloseWidget(graphWidget->parentZoneWidget());
287 287 }
288 288 else {
289 289 // Close the graph
290 290 helper.delayedCloseWidget(graphWidget);
291 291 }
292 292
293 293 auto zoneWidget = tabWidget->createZone(variables, index);
294 294 auto firstGraph = zoneWidget->firstGraph();
295 295 if (firstGraph) {
296 296 firstGraph->addSelectionZones(graphWidget->selectionZoneRanges());
297 297 }
298 298 else {
299 299 qCWarning(LOG_VisualizationZoneWidget())
300 300 << tr("VisualizationTabWidget::dropGraph, no graph added in the widget.");
301 301 Q_ASSERT(false);
302 302 }
303 303 }
304 304 else {
305 305 // The graph is empty, create an empty zone and move the graph inside
306 306
307 307 auto parentZoneWidget = graphWidget->parentZoneWidget();
308 308
309 309 parentDragDropContainer->layout()->removeWidget(graphWidget);
310 310
311 311 auto zoneWidget = tabWidget->createEmptyZone(index);
312 312 zoneWidget->addGraph(graphWidget);
313 313
314 314 // Close the old zone if it was the only graph inside
315 315 if (nbGraph == 1) {
316 316 helper.delayedCloseWidget(parentZoneWidget);
317 317 }
318 318 }
319 319 }
320 320
321 321 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone(
322 322 int index, VisualizationTabWidget *tabWidget)
323 323 {
324 324 auto &helper = sqpApp->dragDropGuiController();
325 325
326 326 auto zoneWidget = qobject_cast<VisualizationZoneWidget *>(helper.getCurrentDragWidget());
327 327 if (!zoneWidget) {
328 328 qCWarning(LOG_VisualizationZoneWidget())
329 329 << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not "
330 330 "found or invalid.");
331 331 Q_ASSERT(false);
332 332 return;
333 333 }
334 334
335 335 auto parentDragDropContainer
336 336 = qobject_cast<VisualizationDragDropContainer *>(zoneWidget->parentWidget());
337 337 if (!parentDragDropContainer) {
338 338 qCWarning(LOG_VisualizationZoneWidget())
339 339 << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of "
340 340 "the dropped zone is not found.");
341 341 Q_ASSERT(false);
342 342 return;
343 343 }
344 344
345 345 // Simple move of the zone, no variable operation associated
346 346 parentDragDropContainer->layout()->removeWidget(zoneWidget);
347 347 tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget);
348 348 }
349 349
350 350 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables(
351 const QList<std::shared_ptr<Variable> > &variables, int index,
351 const std::vector<std::shared_ptr<Variable> > &variables, int index,
352 352 VisualizationTabWidget *tabWidget)
353 353 {
354 354 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
355 355 // compatible variable here
356 if (variables.count() > 1) {
356 if (variables.size() > 1) {
357 357 qCWarning(LOG_VisualizationZoneWidget())
358 358 << tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation "
359 359 "aborted.");
360 360 return;
361 361 }
362 362
363 363 tabWidget->createZone(variables, index);
364 364 }
365 365
366 366 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropProducts(
367 367 const QVariantList &productsMetaData, int index, VisualizationTabWidget *tabWidget)
368 368 {
369 369 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
370 370 // compatible variable here
371 371 if (productsMetaData.count() != 1) {
372 372 qCWarning(LOG_VisualizationZoneWidget())
373 373 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
374 374 "aborted.");
375 375 return;
376 376 }
377 377
378 378 auto context = new QObject{tabWidget};
379 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
379 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
380 380 [this, index, tabWidget, context](auto variable) {
381 381 tabWidget->createZone({variable}, index);
382 382 delete context; // removes the connection
383 383 },
384 384 Qt::QueuedConnection);
385 385
386 386 auto productData = productsMetaData.first().toHash();
387 387 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
388 388 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
389 389 }
@@ -1,659 +1,629
1 1 #include "Visualization/VisualizationZoneWidget.h"
2 2
3 3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 4 #include "Visualization/QCustomPlotSynchronizer.h"
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationWidget.h"
7 7 #include "ui_VisualizationZoneWidget.h"
8 8
9 9 #include "Common/MimeTypesDef.h"
10 10 #include "Common/VisualizationDef.h"
11 11
12 12 #include <Data/DateTimeRange.h>
13 #include <Data/DateTimeRangeHelper.h>
13 14 #include <DataSource/DataSourceController.h>
14 15 #include <Time/TimeController.h>
15 16 #include <Variable/Variable.h>
16 #include <Variable/VariableController.h>
17 #include <Variable/VariableController2.h>
17 18
18 19 #include <Visualization/operations/FindVariableOperation.h>
19 20
20 21 #include <DragAndDrop/DragDropGuiController.h>
21 22 #include <QUuid>
22 23 #include <SqpApplication.h>
23 24 #include <cmath>
24 25
25 26 #include <QLayout>
26 27 #include <QStyle>
27 28
28 29 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
29 30
30 31 namespace {
31 32
32 33 /**
33 34 * Applies a function to all graphs of the zone represented by its layout
34 35 * @param layout the layout that contains graphs
35 36 * @param fun the function to apply to each graph
36 37 */
37 38 template <typename Fun>
38 39 void processGraphs(QLayout &layout, Fun fun)
39 40 {
40 41 for (auto i = 0; i < layout.count(); ++i) {
41 42 if (auto item = layout.itemAt(i)) {
42 43 if (auto visualizationGraphWidget
43 44 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
44 45 fun(*visualizationGraphWidget);
45 46 }
46 47 }
47 48 }
48 49 }
49 50
50 51 /// Generates a default name for a new graph, according to the number of graphs already displayed in
51 52 /// the zone
52 53 QString defaultGraphName(QLayout &layout)
53 54 {
54 55 QSet<QString> existingNames;
55 56 processGraphs(
56 57 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
57 58
58 59 int zoneNum = 1;
59 60 QString name;
60 61 do {
61 62 name = QObject::tr("Graph ").append(QString::number(zoneNum));
62 63 ++zoneNum;
63 64 } while (existingNames.contains(name));
64 65
65 66 return name;
66 67 }
67 68
68 69 } // namespace
69 70
70 71 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
71 72
72 73 explicit VisualizationZoneWidgetPrivate()
73 74 : m_SynchronisationGroupId{QUuid::createUuid()},
74 75 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
75 76 {
76 77 }
77 78 QUuid m_SynchronisationGroupId;
78 79 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
79 80
80 81 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
81 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
82 void dropVariables(const std::vector<std::shared_ptr<Variable> > &variables, int index,
82 83 VisualizationZoneWidget *zoneWidget);
83 84 void dropProducts(const QVariantList &productsData, int index,
84 85 VisualizationZoneWidget *zoneWidget);
85 86 };
86 87
87 88 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
88 89 : VisualizationDragWidget{parent},
89 90 ui{new Ui::VisualizationZoneWidget},
90 91 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
91 92 {
92 93 ui->setupUi(this);
93 94
94 95 ui->zoneNameLabel->setText(name);
95 96
96 97 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
97 98 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
98 99 VisualizationDragDropContainer::DropBehavior::Inserted);
99 100 ui->dragDropContainer->setMimeType(
100 101 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
101 102 ui->dragDropContainer->setMimeType(
102 103 MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
103 104 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
104 105 VisualizationDragDropContainer::DropBehavior::Merged);
105 106 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
106 107 VisualizationDragDropContainer::DropBehavior::Forbidden);
107 108 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
108 109 VisualizationDragDropContainer::DropBehavior::Forbidden);
109 110 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
110 111 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
111 112 ui->dragDropContainer);
112 113 });
113 114
114 115 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
115 116 if (!mimeData) {
116 117 return false;
117 118 }
118 119
119 120 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
120 auto variables = sqpApp->variableController().variablesForMimeData(
121 auto variables = sqpApp->variableController().variables(
121 122 mimeData->data(MIME_TYPE_VARIABLE_LIST));
122 123
123 if (variables.count() != 1) {
124 if (variables.size() != 1) {
124 125 return false;
125 126 }
126 auto variable = variables.first();
127 auto variable = variables.front();
127 128
128 129 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
129 130 return graphWidget->canDrop(*variable);
130 131 }
131 132 }
132 133
133 134 return true;
134 135 };
135 136 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
136 137
137 138 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
138 139 &VisualizationZoneWidget::dropMimeData);
139 140 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
140 141 &VisualizationZoneWidget::dropMimeDataOnGraph);
141 142
142 143 // 'Close' options : widget is deleted when closed
143 144 setAttribute(Qt::WA_DeleteOnClose);
144 145 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
145 146 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
146 147
147 148 // Synchronisation id
148 149 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
149 150 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
150 151 }
151 152
152 153 VisualizationZoneWidget::~VisualizationZoneWidget()
153 154 {
154 155 delete ui;
155 156 }
156 157
157 158 void VisualizationZoneWidget::setZoneRange(const DateTimeRange &range)
158 159 {
159 160 if (auto graph = firstGraph()) {
160 161 graph->setGraphRange(range);
161 162 }
162 163 else {
163 164 qCWarning(LOG_VisualizationZoneWidget())
164 165 << tr("setZoneRange:Cannot set the range of an empty zone.");
165 166 }
166 167 }
167 168
168 169 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
169 170 {
170 171 // Synchronize new graph with others in the zone
171 172 impl->m_Synchronizer->addGraph(*graphWidget);
172 173
173 174 ui->dragDropContainer->addDragWidget(graphWidget);
174 175 }
175 176
176 177 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
177 178 {
178 179 // Synchronize new graph with others in the zone
179 180 impl->m_Synchronizer->addGraph(*graphWidget);
180 181
181 182 ui->dragDropContainer->insertDragWidget(index, graphWidget);
182 183 }
183 184
184 185 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
185 186 {
186 187 return createGraph(variable, -1);
187 188 }
188 189
189 190 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
190 191 int index)
191 192 {
192 193 auto graphWidget
193 194 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
194 195
195 196
196 197 // Set graph properties
197 198 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
198 199 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
199 200
200 201
201 202 // Lambda to synchronize zone widget
202 203 auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange,
203 204 const DateTimeRange &oldGraphRange) {
204 205
205 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
206 auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange);
206 207 auto frameLayout = ui->dragDropContainer->layout();
207 208 for (auto i = 0; i < frameLayout->count(); ++i) {
208 209 auto graphChild
209 210 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
210 211 if (graphChild && (graphChild != graphWidget)) {
211 212
212 213 auto graphChildRange = graphChild->graphRange();
213 214 switch (zoomType) {
214 case AcquisitionZoomType::ZoomIn: {
215 case TransformationType::ZoomIn: {
215 216 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
216 217 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
217 218 graphChildRange.m_TStart += deltaLeft;
218 219 graphChildRange.m_TEnd -= deltaRight;
219 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
220 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
221 << deltaLeft;
222 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
223 << deltaRight;
224 qCDebug(LOG_VisualizationZoneWidget())
225 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
226
227 220 break;
228 221 }
229 222
230 case AcquisitionZoomType::ZoomOut: {
231 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
223 case TransformationType::ZoomOut: {
232 224 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
233 225 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
234 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
235 << deltaLeft;
236 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
237 << deltaRight;
238 qCDebug(LOG_VisualizationZoneWidget())
239 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
240 226 graphChildRange.m_TStart -= deltaLeft;
241 227 graphChildRange.m_TEnd += deltaRight;
242 228 break;
243 229 }
244 case AcquisitionZoomType::PanRight: {
245 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
230 case TransformationType::PanRight: {
246 231 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
247 232 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
248 233 graphChildRange.m_TStart += deltaLeft;
249 234 graphChildRange.m_TEnd += deltaRight;
250 qCDebug(LOG_VisualizationZoneWidget())
251 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
252 235 break;
253 236 }
254 case AcquisitionZoomType::PanLeft: {
255 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
237 case TransformationType::PanLeft: {
256 238 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
257 239 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
258 240 graphChildRange.m_TStart -= deltaLeft;
259 241 graphChildRange.m_TEnd -= deltaRight;
260 242 break;
261 243 }
262 case AcquisitionZoomType::Unknown: {
263 qCDebug(LOG_VisualizationZoneWidget())
264 << tr("Impossible to synchronize: zoom type unknown");
244 case TransformationType::Unknown: {
265 245 break;
266 246 }
267 247 default:
268 248 qCCritical(LOG_VisualizationZoneWidget())
269 249 << tr("Impossible to synchronize: zoom type not take into account");
270 250 // No action
271 251 break;
272 252 }
273 253 graphChild->setFlags(GraphFlag::DisableAll);
274 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
275 << graphChild->graphRange();
276 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
277 << graphChildRange;
278 qCDebug(LOG_VisualizationZoneWidget())
279 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
280 254 graphChild->setGraphRange(graphChildRange);
281 255 graphChild->setFlags(GraphFlag::EnableAll);
282 256 }
283 257 }
284 258 };
285 259
286 260 // connection for synchronization
287 261 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
288 262 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
289 263 &VisualizationZoneWidget::onVariableAdded);
290 264 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
291 265 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
292 266
293 267 auto range = DateTimeRange{};
294 268 if (auto firstGraph = this->firstGraph()) {
295 269 // Case of a new graph in a existant zone
296 270 range = firstGraph->graphRange();
297 271 }
298 272 else {
299 273 // Case of a new graph as the first of the zone
300 274 range = variable->range();
301 275 }
302 276
303 277 this->insertGraph(index, graphWidget);
304 278
305 279 graphWidget->addVariable(variable, range);
306 280 graphWidget->setYRange(variable);
307 281
308 282 return graphWidget;
309 283 }
310 284
311 285 VisualizationGraphWidget *
312 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
286 VisualizationZoneWidget::createGraph(const std::vector<std::shared_ptr<Variable> > variables, int index)
313 287 {
314 if (variables.isEmpty()) {
288 if (variables.empty()) {
315 289 return nullptr;
316 290 }
317 291
318 auto graphWidget = createGraph(variables.first(), index);
292 auto graphWidget = createGraph(variables.front(), index);
319 293 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
320 294 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
321 295 }
322 296
323 297 return graphWidget;
324 298 }
325 299
326 300 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
327 301 {
328 302 VisualizationGraphWidget *firstGraph = nullptr;
329 303 auto layout = ui->dragDropContainer->layout();
330 304 if (layout->count() > 0) {
331 305 if (auto visualizationGraphWidget
332 306 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
333 307 firstGraph = visualizationGraphWidget;
334 308 }
335 309 }
336 310
337 311 return firstGraph;
338 312 }
339 313
340 314 void VisualizationZoneWidget::closeAllGraphs()
341 315 {
342 316 processGraphs(*ui->dragDropContainer->layout(),
343 317 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
344 318 }
345 319
346 320 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
347 321 {
348 322 if (visitor) {
349 323 visitor->visitEnter(this);
350 324
351 325 // Apply visitor to graph children: widgets different from graphs are not visited (no
352 326 // action)
353 327 processGraphs(
354 328 *ui->dragDropContainer->layout(),
355 329 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
356 330
357 331 visitor->visitLeave(this);
358 332 }
359 333 else {
360 334 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
361 335 }
362 336 }
363 337
364 338 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
365 339 {
366 340 // A tab can always accomodate a variable
367 341 Q_UNUSED(variable);
368 342 return true;
369 343 }
370 344
371 345 bool VisualizationZoneWidget::contains(const Variable &variable) const
372 346 {
373 347 Q_UNUSED(variable);
374 348 return false;
375 349 }
376 350
377 351 QString VisualizationZoneWidget::name() const
378 352 {
379 353 return ui->zoneNameLabel->text();
380 354 }
381 355
382 356 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
383 357 {
384 358 Q_UNUSED(position);
385 359
386 360 auto mimeData = new QMimeData;
387 361 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
388 362
389 363 if (auto firstGraph = this->firstGraph()) {
390 364 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
391 365 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
392 366 }
393 367
394 368 return mimeData;
395 369 }
396 370
397 371 bool VisualizationZoneWidget::isDragAllowed() const
398 372 {
399 373 return true;
400 374 }
401 375
402 376 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
403 377 const QPointF &plotPosition,
404 378 VisualizationGraphWidget *graphWidget)
405 379 {
406 380 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
407 381 VisualizationGraphWidget &processedGraph) {
408 382
409 383 switch (sqpApp->plotsCursorMode()) {
410 384 case SqpApplication::PlotsCursorMode::Vertical:
411 385 processedGraph.removeHorizontalCursor();
412 386 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
413 387 break;
414 388 case SqpApplication::PlotsCursorMode::Temporal:
415 389 processedGraph.addVerticalCursor(plotPosition.x());
416 390 processedGraph.removeHorizontalCursor();
417 391 break;
418 392 case SqpApplication::PlotsCursorMode::Horizontal:
419 393 processedGraph.removeVerticalCursor();
420 394 if (&processedGraph == graphWidget) {
421 395 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
422 396 }
423 397 else {
424 398 processedGraph.removeHorizontalCursor();
425 399 }
426 400 break;
427 401 case SqpApplication::PlotsCursorMode::Cross:
428 402 if (&processedGraph == graphWidget) {
429 403 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
430 404 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
431 405 }
432 406 else {
433 407 processedGraph.removeHorizontalCursor();
434 408 processedGraph.removeVerticalCursor();
435 409 }
436 410 break;
437 411 case SqpApplication::PlotsCursorMode::NoCursor:
438 412 processedGraph.removeHorizontalCursor();
439 413 processedGraph.removeVerticalCursor();
440 414 break;
441 415 }
442 416
443 417
444 418 });
445 419 }
446 420
447 421 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
448 422 {
449 423 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
450 424 processedGraph.removeHorizontalCursor();
451 425 processedGraph.removeVerticalCursor();
452 426 });
453 427 }
454 428
455 429 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
456 430 {
457 431 // Closes graphs in the zone
458 432 processGraphs(*ui->dragDropContainer->layout(),
459 433 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
460 434
461 435 // Delete synchronization group from variable controller
462 436 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
463 437 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
464 438
465 439 QWidget::closeEvent(event);
466 440 }
467 441
468 442 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
469 443 {
470 444 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
471 445 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
472 446 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
473 447 }
474 448
475 449 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
476 450 {
477 451 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
478 452 Q_ARG(std::shared_ptr<Variable>, variable),
479 453 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
480 454 }
481 455
482 456 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
483 457 {
484 458 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
485 459 impl->dropGraph(index, this);
486 460 }
487 461 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
488 auto variables = sqpApp->variableController().variablesForMimeData(
462 auto variables = sqpApp->variableController().variables(
489 463 mimeData->data(MIME_TYPE_VARIABLE_LIST));
490 464 impl->dropVariables(variables, index, this);
491 465 }
492 466 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
493 467 auto products = sqpApp->dataSourceController().productsDataForMimeData(
494 468 mimeData->data(MIME_TYPE_PRODUCT_LIST));
495 469 impl->dropProducts(products, index, this);
496 470 }
497 471 else {
498 472 qCWarning(LOG_VisualizationZoneWidget())
499 473 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
500 474 }
501 475 }
502 476
503 477 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
504 478 const QMimeData *mimeData)
505 479 {
506 480 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
507 481 if (!graphWidget) {
508 482 qCWarning(LOG_VisualizationZoneWidget())
509 483 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
510 484 "drop aborted");
511 485 Q_ASSERT(false);
512 486 return;
513 487 }
514 488
515 489 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
516 auto variables = sqpApp->variableController().variablesForMimeData(
490 auto variables = sqpApp->variableController().variables(
517 491 mimeData->data(MIME_TYPE_VARIABLE_LIST));
518 492 for (const auto &var : variables) {
519 493 graphWidget->addVariable(var, graphWidget->graphRange());
520 494 }
521 495 }
522 496 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
523 497 auto products = sqpApp->dataSourceController().productsDataForMimeData(
524 498 mimeData->data(MIME_TYPE_PRODUCT_LIST));
525 499
526 500 auto context = new QObject{this};
527 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
501 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
528 502 [this, graphWidget, context](auto variable) {
529 503 graphWidget->addVariable(variable, graphWidget->graphRange());
530 504 delete context; // removes the connection
531 505 },
532 506 Qt::QueuedConnection);
533 507
534 508 auto productData = products.first().toHash();
535 509 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
536 510 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
537 511 }
538 512 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
539 513 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
540 514 graphWidget->setGraphRange(range);
541 515 }
542 516 else {
543 517 qCWarning(LOG_VisualizationZoneWidget())
544 518 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
545 519 }
546 520 }
547 521
548 522 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
549 523 int index, VisualizationZoneWidget *zoneWidget)
550 524 {
551 525 auto &helper = sqpApp->dragDropGuiController();
552 526
553 527 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
554 528 if (!graphWidget) {
555 529 qCWarning(LOG_VisualizationZoneWidget())
556 530 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
557 531 "found or invalid.");
558 532 Q_ASSERT(false);
559 533 return;
560 534 }
561 535
562 536 auto parentDragDropContainer
563 537 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
564 538 if (!parentDragDropContainer) {
565 539 qCWarning(LOG_VisualizationZoneWidget())
566 540 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
567 541 "the dropped graph is not found.");
568 542 Q_ASSERT(false);
569 543 return;
570 544 }
571 545
572 546 const auto &variables = graphWidget->variables();
573 547
574 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
548 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) {
575 549 // The drop didn't occur in the same zone
576 550
577 551 // Abort the requests for the variables (if any)
578 552 // Commented, because it's not sure if it's needed or not
579 553 // for (const auto& var : variables)
580 554 //{
581 555 // sqpApp->variableController().onAbortProgressRequested(var);
582 556 //}
583 557
584 558 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
585 559 auto nbGraph = parentDragDropContainer->countDragWidget();
586 560 if (nbGraph == 1) {
587 561 // This is the only graph in the previous zone, close the zone
588 562 helper.delayedCloseWidget(previousParentZoneWidget);
589 563 }
590 564 else {
591 565 // Close the graph
592 566 helper.delayedCloseWidget(graphWidget);
593 567 }
594 568
595 569 // Creates the new graph in the zone
596 570 auto newGraphWidget = zoneWidget->createGraph(variables, index);
597 571 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
598 572 }
599 573 else {
600 574 // The drop occurred in the same zone or the graph is empty
601 575 // Simple move of the graph, no variable operation associated
602 576 parentDragDropContainer->layout()->removeWidget(graphWidget);
603 577
604 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
578 if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
605 579 // The graph is empty and dropped in a different zone.
606 580 // Take the range of the first graph in the zone (if existing).
607 581 auto layout = zoneWidget->ui->dragDropContainer->layout();
608 582 if (layout->count() > 0) {
609 583 if (auto visualizationGraphWidget
610 584 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
611 585 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
612 586 }
613 587 }
614 588 }
615 589
616 590 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
617 591 }
618 592 }
619 593
620 594 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
621 const QList<std::shared_ptr<Variable> > &variables, int index,
595 const std::vector<std::shared_ptr<Variable> > &variables, int index,
622 596 VisualizationZoneWidget *zoneWidget)
623 597 {
624 598 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
625 599 // compatible variable here
626 if (variables.count() > 1) {
627 qCWarning(LOG_VisualizationZoneWidget())
628 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
629 "aborted.");
600 if (variables.size() > 1) {
630 601 return;
631 602 }
632
633 603 zoneWidget->createGraph(variables, index);
634 604 }
635 605
636 606 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
637 607 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
638 608 {
639 609 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
640 610 // compatible variable here
641 611 if (productsData.count() != 1) {
642 612 qCWarning(LOG_VisualizationZoneWidget())
643 613 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
644 614 "aborted.");
645 615 return;
646 616 }
647 617
648 618 auto context = new QObject{zoneWidget};
649 connect(&sqpApp->variableController(), &VariableController::variableAdded, context,
619 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
650 620 [this, index, zoneWidget, context](auto variable) {
651 621 zoneWidget->createGraph(variable, index);
652 622 delete context; // removes the connection
653 623 },
654 624 Qt::QueuedConnection);
655 625
656 626 auto productData = productsData.first().toHash();
657 627 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
658 628 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
659 629 }
@@ -1,126 +1,126
1 1 /*------------------------------------------------------------------------------
2 2 -- This file is a part of the SciQLOP Software
3 3 -- Copyright (C) 2018, Plasma Physics Laboratory - CNRS
4 4 --
5 5 -- This program is free software; you can redistribute it and/or modify
6 6 -- it under the terms of the GNU General Public License as published by
7 7 -- the Free Software Foundation; either version 2 of the License, or
8 8 -- (at your option) any later version.
9 9 --
10 10 -- This program is distributed in the hope that it will be useful,
11 11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 -- GNU General Public License for more details.
14 14 --
15 15 -- You should have received a copy of the GNU General Public License
16 16 -- along with this program; if not, write to the Free Software
17 17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 18 -------------------------------------------------------------------------------*/
19 19 /*-- Author : Alexis Jeandet
20 20 -- Mail : alexis.jeandet@member.fsf.org
21 21 ----------------------------------------------------------------------------*/
22 22 #include <string>
23 23 #include <sstream>
24 24 #include <memory>
25 25
26 26 #include <pybind11/pybind11.h>
27 27 #include <pybind11/operators.h>
28 28 #include <pybind11/embed.h>
29 29 #include <pybind11/numpy.h>
30 30 #include <pybind11/chrono.h>
31 31
32 32 #include <SqpApplication.h>
33 #include <Variable/VariableController.h>
33 #include <Variable/VariableController2.h>
34 34 #include <Time/TimeController.h>
35 35 #include <Data/DateTimeRange.h>
36 36 #include <Data/DataSeriesType.h>
37 37 #include <Common/DateUtils.h>
38 38 #include <Variable/Variable.h>
39 39 #include <Data/ScalarSeries.h>
40 40 #include <Data/VectorSeries.h>
41 41
42 42 #include <AmdaProvider.h>
43 43 #include <AmdaResultParser.h>
44 44
45 45 //#include <QDate>
46 46 //#include <QTime>
47 47 //#include <QUuid>
48 48 //#include <QString>
49 49 #include <QFile>
50 50
51 51 #include <pywrappers_common.h>
52 52 #include <CoreWrappers.h>
53 53
54 54 #include "PyTestAmdaWrapper.h"
55 55
56 56
57 57 using namespace std::chrono;
58 58
59 59
60 60
61 61
62 62 PYBIND11_MODULE(pytestamda, m){
63 63
64 64 int argc = 0;
65 65 char ** argv=nullptr;
66 66 SqpApplication::setOrganizationName("LPP");
67 67 SqpApplication::setOrganizationDomain("lpp.fr");
68 68 SqpApplication::setApplicationName("SciQLop");
69 69 static SqpApplication app(argc, argv);
70 70
71 71 auto qtmod = py::module::import("sciqlopqt");
72 72 auto sciqlopmod = py::module::import("pysciqlopcore");
73 73
74 74 m.doc() = "hello";
75 75
76 py::class_<VariableController>(m, "VariableController")
77 .def_static("createVariable",[](const QString &name,
78 std::shared_ptr<IDataProvider> provider, const DateTimeRange& range){
79 return sqpApp->variableController().createVariable(name, {{"dataType", "vector"}, {"xml:id", "c1_b"}}, provider, range);
80 })
81 .def_static("hasPendingDownloads",
82 [](){return sqpApp->variableController().hasPendingDownloads();}
83 )
84 .def_static("addSynchronizationGroup",
85 [](QUuid uuid){sqpApp->variableController().onAddSynchronizationGroupId(uuid);}
86 )
87 .def_static("removeSynchronizationGroup",
88 [](QUuid uuid){sqpApp->variableController().onRemoveSynchronizationGroupId(uuid);}
89 )
90 .def_static("synchronizeVar",
91 [](std::shared_ptr<Variable> variable, QUuid uuid){sqpApp->variableController().onAddSynchronized(variable, uuid);}
92 )
93 .def_static("deSynchronizeVar",
94 [](std::shared_ptr<Variable> variable, QUuid uuid){sqpApp->variableController().desynchronize(variable, uuid);}
95 )
96 .def_static("deleteVariable",
97 [](std::shared_ptr<Variable> variable){
98 sqpApp->variableController().deleteVariable(variable);}
99 )
100 .def_static("update_range",[](std::shared_ptr<Variable> variable, const DateTimeRange &range, bool synchronise){
101 sqpApp->variableController().onRequestDataLoading({variable}, range, synchronise);
102 })
103 .def_static("wait_for_downloads",[](){
104 while (sqpApp->variableController().hasPendingDownloads()) {
105 usleep(100);
106 }
107 });
76 // py::class_<VariableController>(m, "VariableController")
77 // .def_static("createVariable",[](const QString &name,
78 // std::shared_ptr<IDataProvider> provider, const DateTimeRange& range){
79 // return sqpApp->variableController().createVariable(name, {{"dataType", "vector"}, {"xml:id", "c1_b"}}, provider, range);
80 // })
81 // .def_static("hasPendingDownloads",
82 // [](){return sqpApp->variableController().hasPendingDownloads();}
83 // )
84 // .def_static("addSynchronizationGroup",
85 // [](QUuid uuid){sqpApp->variableController().onAddSynchronizationGroupId(uuid);}
86 // )
87 // .def_static("removeSynchronizationGroup",
88 // [](QUuid uuid){sqpApp->variableController().onRemoveSynchronizationGroupId(uuid);}
89 // )
90 // .def_static("synchronizeVar",
91 // [](std::shared_ptr<Variable> variable, QUuid uuid){sqpApp->variableController().onAddSynchronized(variable, uuid);}
92 // )
93 // .def_static("deSynchronizeVar",
94 // [](std::shared_ptr<Variable> variable, QUuid uuid){sqpApp->variableController().desynchronize(variable, uuid);}
95 // )
96 // .def_static("deleteVariable",
97 // [](std::shared_ptr<Variable> variable){
98 // sqpApp->variableController().deleteVariable(variable);}
99 // )
100 // .def_static("update_range",[](std::shared_ptr<Variable> variable, const DateTimeRange &range, bool synchronise){
101 // sqpApp->variableController().onRequestDataLoading({variable}, range, synchronise);
102 // })
103 // .def_static("wait_for_downloads",[](){
104 // while (sqpApp->variableController().hasPendingDownloads()) {
105 // usleep(100);
106 // }
107 // });
108 108
109 109 py::class_<TimeController>(m,"TimeController")
110 110 .def_static("setTime", [](DateTimeRange range){sqpApp->timeController().setDateTimeRange(range);});
111 111
112 112
113 113 auto amda_provider = std::make_shared<AmdaProvider>();
114 114 m.def("amda_provider",[amda_provider](){return amda_provider;}, py::return_value_policy::copy);
115 115
116 116 py::class_<AmdaProvider, std::shared_ptr<AmdaProvider>, IDataProvider>(m, "AmdaProvider");
117 117
118 118 py::class_<AmdaResultParser>(m, "AmdaResultParser")
119 119 .def_static("readTxt", AmdaResultParser::readTxt)
120 120 .def("readScalarTxt", [](const QString& path){
121 121 return std::dynamic_pointer_cast<ScalarSeries>(AmdaResultParser::readTxt(path, DataSeriesType::SCALAR));
122 122 }, py::return_value_policy::copy);
123 123
124 124
125 125 }
126 126
@@ -1,164 +1,164
1 1 #include "AmdaProvider.h"
2 2 #include "AmdaResultParser.h"
3 3
4 4 #include "SqpApplication.h"
5 5 #include <Data/DataSeries.h>
6 6 #include <Data/IDataSeries.h>
7 7 #include <Data/ScalarSeries.h>
8 8 #include <Time/TimeController.h>
9 9 #include <Variable/Variable.h>
10 #include <Variable/VariableController.h>
10 #include <Variable/VariableController2.h>
11 11
12 12 #include <QObject>
13 13 #include <QtTest>
14 14
15 15 #include <memory>
16 16
17 17 // TEST with REF:
18 18 // AmdaData-2012-01-01-12-00-00_2012-01-03-12-00-00
19 19 // imf(0) - Type : Local Parameter @ CDPP/AMDA -
20 20 // Name : bx_gse - Units : nT - Size : 1 -
21 21 // Frame : GSE - Mission : ACE -
22 22 // Instrument : MFI - Dataset : mfi_final-prelim
23 23 // REFERENCE DOWNLOAD FILE =
24 24 // http://amdatest.irap.omp.eu/php/rest/getParameter.php?startTime=2012-01-01T12:00:00&stopTime=2012-01-03T12:00:00&parameterID=imf(0)&outputFormat=ASCII&timeFormat=ISO8601&gzip=0
25 25
26 26 namespace {
27 27
28 28 /// Path for the tests
29 29 const auto TESTS_RESOURCES_PATH
30 30 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaAcquisition"}.absoluteFilePath();
31 31
32 32 /// Delay after each operation on the variable before validating it (in ms)
33 33 const auto OPERATION_DELAY = 10000;
34 34
35 35 template <typename T>
36 36 bool compareDataSeries(std::shared_ptr<IDataSeries> candidate, DateTimeRange candidateCacheRange,
37 37 std::shared_ptr<IDataSeries> reference)
38 38 {
39 39 auto compareLambda = [](const auto &it1, const auto &it2) {
40 40 return (it1.x() == it2.x()) && (it1.value() == it2.value());
41 41 };
42 42
43 43 auto candidateDS = std::dynamic_pointer_cast<T>(candidate);
44 44 auto referenceDS = std::dynamic_pointer_cast<T>(reference);
45 45
46 46 if (candidateDS && referenceDS) {
47 47
48 48 auto itRefs
49 49 = referenceDS->xAxisRange(candidateCacheRange.m_TStart, candidateCacheRange.m_TEnd);
50 50 qDebug() << " DISTANCE" << std::distance(candidateDS->cbegin(), candidateDS->cend())
51 51 << std::distance(itRefs.first, itRefs.second);
52 52
53 53 return std::equal(candidateDS->cbegin(), candidateDS->cend(), itRefs.first, itRefs.second,
54 54 compareLambda);
55 55 }
56 56 else {
57 57 return false;
58 58 }
59 59 }
60 60 }
61 61
62 62 class TestAmdaAcquisition : public QObject {
63 63 Q_OBJECT
64 64
65 65 private slots:
66 66 /// Input data for @sa testAcquisition()
67 67 void testAcquisition_data();
68 68 void testAcquisition();
69 69 };
70 70
71 71 void TestAmdaAcquisition::testAcquisition_data()
72 72 {
73 73 // ////////////// //
74 74 // Test structure //
75 75 // ////////////// //
76 76
77 77 QTest::addColumn<QString>("dataFilename"); // File containing expected data of acquisitions
78 78 QTest::addColumn<DateTimeRange>("initialRange"); // First acquisition
79 79 QTest::addColumn<std::vector<DateTimeRange> >("operations"); // Acquisitions to make
80 80
81 81 // ////////// //
82 82 // Test cases //
83 83 // ////////// //
84 84
85 85 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
86 86 return DateUtils::secondsSinceEpoch(
87 87 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
88 88 };
89 89
90 90
91 91 QTest::newRow("amda")
92 92 << "AmdaData-2012-01-01-12-00-00_2012-01-03-12-00-00.txt"
93 93 << DateTimeRange{dateTime(2012, 1, 2, 2, 3, 0), dateTime(2012, 1, 2, 2, 4, 0)}
94 94 << std::vector<DateTimeRange>{
95 95 // 2 : pan (jump) left for two min
96 96 DateTimeRange{dateTime(2012, 1, 2, 2, 1, 0), dateTime(2012, 1, 2, 2, 2, 0)},
97 97 // 3 : pan (jump) right for four min
98 98 DateTimeRange{dateTime(2012, 1, 2, 2, 5, 0), dateTime(2012, 1, 2, 2, 6, 0)},
99 99 // 4 : pan (overlay) right for 30 sec
100 100 /*SqpRange{dateTime(2012, 1, 2, 2, 5, 30), dateTime(2012, 1, 2, 2, 6, 30)},
101 101 // 5 : pan (overlay) left for 30 sec
102 102 SqpRange{dateTime(2012, 1, 2, 2, 5, 0), dateTime(2012, 1, 2, 2, 6, 0)},
103 103 // 6 : pan (overlay) left for 30 sec - BIS
104 104 SqpRange{dateTime(2012, 1, 2, 2, 4, 30), dateTime(2012, 1, 2, 2, 5, 30)},
105 105 // 7 : Zoom in Inside 20 sec range
106 106 SqpRange{dateTime(2012, 1, 2, 2, 4, 50), dateTime(2012, 1, 2, 2, 5, 10)},
107 107 // 8 : Zoom out Inside 20 sec range
108 108 SqpRange{dateTime(2012, 1, 2, 2, 4, 30), dateTime(2012, 1, 2, 2, 5, 30)}*/};
109 109 }
110 110
111 111 void TestAmdaAcquisition::testAcquisition()
112 112 {
113 113 /// @todo: update test to be compatible with AMDA v2
114 114
115 115 // Retrieves data file
116 116 QFETCH(QString, dataFilename);
117 117 auto filePath = QFileInfo{TESTS_RESOURCES_PATH, dataFilename}.absoluteFilePath();
118 118 auto results = AmdaResultParser::readTxt(filePath, DataSeriesType::SCALAR);
119 119
120 120 /// Lambda used to validate a variable at each step
121 121 auto validateVariable = [results](std::shared_ptr<Variable> variable, const DateTimeRange &range) {
122 122 // Checks that the variable's range has changed
123 123 qInfo() << tr("Compare var range vs range") << variable->range() << range;
124 124 QCOMPARE(variable->range(), range);
125 125
126 126 // Checks the variable's data series
127 127 QVERIFY(compareDataSeries<ScalarSeries>(variable->dataSeries(), variable->cacheRange(),
128 128 results));
129 129 qInfo() << "\n";
130 130 };
131 131
132 132 // Creates variable
133 133 QFETCH(DateTimeRange, initialRange);
134 134 sqpApp->timeController().setDateTimeRange(initialRange);
135 135 auto provider = std::make_shared<AmdaProvider>();
136 136 auto variable = sqpApp->variableController().createVariable(
137 137 "bx_gse", {{"dataType", "scalar"}, {"xml:id", "imf(0)"}}, provider, initialRange);
138 138
139 139 QTest::qWait(OPERATION_DELAY);
140 140 validateVariable(variable, initialRange);
141 141
142 142 // Makes operations on the variable
143 143 QFETCH(std::vector<DateTimeRange>, operations);
144 144 for (const auto &operation : operations) {
145 145 // Asks request on the variable and waits during its execution
146 sqpApp->variableController().onRequestDataLoading({variable}, operation, false);
146 sqpApp->variableController().changeRange({variable}, operation);
147 147
148 148 QTest::qWait(OPERATION_DELAY);
149 149 validateVariable(variable, operation);
150 150 }
151 151 }
152 152
153 153 int main(int argc, char *argv[])
154 154 {
155 155 SqpApplication app(argc, argv);
156 156 app.setAttribute(Qt::AA_Use96Dpi, true);
157 157 TestAmdaAcquisition tc;
158 158 QTEST_SET_MAIN_SOURCE_PATH
159 159 return QTest::qExec(&tc, argc, argv);
160 160 }
161 161
162 162 // QTEST_MAIN(TestAmdaAcquisition)
163 163
164 164 #include "TestAmdaAcquisition.moc"
@@ -1,395 +1,395
1 1 #include "FuzzingDefs.h"
2 2 #include "FuzzingOperations.h"
3 3 #include "FuzzingUtils.h"
4 4 #include "FuzzingValidators.h"
5 5
6 6 #include "AmdaProvider.h"
7 7
8 8 #include <Common/SignalWaiter.h>
9 9 #include <Network/NetworkController.h>
10 10 #include <Settings/SqpSettingsDefs.h>
11 11 #include <SqpApplication.h>
12 12 #include <Time/TimeController.h>
13 13 #include <Variable/Variable.h>
14 #include <Variable/VariableController.h>
14 #include <Variable/VariableController2.h>
15 15
16 16 #include <QLoggingCategory>
17 17 #include <QObject>
18 18 #include <QtTest>
19 19
20 20 #include <memory>
21 21
22 22 Q_LOGGING_CATEGORY(LOG_TestAmdaFuzzing, "TestAmdaFuzzing")
23 23
24 24 /**
25 25 * Macro used to generate a getter for a property in @sa FuzzingTest. The macro generates a static
26 26 * attribute that is initialized by searching in properties the property and use a default value if
27 27 * it's not present. Macro arguments are:
28 28 * - GETTER_NAME : name of the getter
29 29 * - PROPERTY_NAME: used to generate constants for property's name ({PROPERTY_NAME}_PROPERTY) and
30 30 * default value ({PROPERTY_NAME}_DEFAULT_VALUE)
31 31 * - TYPE : return type of the getter
32 32 */
33 33 // clang-format off
34 34 #define DECLARE_PROPERTY_GETTER(GETTER_NAME, PROPERTY_NAME, TYPE) \
35 35 TYPE GETTER_NAME() const \
36 36 { \
37 37 static auto result = m_Properties.value(PROPERTY_NAME##_PROPERTY, PROPERTY_NAME##_DEFAULT_VALUE).value<TYPE>(); \
38 38 return result; \
39 39 } \
40 40 // clang-format on
41 41
42 42 namespace {
43 43
44 44 // /////// //
45 45 // Aliases //
46 46 // /////// //
47 47
48 48 using IntPair = std::pair<int, int>;
49 49 using Weight = double;
50 50 using Weights = std::vector<Weight>;
51 51
52 52 struct OperationProperty {
53 53 Weight m_Weight{1.};
54 54 bool m_WaitAcquisition{false};
55 55 };
56 56
57 57 using VariableOperation = std::pair<VariableId, std::shared_ptr<IFuzzingOperation> >;
58 58 using VariablesOperations = std::vector<VariableOperation>;
59 59
60 60 using OperationsTypes = std::map<FuzzingOperationType, OperationProperty>;
61 61 using OperationsPool = std::map<std::shared_ptr<IFuzzingOperation>, OperationProperty>;
62 62
63 63 using Validators = std::vector<std::shared_ptr<IFuzzingValidator> >;
64 64
65 65 // ///////// //
66 66 // Constants //
67 67 // ///////// //
68 68
69 69 // Defaults values used when the associated properties have not been set for the test
70 70 const auto ACQUISITION_TIMEOUT_DEFAULT_VALUE = 30000;
71 71 const auto NB_MAX_OPERATIONS_DEFAULT_VALUE = 100;
72 72 const auto NB_MAX_SYNC_GROUPS_DEFAULT_VALUE = 1;
73 73 const auto NB_MAX_VARIABLES_DEFAULT_VALUE = 1;
74 74 const auto AVAILABLE_OPERATIONS_DEFAULT_VALUE = QVariant::fromValue(
75 75 OperationsTypes{{FuzzingOperationType::CREATE, {1., true}},
76 76 {FuzzingOperationType::DELETE, {0.1}}, // Delete operation is less frequent
77 77 {FuzzingOperationType::PAN_LEFT, {1.}},
78 78 {FuzzingOperationType::PAN_RIGHT, {1.}},
79 79 {FuzzingOperationType::ZOOM_IN, {1.}},
80 80 {FuzzingOperationType::ZOOM_OUT, {1.}},
81 81 {FuzzingOperationType::SYNCHRONIZE, {0.8}},
82 82 {FuzzingOperationType::DESYNCHRONIZE, {0.4}}});
83 83 const auto CACHE_TOLERANCE_DEFAULT_VALUE = 0.2;
84 84
85 85 /// Min/max delays between each operation (in ms)
86 86 const auto OPERATION_DELAY_BOUNDS_DEFAULT_VALUE = QVariant::fromValue(std::make_pair(100, 3000));
87 87
88 88 /// Validators for the tests (executed in the order in which they're defined)
89 89 const auto VALIDATORS_DEFAULT_VALUE = QVariant::fromValue(
90 90 ValidatorsTypes{{FuzzingValidatorType::RANGE, FuzzingValidatorType::DATA}});
91 91
92 92 /// Min/max number of operations to execute before calling validation
93 93 const auto VALIDATION_FREQUENCY_BOUNDS_DEFAULT_VALUE = QVariant::fromValue(std::make_pair(1, 10));
94 94
95 95 // /////// //
96 96 // Methods //
97 97 // /////// //
98 98
99 99 /// Goes through the variables pool and operations pool to determine the set of {variable/operation}
100 100 /// pairs that are valid (i.e. operation that can be executed on variable)
101 101 std::pair<VariablesOperations, Weights> availableOperations(const FuzzingState &fuzzingState,
102 102 const OperationsPool &operationsPool)
103 103 {
104 104 VariablesOperations result{};
105 105 Weights weights{};
106 106
107 107 for (const auto &variablesPoolEntry : fuzzingState.m_VariablesPool) {
108 108 auto variableId = variablesPoolEntry.first;
109 109
110 110 for (const auto &operationsPoolEntry : operationsPool) {
111 111 auto operation = operationsPoolEntry.first;
112 112 auto operationProperty = operationsPoolEntry.second;
113 113
114 114 // A pair is valid if the current operation can be executed on the current variable
115 115 if (operation->canExecute(variableId, fuzzingState)) {
116 116 result.push_back({variableId, operation});
117 117 weights.push_back(operationProperty.m_Weight);
118 118 }
119 119 }
120 120 }
121 121
122 122 return {result, weights};
123 123 }
124 124
125 125 OperationsPool createOperationsPool(const OperationsTypes &types)
126 126 {
127 127 OperationsPool result{};
128 128
129 129 std::transform(
130 130 types.cbegin(), types.cend(), std::inserter(result, result.end()), [](const auto &type) {
131 131 return std::make_pair(FuzzingOperationFactory::create(type.first), type.second);
132 132 });
133 133
134 134 return result;
135 135 }
136 136
137 137 Validators createValidators(const ValidatorsTypes &types)
138 138 {
139 139 Validators result{};
140 140
141 141 std::transform(types.cbegin(), types.cend(), std::inserter(result, result.end()),
142 142 [](const auto &type) { return FuzzingValidatorFactory::create(type); });
143 143
144 144 return result;
145 145 }
146 146
147 147 /**
148 148 * Validates all the variables' states passed in parameter, according to a set of validators
149 149 * @param variablesPool the variables' states
150 150 * @param validators the validators used for validation
151 151 */
152 152 void validate(const VariablesPool &variablesPool, const Validators &validators)
153 153 {
154 154 for (const auto &variablesPoolEntry : variablesPool) {
155 155 auto variableId = variablesPoolEntry.first;
156 156 const auto &variableState = variablesPoolEntry.second;
157 157
158 158 auto variableMessage = variableState.m_Variable ? variableState.m_Variable->name()
159 159 : QStringLiteral("null variable");
160 160 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Validating state of variable at index"
161 161 << variableId << "(" << variableMessage << ")...";
162 162
163 163 for (const auto &validator : validators) {
164 164 validator->validate(VariableState{variableState});
165 165 }
166 166
167 167 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Validation completed.";
168 168 }
169 169 }
170 170
171 171 /**
172 172 * Class to run random tests
173 173 */
174 174 class FuzzingTest {
175 175 public:
176 explicit FuzzingTest(VariableController &variableController, Properties properties)
176 explicit FuzzingTest(VariableController2 &variableController, Properties properties)
177 177 : m_VariableController{variableController},
178 178 m_Properties{std::move(properties)},
179 179 m_FuzzingState{}
180 180 {
181 181 // Inits variables pool: at init, all variables are null
182 182 for (auto variableId = 0; variableId < nbMaxVariables(); ++variableId) {
183 183 m_FuzzingState.m_VariablesPool[variableId] = VariableState{};
184 184 }
185 185
186 186 // Inits sync groups and registers them into the variable controller
187 187 for (auto i = 0; i < nbMaxSyncGroups(); ++i) {
188 188 auto syncGroupId = SyncGroupId::createUuid();
189 variableController.onAddSynchronizationGroupId(syncGroupId);
189 //variableController.onAddSynchronizationGroupId(syncGroupId);
190 190 m_FuzzingState.m_SyncGroupsPool[syncGroupId] = SyncGroup{};
191 191 }
192 192 }
193 193
194 194 void execute()
195 195 {
196 196 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Running" << nbMaxOperations() << "operations on"
197 197 << nbMaxVariables() << "variable(s)...";
198 198
199 199
200 200 // Inits the count of the number of operations before the next validation
201 201 int nextValidationCounter = 0;
202 202 auto updateValidationCounter = [this, &nextValidationCounter]() {
203 203 nextValidationCounter = RandomGenerator::instance().generateInt(
204 204 validationFrequencies().first, validationFrequencies().second);
205 205 qCInfo(LOG_TestAmdaFuzzing()).noquote()
206 206 << "Next validation in " << nextValidationCounter << "operation(s)...";
207 207 };
208 208 updateValidationCounter();
209 209
210 210 auto canExecute = true;
211 211 for (auto i = 0; i < nbMaxOperations() && canExecute; ++i) {
212 212 // Retrieves all operations that can be executed in the current context
213 213 VariablesOperations variableOperations{};
214 214 Weights weights{};
215 215 std::tie(variableOperations, weights)
216 216 = availableOperations(m_FuzzingState, operationsPool());
217 217
218 218 canExecute = !variableOperations.empty();
219 219 if (canExecute) {
220 220 --nextValidationCounter;
221 221
222 222 // Of the operations available, chooses a random operation and executes it
223 223 auto variableOperation
224 224 = RandomGenerator::instance().randomChoice(variableOperations, weights);
225 225
226 226 auto variableId = variableOperation.first;
227 227 auto fuzzingOperation = variableOperation.second;
228 228
229 229 auto waitAcquisition = nextValidationCounter == 0
230 230 || operationsPool().at(fuzzingOperation).m_WaitAcquisition;
231 231
232 fuzzingOperation->execute(variableId, m_FuzzingState, m_VariableController,
233 m_Properties);
232 // fuzzingOperation->execute(variableId, m_FuzzingState, m_VariableController,
233 // m_Properties);
234 234
235 235 if (waitAcquisition) {
236 236 qCDebug(LOG_TestAmdaFuzzing()) << "Waiting for acquisition to finish...";
237 237 SignalWaiter{m_VariableController, SIGNAL(acquisitionFinished())}.wait(
238 238 acquisitionTimeout());
239 239
240 240 // Validates variables
241 241 if (nextValidationCounter == 0) {
242 242 validate(m_FuzzingState.m_VariablesPool, validators());
243 243 updateValidationCounter();
244 244 }
245 245 }
246 246 else {
247 247 // Delays the next operation with a randomly generated time
248 248 auto delay = RandomGenerator::instance().generateInt(operationDelays().first,
249 249 operationDelays().second);
250 250 qCDebug(LOG_TestAmdaFuzzing())
251 251 << "Waiting " << delay << "ms before the next operation...";
252 252 QTest::qWait(delay);
253 253 }
254 254 }
255 255 else {
256 256 qCInfo(LOG_TestAmdaFuzzing()).noquote()
257 257 << "No more operations are available, the execution of the test will stop...";
258 258 }
259 259 }
260 260
261 261 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Execution of the test completed.";
262 262 }
263 263
264 264 private:
265 265 OperationsPool operationsPool() const
266 266 {
267 267 static auto result = createOperationsPool(
268 268 m_Properties.value(AVAILABLE_OPERATIONS_PROPERTY, AVAILABLE_OPERATIONS_DEFAULT_VALUE)
269 269 .value<OperationsTypes>());
270 270 return result;
271 271 }
272 272
273 273 Validators validators() const
274 274 {
275 275 static auto result
276 276 = createValidators(m_Properties.value(VALIDATORS_PROPERTY, VALIDATORS_DEFAULT_VALUE)
277 277 .value<ValidatorsTypes>());
278 278 return result;
279 279 }
280 280
281 281 DECLARE_PROPERTY_GETTER(nbMaxOperations, NB_MAX_OPERATIONS, int)
282 282 DECLARE_PROPERTY_GETTER(nbMaxSyncGroups, NB_MAX_SYNC_GROUPS, int)
283 283 DECLARE_PROPERTY_GETTER(nbMaxVariables, NB_MAX_VARIABLES, int)
284 284 DECLARE_PROPERTY_GETTER(operationDelays, OPERATION_DELAY_BOUNDS, IntPair)
285 285 DECLARE_PROPERTY_GETTER(validationFrequencies, VALIDATION_FREQUENCY_BOUNDS, IntPair)
286 286 DECLARE_PROPERTY_GETTER(acquisitionTimeout, ACQUISITION_TIMEOUT, int)
287 287
288 VariableController &m_VariableController;
288 VariableController2 &m_VariableController;
289 289 Properties m_Properties;
290 290 FuzzingState m_FuzzingState;
291 291 };
292 292
293 293 } // namespace
294 294
295 295 Q_DECLARE_METATYPE(OperationsTypes)
296 296
297 297 class TestAmdaFuzzing : public QObject {
298 298 Q_OBJECT
299 299
300 300 private slots:
301 301 /// Input data for @sa testFuzzing()
302 302 void testFuzzing_data();
303 303 void testFuzzing();
304 304 };
305 305
306 306 void TestAmdaFuzzing::testFuzzing_data()
307 307 {
308 308 // Note: Comment this line to run fuzzing tests
309 309 QSKIP("Fuzzing tests are disabled by default");
310 310
311 311 // ////////////// //
312 312 // Test structure //
313 313 // ////////////// //
314 314
315 315 QTest::addColumn<Properties>("properties"); // Properties for random test
316 316
317 317 // ////////// //
318 318 // Test cases //
319 319 // ////////// //
320 320
321 321 auto maxRange = DateTimeRange::fromDateTime({2017, 1, 1}, {0, 0}, {2017, 1, 5}, {0, 0});
322 322 MetadataPool metadataPool{{{"dataType", "vector"}, {"xml:id", "imf"}}};
323 323
324 324 // Note: we don't use auto here as we want to pass std::shared_ptr<IDataProvider> as is in the
325 325 // QVariant
326 326 std::shared_ptr<IDataProvider> provider = std::make_shared<AmdaProvider>();
327 327
328 328 QTest::newRow("fuzzingTest") << Properties{
329 329 {MAX_RANGE_PROPERTY, QVariant::fromValue(maxRange)},
330 330 {METADATA_POOL_PROPERTY, QVariant::fromValue(metadataPool)},
331 331 {PROVIDER_PROPERTY, QVariant::fromValue(provider)}};
332 332 }
333 333
334 334 void TestAmdaFuzzing::testFuzzing()
335 335 {
336 336 QFETCH(Properties, properties);
337 337
338 338 // Sets cache property
339 339 QSettings settings{};
340 340 auto cacheTolerance = properties.value(CACHE_TOLERANCE_PROPERTY, CACHE_TOLERANCE_DEFAULT_VALUE);
341 341 settings.setValue(GENERAL_TOLERANCE_AT_INIT_KEY, cacheTolerance);
342 342 settings.setValue(GENERAL_TOLERANCE_AT_UPDATE_KEY, cacheTolerance);
343 343
344 344 auto &variableController = sqpApp->variableController();
345 345 auto &timeController = sqpApp->timeController();
346 346
347 347 // Generates random initial range (bounded to max range)
348 348 auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE))
349 349 .value<DateTimeRange>();
350 350
351 351 QVERIFY(maxRange != INVALID_RANGE);
352 352
353 353 auto initialRangeStart
354 354 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
355 355 auto initialRangeEnd
356 356 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
357 357 if (initialRangeStart > initialRangeEnd) {
358 358 std::swap(initialRangeStart, initialRangeEnd);
359 359 }
360 360
361 361 // Sets initial range on time controller
362 362 DateTimeRange initialRange{initialRangeStart, initialRangeEnd};
363 363 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Setting initial range to" << initialRange << "...";
364 364 timeController.setDateTimeRange(initialRange);
365 365 properties.insert(INITIAL_RANGE_PROPERTY, QVariant::fromValue(initialRange));
366 366
367 367 FuzzingTest test{variableController, properties};
368 368 test.execute();
369 369 }
370 370
371 371 int main(int argc, char *argv[])
372 372 {
373 373 // Increases the test function timeout (which is 5 minutes by default) to 12 hours
374 374 // https://stackoverflow.com/questions/42655932/setting-timeout-to-qt-test
375 375 qputenv("QTEST_FUNCTION_TIMEOUT", QByteArray::number(12*60*60*1000));
376 376
377 377 QLoggingCategory::setFilterRules(
378 378 "*.warning=false\n"
379 379 "*.info=false\n"
380 380 "*.debug=false\n"
381 381 "FuzzingOperations.info=true\n"
382 382 "FuzzingValidators.info=true\n"
383 383 "TestAmdaFuzzing.info=true\n");
384 384
385 385 SqpApplication app{argc, argv};
386 386 SqpApplication::setOrganizationName("LPP");
387 387 SqpApplication::setOrganizationDomain("lpp.fr");
388 388 SqpApplication::setApplicationName("SciQLop-TestFuzzing");
389 389 app.setAttribute(Qt::AA_Use96Dpi, true);
390 390 TestAmdaFuzzing testObject{};
391 391 QTEST_SET_MAIN_SOURCE_PATH
392 392 return QTest::qExec(&testObject, argc, argv);
393 393 }
394 394
395 395 #include "TestAmdaFuzzing.moc"
@@ -1,195 +1,195
1 1 #include "CosinusProvider.h"
2 2 #include "MockDefs.h"
3 3
4 4 #include <Data/DataProviderParameters.h>
5 5 #include <Data/ScalarSeries.h>
6 6 #include <SqpApplication.h>
7 7 #include <Time/TimeController.h>
8 8 #include <Variable/Variable.h>
9 #include <Variable/VariableController.h>
9 #include <Variable/VariableController2.h>
10 10
11 11 #include <QObject>
12 12 #include <QtTest>
13 13
14 14 #include <cmath>
15 15 #include <memory>
16 16
17 17 namespace {
18 18
19 19 /// Path for the tests
20 20 const auto TESTS_RESOURCES_PATH = QFileInfo{
21 21 QString{MOCKPLUGIN_TESTS_RESOURCES_DIR},
22 22 "TestCosinusAcquisition"}.absoluteFilePath();
23 23
24 24 /// Format of dates in data files
25 25 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
26 26
27 27 /**
28 28 * Verifies that the data in the candidate series are identical to the data in the reference series
29 29 * in a specific range
30 30 * @param candidate the candidate data series
31 31 * @param range the range to check
32 32 * @param reference the reference data series
33 33 * @return true if the data of the candidate series and the reference series are identical in the
34 34 * range, false otherwise
35 35 */
36 36 bool checkDataSeries(std::shared_ptr<IDataSeries> candidate, const DateTimeRange &range,
37 37 std::shared_ptr<IDataSeries> reference)
38 38 {
39 39 if (candidate == nullptr || reference == nullptr) {
40 40 return candidate == reference;
41 41 }
42 42
43 43 auto referenceIt = reference->xAxisRange(range.m_TStart, range.m_TEnd);
44 44
45 45 qInfo() << "candidateSize" << std::distance(candidate->cbegin(), candidate->cend());
46 46 qInfo() << "refSize" << std::distance(referenceIt.first, referenceIt.second);
47 47
48 48 return std::equal(candidate->cbegin(), candidate->cend(), referenceIt.first, referenceIt.second,
49 49 [](const auto &it1, const auto &it2) {
50 50 // - milliseconds precision for time
51 51 // - 1e-6 precision for value
52 52 return std::abs(it1.x() - it2.x()) < 1e-3
53 53 && std::abs(it1.value() - it2.value()) < 1e-6;
54 54 });
55 55 }
56 56
57 57 } // namespace
58 58
59 59 /**
60 60 * @brief The TestCosinusAcquisition class tests acquisition in SciQlop (operations like zooms in,
61 61 * zooms out, pans) of data from CosinusProvider
62 62 * @sa CosinusProvider
63 63 */
64 64 class TestCosinusAcquisition : public QObject {
65 65 Q_OBJECT
66 66
67 67 private slots:
68 68 /// Input data for @sa testAcquisition()
69 69 void testAcquisition_data();
70 70 void testAcquisition();
71 71 };
72 72
73 73 void TestCosinusAcquisition::testAcquisition_data()
74 74 {
75 75 // ////////////// //
76 76 // Test structure //
77 77 // ////////////// //
78 78
79 79 QTest::addColumn<DateTimeRange>("referenceRange"); // Range for generating reference series
80 80 QTest::addColumn<DateTimeRange>("initialRange"); // First acquisition
81 81 QTest::addColumn<int>("operationDelay"); // Acquisitions to make
82 82 QTest::addColumn<std::vector<DateTimeRange> >("operations"); // Acquisitions to make
83 83
84 84 // ////////// //
85 85 // Test cases //
86 86 // ////////// //
87 87
88 88 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
89 89 return DateUtils::secondsSinceEpoch(
90 90 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
91 91 };
92 92
93 93 QTest::newRow("cosinus")
94 94 << DateTimeRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)}
95 95 << DateTimeRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 1, 12, 35, 1)} << 250
96 96 << std::vector<DateTimeRange>{
97 97 // Pan (jump) left
98 98 DateTimeRange{dateTime(2017, 1, 1, 12, 45, 0), dateTime(2017, 1, 1, 12, 50, 0)},
99 99 // Pan (jump) right
100 100 DateTimeRange{dateTime(2017, 1, 1, 12, 15, 0), dateTime(2017, 1, 1, 12, 20, 0)},
101 101 // Pan (overlay) right
102 102 DateTimeRange{dateTime(2017, 1, 1, 12, 14, 0), dateTime(2017, 1, 1, 12, 19, 0)},
103 103 // Pan (overlay) left
104 104 DateTimeRange{dateTime(2017, 1, 1, 12, 15, 0), dateTime(2017, 1, 1, 12, 20, 0)},
105 105 // Pan (overlay) left
106 106 DateTimeRange{dateTime(2017, 1, 1, 12, 16, 0), dateTime(2017, 1, 1, 12, 21, 0)},
107 107 // Zoom in
108 108 DateTimeRange{dateTime(2017, 1, 1, 12, 17, 30), dateTime(2017, 1, 1, 12, 19, 30)},
109 109 // Zoom out
110 110 DateTimeRange{dateTime(2017, 1, 1, 12, 12, 30), dateTime(2017, 1, 1, 12, 24, 30)}};
111 111
112 112 QTest::newRow("cosinus_big")
113 113 << DateTimeRange{dateTime(2017, 1, 1, 1, 0, 0), dateTime(2017, 1, 5, 13, 0, 0)}
114 114 << DateTimeRange{dateTime(2017, 1, 2, 6, 30, 0), dateTime(2017, 1, 2, 18, 30, 0)} << 5000
115 115 << std::vector<DateTimeRange>{
116 116 // Pan (jump) left
117 117 DateTimeRange{dateTime(2017, 1, 1, 13, 30, 0), dateTime(2017, 1, 1, 18, 30, 0)},
118 118 // Pan (jump) right
119 119 DateTimeRange{dateTime(2017, 1, 3, 4, 30, 0), dateTime(2017, 1, 3, 10, 30, 0)},
120 120 // Pan (overlay) right
121 121 DateTimeRange{dateTime(2017, 1, 3, 8, 30, 0), dateTime(2017, 1, 3, 12, 30, 0)},
122 122 // Pan (overlay) left
123 123 DateTimeRange{dateTime(2017, 1, 2, 8, 30, 0), dateTime(2017, 1, 3, 10, 30, 0)},
124 124 // Pan (overlay) left
125 125 DateTimeRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 3, 5, 30, 0)},
126 126 // Zoom in
127 127 DateTimeRange{dateTime(2017, 1, 2, 2, 30, 0), dateTime(2017, 1, 2, 8, 30, 0)},
128 128 // Zoom out
129 129 DateTimeRange{dateTime(2017, 1, 1, 14, 30, 0), dateTime(2017, 1, 3, 12, 30, 0)}};
130 130 }
131 131
132 132 void TestCosinusAcquisition::testAcquisition()
133 133 {
134 134 // Retrieves reference range
135 135 QFETCH(DateTimeRange, referenceRange);
136 136 CosinusProvider referenceProvider{};
137 137 auto dataSeries = referenceProvider.provideDataSeries(
138 138 referenceRange, {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 10.}});
139 139
140 140 auto end = dataSeries->cend() - 1;
141 141 qInfo() << dataSeries->nbPoints() << dataSeries->cbegin()->x() << end->x();
142 142
143 143 /// Lambda used to validate a variable at each step
144 144 auto validateVariable
145 145 = [dataSeries](std::shared_ptr<Variable> variable, const DateTimeRange &range) {
146 146 // Checks that the variable's range has changed
147 147 qInfo() << "range vs expected range" << variable->range() << range;
148 148 QCOMPARE(variable->range(), range);
149 149
150 150 // Checks the variable's data series
151 151 QVERIFY(checkDataSeries(variable->dataSeries(), variable->cacheRange(), dataSeries));
152 152 };
153 153
154 154 // Creates variable
155 155 QFETCH(DateTimeRange, initialRange);
156 156 sqpApp->timeController().setDateTimeRange(initialRange);
157 157 auto provider = std::make_shared<CosinusProvider>();
158 158 auto variable = sqpApp->variableController().createVariable(
159 159 "MMS", {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 10.}}, provider, initialRange);
160 160
161 161
162 162 QFETCH(int, operationDelay);
163 163 QTest::qWait(operationDelay);
164 164 validateVariable(variable, initialRange);
165 165
166 166 QTest::qWait(operationDelay);
167 167 // Makes operations on the variable
168 168 QFETCH(std::vector<DateTimeRange>, operations);
169 169 for (const auto &operation : operations) {
170 170 // Asks request on the variable and waits during its execution
171 sqpApp->variableController().onRequestDataLoading({variable}, operation, false);
171 sqpApp->variableController().changeRange(variable, operation);
172 172
173 173 QTest::qWait(operationDelay);
174 174 validateVariable(variable, operation);
175 175 }
176 176
177 177
178 178 for (const auto &operation : operations) {
179 179 // Asks request on the variable and waits during its execution
180 sqpApp->variableController().onRequestDataLoading({variable}, operation, false);
180 sqpApp->variableController().changeRange(variable, operation);
181 181 }
182 182 QTest::qWait(operationDelay);
183 183 validateVariable(variable, operations.back());
184 184 }
185 185
186 186 int main(int argc, char *argv[])
187 187 {
188 188 SqpApplication app{argc, argv};
189 189 app.setAttribute(Qt::AA_Use96Dpi, true);
190 190 TestCosinusAcquisition testObject{};
191 191 QTEST_SET_MAIN_SOURCE_PATH
192 192 return QTest::qExec(&testObject, argc, argv);
193 193 }
194 194
195 195 #include "TestCosinusAcquisition.moc"
General Comments 0
You need to be logged in to leave comments. Login now