##// END OF EJS Templates
Merge branch 'feature/SelectionZones' into develop
trabillard -
r1095:ca242dcb639e merge
parent child
Show More
@@ -0,0 +1,47
1 #ifndef SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
2 #define SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
3
4 #include <Common/spimpl.h>
5 #include <Data/SqpRange.h>
6 #include <Visualization/qcustomplot.h>
7
8 class VisualizationSelectionZoneItem : public QCPItemRect {
9
10 public:
11 VisualizationSelectionZoneItem(QCustomPlot *plot);
12 virtual ~VisualizationSelectionZoneItem();
13
14 void setName(const QString &name);
15 QString name() const;
16
17 SqpRange range() const;
18 void setRange(double tstart, double tend);
19 void setStart(double tstart);
20 void setEnd(double tend);
21
22 void setColor(const QColor &color);
23
24 void setEditionEnabled(bool value);
25 bool isEditionEnabled() const;
26
27 Qt::CursorShape curshorShapeForPosition(const QPoint &position) const;
28 void setHovered(bool value);
29
30 void setAssociatedEditedZones(const QVector<VisualizationSelectionZoneItem *> &associatedZones);
31
32 protected:
33 void mousePressEvent(QMouseEvent *event, const QVariant &details) override;
34 void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) override;
35 void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) override;
36
37 void resizeLeft(double pixelDiff);
38 void resizeRight(double pixelDiff);
39 void move(double pixelDiff);
40
41
42 private:
43 class VisualizationSelectionZoneItemPrivate;
44 spimpl::unique_impl_ptr<VisualizationSelectionZoneItemPrivate> impl;
45 };
46
47 #endif // SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
@@ -0,0 +1,26
1 #ifndef SCIQLOP_VISUALIZATIONSELECTIONZONEMANAGER_H
2 #define SCIQLOP_VISUALIZATIONSELECTIONZONEMANAGER_H
3
4 #include <Common/spimpl.h>
5
6 #include <QVector>
7
8 class VisualizationSelectionZoneItem;
9
10 class VisualizationSelectionZoneManager {
11 public:
12 VisualizationSelectionZoneManager();
13
14 void select(const QVector<VisualizationSelectionZoneItem *> &items);
15 void setSelected(VisualizationSelectionZoneItem *item, bool value);
16
17 void clearSelection();
18
19 QVector<VisualizationSelectionZoneItem *> selectedItems() const;
20
21 private:
22 class VisualizationSelectionZoneManagerPrivate;
23 spimpl::unique_impl_ptr<VisualizationSelectionZoneManagerPrivate> impl;
24 };
25
26 #endif // SCIQLOP_VISUALIZATIONSELECTIONZONEMANAGER_H
@@ -0,0 +1,343
1 #include "Visualization/VisualizationSelectionZoneItem.h"
2
3 const QString &DEFAULT_COLOR = QStringLiteral("#E79D41");
4
5 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
6
7 QCustomPlot *m_Plot;
8 double m_T1 = 0;
9 double m_T2 = 0;
10 QColor m_Color;
11
12 bool m_IsEditionEnabled = true;
13 double m_MovedOrinalT1 = 0;
14 double m_MovedOrinalT2 = 0;
15
16 QCPItemStraightLine *m_LeftLine;
17 QCPItemStraightLine *m_RightLine;
18 QCPItemText *m_NameLabelItem = nullptr;
19
20 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
21 EditionMode m_CurrentEditionMode;
22
23 QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones;
24
25 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
26 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
27 {
28 }
29
30 void updatePosition(VisualizationSelectionZoneItem *item)
31 {
32 item->topLeft->setCoords(m_T1, 0);
33 item->bottomRight->setCoords(m_T2, 1);
34 }
35
36 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
37 {
38 auto distanceLeft = m_LeftLine->selectTest(pos, false);
39 auto distanceRight = m_RightLine->selectTest(pos, false);
40 auto distance = zoneItem->selectTest(pos, false);
41
42 if (distanceRight <= distance) {
43 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
44 }
45 else if (distanceLeft <= distance) {
46 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
47 }
48
49 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
50 }
51
52 double pixelSizeToAxisXSize(double pixels)
53 {
54 auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
55 return axis->pixelToCoord(pixels) - axis->pixelToCoord(0);
56 }
57 };
58
59 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
60 : QCPItemRect(plot),
61 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
62 {
63 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
64 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
65 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
66 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
67 setSelectable(false);
68
69 impl->m_RightLine = new QCPItemStraightLine(plot);
70 impl->m_RightLine->point1->setParentAnchor(topRight);
71 impl->m_RightLine->point2->setParentAnchor(bottomRight);
72 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
73 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
74 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
75 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
76 impl->m_RightLine->setSelectable(false);
77
78 impl->m_LeftLine = new QCPItemStraightLine(plot);
79 impl->m_LeftLine->point1->setParentAnchor(topLeft);
80 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
81 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
82 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
83 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
84 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
85 impl->m_LeftLine->setSelectable(false);
86
87 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
88 &QCPItemStraightLine::setSelected);
89 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
90 &QCPItemStraightLine::setSelected);
91
92 setColor(QColor(DEFAULT_COLOR));
93 }
94
95 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem()
96 {
97 impl->m_Plot->removeItem(impl->m_RightLine);
98 impl->m_Plot->removeItem(impl->m_LeftLine);
99 }
100
101 void VisualizationSelectionZoneItem::setName(const QString &name)
102 {
103 if (name.isEmpty() && impl->m_NameLabelItem) {
104 impl->m_Plot->removeItem(impl->m_NameLabelItem);
105 impl->m_NameLabelItem = nullptr;
106 }
107 else if (!impl->m_NameLabelItem) {
108 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
109 impl->m_NameLabelItem->setText(name);
110 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
111 impl->m_NameLabelItem->setColor(impl->m_Color);
112 impl->m_NameLabelItem->position->setParentAnchor(top);
113 }
114 }
115
116 QString VisualizationSelectionZoneItem::name() const
117 {
118 if (!impl->m_NameLabelItem) {
119 return QString();
120 }
121
122 return impl->m_NameLabelItem->text();
123 }
124
125 SqpRange VisualizationSelectionZoneItem::range() const
126 {
127 SqpRange range;
128 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
129 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
130 return range;
131 }
132
133 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
134 {
135 impl->m_T1 = tstart;
136 impl->m_T2 = tend;
137 impl->updatePosition(this);
138 }
139
140 void VisualizationSelectionZoneItem::setStart(double tstart)
141 {
142 impl->m_T1 = tstart;
143 impl->updatePosition(this);
144 }
145
146 void VisualizationSelectionZoneItem::setEnd(double tend)
147 {
148 impl->m_T2 = tend;
149 impl->updatePosition(this);
150 }
151
152 void VisualizationSelectionZoneItem::setColor(const QColor &color)
153 {
154 impl->m_Color = color;
155
156 auto brushColor = color;
157 brushColor.setAlpha(80);
158 setBrush(QBrush(brushColor));
159 setPen(QPen(Qt::NoPen));
160
161 auto selectedBrushColor = brushColor;
162 selectedBrushColor.setAlpha(150);
163 setSelectedBrush(QBrush(selectedBrushColor));
164 setSelectedPen(QPen(Qt::NoPen));
165
166 auto linePen = QPen(color);
167 linePen.setStyle(Qt::SolidLine);
168 linePen.setWidth(4);
169
170 auto selectedLinePen = linePen;
171 selectedLinePen.setColor(color.darker(120));
172 selectedLinePen.setWidth(4);
173
174 impl->m_LeftLine->setPen(linePen);
175 impl->m_RightLine->setPen(linePen);
176
177 impl->m_LeftLine->setSelectedPen(selectedLinePen);
178 impl->m_RightLine->setSelectedPen(selectedLinePen);
179 }
180
181 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
182 {
183 impl->m_IsEditionEnabled = value;
184 setSelectable(value);
185 if (!value) {
186 setSelected(false);
187 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
188 }
189 }
190
191 bool VisualizationSelectionZoneItem::isEditionEnabled() const
192 {
193 return impl->m_IsEditionEnabled;
194 }
195
196 Qt::CursorShape
197 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
198 {
199 auto mode = impl->m_CurrentEditionMode
200 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
201 ? impl->getEditionMode(position, this)
202 : impl->m_CurrentEditionMode;
203 switch (mode) {
204 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
205 return Qt::SizeAllCursor;
206 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
207 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
208 return Qt::SizeHorCursor;
209 default:
210 return Qt::ArrowCursor;
211 }
212 }
213
214 void VisualizationSelectionZoneItem::setHovered(bool value)
215 {
216 if (value) {
217 auto linePen = impl->m_LeftLine->pen();
218 linePen.setStyle(Qt::DotLine);
219 linePen.setWidth(3);
220
221 auto selectedLinePen = impl->m_LeftLine->selectedPen();
222 ;
223 selectedLinePen.setStyle(Qt::DotLine);
224 selectedLinePen.setWidth(3);
225
226 impl->m_LeftLine->setPen(linePen);
227 impl->m_RightLine->setPen(linePen);
228
229 impl->m_LeftLine->setSelectedPen(selectedLinePen);
230 impl->m_RightLine->setSelectedPen(selectedLinePen);
231 }
232 else {
233 setColor(impl->m_Color);
234 }
235 }
236
237 void VisualizationSelectionZoneItem::setAssociatedEditedZones(
238 const QVector<VisualizationSelectionZoneItem *> &associatedZones)
239 {
240 impl->m_AssociatedEditedZones = associatedZones;
241 impl->m_AssociatedEditedZones.removeAll(this);
242 }
243
244 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
245 {
246 if (isEditionEnabled() && event->button() == Qt::LeftButton) {
247 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
248
249 impl->m_MovedOrinalT1 = impl->m_T1;
250 impl->m_MovedOrinalT2 = impl->m_T2;
251 for (auto associatedZone : impl->m_AssociatedEditedZones) {
252 associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1;
253 associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2;
254 }
255 }
256 else {
257 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
258 event->ignore();
259 }
260 }
261
262 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
263 {
264 if (isEditionEnabled()) {
265 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
266 auto pixelDiff = event->pos().x() - startPos.x();
267 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
268
269 switch (impl->m_CurrentEditionMode) {
270 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
271 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
272 for (auto associatedZone : impl->m_AssociatedEditedZones) {
273 associatedZone->move(pixelDiff);
274 }
275 break;
276 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
277 setStart(impl->m_MovedOrinalT1 + diff);
278 for (auto associatedZone : impl->m_AssociatedEditedZones) {
279 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
280 ? associatedZone->resizeLeft(pixelDiff)
281 : associatedZone->resizeRight(pixelDiff);
282 }
283 break;
284 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
285 setEnd(impl->m_MovedOrinalT2 + diff);
286 for (auto associatedZone : impl->m_AssociatedEditedZones) {
287 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
288 ? associatedZone->resizeRight(pixelDiff)
289 : associatedZone->resizeLeft(pixelDiff);
290 }
291 break;
292 default:
293 break;
294 }
295
296 for (auto associatedZone : impl->m_AssociatedEditedZones) {
297 associatedZone->parentPlot()->replot();
298 }
299 }
300 else {
301 event->ignore();
302 }
303 }
304
305 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
306 {
307 if (isEditionEnabled()) {
308 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
309 }
310 else {
311 event->ignore();
312 }
313
314 impl->m_AssociatedEditedZones.clear();
315 }
316
317 void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff)
318 {
319 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
320 if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) {
321 setStart(impl->m_MovedOrinalT1 + diff);
322 }
323 else {
324 setEnd(impl->m_MovedOrinalT2 + diff);
325 }
326 }
327
328 void VisualizationSelectionZoneItem::resizeRight(double pixelDiff)
329 {
330 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
331 if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) {
332 setStart(impl->m_MovedOrinalT1 + diff);
333 }
334 else {
335 setEnd(impl->m_MovedOrinalT2 + diff);
336 }
337 }
338
339 void VisualizationSelectionZoneItem::move(double pixelDiff)
340 {
341 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
342 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
343 }
@@ -0,0 +1,51
1 #include "Visualization/VisualizationSelectionZoneManager.h"
2 #include "Visualization/VisualizationSelectionZoneItem.h"
3
4 struct VisualizationSelectionZoneManager::VisualizationSelectionZoneManagerPrivate {
5 QVector<VisualizationSelectionZoneItem *> m_SelectedItems;
6 };
7
8 VisualizationSelectionZoneManager::VisualizationSelectionZoneManager()
9 : impl{spimpl::make_unique_impl<VisualizationSelectionZoneManagerPrivate>()}
10 {
11 }
12
13 void VisualizationSelectionZoneManager::select(
14 const QVector<VisualizationSelectionZoneItem *> &items)
15 {
16 clearSelection();
17 for (auto item : items) {
18 setSelected(item, true);
19 }
20 }
21
22 void VisualizationSelectionZoneManager::setSelected(VisualizationSelectionZoneItem *item,
23 bool value)
24 {
25 if (value != item->selected()) {
26 item->setSelected(value);
27 item->parentPlot()->replot();
28 }
29
30 if (!value && impl->m_SelectedItems.contains(item)) {
31 impl->m_SelectedItems.removeAll(item);
32 }
33 else if (value) {
34 impl->m_SelectedItems << item;
35 }
36 }
37
38 void VisualizationSelectionZoneManager::clearSelection()
39 {
40 for (auto item : impl->m_SelectedItems) {
41 item->setSelected(false);
42 item->parentPlot()->replot();
43 }
44
45 impl->m_SelectedItems.clear();
46 }
47
48 QVector<VisualizationSelectionZoneItem *> VisualizationSelectionZoneManager::selectedItems() const
49 {
50 return impl->m_SelectedItems;
51 }
@@ -1,353 +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 171 // //////////////// //
172 172 // Menu and Toolbar //
173 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};
192 auto actionPointerMode = new QAction{QIcon(":/icones/pointer.png"), "Move", this};
193 193 actionPointerMode->setCheckable(true);
194 194 actionPointerMode->setChecked(sqpApp->plotsInteractionMode()
195 195 == SqpApplication::PlotsInteractionMode::None);
196 196 connect(actionPointerMode, &QAction::triggered,
197 197 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::None); });
198 198
199 199 auto actionZoomMode = new QAction{QIcon(":/icones/zoom.png"), "Zoom", this};
200 200 actionZoomMode->setCheckable(true);
201 201 actionZoomMode->setChecked(sqpApp->plotsInteractionMode()
202 202 == SqpApplication::PlotsInteractionMode::ZoomBox);
203 203 connect(actionZoomMode, &QAction::triggered, []() {
204 204 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::ZoomBox);
205 205 });
206 206
207 207 auto actionOrganisationMode = new QAction{QIcon(":/icones/drag.png"), "Organize", this};
208 208 actionOrganisationMode->setCheckable(true);
209 209 actionOrganisationMode->setChecked(sqpApp->plotsInteractionMode()
210 210 == SqpApplication::PlotsInteractionMode::DragAndDrop);
211 211 connect(actionOrganisationMode, &QAction::triggered, []() {
212 212 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::DragAndDrop);
213 213 });
214 214
215 215 auto actionZonesMode = new QAction{QIcon(":/icones/rectangle.png"), "Zones", this};
216 216 actionZonesMode->setCheckable(true);
217 217 actionZonesMode->setChecked(sqpApp->plotsInteractionMode()
218 218 == SqpApplication::PlotsInteractionMode::SelectionZones);
219 219 connect(actionZonesMode, &QAction::triggered, []() {
220 220 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::SelectionZones);
221 221 });
222 222
223 223 auto modeActionGroup = new QActionGroup{this};
224 224 modeActionGroup->addAction(actionZoomMode);
225 225 modeActionGroup->addAction(actionZonesMode);
226 226 modeActionGroup->addAction(actionOrganisationMode);
227 227 modeActionGroup->addAction(actionPointerMode);
228 228 modeActionGroup->setExclusive(true);
229 229
230 230 mainToolBar->addSeparator();
231 231 mainToolBar->addAction(actionPointerMode);
232 232 mainToolBar->addAction(actionZoomMode);
233 233 mainToolBar->addAction(actionOrganisationMode);
234 234 mainToolBar->addAction(actionZonesMode);
235 235 mainToolBar->addSeparator();
236 236
237 237 auto btnCursor = new QToolButton{this};
238 238 btnCursor->setIcon(QIcon(":/icones/cursor.png"));
239 239 btnCursor->setText("Cursor");
240 240 btnCursor->setToolTip("Cursor");
241 241 btnCursor->setPopupMode(QToolButton::InstantPopup);
242 242 auto cursorMenu = new QMenu("CursorMenu", this);
243 243 btnCursor->setMenu(cursorMenu);
244 244
245 245 auto noCursorAction = cursorMenu->addAction("No Cursor");
246 246 noCursorAction->setCheckable(true);
247 247 noCursorAction->setChecked(sqpApp->plotsCursorMode()
248 248 == SqpApplication::PlotsCursorMode::NoCursor);
249 249 connect(noCursorAction, &QAction::triggered,
250 250 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::NoCursor); });
251 251
252 252 cursorMenu->addSeparator();
253 253 auto verticalCursorAction = cursorMenu->addAction("Vertical Cursor");
254 254 verticalCursorAction->setCheckable(true);
255 255 verticalCursorAction->setChecked(sqpApp->plotsCursorMode()
256 256 == SqpApplication::PlotsCursorMode::Vertical);
257 257 connect(verticalCursorAction, &QAction::triggered,
258 258 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Vertical); });
259 259
260 260 auto temporalCursorAction = cursorMenu->addAction("Temporal Cursor");
261 261 temporalCursorAction->setCheckable(true);
262 262 temporalCursorAction->setChecked(sqpApp->plotsCursorMode()
263 263 == SqpApplication::PlotsCursorMode::Temporal);
264 264 connect(temporalCursorAction, &QAction::triggered,
265 265 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Temporal); });
266 266
267 267 auto horizontalCursorAction = cursorMenu->addAction("Horizontal Cursor");
268 268 horizontalCursorAction->setCheckable(true);
269 269 horizontalCursorAction->setChecked(sqpApp->plotsCursorMode()
270 270 == SqpApplication::PlotsCursorMode::Horizontal);
271 271 connect(horizontalCursorAction, &QAction::triggered,
272 272 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Horizontal); });
273 273
274 274 auto crossCursorAction = cursorMenu->addAction("Cross Cursor");
275 275 crossCursorAction->setCheckable(true);
276 276 crossCursorAction->setChecked(sqpApp->plotsCursorMode()
277 277 == SqpApplication::PlotsCursorMode::Cross);
278 278 connect(crossCursorAction, &QAction::triggered,
279 279 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Cross); });
280 280
281 281 mainToolBar->addWidget(btnCursor);
282 282
283 283 auto cursorModeActionGroup = new QActionGroup{this};
284 284 cursorModeActionGroup->setExclusive(true);
285 285 cursorModeActionGroup->addAction(noCursorAction);
286 286 cursorModeActionGroup->addAction(verticalCursorAction);
287 287 cursorModeActionGroup->addAction(temporalCursorAction);
288 288 cursorModeActionGroup->addAction(horizontalCursorAction);
289 289 cursorModeActionGroup->addAction(crossCursorAction);
290 290
291 291 // //////// //
292 292 // Settings //
293 293 // //////// //
294 294
295 295 // Registers "general settings" widget to the settings dialog
296 296 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
297 297 impl->m_GeneralSettingsWidget);
298 298
299 299 // /////////// //
300 300 // Connections //
301 301 // /////////// //
302 302
303 303 // Controllers / controllers connections
304 304 connect(&sqpApp->timeController(), SIGNAL(timeUpdated(SqpRange)), &sqpApp->variableController(),
305 305 SLOT(onDateTimeOnSelection(SqpRange)));
306 306
307 307 // Widgets / controllers connections
308 308
309 309 // DataSource
310 310 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
311 311 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
312 312
313 313 // Time
314 314 connect(timeWidget, SIGNAL(timeUpdated(SqpRange)), &sqpApp->timeController(),
315 315 SLOT(onTimeToUpdate(SqpRange)));
316 316
317 317 // Visualization
318 318 connect(&sqpApp->visualizationController(),
319 319 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
320 320 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
321 321
322 322 connect(&sqpApp->visualizationController(),
323 323 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)), m_Ui->view,
324 324 SLOT(onRangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
325 325
326 326 // Widgets / widgets connections
327 327
328 328 // For the following connections, we use DirectConnection to allow each widget that can
329 329 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
330 330 // The order of connections is also important, since it determines the order in which each
331 331 // widget will attach its menu
332 332 connect(
333 333 m_Ui->variableInspectorWidget,
334 334 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
335 335 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
336 336 Qt::DirectConnection);
337 337 }
338 338
339 339 MainWindow::~MainWindow()
340 340 {
341 341 }
342 342
343 343 void MainWindow::changeEvent(QEvent *e)
344 344 {
345 345 QMainWindow::changeEvent(e);
346 346 switch (e->type()) {
347 347 case QEvent::LanguageChange:
348 348 m_Ui->retranslateUi(this);
349 349 break;
350 350 default:
351 351 break;
352 352 }
353 353 }
@@ -1,19 +1,20
1 1 #ifndef SCIQLOP_MIMETYPESDEF_H
2 2 #define SCIQLOP_MIMETYPESDEF_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <QString>
7 7
8 8 // ////////////////// //
9 9 // SciQlop Mime Types //
10 10 // ////////////////// //
11 11
12 12 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_GRAPH;
13 13 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_ZONE;
14 14 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_VARIABLE_LIST;
15 15 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_PRODUCT_LIST;
16 16 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_TIME_RANGE;
17 extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_SELECTION_ZONE;
17 18
18 19
19 20 #endif // SCIQLOP_MIMETYPESDEF_H
@@ -1,7 +1,8
1 1 #include "Common/MimeTypesDef.h"
2 2
3 3 const QString MIME_TYPE_GRAPH = QStringLiteral("sciqlop/graph");
4 4 const QString MIME_TYPE_ZONE = QStringLiteral("sciqlop/zone");
5 5 const QString MIME_TYPE_VARIABLE_LIST = QStringLiteral("sciqlop/var-list");
6 6 const QString MIME_TYPE_PRODUCT_LIST = QStringLiteral("sciqlop/product-list");
7 7 const QString MIME_TYPE_TIME_RANGE = QStringLiteral("sciqlop/time-range");
8 const QString MIME_TYPE_SELECTION_ZONE = QStringLiteral("sciqlop/selection-zone");
@@ -1,30 +1,34
1 1 #ifndef SCIQLOP_VISUALIZATIONDRAGWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONDRAGWIDGET_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QMimeData>
6 6 #include <QWidget>
7 7
8 8 class VisualizationDragWidget : public QWidget {
9 9 Q_OBJECT
10 10
11 11 public:
12 12 VisualizationDragWidget(QWidget *parent = nullptr);
13 13
14 virtual QMimeData *mimeData() const = 0;
14 virtual QMimeData *mimeData(const QPoint &position) const = 0;
15 15 virtual bool isDragAllowed() const = 0;
16 virtual void highlightForMerge(bool highlighted) { Q_UNUSED(highlighted); };
16 virtual void highlightForMerge(bool highlighted) { Q_UNUSED(highlighted); }
17
18 /// Custom pixmap to display during a drag operation.
19 /// If the provided pixmap is null, a pixmap of the entire widget is used.
20 virtual QPixmap customDragPixmap(const QPoint &dragPosition);
17 21
18 22 protected:
19 23 virtual void mousePressEvent(QMouseEvent *event) override;
20 24 virtual void mouseMoveEvent(QMouseEvent *event) override;
21 25
22 26 private:
23 27 class VisualizationDragWidgetPrivate;
24 28 spimpl::unique_impl_ptr<VisualizationDragWidgetPrivate> impl;
25 29
26 30 signals:
27 31 void dragDetected(VisualizationDragWidget *dragWidget, const QPoint &dragPosition);
28 32 };
29 33
30 34 #endif // SCIQLOP_VISUALIZATIONDRAGWIDGET_H
@@ -1,123 +1,138
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 class VisualizationWidget;
20 21 class VisualizationZoneWidget;
21 22
22 23 namespace Ui {
23 24 class VisualizationGraphWidget;
24 25 } // namespace Ui
25 26
26 27 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
27 28 Q_OBJECT
28 29
29 30 friend class QCustomPlotSynchronizer;
30 31 friend class VisualizationGraphRenderingDelegate;
31 32
32 33 public:
33 34 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
34 35 virtual ~VisualizationGraphWidget();
35 36
37 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
36 38 VisualizationZoneWidget *parentZoneWidget() const noexcept;
37 39
40 /// Returns the main VisualizationWidget which contains the graph or nullptr
41 VisualizationWidget *parentVisualizationWidget() const;
42
38 43 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
39 44 void enableAcquisition(bool enable);
40 45
41 46 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
42 47
43 48 /// Removes a variable from the graph
44 49 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
45 50
46 51 /// Returns the list of all variables used in the graph
47 52 QList<std::shared_ptr<Variable> > variables() const;
48 53
49 54 /// Sets the y-axis range based on the data of a variable
50 55 void setYRange(std::shared_ptr<Variable> variable);
51 56 SqpRange graphRange() const noexcept;
52 57 void setGraphRange(const SqpRange &range);
53 58
59 /// Returns the ranges of all the selection zones on the graph
60 QVector<SqpRange> selectionZoneRanges() const;
61
62 /// Adds new selection zones in the graph
63 void addSelectionZones(const QVector<SqpRange> &ranges);
64
65 /// Undo the last zoom done with a zoom box
66 void undoZoom();
67
54 68 // IVisualizationWidget interface
55 69 void accept(IVisualizationWidgetVisitor *visitor) override;
56 70 bool canDrop(const Variable &variable) const override;
57 71 bool contains(const Variable &variable) const override;
58 72 QString name() const override;
59 73
60 74 // VisualisationDragWidget
61 QMimeData *mimeData() const override;
75 QMimeData *mimeData(const QPoint &position) const override;
76 QPixmap customDragPixmap(const QPoint &dragPosition) override;
62 77 bool isDragAllowed() const override;
63 78 void highlightForMerge(bool highlighted) override;
64 79
65 80 // Cursors
66 81 /// Adds or moves the vertical cursor at the specified value on the x-axis
67 82 void addVerticalCursor(double time);
68 83 /// Adds or moves the vertical cursor at the specified value on the x-axis
69 84 void addVerticalCursorAtViewportPosition(double position);
70 85 void removeVerticalCursor();
71 86 /// Adds or moves the vertical cursor at the specified value on the y-axis
72 87 void addHorizontalCursor(double value);
73 88 /// Adds or moves the vertical cursor at the specified value on the y-axis
74 89 void addHorizontalCursorAtViewportPosition(double position);
75 90 void removeHorizontalCursor();
76 91
77 92 signals:
78 93 void synchronize(const SqpRange &range, const SqpRange &oldRange);
79 94 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
80 95 bool synchronise);
81 96
82 97 /// Signal emitted when the variable is about to be removed from the graph
83 98 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
84 99 /// Signal emitted when the variable has been added to the graph
85 100 void variableAdded(std::shared_ptr<Variable> var);
86 101
87 102 protected:
88 103 void closeEvent(QCloseEvent *event) override;
89 104 void enterEvent(QEvent *event) override;
90 105 void leaveEvent(QEvent *event) override;
91 106
92 QCustomPlot &plot() noexcept;
107 QCustomPlot &plot() const noexcept;
93 108
94 109 private:
95 110 Ui::VisualizationGraphWidget *ui;
96 111
97 112 class VisualizationGraphWidgetPrivate;
98 113 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
99 114
100 115 private slots:
101 116 /// Slot called when right clicking on the graph (displays a menu)
102 117 void onGraphMenuRequested(const QPoint &pos) noexcept;
103 118
104 119 /// Rescale the X axe to range parameter
105 120 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
106 121
107 122 /// Slot called when a mouse double click was made
108 123 void onMouseDoubleClick(QMouseEvent *event) noexcept;
109 124 /// Slot called when a mouse move was made
110 125 void onMouseMove(QMouseEvent *event) noexcept;
111 126 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
112 127 void onMouseWheel(QWheelEvent *event) noexcept;
113 128 /// Slot called when a mouse press was made, to activate the calibration of a graph
114 129 void onMousePress(QMouseEvent *event) noexcept;
115 130 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
116 131 void onMouseRelease(QMouseEvent *event) noexcept;
117 132
118 133 void onDataCacheVariableUpdated();
119 134
120 135 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
121 136 };
122 137
123 138 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,54 +1,63
1 1 #ifndef SCIQLOP_VISUALIZATIONWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include <Data/SqpRange.h>
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 #include <Common/spimpl.h>
11
10 12 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationWidget)
11 13
12 14 class QMenu;
13 15 class Variable;
14 16 class VisualizationTabWidget;
17 class VisualizationSelectionZoneManager;
15 18
16 19 namespace Ui {
17 20 class VisualizationWidget;
18 21 } // namespace Ui
19 22
20 23 class VisualizationWidget : public QWidget, public IVisualizationWidget {
21 24 Q_OBJECT
22 25
23 26 public:
24 27 explicit VisualizationWidget(QWidget *parent = 0);
25 28 virtual ~VisualizationWidget();
26 29
30 /// Returns the class which manage the selection of selection zone across the visualization
31 VisualizationSelectionZoneManager &selectionZoneManager() const;
32
27 33 // IVisualizationWidget interface
28 34 void accept(IVisualizationWidgetVisitor *visitor) override;
29 35 bool canDrop(const Variable &variable) const override;
30 36 bool contains(const Variable &variable) const override;
31 37 QString name() const override;
32 38
33 39 public slots:
34 40 /**
35 41 * Attaches to a menu the menu relative to the visualization of variables
36 42 * @param menu the parent menu of the generated menu
37 43 * @param variables the variables for which to generate the menu
38 44 */
39 45 void attachVariableMenu(QMenu *menu,
40 46 const QVector<std::shared_ptr<Variable> > &variables) noexcept;
41 47
42 48 /// Slot called when a variable is about to be deleted from SciQlop
43 49 void onVariableAboutToBeDeleted(std::shared_ptr<Variable> variable) noexcept;
44 50
45 51 void onRangeChanged(std::shared_ptr<Variable> variable, const SqpRange &range) noexcept;
46 52
47 53 protected:
48 54 void closeEvent(QCloseEvent *event) override;
49 55
50 56 private:
51 57 Ui::VisualizationWidget *ui;
58
59 class VisualizationWidgetPrivate;
60 spimpl::unique_impl_ptr<VisualizationWidgetPrivate> impl;
52 61 };
53 62
54 63 #endif // VISUALIZATIONWIDGET_H
@@ -1,95 +1,98
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 /// Returns the first graph in the zone or nullptr if there is no graph inside
64 VisualizationGraphWidget *firstGraph() const;
65
63 66 // IVisualizationWidget interface
64 67 void accept(IVisualizationWidgetVisitor *visitor) override;
65 68 bool canDrop(const Variable &variable) const override;
66 69 bool contains(const Variable &variable) const override;
67 70 QString name() const override;
68 71
69 72 // VisualisationDragWidget
70 QMimeData *mimeData() const override;
73 QMimeData *mimeData(const QPoint &position) const override;
71 74 bool isDragAllowed() const override;
72 75
73 76 void notifyMouseMoveInGraph(const QPointF &graphPosition, const QPointF &plotPosition,
74 77 VisualizationGraphWidget *graphWidget);
75 78 void notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget);
76 79
77 80 protected:
78 81 void closeEvent(QCloseEvent *event) override;
79 82
80 83 private:
81 84 Ui::VisualizationZoneWidget *ui;
82 85
83 86 class VisualizationZoneWidgetPrivate;
84 87 spimpl::unique_impl_ptr<VisualizationZoneWidgetPrivate> impl;
85 88
86 89 private slots:
87 90 void onVariableAdded(std::shared_ptr<Variable> variable);
88 91 /// Slot called when a variable is about to be removed from a graph contained in the zone
89 92 void onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable);
90 93
91 94 void dropMimeData(int index, const QMimeData *mimeData);
92 95 void dropMimeDataOnGraph(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
93 96 };
94 97
95 98 #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H
@@ -1,101 +1,103
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 'include/Visualization/ColorScaleEditor.h'
21 21 ]
22 22
23 23 gui_ui_files = [
24 24 'ui/DataSource/DataSourceWidget.ui',
25 25 'ui/Settings/SqpSettingsDialog.ui',
26 26 'ui/Settings/SqpSettingsGeneralWidget.ui',
27 27 'ui/SidePane/SqpSidePane.ui',
28 28 'ui/TimeWidget/TimeWidget.ui',
29 29 'ui/Variable/VariableInspectorWidget.ui',
30 30 'ui/Variable/RenameVariableDialog.ui',
31 31 'ui/Variable/VariableMenuHeaderWidget.ui',
32 32 'ui/Visualization/VisualizationGraphWidget.ui',
33 33 'ui/Visualization/VisualizationTabWidget.ui',
34 34 'ui/Visualization/VisualizationWidget.ui',
35 35 'ui/Visualization/VisualizationZoneWidget.ui',
36 36 'ui/Visualization/ColorScaleEditor.ui'
37 37 ]
38 38
39 39 gui_qresources = ['resources/sqpguiresources.qrc']
40 40
41 41 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
42 42 ui_files : gui_ui_files,
43 43 qresources : gui_qresources)
44 44
45 45 gui_sources = [
46 46 'src/SqpApplication.cpp',
47 47 'src/DragAndDrop/DragDropHelper.cpp',
48 48 'src/DragAndDrop/DragDropScroller.cpp',
49 49 'src/DragAndDrop/DragDropTabSwitcher.cpp',
50 50 'src/Common/ColorUtils.cpp',
51 51 'src/Common/VisualizationDef.cpp',
52 52 'src/DataSource/DataSourceTreeWidgetItem.cpp',
53 53 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
54 54 'src/DataSource/DataSourceWidget.cpp',
55 55 'src/DataSource/DataSourceTreeWidget.cpp',
56 56 'src/Settings/SqpSettingsDialog.cpp',
57 57 'src/Settings/SqpSettingsGeneralWidget.cpp',
58 58 'src/SidePane/SqpSidePane.cpp',
59 59 'src/TimeWidget/TimeWidget.cpp',
60 60 'src/Variable/VariableInspectorWidget.cpp',
61 61 'src/Variable/VariableInspectorTableView.cpp',
62 62 'src/Variable/VariableMenuHeaderWidget.cpp',
63 63 'src/Variable/RenameVariableDialog.cpp',
64 64 'src/Visualization/VisualizationGraphHelper.cpp',
65 65 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
66 66 'src/Visualization/VisualizationGraphWidget.cpp',
67 67 'src/Visualization/VisualizationTabWidget.cpp',
68 68 'src/Visualization/VisualizationWidget.cpp',
69 69 'src/Visualization/VisualizationZoneWidget.cpp',
70 70 'src/Visualization/qcustomplot.cpp',
71 71 'src/Visualization/QCustomPlotSynchronizer.cpp',
72 72 'src/Visualization/operations/FindVariableOperation.cpp',
73 73 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
74 74 'src/Visualization/operations/MenuBuilder.cpp',
75 75 'src/Visualization/operations/RemoveVariableOperation.cpp',
76 76 'src/Visualization/operations/RescaleAxeOperation.cpp',
77 77 'src/Visualization/VisualizationDragDropContainer.cpp',
78 78 'src/Visualization/VisualizationDragWidget.cpp',
79 79 'src/Visualization/AxisRenderingUtils.cpp',
80 80 'src/Visualization/PlottablesRenderingUtils.cpp',
81 81 'src/Visualization/MacScrollBarStyle.cpp',
82 82 'src/Visualization/VisualizationCursorItem.cpp',
83 83 'src/Visualization/ColorScaleEditor.cpp',
84 84 'src/Visualization/SqpColorScale.cpp',
85 'src/Visualization/QCPColorMapIterator.cpp'
85 'src/Visualization/QCPColorMapIterator.cpp',
86 'src/Visualization/VisualizationSelectionZoneItem.cpp',
87 'src/Visualization/VisualizationSelectionZoneManager.cpp'
86 88 ]
87 89
88 90 gui_inc = include_directories(['include'])
89 91
90 92 sciqlop_gui_lib = library('sciqlopgui',
91 93 gui_sources,
92 94 gui_moc_files,
93 95 include_directories : [gui_inc],
94 96 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core],
95 97 install : true
96 98 )
97 99
98 100 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
99 101 include_directories : gui_inc,
100 102 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core])
101 103
@@ -1,492 +1,499
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 24 DragDropHelper::PlaceHolderType m_PlaceHolderType;
25 25
26 26 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
27 27 = [](auto mimeData) { return true; };
28 28 VisualizationDragDropContainer::AcceptDragWidgetFunction m_AcceptDragWidgetFun
29 29 = [](auto dragWidget, auto mimeData) { return true; };
30 30
31 31 int m_MinContainerHeight = 0;
32 32
33 33 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
34 34 : m_PlaceHolderType(DragDropHelper::PlaceHolderType::Graph)
35 35 {
36 36 m_Layout = new QVBoxLayout(widget);
37 37 m_Layout->setContentsMargins(0, 0, 0, 0);
38 38 }
39 39
40 40 bool acceptMimeData(const QMimeData *data) const
41 41 {
42 42 auto accepted = false;
43 43 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
44 44 ++it) {
45 45 const auto &type = it.key();
46 46 const auto &behavior = it.value();
47 47
48 48 if (data->hasFormat(type)) {
49 49 if (behavior != DropBehavior::Forbidden) {
50 50 accepted = true;
51 51 }
52 52 else {
53 53 accepted = false;
54 54 break;
55 55 }
56 56 }
57 57 }
58 58
59 59 if (accepted) {
60 60 accepted = m_AcceptMimeDataFun(data);
61 61 }
62 62
63 63 return accepted;
64 64 }
65 65
66 66 bool allowMergeForMimeData(const QMimeData *data) const
67 67 {
68 68 auto result = false;
69 69 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
70 70 ++it) {
71 71
72 72 if (data->hasFormat(it.key())
73 73 && (it.value() == VisualizationDragDropContainer::DropBehavior::Merged
74 74 || it.value()
75 75 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
76 76 result = true;
77 77 }
78 78 else if (data->hasFormat(it.key())
79 79 && it.value() == VisualizationDragDropContainer::DropBehavior::Inserted) {
80 80 // Merge is forbidden if the mime data contain an acceptable type which cannot be
81 81 // merged
82 82 result = false;
83 83 break;
84 84 }
85 85 }
86 86
87 87 return result;
88 88 }
89 89
90 90 bool allowInsertForMimeData(const QMimeData *data) const
91 91 {
92 92 for (auto it = m_AcceptedMimeTypes.constBegin(); it != m_AcceptedMimeTypes.constEnd();
93 93 ++it) {
94 94 if (data->hasFormat(it.key())
95 95 && (it.value() == VisualizationDragDropContainer::DropBehavior::Inserted
96 96 || it.value()
97 97 == VisualizationDragDropContainer::DropBehavior::InsertedAndMerged)) {
98 98 return true;
99 99 }
100 100 }
101 101
102 102 return false;
103 103 }
104 104
105 105 bool hasPlaceHolder() const
106 106 {
107 107 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
108 108 }
109 109
110 110 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
111 111 {
112 112 VisualizationDragWidget *dragWidget = nullptr;
113 113
114 114 for (auto child : parent->children()) {
115 115 auto widget = qobject_cast<VisualizationDragWidget *>(child);
116 116 if (widget && widget->isVisible()) {
117 117 if (widget->frameGeometry().contains(pos)) {
118 118 dragWidget = widget;
119 119 break;
120 120 }
121 121 }
122 122 }
123 123
124 124 return dragWidget;
125 125 }
126 126
127 127 bool cursorIsInContainer(QWidget *container) const
128 128 {
129 129 auto widgetUnderMouse = sqpApp->widgetAt(QCursor::pos());
130 130 return container->isAncestorOf(widgetUnderMouse) && widgetUnderMouse != container
131 131 && sqpApp->dragDropHelper().placeHolder().isAncestorOf(widgetUnderMouse);
132 132 }
133 133
134 134 int countDragWidget(const QWidget *parent, bool onlyVisible = false) const
135 135 {
136 136 auto nbGraph = 0;
137 137 for (auto child : parent->children()) {
138 138 if (qobject_cast<VisualizationDragWidget *>(child)) {
139 139 if (!onlyVisible || qobject_cast<VisualizationDragWidget *>(child)->isVisible()) {
140 140 nbGraph += 1;
141 141 }
142 142 }
143 143 }
144 144
145 145 return nbGraph;
146 146 }
147 147
148 148 bool findPlaceHolderPosition(const QPoint &pos, const QMimeData *mimeData, bool canInsert,
149 149 bool canMerge, const VisualizationDragDropContainer *container);
150 150 };
151 151
152 152 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
153 153 : QFrame{parent},
154 154 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
155 155 {
156 156 setAcceptDrops(true);
157 157 }
158 158
159 159 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
160 160 {
161 161 impl->m_Layout->addWidget(dragWidget);
162 162 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
163 163 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
164 164 &VisualizationDragDropContainer::startDrag);
165 165 }
166 166
167 167 void VisualizationDragDropContainer::insertDragWidget(int index,
168 168 VisualizationDragWidget *dragWidget)
169 169 {
170 170 impl->m_Layout->insertWidget(index, dragWidget);
171 171 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
172 172 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
173 173 &VisualizationDragDropContainer::startDrag);
174 174 }
175 175
176 176 void VisualizationDragDropContainer::setMimeType(
177 177 const QString &mimeType, VisualizationDragDropContainer::DropBehavior behavior)
178 178 {
179 179 impl->m_AcceptedMimeTypes[mimeType] = behavior;
180 180 }
181 181
182 182 int VisualizationDragDropContainer::countDragWidget() const
183 183 {
184 184 return impl->countDragWidget(this);
185 185 }
186 186
187 187 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
188 188 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
189 189 {
190 190 impl->m_AcceptMimeDataFun = fun;
191 191 }
192 192
193 193 void VisualizationDragDropContainer::setAcceptDragWidgetFunction(
194 194 VisualizationDragDropContainer::AcceptDragWidgetFunction fun)
195 195 {
196 196 impl->m_AcceptDragWidgetFun = fun;
197 197 }
198 198
199 199 void VisualizationDragDropContainer::setPlaceHolderType(DragDropHelper::PlaceHolderType type,
200 200 const QString &placeHolderText)
201 201 {
202 202 impl->m_PlaceHolderType = type;
203 203 impl->m_PlaceHolderText = placeHolderText;
204 204 }
205 205
206 206 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
207 207 const QPoint &dragPosition)
208 208 {
209 209 auto &helper = sqpApp->dragDropHelper();
210 210 helper.resetDragAndDrop();
211 211
212 212 // Note: The management of the drag object is done by Qt
213 213 auto drag = new QDrag{dragWidget};
214 214
215 auto mimeData = dragWidget->mimeData();
215 auto mimeData = dragWidget->mimeData(dragPosition);
216 216 drag->setMimeData(mimeData);
217 217
218 auto pixmap = QPixmap(dragWidget->size());
219 dragWidget->render(&pixmap);
218 auto pixmap = dragWidget->customDragPixmap(dragPosition);
219 if (pixmap.isNull()) {
220 pixmap = QPixmap{dragWidget->size()};
221 dragWidget->render(&pixmap);
222 }
223
220 224 drag->setPixmap(pixmap.scaled(DRAGGED_MINIATURE_WIDTH, DRAGGED_MINIATURE_WIDTH,
221 225 Qt::KeepAspectRatio, Qt::SmoothTransformation));
222 226
223 227 auto image = pixmap.toImage();
224 228 mimeData->setImageData(image);
225 229 mimeData->setUrls({helper.imageTemporaryUrl(image)});
226 230
227 231 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
228 helper.setCurrentDragWidget(dragWidget);
229 232
230 if (impl->cursorIsInContainer(this)) {
231 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
232 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex, impl->m_PlaceHolderType,
233 impl->m_PlaceHolderText);
234 dragWidget->setVisible(false);
235 }
236 else {
237 // The drag starts directly outside the drop zone
238 // do not add the placeHolder
233 if (impl->acceptMimeData(mimeData) && impl->allowInsertForMimeData(mimeData)) {
234 helper.setCurrentDragWidget(dragWidget);
235
236 if (impl->cursorIsInContainer(this)) {
237 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
238 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex, impl->m_PlaceHolderType,
239 impl->m_PlaceHolderText);
240 dragWidget->setVisible(false);
241 }
242 else {
243 // The drag starts directly outside the drop zone
244 // do not add the placeHolder
245 }
239 246 }
240 247
241 248 drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction);
242 249
243 250 helper.doCloseWidgets();
244 251 }
245 252 else {
246 253 qCWarning(LOG_VisualizationDragDropContainer())
247 254 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
248 255 "VisualizationDragWidget is not found in this container.");
249 256 }
250 257 }
251 258
252 259 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
253 260 {
254 261 if (impl->acceptMimeData(event->mimeData())) {
255 262 event->acceptProposedAction();
256 263
257 264 auto &helper = sqpApp->dragDropHelper();
258 265
259 266 if (!impl->hasPlaceHolder()) {
260 267 auto dragWidget = helper.getCurrentDragWidget();
261 268
262 269 if (dragWidget) {
263 270 // If the drag&drop is internal to the visualization, entering the container hide
264 271 // the dragWidget which was made visible by the dragLeaveEvent
265 272 auto parentWidget
266 273 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
267 274 if (parentWidget) {
268 275 dragWidget->setVisible(false);
269 276 }
270 277 }
271 278
272 279 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
273 280 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
274 281 if (!impl->findPlaceHolderPosition(event->pos(), event->mimeData(), canInsert, canMerge,
275 282 this)) {
276 283 event->ignore();
277 284 }
278 285 }
279 286 else {
280 287 // do nothing
281 288 }
282 289 }
283 290 else {
284 291 event->ignore();
285 292 }
286 293
287 294 QWidget::dragEnterEvent(event);
288 295 }
289 296
290 297 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
291 298 {
292 299 Q_UNUSED(event);
293 300
294 301 auto &helper = sqpApp->dragDropHelper();
295 302
296 303 if (!impl->cursorIsInContainer(this)) {
297 304 helper.removePlaceHolder();
298 305 helper.setHightlightedDragWidget(nullptr);
299 306 impl->m_MinContainerHeight = 0;
300 307
301 308 auto dragWidget = helper.getCurrentDragWidget();
302 309 if (dragWidget) {
303 310 // dragWidget has a value only if the drag is started from the visualization
304 311 // In that case, shows the drag widget at its original place
305 312 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
306 313 // drop zone (It is not possible to catch a drop event outside of the application)
307 314
308 315 if (dragWidget) {
309 316 dragWidget->setVisible(true);
310 317 }
311 318 }
312 319 }
313 320 else {
314 321 // Leave event probably received for a child widget.
315 322 // Do nothing.
316 323 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
317 324 }
318 325
319 326 QWidget::dragLeaveEvent(event);
320 327 }
321 328
322 329 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
323 330 {
324 331 if (impl->acceptMimeData(event->mimeData())) {
325 332 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
326 333 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
327 334 impl->findPlaceHolderPosition(event->pos(), event->mimeData(), canInsert, canMerge, this);
328 335 }
329 336 else {
330 337 event->ignore();
331 338 }
332 339
333 340 QWidget::dragMoveEvent(event);
334 341 }
335 342
336 343 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
337 344 {
338 345 auto &helper = sqpApp->dragDropHelper();
339 346
340 347 if (impl->acceptMimeData(event->mimeData())) {
341 348 auto dragWidget = helper.getCurrentDragWidget();
342 349 if (impl->hasPlaceHolder()) {
343 350 // drop where the placeHolder is located
344 351
345 352 auto canInsert = impl->allowInsertForMimeData(event->mimeData());
346 353 if (canInsert) {
347 354 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
348 355
349 356 if (dragWidget) {
350 357 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
351 358 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
352 359 // Correction of the index if the drop occurs in the same container
353 360 // and if the drag is started from the visualization (in that case, the
354 361 // dragWidget is hidden)
355 362 droppedIndex -= 1;
356 363 }
357 364
358 365 dragWidget->setVisible(true);
359 366 }
360 367
361 368 event->acceptProposedAction();
362 369
363 370 helper.removePlaceHolder();
364 371
365 372 emit dropOccuredInContainer(droppedIndex, event->mimeData());
366 373 }
367 374 else {
368 375 qCWarning(LOG_VisualizationDragDropContainer()) << tr(
369 376 "VisualizationDragDropContainer::dropEvent, dropping on the placeHolder, but "
370 377 "the insertion is forbidden.");
371 378 Q_ASSERT(false);
372 379 }
373 380 }
374 381 else if (helper.getHightlightedDragWidget()) {
375 382 // drop on the highlighted widget
376 383
377 384 auto canMerge = impl->allowMergeForMimeData(event->mimeData());
378 385 if (canMerge) {
379 386 event->acceptProposedAction();
380 387 emit dropOccuredOnWidget(helper.getHightlightedDragWidget(), event->mimeData());
381 388 }
382 389 else {
383 390 qCWarning(LOG_VisualizationDragDropContainer())
384 391 << tr("VisualizationDragDropContainer::dropEvent, dropping on a widget, but "
385 392 "the merge is forbidden.");
386 393 Q_ASSERT(false);
387 394 }
388 395 }
389 396 }
390 397 else {
391 398 event->ignore();
392 399 }
393 400
394 401 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
395 402 impl->m_MinContainerHeight = 0;
396 403
397 404 QWidget::dropEvent(event);
398 405 }
399 406
400 407
401 408 bool VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
402 409 const QPoint &pos, const QMimeData *mimeData, bool canInsert, bool canMerge,
403 410 const VisualizationDragDropContainer *container)
404 411 {
405 412 auto &helper = sqpApp->dragDropHelper();
406 413
407 414 auto absPos = container->mapToGlobal(pos);
408 415 auto isOnPlaceHolder = helper.placeHolder().isAncestorOf(sqpApp->widgetAt(absPos));
409 416
410 417 if (countDragWidget(container, true) == 0) {
411 418 // Drop on an empty container, just add the placeHolder at the top
412 419 helper.insertPlaceHolder(m_Layout, 0, m_PlaceHolderType, m_PlaceHolderText);
413 420 }
414 421 else if (!isOnPlaceHolder) {
415 422 auto nbDragWidget = countDragWidget(container);
416 423 if (nbDragWidget > 0) {
417 424
418 425 if (m_MinContainerHeight == 0) {
419 426 m_MinContainerHeight = container->size().height();
420 427 }
421 428
422 429 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
423 430 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
424 431
425 432 auto posY = pos.y();
426 433 auto dropIndex = floor(posY / graphHeight);
427 434 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
428 435
429 436
430 437 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
431 438 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
432 439
433 440 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
434 441
435 442 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
436 443
437 444 if (canInsert && (isOnTop || isOnBottom || !canMerge)) {
438 445 if (isOnBottom) {
439 446 dropIndex += 1;
440 447 }
441 448
442 449 if (helper.getCurrentDragWidget()) {
443 450 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
444 451 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
445 452 // Correction of the index if the drop occurs in the same container
446 453 // and if the drag is started from the visualization (in that case, the
447 454 // dragWidget is hidden)
448 455 dropIndex += 1;
449 456 }
450 457 }
451 458
452 459 if (dropIndex != placeHolderIndex) {
453 460 helper.insertPlaceHolder(m_Layout, dropIndex, m_PlaceHolderType,
454 461 m_PlaceHolderText);
455 462 }
456 463
457 464 helper.setHightlightedDragWidget(nullptr);
458 465 }
459 466 else if (canMerge && dragWidgetHovered) {
460 467 // drop on the middle -> merge
461 468 if (hasPlaceHolder()) {
462 469 helper.removePlaceHolder();
463 470 }
464 471
465 472 if (m_AcceptDragWidgetFun(dragWidgetHovered, mimeData)) {
466 473 helper.setHightlightedDragWidget(dragWidgetHovered);
467 474 return true;
468 475 }
469 476 else {
470 477 return false;
471 478 }
472 479 }
473 480 else {
474 481 qCWarning(LOG_VisualizationDragDropContainer())
475 482 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no valid drop "
476 483 "action.");
477 484 }
478 485 }
479 486 else {
480 487 qCWarning(LOG_VisualizationDragDropContainer())
481 488 << tr("VisualizationDragDropContainer::findPlaceHolderPosition, no widget "
482 489 "found in the "
483 490 "container");
484 491 }
485 492 }
486 493 else {
487 494 // the mouse is hover the placeHolder
488 495 // Do nothing
489 496 }
490 497
491 498 return true;
492 499 }
@@ -1,55 +1,61
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 7 #include <SqpApplication.h>
8 8
9 9 struct VisualizationDragWidget::VisualizationDragWidgetPrivate {
10 10
11 11 QPoint m_DragStartPosition;
12 12 bool m_DragStartPositionValid = false;
13 13
14 14 explicit VisualizationDragWidgetPrivate() {}
15 15 };
16 16
17 17 VisualizationDragWidget::VisualizationDragWidget(QWidget *parent)
18 18 : QWidget{parent}, impl{spimpl::make_unique_impl<VisualizationDragWidgetPrivate>()}
19 19 {
20 20 }
21 21
22 QPixmap VisualizationDragWidget::customDragPixmap(const QPoint &dragPosition)
23 {
24 Q_UNUSED(dragPosition);
25 return QPixmap();
26 }
27
22 28 void VisualizationDragWidget::mousePressEvent(QMouseEvent *event)
23 29 {
24 30 if (event->button() == Qt::LeftButton) {
25 31 impl->m_DragStartPosition = event->pos();
26 32 }
27 33
28 34 impl->m_DragStartPositionValid = isDragAllowed();
29 35
30 36 QWidget::mousePressEvent(event);
31 37 }
32 38
33 39 void VisualizationDragWidget::mouseMoveEvent(QMouseEvent *event)
34 40 {
35 41 if (!impl->m_DragStartPositionValid || !isDragAllowed()) {
36 42 return;
37 43 }
38 44
39 45 if (!(event->buttons() & Qt::LeftButton)) {
40 46 return;
41 47 }
42 48
43 49 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::DragAndDrop
44 50 || event->modifiers().testFlag(Qt::AltModifier)) {
45 51
46 52 if ((event->pos() - impl->m_DragStartPosition).manhattanLength()
47 53 < QApplication::startDragDistance()) {
48 54 return;
49 55 }
50 56
51 57 emit dragDetected(this, impl->m_DragStartPosition);
52 58 }
53 59
54 60 QWidget::mouseMoveEvent(event);
55 61 }
@@ -1,331 +1,332
1 1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 2 #include "Visualization/AxisRenderingUtils.h"
3 3 #include "Visualization/ColorScaleEditor.h"
4 4 #include "Visualization/PlottablesRenderingUtils.h"
5 5 #include "Visualization/SqpColorScale.h"
6 6 #include "Visualization/VisualizationGraphWidget.h"
7 7 #include "Visualization/qcustomplot.h"
8 8
9 9 #include <Common/DateUtils.h>
10 10
11 11 #include <Data/IDataSeries.h>
12 12
13 13 #include <SqpApplication.h>
14 14
15 15 namespace {
16 16
17 17 /// Name of the axes layer in QCustomPlot
18 18 const auto AXES_LAYER = QStringLiteral("axes");
19 19
20 20 /// Icon used to show x-axis properties
21 21 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
22 22
23 23 /// Name of the overlay layer in QCustomPlot
24 24 const auto OVERLAY_LAYER = QStringLiteral("overlay");
25 25
26 26 /// Pixmap used to show x-axis properties
27 27 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
28 28
29 29 /// Tooltip format for graphs
30 30 const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
31 31
32 32 /// Tooltip format for colormaps
33 33 const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
34 34
35 35 /// Offset used to shift the tooltip of the mouse
36 36 const auto TOOLTIP_OFFSET = QPoint{20, 20};
37 37
38 38 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
39 39 const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
40 40
41 41 /// Timeout after which the tooltip is displayed
42 42 const auto TOOLTIP_TIMEOUT = 500;
43 43
44 44 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
45 45 {
46 46 tracer.setInterpolating(false);
47 47 tracer.setStyle(QCPItemTracer::tsCircle);
48 48 tracer.setSize(3);
49 49 tracer.setPen(QPen(Qt::black));
50 50 tracer.setBrush(Qt::black);
51 tracer.setSelectable(false);
51 52 }
52 53
53 54 QPixmap pixmap(const QString &iconPath) noexcept
54 55 {
55 56 return QIcon{iconPath}.pixmap(QSize{16, 16});
56 57 }
57 58
58 59 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
59 60 {
60 61 // Icon
61 62 pixmap.setPixmap(
62 63 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
63 64
64 65 // Position
65 66 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
66 67 pixmap.topLeft->setCoords(1, 0);
67 68 pixmap.setClipToAxisRect(false);
68 69
69 70 // Can be selected
70 71 pixmap.setSelectable(true);
71 72 }
72 73
73 74 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
74 75 {
75 76 // Icon
76 77 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
77 78
78 79 // Position
79 80 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
80 81 itemPixmap.topLeft->setCoords(0, 1);
81 82 itemPixmap.setClipToAxisRect(false);
82 83
83 84 // Can be selected
84 85 itemPixmap.setSelectable(true);
85 86 }
86 87
87 88 void initTitleTextStyle(QCPItemText &text) noexcept
88 89 {
89 90 // Font and background styles
90 91 text.setColor(Qt::gray);
91 92 text.setBrush(Qt::white);
92 93
93 94 // Position
94 95 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
95 96 text.position->setType(QCPItemPosition::ptAxisRectRatio);
96 97 text.position->setCoords(0.5, 0);
98 text.setSelectable(false);
97 99 }
98 100
99 101 /**
100 102 * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
101 103 * This method handles the fact that a colormap axis can be logarithmic or linear.
102 104 * @param colormap the colormap for which to calculate the index
103 105 * @param coord the coord to convert to cell index
104 106 * @param xCoord calculates the x index if true, calculates y index if false
105 107 * @return the cell index
106 108 */
107 109 int colorMapCellIndex(const QCPColorMap &colormap, double coord, bool xCoord)
108 110 {
109 111 // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
110 112 auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
111 113 == QCPAxis::stLogarithmic;
112 114
113 115 if (isLogarithmic) {
114 116 // For a logarithmic axis we can't use the conversion method of colormap, so we calculate
115 117 // the index manually based on the position of the coordinate on the axis
116 118
117 119 // Gets the axis range and the number of values between range bounds to calculate the step
118 120 // between each value of the range
119 121 auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
120 122 auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
121 123 auto valueStep
122 124 = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
123 125
124 126 // According to the coord position, calculates the closest index in the range
125 127 return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
126 128 }
127 129 else {
128 130 // For a linear axis, we use the conversion method of colormap
129 131 int index;
130 132 if (xCoord) {
131 133 colormap.data()->coordToCell(coord, 0., &index, nullptr);
132 134 }
133 135 else {
134 136 colormap.data()->coordToCell(0., coord, nullptr, &index);
135 137 }
136 138
137 139 return index;
138 140 }
139 141 }
140 142
141 143 } // namespace
142 144
143 145 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
144 146 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
145 147 : m_Plot{graphWidget.plot()},
146 148 m_PointTracer{new QCPItemTracer{&m_Plot}},
147 149 m_TracerTimer{},
148 150 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
149 151 m_TitleText{new QCPItemText{&m_Plot}},
150 152 m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
151 153 m_ShowXAxis{true},
152 154 m_XAxisLabel{},
153 155 m_ColorScale{SqpColorScale{m_Plot}}
154 156 {
155 157 initPointTracerStyle(*m_PointTracer);
156 158
157 159 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
158 160 m_TracerTimer.setSingleShot(true);
159 161
160 162 // Inits "close button" in plot overlay
161 163 m_ClosePixmap->setLayer(OVERLAY_LAYER);
162 164 initClosePixmapStyle(*m_ClosePixmap);
163 165
164 166 // Connects pixmap selection to graph widget closing
165 QObject::connect(m_ClosePixmap, &QCPItemPixmap::selectionChanged,
166 [&graphWidget](bool selected) {
167 if (selected) {
167 QObject::connect(&m_Plot, &QCustomPlot::itemClick,
168 [&graphWidget, this](auto item, auto mouseEvent) {
169 if (item == m_ClosePixmap) {
168 170 graphWidget.close();
169 171 }
170 172 });
171 173
172 174 // Inits graph name in plot overlay
173 175 m_TitleText->setLayer(OVERLAY_LAYER);
174 176 m_TitleText->setText(graphWidget.name());
175 177 initTitleTextStyle(*m_TitleText);
176 178
177 179 // Inits "show x-axis button" in plot overlay
178 180 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
179 181 initXAxisPixmapStyle(*m_XAxisPixmap);
180 182
181 183 // Connects pixmap selection to graph x-axis showing/hiding
182 QObject::connect(m_XAxisPixmap, &QCPItemPixmap::selectionChanged, [this]() {
183 if (m_XAxisPixmap->selected()) {
184 QObject::connect(&m_Plot, &QCustomPlot::itemClick, [this](auto item, auto mouseEvent) {
185 if (m_XAxisPixmap == item) {
184 186 // Changes the selection state and refreshes the x-axis
185 187 m_ShowXAxis = !m_ShowXAxis;
186 updateXAxisState();
188 this->updateXAxisState();
187 189 m_Plot.layer(AXES_LAYER)->replot();
188 190
189 191 // Deselects the x-axis pixmap and updates icon
190 m_XAxisPixmap->setSelected(false);
191 192 m_XAxisPixmap->setPixmap(
192 193 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
193 194 m_Plot.layer(OVERLAY_LAYER)->replot();
194 195 }
195 196 });
196 197 }
197 198
198 199 /// Updates state of x-axis according to the current selection of x-axis pixmap
199 200 /// @remarks the method doesn't call plot refresh
200 201 void updateXAxisState() noexcept
201 202 {
202 203 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
203 204 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
204 205 }
205 206
206 207 QCustomPlot &m_Plot;
207 208 QCPItemTracer *m_PointTracer;
208 209 QTimer m_TracerTimer;
209 210 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
210 211 QCPItemText *m_TitleText; /// Graph's title
211 212 QCPItemPixmap *m_XAxisPixmap;
212 213 bool m_ShowXAxis; /// X-axis properties are shown or hidden
213 214 QString m_XAxisLabel;
214 215 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
215 216 };
216 217
217 218 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
218 219 VisualizationGraphWidget &graphWidget)
219 220 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
220 221 {
221 222 }
222 223
223 224 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) noexcept
224 225 {
225 226 // Opens color scale editor if color scale is double clicked
226 227 auto colorScale = static_cast<QCPColorScale *>(impl->m_Plot.layoutElementAt(event->pos()));
227 228 if (impl->m_ColorScale.m_Scale == colorScale) {
228 229 if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) {
229 230 impl->m_Plot.replot();
230 231 }
231 232 }
232 233 }
233 234
234 235 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
235 236 {
236 237 // Cancels pending refresh
237 238 impl->m_TracerTimer.disconnect();
238 239
239 240 // Reinits tracers
240 241 impl->m_PointTracer->setGraph(nullptr);
241 242 impl->m_PointTracer->setVisible(false);
242 243 impl->m_Plot.replot();
243 244
244 245 QString tooltip{};
245 246
246 247 // Gets the graph under the mouse position
247 248 auto eventPos = event->pos();
248 249 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
249 250 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
250 251 auto graphData = graph->data();
251 252
252 253 // Gets the closest data point to the mouse
253 254 auto graphDataIt = graphData->findBegin(mouseKey);
254 255 if (graphDataIt != graphData->constEnd()) {
255 256 // Sets tooltip
256 257 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
257 258 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
258 259 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
259 260
260 261 // Displays point tracer
261 262 impl->m_PointTracer->setGraph(graph);
262 263 impl->m_PointTracer->setGraphKey(graphDataIt->key);
263 264 impl->m_PointTracer->setLayer(
264 265 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
265 266 impl->m_PointTracer->setVisible(true);
266 267 impl->m_Plot.replot();
267 268 }
268 269 }
269 270 else if (auto colorMap = qobject_cast<QCPColorMap *>(impl->m_Plot.plottableAt(eventPos))) {
270 271 // Gets x and y coords
271 272 auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
272 273 auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
273 274
274 275 // Calculates x and y cell indexes, and retrieves the underlying value
275 276 auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
276 277 auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
277 278 auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
278 279
279 280 // Sets tooltips
280 281 tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
281 282 formatValue(y, *colorMap->valueAxis()),
282 283 formatValue(value, *colorMap->colorScale()->axis()));
283 284 }
284 285
285 286 if (!tooltip.isEmpty()) {
286 287 // Starts timer to show tooltip after timeout
287 288 auto showTooltip = [tooltip, eventPos, this]() {
288 289 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
289 290 &impl->m_Plot, TOOLTIP_RECT);
290 291 };
291 292
292 293 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
293 294 impl->m_TracerTimer.start();
294 295 }
295 296 }
296 297
297 298 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
298 299 {
299 300 // Updates color scale bounds
300 301 impl->m_ColorScale.updateDataRange();
301 302 impl->m_Plot.replot();
302 303 }
303 304
304 305 void VisualizationGraphRenderingDelegate::setAxesProperties(
305 306 std::shared_ptr<IDataSeries> dataSeries) noexcept
306 307 {
307 308 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
308 309 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
309 310
310 311 auto axisHelper = IAxisHelperFactory::create(dataSeries);
311 312 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
312 313
313 314 // Updates x-axis state
314 315 impl->updateXAxisState();
315 316
316 317 impl->m_Plot.layer(AXES_LAYER)->replot();
317 318 }
318 319
319 320 void VisualizationGraphRenderingDelegate::setPlottablesProperties(
320 321 std::shared_ptr<IDataSeries> dataSeries, PlottablesMap &plottables) noexcept
321 322 {
322 323 auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries);
323 324 plottablesHelper->setProperties(plottables);
324 325 }
325 326
326 327 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
327 328 {
328 329 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
329 330 overlay->setVisible(show);
330 331 overlay->replot();
331 332 }
@@ -1,621 +1,876
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationWidget.h"
7 10 #include "Visualization/VisualizationZoneWidget.h"
8 11 #include "ui_VisualizationGraphWidget.h"
9 12
10 13 #include <Common/MimeTypesDef.h>
11 14 #include <Data/ArrayData.h>
12 15 #include <Data/IDataSeries.h>
13 16 #include <Data/SpectrogramSeries.h>
14 17 #include <DragAndDrop/DragDropHelper.h>
15 18 #include <Settings/SqpSettingsDefs.h>
16 19 #include <SqpApplication.h>
17 20 #include <Time/TimeController.h>
18 21 #include <Variable/Variable.h>
19 22 #include <Variable/VariableController.h>
20 23
21 24 #include <unordered_map>
22 25
23 26 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
24 27
25 28 namespace {
26 29
30 /// Key pressed to enable drag&drop in all modes
31 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
32
27 33 /// Key pressed to enable zoom on horizontal axis
28 34 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
29 35
30 36 /// Key pressed to enable zoom on vertical axis
31 37 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
32 38
33 39 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
34 40 const auto PAN_SPEED = 5;
35 41
36 42 /// Key pressed to enable a calibration pan
37 43 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
38 44
45 /// Key pressed to enable multi selection of selection zones
46 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
47
39 48 /// Minimum size for the zoom box, in percentage of the axis range
40 49 const auto ZOOM_BOX_MIN_SIZE = 0.8;
41 50
42 51 /// Format of the dates appearing in the label of a cursor
43 52 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
44 53
45 54 } // namespace
46 55
47 56 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
48 57
49 58 explicit VisualizationGraphWidgetPrivate(const QString &name)
50 59 : m_Name{name},
51 60 m_DoAcquisition{true},
52 61 m_IsCalibration{false},
53 62 m_RenderingDelegate{nullptr}
54 63 {
55 64 }
56 65
57 66 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
58 67 const SqpRange &range)
59 68 {
60 69 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
61 70
62 71 // Prevents that data has changed to update rendering
63 72 m_RenderingDelegate->onPlotUpdated();
64 73 }
65 74
66 75 QString m_Name;
67 76 // 1 variable -> n qcpplot
68 77 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
69 78 bool m_DoAcquisition;
70 79 bool m_IsCalibration;
71 80 /// Delegate used to attach rendering features to the plot
72 81 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
73 82
74 QCPItemRect *m_DrawingRect = nullptr;
83 QCPItemRect *m_DrawingZoomRect = nullptr;
84 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
85
75 86 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
76 87 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
77 88
78 void configureDrawingRect()
79 {
80 if (m_DrawingRect) {
81 QPen p;
82 p.setWidth(2);
83 m_DrawingRect->setPen(p);
84 }
85 }
89 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
90 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
91 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
92
93 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
86 94
87 95 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
88 96 {
89 97 removeDrawingRect(plot);
90 98
91 99 auto axisPos = posToAxisPos(pos, plot);
92 100
93 m_DrawingRect = new QCPItemRect{&plot};
94 configureDrawingRect();
101 m_DrawingZoomRect = new QCPItemRect{&plot};
102 QPen p;
103 p.setWidth(2);
104 m_DrawingZoomRect->setPen(p);
95 105
96 m_DrawingRect->topLeft->setCoords(axisPos);
97 m_DrawingRect->bottomRight->setCoords(axisPos);
106 m_DrawingZoomRect->topLeft->setCoords(axisPos);
107 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
98 108 }
99 109
100 110 void removeDrawingRect(QCustomPlot &plot)
101 111 {
102 if (m_DrawingRect) {
103 plot.removeItem(m_DrawingRect); // the item is deleted by QCustomPlot
104 m_DrawingRect = nullptr;
112 if (m_DrawingZoomRect) {
113 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
114 m_DrawingZoomRect = nullptr;
105 115 plot.replot(QCustomPlot::rpQueuedReplot);
106 116 }
107 117 }
108 118
119 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
120 {
121 endDrawingZone(graph);
122
123 auto axisPos = posToAxisPos(pos, graph->plot());
124
125 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
126 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
127 m_DrawingZone->setEditionEnabled(false);
128 }
129
130 void endDrawingZone(VisualizationGraphWidget *graph)
131 {
132 if (m_DrawingZone) {
133 auto drawingZoneRange = m_DrawingZone->range();
134 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
135 m_DrawingZone->setEditionEnabled(true);
136 addSelectionZone(m_DrawingZone);
137 }
138 else {
139 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
140 }
141
142 graph->plot().replot(QCustomPlot::rpQueuedReplot);
143 m_DrawingZone = nullptr;
144 }
145 }
146
147 void setSelectionZonesEditionEnabled(bool value)
148 {
149 for (auto s : m_SelectionZones) {
150 s->setEditionEnabled(value);
151 }
152 }
153
154 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
155
156 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
157 const QCustomPlot &plot) const
158 {
159 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
160 auto minDistanceToZone = -1;
161 for (auto zone : m_SelectionZones) {
162 auto distanceToZone = zone->selectTest(pos, false);
163 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
164 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
165 selectionZoneItemUnderCursor = zone;
166 }
167 }
168
169 return selectionZoneItemUnderCursor;
170 }
171
109 172 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
110 173 {
111 174 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
112 175 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
113 176 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
114 177 }
115 178
116 179 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
117 180 {
118 181 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
119 182 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
120
121 183 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
122 184 }
123 185 };
124 186
125 187 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
126 188 : VisualizationDragWidget{parent},
127 189 ui{new Ui::VisualizationGraphWidget},
128 190 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
129 191 {
130 192 ui->setupUi(this);
131 193
132 194 // 'Close' options : widget is deleted when closed
133 195 setAttribute(Qt::WA_DeleteOnClose);
134 196
135 197 // Set qcpplot properties :
136 // - Drag (on x-axis) and zoom are enabled
198 // - zoom is enabled
137 199 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
138 ui->widget->setInteractions(QCP::iRangeZoom | QCP::iSelectItems);
200 ui->widget->setInteractions(QCP::iRangeZoom);
201 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
139 202
140 203 // The delegate must be initialized after the ui as it uses the plot
141 204 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
142 205
143 206 // Init the cursors
144 207 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
145 208 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
146 209 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
147 210 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
148 211
149 212 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
150 213 connect(ui->widget, &QCustomPlot::mouseRelease, this,
151 214 &VisualizationGraphWidget::onMouseRelease);
152 215 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
153 216 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
154 217 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
155 218 &VisualizationGraphWidget::onMouseDoubleClick);
156 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
157 &QCPAxis::rangeChanged),
158 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
219 connect(
220 ui->widget->xAxis,
221 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
222 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
159 223
160 224 // Activates menu when right clicking on the graph
161 225 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
162 226 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
163 227 &VisualizationGraphWidget::onGraphMenuRequested);
164 228
165 229 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
166 230 &VariableController::onRequestDataLoading);
167 231
168 232 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
169 233 &VisualizationGraphWidget::onUpdateVarDisplaying);
170 234
171 235 #ifdef Q_OS_MAC
172 236 plot().setPlottingHint(QCP::phFastPolylines, true);
173 237 #endif
174 238 }
175 239
176 240
177 241 VisualizationGraphWidget::~VisualizationGraphWidget()
178 242 {
179 243 delete ui;
180 244 }
181 245
182 246 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
183 247 {
184 248 auto parent = parentWidget();
185 249 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
186 250 parent = parent->parentWidget();
187 251 }
188 252
189 253 return qobject_cast<VisualizationZoneWidget *>(parent);
190 254 }
191 255
256 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
257 {
258 auto parent = parentWidget();
259 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
260 parent = parent->parentWidget();
261 }
262
263 return qobject_cast<VisualizationWidget *>(parent);
264 }
265
192 266 void VisualizationGraphWidget::enableAcquisition(bool enable)
193 267 {
194 268 impl->m_DoAcquisition = enable;
195 269 }
196 270
197 271 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
198 272 {
199 273 // Uses delegate to create the qcpplot components according to the variable
200 274 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
201 275
202 276 if (auto dataSeries = variable->dataSeries()) {
203 277 // Set axes properties according to the units of the data series
204 278 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
205 279
206 280 // Sets rendering properties for the new plottables
207 281 // Warning: this method must be called after setAxesProperties(), as it can access to some
208 282 // axes properties that have to be initialized
209 283 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
210 284 }
211 285
212 286 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
213 287
214 288 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
215 289
216 290 this->enableAcquisition(false);
217 291 this->setGraphRange(range);
218 292 this->enableAcquisition(true);
219 293
220 294 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
221 295
222 296 emit variableAdded(variable);
223 297 }
224 298
225 299 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
226 300 {
227 301 // Each component associated to the variable :
228 302 // - is removed from qcpplot (which deletes it)
229 303 // - is no longer referenced in the map
230 304 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
231 305 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
232 306 emit variableAboutToBeRemoved(variable);
233 307
234 308 auto &plottablesMap = variableIt->second;
235 309
236 310 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
237 311 plottableIt != plottableEnd;) {
238 312 ui->widget->removePlottable(plottableIt->second);
239 313 plottableIt = plottablesMap.erase(plottableIt);
240 314 }
241 315
242 316 impl->m_VariableToPlotMultiMap.erase(variableIt);
243 317 }
244 318
245 319 // Updates graph
246 320 ui->widget->replot();
247 321 }
248 322
249 323 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
250 324 {
251 325 auto variables = QList<std::shared_ptr<Variable> >{};
252 326 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
253 327 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
254 328 variables << it->first;
255 329 }
256 330
257 331 return variables;
258 332 }
259 333
260 334 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
261 335 {
262 336 if (!variable) {
263 337 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
264 338 return;
265 339 }
266 340
267 341 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
268 342 }
269 343
270 344 SqpRange VisualizationGraphWidget::graphRange() const noexcept
271 345 {
272 346 auto graphRange = ui->widget->xAxis->range();
273 347 return SqpRange{graphRange.lower, graphRange.upper};
274 348 }
275 349
276 350 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
277 351 {
278 352 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
279 353 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
280 354 ui->widget->replot();
281 355 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
282 356 }
283 357
358 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
359 {
360 QVector<SqpRange> ranges;
361 for (auto zone : impl->m_SelectionZones) {
362 ranges << zone->range();
363 }
364
365 return ranges;
366 }
367
368 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
369 {
370 for (const auto &range : ranges) {
371 // note: ownership is transfered to QCustomPlot
372 auto zone = new VisualizationSelectionZoneItem(&plot());
373 zone->setRange(range.m_TStart, range.m_TEnd);
374 impl->addSelectionZone(zone);
375 }
376
377 plot().replot(QCustomPlot::rpQueuedReplot);
378 }
379
380 void VisualizationGraphWidget::undoZoom()
381 {
382 auto zoom = impl->m_ZoomStack.pop();
383 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
384 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
385
386 axisX->setRange(zoom.first);
387 axisY->setRange(zoom.second);
388
389 plot().replot(QCustomPlot::rpQueuedReplot);
390 }
391
284 392 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
285 393 {
286 394 if (visitor) {
287 395 visitor->visit(this);
288 396 }
289 397 else {
290 398 qCCritical(LOG_VisualizationGraphWidget())
291 399 << tr("Can't visit widget : the visitor is null");
292 400 }
293 401 }
294 402
295 403 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
296 404 {
297 405 auto isSpectrogram = [](const auto &variable) {
298 406 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
299 407 };
300 408
301 409 // - A spectrogram series can't be dropped on graph with existing plottables
302 410 // - No data series can be dropped on graph with existing spectrogram series
303 411 return isSpectrogram(variable)
304 412 ? impl->m_VariableToPlotMultiMap.empty()
305 413 : std::none_of(
306 414 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
307 415 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
308 416 }
309 417
310 418 bool VisualizationGraphWidget::contains(const Variable &variable) const
311 419 {
312 420 // Finds the variable among the keys of the map
313 421 auto variablePtr = &variable;
314 422 auto findVariable
315 423 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
316 424
317 425 auto end = impl->m_VariableToPlotMultiMap.cend();
318 426 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
319 427 return it != end;
320 428 }
321 429
322 430 QString VisualizationGraphWidget::name() const
323 431 {
324 432 return impl->m_Name;
325 433 }
326 434
327 QMimeData *VisualizationGraphWidget::mimeData() const
435 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
328 436 {
329 437 auto mimeData = new QMimeData;
330 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
331 438
332 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
333 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
439 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
440 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
441 && selectionZoneItemUnderCursor) {
442 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
443 selectionZoneItemUnderCursor->range()));
444 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
445 selectionZoneItemUnderCursor->range()));
446 }
447 else {
448 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
449
450 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
451 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
452 }
334 453
335 454 return mimeData;
336 455 }
337 456
457 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
458 {
459 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
460 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
461 && selectionZoneItemUnderCursor) {
462
463 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
464 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
465
466 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
467 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
468 .toSize();
469
470 auto pixmap = QPixmap(zoneSize);
471 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
472
473 return pixmap;
474 }
475
476 return QPixmap();
477 }
478
338 479 bool VisualizationGraphWidget::isDragAllowed() const
339 480 {
340 481 return true;
341 482 }
342 483
343 484 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
344 485 {
345 486 if (highlighted) {
346 487 plot().setBackground(QBrush(QColor("#BBD5EE")));
347 488 }
348 489 else {
349 490 plot().setBackground(QBrush(Qt::white));
350 491 }
351 492
352 493 plot().update();
353 494 }
354 495
355 496 void VisualizationGraphWidget::addVerticalCursor(double time)
356 497 {
357 498 impl->m_VerticalCursor->setPosition(time);
358 499 impl->m_VerticalCursor->setVisible(true);
359 500
360 501 auto text
361 502 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
362 503 impl->m_VerticalCursor->setLabelText(text);
363 504 }
364 505
365 506 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
366 507 {
367 508 impl->m_VerticalCursor->setAbsolutePosition(position);
368 509 impl->m_VerticalCursor->setVisible(true);
369 510
370 511 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
371 512 auto text
372 513 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
373 514 impl->m_VerticalCursor->setLabelText(text);
374 515 }
375 516
376 517 void VisualizationGraphWidget::removeVerticalCursor()
377 518 {
378 519 impl->m_VerticalCursor->setVisible(false);
379 520 plot().replot(QCustomPlot::rpQueuedReplot);
380 521 }
381 522
382 523 void VisualizationGraphWidget::addHorizontalCursor(double value)
383 524 {
384 525 impl->m_HorizontalCursor->setPosition(value);
385 526 impl->m_HorizontalCursor->setVisible(true);
386 527 impl->m_HorizontalCursor->setLabelText(QString::number(value));
387 528 }
388 529
389 530 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
390 531 {
391 532 impl->m_HorizontalCursor->setAbsolutePosition(position);
392 533 impl->m_HorizontalCursor->setVisible(true);
393 534
394 535 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
395 536 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
396 537 }
397 538
398 539 void VisualizationGraphWidget::removeHorizontalCursor()
399 540 {
400 541 impl->m_HorizontalCursor->setVisible(false);
401 542 plot().replot(QCustomPlot::rpQueuedReplot);
402 543 }
403 544
404 545 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
405 546 {
406 547 Q_UNUSED(event);
407 548
408 549 // Prevents that all variables will be removed from graph when it will be closed
409 550 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
410 551 emit variableAboutToBeRemoved(variableEntry.first);
411 552 }
412 553 }
413 554
414 555 void VisualizationGraphWidget::enterEvent(QEvent *event)
415 556 {
416 557 Q_UNUSED(event);
417 558 impl->m_RenderingDelegate->showGraphOverlay(true);
418 559 }
419 560
420 561 void VisualizationGraphWidget::leaveEvent(QEvent *event)
421 562 {
422 563 Q_UNUSED(event);
423 564 impl->m_RenderingDelegate->showGraphOverlay(false);
424 565
425 566 if (auto parentZone = parentZoneWidget()) {
426 567 parentZone->notifyMouseLeaveGraph(this);
427 568 }
428 569 else {
429 570 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
430 571 }
572
573 if (impl->m_HoveredZone) {
574 impl->m_HoveredZone->setHovered(false);
575 impl->m_HoveredZone = nullptr;
576 }
431 577 }
432 578
433 QCustomPlot &VisualizationGraphWidget::plot() noexcept
579 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
434 580 {
435 581 return *ui->widget;
436 582 }
437 583
438 584 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
439 585 {
440 586 QMenu graphMenu{};
441 587
442 588 // Iterates on variables (unique keys)
443 589 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
444 590 end = impl->m_VariableToPlotMultiMap.cend();
445 591 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
446 592 // 'Remove variable' action
447 593 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
448 594 [ this, var = it->first ]() { removeVariable(var); });
449 595 }
450 596
597 if (!impl->m_ZoomStack.isEmpty()) {
598 if (!graphMenu.isEmpty()) {
599 graphMenu.addSeparator();
600 }
601
602 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
603 }
604
451 605 if (!graphMenu.isEmpty()) {
452 606 graphMenu.exec(QCursor::pos());
453 607 }
454 608 }
455 609
456 610 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
457 611 {
458 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
459 << QThread::currentThread()->objectName() << "DoAcqui"
460 << impl->m_DoAcquisition;
612 qCDebug(LOG_VisualizationGraphWidget())
613 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
614 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
461 615
462 616 auto graphRange = SqpRange{t1.lower, t1.upper};
463 617 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
464 618
465 619 if (impl->m_DoAcquisition) {
466 620 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
467 621
468 622 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
469 623 end = impl->m_VariableToPlotMultiMap.end();
470 624 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
471 625 variableUnderGraphVector.push_back(it->first);
472 626 }
473 627 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
474 628 !impl->m_IsCalibration);
475 629
476 630 if (!impl->m_IsCalibration) {
477 631 qCDebug(LOG_VisualizationGraphWidget())
478 632 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
479 633 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
480 634 emit synchronize(graphRange, oldGraphRange);
481 635 }
482 636 }
483 637
484 638 auto pos = mapFromGlobal(QCursor::pos());
485 639 auto axisPos = impl->posToAxisPos(pos, plot());
486 640 if (auto parentZone = parentZoneWidget()) {
487 641 if (impl->pointIsInAxisRect(axisPos, plot())) {
488 642 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
489 643 }
490 644 else {
491 645 parentZone->notifyMouseLeaveGraph(this);
492 646 }
493 647 }
494 648 else {
495 649 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
496 650 }
497 651 }
498 652
499 653 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
500 654 {
501 655 impl->m_RenderingDelegate->onMouseDoubleClick(event);
502 656 }
503 657
504 658 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
505 659 {
506 660 // Handles plot rendering when mouse is moving
507 661 impl->m_RenderingDelegate->onMouseMove(event);
508 662
509 663 auto axisPos = impl->posToAxisPos(event->pos(), plot());
510 664
511 if (impl->m_DrawingRect) {
512 impl->m_DrawingRect->bottomRight->setCoords(axisPos);
665 // Zoom box and zone drawing
666 if (impl->m_DrawingZoomRect) {
667 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
668 }
669 else if (impl->m_DrawingZone) {
670 impl->m_DrawingZone->setEnd(axisPos.x());
513 671 }
514 672
673 // Cursor
515 674 if (auto parentZone = parentZoneWidget()) {
516 675 if (impl->pointIsInAxisRect(axisPos, plot())) {
517 676 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
518 677 }
519 678 else {
520 679 parentZone->notifyMouseLeaveGraph(this);
521 680 }
522 681 }
523 682 else {
524 683 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
525 684 }
526 685
686 // Search for the selection zone under the mouse
687 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
688 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
689 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
690
691 // Sets the appropriate cursor shape
692 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
693 setCursor(cursorShape);
694
695 // Manages the hovered zone
696 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
697 if (impl->m_HoveredZone) {
698 impl->m_HoveredZone->setHovered(false);
699 }
700 selectionZoneItemUnderCursor->setHovered(true);
701 impl->m_HoveredZone = selectionZoneItemUnderCursor;
702 plot().replot(QCustomPlot::rpQueuedReplot);
703 }
704 }
705 else {
706 // There is no zone under the mouse or the interaction mode is not "selection zones"
707 if (impl->m_HoveredZone) {
708 impl->m_HoveredZone->setHovered(false);
709 impl->m_HoveredZone = nullptr;
710 }
711
712 setCursor(Qt::ArrowCursor);
713 }
714
715 impl->m_HasMovedMouse = true;
527 716 VisualizationDragWidget::mouseMoveEvent(event);
528 717 }
529 718
530 719 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
531 720 {
532 721 auto value = event->angleDelta().x() + event->angleDelta().y();
533 722 if (value != 0) {
534 723
535 724 auto direction = value > 0 ? 1.0 : -1.0;
536 725 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
537 726 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
538 727 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
539 728
540 729 auto zoomOrientations = QFlags<Qt::Orientation>{};
541 730 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
542 731 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
543 732
544 733 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
545 734
546 735 if (!isZoomX && !isZoomY) {
547 736 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
548 737 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
549 738
550 739 axis->setRange(axis->range() + diff);
551 740
552 741 if (plot().noAntialiasingOnDrag()) {
553 742 plot().setNotAntialiasedElements(QCP::aeAll);
554 743 }
555 744
556 745 plot().replot(QCustomPlot::rpQueuedReplot);
557 746 }
558 747 }
559 748 }
560 749
561 750 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
562 751 {
563 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
564 impl->startDrawingRect(event->pos(), plot());
752 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
753 auto isSelectionZoneMode
754 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
755 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
756
757 if (!isDragDropClick && isLeftClick) {
758 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
759 // Starts a zoom box
760 impl->startDrawingRect(event->pos(), plot());
761 }
762 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
763 // Starts a new selection zone
764 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
765 if (!zoneAtPos) {
766 impl->startDrawingZone(event->pos(), this);
767 }
768 }
769 }
770
771 // Allows mouse panning only in default mode
772 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
773 == SqpApplication::PlotsInteractionMode::None
774 && !isDragDropClick);
775
776 // Allows zone edition only in selection zone mode without drag&drop
777 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
778
779 // Selection / Deselection
780 if (isSelectionZoneMode) {
781 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
782 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
783 if (selectionZoneItemUnderCursor && isLeftClick) {
784 selectionZoneItemUnderCursor->setAssociatedEditedZones(
785 parentVisualizationWidget()->selectionZoneManager().selectedItems());
786 }
787 else if (!isMultiSelectionClick && isLeftClick) {
788 parentVisualizationWidget()->selectionZoneManager().clearSelection();
789 }
790 else {
791 // No selection change
792 }
565 793 }
566 794
795
796 impl->m_HasMovedMouse = false;
567 797 VisualizationDragWidget::mousePressEvent(event);
568 798 }
569 799
570 800 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
571 801 {
572 if (impl->m_DrawingRect) {
802 if (impl->m_DrawingZoomRect) {
573 803
574 804 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
575 805 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
576 806
577 auto newAxisXRange = QCPRange{impl->m_DrawingRect->topLeft->coords().x(),
578 impl->m_DrawingRect->bottomRight->coords().x()};
807 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
808 impl->m_DrawingZoomRect->bottomRight->coords().x()};
579 809
580 auto newAxisYRange = QCPRange{impl->m_DrawingRect->topLeft->coords().y(),
581 impl->m_DrawingRect->bottomRight->coords().y()};
810 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
811 impl->m_DrawingZoomRect->bottomRight->coords().y()};
582 812
583 813 impl->removeDrawingRect(plot());
584 814
585 815 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
586 816 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
817 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
587 818 axisX->setRange(newAxisXRange);
588 819 axisY->setRange(newAxisYRange);
589 820
590 821 plot().replot(QCustomPlot::rpQueuedReplot);
591 822 }
592 823 }
593 824
825 impl->endDrawingZone(this);
826
594 827 impl->m_IsCalibration = false;
828
829 // Selection / Deselection
830 auto isSelectionZoneMode
831 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
832 if (isSelectionZoneMode) {
833 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
834 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
835 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
836 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
837 parentVisualizationWidget()->selectionZoneManager().select(
838 {selectionZoneItemUnderCursor});
839 }
840 else if (!impl->m_HasMovedMouse) {
841 parentVisualizationWidget()->selectionZoneManager().setSelected(
842 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
843 || event->button() == Qt::RightButton);
844 }
845 }
846 else {
847 // No selection change
848 }
849 }
595 850 }
596 851
597 852 void VisualizationGraphWidget::onDataCacheVariableUpdated()
598 853 {
599 854 auto graphRange = ui->widget->xAxis->range();
600 855 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
601 856
602 857 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
603 858 auto variable = variableEntry.first;
604 859 qCDebug(LOG_VisualizationGraphWidget())
605 860 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
606 861 qCDebug(LOG_VisualizationGraphWidget())
607 862 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
608 863 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
609 864 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
610 865 }
611 866 }
612 867 }
613 868
614 869 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
615 870 const SqpRange &range)
616 871 {
617 872 auto it = impl->m_VariableToPlotMultiMap.find(variable);
618 873 if (it != impl->m_VariableToPlotMultiMap.end()) {
619 874 impl->updateData(it->second, variable->dataSeries(), range);
620 875 }
621 876 }
@@ -1,320 +1,329
1 1 #include "Visualization/VisualizationTabWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "ui_VisualizationTabWidget.h"
4 4
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7
8 8 #include "Visualization/MacScrollBarStyle.h"
9 9
10 10 #include "Variable/VariableController.h"
11 11
12 12 #include "Common/MimeTypesDef.h"
13 13
14 14 #include "DragAndDrop/DragDropHelper.h"
15 15 #include "SqpApplication.h"
16 16
17 17 Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget")
18 18
19 19 namespace {
20 20
21 21 /// Generates a default name for a new zone, according to the number of zones already displayed in
22 22 /// the tab
23 23 QString defaultZoneName(const QLayout &layout)
24 24 {
25 25 auto count = 0;
26 26 for (auto i = 0; i < layout.count(); ++i) {
27 27 if (dynamic_cast<VisualizationZoneWidget *>(layout.itemAt(i)->widget())) {
28 28 count++;
29 29 }
30 30 }
31 31
32 32 return QObject::tr("Zone %1").arg(count + 1);
33 33 }
34 34
35 35 /**
36 36 * Applies a function to all zones of the tab represented by its layout
37 37 * @param layout the layout that contains zones
38 38 * @param fun the function to apply to each zone
39 39 */
40 40 template <typename Fun>
41 41 void processZones(QLayout &layout, Fun fun)
42 42 {
43 43 for (auto i = 0; i < layout.count(); ++i) {
44 44 if (auto item = layout.itemAt(i)) {
45 45 if (auto visualizationZoneWidget
46 46 = dynamic_cast<VisualizationZoneWidget *>(item->widget())) {
47 47 fun(*visualizationZoneWidget);
48 48 }
49 49 }
50 50 }
51 51 }
52 52
53 53 } // namespace
54 54
55 55 struct VisualizationTabWidget::VisualizationTabWidgetPrivate {
56 56 explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {}
57 57
58 58 QString m_Name;
59 59
60 60 #ifdef Q_OS_MAC
61 61 std::unique_ptr<MacScrollBarStyle> m_MacScrollBarStyle = std::make_unique<MacScrollBarStyle>();
62 62 #endif
63 63
64 64 void dropGraph(int index, VisualizationTabWidget *tabWidget);
65 65 void dropZone(int index, VisualizationTabWidget *tabWidget);
66 66 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
67 67 VisualizationTabWidget *tabWidget);
68 68 };
69 69
70 70 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent)
71 71 : QWidget{parent},
72 72 ui{new Ui::VisualizationTabWidget},
73 73 impl{spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name)}
74 74 {
75 75 ui->setupUi(this);
76 76
77 77 #ifdef Q_OS_MAC
78 78 impl->m_MacScrollBarStyle->selfInstallOn(ui->scrollArea, true);
79 79 #endif
80 80
81 81 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Zone, "Zone");
82 82 ui->dragDropContainer->layout()->setContentsMargins(0, 0, 0, 12);
83 83 ui->dragDropContainer->layout()->setSpacing(0);
84 84 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
85 85 VisualizationDragDropContainer::DropBehavior::Inserted);
86 86 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
87 87 VisualizationDragDropContainer::DropBehavior::Inserted);
88 88 ui->dragDropContainer->setMimeType(MIME_TYPE_VARIABLE_LIST,
89 89 VisualizationDragDropContainer::DropBehavior::Inserted);
90 90
91 91 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
92 92 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
93 93 ui->dragDropContainer);
94 94 });
95 95
96 96 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
97 97 &VisualizationTabWidget::dropMimeData);
98 98
99 99 sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea);
100 100
101 101 // Widget is deleted when closed
102 102 setAttribute(Qt::WA_DeleteOnClose);
103 103 }
104 104
105 105 VisualizationTabWidget::~VisualizationTabWidget()
106 106 {
107 107 sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea);
108 108 delete ui;
109 109 }
110 110
111 111 void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget)
112 112 {
113 113 ui->dragDropContainer->addDragWidget(zoneWidget);
114 114 }
115 115
116 116 void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget)
117 117 {
118 118 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
119 119 }
120 120
121 121 VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr<Variable> variable)
122 122 {
123 123 return createZone({variable}, -1);
124 124 }
125 125
126 126 VisualizationZoneWidget *
127 127 VisualizationTabWidget::createZone(const QList<std::shared_ptr<Variable> > &variables, int index)
128 128 {
129 129 auto zoneWidget = createEmptyZone(index);
130 130
131 131 // Creates a new graph into the zone
132 132 zoneWidget->createGraph(variables, index);
133 133
134 134 return zoneWidget;
135 135 }
136 136
137 137 VisualizationZoneWidget *VisualizationTabWidget::createEmptyZone(int index)
138 138 {
139 139 auto zoneWidget
140 140 = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this};
141 141 this->insertZone(index, zoneWidget);
142 142
143 143 return zoneWidget;
144 144 }
145 145
146 146 void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor)
147 147 {
148 148 if (visitor) {
149 149 visitor->visitEnter(this);
150 150
151 151 // Apply visitor to zone children: widgets different from zones are not visited (no action)
152 152 processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) {
153 153 zoneWidget.accept(visitor);
154 154 });
155 155
156 156 visitor->visitLeave(this);
157 157 }
158 158 else {
159 159 qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null");
160 160 }
161 161 }
162 162
163 163 bool VisualizationTabWidget::canDrop(const Variable &variable) const
164 164 {
165 165 // A tab can always accomodate a variable
166 166 Q_UNUSED(variable);
167 167 return true;
168 168 }
169 169
170 170 bool VisualizationTabWidget::contains(const Variable &variable) const
171 171 {
172 172 Q_UNUSED(variable);
173 173 return false;
174 174 }
175 175
176 176 QString VisualizationTabWidget::name() const
177 177 {
178 178 return impl->m_Name;
179 179 }
180 180
181 181 void VisualizationTabWidget::closeEvent(QCloseEvent *event)
182 182 {
183 183 // Closes zones in the tab
184 184 processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); });
185 185
186 186 QWidget::closeEvent(event);
187 187 }
188 188
189 189 QLayout &VisualizationTabWidget::tabLayout() const noexcept
190 190 {
191 191 return *ui->dragDropContainer->layout();
192 192 }
193 193
194 194 void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData)
195 195 {
196 196 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
197 197 impl->dropGraph(index, this);
198 198 }
199 199 else if (mimeData->hasFormat(MIME_TYPE_ZONE)) {
200 200 impl->dropZone(index, this);
201 201 }
202 202 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
203 203 auto variables = sqpApp->variableController().variablesForMimeData(
204 204 mimeData->data(MIME_TYPE_VARIABLE_LIST));
205 205 impl->dropVariables(variables, index, this);
206 206 }
207 207 else {
208 208 qCWarning(LOG_VisualizationZoneWidget())
209 209 << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received.");
210 210 }
211 211 }
212 212
213 213 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph(
214 214 int index, VisualizationTabWidget *tabWidget)
215 215 {
216 216 auto &helper = sqpApp->dragDropHelper();
217 217
218 218 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
219 219 if (!graphWidget) {
220 220 qCWarning(LOG_VisualizationZoneWidget())
221 221 << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not "
222 222 "found or invalid.");
223 223 Q_ASSERT(false);
224 224 return;
225 225 }
226 226
227 227 auto parentDragDropContainer
228 228 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
229 229 if (!parentDragDropContainer) {
230 230 qCWarning(LOG_VisualizationZoneWidget())
231 231 << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of "
232 232 "the dropped graph is not found.");
233 233 Q_ASSERT(false);
234 234 return;
235 235 }
236 236
237 237 auto nbGraph = parentDragDropContainer->countDragWidget();
238 238
239 239 const auto &variables = graphWidget->variables();
240 240
241 241 if (!variables.isEmpty()) {
242 242 // Abort the requests for the variables (if any)
243 243 // Commented, because it's not sure if it's needed or not
244 244 // for (const auto& var : variables)
245 245 //{
246 246 // sqpApp->variableController().onAbortProgressRequested(var);
247 247 //}
248 248
249 249 if (nbGraph == 1) {
250 250 // This is the only graph in the previous zone, close the zone
251 251 helper.delayedCloseWidget(graphWidget->parentZoneWidget());
252 252 }
253 253 else {
254 254 // Close the graph
255 255 helper.delayedCloseWidget(graphWidget);
256 256 }
257 257
258 tabWidget->createZone(variables, index);
258 auto zoneWidget = tabWidget->createZone(variables, index);
259 auto firstGraph = zoneWidget->firstGraph();
260 if (firstGraph) {
261 firstGraph->addSelectionZones(graphWidget->selectionZoneRanges());
262 }
263 else {
264 qCWarning(LOG_VisualizationZoneWidget())
265 << tr("VisualizationTabWidget::dropGraph, no graph added in the widget.");
266 Q_ASSERT(false);
267 }
259 268 }
260 269 else {
261 270 // The graph is empty, create an empty zone and move the graph inside
262 271
263 272 auto parentZoneWidget = graphWidget->parentZoneWidget();
264 273
265 274 parentDragDropContainer->layout()->removeWidget(graphWidget);
266 275
267 276 auto zoneWidget = tabWidget->createEmptyZone(index);
268 277 zoneWidget->addGraph(graphWidget);
269 278
270 279 // Close the old zone if it was the only graph inside
271 280 if (nbGraph == 1) {
272 281 helper.delayedCloseWidget(parentZoneWidget);
273 282 }
274 283 }
275 284 }
276 285
277 286 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone(
278 287 int index, VisualizationTabWidget *tabWidget)
279 288 {
280 289 auto &helper = sqpApp->dragDropHelper();
281 290
282 291 auto zoneWidget = qobject_cast<VisualizationZoneWidget *>(helper.getCurrentDragWidget());
283 292 if (!zoneWidget) {
284 293 qCWarning(LOG_VisualizationZoneWidget())
285 294 << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not "
286 295 "found or invalid.");
287 296 Q_ASSERT(false);
288 297 return;
289 298 }
290 299
291 300 auto parentDragDropContainer
292 301 = qobject_cast<VisualizationDragDropContainer *>(zoneWidget->parentWidget());
293 302 if (!parentDragDropContainer) {
294 303 qCWarning(LOG_VisualizationZoneWidget())
295 304 << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of "
296 305 "the dropped zone is not found.");
297 306 Q_ASSERT(false);
298 307 return;
299 308 }
300 309
301 310 // Simple move of the zone, no variable operation associated
302 311 parentDragDropContainer->layout()->removeWidget(zoneWidget);
303 312 tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget);
304 313 }
305 314
306 315 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables(
307 316 const QList<std::shared_ptr<Variable> > &variables, int index,
308 317 VisualizationTabWidget *tabWidget)
309 318 {
310 319 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
311 320 // compatible variable here
312 321 if (variables.count() > 1) {
313 322 qCWarning(LOG_VisualizationZoneWidget())
314 323 << tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation "
315 324 "aborted.");
316 325 return;
317 326 }
318 327
319 328 tabWidget->createZone(variables, index);
320 329 }
@@ -1,178 +1,197
1 1 #include "Visualization/VisualizationWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationGraphWidget.h"
4 #include "Visualization/VisualizationSelectionZoneManager.h"
4 5 #include "Visualization/VisualizationTabWidget.h"
5 6 #include "Visualization/VisualizationZoneWidget.h"
6 7 #include "Visualization/operations/FindVariableOperation.h"
7 8 #include "Visualization/operations/GenerateVariableMenuOperation.h"
8 9 #include "Visualization/operations/RemoveVariableOperation.h"
9 10 #include "Visualization/operations/RescaleAxeOperation.h"
10 11 #include "Visualization/qcustomplot.h"
11 12
12 13 #include "ui_VisualizationWidget.h"
13 14
14 15 #include "DragAndDrop/DragDropHelper.h"
15 16 #include "SqpApplication.h"
16 17
17 18 #include <QToolButton>
18 19
20 #include <memory>
21
19 22 Q_LOGGING_CATEGORY(LOG_VisualizationWidget, "VisualizationWidget")
20 23
24 struct VisualizationWidget::VisualizationWidgetPrivate {
25 std::unique_ptr<VisualizationSelectionZoneManager> m_ZoneSelectionManager = nullptr;
26
27 VisualizationWidgetPrivate()
28 : m_ZoneSelectionManager(std::make_unique<VisualizationSelectionZoneManager>())
29 {
30 }
31 };
32
21 33 VisualizationWidget::VisualizationWidget(QWidget *parent)
22 : QWidget{parent}, ui{new Ui::VisualizationWidget}
34 : QWidget{parent},
35 ui{new Ui::VisualizationWidget},
36 impl{spimpl::make_unique_impl<VisualizationWidgetPrivate>()}
23 37 {
24 38 ui->setupUi(this);
25 39
26 40 auto addTabViewButton = new QToolButton{ui->tabWidget};
27 41 addTabViewButton->setText(tr("Add View"));
28 42 addTabViewButton->setCursor(Qt::ArrowCursor);
29 43 ui->tabWidget->setCornerWidget(addTabViewButton, Qt::TopRightCorner);
30 44
31 45 auto enableMinimumCornerWidgetSize = [this](bool enable) {
32 46
33 47 auto tabViewCornerWidget = ui->tabWidget->cornerWidget();
34 48 auto width = enable ? tabViewCornerWidget->width() : 0;
35 49 auto height = enable ? tabViewCornerWidget->height() : 0;
36 50 tabViewCornerWidget->setMinimumHeight(height);
37 51 tabViewCornerWidget->setMinimumWidth(width);
38 52 ui->tabWidget->setMinimumHeight(height);
39 53 ui->tabWidget->setMinimumWidth(width);
40 54 };
41 55
42 56 auto addTabView = [this, enableMinimumCornerWidgetSize]() {
43 57 auto widget = new VisualizationTabWidget{QString{"View %1"}.arg(ui->tabWidget->count() + 1),
44 58 ui->tabWidget};
45 59 auto index = ui->tabWidget->addTab(widget, widget->name());
46 60 if (ui->tabWidget->count() > 0) {
47 61 enableMinimumCornerWidgetSize(false);
48 62 }
49 63 qCInfo(LOG_VisualizationWidget()) << tr("add the tab of index %1").arg(index);
50 64 };
51 65
52 66 auto removeTabView = [this, enableMinimumCornerWidgetSize](int index) {
53 67 if (ui->tabWidget->count() == 1) {
54 68 enableMinimumCornerWidgetSize(true);
55 69 }
56 70
57 71 // Removes widget from tab and closes it
58 72 auto widget = ui->tabWidget->widget(index);
59 73 ui->tabWidget->removeTab(index);
60 74 if (widget) {
61 75 widget->close();
62 76 }
63 77
64 78 qCInfo(LOG_VisualizationWidget()) << tr("remove the tab of index %1").arg(index);
65 79
66 80 };
67 81
68 82 ui->tabWidget->setTabsClosable(true);
69 83
70 84 connect(addTabViewButton, &QToolButton::clicked, addTabView);
71 85 connect(ui->tabWidget, &QTabWidget::tabCloseRequested, removeTabView);
72 86
73 87 sqpApp->dragDropHelper().addDragDropTabBar(ui->tabWidget->tabBar());
74 88
75 89 // Adds default tab
76 90 addTabView();
77 91 }
78 92
79 93 VisualizationWidget::~VisualizationWidget()
80 94 {
81 95 sqpApp->dragDropHelper().removeDragDropTabBar(ui->tabWidget->tabBar());
82 96 delete ui;
83 97 }
84 98
99 VisualizationSelectionZoneManager &VisualizationWidget::selectionZoneManager() const
100 {
101 return *impl->m_ZoneSelectionManager.get();
102 }
103
85 104 void VisualizationWidget::accept(IVisualizationWidgetVisitor *visitor)
86 105 {
87 106 if (visitor) {
88 107 visitor->visitEnter(this);
89 108
90 109 // Apply visitor for tab children
91 110 for (auto i = 0; i < ui->tabWidget->count(); ++i) {
92 111 // Widgets different from tabs are not visited (no action)
93 112 if (auto visualizationTabWidget
94 113 = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->widget(i))) {
95 114 visualizationTabWidget->accept(visitor);
96 115 }
97 116 }
98 117
99 118 visitor->visitLeave(this);
100 119 }
101 120 else {
102 121 qCCritical(LOG_VisualizationWidget()) << tr("Can't visit widget : the visitor is null");
103 122 }
104 123 }
105 124
106 125 bool VisualizationWidget::canDrop(const Variable &variable) const
107 126 {
108 127 // The main widget can never accomodate a variable
109 128 Q_UNUSED(variable);
110 129 return false;
111 130 }
112 131
113 132 bool VisualizationWidget::contains(const Variable &variable) const
114 133 {
115 134 Q_UNUSED(variable);
116 135 return false;
117 136 }
118 137
119 138 QString VisualizationWidget::name() const
120 139 {
121 140 return QStringLiteral("MainView");
122 141 }
123 142
124 143 void VisualizationWidget::attachVariableMenu(
125 144 QMenu *menu, const QVector<std::shared_ptr<Variable> > &variables) noexcept
126 145 {
127 146 // Menu is generated only if there is a single variable
128 147 if (variables.size() == 1) {
129 148 if (auto variable = variables.first()) {
130 149 // Gets the containers of the variable
131 150 FindVariableOperation findVariableOperation{variable};
132 151 accept(&findVariableOperation);
133 152 auto variableContainers = findVariableOperation.result();
134 153
135 154 // Generates the actions that make it possible to visualize the variable
136 155 GenerateVariableMenuOperation generateVariableMenuOperation{
137 156 menu, variable, std::move(variableContainers)};
138 157 accept(&generateVariableMenuOperation);
139 158 }
140 159 else {
141 160 qCCritical(LOG_VisualizationWidget()) << tr(
142 161 "Can't generate the menu relative to the visualization: the variable is null");
143 162 }
144 163 }
145 164 else {
146 165 qCDebug(LOG_VisualizationWidget())
147 166 << tr("No generation of the menu related to the visualization: several variables are "
148 167 "selected");
149 168 }
150 169 }
151 170
152 171 void VisualizationWidget::onVariableAboutToBeDeleted(std::shared_ptr<Variable> variable) noexcept
153 172 {
154 173 // Calls the operation of removing all references to the variable in the visualization
155 174 auto removeVariableOperation = RemoveVariableOperation{variable};
156 175 accept(&removeVariableOperation);
157 176 }
158 177
159 178 void VisualizationWidget::onRangeChanged(std::shared_ptr<Variable> variable,
160 179 const SqpRange &range) noexcept
161 180 {
162 181 // Calls the operation of rescaling all graph that contrains variable in the visualization
163 182 auto rescaleVariableOperation = RescaleAxeOperation{variable, range};
164 183 accept(&rescaleVariableOperation);
165 184 }
166 185
167 186 void VisualizationWidget::closeEvent(QCloseEvent *event)
168 187 {
169 188 // Closes tabs in the widget
170 189 for (auto i = 0; i < ui->tabWidget->count(); ++i) {
171 190 if (auto visualizationTabWidget
172 191 = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->widget(i))) {
173 192 visualizationTabWidget->close();
174 193 }
175 194 }
176 195
177 196 QWidget::closeEvent(event);
178 197 }
@@ -1,583 +1,587
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 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 // Returns the first graph in the zone or nullptr if there is no graph inside
76 VisualizationGraphWidget *firstGraph(const VisualizationZoneWidget *zoneWidget) const
77 {
78 VisualizationGraphWidget *firstGraph = nullptr;
79 auto layout = zoneWidget->ui->dragDropContainer->layout();
80 if (layout->count() > 0) {
81 if (auto visualizationGraphWidget
82 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
83 firstGraph = visualizationGraphWidget;
84 }
85 }
86
87 return firstGraph;
88 }
89
90 75 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
91 76 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
92 77 VisualizationZoneWidget *zoneWidget);
93 78 };
94 79
95 80 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
96 81 : VisualizationDragWidget{parent},
97 82 ui{new Ui::VisualizationZoneWidget},
98 83 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
99 84 {
100 85 ui->setupUi(this);
101 86
102 87 ui->zoneNameLabel->setText(name);
103 88
104 89 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Graph);
105 90 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
106 91 VisualizationDragDropContainer::DropBehavior::Inserted);
107 92 ui->dragDropContainer->setMimeType(
108 93 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
109 94 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
110 95 VisualizationDragDropContainer::DropBehavior::Merged);
111 96 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
112 97 VisualizationDragDropContainer::DropBehavior::Forbidden);
98 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
99 VisualizationDragDropContainer::DropBehavior::Forbidden);
113 100 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
114 101 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
115 102 ui->dragDropContainer);
116 103 });
117 104
118 105 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
119 106 if (!mimeData) {
120 107 return false;
121 108 }
122 109
123 110 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
124 111 auto variables = sqpApp->variableController().variablesForMimeData(
125 112 mimeData->data(MIME_TYPE_VARIABLE_LIST));
126 113
127 114 if (variables.count() != 1) {
128 115 return false;
129 116 }
130 117 auto variable = variables.first();
131 118
132 119 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
133 120 return graphWidget->canDrop(*variable);
134 121 }
135 122 }
136 123
137 124 return true;
138 125 };
139 126 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
140 127
141 128 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
142 129 &VisualizationZoneWidget::dropMimeData);
143 130 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
144 131 &VisualizationZoneWidget::dropMimeDataOnGraph);
145 132
146 133 // 'Close' options : widget is deleted when closed
147 134 setAttribute(Qt::WA_DeleteOnClose);
148 135 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
149 136 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
150 137
151 138 // Synchronisation id
152 139 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
153 140 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
154 141 }
155 142
156 143 VisualizationZoneWidget::~VisualizationZoneWidget()
157 144 {
158 145 delete ui;
159 146 }
160 147
161 148 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
162 149 {
163 150 // Synchronize new graph with others in the zone
164 151 impl->m_Synchronizer->addGraph(*graphWidget);
165 152
166 153 ui->dragDropContainer->addDragWidget(graphWidget);
167 154 }
168 155
169 156 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
170 157 {
171 158 // Synchronize new graph with others in the zone
172 159 impl->m_Synchronizer->addGraph(*graphWidget);
173 160
174 161 ui->dragDropContainer->insertDragWidget(index, graphWidget);
175 162 }
176 163
177 164 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
178 165 {
179 166 return createGraph(variable, -1);
180 167 }
181 168
182 169 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
183 170 int index)
184 171 {
185 172 auto graphWidget
186 173 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
187 174
188 175
189 176 // Set graph properties
190 177 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
191 178 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
192 179
193 180
194 181 // Lambda to synchronize zone widget
195 182 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
196 183 const SqpRange &oldGraphRange) {
197 184
198 185 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
199 186 auto frameLayout = ui->dragDropContainer->layout();
200 187 for (auto i = 0; i < frameLayout->count(); ++i) {
201 188 auto graphChild
202 189 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
203 190 if (graphChild && (graphChild != graphWidget)) {
204 191
205 192 auto graphChildRange = graphChild->graphRange();
206 193 switch (zoomType) {
207 194 case AcquisitionZoomType::ZoomIn: {
208 195 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
209 196 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
210 197 graphChildRange.m_TStart += deltaLeft;
211 198 graphChildRange.m_TEnd -= deltaRight;
212 199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
213 200 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
214 201 << deltaLeft;
215 202 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
216 203 << deltaRight;
217 204 qCDebug(LOG_VisualizationZoneWidget())
218 205 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
219 206
220 207 break;
221 208 }
222 209
223 210 case AcquisitionZoomType::ZoomOut: {
224 211 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
225 212 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
226 213 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
227 214 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
228 215 << deltaLeft;
229 216 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
230 217 << deltaRight;
231 218 qCDebug(LOG_VisualizationZoneWidget())
232 219 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
233 220 graphChildRange.m_TStart -= deltaLeft;
234 221 graphChildRange.m_TEnd += deltaRight;
235 222 break;
236 223 }
237 224 case AcquisitionZoomType::PanRight: {
238 225 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
239 226 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
240 227 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
241 228 graphChildRange.m_TStart += deltaLeft;
242 229 graphChildRange.m_TEnd += deltaRight;
243 230 qCDebug(LOG_VisualizationZoneWidget())
244 231 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
245 232 break;
246 233 }
247 234 case AcquisitionZoomType::PanLeft: {
248 235 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
249 236 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
250 237 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
251 238 graphChildRange.m_TStart -= deltaLeft;
252 239 graphChildRange.m_TEnd -= deltaRight;
253 240 break;
254 241 }
255 242 case AcquisitionZoomType::Unknown: {
256 243 qCDebug(LOG_VisualizationZoneWidget())
257 244 << tr("Impossible to synchronize: zoom type unknown");
258 245 break;
259 246 }
260 247 default:
261 248 qCCritical(LOG_VisualizationZoneWidget())
262 249 << tr("Impossible to synchronize: zoom type not take into account");
263 250 // No action
264 251 break;
265 252 }
266 253 graphChild->enableAcquisition(false);
267 254 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
268 255 << graphChild->graphRange();
269 256 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
270 257 << graphChildRange;
271 258 qCDebug(LOG_VisualizationZoneWidget())
272 259 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
273 260 graphChild->setGraphRange(graphChildRange);
274 261 graphChild->enableAcquisition(true);
275 262 }
276 263 }
277 264 };
278 265
279 266 // connection for synchronization
280 267 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
281 268 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
282 269 &VisualizationZoneWidget::onVariableAdded);
283 270 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
284 271 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
285 272
286 273 auto range = SqpRange{};
287 if (auto firstGraph = impl->firstGraph(this)) {
274 if (auto firstGraph = this->firstGraph()) {
288 275 // Case of a new graph in a existant zone
289 276 range = firstGraph->graphRange();
290 277 }
291 278 else {
292 279 // Case of a new graph as the first of the zone
293 280 range = variable->range();
294 281 }
295 282
296 283 this->insertGraph(index, graphWidget);
297 284
298 285 graphWidget->addVariable(variable, range);
299 286 graphWidget->setYRange(variable);
300 287
301 288 return graphWidget;
302 289 }
303 290
304 291 VisualizationGraphWidget *
305 292 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
306 293 {
307 294 if (variables.isEmpty()) {
308 295 return nullptr;
309 296 }
310 297
311 298 auto graphWidget = createGraph(variables.first(), index);
312 299 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
313 300 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
314 301 }
315 302
316 303 return graphWidget;
317 304 }
318 305
306 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
307 {
308 VisualizationGraphWidget *firstGraph = nullptr;
309 auto layout = ui->dragDropContainer->layout();
310 if (layout->count() > 0) {
311 if (auto visualizationGraphWidget
312 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
313 firstGraph = visualizationGraphWidget;
314 }
315 }
316
317 return firstGraph;
318 }
319
319 320 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
320 321 {
321 322 if (visitor) {
322 323 visitor->visitEnter(this);
323 324
324 325 // Apply visitor to graph children: widgets different from graphs are not visited (no
325 326 // action)
326 327 processGraphs(
327 328 *ui->dragDropContainer->layout(),
328 329 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
329 330
330 331 visitor->visitLeave(this);
331 332 }
332 333 else {
333 334 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
334 335 }
335 336 }
336 337
337 338 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
338 339 {
339 340 // A tab can always accomodate a variable
340 341 Q_UNUSED(variable);
341 342 return true;
342 343 }
343 344
344 345 bool VisualizationZoneWidget::contains(const Variable &variable) const
345 346 {
346 347 Q_UNUSED(variable);
347 348 return false;
348 349 }
349 350
350 351 QString VisualizationZoneWidget::name() const
351 352 {
352 353 return ui->zoneNameLabel->text();
353 354 }
354 355
355 QMimeData *VisualizationZoneWidget::mimeData() const
356 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
356 357 {
358 Q_UNUSED(position);
359
357 360 auto mimeData = new QMimeData;
358 361 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
359 362
360 if (auto firstGraph = impl->firstGraph(this)) {
363 if (auto firstGraph = this->firstGraph()) {
361 364 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
362 365 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
363 366 }
364 367
365 368 return mimeData;
366 369 }
367 370
368 371 bool VisualizationZoneWidget::isDragAllowed() const
369 372 {
370 373 return true;
371 374 }
372 375
373 376 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
374 377 const QPointF &plotPosition,
375 378 VisualizationGraphWidget *graphWidget)
376 379 {
377 380 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
378 381 VisualizationGraphWidget &processedGraph) {
379 382
380 383 switch (sqpApp->plotsCursorMode()) {
381 384 case SqpApplication::PlotsCursorMode::Vertical:
382 385 processedGraph.removeHorizontalCursor();
383 386 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
384 387 break;
385 388 case SqpApplication::PlotsCursorMode::Temporal:
386 389 processedGraph.addVerticalCursor(plotPosition.x());
387 390 processedGraph.removeHorizontalCursor();
388 391 break;
389 392 case SqpApplication::PlotsCursorMode::Horizontal:
390 393 processedGraph.removeVerticalCursor();
391 394 if (&processedGraph == graphWidget) {
392 395 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
393 396 }
394 397 else {
395 398 processedGraph.removeHorizontalCursor();
396 399 }
397 400 break;
398 401 case SqpApplication::PlotsCursorMode::Cross:
399 402 if (&processedGraph == graphWidget) {
400 403 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
401 404 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
402 405 }
403 406 else {
404 407 processedGraph.removeHorizontalCursor();
405 408 processedGraph.removeVerticalCursor();
406 409 }
407 410 break;
408 411 case SqpApplication::PlotsCursorMode::NoCursor:
409 412 processedGraph.removeHorizontalCursor();
410 413 processedGraph.removeVerticalCursor();
411 414 break;
412 415 }
413 416
414 417
415 418 });
416 419 }
417 420
418 421 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
419 422 {
420 423 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
421 424 processedGraph.removeHorizontalCursor();
422 425 processedGraph.removeVerticalCursor();
423 426 });
424 427 }
425 428
426 429 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
427 430 {
428 431 // Closes graphs in the zone
429 432 processGraphs(*ui->dragDropContainer->layout(),
430 433 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
431 434
432 435 // Delete synchronization group from variable controller
433 436 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
434 437 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
435 438
436 439 QWidget::closeEvent(event);
437 440 }
438 441
439 442 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
440 443 {
441 444 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
442 445 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
443 446 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
444 447 }
445 448
446 449 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
447 450 {
448 451 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
449 452 Q_ARG(std::shared_ptr<Variable>, variable),
450 453 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
451 454 }
452 455
453 456 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
454 457 {
455 458 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
456 459 impl->dropGraph(index, this);
457 460 }
458 461 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
459 462 auto variables = sqpApp->variableController().variablesForMimeData(
460 463 mimeData->data(MIME_TYPE_VARIABLE_LIST));
461 464 impl->dropVariables(variables, index, this);
462 465 }
463 466 else {
464 467 qCWarning(LOG_VisualizationZoneWidget())
465 468 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
466 469 }
467 470 }
468 471
469 472 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
470 473 const QMimeData *mimeData)
471 474 {
472 475 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
473 476 if (!graphWidget) {
474 477 qCWarning(LOG_VisualizationZoneWidget())
475 478 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
476 479 "drop aborted");
477 480 Q_ASSERT(false);
478 481 return;
479 482 }
480 483
481 484 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
482 485 auto variables = sqpApp->variableController().variablesForMimeData(
483 486 mimeData->data(MIME_TYPE_VARIABLE_LIST));
484 487 for (const auto &var : variables) {
485 488 graphWidget->addVariable(var, graphWidget->graphRange());
486 489 }
487 490 }
488 491 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
489 492 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
490 493 graphWidget->setGraphRange(range);
491 494 }
492 495 else {
493 496 qCWarning(LOG_VisualizationZoneWidget())
494 497 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
495 498 }
496 499 }
497 500
498 501 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
499 502 int index, VisualizationZoneWidget *zoneWidget)
500 503 {
501 504 auto &helper = sqpApp->dragDropHelper();
502 505
503 506 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
504 507 if (!graphWidget) {
505 508 qCWarning(LOG_VisualizationZoneWidget())
506 509 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
507 510 "found or invalid.");
508 511 Q_ASSERT(false);
509 512 return;
510 513 }
511 514
512 515 auto parentDragDropContainer
513 516 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
514 517 if (!parentDragDropContainer) {
515 518 qCWarning(LOG_VisualizationZoneWidget())
516 519 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
517 520 "the dropped graph is not found.");
518 521 Q_ASSERT(false);
519 522 return;
520 523 }
521 524
522 525 const auto &variables = graphWidget->variables();
523 526
524 527 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
525 528 // The drop didn't occur in the same zone
526 529
527 530 // Abort the requests for the variables (if any)
528 531 // Commented, because it's not sure if it's needed or not
529 532 // for (const auto& var : variables)
530 533 //{
531 534 // sqpApp->variableController().onAbortProgressRequested(var);
532 535 //}
533 536
534 537 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
535 538 auto nbGraph = parentDragDropContainer->countDragWidget();
536 539 if (nbGraph == 1) {
537 540 // This is the only graph in the previous zone, close the zone
538 541 helper.delayedCloseWidget(previousParentZoneWidget);
539 542 }
540 543 else {
541 544 // Close the graph
542 545 helper.delayedCloseWidget(graphWidget);
543 546 }
544 547
545 548 // Creates the new graph in the zone
546 zoneWidget->createGraph(variables, index);
549 auto newGraphWidget = zoneWidget->createGraph(variables, index);
550 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
547 551 }
548 552 else {
549 553 // The drop occurred in the same zone or the graph is empty
550 554 // Simple move of the graph, no variable operation associated
551 555 parentDragDropContainer->layout()->removeWidget(graphWidget);
552 556
553 557 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
554 558 // The graph is empty and dropped in a different zone.
555 559 // Take the range of the first graph in the zone (if existing).
556 560 auto layout = zoneWidget->ui->dragDropContainer->layout();
557 561 if (layout->count() > 0) {
558 562 if (auto visualizationGraphWidget
559 563 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
560 564 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
561 565 }
562 566 }
563 567 }
564 568
565 569 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
566 570 }
567 571 }
568 572
569 573 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
570 574 const QList<std::shared_ptr<Variable> > &variables, int index,
571 575 VisualizationZoneWidget *zoneWidget)
572 576 {
573 577 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
574 578 // compatible variable here
575 579 if (variables.count() > 1) {
576 580 qCWarning(LOG_VisualizationZoneWidget())
577 581 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
578 582 "aborted.");
579 583 return;
580 584 }
581 585
582 586 zoneWidget->createGraph(variables, index);
583 587 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved

Status change > Approved

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