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