##// END OF EJS Templates
Force range for each transformation among synchronized graphs, ignore missing X unit in data files since it's always time...
jeandet -
r1384:e344de146add
parent child
Show More
@@ -1,180 +1,181
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3
3
4 #include "Visualization/IVisualizationWidget.h"
4 #include "Visualization/IVisualizationWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
6
6
7 #include <QLoggingCategory>
7 #include <QLoggingCategory>
8 #include <QWidget>
8 #include <QWidget>
9 #include <QUuid>
9 #include <QUuid>
10
10
11 #include <memory>
11 #include <memory>
12
12
13 #include <Common/spimpl.h>
13 #include <Common/spimpl.h>
14
14
15 #include <Data/DateTimeRange.h>
15 #include <Data/DateTimeRange.h>
16
16
17 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
17 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
18
18
19 class QCPRange;
19 class QCPRange;
20 class QCustomPlot;
20 class QCustomPlot;
21 class Variable;
21 class Variable;
22 class VisualizationWidget;
22 class VisualizationWidget;
23 class VisualizationZoneWidget;
23 class VisualizationZoneWidget;
24 class VisualizationSelectionZoneItem;
24 class VisualizationSelectionZoneItem;
25
25
26 namespace Ui {
26 namespace Ui {
27 class VisualizationGraphWidget;
27 class VisualizationGraphWidget;
28 } // namespace Ui
28 } // namespace Ui
29
29
30 /// Defines options that can be associated with the graph
30 /// Defines options that can be associated with the graph
31 enum GraphFlag {
31 enum GraphFlag {
32 DisableAll = 0x0, ///< Disables acquisition and synchronization
32 DisableAll = 0x0, ///< Disables acquisition and synchronization
33 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
33 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
34 /// the acquisition of data
34 /// the acquisition of data
35 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
35 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
36 /// the call to the synchronization of the graphs contained in the
36 /// the call to the synchronization of the graphs contained in the
37 /// same zone of this graph
37 /// same zone of this graph
38 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
38 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
39 };
39 };
40
40
41 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
41 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
42 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
42 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
43
43
44 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
44 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
45 Q_OBJECT
45 Q_OBJECT
46
46
47 friend class QCustomPlotSynchronizer;
47 friend class QCustomPlotSynchronizer;
48 friend class VisualizationGraphRenderingDelegate;
48 friend class VisualizationGraphRenderingDelegate;
49
49
50 public:
50 public:
51 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
51 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
52 virtual ~VisualizationGraphWidget();
52 virtual ~VisualizationGraphWidget();
53
53
54 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
54 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
55 VisualizationZoneWidget *parentZoneWidget() const noexcept;
55 VisualizationZoneWidget *parentZoneWidget() const noexcept;
56
56
57 /// Returns the main VisualizationWidget which contains the graph or nullptr
57 /// Returns the main VisualizationWidget which contains the graph or nullptr
58 VisualizationWidget *parentVisualizationWidget() const;
58 VisualizationWidget *parentVisualizationWidget() const;
59
59
60 /// Sets graph options
60 /// Sets graph options
61 void setFlags(GraphFlags flags);
61 void setFlags(GraphFlags flags);
62
62
63 void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range);
63 void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range);
64
64
65 /// Removes a variable from the graph
65 /// Removes a variable from the graph
66 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
66 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
67
67
68 /// Returns the list of all variables used in the graph
68 /// Returns the list of all variables used in the graph
69 std::vector<std::shared_ptr<Variable> > variables() const;
69 std::vector<std::shared_ptr<Variable> > variables() const;
70
70
71 /// Sets the y-axis range based on the data of a variable
71 /// Sets the y-axis range based on the data of a variable
72 void setYRange(std::shared_ptr<Variable> variable);
72 void setYRange(std::shared_ptr<Variable> variable);
73 DateTimeRange graphRange() const noexcept;
73 DateTimeRange graphRange() const noexcept;
74 void setGraphRange(const DateTimeRange &range, bool updateVar=false, bool forward=false);
74 void setGraphRange(const DateTimeRange &range, bool updateVar=false, bool forward=false);
75 void setAutoRangeOnVariableInitialization(bool value);
75 void setAutoRangeOnVariableInitialization(bool value);
76
76
77 // Zones
77 // Zones
78 /// Returns the ranges of all the selection zones on the graph
78 /// Returns the ranges of all the selection zones on the graph
79 QVector<DateTimeRange> selectionZoneRanges() const;
79 QVector<DateTimeRange> selectionZoneRanges() const;
80 /// Adds new selection zones in the graph
80 /// Adds new selection zones in the graph
81 void addSelectionZones(const QVector<DateTimeRange> &ranges);
81 void addSelectionZones(const QVector<DateTimeRange> &ranges);
82 /// Adds a new selection zone in the graph
82 /// Adds a new selection zone in the graph
83 VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range);
83 VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range);
84 /// Removes the specified selection zone
84 /// Removes the specified selection zone
85 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
85 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
86
86
87 /// Undo the last zoom done with a zoom box
87 /// Undo the last zoom done with a zoom box
88 void undoZoom();
88 void undoZoom();
89
89
90 void zoom(double factor, int center, Qt::Orientation orientation, bool forward=true);
90 void zoom(double factor, int center, Qt::Orientation orientation, bool forward=true);
91 void move(double factor, Qt::Orientation orientation, bool forward=true);
91 void move(double factor, Qt::Orientation orientation, bool forward=true);
92 void move(double dx, double dy, bool forward=true);
92 void move(double dx, double dy, bool forward=true);
93 void transform(const DateTimeRangeTransformation& tranformation, bool forward=true);
93 void transform(const DateTimeRangeTransformation& tranformation, bool forward=true);
94
94
95 // IVisualizationWidget interface
95 // IVisualizationWidget interface
96 void accept(IVisualizationWidgetVisitor *visitor) override;
96 void accept(IVisualizationWidgetVisitor *visitor) override;
97 bool canDrop(const Variable &variable) const override;
97 bool canDrop(const Variable &variable) const override;
98 bool contains(const Variable &variable) const override;
98 bool contains(const Variable &variable) const override;
99 QString name() const override;
99 QString name() const override;
100
100
101 // VisualisationDragWidget
101 // VisualisationDragWidget
102 QMimeData *mimeData(const QPoint &position) const override;
102 QMimeData *mimeData(const QPoint &position) const override;
103 QPixmap customDragPixmap(const QPoint &dragPosition) override;
103 QPixmap customDragPixmap(const QPoint &dragPosition) override;
104 bool isDragAllowed() const override;
104 bool isDragAllowed() const override;
105 void highlightForMerge(bool highlighted) override;
105 void highlightForMerge(bool highlighted) override;
106
106
107 // Cursors
107 // Cursors
108 /// Adds or moves the vertical cursor at the specified value on the x-axis
108 /// Adds or moves the vertical cursor at the specified value on the x-axis
109 void addVerticalCursor(double time);
109 void addVerticalCursor(double time);
110 /// Adds or moves the vertical cursor at the specified value on the x-axis
110 /// Adds or moves the vertical cursor at the specified value on the x-axis
111 void addVerticalCursorAtViewportPosition(double position);
111 void addVerticalCursorAtViewportPosition(double position);
112 void removeVerticalCursor();
112 void removeVerticalCursor();
113 /// Adds or moves the vertical cursor at the specified value on the y-axis
113 /// Adds or moves the vertical cursor at the specified value on the y-axis
114 void addHorizontalCursor(double value);
114 void addHorizontalCursor(double value);
115 /// Adds or moves the vertical cursor at the specified value on the y-axis
115 /// Adds or moves the vertical cursor at the specified value on the y-axis
116 void addHorizontalCursorAtViewportPosition(double position);
116 void addHorizontalCursorAtViewportPosition(double position);
117 void removeHorizontalCursor();
117 void removeHorizontalCursor();
118
118
119 signals:
119 signals:
120 void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange);
120 void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange);
121 void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange &range);
121 void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange &range);
122
122
123 /// Signal emitted when the variable is about to be removed from the graph
123 /// Signal emitted when the variable is about to be removed from the graph
124 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
124 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
125 /// Signal emitted when the variable has been added to the graph
125 /// Signal emitted when the variable has been added to the graph
126 void variableAdded(std::shared_ptr<Variable> var);
126 void variableAdded(std::shared_ptr<Variable> var);
127
127
128
128
129 void zoom_sig(double factor, int center, Qt::Orientation orientation, bool forward=true);
129 void zoom_sig(double factor, int center, Qt::Orientation orientation, bool forward=true);
130 void move_sig(double factor, Qt::Orientation orientation, bool forward=true);
130 void move_sig(double factor, Qt::Orientation orientation, bool forward=true);
131 void move_sig(double dx, double dy, bool forward=true);
131 void move_sig(double dx, double dy, bool forward=true);
132 void transform_sig(const DateTimeRangeTransformation& tranformation, bool forward=true);
132 void transform_sig(const DateTimeRangeTransformation& tranformation, bool forward=true);
133 void setrange_sig(const DateTimeRange& range, bool updateVar=false, bool forward=true);
133
134
134 protected:
135 protected:
135 void closeEvent(QCloseEvent *event) override;
136 void closeEvent(QCloseEvent *event) override;
136 void enterEvent(QEvent *event) override;
137 void enterEvent(QEvent *event) override;
137 void leaveEvent(QEvent *event) override;
138 void leaveEvent(QEvent *event) override;
138 void wheelEvent(QWheelEvent * event) override;
139 void wheelEvent(QWheelEvent * event) override;
139 void mouseMoveEvent(QMouseEvent *event) override;
140 void mouseMoveEvent(QMouseEvent *event) override;
140 void mouseReleaseEvent(QMouseEvent *event) override;
141 void mouseReleaseEvent(QMouseEvent *event) override;
141 void mousePressEvent(QMouseEvent *event) override;
142 void mousePressEvent(QMouseEvent *event) override;
142 void mouseDoubleClickEvent(QMouseEvent *event) override;
143 void mouseDoubleClickEvent(QMouseEvent *event) override;
143 void keyReleaseEvent(QKeyEvent * event) override;
144 void keyReleaseEvent(QKeyEvent * event) override;
144 void keyPressEvent(QKeyEvent * event) override;
145 void keyPressEvent(QKeyEvent * event) override;
145
146
146 QCustomPlot &plot() const noexcept;
147 QCustomPlot &plot() const noexcept;
147
148
148 private:
149 private:
149 Ui::VisualizationGraphWidget *ui;
150 Ui::VisualizationGraphWidget *ui;
150
151
151 class VisualizationGraphWidgetPrivate;
152 class VisualizationGraphWidgetPrivate;
152 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
153 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
153
154
154 private slots:
155 private slots:
155 /// Slot called when right clicking on the graph (displays a menu)
156 /// Slot called when right clicking on the graph (displays a menu)
156 void onGraphMenuRequested(const QPoint &pos) noexcept;
157 void onGraphMenuRequested(const QPoint &pos) noexcept;
157
158
158 /// Rescale the X axe to range parameter
159 /// Rescale the X axe to range parameter
159 // void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
160 // void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
160
161
161 /// Slot called when a mouse double click was made
162 /// Slot called when a mouse double click was made
162 void onMouseDoubleClick(QMouseEvent *event) noexcept;
163 void onMouseDoubleClick(QMouseEvent *event) noexcept;
163 /// Slot called when a mouse move was made
164 /// Slot called when a mouse move was made
164 void onMouseMove(QMouseEvent *event) noexcept;
165 void onMouseMove(QMouseEvent *event) noexcept;
165 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
166 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
166 void onMouseWheel(QWheelEvent *event) noexcept;
167 void onMouseWheel(QWheelEvent *event) noexcept;
167 /// Slot called when a mouse press was made, to activate the calibration of a graph
168 /// Slot called when a mouse press was made, to activate the calibration of a graph
168 void onMousePress(QMouseEvent *event) noexcept;
169 void onMousePress(QMouseEvent *event) noexcept;
169 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
170 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
170 void onMouseRelease(QMouseEvent *event) noexcept;
171 void onMouseRelease(QMouseEvent *event) noexcept;
171
172
172 void onDataCacheVariableUpdated();
173 void onDataCacheVariableUpdated();
173
174
174 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range);
175 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range);
175
176
176 void variableUpdated(QUuid id);
177 void variableUpdated(QUuid id);
177 void variableDeleted(const std::shared_ptr<Variable>&);
178 void variableDeleted(const std::shared_ptr<Variable>&);
178 };
179 };
179
180
180 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
181 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,1430 +1,1427
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
13
13
14 #include <Actions/ActionsGuiController.h>
14 #include <Actions/ActionsGuiController.h>
15 #include <Actions/FilteringAction.h>
15 #include <Actions/FilteringAction.h>
16 #include <Common/MimeTypesDef.h>
16 #include <Common/MimeTypesDef.h>
17 #include <Data/ArrayData.h>
17 #include <Data/ArrayData.h>
18 #include <Data/IDataSeries.h>
18 #include <Data/IDataSeries.h>
19 #include <Data/SpectrogramSeries.h>
19 #include <Data/SpectrogramSeries.h>
20 #include <DragAndDrop/DragDropGuiController.h>
20 #include <DragAndDrop/DragDropGuiController.h>
21 #include <Settings/SqpSettingsDefs.h>
21 #include <Settings/SqpSettingsDefs.h>
22 #include <SqpApplication.h>
22 #include <SqpApplication.h>
23 #include <Time/TimeController.h>
23 #include <Time/TimeController.h>
24 #include <Variable/Variable.h>
24 #include <Variable/Variable.h>
25 #include <Variable/VariableController2.h>
25 #include <Variable/VariableController2.h>
26 #include <Data/DateTimeRangeHelper.h>
26 #include <Data/DateTimeRangeHelper.h>
27
27
28 #include <unordered_map>
28 #include <unordered_map>
29
29
30 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
30 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
31
31
32 namespace {
32 namespace {
33
33
34 /// Key pressed to enable drag&drop in all modes
34 /// Key pressed to enable drag&drop in all modes
35 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
35 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
36
36
37 /// Key pressed to enable zoom on horizontal axis
37 /// Key pressed to enable zoom on horizontal axis
38 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
38 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
39
39
40 /// Key pressed to enable zoom on vertical axis
40 /// Key pressed to enable zoom on vertical axis
41 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
41 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
42
42
43 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
43 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
44 const auto PAN_SPEED = 5;
44 const auto PAN_SPEED = 5;
45
45
46 /// Key pressed to enable a calibration pan
46 /// Key pressed to enable a calibration pan
47 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
47 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
48
48
49 /// Key pressed to enable multi selection of selection zones
49 /// Key pressed to enable multi selection of selection zones
50 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
50 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
51
51
52 /// Minimum size for the zoom box, in percentage of the axis range
52 /// Minimum size for the zoom box, in percentage of the axis range
53 const auto ZOOM_BOX_MIN_SIZE = 0.8;
53 const auto ZOOM_BOX_MIN_SIZE = 0.8;
54
54
55 /// Format of the dates appearing in the label of a cursor
55 /// Format of the dates appearing in the label of a cursor
56 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
56 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
57
57
58 } // namespace
58 } // namespace
59
59
60 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
60 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
61
61
62 explicit VisualizationGraphWidgetPrivate(const QString &name)
62 explicit VisualizationGraphWidgetPrivate(const QString &name)
63 : m_Name{name},
63 : m_Name{name},
64 m_Flags{GraphFlag::EnableAll},
64 m_Flags{GraphFlag::EnableAll},
65 m_IsCalibration{false},
65 m_IsCalibration{false},
66 m_RenderingDelegate{nullptr}
66 m_RenderingDelegate{nullptr}
67 {
67 {
68 m_plot = new QCustomPlot();
68 m_plot = new QCustomPlot();
69 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
69 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
70 m_plot->setPlottingHint(QCP::phFastPolylines, true);
70 m_plot->setPlottingHint(QCP::phFastPolylines, true);
71 }
71 }
72
72
73 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
73 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
74 const DateTimeRange &range)
74 const DateTimeRange &range)
75 {
75 {
76 VisualizationGraphHelper::updateData(plottables, variable, range);
76 VisualizationGraphHelper::updateData(plottables, variable, range);
77
77
78 // Prevents that data has changed to update rendering
78 // Prevents that data has changed to update rendering
79 m_RenderingDelegate->onPlotUpdated();
79 m_RenderingDelegate->onPlotUpdated();
80 }
80 }
81
81
82 QString m_Name;
82 QString m_Name;
83 // 1 variable -> n qcpplot
83 // 1 variable -> n qcpplot
84 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
84 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
85 GraphFlags m_Flags;
85 GraphFlags m_Flags;
86 bool m_IsCalibration;
86 bool m_IsCalibration;
87 QCustomPlot* m_plot;
87 QCustomPlot* m_plot;
88 QPoint m_lastMousePos;
88 QPoint m_lastMousePos;
89 QCPRange m_lastXRange;
89 QCPRange m_lastXRange;
90 QCPRange m_lastYRange;
90 QCPRange m_lastYRange;
91 /// Delegate used to attach rendering features to the plot
91 /// Delegate used to attach rendering features to the plot
92 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
92 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
93
93
94 QCPItemRect* m_DrawingZoomRect = nullptr;
94 QCPItemRect* m_DrawingZoomRect = nullptr;
95 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
95 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
96
96
97 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
97 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
99
99
100 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
100 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
101 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
101 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
102 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
102 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
103
103
104 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
104 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
105
105
106 bool m_VariableAutoRangeOnInit = true;
106 bool m_VariableAutoRangeOnInit = true;
107
107
108 inline void enterPlotDrag(const QPoint& position)
108 inline void enterPlotDrag(const QPoint& position)
109 {
109 {
110 m_lastMousePos = m_plot->mapFromParent(position);
110 m_lastMousePos = m_plot->mapFromParent(position);
111 m_lastXRange = m_plot->xAxis->range();
111 m_lastXRange = m_plot->xAxis->range();
112 m_lastYRange = m_plot->yAxis->range();
112 m_lastYRange = m_plot->yAxis->range();
113
113
114 }
114 }
115
115
116 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
116 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
117 void updateZoomRect(const QPoint& newPos)
117 void updateZoomRect(const QPoint& newPos)
118 {
118 {
119 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
119 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
120 m_DrawingZoomRect->bottomRight->setCoords(pos);
120 m_DrawingZoomRect->bottomRight->setCoords(pos);
121 m_plot->replot(QCustomPlot::rpQueuedReplot);
121 m_plot->replot(QCustomPlot::rpQueuedReplot);
122 }
122 }
123
123
124 void applyZoomRect()
124 void applyZoomRect()
125 {
125 {
126 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
126 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
127 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
127 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
128
128
129 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
129 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
130 m_DrawingZoomRect->bottomRight->coords().x()};
130 m_DrawingZoomRect->bottomRight->coords().x()};
131
131
132 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
132 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
133 m_DrawingZoomRect->bottomRight->coords().y()};
133 m_DrawingZoomRect->bottomRight->coords().y()};
134
134
135 removeDrawingRect();
135 removeDrawingRect();
136
136
137 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
137 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
138 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
138 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
139 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
139 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
140 axisX->setRange(newAxisXRange);
140 axisX->setRange(newAxisXRange);
141 axisY->setRange(newAxisYRange);
141 axisY->setRange(newAxisYRange);
142
142
143 m_plot->replot(QCustomPlot::rpQueuedReplot);
143 m_plot->replot(QCustomPlot::rpQueuedReplot);
144 }
144 }
145 }
145 }
146
146
147 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
147 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
148 void updateZoneRect(const QPoint& newPos)
148 void updateZoneRect(const QPoint& newPos)
149 {
149 {
150 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
150 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
151 m_plot->replot(QCustomPlot::rpQueuedReplot);
151 m_plot->replot(QCustomPlot::rpQueuedReplot);
152 }
152 }
153
153
154 void startDrawingRect(const QPoint &pos)
154 void startDrawingRect(const QPoint &pos)
155 {
155 {
156 removeDrawingRect();
156 removeDrawingRect();
157
157
158 auto axisPos = posToAxisPos(pos);
158 auto axisPos = posToAxisPos(pos);
159
159
160 m_DrawingZoomRect = new QCPItemRect{m_plot};
160 m_DrawingZoomRect = new QCPItemRect{m_plot};
161 QPen p;
161 QPen p;
162 p.setWidth(2);
162 p.setWidth(2);
163 m_DrawingZoomRect->setPen(p);
163 m_DrawingZoomRect->setPen(p);
164
164
165 m_DrawingZoomRect->topLeft->setCoords(axisPos);
165 m_DrawingZoomRect->topLeft->setCoords(axisPos);
166 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
166 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
167 }
167 }
168
168
169 void removeDrawingRect()
169 void removeDrawingRect()
170 {
170 {
171 if (m_DrawingZoomRect) {
171 if (m_DrawingZoomRect) {
172 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
172 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
173 m_DrawingZoomRect = nullptr;
173 m_DrawingZoomRect = nullptr;
174 m_plot->replot(QCustomPlot::rpQueuedReplot);
174 m_plot->replot(QCustomPlot::rpQueuedReplot);
175 }
175 }
176 }
176 }
177
177
178 void selectZone(const QPoint &pos)
178 void selectZone(const QPoint &pos)
179 {
179 {
180 auto zoneAtPos = selectionZoneAt(pos);
180 auto zoneAtPos = selectionZoneAt(pos);
181 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
181 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
182 }
182 }
183
183
184 void startDrawingZone(const QPoint &pos)
184 void startDrawingZone(const QPoint &pos)
185 {
185 {
186 endDrawingZone();
186 endDrawingZone();
187
187
188 auto axisPos = posToAxisPos(pos);
188 auto axisPos = posToAxisPos(pos);
189
189
190 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
190 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
191 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
191 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
192 m_DrawingZone->setEditionEnabled(false);
192 m_DrawingZone->setEditionEnabled(false);
193 }
193 }
194
194
195 void endDrawingZone()
195 void endDrawingZone()
196 {
196 {
197 if (m_DrawingZone) {
197 if (m_DrawingZone) {
198 auto drawingZoneRange = m_DrawingZone->range();
198 auto drawingZoneRange = m_DrawingZone->range();
199 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
199 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
200 m_DrawingZone->setEditionEnabled(true);
200 m_DrawingZone->setEditionEnabled(true);
201 addSelectionZone(m_DrawingZone);
201 addSelectionZone(m_DrawingZone);
202 }
202 }
203 else {
203 else {
204 m_plot->removeItem(m_DrawingZone);
204 m_plot->removeItem(m_DrawingZone);
205 }
205 }
206
206
207 m_plot->replot(QCustomPlot::rpQueuedReplot);
207 m_plot->replot(QCustomPlot::rpQueuedReplot);
208 m_DrawingZone = nullptr;
208 m_DrawingZone = nullptr;
209 }
209 }
210 }
210 }
211
211
212 void moveSelectionZone(const QPoint& destination)
212 void moveSelectionZone(const QPoint& destination)
213 {
213 {
214 /*
214 /*
215 * I give up on this for now
215 * I give up on this for now
216 * @TODO implement this, the difficulty is that selection zones have their own
216 * @TODO implement this, the difficulty is that selection zones have their own
217 * event handling code which seems to rely on QCP GUI event handling propagation
217 * event handling code which seems to rely on QCP GUI event handling propagation
218 * which was a realy bad design choice.
218 * which was a realy bad design choice.
219 */
219 */
220 }
220 }
221
221
222 void setSelectionZonesEditionEnabled(bool value)
222 void setSelectionZonesEditionEnabled(bool value)
223 {
223 {
224 for (auto s : m_SelectionZones) {
224 for (auto s : m_SelectionZones) {
225 s->setEditionEnabled(value);
225 s->setEditionEnabled(value);
226 }
226 }
227 }
227 }
228
228
229 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
229 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
230
230
231 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
231 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
232 {
232 {
233 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
233 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
234 auto minDistanceToZone = -1;
234 auto minDistanceToZone = -1;
235 for (auto zone : m_SelectionZones) {
235 for (auto zone : m_SelectionZones) {
236 auto distanceToZone = zone->selectTest(pos, false);
236 auto distanceToZone = zone->selectTest(pos, false);
237 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
237 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
238 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
238 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
239 selectionZoneItemUnderCursor = zone;
239 selectionZoneItemUnderCursor = zone;
240 }
240 }
241 }
241 }
242
242
243 return selectionZoneItemUnderCursor;
243 return selectionZoneItemUnderCursor;
244 }
244 }
245
245
246 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
246 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
247 const QCustomPlot &plot) const
247 const QCustomPlot &plot) const
248 {
248 {
249 QVector<VisualizationSelectionZoneItem *> zones;
249 QVector<VisualizationSelectionZoneItem *> zones;
250 for (auto zone : m_SelectionZones) {
250 for (auto zone : m_SelectionZones) {
251 auto distanceToZone = zone->selectTest(pos, false);
251 auto distanceToZone = zone->selectTest(pos, false);
252 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
252 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
253 zones << zone;
253 zones << zone;
254 }
254 }
255 }
255 }
256
256
257 return zones;
257 return zones;
258 }
258 }
259
259
260 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
260 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
261 {
261 {
262 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
262 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
263 zone->moveToTop();
263 zone->moveToTop();
264 m_SelectionZones.removeAll(zone);
264 m_SelectionZones.removeAll(zone);
265 m_SelectionZones.append(zone);
265 m_SelectionZones.append(zone);
266 }
266 }
267 }
267 }
268
268
269 QPointF posToAxisPos(const QPoint &pos) const
269 QPointF posToAxisPos(const QPoint &pos) const
270 {
270 {
271 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
271 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
272 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
272 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
273 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
273 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
274 }
274 }
275
275
276 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
276 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
277 {
277 {
278 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
278 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
279 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
279 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
280 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
280 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
281 }
281 }
282
282
283 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
283 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
284 {
284 {
285 if (axis->scaleType() == QCPAxis::stLinear)
285 if (axis->scaleType() == QCPAxis::stLinear)
286 {
286 {
287 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
287 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
288 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
288 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
289 }
289 }
290 else
290 else
291 {
291 {
292 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
292 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
293 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
293 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
294 }
294 }
295 }
295 }
296
296
297 void setRange(const DateTimeRange &newRange, bool updateVar=true)
297 void setRange(const DateTimeRange &newRange, bool updateVar=true)
298 {
298 {
299 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
299 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
300 if(updateVar)
300 if(updateVar)
301 {
301 {
302 for (auto it = m_VariableToPlotMultiMap.begin(),
302 for (auto it = m_VariableToPlotMultiMap.begin(),
303 end = m_VariableToPlotMultiMap.end();
303 end = m_VariableToPlotMultiMap.end();
304 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
304 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
305 {
305 {
306 sqpApp->variableController().asyncChangeRange(it->first, newRange);
306 sqpApp->variableController().asyncChangeRange(it->first, newRange);
307 }
307 }
308 }
308 }
309 m_plot->replot(QCustomPlot::rpQueuedReplot);
309 m_plot->replot(QCustomPlot::rpQueuedReplot);
310 }
310 }
311
311
312 void setRange(const QCPRange &newRange)
312 void setRange(const QCPRange &newRange)
313 {
313 {
314 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
314 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
315 setRange(graphRange);
315 setRange(graphRange);
316 }
316 }
317
317
318 void rescaleY()
318 void rescaleY()
319 {
319 {
320 m_plot->yAxis->rescale(true);
320 m_plot->yAxis->rescale(true);
321 }
321 }
322
322
323 std::tuple<double,double> moveGraph(const QPoint& destination)
323 std::tuple<double,double> moveGraph(const QPoint& destination)
324 {
324 {
325 auto currentPos = m_plot->mapFromParent(destination);
325 auto currentPos = m_plot->mapFromParent(destination);
326 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
326 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
327 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
327 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
328 auto oldXRange = xAxis->range();
328 auto oldXRange = xAxis->range();
329 auto oldYRange = yAxis->range();
329 auto oldYRange = yAxis->range();
330 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
330 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
331 xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx);
331 xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx);
332 if(yAxis->scaleType() == QCPAxis::stLinear)
332 if(yAxis->scaleType() == QCPAxis::stLinear)
333 {
333 {
334 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
334 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
335 yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy);
335 yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy);
336 }
336 }
337 else
337 else
338 {
338 {
339 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
339 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
340 yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy);
340 yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy);
341 }
341 }
342 auto newXRange = xAxis->range();
342 auto newXRange = xAxis->range();
343 auto newYRange = yAxis->range();
343 auto newYRange = yAxis->range();
344 setRange(xAxis->range());
344 setRange(xAxis->range());
345 //m_lastMousePos = currentPos;
345 //m_lastMousePos = currentPos;
346 return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower};
346 return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower};
347 }
347 }
348
348
349 void zoom(double factor, int center, Qt::Orientation orientation)
349 void zoom(double factor, int center, Qt::Orientation orientation)
350 {
350 {
351 QCPAxis *axis = m_plot->axisRect()->rangeZoomAxis(orientation);
351 QCPAxis *axis = m_plot->axisRect()->rangeZoomAxis(orientation);
352 axis->scaleRange(factor, axis->pixelToCoord(center));
352 axis->scaleRange(factor, axis->pixelToCoord(center));
353 if (orientation == Qt::Horizontal)
353 if (orientation == Qt::Horizontal)
354 setRange(axis->range());
354 setRange(axis->range());
355 m_plot->replot(QCustomPlot::rpQueuedReplot);
355 m_plot->replot(QCustomPlot::rpQueuedReplot);
356 }
356 }
357
357
358 void transform(const DateTimeRangeTransformation &tranformation)
358 void transform(const DateTimeRangeTransformation &tranformation)
359 {
359 {
360 auto graphRange = m_plot->xAxis->range();
360 auto graphRange = m_plot->xAxis->range();
361 DateTimeRange range{graphRange.lower, graphRange.upper};
361 DateTimeRange range{graphRange.lower, graphRange.upper};
362 range = range.transform(tranformation);
362 range = range.transform(tranformation);
363 setRange(range);
363 setRange(range);
364 m_plot->replot(QCustomPlot::rpQueuedReplot);
364 m_plot->replot(QCustomPlot::rpQueuedReplot);
365 }
365 }
366
366
367 void move(double dx, double dy)
367 void move(double dx, double dy)
368 {
368 {
369 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
369 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
370 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
370 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
371 xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx));
371 xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx));
372 yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy));
372 yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy));
373 setRange(xAxis->range());
373 setRange(xAxis->range());
374 m_plot->replot(QCustomPlot::rpQueuedReplot);
374 m_plot->replot(QCustomPlot::rpQueuedReplot);
375 }
375 }
376
376
377 void move(double factor, Qt::Orientation orientation)
377 void move(double factor, Qt::Orientation orientation)
378 {
378 {
379 auto oldRange = m_plot->xAxis->range();
379 auto oldRange = m_plot->xAxis->range();
380 QCPAxis *axis = m_plot->axisRect()->rangeDragAxis(orientation);
380 QCPAxis *axis = m_plot->axisRect()->rangeDragAxis(orientation);
381 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
381 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
382 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
382 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
383 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
383 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
384 }
384 }
385 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
385 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
386 int start = 0, stop = 0;
386 int start = 0, stop = 0;
387 double diff = 0.;
387 double diff = 0.;
388 if (factor > 0.0) {
388 if (factor > 0.0) {
389 stop = m_plot->width() * factor / 10;
389 stop = m_plot->width() * factor / 10;
390 start = 2 * m_plot->width() * factor / 10;
390 start = 2 * m_plot->width() * factor / 10;
391 }
391 }
392 if (factor < 0.0) {
392 if (factor < 0.0) {
393 factor *= -1.0;
393 factor *= -1.0;
394 start = m_plot->width() * factor / 10;
394 start = m_plot->width() * factor / 10;
395 stop = 2 * m_plot->width() * factor / 10;
395 stop = 2 * m_plot->width() * factor / 10;
396 }
396 }
397 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
397 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
398 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
398 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
399 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
399 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
400 }
400 }
401 if (orientation == Qt::Horizontal)
401 if (orientation == Qt::Horizontal)
402 setRange(axis->range());
402 setRange(axis->range());
403 m_plot->replot(QCustomPlot::rpQueuedReplot);
403 m_plot->replot(QCustomPlot::rpQueuedReplot);
404 }
404 }
405 };
405 };
406
406
407 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
407 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
408 : VisualizationDragWidget{parent},
408 : VisualizationDragWidget{parent},
409 ui{new Ui::VisualizationGraphWidget},
409 ui{new Ui::VisualizationGraphWidget},
410 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
410 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
411 {
411 {
412 ui->setupUi(this);
412 ui->setupUi(this);
413 this->layout()->addWidget(impl->m_plot);
413 this->layout()->addWidget(impl->m_plot);
414 // 'Close' options : widget is deleted when closed
414 // 'Close' options : widget is deleted when closed
415 setAttribute(Qt::WA_DeleteOnClose);
415 setAttribute(Qt::WA_DeleteOnClose);
416
416
417 // The delegate must be initialized after the ui as it uses the plot
417 // The delegate must be initialized after the ui as it uses the plot
418 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
418 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
419
419
420 // Init the cursors
420 // Init the cursors
421 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
421 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
422 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
422 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
423 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
423 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
424 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
424 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
425
425
426 this->setFocusPolicy(Qt::WheelFocus);
426 this->setFocusPolicy(Qt::WheelFocus);
427 this->setMouseTracking(true);
427 this->setMouseTracking(true);
428 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
428 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
429 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
429 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
430 impl->m_plot->setParent(this);
430 impl->m_plot->setParent(this);
431
431
432 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, &VisualizationGraphWidget::variableDeleted);
432 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, &VisualizationGraphWidget::variableDeleted);
433 }
433 }
434
434
435
435
436 VisualizationGraphWidget::~VisualizationGraphWidget()
436 VisualizationGraphWidget::~VisualizationGraphWidget()
437 {
437 {
438 delete ui;
438 delete ui;
439 }
439 }
440
440
441 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
441 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
442 {
442 {
443 auto parent = parentWidget();
443 auto parent = parentWidget();
444 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
444 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
445 parent = parent->parentWidget();
445 parent = parent->parentWidget();
446 }
446 }
447
447
448 return qobject_cast<VisualizationZoneWidget *>(parent);
448 return qobject_cast<VisualizationZoneWidget *>(parent);
449 }
449 }
450
450
451 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
451 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
452 {
452 {
453 auto parent = parentWidget();
453 auto parent = parentWidget();
454 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
454 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
455 parent = parent->parentWidget();
455 parent = parent->parentWidget();
456 }
456 }
457
457
458 return qobject_cast<VisualizationWidget *>(parent);
458 return qobject_cast<VisualizationWidget *>(parent);
459 }
459 }
460
460
461 void VisualizationGraphWidget::setFlags(GraphFlags flags)
461 void VisualizationGraphWidget::setFlags(GraphFlags flags)
462 {
462 {
463 impl->m_Flags = std::move(flags);
463 impl->m_Flags = std::move(flags);
464 }
464 }
465
465
466 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
466 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
467 {
467 {
468 // Uses delegate to create the qcpplot components according to the variable
468 // Uses delegate to create the qcpplot components according to the variable
469 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
469 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
470
470
471 // Sets graph properties
471 // Sets graph properties
472 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
472 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
473
473
474 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
474 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
475
475
476 setGraphRange(range);
476 setGraphRange(range);
477 // If the variable already has its data loaded, load its units and its range in the graph
477 // If the variable already has its data loaded, load its units and its range in the graph
478 if (variable->dataSeries() != nullptr) {
478 if (variable->dataSeries() != nullptr) {
479 impl->m_RenderingDelegate->setAxesUnits(*variable);
479 impl->m_RenderingDelegate->setAxesUnits(*variable);
480 }
480 }
481 else
481 else
482 {
482 {
483 auto context = new QObject{this};
483 auto context = new QObject{this};
484 connect(variable.get(), &Variable::updated, context,
484 connect(variable.get(), &Variable::updated, context,
485 [this, variable, context, range](QUuid)
485 [this, variable, context, range](QUuid)
486 {
486 {
487 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
487 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
488 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
488 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
489 delete context;
489 delete context;
490 }
490 }
491 );
491 );
492 }
492 }
493 //@TODO this is bad! when variable is moved to another graph it still fires
493 //@TODO this is bad! when variable is moved to another graph it still fires
494 // even if this has been deleted
494 // even if this has been deleted
495 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
495 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
496 this->onUpdateVarDisplaying(variable, range); // My bullshit
496 this->onUpdateVarDisplaying(variable, range); // My bullshit
497 emit variableAdded(variable);
497 emit variableAdded(variable);
498 }
498 }
499
499
500 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
500 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
501 {
501 {
502 // Each component associated to the variable :
502 // Each component associated to the variable :
503 // - is removed from qcpplot (which deletes it)
503 // - is removed from qcpplot (which deletes it)
504 // - is no longer referenced in the map
504 // - is no longer referenced in the map
505 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
505 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
506 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
506 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
507 emit variableAboutToBeRemoved(variable);
507 emit variableAboutToBeRemoved(variable);
508
508
509 auto &plottablesMap = variableIt->second;
509 auto &plottablesMap = variableIt->second;
510
510
511 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
511 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
512 plottableIt != plottableEnd;) {
512 plottableIt != plottableEnd;) {
513 impl->m_plot->removePlottable(plottableIt->second);
513 impl->m_plot->removePlottable(plottableIt->second);
514 plottableIt = plottablesMap.erase(plottableIt);
514 plottableIt = plottablesMap.erase(plottableIt);
515 }
515 }
516
516
517 impl->m_VariableToPlotMultiMap.erase(variableIt);
517 impl->m_VariableToPlotMultiMap.erase(variableIt);
518 }
518 }
519
519
520 // Updates graph
520 // Updates graph
521 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
521 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
522 }
522 }
523
523
524 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
524 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
525 {
525 {
526 auto variables = std::vector<std::shared_ptr<Variable> >{};
526 auto variables = std::vector<std::shared_ptr<Variable> >{};
527 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
527 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
528 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
528 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
529 variables.push_back(it->first);
529 variables.push_back(it->first);
530 }
530 }
531
531
532 return variables;
532 return variables;
533 }
533 }
534
534
535 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
535 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
536 {
536 {
537 if (!variable) {
537 if (!variable) {
538 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
538 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
539 return;
539 return;
540 }
540 }
541
541
542 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
542 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
543 }
543 }
544
544
545 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
545 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
546 {
546 {
547 auto graphRange = impl->m_plot->xAxis->range();
547 auto graphRange = impl->m_plot->xAxis->range();
548 return DateTimeRange{graphRange.lower, graphRange.upper};
548 return DateTimeRange{graphRange.lower, graphRange.upper};
549 }
549 }
550
550
551 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool updateVar, bool forward)
551 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool updateVar, bool forward)
552 {
552 {
553 auto oldRange = graphRange();
554 impl->setRange(range, updateVar);
553 impl->setRange(range, updateVar);
555 if(forward)
554 if(forward)
556 {
555 {
557 auto newRange = graphRange();
556 emit this->setrange_sig(this->graphRange(), true, false);
558 if(auto tf = DateTimeRangeHelper::computeTransformation(oldRange,newRange))
559 emit this->transform_sig(tf.value(), false);
560 }
557 }
561
558
562 }
559 }
563
560
564 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
561 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
565 {
562 {
566 impl->m_VariableAutoRangeOnInit = value;
563 impl->m_VariableAutoRangeOnInit = value;
567 }
564 }
568
565
569 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
566 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
570 {
567 {
571 QVector<DateTimeRange> ranges;
568 QVector<DateTimeRange> ranges;
572 for (auto zone : impl->m_SelectionZones) {
569 for (auto zone : impl->m_SelectionZones) {
573 ranges << zone->range();
570 ranges << zone->range();
574 }
571 }
575
572
576 return ranges;
573 return ranges;
577 }
574 }
578
575
579 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
576 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
580 {
577 {
581 for (const auto &range : ranges) {
578 for (const auto &range : ranges) {
582 // note: ownership is transfered to QCustomPlot
579 // note: ownership is transfered to QCustomPlot
583 auto zone = new VisualizationSelectionZoneItem(&plot());
580 auto zone = new VisualizationSelectionZoneItem(&plot());
584 zone->setRange(range.m_TStart, range.m_TEnd);
581 zone->setRange(range.m_TStart, range.m_TEnd);
585 impl->addSelectionZone(zone);
582 impl->addSelectionZone(zone);
586 }
583 }
587
584
588 plot().replot(QCustomPlot::rpQueuedReplot);
585 plot().replot(QCustomPlot::rpQueuedReplot);
589 }
586 }
590
587
591 VisualizationSelectionZoneItem *
588 VisualizationSelectionZoneItem *
592 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
589 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
593 {
590 {
594 // note: ownership is transfered to QCustomPlot
591 // note: ownership is transfered to QCustomPlot
595 auto zone = new VisualizationSelectionZoneItem(&plot());
592 auto zone = new VisualizationSelectionZoneItem(&plot());
596 zone->setName(name);
593 zone->setName(name);
597 zone->setRange(range.m_TStart, range.m_TEnd);
594 zone->setRange(range.m_TStart, range.m_TEnd);
598 impl->addSelectionZone(zone);
595 impl->addSelectionZone(zone);
599
596
600 plot().replot(QCustomPlot::rpQueuedReplot);
597 plot().replot(QCustomPlot::rpQueuedReplot);
601
598
602 return zone;
599 return zone;
603 }
600 }
604
601
605 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
602 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
606 {
603 {
607 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
604 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
608
605
609 if (impl->m_HoveredZone == selectionZone) {
606 if (impl->m_HoveredZone == selectionZone) {
610 impl->m_HoveredZone = nullptr;
607 impl->m_HoveredZone = nullptr;
611 setCursor(Qt::ArrowCursor);
608 setCursor(Qt::ArrowCursor);
612 }
609 }
613
610
614 impl->m_SelectionZones.removeAll(selectionZone);
611 impl->m_SelectionZones.removeAll(selectionZone);
615 plot().removeItem(selectionZone);
612 plot().removeItem(selectionZone);
616 plot().replot(QCustomPlot::rpQueuedReplot);
613 plot().replot(QCustomPlot::rpQueuedReplot);
617 }
614 }
618
615
619 void VisualizationGraphWidget::undoZoom()
616 void VisualizationGraphWidget::undoZoom()
620 {
617 {
621 auto zoom = impl->m_ZoomStack.pop();
618 auto zoom = impl->m_ZoomStack.pop();
622 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
619 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
623 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
620 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
624
621
625 axisX->setRange(zoom.first);
622 axisX->setRange(zoom.first);
626 axisY->setRange(zoom.second);
623 axisY->setRange(zoom.second);
627
624
628 plot().replot(QCustomPlot::rpQueuedReplot);
625 plot().replot(QCustomPlot::rpQueuedReplot);
629 }
626 }
630
627
631 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward)
628 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward)
632 {
629 {
633 impl->zoom(factor, center, orientation);
630 impl->zoom(factor, center, orientation);
634 if(forward && orientation==Qt::Horizontal)
631 if(forward && orientation==Qt::Horizontal)
635 emit this->zoom_sig(factor, center, orientation, false);
632 emit this->setrange_sig(this->graphRange(), true, false);
636 }
633 }
637
634
638 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
635 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
639 {
636 {
640 impl->move(factor, orientation);
637 impl->move(factor, orientation);
641 if(forward)
638 if(forward)
642 emit this->move_sig(factor, orientation, false);
639 emit this->setrange_sig(this->graphRange(), true, false);
643 }
640 }
644
641
645 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
642 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
646 {
643 {
647 impl->move(dx, dy);
644 impl->move(dx, dy);
648 if(forward)
645 if(forward)
649 emit this->move_sig(dx, dy, false);
646 emit this->setrange_sig(this->graphRange(), true, false);
650 }
647 }
651
648
652 void VisualizationGraphWidget::transform(const DateTimeRangeTransformation &tranformation, bool forward)
649 void VisualizationGraphWidget::transform(const DateTimeRangeTransformation &tranformation, bool forward)
653 {
650 {
654 impl->transform(tranformation);
651 impl->transform(tranformation);
655 if(forward)
652 if(forward)
656 emit this->transform_sig(tranformation, false);
653 emit this->setrange_sig(this->graphRange(), true, false);
657 }
654 }
658
655
659 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
656 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
660 {
657 {
661 if (visitor) {
658 if (visitor) {
662 visitor->visit(this);
659 visitor->visit(this);
663 }
660 }
664 else {
661 else {
665 qCCritical(LOG_VisualizationGraphWidget())
662 qCCritical(LOG_VisualizationGraphWidget())
666 << tr("Can't visit widget : the visitor is null");
663 << tr("Can't visit widget : the visitor is null");
667 }
664 }
668 }
665 }
669
666
670 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
667 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
671 {
668 {
672 auto isSpectrogram = [](const auto &variable) {
669 auto isSpectrogram = [](const auto &variable) {
673 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
670 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
674 };
671 };
675
672
676 // - A spectrogram series can't be dropped on graph with existing plottables
673 // - A spectrogram series can't be dropped on graph with existing plottables
677 // - No data series can be dropped on graph with existing spectrogram series
674 // - No data series can be dropped on graph with existing spectrogram series
678 return isSpectrogram(variable)
675 return isSpectrogram(variable)
679 ? impl->m_VariableToPlotMultiMap.empty()
676 ? impl->m_VariableToPlotMultiMap.empty()
680 : std::none_of(
677 : std::none_of(
681 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
678 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
682 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
679 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
683 }
680 }
684
681
685 bool VisualizationGraphWidget::contains(const Variable &variable) const
682 bool VisualizationGraphWidget::contains(const Variable &variable) const
686 {
683 {
687 // Finds the variable among the keys of the map
684 // Finds the variable among the keys of the map
688 auto variablePtr = &variable;
685 auto variablePtr = &variable;
689 auto findVariable
686 auto findVariable
690 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
687 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
691
688
692 auto end = impl->m_VariableToPlotMultiMap.cend();
689 auto end = impl->m_VariableToPlotMultiMap.cend();
693 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
690 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
694 return it != end;
691 return it != end;
695 }
692 }
696
693
697 QString VisualizationGraphWidget::name() const
694 QString VisualizationGraphWidget::name() const
698 {
695 {
699 return impl->m_Name;
696 return impl->m_Name;
700 }
697 }
701
698
702 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
699 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
703 {
700 {
704 auto mimeData = new QMimeData;
701 auto mimeData = new QMimeData;
705
702
706 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
703 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
707 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
704 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
708 && selectionZoneItemUnderCursor) {
705 && selectionZoneItemUnderCursor) {
709 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
706 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
710 selectionZoneItemUnderCursor->range()));
707 selectionZoneItemUnderCursor->range()));
711 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
708 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
712 selectionZoneItemUnderCursor->range()));
709 selectionZoneItemUnderCursor->range()));
713 }
710 }
714 else {
711 else {
715 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
712 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
716
713
717 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
714 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
718 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
715 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
719 }
716 }
720
717
721 return mimeData;
718 return mimeData;
722 }
719 }
723
720
724 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
721 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
725 {
722 {
726 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
723 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
727 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
724 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
728 && selectionZoneItemUnderCursor) {
725 && selectionZoneItemUnderCursor) {
729
726
730 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
727 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
731 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
728 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
732
729
733 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
730 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
734 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
731 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
735 .toSize();
732 .toSize();
736
733
737 auto pixmap = QPixmap(zoneSize);
734 auto pixmap = QPixmap(zoneSize);
738 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
735 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
739
736
740 return pixmap;
737 return pixmap;
741 }
738 }
742
739
743 return QPixmap();
740 return QPixmap();
744 }
741 }
745
742
746 bool VisualizationGraphWidget::isDragAllowed() const
743 bool VisualizationGraphWidget::isDragAllowed() const
747 {
744 {
748 return true;
745 return true;
749 }
746 }
750
747
751 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
748 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
752 {
749 {
753 if (highlighted) {
750 if (highlighted) {
754 plot().setBackground(QBrush(QColor("#BBD5EE")));
751 plot().setBackground(QBrush(QColor("#BBD5EE")));
755 }
752 }
756 else {
753 else {
757 plot().setBackground(QBrush(Qt::white));
754 plot().setBackground(QBrush(Qt::white));
758 }
755 }
759
756
760 plot().update();
757 plot().update();
761 }
758 }
762
759
763 void VisualizationGraphWidget::addVerticalCursor(double time)
760 void VisualizationGraphWidget::addVerticalCursor(double time)
764 {
761 {
765 impl->m_VerticalCursor->setPosition(time);
762 impl->m_VerticalCursor->setPosition(time);
766 impl->m_VerticalCursor->setVisible(true);
763 impl->m_VerticalCursor->setVisible(true);
767
764
768 auto text
765 auto text
769 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
766 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
770 impl->m_VerticalCursor->setLabelText(text);
767 impl->m_VerticalCursor->setLabelText(text);
771 }
768 }
772
769
773 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
770 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
774 {
771 {
775 impl->m_VerticalCursor->setAbsolutePosition(position);
772 impl->m_VerticalCursor->setAbsolutePosition(position);
776 impl->m_VerticalCursor->setVisible(true);
773 impl->m_VerticalCursor->setVisible(true);
777
774
778 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
775 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
779 auto text
776 auto text
780 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
777 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
781 impl->m_VerticalCursor->setLabelText(text);
778 impl->m_VerticalCursor->setLabelText(text);
782 }
779 }
783
780
784 void VisualizationGraphWidget::removeVerticalCursor()
781 void VisualizationGraphWidget::removeVerticalCursor()
785 {
782 {
786 impl->m_VerticalCursor->setVisible(false);
783 impl->m_VerticalCursor->setVisible(false);
787 plot().replot(QCustomPlot::rpQueuedReplot);
784 plot().replot(QCustomPlot::rpQueuedReplot);
788 }
785 }
789
786
790 void VisualizationGraphWidget::addHorizontalCursor(double value)
787 void VisualizationGraphWidget::addHorizontalCursor(double value)
791 {
788 {
792 impl->m_HorizontalCursor->setPosition(value);
789 impl->m_HorizontalCursor->setPosition(value);
793 impl->m_HorizontalCursor->setVisible(true);
790 impl->m_HorizontalCursor->setVisible(true);
794 impl->m_HorizontalCursor->setLabelText(QString::number(value));
791 impl->m_HorizontalCursor->setLabelText(QString::number(value));
795 }
792 }
796
793
797 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
794 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
798 {
795 {
799 impl->m_HorizontalCursor->setAbsolutePosition(position);
796 impl->m_HorizontalCursor->setAbsolutePosition(position);
800 impl->m_HorizontalCursor->setVisible(true);
797 impl->m_HorizontalCursor->setVisible(true);
801
798
802 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
799 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
803 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
800 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
804 }
801 }
805
802
806 void VisualizationGraphWidget::removeHorizontalCursor()
803 void VisualizationGraphWidget::removeHorizontalCursor()
807 {
804 {
808 impl->m_HorizontalCursor->setVisible(false);
805 impl->m_HorizontalCursor->setVisible(false);
809 plot().replot(QCustomPlot::rpQueuedReplot);
806 plot().replot(QCustomPlot::rpQueuedReplot);
810 }
807 }
811
808
812 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
809 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
813 {
810 {
814 Q_UNUSED(event);
811 Q_UNUSED(event);
815
812
816 for (auto i : impl->m_SelectionZones) {
813 for (auto i : impl->m_SelectionZones) {
817 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
814 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
818 }
815 }
819
816
820 // Prevents that all variables will be removed from graph when it will be closed
817 // Prevents that all variables will be removed from graph when it will be closed
821 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
818 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
822 emit variableAboutToBeRemoved(variableEntry.first);
819 emit variableAboutToBeRemoved(variableEntry.first);
823 }
820 }
824 }
821 }
825
822
826 void VisualizationGraphWidget::enterEvent(QEvent *event)
823 void VisualizationGraphWidget::enterEvent(QEvent *event)
827 {
824 {
828 Q_UNUSED(event);
825 Q_UNUSED(event);
829 impl->m_RenderingDelegate->showGraphOverlay(true);
826 impl->m_RenderingDelegate->showGraphOverlay(true);
830 }
827 }
831
828
832 void VisualizationGraphWidget::leaveEvent(QEvent *event)
829 void VisualizationGraphWidget::leaveEvent(QEvent *event)
833 {
830 {
834 Q_UNUSED(event);
831 Q_UNUSED(event);
835 impl->m_RenderingDelegate->showGraphOverlay(false);
832 impl->m_RenderingDelegate->showGraphOverlay(false);
836
833
837 if (auto parentZone = parentZoneWidget()) {
834 if (auto parentZone = parentZoneWidget()) {
838 parentZone->notifyMouseLeaveGraph(this);
835 parentZone->notifyMouseLeaveGraph(this);
839 }
836 }
840 else {
837 else {
841 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
838 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
842 }
839 }
843
840
844 if (impl->m_HoveredZone) {
841 if (impl->m_HoveredZone) {
845 impl->m_HoveredZone->setHovered(false);
842 impl->m_HoveredZone->setHovered(false);
846 impl->m_HoveredZone = nullptr;
843 impl->m_HoveredZone = nullptr;
847 }
844 }
848 }
845 }
849
846
850 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
847 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
851 {
848 {
852 double factor;
849 double factor;
853 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
850 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
854 if (event->modifiers() == Qt::ControlModifier) {
851 if (event->modifiers() == Qt::ControlModifier) {
855 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
852 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
856 {
853 {
857 setCursor(Qt::SizeVerCursor);
854 setCursor(Qt::SizeVerCursor);
858 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
855 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
859 zoom(factor, event->pos().y(), Qt::Vertical);
856 zoom(factor, event->pos().y(), Qt::Vertical);
860 }
857 }
861 }
858 }
862 else if (event->modifiers() == Qt::ShiftModifier) {
859 else if (event->modifiers() == Qt::ShiftModifier) {
863 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
860 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
864 {
861 {
865 setCursor(Qt::SizeHorCursor);
862 setCursor(Qt::SizeHorCursor);
866 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
863 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
867 zoom(factor, event->pos().x(), Qt::Horizontal);
864 zoom(factor, event->pos().x(), Qt::Horizontal);
868 }
865 }
869 }
866 }
870 else {
867 else {
871 move(wheelSteps, Qt::Horizontal);
868 move(wheelSteps, Qt::Horizontal);
872 }
869 }
873 event->accept();
870 event->accept();
874 }
871 }
875
872
876
873
877
874
878 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
875 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
879 {
876 {
880 if(impl->isDrawingZoomRect())
877 if(impl->isDrawingZoomRect())
881 {
878 {
882 impl->updateZoomRect(event->pos());
879 impl->updateZoomRect(event->pos());
883 }
880 }
884 else if (impl->isDrawingZoneRect())
881 else if (impl->isDrawingZoneRect())
885 {
882 {
886 impl->updateZoneRect(event->pos());
883 impl->updateZoneRect(event->pos());
887 }
884 }
888 else if (event->buttons() == Qt::LeftButton)
885 else if (event->buttons() == Qt::LeftButton)
889 {
886 {
890 if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
887 if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
891 {
888 {
892 auto [dx,dy] = impl->moveGraph(event->pos());
889 auto [dx,dy] = impl->moveGraph(event->pos());
893 emit this->move_sig(dx,0., false); // don't sync Y transformations
890 emit this->setrange_sig(this->graphRange(), true, false);
894 }
891 }
895 else if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
892 else if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
896 {
893 {
897
894
898 }
895 }
899 }
896 }
900 else
897 else
901 {
898 {
902 impl->m_RenderingDelegate->updateTooltip(event);
899 impl->m_RenderingDelegate->updateTooltip(event);
903 }
900 }
904 //event->accept();
901 //event->accept();
905 QWidget::mouseMoveEvent(event);
902 QWidget::mouseMoveEvent(event);
906 }
903 }
907
904
908 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
905 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
909 {
906 {
910 if(impl->isDrawingZoomRect())
907 if(impl->isDrawingZoomRect())
911 {
908 {
912 auto oldRange = this->graphRange();
909 auto oldRange = this->graphRange();
913 impl->applyZoomRect();
910 impl->applyZoomRect();
914 auto newRange = this->graphRange();
911 auto newRange = this->graphRange();
915 if(auto tf = DateTimeRangeHelper::computeTransformation(oldRange,newRange))
912 if(auto tf = DateTimeRangeHelper::computeTransformation(oldRange,newRange))
916 emit this->transform_sig(tf.value(), false);
913 emit this->transform_sig(tf.value(), false);
917 }
914 }
918 else if(impl->isDrawingZoneRect())
915 else if(impl->isDrawingZoneRect())
919 {
916 {
920 impl->endDrawingZone();
917 impl->endDrawingZone();
921 }
918 }
922 else
919 else
923 {
920 {
924 setCursor(Qt::ArrowCursor);
921 setCursor(Qt::ArrowCursor);
925 }
922 }
926 event->accept();
923 event->accept();
927 }
924 }
928
925
929 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
926 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
930 {
927 {
931 if (event->button()==Qt::RightButton)
928 if (event->button()==Qt::RightButton)
932 {
929 {
933 onGraphMenuRequested(event->pos());
930 onGraphMenuRequested(event->pos());
934 }
931 }
935 else
932 else
936 {
933 {
937 auto selectedZone = impl->selectionZoneAt(event->pos());
934 auto selectedZone = impl->selectionZoneAt(event->pos());
938 switch (sqpApp->plotsInteractionMode())
935 switch (sqpApp->plotsInteractionMode())
939 {
936 {
940 case SqpApplication::PlotsInteractionMode::DragAndDrop :
937 case SqpApplication::PlotsInteractionMode::DragAndDrop :
941 break;
938 break;
942 case SqpApplication::PlotsInteractionMode::SelectionZones :
939 case SqpApplication::PlotsInteractionMode::SelectionZones :
943 impl->setSelectionZonesEditionEnabled(true);
940 impl->setSelectionZonesEditionEnabled(true);
944 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
941 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
945 {
942 {
946 selectedZone->setAssociatedEditedZones(parentVisualizationWidget()->selectionZoneManager().selectedItems());
943 selectedZone->setAssociatedEditedZones(parentVisualizationWidget()->selectionZoneManager().selectedItems());
947 }
944 }
948 else
945 else
949 {
946 {
950 if (!selectedZone)
947 if (!selectedZone)
951 {
948 {
952 parentVisualizationWidget()->selectionZoneManager().clearSelection();
949 parentVisualizationWidget()->selectionZoneManager().clearSelection();
953 impl->startDrawingZone(event->pos());
950 impl->startDrawingZone(event->pos());
954 }
951 }
955 else
952 else
956 {
953 {
957 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
954 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
958 }
955 }
959 }
956 }
960 break;
957 break;
961 case SqpApplication::PlotsInteractionMode::ZoomBox :
958 case SqpApplication::PlotsInteractionMode::ZoomBox :
962 impl->startDrawingRect(event->pos());
959 impl->startDrawingRect(event->pos());
963 break;
960 break;
964 default:
961 default:
965 if(auto item = impl->m_plot->itemAt(event->pos()))
962 if(auto item = impl->m_plot->itemAt(event->pos()))
966 {
963 {
967 emit impl->m_plot->itemClick(item,event);
964 emit impl->m_plot->itemClick(item,event);
968 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
965 if(qobject_cast<VisualizationSelectionZoneItem*>(item))
969 {
966 {
970 setCursor(Qt::ClosedHandCursor);
967 setCursor(Qt::ClosedHandCursor);
971 impl->enterPlotDrag(event->pos());
968 impl->enterPlotDrag(event->pos());
972 }
969 }
973 }
970 }
974 else
971 else
975 {
972 {
976 setCursor(Qt::ClosedHandCursor);
973 setCursor(Qt::ClosedHandCursor);
977 impl->enterPlotDrag(event->pos());
974 impl->enterPlotDrag(event->pos());
978 }
975 }
979 }
976 }
980 }
977 }
981 //event->accept();
978 //event->accept();
982 QWidget::mousePressEvent(event);
979 QWidget::mousePressEvent(event);
983 }
980 }
984
981
985 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
982 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
986 {
983 {
987 impl->m_RenderingDelegate->onMouseDoubleClick(event);
984 impl->m_RenderingDelegate->onMouseDoubleClick(event);
988 }
985 }
989
986
990 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
987 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
991 {
988 {
992 switch (event->key()) {
989 switch (event->key()) {
993 case Qt::Key_Control:
990 case Qt::Key_Control:
994 event->accept();
991 event->accept();
995 break;
992 break;
996 case Qt::Key_Shift:
993 case Qt::Key_Shift:
997 event->accept();
994 event->accept();
998 break;
995 break;
999 default:
996 default:
1000 QWidget::keyReleaseEvent(event);
997 QWidget::keyReleaseEvent(event);
1001 break;
998 break;
1002 }
999 }
1003 setCursor(Qt::ArrowCursor);
1000 setCursor(Qt::ArrowCursor);
1004 //event->accept();
1001 //event->accept();
1005 }
1002 }
1006
1003
1007 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
1004 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
1008 {
1005 {
1009 switch (event->key()) {
1006 switch (event->key()) {
1010 case Qt::Key_Control:
1007 case Qt::Key_Control:
1011 setCursor(Qt::CrossCursor);
1008 setCursor(Qt::CrossCursor);
1012 break;
1009 break;
1013 case Qt::Key_Shift:
1010 case Qt::Key_Shift:
1014 break;
1011 break;
1015 case Qt::Key_M:
1012 case Qt::Key_M:
1016 impl->rescaleY();
1013 impl->rescaleY();
1017 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1014 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1018 break;
1015 break;
1019 case Qt::Key_Left:
1016 case Qt::Key_Left:
1020 if (event->modifiers() != Qt::ControlModifier) {
1017 if (event->modifiers() != Qt::ControlModifier) {
1021 move(-0.1, Qt::Horizontal);
1018 move(-0.1, Qt::Horizontal);
1022 }
1019 }
1023 else {
1020 else {
1024 zoom(2, this->width() / 2, Qt::Horizontal);
1021 zoom(2, this->width() / 2, Qt::Horizontal);
1025 }
1022 }
1026 break;
1023 break;
1027 case Qt::Key_Right:
1024 case Qt::Key_Right:
1028 if (event->modifiers() != Qt::ControlModifier) {
1025 if (event->modifiers() != Qt::ControlModifier) {
1029 move(0.1, Qt::Horizontal);
1026 move(0.1, Qt::Horizontal);
1030 }
1027 }
1031 else {
1028 else {
1032 zoom(0.5, this->width() / 2, Qt::Horizontal);
1029 zoom(0.5, this->width() / 2, Qt::Horizontal);
1033 }
1030 }
1034 break;
1031 break;
1035 case Qt::Key_Up:
1032 case Qt::Key_Up:
1036 if (event->modifiers() != Qt::ControlModifier) {
1033 if (event->modifiers() != Qt::ControlModifier) {
1037 move(0.1, Qt::Vertical);
1034 move(0.1, Qt::Vertical);
1038 }
1035 }
1039 else {
1036 else {
1040 zoom(0.5, this->height() / 2, Qt::Vertical);
1037 zoom(0.5, this->height() / 2, Qt::Vertical);
1041 }
1038 }
1042 break;
1039 break;
1043 case Qt::Key_Down:
1040 case Qt::Key_Down:
1044 if (event->modifiers() != Qt::ControlModifier) {
1041 if (event->modifiers() != Qt::ControlModifier) {
1045 move(-0.1, Qt::Vertical);
1042 move(-0.1, Qt::Vertical);
1046 }
1043 }
1047 else {
1044 else {
1048 zoom(2, this->height() / 2, Qt::Vertical);
1045 zoom(2, this->height() / 2, Qt::Vertical);
1049 }
1046 }
1050 break;
1047 break;
1051 default:
1048 default:
1052 QWidget::keyPressEvent(event);
1049 QWidget::keyPressEvent(event);
1053 break;
1050 break;
1054 }
1051 }
1055 }
1052 }
1056
1053
1057 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
1054 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
1058 {
1055 {
1059 return *impl->m_plot;
1056 return *impl->m_plot;
1060 }
1057 }
1061
1058
1062 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
1059 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
1063 {
1060 {
1064 QMenu graphMenu{};
1061 QMenu graphMenu{};
1065
1062
1066 // Iterates on variables (unique keys)
1063 // Iterates on variables (unique keys)
1067 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1064 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1068 end = impl->m_VariableToPlotMultiMap.cend();
1065 end = impl->m_VariableToPlotMultiMap.cend();
1069 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
1066 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
1070 // 'Remove variable' action
1067 // 'Remove variable' action
1071 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1068 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1072 [this, var = it->first]() { removeVariable(var); });
1069 [this, var = it->first]() { removeVariable(var); });
1073 }
1070 }
1074
1071
1075 if (!impl->m_ZoomStack.isEmpty()) {
1072 if (!impl->m_ZoomStack.isEmpty()) {
1076 if (!graphMenu.isEmpty()) {
1073 if (!graphMenu.isEmpty()) {
1077 graphMenu.addSeparator();
1074 graphMenu.addSeparator();
1078 }
1075 }
1079
1076
1080 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1077 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1081 }
1078 }
1082
1079
1083 // Selection Zone Actions
1080 // Selection Zone Actions
1084 auto selectionZoneItem = impl->selectionZoneAt(pos);
1081 auto selectionZoneItem = impl->selectionZoneAt(pos);
1085 if (selectionZoneItem) {
1082 if (selectionZoneItem) {
1086 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1083 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1087 selectedItems.removeAll(selectionZoneItem);
1084 selectedItems.removeAll(selectionZoneItem);
1088 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1085 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1089
1086
1090 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1087 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1091 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
1088 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
1092 graphMenu.addSeparator();
1089 graphMenu.addSeparator();
1093 }
1090 }
1094
1091
1095 QHash<QString, QMenu *> subMenus;
1092 QHash<QString, QMenu *> subMenus;
1096 QHash<QString, bool> subMenusEnabled;
1093 QHash<QString, bool> subMenusEnabled;
1097 QHash<QString, FilteringAction *> filteredMenu;
1094 QHash<QString, FilteringAction *> filteredMenu;
1098
1095
1099 for (auto zoneAction : zoneActions) {
1096 for (auto zoneAction : zoneActions) {
1100
1097
1101 auto isEnabled = zoneAction->isEnabled(selectedItems);
1098 auto isEnabled = zoneAction->isEnabled(selectedItems);
1102
1099
1103 auto menu = &graphMenu;
1100 auto menu = &graphMenu;
1104 QString menuPath;
1101 QString menuPath;
1105 for (auto subMenuName : zoneAction->subMenuList()) {
1102 for (auto subMenuName : zoneAction->subMenuList()) {
1106 menuPath += '/';
1103 menuPath += '/';
1107 menuPath += subMenuName;
1104 menuPath += subMenuName;
1108
1105
1109 if (!subMenus.contains(menuPath)) {
1106 if (!subMenus.contains(menuPath)) {
1110 menu = menu->addMenu(subMenuName);
1107 menu = menu->addMenu(subMenuName);
1111 subMenus[menuPath] = menu;
1108 subMenus[menuPath] = menu;
1112 subMenusEnabled[menuPath] = isEnabled;
1109 subMenusEnabled[menuPath] = isEnabled;
1113 }
1110 }
1114 else {
1111 else {
1115 menu = subMenus.value(menuPath);
1112 menu = subMenus.value(menuPath);
1116 if (isEnabled) {
1113 if (isEnabled) {
1117 // The sub menu is enabled if at least one of its actions is enabled
1114 // The sub menu is enabled if at least one of its actions is enabled
1118 subMenusEnabled[menuPath] = true;
1115 subMenusEnabled[menuPath] = true;
1119 }
1116 }
1120 }
1117 }
1121 }
1118 }
1122
1119
1123 FilteringAction *filterAction = nullptr;
1120 FilteringAction *filterAction = nullptr;
1124 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1121 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1125 filterAction = filteredMenu.value(menuPath);
1122 filterAction = filteredMenu.value(menuPath);
1126 if (!filterAction) {
1123 if (!filterAction) {
1127 filterAction = new FilteringAction{this};
1124 filterAction = new FilteringAction{this};
1128 filteredMenu[menuPath] = filterAction;
1125 filteredMenu[menuPath] = filterAction;
1129 menu->addAction(filterAction);
1126 menu->addAction(filterAction);
1130 }
1127 }
1131 }
1128 }
1132
1129
1133 auto action = menu->addAction(zoneAction->name());
1130 auto action = menu->addAction(zoneAction->name());
1134 action->setEnabled(isEnabled);
1131 action->setEnabled(isEnabled);
1135 action->setShortcut(zoneAction->displayedShortcut());
1132 action->setShortcut(zoneAction->displayedShortcut());
1136 QObject::connect(action, &QAction::triggered,
1133 QObject::connect(action, &QAction::triggered,
1137 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1134 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1138
1135
1139 if (filterAction && zoneAction->isFilteringAllowed()) {
1136 if (filterAction && zoneAction->isFilteringAllowed()) {
1140 filterAction->addActionToFilter(action);
1137 filterAction->addActionToFilter(action);
1141 }
1138 }
1142 }
1139 }
1143
1140
1144 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1141 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1145 it.value()->setEnabled(subMenusEnabled[it.key()]);
1142 it.value()->setEnabled(subMenusEnabled[it.key()]);
1146 }
1143 }
1147 }
1144 }
1148
1145
1149 if (!graphMenu.isEmpty()) {
1146 if (!graphMenu.isEmpty()) {
1150 graphMenu.exec(QCursor::pos());
1147 graphMenu.exec(QCursor::pos());
1151 }
1148 }
1152 }
1149 }
1153
1150
1154 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1151 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1155 {
1152 {
1156 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1153 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1157 }
1154 }
1158
1155
1159 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1156 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1160 {
1157 {
1161 // Handles plot rendering when mouse is moving
1158 // Handles plot rendering when mouse is moving
1162 impl->m_RenderingDelegate->updateTooltip(event);
1159 impl->m_RenderingDelegate->updateTooltip(event);
1163
1160
1164 auto axisPos = impl->posToAxisPos(event->pos());
1161 auto axisPos = impl->posToAxisPos(event->pos());
1165
1162
1166 // Zoom box and zone drawing
1163 // Zoom box and zone drawing
1167 if (impl->m_DrawingZoomRect) {
1164 if (impl->m_DrawingZoomRect) {
1168 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1165 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1169 }
1166 }
1170 else if (impl->m_DrawingZone) {
1167 else if (impl->m_DrawingZone) {
1171 impl->m_DrawingZone->setEnd(axisPos.x());
1168 impl->m_DrawingZone->setEnd(axisPos.x());
1172 }
1169 }
1173
1170
1174 // Cursor
1171 // Cursor
1175 if (auto parentZone = parentZoneWidget()) {
1172 if (auto parentZone = parentZoneWidget()) {
1176 if (impl->pointIsInAxisRect(axisPos, plot())) {
1173 if (impl->pointIsInAxisRect(axisPos, plot())) {
1177 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1174 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1178 }
1175 }
1179 else {
1176 else {
1180 parentZone->notifyMouseLeaveGraph(this);
1177 parentZone->notifyMouseLeaveGraph(this);
1181 }
1178 }
1182 }
1179 }
1183
1180
1184 // Search for the selection zone under the mouse
1181 // Search for the selection zone under the mouse
1185 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1182 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1186 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1183 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1187 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1184 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1188
1185
1189 // Sets the appropriate cursor shape
1186 // Sets the appropriate cursor shape
1190 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1187 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1191 setCursor(cursorShape);
1188 setCursor(cursorShape);
1192
1189
1193 // Manages the hovered zone
1190 // Manages the hovered zone
1194 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1191 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1195 if (impl->m_HoveredZone) {
1192 if (impl->m_HoveredZone) {
1196 impl->m_HoveredZone->setHovered(false);
1193 impl->m_HoveredZone->setHovered(false);
1197 }
1194 }
1198 selectionZoneItemUnderCursor->setHovered(true);
1195 selectionZoneItemUnderCursor->setHovered(true);
1199 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1196 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1200 plot().replot(QCustomPlot::rpQueuedReplot);
1197 plot().replot(QCustomPlot::rpQueuedReplot);
1201 }
1198 }
1202 }
1199 }
1203 else {
1200 else {
1204 // There is no zone under the mouse or the interaction mode is not "selection zones"
1201 // There is no zone under the mouse or the interaction mode is not "selection zones"
1205 if (impl->m_HoveredZone) {
1202 if (impl->m_HoveredZone) {
1206 impl->m_HoveredZone->setHovered(false);
1203 impl->m_HoveredZone->setHovered(false);
1207 impl->m_HoveredZone = nullptr;
1204 impl->m_HoveredZone = nullptr;
1208 }
1205 }
1209
1206
1210 setCursor(Qt::ArrowCursor);
1207 setCursor(Qt::ArrowCursor);
1211 }
1208 }
1212
1209
1213 impl->m_HasMovedMouse = true;
1210 impl->m_HasMovedMouse = true;
1214 VisualizationDragWidget::mouseMoveEvent(event);
1211 VisualizationDragWidget::mouseMoveEvent(event);
1215 }
1212 }
1216
1213
1217 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1214 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1218 {
1215 {
1219 // Processes event only if the wheel occurs on axis rect
1216 // Processes event only if the wheel occurs on axis rect
1220 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1217 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1221 return;
1218 return;
1222 }
1219 }
1223
1220
1224 auto value = event->angleDelta().x() + event->angleDelta().y();
1221 auto value = event->angleDelta().x() + event->angleDelta().y();
1225 if (value != 0) {
1222 if (value != 0) {
1226
1223
1227 auto direction = value > 0 ? 1.0 : -1.0;
1224 auto direction = value > 0 ? 1.0 : -1.0;
1228 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1225 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1229 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1226 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1230 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1227 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1231
1228
1232 auto zoomOrientations = QFlags<Qt::Orientation>{};
1229 auto zoomOrientations = QFlags<Qt::Orientation>{};
1233 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1230 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1234 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1231 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1235
1232
1236 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1233 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1237
1234
1238 if (!isZoomX && !isZoomY) {
1235 if (!isZoomX && !isZoomY) {
1239 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1236 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1240 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1237 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1241
1238
1242 axis->setRange(axis->range() + diff);
1239 axis->setRange(axis->range() + diff);
1243
1240
1244 if (plot().noAntialiasingOnDrag()) {
1241 if (plot().noAntialiasingOnDrag()) {
1245 plot().setNotAntialiasedElements(QCP::aeAll);
1242 plot().setNotAntialiasedElements(QCP::aeAll);
1246 }
1243 }
1247
1244
1248 // plot().replot(QCustomPlot::rpQueuedReplot);
1245 // plot().replot(QCustomPlot::rpQueuedReplot);
1249 }
1246 }
1250 }
1247 }
1251 }
1248 }
1252
1249
1253 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1250 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1254 {
1251 {
1255 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1252 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1256 auto isSelectionZoneMode
1253 auto isSelectionZoneMode
1257 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1254 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1258 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1255 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1259
1256
1260 if (!isDragDropClick && isLeftClick) {
1257 if (!isDragDropClick && isLeftClick) {
1261 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1258 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1262 // Starts a zoom box
1259 // Starts a zoom box
1263 impl->startDrawingRect(event->pos());
1260 impl->startDrawingRect(event->pos());
1264 }
1261 }
1265 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1262 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1266 // Starts a new selection zone
1263 // Starts a new selection zone
1267 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1264 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1268 if (!zoneAtPos) {
1265 if (!zoneAtPos) {
1269 impl->startDrawingZone(event->pos());
1266 impl->startDrawingZone(event->pos());
1270 }
1267 }
1271 }
1268 }
1272 }
1269 }
1273
1270
1274
1271
1275 // Allows zone edition only in selection zone mode without drag&drop
1272 // Allows zone edition only in selection zone mode without drag&drop
1276 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1273 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1277
1274
1278 // Selection / Deselection
1275 // Selection / Deselection
1279 if (isSelectionZoneMode) {
1276 if (isSelectionZoneMode) {
1280 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1277 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1281 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1278 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1282
1279
1283
1280
1284 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1281 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1285 && !isMultiSelectionClick) {
1282 && !isMultiSelectionClick) {
1286 parentVisualizationWidget()->selectionZoneManager().select(
1283 parentVisualizationWidget()->selectionZoneManager().select(
1287 {selectionZoneItemUnderCursor});
1284 {selectionZoneItemUnderCursor});
1288 }
1285 }
1289 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1286 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1290 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1287 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1291 }
1288 }
1292 else {
1289 else {
1293 // No selection change
1290 // No selection change
1294 }
1291 }
1295
1292
1296 if (selectionZoneItemUnderCursor && isLeftClick) {
1293 if (selectionZoneItemUnderCursor && isLeftClick) {
1297 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1294 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1298 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1295 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1299 }
1296 }
1300 }
1297 }
1301
1298
1302
1299
1303 impl->m_HasMovedMouse = false;
1300 impl->m_HasMovedMouse = false;
1304 VisualizationDragWidget::mousePressEvent(event);
1301 VisualizationDragWidget::mousePressEvent(event);
1305 }
1302 }
1306
1303
1307 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1304 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1308 {
1305 {
1309 if (impl->m_DrawingZoomRect) {
1306 if (impl->m_DrawingZoomRect) {
1310
1307
1311 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1308 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1312 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1309 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1313
1310
1314 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1311 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1315 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1312 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1316
1313
1317 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1314 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1318 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1315 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1319
1316
1320 impl->removeDrawingRect();
1317 impl->removeDrawingRect();
1321
1318
1322 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1319 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1323 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1320 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1324 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1321 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1325 axisX->setRange(newAxisXRange);
1322 axisX->setRange(newAxisXRange);
1326 axisY->setRange(newAxisYRange);
1323 axisY->setRange(newAxisYRange);
1327
1324
1328 plot().replot(QCustomPlot::rpQueuedReplot);
1325 plot().replot(QCustomPlot::rpQueuedReplot);
1329 }
1326 }
1330 }
1327 }
1331
1328
1332 impl->endDrawingZone();
1329 impl->endDrawingZone();
1333
1330
1334 // Selection / Deselection
1331 // Selection / Deselection
1335 auto isSelectionZoneMode
1332 auto isSelectionZoneMode
1336 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1333 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1337 if (isSelectionZoneMode) {
1334 if (isSelectionZoneMode) {
1338 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1335 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1339 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1336 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1340 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1337 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1341 && !impl->m_HasMovedMouse) {
1338 && !impl->m_HasMovedMouse) {
1342
1339
1343 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1340 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1344 if (zonesUnderCursor.count() > 1) {
1341 if (zonesUnderCursor.count() > 1) {
1345 // There are multiple zones under the mouse.
1342 // There are multiple zones under the mouse.
1346 // Performs the selection with a selection dialog.
1343 // Performs the selection with a selection dialog.
1347 VisualizationMultiZoneSelectionDialog dialog{this};
1344 VisualizationMultiZoneSelectionDialog dialog{this};
1348 dialog.setZones(zonesUnderCursor);
1345 dialog.setZones(zonesUnderCursor);
1349 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1346 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1350 dialog.activateWindow();
1347 dialog.activateWindow();
1351 dialog.raise();
1348 dialog.raise();
1352 if (dialog.exec() == QDialog::Accepted) {
1349 if (dialog.exec() == QDialog::Accepted) {
1353 auto selection = dialog.selectedZones();
1350 auto selection = dialog.selectedZones();
1354
1351
1355 if (!isMultiSelectionClick) {
1352 if (!isMultiSelectionClick) {
1356 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1353 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1357 }
1354 }
1358
1355
1359 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1356 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1360 auto zone = it.key();
1357 auto zone = it.key();
1361 auto isSelected = it.value();
1358 auto isSelected = it.value();
1362 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1359 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1363 isSelected);
1360 isSelected);
1364
1361
1365 if (isSelected) {
1362 if (isSelected) {
1366 // Puts the zone on top of the stack so it can be moved or resized
1363 // Puts the zone on top of the stack so it can be moved or resized
1367 impl->moveSelectionZoneOnTop(zone, plot());
1364 impl->moveSelectionZoneOnTop(zone, plot());
1368 }
1365 }
1369 }
1366 }
1370 }
1367 }
1371 }
1368 }
1372 else {
1369 else {
1373 if (!isMultiSelectionClick) {
1370 if (!isMultiSelectionClick) {
1374 parentVisualizationWidget()->selectionZoneManager().select(
1371 parentVisualizationWidget()->selectionZoneManager().select(
1375 {selectionZoneItemUnderCursor});
1372 {selectionZoneItemUnderCursor});
1376 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1373 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1377 }
1374 }
1378 else {
1375 else {
1379 parentVisualizationWidget()->selectionZoneManager().setSelected(
1376 parentVisualizationWidget()->selectionZoneManager().setSelected(
1380 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1377 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1381 || event->button() == Qt::RightButton);
1378 || event->button() == Qt::RightButton);
1382 }
1379 }
1383 }
1380 }
1384 }
1381 }
1385 else {
1382 else {
1386 // No selection change
1383 // No selection change
1387 }
1384 }
1388 }
1385 }
1389 }
1386 }
1390
1387
1391 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1388 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1392 {
1389 {
1393 auto graphRange = impl->m_plot->xAxis->range();
1390 auto graphRange = impl->m_plot->xAxis->range();
1394 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1391 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1395
1392
1396 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1393 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1397 auto variable = variableEntry.first;
1394 auto variable = variableEntry.first;
1398 qCDebug(LOG_VisualizationGraphWidget())
1395 qCDebug(LOG_VisualizationGraphWidget())
1399 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1396 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1400 qCDebug(LOG_VisualizationGraphWidget())
1397 qCDebug(LOG_VisualizationGraphWidget())
1401 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1398 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1402 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1399 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1403 impl->updateData(variableEntry.second, variable, variable->range());
1400 impl->updateData(variableEntry.second, variable, variable->range());
1404 }
1401 }
1405 }
1402 }
1406 }
1403 }
1407
1404
1408 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1405 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1409 const DateTimeRange &range)
1406 const DateTimeRange &range)
1410 {
1407 {
1411 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1408 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1412 if (it != impl->m_VariableToPlotMultiMap.end()) {
1409 if (it != impl->m_VariableToPlotMultiMap.end()) {
1413 impl->updateData(it->second, variable, range);
1410 impl->updateData(it->second, variable, range);
1414 }
1411 }
1415 }
1412 }
1416
1413
1417 void VisualizationGraphWidget::variableUpdated(QUuid id)
1414 void VisualizationGraphWidget::variableUpdated(QUuid id)
1418 {
1415 {
1419 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1416 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1420 if (var->ID() == id) {
1417 if (var->ID() == id) {
1421 impl->updateData(plotables, var, this->graphRange());
1418 impl->updateData(plotables, var, this->graphRange());
1422 }
1419 }
1423 }
1420 }
1424 this->impl->rescaleY();
1421 this->impl->rescaleY();
1425 }
1422 }
1426
1423
1427 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable> & variable)
1424 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable> & variable)
1428 {
1425 {
1429 this->removeVariable(variable);
1426 this->removeVariable(variable);
1430 }
1427 }
@@ -1,675 +1,677
1 #include "Visualization/VisualizationZoneWidget.h"
1 #include "Visualization/VisualizationZoneWidget.h"
2
2
3 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 #include "Visualization/QCustomPlotSynchronizer.h"
4 #include "Visualization/QCustomPlotSynchronizer.h"
5 #include "Visualization/VisualizationGraphWidget.h"
5 #include "Visualization/VisualizationGraphWidget.h"
6 #include "Visualization/VisualizationWidget.h"
6 #include "Visualization/VisualizationWidget.h"
7 #include "ui_VisualizationZoneWidget.h"
7 #include "ui_VisualizationZoneWidget.h"
8
8
9 #include "Common/MimeTypesDef.h"
9 #include "Common/MimeTypesDef.h"
10 #include "Common/VisualizationDef.h"
10 #include "Common/VisualizationDef.h"
11
11
12 #include <Data/DateTimeRange.h>
12 #include <Data/DateTimeRange.h>
13 #include <Data/DateTimeRangeHelper.h>
13 #include <Data/DateTimeRangeHelper.h>
14 #include <DataSource/DataSourceController.h>
14 #include <DataSource/DataSourceController.h>
15 #include <Time/TimeController.h>
15 #include <Time/TimeController.h>
16 #include <Variable/Variable.h>
16 #include <Variable/Variable.h>
17 #include <Variable/VariableController2.h>
17 #include <Variable/VariableController2.h>
18
18
19 #include <Visualization/operations/FindVariableOperation.h>
19 #include <Visualization/operations/FindVariableOperation.h>
20
20
21 #include <DragAndDrop/DragDropGuiController.h>
21 #include <DragAndDrop/DragDropGuiController.h>
22 #include <QUuid>
22 #include <QUuid>
23 #include <SqpApplication.h>
23 #include <SqpApplication.h>
24 #include <cmath>
24 #include <cmath>
25
25
26 #include <QLayout>
26 #include <QLayout>
27 #include <QStyle>
27 #include <QStyle>
28
28
29 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
29 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
30
30
31 namespace {
31 namespace {
32
32
33 /**
33 /**
34 * Applies a function to all graphs of the zone represented by its layout
34 * Applies a function to all graphs of the zone represented by its layout
35 * @param layout the layout that contains graphs
35 * @param layout the layout that contains graphs
36 * @param fun the function to apply to each graph
36 * @param fun the function to apply to each graph
37 */
37 */
38 template <typename Fun>
38 template <typename Fun>
39 void processGraphs(QLayout &layout, Fun fun)
39 void processGraphs(QLayout &layout, Fun fun)
40 {
40 {
41 for (auto i = 0; i < layout.count(); ++i) {
41 for (auto i = 0; i < layout.count(); ++i) {
42 if (auto item = layout.itemAt(i)) {
42 if (auto item = layout.itemAt(i)) {
43 if (auto visualizationGraphWidget
43 if (auto visualizationGraphWidget
44 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
44 = qobject_cast<VisualizationGraphWidget *>(item->widget())) {
45 fun(*visualizationGraphWidget);
45 fun(*visualizationGraphWidget);
46 }
46 }
47 }
47 }
48 }
48 }
49 }
49 }
50
50
51 /// Generates a default name for a new graph, according to the number of graphs already displayed in
51 /// Generates a default name for a new graph, according to the number of graphs already displayed in
52 /// the zone
52 /// the zone
53 QString defaultGraphName(QLayout &layout)
53 QString defaultGraphName(QLayout &layout)
54 {
54 {
55 QSet<QString> existingNames;
55 QSet<QString> existingNames;
56 processGraphs(
56 processGraphs(
57 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
57 layout, [&existingNames](auto &graphWidget) { existingNames.insert(graphWidget.name()); });
58
58
59 int zoneNum = 1;
59 int zoneNum = 1;
60 QString name;
60 QString name;
61 do {
61 do {
62 name = QObject::tr("Graph ").append(QString::number(zoneNum));
62 name = QObject::tr("Graph ").append(QString::number(zoneNum));
63 ++zoneNum;
63 ++zoneNum;
64 } while (existingNames.contains(name));
64 } while (existingNames.contains(name));
65
65
66 return name;
66 return name;
67 }
67 }
68
68
69 } // namespace
69 } // namespace
70
70
71 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
71 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
72
72
73 explicit VisualizationZoneWidgetPrivate()
73 explicit VisualizationZoneWidgetPrivate()
74 : m_SynchronisationGroupId{QUuid::createUuid()},
74 : m_SynchronisationGroupId{QUuid::createUuid()},
75 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
75 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
76 {
76 {
77 }
77 }
78 QUuid m_SynchronisationGroupId;
78 QUuid m_SynchronisationGroupId;
79 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
79 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
80
80
81 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
81 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
82 void dropVariables(const std::vector<std::shared_ptr<Variable> > &variables, int index,
82 void dropVariables(const std::vector<std::shared_ptr<Variable> > &variables, int index,
83 VisualizationZoneWidget *zoneWidget);
83 VisualizationZoneWidget *zoneWidget);
84 void dropProducts(const QVariantList &productsData, int index,
84 void dropProducts(const QVariantList &productsData, int index,
85 VisualizationZoneWidget *zoneWidget);
85 VisualizationZoneWidget *zoneWidget);
86 };
86 };
87
87
88 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
88 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
89 : VisualizationDragWidget{parent},
89 : VisualizationDragWidget{parent},
90 ui{new Ui::VisualizationZoneWidget},
90 ui{new Ui::VisualizationZoneWidget},
91 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
91 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
92 {
92 {
93 ui->setupUi(this);
93 ui->setupUi(this);
94
94
95 ui->zoneNameLabel->setText(name);
95 ui->zoneNameLabel->setText(name);
96
96
97 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
97 ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph);
98 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
98 ui->dragDropContainer->setMimeType(MIME_TYPE_GRAPH,
99 VisualizationDragDropContainer::DropBehavior::Inserted);
99 VisualizationDragDropContainer::DropBehavior::Inserted);
100 ui->dragDropContainer->setMimeType(
100 ui->dragDropContainer->setMimeType(
101 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
101 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
102 ui->dragDropContainer->setMimeType(
102 ui->dragDropContainer->setMimeType(
103 MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
103 MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
104 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
104 ui->dragDropContainer->setMimeType(MIME_TYPE_TIME_RANGE,
105 VisualizationDragDropContainer::DropBehavior::Merged);
105 VisualizationDragDropContainer::DropBehavior::Merged);
106 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
106 ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE,
107 VisualizationDragDropContainer::DropBehavior::Forbidden);
107 VisualizationDragDropContainer::DropBehavior::Forbidden);
108 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
108 ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE,
109 VisualizationDragDropContainer::DropBehavior::Forbidden);
109 VisualizationDragDropContainer::DropBehavior::Forbidden);
110 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
110 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
111 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
111 return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData,
112 ui->dragDropContainer);
112 ui->dragDropContainer);
113 });
113 });
114
114
115 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
115 auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) {
116 if (!mimeData) {
116 if (!mimeData) {
117 return false;
117 return false;
118 }
118 }
119
119
120 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
120 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
121 auto variables = sqpApp->variableController().variables(
121 auto variables = sqpApp->variableController().variables(
122 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
122 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
123
123
124 if (variables.size() != 1) {
124 if (variables.size() != 1) {
125 return false;
125 return false;
126 }
126 }
127 auto variable = variables.front();
127 auto variable = variables.front();
128
128
129 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
129 if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget *>(dragWidget)) {
130 return graphWidget->canDrop(*variable);
130 return graphWidget->canDrop(*variable);
131 }
131 }
132 }
132 }
133
133
134 return true;
134 return true;
135 };
135 };
136 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
136 ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun);
137
137
138 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
138 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
139 &VisualizationZoneWidget::dropMimeData);
139 &VisualizationZoneWidget::dropMimeData);
140 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
140 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
141 &VisualizationZoneWidget::dropMimeDataOnGraph);
141 &VisualizationZoneWidget::dropMimeDataOnGraph);
142
142
143 // 'Close' options : widget is deleted when closed
143 // 'Close' options : widget is deleted when closed
144 setAttribute(Qt::WA_DeleteOnClose);
144 setAttribute(Qt::WA_DeleteOnClose);
145 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
145 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
146 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
146 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
147
147
148 // Synchronisation id
148 // Synchronisation id
149 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
149 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
150 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
150 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
151 }
151 }
152
152
153 VisualizationZoneWidget::~VisualizationZoneWidget()
153 VisualizationZoneWidget::~VisualizationZoneWidget()
154 {
154 {
155 delete ui;
155 delete ui;
156 }
156 }
157
157
158 void VisualizationZoneWidget::setZoneRange(const DateTimeRange &range)
158 void VisualizationZoneWidget::setZoneRange(const DateTimeRange &range)
159 {
159 {
160 if (auto graph = firstGraph()) {
160 if (auto graph = firstGraph()) {
161 graph->setGraphRange(range);
161 graph->setGraphRange(range);
162 }
162 }
163 else {
163 else {
164 qCWarning(LOG_VisualizationZoneWidget())
164 qCWarning(LOG_VisualizationZoneWidget())
165 << tr("setZoneRange:Cannot set the range of an empty zone.");
165 << tr("setZoneRange:Cannot set the range of an empty zone.");
166 }
166 }
167 }
167 }
168
168
169 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
169 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
170 {
170 {
171 // Synchronize new graph with others in the zone
171 // Synchronize new graph with others in the zone
172 // impl->m_Synchronizer->addGraph(*graphWidget);
172 // impl->m_Synchronizer->addGraph(*graphWidget);
173
173
174 // ui->dragDropContainer->addDragWidget(graphWidget);
174 // ui->dragDropContainer->addDragWidget(graphWidget);
175 insertGraph(0,graphWidget);
175 insertGraph(0,graphWidget);
176
176
177 }
177 }
178
178
179 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
179 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
180 {
180 {
181 DEPRECATE(
181 DEPRECATE(
182 auto layout = ui->dragDropContainer->layout();
182 auto layout = ui->dragDropContainer->layout();
183 for(int i=0;i<layout->count();i++)
183 for(int i=0;i<layout->count();i++)
184 {
184 {
185 auto graph = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(i)->widget());
185 auto graph = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(i)->widget());
186 connect(graphWidget, &VisualizationGraphWidget::zoom_sig, graph, &VisualizationGraphWidget::zoom);
186 connect(graphWidget, &VisualizationGraphWidget::setrange_sig, graph, &VisualizationGraphWidget::setGraphRange);
187 connect(graphWidget, &VisualizationGraphWidget::transform_sig, graph, &VisualizationGraphWidget::transform);
187 connect(graph, &VisualizationGraphWidget::setrange_sig, graphWidget, &VisualizationGraphWidget::setGraphRange);
188
188 // connect(graphWidget, &VisualizationGraphWidget::zoom_sig, graph, &VisualizationGraphWidget::zoom);
189 connect(graphWidget, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move_sig),
189 // connect(graphWidget, &VisualizationGraphWidget::transform_sig, graph, &VisualizationGraphWidget::transform);
190 graph, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move));
190
191 connect(graphWidget, qOverload<double,double,bool>(&VisualizationGraphWidget::move_sig),
191 // connect(graphWidget, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move_sig),
192 graph, qOverload<double,double,bool>(&VisualizationGraphWidget::move));
192 // graph, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move));
193
193 // connect(graphWidget, qOverload<double,double,bool>(&VisualizationGraphWidget::move_sig),
194 connect(graph, &VisualizationGraphWidget::zoom_sig, graphWidget, &VisualizationGraphWidget::zoom);
194 // graph, qOverload<double,double,bool>(&VisualizationGraphWidget::move));
195 connect(graph, &VisualizationGraphWidget::transform_sig, graphWidget, &VisualizationGraphWidget::transform);
195
196
196 // connect(graph, &VisualizationGraphWidget::zoom_sig, graphWidget, &VisualizationGraphWidget::zoom);
197 connect(graph, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move_sig),
197 // connect(graph, &VisualizationGraphWidget::transform_sig, graphWidget, &VisualizationGraphWidget::transform);
198 graphWidget, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move));
198
199 connect(graph, qOverload<double,double,bool>(&VisualizationGraphWidget::move_sig),
199 // connect(graph, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move_sig),
200 graphWidget, qOverload<double,double,bool>(&VisualizationGraphWidget::move));
200 // graphWidget, qOverload<double,Qt::Orientation,bool>(&VisualizationGraphWidget::move));
201 // connect(graph, qOverload<double,double,bool>(&VisualizationGraphWidget::move_sig),
202 // graphWidget, qOverload<double,double,bool>(&VisualizationGraphWidget::move));
201 }
203 }
202 if(auto graph = firstGraph())
204 if(auto graph = firstGraph())
203 {
205 {
204 graphWidget->setGraphRange(graph->graphRange(), true);
206 graphWidget->setGraphRange(graph->graphRange(), true);
205 }
207 }
206 )
208 )
207
209
208 // Synchronize new graph with others in the zone
210 // Synchronize new graph with others in the zone
209 impl->m_Synchronizer->addGraph(*graphWidget);
211 impl->m_Synchronizer->addGraph(*graphWidget);
210
212
211 ui->dragDropContainer->insertDragWidget(index, graphWidget);
213 ui->dragDropContainer->insertDragWidget(index, graphWidget);
212
214
213 }
215 }
214
216
215 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
217 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
216 {
218 {
217 return createGraph(variable, -1);
219 return createGraph(variable, -1);
218 }
220 }
219
221
220 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
222 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
221 int index)
223 int index)
222 {
224 {
223 auto graphWidget
225 auto graphWidget
224 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
226 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
225
227
226
228
227 // Set graph properties
229 // Set graph properties
228 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
230 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
229 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
231 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
230
232
231
233
232 // Lambda to synchronize zone widget
234 // Lambda to synchronize zone widget
233 auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange,
235 auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange,
234 const DateTimeRange &oldGraphRange) {
236 const DateTimeRange &oldGraphRange) {
235
237
236 auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange);
238 auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange);
237 auto frameLayout = ui->dragDropContainer->layout();
239 auto frameLayout = ui->dragDropContainer->layout();
238 for (auto i = 0; i < frameLayout->count(); ++i) {
240 for (auto i = 0; i < frameLayout->count(); ++i) {
239 auto graphChild
241 auto graphChild
240 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
242 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
241 if (graphChild && (graphChild != graphWidget)) {
243 if (graphChild && (graphChild != graphWidget)) {
242
244
243 auto graphChildRange = graphChild->graphRange();
245 auto graphChildRange = graphChild->graphRange();
244 switch (zoomType) {
246 switch (zoomType) {
245 case TransformationType::ZoomIn: {
247 case TransformationType::ZoomIn: {
246 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
248 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
247 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
249 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
248 graphChildRange.m_TStart += deltaLeft;
250 graphChildRange.m_TStart += deltaLeft;
249 graphChildRange.m_TEnd -= deltaRight;
251 graphChildRange.m_TEnd -= deltaRight;
250 break;
252 break;
251 }
253 }
252
254
253 case TransformationType::ZoomOut: {
255 case TransformationType::ZoomOut: {
254 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
256 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
255 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
257 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
256 graphChildRange.m_TStart -= deltaLeft;
258 graphChildRange.m_TStart -= deltaLeft;
257 graphChildRange.m_TEnd += deltaRight;
259 graphChildRange.m_TEnd += deltaRight;
258 break;
260 break;
259 }
261 }
260 case TransformationType::PanRight: {
262 case TransformationType::PanRight: {
261 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
263 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
262 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
264 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
263 graphChildRange.m_TStart += deltaLeft;
265 graphChildRange.m_TStart += deltaLeft;
264 graphChildRange.m_TEnd += deltaRight;
266 graphChildRange.m_TEnd += deltaRight;
265 break;
267 break;
266 }
268 }
267 case TransformationType::PanLeft: {
269 case TransformationType::PanLeft: {
268 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
270 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
269 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
271 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
270 graphChildRange.m_TStart -= deltaLeft;
272 graphChildRange.m_TStart -= deltaLeft;
271 graphChildRange.m_TEnd -= deltaRight;
273 graphChildRange.m_TEnd -= deltaRight;
272 break;
274 break;
273 }
275 }
274 case TransformationType::Unknown: {
276 case TransformationType::Unknown: {
275 break;
277 break;
276 }
278 }
277 default:
279 default:
278 qCCritical(LOG_VisualizationZoneWidget())
280 qCCritical(LOG_VisualizationZoneWidget())
279 << tr("Impossible to synchronize: zoom type not take into account");
281 << tr("Impossible to synchronize: zoom type not take into account");
280 // No action
282 // No action
281 break;
283 break;
282 }
284 }
283 graphChild->setFlags(GraphFlag::DisableAll);
285 graphChild->setFlags(GraphFlag::DisableAll);
284 graphChild->setGraphRange(graphChildRange);
286 graphChild->setGraphRange(graphChildRange);
285 graphChild->setFlags(GraphFlag::EnableAll);
287 graphChild->setFlags(GraphFlag::EnableAll);
286 }
288 }
287 }
289 }
288 };
290 };
289
291
290 // connection for synchronization
292 // connection for synchronization
291 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
293 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
292 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
294 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
293 &VisualizationZoneWidget::onVariableAdded);
295 &VisualizationZoneWidget::onVariableAdded);
294 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
296 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
295 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
297 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
296
298
297 auto range = DateTimeRange{};
299 auto range = DateTimeRange{};
298 if (auto firstGraph = this->firstGraph()) {
300 if (auto firstGraph = this->firstGraph()) {
299 // Case of a new graph in a existant zone
301 // Case of a new graph in a existant zone
300 range = firstGraph->graphRange();
302 range = firstGraph->graphRange();
301 }
303 }
302 else {
304 else {
303 // Case of a new graph as the first of the zone
305 // Case of a new graph as the first of the zone
304 range = variable->range();
306 range = variable->range();
305 }
307 }
306
308
307 this->insertGraph(index, graphWidget);
309 this->insertGraph(index, graphWidget);
308
310
309 graphWidget->addVariable(variable, range);
311 graphWidget->addVariable(variable, range);
310 graphWidget->setYRange(variable);
312 graphWidget->setYRange(variable);
311
313
312 return graphWidget;
314 return graphWidget;
313 }
315 }
314
316
315 VisualizationGraphWidget *
317 VisualizationGraphWidget *
316 VisualizationZoneWidget::createGraph(const std::vector<std::shared_ptr<Variable> > variables, int index)
318 VisualizationZoneWidget::createGraph(const std::vector<std::shared_ptr<Variable> > variables, int index)
317 {
319 {
318 if (variables.empty()) {
320 if (variables.empty()) {
319 return nullptr;
321 return nullptr;
320 }
322 }
321
323
322 auto graphWidget = createGraph(variables.front(), index);
324 auto graphWidget = createGraph(variables.front(), index);
323 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
325 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
324 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
326 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
325 }
327 }
326
328
327 return graphWidget;
329 return graphWidget;
328 }
330 }
329
331
330 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
332 VisualizationGraphWidget *VisualizationZoneWidget::firstGraph() const
331 {
333 {
332 VisualizationGraphWidget *firstGraph = nullptr;
334 VisualizationGraphWidget *firstGraph = nullptr;
333 auto layout = ui->dragDropContainer->layout();
335 auto layout = ui->dragDropContainer->layout();
334 if (layout->count() > 0) {
336 if (layout->count() > 0) {
335 if (auto visualizationGraphWidget
337 if (auto visualizationGraphWidget
336 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
338 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
337 firstGraph = visualizationGraphWidget;
339 firstGraph = visualizationGraphWidget;
338 }
340 }
339 }
341 }
340
342
341 return firstGraph;
343 return firstGraph;
342 }
344 }
343
345
344 void VisualizationZoneWidget::closeAllGraphs()
346 void VisualizationZoneWidget::closeAllGraphs()
345 {
347 {
346 processGraphs(*ui->dragDropContainer->layout(),
348 processGraphs(*ui->dragDropContainer->layout(),
347 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
349 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
348 }
350 }
349
351
350 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
352 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
351 {
353 {
352 if (visitor) {
354 if (visitor) {
353 visitor->visitEnter(this);
355 visitor->visitEnter(this);
354
356
355 // Apply visitor to graph children: widgets different from graphs are not visited (no
357 // Apply visitor to graph children: widgets different from graphs are not visited (no
356 // action)
358 // action)
357 processGraphs(
359 processGraphs(
358 *ui->dragDropContainer->layout(),
360 *ui->dragDropContainer->layout(),
359 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
361 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
360
362
361 visitor->visitLeave(this);
363 visitor->visitLeave(this);
362 }
364 }
363 else {
365 else {
364 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
366 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
365 }
367 }
366 }
368 }
367
369
368 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
370 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
369 {
371 {
370 // A tab can always accomodate a variable
372 // A tab can always accomodate a variable
371 Q_UNUSED(variable);
373 Q_UNUSED(variable);
372 return true;
374 return true;
373 }
375 }
374
376
375 bool VisualizationZoneWidget::contains(const Variable &variable) const
377 bool VisualizationZoneWidget::contains(const Variable &variable) const
376 {
378 {
377 Q_UNUSED(variable);
379 Q_UNUSED(variable);
378 return false;
380 return false;
379 }
381 }
380
382
381 QString VisualizationZoneWidget::name() const
383 QString VisualizationZoneWidget::name() const
382 {
384 {
383 return ui->zoneNameLabel->text();
385 return ui->zoneNameLabel->text();
384 }
386 }
385
387
386 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
388 QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const
387 {
389 {
388 Q_UNUSED(position);
390 Q_UNUSED(position);
389
391
390 auto mimeData = new QMimeData;
392 auto mimeData = new QMimeData;
391 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
393 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
392
394
393 if (auto firstGraph = this->firstGraph()) {
395 if (auto firstGraph = this->firstGraph()) {
394 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
396 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
395 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
397 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
396 }
398 }
397
399
398 return mimeData;
400 return mimeData;
399 }
401 }
400
402
401 bool VisualizationZoneWidget::isDragAllowed() const
403 bool VisualizationZoneWidget::isDragAllowed() const
402 {
404 {
403 return true;
405 return true;
404 }
406 }
405
407
406 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
408 void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF &graphPosition,
407 const QPointF &plotPosition,
409 const QPointF &plotPosition,
408 VisualizationGraphWidget *graphWidget)
410 VisualizationGraphWidget *graphWidget)
409 {
411 {
410 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
412 processGraphs(*ui->dragDropContainer->layout(), [&graphPosition, &plotPosition, &graphWidget](
411 VisualizationGraphWidget &processedGraph) {
413 VisualizationGraphWidget &processedGraph) {
412
414
413 switch (sqpApp->plotsCursorMode()) {
415 switch (sqpApp->plotsCursorMode()) {
414 case SqpApplication::PlotsCursorMode::Vertical:
416 case SqpApplication::PlotsCursorMode::Vertical:
415 processedGraph.removeHorizontalCursor();
417 processedGraph.removeHorizontalCursor();
416 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
418 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
417 break;
419 break;
418 case SqpApplication::PlotsCursorMode::Temporal:
420 case SqpApplication::PlotsCursorMode::Temporal:
419 processedGraph.addVerticalCursor(plotPosition.x());
421 processedGraph.addVerticalCursor(plotPosition.x());
420 processedGraph.removeHorizontalCursor();
422 processedGraph.removeHorizontalCursor();
421 break;
423 break;
422 case SqpApplication::PlotsCursorMode::Horizontal:
424 case SqpApplication::PlotsCursorMode::Horizontal:
423 processedGraph.removeVerticalCursor();
425 processedGraph.removeVerticalCursor();
424 if (&processedGraph == graphWidget) {
426 if (&processedGraph == graphWidget) {
425 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
427 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
426 }
428 }
427 else {
429 else {
428 processedGraph.removeHorizontalCursor();
430 processedGraph.removeHorizontalCursor();
429 }
431 }
430 break;
432 break;
431 case SqpApplication::PlotsCursorMode::Cross:
433 case SqpApplication::PlotsCursorMode::Cross:
432 if (&processedGraph == graphWidget) {
434 if (&processedGraph == graphWidget) {
433 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
435 processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x());
434 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
436 processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y());
435 }
437 }
436 else {
438 else {
437 processedGraph.removeHorizontalCursor();
439 processedGraph.removeHorizontalCursor();
438 processedGraph.removeVerticalCursor();
440 processedGraph.removeVerticalCursor();
439 }
441 }
440 break;
442 break;
441 case SqpApplication::PlotsCursorMode::NoCursor:
443 case SqpApplication::PlotsCursorMode::NoCursor:
442 processedGraph.removeHorizontalCursor();
444 processedGraph.removeHorizontalCursor();
443 processedGraph.removeVerticalCursor();
445 processedGraph.removeVerticalCursor();
444 break;
446 break;
445 }
447 }
446
448
447
449
448 });
450 });
449 }
451 }
450
452
451 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
453 void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget *graphWidget)
452 {
454 {
453 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
455 processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &processedGraph) {
454 processedGraph.removeHorizontalCursor();
456 processedGraph.removeHorizontalCursor();
455 processedGraph.removeVerticalCursor();
457 processedGraph.removeVerticalCursor();
456 });
458 });
457 }
459 }
458
460
459 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
461 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
460 {
462 {
461 // Closes graphs in the zone
463 // Closes graphs in the zone
462 processGraphs(*ui->dragDropContainer->layout(),
464 processGraphs(*ui->dragDropContainer->layout(),
463 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
465 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
464
466
465 // Delete synchronization group from variable controller
467 // Delete synchronization group from variable controller
466 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
468 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
467 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
469 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
468
470
469 QWidget::closeEvent(event);
471 QWidget::closeEvent(event);
470 }
472 }
471
473
472 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
474 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
473 {
475 {
474 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
476 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
475 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
477 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
476 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
478 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
477 }
479 }
478
480
479 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
481 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
480 {
482 {
481 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
483 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
482 Q_ARG(std::shared_ptr<Variable>, variable),
484 Q_ARG(std::shared_ptr<Variable>, variable),
483 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
485 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
484 }
486 }
485
487
486 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
488 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
487 {
489 {
488 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
490 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
489 impl->dropGraph(index, this);
491 impl->dropGraph(index, this);
490 }
492 }
491 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
493 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
492 auto variables = sqpApp->variableController().variables(
494 auto variables = sqpApp->variableController().variables(
493 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
495 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
494 impl->dropVariables(variables, index, this);
496 impl->dropVariables(variables, index, this);
495 }
497 }
496 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
498 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
497 auto products = sqpApp->dataSourceController().productsDataForMimeData(
499 auto products = sqpApp->dataSourceController().productsDataForMimeData(
498 mimeData->data(MIME_TYPE_PRODUCT_LIST));
500 mimeData->data(MIME_TYPE_PRODUCT_LIST));
499 impl->dropProducts(products, index, this);
501 impl->dropProducts(products, index, this);
500 }
502 }
501 else {
503 else {
502 qCWarning(LOG_VisualizationZoneWidget())
504 qCWarning(LOG_VisualizationZoneWidget())
503 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
505 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
504 }
506 }
505 }
507 }
506
508
507 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
509 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
508 const QMimeData *mimeData)
510 const QMimeData *mimeData)
509 {
511 {
510 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
512 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
511 if (!graphWidget) {
513 if (!graphWidget) {
512 qCWarning(LOG_VisualizationZoneWidget())
514 qCWarning(LOG_VisualizationZoneWidget())
513 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
515 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
514 "drop aborted");
516 "drop aborted");
515 Q_ASSERT(false);
517 Q_ASSERT(false);
516 return;
518 return;
517 }
519 }
518
520
519 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
521 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
520 auto variables = sqpApp->variableController().variables(
522 auto variables = sqpApp->variableController().variables(
521 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
523 Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST)));
522 for (const auto &var : variables) {
524 for (const auto &var : variables) {
523 graphWidget->addVariable(var, graphWidget->graphRange());
525 graphWidget->addVariable(var, graphWidget->graphRange());
524 }
526 }
525 }
527 }
526 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
528 else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
527 auto products = sqpApp->dataSourceController().productsDataForMimeData(
529 auto products = sqpApp->dataSourceController().productsDataForMimeData(
528 mimeData->data(MIME_TYPE_PRODUCT_LIST));
530 mimeData->data(MIME_TYPE_PRODUCT_LIST));
529
531
530 auto context = new QObject{this};
532 auto context = new QObject{this};
531 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
533 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
532 // BTW this is really dangerous, this assumes the next created variable will be this one...
534 // BTW this is really dangerous, this assumes the next created variable will be this one...
533 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
535 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
534 [this, graphWidget, context, range](auto variable) {
536 [this, graphWidget, context, range](auto variable) {
535 if(sqpApp->variableController().isReady(variable))
537 if(sqpApp->variableController().isReady(variable))
536 {
538 {
537 graphWidget->addVariable(variable, range);
539 graphWidget->addVariable(variable, range);
538 delete context;
540 delete context;
539 }
541 }
540 else
542 else
541 {
543 {
542 // -> this is pure insanity! this is a workaround to make a bad design work
544 // -> this is pure insanity! this is a workaround to make a bad design work
543 QObject::connect(variable.get(), &Variable::updated,context,
545 QObject::connect(variable.get(), &Variable::updated,context,
544 [graphWidget, context, range, variable]()
546 [graphWidget, context, range, variable]()
545 {
547 {
546 graphWidget->addVariable(variable, range);
548 graphWidget->addVariable(variable, range);
547 delete context;
549 delete context;
548 });
550 });
549 }
551 }
550
552
551 },
553 },
552 Qt::QueuedConnection);
554 Qt::QueuedConnection);
553
555
554 auto productData = products.first().toHash();
556 auto productData = products.first().toHash();
555 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
557 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
556 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
558 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
557 }
559 }
558 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
560 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
559 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
561 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
560 graphWidget->setGraphRange(range, true, true);
562 graphWidget->setGraphRange(range, true, true);
561 }
563 }
562 else {
564 else {
563 qCWarning(LOG_VisualizationZoneWidget())
565 qCWarning(LOG_VisualizationZoneWidget())
564 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
566 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
565 }
567 }
566 }
568 }
567
569
568 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
570 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
569 int index, VisualizationZoneWidget *zoneWidget)
571 int index, VisualizationZoneWidget *zoneWidget)
570 {
572 {
571 auto &helper = sqpApp->dragDropGuiController();
573 auto &helper = sqpApp->dragDropGuiController();
572
574
573 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
575 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
574 if (!graphWidget) {
576 if (!graphWidget) {
575 qCWarning(LOG_VisualizationZoneWidget())
577 qCWarning(LOG_VisualizationZoneWidget())
576 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
578 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
577 "found or invalid.");
579 "found or invalid.");
578 Q_ASSERT(false);
580 Q_ASSERT(false);
579 return;
581 return;
580 }
582 }
581
583
582 auto parentDragDropContainer
584 auto parentDragDropContainer
583 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
585 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
584 if (!parentDragDropContainer) {
586 if (!parentDragDropContainer) {
585 qCWarning(LOG_VisualizationZoneWidget())
587 qCWarning(LOG_VisualizationZoneWidget())
586 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
588 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
587 "the dropped graph is not found.");
589 "the dropped graph is not found.");
588 Q_ASSERT(false);
590 Q_ASSERT(false);
589 return;
591 return;
590 }
592 }
591
593
592 const auto &variables = graphWidget->variables();
594 const auto &variables = graphWidget->variables();
593
595
594 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) {
596 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) {
595 // The drop didn't occur in the same zone
597 // The drop didn't occur in the same zone
596
598
597 // Abort the requests for the variables (if any)
599 // Abort the requests for the variables (if any)
598 // Commented, because it's not sure if it's needed or not
600 // Commented, because it's not sure if it's needed or not
599 // for (const auto& var : variables)
601 // for (const auto& var : variables)
600 //{
602 //{
601 // sqpApp->variableController().onAbortProgressRequested(var);
603 // sqpApp->variableController().onAbortProgressRequested(var);
602 //}
604 //}
603
605
604 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
606 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
605 auto nbGraph = parentDragDropContainer->countDragWidget();
607 auto nbGraph = parentDragDropContainer->countDragWidget();
606 if (nbGraph == 1) {
608 if (nbGraph == 1) {
607 // This is the only graph in the previous zone, close the zone
609 // This is the only graph in the previous zone, close the zone
608 helper.delayedCloseWidget(previousParentZoneWidget);
610 helper.delayedCloseWidget(previousParentZoneWidget);
609 }
611 }
610 else {
612 else {
611 // Close the graph
613 // Close the graph
612 helper.delayedCloseWidget(graphWidget);
614 helper.delayedCloseWidget(graphWidget);
613 }
615 }
614
616
615 // Creates the new graph in the zone
617 // Creates the new graph in the zone
616 auto newGraphWidget = zoneWidget->createGraph(variables, index);
618 auto newGraphWidget = zoneWidget->createGraph(variables, index);
617 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
619 newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges());
618 }
620 }
619 else {
621 else {
620 // The drop occurred in the same zone or the graph is empty
622 // The drop occurred in the same zone or the graph is empty
621 // Simple move of the graph, no variable operation associated
623 // Simple move of the graph, no variable operation associated
622 parentDragDropContainer->layout()->removeWidget(graphWidget);
624 parentDragDropContainer->layout()->removeWidget(graphWidget);
623
625
624 if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
626 if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
625 // The graph is empty and dropped in a different zone.
627 // The graph is empty and dropped in a different zone.
626 // Take the range of the first graph in the zone (if existing).
628 // Take the range of the first graph in the zone (if existing).
627 auto layout = zoneWidget->ui->dragDropContainer->layout();
629 auto layout = zoneWidget->ui->dragDropContainer->layout();
628 if (layout->count() > 0) {
630 if (layout->count() > 0) {
629 if (auto visualizationGraphWidget
631 if (auto visualizationGraphWidget
630 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
632 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
631 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
633 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
632 }
634 }
633 }
635 }
634 }
636 }
635
637
636 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
638 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
637 }
639 }
638 }
640 }
639
641
640 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
642 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
641 const std::vector<std::shared_ptr<Variable> > &variables, int index,
643 const std::vector<std::shared_ptr<Variable> > &variables, int index,
642 VisualizationZoneWidget *zoneWidget)
644 VisualizationZoneWidget *zoneWidget)
643 {
645 {
644 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
646 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
645 // compatible variable here
647 // compatible variable here
646 if (variables.size() > 1) {
648 if (variables.size() > 1) {
647 return;
649 return;
648 }
650 }
649 zoneWidget->createGraph(variables, index);
651 zoneWidget->createGraph(variables, index);
650 }
652 }
651
653
652 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
654 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts(
653 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
655 const QVariantList &productsData, int index, VisualizationZoneWidget *zoneWidget)
654 {
656 {
655 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
657 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
656 // compatible variable here
658 // compatible variable here
657 if (productsData.count() != 1) {
659 if (productsData.count() != 1) {
658 qCWarning(LOG_VisualizationZoneWidget())
660 qCWarning(LOG_VisualizationZoneWidget())
659 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
661 << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation "
660 "aborted.");
662 "aborted.");
661 return;
663 return;
662 }
664 }
663
665
664 auto context = new QObject{zoneWidget};
666 auto context = new QObject{zoneWidget};
665 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
667 connect(&sqpApp->variableController(), &VariableController2::variableAdded, context,
666 [this, index, zoneWidget, context](auto variable) {
668 [this, index, zoneWidget, context](auto variable) {
667 zoneWidget->createGraph(variable, index);
669 zoneWidget->createGraph(variable, index);
668 delete context; // removes the connection
670 delete context; // removes the connection
669 },
671 },
670 Qt::QueuedConnection);
672 Qt::QueuedConnection);
671
673
672 auto productData = productsData.first().toHash();
674 auto productData = productsData.first().toHash();
673 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
675 QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable",
674 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
676 Qt::QueuedConnection, Q_ARG(QVariantHash, productData));
675 }
677 }
@@ -1,432 +1,438
1 #include "AmdaResultParserHelper.h"
1 #include "AmdaResultParserHelper.h"
2
2
3 #include <Common/DateUtils.h>
3 #include <Common/DateUtils.h>
4 #include <Common/SortUtils.h>
4 #include <Common/SortUtils.h>
5
5
6 #include <Data/DataSeriesUtils.h>
6 #include <Data/DataSeriesUtils.h>
7 #include <Data/ScalarSeries.h>
7 #include <Data/ScalarSeries.h>
8 #include <Data/SpectrogramSeries.h>
8 #include <Data/SpectrogramSeries.h>
9 #include <Data/Unit.h>
9 #include <Data/Unit.h>
10 #include <Data/VectorSeries.h>
10 #include <Data/VectorSeries.h>
11
11
12 #include <QtCore/QDateTime>
12 #include <QtCore/QDateTime>
13 #include <QtCore/QRegularExpression>
13 #include <QtCore/QRegularExpression>
14
14
15 #include <functional>
15 #include <functional>
16
16
17 Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
17 Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
18
18
19 namespace {
19 namespace {
20
20
21 // ///////// //
21 // ///////// //
22 // Constants //
22 // Constants //
23 // ///////// //
23 // ///////// //
24
24
25 /// Separator between values in a result line
25 /// Separator between values in a result line
26 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
26 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
27
27
28 /// Format for dates in result files
28 /// Format for dates in result files
29 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
29 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
30
30
31 // /////// //
31 // /////// //
32 // Methods //
32 // Methods //
33 // /////// //
33 // /////// //
34
34
35 /**
35 /**
36 * Checks that the properties contain a specific unit and that this unit is valid
36 * Checks that the properties contain a specific unit and that this unit is valid
37 * @param properties the properties map in which to search unit
37 * @param properties the properties map in which to search unit
38 * @param key the key to search for the unit in the properties
38 * @param key the key to search for the unit in the properties
39 * @param errorMessage the error message to log in case the unit is invalid
39 * @param errorMessage the error message to log in case the unit is invalid
40 * @return true if the unit is valid, false it it's invalid or was not found in the properties
40 * @return true if the unit is valid, false it it's invalid or was not found in the properties
41 */
41 */
42 bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
42 bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
43 {
43 {
44 auto unit = properties.value(key).value<Unit>();
44 auto unit = properties.value(key).value<Unit>();
45 if (unit.m_Name.isEmpty()) {
45 if (unit.m_Name.isEmpty()) {
46 qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
46 qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
47 return false;
47 return false;
48 }
48 }
49
49
50 return true;
50 return true;
51 }
51 }
52
52
53 QDateTime dateTimeFromString(const QString &stringDate) noexcept
53 QDateTime dateTimeFromString(const QString &stringDate) noexcept
54 {
54 {
55 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
55 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
56 return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
56 return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
57 #else
57 #else
58 return QDateTime::fromString(stringDate, DATE_FORMAT);
58 return QDateTime::fromString(stringDate, DATE_FORMAT);
59 #endif
59 #endif
60 }
60 }
61
61
62 /// Converts a string date to a double date
62 /// Converts a string date to a double date
63 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
63 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
64 double doubleDate(const QString &stringDate) noexcept
64 double doubleDate(const QString &stringDate) noexcept
65 {
65 {
66 // Format: yyyy-MM-ddThh:mm:ss.zzz
66 // Format: yyyy-MM-ddThh:mm:ss.zzz
67 auto dateTime = dateTimeFromString(stringDate);
67 auto dateTime = dateTimeFromString(stringDate);
68 dateTime.setTimeSpec(Qt::UTC);
68 dateTime.setTimeSpec(Qt::UTC);
69 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
69 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
70 : std::numeric_limits<double>::quiet_NaN();
70 : std::numeric_limits<double>::quiet_NaN();
71 }
71 }
72
72
73 /**
73 /**
74 * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it
74 * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it
75 * @param xAxisData the vector in which to store the x-axis data extracted
75 * @param xAxisData the vector in which to store the x-axis data extracted
76 * @param valuesData the vector in which to store the value extracted
76 * @param valuesData the vector in which to store the value extracted
77 * @param line the line to read to extract the property
77 * @param line the line to read to extract the property
78 * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three
78 * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three
79 * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read
79 * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read
80 * and inserted first, then the value of the first column, and finally the value of the second
80 * and inserted first, then the value of the first column, and finally the value of the second
81 * column.
81 * column.
82 * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read
82 * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read
83 * value is -1, then this value is considered as invalid and converted to NaN
83 * value is -1, then this value is considered as invalid and converted to NaN
84 */
84 */
85 void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData,
85 void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData,
86 const QString &line, const std::vector<int> &valuesIndexes,
86 const QString &line, const std::vector<int> &valuesIndexes,
87 double fillValue = std::numeric_limits<double>::quiet_NaN())
87 double fillValue = std::numeric_limits<double>::quiet_NaN())
88 {
88 {
89 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
89 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
90
90
91 // Checks that the line contains expected number of values + x-axis value
91 // Checks that the line contains expected number of values + x-axis value
92 if (static_cast<size_t>(lineData.size()) == valuesIndexes.size() + 1) {
92 if (static_cast<size_t>(lineData.size()) == valuesIndexes.size() + 1) {
93 // X : the data is converted from date to double (in secs)
93 // X : the data is converted from date to double (in secs)
94 auto x = doubleDate(lineData.at(0));
94 auto x = doubleDate(lineData.at(0));
95
95
96 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
96 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
97 if (!std::isnan(x)) {
97 if (!std::isnan(x)) {
98 xAxisData.push_back(x);
98 xAxisData.push_back(x);
99
99
100 // Values
100 // Values
101 for (auto valueIndex : valuesIndexes) {
101 for (auto valueIndex : valuesIndexes) {
102 bool valueOk;
102 bool valueOk;
103 // we use valueIndex + 1 to skip column 0 (x-axis value)
103 // we use valueIndex + 1 to skip column 0 (x-axis value)
104 auto value = lineData.at(valueIndex + 1).toDouble(&valueOk);
104 auto value = lineData.at(valueIndex + 1).toDouble(&valueOk);
105
105
106 if (!valueOk) {
106 if (!valueOk) {
107 qCWarning(LOG_AmdaResultParserHelper())
107 qCWarning(LOG_AmdaResultParserHelper())
108 << QObject::tr(
108 << QObject::tr(
109 "Value from (line %1, column %2) is invalid and will be "
109 "Value from (line %1, column %2) is invalid and will be "
110 "converted to NaN")
110 "converted to NaN")
111 .arg(line, valueIndex);
111 .arg(line, valueIndex);
112 value = std::numeric_limits<double>::quiet_NaN();
112 value = std::numeric_limits<double>::quiet_NaN();
113 }
113 }
114
114
115 // Handles fill value
115 // Handles fill value
116 if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) {
116 if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) {
117 value = std::numeric_limits<double>::quiet_NaN();
117 value = std::numeric_limits<double>::quiet_NaN();
118 }
118 }
119
119
120 valuesData.push_back(value);
120 valuesData.push_back(value);
121 }
121 }
122 }
122 }
123 else {
123 else {
124 qCWarning(LOG_AmdaResultParserHelper())
124 qCWarning(LOG_AmdaResultParserHelper())
125 << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line);
125 << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line);
126 }
126 }
127 }
127 }
128 else {
128 else {
129 qCWarning(LOG_AmdaResultParserHelper())
129 qCWarning(LOG_AmdaResultParserHelper())
130 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
130 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
131 }
131 }
132 }
132 }
133
133
134 /**
134 /**
135 * Reads a line from the AMDA file and tries to extract a property from it
135 * Reads a line from the AMDA file and tries to extract a property from it
136 * @param properties the properties map in which to put the property extracted from the line
136 * @param properties the properties map in which to put the property extracted from the line
137 * @param key the key to which the property is added in the properties map
137 * @param key the key to which the property is added in the properties map
138 * @param line the line to read to extract the property
138 * @param line the line to read to extract the property
139 * @param regexes the expected regexes to extract the property. If the line matches one regex, the
139 * @param regexes the expected regexes to extract the property. If the line matches one regex, the
140 * property is generated
140 * property is generated
141 * @param fun the function used to generate the property
141 * @param fun the function used to generate the property
142 * @return true if the property could be generated, false if the line does not match the regex, or
142 * @return true if the property could be generated, false if the line does not match the regex, or
143 * if a property has already been generated for the key
143 * if a property has already been generated for the key
144 */
144 */
145 template <typename GeneratePropertyFun>
145 template <typename GeneratePropertyFun>
146 bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
146 bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
147 const std::vector<QRegularExpression> &regexes, GeneratePropertyFun fun)
147 const std::vector<QRegularExpression> &regexes, GeneratePropertyFun fun)
148 {
148 {
149 if (properties.contains(key)) {
149 if (properties.contains(key)) {
150 return false;
150 return false;
151 }
151 }
152
152
153 // Searches for a match among all possible regexes
153 // Searches for a match among all possible regexes
154 auto hasMatch = false;
154 auto hasMatch = false;
155 for (auto regexIt = regexes.cbegin(), end = regexes.cend(); regexIt != end && !hasMatch;
155 for (auto regexIt = regexes.cbegin(), end = regexes.cend(); regexIt != end && !hasMatch;
156 ++regexIt) {
156 ++regexIt) {
157 auto match = regexIt->match(line);
157 auto match = regexIt->match(line);
158 auto hasMatch = match.hasMatch();
158 auto hasMatch = match.hasMatch();
159 if (hasMatch) {
159 if (hasMatch) {
160 properties.insert(key, fun(match));
160 properties.insert(key, fun(match));
161 }
161 }
162 }
162 }
163
163
164 return hasMatch;
164 return hasMatch;
165 }
165 }
166
166
167 /**
167 /**
168 * Reads a line from the AMDA file and tries to extract a data from it. Date is converted to double
168 * Reads a line from the AMDA file and tries to extract a data from it. Date is converted to double
169 * @sa tryReadProperty()
169 * @sa tryReadProperty()
170 */
170 */
171 bool tryReadDate(Properties &properties, const QString &key, const QString &line,
171 bool tryReadDate(Properties &properties, const QString &key, const QString &line,
172 const std::vector<QRegularExpression> &regexes, bool timeUnit = false)
172 const std::vector<QRegularExpression> &regexes, bool timeUnit = false)
173 {
173 {
174 return tryReadProperty(properties, key, line, regexes, [timeUnit](const auto &match) {
174 return tryReadProperty(properties, key, line, regexes, [timeUnit](const auto &match) {
175 return QVariant::fromValue(doubleDate(match.captured(1)));
175 return QVariant::fromValue(doubleDate(match.captured(1)));
176 });
176 });
177 }
177 }
178
178
179 /**
179 /**
180 * Reads a line from the AMDA file and tries to extract a double from it
180 * Reads a line from the AMDA file and tries to extract a double from it
181 * @sa tryReadProperty()
181 * @sa tryReadProperty()
182 */
182 */
183 bool tryReadDouble(Properties &properties, const QString &key, const QString &line,
183 bool tryReadDouble(Properties &properties, const QString &key, const QString &line,
184 const std::vector<QRegularExpression> &regexes)
184 const std::vector<QRegularExpression> &regexes)
185 {
185 {
186 return tryReadProperty(properties, key, line, regexes, [](const auto &match) {
186 return tryReadProperty(properties, key, line, regexes, [](const auto &match) {
187 bool ok;
187 bool ok;
188
188
189 // If the value can't be converted to double, it is set to NaN
189 // If the value can't be converted to double, it is set to NaN
190 auto doubleValue = match.captured(1).toDouble(&ok);
190 auto doubleValue = match.captured(1).toDouble(&ok);
191 if (!ok) {
191 if (!ok) {
192 doubleValue = std::numeric_limits<double>::quiet_NaN();
192 doubleValue = std::numeric_limits<double>::quiet_NaN();
193 }
193 }
194
194
195 return QVariant::fromValue(doubleValue);
195 return QVariant::fromValue(doubleValue);
196 });
196 });
197 }
197 }
198
198
199 /**
199 /**
200 * Reads a line from the AMDA file and tries to extract a vector of doubles from it
200 * Reads a line from the AMDA file and tries to extract a vector of doubles from it
201 * @param sep the separator of double values in the line
201 * @param sep the separator of double values in the line
202 * @sa tryReadProperty()
202 * @sa tryReadProperty()
203 */
203 */
204 bool tryReadDoubles(Properties &properties, const QString &key, const QString &line,
204 bool tryReadDoubles(Properties &properties, const QString &key, const QString &line,
205 const std::vector<QRegularExpression> &regexes,
205 const std::vector<QRegularExpression> &regexes,
206 const QString &sep = QStringLiteral(","))
206 const QString &sep = QStringLiteral(","))
207 {
207 {
208 return tryReadProperty(properties, key, line, regexes, [sep](const auto &match) {
208 return tryReadProperty(properties, key, line, regexes, [sep](const auto &match) {
209 std::vector<double> doubleValues{};
209 std::vector<double> doubleValues{};
210
210
211 // If the value can't be converted to double, it is set to NaN
211 // If the value can't be converted to double, it is set to NaN
212 auto values = match.captured(1).split(sep);
212 auto values = match.captured(1).split(sep);
213 for (auto value : values) {
213 for (auto value : values) {
214 bool ok;
214 bool ok;
215
215
216 auto doubleValue = value.toDouble(&ok);
216 auto doubleValue = value.toDouble(&ok);
217 if (!ok) {
217 if (!ok) {
218 doubleValue = std::numeric_limits<double>::quiet_NaN();
218 doubleValue = std::numeric_limits<double>::quiet_NaN();
219 }
219 }
220
220
221 doubleValues.push_back(doubleValue);
221 doubleValues.push_back(doubleValue);
222 }
222 }
223
223
224 return QVariant::fromValue(doubleValues);
224 return QVariant::fromValue(doubleValues);
225 });
225 });
226 }
226 }
227
227
228 /**
228 /**
229 * Reads a line from the AMDA file and tries to extract a unit from it
229 * Reads a line from the AMDA file and tries to extract a unit from it
230 * @sa tryReadProperty()
230 * @sa tryReadProperty()
231 */
231 */
232 bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
232 bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
233 const std::vector<QRegularExpression> &regexes, bool timeUnit = false)
233 const std::vector<QRegularExpression> &regexes, bool timeUnit = false)
234 {
234 {
235 return tryReadProperty(properties, key, line, regexes, [timeUnit](const auto &match) {
235 return tryReadProperty(properties, key, line, regexes, [timeUnit](const auto &match) {
236 return QVariant::fromValue(Unit{match.captured(1), timeUnit});
236 return QVariant::fromValue(Unit{match.captured(1), timeUnit});
237 });
237 });
238 }
238 }
239
239
240 } // namespace
240 } // namespace
241
241
242 // ////////////////// //
242 // ////////////////// //
243 // ScalarParserHelper //
243 // ScalarParserHelper //
244 // ////////////////// //
244 // ////////////////// //
245
245
246 bool ScalarParserHelper::checkProperties()
246 bool ScalarParserHelper::checkProperties()
247 {
247 {
248 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
248 if(auto hasXUnit = checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY, QObject::tr("The x-axis unit could not be found in the file"));!hasXUnit)
249 QObject::tr("The x-axis unit could not be found in the file"));
249 {
250 m_Properties[X_AXIS_UNIT_PROPERTY] = QVariant::fromValue(Unit{"s",true});
251 }
252 return true;
250 }
253 }
251
254
252 IDataSeries* ScalarParserHelper::createSeries()
255 IDataSeries* ScalarParserHelper::createSeries()
253 {
256 {
254 return new ScalarSeries(std::move(m_XAxisData), std::move(m_ValuesData),
257 return new ScalarSeries(std::move(m_XAxisData), std::move(m_ValuesData),
255 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
258 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
256 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
259 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
257 }
260 }
258
261
259 void ScalarParserHelper::readPropertyLine(const QString &line)
262 void ScalarParserHelper::readPropertyLine(const QString &line)
260 {
263 {
261 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line,
264 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line,
262 {DEFAULT_X_AXIS_UNIT_REGEX, ALTERNATIVE_X_AXIS_UNIT_REGEX}, true);
265 {DEFAULT_X_AXIS_UNIT_REGEX, ALTERNATIVE_X_AXIS_UNIT_REGEX}, true);
263 }
266 }
264
267
265 void ScalarParserHelper::readResultLine(const QString &line)
268 void ScalarParserHelper::readResultLine(const QString &line)
266 {
269 {
267 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
270 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
268 }
271 }
269
272
270 std::vector<int> ScalarParserHelper::valuesIndexes() const
273 std::vector<int> ScalarParserHelper::valuesIndexes() const
271 {
274 {
272 // Only one value to read
275 // Only one value to read
273 static auto result = std::vector<int>{0};
276 static auto result = std::vector<int>{0};
274 return result;
277 return result;
275 }
278 }
276
279
277 // /////////////////////// //
280 // /////////////////////// //
278 // SpectrogramParserHelper //
281 // SpectrogramParserHelper //
279 // /////////////////////// //
282 // /////////////////////// //
280
283
281 bool SpectrogramParserHelper::checkProperties()
284 bool SpectrogramParserHelper::checkProperties()
282 {
285 {
283 // Generates y-axis data from bands extracted (take the middle of the intervals)
286 // Generates y-axis data from bands extracted (take the middle of the intervals)
284 auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >();
287 auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >();
285 auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >();
288 auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >();
286
289
287 if (minBands.size() < 2 || minBands.size() != maxBands.size()) {
290 if (minBands.size() < 2 || minBands.size() != maxBands.size()) {
288 qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr(
291 qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr(
289 "Can't generate y-axis data from bands extracted: bands intervals are invalid");
292 "Can't generate y-axis data from bands extracted: bands intervals are invalid");
290 return false;
293 return false;
291 }
294 }
292
295
293 std::transform(
296 std::transform(
294 minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData),
297 minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData),
295 [](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; });
298 [](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; });
296
299
297 // Generates values indexes, i.e. the order in which each value will be retrieved (in ascending
300 // Generates values indexes, i.e. the order in which each value will be retrieved (in ascending
298 // order of the associated bands)
301 // order of the associated bands)
299 m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>());
302 m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>());
300
303
301 // Sorts y-axis data accoding to the ascending order
304 // Sorts y-axis data accoding to the ascending order
302 m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes);
305 m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes);
303
306
304 // Sets fill value
307 // Sets fill value
305 m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
308 m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
306
309
307 return true;
310 return true;
308 }
311 }
309
312
310 IDataSeries* SpectrogramParserHelper::createSeries()
313 IDataSeries* SpectrogramParserHelper::createSeries()
311 {
314 {
312 // Before creating the series, we handle its data holes
315 // Before creating the series, we handle its data holes
313 handleDataHoles();
316 handleDataHoles();
314
317
315 return new SpectrogramSeries(
318 return new SpectrogramSeries(
316 std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData),
319 std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData),
317 Unit{"t", true}, // x-axis unit is always a time unit
320 Unit{"t", true}, // x-axis unit is always a time unit
318 m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(),
321 m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(),
319 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>(),
322 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>(),
320 m_Properties.value(MIN_SAMPLING_PROPERTY).value<double>());
323 m_Properties.value(MIN_SAMPLING_PROPERTY).value<double>());
321 }
324 }
322
325
323 void SpectrogramParserHelper::readPropertyLine(const QString &line)
326 void SpectrogramParserHelper::readPropertyLine(const QString &line)
324 {
327 {
325 // Set of functions to test on the line to generate a property. If a function is valid (i.e. a
328 // Set of functions to test on the line to generate a property. If a function is valid (i.e. a
326 // property has been generated for the line), the line is treated as processed and the other
329 // property has been generated for the line), the line is treated as processed and the other
327 // functions are not called
330 // functions are not called
328 std::vector<std::function<bool()> > functions{
331 std::vector<std::function<bool()> > functions{
329 // values unit
332 // values unit
330 [&] {
333 [&] {
331 return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line,
334 return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line,
332 {SPECTROGRAM_VALUES_UNIT_REGEX});
335 {SPECTROGRAM_VALUES_UNIT_REGEX});
333 },
336 },
334 // y-axis unit
337 // y-axis unit
335 [&] {
338 [&] {
336 return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line,
339 return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line,
337 {SPECTROGRAM_Y_AXIS_UNIT_REGEX});
340 {SPECTROGRAM_Y_AXIS_UNIT_REGEX});
338 },
341 },
339 // min sampling
342 // min sampling
340 [&] {
343 [&] {
341 return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line,
344 return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line,
342 {SPECTROGRAM_MIN_SAMPLING_REGEX});
345 {SPECTROGRAM_MIN_SAMPLING_REGEX});
343 },
346 },
344 // max sampling
347 // max sampling
345 [&] {
348 [&] {
346 return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line,
349 return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line,
347 {SPECTROGRAM_MAX_SAMPLING_REGEX});
350 {SPECTROGRAM_MAX_SAMPLING_REGEX});
348 },
351 },
349 // fill value
352 // fill value
350 [&] {
353 [&] {
351 return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line,
354 return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line,
352 {SPECTROGRAM_FILL_VALUE_REGEX});
355 {SPECTROGRAM_FILL_VALUE_REGEX});
353 },
356 },
354 // min bounds of each band
357 // min bounds of each band
355 [&] {
358 [&] {
356 return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line,
359 return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line,
357 {SPECTROGRAM_MIN_BANDS_REGEX});
360 {SPECTROGRAM_MIN_BANDS_REGEX});
358 },
361 },
359 // max bounds of each band
362 // max bounds of each band
360 [&] {
363 [&] {
361 return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line,
364 return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line,
362 {SPECTROGRAM_MAX_BANDS_REGEX});
365 {SPECTROGRAM_MAX_BANDS_REGEX});
363 },
366 },
364 // start time of data
367 // start time of data
365 [&] {
368 [&] {
366 return tryReadDate(m_Properties, START_TIME_PROPERTY, line,
369 return tryReadDate(m_Properties, START_TIME_PROPERTY, line,
367 {SPECTROGRAM_START_TIME_REGEX});
370 {SPECTROGRAM_START_TIME_REGEX});
368 },
371 },
369 // end time of data
372 // end time of data
370 [&] {
373 [&] {
371 return tryReadDate(m_Properties, END_TIME_PROPERTY, line, {SPECTROGRAM_END_TIME_REGEX});
374 return tryReadDate(m_Properties, END_TIME_PROPERTY, line, {SPECTROGRAM_END_TIME_REGEX});
372 }};
375 }};
373
376
374 for (auto function : functions) {
377 for (auto function : functions) {
375 // Stops at the first function that is valid
378 // Stops at the first function that is valid
376 if (function()) {
379 if (function()) {
377 return;
380 return;
378 }
381 }
379 }
382 }
380 }
383 }
381
384
382 void SpectrogramParserHelper::readResultLine(const QString &line)
385 void SpectrogramParserHelper::readResultLine(const QString &line)
383 {
386 {
384 tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue);
387 tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue);
385 }
388 }
386
389
387 void SpectrogramParserHelper::handleDataHoles()
390 void SpectrogramParserHelper::handleDataHoles()
388 {
391 {
389 // Fills data holes according to the max resolution found in the AMDA file
392 // Fills data holes according to the max resolution found in the AMDA file
390 auto resolution = m_Properties.value(MAX_SAMPLING_PROPERTY).value<double>();
393 auto resolution = m_Properties.value(MAX_SAMPLING_PROPERTY).value<double>();
391 auto fillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
394 auto fillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
392 auto minBound = m_Properties.value(START_TIME_PROPERTY).value<double>();
395 auto minBound = m_Properties.value(START_TIME_PROPERTY).value<double>();
393 auto maxBound = m_Properties.value(END_TIME_PROPERTY).value<double>();
396 auto maxBound = m_Properties.value(END_TIME_PROPERTY).value<double>();
394
397
395 DataSeriesUtils::fillDataHoles(m_XAxisData, m_ValuesData, resolution, fillValue, minBound,
398 DataSeriesUtils::fillDataHoles(m_XAxisData, m_ValuesData, resolution, fillValue, minBound,
396 maxBound);
399 maxBound);
397 }
400 }
398
401
399 // ////////////////// //
402 // ////////////////// //
400 // VectorParserHelper //
403 // VectorParserHelper //
401 // ////////////////// //
404 // ////////////////// //
402
405
403 bool VectorParserHelper::checkProperties()
406 bool VectorParserHelper::checkProperties()
404 {
407 {
405 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
408 if(auto hasXUnit = checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY, QObject::tr("The x-axis unit could not be found in the file"));!hasXUnit)
406 QObject::tr("The x-axis unit could not be found in the file"));
409 {
410 m_Properties[X_AXIS_UNIT_PROPERTY] = QVariant::fromValue(Unit{"s",true});
411 }
412 return true;
407 }
413 }
408
414
409 IDataSeries* VectorParserHelper::createSeries()
415 IDataSeries* VectorParserHelper::createSeries()
410 {
416 {
411 return new VectorSeries(std::move(m_XAxisData), std::move(m_ValuesData),
417 return new VectorSeries(std::move(m_XAxisData), std::move(m_ValuesData),
412 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
418 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
413 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
419 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
414 }
420 }
415
421
416 void VectorParserHelper::readPropertyLine(const QString &line)
422 void VectorParserHelper::readPropertyLine(const QString &line)
417 {
423 {
418 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line,
424 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line,
419 {DEFAULT_X_AXIS_UNIT_REGEX, ALTERNATIVE_X_AXIS_UNIT_REGEX}, true);
425 {DEFAULT_X_AXIS_UNIT_REGEX, ALTERNATIVE_X_AXIS_UNIT_REGEX}, true);
420 }
426 }
421
427
422 void VectorParserHelper::readResultLine(const QString &line)
428 void VectorParserHelper::readResultLine(const QString &line)
423 {
429 {
424 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
430 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
425 }
431 }
426
432
427 std::vector<int> VectorParserHelper::valuesIndexes() const
433 std::vector<int> VectorParserHelper::valuesIndexes() const
428 {
434 {
429 // 3 values to read, in order in the file (x, y, z)
435 // 3 values to read, in order in the file (x, y, z)
430 static auto result = std::vector<int>{0, 1, 2};
436 static auto result = std::vector<int>{0, 1, 2};
431 return result;
437 return result;
432 }
438 }
General Comments 0
You need to be logged in to leave comments. Login now