##// END OF EJS Templates
drag of selection zones
trabillard -
r1047:d404ba4b75e5
parent child
Show More
@@ -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,126 +1,127
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 15
16 16 class QCPRange;
17 17 class QCustomPlot;
18 18 class SqpRange;
19 19 class Variable;
20 20 class VisualizationZoneWidget;
21 21
22 22 namespace Ui {
23 23 class VisualizationGraphWidget;
24 24 } // namespace Ui
25 25
26 26 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
27 27 Q_OBJECT
28 28
29 29 friend class QCustomPlotSynchronizer;
30 30 friend class VisualizationGraphRenderingDelegate;
31 31
32 32 public:
33 33 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
34 34 virtual ~VisualizationGraphWidget();
35 35
36 36 VisualizationZoneWidget *parentZoneWidget() const noexcept;
37 37
38 38 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
39 39 void enableAcquisition(bool enable);
40 40
41 41 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
42 42
43 43 /// Removes a variable from the graph
44 44 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
45 45
46 46 /// Returns the list of all variables used in the graph
47 47 QList<std::shared_ptr<Variable> > variables() const;
48 48
49 49 /// Sets the y-axis range based on the data of a variable
50 50 void setYRange(std::shared_ptr<Variable> variable);
51 51 SqpRange graphRange() const noexcept;
52 52 void setGraphRange(const SqpRange &range);
53 53
54 54 /// Undo the last zoom done with a zoom box
55 55 void undoZoom();
56 56
57 57 // IVisualizationWidget interface
58 58 void accept(IVisualizationWidgetVisitor *visitor) override;
59 59 bool canDrop(const Variable &variable) const override;
60 60 bool contains(const Variable &variable) const override;
61 61 QString name() const override;
62 62
63 63 // VisualisationDragWidget
64 QMimeData *mimeData() const override;
64 QMimeData *mimeData(const QPoint &position) const override;
65 QPixmap customDragPixmap(const QPoint &dragPosition) override;
65 66 bool isDragAllowed() const override;
66 67 void highlightForMerge(bool highlighted) override;
67 68
68 69 // Cursors
69 70 /// Adds or moves the vertical cursor at the specified value on the x-axis
70 71 void addVerticalCursor(double time);
71 72 /// Adds or moves the vertical cursor at the specified value on the x-axis
72 73 void addVerticalCursorAtViewportPosition(double position);
73 74 void removeVerticalCursor();
74 75 /// Adds or moves the vertical cursor at the specified value on the y-axis
75 76 void addHorizontalCursor(double value);
76 77 /// Adds or moves the vertical cursor at the specified value on the y-axis
77 78 void addHorizontalCursorAtViewportPosition(double position);
78 79 void removeHorizontalCursor();
79 80
80 81 signals:
81 82 void synchronize(const SqpRange &range, const SqpRange &oldRange);
82 83 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
83 84 bool synchronise);
84 85
85 86 /// Signal emitted when the variable is about to be removed from the graph
86 87 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
87 88 /// Signal emitted when the variable has been added to the graph
88 89 void variableAdded(std::shared_ptr<Variable> var);
89 90
90 91 protected:
91 92 void closeEvent(QCloseEvent *event) override;
92 93 void enterEvent(QEvent *event) override;
93 94 void leaveEvent(QEvent *event) override;
94 95
95 QCustomPlot &plot() noexcept;
96 QCustomPlot &plot() const noexcept;
96 97
97 98 private:
98 99 Ui::VisualizationGraphWidget *ui;
99 100
100 101 class VisualizationGraphWidgetPrivate;
101 102 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
102 103
103 104 private slots:
104 105 /// Slot called when right clicking on the graph (displays a menu)
105 106 void onGraphMenuRequested(const QPoint &pos) noexcept;
106 107
107 108 /// Rescale the X axe to range parameter
108 109 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
109 110
110 111 /// Slot called when a mouse double click was made
111 112 void onMouseDoubleClick(QMouseEvent *event) noexcept;
112 113 /// Slot called when a mouse move was made
113 114 void onMouseMove(QMouseEvent *event) noexcept;
114 115 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
115 116 void onMouseWheel(QWheelEvent *event) noexcept;
116 117 /// Slot called when a mouse press was made, to activate the calibration of a graph
117 118 void onMousePress(QMouseEvent *event) noexcept;
118 119 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
119 120 void onMouseRelease(QMouseEvent *event) noexcept;
120 121
121 122 void onDataCacheVariableUpdated();
122 123
123 124 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
124 125 };
125 126
126 127 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,95 +1,95
1 1 #ifndef SCIQLOP_VISUALIZATIONZONEWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONZONEWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationZoneWidget)
15 15
16 16 namespace Ui {
17 17 class VisualizationZoneWidget;
18 18 } // namespace Ui
19 19
20 20 class Variable;
21 21 class VisualizationGraphWidget;
22 22
23 23 class VisualizationZoneWidget : public VisualizationDragWidget, public IVisualizationWidget {
24 24 Q_OBJECT
25 25
26 26 public:
27 27 explicit VisualizationZoneWidget(const QString &name = {}, QWidget *parent = 0);
28 28 virtual ~VisualizationZoneWidget();
29 29
30 30 /// Adds a graph widget
31 31 void addGraph(VisualizationGraphWidget *graphWidget);
32 32
33 33 /// Inserts a graph widget
34 34 void insertGraph(int index, VisualizationGraphWidget *graphWidget);
35 35
36 36 /**
37 37 * Creates a graph using a variable. The variable will be displayed in the new graph.
38 38 * The graph is added at the end.
39 39 * @param variable the variable for which to create the graph
40 40 * @return the pointer to the created graph
41 41 */
42 42 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable);
43 43
44 44 /**
45 45 * Creates a graph using a variable. The variable will be displayed in the new graph.
46 46 * The graph is inserted at the specified index.
47 47 * @param variable the variable for which to create the graph
48 48 * @param index The index where the graph should be inserted in the layout
49 49 * @return the pointer to the created graph
50 50 */
51 51 VisualizationGraphWidget *createGraph(std::shared_ptr<Variable> variable, int index);
52 52
53 53 /**
54 54 * Creates a graph using a list of variables. The variables will be displayed in the new graph.
55 55 * The graph is inserted at the specified index.
56 56 * @param variables List of variables to be added to the graph
57 57 * @param index The index where the graph should be inserted in the layout
58 58 * @return the pointer to the created graph
59 59 */
60 60 VisualizationGraphWidget *createGraph(const QList<std::shared_ptr<Variable> > variables,
61 61 int index);
62 62
63 63 // IVisualizationWidget interface
64 64 void accept(IVisualizationWidgetVisitor *visitor) override;
65 65 bool canDrop(const Variable &variable) const override;
66 66 bool contains(const Variable &variable) const override;
67 67 QString name() const override;
68 68
69 69 // VisualisationDragWidget
70 QMimeData *mimeData() const override;
70 QMimeData *mimeData(const QPoint &position) const override;
71 71 bool isDragAllowed() const override;
72 72
73 73 void notifyMouseMoveInGraph(const QPointF &graphPosition, const QPointF &plotPosition,
74 74 VisualizationGraphWidget *graphWidget);
75 75 void notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget);
76 76
77 77 protected:
78 78 void closeEvent(QCloseEvent *event) override;
79 79
80 80 private:
81 81 Ui::VisualizationZoneWidget *ui;
82 82
83 83 class VisualizationZoneWidgetPrivate;
84 84 spimpl::unique_impl_ptr<VisualizationZoneWidgetPrivate> impl;
85 85
86 86 private slots:
87 87 void onVariableAdded(std::shared_ptr<Variable> variable);
88 88 /// Slot called when a variable is about to be removed from a graph contained in the zone
89 89 void onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable);
90 90
91 91 void dropMimeData(int index, const QMimeData *mimeData);
92 92 void dropMimeDataOnGraph(VisualizationDragWidget *dragWidget, const QMimeData *mimeData);
93 93 };
94 94
95 95 #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H
@@ -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 virtual 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,747 +1,797
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 7 #include "Visualization/VisualizationSelectionZoneItem.h"
8 8 #include "Visualization/VisualizationZoneWidget.h"
9 9 #include "ui_VisualizationGraphWidget.h"
10 10
11 11 #include <Common/MimeTypesDef.h>
12 12 #include <Data/ArrayData.h>
13 13 #include <Data/IDataSeries.h>
14 14 #include <Data/SpectrogramSeries.h>
15 15 #include <DragAndDrop/DragDropHelper.h>
16 16 #include <Settings/SqpSettingsDefs.h>
17 17 #include <SqpApplication.h>
18 18 #include <Time/TimeController.h>
19 19 #include <Variable/Variable.h>
20 20 #include <Variable/VariableController.h>
21 21
22 22 #include <unordered_map>
23 23
24 24 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
25 25
26 26 namespace {
27 27
28 /// Key pressed to enable drag&drop in all modes
29 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
30
28 31 /// Key pressed to enable zoom on horizontal axis
29 32 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
30 33
31 34 /// Key pressed to enable zoom on vertical axis
32 35 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
33 36
34 37 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
35 38 const auto PAN_SPEED = 5;
36 39
37 40 /// Key pressed to enable a calibration pan
38 41 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
39 42
40 43 /// Minimum size for the zoom box, in percentage of the axis range
41 44 const auto ZOOM_BOX_MIN_SIZE = 0.8;
42 45
43 46 /// Format of the dates appearing in the label of a cursor
44 47 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
45 48
46 49 } // namespace
47 50
48 51 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
49 52
50 53 explicit VisualizationGraphWidgetPrivate(const QString &name)
51 54 : m_Name{name},
52 55 m_DoAcquisition{true},
53 56 m_IsCalibration{false},
54 57 m_RenderingDelegate{nullptr}
55 58 {
56 59 }
57 60
58 61 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
59 62 const SqpRange &range)
60 63 {
61 64 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
62 65
63 66 // Prevents that data has changed to update rendering
64 67 m_RenderingDelegate->onPlotUpdated();
65 68 }
66 69
67 70 QString m_Name;
68 71 // 1 variable -> n qcpplot
69 72 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
70 73 bool m_DoAcquisition;
71 74 bool m_IsCalibration;
72 75 /// Delegate used to attach rendering features to the plot
73 76 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
74 77
75 78 QCPItemRect *m_DrawingZoomRect = nullptr;
76 79 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
77 80
78 81 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
79 82 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
80 83
81 84 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
82 85 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
83 86 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
84 87
85 88 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
86 89 {
87 90 removeDrawingRect(plot);
88 91
89 92 auto axisPos = posToAxisPos(pos, plot);
90 93
91 94 m_DrawingZoomRect = new QCPItemRect{&plot};
92 95 QPen p;
93 96 p.setWidth(2);
94 97 m_DrawingZoomRect->setPen(p);
95 98
96 99 m_DrawingZoomRect->topLeft->setCoords(axisPos);
97 100 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
98 101 }
99 102
100 103 void removeDrawingRect(QCustomPlot &plot)
101 104 {
102 105 if (m_DrawingZoomRect) {
103 106 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
104 107 m_DrawingZoomRect = nullptr;
105 108 plot.replot(QCustomPlot::rpQueuedReplot);
106 109 }
107 110 }
108 111
109 112 void startDrawingZone(const QPoint &pos, QCustomPlot &plot)
110 113 {
111 114 endDrawingZone(plot);
112 115
113 116 auto axisPos = posToAxisPos(pos, plot);
114 117
115 118 m_DrawingZone = new VisualizationSelectionZoneItem{&plot};
116 119 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
117 120 m_DrawingZone->setEditionEnabled(false);
118 121 }
119 122
120 123 void endDrawingZone(QCustomPlot &plot)
121 124 {
122 125 if (m_DrawingZone) {
123 126 auto drawingZoneRange = m_DrawingZone->range();
124 127 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
125 128 m_DrawingZone->setEditionEnabled(true);
126 129 m_SelectionZones.append(m_DrawingZone);
127 130 }
128 131 else {
129 132 plot.removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
130 133 }
131 134
132 135 plot.replot(QCustomPlot::rpQueuedReplot);
133 136 m_DrawingZone = nullptr;
134 137 }
135 138 }
136 139
137 140 void setSelectionZonesEditionEnabled(bool value)
138 141 {
139 142 for (auto s : m_SelectionZones) {
140 143 s->setEditionEnabled(value);
141 144 }
142 145 }
143 146
147 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
148 const QCustomPlot &plot) const
149 {
150 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
151 auto minDistanceToZone = -1;
152 for (auto zone : m_SelectionZones) {
153 auto distanceToZone = zone->selectTest(pos, false);
154 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
155 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
156 selectionZoneItemUnderCursor = zone;
157 }
158 }
159
160 return selectionZoneItemUnderCursor;
161 }
162
144 163 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
145 164 {
146 165 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
147 166 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
148 167 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
149 168 }
150 169
151 170 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
152 171 {
153 172 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
154 173 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
155 174 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
156 175 }
157 176 };
158 177
159 178 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
160 179 : VisualizationDragWidget{parent},
161 180 ui{new Ui::VisualizationGraphWidget},
162 181 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
163 182 {
164 183 ui->setupUi(this);
165 184
166 185 // 'Close' options : widget is deleted when closed
167 186 setAttribute(Qt::WA_DeleteOnClose);
168 187
169 188 // Set qcpplot properties :
170 189 // - zoom is enabled
171 190 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
172 191 ui->widget->setInteractions(QCP::iRangeZoom | QCP::iSelectItems);
173 192 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
174 193
175 194 // The delegate must be initialized after the ui as it uses the plot
176 195 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
177 196
178 197 // Init the cursors
179 198 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
180 199 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
181 200 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
182 201 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
183 202
184 203 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
185 204 connect(ui->widget, &QCustomPlot::mouseRelease, this,
186 205 &VisualizationGraphWidget::onMouseRelease);
187 206 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
188 207 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
189 208 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
190 209 &VisualizationGraphWidget::onMouseDoubleClick);
191 210 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
192 211 &QCPAxis::rangeChanged),
193 212 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
194 213
195 214 // Activates menu when right clicking on the graph
196 215 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
197 216 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
198 217 &VisualizationGraphWidget::onGraphMenuRequested);
199 218
200 219 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
201 220 &VariableController::onRequestDataLoading);
202 221
203 222 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
204 223 &VisualizationGraphWidget::onUpdateVarDisplaying);
205 224
206 225 #ifdef Q_OS_MAC
207 226 plot().setPlottingHint(QCP::phFastPolylines, true);
208 227 #endif
209 228 }
210 229
211 230
212 231 VisualizationGraphWidget::~VisualizationGraphWidget()
213 232 {
214 233 delete ui;
215 234 }
216 235
217 236 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
218 237 {
219 238 auto parent = parentWidget();
220 239 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
221 240 parent = parent->parentWidget();
222 241 }
223 242
224 243 return qobject_cast<VisualizationZoneWidget *>(parent);
225 244 }
226 245
227 246 void VisualizationGraphWidget::enableAcquisition(bool enable)
228 247 {
229 248 impl->m_DoAcquisition = enable;
230 249 }
231 250
232 251 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
233 252 {
234 253 // Uses delegate to create the qcpplot components according to the variable
235 254 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
236 255
237 256 if (auto dataSeries = variable->dataSeries()) {
238 257 // Set axes properties according to the units of the data series
239 258 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
240 259
241 260 // Sets rendering properties for the new plottables
242 261 // Warning: this method must be called after setAxesProperties(), as it can access to some
243 262 // axes properties that have to be initialized
244 263 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
245 264 }
246 265
247 266 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
248 267
249 268 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
250 269
251 270 this->enableAcquisition(false);
252 271 this->setGraphRange(range);
253 272 this->enableAcquisition(true);
254 273
255 274 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
256 275
257 276 emit variableAdded(variable);
258 277 }
259 278
260 279 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
261 280 {
262 281 // Each component associated to the variable :
263 282 // - is removed from qcpplot (which deletes it)
264 283 // - is no longer referenced in the map
265 284 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
266 285 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
267 286 emit variableAboutToBeRemoved(variable);
268 287
269 288 auto &plottablesMap = variableIt->second;
270 289
271 290 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
272 291 plottableIt != plottableEnd;) {
273 292 ui->widget->removePlottable(plottableIt->second);
274 293 plottableIt = plottablesMap.erase(plottableIt);
275 294 }
276 295
277 296 impl->m_VariableToPlotMultiMap.erase(variableIt);
278 297 }
279 298
280 299 // Updates graph
281 300 ui->widget->replot();
282 301 }
283 302
284 303 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
285 304 {
286 305 auto variables = QList<std::shared_ptr<Variable> >{};
287 306 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
288 307 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
289 308 variables << it->first;
290 309 }
291 310
292 311 return variables;
293 312 }
294 313
295 314 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
296 315 {
297 316 if (!variable) {
298 317 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
299 318 return;
300 319 }
301 320
302 321 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
303 322 }
304 323
305 324 SqpRange VisualizationGraphWidget::graphRange() const noexcept
306 325 {
307 326 auto graphRange = ui->widget->xAxis->range();
308 327 return SqpRange{graphRange.lower, graphRange.upper};
309 328 }
310 329
311 330 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
312 331 {
313 332 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
314 333 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
315 334 ui->widget->replot();
316 335 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
317 336 }
318 337
319 338 void VisualizationGraphWidget::undoZoom()
320 339 {
321 340 auto zoom = impl->m_ZoomStack.pop();
322 341 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
323 342 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
324 343
325 344 axisX->setRange(zoom.first);
326 345 axisY->setRange(zoom.second);
327 346
328 347 plot().replot(QCustomPlot::rpQueuedReplot);
329 348 }
330 349
331 350 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
332 351 {
333 352 if (visitor) {
334 353 visitor->visit(this);
335 354 }
336 355 else {
337 356 qCCritical(LOG_VisualizationGraphWidget())
338 357 << tr("Can't visit widget : the visitor is null");
339 358 }
340 359 }
341 360
342 361 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
343 362 {
344 363 auto isSpectrogram = [](const auto &variable) {
345 364 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
346 365 };
347 366
348 367 // - A spectrogram series can't be dropped on graph with existing plottables
349 368 // - No data series can be dropped on graph with existing spectrogram series
350 369 return isSpectrogram(variable)
351 370 ? impl->m_VariableToPlotMultiMap.empty()
352 371 : std::none_of(
353 372 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
354 373 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
355 374 }
356 375
357 376 bool VisualizationGraphWidget::contains(const Variable &variable) const
358 377 {
359 378 // Finds the variable among the keys of the map
360 379 auto variablePtr = &variable;
361 380 auto findVariable
362 381 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
363 382
364 383 auto end = impl->m_VariableToPlotMultiMap.cend();
365 384 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
366 385 return it != end;
367 386 }
368 387
369 388 QString VisualizationGraphWidget::name() const
370 389 {
371 390 return impl->m_Name;
372 391 }
373 392
374 QMimeData *VisualizationGraphWidget::mimeData() const
393 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
375 394 {
376 395 auto mimeData = new QMimeData;
377 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
378 396
379 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
380 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
397 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
398 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
399 && selectionZoneItemUnderCursor) {
400 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
401 selectionZoneItemUnderCursor->range()));
402 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
403 selectionZoneItemUnderCursor->range()));
404 }
405 else {
406 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
407
408 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
409 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
410 }
381 411
382 412 return mimeData;
383 413 }
384 414
415 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
416 {
417 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
418 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
419 && selectionZoneItemUnderCursor) {
420
421 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
422 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
423
424 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
425 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
426 .toSize();
427
428 auto pixmap = QPixmap(zoneSize);
429 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
430
431 return pixmap;
432 }
433
434 return QPixmap();
435 }
436
385 437 bool VisualizationGraphWidget::isDragAllowed() const
386 438 {
387 439 return true;
388 440 }
389 441
390 442 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
391 443 {
392 444 if (highlighted) {
393 445 plot().setBackground(QBrush(QColor("#BBD5EE")));
394 446 }
395 447 else {
396 448 plot().setBackground(QBrush(Qt::white));
397 449 }
398 450
399 451 plot().update();
400 452 }
401 453
402 454 void VisualizationGraphWidget::addVerticalCursor(double time)
403 455 {
404 456 impl->m_VerticalCursor->setPosition(time);
405 457 impl->m_VerticalCursor->setVisible(true);
406 458
407 459 auto text
408 460 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
409 461 impl->m_VerticalCursor->setLabelText(text);
410 462 }
411 463
412 464 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
413 465 {
414 466 impl->m_VerticalCursor->setAbsolutePosition(position);
415 467 impl->m_VerticalCursor->setVisible(true);
416 468
417 469 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
418 470 auto text
419 471 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
420 472 impl->m_VerticalCursor->setLabelText(text);
421 473 }
422 474
423 475 void VisualizationGraphWidget::removeVerticalCursor()
424 476 {
425 477 impl->m_VerticalCursor->setVisible(false);
426 478 plot().replot(QCustomPlot::rpQueuedReplot);
427 479 }
428 480
429 481 void VisualizationGraphWidget::addHorizontalCursor(double value)
430 482 {
431 483 impl->m_HorizontalCursor->setPosition(value);
432 484 impl->m_HorizontalCursor->setVisible(true);
433 485 impl->m_HorizontalCursor->setLabelText(QString::number(value));
434 486 }
435 487
436 488 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
437 489 {
438 490 impl->m_HorizontalCursor->setAbsolutePosition(position);
439 491 impl->m_HorizontalCursor->setVisible(true);
440 492
441 493 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
442 494 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
443 495 }
444 496
445 497 void VisualizationGraphWidget::removeHorizontalCursor()
446 498 {
447 499 impl->m_HorizontalCursor->setVisible(false);
448 500 plot().replot(QCustomPlot::rpQueuedReplot);
449 501 }
450 502
451 503 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
452 504 {
453 505 Q_UNUSED(event);
454 506
455 507 // Prevents that all variables will be removed from graph when it will be closed
456 508 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
457 509 emit variableAboutToBeRemoved(variableEntry.first);
458 510 }
459 511 }
460 512
461 513 void VisualizationGraphWidget::enterEvent(QEvent *event)
462 514 {
463 515 Q_UNUSED(event);
464 516 impl->m_RenderingDelegate->showGraphOverlay(true);
465 517 }
466 518
467 519 void VisualizationGraphWidget::leaveEvent(QEvent *event)
468 520 {
469 521 Q_UNUSED(event);
470 522 impl->m_RenderingDelegate->showGraphOverlay(false);
471 523
472 524 if (auto parentZone = parentZoneWidget()) {
473 525 parentZone->notifyMouseLeaveGraph(this);
474 526 }
475 527 else {
476 528 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
477 529 }
478 530
479 531 if (impl->m_HoveredZone) {
480 532 impl->m_HoveredZone->setHovered(false);
481 533 impl->m_HoveredZone = nullptr;
482 534 }
483 535 }
484 536
485 QCustomPlot &VisualizationGraphWidget::plot() noexcept
537 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
486 538 {
487 539 return *ui->widget;
488 540 }
489 541
490 542 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
491 543 {
492 544 QMenu graphMenu{};
493 545
494 546 // Iterates on variables (unique keys)
495 547 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
496 548 end = impl->m_VariableToPlotMultiMap.cend();
497 549 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
498 550 // 'Remove variable' action
499 551 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
500 552 [ this, var = it->first ]() { removeVariable(var); });
501 553 }
502 554
503 555 if (!impl->m_ZoomStack.isEmpty()) {
504 556 if (!graphMenu.isEmpty()) {
505 557 graphMenu.addSeparator();
506 558 }
507 559
508 560 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
509 561 }
510 562
511 563 if (!graphMenu.isEmpty()) {
512 564 graphMenu.exec(QCursor::pos());
513 565 }
514 566 }
515 567
516 568 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
517 569 {
518 570 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
519 571 << QThread::currentThread()->objectName() << "DoAcqui"
520 572 << impl->m_DoAcquisition;
521 573
522 574 auto graphRange = SqpRange{t1.lower, t1.upper};
523 575 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
524 576
525 577 if (impl->m_DoAcquisition) {
526 578 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
527 579
528 580 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
529 581 end = impl->m_VariableToPlotMultiMap.end();
530 582 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
531 583 variableUnderGraphVector.push_back(it->first);
532 584 }
533 585 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
534 586 !impl->m_IsCalibration);
535 587
536 588 if (!impl->m_IsCalibration) {
537 589 qCDebug(LOG_VisualizationGraphWidget())
538 590 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
539 591 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
540 592 emit synchronize(graphRange, oldGraphRange);
541 593 }
542 594 }
543 595
544 596 auto pos = mapFromGlobal(QCursor::pos());
545 597 auto axisPos = impl->posToAxisPos(pos, plot());
546 598 if (auto parentZone = parentZoneWidget()) {
547 599 if (impl->pointIsInAxisRect(axisPos, plot())) {
548 600 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
549 601 }
550 602 else {
551 603 parentZone->notifyMouseLeaveGraph(this);
552 604 }
553 605 }
554 606 else {
555 607 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
556 608 }
557 609 }
558 610
559 611 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
560 612 {
561 613 impl->m_RenderingDelegate->onMouseDoubleClick(event);
562 614 }
563 615
564 616 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
565 617 {
566 618 // Handles plot rendering when mouse is moving
567 619 impl->m_RenderingDelegate->onMouseMove(event);
568 620
569 621 auto axisPos = impl->posToAxisPos(event->pos(), plot());
570 622
571 623 // Zoom box and zone drawing
572 624 if (impl->m_DrawingZoomRect) {
573 625 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
574 626 }
575 627 else if (impl->m_DrawingZone) {
576 628 impl->m_DrawingZone->setEnd(axisPos.x());
577 629 }
578 630
579 631 // Cursor
580 632 if (auto parentZone = parentZoneWidget()) {
581 633 if (impl->pointIsInAxisRect(axisPos, plot())) {
582 634 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
583 635 }
584 636 else {
585 637 parentZone->notifyMouseLeaveGraph(this);
586 638 }
587 639 }
588 640 else {
589 641 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
590 642 }
591 643
592 644 // Search for the selection zone under the mouse
593 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
594 auto minDistanceToZone = -1;
595 for (auto zone : impl->m_SelectionZones) {
596 auto distanceToZone = zone->selectTest(event->pos(), true);
597 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone) && distanceToZone >= 0
598 && distanceToZone < plot().selectionTolerance()) {
599 selectionZoneItemUnderCursor = zone;
600 }
601 }
602
645 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
603 646 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
604 647 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
605 648
606 649 // Sets the appropriate cursor shape
607 650 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
608 651 setCursor(cursorShape);
609 652
610 653 // Manages the hovered zone
611 654 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
612 655 if (impl->m_HoveredZone) {
613 656 impl->m_HoveredZone->setHovered(false);
614 657 }
615 658 selectionZoneItemUnderCursor->setHovered(true);
616 659 impl->m_HoveredZone = selectionZoneItemUnderCursor;
617 660 plot().replot(QCustomPlot::rpQueuedReplot);
618 661 }
619 662 }
620 663 else {
621 664 // There is no zone under the mouse or the interaction mode is not "selection zones"
622 665 if (impl->m_HoveredZone) {
623 666 impl->m_HoveredZone->setHovered(false);
624 667 impl->m_HoveredZone = nullptr;
625 668 }
626 669
627 670 setCursor(Qt::ArrowCursor);
628 671 }
629 672
630 673 VisualizationDragWidget::mouseMoveEvent(event);
631 674 }
632 675
633 676 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
634 677 {
635 678 auto value = event->angleDelta().x() + event->angleDelta().y();
636 679 if (value != 0) {
637 680
638 681 auto direction = value > 0 ? 1.0 : -1.0;
639 682 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
640 683 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
641 684 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
642 685
643 686 auto zoomOrientations = QFlags<Qt::Orientation>{};
644 687 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
645 688 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
646 689
647 690 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
648 691
649 692 if (!isZoomX && !isZoomY) {
650 693 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
651 694 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
652 695
653 696 axis->setRange(axis->range() + diff);
654 697
655 698 if (plot().noAntialiasingOnDrag()) {
656 699 plot().setNotAntialiasedElements(QCP::aeAll);
657 700 }
658 701
659 702 plot().replot(QCustomPlot::rpQueuedReplot);
660 703 }
661 704 }
662 705 }
663 706
664 707 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
665 708 {
666 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
667 // Starts a zoom box
668 impl->startDrawingRect(event->pos(), plot());
669 }
670 else if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
671 && impl->m_DrawingZone == nullptr) {
672 // Starts a new selection zone
673 auto itemAtPos = plot().itemAt(event->pos(), true);
674 if (!itemAtPos) {
675 impl->startDrawingZone(event->pos(), plot());
709 bool isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
710
711 if (!isDragDropClick) {
712 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
713 // Starts a zoom box
714 impl->startDrawingRect(event->pos(), plot());
715 }
716 else if (sqpApp->plotsInteractionMode()
717 == SqpApplication::PlotsInteractionMode::SelectionZones
718 && impl->m_DrawingZone == nullptr) {
719 // Starts a new selection zone
720 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
721 if (!zoneAtPos) {
722 impl->startDrawingZone(event->pos(), plot());
723 }
676 724 }
677 725 }
678 726 else if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None) {
679 727 plot().setInteraction(QCP::iRangeDrag, true);
680 728 }
681 729
682 730 // Allows mouse panning only in default mode
683 731 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
684 == SqpApplication::PlotsInteractionMode::None);
732 == SqpApplication::PlotsInteractionMode::None
733 && !isDragDropClick);
685 734
686 // Allows zone edition only in selection zone mode
687 impl->setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode()
688 == SqpApplication::PlotsInteractionMode::SelectionZones);
735 // Allows zone edition only in selection zone mode without ALT pressed (ALT is for drag&drop)
736 impl->setSelectionZonesEditionEnabled(
737 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
738 && !isDragDropClick);
689 739
690 740 VisualizationDragWidget::mousePressEvent(event);
691 741 }
692 742
693 743 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
694 744 {
695 745 if (impl->m_DrawingZoomRect) {
696 746
697 747 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
698 748 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
699 749
700 750 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
701 751 impl->m_DrawingZoomRect->bottomRight->coords().x()};
702 752
703 753 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
704 754 impl->m_DrawingZoomRect->bottomRight->coords().y()};
705 755
706 756 impl->removeDrawingRect(plot());
707 757
708 758 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
709 759 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
710 760 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
711 761 axisX->setRange(newAxisXRange);
712 762 axisY->setRange(newAxisYRange);
713 763
714 764 plot().replot(QCustomPlot::rpQueuedReplot);
715 765 }
716 766 }
717 767
718 768 impl->endDrawingZone(plot());
719 769
720 770 impl->m_IsCalibration = false;
721 771 }
722 772
723 773 void VisualizationGraphWidget::onDataCacheVariableUpdated()
724 774 {
725 775 auto graphRange = ui->widget->xAxis->range();
726 776 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
727 777
728 778 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
729 779 auto variable = variableEntry.first;
730 780 qCDebug(LOG_VisualizationGraphWidget())
731 781 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
732 782 qCDebug(LOG_VisualizationGraphWidget())
733 783 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
734 784 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
735 785 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
736 786 }
737 787 }
738 788 }
739 789
740 790 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
741 791 const SqpRange &range)
742 792 {
743 793 auto it = impl->m_VariableToPlotMultiMap.find(variable);
744 794 if (it != impl->m_VariableToPlotMultiMap.end()) {
745 795 impl->updateData(it->second, variable->dataSeries(), range);
746 796 }
747 797 }
@@ -1,271 +1,272
1 1 #include "Visualization/VisualizationSelectionZoneItem.h"
2 2
3 3 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
4 4
5 5 QCustomPlot *m_Plot;
6 6 double m_T1 = 0;
7 7 double m_T2 = 0;
8 8 QColor m_Color;
9 9
10 10 bool m_IsEditionEnabled = true;
11 11 double m_MovedOrinalT1 = 0;
12 12 double m_MovedOrinalT2 = 0;
13 13
14 14 QCPItemStraightLine *m_LeftLine;
15 15 QCPItemStraightLine *m_RightLine;
16 16 QCPItemText *m_NameLabelItem = nullptr;
17 17
18 18 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
19 19 EditionMode m_CurrentEditionMode;
20 20
21 21 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
22 22 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
23 23 {
24 24 }
25 25
26 26 void updatePosition(VisualizationSelectionZoneItem *item)
27 27 {
28 28 item->topLeft->setCoords(m_T1, 0);
29 29 item->bottomRight->setCoords(m_T2, 1);
30 30 }
31 31
32 32 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
33 33 {
34 34 auto distanceLeft = m_LeftLine->selectTest(pos, false);
35 35 auto distanceRight = m_RightLine->selectTest(pos, false);
36 auto distance = zoneItem->selectTest(pos, true);
36 auto distance = zoneItem->selectTest(pos, false);
37 37
38 38 if (distanceRight <= distance) {
39 39 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
40 40 }
41 41 else if (distanceLeft <= distance) {
42 42 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
43 43 }
44 44
45 45 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
46 46 }
47 47 };
48 48
49 49 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
50 50 : QCPItemRect(plot),
51 51 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
52 52 {
53 53 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
54 54 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
55 55 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
56 56 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
57 57
58 58 impl->m_RightLine = new QCPItemStraightLine(plot);
59 59 impl->m_RightLine->point1->setParentAnchor(topRight);
60 60 impl->m_RightLine->point2->setParentAnchor(bottomRight);
61 61 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
62 62 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
63 63 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
64 64 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
65 65
66 66 impl->m_LeftLine = new QCPItemStraightLine(plot);
67 67 impl->m_LeftLine->point1->setParentAnchor(topLeft);
68 68 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
69 69 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
70 70 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
71 71 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
72 72 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
73 73
74 74 impl->m_RightLine->setSelectable(false);
75 75 impl->m_LeftLine->setSelectable(false);
76 76
77 77 // connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
78 78 // &QCPItemStraightLine::setSelected);
79 79 // connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
80 80 // &QCPItemStraightLine::setSelected);
81 81
82 82 setColor(QColor("#E79D41"));
83 83 }
84 84
85 85 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem()
86 86 {
87 87 impl->m_Plot->removeItem(impl->m_RightLine);
88 88 impl->m_Plot->removeItem(impl->m_LeftLine);
89 89 }
90 90
91 91 void VisualizationSelectionZoneItem::setName(const QString &name)
92 92 {
93 93 if (name.isEmpty() && impl->m_NameLabelItem) {
94 94 impl->m_Plot->removeItem(impl->m_NameLabelItem);
95 95 impl->m_NameLabelItem = nullptr;
96 96 }
97 97 else if (!impl->m_NameLabelItem) {
98 98 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
99 99 impl->m_NameLabelItem->setText(name);
100 100 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
101 101 impl->m_NameLabelItem->setColor(impl->m_Color);
102 102 impl->m_NameLabelItem->position->setParentAnchor(top);
103 103 }
104 104 }
105 105
106 106 QString VisualizationSelectionZoneItem::name() const
107 107 {
108 108 if (!impl->m_NameLabelItem) {
109 109 return QString();
110 110 }
111 111
112 112 return impl->m_NameLabelItem->text();
113 113 }
114 114
115 115 SqpRange VisualizationSelectionZoneItem::range() const
116 116 {
117 117 SqpRange range;
118 118 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
119 119 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
120 120 return range;
121 121 }
122 122
123 123 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
124 124 {
125 125 impl->m_T1 = tstart;
126 126 impl->m_T2 = tend;
127 127 impl->updatePosition(this);
128 128 }
129 129
130 130 void VisualizationSelectionZoneItem::setStart(double tstart)
131 131 {
132 132 impl->m_T1 = tstart;
133 133 impl->updatePosition(this);
134 134 }
135 135
136 136 void VisualizationSelectionZoneItem::setEnd(double tend)
137 137 {
138 138 impl->m_T2 = tend;
139 139 impl->updatePosition(this);
140 140 }
141 141
142 142 void VisualizationSelectionZoneItem::setColor(const QColor &color)
143 143 {
144 144 impl->m_Color = color;
145 145
146 146 auto brushColor = color;
147 147 brushColor.setAlpha(40);
148 148 setBrush(QBrush(brushColor));
149 149 setPen(QPen(Qt::NoPen));
150 150
151 151 auto selectedBrushColor = brushColor;
152 152 selectedBrushColor.setAlpha(65);
153 153 setSelectedBrush(QBrush(selectedBrushColor));
154 154 setSelectedPen(QPen(Qt::NoPen));
155 155
156 156 auto linePen = QPen(color);
157 157 linePen.setStyle(Qt::SolidLine);
158 158 linePen.setWidth(2);
159 159
160 160 auto selectedLinePen = linePen;
161 161 selectedLinePen.setColor(color.darker(30));
162 162
163 163 impl->m_LeftLine->setPen(linePen);
164 164 impl->m_RightLine->setPen(linePen);
165 165
166 166 impl->m_LeftLine->setSelectedPen(selectedLinePen);
167 167 impl->m_RightLine->setSelectedPen(selectedLinePen);
168 168 }
169 169
170 170 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
171 171 {
172 172 impl->m_IsEditionEnabled = value;
173 173 setSelectable(value);
174 174 if (!value) {
175 175 setSelected(false);
176 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
176 177 }
177 178 }
178 179
179 180 bool VisualizationSelectionZoneItem::isEditionEnabled() const
180 181 {
181 182 return impl->m_IsEditionEnabled;
182 183 }
183 184
184 185 Qt::CursorShape
185 186 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
186 187 {
187 188 auto mode = impl->m_CurrentEditionMode
188 189 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
189 190 ? impl->getEditionMode(position, this)
190 191 : impl->m_CurrentEditionMode;
191 192 switch (mode) {
192 193 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
193 194 return Qt::SizeAllCursor;
194 195 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
195 196 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
196 197 return Qt::SizeHorCursor;
197 198 default:
198 199 return Qt::ArrowCursor;
199 200 }
200 201 }
201 202
202 203 void VisualizationSelectionZoneItem::setHovered(bool value)
203 204 {
204 205 if (value) {
205 206 auto linePen = impl->m_LeftLine->pen();
206 207 linePen.setStyle(Qt::DotLine);
207 208 linePen.setWidth(3);
208 209
209 210 auto selectedLinePen = impl->m_LeftLine->selectedPen();
210 211 ;
211 212 selectedLinePen.setStyle(Qt::DotLine);
212 213 selectedLinePen.setWidth(3);
213 214
214 215 impl->m_LeftLine->setPen(linePen);
215 216 impl->m_RightLine->setPen(linePen);
216 217
217 218 impl->m_LeftLine->setSelectedPen(selectedLinePen);
218 219 impl->m_RightLine->setSelectedPen(selectedLinePen);
219 220 }
220 221 else {
221 222 setColor(impl->m_Color);
222 223 }
223 224 }
224 225
225 226 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
226 227 {
227 228 if (isEditionEnabled()) {
228 229 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
229 230
230 231 impl->m_MovedOrinalT1 = impl->m_T1;
231 232 impl->m_MovedOrinalT2 = impl->m_T2;
232 233 }
233 234 else {
234 235 event->ignore();
235 236 }
236 237 }
237 238
238 239 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
239 240 {
240 241 if (isEditionEnabled()) {
241 242 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
242 243 auto diff = axis->pixelToCoord(event->pos().x()) - axis->pixelToCoord(startPos.x());
243 244
244 245 switch (impl->m_CurrentEditionMode) {
245 246 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
246 247 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
247 248 break;
248 249 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
249 250 setStart(impl->m_MovedOrinalT1 + diff);
250 251 break;
251 252 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
252 253 setEnd(impl->m_MovedOrinalT2 + diff);
253 254 break;
254 255 // default:
255 256 // unknown edition mode
256 257 }
257 258 }
258 259 else {
259 260 event->ignore();
260 261 }
261 262 }
262 263
263 264 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
264 265 {
265 266 if (isEditionEnabled()) {
266 267 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
267 268 }
268 269 else {
269 270 event->ignore();
270 271 }
271 272 }
@@ -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 75 // Returns the first graph in the zone or nullptr if there is no graph inside
76 76 VisualizationGraphWidget *firstGraph(const VisualizationZoneWidget *zoneWidget) const
77 77 {
78 78 VisualizationGraphWidget *firstGraph = nullptr;
79 79 auto layout = zoneWidget->ui->dragDropContainer->layout();
80 80 if (layout->count() > 0) {
81 81 if (auto visualizationGraphWidget
82 82 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
83 83 firstGraph = visualizationGraphWidget;
84 84 }
85 85 }
86 86
87 87 return firstGraph;
88 88 }
89 89
90 90 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
91 91 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
92 92 VisualizationZoneWidget *zoneWidget);
93 93 };
94 94
95 95 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
96 96 : VisualizationDragWidget{parent},
97 97 ui{new Ui::VisualizationZoneWidget},
98 98 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
99 99 {
100 100 ui->setupUi(this);
101 101
102 102 ui->zoneNameLabel->setText(name);
103 103
104 104 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Graph);
105 105 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
106 106 VisualizationDragDropContainer::DropBehavior::Inserted);
107 107 ui->dragDropContainer->setMimeType(
108 108 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
109 109 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
110 110 VisualizationDragDropContainer::DropBehavior::Merged);
111 111 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
112 112 VisualizationDragDropContainer::DropBehavior::Forbidden);
113 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
114 VisualizationDragDropContainer::DropBehavior::Forbidden);
113 115 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
114 116 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
115 117 ui->dragDropContainer);
116 118 });
117 119
118 120 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
119 121 if (!mimeData) {
120 122 return false;
121 123 }
122 124
123 125 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
124 126 auto variables = sqpApp->variableController().variablesForMimeData(
125 127 mimeData->data(MIME_TYPE_VARIABLE_LIST));
126 128
127 129 if (variables.count() != 1) {
128 130 return false;
129 131 }
130 132 auto variable = variables.first();
131 133
132 134 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
133 135 return graphWidget->canDrop(*variable);
134 136 }
135 137 }
136 138
137 139 return true;
138 140 };
139 141 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
140 142
141 143 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
142 144 &VisualizationZoneWidget::dropMimeData);
143 145 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
144 146 &VisualizationZoneWidget::dropMimeDataOnGraph);
145 147
146 148 // 'Close' options : widget is deleted when closed
147 149 setAttribute(Qt::WA_DeleteOnClose);
148 150 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
149 151 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
150 152
151 153 // Synchronisation id
152 154 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
153 155 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
154 156 }
155 157
156 158 VisualizationZoneWidget::~VisualizationZoneWidget()
157 159 {
158 160 delete ui;
159 161 }
160 162
161 163 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
162 164 {
163 165 // Synchronize new graph with others in the zone
164 166 impl->m_Synchronizer->addGraph(*graphWidget);
165 167
166 168 ui->dragDropContainer->addDragWidget(graphWidget);
167 169 }
168 170
169 171 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
170 172 {
171 173 // Synchronize new graph with others in the zone
172 174 impl->m_Synchronizer->addGraph(*graphWidget);
173 175
174 176 ui->dragDropContainer->insertDragWidget(index, graphWidget);
175 177 }
176 178
177 179 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
178 180 {
179 181 return createGraph(variable, -1);
180 182 }
181 183
182 184 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
183 185 int index)
184 186 {
185 187 auto graphWidget
186 188 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
187 189
188 190
189 191 // Set graph properties
190 192 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
191 193 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
192 194
193 195
194 196 // Lambda to synchronize zone widget
195 197 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
196 198 const SqpRange &oldGraphRange) {
197 199
198 200 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
199 201 auto frameLayout = ui->dragDropContainer->layout();
200 202 for (auto i = 0; i < frameLayout->count(); ++i) {
201 203 auto graphChild
202 204 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
203 205 if (graphChild && (graphChild != graphWidget)) {
204 206
205 207 auto graphChildRange = graphChild->graphRange();
206 208 switch (zoomType) {
207 209 case AcquisitionZoomType::ZoomIn: {
208 210 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
209 211 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
210 212 graphChildRange.m_TStart += deltaLeft;
211 213 graphChildRange.m_TEnd -= deltaRight;
212 214 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
213 215 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
214 216 << deltaLeft;
215 217 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
216 218 << deltaRight;
217 219 qCDebug(LOG_VisualizationZoneWidget())
218 220 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
219 221
220 222 break;
221 223 }
222 224
223 225 case AcquisitionZoomType::ZoomOut: {
224 226 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
225 227 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
226 228 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
227 229 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
228 230 << deltaLeft;
229 231 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
230 232 << deltaRight;
231 233 qCDebug(LOG_VisualizationZoneWidget())
232 234 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
233 235 graphChildRange.m_TStart -= deltaLeft;
234 236 graphChildRange.m_TEnd += deltaRight;
235 237 break;
236 238 }
237 239 case AcquisitionZoomType::PanRight: {
238 240 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
239 241 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
240 242 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
241 243 graphChildRange.m_TStart += deltaLeft;
242 244 graphChildRange.m_TEnd += deltaRight;
243 245 qCDebug(LOG_VisualizationZoneWidget())
244 246 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
245 247 break;
246 248 }
247 249 case AcquisitionZoomType::PanLeft: {
248 250 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
249 251 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
250 252 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
251 253 graphChildRange.m_TStart -= deltaLeft;
252 254 graphChildRange.m_TEnd -= deltaRight;
253 255 break;
254 256 }
255 257 case AcquisitionZoomType::Unknown: {
256 258 qCDebug(LOG_VisualizationZoneWidget())
257 259 << tr("Impossible to synchronize: zoom type unknown");
258 260 break;
259 261 }
260 262 default:
261 263 qCCritical(LOG_VisualizationZoneWidget())
262 264 << tr("Impossible to synchronize: zoom type not take into account");
263 265 // No action
264 266 break;
265 267 }
266 268 graphChild->enableAcquisition(false);
267 269 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
268 270 << graphChild->graphRange();
269 271 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
270 272 << graphChildRange;
271 273 qCDebug(LOG_VisualizationZoneWidget())
272 274 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
273 275 graphChild->setGraphRange(graphChildRange);
274 276 graphChild->enableAcquisition(true);
275 277 }
276 278 }
277 279 };
278 280
279 281 // connection for synchronization
280 282 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
281 283 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
282 284 &VisualizationZoneWidget::onVariableAdded);
283 285 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
284 286 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
285 287
286 288 auto range = SqpRange{};
287 289 if (auto firstGraph = impl->firstGraph(this)) {
288 290 // Case of a new graph in a existant zone
289 291 range = firstGraph->graphRange();
290 292 }
291 293 else {
292 294 // Case of a new graph as the first of the zone
293 295 range = variable->range();
294 296 }
295 297
296 298 this->insertGraph(index, graphWidget);
297 299
298 300 graphWidget->addVariable(variable, range);
299 301 graphWidget->setYRange(variable);
300 302
301 303 return graphWidget;
302 304 }
303 305
304 306 VisualizationGraphWidget *
305 307 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
306 308 {
307 309 if (variables.isEmpty()) {
308 310 return nullptr;
309 311 }
310 312
311 313 auto graphWidget = createGraph(variables.first(), index);
312 314 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
313 315 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
314 316 }
315 317
316 318 return graphWidget;
317 319 }
318 320
319 321 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
320 322 {
321 323 if (visitor) {
322 324 visitor->visitEnter(this);
323 325
324 326 // Apply visitor to graph children: widgets different from graphs are not visited (no
325 327 // action)
326 328 processGraphs(
327 329 *ui->dragDropContainer->layout(),
328 330 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
329 331
330 332 visitor->visitLeave(this);
331 333 }
332 334 else {
333 335 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
334 336 }
335 337 }
336 338
337 339 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
338 340 {
339 341 // A tab can always accomodate a variable
340 342 Q_UNUSED(variable);
341 343 return true;
342 344 }
343 345
344 346 bool VisualizationZoneWidget::contains(const Variable &variable) const
345 347 {
346 348 Q_UNUSED(variable);
347 349 return false;
348 350 }
349 351
350 352 QString VisualizationZoneWidget::name() const
351 353 {
352 354 return ui->zoneNameLabel->text();
353 355 }
354 356
355 QMimeData *VisualizationZoneWidget::mimeData() const
357 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
356 358 {
359 Q_UNUSED(position);
360
357 361 auto mimeData = new QMimeData;
358 362 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
359 363
360 364 if (auto firstGraph = impl->firstGraph(this)) {
361 365 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
362 366 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
363 367 }
364 368
365 369 return mimeData;
366 370 }
367 371
368 372 bool VisualizationZoneWidget::isDragAllowed() const
369 373 {
370 374 return true;
371 375 }
372 376
373 377 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
374 378 const QPointF &plotPosition,
375 379 VisualizationGraphWidget *graphWidget)
376 380 {
377 381 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
378 382 VisualizationGraphWidget &processedGraph) {
379 383
380 384 switch (sqpApp->plotsCursorMode()) {
381 385 case SqpApplication::PlotsCursorMode::Vertical:
382 386 processedGraph.removeHorizontalCursor();
383 387 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
384 388 break;
385 389 case SqpApplication::PlotsCursorMode::Temporal:
386 390 processedGraph.addVerticalCursor(plotPosition.x());
387 391 processedGraph.removeHorizontalCursor();
388 392 break;
389 393 case SqpApplication::PlotsCursorMode::Horizontal:
390 394 processedGraph.removeVerticalCursor();
391 395 if (&processedGraph == graphWidget) {
392 396 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
393 397 }
394 398 else {
395 399 processedGraph.removeHorizontalCursor();
396 400 }
397 401 break;
398 402 case SqpApplication::PlotsCursorMode::Cross:
399 403 if (&processedGraph == graphWidget) {
400 404 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
401 405 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
402 406 }
403 407 else {
404 408 processedGraph.removeHorizontalCursor();
405 409 processedGraph.removeVerticalCursor();
406 410 }
407 411 break;
408 412 case SqpApplication::PlotsCursorMode::NoCursor:
409 413 processedGraph.removeHorizontalCursor();
410 414 processedGraph.removeVerticalCursor();
411 415 break;
412 416 }
413 417
414 418
415 419 });
416 420 }
417 421
418 422 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
419 423 {
420 424 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
421 425 processedGraph.removeHorizontalCursor();
422 426 processedGraph.removeVerticalCursor();
423 427 });
424 428 }
425 429
426 430 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
427 431 {
428 432 // Closes graphs in the zone
429 433 processGraphs(*ui->dragDropContainer->layout(),
430 434 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
431 435
432 436 // Delete synchronization group from variable controller
433 437 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
434 438 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
435 439
436 440 QWidget::closeEvent(event);
437 441 }
438 442
439 443 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
440 444 {
441 445 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
442 446 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
443 447 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
444 448 }
445 449
446 450 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
447 451 {
448 452 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
449 453 Q_ARG(std::shared_ptr<Variable>, variable),
450 454 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
451 455 }
452 456
453 457 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
454 458 {
455 459 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
456 460 impl->dropGraph(index, this);
457 461 }
458 462 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
459 463 auto variables = sqpApp->variableController().variablesForMimeData(
460 464 mimeData->data(MIME_TYPE_VARIABLE_LIST));
461 465 impl->dropVariables(variables, index, this);
462 466 }
463 467 else {
464 468 qCWarning(LOG_VisualizationZoneWidget())
465 469 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
466 470 }
467 471 }
468 472
469 473 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
470 474 const QMimeData *mimeData)
471 475 {
472 476 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
473 477 if (!graphWidget) {
474 478 qCWarning(LOG_VisualizationZoneWidget())
475 479 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
476 480 "drop aborted");
477 481 Q_ASSERT(false);
478 482 return;
479 483 }
480 484
481 485 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
482 486 auto variables = sqpApp->variableController().variablesForMimeData(
483 487 mimeData->data(MIME_TYPE_VARIABLE_LIST));
484 488 for (const auto &var : variables) {
485 489 graphWidget->addVariable(var, graphWidget->graphRange());
486 490 }
487 491 }
488 492 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
489 493 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
490 494 graphWidget->setGraphRange(range);
491 495 }
492 496 else {
493 497 qCWarning(LOG_VisualizationZoneWidget())
494 498 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
495 499 }
496 500 }
497 501
498 502 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
499 503 int index, VisualizationZoneWidget *zoneWidget)
500 504 {
501 505 auto &helper = sqpApp->dragDropHelper();
502 506
503 507 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
504 508 if (!graphWidget) {
505 509 qCWarning(LOG_VisualizationZoneWidget())
506 510 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
507 511 "found or invalid.");
508 512 Q_ASSERT(false);
509 513 return;
510 514 }
511 515
512 516 auto parentDragDropContainer
513 517 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
514 518 if (!parentDragDropContainer) {
515 519 qCWarning(LOG_VisualizationZoneWidget())
516 520 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
517 521 "the dropped graph is not found.");
518 522 Q_ASSERT(false);
519 523 return;
520 524 }
521 525
522 526 const auto &variables = graphWidget->variables();
523 527
524 528 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
525 529 // The drop didn't occur in the same zone
526 530
527 531 // Abort the requests for the variables (if any)
528 532 // Commented, because it's not sure if it's needed or not
529 533 // for (const auto& var : variables)
530 534 //{
531 535 // sqpApp->variableController().onAbortProgressRequested(var);
532 536 //}
533 537
534 538 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
535 539 auto nbGraph = parentDragDropContainer->countDragWidget();
536 540 if (nbGraph == 1) {
537 541 // This is the only graph in the previous zone, close the zone
538 542 helper.delayedCloseWidget(previousParentZoneWidget);
539 543 }
540 544 else {
541 545 // Close the graph
542 546 helper.delayedCloseWidget(graphWidget);
543 547 }
544 548
545 549 // Creates the new graph in the zone
546 550 zoneWidget->createGraph(variables, index);
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 0
You need to be logged in to leave comments. Login now