##// END OF EJS Templates
Add action to remove the selected zone with the "del" buttons
trabillard -
r1117:eff9e479c5d5
parent child
Show More
@@ -1,58 +1,63
1 1 #ifndef SCIQLOP_SELECTIONZONEACTION_H
2 2 #define SCIQLOP_SELECTIONZONEACTION_H
3 3
4 4 #include <Common/spimpl.h>
5 5
6 6 #include <QLoggingCategory>
7 7 #include <QObject>
8 8
9 9 #include <functional>
10 10
11 11 class VisualizationSelectionZoneItem;
12 12
13 13 Q_DECLARE_LOGGING_CATEGORY(LOG_SelectionZoneAction)
14 14
15 15 /**
16 16 * @brief The SelectionZoneAction class represents an action on a selection zone in the
17 17 * visualization.
18 18 *
19 19 * The action is a function that will be executed when the slot execute() is called.
20 20 */
21 21 class SelectionZoneAction : public QObject {
22 22
23 23 Q_OBJECT
24 24
25 25 public:
26 26 /// Signature of the function associated to the action
27 27 using ExecuteFunction
28 28 = std::function<void(const QVector<VisualizationSelectionZoneItem *> &item)>;
29 29
30 30 using EnableFunction
31 31 = std::function<bool(const QVector<VisualizationSelectionZoneItem *> &item)>;
32 32
33 33 /**
34 34 * @param name the name of the action, displayed to the user
35 35 * @param fun the function that will be called when the action is executed
36 36 * @sa execute()
37 37 */
38 38 explicit SelectionZoneAction(const QString &name, ExecuteFunction fun);
39 39
40 40 /// Sets the function which determine if the action should be enabled or disabled
41 41 void setEnableFunction(EnableFunction fun);
42 42
43 /// Sets the shortcut displayed by the action.
44 /// Note: The shortcut is only displayed and not active because it is not permanently stored
45 void setDisplayedShortcut(const QKeySequence &shortcut);
46 QKeySequence displayedShortcut() const;
47
43 48 /// The name of the action
44 49 QString name() const noexcept;
45 50
46 51 public slots:
47 52 /// Executes the action
48 53 void execute(const QVector<VisualizationSelectionZoneItem *> &item);
49 54
50 55 /// Returns true if the action is enabled
51 56 bool isEnabled(const QVector<VisualizationSelectionZoneItem *> &item);
52 57
53 58 private:
54 59 class SelectionZoneActionPrivate;
55 60 spimpl::unique_impl_ptr<SelectionZoneActionPrivate> impl;
56 61 };
57 62
58 63 #endif // SCIQLOP_SELECTIONZONEACTION_H
@@ -1,41 +1,52
1 1 #include <Actions/SelectionZoneAction.h>
2 2 #include <Visualization/VisualizationSelectionZoneItem.h>
3 3
4 4 Q_LOGGING_CATEGORY(LOG_SelectionZoneAction, "SelectionZoneAction")
5 5
6 6 struct SelectionZoneAction::SelectionZoneActionPrivate {
7 7 explicit SelectionZoneActionPrivate(const QString &name,
8 8 SelectionZoneAction::ExecuteFunction fun)
9 9 : m_Name{name}, m_Fun{std::move(fun)}
10 10 {
11 11 }
12 12
13 13 QString m_Name;
14 QKeySequence m_DisplayedShortcut;
14 15 SelectionZoneAction::ExecuteFunction m_Fun;
15 16 SelectionZoneAction::EnableFunction m_EnableFun = [](auto zones) { return true; };
16 17 };
17 18
18 19 SelectionZoneAction::SelectionZoneAction(const QString &name, ExecuteFunction fun)
19 20 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, std::move(fun))}
20 21 {
21 22 }
22 23
23 24 void SelectionZoneAction::setEnableFunction(EnableFunction fun)
24 25 {
25 26 impl->m_EnableFun = std::move(fun);
26 27 }
27 28
29 void SelectionZoneAction::setDisplayedShortcut(const QKeySequence &shortcut)
30 {
31 impl->m_DisplayedShortcut = shortcut;
32 }
33
34 QKeySequence SelectionZoneAction::displayedShortcut() const
35 {
36 return impl->m_DisplayedShortcut;
37 }
38
28 39 QString SelectionZoneAction::name() const noexcept
29 40 {
30 41 return impl->m_Name;
31 42 }
32 43
33 44 void SelectionZoneAction::execute(const QVector<VisualizationSelectionZoneItem *> &item)
34 45 {
35 46 impl->m_Fun(item);
36 47 }
37 48
38 49 bool SelectionZoneAction::isEnabled(const QVector<VisualizationSelectionZoneItem *> &item)
39 50 {
40 51 return impl->m_EnableFun(item);
41 52 }
@@ -1,107 +1,109
1 1 #include "Visualization/VisualizationActionManager.h"
2 2 #include "Visualization/VisualizationGraphWidget.h"
3 3 #include "Visualization/VisualizationSelectionZoneItem.h"
4 4
5 5 #include <Actions/ActionsGuiController.h>
6 6 #include <SqpApplication.h>
7 7
8 8 VisualizationActionManager::VisualizationActionManager() {}
9 9
10 10 void VisualizationActionManager::installSelectionZoneActions()
11 11 {
12 12 auto &actionController = sqpApp->actionsGuiController();
13 13
14 actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto zones) {
15 for (auto selectionZone : zones) {
16 if (auto graph = selectionZone->parentGraphWidget()) {
17 graph->removeSelectionZone(selectionZone);
18 }
19 }
20 });
14 auto removeZonesAction
15 = actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto zones) {
16 for (auto selectionZone : zones) {
17 if (auto graph = selectionZone->parentGraphWidget()) {
18 graph->removeSelectionZone(selectionZone);
19 }
20 }
21 });
22 removeZonesAction->setDisplayedShortcut(QKeySequence::Delete);
21 23
22 24 auto alignEnableFuntion = [](auto items) { return items.count() > 1; };
23 25
24 26 // Vertical alignment actions
25 27 auto alignLeftAction
26 28 = actionController.addSectionZoneAction("Align Vertically / Left", [](auto zones) {
27 29 Q_ASSERT(zones.count() > 1);
28 30 auto ref = zones.takeFirst();
29 31 ref->alignZonesVerticallyOnLeft(zones, false);
30 32 });
31 33 alignLeftAction->setEnableFunction(alignEnableFuntion);
32 34
33 35 auto alignLeftBorderAction
34 36 = actionController.addSectionZoneAction("Align Vertically / Left Borders", [](auto zones) {
35 37 Q_ASSERT(zones.count() > 1);
36 38 auto ref = zones.takeFirst();
37 39 ref->alignZonesVerticallyOnLeft(zones, true);
38 40 });
39 41 alignLeftBorderAction->setEnableFunction(alignEnableFuntion);
40 42
41 43 auto alignRightAction
42 44 = actionController.addSectionZoneAction("Align Vertically / Right", [](auto zones) {
43 45 Q_ASSERT(zones.count() > 1);
44 46 auto ref = zones.takeFirst();
45 47 ref->alignZonesVerticallyOnRight(zones, false);
46 48 });
47 49 alignRightAction->setEnableFunction(alignEnableFuntion);
48 50
49 51 auto alignRightBorderAction
50 52 = actionController.addSectionZoneAction("Align Vertically / Right Borders", [](auto zones) {
51 53 Q_ASSERT(zones.count() > 1);
52 54 auto ref = zones.takeFirst();
53 55 ref->alignZonesVerticallyOnRight(zones, true);
54 56 });
55 57 alignRightBorderAction->setEnableFunction(alignEnableFuntion);
56 58
57 59 auto alignLeftAndRightAction = actionController.addSectionZoneAction(
58 60 "Align Vertically / Left and Right", [](auto zones) {
59 61 Q_ASSERT(zones.count() > 1);
60 62 auto ref = zones.takeFirst();
61 63 ref->alignZonesVerticallyOnLeft(zones, false);
62 64 ref->alignZonesVerticallyOnRight(zones, true);
63 65 });
64 66 alignLeftAndRightAction->setEnableFunction(alignEnableFuntion);
65 67
66 68 // Temporal alignment actions
67 69 auto alignLeftTemporallyAction
68 70 = actionController.addSectionZoneAction("Align Temporally / Left", [](auto zones) {
69 71 Q_ASSERT(zones.count() > 1);
70 72 auto ref = zones.takeFirst();
71 73 ref->alignZonesTemporallyOnLeft(zones, false);
72 74 });
73 75 alignLeftTemporallyAction->setEnableFunction(alignEnableFuntion);
74 76
75 77 auto alignLeftBorderTemporallyAction
76 78 = actionController.addSectionZoneAction("Align Temporally / Left Borders", [](auto zones) {
77 79 Q_ASSERT(zones.count() > 1);
78 80 auto ref = zones.takeFirst();
79 81 ref->alignZonesTemporallyOnLeft(zones, true);
80 82 });
81 83 alignLeftBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
82 84
83 85 auto alignRightTemporallyAction
84 86 = actionController.addSectionZoneAction("Align Temporally / Right", [](auto zones) {
85 87 Q_ASSERT(zones.count() > 1);
86 88 auto ref = zones.takeFirst();
87 89 ref->alignZonesTemporallyOnRight(zones, false);
88 90 });
89 91 alignRightTemporallyAction->setEnableFunction(alignEnableFuntion);
90 92
91 93 auto alignRightBorderTemporallyAction
92 94 = actionController.addSectionZoneAction("Align Temporally / Right Borders", [](auto zones) {
93 95 Q_ASSERT(zones.count() > 1);
94 96 auto ref = zones.takeFirst();
95 97 ref->alignZonesTemporallyOnRight(zones, true);
96 98 });
97 99 alignRightBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
98 100
99 101 auto alignLeftAndRightTemporallyAction = actionController.addSectionZoneAction(
100 102 "Align Temporally / Left and Right", [](auto zones) {
101 103 Q_ASSERT(zones.count() > 1);
102 104 auto ref = zones.takeFirst();
103 105 ref->alignZonesTemporallyOnLeft(zones, false);
104 106 ref->alignZonesTemporallyOnRight(zones, true);
105 107 });
106 108 alignLeftAndRightTemporallyAction->setEnableFunction(alignEnableFuntion);
107 109 }
@@ -1,904 +1,911
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 381 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
382 382 {
383 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
384
385 if (impl->m_HoveredZone == selectionZone) {
386 impl->m_HoveredZone = nullptr;
387 setCursor(Qt::ArrowCursor);
388 }
389
383 390 impl->m_SelectionZones.removeAll(selectionZone);
384 391 plot().removeItem(selectionZone);
385 392 plot().replot(QCustomPlot::rpQueuedReplot);
386 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
387 393 }
388 394
389 395 void VisualizationGraphWidget::undoZoom()
390 396 {
391 397 auto zoom = impl->m_ZoomStack.pop();
392 398 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
393 399 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
394 400
395 401 axisX->setRange(zoom.first);
396 402 axisY->setRange(zoom.second);
397 403
398 404 plot().replot(QCustomPlot::rpQueuedReplot);
399 405 }
400 406
401 407 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
402 408 {
403 409 if (visitor) {
404 410 visitor->visit(this);
405 411 }
406 412 else {
407 413 qCCritical(LOG_VisualizationGraphWidget())
408 414 << tr("Can't visit widget : the visitor is null");
409 415 }
410 416 }
411 417
412 418 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
413 419 {
414 420 auto isSpectrogram = [](const auto &variable) {
415 421 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
416 422 };
417 423
418 424 // - A spectrogram series can't be dropped on graph with existing plottables
419 425 // - No data series can be dropped on graph with existing spectrogram series
420 426 return isSpectrogram(variable)
421 427 ? impl->m_VariableToPlotMultiMap.empty()
422 428 : std::none_of(
423 429 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
424 430 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
425 431 }
426 432
427 433 bool VisualizationGraphWidget::contains(const Variable &variable) const
428 434 {
429 435 // Finds the variable among the keys of the map
430 436 auto variablePtr = &variable;
431 437 auto findVariable
432 438 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
433 439
434 440 auto end = impl->m_VariableToPlotMultiMap.cend();
435 441 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
436 442 return it != end;
437 443 }
438 444
439 445 QString VisualizationGraphWidget::name() const
440 446 {
441 447 return impl->m_Name;
442 448 }
443 449
444 450 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
445 451 {
446 452 auto mimeData = new QMimeData;
447 453
448 454 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
449 455 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
450 456 && selectionZoneItemUnderCursor) {
451 457 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
452 458 selectionZoneItemUnderCursor->range()));
453 459 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
454 460 selectionZoneItemUnderCursor->range()));
455 461 }
456 462 else {
457 463 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
458 464
459 465 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
460 466 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
461 467 }
462 468
463 469 return mimeData;
464 470 }
465 471
466 472 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
467 473 {
468 474 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
469 475 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
470 476 && selectionZoneItemUnderCursor) {
471 477
472 478 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
473 479 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
474 480
475 481 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
476 482 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
477 483 .toSize();
478 484
479 485 auto pixmap = QPixmap(zoneSize);
480 486 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
481 487
482 488 return pixmap;
483 489 }
484 490
485 491 return QPixmap();
486 492 }
487 493
488 494 bool VisualizationGraphWidget::isDragAllowed() const
489 495 {
490 496 return true;
491 497 }
492 498
493 499 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
494 500 {
495 501 if (highlighted) {
496 502 plot().setBackground(QBrush(QColor("#BBD5EE")));
497 503 }
498 504 else {
499 505 plot().setBackground(QBrush(Qt::white));
500 506 }
501 507
502 508 plot().update();
503 509 }
504 510
505 511 void VisualizationGraphWidget::addVerticalCursor(double time)
506 512 {
507 513 impl->m_VerticalCursor->setPosition(time);
508 514 impl->m_VerticalCursor->setVisible(true);
509 515
510 516 auto text
511 517 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
512 518 impl->m_VerticalCursor->setLabelText(text);
513 519 }
514 520
515 521 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
516 522 {
517 523 impl->m_VerticalCursor->setAbsolutePosition(position);
518 524 impl->m_VerticalCursor->setVisible(true);
519 525
520 526 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
521 527 auto text
522 528 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
523 529 impl->m_VerticalCursor->setLabelText(text);
524 530 }
525 531
526 532 void VisualizationGraphWidget::removeVerticalCursor()
527 533 {
528 534 impl->m_VerticalCursor->setVisible(false);
529 535 plot().replot(QCustomPlot::rpQueuedReplot);
530 536 }
531 537
532 538 void VisualizationGraphWidget::addHorizontalCursor(double value)
533 539 {
534 540 impl->m_HorizontalCursor->setPosition(value);
535 541 impl->m_HorizontalCursor->setVisible(true);
536 542 impl->m_HorizontalCursor->setLabelText(QString::number(value));
537 543 }
538 544
539 545 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
540 546 {
541 547 impl->m_HorizontalCursor->setAbsolutePosition(position);
542 548 impl->m_HorizontalCursor->setVisible(true);
543 549
544 550 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
545 551 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
546 552 }
547 553
548 554 void VisualizationGraphWidget::removeHorizontalCursor()
549 555 {
550 556 impl->m_HorizontalCursor->setVisible(false);
551 557 plot().replot(QCustomPlot::rpQueuedReplot);
552 558 }
553 559
554 560 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
555 561 {
556 562 Q_UNUSED(event);
557 563
558 564 // Prevents that all variables will be removed from graph when it will be closed
559 565 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
560 566 emit variableAboutToBeRemoved(variableEntry.first);
561 567 }
562 568 }
563 569
564 570 void VisualizationGraphWidget::enterEvent(QEvent *event)
565 571 {
566 572 Q_UNUSED(event);
567 573 impl->m_RenderingDelegate->showGraphOverlay(true);
568 574 }
569 575
570 576 void VisualizationGraphWidget::leaveEvent(QEvent *event)
571 577 {
572 578 Q_UNUSED(event);
573 579 impl->m_RenderingDelegate->showGraphOverlay(false);
574 580
575 581 if (auto parentZone = parentZoneWidget()) {
576 582 parentZone->notifyMouseLeaveGraph(this);
577 583 }
578 584 else {
579 585 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
580 586 }
581 587
582 588 if (impl->m_HoveredZone) {
583 589 impl->m_HoveredZone->setHovered(false);
584 590 impl->m_HoveredZone = nullptr;
585 591 }
586 592 }
587 593
588 594 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
589 595 {
590 596 return *ui->widget;
591 597 }
592 598
593 599 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
594 600 {
595 601 QMenu graphMenu{};
596 602
597 603 // Iterates on variables (unique keys)
598 604 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
599 605 end = impl->m_VariableToPlotMultiMap.cend();
600 606 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
601 607 // 'Remove variable' action
602 608 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
603 609 [ this, var = it->first ]() { removeVariable(var); });
604 610 }
605 611
606 612 if (!impl->m_ZoomStack.isEmpty()) {
607 613 if (!graphMenu.isEmpty()) {
608 614 graphMenu.addSeparator();
609 615 }
610 616
611 617 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
612 618 }
613 619
614 620 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
615 621 if (selectionZoneItem) {
616 622 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
617 623 selectedItems.removeAll(selectionZoneItem);
618 624 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
619 625
620 626 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
621 627 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
622 628 graphMenu.addSeparator();
623 629 }
624 630
625 631 for (auto zoneAction : zoneActions) {
626 632 auto action = graphMenu.addAction(zoneAction->name());
627 633 action->setEnabled(zoneAction->isEnabled(selectedItems));
634 action->setShortcut(zoneAction->displayedShortcut());
628 635 QObject::connect(action, &QAction::triggered,
629 636 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
630 637 }
631 638 }
632 639
633 640 if (!graphMenu.isEmpty()) {
634 641 graphMenu.exec(QCursor::pos());
635 642 }
636 643 }
637 644
638 645 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
639 646 {
640 647 qCDebug(LOG_VisualizationGraphWidget())
641 648 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
642 649 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
643 650
644 651 auto graphRange = SqpRange{t1.lower, t1.upper};
645 652 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
646 653
647 654 if (impl->m_DoAcquisition) {
648 655 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
649 656
650 657 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
651 658 end = impl->m_VariableToPlotMultiMap.end();
652 659 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
653 660 variableUnderGraphVector.push_back(it->first);
654 661 }
655 662 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
656 663 !impl->m_IsCalibration);
657 664
658 665 if (!impl->m_IsCalibration) {
659 666 qCDebug(LOG_VisualizationGraphWidget())
660 667 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
661 668 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
662 669 emit synchronize(graphRange, oldGraphRange);
663 670 }
664 671 }
665 672
666 673 auto pos = mapFromGlobal(QCursor::pos());
667 674 auto axisPos = impl->posToAxisPos(pos, plot());
668 675 if (auto parentZone = parentZoneWidget()) {
669 676 if (impl->pointIsInAxisRect(axisPos, plot())) {
670 677 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
671 678 }
672 679 else {
673 680 parentZone->notifyMouseLeaveGraph(this);
674 681 }
675 682 }
676 683 else {
677 684 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
678 685 }
679 686 }
680 687
681 688 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
682 689 {
683 690 impl->m_RenderingDelegate->onMouseDoubleClick(event);
684 691 }
685 692
686 693 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
687 694 {
688 695 // Handles plot rendering when mouse is moving
689 696 impl->m_RenderingDelegate->onMouseMove(event);
690 697
691 698 auto axisPos = impl->posToAxisPos(event->pos(), plot());
692 699
693 700 // Zoom box and zone drawing
694 701 if (impl->m_DrawingZoomRect) {
695 702 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
696 703 }
697 704 else if (impl->m_DrawingZone) {
698 705 impl->m_DrawingZone->setEnd(axisPos.x());
699 706 }
700 707
701 708 // Cursor
702 709 if (auto parentZone = parentZoneWidget()) {
703 710 if (impl->pointIsInAxisRect(axisPos, plot())) {
704 711 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
705 712 }
706 713 else {
707 714 parentZone->notifyMouseLeaveGraph(this);
708 715 }
709 716 }
710 717 else {
711 718 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
712 719 }
713 720
714 721 // Search for the selection zone under the mouse
715 722 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
716 723 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
717 724 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
718 725
719 726 // Sets the appropriate cursor shape
720 727 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
721 728 setCursor(cursorShape);
722 729
723 730 // Manages the hovered zone
724 731 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
725 732 if (impl->m_HoveredZone) {
726 733 impl->m_HoveredZone->setHovered(false);
727 734 }
728 735 selectionZoneItemUnderCursor->setHovered(true);
729 736 impl->m_HoveredZone = selectionZoneItemUnderCursor;
730 737 plot().replot(QCustomPlot::rpQueuedReplot);
731 738 }
732 739 }
733 740 else {
734 741 // There is no zone under the mouse or the interaction mode is not "selection zones"
735 742 if (impl->m_HoveredZone) {
736 743 impl->m_HoveredZone->setHovered(false);
737 744 impl->m_HoveredZone = nullptr;
738 745 }
739 746
740 747 setCursor(Qt::ArrowCursor);
741 748 }
742 749
743 750 impl->m_HasMovedMouse = true;
744 751 VisualizationDragWidget::mouseMoveEvent(event);
745 752 }
746 753
747 754 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
748 755 {
749 756 auto value = event->angleDelta().x() + event->angleDelta().y();
750 757 if (value != 0) {
751 758
752 759 auto direction = value > 0 ? 1.0 : -1.0;
753 760 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
754 761 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
755 762 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
756 763
757 764 auto zoomOrientations = QFlags<Qt::Orientation>{};
758 765 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
759 766 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
760 767
761 768 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
762 769
763 770 if (!isZoomX && !isZoomY) {
764 771 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
765 772 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
766 773
767 774 axis->setRange(axis->range() + diff);
768 775
769 776 if (plot().noAntialiasingOnDrag()) {
770 777 plot().setNotAntialiasedElements(QCP::aeAll);
771 778 }
772 779
773 780 plot().replot(QCustomPlot::rpQueuedReplot);
774 781 }
775 782 }
776 783 }
777 784
778 785 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
779 786 {
780 787 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
781 788 auto isSelectionZoneMode
782 789 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
783 790 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
784 791
785 792 if (!isDragDropClick && isLeftClick) {
786 793 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
787 794 // Starts a zoom box
788 795 impl->startDrawingRect(event->pos(), plot());
789 796 }
790 797 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
791 798 // Starts a new selection zone
792 799 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
793 800 if (!zoneAtPos) {
794 801 impl->startDrawingZone(event->pos(), this);
795 802 }
796 803 }
797 804 }
798 805
799 806 // Allows mouse panning only in default mode
800 807 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
801 808 == SqpApplication::PlotsInteractionMode::None
802 809 && !isDragDropClick);
803 810
804 811 // Allows zone edition only in selection zone mode without drag&drop
805 812 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
806 813
807 814 // Selection / Deselection
808 815 if (isSelectionZoneMode) {
809 816 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
810 817 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
811 818 if (selectionZoneItemUnderCursor && isLeftClick) {
812 819 selectionZoneItemUnderCursor->setAssociatedEditedZones(
813 820 parentVisualizationWidget()->selectionZoneManager().selectedItems());
814 821 }
815 822 else if (!isMultiSelectionClick && isLeftClick) {
816 823 parentVisualizationWidget()->selectionZoneManager().clearSelection();
817 824 }
818 825 else {
819 826 // No selection change
820 827 }
821 828 }
822 829
823 830
824 831 impl->m_HasMovedMouse = false;
825 832 VisualizationDragWidget::mousePressEvent(event);
826 833 }
827 834
828 835 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
829 836 {
830 837 if (impl->m_DrawingZoomRect) {
831 838
832 839 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
833 840 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
834 841
835 842 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
836 843 impl->m_DrawingZoomRect->bottomRight->coords().x()};
837 844
838 845 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
839 846 impl->m_DrawingZoomRect->bottomRight->coords().y()};
840 847
841 848 impl->removeDrawingRect(plot());
842 849
843 850 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
844 851 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
845 852 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
846 853 axisX->setRange(newAxisXRange);
847 854 axisY->setRange(newAxisYRange);
848 855
849 856 plot().replot(QCustomPlot::rpQueuedReplot);
850 857 }
851 858 }
852 859
853 860 impl->endDrawingZone(this);
854 861
855 862 impl->m_IsCalibration = false;
856 863
857 864 // Selection / Deselection
858 865 auto isSelectionZoneMode
859 866 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
860 867 if (isSelectionZoneMode) {
861 868 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
862 869 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
863 870 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
864 871 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
865 872 parentVisualizationWidget()->selectionZoneManager().select(
866 873 {selectionZoneItemUnderCursor});
867 874 }
868 875 else if (!impl->m_HasMovedMouse) {
869 876 parentVisualizationWidget()->selectionZoneManager().setSelected(
870 877 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
871 878 || event->button() == Qt::RightButton);
872 879 }
873 880 }
874 881 else {
875 882 // No selection change
876 883 }
877 884 }
878 885 }
879 886
880 887 void VisualizationGraphWidget::onDataCacheVariableUpdated()
881 888 {
882 889 auto graphRange = ui->widget->xAxis->range();
883 890 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
884 891
885 892 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
886 893 auto variable = variableEntry.first;
887 894 qCDebug(LOG_VisualizationGraphWidget())
888 895 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
889 896 qCDebug(LOG_VisualizationGraphWidget())
890 897 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
891 898 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
892 899 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
893 900 }
894 901 }
895 902 }
896 903
897 904 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
898 905 const SqpRange &range)
899 906 {
900 907 auto it = impl->m_VariableToPlotMultiMap.find(variable);
901 908 if (it != impl->m_VariableToPlotMultiMap.end()) {
902 909 impl->updateData(it->second, variable->dataSeries(), range);
903 910 }
904 911 }
@@ -1,51 +1,51
1 1 #include "Visualization/VisualizationSelectionZoneManager.h"
2 2 #include "Visualization/VisualizationSelectionZoneItem.h"
3 3
4 4 struct VisualizationSelectionZoneManager::VisualizationSelectionZoneManagerPrivate {
5 5 QVector<VisualizationSelectionZoneItem *> m_SelectedItems;
6 6 };
7 7
8 8 VisualizationSelectionZoneManager::VisualizationSelectionZoneManager()
9 9 : impl{spimpl::make_unique_impl<VisualizationSelectionZoneManagerPrivate>()}
10 10 {
11 11 }
12 12
13 13 void VisualizationSelectionZoneManager::select(
14 14 const QVector<VisualizationSelectionZoneItem *> &items)
15 15 {
16 16 clearSelection();
17 17 for (auto item : items) {
18 18 setSelected(item, true);
19 19 }
20 20 }
21 21
22 22 void VisualizationSelectionZoneManager::setSelected(VisualizationSelectionZoneItem *item,
23 23 bool value)
24 24 {
25 25 if (value != item->selected()) {
26 26 item->setSelected(value);
27 item->parentPlot()->replot();
27 item->parentPlot()->replot(QCustomPlot::rpQueuedReplot);
28 28 }
29 29
30 30 if (!value && impl->m_SelectedItems.contains(item)) {
31 31 impl->m_SelectedItems.removeAll(item);
32 32 }
33 33 else if (value) {
34 34 impl->m_SelectedItems << item;
35 35 }
36 36 }
37 37
38 38 void VisualizationSelectionZoneManager::clearSelection()
39 39 {
40 40 for (auto item : impl->m_SelectedItems) {
41 41 item->setSelected(false);
42 42 item->parentPlot()->replot();
43 43 }
44 44
45 45 impl->m_SelectedItems.clear();
46 46 }
47 47
48 48 QVector<VisualizationSelectionZoneItem *> VisualizationSelectionZoneManager::selectedItems() const
49 49 {
50 50 return impl->m_SelectedItems;
51 51 }
@@ -1,201 +1,215
1 1 #include "Visualization/VisualizationWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationActionManager.h"
4 4 #include "Visualization/VisualizationGraphWidget.h"
5 #include "Visualization/VisualizationSelectionZoneItem.h"
5 6 #include "Visualization/VisualizationSelectionZoneManager.h"
6 7 #include "Visualization/VisualizationTabWidget.h"
7 8 #include "Visualization/VisualizationZoneWidget.h"
8 9 #include "Visualization/operations/FindVariableOperation.h"
9 10 #include "Visualization/operations/GenerateVariableMenuOperation.h"
10 11 #include "Visualization/operations/RemoveVariableOperation.h"
11 12 #include "Visualization/operations/RescaleAxeOperation.h"
12 13 #include "Visualization/qcustomplot.h"
13 14
14 15 #include "ui_VisualizationWidget.h"
15 16
16 17 #include "DragAndDrop/DragDropGuiController.h"
17 18 #include "SqpApplication.h"
18 19
19 20 #include <QToolButton>
20 21
21 22 #include <memory>
22 23
23 24 Q_LOGGING_CATEGORY(LOG_VisualizationWidget, "VisualizationWidget")
24 25
25 26 struct VisualizationWidget::VisualizationWidgetPrivate {
26 27 std::unique_ptr<VisualizationSelectionZoneManager> m_ZoneSelectionManager = nullptr;
27 28 VisualizationActionManager m_ActionManager;
28 29
29 30 VisualizationWidgetPrivate()
30 31 : m_ZoneSelectionManager(std::make_unique<VisualizationSelectionZoneManager>())
31 32 {
32 33 }
33 34 };
34 35
35 36 VisualizationWidget::VisualizationWidget(QWidget *parent)
36 37 : QWidget{parent},
37 38 ui{new Ui::VisualizationWidget},
38 39 impl{spimpl::make_unique_impl<VisualizationWidgetPrivate>()}
39 40 {
40 41 ui->setupUi(this);
41 42
42 43 auto addTabViewButton = new QToolButton{ui->tabWidget};
43 44 addTabViewButton->setText(tr("Add View"));
44 45 addTabViewButton->setCursor(Qt::ArrowCursor);
45 46 ui->tabWidget->setCornerWidget(addTabViewButton, Qt::TopRightCorner);
46 47
47 48 auto enableMinimumCornerWidgetSize = [this](bool enable) {
48 49
49 50 auto tabViewCornerWidget = ui->tabWidget->cornerWidget();
50 51 auto width = enable ? tabViewCornerWidget->width() : 0;
51 52 auto height = enable ? tabViewCornerWidget->height() : 0;
52 53 tabViewCornerWidget->setMinimumHeight(height);
53 54 tabViewCornerWidget->setMinimumWidth(width);
54 55 ui->tabWidget->setMinimumHeight(height);
55 56 ui->tabWidget->setMinimumWidth(width);
56 57 };
57 58
58 59 auto addTabView = [this, enableMinimumCornerWidgetSize]() {
59 60 auto widget = new VisualizationTabWidget{QString{"View %1"}.arg(ui->tabWidget->count() + 1),
60 61 ui->tabWidget};
61 62 auto index = ui->tabWidget->addTab(widget, widget->name());
62 63 if (ui->tabWidget->count() > 0) {
63 64 enableMinimumCornerWidgetSize(false);
64 65 }
65 66 qCInfo(LOG_VisualizationWidget()) << tr("add the tab of index %1").arg(index);
66 67 };
67 68
68 69 auto removeTabView = [this, enableMinimumCornerWidgetSize](int index) {
69 70 if (ui->tabWidget->count() == 1) {
70 71 enableMinimumCornerWidgetSize(true);
71 72 }
72 73
73 74 // Removes widget from tab and closes it
74 75 auto widget = ui->tabWidget->widget(index);
75 76 ui->tabWidget->removeTab(index);
76 77 if (widget) {
77 78 widget->close();
78 79 }
79 80
80 81 qCInfo(LOG_VisualizationWidget()) << tr("remove the tab of index %1").arg(index);
81 82
82 83 };
83 84
84 85 ui->tabWidget->setTabsClosable(true);
85 86
86 87 connect(addTabViewButton, &QToolButton::clicked, addTabView);
87 88 connect(ui->tabWidget, &QTabWidget::tabCloseRequested, removeTabView);
88 89
89 90 sqpApp->dragDropGuiController().addDragDropTabBar(ui->tabWidget->tabBar());
90 91
92 // Actions
91 93 impl->m_ActionManager.installSelectionZoneActions();
92 94
95 auto removeZoneAction = new QAction("Remove selected zone(s)");
96 removeZoneAction->setShortcut(QKeySequence::Delete);
97 connect(removeZoneAction, &QAction::triggered, [this]() {
98 auto selection = impl->m_ZoneSelectionManager->selectedItems();
99 for (auto selectionZone : selection) {
100 if (auto graph = selectionZone->parentGraphWidget()) {
101 graph->removeSelectionZone(selectionZone);
102 }
103 }
104 });
105 addAction(removeZoneAction);
106
93 107 // Adds default tab
94 108 addTabView();
95 109 }
96 110
97 111 VisualizationWidget::~VisualizationWidget()
98 112 {
99 113 sqpApp->dragDropGuiController().removeDragDropTabBar(ui->tabWidget->tabBar());
100 114 delete ui;
101 115 }
102 116
103 117 VisualizationSelectionZoneManager &VisualizationWidget::selectionZoneManager() const
104 118 {
105 119 return *impl->m_ZoneSelectionManager.get();
106 120 }
107 121
108 122 void VisualizationWidget::accept(IVisualizationWidgetVisitor *visitor)
109 123 {
110 124 if (visitor) {
111 125 visitor->visitEnter(this);
112 126
113 127 // Apply visitor for tab children
114 128 for (auto i = 0; i < ui->tabWidget->count(); ++i) {
115 129 // Widgets different from tabs are not visited (no action)
116 130 if (auto visualizationTabWidget
117 131 = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->widget(i))) {
118 132 visualizationTabWidget->accept(visitor);
119 133 }
120 134 }
121 135
122 136 visitor->visitLeave(this);
123 137 }
124 138 else {
125 139 qCCritical(LOG_VisualizationWidget()) << tr("Can't visit widget : the visitor is null");
126 140 }
127 141 }
128 142
129 143 bool VisualizationWidget::canDrop(const Variable &variable) const
130 144 {
131 145 // The main widget can never accomodate a variable
132 146 Q_UNUSED(variable);
133 147 return false;
134 148 }
135 149
136 150 bool VisualizationWidget::contains(const Variable &variable) const
137 151 {
138 152 Q_UNUSED(variable);
139 153 return false;
140 154 }
141 155
142 156 QString VisualizationWidget::name() const
143 157 {
144 158 return QStringLiteral("MainView");
145 159 }
146 160
147 161 void VisualizationWidget::attachVariableMenu(
148 162 QMenu *menu, const QVector<std::shared_ptr<Variable> > &variables) noexcept
149 163 {
150 164 // Menu is generated only if there is a single variable
151 165 if (variables.size() == 1) {
152 166 if (auto variable = variables.first()) {
153 167 // Gets the containers of the variable
154 168 FindVariableOperation findVariableOperation{variable};
155 169 accept(&findVariableOperation);
156 170 auto variableContainers = findVariableOperation.result();
157 171
158 172 // Generates the actions that make it possible to visualize the variable
159 173 GenerateVariableMenuOperation generateVariableMenuOperation{
160 174 menu, variable, std::move(variableContainers)};
161 175 accept(&generateVariableMenuOperation);
162 176 }
163 177 else {
164 178 qCCritical(LOG_VisualizationWidget()) << tr(
165 179 "Can't generate the menu relative to the visualization: the variable is null");
166 180 }
167 181 }
168 182 else {
169 183 qCDebug(LOG_VisualizationWidget())
170 184 << tr("No generation of the menu related to the visualization: several variables are "
171 185 "selected");
172 186 }
173 187 }
174 188
175 189 void VisualizationWidget::onVariableAboutToBeDeleted(std::shared_ptr<Variable> variable) noexcept
176 190 {
177 191 // Calls the operation of removing all references to the variable in the visualization
178 192 auto removeVariableOperation = RemoveVariableOperation{variable};
179 193 accept(&removeVariableOperation);
180 194 }
181 195
182 196 void VisualizationWidget::onRangeChanged(std::shared_ptr<Variable> variable,
183 197 const SqpRange &range) noexcept
184 198 {
185 199 // Calls the operation of rescaling all graph that contrains variable in the visualization
186 200 auto rescaleVariableOperation = RescaleAxeOperation{variable, range};
187 201 accept(&rescaleVariableOperation);
188 202 }
189 203
190 204 void VisualizationWidget::closeEvent(QCloseEvent *event)
191 205 {
192 206 // Closes tabs in the widget
193 207 for (auto i = 0; i < ui->tabWidget->count(); ++i) {
194 208 if (auto visualizationTabWidget
195 209 = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->widget(i))) {
196 210 visualizationTabWidget->close();
197 211 }
198 212 }
199 213
200 214 QWidget::closeEvent(event);
201 215 }
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