##// END OF EJS Templates
[WIP] new generic WS plugin and few other fixes...
jeandet -
r1352:fb8f470a1906
parent child
Show More
@@ -0,0 +1,25
1 include_directories(include)
2 FILE (GLOB_RECURSE genericWS_SRCS
3 include/*.h
4 src/*.cpp
5 resources/*.qrc
6 )
7
8 add_definitions(-DQT_PLUGIN)
9 add_definitions(-DSCIQLOP_PLUGIN_JSON_FILE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/genericWS.json")
10 if(NOT BUILD_SHARED_LIBS)
11 add_definitions(-DQT_STATICPLUGIN)
12 endif()
13
14 add_library(generic_ws ${genericWS_SRCS})
15 SET_TARGET_PROPERTIES(generic_ws PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
16
17 target_link_libraries(generic_ws PUBLIC sciqlopgui)
18
19 install(TARGETS generic_ws
20 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop
21 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop
22 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
23
24 include(sciqlop_tests)
25
@@ -0,0 +1,26
1 #ifndef GENERICWSENGINE_H
2 #define GENERICWSENGINE_H
3 #include <Data/IDataProvider.h>
4 #include <Data/DataProviderParameters.h>
5
6 class GenericWSEngine: public IDataProvider
7 {
8 public:
9 virtual std::shared_ptr<IDataProvider> clone() const override
10 {
11 return std::make_shared<GenericWSEngine>();
12 }
13
14 virtual IDataSeries* getData(const DataProviderParameters &parameters) override
15 {
16 auto range = parameters.m_Range;
17 auto metadata = parameters.m_Data;
18 auto WS = metadata["WS"].toString();
19 auto parameter = metadata["WS"].toString();
20 return nullptr;
21 }
22
23
24 };
25
26 #endif
@@ -0,0 +1,24
1 #ifndef GENERICWSPLUGIN_H
2 #define GENERICWSPLUGIN_H
3
4 #include <Plugin/IPlugin.h>
5
6
7 #include <memory>
8
9 #ifndef SCIQLOP_PLUGIN_JSON_FILE_PATH
10 #define SCIQLOP_PLUGIN_JSON_FILE_PATH "genericWS.json"
11 #endif
12
13 class DataSourceItem;
14
15 class GenericWS : public QObject, public IPlugin {
16 Q_OBJECT
17 Q_INTERFACES(IPlugin)
18 Q_PLUGIN_METADATA(IID "sciqlop.plugin.IPlugin" FILE SCIQLOP_PLUGIN_JSON_FILE_PATH)
19 public:
20 /// @sa IPlugin::initialize()
21 void initialize() override;
22 };
23
24 #endif // GENERICWSPLUGIN_H
@@ -0,0 +1,3
1 {
2 "name" : "GenericWS"
3 }
@@ -0,0 +1,1
1 #include "GenericWSEngine.h"
@@ -0,0 +1,1
1 #include "GenericWSEngine.h"
@@ -0,0 +1,1
1 Subproject commit f2fa281306099e7136a288dd70fd97d0e33afa15
@@ -1,11 +1,12
1 1 build/
2 2 CMakeLists.txt.user
3 3 /.project
4 4 core/src/Version.cpp
5 5 core/include/Version.h
6 6 3rdparty/
7 7 subprojects/CatalogueAPI/
8 8 subprojects/QxOrm/
9 9 documentation/*
10 .idea/*
10 **/.idea/*
11 11 **/__pycache__/*
12 *.srctrl*
@@ -1,60 +1,63
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 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})
50 find_package(SciQLOPCore CONFIG QUIET)
51 if (NOT SciQLOPCore_FOUND)
52 if(NOT IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/core)
53 message("Init submodule Core")
54 execute_process(COMMAND git submodule init core WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
55 execute_process(COMMAND git submodule update core WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
56 endif()
54 57 add_subdirectory(core)
55 58 endif()
56 59
57 60 add_subdirectory(gui)
58 61 add_subdirectory(app)
59 62 add_subdirectory(plugins)
60 63 add_subdirectory(docs)
@@ -1,1 +1,1
1 Subproject commit a05b0ab234936e28b6ba79535ccaee297d83a1e8
1 Subproject commit 6b6270b07547ddc94a57ecd2cdb54784412c1081
@@ -1,157 +1,160
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 #include <QUuid>
9 10
10 11 #include <memory>
11 12
12 13 #include <Common/spimpl.h>
13 14
14 15 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 16
16 17 class QCPRange;
17 18 class QCustomPlot;
18 19 class DateTimeRange;
19 20 class Variable;
20 21 class VisualizationWidget;
21 22 class VisualizationZoneWidget;
22 23 class VisualizationSelectionZoneItem;
23 24
24 25 namespace Ui {
25 26 class VisualizationGraphWidget;
26 27 } // namespace Ui
27 28
28 29 /// Defines options that can be associated with the graph
29 30 enum GraphFlag {
30 31 DisableAll = 0x0, ///< Disables acquisition and synchronization
31 32 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
32 33 /// the acquisition of data
33 34 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
34 35 /// the call to the synchronization of the graphs contained in the
35 36 /// same zone of this graph
36 37 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
37 38 };
38 39
39 40 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
40 41 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
41 42
42 43 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
43 44 Q_OBJECT
44 45
45 46 friend class QCustomPlotSynchronizer;
46 47 friend class VisualizationGraphRenderingDelegate;
47 48
48 49 public:
49 50 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
50 51 virtual ~VisualizationGraphWidget();
51 52
52 53 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
53 54 VisualizationZoneWidget *parentZoneWidget() const noexcept;
54 55
55 56 /// Returns the main VisualizationWidget which contains the graph or nullptr
56 57 VisualizationWidget *parentVisualizationWidget() const;
57 58
58 59 /// Sets graph options
59 60 void setFlags(GraphFlags flags);
60 61
61 62 void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range);
62 63
63 64 /// Removes a variable from the graph
64 65 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
65 66
66 67 /// Returns the list of all variables used in the graph
67 68 std::vector<std::shared_ptr<Variable> > variables() const;
68 69
69 70 /// Sets the y-axis range based on the data of a variable
70 71 void setYRange(std::shared_ptr<Variable> variable);
71 72 DateTimeRange graphRange() const noexcept;
72 73 void setGraphRange(const DateTimeRange &range, bool calibration = false);
73 74 void setAutoRangeOnVariableInitialization(bool value);
74 75
75 76 // Zones
76 77 /// Returns the ranges of all the selection zones on the graph
77 78 QVector<DateTimeRange> selectionZoneRanges() const;
78 79 /// Adds new selection zones in the graph
79 80 void addSelectionZones(const QVector<DateTimeRange> &ranges);
80 81 /// Adds a new selection zone in the graph
81 82 VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range);
82 83 /// Removes the specified selection zone
83 84 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
84 85
85 86 /// Undo the last zoom done with a zoom box
86 87 void undoZoom();
87 88
88 89 // IVisualizationWidget interface
89 90 void accept(IVisualizationWidgetVisitor *visitor) override;
90 91 bool canDrop(const Variable &variable) const override;
91 92 bool contains(const Variable &variable) const override;
92 93 QString name() const override;
93 94
94 95 // VisualisationDragWidget
95 96 QMimeData *mimeData(const QPoint &position) const override;
96 97 QPixmap customDragPixmap(const QPoint &dragPosition) override;
97 98 bool isDragAllowed() const override;
98 99 void highlightForMerge(bool highlighted) override;
99 100
100 101 // Cursors
101 102 /// Adds or moves the vertical cursor at the specified value on the x-axis
102 103 void addVerticalCursor(double time);
103 104 /// Adds or moves the vertical cursor at the specified value on the x-axis
104 105 void addVerticalCursorAtViewportPosition(double position);
105 106 void removeVerticalCursor();
106 107 /// Adds or moves the vertical cursor at the specified value on the y-axis
107 108 void addHorizontalCursor(double value);
108 109 /// Adds or moves the vertical cursor at the specified value on the y-axis
109 110 void addHorizontalCursorAtViewportPosition(double position);
110 111 void removeHorizontalCursor();
111 112
112 113 signals:
113 114 void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange);
114 115 void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange &range);
115 116
116 117 /// Signal emitted when the variable is about to be removed from the graph
117 118 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
118 119 /// Signal emitted when the variable has been added to the graph
119 120 void variableAdded(std::shared_ptr<Variable> var);
120 121
121 122 protected:
122 123 void closeEvent(QCloseEvent *event) override;
123 124 void enterEvent(QEvent *event) override;
124 125 void leaveEvent(QEvent *event) override;
125 126
126 127 QCustomPlot &plot() const noexcept;
127 128
128 129 private:
129 130 Ui::VisualizationGraphWidget *ui;
130 131
131 132 class VisualizationGraphWidgetPrivate;
132 133 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
133 134
134 135 private slots:
135 136 /// Slot called when right clicking on the graph (displays a menu)
136 137 void onGraphMenuRequested(const QPoint &pos) noexcept;
137 138
138 139 /// Rescale the X axe to range parameter
139 140 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
140 141
141 142 /// Slot called when a mouse double click was made
142 143 void onMouseDoubleClick(QMouseEvent *event) noexcept;
143 144 /// Slot called when a mouse move was made
144 145 void onMouseMove(QMouseEvent *event) noexcept;
145 146 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
146 147 void onMouseWheel(QWheelEvent *event) noexcept;
147 148 /// Slot called when a mouse press was made, to activate the calibration of a graph
148 149 void onMousePress(QMouseEvent *event) noexcept;
149 150 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
150 151 void onMouseRelease(QMouseEvent *event) noexcept;
151 152
152 153 void onDataCacheVariableUpdated();
153 154
154 155 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range);
156
157 void variableUpdated(QUuid id);
155 158 };
156 159
157 160 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,251 +1,248
1 1 #include <Variable/RenameVariableDialog.h>
2 2 #include <Variable/Variable.h>
3 3 #include <Variable/VariableController2.h>
4 4 #include <Variable/VariableInspectorWidget.h>
5 5 #include <Variable/VariableMenuHeaderWidget.h>
6 6 #include <Variable/VariableModel2.h>
7 7 #include <DataSource/DataSourceController.h>
8 8
9 9 #include <ui_VariableInspectorWidget.h>
10 10
11 11 #include <QMouseEvent>
12 12 #include <QSortFilterProxyModel>
13 13 #include <QStyledItemDelegate>
14 14 #include <QWidgetAction>
15 15
16 16 #include <DragAndDrop/DragDropGuiController.h>
17 17 #include <SqpApplication.h>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget")
20 20
21 21
22 22 class QProgressBarItemDelegate : public QStyledItemDelegate {
23 23
24 24 public:
25 25 QProgressBarItemDelegate(QObject *parent) : QStyledItemDelegate{parent} {}
26 26
27 27 void paint(QPainter *painter, const QStyleOptionViewItem &option,
28 28 const QModelIndex &index) const
29 29 {
30 30 auto data = index.data(Qt::DisplayRole);
31 31 auto progressData = index.data(VariableRoles::ProgressRole);
32 32 if (data.isValid() && progressData.isValid()) {
33 33 auto name = data.value<QString>();
34 34 auto progress = progressData.value<double>();
35 35 if (progress > 0) {
36 36 auto cancelButtonWidth = 20;
37 37 auto progressBarOption = QStyleOptionProgressBar{};
38 38 auto progressRect = option.rect;
39 39 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
40 40 progressBarOption.rect = progressRect;
41 41 progressBarOption.minimum = 0;
42 42 progressBarOption.maximum = 100;
43 43 progressBarOption.progress = progress;
44 44 progressBarOption.text
45 45 = QString("%1 %2").arg(name).arg(QString::number(progress, 'f', 2) + "%");
46 46 progressBarOption.textVisible = true;
47 47 progressBarOption.textAlignment = Qt::AlignCenter;
48 48
49 49
50 50 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption,
51 51 painter);
52 52
53 53 // Cancel button
54 54 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
55 55 option.rect.height());
56 56 auto buttonOption = QStyleOptionButton{};
57 57 buttonOption.rect = buttonRect;
58 58 buttonOption.text = "X";
59 59
60 60 QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter);
61 61 }
62 62 else {
63 63 QStyledItemDelegate::paint(painter, option, index);
64 64 }
65 65 }
66 66 else {
67 67 QStyledItemDelegate::paint(painter, option, index);
68 68 }
69 69 }
70 70
71 71 bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
72 72 const QModelIndex &index)
73 73 {
74 74 if (event->type() == QEvent::MouseButtonRelease) {
75 75 auto data = index.data(Qt::DisplayRole);
76 76 auto progressData = index.data(VariableRoles::ProgressRole);
77 77 if (data.isValid() && progressData.isValid()) {
78 78 auto cancelButtonWidth = 20;
79 79 auto progressRect = option.rect;
80 80 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
81 81 // Cancel button
82 82 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
83 83 option.rect.height());
84 84
85 85 auto e = (QMouseEvent *)event;
86 86 auto clickX = e->x();
87 87 auto clickY = e->y();
88 88
89 89 auto x = buttonRect.left(); // the X coordinate
90 90 auto y = buttonRect.top(); // the Y coordinate
91 91 auto w = buttonRect.width(); // button width
92 92 auto h = buttonRect.height(); // button height
93 93
94 94 if (clickX > x && clickX < x + w) {
95 95 if (clickY > y && clickY < y + h) {
96 96 //auto& variableModel = sqpApp->variableModel();
97 97 //variableModel->abortProgress(index);
98 98 }
99 99 return true;
100 100 }
101 101 else {
102 102 return QStyledItemDelegate::editorEvent(event, model, option, index);
103 103 }
104 104 }
105 105 else {
106 106 return QStyledItemDelegate::editorEvent(event, model, option, index);
107 107 }
108 108 }
109 109 else {
110 110 return QStyledItemDelegate::editorEvent(event, model, option, index);
111 111 }
112 112
113 113
114 114 return QStyledItemDelegate::editorEvent(event, model, option, index);
115 115 }
116 116 };
117 117
118 118 VariableInspectorWidget::VariableInspectorWidget(QWidget *parent)
119 119 : QWidget{parent},
120 120 ui{new Ui::VariableInspectorWidget},
121 121 m_ProgressBarItemDelegate{new QProgressBarItemDelegate{this}}
122 122 {
123 123 ui->setupUi(this);
124 124
125 125 // Sets model for table
126 126 // auto sortFilterModel = new QSortFilterProxyModel{this};
127 127 // sortFilterModel->setSourceModel(sqpApp->variableController().variableModel());
128 128
129 129 m_model = new VariableModel2();
130 130 ui->tableView->setModel(m_model);
131 131 connect(m_model, &VariableModel2::createVariable,
132 132 [](const QVariantHash &productData)
133 133 {
134 134 sqpApp->dataSourceController().requestVariable(productData);
135 135 });
136 136 auto vc = &(sqpApp->variableController());
137 137 connect(vc, &VariableController2::variableAdded, m_model, &VariableModel2::variableAdded);
138 138 connect(vc, &VariableController2::variableDeleted, m_model, &VariableModel2::variableDeleted);
139 139 connect(m_model, &VariableModel2::asyncChangeRange, vc, &VariableController2::asyncChangeRange);
140 140
141 141 // Adds extra signal/slot between view and model, so the view can be updated instantly when
142 142 // there is a change of data in the model
143 143 //connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
144 144 // SLOT(refresh()));
145 145
146 146 //ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel());
147 147 ui->tableView->setItemDelegateForColumn(0, m_ProgressBarItemDelegate);
148 148
149 149 // Fixes column sizes
150 150 auto model = ui->tableView->model();
151 151 const auto count = model->columnCount();
152 152 for (auto i = 0; i < count; ++i) {
153 153 ui->tableView->setColumnWidth(
154 154 i, model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).toSize().width());
155 155 }
156 156
157 157 // Sets selection options
158 158 ui->tableView->setSelectionBehavior(QTableView::SelectRows);
159 159 ui->tableView->setSelectionMode(QTableView::ExtendedSelection);
160 160
161 161 // Connection to show a menu when right clicking on the tree
162 162 ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
163 163 connect(ui->tableView, &QTableView::customContextMenuRequested, this,
164 164 &VariableInspectorWidget::onTableMenuRequested);
165 165 }
166 166
167 167 VariableInspectorWidget::~VariableInspectorWidget()
168 168 {
169 169 delete ui;
170 170 }
171 171
172 172 void VariableInspectorWidget::onTableMenuRequested(const QPoint &pos) noexcept
173 173 {
174 174 auto selectedRows = ui->tableView->selectionModel()->selectedRows();
175
176 // Gets the model to retrieve the underlying selected variables
177 auto& vc = sqpApp->variableController();
178 175 auto selectedVariables = QVector<std::shared_ptr<Variable> >{};
179 176 for (const auto &selectedRow : qAsConst(selectedRows)) {
180 if (auto selectedVariable = vc[selectedRow.row()]) {
177 if (auto selectedVariable = this->m_model->variables()[selectedRow.row()]) {
181 178 selectedVariables.push_back(selectedVariable);
182 179 }
183 180 }
184 181
185 182 QMenu tableMenu{};
186 183
187 184 // Emits a signal so that potential receivers can populate the menu before displaying it
188 185 emit tableMenuAboutToBeDisplayed(&tableMenu, selectedVariables);
189 186
190 187 // Adds menu-specific actions
191 188 if (!selectedVariables.isEmpty()) {
192 189 tableMenu.addSeparator();
193 190
194 191 // 'Rename' and 'Duplicate' actions (only if one variable selected)
195 192 if (selectedVariables.size() == 1) {
196 193 auto selectedVariable = selectedVariables.front();
197 194
198 195 auto duplicateFun = [varW = std::weak_ptr<Variable>(selectedVariable)]()
199 196 {
200 197 if (auto var = varW.lock()) {
201 198 sqpApp->variableController().cloneVariable(var);
202 199 }
203 200 };
204 201
205 202 tableMenu.addAction(tr("Duplicate"), duplicateFun);
206 203
207 204 auto renameFun = [ varW = std::weak_ptr<Variable>(selectedVariable), this ]()
208 205 {
209 206 if (auto var = varW.lock()) {
210 207 // Generates forbidden names (names associated to existing variables)
211 208 auto allVariables = sqpApp->variableController().variables();
212 209 auto forbiddenNames = QVector<QString>(allVariables.size());
213 210 std::transform(allVariables.cbegin(), allVariables.cend(),
214 211 forbiddenNames.begin(),
215 212 [](const auto &variable) { return variable->name(); });
216 213
217 214 RenameVariableDialog dialog{var->name(), forbiddenNames, this};
218 215 if (dialog.exec() == QDialog::Accepted) {
219 216 var->setName(dialog.name());
220 217 }
221 218 }
222 219 };
223 220
224 221 tableMenu.addAction(tr("Rename..."), renameFun);
225 222 }
226 223
227 224 // 'Delete' action
228 225 auto deleteFun = [&selectedVariables]() {
229 226 for(const auto& var:selectedVariables)
230 227 sqpApp->variableController().deleteVariable(var);
231 228 };
232 229
233 230 tableMenu.addAction(QIcon{":/icones/delete.png"}, tr("Delete"), deleteFun);
234 231 }
235 232
236 233 if (!tableMenu.isEmpty()) {
237 234 // Generates menu header (inserted before first action)
238 235 auto firstAction = tableMenu.actions().first();
239 236 auto headerAction = new QWidgetAction{&tableMenu};
240 237 headerAction->setDefaultWidget(new VariableMenuHeaderWidget{selectedVariables, &tableMenu});
241 238 tableMenu.insertAction(firstAction, headerAction);
242 239
243 240 // Displays menu
244 241 tableMenu.exec(QCursor::pos());
245 242 }
246 243 }
247 244
248 245 void VariableInspectorWidget::refresh() noexcept
249 246 {
250 247 ui->tableView->viewport()->update();
251 248 }
@@ -1,1042 +1,1051
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/VariableController2.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 256 //@TODO implement this :)
257 257 // connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
258 258 // &VariableController::onRequestDataLoading);
259 259
260 260 // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this,
261 261 // &VisualizationGraphWidget::onUpdateVarDisplaying);
262 262
263 263 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
264 264 plot().setPlottingHint(QCP::phFastPolylines, true);
265 265 }
266 266
267 267
268 268 VisualizationGraphWidget::~VisualizationGraphWidget()
269 269 {
270 270 delete ui;
271 271 }
272 272
273 273 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
274 274 {
275 275 auto parent = parentWidget();
276 276 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
277 277 parent = parent->parentWidget();
278 278 }
279 279
280 280 return qobject_cast<VisualizationZoneWidget *>(parent);
281 281 }
282 282
283 283 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
284 284 {
285 285 auto parent = parentWidget();
286 286 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
287 287 parent = parent->parentWidget();
288 288 }
289 289
290 290 return qobject_cast<VisualizationWidget *>(parent);
291 291 }
292 292
293 293 void VisualizationGraphWidget::setFlags(GraphFlags flags)
294 294 {
295 295 impl->m_Flags = std::move(flags);
296 296 }
297 297
298 298 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
299 299 {
300 300 // Uses delegate to create the qcpplot components according to the variable
301 301 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
302 302
303 303 // Sets graph properties
304 304 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
305 305
306 306 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
307 307
308 308 // If the variable already has its data loaded, load its units and its range in the graph
309 309 if (variable->dataSeries() != nullptr) {
310 310 impl->m_RenderingDelegate->setAxesUnits(*variable);
311 311 this->setFlags(GraphFlag::DisableAll);
312 312 setGraphRange(range);
313 313 this->setFlags(GraphFlag::EnableAll);
314 314 }
315 connect(variable.get(),&Variable::updated,
316 [variable=variable,this]()
317 {
318 QMetaObject::invokeMethod(this,[variable=variable,this](){this->onUpdateVarDisplaying(variable,this->graphRange());});
319 });
315 //@TODO this is bad! when variable is moved to another graph it still fires
316 // even if this has been deleted
317 connect(variable.get(),&Variable::updated,this, &VisualizationGraphWidget::variableUpdated);
320 318 this->onUpdateVarDisplaying(variable,range);//My bullshit
321 319 emit variableAdded(variable);
322 320 }
323 321
324 322 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
325 323 {
326 324 // Each component associated to the variable :
327 325 // - is removed from qcpplot (which deletes it)
328 326 // - is no longer referenced in the map
329 327 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
330 328 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
331 329 emit variableAboutToBeRemoved(variable);
332 330
333 331 auto &plottablesMap = variableIt->second;
334 332
335 333 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
336 334 plottableIt != plottableEnd;) {
337 335 ui->widget->removePlottable(plottableIt->second);
338 336 plottableIt = plottablesMap.erase(plottableIt);
339 337 }
340 338
341 339 impl->m_VariableToPlotMultiMap.erase(variableIt);
342 340 }
343 341
344 342 // Updates graph
345 343 ui->widget->replot();
346 344 }
347 345
348 346 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
349 347 {
350 348 auto variables = std::vector<std::shared_ptr<Variable> >{};
351 349 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
352 350 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
353 351 variables.push_back (it->first);
354 352 }
355 353
356 354 return variables;
357 355 }
358 356
359 357 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
360 358 {
361 359 if (!variable) {
362 360 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
363 361 return;
364 362 }
365 363
366 364 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
367 365 }
368 366
369 367 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
370 368 {
371 369 auto graphRange = ui->widget->xAxis->range();
372 370 return DateTimeRange{graphRange.lower, graphRange.upper};
373 371 }
374 372
375 373 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration)
376 374 {
377 375 if (calibration) {
378 376 impl->m_IsCalibration = true;
379 377 }
380 378
381 379 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
382 380 ui->widget->replot();
383 381
384 382 if (calibration) {
385 383 impl->m_IsCalibration = false;
386 384 }
387 385 }
388 386
389 387 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
390 388 {
391 389 impl->m_VariableAutoRangeOnInit = value;
392 390 }
393 391
394 392 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
395 393 {
396 394 QVector<DateTimeRange> ranges;
397 395 for (auto zone : impl->m_SelectionZones) {
398 396 ranges << zone->range();
399 397 }
400 398
401 399 return ranges;
402 400 }
403 401
404 402 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
405 403 {
406 404 for (const auto &range : ranges) {
407 405 // note: ownership is transfered to QCustomPlot
408 406 auto zone = new VisualizationSelectionZoneItem(&plot());
409 407 zone->setRange(range.m_TStart, range.m_TEnd);
410 408 impl->addSelectionZone(zone);
411 409 }
412 410
413 411 plot().replot(QCustomPlot::rpQueuedReplot);
414 412 }
415 413
416 414 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
417 415 const DateTimeRange &range)
418 416 {
419 417 // note: ownership is transfered to QCustomPlot
420 418 auto zone = new VisualizationSelectionZoneItem(&plot());
421 419 zone->setName(name);
422 420 zone->setRange(range.m_TStart, range.m_TEnd);
423 421 impl->addSelectionZone(zone);
424 422
425 423 plot().replot(QCustomPlot::rpQueuedReplot);
426 424
427 425 return zone;
428 426 }
429 427
430 428 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
431 429 {
432 430 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
433 431
434 432 if (impl->m_HoveredZone == selectionZone) {
435 433 impl->m_HoveredZone = nullptr;
436 434 setCursor(Qt::ArrowCursor);
437 435 }
438 436
439 437 impl->m_SelectionZones.removeAll(selectionZone);
440 438 plot().removeItem(selectionZone);
441 439 plot().replot(QCustomPlot::rpQueuedReplot);
442 440 }
443 441
444 442 void VisualizationGraphWidget::undoZoom()
445 443 {
446 444 auto zoom = impl->m_ZoomStack.pop();
447 445 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
448 446 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
449 447
450 448 axisX->setRange(zoom.first);
451 449 axisY->setRange(zoom.second);
452 450
453 451 plot().replot(QCustomPlot::rpQueuedReplot);
454 452 }
455 453
456 454 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
457 455 {
458 456 if (visitor) {
459 457 visitor->visit(this);
460 458 }
461 459 else {
462 460 qCCritical(LOG_VisualizationGraphWidget())
463 461 << tr("Can't visit widget : the visitor is null");
464 462 }
465 463 }
466 464
467 465 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
468 466 {
469 467 auto isSpectrogram = [](const auto &variable) {
470 468 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
471 469 };
472 470
473 471 // - A spectrogram series can't be dropped on graph with existing plottables
474 472 // - No data series can be dropped on graph with existing spectrogram series
475 473 return isSpectrogram(variable)
476 474 ? impl->m_VariableToPlotMultiMap.empty()
477 475 : std::none_of(
478 476 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
479 477 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
480 478 }
481 479
482 480 bool VisualizationGraphWidget::contains(const Variable &variable) const
483 481 {
484 482 // Finds the variable among the keys of the map
485 483 auto variablePtr = &variable;
486 484 auto findVariable
487 485 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
488 486
489 487 auto end = impl->m_VariableToPlotMultiMap.cend();
490 488 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
491 489 return it != end;
492 490 }
493 491
494 492 QString VisualizationGraphWidget::name() const
495 493 {
496 494 return impl->m_Name;
497 495 }
498 496
499 497 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
500 498 {
501 499 auto mimeData = new QMimeData;
502 500
503 501 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
504 502 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
505 503 && selectionZoneItemUnderCursor) {
506 504 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
507 505 selectionZoneItemUnderCursor->range()));
508 506 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
509 507 selectionZoneItemUnderCursor->range()));
510 508 }
511 509 else {
512 510 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
513 511
514 512 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
515 513 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
516 514 }
517 515
518 516 return mimeData;
519 517 }
520 518
521 519 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
522 520 {
523 521 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
524 522 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
525 523 && selectionZoneItemUnderCursor) {
526 524
527 525 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
528 526 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
529 527
530 528 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
531 529 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
532 530 .toSize();
533 531
534 532 auto pixmap = QPixmap(zoneSize);
535 533 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
536 534
537 535 return pixmap;
538 536 }
539 537
540 538 return QPixmap();
541 539 }
542 540
543 541 bool VisualizationGraphWidget::isDragAllowed() const
544 542 {
545 543 return true;
546 544 }
547 545
548 546 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
549 547 {
550 548 if (highlighted) {
551 549 plot().setBackground(QBrush(QColor("#BBD5EE")));
552 550 }
553 551 else {
554 552 plot().setBackground(QBrush(Qt::white));
555 553 }
556 554
557 555 plot().update();
558 556 }
559 557
560 558 void VisualizationGraphWidget::addVerticalCursor(double time)
561 559 {
562 560 impl->m_VerticalCursor->setPosition(time);
563 561 impl->m_VerticalCursor->setVisible(true);
564 562
565 563 auto text
566 564 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
567 565 impl->m_VerticalCursor->setLabelText(text);
568 566 }
569 567
570 568 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
571 569 {
572 570 impl->m_VerticalCursor->setAbsolutePosition(position);
573 571 impl->m_VerticalCursor->setVisible(true);
574 572
575 573 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
576 574 auto text
577 575 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
578 576 impl->m_VerticalCursor->setLabelText(text);
579 577 }
580 578
581 579 void VisualizationGraphWidget::removeVerticalCursor()
582 580 {
583 581 impl->m_VerticalCursor->setVisible(false);
584 582 plot().replot(QCustomPlot::rpQueuedReplot);
585 583 }
586 584
587 585 void VisualizationGraphWidget::addHorizontalCursor(double value)
588 586 {
589 587 impl->m_HorizontalCursor->setPosition(value);
590 588 impl->m_HorizontalCursor->setVisible(true);
591 589 impl->m_HorizontalCursor->setLabelText(QString::number(value));
592 590 }
593 591
594 592 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
595 593 {
596 594 impl->m_HorizontalCursor->setAbsolutePosition(position);
597 595 impl->m_HorizontalCursor->setVisible(true);
598 596
599 597 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
600 598 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
601 599 }
602 600
603 601 void VisualizationGraphWidget::removeHorizontalCursor()
604 602 {
605 603 impl->m_HorizontalCursor->setVisible(false);
606 604 plot().replot(QCustomPlot::rpQueuedReplot);
607 605 }
608 606
609 607 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
610 608 {
611 609 Q_UNUSED(event);
612 610
613 611 for (auto i : impl->m_SelectionZones) {
614 612 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
615 613 }
616 614
617 615 // Prevents that all variables will be removed from graph when it will be closed
618 616 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
619 617 emit variableAboutToBeRemoved(variableEntry.first);
620 618 }
621 619 }
622 620
623 621 void VisualizationGraphWidget::enterEvent(QEvent *event)
624 622 {
625 623 Q_UNUSED(event);
626 624 impl->m_RenderingDelegate->showGraphOverlay(true);
627 625 }
628 626
629 627 void VisualizationGraphWidget::leaveEvent(QEvent *event)
630 628 {
631 629 Q_UNUSED(event);
632 630 impl->m_RenderingDelegate->showGraphOverlay(false);
633 631
634 632 if (auto parentZone = parentZoneWidget()) {
635 633 parentZone->notifyMouseLeaveGraph(this);
636 634 }
637 635 else {
638 636 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
639 637 }
640 638
641 639 if (impl->m_HoveredZone) {
642 640 impl->m_HoveredZone->setHovered(false);
643 641 impl->m_HoveredZone = nullptr;
644 642 }
645 643 }
646 644
647 645 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
648 646 {
649 647 return *ui->widget;
650 648 }
651 649
652 650 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
653 651 {
654 652 QMenu graphMenu{};
655 653
656 654 // Iterates on variables (unique keys)
657 655 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
658 656 end = impl->m_VariableToPlotMultiMap.cend();
659 657 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
660 658 // 'Remove variable' action
661 659 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
662 660 [ this, var = it->first ]() { removeVariable(var); });
663 661 }
664 662
665 663 if (!impl->m_ZoomStack.isEmpty()) {
666 664 if (!graphMenu.isEmpty()) {
667 665 graphMenu.addSeparator();
668 666 }
669 667
670 668 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
671 669 }
672 670
673 671 // Selection Zone Actions
674 672 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
675 673 if (selectionZoneItem) {
676 674 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
677 675 selectedItems.removeAll(selectionZoneItem);
678 676 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
679 677
680 678 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
681 679 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
682 680 graphMenu.addSeparator();
683 681 }
684 682
685 683 QHash<QString, QMenu *> subMenus;
686 684 QHash<QString, bool> subMenusEnabled;
687 685 QHash<QString, FilteringAction *> filteredMenu;
688 686
689 687 for (auto zoneAction : zoneActions) {
690 688
691 689 auto isEnabled = zoneAction->isEnabled(selectedItems);
692 690
693 691 auto menu = &graphMenu;
694 692 QString menuPath;
695 693 for (auto subMenuName : zoneAction->subMenuList()) {
696 694 menuPath += '/';
697 695 menuPath += subMenuName;
698 696
699 697 if (!subMenus.contains(menuPath)) {
700 698 menu = menu->addMenu(subMenuName);
701 699 subMenus[menuPath] = menu;
702 700 subMenusEnabled[menuPath] = isEnabled;
703 701 }
704 702 else {
705 703 menu = subMenus.value(menuPath);
706 704 if (isEnabled) {
707 705 // The sub menu is enabled if at least one of its actions is enabled
708 706 subMenusEnabled[menuPath] = true;
709 707 }
710 708 }
711 709 }
712 710
713 711 FilteringAction *filterAction = nullptr;
714 712 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
715 713 filterAction = filteredMenu.value(menuPath);
716 714 if (!filterAction) {
717 715 filterAction = new FilteringAction{this};
718 716 filteredMenu[menuPath] = filterAction;
719 717 menu->addAction(filterAction);
720 718 }
721 719 }
722 720
723 721 auto action = menu->addAction(zoneAction->name());
724 722 action->setEnabled(isEnabled);
725 723 action->setShortcut(zoneAction->displayedShortcut());
726 724 QObject::connect(action, &QAction::triggered,
727 725 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
728 726
729 727 if (filterAction && zoneAction->isFilteringAllowed()) {
730 728 filterAction->addActionToFilter(action);
731 729 }
732 730 }
733 731
734 732 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
735 733 it.value()->setEnabled(subMenusEnabled[it.key()]);
736 734 }
737 735 }
738 736
739 737 if (!graphMenu.isEmpty()) {
740 738 graphMenu.exec(QCursor::pos());
741 739 }
742 740 }
743 741
744 742 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
745 743 {
746 744 auto graphRange = DateTimeRange{t1.lower, t1.upper};
747 745 auto oldGraphRange = DateTimeRange{t2.lower, t2.upper};
748 746
749 747 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
750 748 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
751 749 end = impl->m_VariableToPlotMultiMap.end();
752 750 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
753 751 sqpApp->variableController().asyncChangeRange(it->first, graphRange);
754 752 }
755 753 }
756 754
757 755 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration)
758 756 {
759 757 emit synchronize(graphRange, oldGraphRange);
760 758 }
761 759
762 760 auto pos = mapFromGlobal(QCursor::pos());
763 761 auto axisPos = impl->posToAxisPos(pos, plot());
764 762 if (auto parentZone = parentZoneWidget()) {
765 763 if (impl->pointIsInAxisRect(axisPos, plot())) {
766 764 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
767 765 }
768 766 else {
769 767 parentZone->notifyMouseLeaveGraph(this);
770 768 }
771 769 }
772 770
773 771 // Quits calibration
774 772 impl->m_IsCalibration = false;
775 773 }
776 774
777 775 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
778 776 {
779 777 impl->m_RenderingDelegate->onMouseDoubleClick(event);
780 778 }
781 779
782 780 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
783 781 {
784 782 // Handles plot rendering when mouse is moving
785 783 impl->m_RenderingDelegate->onMouseMove(event);
786 784
787 785 auto axisPos = impl->posToAxisPos(event->pos(), plot());
788 786
789 787 // Zoom box and zone drawing
790 788 if (impl->m_DrawingZoomRect) {
791 789 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
792 790 }
793 791 else if (impl->m_DrawingZone) {
794 792 impl->m_DrawingZone->setEnd(axisPos.x());
795 793 }
796 794
797 795 // Cursor
798 796 if (auto parentZone = parentZoneWidget()) {
799 797 if (impl->pointIsInAxisRect(axisPos, plot())) {
800 798 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
801 799 }
802 800 else {
803 801 parentZone->notifyMouseLeaveGraph(this);
804 802 }
805 803 }
806 804
807 805 // Search for the selection zone under the mouse
808 806 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
809 807 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
810 808 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
811 809
812 810 // Sets the appropriate cursor shape
813 811 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
814 812 setCursor(cursorShape);
815 813
816 814 // Manages the hovered zone
817 815 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
818 816 if (impl->m_HoveredZone) {
819 817 impl->m_HoveredZone->setHovered(false);
820 818 }
821 819 selectionZoneItemUnderCursor->setHovered(true);
822 820 impl->m_HoveredZone = selectionZoneItemUnderCursor;
823 821 plot().replot(QCustomPlot::rpQueuedReplot);
824 822 }
825 823 }
826 824 else {
827 825 // There is no zone under the mouse or the interaction mode is not "selection zones"
828 826 if (impl->m_HoveredZone) {
829 827 impl->m_HoveredZone->setHovered(false);
830 828 impl->m_HoveredZone = nullptr;
831 829 }
832 830
833 831 setCursor(Qt::ArrowCursor);
834 832 }
835 833
836 834 impl->m_HasMovedMouse = true;
837 835 VisualizationDragWidget::mouseMoveEvent(event);
838 836 }
839 837
840 838 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
841 839 {
842 840 // Processes event only if the wheel occurs on axis rect
843 841 if (!dynamic_cast<QCPAxisRect *>(ui->widget->layoutElementAt(event->posF()))) {
844 842 return;
845 843 }
846 844
847 845 auto value = event->angleDelta().x() + event->angleDelta().y();
848 846 if (value != 0) {
849 847
850 848 auto direction = value > 0 ? 1.0 : -1.0;
851 849 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
852 850 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
853 851 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
854 852
855 853 auto zoomOrientations = QFlags<Qt::Orientation>{};
856 854 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
857 855 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
858 856
859 857 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
860 858
861 859 if (!isZoomX && !isZoomY) {
862 860 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
863 861 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
864 862
865 863 axis->setRange(axis->range() + diff);
866 864
867 865 if (plot().noAntialiasingOnDrag()) {
868 866 plot().setNotAntialiasedElements(QCP::aeAll);
869 867 }
870 868
871 869 //plot().replot(QCustomPlot::rpQueuedReplot);
872 870 }
873 871 }
874 872 }
875 873
876 874 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
877 875 {
878 876 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
879 877 auto isSelectionZoneMode
880 878 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
881 879 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
882 880
883 881 if (!isDragDropClick && isLeftClick) {
884 882 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
885 883 // Starts a zoom box
886 884 impl->startDrawingRect(event->pos(), plot());
887 885 }
888 886 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
889 887 // Starts a new selection zone
890 888 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
891 889 if (!zoneAtPos) {
892 890 impl->startDrawingZone(event->pos(), this);
893 891 }
894 892 }
895 893 }
896 894
897 895 // Allows mouse panning only in default mode
898 896 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
899 897 == SqpApplication::PlotsInteractionMode::None
900 898 && !isDragDropClick);
901 899
902 900 // Allows zone edition only in selection zone mode without drag&drop
903 901 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
904 902
905 903 // Selection / Deselection
906 904 if (isSelectionZoneMode) {
907 905 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
908 906 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
909 907
910 908
911 909 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
912 910 && !isMultiSelectionClick) {
913 911 parentVisualizationWidget()->selectionZoneManager().select(
914 912 {selectionZoneItemUnderCursor});
915 913 }
916 914 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
917 915 parentVisualizationWidget()->selectionZoneManager().clearSelection();
918 916 }
919 917 else {
920 918 // No selection change
921 919 }
922 920
923 921 if (selectionZoneItemUnderCursor && isLeftClick) {
924 922 selectionZoneItemUnderCursor->setAssociatedEditedZones(
925 923 parentVisualizationWidget()->selectionZoneManager().selectedItems());
926 924 }
927 925 }
928 926
929 927
930 928 impl->m_HasMovedMouse = false;
931 929 VisualizationDragWidget::mousePressEvent(event);
932 930 }
933 931
934 932 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
935 933 {
936 934 if (impl->m_DrawingZoomRect) {
937 935
938 936 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
939 937 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
940 938
941 939 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
942 940 impl->m_DrawingZoomRect->bottomRight->coords().x()};
943 941
944 942 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
945 943 impl->m_DrawingZoomRect->bottomRight->coords().y()};
946 944
947 945 impl->removeDrawingRect(plot());
948 946
949 947 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
950 948 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
951 949 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
952 950 axisX->setRange(newAxisXRange);
953 951 axisY->setRange(newAxisYRange);
954 952
955 953 plot().replot(QCustomPlot::rpQueuedReplot);
956 954 }
957 955 }
958 956
959 957 impl->endDrawingZone(this);
960 958
961 959 // Selection / Deselection
962 960 auto isSelectionZoneMode
963 961 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
964 962 if (isSelectionZoneMode) {
965 963 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
966 964 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
967 965 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
968 966 && !impl->m_HasMovedMouse) {
969 967
970 968 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
971 969 if (zonesUnderCursor.count() > 1) {
972 970 // There are multiple zones under the mouse.
973 971 // Performs the selection with a selection dialog.
974 972 VisualizationMultiZoneSelectionDialog dialog{this};
975 973 dialog.setZones(zonesUnderCursor);
976 974 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
977 975 dialog.activateWindow();
978 976 dialog.raise();
979 977 if (dialog.exec() == QDialog::Accepted) {
980 978 auto selection = dialog.selectedZones();
981 979
982 980 if (!isMultiSelectionClick) {
983 981 parentVisualizationWidget()->selectionZoneManager().clearSelection();
984 982 }
985 983
986 984 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
987 985 auto zone = it.key();
988 986 auto isSelected = it.value();
989 987 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
990 988 isSelected);
991 989
992 990 if (isSelected) {
993 991 // Puts the zone on top of the stack so it can be moved or resized
994 992 impl->moveSelectionZoneOnTop(zone, plot());
995 993 }
996 994 }
997 995 }
998 996 }
999 997 else {
1000 998 if (!isMultiSelectionClick) {
1001 999 parentVisualizationWidget()->selectionZoneManager().select(
1002 1000 {selectionZoneItemUnderCursor});
1003 1001 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1004 1002 }
1005 1003 else {
1006 1004 parentVisualizationWidget()->selectionZoneManager().setSelected(
1007 1005 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1008 1006 || event->button() == Qt::RightButton);
1009 1007 }
1010 1008 }
1011 1009 }
1012 1010 else {
1013 1011 // No selection change
1014 1012 }
1015 1013 }
1016 1014 }
1017 1015
1018 1016 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1019 1017 {
1020 1018 auto graphRange = ui->widget->xAxis->range();
1021 1019 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1022 1020
1023 1021 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1024 1022 auto variable = variableEntry.first;
1025 1023 qCDebug(LOG_VisualizationGraphWidget())
1026 1024 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1027 1025 qCDebug(LOG_VisualizationGraphWidget())
1028 1026 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1029 1027 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1030 1028 impl->updateData(variableEntry.second, variable, variable->range());
1031 1029 }
1032 1030 }
1033 1031 }
1034 1032
1035 1033 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1036 1034 const DateTimeRange &range)
1037 1035 {
1038 1036 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1039 1037 if (it != impl->m_VariableToPlotMultiMap.end()) {
1040 1038 impl->updateData(it->second, variable, range);
1041 1039 }
1042 1040 }
1041
1042 void VisualizationGraphWidget::variableUpdated(QUuid id)
1043 {
1044 for(auto& [var,plotables]:impl->m_VariableToPlotMultiMap)
1045 {
1046 if(var->ID()==id)
1047 {
1048 impl->updateData(plotables,var,this->graphRange());
1049 }
1050 }
1051 }
@@ -1,2 +1,3
1 1 add_subdirectory(mockplugin)
2 2 add_subdirectory(amda)
3 add_subdirectory(generic_ws)
@@ -1,88 +1,88
1 1 #include "AmdaProvider.h"
2 2 #include "AmdaDefs.h"
3 3 #include "AmdaResultParser.h"
4 4 #include "AmdaServer.h"
5 5
6 6 #include <Common/DateUtils.h>
7 7 #include <Data/DataProviderParameters.h>
8 8 #include <Network/NetworkController.h>
9 9 #include <SqpApplication.h>
10 10 #include <Variable/Variable.h>
11 11
12 12 #include <QNetworkAccessManager>
13 13 #include <QNetworkReply>
14 14 #include <QTemporaryFile>
15 15 #include <QThread>
16 16 #include <QJsonDocument>
17 17 #include <Network/Downloader.h>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
20 20
21 21 namespace {
22 22
23 23 /// URL format for a request on AMDA server. The parameters are as follows:
24 24 /// - %1: server URL
25 25 /// - %2: start date
26 26 /// - %3: end date
27 27 /// - %4: parameter id
28 28 /// AMDA V2: http://amdatest.irap.omp.eu/php/rest/
29 29 const auto AMDA_URL_FORMAT = QStringLiteral(
30 30 "http://%1/php/rest/"
31 31 "getParameter.php?startTime=%2&stopTime=%3&parameterID=%4&outputFormat=ASCII&"
32 32 "timeFormat=ISO8601&gzip=0");
33 33
34 34 const auto AMDA_URL_FORMAT_WITH_TOKEN = QStringLiteral(
35 35 "http://%1/php/rest/"
36 36 "getParameter.php?startTime=%2&stopTime=%3&parameterID=%4&outputFormat=ASCII&"
37 37 "timeFormat=ISO8601&gzip=0&"
38 38 "token=%5");
39 39
40 40 const auto AMDA_TOKEN_URL_FORMAT = QStringLiteral(
41 41 "http://%1/php/rest/"
42 42 "auth.php");
43 43
44 44 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
45 45 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
46 46
47 47 /// Formats a time to a date that can be passed in URL
48 48 QString dateFormat(double sqpRange) noexcept
49 49 {
50 50 auto dateTime = DateUtils::dateTime(sqpRange);
51 51 return dateTime.toString(AMDA_TIME_FORMAT);
52 52 }
53 53
54 54
55 55 } // namespace
56 56
57 57 AmdaProvider::AmdaProvider()
58 58 {
59 59
60 60 }
61 61
62 62 std::shared_ptr<IDataProvider> AmdaProvider::clone() const
63 63 {
64 64 // No copy is made in the clone
65 65 return std::make_shared<AmdaProvider>();
66 66 }
67 67
68 68 IDataSeries* AmdaProvider::getData(const DataProviderParameters &parameters)
69 69 {
70 auto range = parameters.m_Times.front();
70 auto range = parameters.m_Range;
71 71 auto metaData = parameters.m_Data;
72 72 auto productId = metaData.value(AMDA_XML_ID_KEY).toString();
73 73 auto productValueType
74 74 = DataSeriesTypeUtils::fromString(metaData.value(AMDA_DATA_TYPE_KEY).toString());
75 75 auto startDate = dateFormat(range.m_TStart);
76 76 auto endDate = dateFormat(range.m_TEnd);
77 77 QVariantHash urlProperties{{AMDA_SERVER_KEY, metaData.value(AMDA_SERVER_KEY)}};
78 78 auto token_url = QString{AMDA_TOKEN_URL_FORMAT}.arg(AmdaServer::instance().url(urlProperties));
79 79 auto response = Downloader::get(token_url);
80 80 auto url = QString{AMDA_URL_FORMAT_WITH_TOKEN}.arg(AmdaServer::instance().url(urlProperties),
81 81 startDate, endDate, productId, QString(response.data()));
82 82 response = Downloader::get(url);
83 83 auto test = QJsonDocument::fromJson(response.data());
84 84 url = test["dataFileURLs"].toString();
85 85 response = Downloader::get(url);
86 86 return AmdaResultParser::readTxt(QTextStream{response.data()},productValueType);
87 87 }
88 88
@@ -1,99 +1,99
1 1 #include "AmdaServer.h"
2 2
3 3 #include "AmdaDefs.h"
4 4
5 5 Q_LOGGING_CATEGORY(LOG_AmdaServer, "AmdaServer")
6 6
7 7 namespace {
8 8
9 9 /// URL of the default AMDA server
10 10 const auto AMDA_DEFAULT_SERVER_URL = QStringLiteral("amda.irap.omp.eu");
11 11
12 12 /// URL of the AMDA test server
13 const auto AMDA_TEST_SERVER_URL = QStringLiteral("amdatest.irap.omp.eu");
13 const auto AMDA_TEST_SERVER_URL = QStringLiteral("amdadev.irap.omp.eu/");
14 14
15 15 /// Port used for local server
16 16 const auto AMDA_LOCAL_SERVER_PORT = 6543;
17 17
18 18 /// URL of the local server
19 19 const auto AMDA_LOCAL_SERVER_URL
20 20 = QString{"localhost:%1"}.arg(QString::number(AMDA_LOCAL_SERVER_PORT));
21 21
22 22 /// Default AMDA server
23 23 struct AmdaDefaultServer : public AmdaServer {
24 24 public:
25 25 QString name() const override { return QStringLiteral("AMDA (default)"); }
26 26 QString url(const QVariantHash &properties) const override
27 27 {
28 28 Q_UNUSED(properties);
29 29 return AMDA_DEFAULT_SERVER_URL;
30 30 }
31 31 };
32 32
33 33 /// Alternative AMDA server (tests)
34 34 struct AmdaTestServer : public AmdaServer {
35 35 public:
36 36 QString name() const override { return QStringLiteral("AMDA (test)"); }
37 37 QString url(const QVariantHash &properties) const override
38 38 {
39 39 Q_UNUSED(properties);
40 40 return AMDA_TEST_SERVER_URL;
41 41 }
42 42 };
43 43
44 44 /// Hybrid AMDA server: use both of default and test server.
45 45 /// The server used is relative to each product for which to retrieve url, according to its "server"
46 46 /// property
47 47 struct AmdaHybridServer : public AmdaServer {
48 48 public:
49 49 QString name() const override { return QStringLiteral("AMDA (hybrid)"); }
50 50 QString url(const QVariantHash &properties) const override
51 51 {
52 52 // Reads "server" property to determine which server url to use
53 53 auto server = properties.value(AMDA_SERVER_KEY).toString();
54 54 return server == QString{"amdatest"} ? AMDA_TEST_SERVER_URL : AMDA_DEFAULT_SERVER_URL;
55 55 }
56 56 };
57 57
58 58 /// Local AMDA server: use local python server to simulate AMDA requests
59 59 struct AmdaLocalServer : public AmdaServer {
60 60 public:
61 61 QString name() const override { return AMDA_LOCAL_SERVER_URL; }
62 62 QString url(const QVariantHash &properties) const override
63 63 {
64 64 Q_UNUSED(properties);
65 65 return AMDA_LOCAL_SERVER_URL;
66 66 }
67 67 };
68 68
69 69 /// @return an AMDA server instance created from the name of the server passed in parameter. If the
70 70 /// name does not match any known server, a default server instance is created
71 71 std::unique_ptr<AmdaServer> createInstance(const QString &server)
72 72 {
73 73 if (server == QString{"amdatest"}) {
74 74 return std::make_unique<AmdaTestServer>();
75 75 }
76 76 else if (server == QString{"hybrid"}) {
77 77 return std::make_unique<AmdaHybridServer>();
78 78 }
79 79 else if (server == QString{"localhost"}) {
80 80 return std::make_unique<AmdaLocalServer>();
81 81 }
82 82 else {
83 83 if (server != QString{"default"}) {
84 84 qCWarning(LOG_AmdaServer())
85 85 << QObject::tr("Unknown server '%1': default AMDA server will be used").arg(server);
86 86 }
87 87
88 88 return std::make_unique<AmdaDefaultServer>();
89 89 }
90 90 }
91 91
92 92 } // namespace
93 93
94 94 AmdaServer &AmdaServer::instance()
95 95 {
96 96 // Creates instance depending on the SCIQLOP_AMDA_SERVER value at compile time
97 97 static auto instance = createInstance(SCIQLOP_AMDA_SERVER);
98 98 return *instance;
99 99 }
@@ -1,199 +1,199
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 <Data/SpectrogramSeries.h>
7 7 #include <Data/VectorSeries.h>
8 8
9 9 #include <cmath>
10 10 #include <set>
11 11
12 12 #include <QFuture>
13 13 #include <QThread>
14 14 #include <QtConcurrent/QtConcurrent>
15 15
16 16
17 17 namespace {
18 18
19 19 /// Number of bands generated for a spectrogram
20 20 const auto SPECTROGRAM_NUMBER_BANDS = 30;
21 21
22 22 /// Bands for which to generate NaN values for a spectrogram
23 23 const auto SPECTROGRAM_NAN_BANDS = std::set<int>{1, 3, 10, 20};
24 24
25 25 /// Bands for which to generate zeros for a spectrogram
26 26 const auto SPECTROGRAM_ZERO_BANDS = std::set<int>{2, 15, 19, 29};
27 27
28 28 /// Abstract cosinus type
29 29 struct ICosinusType {
30 30 virtual ~ICosinusType() = default;
31 31 /// @return the number of components generated for the type
32 32 virtual std::size_t componentCount() const = 0;
33 33 /// @return the data series created for the type
34 34 virtual IDataSeries* createDataSeries(std::vector<double> xAxisData,
35 35 std::vector<double> valuesData) const = 0;
36 36 /// Generates values (one value per component)
37 37 /// @param x the x-axis data used to generate values
38 38 /// @param values the vector in which to insert the generated values
39 39 /// @param dataIndex the index of insertion of the generated values
40 40 ///
41 41 virtual void generateValues(double x, std::vector<double> &values, int dataIndex) const = 0;
42 42 };
43 43
44 44 struct ScalarCosinus : public ICosinusType {
45 45 std::size_t componentCount() const override { return 1; }
46 46
47 47 IDataSeries* createDataSeries(std::vector<double> xAxisData,
48 48 std::vector<double> valuesData) const override
49 49 {
50 50 return new ScalarSeries(std::move(xAxisData), std::move(valuesData),
51 51 Unit{QStringLiteral("t"), true}, Unit{});
52 52 }
53 53
54 54 void generateValues(double x, std::vector<double> &values, int dataIndex) const override
55 55 {
56 56 values[dataIndex] = std::cos(x);
57 57 }
58 58 };
59 59
60 60 struct SpectrogramCosinus : public ICosinusType {
61 61 /// Ctor with y-axis
62 62 explicit SpectrogramCosinus(std::vector<double> yAxisData, Unit yAxisUnit, Unit valuesUnit)
63 63 : m_YAxisData{std::move(yAxisData)},
64 64 m_YAxisUnit{std::move(yAxisUnit)},
65 65 m_ValuesUnit{std::move(valuesUnit)}
66 66 {
67 67 }
68 68
69 69 std::size_t componentCount() const override { return m_YAxisData.size(); }
70 70
71 71 IDataSeries* createDataSeries(std::vector<double> xAxisData,
72 72 std::vector<double> valuesData) const override
73 73 {
74 74 return new SpectrogramSeries(
75 75 std::move(xAxisData), m_YAxisData, std::move(valuesData),
76 76 Unit{QStringLiteral("t"), true}, m_YAxisUnit, m_ValuesUnit);
77 77 }
78 78
79 79 void generateValues(double x, std::vector<double> &values, int dataIndex) const override
80 80 {
81 81 auto componentCount = this->componentCount();
82 82 for (int i = 0; i < componentCount; ++i) {
83 83 auto y = m_YAxisData[i];
84 84
85 85 double value;
86 86
87 87 if (SPECTROGRAM_ZERO_BANDS.find(y) != SPECTROGRAM_ZERO_BANDS.end()) {
88 88 value = 0.;
89 89 }
90 90 else if (SPECTROGRAM_NAN_BANDS.find(y) != SPECTROGRAM_NAN_BANDS.end()) {
91 91 value = std::numeric_limits<double>::quiet_NaN();
92 92 }
93 93 else {
94 94 // Generates value for non NaN/zero bands
95 95 auto r = 3 * std::sqrt(x * x + y * y) + 1e-2;
96 96 value = 2 * x * (std::cos(r + 2) / r - std::sin(r + 2) / r);
97 97 }
98 98
99 99 values[componentCount * dataIndex + i] = value;
100 100 }
101 101 }
102 102
103 103 std::vector<double> m_YAxisData;
104 104 Unit m_YAxisUnit;
105 105 Unit m_ValuesUnit;
106 106 };
107 107
108 108 struct VectorCosinus : public ICosinusType {
109 109 std::size_t componentCount() const override { return 3; }
110 110
111 111 IDataSeries* createDataSeries(std::vector<double> xAxisData,
112 112 std::vector<double> valuesData) const override
113 113 {
114 114 return new VectorSeries(std::move(xAxisData), std::move(valuesData),
115 115 Unit{QStringLiteral("t"), true}, Unit{});
116 116 }
117 117
118 118 void generateValues(double x, std::vector<double> &values, int dataIndex) const override
119 119 {
120 120 // Generates value for each component: cos(x), cos(x)/2, cos(x)/3
121 121 auto xValue = std::cos(x);
122 122 auto componentCount = this->componentCount();
123 123 for (auto i = 0; i < componentCount; ++i) {
124 124 values[componentCount * dataIndex + i] = xValue / (i + 1);
125 125 }
126 126 }
127 127 };
128 128
129 129 /// Converts string to cosinus type
130 130 /// @return the cosinus type if the string could be converted, nullptr otherwise
131 131 std::unique_ptr<ICosinusType> cosinusType(const QString &type) noexcept
132 132 {
133 133 if (type.compare(QStringLiteral("scalar"), Qt::CaseInsensitive) == 0) {
134 134 return std::make_unique<ScalarCosinus>();
135 135 }
136 136 else if (type.compare(QStringLiteral("spectrogram"), Qt::CaseInsensitive) == 0) {
137 137 // Generates default y-axis data for spectrogram [0., 1., 2., ...]
138 138 std::vector<double> yAxisData(SPECTROGRAM_NUMBER_BANDS);
139 139 std::iota(yAxisData.begin(), yAxisData.end(), 0.);
140 140
141 141 return std::make_unique<SpectrogramCosinus>(std::move(yAxisData), Unit{"eV"},
142 142 Unit{"eV/(cm^2-s-sr-eV)"});
143 143 }
144 144 else if (type.compare(QStringLiteral("vector"), Qt::CaseInsensitive) == 0) {
145 145 return std::make_unique<VectorCosinus>();
146 146 }
147 147 else {
148 148 return nullptr;
149 149 }
150 150 }
151 151
152 152 } // namespace
153 153
154 154 std::shared_ptr<IDataProvider> CosinusProvider::clone() const
155 155 {
156 156 // No copy is made in clone
157 157 return std::make_shared<CosinusProvider>();
158 158 }
159 159
160 160 IDataSeries *CosinusProvider::_generate(const DateTimeRange &range, const QVariantHash &metaData)
161 161 {
162 162 auto dataIndex = 0;
163 163
164 164 // Retrieves cosinus type
165 165 auto typeVariant = metaData.value(COSINUS_TYPE_KEY, COSINUS_TYPE_DEFAULT_VALUE);
166 166 auto type = cosinusType(typeVariant.toString());
167 167 auto freqVariant = metaData.value(COSINUS_FREQUENCY_KEY, COSINUS_FREQUENCY_DEFAULT_VALUE);
168 168 double freq = freqVariant.toDouble();
169 169 double start = std::ceil(range.m_TStart * freq);
170 170 double end = std::floor(range.m_TEnd * freq);
171 171 if (end < start) {
172 172 std::swap(start, end);
173 173 }
174 174 std::size_t dataCount = static_cast<std::size_t>(end - start + 1);
175 175 std::size_t componentCount = type->componentCount();
176 176
177 177 auto xAxisData = std::vector<double>{};
178 178 xAxisData.resize(dataCount);
179 179
180 180 auto valuesData = std::vector<double>{};
181 181 valuesData.resize(dataCount * componentCount);
182 182
183 183 int progress = 0;
184 184 auto progressEnd = dataCount;
185 185 for (auto time = start; time <= end; ++time, ++dataIndex)
186 186 {
187 187 const auto x = time / freq;
188 188 xAxisData[dataIndex] = x;
189 189 // Generates values (depending on the type)
190 190 type->generateValues(x, valuesData, dataIndex);
191 191 }
192 192 return type->createDataSeries(std::move(xAxisData), std::move(valuesData));
193 193 }
194 194
195 195 IDataSeries* CosinusProvider::getData(const DataProviderParameters &parameters)
196 196 {
197 return _generate(parameters.m_Times.front(),parameters.m_Data);
197 return _generate(parameters.m_Range, parameters.m_Data);
198 198 }
199 199
General Comments 0
You need to be logged in to leave comments. Login now