##// END OF EJS Templates
Action "Remove Selected Zones"
trabillard -
r1114:716d2411dc36
parent child
Show More
@@ -1,138 +1,141
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 VisualizationWidget;
21 21 class VisualizationZoneWidget;
22 class VisualizationSelectionZoneItem;
22 23
23 24 namespace Ui {
24 25 class VisualizationGraphWidget;
25 26 } // namespace Ui
26 27
27 28 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
28 29 Q_OBJECT
29 30
30 31 friend class QCustomPlotSynchronizer;
31 32 friend class VisualizationGraphRenderingDelegate;
32 33
33 34 public:
34 35 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
35 36 virtual ~VisualizationGraphWidget();
36 37
37 38 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
38 39 VisualizationZoneWidget *parentZoneWidget() const noexcept;
39 40
40 41 /// Returns the main VisualizationWidget which contains the graph or nullptr
41 42 VisualizationWidget *parentVisualizationWidget() const;
42 43
43 44 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
44 45 void enableAcquisition(bool enable);
45 46
46 47 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
47 48
48 49 /// Removes a variable from the graph
49 50 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
50 51
51 52 /// Returns the list of all variables used in the graph
52 53 QList<std::shared_ptr<Variable> > variables() const;
53 54
54 55 /// Sets the y-axis range based on the data of a variable
55 56 void setYRange(std::shared_ptr<Variable> variable);
56 57 SqpRange graphRange() const noexcept;
57 58 void setGraphRange(const SqpRange &range);
58 59
60 // Zones
59 61 /// Returns the ranges of all the selection zones on the graph
60 62 QVector<SqpRange> selectionZoneRanges() const;
61
62 63 /// Adds new selection zones in the graph
63 64 void addSelectionZones(const QVector<SqpRange> &ranges);
65 /// Removes the specified selection zone
66 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
64 67
65 68 /// Undo the last zoom done with a zoom box
66 69 void undoZoom();
67 70
68 71 // IVisualizationWidget interface
69 72 void accept(IVisualizationWidgetVisitor *visitor) override;
70 73 bool canDrop(const Variable &variable) const override;
71 74 bool contains(const Variable &variable) const override;
72 75 QString name() const override;
73 76
74 77 // VisualisationDragWidget
75 78 QMimeData *mimeData(const QPoint &position) const override;
76 79 QPixmap customDragPixmap(const QPoint &dragPosition) override;
77 80 bool isDragAllowed() const override;
78 81 void highlightForMerge(bool highlighted) override;
79 82
80 83 // Cursors
81 84 /// Adds or moves the vertical cursor at the specified value on the x-axis
82 85 void addVerticalCursor(double time);
83 86 /// Adds or moves the vertical cursor at the specified value on the x-axis
84 87 void addVerticalCursorAtViewportPosition(double position);
85 88 void removeVerticalCursor();
86 89 /// Adds or moves the vertical cursor at the specified value on the y-axis
87 90 void addHorizontalCursor(double value);
88 91 /// Adds or moves the vertical cursor at the specified value on the y-axis
89 92 void addHorizontalCursorAtViewportPosition(double position);
90 93 void removeHorizontalCursor();
91 94
92 95 signals:
93 96 void synchronize(const SqpRange &range, const SqpRange &oldRange);
94 97 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
95 98 bool synchronise);
96 99
97 100 /// Signal emitted when the variable is about to be removed from the graph
98 101 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
99 102 /// Signal emitted when the variable has been added to the graph
100 103 void variableAdded(std::shared_ptr<Variable> var);
101 104
102 105 protected:
103 106 void closeEvent(QCloseEvent *event) override;
104 107 void enterEvent(QEvent *event) override;
105 108 void leaveEvent(QEvent *event) override;
106 109
107 110 QCustomPlot &plot() const noexcept;
108 111
109 112 private:
110 113 Ui::VisualizationGraphWidget *ui;
111 114
112 115 class VisualizationGraphWidgetPrivate;
113 116 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
114 117
115 118 private slots:
116 119 /// Slot called when right clicking on the graph (displays a menu)
117 120 void onGraphMenuRequested(const QPoint &pos) noexcept;
118 121
119 122 /// Rescale the X axe to range parameter
120 123 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
121 124
122 125 /// Slot called when a mouse double click was made
123 126 void onMouseDoubleClick(QMouseEvent *event) noexcept;
124 127 /// Slot called when a mouse move was made
125 128 void onMouseMove(QMouseEvent *event) noexcept;
126 129 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
127 130 void onMouseWheel(QWheelEvent *event) noexcept;
128 131 /// Slot called when a mouse press was made, to activate the calibration of a graph
129 132 void onMousePress(QMouseEvent *event) noexcept;
130 133 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
131 134 void onMouseRelease(QMouseEvent *event) noexcept;
132 135
133 136 void onDataCacheVariableUpdated();
134 137
135 138 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
136 139 };
137 140
138 141 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,47 +1,51
1 1 #ifndef SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
2 2 #define SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <Data/SqpRange.h>
6 6 #include <Visualization/qcustomplot.h>
7 7
8 class VisualizationGraphWidget;
9
8 10 class VisualizationSelectionZoneItem : public QCPItemRect {
9 11
10 12 public:
11 13 VisualizationSelectionZoneItem(QCustomPlot *plot);
12 14 virtual ~VisualizationSelectionZoneItem();
13 15
16 VisualizationGraphWidget *parentGraphWidget() const noexcept;
17
14 18 void setName(const QString &name);
15 19 QString name() const;
16 20
17 21 SqpRange range() const;
18 22 void setRange(double tstart, double tend);
19 23 void setStart(double tstart);
20 24 void setEnd(double tend);
21 25
22 26 void setColor(const QColor &color);
23 27
24 28 void setEditionEnabled(bool value);
25 29 bool isEditionEnabled() const;
26 30
27 31 Qt::CursorShape curshorShapeForPosition(const QPoint &position) const;
28 32 void setHovered(bool value);
29 33
30 34 void setAssociatedEditedZones(const QVector<VisualizationSelectionZoneItem *> &associatedZones);
31 35
32 36 protected:
33 37 void mousePressEvent(QMouseEvent *event, const QVariant &details) override;
34 38 void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) override;
35 39 void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) override;
36 40
37 41 void resizeLeft(double pixelDiff);
38 42 void resizeRight(double pixelDiff);
39 43 void move(double pixelDiff);
40 44
41 45
42 46 private:
43 47 class VisualizationSelectionZoneItemPrivate;
44 48 spimpl::unique_impl_ptr<VisualizationSelectionZoneItemPrivate> impl;
45 49 };
46 50
47 51 #endif // SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
@@ -1,14 +1,22
1 1 #include "Visualization/VisualizationActionManager.h"
2 #include "Visualization/VisualizationGraphWidget.h"
3 #include "Visualization/VisualizationSelectionZoneItem.h"
2 4
3 5 #include <Actions/ActionsGuiController.h>
4 6 #include <SqpApplication.h>
5 7
6 8 VisualizationActionManager::VisualizationActionManager() {}
7 9
8 10 void VisualizationActionManager::installSelectionZoneActions()
9 11 {
10 12 auto &actionController = sqpApp->actionsGuiController();
11 actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto &zone) {});
12 actionController.addSectionZoneAction("Align Left", [](auto &zone) {});
13 actionController.addSectionZoneAction("Align Right", [](auto &zone) {});
13 actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto &zones) {
14 for (auto selectionZone : zones) {
15 if (auto graph = selectionZone->parentGraphWidget()) {
16 graph->removeSelectionZone(selectionZone);
17 }
18 }
19 });
20 actionController.addSectionZoneAction("Align Left", [](auto &zones) {});
21 actionController.addSectionZoneAction("Align Right", [](auto &zones) {});
14 22 }
@@ -1,893 +1,901
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/VisualizationSelectionZoneManager.h"
9 9 #include "Visualization/VisualizationWidget.h"
10 10 #include "Visualization/VisualizationZoneWidget.h"
11 11 #include "ui_VisualizationGraphWidget.h"
12 12
13 13 #include <Actions/ActionsGuiController.h>
14 14 #include <Common/MimeTypesDef.h>
15 15 #include <Data/ArrayData.h>
16 16 #include <Data/IDataSeries.h>
17 17 #include <Data/SpectrogramSeries.h>
18 18 #include <DragAndDrop/DragDropGuiController.h>
19 19 #include <Settings/SqpSettingsDefs.h>
20 20 #include <SqpApplication.h>
21 21 #include <Time/TimeController.h>
22 22 #include <Variable/Variable.h>
23 23 #include <Variable/VariableController.h>
24 24
25 25 #include <unordered_map>
26 26
27 27 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 28
29 29 namespace {
30 30
31 31 /// Key pressed to enable drag&drop in all modes
32 32 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
33 33
34 34 /// Key pressed to enable zoom on horizontal axis
35 35 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
36 36
37 37 /// Key pressed to enable zoom on vertical axis
38 38 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
39 39
40 40 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
41 41 const auto PAN_SPEED = 5;
42 42
43 43 /// Key pressed to enable a calibration pan
44 44 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
45 45
46 46 /// Key pressed to enable multi selection of selection zones
47 47 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
48 48
49 49 /// Minimum size for the zoom box, in percentage of the axis range
50 50 const auto ZOOM_BOX_MIN_SIZE = 0.8;
51 51
52 52 /// Format of the dates appearing in the label of a cursor
53 53 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
54 54
55 55 } // namespace
56 56
57 57 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
58 58
59 59 explicit VisualizationGraphWidgetPrivate(const QString &name)
60 60 : m_Name{name},
61 61 m_DoAcquisition{true},
62 62 m_IsCalibration{false},
63 63 m_RenderingDelegate{nullptr}
64 64 {
65 65 }
66 66
67 67 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
68 68 const SqpRange &range)
69 69 {
70 70 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
71 71
72 72 // Prevents that data has changed to update rendering
73 73 m_RenderingDelegate->onPlotUpdated();
74 74 }
75 75
76 76 QString m_Name;
77 77 // 1 variable -> n qcpplot
78 78 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
79 79 bool m_DoAcquisition;
80 80 bool m_IsCalibration;
81 81 /// Delegate used to attach rendering features to the plot
82 82 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
83 83
84 84 QCPItemRect *m_DrawingZoomRect = nullptr;
85 85 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
86 86
87 87 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
88 88 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
89 89
90 90 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
91 91 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
92 92 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
93 93
94 94 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
95 95
96 96 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
97 97 {
98 98 removeDrawingRect(plot);
99 99
100 100 auto axisPos = posToAxisPos(pos, plot);
101 101
102 102 m_DrawingZoomRect = new QCPItemRect{&plot};
103 103 QPen p;
104 104 p.setWidth(2);
105 105 m_DrawingZoomRect->setPen(p);
106 106
107 107 m_DrawingZoomRect->topLeft->setCoords(axisPos);
108 108 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
109 109 }
110 110
111 111 void removeDrawingRect(QCustomPlot &plot)
112 112 {
113 113 if (m_DrawingZoomRect) {
114 114 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
115 115 m_DrawingZoomRect = nullptr;
116 116 plot.replot(QCustomPlot::rpQueuedReplot);
117 117 }
118 118 }
119 119
120 120 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
121 121 {
122 122 endDrawingZone(graph);
123 123
124 124 auto axisPos = posToAxisPos(pos, graph->plot());
125 125
126 126 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
127 127 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
128 128 m_DrawingZone->setEditionEnabled(false);
129 129 }
130 130
131 131 void endDrawingZone(VisualizationGraphWidget *graph)
132 132 {
133 133 if (m_DrawingZone) {
134 134 auto drawingZoneRange = m_DrawingZone->range();
135 135 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
136 136 m_DrawingZone->setEditionEnabled(true);
137 137 addSelectionZone(m_DrawingZone);
138 138 }
139 139 else {
140 140 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
141 141 }
142 142
143 143 graph->plot().replot(QCustomPlot::rpQueuedReplot);
144 144 m_DrawingZone = nullptr;
145 145 }
146 146 }
147 147
148 148 void setSelectionZonesEditionEnabled(bool value)
149 149 {
150 150 for (auto s : m_SelectionZones) {
151 151 s->setEditionEnabled(value);
152 152 }
153 153 }
154 154
155 155 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
156 156
157 157 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
158 158 const QCustomPlot &plot) const
159 159 {
160 160 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
161 161 auto minDistanceToZone = -1;
162 162 for (auto zone : m_SelectionZones) {
163 163 auto distanceToZone = zone->selectTest(pos, false);
164 164 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
165 165 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
166 166 selectionZoneItemUnderCursor = zone;
167 167 }
168 168 }
169 169
170 170 return selectionZoneItemUnderCursor;
171 171 }
172 172
173 173 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
174 174 {
175 175 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
176 176 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
177 177 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
178 178 }
179 179
180 180 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
181 181 {
182 182 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
183 183 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
184 184 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
185 185 }
186 186 };
187 187
188 188 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
189 189 : VisualizationDragWidget{parent},
190 190 ui{new Ui::VisualizationGraphWidget},
191 191 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
192 192 {
193 193 ui->setupUi(this);
194 194
195 195 // 'Close' options : widget is deleted when closed
196 196 setAttribute(Qt::WA_DeleteOnClose);
197 197
198 198 // Set qcpplot properties :
199 199 // - zoom is enabled
200 200 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
201 201 ui->widget->setInteractions(QCP::iRangeZoom);
202 202 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
203 203
204 204 // The delegate must be initialized after the ui as it uses the plot
205 205 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
206 206
207 207 // Init the cursors
208 208 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
209 209 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
210 210 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
211 211 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
212 212
213 213 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
214 214 connect(ui->widget, &QCustomPlot::mouseRelease, this,
215 215 &VisualizationGraphWidget::onMouseRelease);
216 216 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
217 217 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
218 218 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
219 219 &VisualizationGraphWidget::onMouseDoubleClick);
220 220 connect(
221 221 ui->widget->xAxis,
222 222 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
223 223 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
224 224
225 225 // Activates menu when right clicking on the graph
226 226 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
227 227 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
228 228 &VisualizationGraphWidget::onGraphMenuRequested);
229 229
230 230 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
231 231 &VariableController::onRequestDataLoading);
232 232
233 233 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
234 234 &VisualizationGraphWidget::onUpdateVarDisplaying);
235 235
236 236 #ifdef Q_OS_MAC
237 237 plot().setPlottingHint(QCP::phFastPolylines, true);
238 238 #endif
239 239 }
240 240
241 241
242 242 VisualizationGraphWidget::~VisualizationGraphWidget()
243 243 {
244 244 delete ui;
245 245 }
246 246
247 247 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
248 248 {
249 249 auto parent = parentWidget();
250 250 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
251 251 parent = parent->parentWidget();
252 252 }
253 253
254 254 return qobject_cast<VisualizationZoneWidget *>(parent);
255 255 }
256 256
257 257 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
258 258 {
259 259 auto parent = parentWidget();
260 260 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
261 261 parent = parent->parentWidget();
262 262 }
263 263
264 264 return qobject_cast<VisualizationWidget *>(parent);
265 265 }
266 266
267 267 void VisualizationGraphWidget::enableAcquisition(bool enable)
268 268 {
269 269 impl->m_DoAcquisition = enable;
270 270 }
271 271
272 272 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
273 273 {
274 274 // Uses delegate to create the qcpplot components according to the variable
275 275 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
276 276
277 277 if (auto dataSeries = variable->dataSeries()) {
278 278 // Set axes properties according to the units of the data series
279 279 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
280 280
281 281 // Sets rendering properties for the new plottables
282 282 // Warning: this method must be called after setAxesProperties(), as it can access to some
283 283 // axes properties that have to be initialized
284 284 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
285 285 }
286 286
287 287 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
288 288
289 289 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
290 290
291 291 this->enableAcquisition(false);
292 292 this->setGraphRange(range);
293 293 this->enableAcquisition(true);
294 294
295 295 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
296 296
297 297 emit variableAdded(variable);
298 298 }
299 299
300 300 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
301 301 {
302 302 // Each component associated to the variable :
303 303 // - is removed from qcpplot (which deletes it)
304 304 // - is no longer referenced in the map
305 305 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
306 306 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
307 307 emit variableAboutToBeRemoved(variable);
308 308
309 309 auto &plottablesMap = variableIt->second;
310 310
311 311 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
312 312 plottableIt != plottableEnd;) {
313 313 ui->widget->removePlottable(plottableIt->second);
314 314 plottableIt = plottablesMap.erase(plottableIt);
315 315 }
316 316
317 317 impl->m_VariableToPlotMultiMap.erase(variableIt);
318 318 }
319 319
320 320 // Updates graph
321 321 ui->widget->replot();
322 322 }
323 323
324 324 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
325 325 {
326 326 auto variables = QList<std::shared_ptr<Variable> >{};
327 327 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
328 328 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
329 329 variables << it->first;
330 330 }
331 331
332 332 return variables;
333 333 }
334 334
335 335 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
336 336 {
337 337 if (!variable) {
338 338 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
339 339 return;
340 340 }
341 341
342 342 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
343 343 }
344 344
345 345 SqpRange VisualizationGraphWidget::graphRange() const noexcept
346 346 {
347 347 auto graphRange = ui->widget->xAxis->range();
348 348 return SqpRange{graphRange.lower, graphRange.upper};
349 349 }
350 350
351 351 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
352 352 {
353 353 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
354 354 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
355 355 ui->widget->replot();
356 356 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
357 357 }
358 358
359 359 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
360 360 {
361 361 QVector<SqpRange> ranges;
362 362 for (auto zone : impl->m_SelectionZones) {
363 363 ranges << zone->range();
364 364 }
365 365
366 366 return ranges;
367 367 }
368 368
369 369 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
370 370 {
371 371 for (const auto &range : ranges) {
372 372 // note: ownership is transfered to QCustomPlot
373 373 auto zone = new VisualizationSelectionZoneItem(&plot());
374 374 zone->setRange(range.m_TStart, range.m_TEnd);
375 375 impl->addSelectionZone(zone);
376 376 }
377 377
378 378 plot().replot(QCustomPlot::rpQueuedReplot);
379 379 }
380 380
381 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
382 {
383 impl->m_SelectionZones.removeAll(selectionZone);
384 plot().removeItem(selectionZone);
385 plot().replot(QCustomPlot::rpQueuedReplot);
386 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
387 }
388
381 389 void VisualizationGraphWidget::undoZoom()
382 390 {
383 391 auto zoom = impl->m_ZoomStack.pop();
384 392 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
385 393 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
386 394
387 395 axisX->setRange(zoom.first);
388 396 axisY->setRange(zoom.second);
389 397
390 398 plot().replot(QCustomPlot::rpQueuedReplot);
391 399 }
392 400
393 401 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
394 402 {
395 403 if (visitor) {
396 404 visitor->visit(this);
397 405 }
398 406 else {
399 407 qCCritical(LOG_VisualizationGraphWidget())
400 408 << tr("Can't visit widget : the visitor is null");
401 409 }
402 410 }
403 411
404 412 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
405 413 {
406 414 auto isSpectrogram = [](const auto &variable) {
407 415 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
408 416 };
409 417
410 418 // - A spectrogram series can't be dropped on graph with existing plottables
411 419 // - No data series can be dropped on graph with existing spectrogram series
412 420 return isSpectrogram(variable)
413 421 ? impl->m_VariableToPlotMultiMap.empty()
414 422 : std::none_of(
415 423 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
416 424 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
417 425 }
418 426
419 427 bool VisualizationGraphWidget::contains(const Variable &variable) const
420 428 {
421 429 // Finds the variable among the keys of the map
422 430 auto variablePtr = &variable;
423 431 auto findVariable
424 432 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
425 433
426 434 auto end = impl->m_VariableToPlotMultiMap.cend();
427 435 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
428 436 return it != end;
429 437 }
430 438
431 439 QString VisualizationGraphWidget::name() const
432 440 {
433 441 return impl->m_Name;
434 442 }
435 443
436 444 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
437 445 {
438 446 auto mimeData = new QMimeData;
439 447
440 448 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
441 449 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
442 450 && selectionZoneItemUnderCursor) {
443 451 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
444 452 selectionZoneItemUnderCursor->range()));
445 453 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
446 454 selectionZoneItemUnderCursor->range()));
447 455 }
448 456 else {
449 457 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
450 458
451 459 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
452 460 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
453 461 }
454 462
455 463 return mimeData;
456 464 }
457 465
458 466 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
459 467 {
460 468 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
461 469 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
462 470 && selectionZoneItemUnderCursor) {
463 471
464 472 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
465 473 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
466 474
467 475 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
468 476 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
469 477 .toSize();
470 478
471 479 auto pixmap = QPixmap(zoneSize);
472 480 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
473 481
474 482 return pixmap;
475 483 }
476 484
477 485 return QPixmap();
478 486 }
479 487
480 488 bool VisualizationGraphWidget::isDragAllowed() const
481 489 {
482 490 return true;
483 491 }
484 492
485 493 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
486 494 {
487 495 if (highlighted) {
488 496 plot().setBackground(QBrush(QColor("#BBD5EE")));
489 497 }
490 498 else {
491 499 plot().setBackground(QBrush(Qt::white));
492 500 }
493 501
494 502 plot().update();
495 503 }
496 504
497 505 void VisualizationGraphWidget::addVerticalCursor(double time)
498 506 {
499 507 impl->m_VerticalCursor->setPosition(time);
500 508 impl->m_VerticalCursor->setVisible(true);
501 509
502 510 auto text
503 511 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
504 512 impl->m_VerticalCursor->setLabelText(text);
505 513 }
506 514
507 515 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
508 516 {
509 517 impl->m_VerticalCursor->setAbsolutePosition(position);
510 518 impl->m_VerticalCursor->setVisible(true);
511 519
512 520 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
513 521 auto text
514 522 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
515 523 impl->m_VerticalCursor->setLabelText(text);
516 524 }
517 525
518 526 void VisualizationGraphWidget::removeVerticalCursor()
519 527 {
520 528 impl->m_VerticalCursor->setVisible(false);
521 529 plot().replot(QCustomPlot::rpQueuedReplot);
522 530 }
523 531
524 532 void VisualizationGraphWidget::addHorizontalCursor(double value)
525 533 {
526 534 impl->m_HorizontalCursor->setPosition(value);
527 535 impl->m_HorizontalCursor->setVisible(true);
528 536 impl->m_HorizontalCursor->setLabelText(QString::number(value));
529 537 }
530 538
531 539 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
532 540 {
533 541 impl->m_HorizontalCursor->setAbsolutePosition(position);
534 542 impl->m_HorizontalCursor->setVisible(true);
535 543
536 544 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
537 545 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
538 546 }
539 547
540 548 void VisualizationGraphWidget::removeHorizontalCursor()
541 549 {
542 550 impl->m_HorizontalCursor->setVisible(false);
543 551 plot().replot(QCustomPlot::rpQueuedReplot);
544 552 }
545 553
546 554 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
547 555 {
548 556 Q_UNUSED(event);
549 557
550 558 // Prevents that all variables will be removed from graph when it will be closed
551 559 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
552 560 emit variableAboutToBeRemoved(variableEntry.first);
553 561 }
554 562 }
555 563
556 564 void VisualizationGraphWidget::enterEvent(QEvent *event)
557 565 {
558 566 Q_UNUSED(event);
559 567 impl->m_RenderingDelegate->showGraphOverlay(true);
560 568 }
561 569
562 570 void VisualizationGraphWidget::leaveEvent(QEvent *event)
563 571 {
564 572 Q_UNUSED(event);
565 573 impl->m_RenderingDelegate->showGraphOverlay(false);
566 574
567 575 if (auto parentZone = parentZoneWidget()) {
568 576 parentZone->notifyMouseLeaveGraph(this);
569 577 }
570 578 else {
571 579 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
572 580 }
573 581
574 582 if (impl->m_HoveredZone) {
575 583 impl->m_HoveredZone->setHovered(false);
576 584 impl->m_HoveredZone = nullptr;
577 585 }
578 586 }
579 587
580 588 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
581 589 {
582 590 return *ui->widget;
583 591 }
584 592
585 593 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
586 594 {
587 595 QMenu graphMenu{};
588 596
589 597 // Iterates on variables (unique keys)
590 598 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
591 599 end = impl->m_VariableToPlotMultiMap.cend();
592 600 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
593 601 // 'Remove variable' action
594 602 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
595 603 [ this, var = it->first ]() { removeVariable(var); });
596 604 }
597 605
598 606 if (!impl->m_ZoomStack.isEmpty()) {
599 607 if (!graphMenu.isEmpty()) {
600 608 graphMenu.addSeparator();
601 609 }
602 610
603 611 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
604 612 }
605 613
606 614 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
607 615 if (selectionZoneItem) {
608 616 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
609 617 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
610 618 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
611 619 graphMenu.addSeparator();
612 620 }
613 621
614 622 for (auto zoneAction : zoneActions) {
615 623 auto action = graphMenu.addAction(zoneAction->name());
616 624 QObject::connect(action, &QAction::triggered, [zoneAction, &selectedItems]() {
617 625 zoneAction->execute(selectedItems);
618 626 });
619 627 }
620 628 }
621 629
622 630 if (!graphMenu.isEmpty()) {
623 631 graphMenu.exec(QCursor::pos());
624 632 }
625 633 }
626 634
627 635 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
628 636 {
629 637 qCDebug(LOG_VisualizationGraphWidget())
630 638 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
631 639 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
632 640
633 641 auto graphRange = SqpRange{t1.lower, t1.upper};
634 642 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
635 643
636 644 if (impl->m_DoAcquisition) {
637 645 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
638 646
639 647 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
640 648 end = impl->m_VariableToPlotMultiMap.end();
641 649 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
642 650 variableUnderGraphVector.push_back(it->first);
643 651 }
644 652 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
645 653 !impl->m_IsCalibration);
646 654
647 655 if (!impl->m_IsCalibration) {
648 656 qCDebug(LOG_VisualizationGraphWidget())
649 657 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
650 658 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
651 659 emit synchronize(graphRange, oldGraphRange);
652 660 }
653 661 }
654 662
655 663 auto pos = mapFromGlobal(QCursor::pos());
656 664 auto axisPos = impl->posToAxisPos(pos, plot());
657 665 if (auto parentZone = parentZoneWidget()) {
658 666 if (impl->pointIsInAxisRect(axisPos, plot())) {
659 667 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
660 668 }
661 669 else {
662 670 parentZone->notifyMouseLeaveGraph(this);
663 671 }
664 672 }
665 673 else {
666 674 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
667 675 }
668 676 }
669 677
670 678 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
671 679 {
672 680 impl->m_RenderingDelegate->onMouseDoubleClick(event);
673 681 }
674 682
675 683 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
676 684 {
677 685 // Handles plot rendering when mouse is moving
678 686 impl->m_RenderingDelegate->onMouseMove(event);
679 687
680 688 auto axisPos = impl->posToAxisPos(event->pos(), plot());
681 689
682 690 // Zoom box and zone drawing
683 691 if (impl->m_DrawingZoomRect) {
684 692 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
685 693 }
686 694 else if (impl->m_DrawingZone) {
687 695 impl->m_DrawingZone->setEnd(axisPos.x());
688 696 }
689 697
690 698 // Cursor
691 699 if (auto parentZone = parentZoneWidget()) {
692 700 if (impl->pointIsInAxisRect(axisPos, plot())) {
693 701 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
694 702 }
695 703 else {
696 704 parentZone->notifyMouseLeaveGraph(this);
697 705 }
698 706 }
699 707 else {
700 708 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
701 709 }
702 710
703 711 // Search for the selection zone under the mouse
704 712 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
705 713 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
706 714 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
707 715
708 716 // Sets the appropriate cursor shape
709 717 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
710 718 setCursor(cursorShape);
711 719
712 720 // Manages the hovered zone
713 721 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
714 722 if (impl->m_HoveredZone) {
715 723 impl->m_HoveredZone->setHovered(false);
716 724 }
717 725 selectionZoneItemUnderCursor->setHovered(true);
718 726 impl->m_HoveredZone = selectionZoneItemUnderCursor;
719 727 plot().replot(QCustomPlot::rpQueuedReplot);
720 728 }
721 729 }
722 730 else {
723 731 // There is no zone under the mouse or the interaction mode is not "selection zones"
724 732 if (impl->m_HoveredZone) {
725 733 impl->m_HoveredZone->setHovered(false);
726 734 impl->m_HoveredZone = nullptr;
727 735 }
728 736
729 737 setCursor(Qt::ArrowCursor);
730 738 }
731 739
732 740 impl->m_HasMovedMouse = true;
733 741 VisualizationDragWidget::mouseMoveEvent(event);
734 742 }
735 743
736 744 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
737 745 {
738 746 auto value = event->angleDelta().x() + event->angleDelta().y();
739 747 if (value != 0) {
740 748
741 749 auto direction = value > 0 ? 1.0 : -1.0;
742 750 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
743 751 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
744 752 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
745 753
746 754 auto zoomOrientations = QFlags<Qt::Orientation>{};
747 755 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
748 756 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
749 757
750 758 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
751 759
752 760 if (!isZoomX && !isZoomY) {
753 761 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
754 762 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
755 763
756 764 axis->setRange(axis->range() + diff);
757 765
758 766 if (plot().noAntialiasingOnDrag()) {
759 767 plot().setNotAntialiasedElements(QCP::aeAll);
760 768 }
761 769
762 770 plot().replot(QCustomPlot::rpQueuedReplot);
763 771 }
764 772 }
765 773 }
766 774
767 775 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
768 776 {
769 777 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
770 778 auto isSelectionZoneMode
771 779 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
772 780 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
773 781
774 782 if (!isDragDropClick && isLeftClick) {
775 783 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
776 784 // Starts a zoom box
777 785 impl->startDrawingRect(event->pos(), plot());
778 786 }
779 787 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
780 788 // Starts a new selection zone
781 789 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
782 790 if (!zoneAtPos) {
783 791 impl->startDrawingZone(event->pos(), this);
784 792 }
785 793 }
786 794 }
787 795
788 796 // Allows mouse panning only in default mode
789 797 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
790 798 == SqpApplication::PlotsInteractionMode::None
791 799 && !isDragDropClick);
792 800
793 801 // Allows zone edition only in selection zone mode without drag&drop
794 802 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
795 803
796 804 // Selection / Deselection
797 805 if (isSelectionZoneMode) {
798 806 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
799 807 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
800 808 if (selectionZoneItemUnderCursor && isLeftClick) {
801 809 selectionZoneItemUnderCursor->setAssociatedEditedZones(
802 810 parentVisualizationWidget()->selectionZoneManager().selectedItems());
803 811 }
804 812 else if (!isMultiSelectionClick && isLeftClick) {
805 813 parentVisualizationWidget()->selectionZoneManager().clearSelection();
806 814 }
807 815 else {
808 816 // No selection change
809 817 }
810 818 }
811 819
812 820
813 821 impl->m_HasMovedMouse = false;
814 822 VisualizationDragWidget::mousePressEvent(event);
815 823 }
816 824
817 825 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
818 826 {
819 827 if (impl->m_DrawingZoomRect) {
820 828
821 829 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
822 830 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
823 831
824 832 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
825 833 impl->m_DrawingZoomRect->bottomRight->coords().x()};
826 834
827 835 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
828 836 impl->m_DrawingZoomRect->bottomRight->coords().y()};
829 837
830 838 impl->removeDrawingRect(plot());
831 839
832 840 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
833 841 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
834 842 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
835 843 axisX->setRange(newAxisXRange);
836 844 axisY->setRange(newAxisYRange);
837 845
838 846 plot().replot(QCustomPlot::rpQueuedReplot);
839 847 }
840 848 }
841 849
842 850 impl->endDrawingZone(this);
843 851
844 852 impl->m_IsCalibration = false;
845 853
846 854 // Selection / Deselection
847 855 auto isSelectionZoneMode
848 856 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
849 857 if (isSelectionZoneMode) {
850 858 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
851 859 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
852 860 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
853 861 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
854 862 parentVisualizationWidget()->selectionZoneManager().select(
855 863 {selectionZoneItemUnderCursor});
856 864 }
857 865 else if (!impl->m_HasMovedMouse) {
858 866 parentVisualizationWidget()->selectionZoneManager().setSelected(
859 867 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
860 868 || event->button() == Qt::RightButton);
861 869 }
862 870 }
863 871 else {
864 872 // No selection change
865 873 }
866 874 }
867 875 }
868 876
869 877 void VisualizationGraphWidget::onDataCacheVariableUpdated()
870 878 {
871 879 auto graphRange = ui->widget->xAxis->range();
872 880 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
873 881
874 882 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
875 883 auto variable = variableEntry.first;
876 884 qCDebug(LOG_VisualizationGraphWidget())
877 885 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
878 886 qCDebug(LOG_VisualizationGraphWidget())
879 887 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
880 888 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
881 889 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
882 890 }
883 891 }
884 892 }
885 893
886 894 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
887 895 const SqpRange &range)
888 896 {
889 897 auto it = impl->m_VariableToPlotMultiMap.find(variable);
890 898 if (it != impl->m_VariableToPlotMultiMap.end()) {
891 899 impl->updateData(it->second, variable->dataSeries(), range);
892 900 }
893 901 }
@@ -1,343 +1,354
1 1 #include "Visualization/VisualizationSelectionZoneItem.h"
2 #include "Visualization/VisualizationGraphWidget.h"
2 3
3 4 const QString &DEFAULT_COLOR = QStringLiteral("#E79D41");
4 5
5 6 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
6 7
7 8 QCustomPlot *m_Plot;
8 9 double m_T1 = 0;
9 10 double m_T2 = 0;
10 11 QColor m_Color;
11 12
12 13 bool m_IsEditionEnabled = true;
13 14 double m_MovedOrinalT1 = 0;
14 15 double m_MovedOrinalT2 = 0;
15 16
16 17 QCPItemStraightLine *m_LeftLine;
17 18 QCPItemStraightLine *m_RightLine;
18 19 QCPItemText *m_NameLabelItem = nullptr;
19 20
20 21 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
21 22 EditionMode m_CurrentEditionMode;
22 23
23 24 QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones;
24 25
25 26 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
26 27 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
27 28 {
28 29 }
29 30
30 31 void updatePosition(VisualizationSelectionZoneItem *item)
31 32 {
32 33 item->topLeft->setCoords(m_T1, 0);
33 34 item->bottomRight->setCoords(m_T2, 1);
34 35 }
35 36
36 37 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
37 38 {
38 39 auto distanceLeft = m_LeftLine->selectTest(pos, false);
39 40 auto distanceRight = m_RightLine->selectTest(pos, false);
40 41 auto distance = zoneItem->selectTest(pos, false);
41 42
42 43 if (distanceRight <= distance) {
43 44 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
44 45 }
45 46 else if (distanceLeft <= distance) {
46 47 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
47 48 }
48 49
49 50 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
50 51 }
51 52
52 53 double pixelSizeToAxisXSize(double pixels)
53 54 {
54 55 auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
55 56 return axis->pixelToCoord(pixels) - axis->pixelToCoord(0);
56 57 }
57 58 };
58 59
59 60 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
60 61 : QCPItemRect(plot),
61 62 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
62 63 {
63 64 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
64 65 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
65 66 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
66 67 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
67 68 setSelectable(false);
68 69
69 70 impl->m_RightLine = new QCPItemStraightLine(plot);
70 71 impl->m_RightLine->point1->setParentAnchor(topRight);
71 72 impl->m_RightLine->point2->setParentAnchor(bottomRight);
72 73 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
73 74 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
74 75 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
75 76 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
76 77 impl->m_RightLine->setSelectable(false);
77 78
78 79 impl->m_LeftLine = new QCPItemStraightLine(plot);
79 80 impl->m_LeftLine->point1->setParentAnchor(topLeft);
80 81 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
81 82 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
82 83 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
83 84 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
84 85 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
85 86 impl->m_LeftLine->setSelectable(false);
86 87
87 88 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
88 89 &QCPItemStraightLine::setSelected);
89 90 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
90 91 &QCPItemStraightLine::setSelected);
91 92
92 93 setColor(QColor(DEFAULT_COLOR));
93 94 }
94 95
95 96 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem()
96 97 {
97 98 impl->m_Plot->removeItem(impl->m_RightLine);
98 99 impl->m_Plot->removeItem(impl->m_LeftLine);
99 100 }
100 101
102 VisualizationGraphWidget *VisualizationSelectionZoneItem::parentGraphWidget() const noexcept
103 {
104 auto parent = impl->m_Plot->parentWidget();
105 while (parent != nullptr && !qobject_cast<VisualizationGraphWidget *>(parent)) {
106 parent = parent->parentWidget();
107 }
108
109 return qobject_cast<VisualizationGraphWidget *>(parent);
110 }
111
101 112 void VisualizationSelectionZoneItem::setName(const QString &name)
102 113 {
103 114 if (name.isEmpty() && impl->m_NameLabelItem) {
104 115 impl->m_Plot->removeItem(impl->m_NameLabelItem);
105 116 impl->m_NameLabelItem = nullptr;
106 117 }
107 118 else if (!impl->m_NameLabelItem) {
108 119 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
109 120 impl->m_NameLabelItem->setText(name);
110 121 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
111 122 impl->m_NameLabelItem->setColor(impl->m_Color);
112 123 impl->m_NameLabelItem->position->setParentAnchor(top);
113 124 }
114 125 }
115 126
116 127 QString VisualizationSelectionZoneItem::name() const
117 128 {
118 129 if (!impl->m_NameLabelItem) {
119 130 return QString();
120 131 }
121 132
122 133 return impl->m_NameLabelItem->text();
123 134 }
124 135
125 136 SqpRange VisualizationSelectionZoneItem::range() const
126 137 {
127 138 SqpRange range;
128 139 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
129 140 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
130 141 return range;
131 142 }
132 143
133 144 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
134 145 {
135 146 impl->m_T1 = tstart;
136 147 impl->m_T2 = tend;
137 148 impl->updatePosition(this);
138 149 }
139 150
140 151 void VisualizationSelectionZoneItem::setStart(double tstart)
141 152 {
142 153 impl->m_T1 = tstart;
143 154 impl->updatePosition(this);
144 155 }
145 156
146 157 void VisualizationSelectionZoneItem::setEnd(double tend)
147 158 {
148 159 impl->m_T2 = tend;
149 160 impl->updatePosition(this);
150 161 }
151 162
152 163 void VisualizationSelectionZoneItem::setColor(const QColor &color)
153 164 {
154 165 impl->m_Color = color;
155 166
156 167 auto brushColor = color;
157 168 brushColor.setAlpha(80);
158 169 setBrush(QBrush(brushColor));
159 170 setPen(QPen(Qt::NoPen));
160 171
161 172 auto selectedBrushColor = brushColor;
162 173 selectedBrushColor.setAlpha(150);
163 174 setSelectedBrush(QBrush(selectedBrushColor));
164 175 setSelectedPen(QPen(Qt::NoPen));
165 176
166 177 auto linePen = QPen(color);
167 178 linePen.setStyle(Qt::SolidLine);
168 179 linePen.setWidth(4);
169 180
170 181 auto selectedLinePen = linePen;
171 182 selectedLinePen.setColor(color.darker(120));
172 183 selectedLinePen.setWidth(4);
173 184
174 185 impl->m_LeftLine->setPen(linePen);
175 186 impl->m_RightLine->setPen(linePen);
176 187
177 188 impl->m_LeftLine->setSelectedPen(selectedLinePen);
178 189 impl->m_RightLine->setSelectedPen(selectedLinePen);
179 190 }
180 191
181 192 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
182 193 {
183 194 impl->m_IsEditionEnabled = value;
184 195 setSelectable(value);
185 196 if (!value) {
186 197 setSelected(false);
187 198 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
188 199 }
189 200 }
190 201
191 202 bool VisualizationSelectionZoneItem::isEditionEnabled() const
192 203 {
193 204 return impl->m_IsEditionEnabled;
194 205 }
195 206
196 207 Qt::CursorShape
197 208 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
198 209 {
199 210 auto mode = impl->m_CurrentEditionMode
200 211 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
201 212 ? impl->getEditionMode(position, this)
202 213 : impl->m_CurrentEditionMode;
203 214 switch (mode) {
204 215 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
205 216 return Qt::SizeAllCursor;
206 217 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
207 218 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
208 219 return Qt::SizeHorCursor;
209 220 default:
210 221 return Qt::ArrowCursor;
211 222 }
212 223 }
213 224
214 225 void VisualizationSelectionZoneItem::setHovered(bool value)
215 226 {
216 227 if (value) {
217 228 auto linePen = impl->m_LeftLine->pen();
218 229 linePen.setStyle(Qt::DotLine);
219 230 linePen.setWidth(3);
220 231
221 232 auto selectedLinePen = impl->m_LeftLine->selectedPen();
222 233 ;
223 234 selectedLinePen.setStyle(Qt::DotLine);
224 235 selectedLinePen.setWidth(3);
225 236
226 237 impl->m_LeftLine->setPen(linePen);
227 238 impl->m_RightLine->setPen(linePen);
228 239
229 240 impl->m_LeftLine->setSelectedPen(selectedLinePen);
230 241 impl->m_RightLine->setSelectedPen(selectedLinePen);
231 242 }
232 243 else {
233 244 setColor(impl->m_Color);
234 245 }
235 246 }
236 247
237 248 void VisualizationSelectionZoneItem::setAssociatedEditedZones(
238 249 const QVector<VisualizationSelectionZoneItem *> &associatedZones)
239 250 {
240 251 impl->m_AssociatedEditedZones = associatedZones;
241 252 impl->m_AssociatedEditedZones.removeAll(this);
242 253 }
243 254
244 255 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
245 256 {
246 257 if (isEditionEnabled() && event->button() == Qt::LeftButton) {
247 258 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
248 259
249 260 impl->m_MovedOrinalT1 = impl->m_T1;
250 261 impl->m_MovedOrinalT2 = impl->m_T2;
251 262 for (auto associatedZone : impl->m_AssociatedEditedZones) {
252 263 associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1;
253 264 associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2;
254 265 }
255 266 }
256 267 else {
257 268 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
258 269 event->ignore();
259 270 }
260 271 }
261 272
262 273 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
263 274 {
264 275 if (isEditionEnabled()) {
265 276 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
266 277 auto pixelDiff = event->pos().x() - startPos.x();
267 278 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
268 279
269 280 switch (impl->m_CurrentEditionMode) {
270 281 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
271 282 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
272 283 for (auto associatedZone : impl->m_AssociatedEditedZones) {
273 284 associatedZone->move(pixelDiff);
274 285 }
275 286 break;
276 287 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
277 288 setStart(impl->m_MovedOrinalT1 + diff);
278 289 for (auto associatedZone : impl->m_AssociatedEditedZones) {
279 290 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
280 291 ? associatedZone->resizeLeft(pixelDiff)
281 292 : associatedZone->resizeRight(pixelDiff);
282 293 }
283 294 break;
284 295 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
285 296 setEnd(impl->m_MovedOrinalT2 + diff);
286 297 for (auto associatedZone : impl->m_AssociatedEditedZones) {
287 298 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
288 299 ? associatedZone->resizeRight(pixelDiff)
289 300 : associatedZone->resizeLeft(pixelDiff);
290 301 }
291 302 break;
292 303 default:
293 304 break;
294 305 }
295 306
296 307 for (auto associatedZone : impl->m_AssociatedEditedZones) {
297 308 associatedZone->parentPlot()->replot();
298 309 }
299 310 }
300 311 else {
301 312 event->ignore();
302 313 }
303 314 }
304 315
305 316 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
306 317 {
307 318 if (isEditionEnabled()) {
308 319 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
309 320 }
310 321 else {
311 322 event->ignore();
312 323 }
313 324
314 325 impl->m_AssociatedEditedZones.clear();
315 326 }
316 327
317 328 void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff)
318 329 {
319 330 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
320 331 if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) {
321 332 setStart(impl->m_MovedOrinalT1 + diff);
322 333 }
323 334 else {
324 335 setEnd(impl->m_MovedOrinalT2 + diff);
325 336 }
326 337 }
327 338
328 339 void VisualizationSelectionZoneItem::resizeRight(double pixelDiff)
329 340 {
330 341 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
331 342 if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) {
332 343 setStart(impl->m_MovedOrinalT1 + diff);
333 344 }
334 345 else {
335 346 setEnd(impl->m_MovedOrinalT2 + diff);
336 347 }
337 348 }
338 349
339 350 void VisualizationSelectionZoneItem::move(double pixelDiff)
340 351 {
341 352 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
342 353 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
343 354 }
General Comments 4
Under Review
author

Auto status change to "Under Review"

Approved

Status change > Approved

Approved

Status change > Approved

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