##// END OF EJS Templates
Merge branch 'feature/InteractionModesAndCursors' into develop
trabillard -
r1010:cf3e9de4d0c6 merge
parent child
Show More
@@ -0,0 +1,26
1 #ifndef SCIQLOP_VISUALIZATIONCURSORITEM_H
2 #define SCIQLOP_VISUALIZATIONCURSORITEM_H
3
4 #include <Common/spimpl.h>
5 #include <SqpApplication.h>
6
7 class QCustomPlot;
8
9 class VisualizationCursorItem {
10 public:
11 VisualizationCursorItem(QCustomPlot *plot);
12
13 void setVisible(bool value);
14 bool isVisible() const;
15
16 void setPosition(double value);
17 void setAbsolutePosition(double value);
18 void setOrientation(Qt::Orientation orientation);
19 void setLabelText(const QString &text);
20
21 private:
22 class VisualizationCursorItemPrivate;
23 spimpl::unique_impl_ptr<VisualizationCursorItemPrivate> impl;
24 };
25
26 #endif // SCIQLOP_VISUALIZATIONCURSORITEM_H
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,163
1 #include <Common/DateUtils.h>
2 #include <Visualization/VisualizationCursorItem.h>
3 #include <Visualization/qcustomplot.h>
4
5 /// Width of the cursor in pixel
6 const auto CURSOR_WIDTH = 3;
7
8 /// Color of the cursor in the graph
9 const auto CURSOR_COLOR = QColor{68, 114, 196};
10
11 /// Line style of the cursor in the graph
12 auto CURSOR_PEN_STYLE = Qt::DotLine;
13
14 struct VisualizationCursorItem::VisualizationCursorItemPrivate {
15
16 QCustomPlot *m_Plot = nullptr;
17
18 QCPItemStraightLine *m_LineItem = nullptr;
19 QCPItemText *m_LabelItem = nullptr;
20
21 Qt::Orientation m_Orientation;
22 double m_Position = 0.0;
23 bool m_IsAbsolutePosition = false;
24 QString m_LabelText;
25
26 explicit VisualizationCursorItemPrivate(QCustomPlot *plot)
27 : m_Plot(plot), m_Orientation(Qt::Vertical)
28 {
29 }
30
31 void updateOrientation()
32 {
33 if (m_LineItem) {
34 switch (m_Orientation) {
35 case Qt::Vertical:
36 m_LineItem->point1->setTypeX(m_IsAbsolutePosition
37 ? QCPItemPosition::ptAbsolute
38 : QCPItemPosition::ptPlotCoords);
39 m_LineItem->point1->setTypeY(QCPItemPosition::ptAxisRectRatio);
40 m_LineItem->point2->setTypeX(m_IsAbsolutePosition
41 ? QCPItemPosition::ptAbsolute
42 : QCPItemPosition::ptPlotCoords);
43 m_LineItem->point2->setTypeY(QCPItemPosition::ptAxisRectRatio);
44 m_LabelItem->setPositionAlignment(Qt::AlignLeft | Qt::AlignTop);
45 break;
46 case Qt::Horizontal:
47 m_LineItem->point1->setTypeX(QCPItemPosition::ptAxisRectRatio);
48 m_LineItem->point1->setTypeY(m_IsAbsolutePosition
49 ? QCPItemPosition::ptAbsolute
50 : QCPItemPosition::ptPlotCoords);
51 m_LineItem->point2->setTypeX(QCPItemPosition::ptAxisRectRatio);
52 m_LineItem->point2->setTypeY(m_IsAbsolutePosition
53 ? QCPItemPosition::ptAbsolute
54 : QCPItemPosition::ptPlotCoords);
55 m_LabelItem->setPositionAlignment(Qt::AlignRight | Qt::AlignBottom);
56 }
57 }
58 }
59
60 void updateCursorPosition()
61 {
62 if (m_LineItem) {
63 switch (m_Orientation) {
64 case Qt::Vertical:
65 m_LineItem->point1->setCoords(m_Position, 0);
66 m_LineItem->point2->setCoords(m_Position, 1);
67 m_LabelItem->position->setCoords(5, 5);
68 break;
69 case Qt::Horizontal:
70 m_LineItem->point1->setCoords(1, m_Position);
71 m_LineItem->point2->setCoords(0, m_Position);
72 m_LabelItem->position->setCoords(-5, -5);
73 }
74 }
75 }
76
77 void updateLabelText()
78 {
79 if (m_LabelItem) {
80 m_LabelItem->setText(m_LabelText);
81 }
82 }
83 };
84
85 VisualizationCursorItem::VisualizationCursorItem(QCustomPlot *plot)
86 : impl{spimpl::make_unique_impl<VisualizationCursorItemPrivate>(plot)}
87 {
88 }
89
90 void VisualizationCursorItem::setVisible(bool value)
91 {
92 if (value != isVisible()) {
93
94 if (value) {
95 Q_ASSERT(!impl->m_LineItem && !impl->m_Plot);
96
97 impl->m_LineItem = new QCPItemStraightLine{impl->m_Plot};
98 auto pen = QPen{CURSOR_PEN_STYLE};
99 pen.setColor(CURSOR_COLOR);
100 pen.setWidth(CURSOR_WIDTH);
101 impl->m_LineItem->setPen(pen);
102 impl->m_LineItem->setSelectable(false);
103
104 impl->m_LabelItem = new QCPItemText{impl->m_Plot};
105 impl->m_LabelItem->setColor(CURSOR_COLOR);
106 impl->m_LabelItem->setSelectable(false);
107 impl->m_LabelItem->position->setParentAnchor(impl->m_LineItem->point1);
108 impl->m_LabelItem->position->setTypeX(QCPItemPosition::ptAbsolute);
109 impl->m_LabelItem->position->setTypeY(QCPItemPosition::ptAbsolute);
110
111 auto font = impl->m_LabelItem->font();
112 font.setPointSize(10);
113 font.setBold(true);
114 impl->m_LabelItem->setFont(font);
115
116 impl->updateOrientation();
117 impl->updateLabelText();
118 impl->updateCursorPosition();
119 }
120 else {
121 Q_ASSERT(impl->m_LineItem && impl->m_Plot);
122
123 // Note: the items are destroyed by QCustomPlot in removeItem
124 impl->m_Plot->removeItem(impl->m_LineItem);
125 impl->m_LineItem = nullptr;
126 impl->m_Plot->removeItem(impl->m_LabelItem);
127 impl->m_LabelItem = nullptr;
128 }
129 }
130 }
131
132 bool VisualizationCursorItem::isVisible() const
133 {
134 return impl->m_LineItem != nullptr;
135 }
136
137 void VisualizationCursorItem::setPosition(double value)
138 {
139 impl->m_Position = value;
140 impl->m_IsAbsolutePosition = false;
141 impl->updateLabelText();
142 impl->updateCursorPosition();
143 }
144
145 void VisualizationCursorItem::setAbsolutePosition(double value)
146 {
147 setPosition(value);
148 impl->m_IsAbsolutePosition = true;
149 }
150
151 void VisualizationCursorItem::setOrientation(Qt::Orientation orientation)
152 {
153 impl->m_Orientation = orientation;
154 impl->updateLabelText();
155 impl->updateOrientation();
156 impl->updateCursorPosition();
157 }
158
159 void VisualizationCursorItem::setLabelText(const QString &text)
160 {
161 impl->m_LabelText = text;
162 impl->updateLabelText();
163 }
@@ -1,254 +1,353
1 1 /*------------------------------------------------------------------------------
2 2 -- This file is a part of the SciQLop Software
3 3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 4 --
5 5 -- This program is free software; you can redistribute it and/or modify
6 6 -- it under the terms of the GNU General Public License as published by
7 7 -- the Free Software Foundation; either version 2 of the License, or
8 8 -- (at your option) any later version.
9 9 --
10 10 -- This program is distributed in the hope that it will be useful,
11 11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 -- GNU General Public License for more details.
14 14 --
15 15 -- You should have received a copy of the GNU General Public License
16 16 -- along with this program; if not, write to the Free Software
17 17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 18 -------------------------------------------------------------------------------*/
19 19 /*-- Author : Alexis Jeandet
20 20 -- Mail : alexis.jeandet@member.fsf.org
21 21 ----------------------------------------------------------------------------*/
22 22 #include "MainWindow.h"
23 23 #include "ui_MainWindow.h"
24 24
25 25 #include <DataSource/DataSourceController.h>
26 26 #include <DataSource/DataSourceWidget.h>
27 27 #include <Settings/SqpSettingsDialog.h>
28 28 #include <Settings/SqpSettingsGeneralWidget.h>
29 29 #include <SidePane/SqpSidePane.h>
30 30 #include <SqpApplication.h>
31 31 #include <Time/TimeController.h>
32 32 #include <TimeWidget/TimeWidget.h>
33 33 #include <Variable/Variable.h>
34 34 #include <Variable/VariableController.h>
35 35 #include <Visualization/VisualizationController.h>
36 36
37 37 #include <QAction>
38 38 #include <QDate>
39 39 #include <QDir>
40 40 #include <QFileDialog>
41 41 #include <QToolBar>
42 42 #include <QToolButton>
43 43 #include <memory.h>
44 44
45 45 #include "iostream"
46 46
47 47 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
48 48
49 49 namespace {
50 50 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
51 51 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
52 52 const auto VIEWPLITTERINDEX = 2;
53 53 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
54 54 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
55 55 }
56 56
57 57 class MainWindow::MainWindowPrivate {
58 58 public:
59 59 explicit MainWindowPrivate(MainWindow *mainWindow)
60 60 : m_LastOpenLeftInspectorSize{},
61 61 m_LastOpenRightInspectorSize{},
62 62 m_GeneralSettingsWidget{new SqpSettingsGeneralWidget{mainWindow}},
63 63 m_SettingsDialog{new SqpSettingsDialog{mainWindow}}
64 64 {
65 65 }
66 66
67 67 QSize m_LastOpenLeftInspectorSize;
68 68 QSize m_LastOpenRightInspectorSize;
69 69 /// General settings widget. MainWindow has the ownership
70 70 SqpSettingsGeneralWidget *m_GeneralSettingsWidget;
71 71 /// Settings dialog. MainWindow has the ownership
72 72 SqpSettingsDialog *m_SettingsDialog;
73 73 };
74 74
75 75 MainWindow::MainWindow(QWidget *parent)
76 76 : QMainWindow{parent},
77 77 m_Ui{new Ui::MainWindow},
78 78 impl{spimpl::make_unique_impl<MainWindowPrivate>(this)}
79 79 {
80 80 m_Ui->setupUi(this);
81 81
82 82 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
83 83 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
84 84
85 85
86 86 auto leftSidePane = m_Ui->leftInspectorSidePane->sidePane();
87 87 auto openLeftInspectorAction = new QAction{QIcon{
88 88 ":/icones/previous.png",
89 89 },
90 90 tr("Show/hide the left inspector"), this};
91 91
92 92
93 93 auto spacerLeftTop = new QWidget{};
94 94 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
95 95
96 96 auto spacerLeftBottom = new QWidget{};
97 97 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
98 98
99 99 leftSidePane->addWidget(spacerLeftTop);
100 100 leftSidePane->addAction(openLeftInspectorAction);
101 101 leftSidePane->addWidget(spacerLeftBottom);
102 102
103 103
104 104 auto rightSidePane = m_Ui->rightInspectorSidePane->sidePane();
105 105 auto openRightInspectorAction = new QAction{QIcon{
106 106 ":/icones/next.png",
107 107 },
108 108 tr("Show/hide the right inspector"), this};
109 109
110 110 auto spacerRightTop = new QWidget{};
111 111 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
112 112
113 113 auto spacerRightBottom = new QWidget{};
114 114 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
115 115
116 116 rightSidePane->addWidget(spacerRightTop);
117 117 rightSidePane->addAction(openRightInspectorAction);
118 118 rightSidePane->addWidget(spacerRightBottom);
119 119
120 120 openLeftInspectorAction->setCheckable(true);
121 121 openRightInspectorAction->setCheckable(true);
122 122
123 123 auto openInspector = [this](bool checked, bool right, auto action) {
124 124
125 125 action->setIcon(QIcon{(checked xor right) ? ":/icones/next.png" : ":/icones/previous.png"});
126 126
127 127 auto &lastInspectorSize
128 128 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
129 129
130 130 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
131 131 : m_Ui->leftMainInspectorWidget->size();
132 132
133 133 // Update of the last opened geometry
134 134 if (checked) {
135 135 lastInspectorSize = nextInspectorSize;
136 136 }
137 137
138 138 auto startSize = lastInspectorSize;
139 139 auto endSize = startSize;
140 140 endSize.setWidth(0);
141 141
142 142 auto splitterInspectorIndex
143 143 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
144 144
145 145 auto currentSizes = m_Ui->splitter->sizes();
146 146 if (checked) {
147 147 // adjust sizes individually here, e.g.
148 148 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
149 149 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
150 150 m_Ui->splitter->setSizes(currentSizes);
151 151 }
152 152 else {
153 153 // adjust sizes individually here, e.g.
154 154 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
155 155 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
156 156 m_Ui->splitter->setSizes(currentSizes);
157 157 }
158 158
159 159 };
160 160
161 161
162 162 connect(openLeftInspectorAction, &QAction::triggered,
163 163 [openInspector, openLeftInspectorAction](bool checked) {
164 164 openInspector(checked, false, openLeftInspectorAction);
165 165 });
166 166 connect(openRightInspectorAction, &QAction::triggered,
167 167 [openInspector, openRightInspectorAction](bool checked) {
168 168 openInspector(checked, true, openRightInspectorAction);
169 169 });
170 170
171 // //// //
172 // Menu //
173 // //// //
171 // //////////////// //
172 // Menu and Toolbar //
173 // //////////////// //
174 174 this->menuBar()->addAction(tr("File"));
175 175 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
176 176 toolsMenu->addAction(tr("Settings..."), [this]() {
177 177 // Loads settings
178 178 impl->m_SettingsDialog->loadSettings();
179 179
180 180 // Open settings dialog and save settings if the dialog is accepted
181 181 if (impl->m_SettingsDialog->exec() == QDialog::Accepted) {
182 182 impl->m_SettingsDialog->saveSettings();
183 183 }
184 184
185 185 });
186 186
187 187 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
188 188
189 189 auto timeWidget = new TimeWidget{};
190 190 mainToolBar->addWidget(timeWidget);
191 191
192 auto actionPointerMode = new QAction{QIcon(":/icones/pointer.png"), "Pointer", this};
193 actionPointerMode->setCheckable(true);
194 actionPointerMode->setChecked(sqpApp->plotsInteractionMode()
195 == SqpApplication::PlotsInteractionMode::None);
196 connect(actionPointerMode, &QAction::triggered,
197 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::None); });
198
199 auto actionZoomMode = new QAction{QIcon(":/icones/zoom.png"), "Zoom", this};
200 actionZoomMode->setCheckable(true);
201 actionZoomMode->setChecked(sqpApp->plotsInteractionMode()
202 == SqpApplication::PlotsInteractionMode::ZoomBox);
203 connect(actionZoomMode, &QAction::triggered, []() {
204 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::ZoomBox);
205 });
206
207 auto actionOrganisationMode = new QAction{QIcon(":/icones/drag.png"), "Organize", this};
208 actionOrganisationMode->setCheckable(true);
209 actionOrganisationMode->setChecked(sqpApp->plotsInteractionMode()
210 == SqpApplication::PlotsInteractionMode::DragAndDrop);
211 connect(actionOrganisationMode, &QAction::triggered, []() {
212 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::DragAndDrop);
213 });
214
215 auto actionZonesMode = new QAction{QIcon(":/icones/rectangle.png"), "Zones", this};
216 actionZonesMode->setCheckable(true);
217 actionZonesMode->setChecked(sqpApp->plotsInteractionMode()
218 == SqpApplication::PlotsInteractionMode::SelectionZones);
219 connect(actionZonesMode, &QAction::triggered, []() {
220 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::SelectionZones);
221 });
222
223 auto modeActionGroup = new QActionGroup{this};
224 modeActionGroup->addAction(actionZoomMode);
225 modeActionGroup->addAction(actionZonesMode);
226 modeActionGroup->addAction(actionOrganisationMode);
227 modeActionGroup->addAction(actionPointerMode);
228 modeActionGroup->setExclusive(true);
229
230 mainToolBar->addSeparator();
231 mainToolBar->addAction(actionPointerMode);
232 mainToolBar->addAction(actionZoomMode);
233 mainToolBar->addAction(actionOrganisationMode);
234 mainToolBar->addAction(actionZonesMode);
235 mainToolBar->addSeparator();
236
237 auto btnCursor = new QToolButton{this};
238 btnCursor->setIcon(QIcon(":/icones/cursor.png"));
239 btnCursor->setText("Cursor");
240 btnCursor->setToolTip("Cursor");
241 btnCursor->setPopupMode(QToolButton::InstantPopup);
242 auto cursorMenu = new QMenu("CursorMenu", this);
243 btnCursor->setMenu(cursorMenu);
244
245 auto noCursorAction = cursorMenu->addAction("No Cursor");
246 noCursorAction->setCheckable(true);
247 noCursorAction->setChecked(sqpApp->plotsCursorMode()
248 == SqpApplication::PlotsCursorMode::NoCursor);
249 connect(noCursorAction, &QAction::triggered,
250 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::NoCursor); });
251
252 cursorMenu->addSeparator();
253 auto verticalCursorAction = cursorMenu->addAction("Vertical Cursor");
254 verticalCursorAction->setCheckable(true);
255 verticalCursorAction->setChecked(sqpApp->plotsCursorMode()
256 == SqpApplication::PlotsCursorMode::Vertical);
257 connect(verticalCursorAction, &QAction::triggered,
258 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Vertical); });
259
260 auto temporalCursorAction = cursorMenu->addAction("Temporal Cursor");
261 temporalCursorAction->setCheckable(true);
262 temporalCursorAction->setChecked(sqpApp->plotsCursorMode()
263 == SqpApplication::PlotsCursorMode::Temporal);
264 connect(temporalCursorAction, &QAction::triggered,
265 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Temporal); });
266
267 auto horizontalCursorAction = cursorMenu->addAction("Horizontal Cursor");
268 horizontalCursorAction->setCheckable(true);
269 horizontalCursorAction->setChecked(sqpApp->plotsCursorMode()
270 == SqpApplication::PlotsCursorMode::Horizontal);
271 connect(horizontalCursorAction, &QAction::triggered,
272 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Horizontal); });
273
274 auto crossCursorAction = cursorMenu->addAction("Cross Cursor");
275 crossCursorAction->setCheckable(true);
276 crossCursorAction->setChecked(sqpApp->plotsCursorMode()
277 == SqpApplication::PlotsCursorMode::Cross);
278 connect(crossCursorAction, &QAction::triggered,
279 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Cross); });
280
281 mainToolBar->addWidget(btnCursor);
282
283 auto cursorModeActionGroup = new QActionGroup{this};
284 cursorModeActionGroup->setExclusive(true);
285 cursorModeActionGroup->addAction(noCursorAction);
286 cursorModeActionGroup->addAction(verticalCursorAction);
287 cursorModeActionGroup->addAction(temporalCursorAction);
288 cursorModeActionGroup->addAction(horizontalCursorAction);
289 cursorModeActionGroup->addAction(crossCursorAction);
290
192 291 // //////// //
193 292 // Settings //
194 293 // //////// //
195 294
196 295 // Registers "general settings" widget to the settings dialog
197 296 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
198 297 impl->m_GeneralSettingsWidget);
199 298
200 299 // /////////// //
201 300 // Connections //
202 301 // /////////// //
203 302
204 303 // Controllers / controllers connections
205 304 connect(&sqpApp->timeController(), SIGNAL(timeUpdated(SqpRange)), &sqpApp->variableController(),
206 305 SLOT(onDateTimeOnSelection(SqpRange)));
207 306
208 307 // Widgets / controllers connections
209 308
210 309 // DataSource
211 310 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
212 311 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
213 312
214 313 // Time
215 314 connect(timeWidget, SIGNAL(timeUpdated(SqpRange)), &sqpApp->timeController(),
216 315 SLOT(onTimeToUpdate(SqpRange)));
217 316
218 317 // Visualization
219 318 connect(&sqpApp->visualizationController(),
220 319 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
221 320 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
222 321
223 322 connect(&sqpApp->visualizationController(),
224 323 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)), m_Ui->view,
225 324 SLOT(onRangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
226 325
227 326 // Widgets / widgets connections
228 327
229 328 // For the following connections, we use DirectConnection to allow each widget that can
230 329 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
231 330 // The order of connections is also important, since it determines the order in which each
232 331 // widget will attach its menu
233 332 connect(
234 333 m_Ui->variableInspectorWidget,
235 334 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
236 335 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
237 336 Qt::DirectConnection);
238 337 }
239 338
240 339 MainWindow::~MainWindow()
241 340 {
242 341 }
243 342
244 343 void MainWindow::changeEvent(QEvent *e)
245 344 {
246 345 QMainWindow::changeEvent(e);
247 346 switch (e->type()) {
248 347 case QEvent::LanguageChange:
249 348 m_Ui->retranslateUi(this);
250 349 break;
251 350 default:
252 351 break;
253 352 }
254 353 }
@@ -1,7 +1,8
1 1 #ifndef SCIQLOP_VISUALIZATIONDEF_H
2 2 #define SCIQLOP_VISUALIZATIONDEF_H
3 3
4 4 /// Minimum height for graph added in zones (in pixels)
5 5 extern const int GRAPH_MINIMUM_HEIGHT;
6 6
7
7 8 #endif // SCIQLOP_VISUALIZATIONDEF_H
@@ -1,57 +1,67
1 1 #ifndef SCIQLOP_SQPAPPLICATION_H
2 2 #define SCIQLOP_SQPAPPLICATION_H
3 3
4 4 #include "SqpApplication.h"
5 5
6 6 #include <QApplication>
7 7 #include <QLoggingCategory>
8 8
9 9 #include <Common/spimpl.h>
10 10
11 11 Q_DECLARE_LOGGING_CATEGORY(LOG_SqpApplication)
12 12
13 13 #if defined(sqpApp)
14 14 #undef sqpApp
15 15 #endif
16 16 #define sqpApp (static_cast<SqpApplication *>(QCoreApplication::instance()))
17 17
18 18 class DataSourceController;
19 19 class NetworkController;
20 20 class TimeController;
21 21 class VariableController;
22 22 class VisualizationController;
23 23 class DragDropHelper;
24 24
25 25 /**
26 26 * @brief The SqpApplication class aims to make the link between SciQlop
27 27 * and its plugins. This is the intermediate class that SciQlop has to use
28 28 * in the way to connect a data source. Please first use load method to initialize
29 29 * a plugin specified by its metadata name (JSON plugin source) then others specifics
30 30 * method will be able to access it.
31 31 * You can load a data source driver plugin then create a data source.
32 32 */
33 33
34 34 class SqpApplication : public QApplication {
35 35 Q_OBJECT
36 36 public:
37 37 explicit SqpApplication(int &argc, char **argv);
38 38 virtual ~SqpApplication();
39 39 void initialize();
40 40
41 41 /// Accessors for the differents sciqlop controllers
42 42 DataSourceController &dataSourceController() noexcept;
43 43 NetworkController &networkController() noexcept;
44 44 TimeController &timeController() noexcept;
45 45 VariableController &variableController() noexcept;
46 46 VisualizationController &visualizationController() noexcept;
47 47
48 48 /// Accessors for the differents sciqlop helpers, these helpers classes are like controllers but
49 49 /// doesn't live in a thread and access gui
50 50 DragDropHelper &dragDropHelper() noexcept;
51 51
52 enum class PlotsInteractionMode { None, ZoomBox, DragAndDrop, SelectionZones };
53
54 enum class PlotsCursorMode { NoCursor, Vertical, Temporal, Horizontal, Cross };
55
56 PlotsInteractionMode plotsInteractionMode() const;
57 void setPlotsInteractionMode(PlotsInteractionMode mode);
58
59 PlotsCursorMode plotsCursorMode() const;
60 void setPlotsCursorMode(PlotsCursorMode mode);
61
52 62 private:
53 63 class SqpApplicationPrivate;
54 64 spimpl::unique_impl_ptr<SqpApplicationPrivate> impl;
55 65 };
56 66
57 67 #endif // SCIQLOP_SQPAPPLICATION_H
@@ -1,109 +1,121
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 15
16 16 class QCPRange;
17 17 class QCustomPlot;
18 18 class SqpRange;
19 19 class Variable;
20 20 class VisualizationZoneWidget;
21 21
22 22 namespace Ui {
23 23 class VisualizationGraphWidget;
24 24 } // namespace Ui
25 25
26 26 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
27 27 Q_OBJECT
28 28
29 29 friend class QCustomPlotSynchronizer;
30 30 friend class VisualizationGraphRenderingDelegate;
31 31
32 32 public:
33 33 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
34 34 virtual ~VisualizationGraphWidget();
35 35
36 36 VisualizationZoneWidget *parentZoneWidget() const noexcept;
37 37
38 38 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
39 39 void enableAcquisition(bool enable);
40 40
41 41 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
42 42
43 43 /// Removes a variable from the graph
44 44 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
45 45
46 46 /// Returns the list of all variables used in the graph
47 47 QList<std::shared_ptr<Variable> > variables() const;
48 48
49 49 /// Sets the y-axis range based on the data of a variable
50 50 void setYRange(std::shared_ptr<Variable> variable);
51 51 SqpRange graphRange() const noexcept;
52 52 void setGraphRange(const SqpRange &range);
53 53
54 54 // IVisualizationWidget interface
55 55 void accept(IVisualizationWidgetVisitor *visitor) override;
56 56 bool canDrop(const Variable &variable) const override;
57 57 bool contains(const Variable &variable) const override;
58 58 QString name() const override;
59 59
60 60 // VisualisationDragWidget
61 61 QMimeData *mimeData() const override;
62 62 bool isDragAllowed() const override;
63 63 void highlightForMerge(bool highlighted) override;
64 64
65 // Cursors
66 /// Adds or moves the vertical cursor at the specified value on the x-axis
67 void addVerticalCursor(double time);
68 /// Adds or moves the vertical cursor at the specified value on the x-axis
69 void addVerticalCursorAtViewportPosition(double position);
70 void removeVerticalCursor();
71 /// Adds or moves the vertical cursor at the specified value on the y-axis
72 void addHorizontalCursor(double value);
73 /// Adds or moves the vertical cursor at the specified value on the y-axis
74 void addHorizontalCursorAtViewportPosition(double position);
75 void removeHorizontalCursor();
76
65 77 signals:
66 78 void synchronize(const SqpRange &range, const SqpRange &oldRange);
67 79 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
68 80 bool synchronise);
69 81
70 82 /// Signal emitted when the variable is about to be removed from the graph
71 83 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
72 84 /// Signal emitted when the variable has been added to the graph
73 85 void variableAdded(std::shared_ptr<Variable> var);
74 86
75 87 protected:
76 88 void closeEvent(QCloseEvent *event) override;
77 89 void enterEvent(QEvent *event) override;
78 90 void leaveEvent(QEvent *event) override;
79 91
80 92 QCustomPlot &plot() noexcept;
81 93
82 94 private:
83 95 Ui::VisualizationGraphWidget *ui;
84 96
85 97 class VisualizationGraphWidgetPrivate;
86 98 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
87 99
88 100 private slots:
89 101 /// Slot called when right clicking on the graph (displays a menu)
90 102 void onGraphMenuRequested(const QPoint &pos) noexcept;
91 103
92 104 /// Rescale the X axe to range parameter
93 105 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
94 106
95 107 /// Slot called when a mouse move was made
96 108 void onMouseMove(QMouseEvent *event) noexcept;
97 109 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
98 110 void onMouseWheel(QWheelEvent *event) noexcept;
99 111 /// Slot called when a mouse press was made, to activate the calibration of a graph
100 112 void onMousePress(QMouseEvent *event) noexcept;
101 113 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
102 114 void onMouseRelease(QMouseEvent *event) noexcept;
103 115
104 116 void onDataCacheVariableUpdated();
105 117
106 118 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
107 119 };
108 120
109 121 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,91 +1,95
1 1 #ifndef SCIQLOP_VISUALIZATIONZONEWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONZONEWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationZoneWidget)
15 15
16 16 namespace Ui {
17 17 class VisualizationZoneWidget;
18 18 } // namespace Ui
19 19
20 20 class Variable;
21 21 class VisualizationGraphWidget;
22 22
23 23 class VisualizationZoneWidget : public VisualizationDragWidget, public IVisualizationWidget {
24 24 Q_OBJECT
25 25
26 26 public:
27 27 explicit VisualizationZoneWidget(const QString &name = {}, QWidget *parent = 0);
28 28 virtual ~VisualizationZoneWidget();
29 29
30 30 /// Adds a graph widget
31 31 void addGraph(VisualizationGraphWidget *graphWidget);
32 32
33 33 /// Inserts a graph widget
34 34 void insertGraph(int index, VisualizationGraphWidget *graphWidget);
35 35
36 36 /**
37 37 * Creates a graph using a variable. The variable will be displayed in the new graph.
38 38 * The graph is added at the end.
39 39 * @param variable the variable for which to create the graph
40 40 * @return the pointer to the created graph
41 41 */
42 42 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable);
43 43
44 44 /**
45 45 * Creates a graph using a variable. The variable will be displayed in the new graph.
46 46 * The graph is inserted at the specified index.
47 47 * @param variable the variable for which to create the graph
48 48 * @param index The index where the graph should be inserted in the layout
49 49 * @return the pointer to the created graph
50 50 */
51 51 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable, int index);
52 52
53 53 /**
54 54 * Creates a graph using a list of variables. The variables will be displayed in the new graph.
55 55 * The graph is inserted at the specified index.
56 56 * @param variables List of variables to be added to the graph
57 57 * @param index The index where the graph should be inserted in the layout
58 58 * @return the pointer to the created graph
59 59 */
60 60 VisualizationGraphWidget *createGraph(const QList<std::shared_ptr<Variable> > variables,
61 61 int index);
62 62
63 63 // IVisualizationWidget interface
64 64 void accept(IVisualizationWidgetVisitor *visitor) override;
65 65 bool canDrop(const Variable &variable) const override;
66 66 bool contains(const Variable &variable) const override;
67 67 QString name() const override;
68 68
69 69 // VisualisationDragWidget
70 70 QMimeData *mimeData() const override;
71 71 bool isDragAllowed() const override;
72 72
73 void notifyMouseMoveInGraph(const QPointF &graphPosition, const QPointF &plotPosition,
74 VisualizationGraphWidget *graphWidget);
75 void notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget);
76
73 77 protected:
74 78 void closeEvent(QCloseEvent *event) override;
75 79
76 80 private:
77 81 Ui::VisualizationZoneWidget *ui;
78 82
79 83 class VisualizationZoneWidgetPrivate;
80 84 spimpl::unique_impl_ptr<VisualizationZoneWidgetPrivate> impl;
81 85
82 86 private slots:
83 87 void onVariableAdded(std::shared_ptr<Variable> variable);
84 88 /// Slot called when a variable is about to be removed from a graph contained in the zone
85 89 void onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable);
86 90
87 91 void dropMimeData(int index, const QMimeData *mimeData);
88 92 void dropMimeDataOnGraph(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
89 93 };
90 94
91 95 #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H
@@ -1,95 +1,96
1 1
2 2 gui_moc_headers = [
3 3 'include/DataSource/DataSourceWidget.h',
4 4 'include/Settings/SqpSettingsDialog.h',
5 5 'include/Settings/SqpSettingsGeneralWidget.h',
6 6 'include/SidePane/SqpSidePane.h',
7 7 'include/SqpApplication.h',
8 8 'include/DragAndDrop/DragDropScroller.h',
9 9 'include/DragAndDrop/DragDropTabSwitcher.h',
10 10 'include/TimeWidget/TimeWidget.h',
11 11 'include/Variable/VariableInspectorWidget.h',
12 12 'include/Variable/RenameVariableDialog.h',
13 13 'include/Visualization/qcustomplot.h',
14 14 'include/Visualization/VisualizationGraphWidget.h',
15 15 'include/Visualization/VisualizationTabWidget.h',
16 16 'include/Visualization/VisualizationWidget.h',
17 17 'include/Visualization/VisualizationZoneWidget.h',
18 18 'include/Visualization/VisualizationDragDropContainer.h',
19 19 'include/Visualization/VisualizationDragWidget.h'
20 20 ]
21 21
22 22 gui_ui_files = [
23 23 'ui/DataSource/DataSourceWidget.ui',
24 24 'ui/Settings/SqpSettingsDialog.ui',
25 25 'ui/Settings/SqpSettingsGeneralWidget.ui',
26 26 'ui/SidePane/SqpSidePane.ui',
27 27 'ui/TimeWidget/TimeWidget.ui',
28 28 'ui/Variable/VariableInspectorWidget.ui',
29 29 'ui/Variable/RenameVariableDialog.ui',
30 30 'ui/Variable/VariableMenuHeaderWidget.ui',
31 31 'ui/Visualization/VisualizationGraphWidget.ui',
32 32 'ui/Visualization/VisualizationTabWidget.ui',
33 33 'ui/Visualization/VisualizationWidget.ui',
34 34 'ui/Visualization/VisualizationZoneWidget.ui'
35 35 ]
36 36
37 37 gui_qresources = ['resources/sqpguiresources.qrc']
38 38
39 39 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
40 40 ui_files : gui_ui_files,
41 41 qresources : gui_qresources)
42 42
43 43 gui_sources = [
44 44 'src/SqpApplication.cpp',
45 45 'src/DragAndDrop/DragDropHelper.cpp',
46 46 'src/DragAndDrop/DragDropScroller.cpp',
47 47 'src/DragAndDrop/DragDropTabSwitcher.cpp',
48 48 'src/Common/ColorUtils.cpp',
49 49 'src/Common/VisualizationDef.cpp',
50 50 'src/DataSource/DataSourceTreeWidgetItem.cpp',
51 51 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
52 52 'src/DataSource/DataSourceWidget.cpp',
53 53 'src/DataSource/DataSourceTreeWidget.cpp',
54 54 'src/Settings/SqpSettingsDialog.cpp',
55 55 'src/Settings/SqpSettingsGeneralWidget.cpp',
56 56 'src/SidePane/SqpSidePane.cpp',
57 57 'src/TimeWidget/TimeWidget.cpp',
58 58 'src/Variable/VariableInspectorWidget.cpp',
59 59 'src/Variable/VariableInspectorTableView.cpp',
60 60 'src/Variable/VariableMenuHeaderWidget.cpp',
61 61 'src/Variable/RenameVariableDialog.cpp',
62 62 'src/Visualization/VisualizationGraphHelper.cpp',
63 63 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
64 64 'src/Visualization/VisualizationGraphWidget.cpp',
65 65 'src/Visualization/VisualizationTabWidget.cpp',
66 66 'src/Visualization/VisualizationWidget.cpp',
67 67 'src/Visualization/VisualizationZoneWidget.cpp',
68 68 'src/Visualization/qcustomplot.cpp',
69 69 'src/Visualization/QCustomPlotSynchronizer.cpp',
70 70 'src/Visualization/operations/FindVariableOperation.cpp',
71 71 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
72 72 'src/Visualization/operations/MenuBuilder.cpp',
73 73 'src/Visualization/operations/RemoveVariableOperation.cpp',
74 74 'src/Visualization/operations/RescaleAxeOperation.cpp',
75 75 'src/Visualization/VisualizationDragDropContainer.cpp',
76 76 'src/Visualization/VisualizationDragWidget.cpp',
77 77 'src/Visualization/AxisRenderingUtils.cpp',
78 78 'src/Visualization/PlottablesRenderingUtils.cpp',
79 'src/Visualization/MacScrollBarStyle.cpp'
79 'src/Visualization/MacScrollBarStyle.cpp',
80 'src/Visualization/VisualizationCursorItem.cpp'
80 81 ]
81 82
82 83 gui_inc = include_directories(['include'])
83 84
84 85 sciqlop_gui_lib = library('sciqlopgui',
85 86 gui_sources,
86 87 gui_moc_files,
87 88 include_directories : [gui_inc],
88 89 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core],
89 90 install : true
90 91 )
91 92
92 93 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
93 94 include_directories : gui_inc,
94 95 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core])
95 96
1 NO CONTENT: modified file, binary diff hidden
@@ -1,17 +1,22
1 1 <RCC>
2 2 <qresource prefix="/">
3 3 <file>icones/dataSourceComponent.png</file>
4 4 <file>icones/dataSourceNode.png</file>
5 5 <file>icones/dataSourceProduct.png</file>
6 6 <file>icones/dataSourceRoot.png</file>
7 7 <file>icones/delete.png</file>
8 8 <file>icones/down.png</file>
9 9 <file>icones/openInspector.png</file>
10 10 <file>icones/next.png</file>
11 11 <file>icones/plot.png</file>
12 12 <file>icones/previous.png</file>
13 13 <file>icones/unplot.png</file>
14 14 <file>icones/up.png</file>
15 15 <file>icones/time.png</file>
16 <file>icones/zoom.png</file>
17 <file>icones/rectangle.png</file>
18 <file>icones/drag.png</file>
19 <file>icones/cursor.png</file>
20 <file>icones/pointer.png</file>
16 21 </qresource>
17 22 </RCC>
@@ -1,163 +1,188
1 1 #include "SqpApplication.h"
2 2
3 3 #include <Data/IDataProvider.h>
4 4 #include <DataSource/DataSourceController.h>
5 5 #include <DragAndDrop/DragDropHelper.h>
6 6 #include <Network/NetworkController.h>
7 7 #include <QThread>
8 8 #include <Time/TimeController.h>
9 9 #include <Variable/Variable.h>
10 10 #include <Variable/VariableController.h>
11 11 #include <Variable/VariableModel.h>
12 12 #include <Visualization/VisualizationController.h>
13 13
14 14 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
15 15
16 16 class SqpApplication::SqpApplicationPrivate {
17 17 public:
18 18 SqpApplicationPrivate()
19 19 : m_DataSourceController{std::make_unique<DataSourceController>()},
20 20 m_VariableController{std::make_unique<VariableController>()},
21 21 m_TimeController{std::make_unique<TimeController>()},
22 22 m_NetworkController{std::make_unique<NetworkController>()},
23 23 m_VisualizationController{std::make_unique<VisualizationController>()},
24 m_DragDropHelper{std::make_unique<DragDropHelper>()}
24 m_DragDropHelper{std::make_unique<DragDropHelper>()},
25 m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None),
26 m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor)
25 27 {
26 28 // /////////////////////////////// //
27 29 // Connections between controllers //
28 30 // /////////////////////////////// //
29 31
30 32 // VariableController <-> DataSourceController
31 33 connect(m_DataSourceController.get(),
32 34 SIGNAL(variableCreationRequested(const QString &, const QVariantHash &,
33 35 std::shared_ptr<IDataProvider>)),
34 36 m_VariableController.get(),
35 37 SLOT(createVariable(const QString &, const QVariantHash &,
36 38 std::shared_ptr<IDataProvider>)));
37 39
38 40 connect(m_VariableController->variableModel(), &VariableModel::requestVariable,
39 41 m_DataSourceController.get(), &DataSourceController::requestVariable);
40 42
41 43 // VariableController <-> VisualizationController
42 44 connect(m_VariableController.get(),
43 45 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
44 46 m_VisualizationController.get(),
45 47 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
46 48
47 49 connect(m_VariableController.get(),
48 50 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)),
49 51 m_VisualizationController.get(),
50 52 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
51 53
52 54
53 55 m_DataSourceController->moveToThread(&m_DataSourceControllerThread);
54 56 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
55 57 m_NetworkController->moveToThread(&m_NetworkControllerThread);
56 58 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
57 59 m_VariableController->moveToThread(&m_VariableControllerThread);
58 60 m_VariableControllerThread.setObjectName("VariableControllerThread");
59 61 m_VisualizationController->moveToThread(&m_VisualizationControllerThread);
60 62 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
61 63
62 64
63 65 // Additionnal init
64 66 m_VariableController->setTimeController(m_TimeController.get());
65 67 }
66 68
67 69 virtual ~SqpApplicationPrivate()
68 70 {
69 71 m_DataSourceControllerThread.quit();
70 72 m_DataSourceControllerThread.wait();
71 73
72 74 m_NetworkControllerThread.quit();
73 75 m_NetworkControllerThread.wait();
74 76
75 77 m_VariableControllerThread.quit();
76 78 m_VariableControllerThread.wait();
77 79
78 80 m_VisualizationControllerThread.quit();
79 81 m_VisualizationControllerThread.wait();
80 82 }
81 83
82 84 std::unique_ptr<DataSourceController> m_DataSourceController;
83 85 std::unique_ptr<VariableController> m_VariableController;
84 86 std::unique_ptr<TimeController> m_TimeController;
85 87 std::unique_ptr<NetworkController> m_NetworkController;
86 88 std::unique_ptr<VisualizationController> m_VisualizationController;
87 89 QThread m_DataSourceControllerThread;
88 90 QThread m_NetworkControllerThread;
89 91 QThread m_VariableControllerThread;
90 92 QThread m_VisualizationControllerThread;
91 93
92 94 std::unique_ptr<DragDropHelper> m_DragDropHelper;
95
96 SqpApplication::PlotsInteractionMode m_PlotInterractionMode;
97 SqpApplication::PlotsCursorMode m_PlotCursorMode;
93 98 };
94 99
95 100
96 101 SqpApplication::SqpApplication(int &argc, char **argv)
97 102 : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()}
98 103 {
99 104 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
100 105
101 106 connect(&impl->m_DataSourceControllerThread, &QThread::started,
102 107 impl->m_DataSourceController.get(), &DataSourceController::initialize);
103 108 connect(&impl->m_DataSourceControllerThread, &QThread::finished,
104 109 impl->m_DataSourceController.get(), &DataSourceController::finalize);
105 110
106 111 connect(&impl->m_NetworkControllerThread, &QThread::started, impl->m_NetworkController.get(),
107 112 &NetworkController::initialize);
108 113 connect(&impl->m_NetworkControllerThread, &QThread::finished, impl->m_NetworkController.get(),
109 114 &NetworkController::finalize);
110 115
111 116 connect(&impl->m_VariableControllerThread, &QThread::started, impl->m_VariableController.get(),
112 117 &VariableController::initialize);
113 118 connect(&impl->m_VariableControllerThread, &QThread::finished, impl->m_VariableController.get(),
114 119 &VariableController::finalize);
115 120
116 121 connect(&impl->m_VisualizationControllerThread, &QThread::started,
117 122 impl->m_VisualizationController.get(), &VisualizationController::initialize);
118 123 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
119 124 impl->m_VisualizationController.get(), &VisualizationController::finalize);
120 125
121 126 impl->m_DataSourceControllerThread.start();
122 127 impl->m_NetworkControllerThread.start();
123 128 impl->m_VariableControllerThread.start();
124 129 impl->m_VisualizationControllerThread.start();
125 130 }
126 131
127 132 SqpApplication::~SqpApplication()
128 133 {
129 134 }
130 135
131 136 void SqpApplication::initialize()
132 137 {
133 138 }
134 139
135 140 DataSourceController &SqpApplication::dataSourceController() noexcept
136 141 {
137 142 return *impl->m_DataSourceController;
138 143 }
139 144
140 145 NetworkController &SqpApplication::networkController() noexcept
141 146 {
142 147 return *impl->m_NetworkController;
143 148 }
144 149
145 150 TimeController &SqpApplication::timeController() noexcept
146 151 {
147 152 return *impl->m_TimeController;
148 153 }
149 154
150 155 VariableController &SqpApplication::variableController() noexcept
151 156 {
152 157 return *impl->m_VariableController;
153 158 }
154 159
155 160 VisualizationController &SqpApplication::visualizationController() noexcept
156 161 {
157 162 return *impl->m_VisualizationController;
158 163 }
159 164
160 165 DragDropHelper &SqpApplication::dragDropHelper() noexcept
161 166 {
162 167 return *impl->m_DragDropHelper;
163 168 }
169
170 SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const
171 {
172 return impl->m_PlotInterractionMode;
173 }
174
175 void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode)
176 {
177 impl->m_PlotInterractionMode = mode;
178 }
179
180 SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const
181 {
182 return impl->m_PlotCursorMode;
183 }
184
185 void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode)
186 {
187 impl->m_PlotCursorMode = mode;
188 }
@@ -1,472 +1,473
1 1 #include "Visualization/VisualizationDragDropContainer.h"
2 2 #include "DragAndDrop/DragDropHelper.h"
3 3 #include "SqpApplication.h"
4 4 #include "Visualization/VisualizationDragWidget.h"
5 5
6 6 #include "Common/VisualizationDef.h"
7 7
8 8 #include <QDrag>
9 9 #include <QDragEnterEvent>
10 10 #include <QVBoxLayout>
11 11
12 12 #include <cmath>
13 13 #include <memory>
14 14
15 15 Q_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer, "VisualizationDragDropContainer")
16 16
17 17 auto DRAGGED_MINIATURE_WIDTH = 200; // in pixels
18 18
19 19 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
20 20
21 21 QVBoxLayout *m_Layout;
22 22 QHash<QString, VisualizationDragDropContainer::DropBehavior> m_AcceptedMimeTypes;
23 23 QString m_PlaceHolderText;
24 DragDropHelper::PlaceHolderType m_PlaceHolderType = DragDropHelper::PlaceHolderType::Graph;
24 DragDropHelper::PlaceHolderType m_PlaceHolderType;
25 25
26 26 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
27 27 = [](auto mimeData) { return true; };
28 28
29 29 int m_MinContainerHeight = 0;
30 30
31 31 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
32 : m_PlaceHolderType(DragDropHelper::PlaceHolderType::Graph)
32 33 {
33 34 m_Layout = new QVBoxLayout(widget);
34 35 m_Layout->setContentsMargins(0, 0, 0, 0);
35 36 }
36 37
37 38 bool acceptMimeData(const QMimeData *data) const
38 39 {
39 40 auto accepted = false;
40 41 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
41 42 ++it) {
42 43 const auto &type = it.key();
43 44 const auto &behavior = it.value();
44 45
45 46 if (data->hasFormat(type)) {
46 47 if (behavior != DropBehavior::Forbidden) {
47 48 accepted = true;
48 49 }
49 50 else {
50 51 accepted = false;
51 52 break;
52 53 }
53 54 }
54 55 }
55 56
56 57 if (accepted) {
57 58 accepted = m_AcceptMimeDataFun(data);
58 59 }
59 60
60 61 return accepted;
61 62 }
62 63
63 64 bool allowMergeForMimeData(const QMimeData *data) const
64 65 {
65 66 auto result = false;
66 67 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
67 68 ++it) {
68 69
69 70 if (data->hasFormat(it.key())
70 71 && (it.value() == VisualizationDragDropContainer::DropBehavior::Merged
71 72 || it.value()
72 73 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
73 74 result = true;
74 75 }
75 76 else if (data->hasFormat(it.key())
76 77 && it.value() == VisualizationDragDropContainer::DropBehavior::Inserted) {
77 78 // Merge is forbidden if the mime data contain an acceptable type which cannot be
78 79 // merged
79 80 result = false;
80 81 break;
81 82 }
82 83 }
83 84
84 85 return result;
85 86 }
86 87
87 88 bool allowInsertForMimeData(const QMimeData *data) const
88 89 {
89 90 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
90 91 ++it) {
91 92 if (data->hasFormat(it.key())
92 93 && (it.value() == VisualizationDragDropContainer::DropBehavior::Inserted
93 94 || it.value()
94 95 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
95 96 return true;
96 97 }
97 98 }
98 99
99 100 return false;
100 101 }
101 102
102 103 bool hasPlaceHolder() const
103 104 {
104 105 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
105 106 }
106 107
107 108 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
108 109 {
109 110 VisualizationDragWidget *dragWidget = nullptr;
110 111
111 112 for (auto child : parent->children()) {
112 113 auto widget = qobject_cast<VisualizationDragWidget *>(child);
113 114 if (widget && widget->isVisible()) {
114 115 if (widget->frameGeometry().contains(pos)) {
115 116 dragWidget = widget;
116 117 break;
117 118 }
118 119 }
119 120 }
120 121
121 122 return dragWidget;
122 123 }
123 124
124 125 bool cursorIsInContainer(QWidget *container) const
125 126 {
126 127 auto widgetUnderMouse = sqpApp->widgetAt(QCursor::pos());
127 128 return container->isAncestorOf(widgetUnderMouse) && widgetUnderMouse != container
128 129 && sqpApp->dragDropHelper().placeHolder().isAncestorOf(widgetUnderMouse);
129 130 }
130 131
131 132 int countDragWidget(const QWidget *parent, bool onlyVisible = false) const
132 133 {
133 134 auto nbGraph = 0;
134 135 for (auto child : parent->children()) {
135 136 if (qobject_cast<VisualizationDragWidget *>(child)) {
136 137 if (!onlyVisible || qobject_cast<VisualizationDragWidget *>(child)->isVisible()) {
137 138 nbGraph += 1;
138 139 }
139 140 }
140 141 }
141 142
142 143 return nbGraph;
143 144 }
144 145
145 146 void findPlaceHolderPosition(const QPoint &pos, bool canInsert, bool canMerge,
146 147 const VisualizationDragDropContainer *container);
147 148 };
148 149
149 150 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
150 151 : QFrame{parent},
151 152 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
152 153 {
153 154 setAcceptDrops(true);
154 155 }
155 156
156 157 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
157 158 {
158 159 impl->m_Layout->addWidget(dragWidget);
159 160 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
160 161 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
161 162 &VisualizationDragDropContainer::startDrag);
162 163 }
163 164
164 165 void VisualizationDragDropContainer::insertDragWidget(int index,
165 166 VisualizationDragWidget *dragWidget)
166 167 {
167 168 impl->m_Layout->insertWidget(index, dragWidget);
168 169 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
169 170 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
170 171 &VisualizationDragDropContainer::startDrag);
171 172 }
172 173
173 174 void VisualizationDragDropContainer::setMimeType(
174 175 const QString &mimeType, VisualizationDragDropContainer::DropBehavior behavior)
175 176 {
176 177 impl->m_AcceptedMimeTypes[mimeType] = behavior;
177 178 }
178 179
179 180 int VisualizationDragDropContainer::countDragWidget() const
180 181 {
181 182 return impl->countDragWidget(this);
182 183 }
183 184
184 185 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
185 186 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
186 187 {
187 188 impl->m_AcceptMimeDataFun = fun;
188 189 }
189 190
190 191 void VisualizationDragDropContainer::setPlaceHolderType(DragDropHelper::PlaceHolderType type,
191 192 const QString &placeHolderText)
192 193 {
193 194 impl->m_PlaceHolderType = type;
194 195 impl->m_PlaceHolderText = placeHolderText;
195 196 }
196 197
197 198 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
198 199 const QPoint &dragPosition)
199 200 {
200 201 auto &helper = sqpApp->dragDropHelper();
201 202 helper.resetDragAndDrop();
202 203
203 204 // Note: The management of the drag object is done by Qt
204 205 auto drag = new QDrag{dragWidget};
205 206
206 207 auto mimeData = dragWidget->mimeData();
207 208 drag->setMimeData(mimeData);
208 209
209 210 auto pixmap = QPixmap(dragWidget->size());
210 211 dragWidget->render(&pixmap);
211 212 drag->setPixmap(pixmap.scaled(DRAGGED_MINIATURE_WIDTH, DRAGGED_MINIATURE_WIDTH,
212 213 Qt::KeepAspectRatio, Qt::SmoothTransformation));
213 214
214 215 auto image = pixmap.toImage();
215 216 mimeData->setImageData(image);
216 217 mimeData->setUrls({helper.imageTemporaryUrl(image)});
217 218
218 219 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
219 220 helper.setCurrentDragWidget(dragWidget);
220 221
221 222 if (impl->cursorIsInContainer(this)) {
222 223 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
223 224 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex, impl->m_PlaceHolderType,
224 225 impl->m_PlaceHolderText);
225 226 dragWidget->setVisible(false);
226 227 }
227 228 else {
228 229 // The drag starts directly outside the drop zone
229 230 // do not add the placeHolder
230 231 }
231 232
232 233 drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction);
233 234
234 235 helper.doCloseWidgets();
235 236 }
236 237 else {
237 238 qCWarning(LOG_VisualizationDragDropContainer())
238 239 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
239 240 "VisualizationDragWidget is not found in this container.");
240 241 }
241 242 }
242 243
243 244 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
244 245 {
245 246 if (impl->acceptMimeData(event->mimeData())) {
246 247 event->acceptProposedAction();
247 248
248 249 auto &helper = sqpApp->dragDropHelper();
249 250
250 251 if (!impl->hasPlaceHolder()) {
251 252 auto dragWidget = helper.getCurrentDragWidget();
252 253
253 254 if (dragWidget) {
254 255 // If the drag&drop is internal to the visualization, entering the container hide
255 256 // the dragWidget which was made visible by the dragLeaveEvent
256 257 auto parentWidget
257 258 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
258 259 if (parentWidget) {
259 260 dragWidget->setVisible(false);
260 261 }
261 262 }
262 263
263 264 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
264 265 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
265 266 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
266 267 }
267 268 else {
268 269 // do nothing
269 270 }
270 271 }
271 272 else {
272 273 event->ignore();
273 274 }
274 275
275 276 QWidget::dragEnterEvent(event);
276 277 }
277 278
278 279 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
279 280 {
280 281 Q_UNUSED(event);
281 282
282 283 auto &helper = sqpApp->dragDropHelper();
283 284
284 285 if (!impl->cursorIsInContainer(this)) {
285 286 helper.removePlaceHolder();
286 287 helper.setHightlightedDragWidget(nullptr);
287 288 impl->m_MinContainerHeight = 0;
288 289
289 290 auto dragWidget = helper.getCurrentDragWidget();
290 291 if (dragWidget) {
291 292 // dragWidget has a value only if the drag is started from the visualization
292 293 // In that case, shows the drag widget at its original place
293 294 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
294 295 // drop zone (It is not possible to catch a drop event outside of the application)
295 296
296 297 if (dragWidget) {
297 298 dragWidget->setVisible(true);
298 299 }
299 300 }
300 301 }
301 302 else {
302 303 // Leave event probably received for a child widget.
303 304 // Do nothing.
304 305 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
305 306 }
306 307
307 308 QWidget::dragLeaveEvent(event);
308 309 }
309 310
310 311 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
311 312 {
312 313 if (impl->acceptMimeData(event->mimeData())) {
313 314 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
314 315 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
315 316 impl->findPlaceHolderPosition(event->pos(), canInsert, canMerge, this);
316 317 }
317 318 else {
318 319 event->ignore();
319 320 }
320 321
321 322 QWidget::dragMoveEvent(event);
322 323 }
323 324
324 325 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
325 326 {
326 327 auto &helper = sqpApp->dragDropHelper();
327 328
328 329 if (impl->acceptMimeData(event->mimeData())) {
329 330 auto dragWidget = helper.getCurrentDragWidget();
330 331 if (impl->hasPlaceHolder()) {
331 332 // drop where the placeHolder is located
332 333
333 334 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
334 335 if (canInsert) {
335 336 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
336 337
337 338 if (dragWidget) {
338 339 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
339 340 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
340 341 // Correction of the index if the drop occurs in the same container
341 342 // and if the drag is started from the visualization (in that case, the
342 343 // dragWidget is hidden)
343 344 droppedIndex -= 1;
344 345 }
345 346
346 347 dragWidget->setVisible(true);
347 348 }
348 349
349 350 event->acceptProposedAction();
350 351
351 352 helper.removePlaceHolder();
352 353
353 354 emit dropOccuredInContainer(droppedIndex, event->mimeData());
354 355 }
355 356 else {
356 357 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
357 358 "VisualizationDragDropContainer::dropEvent, dropping on the placeHolder, but "
358 359 "the insertion is forbidden.");
359 360 Q_ASSERT(false);
360 361 }
361 362 }
362 363 else if (helper.getHightlightedDragWidget()) {
363 364 // drop on the highlighted widget
364 365
365 366 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
366 367 if (canMerge) {
367 368 event->acceptProposedAction();
368 369 emit dropOccuredOnWidget(helper.getHightlightedDragWidget(), event->mimeData());
369 370 }
370 371 else {
371 372 qCWarning(LOG_VisualizationDragDropContainer())
372 373 << tr("VisualizationDragDropContainer::dropEvent, dropping on a widget, but "
373 374 "the merge is forbidden.");
374 375 Q_ASSERT(false);
375 376 }
376 377 }
377 378 }
378 379 else {
379 380 event->ignore();
380 381 }
381 382
382 383 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
383 384 impl->m_MinContainerHeight = 0;
384 385
385 386 QWidget::dropEvent(event);
386 387 }
387 388
388 389
389 390 void VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
390 391 const QPoint &pos, bool canInsert, bool canMerge,
391 392 const VisualizationDragDropContainer *container)
392 393 {
393 394 auto &helper = sqpApp->dragDropHelper();
394 395
395 396 auto absPos = container->mapToGlobal(pos);
396 397 auto isOnPlaceHolder = helper.placeHolder().isAncestorOf(sqpApp->widgetAt(absPos));
397 398
398 399 if (countDragWidget(container, true) == 0) {
399 400 // Drop on an empty container, just add the placeHolder at the top
400 401 helper.insertPlaceHolder(m_Layout, 0, m_PlaceHolderType, m_PlaceHolderText);
401 402 }
402 403 else if (!isOnPlaceHolder) {
403 404 auto nbDragWidget = countDragWidget(container);
404 405 if (nbDragWidget > 0) {
405 406
406 407 if (m_MinContainerHeight == 0) {
407 408 m_MinContainerHeight = container->size().height();
408 409 }
409 410
410 411 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
411 412 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
412 413
413 414 auto posY = pos.y();
414 415 auto dropIndex = floor(posY / graphHeight);
415 416 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
416 417
417 418
418 419 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
419 420 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
420 421
421 422 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
422 423
423 424 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
424 425
425 426 if (canInsert && (isOnTop || isOnBottom || !canMerge)) {
426 427 if (isOnBottom) {
427 428 dropIndex += 1;
428 429 }
429 430
430 431 if (helper.getCurrentDragWidget()) {
431 432 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
432 433 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
433 434 // Correction of the index if the drop occurs in the same container
434 435 // and if the drag is started from the visualization (in that case, the
435 436 // dragWidget is hidden)
436 437 dropIndex += 1;
437 438 }
438 439 }
439 440
440 441 if (dropIndex != placeHolderIndex) {
441 442 helper.insertPlaceHolder(m_Layout, dropIndex, m_PlaceHolderType,
442 443 m_PlaceHolderText);
443 444 }
444 445
445 446 helper.setHightlightedDragWidget(nullptr);
446 447 }
447 448 else if (canMerge && dragWidgetHovered) {
448 449 // drop on the middle -> merge
449 450 if (hasPlaceHolder()) {
450 451 helper.removePlaceHolder();
451 452 }
452 453
453 454 helper.setHightlightedDragWidget(dragWidgetHovered);
454 455 }
455 456 else {
456 457 qCWarning(LOG_VisualizationDragDropContainer())
457 458 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
458 459 "action.");
459 460 }
460 461 }
461 462 else {
462 463 qCWarning(LOG_VisualizationDragDropContainer())
463 464 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no widget "
464 465 "found in the "
465 466 "container");
466 467 }
467 468 }
468 469 else {
469 470 // the mouse is hover the placeHolder
470 471 // Do nothing
471 472 }
472 473 }
@@ -1,53 +1,55
1 1 #include "Visualization/VisualizationDragWidget.h"
2 2 #include "Visualization/VisualizationDragDropContainer.h"
3 3
4 4 #include <QApplication>
5 5 #include <QMouseEvent>
6 6
7 #include <SqpApplication.h>
8
7 9 struct VisualizationDragWidget::VisualizationDragWidgetPrivate {
8 10
9 11 QPoint m_DragStartPosition;
10 12 bool m_DragStartPositionValid = false;
11 13
12 14 explicit VisualizationDragWidgetPrivate() {}
13 15 };
14 16
15 17 VisualizationDragWidget::VisualizationDragWidget(QWidget *parent)
16 18 : QWidget{parent}, impl{spimpl::make_unique_impl<VisualizationDragWidgetPrivate>()}
17 19 {
18 20 }
19 21
20 22 void VisualizationDragWidget::mousePressEvent(QMouseEvent *event)
21 23 {
22 24 if (event->button() == Qt::LeftButton) {
23 25 impl->m_DragStartPosition = event->pos();
24 26 }
25 27
26 28 impl->m_DragStartPositionValid = isDragAllowed();
27 29
28 30 QWidget::mousePressEvent(event);
29 31 }
30 32
31 33 void VisualizationDragWidget::mouseMoveEvent(QMouseEvent *event)
32 34 {
33 35 if (!impl->m_DragStartPositionValid || !isDragAllowed()) {
34 36 return;
35 37 }
36 38
37 39 if (!(event->buttons() & Qt::LeftButton)) {
38 40 return;
39 41 }
40 42
41 if (!event->modifiers().testFlag(Qt::AltModifier)) {
42 return;
43 }
43 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::DragAndDrop
44 || event->modifiers().testFlag(Qt::AltModifier)) {
44 45
45 if ((event->pos() - impl->m_DragStartPosition).manhattanLength()
46 < QApplication::startDragDistance()) {
47 return;
48 }
46 if ((event->pos() - impl->m_DragStartPosition).manhattanLength()
47 < QApplication::startDragDistance()) {
48 return;
49 }
49 50
50 emit dragDetected(this, impl->m_DragStartPosition);
51 emit dragDetected(this, impl->m_DragStartPosition);
52 }
51 53
52 54 QWidget::mouseMoveEvent(event);
53 55 }
@@ -1,406 +1,597
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 4 #include "Visualization/VisualizationDefs.h"
4 5 #include "Visualization/VisualizationGraphHelper.h"
5 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 7 #include "Visualization/VisualizationZoneWidget.h"
7 8 #include "ui_VisualizationGraphWidget.h"
8 9
9 10 #include <Common/MimeTypesDef.h>
10 11 #include <Data/ArrayData.h>
11 12 #include <Data/IDataSeries.h>
12 13 #include <DragAndDrop/DragDropHelper.h>
13 14 #include <Settings/SqpSettingsDefs.h>
14 15 #include <SqpApplication.h>
15 16 #include <Time/TimeController.h>
16 17 #include <Variable/Variable.h>
17 18 #include <Variable/VariableController.h>
18 19
19 20 #include <unordered_map>
20 21
21 22 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
22 23
23 24 namespace {
24 25
25 26 /// Key pressed to enable zoom on horizontal axis
26 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
27 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
27 28
28 29 /// Key pressed to enable zoom on vertical axis
29 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
30 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
31
32 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
33 const auto PAN_SPEED = 5;
34
35 /// Key pressed to enable a calibration pan
36 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
37
38 /// Minimum size for the zoom box, in percentage of the axis range
39 const auto ZOOM_BOX_MIN_SIZE = 0.8;
40
41 /// Format of the dates appearing in the label of a cursor
42 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
30 43
31 44 } // namespace
32 45
33 46 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
34 47
35 48 explicit VisualizationGraphWidgetPrivate(const QString &name)
36 49 : m_Name{name},
37 50 m_DoAcquisition{true},
38 51 m_IsCalibration{false},
39 52 m_RenderingDelegate{nullptr}
40 53 {
41 54 }
42 55
43 56 QString m_Name;
44 57 // 1 variable -> n qcpplot
45 58 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
46 59 bool m_DoAcquisition;
47 60 bool m_IsCalibration;
48 61 /// Delegate used to attach rendering features to the plot
49 62 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
63
64 QCPItemRect *m_DrawingRect = nullptr;
65 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
66 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
67
68 void configureDrawingRect()
69 {
70 if (m_DrawingRect) {
71 QPen p;
72 p.setWidth(2);
73 m_DrawingRect->setPen(p);
74 }
75 }
76
77 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
78 {
79 removeDrawingRect(plot);
80
81 auto axisPos = posToAxisPos(pos, plot);
82
83 m_DrawingRect = new QCPItemRect{&plot};
84 configureDrawingRect();
85
86 m_DrawingRect->topLeft->setCoords(axisPos);
87 m_DrawingRect->bottomRight->setCoords(axisPos);
88 }
89
90 void removeDrawingRect(QCustomPlot &plot)
91 {
92 if (m_DrawingRect) {
93 plot.removeItem(m_DrawingRect); // the item is deleted by QCustomPlot
94 m_DrawingRect = nullptr;
95 plot.replot(QCustomPlot::rpQueuedReplot);
96 }
97 }
98
99 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
100 {
101 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
102 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
103 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
104 }
105
106 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
107 {
108 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
109 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
110
111 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
112 }
50 113 };
51 114
52 115 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
53 116 : VisualizationDragWidget{parent},
54 117 ui{new Ui::VisualizationGraphWidget},
55 118 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
56 119 {
57 120 ui->setupUi(this);
58 121
59 122 // 'Close' options : widget is deleted when closed
60 123 setAttribute(Qt::WA_DeleteOnClose);
61 124
62 125 // Set qcpplot properties :
63 126 // - Drag (on x-axis) and zoom are enabled
64 127 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
65 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
66 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
128 ui->widget->setInteractions(QCP::iRangeZoom | QCP::iSelectItems);
67 129
68 130 // The delegate must be initialized after the ui as it uses the plot
69 131 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
70 132
133 // Init the cursors
134 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
135 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
136 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
137 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
138
71 139 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
72 140 connect(ui->widget, &QCustomPlot::mouseRelease, this,
73 141 &VisualizationGraphWidget::onMouseRelease);
74 142 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
75 143 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
76 144 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
77 145 &QCPAxis::rangeChanged),
78 146 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
79 147
80 148 // Activates menu when right clicking on the graph
81 149 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
82 150 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
83 151 &VisualizationGraphWidget::onGraphMenuRequested);
84 152
85 153 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
86 154 &VariableController::onRequestDataLoading);
87 155
88 156 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
89 157 &VisualizationGraphWidget::onUpdateVarDisplaying);
90 158
91 159 #ifdef Q_OS_MAC
92 160 plot().setPlottingHint(QCP::phFastPolylines, true);
93 161 #endif
94 162 }
95 163
96 164
97 165 VisualizationGraphWidget::~VisualizationGraphWidget()
98 166 {
99 167 delete ui;
100 168 }
101 169
102 170 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
103 171 {
104 172 auto parent = parentWidget();
105 173 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
106 174 parent = parent->parentWidget();
107 175 }
108 176
109 177 return qobject_cast<VisualizationZoneWidget *>(parent);
110 178 }
111 179
112 180 void VisualizationGraphWidget::enableAcquisition(bool enable)
113 181 {
114 182 impl->m_DoAcquisition = enable;
115 183 }
116 184
117 185 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
118 186 {
119 187 // Uses delegate to create the qcpplot components according to the variable
120 188 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
121 189
122 190 if (auto dataSeries = variable->dataSeries()) {
123 191 // Set axes properties according to the units of the data series
124 192 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
125 193
126 194 // Sets rendering properties for the new plottables
127 195 // Warning: this method must be called after setAxesProperties(), as it can access to some
128 196 // axes properties that have to be initialized
129 197 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
130 198 }
131 199
132 200 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
133 201
134 202 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
135 203
136 204 this->enableAcquisition(false);
137 205 this->setGraphRange(range);
138 206 this->enableAcquisition(true);
139 207
140 208 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
141 209
142 210 emit variableAdded(variable);
143 211 }
144 212
145 213 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
146 214 {
147 215 // Each component associated to the variable :
148 216 // - is removed from qcpplot (which deletes it)
149 217 // - is no longer referenced in the map
150 218 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
151 219 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
152 220 emit variableAboutToBeRemoved(variable);
153 221
154 222 auto &plottablesMap = variableIt->second;
155 223
156 224 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
157 225 plottableIt != plottableEnd;) {
158 226 ui->widget->removePlottable(plottableIt->second);
159 227 plottableIt = plottablesMap.erase(plottableIt);
160 228 }
161 229
162 230 impl->m_VariableToPlotMultiMap.erase(variableIt);
163 231 }
164 232
165 233 // Updates graph
166 234 ui->widget->replot();
167 235 }
168 236
169 237 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
170 238 {
171 239 auto variables = QList<std::shared_ptr<Variable> >{};
172 240 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
173 241 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
174 242 variables << it->first;
175 243 }
176 244
177 245 return variables;
178 246 }
179 247
180 248 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
181 249 {
182 250 if (!variable) {
183 251 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
184 252 return;
185 253 }
186 254
187 255 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
188 256 }
189 257
190 258 SqpRange VisualizationGraphWidget::graphRange() const noexcept
191 259 {
192 260 auto graphRange = ui->widget->xAxis->range();
193 261 return SqpRange{graphRange.lower, graphRange.upper};
194 262 }
195 263
196 264 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
197 265 {
198 266 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
199 267 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
200 268 ui->widget->replot();
201 269 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
202 270 }
203 271
204 272 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
205 273 {
206 274 if (visitor) {
207 275 visitor->visit(this);
208 276 }
209 277 else {
210 278 qCCritical(LOG_VisualizationGraphWidget())
211 279 << tr("Can't visit widget : the visitor is null");
212 280 }
213 281 }
214 282
215 283 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
216 284 {
217 285 /// @todo : for the moment, a graph can always accomodate a variable
218 286 Q_UNUSED(variable);
219 287 return true;
220 288 }
221 289
222 290 bool VisualizationGraphWidget::contains(const Variable &variable) const
223 291 {
224 292 // Finds the variable among the keys of the map
225 293 auto variablePtr = &variable;
226 294 auto findVariable
227 295 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
228 296
229 297 auto end = impl->m_VariableToPlotMultiMap.cend();
230 298 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
231 299 return it != end;
232 300 }
233 301
234 302 QString VisualizationGraphWidget::name() const
235 303 {
236 304 return impl->m_Name;
237 305 }
238 306
239 307 QMimeData *VisualizationGraphWidget::mimeData() const
240 308 {
241 309 auto mimeData = new QMimeData;
242 310 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
243 311
244 312 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
245 313 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
246 314
247 315 return mimeData;
248 316 }
249 317
250 318 bool VisualizationGraphWidget::isDragAllowed() const
251 319 {
252 320 return true;
253 321 }
254 322
255 323 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
256 324 {
257 325 if (highlighted) {
258 326 plot().setBackground(QBrush(QColor("#BBD5EE")));
259 327 }
260 328 else {
261 329 plot().setBackground(QBrush(Qt::white));
262 330 }
263 331
264 332 plot().update();
265 333 }
266 334
335 void VisualizationGraphWidget::addVerticalCursor(double time)
336 {
337 impl->m_VerticalCursor->setPosition(time);
338 impl->m_VerticalCursor->setVisible(true);
339
340 auto text
341 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
342 impl->m_VerticalCursor->setLabelText(text);
343 }
344
345 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
346 {
347 impl->m_VerticalCursor->setAbsolutePosition(position);
348 impl->m_VerticalCursor->setVisible(true);
349
350 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
351 auto text
352 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
353 impl->m_VerticalCursor->setLabelText(text);
354 }
355
356 void VisualizationGraphWidget::removeVerticalCursor()
357 {
358 impl->m_VerticalCursor->setVisible(false);
359 plot().replot(QCustomPlot::rpQueuedReplot);
360 }
361
362 void VisualizationGraphWidget::addHorizontalCursor(double value)
363 {
364 impl->m_HorizontalCursor->setPosition(value);
365 impl->m_HorizontalCursor->setVisible(true);
366 impl->m_HorizontalCursor->setLabelText(QString::number(value));
367 }
368
369 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
370 {
371 impl->m_HorizontalCursor->setAbsolutePosition(position);
372 impl->m_HorizontalCursor->setVisible(true);
373
374 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
375 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
376 }
377
378 void VisualizationGraphWidget::removeHorizontalCursor()
379 {
380 impl->m_HorizontalCursor->setVisible(false);
381 plot().replot(QCustomPlot::rpQueuedReplot);
382 }
383
267 384 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
268 385 {
269 386 Q_UNUSED(event);
270 387
271 388 // Prevents that all variables will be removed from graph when it will be closed
272 389 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
273 390 emit variableAboutToBeRemoved(variableEntry.first);
274 391 }
275 392 }
276 393
277 394 void VisualizationGraphWidget::enterEvent(QEvent *event)
278 395 {
279 396 Q_UNUSED(event);
280 397 impl->m_RenderingDelegate->showGraphOverlay(true);
281 398 }
282 399
283 400 void VisualizationGraphWidget::leaveEvent(QEvent *event)
284 401 {
285 402 Q_UNUSED(event);
286 403 impl->m_RenderingDelegate->showGraphOverlay(false);
404
405 if (auto parentZone = parentZoneWidget()) {
406 parentZone->notifyMouseLeaveGraph(this);
407 }
408 else {
409 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
410 }
287 411 }
288 412
289 413 QCustomPlot &VisualizationGraphWidget::plot() noexcept
290 414 {
291 415 return *ui->widget;
292 416 }
293 417
294 418 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
295 419 {
296 420 QMenu graphMenu{};
297 421
298 422 // Iterates on variables (unique keys)
299 423 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
300 424 end = impl->m_VariableToPlotMultiMap.cend();
301 425 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
302 426 // 'Remove variable' action
303 427 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
304 428 [ this, var = it->first ]() { removeVariable(var); });
305 429 }
306 430
307 431 if (!graphMenu.isEmpty()) {
308 432 graphMenu.exec(QCursor::pos());
309 433 }
310 434 }
311 435
312 436 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
313 437 {
314 438 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
315 439 << QThread::currentThread()->objectName() << "DoAcqui"
316 440 << impl->m_DoAcquisition;
317 441
318 442 auto graphRange = SqpRange{t1.lower, t1.upper};
319 443 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
320 444
321 445 if (impl->m_DoAcquisition) {
322 446 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
323 447
324 448 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
325 449 end = impl->m_VariableToPlotMultiMap.end();
326 450 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
327 451 variableUnderGraphVector.push_back(it->first);
328 452 }
329 453 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
330 454 !impl->m_IsCalibration);
331 455
332 456 if (!impl->m_IsCalibration) {
333 457 qCDebug(LOG_VisualizationGraphWidget())
334 458 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
335 459 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
336 460 emit synchronize(graphRange, oldGraphRange);
337 461 }
338 462 }
463
464 auto pos = mapFromGlobal(QCursor::pos());
465 auto axisPos = impl->posToAxisPos(pos, plot());
466 if (auto parentZone = parentZoneWidget()) {
467 if (impl->pointIsInAxisRect(axisPos, plot())) {
468 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
469 }
470 else {
471 parentZone->notifyMouseLeaveGraph(this);
472 }
473 }
474 else {
475 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
476 }
339 477 }
340 478
341 479 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
342 480 {
343 481 // Handles plot rendering when mouse is moving
344 482 impl->m_RenderingDelegate->onMouseMove(event);
345 483
484 auto axisPos = impl->posToAxisPos(event->pos(), plot());
485
486 if (impl->m_DrawingRect) {
487 impl->m_DrawingRect->bottomRight->setCoords(axisPos);
488 }
489
490 if (auto parentZone = parentZoneWidget()) {
491 if (impl->pointIsInAxisRect(axisPos, plot())) {
492 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
493 }
494 else {
495 parentZone->notifyMouseLeaveGraph(this);
496 }
497 }
498 else {
499 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
500 }
501
346 502 VisualizationDragWidget::mouseMoveEvent(event);
347 503 }
348 504
349 505 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
350 506 {
351 auto zoomOrientations = QFlags<Qt::Orientation>{};
507 auto value = event->angleDelta().x() + event->angleDelta().y();
508 if (value != 0) {
509
510 auto direction = value > 0 ? 1.0 : -1.0;
511 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
512 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
513 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
514
515 auto zoomOrientations = QFlags<Qt::Orientation>{};
516 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
517 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
518
519 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
352 520
353 // Lambda that enables a zoom orientation if the key modifier related to this orientation
354 // has
355 // been pressed
356 auto enableOrientation
357 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
358 auto orientationEnabled = event->modifiers().testFlag(modifier);
359 zoomOrientations.setFlag(orientation, orientationEnabled);
360 };
361 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
362 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
521 if (!isZoomX && !isZoomY) {
522 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
523 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
363 524
364 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
525 axis->setRange(axis->range() + diff);
526
527 if (plot().noAntialiasingOnDrag()) {
528 plot().setNotAntialiasedElements(QCP::aeAll);
529 }
530
531 plot().replot(QCustomPlot::rpQueuedReplot);
532 }
533 }
365 534 }
366 535
367 536 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
368 537 {
369 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
370
371 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
538 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
539 impl->startDrawingRect(event->pos(), plot());
540 }
372 541
373 542 VisualizationDragWidget::mousePressEvent(event);
374 543 }
375 544
376 545 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
377 546 {
547 if (impl->m_DrawingRect) {
548
549 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
550 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
551
552 auto newAxisXRange = QCPRange{impl->m_DrawingRect->topLeft->coords().x(),
553 impl->m_DrawingRect->bottomRight->coords().x()};
554
555 auto newAxisYRange = QCPRange{impl->m_DrawingRect->topLeft->coords().y(),
556 impl->m_DrawingRect->bottomRight->coords().y()};
557
558 impl->removeDrawingRect(plot());
559
560 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
561 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
562 axisX->setRange(newAxisXRange);
563 axisY->setRange(newAxisYRange);
564
565 plot().replot(QCustomPlot::rpQueuedReplot);
566 }
567 }
568
378 569 impl->m_IsCalibration = false;
379 570 }
380 571
381 572 void VisualizationGraphWidget::onDataCacheVariableUpdated()
382 573 {
383 574 auto graphRange = ui->widget->xAxis->range();
384 575 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
385 576
386 577 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
387 578 auto variable = variableEntry.first;
388 579 qCDebug(LOG_VisualizationGraphWidget())
389 580 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
390 581 qCDebug(LOG_VisualizationGraphWidget())
391 582 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
392 583 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
393 584 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
394 585 variable->range());
395 586 }
396 587 }
397 588 }
398 589
399 590 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
400 591 const SqpRange &range)
401 592 {
402 593 auto it = impl->m_VariableToPlotMultiMap.find(variable);
403 594 if (it != impl->m_VariableToPlotMultiMap.end()) {
404 595 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
405 596 }
406 597 }
@@ -1,507 +1,560
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/SqpRange.h>
13 13 #include <Time/TimeController.h>
14 14 #include <Variable/Variable.h>
15 15 #include <Variable/VariableController.h>
16 16
17 17 #include <Visualization/operations/FindVariableOperation.h>
18 18
19 19 #include <DragAndDrop/DragDropHelper.h>
20 20 #include <QUuid>
21 21 #include <SqpApplication.h>
22 22 #include <cmath>
23 23
24 24 #include <QLayout>
25 25
26 26 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
27 27
28 28 namespace {
29 29
30 30
31 31 /// Generates a default name for a new graph, according to the number of graphs already displayed in
32 32 /// the zone
33 33 QString defaultGraphName(const QLayout &layout)
34 34 {
35 35 auto count = 0;
36 36 for (auto i = 0; i < layout.count(); ++i) {
37 37 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
38 38 count++;
39 39 }
40 40 }
41 41
42 42 return QObject::tr("Graph %1").arg(count + 1);
43 43 }
44 44
45 45 /**
46 46 * Applies a function to all graphs of the zone represented by its layout
47 47 * @param layout the layout that contains graphs
48 48 * @param fun the function to apply to each graph
49 49 */
50 50 template <typename Fun>
51 51 void processGraphs(QLayout &layout, Fun fun)
52 52 {
53 53 for (auto i = 0; i < layout.count(); ++i) {
54 54 if (auto item = layout.itemAt(i)) {
55 55 if (auto visualizationGraphWidget
56 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
56 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
57 57 fun(*visualizationGraphWidget);
58 58 }
59 59 }
60 60 }
61 61 }
62 62
63 63 } // namespace
64 64
65 65 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
66 66
67 67 explicit VisualizationZoneWidgetPrivate()
68 68 : m_SynchronisationGroupId{QUuid::createUuid()},
69 69 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
70 70 {
71 71 }
72 72 QUuid m_SynchronisationGroupId;
73 73 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
74 74
75 75 // Returns the first graph in the zone or nullptr if there is no graph inside
76 76 VisualizationGraphWidget *firstGraph(const VisualizationZoneWidget *zoneWidget) const
77 77 {
78 78 VisualizationGraphWidget *firstGraph = nullptr;
79 79 auto layout = zoneWidget->ui->dragDropContainer->layout();
80 80 if (layout->count() > 0) {
81 81 if (auto visualizationGraphWidget
82 82 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
83 83 firstGraph = visualizationGraphWidget;
84 84 }
85 85 }
86 86
87 87 return firstGraph;
88 88 }
89 89
90 90 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
91 91 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
92 92 VisualizationZoneWidget *zoneWidget);
93 93 };
94 94
95 95 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
96 96 : VisualizationDragWidget{parent},
97 97 ui{new Ui::VisualizationZoneWidget},
98 98 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
99 99 {
100 100 ui->setupUi(this);
101 101
102 102 ui->zoneNameLabel->setText(name);
103 103
104 104 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Graph);
105 105 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
106 106 VisualizationDragDropContainer::DropBehavior::Inserted);
107 107 ui->dragDropContainer->setMimeType(
108 108 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
109 109 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
110 110 VisualizationDragDropContainer::DropBehavior::Merged);
111 111 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
112 112 VisualizationDragDropContainer::DropBehavior::Forbidden);
113 113 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
114 114 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
115 115 ui->dragDropContainer);
116 116 });
117 117
118 118 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
119 119 &VisualizationZoneWidget::dropMimeData);
120 120 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
121 121 &VisualizationZoneWidget::dropMimeDataOnGraph);
122 122
123 123 // 'Close' options : widget is deleted when closed
124 124 setAttribute(Qt::WA_DeleteOnClose);
125 125 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
126 126 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
127 127
128 128 // Synchronisation id
129 129 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
130 130 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
131 131 }
132 132
133 133 VisualizationZoneWidget::~VisualizationZoneWidget()
134 134 {
135 135 delete ui;
136 136 }
137 137
138 138 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
139 139 {
140 140 // Synchronize new graph with others in the zone
141 141 impl->m_Synchronizer->addGraph(*graphWidget);
142 142
143 143 ui->dragDropContainer->addDragWidget(graphWidget);
144 144 }
145 145
146 146 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
147 147 {
148 148 // Synchronize new graph with others in the zone
149 149 impl->m_Synchronizer->addGraph(*graphWidget);
150 150
151 151 ui->dragDropContainer->insertDragWidget(index, graphWidget);
152 152 }
153 153
154 154 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
155 155 {
156 156 return createGraph(variable, -1);
157 157 }
158 158
159 159 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
160 160 int index)
161 161 {
162 162 auto graphWidget
163 163 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
164 164
165 165
166 166 // Set graph properties
167 167 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
168 168 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
169 169
170 170
171 171 // Lambda to synchronize zone widget
172 172 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
173 173 const SqpRange &oldGraphRange) {
174 174
175 175 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
176 176 auto frameLayout = ui->dragDropContainer->layout();
177 177 for (auto i = 0; i < frameLayout->count(); ++i) {
178 178 auto graphChild
179 179 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
180 180 if (graphChild && (graphChild != graphWidget)) {
181 181
182 182 auto graphChildRange = graphChild->graphRange();
183 183 switch (zoomType) {
184 184 case AcquisitionZoomType::ZoomIn: {
185 185 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
186 186 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
187 187 graphChildRange.m_TStart += deltaLeft;
188 188 graphChildRange.m_TEnd -= deltaRight;
189 189 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
190 190 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
191 191 << deltaLeft;
192 192 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
193 193 << deltaRight;
194 194 qCDebug(LOG_VisualizationZoneWidget())
195 195 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
196 196
197 197 break;
198 198 }
199 199
200 200 case AcquisitionZoomType::ZoomOut: {
201 201 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
202 202 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
203 203 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
204 204 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
205 205 << deltaLeft;
206 206 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
207 207 << deltaRight;
208 208 qCDebug(LOG_VisualizationZoneWidget())
209 209 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
210 210 graphChildRange.m_TStart -= deltaLeft;
211 211 graphChildRange.m_TEnd += deltaRight;
212 212 break;
213 213 }
214 214 case AcquisitionZoomType::PanRight: {
215 215 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
216 216 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
217 217 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
218 218 graphChildRange.m_TStart += deltaLeft;
219 219 graphChildRange.m_TEnd += deltaRight;
220 220 qCDebug(LOG_VisualizationZoneWidget())
221 221 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
222 222 break;
223 223 }
224 224 case AcquisitionZoomType::PanLeft: {
225 225 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
226 226 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
227 227 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
228 228 graphChildRange.m_TStart -= deltaLeft;
229 229 graphChildRange.m_TEnd -= deltaRight;
230 230 break;
231 231 }
232 232 case AcquisitionZoomType::Unknown: {
233 233 qCDebug(LOG_VisualizationZoneWidget())
234 234 << tr("Impossible to synchronize: zoom type unknown");
235 235 break;
236 236 }
237 237 default:
238 238 qCCritical(LOG_VisualizationZoneWidget())
239 239 << tr("Impossible to synchronize: zoom type not take into account");
240 240 // No action
241 241 break;
242 242 }
243 243 graphChild->enableAcquisition(false);
244 244 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
245 245 << graphChild->graphRange();
246 246 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
247 247 << graphChildRange;
248 248 qCDebug(LOG_VisualizationZoneWidget())
249 249 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
250 250 graphChild->setGraphRange(graphChildRange);
251 251 graphChild->enableAcquisition(true);
252 252 }
253 253 }
254 254 };
255 255
256 256 // connection for synchronization
257 257 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
258 258 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
259 259 &VisualizationZoneWidget::onVariableAdded);
260 260 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
261 261 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
262 262
263 263 auto range = SqpRange{};
264 264 if (auto firstGraph = impl->firstGraph(this)) {
265 265 // Case of a new graph in a existant zone
266 266 range = firstGraph->graphRange();
267 267 }
268 268 else {
269 269 // Case of a new graph as the first of the zone
270 270 range = variable->range();
271 271 }
272 272
273 273 this->insertGraph(index, graphWidget);
274 274
275 275 graphWidget->addVariable(variable, range);
276 276 graphWidget->setYRange(variable);
277 277
278 278 return graphWidget;
279 279 }
280 280
281 281 VisualizationGraphWidget *
282 282 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
283 283 {
284 284 if (variables.isEmpty()) {
285 285 return nullptr;
286 286 }
287 287
288 288 auto graphWidget = createGraph(variables.first(), index);
289 289 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
290 290 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
291 291 }
292 292
293 293 return graphWidget;
294 294 }
295 295
296 296 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
297 297 {
298 298 if (visitor) {
299 299 visitor->visitEnter(this);
300 300
301 301 // Apply visitor to graph children: widgets different from graphs are not visited (no
302 302 // action)
303 303 processGraphs(
304 304 *ui->dragDropContainer->layout(),
305 305 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
306 306
307 307 visitor->visitLeave(this);
308 308 }
309 309 else {
310 310 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
311 311 }
312 312 }
313 313
314 314 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
315 315 {
316 316 // A tab can always accomodate a variable
317 317 Q_UNUSED(variable);
318 318 return true;
319 319 }
320 320
321 321 bool VisualizationZoneWidget::contains(const Variable &variable) const
322 322 {
323 323 Q_UNUSED(variable);
324 324 return false;
325 325 }
326 326
327 327 QString VisualizationZoneWidget::name() const
328 328 {
329 329 return ui->zoneNameLabel->text();
330 330 }
331 331
332 332 QMimeData *VisualizationZoneWidget::mimeData() const
333 333 {
334 334 auto mimeData = new QMimeData;
335 335 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
336 336
337 337 if (auto firstGraph = impl->firstGraph(this)) {
338 338 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
339 339 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
340 340 }
341 341
342 342 return mimeData;
343 343 }
344 344
345 345 bool VisualizationZoneWidget::isDragAllowed() const
346 346 {
347 347 return true;
348 348 }
349 349
350 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
351 const QPointF &plotPosition,
352 VisualizationGraphWidget *graphWidget)
353 {
354 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
355 VisualizationGraphWidget &processedGraph) {
356
357 switch (sqpApp->plotsCursorMode()) {
358 case SqpApplication::PlotsCursorMode::Vertical:
359 processedGraph.removeHorizontalCursor();
360 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
361 break;
362 case SqpApplication::PlotsCursorMode::Temporal:
363 processedGraph.addVerticalCursor(plotPosition.x());
364 processedGraph.removeHorizontalCursor();
365 break;
366 case SqpApplication::PlotsCursorMode::Horizontal:
367 processedGraph.removeVerticalCursor();
368 if (&processedGraph == graphWidget) {
369 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
370 }
371 else {
372 processedGraph.removeHorizontalCursor();
373 }
374 break;
375 case SqpApplication::PlotsCursorMode::Cross:
376 if (&processedGraph == graphWidget) {
377 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
378 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
379 }
380 else {
381 processedGraph.removeHorizontalCursor();
382 processedGraph.removeVerticalCursor();
383 }
384 break;
385 case SqpApplication::PlotsCursorMode::NoCursor:
386 processedGraph.removeHorizontalCursor();
387 processedGraph.removeVerticalCursor();
388 break;
389 }
390
391
392 });
393 }
394
395 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
396 {
397 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
398 processedGraph.removeHorizontalCursor();
399 processedGraph.removeVerticalCursor();
400 });
401 }
402
350 403 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
351 404 {
352 405 // Closes graphs in the zone
353 406 processGraphs(*ui->dragDropContainer->layout(),
354 407 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
355 408
356 409 // Delete synchronization group from variable controller
357 410 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
358 411 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
359 412
360 413 QWidget::closeEvent(event);
361 414 }
362 415
363 416 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
364 417 {
365 418 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
366 419 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
367 420 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
368 421 }
369 422
370 423 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
371 424 {
372 425 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
373 426 Q_ARG(std::shared_ptr<Variable>, variable),
374 427 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
375 428 }
376 429
377 430 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
378 431 {
379 432 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
380 433 impl->dropGraph(index, this);
381 434 }
382 435 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
383 436 auto variables = sqpApp->variableController().variablesForMimeData(
384 437 mimeData->data(MIME_TYPE_VARIABLE_LIST));
385 438 impl->dropVariables(variables, index, this);
386 439 }
387 440 else {
388 441 qCWarning(LOG_VisualizationZoneWidget())
389 442 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
390 443 }
391 444 }
392 445
393 446 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
394 447 const QMimeData *mimeData)
395 448 {
396 449 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
397 450 if (!graphWidget) {
398 451 qCWarning(LOG_VisualizationZoneWidget())
399 452 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
400 453 "drop aborted");
401 454 Q_ASSERT(false);
402 455 return;
403 456 }
404 457
405 458 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
406 459 auto variables = sqpApp->variableController().variablesForMimeData(
407 460 mimeData->data(MIME_TYPE_VARIABLE_LIST));
408 461 for (const auto &var : variables) {
409 462 graphWidget->addVariable(var, graphWidget->graphRange());
410 463 }
411 464 }
412 465 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
413 466 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
414 467 graphWidget->setGraphRange(range);
415 468 }
416 469 else {
417 470 qCWarning(LOG_VisualizationZoneWidget())
418 471 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
419 472 }
420 473 }
421 474
422 475 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
423 476 int index, VisualizationZoneWidget *zoneWidget)
424 477 {
425 478 auto &helper = sqpApp->dragDropHelper();
426 479
427 480 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
428 481 if (!graphWidget) {
429 482 qCWarning(LOG_VisualizationZoneWidget())
430 483 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
431 484 "found or invalid.");
432 485 Q_ASSERT(false);
433 486 return;
434 487 }
435 488
436 489 auto parentDragDropContainer
437 490 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
438 491 if (!parentDragDropContainer) {
439 492 qCWarning(LOG_VisualizationZoneWidget())
440 493 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
441 494 "the dropped graph is not found.");
442 495 Q_ASSERT(false);
443 496 return;
444 497 }
445 498
446 499 const auto &variables = graphWidget->variables();
447 500
448 501 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
449 502 // The drop didn't occur in the same zone
450 503
451 504 // Abort the requests for the variables (if any)
452 505 // Commented, because it's not sure if it's needed or not
453 506 // for (const auto& var : variables)
454 507 //{
455 508 // sqpApp->variableController().onAbortProgressRequested(var);
456 509 //}
457 510
458 511 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
459 512 auto nbGraph = parentDragDropContainer->countDragWidget();
460 513 if (nbGraph == 1) {
461 514 // This is the only graph in the previous zone, close the zone
462 515 helper.delayedCloseWidget(previousParentZoneWidget);
463 516 }
464 517 else {
465 518 // Close the graph
466 519 helper.delayedCloseWidget(graphWidget);
467 520 }
468 521
469 522 // Creates the new graph in the zone
470 523 zoneWidget->createGraph(variables, index);
471 524 }
472 525 else {
473 526 // The drop occurred in the same zone or the graph is empty
474 527 // Simple move of the graph, no variable operation associated
475 528 parentDragDropContainer->layout()->removeWidget(graphWidget);
476 529
477 530 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
478 531 // The graph is empty and dropped in a different zone.
479 532 // Take the range of the first graph in the zone (if existing).
480 533 auto layout = zoneWidget->ui->dragDropContainer->layout();
481 534 if (layout->count() > 0) {
482 535 if (auto visualizationGraphWidget
483 536 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
484 537 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
485 538 }
486 539 }
487 540 }
488 541
489 542 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
490 543 }
491 544 }
492 545
493 546 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
494 547 const QList<std::shared_ptr<Variable> > &variables, int index,
495 548 VisualizationZoneWidget *zoneWidget)
496 549 {
497 550 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
498 551 // compatible variable here
499 552 if (variables.count() > 1) {
500 553 qCWarning(LOG_VisualizationZoneWidget())
501 554 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
502 555 "aborted.");
503 556 return;
504 557 }
505 558
506 559 zoneWidget->createGraph(variables, index);
507 560 }
General Comments 1
Under Review
author

Auto status change to "Under Review"

You need to be logged in to leave comments. Login now