##// END OF EJS Templates
Improves visual effect of dropping a variable in a graph
trabillard -
r879:5662e2f354c4
parent child
Show More
@@ -1,85 +1,87
1 1 #ifndef SCIQLOP_DRAGDROPHELPER_H
2 2 #define SCIQLOP_DRAGDROPHELPER_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QLoggingCategory>
6 6 #include <QWidget>
7 7
8 8 class QVBoxLayout;
9 9 class QScrollArea;
10 10 class VisualizationDragWidget;
11 11 class VisualizationDragDropContainer;
12 12 class QMimeData;
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_DragDropHelper)
15 15
16 16 /**
17 17 * @brief Helper class for drag&drop operations.
18 18 * @note The helper is accessible from the sqpApp singleton and has the same life as the whole
19 19 * application (like a controller). But contrary to a controller, it doesn't live in a thread and
20 20 * can interect with the gui.
21 21 * @see SqpApplication
22 22 */
23 23 class DragDropHelper {
24 24 public:
25 25 static const QString MIME_TYPE_GRAPH;
26 26 static const QString MIME_TYPE_ZONE;
27 27
28 28 DragDropHelper();
29 29 virtual ~DragDropHelper();
30 30
31 31 /// Resets some internal variables. Must be called before any new drag&drop operation.
32 32 void resetDragAndDrop();
33 33
34 34 /// Sets the visualization widget currently being drag on the visualization.
35 35 void setCurrentDragWidget(VisualizationDragWidget *dragWidget);
36 36
37 37 /// Returns the visualization widget currently being drag on the visualization.
38 38 /// Can be null if a new visualization widget is intended to be created by the drag&drop
39 39 /// operation.
40 40 VisualizationDragWidget *getCurrentDragWidget() const;
41 41
42 42 QWidget &placeHolder() const;
43 43 void insertPlaceHolder(QVBoxLayout *layout, int index);
44 44 void removePlaceHolder();
45 45 bool isPlaceHolderSet() const;
46 46
47 47 /// Checks if the specified mime data is valid for a drop in the visualization
48 48 bool checkMimeDataForVisualization(const QMimeData *mimeData,
49 49 VisualizationDragDropContainer *dropContainer);
50 50
51 51 void addDragDropScrollArea(QScrollArea *scrollArea);
52 52 void removeDragDropScrollArea(QScrollArea *scrollArea);
53 53
54 54 QUrl imageTemporaryUrl(const QImage &image) const;
55 55
56 void setHightlightedDragWidget(VisualizationDragWidget *dragWidget);
57
56 58 private:
57 59 class DragDropHelperPrivate;
58 60 spimpl::unique_impl_ptr<DragDropHelperPrivate> impl;
59 61 };
60 62
61 63 /**
62 64 * @brief Event filter class which manage the scroll of QScrollArea during a drag&drop operation.
63 65 * @note A QScrollArea inside an other QScrollArea is not fully supported.
64 66 */
65 67 class DragDropScroller : public QObject {
66 68 Q_OBJECT
67 69
68 70 public:
69 71 DragDropScroller(QObject *parent = nullptr);
70 72
71 73 void addScrollArea(QScrollArea *scrollArea);
72 74 void removeScrollArea(QScrollArea *scrollArea);
73 75
74 76 protected:
75 77 bool eventFilter(QObject *obj, QEvent *event);
76 78
77 79 private:
78 80 class DragDropScrollerPrivate;
79 81 spimpl::unique_impl_ptr<DragDropScrollerPrivate> impl;
80 82
81 83 private slots:
82 84 void onTimer();
83 85 };
84 86
85 87 #endif // SCIQLOP_DRAGDROPHELPER_H
@@ -1,29 +1,30
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 14 virtual QMimeData *mimeData() const = 0;
15 15 virtual bool isDragAllowed() const = 0;
16 virtual void highlightForMerge(bool highlighted) { Q_UNUSED(highlighted); };
16 17
17 18 protected:
18 19 virtual void mousePressEvent(QMouseEvent *event) override;
19 20 virtual void mouseMoveEvent(QMouseEvent *event) override;
20 21
21 22 private:
22 23 class VisualizationDragWidgetPrivate;
23 24 spimpl::unique_impl_ptr<VisualizationDragWidgetPrivate> impl;
24 25
25 26 signals:
26 27 void dragDetected(VisualizationDragWidget *dragWidget, const QPoint &dragPosition);
27 28 };
28 29
29 30 #endif // SCIQLOP_VISUALIZATIONDRAGWIDGET_H
@@ -1,107 +1,108
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 15
16 16 class QCPRange;
17 17 class QCustomPlot;
18 18 class SqpRange;
19 19 class Variable;
20 20 class VisualizationZoneWidget;
21 21
22 22 namespace Ui {
23 23 class VisualizationGraphWidget;
24 24 } // namespace Ui
25 25
26 26 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
27 27 Q_OBJECT
28 28
29 29 friend class QCustomPlotSynchronizer;
30 30 friend class VisualizationGraphRenderingDelegate;
31 31
32 32 public:
33 33 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
34 34 virtual ~VisualizationGraphWidget();
35 35
36 36 VisualizationZoneWidget *parentZoneWidget() const noexcept;
37 37
38 38 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
39 39 void enableAcquisition(bool enable);
40 40
41 41 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
42 42
43 43 /// Removes a variable from the graph
44 44 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
45 45
46 46 /// Returns the list of all variables used in the graph
47 47 QList<std::shared_ptr<Variable> > variables() const;
48 48
49 49 void setYRange(const SqpRange &range);
50 50 SqpRange graphRange() const noexcept;
51 51 void setGraphRange(const SqpRange &range);
52 52
53 53 // IVisualizationWidget interface
54 54 void accept(IVisualizationWidgetVisitor *visitor) override;
55 55 bool canDrop(const Variable &variable) const override;
56 56 bool contains(const Variable &variable) const override;
57 57 QString name() const override;
58 58
59 59 // VisualisationDragWidget
60 60 QMimeData *mimeData() const override;
61 61 bool isDragAllowed() const override;
62 void highlightForMerge(bool highlighted) override;
62 63
63 64 signals:
64 65 void synchronize(const SqpRange &range, const SqpRange &oldRange);
65 66 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
66 67 bool synchronise);
67 68
68 69 /// Signal emitted when the variable is about to be removed from the graph
69 70 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
70 71 /// Signal emitted when the variable has been added to the graph
71 72 void variableAdded(std::shared_ptr<Variable> var);
72 73
73 74 protected:
74 75 void closeEvent(QCloseEvent *event) override;
75 76 void enterEvent(QEvent *event) override;
76 77 void leaveEvent(QEvent *event) override;
77 78
78 79 QCustomPlot &plot() noexcept;
79 80
80 81 private:
81 82 Ui::VisualizationGraphWidget *ui;
82 83
83 84 class VisualizationGraphWidgetPrivate;
84 85 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
85 86
86 87 private slots:
87 88 /// Slot called when right clicking on the graph (displays a menu)
88 89 void onGraphMenuRequested(const QPoint &pos) noexcept;
89 90
90 91 /// Rescale the X axe to range parameter
91 92 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
92 93
93 94 /// Slot called when a mouse move was made
94 95 void onMouseMove(QMouseEvent *event) noexcept;
95 96 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
96 97 void onMouseWheel(QWheelEvent *event) noexcept;
97 98 /// Slot called when a mouse press was made, to activate the calibration of a graph
98 99 void onMousePress(QMouseEvent *event) noexcept;
99 100 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
100 101 void onMouseRelease(QMouseEvent *event) noexcept;
101 102
102 103 void onDataCacheVariableUpdated();
103 104
104 105 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
105 106 };
106 107
107 108 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,295 +1,309
1 1 #include "DragDropHelper.h"
2 2 #include "SqpApplication.h"
3 3 #include "Visualization/VisualizationDragDropContainer.h"
4 4 #include "Visualization/VisualizationDragWidget.h"
5 5 #include "Visualization/VisualizationWidget.h"
6 6 #include "Visualization/operations/FindVariableOperation.h"
7 7
8 8 #include "Variable/VariableController.h"
9 9
10 10 #include "Common/MimeTypesDef.h"
11 11 #include "Common/VisualizationDef.h"
12 12
13 13 #include <QDir>
14 14 #include <QDragEnterEvent>
15 15 #include <QDragMoveEvent>
16 16 #include <QScrollArea>
17 17 #include <QScrollBar>
18 18 #include <QTimer>
19 19 #include <QVBoxLayout>
20 20
21 21 const int SCROLL_SPEED = 5;
22 22 const int SCROLL_ZONE_SIZE = 50;
23 23
24 24 Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDrophelper")
25 25
26 26 struct DragDropScroller::DragDropScrollerPrivate {
27 27
28 28 QList<QScrollArea *> m_ScrollAreas;
29 29 QScrollArea *m_CurrentScrollArea = nullptr;
30 30 std::unique_ptr<QTimer> m_Timer = nullptr;
31 31
32
33 32 enum class ScrollDirection { up, down, unknown };
34 33 ScrollDirection m_Direction = ScrollDirection::unknown;
35 34
36 35 explicit DragDropScrollerPrivate() : m_Timer{std::make_unique<QTimer>()}
37 36 {
38 37 m_Timer->setInterval(0);
39 38 }
40 39 };
41 40
42 41 DragDropScroller::DragDropScroller(QObject *parent)
43 42 : QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()}
44 43 {
45 44 connect(impl->m_Timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer);
46 45 }
47 46
48 47 void DragDropScroller::addScrollArea(QScrollArea *scrollArea)
49 48 {
50 49 impl->m_ScrollAreas << scrollArea;
51 50 scrollArea->viewport()->setAcceptDrops(true);
52 51 }
53 52
54 53 void DragDropScroller::removeScrollArea(QScrollArea *scrollArea)
55 54 {
56 55 impl->m_ScrollAreas.removeAll(scrollArea);
57 56 scrollArea->viewport()->setAcceptDrops(false);
58 57 }
59 58
60 59 bool DragDropScroller::eventFilter(QObject *obj, QEvent *event)
61 60 {
62 61 if (event->type() == QEvent::DragMove) {
63 62 auto w = static_cast<QWidget *>(obj);
64 63
65 64 if (impl->m_CurrentScrollArea && impl->m_CurrentScrollArea->isAncestorOf(w)) {
66 65 auto moveEvent = static_cast<QDragMoveEvent *>(event);
67 66
68 67 auto pos = moveEvent->pos();
69 68 if (impl->m_CurrentScrollArea->viewport() != w) {
70 69 auto globalPos = w->mapToGlobal(moveEvent->pos());
71 70 pos = impl->m_CurrentScrollArea->viewport()->mapFromGlobal(globalPos);
72 71 }
73 72
74 73 auto isInTopZone = pos.y() > impl->m_CurrentScrollArea->viewport()->size().height()
75 74 - SCROLL_ZONE_SIZE;
76 75 auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE;
77 76
78 77 if (!isInTopZone && !isInBottomZone) {
79 78 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
80 79 impl->m_Timer->stop();
81 80 }
82 81 else if (!impl->m_Timer->isActive()) {
83 82 impl->m_Direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up
84 83 : DragDropScrollerPrivate::ScrollDirection::down;
85 84 impl->m_Timer->start();
86 85 }
87 86 }
88 87 }
89 88 else if (event->type() == QEvent::DragEnter) {
90 89 auto w = static_cast<QWidget *>(obj);
91 90
92 91 for (auto scrollArea : impl->m_ScrollAreas) {
93 92 if (impl->m_CurrentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) {
94 93 auto enterEvent = static_cast<QDragEnterEvent *>(event);
95 94 enterEvent->acceptProposedAction();
96 95 enterEvent->setDropAction(Qt::IgnoreAction);
97 96 impl->m_CurrentScrollArea = scrollArea;
98 97 break;
99 98 }
100 99 }
101 100 }
102 101 else if (event->type() == QEvent::DragLeave) {
103 102 if (impl->m_CurrentScrollArea) {
104 103 if (!QRect(QPoint(), impl->m_CurrentScrollArea->size())
105 104 .contains(impl->m_CurrentScrollArea->mapFromGlobal(QCursor::pos()))) {
106 105 impl->m_CurrentScrollArea = nullptr;
107 106 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
108 107 impl->m_Timer->stop();
109 108 }
110 109 }
111 110 }
112 111 else if (event->type() == QEvent::Drop) {
113 112 if (impl->m_CurrentScrollArea) {
114 113 impl->m_CurrentScrollArea = nullptr;
115 114 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
116 115 impl->m_Timer->stop();
117 116 }
118 117 }
119 118
120 119 return false;
121 120 }
122 121
123 122 void DragDropScroller::onTimer()
124 123 {
125 124 if (impl->m_CurrentScrollArea) {
126 125 auto mvt = 0;
127 126 switch (impl->m_Direction) {
128 127 case DragDropScrollerPrivate::ScrollDirection::up:
129 128 mvt = SCROLL_SPEED;
130 129 break;
131 130 case DragDropScrollerPrivate::ScrollDirection::down:
132 131 mvt = -SCROLL_SPEED;
133 132 break;
134 133 default:
135 134 break;
136 135 }
137 136
138 137 impl->m_CurrentScrollArea->verticalScrollBar()->setValue(
139 138 impl->m_CurrentScrollArea->verticalScrollBar()->value() + mvt);
140 139 }
141 140 }
142 141
143 142 struct DragDropHelper::DragDropHelperPrivate {
144 143
145 144 VisualizationDragWidget *m_CurrentDragWidget = nullptr;
146 145 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
147 146 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
148 147 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
149 148 // QTemporaryFile to have a name which is not generated.
150 149
150 VisualizationDragWidget *m_HighlightedDragWidget = nullptr;
151
151 152 explicit DragDropHelperPrivate()
152 153 : m_PlaceHolder{std::make_unique<QWidget>()},
153 154 m_DragDropScroller{std::make_unique<DragDropScroller>()}
154 155 {
155 156 m_PlaceHolder->setStyleSheet("background-color: #BBD5EE; border:2px solid #2A7FD4");
156 157 sqpApp->installEventFilter(m_DragDropScroller.get());
157 158
158 159
159 160 m_ImageTempUrl = QDir::temp().absoluteFilePath("Scqlop_graph.png");
160 161 }
161 162
162 163 void preparePlaceHolder() const
163 164 {
164 165 if (m_CurrentDragWidget) {
165 166 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
166 167 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
167 168 }
168 169 else {
169 170 // Configuration of the placeHolder when there is no dragWidget
170 171 // (for instance with a drag from a variable)
171 172
172 173 m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
173 174 m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
174 175 }
175 176 }
176 177 };
177 178
178 179
179 180 DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()}
180 181 {
181 182 }
182 183
183 184 DragDropHelper::~DragDropHelper()
184 185 {
185 186 QFile::remove(impl->m_ImageTempUrl);
186 187 }
187 188
188 189 void DragDropHelper::resetDragAndDrop()
189 190 {
190 191 setCurrentDragWidget(nullptr);
192 impl->m_HighlightedDragWidget = nullptr;
191 193 }
192 194
193 195 void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
194 196 {
195 197 impl->m_CurrentDragWidget = dragWidget;
196 198 }
197 199
198 200 VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const
199 201 {
200 202 return impl->m_CurrentDragWidget;
201 203 }
202 204
203 205
204 206 QWidget &DragDropHelper::placeHolder() const
205 207 {
206 208 return *impl->m_PlaceHolder;
207 209 }
208 210
209 211 void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index)
210 212 {
211 213 removePlaceHolder();
212 214 impl->preparePlaceHolder();
213 215 layout->insertWidget(index, impl->m_PlaceHolder.get());
214 216 impl->m_PlaceHolder->show();
215 217 }
216 218
217 219 void DragDropHelper::removePlaceHolder()
218 220 {
219 221 auto parentWidget = impl->m_PlaceHolder->parentWidget();
220 222 if (parentWidget) {
221 223 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
222 224 impl->m_PlaceHolder->setParent(nullptr);
223 225 impl->m_PlaceHolder->hide();
224 226 }
225 227 }
226 228
227 229 bool DragDropHelper::isPlaceHolderSet() const
228 230 {
229 231 return impl->m_PlaceHolder->parentWidget();
230 232 }
231 233
232 234 void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea)
233 235 {
234 236 impl->m_DragDropScroller->addScrollArea(scrollArea);
235 237 }
236 238
237 239 void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea)
238 240 {
239 241 impl->m_DragDropScroller->removeScrollArea(scrollArea);
240 242 }
241 243
242 244 QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const
243 245 {
244 246 image.save(impl->m_ImageTempUrl);
245 247 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
246 248 }
247 249
250 void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget)
251 {
252 if (impl->m_HighlightedDragWidget) {
253 impl->m_HighlightedDragWidget->highlightForMerge(false);
254 }
255
256 if (dragWidget) {
257 impl->m_HighlightedDragWidget = dragWidget;
258 impl->m_HighlightedDragWidget->highlightForMerge(true);
259 }
260 }
261
248 262 bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData,
249 263 VisualizationDragDropContainer *dropContainer)
250 264 {
251 265 if (!mimeData || !dropContainer) {
252 266 qCWarning(LOG_DragDropHelper()) << QObject::tr(
253 267 "DragDropHelper::checkMimeDataForVisualization, invalid input parameters.");
254 268 Q_ASSERT(false);
255 269 }
256 270
257 271 auto result = true;
258 272
259 273 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
260 274 auto variables = sqpApp->variableController().variablesForMimeData(
261 275 mimeData->data(MIME_TYPE_VARIABLE_LIST));
262 276
263 277 if (variables.count() == 1) {
264 278 // Check that the viariable is not already in a graph
265 279
266 280 // Search for the top level VisualizationWidget
267 281 auto parent = dropContainer->parentWidget();
268 282 while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) {
269 283 parent = parent->parentWidget();
270 284 }
271 285
272 286 if (parent) {
273 287 auto visualizationWidget = static_cast<VisualizationWidget *>(parent);
274 288
275 289 FindVariableOperation findVariableOperation{variables.first()};
276 290 visualizationWidget->accept(&findVariableOperation);
277 291 auto variableContainers = findVariableOperation.result();
278 292 if (!variableContainers.empty()) {
279 293 result = false;
280 294 }
281 295 }
282 296 else {
283 297 qCWarning(LOG_DragDropHelper()) << QObject::tr(
284 298 "DragDropHelper::checkMimeDataForVisualization, the parent "
285 299 "VisualizationWidget cannot be found.");
286 300 result = false;
287 301 }
288 302 }
289 303 else {
290 304 result = false;
291 305 }
292 306 }
293 307
294 308 return result;
295 309 }
@@ -1,362 +1,380
1 1 #include "Visualization/VisualizationDragDropContainer.h"
2 2 #include "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 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
18 18
19 19 QVBoxLayout *m_Layout;
20 20 QStringList m_AcceptedMimeTypes;
21 21 QStringList m_MergeAllowedMimeTypes;
22 22 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
23 23 = [](auto mimeData) { return true; };
24 int m_MinContainerHeight = 0;
24 25
25 26 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
26 27 {
27 28 m_Layout = new QVBoxLayout(widget);
28 29 m_Layout->setContentsMargins(0, 0, 0, 0);
29 30 }
30 31
31 32 bool acceptMimeData(const QMimeData *data) const
32 33 {
33 34 for (const auto &type : m_AcceptedMimeTypes) {
34 35 if (data->hasFormat(type) && m_AcceptMimeDataFun(data)) {
35 36 return true;
36 37 }
37 38 }
38 39
39 40 return false;
40 41 }
41 42
42 43 bool allowMergeMimeData(const QMimeData *data) const
43 44 {
44 45 for (const auto &type : m_MergeAllowedMimeTypes) {
45 46 if (data->hasFormat(type)) {
46 47 return true;
47 48 }
48 49 }
49 50
50 51 return false;
51 52 }
52 53
53 54 bool hasPlaceHolder() const
54 55 {
55 56 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
56 57 }
57 58
58 VisualizationDragWidget *getChildDragWidgetAt(QWidget *parent, const QPoint &pos) const
59 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
59 60 {
60 61 VisualizationDragWidget *dragWidget = nullptr;
61 62
62 63 for (auto child : parent->children()) {
63 64 auto widget = qobject_cast<VisualizationDragWidget *>(child);
64 65 if (widget && widget->isVisible()) {
65 66 if (widget->frameGeometry().contains(pos)) {
66 67 dragWidget = widget;
67 68 break;
68 69 }
69 70 }
70 71 }
71 72
72 73 return dragWidget;
73 74 }
74 75
75 76 bool cursorIsInContainer(QWidget *container) const
76 77 {
77 78 auto adustNum = 18; // to be safe, in case of scrollbar on the side
78 79 auto containerRect = QRect(QPoint(), container->contentsRect().size())
79 80 .adjusted(adustNum, adustNum, -adustNum, -adustNum);
80 81 return containerRect.contains(container->mapFromGlobal(QCursor::pos()));
81 82 }
83
84 int countDragWidget(const QWidget *parent) const
85 {
86 auto nbGraph = 0;
87 for (auto child : parent->children()) {
88 if (qobject_cast<VisualizationDragWidget *>(child)) {
89 nbGraph += 1;
90 }
91 }
92
93 return nbGraph;
94 }
95
96 void findPlaceHolderPosition(const QPoint &pos, bool canMerge,
97 const VisualizationDragDropContainer *container);
82 98 };
83 99
84 100 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
85 101 : QWidget{parent},
86 102 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
87 103 {
88 104 setAcceptDrops(true);
89 105 }
90 106
91 107 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
92 108 {
93 109 impl->m_Layout->addWidget(dragWidget);
94 110 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
95 111 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
96 112 &VisualizationDragDropContainer::startDrag);
97 113 }
98 114
99 115 void VisualizationDragDropContainer::insertDragWidget(int index,
100 116 VisualizationDragWidget *dragWidget)
101 117 {
102 118 impl->m_Layout->insertWidget(index, dragWidget);
103 119 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
104 120 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
105 121 &VisualizationDragDropContainer::startDrag);
106 122 }
107 123
108 124 void VisualizationDragDropContainer::setAcceptedMimeTypes(const QStringList &mimeTypes)
109 125 {
110 126 impl->m_AcceptedMimeTypes = mimeTypes;
111 127 }
112 128
113 129 void VisualizationDragDropContainer::setMergeAllowedMimeTypes(const QStringList &mimeTypes)
114 130 {
115 131 impl->m_MergeAllowedMimeTypes = mimeTypes;
116 132 }
117 133
118 134 int VisualizationDragDropContainer::countDragWidget() const
119 135 {
120 auto nbGraph = 0;
121 for (auto child : children()) {
122 if (qobject_cast<VisualizationDragWidget *>(child)) {
123 nbGraph += 1;
124 }
125 }
126
127 return nbGraph;
136 return impl->countDragWidget(this);
128 137 }
129 138
130 139 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
131 140 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
132 141 {
133 142 impl->m_AcceptMimeDataFun = fun;
134 143 }
135 144
136 145 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
137 146 const QPoint &dragPosition)
138 147 {
139 148 auto &helper = sqpApp->dragDropHelper();
140 149 helper.resetDragAndDrop();
141 150
142 151 // Note: The management of the drag object is done by Qt
143 152 auto drag = new QDrag{dragWidget};
144 153 drag->setHotSpot(dragPosition);
145 154
146 155 auto mimeData = dragWidget->mimeData();
147 156 drag->setMimeData(mimeData);
148 157
149 158 auto pixmap = QPixmap(dragWidget->size());
150 159 dragWidget->render(&pixmap);
151 160 drag->setPixmap(pixmap);
152 161
153 162 auto image = pixmap.toImage();
154 163 mimeData->setImageData(image);
155 164 mimeData->setUrls({helper.imageTemporaryUrl(image)});
156 165
157 166 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
158 167 helper.setCurrentDragWidget(dragWidget);
159 168
160 169 if (impl->cursorIsInContainer(this)) {
161 170 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
162 171 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex);
163 172 dragWidget->setVisible(false);
164 173 }
165 174
166 175 // Note: The exec() is blocking on windows but not on linux and macOS
167 176 drag->exec(Qt::MoveAction | Qt::CopyAction);
168 177 }
169 178 else {
170 179 qCWarning(LOG_VisualizationDragDropContainer())
171 180 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
172 181 "VisualizationDragWidget is not found in this container.");
173 182 }
174 183 }
175 184
176 185 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
177 186 {
178 187 if (impl->acceptMimeData(event->mimeData())) {
179 188 event->acceptProposedAction();
180 189
181 190 auto &helper = sqpApp->dragDropHelper();
182 191
183 192 if (!impl->hasPlaceHolder()) {
184 193 auto dragWidget = helper.getCurrentDragWidget();
185 194
186 195 if (dragWidget) {
187 196 // If the drag&drop is internal to the visualization, entering the container hide
188 // the dragWidget which was hidden by the dragLeaveEvent
197 // the dragWidget which was made visible by the dragLeaveEvent
189 198 auto parentWidget
190 199 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
191 200 if (parentWidget) {
192 201 dragWidget->setVisible(false);
193 202 }
194 203 }
195 204
196 auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos());
197
198 if (dragWidgetHovered) {
199 auto hoveredWidgetIndex = impl->m_Layout->indexOf(dragWidgetHovered);
200
201 if (dragWidget) {
202 auto dragWidgetIndex = impl->m_Layout->indexOf(helper.getCurrentDragWidget());
203 if (dragWidgetIndex >= 0 && dragWidgetIndex <= hoveredWidgetIndex) {
204 // Correction of the index if the drop occurs in the same container
205 // and if the drag is started from the visualization (in that case, the
206 // dragWidget is hidden)
207 hoveredWidgetIndex += 1;
208 }
209 }
210
211 helper.insertPlaceHolder(impl->m_Layout, hoveredWidgetIndex);
212 }
213 else {
214 helper.insertPlaceHolder(impl->m_Layout, 0);
215 }
205 auto canMerge = impl->allowMergeMimeData(event->mimeData());
206 impl->findPlaceHolderPosition(event->pos(), canMerge, this);
216 207 }
217 208 else {
218 209 // do nothing
219 210 }
220 211 }
221 212 else {
222 213 event->ignore();
223 214 }
224 215
225 216 QWidget::dragEnterEvent(event);
226 217 }
227 218
228 219 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
229 220 {
230 221 Q_UNUSED(event);
231 222
232 223 auto &helper = sqpApp->dragDropHelper();
233 224
234 225 if (!impl->cursorIsInContainer(this)) {
235 226 helper.removePlaceHolder();
227 helper.setHightlightedDragWidget(nullptr);
228 impl->m_MinContainerHeight = 0;
236 229
237 230 auto dragWidget = helper.getCurrentDragWidget();
238 231 if (dragWidget) {
239 232 // dragWidget has a value only if the drag is started from the visualization
240 233 // In that case, shows the drag widget at its original place
241 234 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
242 235 // drop zone (It is not possible to catch a drop event outside of the application)
243 236
244 237 if (dragWidget) {
245 238 dragWidget->setVisible(true);
246 239 }
247 240 }
248 241 }
249 242 else {
250 243 // Leave event probably received for a child widget.
251 244 // Do nothing.
252 245 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
253 246 }
254 247
255 248 QWidget::dragLeaveEvent(event);
256 249 }
257 250
258 251 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
259 252 {
260 253 if (impl->acceptMimeData(event->mimeData())) {
261 auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos());
262 if (dragWidgetHovered) {
263 auto canMerge = impl->allowMergeMimeData(event->mimeData());
264
265 auto nbDragWidget = countDragWidget();
266 if (nbDragWidget > 0) {
267 auto graphHeight = qMax(size().height() / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
268
269 auto dropIndex = floor(event->pos().y() / graphHeight);
270 auto zoneSize = qMin(graphHeight / 3.0, 150.0);
271
272 auto isOnTop = event->pos().y() < dropIndex * graphHeight + zoneSize;
273 auto isOnBottom = event->pos().y() > (dropIndex + 1) * graphHeight - zoneSize;
274
275 auto &helper = sqpApp->dragDropHelper();
276 auto placeHolderIndex = impl->m_Layout->indexOf(&(helper.placeHolder()));
277
278 if (isOnTop || isOnBottom) {
279 if (isOnBottom) {
280 dropIndex += 1;
281 }
282
283 if (helper.getCurrentDragWidget()) {
284 auto dragWidgetIndex
285 = impl->m_Layout->indexOf(helper.getCurrentDragWidget());
286 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
287 // Correction of the index if the drop occurs in the same container
288 // and if the drag is started from the visualization (in that case, the
289 // dragWidget is hidden)
290 dropIndex += 1;
291 }
292 }
293
294 if (dropIndex != placeHolderIndex) {
295 helper.insertPlaceHolder(impl->m_Layout, dropIndex);
296 }
297 }
298 else if (canMerge) {
299 // drop on the middle -> merge
300 if (impl->hasPlaceHolder()) {
301 helper.removePlaceHolder();
302 }
303 }
304 }
305 else {
306 qCWarning(LOG_VisualizationDragDropContainer())
307 << tr("VisualizationDragDropContainer::dragMoveEvent, no widget found in the "
308 "container");
309 }
310 }
311 else {
312 // No hovered drag widget, the mouse is probably hover the placeHolder
313 // Do nothing
314 }
254 auto canMerge = impl->allowMergeMimeData(event->mimeData());
255 impl->findPlaceHolderPosition(event->pos(), canMerge, this);
315 256 }
316 257 else {
317 258 event->ignore();
318 259 }
319 260
320 261 QWidget::dragMoveEvent(event);
321 262 }
322 263
323 264 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
324 265 {
325 266 if (impl->acceptMimeData(event->mimeData())) {
326 267 auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget();
327 268 if (impl->hasPlaceHolder()) {
328 269 auto &helper = sqpApp->dragDropHelper();
329 270
330 271 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
331 272
332 273 if (dragWidget) {
333 274 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
334 275 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
335 276 // Correction of the index if the drop occurs in the same container
336 277 // and if the drag is started from the visualization (in that case, the
337 278 // dragWidget is hidden)
338 279 droppedIndex -= 1;
339 280 }
340 281
341 282 dragWidget->setVisible(true);
342 283 }
343 284
344 285 event->acceptProposedAction();
345 286
346 287 helper.removePlaceHolder();
347 288
348 289 emit dropOccured(droppedIndex, event->mimeData());
349 290 }
350 291 else {
351 292 qCWarning(LOG_VisualizationDragDropContainer())
352 293 << tr("VisualizationDragDropContainer::dropEvent, couldn't drop because the "
353 294 "placeHolder is not found.");
354 Q_ASSERT(false);
295 // Q_ASSERT(false);
355 296 }
356 297 }
357 298 else {
358 299 event->ignore();
359 300 }
360 301
302 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
303 impl->m_MinContainerHeight = 0;
304
361 305 QWidget::dropEvent(event);
362 306 }
307
308
309 void VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
310 const QPoint &pos, bool canMerge, const VisualizationDragDropContainer *container)
311 {
312 auto &helper = sqpApp->dragDropHelper();
313
314 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
315 if (dragWidgetHovered) {
316 auto nbDragWidget = countDragWidget(container);
317 if (nbDragWidget > 0) {
318
319 if (m_MinContainerHeight == 0) {
320 m_MinContainerHeight = container->size().height();
321 }
322
323 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
324 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
325
326 auto posY = pos.y();
327 auto dropIndex = floor(posY / graphHeight);
328 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
329
330
331 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
332 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
333
334 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
335
336 if (isOnTop || isOnBottom || !canMerge) {
337 if (isOnBottom) {
338 dropIndex += 1;
339 }
340
341 if (helper.getCurrentDragWidget()) {
342 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
343 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
344 // Correction of the index if the drop occurs in the same container
345 // and if the drag is started from the visualization (in that case, the
346 // dragWidget is hidden)
347 dropIndex += 1;
348 }
349 }
350
351 if (dropIndex != placeHolderIndex) {
352 helper.insertPlaceHolder(m_Layout, dropIndex);
353 }
354
355 helper.setHightlightedDragWidget(nullptr);
356 }
357 else if (canMerge) {
358 // drop on the middle -> merge
359 if (hasPlaceHolder()) {
360 helper.removePlaceHolder();
361 }
362
363 helper.setHightlightedDragWidget(dragWidgetHovered);
364 }
365 }
366 else {
367 qCWarning(LOG_VisualizationDragDropContainer())
368 << tr("VisualizationDragDropContainer::dragMoveEvent, no widget found in the "
369 "container");
370 }
371 }
372 else if (!hasPlaceHolder()) {
373 // Drop on an empty container, just add the placeHolder at the top
374 helper.insertPlaceHolder(m_Layout, 0);
375 }
376 else {
377 // No hovered drag widget, the mouse is probably hover the placeHolder
378 // Do nothing
379 }
380 }
@@ -1,385 +1,397
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationDefs.h"
4 4 #include "Visualization/VisualizationGraphHelper.h"
5 5 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7 #include "ui_VisualizationGraphWidget.h"
8 8
9 9 #include <Common/MimeTypesDef.h>
10 10 #include <Data/ArrayData.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <DragDropHelper.h>
13 13 #include <Settings/SqpSettingsDefs.h>
14 14 #include <SqpApplication.h>
15 15 #include <Variable/Variable.h>
16 16 #include <Variable/VariableController.h>
17 17
18 18 #include <unordered_map>
19 19
20 20 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
21 21
22 22 namespace {
23 23
24 24 /// Key pressed to enable zoom on horizontal axis
25 25 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
26 26
27 27 /// Key pressed to enable zoom on vertical axis
28 28 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
29 29
30 30 } // namespace
31 31
32 32 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
33 33
34 34 explicit VisualizationGraphWidgetPrivate(const QString &name)
35 35 : m_Name{name},
36 36 m_DoAcquisition{true},
37 37 m_IsCalibration{false},
38 38 m_RenderingDelegate{nullptr}
39 39 {
40 40 }
41 41
42 42 QString m_Name;
43 43 // 1 variable -> n qcpplot
44 44 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
45 45 bool m_DoAcquisition;
46 46 bool m_IsCalibration;
47 47 QCPItemTracer *m_TextTracer;
48 48 /// Delegate used to attach rendering features to the plot
49 49 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
50 50 };
51 51
52 52 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
53 53 : VisualizationDragWidget{parent},
54 54 ui{new Ui::VisualizationGraphWidget},
55 55 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
56 56 {
57 57 ui->setupUi(this);
58 58
59 59 // 'Close' options : widget is deleted when closed
60 60 setAttribute(Qt::WA_DeleteOnClose);
61 61
62 62 // Set qcpplot properties :
63 63 // - Drag (on x-axis) and zoom are enabled
64 64 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
65 65 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
66 66 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
67 67
68 68 // The delegate must be initialized after the ui as it uses the plot
69 69 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
70 70
71 71 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
72 72 connect(ui->widget, &QCustomPlot::mouseRelease, this,
73 73 &VisualizationGraphWidget::onMouseRelease);
74 74 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
75 75 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
76 76 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
77 77 &QCPAxis::rangeChanged),
78 78 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
79 79
80 80 // Activates menu when right clicking on the graph
81 81 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
82 82 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
83 83 &VisualizationGraphWidget::onGraphMenuRequested);
84 84
85 85 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
86 86 &VariableController::onRequestDataLoading);
87 87
88 88 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
89 89 &VisualizationGraphWidget::onUpdateVarDisplaying);
90 90 }
91 91
92 92
93 93 VisualizationGraphWidget::~VisualizationGraphWidget()
94 94 {
95 95 delete ui;
96 96 }
97 97
98 98 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
99 99 {
100 100 auto parent = parentWidget();
101 101 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
102 102 parent = parent->parentWidget();
103 103 }
104 104
105 105 return qobject_cast<VisualizationZoneWidget *>(parent);
106 106 }
107 107
108 108 void VisualizationGraphWidget::enableAcquisition(bool enable)
109 109 {
110 110 impl->m_DoAcquisition = enable;
111 111 }
112 112
113 113 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
114 114 {
115 115 // Uses delegate to create the qcpplot components according to the variable
116 116 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
117 117 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
118 118
119 119 // Set axes properties according to the units of the data series
120 120 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
121 121 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
122 122 auto xAxisUnit = Unit{};
123 123 auto valuesUnit = Unit{};
124 124
125 125 if (auto dataSeries = variable->dataSeries()) {
126 126 dataSeries->lockRead();
127 127 xAxisUnit = dataSeries->xAxisUnit();
128 128 valuesUnit = dataSeries->valuesUnit();
129 129 dataSeries->unlock();
130 130 }
131 131 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
132 132
133 133 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
134 134
135 135 this->enableAcquisition(false);
136 136 this->setGraphRange(range);
137 137 this->enableAcquisition(true);
138 138
139 139 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
140 140
141 141 emit variableAdded(variable);
142 142 }
143 143
144 144 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
145 145 {
146 146 // Each component associated to the variable :
147 147 // - is removed from qcpplot (which deletes it)
148 148 // - is no longer referenced in the map
149 149 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
150 150 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
151 151 emit variableAboutToBeRemoved(variable);
152 152
153 153 auto &plottablesMap = variableIt->second;
154 154
155 155 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
156 156 plottableIt != plottableEnd;) {
157 157 ui->widget->removePlottable(plottableIt->second);
158 158 plottableIt = plottablesMap.erase(plottableIt);
159 159 }
160 160
161 161 impl->m_VariableToPlotMultiMap.erase(variableIt);
162 162 }
163 163
164 164 // Updates graph
165 165 ui->widget->replot();
166 166 }
167 167
168 168 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
169 169 {
170 170 auto variables = QList<std::shared_ptr<Variable> >{};
171 171 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
172 172 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
173 173 variables << it->first;
174 174 }
175 175
176 176 return variables;
177 177 }
178 178
179 179 void VisualizationGraphWidget::setYRange(const SqpRange &range)
180 180 {
181 181 ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd);
182 182 }
183 183
184 184 SqpRange VisualizationGraphWidget::graphRange() const noexcept
185 185 {
186 186 auto graphRange = ui->widget->xAxis->range();
187 187 return SqpRange{graphRange.lower, graphRange.upper};
188 188 }
189 189
190 190 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
191 191 {
192 192 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
193 193 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
194 194 ui->widget->replot();
195 195 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
196 196 }
197 197
198 198 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
199 199 {
200 200 if (visitor) {
201 201 visitor->visit(this);
202 202 }
203 203 else {
204 204 qCCritical(LOG_VisualizationGraphWidget())
205 205 << tr("Can't visit widget : the visitor is null");
206 206 }
207 207 }
208 208
209 209 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
210 210 {
211 211 /// @todo : for the moment, a graph can always accomodate a variable
212 212 Q_UNUSED(variable);
213 213 return true;
214 214 }
215 215
216 216 bool VisualizationGraphWidget::contains(const Variable &variable) const
217 217 {
218 218 // Finds the variable among the keys of the map
219 219 auto variablePtr = &variable;
220 220 auto findVariable
221 221 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
222 222
223 223 auto end = impl->m_VariableToPlotMultiMap.cend();
224 224 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
225 225 return it != end;
226 226 }
227 227
228 228 QString VisualizationGraphWidget::name() const
229 229 {
230 230 return impl->m_Name;
231 231 }
232 232
233 233 QMimeData *VisualizationGraphWidget::mimeData() const
234 234 {
235 235 auto mimeData = new QMimeData;
236 236 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
237 237
238 238 return mimeData;
239 239 }
240 240
241 241 bool VisualizationGraphWidget::isDragAllowed() const
242 242 {
243 243 return true;
244 244 }
245 245
246 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
247 {
248 if (highlighted) {
249 plot().setBackground(QBrush(QColor("#BBD5EE")));
250 }
251 else {
252 plot().setBackground(QBrush(Qt::white));
253 }
254
255 plot().update();
256 }
257
246 258 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
247 259 {
248 260 Q_UNUSED(event);
249 261
250 262 // Prevents that all variables will be removed from graph when it will be closed
251 263 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
252 264 emit variableAboutToBeRemoved(variableEntry.first);
253 265 }
254 266 }
255 267
256 268 void VisualizationGraphWidget::enterEvent(QEvent *event)
257 269 {
258 270 Q_UNUSED(event);
259 271 impl->m_RenderingDelegate->showGraphOverlay(true);
260 272 }
261 273
262 274 void VisualizationGraphWidget::leaveEvent(QEvent *event)
263 275 {
264 276 Q_UNUSED(event);
265 277 impl->m_RenderingDelegate->showGraphOverlay(false);
266 278 }
267 279
268 280 QCustomPlot &VisualizationGraphWidget::plot() noexcept
269 281 {
270 282 return *ui->widget;
271 283 }
272 284
273 285 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
274 286 {
275 287 QMenu graphMenu{};
276 288
277 289 // Iterates on variables (unique keys)
278 290 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
279 291 end = impl->m_VariableToPlotMultiMap.cend();
280 292 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
281 293 // 'Remove variable' action
282 294 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
283 295 [ this, var = it->first ]() { removeVariable(var); });
284 296 }
285 297
286 298 if (!graphMenu.isEmpty()) {
287 299 graphMenu.exec(QCursor::pos());
288 300 }
289 301 }
290 302
291 303 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
292 304 {
293 305 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
294 306 << QThread::currentThread()->objectName() << "DoAcqui"
295 307 << impl->m_DoAcquisition;
296 308
297 309 auto graphRange = SqpRange{t1.lower, t1.upper};
298 310 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
299 311
300 312 if (impl->m_DoAcquisition) {
301 313 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
302 314
303 315 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
304 316 end = impl->m_VariableToPlotMultiMap.end();
305 317 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
306 318 variableUnderGraphVector.push_back(it->first);
307 319 }
308 320 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
309 321 !impl->m_IsCalibration);
310 322
311 323 if (!impl->m_IsCalibration) {
312 324 qCDebug(LOG_VisualizationGraphWidget())
313 325 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
314 326 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
315 327 emit synchronize(graphRange, oldGraphRange);
316 328 }
317 329 }
318 330 }
319 331
320 332 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
321 333 {
322 334 // Handles plot rendering when mouse is moving
323 335 impl->m_RenderingDelegate->onMouseMove(event);
324 336
325 337 VisualizationDragWidget::mouseMoveEvent(event);
326 338 }
327 339
328 340 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
329 341 {
330 342 auto zoomOrientations = QFlags<Qt::Orientation>{};
331 343
332 344 // Lambda that enables a zoom orientation if the key modifier related to this orientation
333 345 // has
334 346 // been pressed
335 347 auto enableOrientation
336 348 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
337 349 auto orientationEnabled = event->modifiers().testFlag(modifier);
338 350 zoomOrientations.setFlag(orientation, orientationEnabled);
339 351 };
340 352 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
341 353 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
342 354
343 355 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
344 356 }
345 357
346 358 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
347 359 {
348 360 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
349 361
350 362 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
351 363
352 364 VisualizationDragWidget::mousePressEvent(event);
353 365 }
354 366
355 367 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
356 368 {
357 369 impl->m_IsCalibration = false;
358 370 }
359 371
360 372 void VisualizationGraphWidget::onDataCacheVariableUpdated()
361 373 {
362 374 auto graphRange = ui->widget->xAxis->range();
363 375 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
364 376
365 377 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
366 378 auto variable = variableEntry.first;
367 379 qCDebug(LOG_VisualizationGraphWidget())
368 380 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
369 381 qCDebug(LOG_VisualizationGraphWidget())
370 382 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
371 383 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
372 384 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
373 385 variable->range());
374 386 }
375 387 }
376 388 }
377 389
378 390 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
379 391 const SqpRange &range)
380 392 {
381 393 auto it = impl->m_VariableToPlotMultiMap.find(variable);
382 394 if (it != impl->m_VariableToPlotMultiMap.end()) {
383 395 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
384 396 }
385 397 }
@@ -1,468 +1,469
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 <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15
16 16 #include <Visualization/operations/FindVariableOperation.h>
17 17
18 18 #include <DragDropHelper.h>
19 19 #include <QUuid>
20 20 #include <SqpApplication.h>
21 21 #include <cmath>
22 22
23 23 #include <QLayout>
24 24
25 25 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
26 26
27 27 namespace {
28 28
29 29
30 30 /// Generates a default name for a new graph, according to the number of graphs already displayed in
31 31 /// the zone
32 32 QString defaultGraphName(const QLayout &layout)
33 33 {
34 34 auto count = 0;
35 35 for (auto i = 0; i < layout.count(); ++i) {
36 36 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
37 37 count++;
38 38 }
39 39 }
40 40
41 41 return QObject::tr("Graph %1").arg(count + 1);
42 42 }
43 43
44 44 /**
45 45 * Applies a function to all graphs of the zone represented by its layout
46 46 * @param layout the layout that contains graphs
47 47 * @param fun the function to apply to each graph
48 48 */
49 49 template <typename Fun>
50 50 void processGraphs(QLayout &layout, Fun fun)
51 51 {
52 52 for (auto i = 0; i < layout.count(); ++i) {
53 53 if (auto item = layout.itemAt(i)) {
54 54 if (auto visualizationGraphWidget
55 55 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
56 56 fun(*visualizationGraphWidget);
57 57 }
58 58 }
59 59 }
60 60 }
61 61
62 62 } // namespace
63 63
64 64 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
65 65
66 66 explicit VisualizationZoneWidgetPrivate()
67 67 : m_SynchronisationGroupId{QUuid::createUuid()},
68 68 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
69 69 {
70 70 }
71 71 QUuid m_SynchronisationGroupId;
72 72 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
73 73
74 74 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
75 75 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
76 76 VisualizationZoneWidget *zoneWidget);
77 77 };
78 78
79 79 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
80 80 : VisualizationDragWidget{parent},
81 81 ui{new Ui::VisualizationZoneWidget},
82 82 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
83 83 {
84 84 ui->setupUi(this);
85 85
86 86 ui->zoneNameLabel->setText(name);
87 87
88 88 ui->dragDropContainer->setAcceptedMimeTypes({MIME_TYPE_GRAPH, MIME_TYPE_VARIABLE_LIST});
89 ui->dragDropContainer->setMergeAllowedMimeTypes({MIME_TYPE_VARIABLE_LIST});
89 90 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
90 91 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
91 92 ui->dragDropContainer);
92 93 });
93 94 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this,
94 95 &VisualizationZoneWidget::dropMimeData);
95 96
96 97 // 'Close' options : widget is deleted when closed
97 98 setAttribute(Qt::WA_DeleteOnClose);
98 99 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
99 100 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
100 101
101 102 // Synchronisation id
102 103 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
103 104 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
104 105 }
105 106
106 107 VisualizationZoneWidget::~VisualizationZoneWidget()
107 108 {
108 109 delete ui;
109 110 }
110 111
111 112 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
112 113 {
113 114 // Synchronize new graph with others in the zone
114 115 impl->m_Synchronizer->addGraph(*graphWidget);
115 116
116 117 ui->dragDropContainer->addDragWidget(graphWidget);
117 118 }
118 119
119 120 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
120 121 {
121 122 // Synchronize new graph with others in the zone
122 123 impl->m_Synchronizer->addGraph(*graphWidget);
123 124
124 125 ui->dragDropContainer->insertDragWidget(index, graphWidget);
125 126 }
126 127
127 128 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
128 129 {
129 130 return createGraph(variable, -1);
130 131 }
131 132
132 133 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
133 134 int index)
134 135 {
135 136 auto graphWidget
136 137 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
137 138
138 139
139 140 // Set graph properties
140 141 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
141 142 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
142 143
143 144
144 145 // Lambda to synchronize zone widget
145 146 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
146 147 const SqpRange &oldGraphRange) {
147 148
148 149 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
149 150 auto frameLayout = ui->dragDropContainer->layout();
150 151 for (auto i = 0; i < frameLayout->count(); ++i) {
151 152 auto graphChild
152 153 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
153 154 if (graphChild && (graphChild != graphWidget)) {
154 155
155 156 auto graphChildRange = graphChild->graphRange();
156 157 switch (zoomType) {
157 158 case AcquisitionZoomType::ZoomIn: {
158 159 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
159 160 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
160 161 graphChildRange.m_TStart += deltaLeft;
161 162 graphChildRange.m_TEnd -= deltaRight;
162 163 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
163 164 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
164 165 << deltaLeft;
165 166 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
166 167 << deltaRight;
167 168 qCDebug(LOG_VisualizationZoneWidget())
168 169 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
169 170
170 171 break;
171 172 }
172 173
173 174 case AcquisitionZoomType::ZoomOut: {
174 175 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
175 176 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
176 177 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
177 178 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
178 179 << deltaLeft;
179 180 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
180 181 << deltaRight;
181 182 qCDebug(LOG_VisualizationZoneWidget())
182 183 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
183 184 graphChildRange.m_TStart -= deltaLeft;
184 185 graphChildRange.m_TEnd += deltaRight;
185 186 break;
186 187 }
187 188 case AcquisitionZoomType::PanRight: {
188 189 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
189 190 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
190 191 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
191 192 graphChildRange.m_TStart += deltaLeft;
192 193 graphChildRange.m_TEnd += deltaRight;
193 194 qCDebug(LOG_VisualizationZoneWidget())
194 195 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
195 196 break;
196 197 }
197 198 case AcquisitionZoomType::PanLeft: {
198 199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
199 200 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
200 201 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
201 202 graphChildRange.m_TStart -= deltaLeft;
202 203 graphChildRange.m_TEnd -= deltaRight;
203 204 break;
204 205 }
205 206 case AcquisitionZoomType::Unknown: {
206 207 qCDebug(LOG_VisualizationZoneWidget())
207 208 << tr("Impossible to synchronize: zoom type unknown");
208 209 break;
209 210 }
210 211 default:
211 212 qCCritical(LOG_VisualizationZoneWidget())
212 213 << tr("Impossible to synchronize: zoom type not take into account");
213 214 // No action
214 215 break;
215 216 }
216 217 graphChild->enableAcquisition(false);
217 218 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
218 219 << graphChild->graphRange();
219 220 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
220 221 << graphChildRange;
221 222 qCDebug(LOG_VisualizationZoneWidget())
222 223 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
223 224 graphChild->setGraphRange(graphChildRange);
224 225 graphChild->enableAcquisition(true);
225 226 }
226 227 }
227 228 };
228 229
229 230 // connection for synchronization
230 231 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
231 232 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
232 233 &VisualizationZoneWidget::onVariableAdded);
233 234 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
234 235 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
235 236
236 237 auto range = SqpRange{};
237 238
238 239 // Apply visitor to graph children
239 240 auto layout = ui->dragDropContainer->layout();
240 241 if (layout->count() > 0) {
241 242 // Case of a new graph in a existant zone
242 243 if (auto visualizationGraphWidget
243 244 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
244 245 range = visualizationGraphWidget->graphRange();
245 246 }
246 247 }
247 248 else {
248 249 // Case of a new graph as the first of the zone
249 250 range = variable->range();
250 251 }
251 252
252 253 this->insertGraph(index, graphWidget);
253 254
254 255 graphWidget->addVariable(variable, range);
255 256
256 257 // get y using variable range
257 258 if (auto dataSeries = variable->dataSeries()) {
258 259 dataSeries->lockRead();
259 260 auto valuesBounds
260 261 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
261 262 auto end = dataSeries->cend();
262 263 if (valuesBounds.first != end && valuesBounds.second != end) {
263 264 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
264 265
265 266 auto minValue = rangeValue(valuesBounds.first->minValue());
266 267 auto maxValue = rangeValue(valuesBounds.second->maxValue());
267 268
268 269 graphWidget->setYRange(SqpRange{minValue, maxValue});
269 270 }
270 271 dataSeries->unlock();
271 272 }
272 273
273 274 return graphWidget;
274 275 }
275 276
276 277 VisualizationGraphWidget *
277 278 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
278 279 {
279 280 if (variables.isEmpty()) {
280 281 return nullptr;
281 282 }
282 283
283 284 auto graphWidget = createGraph(variables.first(), index);
284 285 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
285 286 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
286 287 }
287 288
288 289 return graphWidget;
289 290 }
290 291
291 292 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
292 293 {
293 294 if (visitor) {
294 295 visitor->visitEnter(this);
295 296
296 297 // Apply visitor to graph children: widgets different from graphs are not visited (no
297 298 // action)
298 299 processGraphs(
299 300 *ui->dragDropContainer->layout(),
300 301 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
301 302
302 303 visitor->visitLeave(this);
303 304 }
304 305 else {
305 306 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
306 307 }
307 308 }
308 309
309 310 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
310 311 {
311 312 // A tab can always accomodate a variable
312 313 Q_UNUSED(variable);
313 314 return true;
314 315 }
315 316
316 317 bool VisualizationZoneWidget::contains(const Variable &variable) const
317 318 {
318 319 Q_UNUSED(variable);
319 320 return false;
320 321 }
321 322
322 323 QString VisualizationZoneWidget::name() const
323 324 {
324 325 return ui->zoneNameLabel->text();
325 326 }
326 327
327 328 QMimeData *VisualizationZoneWidget::mimeData() const
328 329 {
329 330 auto mimeData = new QMimeData;
330 331 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
331 332
332 333 return mimeData;
333 334 }
334 335
335 336 bool VisualizationZoneWidget::isDragAllowed() const
336 337 {
337 338 return true;
338 339 }
339 340
340 341 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
341 342 {
342 343 // Closes graphs in the zone
343 344 processGraphs(*ui->dragDropContainer->layout(),
344 345 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
345 346
346 347 // Delete synchronization group from variable controller
347 348 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
348 349 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
349 350
350 351 QWidget::closeEvent(event);
351 352 }
352 353
353 354 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
354 355 {
355 356 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
356 357 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
357 358 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
358 359 }
359 360
360 361 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
361 362 {
362 363 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
363 364 Q_ARG(std::shared_ptr<Variable>, variable),
364 365 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
365 366 }
366 367
367 368 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
368 369 {
369 370 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
370 371 impl->dropGraph(index, this);
371 372 }
372 373 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
373 374 auto variables = sqpApp->variableController().variablesForMimeData(
374 375 mimeData->data(MIME_TYPE_VARIABLE_LIST));
375 376 impl->dropVariables(variables, index, this);
376 377 }
377 378 else {
378 379 qCWarning(LOG_VisualizationZoneWidget())
379 380 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
380 381 }
381 382 }
382 383
383 384 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
384 385 int index, VisualizationZoneWidget *zoneWidget)
385 386 {
386 387 auto &helper = sqpApp->dragDropHelper();
387 388
388 389 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
389 390 if (!graphWidget) {
390 391 qCWarning(LOG_VisualizationZoneWidget())
391 392 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
392 393 "found or invalid.");
393 394 Q_ASSERT(false);
394 395 return;
395 396 }
396 397
397 398 auto parentDragDropContainer
398 399 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
399 400 if (!parentDragDropContainer) {
400 401 qCWarning(LOG_VisualizationZoneWidget())
401 402 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
402 403 "the dropped graph is not found.");
403 404 Q_ASSERT(false);
404 405 return;
405 406 }
406 407
407 408 const auto &variables = graphWidget->variables();
408 409
409 410 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
410 411 // The drop didn't occur in the same zone
411 412
412 413 // Abort the requests for the variables (if any)
413 414 // Commented, because it's not sure if it's needed or not
414 415 // for (const auto& var : variables)
415 416 //{
416 417 // sqpApp->variableController().onAbortProgressRequested(var);
417 418 //}
418 419
419 420 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
420 421 auto nbGraph = parentDragDropContainer->countDragWidget();
421 422 if (nbGraph == 1) {
422 423 // This is the only graph in the previous zone, close the zone
423 424 previousParentZoneWidget->close();
424 425 }
425 426 else {
426 427 // Close the graph
427 428 graphWidget->close();
428 429 }
429 430
430 431 // Creates the new graph in the zone
431 432 zoneWidget->createGraph(variables, index);
432 433 }
433 434 else {
434 435 // The drop occurred in the same zone or the graph is empty
435 436 // Simple move of the graph, no variable operation associated
436 437 parentDragDropContainer->layout()->removeWidget(graphWidget);
437 438
438 439 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
439 440 // The graph is empty and dropped in a different zone.
440 441 // Take the range of the first graph in the zone (if existing).
441 442 auto layout = zoneWidget->ui->dragDropContainer->layout();
442 443 if (layout->count() > 0) {
443 444 if (auto visualizationGraphWidget
444 445 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
445 446 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
446 447 }
447 448 }
448 449 }
449 450
450 451 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
451 452 }
452 453 }
453 454
454 455 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
455 456 const QList<std::shared_ptr<Variable> > &variables, int index,
456 457 VisualizationZoneWidget *zoneWidget)
457 458 {
458 459 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
459 460 // compatible variable here
460 461 if (variables.count() > 1) {
461 462 qCWarning(LOG_VisualizationZoneWidget())
462 463 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
463 464 "aborted.");
464 465 return;
465 466 }
466 467
467 468 zoneWidget->createGraph(variables, index);
468 469 }
General Comments 3
Under Review
author

Pull request updated. Auto status change to "Under Review"

Changed commits:
  * 1 added
  * 0 removed

Changed files:
  * A core/tests/meson.build
You need to be logged in to leave comments. Login now