##// END OF EJS Templates
Ported Mock plugin to new IDataProvider interface compatible with...
jeandet -
r1350:09a8c8a01145
parent child
Show More
@@ -1,1 +1,1
1 Subproject commit de55e7be90966d1efbd34a10df9a3231c50e245b
1 Subproject commit c08d1b8ad2976824def52cf0fca26a72b1069356
@@ -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 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 247 auto variables = sqpApp->variableController().variables(
248 mimeData->data(MIME_TYPE_VARIABLE_LIST));
248 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
249 249
250 250 if (variables.size() == 1) {
251 251
252 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,200 +1,199
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/VariableController2.h>
13 13 #include <Variable/VariableModel2.h>
14 14 #include <Visualization/VisualizationController.h>
15 15
16 16 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
17 17
18 18 class SqpApplication::SqpApplicationPrivate {
19 19 public:
20 20 SqpApplicationPrivate()
21 21 : m_VariableController{std::make_shared<VariableController2>()},
22 m_VariableModel{m_VariableController},
23 22 m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None),
24 23 m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor)
25 24 {
26 25 // /////////////////////////////// //
27 26 // Connections between controllers //
28 27 // /////////////////////////////// //
29 28
30 29 // VariableController <-> DataSourceController
31 30 connect(&m_DataSourceController,
32 SIGNAL(variableCreationRequested(const QString &, const QVariantHash &,
33 std::shared_ptr<IDataProvider>)),
34 m_VariableController.get(),
35 SLOT(createVariable(const QString &, const QVariantHash &,
36 std::shared_ptr<IDataProvider>)));
31 &DataSourceController::createVariable,[](const QString &variableName,
32 const QVariantHash &variableMetadata,
33 std::shared_ptr<IDataProvider> variableProvider)
34 {
35 sqpApp->variableController().createVariable(variableName,variableMetadata,variableProvider,sqpApp->timeController().dateTime());
36 });
37 37
38 38 // connect(m_VariableController->variableModel(), &VariableModel::requestVariable,
39 39 // m_DataSourceController.get(), &DataSourceController::requestVariable);
40 40
41 41 // VariableController <-> VisualizationController
42 42 // connect(m_VariableController.get(),
43 43 // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
44 44 // m_VisualizationController.get(),
45 45 // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
46 46
47 47 // connect(m_VariableController.get(),
48 48 // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)),
49 49 // m_VisualizationController.get(),
50 50 // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)));
51 51
52 52
53 53 m_DataSourceController.moveToThread(&m_DataSourceControllerThread);
54 54 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
55 55 m_NetworkController.moveToThread(&m_NetworkControllerThread);
56 56 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
57 57 m_VisualizationController.moveToThread(&m_VisualizationControllerThread);
58 58 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
59 59
60 60 // Additionnal init
61 61 //m_VariableController->setTimeController(m_TimeController.get());
62 62 }
63 63
64 64 virtual ~SqpApplicationPrivate()
65 65 {
66 66 m_DataSourceControllerThread.quit();
67 67 m_DataSourceControllerThread.wait();
68 68
69 69 m_NetworkControllerThread.quit();
70 70 m_NetworkControllerThread.wait();
71 71
72 72 m_VisualizationControllerThread.quit();
73 73 m_VisualizationControllerThread.wait();
74 74 }
75 75
76 76 DataSourceController m_DataSourceController;
77 77 std::shared_ptr<VariableController2> m_VariableController;
78 78 TimeController m_TimeController;
79 79 NetworkController m_NetworkController;
80 80 VisualizationController m_VisualizationController;
81 81 CatalogueController m_CatalogueController;
82 VariableModel2 m_VariableModel;
83 82
84 83 QThread m_DataSourceControllerThread;
85 84 QThread m_NetworkControllerThread;
86 85 QThread m_VisualizationControllerThread;
87 86
88 87 DragDropGuiController m_DragDropGuiController;
89 88 ActionsGuiController m_ActionsGuiController;
90 89
91 90 SqpApplication::PlotsInteractionMode m_PlotInterractionMode;
92 91 SqpApplication::PlotsCursorMode m_PlotCursorMode;
93 92 };
94 93
95 94
96 95 SqpApplication::SqpApplication(int &argc, char **argv)
97 96 : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()}
98 97 {
99 98 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
100 99
101 100 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
102 101
103 102 connect(&impl->m_DataSourceControllerThread, &QThread::started,
104 103 &impl->m_DataSourceController, &DataSourceController::initialize);
105 104 connect(&impl->m_DataSourceControllerThread, &QThread::finished,
106 105 &impl->m_DataSourceController, &DataSourceController::finalize);
107 106
108 107 connect(&impl->m_NetworkControllerThread, &QThread::started, &impl->m_NetworkController,
109 108 &NetworkController::initialize);
110 109 connect(&impl->m_NetworkControllerThread, &QThread::finished, &impl->m_NetworkController,
111 110 &NetworkController::finalize);
112 111
113 112 connect(&impl->m_VisualizationControllerThread, &QThread::started,
114 113 &impl->m_VisualizationController, &VisualizationController::initialize);
115 114 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
116 115 &impl->m_VisualizationController, &VisualizationController::finalize);
117 116
118 117 impl->m_DataSourceControllerThread.start();
119 118 impl->m_NetworkControllerThread.start();
120 119 impl->m_VisualizationControllerThread.start();
121 120 impl->m_CatalogueController.initialize();
122 121 }
123 122
124 123 SqpApplication::~SqpApplication()
125 124 {
126 125 }
127 126
128 127 void SqpApplication::initialize()
129 128 {
130 129 }
131 130
132 131 DataSourceController &SqpApplication::dataSourceController() noexcept
133 132 {
134 133 return impl->m_DataSourceController;
135 134 }
136 135
137 136 NetworkController &SqpApplication::networkController() noexcept
138 137 {
139 138 return impl->m_NetworkController;
140 139 }
141 140
142 141 TimeController &SqpApplication::timeController() noexcept
143 142 {
144 143 return impl->m_TimeController;
145 144 }
146 145
147 146 VariableController2 &SqpApplication::variableController() noexcept
148 147 {
149 148 return *impl->m_VariableController;
150 149 }
151 150
152 151 std::shared_ptr<VariableController2> SqpApplication::variableControllerOwner() noexcept
153 152 {
154 153 return impl->m_VariableController;
155 154 }
156 155
157 156 //VariableModel2 &SqpApplication::variableModel() noexcept
158 157 //{
159 158 // return impl->m_VariableModel;
160 159 //}
161 160
162 161 VisualizationController &SqpApplication::visualizationController() noexcept
163 162 {
164 163 return impl->m_VisualizationController;
165 164 }
166 165
167 166 CatalogueController &SqpApplication::catalogueController() noexcept
168 167 {
169 168 return impl->m_CatalogueController;
170 169 }
171 170
172 171 DragDropGuiController &SqpApplication::dragDropGuiController() noexcept
173 172 {
174 173 return impl->m_DragDropGuiController;
175 174 }
176 175
177 176 ActionsGuiController &SqpApplication::actionsGuiController() noexcept
178 177 {
179 178 return impl->m_ActionsGuiController;
180 179 }
181 180
182 181 SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const
183 182 {
184 183 return impl->m_PlotInterractionMode;
185 184 }
186 185
187 186 void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode)
188 187 {
189 188 impl->m_PlotInterractionMode = mode;
190 189 }
191 190
192 191 SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const
193 192 {
194 193 return impl->m_PlotCursorMode;
195 194 }
196 195
197 196 void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode)
198 197 {
199 198 impl->m_PlotCursorMode = mode;
200 199 }
@@ -1,241 +1,251
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 #include <DataSource/DataSourceController.h>
7 8
8 9 #include <ui_VariableInspectorWidget.h>
9 10
10 11 #include <QMouseEvent>
11 12 #include <QSortFilterProxyModel>
12 13 #include <QStyledItemDelegate>
13 14 #include <QWidgetAction>
14 15
15 16 #include <DragAndDrop/DragDropGuiController.h>
16 17 #include <SqpApplication.h>
17 18
18 19 Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget")
19 20
20 21
21 22 class QProgressBarItemDelegate : public QStyledItemDelegate {
22 23
23 24 public:
24 25 QProgressBarItemDelegate(QObject *parent) : QStyledItemDelegate{parent} {}
25 26
26 27 void paint(QPainter *painter, const QStyleOptionViewItem &option,
27 28 const QModelIndex &index) const
28 29 {
29 30 auto data = index.data(Qt::DisplayRole);
30 31 auto progressData = index.data(VariableRoles::ProgressRole);
31 32 if (data.isValid() && progressData.isValid()) {
32 33 auto name = data.value<QString>();
33 34 auto progress = progressData.value<double>();
34 35 if (progress > 0) {
35 36 auto cancelButtonWidth = 20;
36 37 auto progressBarOption = QStyleOptionProgressBar{};
37 38 auto progressRect = option.rect;
38 39 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
39 40 progressBarOption.rect = progressRect;
40 41 progressBarOption.minimum = 0;
41 42 progressBarOption.maximum = 100;
42 43 progressBarOption.progress = progress;
43 44 progressBarOption.text
44 45 = QString("%1 %2").arg(name).arg(QString::number(progress, 'f', 2) + "%");
45 46 progressBarOption.textVisible = true;
46 47 progressBarOption.textAlignment = Qt::AlignCenter;
47 48
48 49
49 50 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption,
50 51 painter);
51 52
52 53 // Cancel button
53 54 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
54 55 option.rect.height());
55 56 auto buttonOption = QStyleOptionButton{};
56 57 buttonOption.rect = buttonRect;
57 58 buttonOption.text = "X";
58 59
59 60 QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter);
60 61 }
61 62 else {
62 63 QStyledItemDelegate::paint(painter, option, index);
63 64 }
64 65 }
65 66 else {
66 67 QStyledItemDelegate::paint(painter, option, index);
67 68 }
68 69 }
69 70
70 71 bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
71 72 const QModelIndex &index)
72 73 {
73 74 if (event->type() == QEvent::MouseButtonRelease) {
74 75 auto data = index.data(Qt::DisplayRole);
75 76 auto progressData = index.data(VariableRoles::ProgressRole);
76 77 if (data.isValid() && progressData.isValid()) {
77 78 auto cancelButtonWidth = 20;
78 79 auto progressRect = option.rect;
79 80 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
80 81 // Cancel button
81 82 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
82 83 option.rect.height());
83 84
84 85 auto e = (QMouseEvent *)event;
85 86 auto clickX = e->x();
86 87 auto clickY = e->y();
87 88
88 89 auto x = buttonRect.left(); // the X coordinate
89 90 auto y = buttonRect.top(); // the Y coordinate
90 91 auto w = buttonRect.width(); // button width
91 92 auto h = buttonRect.height(); // button height
92 93
93 94 if (clickX > x && clickX < x + w) {
94 95 if (clickY > y && clickY < y + h) {
95 96 //auto& variableModel = sqpApp->variableModel();
96 97 //variableModel->abortProgress(index);
97 98 }
98 99 return true;
99 100 }
100 101 else {
101 102 return QStyledItemDelegate::editorEvent(event, model, option, index);
102 103 }
103 104 }
104 105 else {
105 106 return QStyledItemDelegate::editorEvent(event, model, option, index);
106 107 }
107 108 }
108 109 else {
109 110 return QStyledItemDelegate::editorEvent(event, model, option, index);
110 111 }
111 112
112 113
113 114 return QStyledItemDelegate::editorEvent(event, model, option, index);
114 115 }
115 116 };
116 117
117 118 VariableInspectorWidget::VariableInspectorWidget(QWidget *parent)
118 119 : QWidget{parent},
119 120 ui{new Ui::VariableInspectorWidget},
120 121 m_ProgressBarItemDelegate{new QProgressBarItemDelegate{this}}
121 122 {
122 123 ui->setupUi(this);
123 124
124 125 // Sets model for table
125 126 // auto sortFilterModel = new QSortFilterProxyModel{this};
126 127 // sortFilterModel->setSourceModel(sqpApp->variableController().variableModel());
127 128
128 m_model = new VariableModel2(sqpApp->variableControllerOwner());
129 m_model = new VariableModel2();
129 130 ui->tableView->setModel(m_model);
131 connect(m_model, &VariableModel2::createVariable,
132 [](const QVariantHash &productData)
133 {
134 sqpApp->dataSourceController().requestVariable(productData);
135 });
136 auto vc = &(sqpApp->variableController());
137 connect(vc, &VariableController2::variableAdded, m_model, &VariableModel2::variableAdded);
138 connect(vc, &VariableController2::variableDeleted, m_model, &VariableModel2::variableDeleted);
139 connect(m_model, &VariableModel2::asyncChangeRange, vc, &VariableController2::asyncChangeRange);
130 140
131 141 // Adds extra signal/slot between view and model, so the view can be updated instantly when
132 142 // there is a change of data in the model
133 connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
134 SLOT(refresh()));
143 //connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
144 // SLOT(refresh()));
135 145
136 146 //ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel());
137 147 ui->tableView->setItemDelegateForColumn(0, m_ProgressBarItemDelegate);
138 148
139 149 // Fixes column sizes
140 150 auto model = ui->tableView->model();
141 151 const auto count = model->columnCount();
142 152 for (auto i = 0; i < count; ++i) {
143 153 ui->tableView->setColumnWidth(
144 154 i, model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).toSize().width());
145 155 }
146 156
147 157 // Sets selection options
148 158 ui->tableView->setSelectionBehavior(QTableView::SelectRows);
149 159 ui->tableView->setSelectionMode(QTableView::ExtendedSelection);
150 160
151 161 // Connection to show a menu when right clicking on the tree
152 162 ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
153 163 connect(ui->tableView, &QTableView::customContextMenuRequested, this,
154 164 &VariableInspectorWidget::onTableMenuRequested);
155 165 }
156 166
157 167 VariableInspectorWidget::~VariableInspectorWidget()
158 168 {
159 169 delete ui;
160 170 }
161 171
162 172 void VariableInspectorWidget::onTableMenuRequested(const QPoint &pos) noexcept
163 173 {
164 174 auto selectedRows = ui->tableView->selectionModel()->selectedRows();
165 175
166 176 // Gets the model to retrieve the underlying selected variables
167 177 auto& vc = sqpApp->variableController();
168 178 auto selectedVariables = QVector<std::shared_ptr<Variable> >{};
169 179 for (const auto &selectedRow : qAsConst(selectedRows)) {
170 180 if (auto selectedVariable = vc[selectedRow.row()]) {
171 181 selectedVariables.push_back(selectedVariable);
172 182 }
173 183 }
174 184
175 185 QMenu tableMenu{};
176 186
177 187 // Emits a signal so that potential receivers can populate the menu before displaying it
178 188 emit tableMenuAboutToBeDisplayed(&tableMenu, selectedVariables);
179 189
180 190 // Adds menu-specific actions
181 191 if (!selectedVariables.isEmpty()) {
182 192 tableMenu.addSeparator();
183 193
184 194 // 'Rename' and 'Duplicate' actions (only if one variable selected)
185 195 if (selectedVariables.size() == 1) {
186 196 auto selectedVariable = selectedVariables.front();
187 197
188 198 auto duplicateFun = [varW = std::weak_ptr<Variable>(selectedVariable)]()
189 199 {
190 200 if (auto var = varW.lock()) {
191 201 sqpApp->variableController().cloneVariable(var);
192 202 }
193 203 };
194 204
195 205 tableMenu.addAction(tr("Duplicate"), duplicateFun);
196 206
197 207 auto renameFun = [ varW = std::weak_ptr<Variable>(selectedVariable), this ]()
198 208 {
199 209 if (auto var = varW.lock()) {
200 210 // Generates forbidden names (names associated to existing variables)
201 211 auto allVariables = sqpApp->variableController().variables();
202 212 auto forbiddenNames = QVector<QString>(allVariables.size());
203 213 std::transform(allVariables.cbegin(), allVariables.cend(),
204 214 forbiddenNames.begin(),
205 215 [](const auto &variable) { return variable->name(); });
206 216
207 217 RenameVariableDialog dialog{var->name(), forbiddenNames, this};
208 218 if (dialog.exec() == QDialog::Accepted) {
209 219 var->setName(dialog.name());
210 220 }
211 221 }
212 222 };
213 223
214 224 tableMenu.addAction(tr("Rename..."), renameFun);
215 225 }
216 226
217 227 // 'Delete' action
218 228 auto deleteFun = [&selectedVariables]() {
219 229 for(const auto& var:selectedVariables)
220 230 sqpApp->variableController().deleteVariable(var);
221 231 };
222 232
223 233 tableMenu.addAction(QIcon{":/icones/delete.png"}, tr("Delete"), deleteFun);
224 234 }
225 235
226 236 if (!tableMenu.isEmpty()) {
227 237 // Generates menu header (inserted before first action)
228 238 auto firstAction = tableMenu.actions().first();
229 239 auto headerAction = new QWidgetAction{&tableMenu};
230 240 headerAction->setDefaultWidget(new VariableMenuHeaderWidget{selectedVariables, &tableMenu});
231 241 tableMenu.insertAction(firstAction, headerAction);
232 242
233 243 // Displays menu
234 244 tableMenu.exec(QCursor::pos());
235 245 }
236 246 }
237 247
238 248 void VariableInspectorWidget::refresh() noexcept
239 249 {
240 250 ui->tableView->viewport()->update();
241 251 }
@@ -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 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 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 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 233 auto variables = sqpApp->variableController().variables(
234 mimeData->data(MIME_TYPE_VARIABLE_LIST));
234 Variable::variablesIDs(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 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 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 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 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,629 +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 13 #include <Data/DateTimeRangeHelper.h>
14 14 #include <DataSource/DataSourceController.h>
15 15 #include <Time/TimeController.h>
16 16 #include <Variable/Variable.h>
17 17 #include <Variable/VariableController2.h>
18 18
19 19 #include <Visualization/operations/FindVariableOperation.h>
20 20
21 21 #include <DragAndDrop/DragDropGuiController.h>
22 22 #include <QUuid>
23 23 #include <SqpApplication.h>
24 24 #include <cmath>
25 25
26 26 #include <QLayout>
27 27 #include <QStyle>
28 28
29 29 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
30 30
31 31 namespace {
32 32
33 33 /**
34 34 * Applies a function to all graphs of the zone represented by its layout
35 35 * @param layout the layout that contains graphs
36 36 * @param fun the function to apply to each graph
37 37 */
38 38 template <typename Fun>
39 39 void processGraphs(QLayout &layout, Fun fun)
40 40 {
41 41 for (auto i = 0; i < layout.count(); ++i) {
42 42 if (auto item = layout.itemAt(i)) {
43 43 if (auto visualizationGraphWidget
44 44 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
45 45 fun(*visualizationGraphWidget);
46 46 }
47 47 }
48 48 }
49 49 }
50 50
51 51 /// Generates a default name for a new graph, according to the number of graphs already displayed in
52 52 /// the zone
53 53 QString defaultGraphName(QLayout &layout)
54 54 {
55 55 QSet<QString> existingNames;
56 56 processGraphs(
57 57 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
58 58
59 59 int zoneNum = 1;
60 60 QString name;
61 61 do {
62 62 name = QObject::tr("Graph ").append(QString::number(zoneNum));
63 63 ++zoneNum;
64 64 } while (existingNames.contains(name));
65 65
66 66 return name;
67 67 }
68 68
69 69 } // namespace
70 70
71 71 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
72 72
73 73 explicit VisualizationZoneWidgetPrivate()
74 74 : m_SynchronisationGroupId{QUuid::createUuid()},
75 75 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
76 76 {
77 77 }
78 78 QUuid m_SynchronisationGroupId;
79 79 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
80 80
81 81 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
82 82 void dropVariables(const std::vector<std::shared_ptr<Variable> > &variables, int index,
83 83 VisualizationZoneWidget *zoneWidget);
84 84 void dropProducts(const QVariantList &productsData, int index,
85 85 VisualizationZoneWidget *zoneWidget);
86 86 };
87 87
88 88 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
89 89 : VisualizationDragWidget{parent},
90 90 ui{new Ui::VisualizationZoneWidget},
91 91 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
92 92 {
93 93 ui->setupUi(this);
94 94
95 95 ui->zoneNameLabel->setText(name);
96 96
97 97 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
98 98 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
99 99 VisualizationDragDropContainer::DropBehavior::Inserted);
100 100 ui->dragDropContainer->setMimeType(
101 101 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
102 102 ui->dragDropContainer->setMimeType(
103 103 MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
104 104 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
105 105 VisualizationDragDropContainer::DropBehavior::Merged);
106 106 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
107 107 VisualizationDragDropContainer::DropBehavior::Forbidden);
108 108 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
109 109 VisualizationDragDropContainer::DropBehavior::Forbidden);
110 110 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
111 111 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
112 112 ui->dragDropContainer);
113 113 });
114 114
115 115 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
116 116 if (!mimeData) {
117 117 return false;
118 118 }
119 119
120 120 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
121 121 auto variables = sqpApp->variableController().variables(
122 mimeData->data(MIME_TYPE_VARIABLE_LIST));
122 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
123 123
124 124 if (variables.size() != 1) {
125 125 return false;
126 126 }
127 127 auto variable = variables.front();
128 128
129 129 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
130 130 return graphWidget->canDrop(*variable);
131 131 }
132 132 }
133 133
134 134 return true;
135 135 };
136 136 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
137 137
138 138 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
139 139 &VisualizationZoneWidget::dropMimeData);
140 140 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
141 141 &VisualizationZoneWidget::dropMimeDataOnGraph);
142 142
143 143 // 'Close' options : widget is deleted when closed
144 144 setAttribute(Qt::WA_DeleteOnClose);
145 145 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
146 146 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
147 147
148 148 // Synchronisation id
149 149 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
150 150 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
151 151 }
152 152
153 153 VisualizationZoneWidget::~VisualizationZoneWidget()
154 154 {
155 155 delete ui;
156 156 }
157 157
158 158 void VisualizationZoneWidget::setZoneRange(const DateTimeRange &range)
159 159 {
160 160 if (auto graph = firstGraph()) {
161 161 graph->setGraphRange(range);
162 162 }
163 163 else {
164 164 qCWarning(LOG_VisualizationZoneWidget())
165 165 << tr("setZoneRange:Cannot set the range of an empty zone.");
166 166 }
167 167 }
168 168
169 169 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
170 170 {
171 171 // Synchronize new graph with others in the zone
172 172 impl->m_Synchronizer->addGraph(*graphWidget);
173 173
174 174 ui->dragDropContainer->addDragWidget(graphWidget);
175 175 }
176 176
177 177 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
178 178 {
179 179 // Synchronize new graph with others in the zone
180 180 impl->m_Synchronizer->addGraph(*graphWidget);
181 181
182 182 ui->dragDropContainer->insertDragWidget(index, graphWidget);
183 183 }
184 184
185 185 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
186 186 {
187 187 return createGraph(variable, -1);
188 188 }
189 189
190 190 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
191 191 int index)
192 192 {
193 193 auto graphWidget
194 194 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
195 195
196 196
197 197 // Set graph properties
198 198 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
199 199 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
200 200
201 201
202 202 // Lambda to synchronize zone widget
203 203 auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange,
204 204 const DateTimeRange &oldGraphRange) {
205 205
206 206 auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange);
207 207 auto frameLayout = ui->dragDropContainer->layout();
208 208 for (auto i = 0; i < frameLayout->count(); ++i) {
209 209 auto graphChild
210 210 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
211 211 if (graphChild && (graphChild != graphWidget)) {
212 212
213 213 auto graphChildRange = graphChild->graphRange();
214 214 switch (zoomType) {
215 215 case TransformationType::ZoomIn: {
216 216 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
217 217 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
218 218 graphChildRange.m_TStart += deltaLeft;
219 219 graphChildRange.m_TEnd -= deltaRight;
220 220 break;
221 221 }
222 222
223 223 case TransformationType::ZoomOut: {
224 224 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
225 225 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
226 226 graphChildRange.m_TStart -= deltaLeft;
227 227 graphChildRange.m_TEnd += deltaRight;
228 228 break;
229 229 }
230 230 case TransformationType::PanRight: {
231 231 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
232 232 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
233 233 graphChildRange.m_TStart += deltaLeft;
234 234 graphChildRange.m_TEnd += deltaRight;
235 235 break;
236 236 }
237 237 case TransformationType::PanLeft: {
238 238 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
239 239 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
240 240 graphChildRange.m_TStart -= deltaLeft;
241 241 graphChildRange.m_TEnd -= deltaRight;
242 242 break;
243 243 }
244 244 case TransformationType::Unknown: {
245 245 break;
246 246 }
247 247 default:
248 248 qCCritical(LOG_VisualizationZoneWidget())
249 249 << tr("Impossible to synchronize: zoom type not take into account");
250 250 // No action
251 251 break;
252 252 }
253 253 graphChild->setFlags(GraphFlag::DisableAll);
254 254 graphChild->setGraphRange(graphChildRange);
255 255 graphChild->setFlags(GraphFlag::EnableAll);
256 256 }
257 257 }
258 258 };
259 259
260 260 // connection for synchronization
261 261 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
262 262 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
263 263 &VisualizationZoneWidget::onVariableAdded);
264 264 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
265 265 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
266 266
267 267 auto range = DateTimeRange{};
268 268 if (auto firstGraph = this->firstGraph()) {
269 269 // Case of a new graph in a existant zone
270 270 range = firstGraph->graphRange();
271 271 }
272 272 else {
273 273 // Case of a new graph as the first of the zone
274 274 range = variable->range();
275 275 }
276 276
277 277 this->insertGraph(index, graphWidget);
278 278
279 279 graphWidget->addVariable(variable, range);
280 280 graphWidget->setYRange(variable);
281 281
282 282 return graphWidget;
283 283 }
284 284
285 285 VisualizationGraphWidget *
286 286 VisualizationZoneWidget::createGraph(const std::vector<std::shared_ptr<Variable> > variables, int index)
287 287 {
288 288 if (variables.empty()) {
289 289 return nullptr;
290 290 }
291 291
292 292 auto graphWidget = createGraph(variables.front(), index);
293 293 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
294 294 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
295 295 }
296 296
297 297 return graphWidget;
298 298 }
299 299
300 300 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
301 301 {
302 302 VisualizationGraphWidget *firstGraph = nullptr;
303 303 auto layout = ui->dragDropContainer->layout();
304 304 if (layout->count() > 0) {
305 305 if (auto visualizationGraphWidget
306 306 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
307 307 firstGraph = visualizationGraphWidget;
308 308 }
309 309 }
310 310
311 311 return firstGraph;
312 312 }
313 313
314 314 void VisualizationZoneWidget::closeAllGraphs()
315 315 {
316 316 processGraphs(*ui->dragDropContainer->layout(),
317 317 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
318 318 }
319 319
320 320 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
321 321 {
322 322 if (visitor) {
323 323 visitor->visitEnter(this);
324 324
325 325 // Apply visitor to graph children: widgets different from graphs are not visited (no
326 326 // action)
327 327 processGraphs(
328 328 *ui->dragDropContainer->layout(),
329 329 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
330 330
331 331 visitor->visitLeave(this);
332 332 }
333 333 else {
334 334 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
335 335 }
336 336 }
337 337
338 338 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
339 339 {
340 340 // A tab can always accomodate a variable
341 341 Q_UNUSED(variable);
342 342 return true;
343 343 }
344 344
345 345 bool VisualizationZoneWidget::contains(const Variable &variable) const
346 346 {
347 347 Q_UNUSED(variable);
348 348 return false;
349 349 }
350 350
351 351 QString VisualizationZoneWidget::name() const
352 352 {
353 353 return ui->zoneNameLabel->text();
354 354 }
355 355
356 356 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
357 357 {
358 358 Q_UNUSED(position);
359 359
360 360 auto mimeData = new QMimeData;
361 361 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
362 362
363 363 if (auto firstGraph = this->firstGraph()) {
364 364 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
365 365 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
366 366 }
367 367
368 368 return mimeData;
369 369 }
370 370
371 371 bool VisualizationZoneWidget::isDragAllowed() const
372 372 {
373 373 return true;
374 374 }
375 375
376 376 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
377 377 const QPointF &plotPosition,
378 378 VisualizationGraphWidget *graphWidget)
379 379 {
380 380 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
381 381 VisualizationGraphWidget &processedGraph) {
382 382
383 383 switch (sqpApp->plotsCursorMode()) {
384 384 case SqpApplication::PlotsCursorMode::Vertical:
385 385 processedGraph.removeHorizontalCursor();
386 386 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
387 387 break;
388 388 case SqpApplication::PlotsCursorMode::Temporal:
389 389 processedGraph.addVerticalCursor(plotPosition.x());
390 390 processedGraph.removeHorizontalCursor();
391 391 break;
392 392 case SqpApplication::PlotsCursorMode::Horizontal:
393 393 processedGraph.removeVerticalCursor();
394 394 if (&processedGraph == graphWidget) {
395 395 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
396 396 }
397 397 else {
398 398 processedGraph.removeHorizontalCursor();
399 399 }
400 400 break;
401 401 case SqpApplication::PlotsCursorMode::Cross:
402 402 if (&processedGraph == graphWidget) {
403 403 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
404 404 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
405 405 }
406 406 else {
407 407 processedGraph.removeHorizontalCursor();
408 408 processedGraph.removeVerticalCursor();
409 409 }
410 410 break;
411 411 case SqpApplication::PlotsCursorMode::NoCursor:
412 412 processedGraph.removeHorizontalCursor();
413 413 processedGraph.removeVerticalCursor();
414 414 break;
415 415 }
416 416
417 417
418 418 });
419 419 }
420 420
421 421 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
422 422 {
423 423 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
424 424 processedGraph.removeHorizontalCursor();
425 425 processedGraph.removeVerticalCursor();
426 426 });
427 427 }
428 428
429 429 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
430 430 {
431 431 // Closes graphs in the zone
432 432 processGraphs(*ui->dragDropContainer->layout(),
433 433 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
434 434
435 435 // Delete synchronization group from variable controller
436 436 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
437 437 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
438 438
439 439 QWidget::closeEvent(event);
440 440 }
441 441
442 442 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
443 443 {
444 444 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
445 445 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
446 446 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
447 447 }
448 448
449 449 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
450 450 {
451 451 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
452 452 Q_ARG(std::shared_ptr<Variable>, variable),
453 453 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
454 454 }
455 455
456 456 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
457 457 {
458 458 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
459 459 impl->dropGraph(index, this);
460 460 }
461 461 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
462 462 auto variables = sqpApp->variableController().variables(
463 mimeData->data(MIME_TYPE_VARIABLE_LIST));
463 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
464 464 impl->dropVariables(variables, index, this);
465 465 }
466 466 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
467 467 auto products = sqpApp->dataSourceController().productsDataForMimeData(
468 468 mimeData->data(MIME_TYPE_PRODUCT_LIST));
469 469 impl->dropProducts(products, index, this);
470 470 }
471 471 else {
472 472 qCWarning(LOG_VisualizationZoneWidget())
473 473 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
474 474 }
475 475 }
476 476
477 477 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
478 478 const QMimeData *mimeData)
479 479 {
480 480 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
481 481 if (!graphWidget) {
482 482 qCWarning(LOG_VisualizationZoneWidget())
483 483 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
484 484 "drop aborted");
485 485 Q_ASSERT(false);
486 486 return;
487 487 }
488 488
489 489 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
490 490 auto variables = sqpApp->variableController().variables(
491 mimeData->data(MIME_TYPE_VARIABLE_LIST));
491 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
492 492 for (const auto &var : variables) {
493 493 graphWidget->addVariable(var, graphWidget->graphRange());
494 494 }
495 495 }
496 496 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
497 497 auto products = sqpApp->dataSourceController().productsDataForMimeData(
498 498 mimeData->data(MIME_TYPE_PRODUCT_LIST));
499 499
500 500 auto context = new QObject{this};
501 501 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
502 502 [this, graphWidget, context](auto variable) {
503 503 graphWidget->addVariable(variable, graphWidget->graphRange());
504 504 delete context; // removes the connection
505 505 },
506 506 Qt::QueuedConnection);
507 507
508 508 auto productData = products.first().toHash();
509 509 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
510 510 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
511 511 }
512 512 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
513 513 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
514 514 graphWidget->setGraphRange(range);
515 515 }
516 516 else {
517 517 qCWarning(LOG_VisualizationZoneWidget())
518 518 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
519 519 }
520 520 }
521 521
522 522 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
523 523 int index, VisualizationZoneWidget *zoneWidget)
524 524 {
525 525 auto &helper = sqpApp->dragDropGuiController();
526 526
527 527 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
528 528 if (!graphWidget) {
529 529 qCWarning(LOG_VisualizationZoneWidget())
530 530 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
531 531 "found or invalid.");
532 532 Q_ASSERT(false);
533 533 return;
534 534 }
535 535
536 536 auto parentDragDropContainer
537 537 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
538 538 if (!parentDragDropContainer) {
539 539 qCWarning(LOG_VisualizationZoneWidget())
540 540 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
541 541 "the dropped graph is not found.");
542 542 Q_ASSERT(false);
543 543 return;
544 544 }
545 545
546 546 const auto &variables = graphWidget->variables();
547 547
548 548 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) {
549 549 // The drop didn't occur in the same zone
550 550
551 551 // Abort the requests for the variables (if any)
552 552 // Commented, because it's not sure if it's needed or not
553 553 // for (const auto& var : variables)
554 554 //{
555 555 // sqpApp->variableController().onAbortProgressRequested(var);
556 556 //}
557 557
558 558 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
559 559 auto nbGraph = parentDragDropContainer->countDragWidget();
560 560 if (nbGraph == 1) {
561 561 // This is the only graph in the previous zone, close the zone
562 562 helper.delayedCloseWidget(previousParentZoneWidget);
563 563 }
564 564 else {
565 565 // Close the graph
566 566 helper.delayedCloseWidget(graphWidget);
567 567 }
568 568
569 569 // Creates the new graph in the zone
570 570 auto newGraphWidget = zoneWidget->createGraph(variables, index);
571 571 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
572 572 }
573 573 else {
574 574 // The drop occurred in the same zone or the graph is empty
575 575 // Simple move of the graph, no variable operation associated
576 576 parentDragDropContainer->layout()->removeWidget(graphWidget);
577 577
578 578 if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
579 579 // The graph is empty and dropped in a different zone.
580 580 // Take the range of the first graph in the zone (if existing).
581 581 auto layout = zoneWidget->ui->dragDropContainer->layout();
582 582 if (layout->count() > 0) {
583 583 if (auto visualizationGraphWidget
584 584 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
585 585 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
586 586 }
587 587 }
588 588 }
589 589
590 590 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
591 591 }
592 592 }
593 593
594 594 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
595 595 const std::vector<std::shared_ptr<Variable> > &variables, int index,
596 596 VisualizationZoneWidget *zoneWidget)
597 597 {
598 598 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
599 599 // compatible variable here
600 600 if (variables.size() > 1) {
601 601 return;
602 602 }
603 603 zoneWidget->createGraph(variables, index);
604 604 }
605 605
606 606 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
607 607 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
608 608 {
609 609 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
610 610 // compatible variable here
611 611 if (productsData.count() != 1) {
612 612 qCWarning(LOG_VisualizationZoneWidget())
613 613 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
614 614 "aborted.");
615 615 return;
616 616 }
617 617
618 618 auto context = new QObject{zoneWidget};
619 619 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
620 620 [this, index, zoneWidget, context](auto variable) {
621 621 zoneWidget->createGraph(variable, index);
622 622 delete context; // removes the connection
623 623 },
624 624 Qt::QueuedConnection);
625 625
626 626 auto productData = productsData.first().toHash();
627 627 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
628 628 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
629 629 }
@@ -1,41 +1,45
1 1 #ifndef SCIQLOP_COSINUSPROVIDER_H
2 2 #define SCIQLOP_COSINUSPROVIDER_H
3 3
4 4 #include "MockPluginGlobal.h"
5 5
6 6 #include <Data/IDataProvider.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QUuid>
10 10
11 11 #include <QHash>
12 12 Q_DECLARE_LOGGING_CATEGORY(LOG_CosinusProvider)
13 13
14 14 /**
15 15 * @brief The CosinusProvider class is an example of how a data provider can generate data
16 16 */
17 17 class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider {
18 18 public:
19 19 std::shared_ptr<IDataProvider> clone() const override;
20 20
21 21 /// @sa IDataProvider::requestDataLoading(). The current impl isn't thread safe.
22 22 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
23 23
24 24
25 virtual IDataSeries* getData(const DataProviderParameters &parameters) override;
26
25 27 /// @sa IDataProvider::requestDataAborting(). The current impl isn't thread safe.
26 28 void requestDataAborting(QUuid acqIdentifier) override;
27 29
28 30
29 31 /// Provide data
30 32 std::shared_ptr<IDataSeries> provideDataSeries(const DateTimeRange &dataRangeRequested,
31 33 const QVariantHash &data);
32 34
33 35
34 36 private:
35 37 std::shared_ptr<IDataSeries>
36 38 retrieveData(QUuid acqIdentifier, const DateTimeRange &dataRangeRequested, const QVariantHash &data);
37 39
40 IDataSeries* _generate(const DateTimeRange &range, const QVariantHash &metaData);
41
38 42 QHash<QUuid, bool> m_VariableToEnableProvider;
39 43 };
40 44
41 45 #endif // SCIQLOP_COSINUSPROVIDER_H
@@ -1,291 +1,332
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 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
17 17
18 18 namespace {
19 19
20 20 /// Number of bands generated for a spectrogram
21 21 const auto SPECTROGRAM_NUMBER_BANDS = 30;
22 22
23 23 /// Bands for which to generate NaN values for a spectrogram
24 24 const auto SPECTROGRAM_NAN_BANDS = std::set<int>{1, 3, 10, 20};
25 25
26 26 /// Bands for which to generate zeros for a spectrogram
27 27 const auto SPECTROGRAM_ZERO_BANDS = std::set<int>{2, 15, 19, 29};
28 28
29 29 /// Abstract cosinus type
30 30 struct ICosinusType {
31 31 virtual ~ICosinusType() = default;
32 32 /// @return the number of components generated for the type
33 virtual int componentCount() const = 0;
33 virtual std::size_t componentCount() const = 0;
34 34 /// @return the data series created for the type
35 virtual std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData,
35 virtual IDataSeries* createDataSeries(std::vector<double> xAxisData,
36 36 std::vector<double> valuesData) const = 0;
37 37 /// Generates values (one value per component)
38 38 /// @param x the x-axis data used to generate values
39 39 /// @param values the vector in which to insert the generated values
40 40 /// @param dataIndex the index of insertion of the generated values
41 41 ///
42 42 virtual void generateValues(double x, std::vector<double> &values, int dataIndex) const = 0;
43 43 };
44 44
45 45 struct ScalarCosinus : public ICosinusType {
46 int componentCount() const override { return 1; }
46 std::size_t componentCount() const override { return 1; }
47 47
48 std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData,
48 IDataSeries* createDataSeries(std::vector<double> xAxisData,
49 49 std::vector<double> valuesData) const override
50 50 {
51 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
51 return new ScalarSeries(std::move(xAxisData), std::move(valuesData),
52 52 Unit{QStringLiteral("t"), true}, Unit{});
53 53 }
54 54
55 55 void generateValues(double x, std::vector<double> &values, int dataIndex) const override
56 56 {
57 57 values[dataIndex] = std::cos(x);
58 58 }
59 59 };
60 60
61 61 struct SpectrogramCosinus : public ICosinusType {
62 62 /// Ctor with y-axis
63 63 explicit SpectrogramCosinus(std::vector<double> yAxisData, Unit yAxisUnit, Unit valuesUnit)
64 64 : m_YAxisData{std::move(yAxisData)},
65 65 m_YAxisUnit{std::move(yAxisUnit)},
66 66 m_ValuesUnit{std::move(valuesUnit)}
67 67 {
68 68 }
69 69
70 int componentCount() const override { return m_YAxisData.size(); }
70 std::size_t componentCount() const override { return m_YAxisData.size(); }
71 71
72 std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData,
72 IDataSeries* createDataSeries(std::vector<double> xAxisData,
73 73 std::vector<double> valuesData) const override
74 74 {
75 return std::make_shared<SpectrogramSeries>(
75 return new SpectrogramSeries(
76 76 std::move(xAxisData), m_YAxisData, std::move(valuesData),
77 77 Unit{QStringLiteral("t"), true}, m_YAxisUnit, m_ValuesUnit);
78 78 }
79 79
80 80 void generateValues(double x, std::vector<double> &values, int dataIndex) const override
81 81 {
82 82 auto componentCount = this->componentCount();
83 83 for (int i = 0; i < componentCount; ++i) {
84 84 auto y = m_YAxisData[i];
85 85
86 86 double value;
87 87
88 88 if (SPECTROGRAM_ZERO_BANDS.find(y) != SPECTROGRAM_ZERO_BANDS.end()) {
89 89 value = 0.;
90 90 }
91 91 else if (SPECTROGRAM_NAN_BANDS.find(y) != SPECTROGRAM_NAN_BANDS.end()) {
92 92 value = std::numeric_limits<double>::quiet_NaN();
93 93 }
94 94 else {
95 95 // Generates value for non NaN/zero bands
96 96 auto r = 3 * std::sqrt(x * x + y * y) + 1e-2;
97 97 value = 2 * x * (std::cos(r + 2) / r - std::sin(r + 2) / r);
98 98 }
99 99
100 100 values[componentCount * dataIndex + i] = value;
101 101 }
102 102 }
103 103
104 104 std::vector<double> m_YAxisData;
105 105 Unit m_YAxisUnit;
106 106 Unit m_ValuesUnit;
107 107 };
108 108
109 109 struct VectorCosinus : public ICosinusType {
110 int componentCount() const override { return 3; }
110 std::size_t componentCount() const override { return 3; }
111 111
112 std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData,
112 IDataSeries* createDataSeries(std::vector<double> xAxisData,
113 113 std::vector<double> valuesData) const override
114 114 {
115 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(valuesData),
115 return new VectorSeries(std::move(xAxisData), std::move(valuesData),
116 116 Unit{QStringLiteral("t"), true}, Unit{});
117 117 }
118 118
119 119 void generateValues(double x, std::vector<double> &values, int dataIndex) const override
120 120 {
121 121 // Generates value for each component: cos(x), cos(x)/2, cos(x)/3
122 122 auto xValue = std::cos(x);
123 123 auto componentCount = this->componentCount();
124 124 for (auto i = 0; i < componentCount; ++i) {
125 125 values[componentCount * dataIndex + i] = xValue / (i + 1);
126 126 }
127 127 }
128 128 };
129 129
130 130 /// Converts string to cosinus type
131 131 /// @return the cosinus type if the string could be converted, nullptr otherwise
132 132 std::unique_ptr<ICosinusType> cosinusType(const QString &type) noexcept
133 133 {
134 134 if (type.compare(QStringLiteral("scalar"), Qt::CaseInsensitive) == 0) {
135 135 return std::make_unique<ScalarCosinus>();
136 136 }
137 137 else if (type.compare(QStringLiteral("spectrogram"), Qt::CaseInsensitive) == 0) {
138 138 // Generates default y-axis data for spectrogram [0., 1., 2., ...]
139 139 std::vector<double> yAxisData(SPECTROGRAM_NUMBER_BANDS);
140 140 std::iota(yAxisData.begin(), yAxisData.end(), 0.);
141 141
142 142 return std::make_unique<SpectrogramCosinus>(std::move(yAxisData), Unit{"eV"},
143 143 Unit{"eV/(cm^2-s-sr-eV)"});
144 144 }
145 145 else if (type.compare(QStringLiteral("vector"), Qt::CaseInsensitive) == 0) {
146 146 return std::make_unique<VectorCosinus>();
147 147 }
148 148 else {
149 149 return nullptr;
150 150 }
151 151 }
152 152
153 153 } // namespace
154 154
155 155 std::shared_ptr<IDataProvider> CosinusProvider::clone() const
156 156 {
157 157 // No copy is made in clone
158 158 return std::make_shared<CosinusProvider>();
159 159 }
160 160
161 161 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
162 162 const DateTimeRange &dataRangeRequested,
163 163 const QVariantHash &data)
164 164 {
165 165 // TODO: Add Mutex
166 166 auto dataIndex = 0;
167 167
168 168 // Retrieves cosinus type
169 169 auto typeVariant = data.value(COSINUS_TYPE_KEY, COSINUS_TYPE_DEFAULT_VALUE);
170 170 if (!typeVariant.canConvert<QString>()) {
171 171 qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: invalid type");
172 172 return nullptr;
173 173 }
174 174
175 175 auto type = cosinusType(typeVariant.toString());
176 176 if (!type) {
177 177 qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: unknown type");
178 178 return nullptr;
179 179 }
180 180
181 181 // Retrieves frequency
182 182 auto freqVariant = data.value(COSINUS_FREQUENCY_KEY, COSINUS_FREQUENCY_DEFAULT_VALUE);
183 183 if (!freqVariant.canConvert<double>()) {
184 184 qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: invalid frequency");
185 185 return nullptr;
186 186 }
187 187
188 188 // Gets the timerange from the parameters
189 189 double freq = freqVariant.toDouble();
190 190 double start = std::ceil(dataRangeRequested.m_TStart * freq);
191 191 double end = std::floor(dataRangeRequested.m_TEnd * freq);
192 192
193 193 // We assure that timerange is valid
194 194 if (end < start) {
195 195 std::swap(start, end);
196 196 }
197 197
198 198 // Generates scalar series containing cosinus values (one value per second, end value is
199 199 // included)
200 200 auto dataCount = end - start + 1;
201 201
202 202 // Number of components (depending on the cosinus type)
203 203 auto componentCount = type->componentCount();
204 204
205 205 auto xAxisData = std::vector<double>{};
206 206 xAxisData.resize(dataCount);
207 207
208 208 auto valuesData = std::vector<double>{};
209 209 valuesData.resize(dataCount * componentCount);
210 210
211 211 int progress = 0;
212 212 auto progressEnd = dataCount;
213 213 for (auto time = start; time <= end; ++time, ++dataIndex) {
214 214 auto it = m_VariableToEnableProvider.find(acqIdentifier);
215 215 if (it != m_VariableToEnableProvider.end() && it.value()) {
216 216 const auto x = time / freq;
217 217
218 218 xAxisData[dataIndex] = x;
219 219
220 220 // Generates values (depending on the type)
221 221 type->generateValues(x, valuesData, dataIndex);
222 222
223 223 // progression
224 224 int currentProgress = (time - start) * 100.0 / progressEnd;
225 225 if (currentProgress != progress) {
226 226 progress = currentProgress;
227 227
228 228 emit dataProvidedProgress(acqIdentifier, progress);
229 229 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::retrieveData"
230 230 << QThread::currentThread()->objectName()
231 231 << progress;
232 232 // NOTE: Try to use multithread if possible
233 233 }
234 234 }
235 235 else {
236 236 if (!it.value()) {
237 237 qCDebug(LOG_CosinusProvider())
238 238 << "CosinusProvider::retrieveData: ARRET De l'acquisition detecté"
239 239 << end - time;
240 240 }
241 241 }
242 242 }
243 243 if (progress != 100) {
244 244 // We can close progression beacause all data has been retrieved
245 245 emit dataProvidedProgress(acqIdentifier, 100);
246 246 }
247 return std::shared_ptr<IDataSeries>(type->createDataSeries(std::move(xAxisData), std::move(valuesData)));
248 }
249
250 IDataSeries *CosinusProvider::_generate(const DateTimeRange &range, const QVariantHash &metaData)
251 {
252 auto dataIndex = 0;
253
254 // Retrieves cosinus type
255 auto typeVariant = metaData.value(COSINUS_TYPE_KEY, COSINUS_TYPE_DEFAULT_VALUE);
256 auto type = cosinusType(typeVariant.toString());
257 auto freqVariant = metaData.value(COSINUS_FREQUENCY_KEY, COSINUS_FREQUENCY_DEFAULT_VALUE);
258 double freq = freqVariant.toDouble();
259 double start = std::ceil(range.m_TStart * freq);
260 double end = std::floor(range.m_TEnd * freq);
261 if (end < start) {
262 std::swap(start, end);
263 }
264 std::size_t dataCount = static_cast<std::size_t>(end - start + 1);
265 std::size_t componentCount = type->componentCount();
266
267 auto xAxisData = std::vector<double>{};
268 xAxisData.resize(dataCount);
269
270 auto valuesData = std::vector<double>{};
271 valuesData.resize(dataCount * componentCount);
272
273 int progress = 0;
274 auto progressEnd = dataCount;
275 for (auto time = start; time <= end; ++time, ++dataIndex)
276 {
277 const auto x = time / freq;
278 xAxisData[dataIndex] = x;
279 // Generates values (depending on the type)
280 type->generateValues(x, valuesData, dataIndex);
281 }
247 282 return type->createDataSeries(std::move(xAxisData), std::move(valuesData));
248 283 }
249 284
250 285 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
251 286 const DataProviderParameters &parameters)
252 287 {
253 288 // TODO: Add Mutex
254 289 m_VariableToEnableProvider[acqIdentifier] = true;
255 290 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
256 291 << QThread::currentThread()->objectName();
257 292 // NOTE: Try to use multithread if possible
258 293 const auto times = parameters.m_Times;
259 294
260 295 for (const auto &dateTime : qAsConst(times)) {
261 296 if (m_VariableToEnableProvider[acqIdentifier]) {
262 297 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime, parameters.m_Data);
263 298 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
264 299 }
265 300 }
266 301 }
267 302
303 IDataSeries* CosinusProvider::getData(const DataProviderParameters &parameters)
304 {
305 return _generate(parameters.m_Times.front(),parameters.m_Data);
306 }
307
308
268 309 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
269 310 {
270 311 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
271 312 << QThread::currentThread()->objectName();
272 313 auto it = m_VariableToEnableProvider.find(acqIdentifier);
273 314 if (it != m_VariableToEnableProvider.end()) {
274 315 it.value() = false;
275 316 }
276 317 else {
277 318 qCDebug(LOG_CosinusProvider())
278 319 << tr("Aborting progression of inexistant identifier detected !!!");
279 320 }
280 321 }
281 322
282 323 std::shared_ptr<IDataSeries> CosinusProvider::provideDataSeries(const DateTimeRange &dataRangeRequested,
283 324 const QVariantHash &data)
284 325 {
285 326 auto uid = QUuid::createUuid();
286 327 m_VariableToEnableProvider[uid] = true;
287 328 auto dataSeries = this->retrieveData(uid, dataRangeRequested, data);
288 329
289 330 m_VariableToEnableProvider.remove(uid);
290 331 return dataSeries;
291 332 }
General Comments 0
You need to be logged in to leave comments. Login now