##// END OF EJS Templates
Partly reimplemented Graph event handling + bugfix...
jeandet -
r1362:0033c08c242f Pybind11
parent child
Show More
@@ -1,160 +1,169
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 #include <QUuid>
10 10
11 11 #include <memory>
12 12
13 13 #include <Common/spimpl.h>
14 14
15 15 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
16 16
17 17 class QCPRange;
18 18 class QCustomPlot;
19 19 class DateTimeRange;
20 20 class Variable;
21 21 class VisualizationWidget;
22 22 class VisualizationZoneWidget;
23 23 class VisualizationSelectionZoneItem;
24 24
25 25 namespace Ui {
26 26 class VisualizationGraphWidget;
27 27 } // namespace Ui
28 28
29 29 /// Defines options that can be associated with the graph
30 30 enum GraphFlag {
31 31 DisableAll = 0x0, ///< Disables acquisition and synchronization
32 32 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
33 33 /// the acquisition of data
34 34 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
35 35 /// the call to the synchronization of the graphs contained in the
36 36 /// same zone of this graph
37 37 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
38 38 };
39 39
40 40 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
41 41 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
42 42
43 43 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
44 44 Q_OBJECT
45 45
46 46 friend class QCustomPlotSynchronizer;
47 47 friend class VisualizationGraphRenderingDelegate;
48 48
49 49 public:
50 50 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
51 51 virtual ~VisualizationGraphWidget();
52 52
53 53 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
54 54 VisualizationZoneWidget *parentZoneWidget() const noexcept;
55 55
56 56 /// Returns the main VisualizationWidget which contains the graph or nullptr
57 57 VisualizationWidget *parentVisualizationWidget() const;
58 58
59 59 /// Sets graph options
60 60 void setFlags(GraphFlags flags);
61 61
62 62 void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range);
63 63
64 64 /// Removes a variable from the graph
65 65 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
66 66
67 67 /// Returns the list of all variables used in the graph
68 68 std::vector<std::shared_ptr<Variable> > variables() const;
69 69
70 70 /// Sets the y-axis range based on the data of a variable
71 71 void setYRange(std::shared_ptr<Variable> variable);
72 72 DateTimeRange graphRange() const noexcept;
73 73 void setGraphRange(const DateTimeRange &range, bool calibration = false);
74 74 void setAutoRangeOnVariableInitialization(bool value);
75 75
76 76 // Zones
77 77 /// Returns the ranges of all the selection zones on the graph
78 78 QVector<DateTimeRange> selectionZoneRanges() const;
79 79 /// Adds new selection zones in the graph
80 80 void addSelectionZones(const QVector<DateTimeRange> &ranges);
81 81 /// Adds a new selection zone in the graph
82 82 VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range);
83 83 /// Removes the specified selection zone
84 84 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
85 85
86 86 /// Undo the last zoom done with a zoom box
87 87 void undoZoom();
88 88
89 void zoom(double factor, int center, Qt::Orientation orientation);
90 void move(double factor, Qt::Orientation orientation);
91
89 92 // IVisualizationWidget interface
90 93 void accept(IVisualizationWidgetVisitor *visitor) override;
91 94 bool canDrop(const Variable &variable) const override;
92 95 bool contains(const Variable &variable) const override;
93 96 QString name() const override;
94 97
95 98 // VisualisationDragWidget
96 99 QMimeData *mimeData(const QPoint &position) const override;
97 100 QPixmap customDragPixmap(const QPoint &dragPosition) override;
98 101 bool isDragAllowed() const override;
99 102 void highlightForMerge(bool highlighted) override;
100 103
101 104 // Cursors
102 105 /// Adds or moves the vertical cursor at the specified value on the x-axis
103 106 void addVerticalCursor(double time);
104 107 /// Adds or moves the vertical cursor at the specified value on the x-axis
105 108 void addVerticalCursorAtViewportPosition(double position);
106 109 void removeVerticalCursor();
107 110 /// Adds or moves the vertical cursor at the specified value on the y-axis
108 111 void addHorizontalCursor(double value);
109 112 /// Adds or moves the vertical cursor at the specified value on the y-axis
110 113 void addHorizontalCursorAtViewportPosition(double position);
111 114 void removeHorizontalCursor();
112 115
113 116 signals:
114 117 void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange);
115 118 void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange &range);
116 119
117 120 /// Signal emitted when the variable is about to be removed from the graph
118 121 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
119 122 /// Signal emitted when the variable has been added to the graph
120 123 void variableAdded(std::shared_ptr<Variable> var);
121 124
122 125 protected:
123 126 void closeEvent(QCloseEvent *event) override;
124 127 void enterEvent(QEvent *event) override;
125 128 void leaveEvent(QEvent *event) override;
129 void wheelEvent(QWheelEvent * event) override;
130 void mouseMoveEvent(QMouseEvent *event) override;
131 void mouseReleaseEvent(QMouseEvent *event) override;
132 void mousePressEvent(QMouseEvent *event) override;
133 void keyReleaseEvent(QKeyEvent * event) override;
134 void keyPressEvent(QKeyEvent * event) override;
126 135
127 136 QCustomPlot &plot() const noexcept;
128 137
129 138 private:
130 139 Ui::VisualizationGraphWidget *ui;
131 140
132 141 class VisualizationGraphWidgetPrivate;
133 142 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
134
143 QPoint _dragLastPos;
135 144 private slots:
136 145 /// Slot called when right clicking on the graph (displays a menu)
137 146 void onGraphMenuRequested(const QPoint &pos) noexcept;
138 147
139 148 /// Rescale the X axe to range parameter
140 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
149 void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
141 150
142 151 /// Slot called when a mouse double click was made
143 152 void onMouseDoubleClick(QMouseEvent *event) noexcept;
144 153 /// Slot called when a mouse move was made
145 154 void onMouseMove(QMouseEvent *event) noexcept;
146 155 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
147 156 void onMouseWheel(QWheelEvent *event) noexcept;
148 157 /// Slot called when a mouse press was made, to activate the calibration of a graph
149 158 void onMousePress(QMouseEvent *event) noexcept;
150 159 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
151 160 void onMouseRelease(QMouseEvent *event) noexcept;
152 161
153 162 void onDataCacheVariableUpdated();
154 163
155 164 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range);
156 165
157 166 void variableUpdated(QUuid id);
158 167 };
159 168
160 169 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,1045 +1,1232
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/VisualizationMultiZoneSelectionDialog.h"
8 8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 10 #include "Visualization/VisualizationWidget.h"
11 11 #include "Visualization/VisualizationZoneWidget.h"
12 12 #include "ui_VisualizationGraphWidget.h"
13 13
14 14 #include <Actions/ActionsGuiController.h>
15 15 #include <Actions/FilteringAction.h>
16 16 #include <Common/MimeTypesDef.h>
17 17 #include <Data/ArrayData.h>
18 18 #include <Data/IDataSeries.h>
19 19 #include <Data/SpectrogramSeries.h>
20 20 #include <DragAndDrop/DragDropGuiController.h>
21 21 #include <Settings/SqpSettingsDefs.h>
22 22 #include <SqpApplication.h>
23 23 #include <Time/TimeController.h>
24 24 #include <Variable/Variable.h>
25 25 #include <Variable/VariableController2.h>
26 26
27 27 #include <unordered_map>
28 28
29 29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
30 30
31 31 namespace {
32 32
33 33 /// Key pressed to enable drag&drop in all modes
34 34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
35 35
36 36 /// Key pressed to enable zoom on horizontal axis
37 37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
38 38
39 39 /// Key pressed to enable zoom on vertical axis
40 40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
41 41
42 42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
43 43 const auto PAN_SPEED = 5;
44 44
45 45 /// Key pressed to enable a calibration pan
46 46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
47 47
48 48 /// Key pressed to enable multi selection of selection zones
49 49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
50 50
51 51 /// Minimum size for the zoom box, in percentage of the axis range
52 52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
53 53
54 54 /// Format of the dates appearing in the label of a cursor
55 55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
56 56
57 57 } // namespace
58 58
59 59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
60 60
61 61 explicit VisualizationGraphWidgetPrivate(const QString &name)
62 62 : m_Name{name},
63 63 m_Flags{GraphFlag::EnableAll},
64 64 m_IsCalibration{false},
65 65 m_RenderingDelegate{nullptr}
66 66 {
67 67 }
68 68
69 69 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
70 70 const DateTimeRange &range)
71 71 {
72 72 VisualizationGraphHelper::updateData(plottables, variable, range);
73 73
74 74 // Prevents that data has changed to update rendering
75 75 m_RenderingDelegate->onPlotUpdated();
76 76 }
77 77
78 78 QString m_Name;
79 79 // 1 variable -> n qcpplot
80 80 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
81 81 GraphFlags m_Flags;
82 82 bool m_IsCalibration;
83 83 /// Delegate used to attach rendering features to the plot
84 84 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
85 85
86 86 QCPItemRect *m_DrawingZoomRect = nullptr;
87 87 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
88 88
89 89 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
90 90 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
91 91
92 92 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
93 93 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
94 94 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
95 95
96 96 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
97 97
98 98 bool m_VariableAutoRangeOnInit = true;
99 99
100 100 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
101 101 {
102 102 removeDrawingRect(plot);
103 103
104 104 auto axisPos = posToAxisPos(pos, plot);
105 105
106 106 m_DrawingZoomRect = new QCPItemRect{&plot};
107 107 QPen p;
108 108 p.setWidth(2);
109 109 m_DrawingZoomRect->setPen(p);
110 110
111 111 m_DrawingZoomRect->topLeft->setCoords(axisPos);
112 112 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
113 113 }
114 114
115 115 void removeDrawingRect(QCustomPlot &plot)
116 116 {
117 117 if (m_DrawingZoomRect) {
118 118 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
119 119 m_DrawingZoomRect = nullptr;
120 120 plot.replot(QCustomPlot::rpQueuedReplot);
121 121 }
122 122 }
123 123
124 124 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
125 125 {
126 126 endDrawingZone(graph);
127 127
128 128 auto axisPos = posToAxisPos(pos, graph->plot());
129 129
130 130 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
131 131 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
132 132 m_DrawingZone->setEditionEnabled(false);
133 133 }
134 134
135 135 void endDrawingZone(VisualizationGraphWidget *graph)
136 136 {
137 137 if (m_DrawingZone) {
138 138 auto drawingZoneRange = m_DrawingZone->range();
139 139 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
140 140 m_DrawingZone->setEditionEnabled(true);
141 141 addSelectionZone(m_DrawingZone);
142 142 }
143 143 else {
144 144 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
145 145 }
146 146
147 147 graph->plot().replot(QCustomPlot::rpQueuedReplot);
148 148 m_DrawingZone = nullptr;
149 149 }
150 150 }
151 151
152 152 void setSelectionZonesEditionEnabled(bool value)
153 153 {
154 154 for (auto s : m_SelectionZones) {
155 155 s->setEditionEnabled(value);
156 156 }
157 157 }
158 158
159 159 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
160 160
161 161 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
162 162 const QCustomPlot &plot) const
163 163 {
164 164 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
165 165 auto minDistanceToZone = -1;
166 166 for (auto zone : m_SelectionZones) {
167 167 auto distanceToZone = zone->selectTest(pos, false);
168 168 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
169 169 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
170 170 selectionZoneItemUnderCursor = zone;
171 171 }
172 172 }
173 173
174 174 return selectionZoneItemUnderCursor;
175 175 }
176 176
177 177 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
178 178 const QCustomPlot &plot) const
179 179 {
180 180 QVector<VisualizationSelectionZoneItem *> zones;
181 181 for (auto zone : m_SelectionZones) {
182 182 auto distanceToZone = zone->selectTest(pos, false);
183 183 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
184 184 zones << zone;
185 185 }
186 186 }
187 187
188 188 return zones;
189 189 }
190 190
191 191 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
192 192 {
193 193 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
194 194 zone->moveToTop();
195 195 m_SelectionZones.removeAll(zone);
196 196 m_SelectionZones.append(zone);
197 197 }
198 198 }
199 199
200 200 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
201 201 {
202 202 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
203 203 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
204 204 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
205 205 }
206 206
207 207 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
208 208 {
209 209 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
210 210 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
211 211 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
212 212 }
213 213 };
214 214
215 215 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
216 216 : VisualizationDragWidget{parent},
217 217 ui{new Ui::VisualizationGraphWidget},
218 218 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
219 219 {
220 220 ui->setupUi(this);
221 221
222 222 // 'Close' options : widget is deleted when closed
223 223 setAttribute(Qt::WA_DeleteOnClose);
224 224
225 225 // The delegate must be initialized after the ui as it uses the plot
226 226 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
227 227
228 228 // Init the cursors
229 229 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
230 230 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
231 231 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
232 232 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
233 233
234 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
235 connect(ui->widget, &QCustomPlot::mouseRelease, this,
236 &VisualizationGraphWidget::onMouseRelease);
237 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
238 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
239 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
240 &VisualizationGraphWidget::onMouseDoubleClick);
241 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
242 &QCPAxis::rangeChanged),
243 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
234 // ↓ swhitch to this ASAP, VisualizationGraphWidget should intercept all UI events
235 this->setFocusPolicy(Qt::WheelFocus);
236 ui->widget->setAttribute(Qt::WA_TransparentForMouseEvents);
237 // connect(ui->widget, &QCustomPlot::mousePress, this,
238 // &VisualizationGraphWidget::onMousePress); connect(ui->widget, &QCustomPlot::mouseRelease,
239 // this,
240 // &VisualizationGraphWidget::onMouseRelease);
241 // connect(ui->widget, &QCustomPlot::mouseMove, this,
242 // &VisualizationGraphWidget::onMouseMove); connect(ui->widget, &QCustomPlot::mouseWheel,
243 // this, &VisualizationGraphWidget::onMouseWheel); connect(ui->widget,
244 // &QCustomPlot::mouseDoubleClick, this,
245 // &VisualizationGraphWidget::onMouseDoubleClick);
246 // connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange
247 // &)>(
248 // &QCPAxis::rangeChanged),
249 // this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
244 250
245 251 // Activates menu when right clicking on the graph
246 252 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
247 253 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
248 254 &VisualizationGraphWidget::onGraphMenuRequested);
249 255
250 256 //@TODO implement this :)
251 // connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
252 // &VariableController::onRequestDataLoading);
257 // connect(this, &VisualizationGraphWidget::requestDataLoading,
258 // &sqpApp->variableController(),
259 // &VariableController::onRequestDataLoading);
253 260
254 // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this,
255 // &VisualizationGraphWidget::onUpdateVarDisplaying);
261 // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this,
262 // &VisualizationGraphWidget::onUpdateVarDisplaying);
256 263
257 264 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
258 265 plot().setPlottingHint(QCP::phFastPolylines, true);
259 266 }
260 267
261 268
262 269 VisualizationGraphWidget::~VisualizationGraphWidget()
263 270 {
264 271 delete ui;
265 272 }
266 273
267 274 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
268 275 {
269 276 auto parent = parentWidget();
270 277 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
271 278 parent = parent->parentWidget();
272 279 }
273 280
274 281 return qobject_cast<VisualizationZoneWidget *>(parent);
275 282 }
276 283
277 284 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
278 285 {
279 286 auto parent = parentWidget();
280 287 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
281 288 parent = parent->parentWidget();
282 289 }
283 290
284 291 return qobject_cast<VisualizationWidget *>(parent);
285 292 }
286 293
287 294 void VisualizationGraphWidget::setFlags(GraphFlags flags)
288 295 {
289 296 impl->m_Flags = std::move(flags);
290 297 }
291 298
292 299 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
293 300 {
294 301 // Uses delegate to create the qcpplot components according to the variable
295 302 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
296 303
297 304 // Sets graph properties
298 305 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
299 306
300 307 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
301 308
302 309 // If the variable already has its data loaded, load its units and its range in the graph
303 310 if (variable->dataSeries() != nullptr) {
304 311 impl->m_RenderingDelegate->setAxesUnits(*variable);
305 312 this->setFlags(GraphFlag::DisableAll);
306 313 setGraphRange(range);
307 314 this->setFlags(GraphFlag::EnableAll);
308 315 }
309 316 //@TODO this is bad! when variable is moved to another graph it still fires
310 317 // even if this has been deleted
311 connect(variable.get(),&Variable::updated,this, &VisualizationGraphWidget::variableUpdated);
312 this->onUpdateVarDisplaying(variable,range);//My bullshit
318 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
319 this->onUpdateVarDisplaying(variable, range); // My bullshit
313 320 emit variableAdded(variable);
314 321 }
315 322
316 323 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
317 324 {
318 325 // Each component associated to the variable :
319 326 // - is removed from qcpplot (which deletes it)
320 327 // - is no longer referenced in the map
321 328 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
322 329 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
323 330 emit variableAboutToBeRemoved(variable);
324 331
325 332 auto &plottablesMap = variableIt->second;
326 333
327 334 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
328 335 plottableIt != plottableEnd;) {
329 336 ui->widget->removePlottable(plottableIt->second);
330 337 plottableIt = plottablesMap.erase(plottableIt);
331 338 }
332 339
333 340 impl->m_VariableToPlotMultiMap.erase(variableIt);
334 341 }
335 342
336 343 // Updates graph
337 ui->widget->replot();
344 ui->widget->replot(QCustomPlot::rpQueuedReplot);
338 345 }
339 346
340 347 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
341 348 {
342 349 auto variables = std::vector<std::shared_ptr<Variable> >{};
343 350 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
344 351 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
345 variables.push_back (it->first);
352 variables.push_back(it->first);
346 353 }
347 354
348 355 return variables;
349 356 }
350 357
351 358 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
352 359 {
353 360 if (!variable) {
354 361 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
355 362 return;
356 363 }
357 364
358 365 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
359 366 }
360 367
361 368 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
362 369 {
363 370 auto graphRange = ui->widget->xAxis->range();
364 371 return DateTimeRange{graphRange.lower, graphRange.upper};
365 372 }
366 373
367 374 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration)
368 375 {
369 376 if (calibration) {
370 377 impl->m_IsCalibration = true;
371 378 }
372 379
373 380 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
374 ui->widget->replot();
381 ui->widget->replot(QCustomPlot::rpQueuedReplot);
375 382
376 383 if (calibration) {
377 384 impl->m_IsCalibration = false;
378 385 }
379 386 }
380 387
381 388 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
382 389 {
383 390 impl->m_VariableAutoRangeOnInit = value;
384 391 }
385 392
386 393 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
387 394 {
388 395 QVector<DateTimeRange> ranges;
389 396 for (auto zone : impl->m_SelectionZones) {
390 397 ranges << zone->range();
391 398 }
392 399
393 400 return ranges;
394 401 }
395 402
396 403 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
397 404 {
398 405 for (const auto &range : ranges) {
399 406 // note: ownership is transfered to QCustomPlot
400 407 auto zone = new VisualizationSelectionZoneItem(&plot());
401 408 zone->setRange(range.m_TStart, range.m_TEnd);
402 409 impl->addSelectionZone(zone);
403 410 }
404 411
405 412 plot().replot(QCustomPlot::rpQueuedReplot);
406 413 }
407 414
408 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
409 const DateTimeRange &range)
415 VisualizationSelectionZoneItem *
416 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
410 417 {
411 418 // note: ownership is transfered to QCustomPlot
412 419 auto zone = new VisualizationSelectionZoneItem(&plot());
413 420 zone->setName(name);
414 421 zone->setRange(range.m_TStart, range.m_TEnd);
415 422 impl->addSelectionZone(zone);
416 423
417 424 plot().replot(QCustomPlot::rpQueuedReplot);
418 425
419 426 return zone;
420 427 }
421 428
422 429 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
423 430 {
424 431 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
425 432
426 433 if (impl->m_HoveredZone == selectionZone) {
427 434 impl->m_HoveredZone = nullptr;
428 435 setCursor(Qt::ArrowCursor);
429 436 }
430 437
431 438 impl->m_SelectionZones.removeAll(selectionZone);
432 439 plot().removeItem(selectionZone);
433 440 plot().replot(QCustomPlot::rpQueuedReplot);
434 441 }
435 442
436 443 void VisualizationGraphWidget::undoZoom()
437 444 {
438 445 auto zoom = impl->m_ZoomStack.pop();
439 446 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
440 447 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
441 448
442 449 axisX->setRange(zoom.first);
443 450 axisY->setRange(zoom.second);
444 451
445 452 plot().replot(QCustomPlot::rpQueuedReplot);
446 453 }
447 454
455 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation)
456
457 {
458 auto oldRange = ui->widget->xAxis->range();
459 QCPAxis *axis = ui->widget->axisRect()->rangeZoomAxis(orientation);
460 axis->scaleRange(factor, axis->pixelToCoord(center));
461 if (orientation == Qt::Horizontal)
462 onRangeChanged(axis->range(), oldRange);
463 ui->widget->replot(QCustomPlot::rpQueuedReplot);
464 }
465
466 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation)
467 {
468 auto oldRange = ui->widget->xAxis->range();
469 QCPAxis *axis = ui->widget->axisRect()->rangeDragAxis(orientation);
470 if (ui->widget->xAxis->scaleType() == QCPAxis::stLinear) {
471 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
472 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
473 }
474 else if (ui->widget->xAxis->scaleType() == QCPAxis::stLogarithmic) {
475 int start = 0, stop = 0;
476 double diff = 0.;
477 if (factor > 0.0) {
478 stop = this->width() * factor / 10;
479 start = 2 * this->width() * factor / 10;
480 }
481 if (factor < 0.0) {
482 factor *= -1.0;
483 start = this->width() * factor / 10;
484 stop = 2 * this->width() * factor / 10;
485 }
486 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
487 axis->setRange(ui->widget->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
488 ui->widget->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
489 }
490 if (orientation == Qt::Horizontal)
491 onRangeChanged(axis->range(), oldRange);
492 ui->widget->replot(QCustomPlot::rpQueuedReplot);
493 }
494
448 495 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
449 496 {
450 497 if (visitor) {
451 498 visitor->visit(this);
452 499 }
453 500 else {
454 501 qCCritical(LOG_VisualizationGraphWidget())
455 502 << tr("Can't visit widget : the visitor is null");
456 503 }
457 504 }
458 505
459 506 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
460 507 {
461 508 auto isSpectrogram = [](const auto &variable) {
462 509 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
463 510 };
464 511
465 512 // - A spectrogram series can't be dropped on graph with existing plottables
466 513 // - No data series can be dropped on graph with existing spectrogram series
467 514 return isSpectrogram(variable)
468 515 ? impl->m_VariableToPlotMultiMap.empty()
469 516 : std::none_of(
470 517 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
471 518 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
472 519 }
473 520
474 521 bool VisualizationGraphWidget::contains(const Variable &variable) const
475 522 {
476 523 // Finds the variable among the keys of the map
477 524 auto variablePtr = &variable;
478 525 auto findVariable
479 526 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
480 527
481 528 auto end = impl->m_VariableToPlotMultiMap.cend();
482 529 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
483 530 return it != end;
484 531 }
485 532
486 533 QString VisualizationGraphWidget::name() const
487 534 {
488 535 return impl->m_Name;
489 536 }
490 537
491 538 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
492 539 {
493 540 auto mimeData = new QMimeData;
494 541
495 542 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
496 543 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
497 544 && selectionZoneItemUnderCursor) {
498 545 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
499 546 selectionZoneItemUnderCursor->range()));
500 547 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
501 548 selectionZoneItemUnderCursor->range()));
502 549 }
503 550 else {
504 551 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
505 552
506 553 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
507 554 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
508 555 }
509 556
510 557 return mimeData;
511 558 }
512 559
513 560 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
514 561 {
515 562 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
516 563 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
517 564 && selectionZoneItemUnderCursor) {
518 565
519 566 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
520 567 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
521 568
522 569 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
523 570 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
524 571 .toSize();
525 572
526 573 auto pixmap = QPixmap(zoneSize);
527 574 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
528 575
529 576 return pixmap;
530 577 }
531 578
532 579 return QPixmap();
533 580 }
534 581
535 582 bool VisualizationGraphWidget::isDragAllowed() const
536 583 {
537 584 return true;
538 585 }
539 586
540 587 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
541 588 {
542 589 if (highlighted) {
543 590 plot().setBackground(QBrush(QColor("#BBD5EE")));
544 591 }
545 592 else {
546 593 plot().setBackground(QBrush(Qt::white));
547 594 }
548 595
549 596 plot().update();
550 597 }
551 598
552 599 void VisualizationGraphWidget::addVerticalCursor(double time)
553 600 {
554 601 impl->m_VerticalCursor->setPosition(time);
555 602 impl->m_VerticalCursor->setVisible(true);
556 603
557 604 auto text
558 605 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
559 606 impl->m_VerticalCursor->setLabelText(text);
560 607 }
561 608
562 609 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
563 610 {
564 611 impl->m_VerticalCursor->setAbsolutePosition(position);
565 612 impl->m_VerticalCursor->setVisible(true);
566 613
567 614 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
568 615 auto text
569 616 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
570 617 impl->m_VerticalCursor->setLabelText(text);
571 618 }
572 619
573 620 void VisualizationGraphWidget::removeVerticalCursor()
574 621 {
575 622 impl->m_VerticalCursor->setVisible(false);
576 623 plot().replot(QCustomPlot::rpQueuedReplot);
577 624 }
578 625
579 626 void VisualizationGraphWidget::addHorizontalCursor(double value)
580 627 {
581 628 impl->m_HorizontalCursor->setPosition(value);
582 629 impl->m_HorizontalCursor->setVisible(true);
583 630 impl->m_HorizontalCursor->setLabelText(QString::number(value));
584 631 }
585 632
586 633 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
587 634 {
588 635 impl->m_HorizontalCursor->setAbsolutePosition(position);
589 636 impl->m_HorizontalCursor->setVisible(true);
590 637
591 638 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
592 639 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
593 640 }
594 641
595 642 void VisualizationGraphWidget::removeHorizontalCursor()
596 643 {
597 644 impl->m_HorizontalCursor->setVisible(false);
598 645 plot().replot(QCustomPlot::rpQueuedReplot);
599 646 }
600 647
601 648 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
602 649 {
603 650 Q_UNUSED(event);
604 651
605 652 for (auto i : impl->m_SelectionZones) {
606 653 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
607 654 }
608 655
609 656 // Prevents that all variables will be removed from graph when it will be closed
610 657 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
611 658 emit variableAboutToBeRemoved(variableEntry.first);
612 659 }
613 660 }
614 661
615 662 void VisualizationGraphWidget::enterEvent(QEvent *event)
616 663 {
617 664 Q_UNUSED(event);
618 665 impl->m_RenderingDelegate->showGraphOverlay(true);
619 666 }
620 667
621 668 void VisualizationGraphWidget::leaveEvent(QEvent *event)
622 669 {
623 670 Q_UNUSED(event);
624 671 impl->m_RenderingDelegate->showGraphOverlay(false);
625 672
626 673 if (auto parentZone = parentZoneWidget()) {
627 674 parentZone->notifyMouseLeaveGraph(this);
628 675 }
629 676 else {
630 677 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
631 678 }
632 679
633 680 if (impl->m_HoveredZone) {
634 681 impl->m_HoveredZone->setHovered(false);
635 682 impl->m_HoveredZone = nullptr;
636 683 }
637 684 }
638 685
686 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
687 {
688 double factor;
689 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
690 if (event->modifiers() == Qt::ControlModifier) {
691 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
692 {
693 setCursor(Qt::SizeVerCursor);
694 factor = pow(ui->widget->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
695 zoom(factor, event->pos().y(), Qt::Vertical);
696 }
697 }
698 else if (event->modifiers() == Qt::ShiftModifier) {
699 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
700 {
701 setCursor(Qt::SizeHorCursor);
702 factor = pow(ui->widget->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
703 zoom(factor, event->pos().x(), Qt::Horizontal);
704 }
705 }
706 else {
707 move(wheelSteps, Qt::Horizontal);
708 }
709 QWidget::wheelEvent(event);
710 }
711
712 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
713 {
714 if (axis->scaleType() == QCPAxis::stLinear)
715 {
716 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
717 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
718 }
719 else
720 {
721 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
722 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
723 }
724 }
725
726 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
727 {
728 if (event->buttons() & Qt::LeftButton) {
729 auto currentPos = event->pos();
730 auto xAxis = ui->widget->axisRect()->rangeDragAxis(Qt::Horizontal);
731 auto yAxis = ui->widget->axisRect()->rangeDragAxis(Qt::Vertical);
732 auto oldXRange = xAxis->range();
733 xAxis->setRange(_pixDistanceToRange(_dragLastPos.x(), currentPos.x(), xAxis));
734 yAxis->setRange(_pixDistanceToRange(_dragLastPos.y(), currentPos.y(), yAxis));
735 onRangeChanged(oldXRange, xAxis->range());
736 ui->widget->replot(QCustomPlot::rpQueuedReplot);
737 _dragLastPos = currentPos;
738 }
739 QWidget::mouseMoveEvent(event);
740 }
741
742 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
743 {
744 setCursor(Qt::ArrowCursor);
745 QWidget::mouseReleaseEvent(event);
746 }
747
748 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
749 {
750 if (event->button() == Qt::LeftButton) {
751 if (event->modifiers() == Qt::ControlModifier) {
752 }
753 else {
754 setCursor(Qt::ClosedHandCursor);
755 this->_dragLastPos= event->pos();
756 }
757 }
758 QWidget::mousePressEvent(event);
759 }
760
761 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
762 {
763 switch (event->key()) {
764 case Qt::Key_Control:
765 event->accept();
766 break;
767 case Qt::Key_Shift:
768 event->accept();
769 break;
770 default:
771 QWidget::keyReleaseEvent(event);
772 break;
773 }
774 setCursor(Qt::ArrowCursor);
775 }
776
777 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
778 {
779 switch (event->key()) {
780 case Qt::Key_Control:
781 setCursor(Qt::CrossCursor);
782 break;
783 case Qt::Key_Shift:
784 break;
785 case Qt::Key_M:
786 ui->widget->rescaleAxes();
787 ui->widget->replot(QCustomPlot::rpQueuedReplot);
788 break;
789 case Qt::Key_Left:
790 if (event->modifiers() != Qt::ControlModifier) {
791 move(-0.1, Qt::Horizontal);
792 }
793 else {
794 zoom(2, this->width() / 2, Qt::Horizontal);
795 }
796 break;
797 case Qt::Key_Right:
798 if (event->modifiers() != Qt::ControlModifier) {
799 move(0.1, Qt::Horizontal);
800 }
801 else {
802 zoom(0.5, this->width() / 2, Qt::Horizontal);
803 }
804 break;
805 case Qt::Key_Up:
806 if (event->modifiers() != Qt::ControlModifier) {
807 move(0.1, Qt::Vertical);
808 }
809 else {
810 zoom(0.5, this->height() / 2, Qt::Vertical);
811 }
812 break;
813 case Qt::Key_Down:
814 if (event->modifiers() != Qt::ControlModifier) {
815 move(-0.1, Qt::Vertical);
816 }
817 else {
818 zoom(2, this->height() / 2, Qt::Vertical);
819 }
820 break;
821 default:
822 QWidget::keyPressEvent(event);
823 break;
824 }
825 }
826
639 827 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
640 828 {
641 829 return *ui->widget;
642 830 }
643 831
644 832 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
645 833 {
646 834 QMenu graphMenu{};
647 835
648 836 // Iterates on variables (unique keys)
649 837 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
650 838 end = impl->m_VariableToPlotMultiMap.cend();
651 839 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
652 840 // 'Remove variable' action
653 841 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
654 [ this, var = it->first ]() { removeVariable(var); });
842 [this, var = it->first]() { removeVariable(var); });
655 843 }
656 844
657 845 if (!impl->m_ZoomStack.isEmpty()) {
658 846 if (!graphMenu.isEmpty()) {
659 847 graphMenu.addSeparator();
660 848 }
661 849
662 850 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
663 851 }
664 852
665 853 // Selection Zone Actions
666 854 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
667 855 if (selectionZoneItem) {
668 856 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
669 857 selectedItems.removeAll(selectionZoneItem);
670 858 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
671 859
672 860 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
673 861 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
674 862 graphMenu.addSeparator();
675 863 }
676 864
677 865 QHash<QString, QMenu *> subMenus;
678 866 QHash<QString, bool> subMenusEnabled;
679 867 QHash<QString, FilteringAction *> filteredMenu;
680 868
681 869 for (auto zoneAction : zoneActions) {
682 870
683 871 auto isEnabled = zoneAction->isEnabled(selectedItems);
684 872
685 873 auto menu = &graphMenu;
686 874 QString menuPath;
687 875 for (auto subMenuName : zoneAction->subMenuList()) {
688 876 menuPath += '/';
689 877 menuPath += subMenuName;
690 878
691 879 if (!subMenus.contains(menuPath)) {
692 880 menu = menu->addMenu(subMenuName);
693 881 subMenus[menuPath] = menu;
694 882 subMenusEnabled[menuPath] = isEnabled;
695 883 }
696 884 else {
697 885 menu = subMenus.value(menuPath);
698 886 if (isEnabled) {
699 887 // The sub menu is enabled if at least one of its actions is enabled
700 888 subMenusEnabled[menuPath] = true;
701 889 }
702 890 }
703 891 }
704 892
705 893 FilteringAction *filterAction = nullptr;
706 894 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
707 895 filterAction = filteredMenu.value(menuPath);
708 896 if (!filterAction) {
709 897 filterAction = new FilteringAction{this};
710 898 filteredMenu[menuPath] = filterAction;
711 899 menu->addAction(filterAction);
712 900 }
713 901 }
714 902
715 903 auto action = menu->addAction(zoneAction->name());
716 904 action->setEnabled(isEnabled);
717 905 action->setShortcut(zoneAction->displayedShortcut());
718 906 QObject::connect(action, &QAction::triggered,
719 907 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
720 908
721 909 if (filterAction && zoneAction->isFilteringAllowed()) {
722 910 filterAction->addActionToFilter(action);
723 911 }
724 912 }
725 913
726 914 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
727 915 it.value()->setEnabled(subMenusEnabled[it.key()]);
728 916 }
729 917 }
730 918
731 919 if (!graphMenu.isEmpty()) {
732 920 graphMenu.exec(QCursor::pos());
733 921 }
734 922 }
735 923
736 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
924 void VisualizationGraphWidget::onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange)
737 925 {
738 auto graphRange = DateTimeRange{t1.lower, t1.upper};
739 auto oldGraphRange = DateTimeRange{t2.lower, t2.upper};
926 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
927 auto oldGraphRange = DateTimeRange{oldRange.lower, oldRange.upper};
740 928
741 929 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
742 930 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
743 931 end = impl->m_VariableToPlotMultiMap.end();
744 932 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
745 933 sqpApp->variableController().asyncChangeRange(it->first, graphRange);
746 934 }
747 935 }
748 936
749 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration)
750 {
751 emit synchronize(graphRange, oldGraphRange);
752 }
753
754 auto pos = mapFromGlobal(QCursor::pos());
755 auto axisPos = impl->posToAxisPos(pos, plot());
756 if (auto parentZone = parentZoneWidget()) {
757 if (impl->pointIsInAxisRect(axisPos, plot())) {
758 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
759 }
760 else {
761 parentZone->notifyMouseLeaveGraph(this);
762 }
763 }
937 // if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration)
938 // {
939 // emit synchronize(graphRange, oldGraphRange);
940 // }
941
942 // auto pos = mapFromGlobal(QCursor::pos());
943 // auto axisPos = impl->posToAxisPos(pos, plot());
944 // if (auto parentZone = parentZoneWidget()) {
945 // if (impl->pointIsInAxisRect(axisPos, plot())) {
946 // parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
947 // }
948 // else {
949 // parentZone->notifyMouseLeaveGraph(this);
950 // }
951 // }
764 952
765 953 // Quits calibration
766 impl->m_IsCalibration = false;
954 // impl->m_IsCalibration = false;
767 955 }
768 956
769 957 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
770 958 {
771 959 impl->m_RenderingDelegate->onMouseDoubleClick(event);
772 960 }
773 961
774 962 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
775 963 {
776 964 // Handles plot rendering when mouse is moving
777 965 impl->m_RenderingDelegate->onMouseMove(event);
778 966
779 967 auto axisPos = impl->posToAxisPos(event->pos(), plot());
780 968
781 969 // Zoom box and zone drawing
782 970 if (impl->m_DrawingZoomRect) {
783 971 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
784 972 }
785 973 else if (impl->m_DrawingZone) {
786 974 impl->m_DrawingZone->setEnd(axisPos.x());
787 975 }
788 976
789 977 // Cursor
790 978 if (auto parentZone = parentZoneWidget()) {
791 979 if (impl->pointIsInAxisRect(axisPos, plot())) {
792 980 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
793 981 }
794 982 else {
795 983 parentZone->notifyMouseLeaveGraph(this);
796 984 }
797 985 }
798 986
799 987 // Search for the selection zone under the mouse
800 988 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
801 989 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
802 990 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
803 991
804 992 // Sets the appropriate cursor shape
805 993 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
806 994 setCursor(cursorShape);
807 995
808 996 // Manages the hovered zone
809 997 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
810 998 if (impl->m_HoveredZone) {
811 999 impl->m_HoveredZone->setHovered(false);
812 1000 }
813 1001 selectionZoneItemUnderCursor->setHovered(true);
814 1002 impl->m_HoveredZone = selectionZoneItemUnderCursor;
815 1003 plot().replot(QCustomPlot::rpQueuedReplot);
816 1004 }
817 1005 }
818 1006 else {
819 1007 // There is no zone under the mouse or the interaction mode is not "selection zones"
820 1008 if (impl->m_HoveredZone) {
821 1009 impl->m_HoveredZone->setHovered(false);
822 1010 impl->m_HoveredZone = nullptr;
823 1011 }
824 1012
825 1013 setCursor(Qt::ArrowCursor);
826 1014 }
827 1015
828 1016 impl->m_HasMovedMouse = true;
829 1017 VisualizationDragWidget::mouseMoveEvent(event);
830 1018 }
831 1019
832 1020 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
833 1021 {
834 1022 // Processes event only if the wheel occurs on axis rect
835 1023 if (!dynamic_cast<QCPAxisRect *>(ui->widget->layoutElementAt(event->posF()))) {
836 1024 return;
837 1025 }
838 1026
839 1027 auto value = event->angleDelta().x() + event->angleDelta().y();
840 1028 if (value != 0) {
841 1029
842 1030 auto direction = value > 0 ? 1.0 : -1.0;
843 1031 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
844 1032 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
845 1033 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
846 1034
847 1035 auto zoomOrientations = QFlags<Qt::Orientation>{};
848 1036 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
849 1037 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
850 1038
851 1039 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
852 1040
853 1041 if (!isZoomX && !isZoomY) {
854 1042 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
855 1043 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
856 1044
857 1045 axis->setRange(axis->range() + diff);
858 1046
859 1047 if (plot().noAntialiasingOnDrag()) {
860 1048 plot().setNotAntialiasedElements(QCP::aeAll);
861 1049 }
862 1050
863 //plot().replot(QCustomPlot::rpQueuedReplot);
1051 // plot().replot(QCustomPlot::rpQueuedReplot);
864 1052 }
865 1053 }
866 1054 }
867 1055
868 1056 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
869 1057 {
870 1058 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
871 1059 auto isSelectionZoneMode
872 1060 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
873 1061 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
874 1062
875 1063 if (!isDragDropClick && isLeftClick) {
876 1064 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
877 1065 // Starts a zoom box
878 1066 impl->startDrawingRect(event->pos(), plot());
879 1067 }
880 1068 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
881 1069 // Starts a new selection zone
882 1070 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
883 1071 if (!zoneAtPos) {
884 1072 impl->startDrawingZone(event->pos(), this);
885 1073 }
886 1074 }
887 1075 }
888 1076
889 1077 // Allows mouse panning only in default mode
890 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
891 == SqpApplication::PlotsInteractionMode::None
892 && !isDragDropClick);
1078 // plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
1079 // ==
1080 // SqpApplication::PlotsInteractionMode::None
1081 // && !isDragDropClick);
893 1082
894 1083 // Allows zone edition only in selection zone mode without drag&drop
895 1084 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
896 1085
897 1086 // Selection / Deselection
898 1087 if (isSelectionZoneMode) {
899 1088 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
900 1089 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
901 1090
902 1091
903 1092 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
904 1093 && !isMultiSelectionClick) {
905 1094 parentVisualizationWidget()->selectionZoneManager().select(
906 1095 {selectionZoneItemUnderCursor});
907 1096 }
908 1097 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
909 1098 parentVisualizationWidget()->selectionZoneManager().clearSelection();
910 1099 }
911 1100 else {
912 1101 // No selection change
913 1102 }
914 1103
915 1104 if (selectionZoneItemUnderCursor && isLeftClick) {
916 1105 selectionZoneItemUnderCursor->setAssociatedEditedZones(
917 1106 parentVisualizationWidget()->selectionZoneManager().selectedItems());
918 1107 }
919 1108 }
920 1109
921 1110
922 1111 impl->m_HasMovedMouse = false;
923 1112 VisualizationDragWidget::mousePressEvent(event);
924 1113 }
925 1114
926 1115 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
927 1116 {
928 1117 if (impl->m_DrawingZoomRect) {
929 1118
930 1119 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
931 1120 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
932 1121
933 1122 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
934 1123 impl->m_DrawingZoomRect->bottomRight->coords().x()};
935 1124
936 1125 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
937 1126 impl->m_DrawingZoomRect->bottomRight->coords().y()};
938 1127
939 1128 impl->removeDrawingRect(plot());
940 1129
941 1130 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
942 1131 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
943 1132 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
944 1133 axisX->setRange(newAxisXRange);
945 1134 axisY->setRange(newAxisYRange);
946 1135
947 1136 plot().replot(QCustomPlot::rpQueuedReplot);
948 1137 }
949 1138 }
950 1139
951 1140 impl->endDrawingZone(this);
952 1141
953 1142 // Selection / Deselection
954 1143 auto isSelectionZoneMode
955 1144 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
956 1145 if (isSelectionZoneMode) {
957 1146 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
958 1147 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
959 1148 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
960 1149 && !impl->m_HasMovedMouse) {
961 1150
962 1151 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
963 1152 if (zonesUnderCursor.count() > 1) {
964 1153 // There are multiple zones under the mouse.
965 1154 // Performs the selection with a selection dialog.
966 1155 VisualizationMultiZoneSelectionDialog dialog{this};
967 1156 dialog.setZones(zonesUnderCursor);
968 1157 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
969 1158 dialog.activateWindow();
970 1159 dialog.raise();
971 1160 if (dialog.exec() == QDialog::Accepted) {
972 1161 auto selection = dialog.selectedZones();
973 1162
974 1163 if (!isMultiSelectionClick) {
975 1164 parentVisualizationWidget()->selectionZoneManager().clearSelection();
976 1165 }
977 1166
978 1167 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
979 1168 auto zone = it.key();
980 1169 auto isSelected = it.value();
981 1170 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
982 1171 isSelected);
983 1172
984 1173 if (isSelected) {
985 1174 // Puts the zone on top of the stack so it can be moved or resized
986 1175 impl->moveSelectionZoneOnTop(zone, plot());
987 1176 }
988 1177 }
989 1178 }
990 1179 }
991 1180 else {
992 1181 if (!isMultiSelectionClick) {
993 1182 parentVisualizationWidget()->selectionZoneManager().select(
994 1183 {selectionZoneItemUnderCursor});
995 1184 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
996 1185 }
997 1186 else {
998 1187 parentVisualizationWidget()->selectionZoneManager().setSelected(
999 1188 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1000 1189 || event->button() == Qt::RightButton);
1001 1190 }
1002 1191 }
1003 1192 }
1004 1193 else {
1005 1194 // No selection change
1006 1195 }
1007 1196 }
1008 1197 }
1009 1198
1010 1199 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1011 1200 {
1012 1201 auto graphRange = ui->widget->xAxis->range();
1013 1202 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1014 1203
1015 1204 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1016 1205 auto variable = variableEntry.first;
1017 1206 qCDebug(LOG_VisualizationGraphWidget())
1018 1207 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1019 1208 qCDebug(LOG_VisualizationGraphWidget())
1020 1209 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1021 1210 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1022 1211 impl->updateData(variableEntry.second, variable, variable->range());
1023 1212 }
1024 1213 }
1025 1214 }
1026 1215
1027 1216 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1028 1217 const DateTimeRange &range)
1029 1218 {
1030 1219 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1031 1220 if (it != impl->m_VariableToPlotMultiMap.end()) {
1032 1221 impl->updateData(it->second, variable, range);
1033 1222 }
1034 1223 }
1035 1224
1036 1225 void VisualizationGraphWidget::variableUpdated(QUuid id)
1037 1226 {
1038 for(auto& [var,plotables]:impl->m_VariableToPlotMultiMap)
1039 {
1040 if(var->ID()==id)
1041 {
1042 impl->updateData(plotables,var,this->graphRange());
1227 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1228 if (var->ID() == id) {
1229 impl->updateData(plotables, var, this->graphRange());
1043 1230 }
1044 1231 }
1045 1232 }
@@ -1,134 +1,148
1 1 #ifndef GUITESTUTILS_H
2 2 #define GUITESTUTILS_H
3 3
4 4 #include <Common/cpp_utils.h>
5 5 #include <QPoint>
6 6 #include <QCursor>
7 7 #include <QMouseEvent>
8 8 #include <QCoreApplication>
9 9 #include <QtTest>
10 10 #include <QDesktopWidget>
11 11
12 12 #include <qcustomplot.h>
13 13
14 14 template <typename T>
15 15 QPoint center(T* widget)
16 16 {
17 17 return QPoint{widget->width()/2,widget->height()/2};
18 18 }
19 19
20 20 HAS_METHOD(viewport)
21 21
22 22 template <typename T>
23 using is_QWidgetOrDerived = std::is_base_of<QWidget,T>;
23 static inline constexpr bool is_QWidgetOrDerived = std::is_base_of<QWidget,T>::value;
24 24
25 25 template <typename T> using viewport_type = decltype(std::declval<T>().viewport());
26 26
27 27 HAS_METHOD(topLevelItem)
28 28
29 29 template<typename T>
30 30 void mouseMove(T* widget, QPoint pos, Qt::MouseButton mouseModifier)
31 31 {
32 32 QCursor::setPos(widget->mapToGlobal(pos));
33 33 QMouseEvent event(QEvent::MouseMove, pos, Qt::NoButton, mouseModifier, Qt::NoModifier);
34 if constexpr(has_viewport<T> && is_QWidgetOrDerived<viewport_type<T>>::value )
34 if constexpr(has_viewport<T>)
35 35 {
36 qApp->sendEvent(widget->viewport(), &event);
36 if constexpr(is_QWidgetOrDerived<viewport_type<T>>)
37 {
38 qApp->sendEvent(widget->viewport(), &event);
39 }
40 else
41 {
42 qApp->sendEvent(widget, &event);
43 }
37 44 }
38 45 else
39 46 {
40 47 qApp->sendEvent(widget, &event);
41 48 }
42 49 qApp->processEvents();
43 50 }
44 51
45 52
46 53 template <typename T>
47 54 void setMouseTracking(T* widget)
48 55 {
49 if constexpr(has_viewport<T> && is_QWidgetOrDerived<viewport_type<T>>::value)
56 if constexpr(has_viewport<T>)
50 57 {
51 widget->viewport()->setMouseTracking(true);
58 if constexpr(is_QWidgetOrDerived<viewport_type<T>>)
59 {
60 widget->viewport()->setMouseTracking(true);
61 }
62 else
63 {
64 widget->setMouseTracking(true);
65 }
52 66 }
53 67 else
54 68 {
55 69 widget->setMouseTracking(true);
56 70 }
57 71 }
58 72
59 73 template <typename T, typename T2>
60 74 auto getItem(T* widget, T2 itemIndex)
61 75 {
62 76 if constexpr(has_topLevelItem<T>)
63 77 {
64 78 return widget->topLevelItem(itemIndex);
65 79 }
66 80 else
67 81 {
68 82 return widget->item(itemIndex);
69 83 }
70 84 }
71 85
72 86 #define SELECT_ITEM(widget, itemIndex, item)\
73 87 auto item = getItem(widget, itemIndex);\
74 88 {\
75 89 auto itemCenterPos = widget->visualItemRect(item).center();\
76 90 QTest::mouseClick(widget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos);\
77 91 QVERIFY(widget->selectedItems().size() > 0);\
78 92 QVERIFY(widget->selectedItems().contains(item));\
79 93 }
80 94
81 95
82 96 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName)\
83 97 childType* child = parent.findChild<childType*>(childName); \
84 98 QVERIFY(child!=Q_NULLPTR); \
85 99 setMouseTracking(child);
86 100
87 101 template<typename T1, typename T2, typename T3, typename T4=void>
88 102 void dragnDropItem(T1* sourceWidget, T2* destWidget, T3* item, T4* destItem=Q_NULLPTR)
89 103 {
90 104 auto itemCenterPos = sourceWidget->visualItemRect(item).center();
91 105 if constexpr(has_viewport<T1>)
92 106 {
93 107 QTest::mousePress(sourceWidget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos);
94 108 }
95 109 else
96 110 {
97 111 QTest::mousePress(sourceWidget, Qt::LeftButton, Qt::NoModifier, itemCenterPos);
98 112 }
99 113 mouseMove(sourceWidget,itemCenterPos, Qt::LeftButton);
100 114 itemCenterPos+=QPoint(0,-10);
101 115 QTimer::singleShot(100,[destWidget,destItem](){
102 116 mouseMove(destWidget, destWidget->rect().center(),Qt::LeftButton);
103 117 mouseMove(destWidget, destWidget->rect().center()+QPoint(0,-10),Qt::LeftButton);
104 118 if constexpr(!std::is_same_v<void, T4>)
105 119 {
106 120 auto destItemCenterPos = destWidget->visualItemRect(destItem).center();
107 121 QTest::mouseRelease(destWidget, Qt::LeftButton, Qt::NoModifier, destItemCenterPos);
108 122 }
109 123 else if constexpr(has_viewport<T2>)
110 124 {
111 125 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
112 126 }
113 127 else
114 128 {
115 129 QTest::mouseRelease(destWidget, Qt::LeftButton);
116 130 }
117 131 QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton);
118 132 });
119 133 mouseMove(sourceWidget,itemCenterPos,Qt::LeftButton);
120 134 }
121 135
122 136 #define PREPARE_GUI_TEST(main_widget)\
123 137 main_widget.setGeometry(QRect(QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)),\
124 138 QSize(500, 500)));\
125 139 main_widget.show();\
126 140 qApp->setActiveWindow(&main_widget);\
127 141 QVERIFY(QTest::qWaitForWindowActive(&main_widget))
128 142
129 143 #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName)\
130 144 childType* child = parent.findChild<childType*>(childName); \
131 145 QVERIFY(child!=Q_NULLPTR); \
132 146 setMouseTracking(child);
133 147
134 148 #endif
@@ -1,82 +1,79
1 1 #include <QtTest>
2 2 #include <QObject>
3 3 #include <QString>
4 4 #include <QScreen>
5 5 #include <QMainWindow>
6 6 #include <QWheelEvent>
7 7
8 8 #include <qcustomplot.h>
9 9
10 10 #include <SqpApplication.h>
11 11 #include <Variable/VariableController2.h>
12 12 #include <Common/cpp_utils.h>
13 13
14 14 #include <Visualization/VisualizationGraphWidget.h>
15 15 #include <TestProviders.h>
16 16 #include <GUITestUtils.h>
17 17
18 18
19 19 ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication *>(qApp)->variableController().isReady)
20 20
21 21 #define A_SIMPLE_GRAPH_FIXTURE \
22 22 VisualizationGraphWidget w;\
23 23 PREPARE_GUI_TEST(w);\
24 24 auto provider = std::make_shared<SimpleRange<10> >();\
25 25 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),\
26 26 QTime(16, 00));\
27 27 auto var = static_cast<SqpApplication *>(qApp)->variableController().createVariable(\
28 28 "V1", {{"", "scalar"}}, provider, range);\
29 29 while (!isReady(var))\
30 30 QCoreApplication::processEvents();\
31 31 w.addVariable(var, range);\
32 32 GET_CHILD_WIDGET_FOR_GUI_TESTS(w, plot, QCustomPlot, "widget");\
33 auto cent = center(plot);\
33 auto cent = center(&w);\
34 34
35 35
36 36 class A_SimpleGraph : public QObject {
37 37 Q_OBJECT
38 38 public:
39 39 explicit A_SimpleGraph(QObject *parent = Q_NULLPTR) : QObject(parent) {}
40 40
41 41 private slots:
42 42 void scrolls_with_mouse()
43 43 {
44 44 A_SIMPLE_GRAPH_FIXTURE
45 45
46 46 for (auto i = 0; i < 10; i++) {
47 QTest::mousePress(plot, Qt::LeftButton, Qt::NoModifier, cent, 5);
48 mouseMove(plot, {cent.x() + 200, cent.y()}, Qt::LeftButton);
49 QTest::mouseRelease(plot, Qt::LeftButton);
47 QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 500);
48 mouseMove(&w, {cent.x() + 200, cent.y()}, Qt::LeftButton);
49 QTest::mouseRelease(&w, Qt::LeftButton);
50 50 while (!isReady(var))
51 51 QCoreApplication::processEvents();
52 /*
53 * Just for visual inspection while running tests
54 */
55 plot->rescaleAxes();
56 plot->replot();
57 QCoreApplication::processEvents();
58 52 }
59 53 while (!isReady(var))
60 54 QCoreApplication::processEvents();
61 55 auto r = var->range();
56 /*
57 * Scrolling to the left implies going back in time
58 * Scroll only implies keeping the same delta T -> shit only transformation
59 */
62 60 QVERIFY(r.m_TEnd < range.m_TEnd);
63 // this fails :(
64 61 QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1));
65 62 }
66 63 };
67 64
68 65 QT_BEGIN_NAMESPACE
69 66 QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
70 67 QT_END_NAMESPACE
71 68 int main(int argc, char *argv[])
72 69 {
73 70 SqpApplication app{argc, argv};
74 71 app.setAttribute(Qt::AA_Use96Dpi, true);
75 72 QTEST_DISABLE_KEYPAD_NAVIGATION
76 73 QTEST_ADD_GPU_BLACKLIST_SUPPORT
77 74 A_SimpleGraph tc;
78 75 QTEST_SET_MAIN_SOURCE_PATH
79 76 return QTest::qExec(&tc, argc, argv);
80 77 }
81 78
82 79 #include "main.moc"
General Comments 0
You need to be logged in to leave comments. Login now