##// END OF EJS Templates
Added shortcut 'l' to toggle Y axis log scale (broken for spectro)...
jeandet -
r1501:db2de8e31bd4
parent child
Show More
@@ -1,185 +1,187
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 <QUuid>
8 #include <QUuid>
9 #include <QWidget>
9 #include <QWidget>
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 Variable2;
21 class Variable2;
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 {
27 {
28 class VisualizationGraphWidget;
28 class VisualizationGraphWidget;
29 } // namespace Ui
29 } // namespace Ui
30
30
31 /// Defines options that can be associated with the graph
31 /// Defines options that can be associated with the graph
32 enum GraphFlag
32 enum GraphFlag
33 {
33 {
34 DisableAll = 0x0, ///< Disables acquisition and synchronization
34 DisableAll = 0x0, ///< Disables acquisition and synchronization
35 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
35 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
36 /// the acquisition of data
36 /// the acquisition of data
37 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
37 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
38 /// the call to the synchronization of the graphs contained in the
38 /// the call to the synchronization of the graphs contained in the
39 /// same zone of this graph
39 /// same zone of this graph
40 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
40 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
41 };
41 };
42
42
43 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
43 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
44 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
44 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
45
45
46 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget
46 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget
47 {
47 {
48 Q_OBJECT
48 Q_OBJECT
49
49
50 friend class QCustomPlotSynchronizer;
50 friend class QCustomPlotSynchronizer;
51 friend class VisualizationGraphRenderingDelegate;
51 friend class VisualizationGraphRenderingDelegate;
52
52
53 public:
53 public:
54 explicit VisualizationGraphWidget(const QString& name = {}, QWidget* parent = 0);
54 explicit VisualizationGraphWidget(const QString& name = {}, QWidget* parent = 0);
55 virtual ~VisualizationGraphWidget();
55 virtual ~VisualizationGraphWidget();
56
56
57 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
57 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
58 VisualizationZoneWidget* parentZoneWidget() const noexcept;
58 VisualizationZoneWidget* parentZoneWidget() const noexcept;
59
59
60 /// Returns the main VisualizationWidget which contains the graph or nullptr
60 /// Returns the main VisualizationWidget which contains the graph or nullptr
61 VisualizationWidget* parentVisualizationWidget() const;
61 VisualizationWidget* parentVisualizationWidget() const;
62
62
63 /// Sets graph options
63 /// Sets graph options
64 void setFlags(GraphFlags flags);
64 void setFlags(GraphFlags flags);
65
65
66 void addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range);
66 void addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range);
67
67
68 /// Removes a variable from the graph
68 /// Removes a variable from the graph
69 void removeVariable(std::shared_ptr<Variable2> variable) noexcept;
69 void removeVariable(std::shared_ptr<Variable2> variable) noexcept;
70
70
71 /// Returns the list of all variables used in the graph
71 /// Returns the list of all variables used in the graph
72 std::vector<std::shared_ptr<Variable2>> variables() const;
72 std::vector<std::shared_ptr<Variable2>> variables() const;
73
73
74 /// Sets the y-axis range based on the data of a variable
74 /// Sets the y-axis range based on the data of a variable
75 void setYRange(std::shared_ptr<Variable2> variable);
75 void setYRange(std::shared_ptr<Variable2> variable);
76 DateTimeRange graphRange() const noexcept;
76 DateTimeRange graphRange() const noexcept;
77 void setGraphRange(const DateTimeRange& range, bool updateVar = false, bool forward = false);
77 void setGraphRange(const DateTimeRange& range, bool updateVar = false, bool forward = false);
78 void setAutoRangeOnVariableInitialization(bool value);
78 void setAutoRangeOnVariableInitialization(bool value);
79
79
80 // Zones
80 // Zones
81 /// Returns the ranges of all the selection zones on the graph
81 /// Returns the ranges of all the selection zones on the graph
82 QVector<DateTimeRange> selectionZoneRanges() const;
82 QVector<DateTimeRange> selectionZoneRanges() const;
83 /// Adds new selection zones in the graph
83 /// Adds new selection zones in the graph
84 void addSelectionZones(const QVector<DateTimeRange>& ranges);
84 void addSelectionZones(const QVector<DateTimeRange>& ranges);
85 /// Adds a new selection zone in the graph
85 /// Adds a new selection zone in the graph
86 VisualizationSelectionZoneItem* addSelectionZone(
86 VisualizationSelectionZoneItem* addSelectionZone(
87 const QString& name, const DateTimeRange& range);
87 const QString& name, const DateTimeRange& range);
88 /// Removes the specified selection zone
88 /// Removes the specified selection zone
89 void removeSelectionZone(VisualizationSelectionZoneItem* selectionZone);
89 void removeSelectionZone(VisualizationSelectionZoneItem* selectionZone);
90
90
91 /// Undo the last zoom done with a zoom box
91 /// Undo the last zoom done with a zoom box
92 void undoZoom();
92 void undoZoom();
93
93
94 void zoom(double factor, int center, Qt::Orientation orientation, bool forward = true);
94 void zoom(double factor, int center, Qt::Orientation orientation, bool forward = true);
95 void move(double factor, Qt::Orientation orientation, bool forward = true);
95 void move(double factor, Qt::Orientation orientation, bool forward = true);
96 void move(double dx, double dy, bool forward = true);
96 void move(double dx, double dy, bool forward = true);
97 void transform(const DateTimeRangeTransformation& tranformation, bool forward = true);
97 void transform(const DateTimeRangeTransformation& tranformation, bool forward = true);
98
98
99 void toggle_y_log_scale();
100
99 // IVisualizationWidget interface
101 // IVisualizationWidget interface
100 void accept(IVisualizationWidgetVisitor* visitor) override;
102 void accept(IVisualizationWidgetVisitor* visitor) override;
101 bool canDrop(Variable2& variable) const override;
103 bool canDrop(Variable2& variable) const override;
102 bool contains(Variable2& variable) const override;
104 bool contains(Variable2& variable) const override;
103 QString name() const override;
105 QString name() const override;
104
106
105 // VisualisationDragWidget
107 // VisualisationDragWidget
106 QMimeData* mimeData(const QPoint& position) const override;
108 QMimeData* mimeData(const QPoint& position) const override;
107 QPixmap customDragPixmap(const QPoint& dragPosition) override;
109 QPixmap customDragPixmap(const QPoint& dragPosition) override;
108 bool isDragAllowed() const override;
110 bool isDragAllowed() const override;
109 void highlightForMerge(bool highlighted) override;
111 void highlightForMerge(bool highlighted) override;
110
112
111 // Cursors
113 // Cursors
112 /// Adds or moves the vertical cursor at the specified value on the x-axis
114 /// Adds or moves the vertical cursor at the specified value on the x-axis
113 void addVerticalCursor(double time);
115 void addVerticalCursor(double time);
114 /// Adds or moves the vertical cursor at the specified value on the x-axis
116 /// Adds or moves the vertical cursor at the specified value on the x-axis
115 void addVerticalCursorAtViewportPosition(double position);
117 void addVerticalCursorAtViewportPosition(double position);
116 void removeVerticalCursor();
118 void removeVerticalCursor();
117 /// Adds or moves the vertical cursor at the specified value on the y-axis
119 /// Adds or moves the vertical cursor at the specified value on the y-axis
118 void addHorizontalCursor(double value);
120 void addHorizontalCursor(double value);
119 /// Adds or moves the vertical cursor at the specified value on the y-axis
121 /// Adds or moves the vertical cursor at the specified value on the y-axis
120 void addHorizontalCursorAtViewportPosition(double position);
122 void addHorizontalCursorAtViewportPosition(double position);
121 void removeHorizontalCursor();
123 void removeHorizontalCursor();
122
124
123 signals:
125 signals:
124 void synchronize(const DateTimeRange& range, const DateTimeRange& oldRange);
126 void synchronize(const DateTimeRange& range, const DateTimeRange& oldRange);
125 void changeRange(const std::shared_ptr<Variable2>& variable, const DateTimeRange& range);
127 void changeRange(const std::shared_ptr<Variable2>& variable, const DateTimeRange& range);
126
128
127 /// Signal emitted when the variable is about to be removed from the graph
129 /// Signal emitted when the variable is about to be removed from the graph
128 void variableAboutToBeRemoved(std::shared_ptr<Variable2> var);
130 void variableAboutToBeRemoved(std::shared_ptr<Variable2> var);
129 /// Signal emitted when the variable has been added to the graph
131 /// Signal emitted when the variable has been added to the graph
130 void variableAdded(std::shared_ptr<Variable2> var);
132 void variableAdded(std::shared_ptr<Variable2> var);
131
133
132
134
133 void zoom_sig(double factor, int center, Qt::Orientation orientation, bool forward = true);
135 void zoom_sig(double factor, int center, Qt::Orientation orientation, bool forward = true);
134 void move_sig(double factor, Qt::Orientation orientation, bool forward = true);
136 void move_sig(double factor, Qt::Orientation orientation, bool forward = true);
135 void move_sig(double dx, double dy, bool forward = true);
137 void move_sig(double dx, double dy, bool forward = true);
136 void transform_sig(const DateTimeRangeTransformation& tranformation, bool forward = true);
138 void transform_sig(const DateTimeRangeTransformation& tranformation, bool forward = true);
137 void setrange_sig(const DateTimeRange& range, bool updateVar = false, bool forward = true);
139 void setrange_sig(const DateTimeRange& range, bool updateVar = false, bool forward = true);
138
140
139 protected:
141 protected:
140 void closeEvent(QCloseEvent* event) override;
142 void closeEvent(QCloseEvent* event) override;
141 void enterEvent(QEvent* event) override;
143 void enterEvent(QEvent* event) override;
142 void leaveEvent(QEvent* event) override;
144 void leaveEvent(QEvent* event) override;
143 void wheelEvent(QWheelEvent* event) override;
145 void wheelEvent(QWheelEvent* event) override;
144 void mouseMoveEvent(QMouseEvent* event) override;
146 void mouseMoveEvent(QMouseEvent* event) override;
145 void mouseReleaseEvent(QMouseEvent* event) override;
147 void mouseReleaseEvent(QMouseEvent* event) override;
146 void mousePressEvent(QMouseEvent* event) override;
148 void mousePressEvent(QMouseEvent* event) override;
147 void mouseDoubleClickEvent(QMouseEvent* event) override;
149 void mouseDoubleClickEvent(QMouseEvent* event) override;
148 void keyReleaseEvent(QKeyEvent* event) override;
150 void keyReleaseEvent(QKeyEvent* event) override;
149 void keyPressEvent(QKeyEvent* event) override;
151 void keyPressEvent(QKeyEvent* event) override;
150
152
151 QCustomPlot& plot() const noexcept;
153 QCustomPlot& plot() const noexcept;
152
154
153 private:
155 private:
154 Ui::VisualizationGraphWidget* ui;
156 Ui::VisualizationGraphWidget* ui;
155
157
156 class VisualizationGraphWidgetPrivate;
158 class VisualizationGraphWidgetPrivate;
157 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
159 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
158
160
159 private slots:
161 private slots:
160 /// Slot called when right clicking on the graph (displays a menu)
162 /// Slot called when right clicking on the graph (displays a menu)
161 void onGraphMenuRequested(const QPoint& pos) noexcept;
163 void onGraphMenuRequested(const QPoint& pos) noexcept;
162
164
163 /// Rescale the X axe to range parameter
165 /// Rescale the X axe to range parameter
164 // void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
166 // void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
165
167
166 /// Slot called when a mouse double click was made
168 /// Slot called when a mouse double click was made
167 void onMouseDoubleClick(QMouseEvent* event) noexcept;
169 void onMouseDoubleClick(QMouseEvent* event) noexcept;
168 /// Slot called when a mouse move was made
170 /// Slot called when a mouse move was made
169 void onMouseMove(QMouseEvent* event) noexcept;
171 void onMouseMove(QMouseEvent* event) noexcept;
170 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
172 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
171 void onMouseWheel(QWheelEvent* event) noexcept;
173 void onMouseWheel(QWheelEvent* event) noexcept;
172 /// Slot called when a mouse press was made, to activate the calibration of a graph
174 /// Slot called when a mouse press was made, to activate the calibration of a graph
173 void onMousePress(QMouseEvent* event) noexcept;
175 void onMousePress(QMouseEvent* event) noexcept;
174 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
176 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
175 void onMouseRelease(QMouseEvent* event) noexcept;
177 void onMouseRelease(QMouseEvent* event) noexcept;
176
178
177 void onDataCacheVariableUpdated();
179 void onDataCacheVariableUpdated();
178
180
179 void onUpdateVarDisplaying(std::shared_ptr<Variable2> variable, const DateTimeRange& range);
181 void onUpdateVarDisplaying(std::shared_ptr<Variable2> variable, const DateTimeRange& range);
180
182
181 void variableUpdated(QUuid id);
183 void variableUpdated(QUuid id);
182 void variableDeleted(const std::shared_ptr<Variable2>&);
184 void variableDeleted(const std::shared_ptr<Variable2>&);
183 };
185 };
184
186
185 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
187 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,1578 +1,1601
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 <MimeTypes/MimeTypes.h>
16 #include <MimeTypes/MimeTypes.h>
17 #include <cpp_utils_qt/cpp_utils_qt.hpp>
17 #include <cpp_utils_qt/cpp_utils_qt.hpp>
18 #include <containers/algorithms.hpp>
18 #include <containers/algorithms.hpp>
19 #include <Data/DateTimeRangeHelper.h>
19 #include <Data/DateTimeRangeHelper.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/Variable2.h>
24 #include <Variable/Variable2.h>
25 #include <Variable/VariableController2.h>
25 #include <Variable/VariableController2.h>
26
26
27 #include <unordered_map>
27 #include <unordered_map>
28
28
29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
30
30
31 namespace
31 namespace
32 {
32 {
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
62
63 explicit VisualizationGraphWidgetPrivate(const QString& name)
63 explicit VisualizationGraphWidgetPrivate(const QString& name)
64 : m_Name { name }
64 : m_Name { name }
65 , m_Flags { GraphFlag::EnableAll }
65 , m_Flags { GraphFlag::EnableAll }
66 , m_IsCalibration { false }
66 , m_IsCalibration { false }
67 , m_RenderingDelegate { nullptr }
67 , m_RenderingDelegate { nullptr }
68 {
68 {
69 m_plot = new QCustomPlot();
69 m_plot = new QCustomPlot();
70 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
70 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
71 m_plot->setPlottingHint(QCP::phFastPolylines, true);
71 m_plot->setPlottingHint(QCP::phFastPolylines, true);
72 }
72 }
73
73
74 void updateData(
74 void updateData(
75 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& range)
75 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& range)
76 {
76 {
77 VisualizationGraphHelper::updateData(plottables, variable, range);
77 VisualizationGraphHelper::updateData(plottables, variable, range);
78
78
79 // Prevents that data has changed to update rendering
79 // Prevents that data has changed to update rendering
80 m_RenderingDelegate->onPlotUpdated();
80 m_RenderingDelegate->onPlotUpdated();
81 }
81 }
82
82
83 QString m_Name;
83 QString m_Name;
84 // 1 variable -> n qcpplot
84 // 1 variable -> n qcpplot
85 std::map<std::shared_ptr<Variable2>, PlottablesMap> m_VariableToPlotMultiMap;
85 std::map<std::shared_ptr<Variable2>, PlottablesMap> m_VariableToPlotMultiMap;
86 GraphFlags m_Flags;
86 GraphFlags m_Flags;
87 bool m_IsCalibration;
87 bool m_IsCalibration;
88 QCustomPlot* m_plot;
88 QCustomPlot* m_plot;
89 QPoint m_lastMousePos;
89 QPoint m_lastMousePos;
90 QCPRange m_lastXRange;
90 QCPRange m_lastXRange;
91 QCPRange m_lastYRange;
91 QCPRange m_lastYRange;
92 /// Delegate used to attach rendering features to the plot
92 /// Delegate used to attach rendering features to the plot
93 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
93 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
94
94
95 QCPItemRect* m_DrawingZoomRect = nullptr;
95 QCPItemRect* m_DrawingZoomRect = nullptr;
96 QStack<QPair<QCPRange, QCPRange>> m_ZoomStack;
96 QStack<QPair<QCPRange, QCPRange>> m_ZoomStack;
97
97
98 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
99 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
99 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
100
100
101 VisualizationSelectionZoneItem* m_DrawingZone = nullptr;
101 VisualizationSelectionZoneItem* m_DrawingZone = nullptr;
102 VisualizationSelectionZoneItem* m_HoveredZone = nullptr;
102 VisualizationSelectionZoneItem* m_HoveredZone = nullptr;
103 QVector<VisualizationSelectionZoneItem*> m_SelectionZones;
103 QVector<VisualizationSelectionZoneItem*> m_SelectionZones;
104
104
105 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
105 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
106
106
107 bool m_VariableAutoRangeOnInit = true;
107 bool m_VariableAutoRangeOnInit = true;
108
108
109 inline void enterPlotDrag(const QPoint& position)
109 inline void enterPlotDrag(const QPoint& position)
110 {
110 {
111 m_lastMousePos = m_plot->mapFromParent(position);
111 m_lastMousePos = m_plot->mapFromParent(position);
112 m_lastXRange = m_plot->xAxis->range();
112 m_lastXRange = m_plot->xAxis->range();
113 m_lastYRange = m_plot->yAxis->range();
113 m_lastYRange = m_plot->yAxis->range();
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()),
119 QPointF pos { m_plot->xAxis->pixelToCoord(newPos.x()),
120 m_plot->yAxis->pixelToCoord(newPos.y()) };
120 m_plot->yAxis->pixelToCoord(newPos.y()) };
121 m_DrawingZoomRect->bottomRight->setCoords(pos);
121 m_DrawingZoomRect->bottomRight->setCoords(pos);
122 m_plot->replot(QCustomPlot::rpQueuedReplot);
122 m_plot->replot(QCustomPlot::rpQueuedReplot);
123 }
123 }
124
124
125 void applyZoomRect()
125 void applyZoomRect()
126 {
126 {
127 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
127 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
128 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
128 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
129
129
130 auto newAxisXRange = QCPRange { m_DrawingZoomRect->topLeft->coords().x(),
130 auto newAxisXRange = QCPRange { m_DrawingZoomRect->topLeft->coords().x(),
131 m_DrawingZoomRect->bottomRight->coords().x() };
131 m_DrawingZoomRect->bottomRight->coords().x() };
132
132
133 auto newAxisYRange = QCPRange { m_DrawingZoomRect->topLeft->coords().y(),
133 auto newAxisYRange = QCPRange { m_DrawingZoomRect->topLeft->coords().y(),
134 m_DrawingZoomRect->bottomRight->coords().y() };
134 m_DrawingZoomRect->bottomRight->coords().y() };
135
135
136 removeDrawingRect();
136 removeDrawingRect();
137
137
138 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
138 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
139 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
139 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
140 {
140 {
141 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
141 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
142 axisX->setRange(newAxisXRange);
142 axisX->setRange(newAxisXRange);
143 axisY->setRange(newAxisYRange);
143 axisY->setRange(newAxisYRange);
144
144
145 m_plot->replot(QCustomPlot::rpQueuedReplot);
145 m_plot->replot(QCustomPlot::rpQueuedReplot);
146 }
146 }
147 }
147 }
148
148
149 inline bool isDrawingZoneRect() { return m_DrawingZone != nullptr; }
149 inline bool isDrawingZoneRect() { return m_DrawingZone != nullptr; }
150 void updateZoneRect(const QPoint& newPos)
150 void updateZoneRect(const QPoint& newPos)
151 {
151 {
152 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
152 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
153 m_plot->replot(QCustomPlot::rpQueuedReplot);
153 m_plot->replot(QCustomPlot::rpQueuedReplot);
154 }
154 }
155
155
156 void startDrawingRect(const QPoint& pos)
156 void startDrawingRect(const QPoint& pos)
157 {
157 {
158 removeDrawingRect();
158 removeDrawingRect();
159
159
160 auto axisPos = posToAxisPos(pos);
160 auto axisPos = posToAxisPos(pos);
161
161
162 m_DrawingZoomRect = new QCPItemRect { m_plot };
162 m_DrawingZoomRect = new QCPItemRect { m_plot };
163 QPen p;
163 QPen p;
164 p.setWidth(2);
164 p.setWidth(2);
165 m_DrawingZoomRect->setPen(p);
165 m_DrawingZoomRect->setPen(p);
166
166
167 m_DrawingZoomRect->topLeft->setCoords(axisPos);
167 m_DrawingZoomRect->topLeft->setCoords(axisPos);
168 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
168 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
169 }
169 }
170
170
171 void removeDrawingRect()
171 void removeDrawingRect()
172 {
172 {
173 if (m_DrawingZoomRect)
173 if (m_DrawingZoomRect)
174 {
174 {
175 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
175 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
176 m_DrawingZoomRect = nullptr;
176 m_DrawingZoomRect = nullptr;
177 m_plot->replot(QCustomPlot::rpQueuedReplot);
177 m_plot->replot(QCustomPlot::rpQueuedReplot);
178 }
178 }
179 }
179 }
180
180
181 void selectZone(const QPoint& pos)
181 void selectZone(const QPoint& pos)
182 {
182 {
183 auto zoneAtPos = selectionZoneAt(pos);
183 auto zoneAtPos = selectionZoneAt(pos);
184 setSelectionZonesEditionEnabled(
184 setSelectionZonesEditionEnabled(
185 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
185 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
186 }
186 }
187
187
188 void startDrawingZone(const QPoint& pos)
188 void startDrawingZone(const QPoint& pos)
189 {
189 {
190 endDrawingZone();
190 endDrawingZone();
191
191
192 auto axisPos = posToAxisPos(pos);
192 auto axisPos = posToAxisPos(pos);
193
193
194 m_DrawingZone = new VisualizationSelectionZoneItem { m_plot };
194 m_DrawingZone = new VisualizationSelectionZoneItem { m_plot };
195 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
195 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
196 m_DrawingZone->setEditionEnabled(false);
196 m_DrawingZone->setEditionEnabled(false);
197 }
197 }
198
198
199 void endDrawingZone()
199 void endDrawingZone()
200 {
200 {
201 if (m_DrawingZone)
201 if (m_DrawingZone)
202 {
202 {
203 auto drawingZoneRange = m_DrawingZone->range();
203 auto drawingZoneRange = m_DrawingZone->range();
204 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0)
204 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0)
205 {
205 {
206 m_DrawingZone->setEditionEnabled(true);
206 m_DrawingZone->setEditionEnabled(true);
207 addSelectionZone(m_DrawingZone);
207 addSelectionZone(m_DrawingZone);
208 }
208 }
209 else
209 else
210 {
210 {
211 m_plot->removeItem(m_DrawingZone);
211 m_plot->removeItem(m_DrawingZone);
212 }
212 }
213
213
214 m_plot->replot(QCustomPlot::rpQueuedReplot);
214 m_plot->replot(QCustomPlot::rpQueuedReplot);
215 m_DrawingZone = nullptr;
215 m_DrawingZone = nullptr;
216 }
216 }
217 }
217 }
218
218
219 void moveSelectionZone(const QPoint& destination)
219 void moveSelectionZone(const QPoint& destination)
220 {
220 {
221 /*
221 /*
222 * I give up on this for now
222 * I give up on this for now
223 * TODO implement this, the difficulty is that selection zones have their own
223 * TODO implement this, the difficulty is that selection zones have their own
224 * event handling code which seems to rely on QCP GUI event handling propagation
224 * event handling code which seems to rely on QCP GUI event handling propagation
225 * which was a realy bad design choice.
225 * which was a realy bad design choice.
226 */
226 */
227 }
227 }
228
228
229 void setSelectionZonesEditionEnabled(bool value)
229 void setSelectionZonesEditionEnabled(bool value)
230 {
230 {
231 for (auto s : m_SelectionZones)
231 for (auto s : m_SelectionZones)
232 {
232 {
233 s->setEditionEnabled(value);
233 s->setEditionEnabled(value);
234 }
234 }
235 }
235 }
236
236
237 void addSelectionZone(VisualizationSelectionZoneItem* zone) { m_SelectionZones << zone; }
237 void addSelectionZone(VisualizationSelectionZoneItem* zone) { m_SelectionZones << zone; }
238
238
239 VisualizationSelectionZoneItem* selectionZoneAt(const QPoint& pos) const
239 VisualizationSelectionZoneItem* selectionZoneAt(const QPoint& pos) const
240 {
240 {
241 VisualizationSelectionZoneItem* selectionZoneItemUnderCursor = nullptr;
241 VisualizationSelectionZoneItem* selectionZoneItemUnderCursor = nullptr;
242 auto minDistanceToZone = -1;
242 auto minDistanceToZone = -1;
243 for (auto zone : m_SelectionZones)
243 for (auto zone : m_SelectionZones)
244 {
244 {
245 auto distanceToZone = zone->selectTest(pos, false);
245 auto distanceToZone = zone->selectTest(pos, false);
246 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
246 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
247 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance())
247 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance())
248 {
248 {
249 selectionZoneItemUnderCursor = zone;
249 selectionZoneItemUnderCursor = zone;
250 }
250 }
251 }
251 }
252
252
253 return selectionZoneItemUnderCursor;
253 return selectionZoneItemUnderCursor;
254 }
254 }
255
255
256 QVector<VisualizationSelectionZoneItem*> selectionZonesAt(
256 QVector<VisualizationSelectionZoneItem*> selectionZonesAt(
257 const QPoint& pos, const QCustomPlot& plot) const
257 const QPoint& pos, const QCustomPlot& plot) const
258 {
258 {
259 QVector<VisualizationSelectionZoneItem*> zones;
259 QVector<VisualizationSelectionZoneItem*> zones;
260 for (auto zone : m_SelectionZones)
260 for (auto zone : m_SelectionZones)
261 {
261 {
262 auto distanceToZone = zone->selectTest(pos, false);
262 auto distanceToZone = zone->selectTest(pos, false);
263 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance())
263 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance())
264 {
264 {
265 zones << zone;
265 zones << zone;
266 }
266 }
267 }
267 }
268
268
269 return zones;
269 return zones;
270 }
270 }
271
271
272 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem* zone, QCustomPlot& plot)
272 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem* zone, QCustomPlot& plot)
273 {
273 {
274 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone)
274 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone)
275 {
275 {
276 zone->moveToTop();
276 zone->moveToTop();
277 m_SelectionZones.removeAll(zone);
277 m_SelectionZones.removeAll(zone);
278 m_SelectionZones.append(zone);
278 m_SelectionZones.append(zone);
279 }
279 }
280 }
280 }
281
281
282 QPointF posToAxisPos(const QPoint& pos) const
282 QPointF posToAxisPos(const QPoint& pos) const
283 {
283 {
284 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
284 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
285 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
285 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
286 return QPointF { axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y()) };
286 return QPointF { axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y()) };
287 }
287 }
288
288
289 bool pointIsInAxisRect(const QPointF& axisPoint, QCustomPlot& plot) const
289 bool pointIsInAxisRect(const QPointF& axisPoint, QCustomPlot& plot) const
290 {
290 {
291 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
291 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
292 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
292 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
293 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
293 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
294 }
294 }
295
295
296 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis* axis)
296 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis* axis)
297 {
297 {
298 if (axis->scaleType() == QCPAxis::stLinear)
298 if (axis->scaleType() == QCPAxis::stLinear)
299 {
299 {
300 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
300 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
301 return QCPRange { axis->range().lower + diff, axis->range().upper + diff };
301 return QCPRange { axis->range().lower + diff, axis->range().upper + diff };
302 }
302 }
303 else
303 else
304 {
304 {
305 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
305 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
306 return QCPRange { axis->range().lower * diff, axis->range().upper * diff };
306 return QCPRange { axis->range().lower * diff, axis->range().upper * diff };
307 }
307 }
308 }
308 }
309
309
310 void setRange(const DateTimeRange& newRange, bool updateVar = true)
310 void setRange(const DateTimeRange& newRange, bool updateVar = true)
311 {
311 {
312 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
312 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
313 if (updateVar)
313 if (updateVar)
314 {
314 {
315 for (auto it = m_VariableToPlotMultiMap.begin(), end = m_VariableToPlotMultiMap.end();
315 for (auto it = m_VariableToPlotMultiMap.begin(), end = m_VariableToPlotMultiMap.end();
316 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
316 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
317 {
317 {
318 sqpApp->variableController().asyncChangeRange(it->first, newRange);
318 sqpApp->variableController().asyncChangeRange(it->first, newRange);
319 }
319 }
320 }
320 }
321 m_plot->replot(QCustomPlot::rpQueuedReplot);
321 m_plot->replot(QCustomPlot::rpQueuedReplot);
322 }
322 }
323
323
324 void setRange(const QCPRange& newRange)
324 void setRange(const QCPRange& newRange)
325 {
325 {
326 auto graphRange = DateTimeRange { newRange.lower, newRange.upper };
326 auto graphRange = DateTimeRange { newRange.lower, newRange.upper };
327 setRange(graphRange);
327 setRange(graphRange);
328 }
328 }
329
329
330 void rescaleY() { m_plot->yAxis->rescale(true); }
330 void rescaleY() { m_plot->yAxis->rescale(true); }
331
331
332 std::tuple<double, double> moveGraph(const QPoint& destination)
332 std::tuple<double, double> moveGraph(const QPoint& destination)
333 {
333 {
334 auto currentPos = m_plot->mapFromParent(destination);
334 auto currentPos = m_plot->mapFromParent(destination);
335 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
335 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
336 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
336 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
337 auto oldXRange = xAxis->range();
337 auto oldXRange = xAxis->range();
338 auto oldYRange = yAxis->range();
338 auto oldYRange = yAxis->range();
339 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
339 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
340 xAxis->setRange(m_lastXRange.lower + dx, m_lastXRange.upper + dx);
340 xAxis->setRange(m_lastXRange.lower + dx, m_lastXRange.upper + dx);
341 if (yAxis->scaleType() == QCPAxis::stLinear)
341 if (yAxis->scaleType() == QCPAxis::stLinear)
342 {
342 {
343 double dy
343 double dy
344 = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
344 = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
345 yAxis->setRange(m_lastYRange.lower + dy, m_lastYRange.upper + dy);
345 yAxis->setRange(m_lastYRange.lower + dy, m_lastYRange.upper + dy);
346 }
346 }
347 else
347 else
348 {
348 {
349 double dy
349 double dy
350 = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
350 = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
351 yAxis->setRange(m_lastYRange.lower * dy, m_lastYRange.upper * dy);
351 yAxis->setRange(m_lastYRange.lower * dy, m_lastYRange.upper * dy);
352 }
352 }
353 auto newXRange = xAxis->range();
353 auto newXRange = xAxis->range();
354 auto newYRange = yAxis->range();
354 auto newYRange = yAxis->range();
355 setRange(xAxis->range());
355 setRange(xAxis->range());
356 // m_lastMousePos = currentPos;
356 // m_lastMousePos = currentPos;
357 return { newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower };
357 return { newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower };
358 }
358 }
359
359
360 void zoom(double factor, int center, Qt::Orientation orientation)
360 void zoom(double factor, int center, Qt::Orientation orientation)
361 {
361 {
362 QCPAxis* axis = m_plot->axisRect()->rangeZoomAxis(orientation);
362 QCPAxis* axis = m_plot->axisRect()->rangeZoomAxis(orientation);
363 axis->scaleRange(factor, axis->pixelToCoord(center));
363 axis->scaleRange(factor, axis->pixelToCoord(center));
364 if (orientation == Qt::Horizontal)
364 if (orientation == Qt::Horizontal)
365 setRange(axis->range());
365 setRange(axis->range());
366 m_plot->replot(QCustomPlot::rpQueuedReplot);
366 m_plot->replot(QCustomPlot::rpQueuedReplot);
367 }
367 }
368
368
369 void transform(const DateTimeRangeTransformation& tranformation)
369 void transform(const DateTimeRangeTransformation& tranformation)
370 {
370 {
371 auto graphRange = m_plot->xAxis->range();
371 auto graphRange = m_plot->xAxis->range();
372 DateTimeRange range { graphRange.lower, graphRange.upper };
372 DateTimeRange range { graphRange.lower, graphRange.upper };
373 range = range.transform(tranformation);
373 range = range.transform(tranformation);
374 setRange(range);
374 setRange(range);
375 m_plot->replot(QCustomPlot::rpQueuedReplot);
375 m_plot->replot(QCustomPlot::rpQueuedReplot);
376 }
376 }
377
377
378 void move(double dx, double dy)
378 void move(double dx, double dy)
379 {
379 {
380 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
380 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
381 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
381 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
382 xAxis->setRange(QCPRange(xAxis->range().lower + dx, xAxis->range().upper + dx));
382 xAxis->setRange(QCPRange(xAxis->range().lower + dx, xAxis->range().upper + dx));
383 yAxis->setRange(QCPRange(yAxis->range().lower + dy, yAxis->range().upper + dy));
383 yAxis->setRange(QCPRange(yAxis->range().lower + dy, yAxis->range().upper + dy));
384 setRange(xAxis->range());
384 setRange(xAxis->range());
385 m_plot->replot(QCustomPlot::rpQueuedReplot);
385 m_plot->replot(QCustomPlot::rpQueuedReplot);
386 }
386 }
387
387
388 void move(double factor, Qt::Orientation orientation)
388 void move(double factor, Qt::Orientation orientation)
389 {
389 {
390 auto oldRange = m_plot->xAxis->range();
390 auto oldRange = m_plot->xAxis->range();
391 QCPAxis* axis = m_plot->axisRect()->rangeDragAxis(orientation);
391 QCPAxis* axis = m_plot->axisRect()->rangeDragAxis(orientation);
392 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear)
392 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear)
393 {
393 {
394 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
394 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
395 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
395 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
396 }
396 }
397 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic)
397 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic)
398 {
398 {
399 int start = 0, stop = 0;
399 int start = 0, stop = 0;
400 double diff = 0.;
400 double diff = 0.;
401 if (factor > 0.0)
401 if (factor > 0.0)
402 {
402 {
403 stop = m_plot->width() * factor / 10;
403 stop = m_plot->width() * factor / 10;
404 start = 2 * m_plot->width() * factor / 10;
404 start = 2 * m_plot->width() * factor / 10;
405 }
405 }
406 if (factor < 0.0)
406 if (factor < 0.0)
407 {
407 {
408 factor *= -1.0;
408 factor *= -1.0;
409 start = m_plot->width() * factor / 10;
409 start = m_plot->width() * factor / 10;
410 stop = 2 * m_plot->width() * factor / 10;
410 stop = 2 * m_plot->width() * factor / 10;
411 }
411 }
412 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
412 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
413 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
413 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
414 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
414 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
415 }
415 }
416 if (orientation == Qt::Horizontal)
416 if (orientation == Qt::Horizontal)
417 setRange(axis->range());
417 setRange(axis->range());
418 m_plot->replot(QCustomPlot::rpQueuedReplot);
418 m_plot->replot(QCustomPlot::rpQueuedReplot);
419 }
419 }
420 void toggle_y_log_scale()
421 {
422 if(m_plot->yAxis->scaleType()==QCPAxis::stLinear)
423 {
424 m_plot->yAxis->setScaleType(QCPAxis::stLogarithmic);
425 QSharedPointer<QCPAxisTickerLog> logTicker(new QCPAxisTickerLog);
426 m_plot->yAxis->setTicker(logTicker);
427 }
428 else
429 {
430 m_plot->yAxis->setScaleType(QCPAxis::stLinear);
431 QSharedPointer<QCPAxisTicker> ticker(new QCPAxisTicker);
432 m_plot->yAxis->setTicker(ticker);
433 }
434 }
420 };
435 };
421
436
422 VisualizationGraphWidget::VisualizationGraphWidget(const QString& name, QWidget* parent)
437 VisualizationGraphWidget::VisualizationGraphWidget(const QString& name, QWidget* parent)
423 : VisualizationDragWidget { parent }
438 : VisualizationDragWidget { parent }
424 , ui { new Ui::VisualizationGraphWidget }
439 , ui { new Ui::VisualizationGraphWidget }
425 , impl { spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name) }
440 , impl { spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name) }
426 {
441 {
427 ui->setupUi(this);
442 ui->setupUi(this);
428 this->layout()->addWidget(impl->m_plot);
443 this->layout()->addWidget(impl->m_plot);
429 // 'Close' options : widget is deleted when closed
444 // 'Close' options : widget is deleted when closed
430 setAttribute(Qt::WA_DeleteOnClose);
445 setAttribute(Qt::WA_DeleteOnClose);
431
446
432 // The delegate must be initialized after the ui as it uses the plot
447 // The delegate must be initialized after the ui as it uses the plot
433 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
448 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
434
449
435 // Init the cursors
450 // Init the cursors
436 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
451 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
437 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
452 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
438 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
453 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
439 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
454 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
440
455
441 this->setFocusPolicy(Qt::WheelFocus);
456 this->setFocusPolicy(Qt::WheelFocus);
442 this->setMouseTracking(true);
457 this->setMouseTracking(true);
443 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
458 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
444 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
459 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
445 impl->m_plot->setParent(this);
460 impl->m_plot->setParent(this);
446
461
447 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this,
462 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this,
448 &VisualizationGraphWidget::variableDeleted);
463 &VisualizationGraphWidget::variableDeleted);
449 }
464 }
450
465
451
466
452 VisualizationGraphWidget::~VisualizationGraphWidget()
467 VisualizationGraphWidget::~VisualizationGraphWidget()
453 {
468 {
454 delete ui;
469 delete ui;
455 }
470 }
456
471
457 VisualizationZoneWidget* VisualizationGraphWidget::parentZoneWidget() const noexcept
472 VisualizationZoneWidget* VisualizationGraphWidget::parentZoneWidget() const noexcept
458 {
473 {
459 auto parent = parentWidget();
474 auto parent = parentWidget();
460 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget*>(parent))
475 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget*>(parent))
461 {
476 {
462 parent = parent->parentWidget();
477 parent = parent->parentWidget();
463 }
478 }
464
479
465 return qobject_cast<VisualizationZoneWidget*>(parent);
480 return qobject_cast<VisualizationZoneWidget*>(parent);
466 }
481 }
467
482
468 VisualizationWidget* VisualizationGraphWidget::parentVisualizationWidget() const
483 VisualizationWidget* VisualizationGraphWidget::parentVisualizationWidget() const
469 {
484 {
470 auto parent = parentWidget();
485 auto parent = parentWidget();
471 while (parent != nullptr && !qobject_cast<VisualizationWidget*>(parent))
486 while (parent != nullptr && !qobject_cast<VisualizationWidget*>(parent))
472 {
487 {
473 parent = parent->parentWidget();
488 parent = parent->parentWidget();
474 }
489 }
475
490
476 return qobject_cast<VisualizationWidget*>(parent);
491 return qobject_cast<VisualizationWidget*>(parent);
477 }
492 }
478
493
479 void VisualizationGraphWidget::setFlags(GraphFlags flags)
494 void VisualizationGraphWidget::setFlags(GraphFlags flags)
480 {
495 {
481 impl->m_Flags = std::move(flags);
496 impl->m_Flags = std::move(flags);
482 }
497 }
483
498
484 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range)
499 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range)
485 {
500 {
486 // Uses delegate to create the qcpplot components according to the variable
501 // Uses delegate to create the qcpplot components according to the variable
487 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
502 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
488
503
489 // Sets graph properties
504 // Sets graph properties
490 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
505 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
491
506
492 impl->m_VariableToPlotMultiMap.insert({ variable, std::move(createdPlottables) });
507 impl->m_VariableToPlotMultiMap.insert({ variable, std::move(createdPlottables) });
493
508
494 setGraphRange(range);
509 setGraphRange(range);
495 // If the variable already has its data loaded, load its units and its range in the graph
510 // If the variable already has its data loaded, load its units and its range in the graph
496 if (variable->data() != nullptr)
511 if (variable->data() != nullptr)
497 {
512 {
498 impl->m_RenderingDelegate->setAxesUnits(*variable);
513 impl->m_RenderingDelegate->setAxesUnits(*variable);
499 }
514 }
500 else
515 else
501 {
516 {
502 auto context = new QObject { this };
517 auto context = new QObject { this };
503 connect(
518 connect(
504 variable.get(), &Variable2::updated, context, [this, variable, context, range](QUuid) {
519 variable.get(), &Variable2::updated, context, [this, variable, context, range](QUuid) {
505 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
520 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
506 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
521 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
507 delete context;
522 delete context;
508 });
523 });
509 }
524 }
510 //TODO this is bad! when variable is moved to another graph it still fires
525 //TODO this is bad! when variable is moved to another graph it still fires
511 // even if this has been deleted
526 // even if this has been deleted
512 connect(variable.get(), &Variable2::updated, this, &VisualizationGraphWidget::variableUpdated);
527 connect(variable.get(), &Variable2::updated, this, &VisualizationGraphWidget::variableUpdated);
513 this->onUpdateVarDisplaying(variable, range); // My bullshit
528 this->onUpdateVarDisplaying(variable, range); // My bullshit
514 emit variableAdded(variable);
529 emit variableAdded(variable);
515 }
530 }
516
531
517 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable2> variable) noexcept
532 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable2> variable) noexcept
518 {
533 {
519 // Each component associated to the variable :
534 // Each component associated to the variable :
520 // - is removed from qcpplot (which deletes it)
535 // - is removed from qcpplot (which deletes it)
521 // - is no longer referenced in the map
536 // - is no longer referenced in the map
522 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
537 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
523 if (variableIt != impl->m_VariableToPlotMultiMap.cend())
538 if (variableIt != impl->m_VariableToPlotMultiMap.cend())
524 {
539 {
525 emit variableAboutToBeRemoved(variable);
540 emit variableAboutToBeRemoved(variable);
526
541
527 auto& plottablesMap = variableIt->second;
542 auto& plottablesMap = variableIt->second;
528
543
529 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
544 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
530 plottableIt != plottableEnd;)
545 plottableIt != plottableEnd;)
531 {
546 {
532 impl->m_plot->removePlottable(plottableIt->second);
547 impl->m_plot->removePlottable(plottableIt->second);
533 plottableIt = plottablesMap.erase(plottableIt);
548 plottableIt = plottablesMap.erase(plottableIt);
534 }
549 }
535
550
536 impl->m_VariableToPlotMultiMap.erase(variableIt);
551 impl->m_VariableToPlotMultiMap.erase(variableIt);
537 }
552 }
538
553
539 // Updates graph
554 // Updates graph
540 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
555 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
541 }
556 }
542
557
543 std::vector<std::shared_ptr<Variable2>> VisualizationGraphWidget::variables() const
558 std::vector<std::shared_ptr<Variable2>> VisualizationGraphWidget::variables() const
544 {
559 {
545 auto variables = std::vector<std::shared_ptr<Variable2>> {};
560 auto variables = std::vector<std::shared_ptr<Variable2>> {};
546 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
561 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
547 it != std::cend(impl->m_VariableToPlotMultiMap); ++it)
562 it != std::cend(impl->m_VariableToPlotMultiMap); ++it)
548 {
563 {
549 variables.push_back(it->first);
564 variables.push_back(it->first);
550 }
565 }
551
566
552 return variables;
567 return variables;
553 }
568 }
554
569
555 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable2> variable)
570 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable2> variable)
556 {
571 {
557 if (!variable)
572 if (!variable)
558 {
573 {
559 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
574 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
560 return;
575 return;
561 }
576 }
562
577
563 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
578 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
564 }
579 }
565
580
566 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
581 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
567 {
582 {
568 auto graphRange = impl->m_plot->xAxis->range();
583 auto graphRange = impl->m_plot->xAxis->range();
569 return DateTimeRange { graphRange.lower, graphRange.upper };
584 return DateTimeRange { graphRange.lower, graphRange.upper };
570 }
585 }
571
586
572 void VisualizationGraphWidget::setGraphRange(
587 void VisualizationGraphWidget::setGraphRange(
573 const DateTimeRange& range, bool updateVar, bool forward)
588 const DateTimeRange& range, bool updateVar, bool forward)
574 {
589 {
575 impl->setRange(range, updateVar);
590 impl->setRange(range, updateVar);
576 if (forward)
591 if (forward)
577 {
592 {
578 emit this->setrange_sig(this->graphRange(), true, false);
593 emit this->setrange_sig(this->graphRange(), true, false);
579 }
594 }
580 }
595 }
581
596
582 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
597 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
583 {
598 {
584 impl->m_VariableAutoRangeOnInit = value;
599 impl->m_VariableAutoRangeOnInit = value;
585 }
600 }
586
601
587 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
602 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
588 {
603 {
589 QVector<DateTimeRange> ranges;
604 QVector<DateTimeRange> ranges;
590 for (auto zone : impl->m_SelectionZones)
605 for (auto zone : impl->m_SelectionZones)
591 {
606 {
592 ranges << zone->range();
607 ranges << zone->range();
593 }
608 }
594
609
595 return ranges;
610 return ranges;
596 }
611 }
597
612
598 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange>& ranges)
613 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange>& ranges)
599 {
614 {
600 for (const auto& range : ranges)
615 for (const auto& range : ranges)
601 {
616 {
602 // note: ownership is transfered to QCustomPlot
617 // note: ownership is transfered to QCustomPlot
603 auto zone = new VisualizationSelectionZoneItem(&plot());
618 auto zone = new VisualizationSelectionZoneItem(&plot());
604 zone->setRange(range.m_TStart, range.m_TEnd);
619 zone->setRange(range.m_TStart, range.m_TEnd);
605 impl->addSelectionZone(zone);
620 impl->addSelectionZone(zone);
606 }
621 }
607
622
608 plot().replot(QCustomPlot::rpQueuedReplot);
623 plot().replot(QCustomPlot::rpQueuedReplot);
609 }
624 }
610
625
611 VisualizationSelectionZoneItem* VisualizationGraphWidget::addSelectionZone(
626 VisualizationSelectionZoneItem* VisualizationGraphWidget::addSelectionZone(
612 const QString& name, const DateTimeRange& range)
627 const QString& name, const DateTimeRange& range)
613 {
628 {
614 // note: ownership is transfered to QCustomPlot
629 // note: ownership is transfered to QCustomPlot
615 auto zone = new VisualizationSelectionZoneItem(&plot());
630 auto zone = new VisualizationSelectionZoneItem(&plot());
616 zone->setName(name);
631 zone->setName(name);
617 zone->setRange(range.m_TStart, range.m_TEnd);
632 zone->setRange(range.m_TStart, range.m_TEnd);
618 impl->addSelectionZone(zone);
633 impl->addSelectionZone(zone);
619
634
620 plot().replot(QCustomPlot::rpQueuedReplot);
635 plot().replot(QCustomPlot::rpQueuedReplot);
621
636
622 return zone;
637 return zone;
623 }
638 }
624
639
625 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem* selectionZone)
640 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem* selectionZone)
626 {
641 {
627 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
642 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
628
643
629 if (impl->m_HoveredZone == selectionZone)
644 if (impl->m_HoveredZone == selectionZone)
630 {
645 {
631 impl->m_HoveredZone = nullptr;
646 impl->m_HoveredZone = nullptr;
632 setCursor(Qt::ArrowCursor);
647 setCursor(Qt::ArrowCursor);
633 }
648 }
634
649
635 impl->m_SelectionZones.removeAll(selectionZone);
650 impl->m_SelectionZones.removeAll(selectionZone);
636 plot().removeItem(selectionZone);
651 plot().removeItem(selectionZone);
637 plot().replot(QCustomPlot::rpQueuedReplot);
652 plot().replot(QCustomPlot::rpQueuedReplot);
638 }
653 }
639
654
640 void VisualizationGraphWidget::undoZoom()
655 void VisualizationGraphWidget::undoZoom()
641 {
656 {
642 auto zoom = impl->m_ZoomStack.pop();
657 auto zoom = impl->m_ZoomStack.pop();
643 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
658 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
644 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
659 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
645
660
646 axisX->setRange(zoom.first);
661 axisX->setRange(zoom.first);
647 axisY->setRange(zoom.second);
662 axisY->setRange(zoom.second);
648
663
649 plot().replot(QCustomPlot::rpQueuedReplot);
664 plot().replot(QCustomPlot::rpQueuedReplot);
650 }
665 }
651
666
652 void VisualizationGraphWidget::zoom(
667 void VisualizationGraphWidget::zoom(
653 double factor, int center, Qt::Orientation orientation, bool forward)
668 double factor, int center, Qt::Orientation orientation, bool forward)
654 {
669 {
655 impl->zoom(factor, center, orientation);
670 impl->zoom(factor, center, orientation);
656 if (forward && orientation == Qt::Horizontal)
671 if (forward && orientation == Qt::Horizontal)
657 emit this->setrange_sig(this->graphRange(), true, false);
672 emit this->setrange_sig(this->graphRange(), true, false);
658 }
673 }
659
674
660 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
675 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
661 {
676 {
662 impl->move(factor, orientation);
677 impl->move(factor, orientation);
663 if (forward)
678 if (forward)
664 emit this->setrange_sig(this->graphRange(), true, false);
679 emit this->setrange_sig(this->graphRange(), true, false);
665 }
680 }
666
681
667 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
682 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
668 {
683 {
669 impl->move(dx, dy);
684 impl->move(dx, dy);
670 if (forward)
685 if (forward)
671 emit this->setrange_sig(this->graphRange(), true, false);
686 emit this->setrange_sig(this->graphRange(), true, false);
672 }
687 }
673
688
674 void VisualizationGraphWidget::transform(
689 void VisualizationGraphWidget::transform(
675 const DateTimeRangeTransformation& tranformation, bool forward)
690 const DateTimeRangeTransformation& tranformation, bool forward)
676 {
691 {
677 impl->transform(tranformation);
692 impl->transform(tranformation);
678 if (forward)
693 if (forward)
679 emit this->setrange_sig(this->graphRange(), true, false);
694 emit this->setrange_sig(this->graphRange(), true, false);
680 }
695 }
681
696
697 void VisualizationGraphWidget::toggle_y_log_scale()
698 {
699 impl->toggle_y_log_scale();
700 }
701
682 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor* visitor)
702 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor* visitor)
683 {
703 {
684 if (visitor)
704 if (visitor)
685 {
705 {
686 visitor->visit(this);
706 visitor->visit(this);
687 }
707 }
688 else
708 else
689 {
709 {
690 qCCritical(LOG_VisualizationGraphWidget())
710 qCCritical(LOG_VisualizationGraphWidget())
691 << tr("Can't visit widget : the visitor is null");
711 << tr("Can't visit widget : the visitor is null");
692 }
712 }
693 }
713 }
694
714
695 bool VisualizationGraphWidget::canDrop(Variable2& variable) const
715 bool VisualizationGraphWidget::canDrop(Variable2& variable) const
696 {
716 {
697 auto isSpectrogram
717 auto isSpectrogram
698 = [](auto& variable) { return variable.type() == DataSeriesType::SPECTROGRAM; };
718 = [](auto& variable) { return variable.type() == DataSeriesType::SPECTROGRAM; };
699
719
700 // - A spectrogram series can't be dropped on graph with existing plottables
720 // - A spectrogram series can't be dropped on graph with existing plottables
701 // - No data series can be dropped on graph with existing spectrogram series
721 // - No data series can be dropped on graph with existing spectrogram series
702 return isSpectrogram(variable)
722 return isSpectrogram(variable)
703 ? impl->m_VariableToPlotMultiMap.empty()
723 ? impl->m_VariableToPlotMultiMap.empty()
704 : std::none_of(impl->m_VariableToPlotMultiMap.cbegin(),
724 : std::none_of(impl->m_VariableToPlotMultiMap.cbegin(),
705 impl->m_VariableToPlotMultiMap.cend(),
725 impl->m_VariableToPlotMultiMap.cend(),
706 [isSpectrogram](const auto& entry) { return isSpectrogram(*entry.first); });
726 [isSpectrogram](const auto& entry) { return isSpectrogram(*entry.first); });
707 }
727 }
708
728
709 bool VisualizationGraphWidget::contains(Variable2& variable) const
729 bool VisualizationGraphWidget::contains(Variable2& variable) const
710 {
730 {
711 // Finds the variable among the keys of the map
731 // Finds the variable among the keys of the map
712 auto variablePtr = &variable;
732 auto variablePtr = &variable;
713 auto findVariable
733 auto findVariable
714 = [variablePtr](const auto& entry) { return variablePtr == entry.first.get(); };
734 = [variablePtr](const auto& entry) { return variablePtr == entry.first.get(); };
715
735
716 auto end = impl->m_VariableToPlotMultiMap.cend();
736 auto end = impl->m_VariableToPlotMultiMap.cend();
717 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
737 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
718 return it != end;
738 return it != end;
719 }
739 }
720
740
721 QString VisualizationGraphWidget::name() const
741 QString VisualizationGraphWidget::name() const
722 {
742 {
723 return impl->m_Name;
743 return impl->m_Name;
724 }
744 }
725
745
726 QMimeData* VisualizationGraphWidget::mimeData(const QPoint& position) const
746 QMimeData* VisualizationGraphWidget::mimeData(const QPoint& position) const
727 {
747 {
728 auto mimeData = new QMimeData;
748 auto mimeData = new QMimeData;
729
749
730 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
750 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
731 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
751 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
732 && selectionZoneItemUnderCursor)
752 && selectionZoneItemUnderCursor)
733 {
753 {
734 mimeData->setData(MIME::MIME_TYPE_TIME_RANGE,
754 mimeData->setData(MIME::MIME_TYPE_TIME_RANGE,
735 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
755 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
736 mimeData->setData(MIME::MIME_TYPE_SELECTION_ZONE,
756 mimeData->setData(MIME::MIME_TYPE_SELECTION_ZONE,
737 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
757 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
738 }
758 }
739 else
759 else
740 {
760 {
741 mimeData->setData(MIME::MIME_TYPE_GRAPH, QByteArray {});
761 mimeData->setData(MIME::MIME_TYPE_GRAPH, QByteArray {});
742
762
743 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
763 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
744 mimeData->setData(MIME::MIME_TYPE_TIME_RANGE, timeRangeData);
764 mimeData->setData(MIME::MIME_TYPE_TIME_RANGE, timeRangeData);
745 }
765 }
746
766
747 return mimeData;
767 return mimeData;
748 }
768 }
749
769
750 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint& dragPosition)
770 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint& dragPosition)
751 {
771 {
752 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
772 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
753 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
773 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
754 && selectionZoneItemUnderCursor)
774 && selectionZoneItemUnderCursor)
755 {
775 {
756
776
757 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
777 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
758 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
778 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
759
779
760 auto zoneSize = QSizeF { qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
780 auto zoneSize = QSizeF { qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
761 qAbs(zoneBottomRight.y() - zoneTopLeft.y()) }
781 qAbs(zoneBottomRight.y() - zoneTopLeft.y()) }
762 .toSize();
782 .toSize();
763
783
764 auto pixmap = QPixmap(zoneSize);
784 auto pixmap = QPixmap(zoneSize);
765 render(&pixmap, QPoint(), QRegion { QRect { zoneTopLeft.toPoint(), zoneSize } });
785 render(&pixmap, QPoint(), QRegion { QRect { zoneTopLeft.toPoint(), zoneSize } });
766
786
767 return pixmap;
787 return pixmap;
768 }
788 }
769
789
770 return QPixmap();
790 return QPixmap();
771 }
791 }
772
792
773 bool VisualizationGraphWidget::isDragAllowed() const
793 bool VisualizationGraphWidget::isDragAllowed() const
774 {
794 {
775 return true;
795 return true;
776 }
796 }
777
797
778 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
798 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
779 {
799 {
780 if (highlighted)
800 if (highlighted)
781 {
801 {
782 plot().setBackground(QBrush(QColor("#BBD5EE")));
802 plot().setBackground(QBrush(QColor("#BBD5EE")));
783 }
803 }
784 else
804 else
785 {
805 {
786 plot().setBackground(QBrush(Qt::white));
806 plot().setBackground(QBrush(Qt::white));
787 }
807 }
788
808
789 plot().update();
809 plot().update();
790 }
810 }
791
811
792 void VisualizationGraphWidget::addVerticalCursor(double time)
812 void VisualizationGraphWidget::addVerticalCursor(double time)
793 {
813 {
794 impl->m_VerticalCursor->setPosition(time);
814 impl->m_VerticalCursor->setPosition(time);
795 impl->m_VerticalCursor->setVisible(true);
815 impl->m_VerticalCursor->setVisible(true);
796
816
797 auto text
817 auto text
798 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
818 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
799 impl->m_VerticalCursor->setLabelText(text);
819 impl->m_VerticalCursor->setLabelText(text);
800 }
820 }
801
821
802 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
822 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
803 {
823 {
804 impl->m_VerticalCursor->setAbsolutePosition(position);
824 impl->m_VerticalCursor->setAbsolutePosition(position);
805 impl->m_VerticalCursor->setVisible(true);
825 impl->m_VerticalCursor->setVisible(true);
806
826
807 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
827 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
808 auto text
828 auto text
809 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
829 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
810 impl->m_VerticalCursor->setLabelText(text);
830 impl->m_VerticalCursor->setLabelText(text);
811 }
831 }
812
832
813 void VisualizationGraphWidget::removeVerticalCursor()
833 void VisualizationGraphWidget::removeVerticalCursor()
814 {
834 {
815 impl->m_VerticalCursor->setVisible(false);
835 impl->m_VerticalCursor->setVisible(false);
816 plot().replot(QCustomPlot::rpQueuedReplot);
836 plot().replot(QCustomPlot::rpQueuedReplot);
817 }
837 }
818
838
819 void VisualizationGraphWidget::addHorizontalCursor(double value)
839 void VisualizationGraphWidget::addHorizontalCursor(double value)
820 {
840 {
821 impl->m_HorizontalCursor->setPosition(value);
841 impl->m_HorizontalCursor->setPosition(value);
822 impl->m_HorizontalCursor->setVisible(true);
842 impl->m_HorizontalCursor->setVisible(true);
823 impl->m_HorizontalCursor->setLabelText(QString::number(value));
843 impl->m_HorizontalCursor->setLabelText(QString::number(value));
824 }
844 }
825
845
826 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
846 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
827 {
847 {
828 impl->m_HorizontalCursor->setAbsolutePosition(position);
848 impl->m_HorizontalCursor->setAbsolutePosition(position);
829 impl->m_HorizontalCursor->setVisible(true);
849 impl->m_HorizontalCursor->setVisible(true);
830
850
831 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
851 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
832 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
852 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
833 }
853 }
834
854
835 void VisualizationGraphWidget::removeHorizontalCursor()
855 void VisualizationGraphWidget::removeHorizontalCursor()
836 {
856 {
837 impl->m_HorizontalCursor->setVisible(false);
857 impl->m_HorizontalCursor->setVisible(false);
838 plot().replot(QCustomPlot::rpQueuedReplot);
858 plot().replot(QCustomPlot::rpQueuedReplot);
839 }
859 }
840
860
841 void VisualizationGraphWidget::closeEvent(QCloseEvent* event)
861 void VisualizationGraphWidget::closeEvent(QCloseEvent* event)
842 {
862 {
843 Q_UNUSED(event);
863 Q_UNUSED(event);
844
864
845 for (auto i : impl->m_SelectionZones)
865 for (auto i : impl->m_SelectionZones)
846 {
866 {
847 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
867 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
848 }
868 }
849
869
850 // Prevents that all variables will be removed from graph when it will be closed
870 // Prevents that all variables will be removed from graph when it will be closed
851 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
871 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
852 {
872 {
853 emit variableAboutToBeRemoved(variableEntry.first);
873 emit variableAboutToBeRemoved(variableEntry.first);
854 }
874 }
855 }
875 }
856
876
857 void VisualizationGraphWidget::enterEvent(QEvent* event)
877 void VisualizationGraphWidget::enterEvent(QEvent* event)
858 {
878 {
859 Q_UNUSED(event);
879 Q_UNUSED(event);
860 impl->m_RenderingDelegate->showGraphOverlay(true);
880 impl->m_RenderingDelegate->showGraphOverlay(true);
861 }
881 }
862
882
863 void VisualizationGraphWidget::leaveEvent(QEvent* event)
883 void VisualizationGraphWidget::leaveEvent(QEvent* event)
864 {
884 {
865 Q_UNUSED(event);
885 Q_UNUSED(event);
866 impl->m_RenderingDelegate->showGraphOverlay(false);
886 impl->m_RenderingDelegate->showGraphOverlay(false);
867
887
868 if (auto parentZone = parentZoneWidget())
888 if (auto parentZone = parentZoneWidget())
869 {
889 {
870 parentZone->notifyMouseLeaveGraph(this);
890 parentZone->notifyMouseLeaveGraph(this);
871 }
891 }
872 else
892 else
873 {
893 {
874 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
894 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
875 }
895 }
876
896
877 if (impl->m_HoveredZone)
897 if (impl->m_HoveredZone)
878 {
898 {
879 impl->m_HoveredZone->setHovered(false);
899 impl->m_HoveredZone->setHovered(false);
880 impl->m_HoveredZone = nullptr;
900 impl->m_HoveredZone = nullptr;
881 }
901 }
882 }
902 }
883
903
884 void VisualizationGraphWidget::wheelEvent(QWheelEvent* event)
904 void VisualizationGraphWidget::wheelEvent(QWheelEvent* event)
885 {
905 {
886 double factor;
906 double factor;
887 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
907 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
888 if (event->modifiers() == Qt::ControlModifier)
908 if (event->modifiers() == Qt::ControlModifier)
889 {
909 {
890 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
910 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
891 {
911 {
892 setCursor(Qt::SizeVerCursor);
912 setCursor(Qt::SizeVerCursor);
893 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
913 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
894 zoom(factor, event->pos().y(), Qt::Vertical);
914 zoom(factor, event->pos().y(), Qt::Vertical);
895 }
915 }
896 }
916 }
897 else if (event->modifiers() == Qt::ShiftModifier)
917 else if (event->modifiers() == Qt::ShiftModifier)
898 {
918 {
899 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
919 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
900 {
920 {
901 setCursor(Qt::SizeHorCursor);
921 setCursor(Qt::SizeHorCursor);
902 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
922 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
903 zoom(factor, event->pos().x(), Qt::Horizontal);
923 zoom(factor, event->pos().x(), Qt::Horizontal);
904 }
924 }
905 }
925 }
906 else
926 else
907 {
927 {
908 move(wheelSteps, Qt::Horizontal);
928 move(wheelSteps, Qt::Horizontal);
909 }
929 }
910 event->accept();
930 event->accept();
911 }
931 }
912
932
913
933
914 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent* event)
934 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent* event)
915 {
935 {
916 if (impl->isDrawingZoomRect())
936 if (impl->isDrawingZoomRect())
917 {
937 {
918 impl->updateZoomRect(event->pos());
938 impl->updateZoomRect(event->pos());
919 }
939 }
920 else if (impl->isDrawingZoneRect())
940 else if (impl->isDrawingZoneRect())
921 {
941 {
922 impl->updateZoneRect(event->pos());
942 impl->updateZoneRect(event->pos());
923 }
943 }
924 else if (event->buttons() == Qt::LeftButton)
944 else if (event->buttons() == Qt::LeftButton)
925 {
945 {
926 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
946 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
927 {
947 {
928 auto [dx, dy] = impl->moveGraph(event->pos());
948 auto [dx, dy] = impl->moveGraph(event->pos());
929 emit this->setrange_sig(this->graphRange(), true, false);
949 emit this->setrange_sig(this->graphRange(), true, false);
930 }
950 }
931 else if (sqpApp->plotsInteractionMode()
951 else if (sqpApp->plotsInteractionMode()
932 == SqpApplication::PlotsInteractionMode::SelectionZones)
952 == SqpApplication::PlotsInteractionMode::SelectionZones)
933 {
953 {
934 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
954 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
935 if (auto item = impl->m_plot->itemAt(posInPlot))
955 if (auto item = impl->m_plot->itemAt(posInPlot))
936 {
956 {
937 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
957 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
938 {
958 {
939 QMouseEvent e { QEvent::MouseMove, posInPlot, event->button(), event->buttons(),
959 QMouseEvent e { QEvent::MouseMove, posInPlot, event->button(), event->buttons(),
940 event->modifiers() };
960 event->modifiers() };
941 sqpApp->sendEvent(this->impl->m_plot, &e);
961 sqpApp->sendEvent(this->impl->m_plot, &e);
942 this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh);
962 this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh);
943 }
963 }
944 }
964 }
945 }
965 }
946 }
966 }
947 else
967 else
948 {
968 {
949 impl->m_RenderingDelegate->updateTooltip(event);
969 impl->m_RenderingDelegate->updateTooltip(event);
950 }
970 }
951 // event->accept();
971 // event->accept();
952 VisualizationDragWidget::mouseMoveEvent(event);
972 VisualizationDragWidget::mouseMoveEvent(event);
953 }
973 }
954
974
955 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent* event)
975 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent* event)
956 {
976 {
957 if (impl->isDrawingZoomRect())
977 if (impl->isDrawingZoomRect())
958 {
978 {
959 auto oldRange = this->graphRange();
979 auto oldRange = this->graphRange();
960 impl->applyZoomRect();
980 impl->applyZoomRect();
961 auto newRange = this->graphRange();
981 auto newRange = this->graphRange();
962 if (auto tf = DateTimeRangeHelper::computeTransformation(oldRange, newRange))
982 if (auto tf = DateTimeRangeHelper::computeTransformation(oldRange, newRange))
963 emit this->transform_sig(tf.value(), false);
983 emit this->transform_sig(tf.value(), false);
964 }
984 }
965 else if (impl->isDrawingZoneRect())
985 else if (impl->isDrawingZoneRect())
966 {
986 {
967 impl->endDrawingZone();
987 impl->endDrawingZone();
968 }
988 }
969 else
989 else
970 {
990 {
971 setCursor(Qt::ArrowCursor);
991 setCursor(Qt::ArrowCursor);
972 }
992 }
973 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
993 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
974 if (auto item = impl->m_plot->itemAt(posInPlot))
994 if (auto item = impl->m_plot->itemAt(posInPlot))
975 {
995 {
976 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
996 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
977 {
997 {
978 QMouseEvent e { QEvent::MouseButtonRelease, posInPlot, event->button(),
998 QMouseEvent e { QEvent::MouseButtonRelease, posInPlot, event->button(),
979 event->buttons(), event->modifiers() };
999 event->buttons(), event->modifiers() };
980 sqpApp->sendEvent(this->impl->m_plot, &e);
1000 sqpApp->sendEvent(this->impl->m_plot, &e);
981 }
1001 }
982 }
1002 }
983 event->accept();
1003 event->accept();
984 }
1004 }
985
1005
986 void VisualizationGraphWidget::mousePressEvent(QMouseEvent* event)
1006 void VisualizationGraphWidget::mousePressEvent(QMouseEvent* event)
987 {
1007 {
988 if (event->button() == Qt::RightButton)
1008 if (event->button() == Qt::RightButton)
989 {
1009 {
990 onGraphMenuRequested(event->pos());
1010 onGraphMenuRequested(event->pos());
991 }
1011 }
992 else
1012 else
993 {
1013 {
994 auto selectedZone = impl->selectionZoneAt(event->pos());
1014 auto selectedZone = impl->selectionZoneAt(event->pos());
995 switch (sqpApp->plotsInteractionMode())
1015 switch (sqpApp->plotsInteractionMode())
996 {
1016 {
997 case SqpApplication::PlotsInteractionMode::DragAndDrop:
1017 case SqpApplication::PlotsInteractionMode::DragAndDrop:
998 break;
1018 break;
999 case SqpApplication::PlotsInteractionMode::SelectionZones:
1019 case SqpApplication::PlotsInteractionMode::SelectionZones:
1000 impl->setSelectionZonesEditionEnabled(true);
1020 impl->setSelectionZonesEditionEnabled(true);
1001 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
1021 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
1002 {
1022 {
1003 auto alreadySelectedZones
1023 auto alreadySelectedZones
1004 = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1024 = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1005 selectedZone->setAssociatedEditedZones(alreadySelectedZones);
1025 selectedZone->setAssociatedEditedZones(alreadySelectedZones);
1006 if (cpp_utils::containers::contains(alreadySelectedZones, selectedZone))
1026 if (cpp_utils::containers::contains(alreadySelectedZones, selectedZone))
1007 {
1027 {
1008 alreadySelectedZones.removeOne(selectedZone);
1028 alreadySelectedZones.removeOne(selectedZone);
1009 }
1029 }
1010 else
1030 else
1011 {
1031 {
1012 alreadySelectedZones.append(selectedZone);
1032 alreadySelectedZones.append(selectedZone);
1013 }
1033 }
1014 parentVisualizationWidget()->selectionZoneManager().select(
1034 parentVisualizationWidget()->selectionZoneManager().select(
1015 alreadySelectedZones);
1035 alreadySelectedZones);
1016 }
1036 }
1017 else
1037 else
1018 {
1038 {
1019 if (!selectedZone)
1039 if (!selectedZone)
1020 {
1040 {
1021 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1041 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1022 impl->startDrawingZone(event->pos());
1042 impl->startDrawingZone(event->pos());
1023 }
1043 }
1024 else
1044 else
1025 {
1045 {
1026 parentVisualizationWidget()->selectionZoneManager().select(
1046 parentVisualizationWidget()->selectionZoneManager().select(
1027 { selectedZone });
1047 { selectedZone });
1028 }
1048 }
1029 }
1049 }
1030 {
1050 {
1031 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
1051 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
1032 if (auto item = impl->m_plot->itemAt(posInPlot))
1052 if (auto item = impl->m_plot->itemAt(posInPlot))
1033 {
1053 {
1034 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1054 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1035 {
1055 {
1036 QMouseEvent e { QEvent::MouseButtonPress, posInPlot, event->button(),
1056 QMouseEvent e { QEvent::MouseButtonPress, posInPlot, event->button(),
1037 event->buttons(), event->modifiers() };
1057 event->buttons(), event->modifiers() };
1038 sqpApp->sendEvent(this->impl->m_plot, &e);
1058 sqpApp->sendEvent(this->impl->m_plot, &e);
1039 }
1059 }
1040 }
1060 }
1041 }
1061 }
1042 break;
1062 break;
1043 case SqpApplication::PlotsInteractionMode::ZoomBox:
1063 case SqpApplication::PlotsInteractionMode::ZoomBox:
1044 impl->startDrawingRect(event->pos());
1064 impl->startDrawingRect(event->pos());
1045 break;
1065 break;
1046 default:
1066 default:
1047 if (auto item = impl->m_plot->itemAt(event->pos()))
1067 if (auto item = impl->m_plot->itemAt(event->pos()))
1048 {
1068 {
1049 emit impl->m_plot->itemClick(item, event);
1069 emit impl->m_plot->itemClick(item, event);
1050 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1070 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1051 {
1071 {
1052 setCursor(Qt::ClosedHandCursor);
1072 setCursor(Qt::ClosedHandCursor);
1053 impl->enterPlotDrag(event->pos());
1073 impl->enterPlotDrag(event->pos());
1054 }
1074 }
1055 }
1075 }
1056 else
1076 else
1057 {
1077 {
1058 setCursor(Qt::ClosedHandCursor);
1078 setCursor(Qt::ClosedHandCursor);
1059 impl->enterPlotDrag(event->pos());
1079 impl->enterPlotDrag(event->pos());
1060 }
1080 }
1061 }
1081 }
1062 }
1082 }
1063 // event->accept();
1083 // event->accept();
1064 VisualizationDragWidget::mousePressEvent(event);
1084 VisualizationDragWidget::mousePressEvent(event);
1065 }
1085 }
1066
1086
1067 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent* event)
1087 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent* event)
1068 {
1088 {
1069 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1089 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1070 }
1090 }
1071
1091
1072 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent* event)
1092 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent* event)
1073 {
1093 {
1074 switch (event->key())
1094 switch (event->key())
1075 {
1095 {
1076 case Qt::Key_Control:
1096 case Qt::Key_Control:
1077 event->accept();
1097 event->accept();
1078 break;
1098 break;
1079 case Qt::Key_Shift:
1099 case Qt::Key_Shift:
1080 event->accept();
1100 event->accept();
1081 break;
1101 break;
1082 default:
1102 default:
1083 QWidget::keyReleaseEvent(event);
1103 QWidget::keyReleaseEvent(event);
1084 break;
1104 break;
1085 }
1105 }
1086 setCursor(Qt::ArrowCursor);
1106 setCursor(Qt::ArrowCursor);
1087 // event->accept();
1107 // event->accept();
1088 }
1108 }
1089
1109
1090 void VisualizationGraphWidget::keyPressEvent(QKeyEvent* event)
1110 void VisualizationGraphWidget::keyPressEvent(QKeyEvent* event)
1091 {
1111 {
1092 switch (event->key())
1112 switch (event->key())
1093 {
1113 {
1094 case Qt::Key_Control:
1114 case Qt::Key_Control:
1095 setCursor(Qt::CrossCursor);
1115 setCursor(Qt::CrossCursor);
1096 break;
1116 break;
1097 case Qt::Key_Shift:
1117 case Qt::Key_Shift:
1098 break;
1118 break;
1099 case Qt::Key_M:
1119 case Qt::Key_M:
1100 impl->rescaleY();
1120 impl->rescaleY();
1101 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1121 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1102 break;
1122 break;
1103 case Qt::Key_Left:
1123 case Qt::Key_Left:
1104 if (event->modifiers() != Qt::ControlModifier)
1124 if (event->modifiers() != Qt::ControlModifier)
1105 {
1125 {
1106 move(-0.1, Qt::Horizontal);
1126 move(-0.1, Qt::Horizontal);
1107 }
1127 }
1108 else
1128 else
1109 {
1129 {
1110 zoom(2, this->width() / 2, Qt::Horizontal);
1130 zoom(2, this->width() / 2, Qt::Horizontal);
1111 }
1131 }
1112 break;
1132 break;
1113 case Qt::Key_Right:
1133 case Qt::Key_Right:
1114 if (event->modifiers() != Qt::ControlModifier)
1134 if (event->modifiers() != Qt::ControlModifier)
1115 {
1135 {
1116 move(0.1, Qt::Horizontal);
1136 move(0.1, Qt::Horizontal);
1117 }
1137 }
1118 else
1138 else
1119 {
1139 {
1120 zoom(0.5, this->width() / 2, Qt::Horizontal);
1140 zoom(0.5, this->width() / 2, Qt::Horizontal);
1121 }
1141 }
1122 break;
1142 break;
1123 case Qt::Key_Up:
1143 case Qt::Key_Up:
1124 if (event->modifiers() != Qt::ControlModifier)
1144 if (event->modifiers() != Qt::ControlModifier)
1125 {
1145 {
1126 move(0.1, Qt::Vertical);
1146 move(0.1, Qt::Vertical);
1127 }
1147 }
1128 else
1148 else
1129 {
1149 {
1130 zoom(0.5, this->height() / 2, Qt::Vertical);
1150 zoom(0.5, this->height() / 2, Qt::Vertical);
1131 }
1151 }
1132 break;
1152 break;
1133 case Qt::Key_Down:
1153 case Qt::Key_Down:
1134 if (event->modifiers() != Qt::ControlModifier)
1154 if (event->modifiers() != Qt::ControlModifier)
1135 {
1155 {
1136 move(-0.1, Qt::Vertical);
1156 move(-0.1, Qt::Vertical);
1137 }
1157 }
1138 else
1158 else
1139 {
1159 {
1140 zoom(2, this->height() / 2, Qt::Vertical);
1160 zoom(2, this->height() / 2, Qt::Vertical);
1141 }
1161 }
1142 break;
1162 break;
1163 case Qt::Key_L:
1164 toggle_y_log_scale();
1165 break;
1143 default:
1166 default:
1144 QWidget::keyPressEvent(event);
1167 QWidget::keyPressEvent(event);
1145 break;
1168 break;
1146 }
1169 }
1147 }
1170 }
1148
1171
1149 QCustomPlot& VisualizationGraphWidget::plot() const noexcept
1172 QCustomPlot& VisualizationGraphWidget::plot() const noexcept
1150 {
1173 {
1151 return *impl->m_plot;
1174 return *impl->m_plot;
1152 }
1175 }
1153
1176
1154 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint& pos) noexcept
1177 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint& pos) noexcept
1155 {
1178 {
1156 QMenu graphMenu {};
1179 QMenu graphMenu {};
1157
1180
1158 // Iterates on variables (unique keys)
1181 // Iterates on variables (unique keys)
1159 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1182 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1160 end = impl->m_VariableToPlotMultiMap.cend();
1183 end = impl->m_VariableToPlotMultiMap.cend();
1161 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first))
1184 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first))
1162 {
1185 {
1163 // 'Remove variable' action
1186 // 'Remove variable' action
1164 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1187 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1165 [this, var = it->first]() { removeVariable(var); });
1188 [this, var = it->first]() { removeVariable(var); });
1166 }
1189 }
1167
1190
1168 if (!impl->m_ZoomStack.isEmpty())
1191 if (!impl->m_ZoomStack.isEmpty())
1169 {
1192 {
1170 if (!graphMenu.isEmpty())
1193 if (!graphMenu.isEmpty())
1171 {
1194 {
1172 graphMenu.addSeparator();
1195 graphMenu.addSeparator();
1173 }
1196 }
1174
1197
1175 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1198 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1176 }
1199 }
1177
1200
1178 // Selection Zone Actions
1201 // Selection Zone Actions
1179 auto selectionZoneItem = impl->selectionZoneAt(pos);
1202 auto selectionZoneItem = impl->selectionZoneAt(pos);
1180 if (selectionZoneItem)
1203 if (selectionZoneItem)
1181 {
1204 {
1182 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1205 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1183 selectedItems.removeAll(selectionZoneItem);
1206 selectedItems.removeAll(selectionZoneItem);
1184 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1207 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1185
1208
1186 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1209 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1187 if (!zoneActions.isEmpty() && !graphMenu.isEmpty())
1210 if (!zoneActions.isEmpty() && !graphMenu.isEmpty())
1188 {
1211 {
1189 graphMenu.addSeparator();
1212 graphMenu.addSeparator();
1190 }
1213 }
1191
1214
1192 QHash<QString, QMenu*> subMenus;
1215 QHash<QString, QMenu*> subMenus;
1193 QHash<QString, bool> subMenusEnabled;
1216 QHash<QString, bool> subMenusEnabled;
1194 QHash<QString, FilteringAction*> filteredMenu;
1217 QHash<QString, FilteringAction*> filteredMenu;
1195
1218
1196 for (auto zoneAction : zoneActions)
1219 for (auto zoneAction : zoneActions)
1197 {
1220 {
1198
1221
1199 auto isEnabled = zoneAction->isEnabled(selectedItems);
1222 auto isEnabled = zoneAction->isEnabled(selectedItems);
1200
1223
1201 auto menu = &graphMenu;
1224 auto menu = &graphMenu;
1202 QString menuPath;
1225 QString menuPath;
1203 for (auto subMenuName : zoneAction->subMenuList())
1226 for (auto subMenuName : zoneAction->subMenuList())
1204 {
1227 {
1205 menuPath += '/';
1228 menuPath += '/';
1206 menuPath += subMenuName;
1229 menuPath += subMenuName;
1207
1230
1208 if (!subMenus.contains(menuPath))
1231 if (!subMenus.contains(menuPath))
1209 {
1232 {
1210 menu = menu->addMenu(subMenuName);
1233 menu = menu->addMenu(subMenuName);
1211 subMenus[menuPath] = menu;
1234 subMenus[menuPath] = menu;
1212 subMenusEnabled[menuPath] = isEnabled;
1235 subMenusEnabled[menuPath] = isEnabled;
1213 }
1236 }
1214 else
1237 else
1215 {
1238 {
1216 menu = subMenus.value(menuPath);
1239 menu = subMenus.value(menuPath);
1217 if (isEnabled)
1240 if (isEnabled)
1218 {
1241 {
1219 // The sub menu is enabled if at least one of its actions is enabled
1242 // The sub menu is enabled if at least one of its actions is enabled
1220 subMenusEnabled[menuPath] = true;
1243 subMenusEnabled[menuPath] = true;
1221 }
1244 }
1222 }
1245 }
1223 }
1246 }
1224
1247
1225 FilteringAction* filterAction = nullptr;
1248 FilteringAction* filterAction = nullptr;
1226 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList()))
1249 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList()))
1227 {
1250 {
1228 filterAction = filteredMenu.value(menuPath);
1251 filterAction = filteredMenu.value(menuPath);
1229 if (!filterAction)
1252 if (!filterAction)
1230 {
1253 {
1231 filterAction = new FilteringAction { this };
1254 filterAction = new FilteringAction { this };
1232 filteredMenu[menuPath] = filterAction;
1255 filteredMenu[menuPath] = filterAction;
1233 menu->addAction(filterAction);
1256 menu->addAction(filterAction);
1234 }
1257 }
1235 }
1258 }
1236
1259
1237 auto action = menu->addAction(zoneAction->name());
1260 auto action = menu->addAction(zoneAction->name());
1238 action->setEnabled(isEnabled);
1261 action->setEnabled(isEnabled);
1239 action->setShortcut(zoneAction->displayedShortcut());
1262 action->setShortcut(zoneAction->displayedShortcut());
1240 QObject::connect(action, &QAction::triggered,
1263 QObject::connect(action, &QAction::triggered,
1241 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1264 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1242
1265
1243 if (filterAction && zoneAction->isFilteringAllowed())
1266 if (filterAction && zoneAction->isFilteringAllowed())
1244 {
1267 {
1245 filterAction->addActionToFilter(action);
1268 filterAction->addActionToFilter(action);
1246 }
1269 }
1247 }
1270 }
1248
1271
1249 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it)
1272 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it)
1250 {
1273 {
1251 it.value()->setEnabled(subMenusEnabled[it.key()]);
1274 it.value()->setEnabled(subMenusEnabled[it.key()]);
1252 }
1275 }
1253 }
1276 }
1254
1277
1255 if (!graphMenu.isEmpty())
1278 if (!graphMenu.isEmpty())
1256 {
1279 {
1257 graphMenu.exec(QCursor::pos());
1280 graphMenu.exec(QCursor::pos());
1258 }
1281 }
1259 }
1282 }
1260
1283
1261 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent* event) noexcept
1284 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent* event) noexcept
1262 {
1285 {
1263 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1286 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1264 }
1287 }
1265
1288
1266 void VisualizationGraphWidget::onMouseMove(QMouseEvent* event) noexcept
1289 void VisualizationGraphWidget::onMouseMove(QMouseEvent* event) noexcept
1267 {
1290 {
1268 // Handles plot rendering when mouse is moving
1291 // Handles plot rendering when mouse is moving
1269 impl->m_RenderingDelegate->updateTooltip(event);
1292 impl->m_RenderingDelegate->updateTooltip(event);
1270
1293
1271 auto axisPos = impl->posToAxisPos(event->pos());
1294 auto axisPos = impl->posToAxisPos(event->pos());
1272
1295
1273 // Zoom box and zone drawing
1296 // Zoom box and zone drawing
1274 if (impl->m_DrawingZoomRect)
1297 if (impl->m_DrawingZoomRect)
1275 {
1298 {
1276 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1299 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1277 }
1300 }
1278 else if (impl->m_DrawingZone)
1301 else if (impl->m_DrawingZone)
1279 {
1302 {
1280 impl->m_DrawingZone->setEnd(axisPos.x());
1303 impl->m_DrawingZone->setEnd(axisPos.x());
1281 }
1304 }
1282
1305
1283 // Cursor
1306 // Cursor
1284 if (auto parentZone = parentZoneWidget())
1307 if (auto parentZone = parentZoneWidget())
1285 {
1308 {
1286 if (impl->pointIsInAxisRect(axisPos, plot()))
1309 if (impl->pointIsInAxisRect(axisPos, plot()))
1287 {
1310 {
1288 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1311 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1289 }
1312 }
1290 else
1313 else
1291 {
1314 {
1292 parentZone->notifyMouseLeaveGraph(this);
1315 parentZone->notifyMouseLeaveGraph(this);
1293 }
1316 }
1294 }
1317 }
1295
1318
1296 // Search for the selection zone under the mouse
1319 // Search for the selection zone under the mouse
1297 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1320 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1298 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1321 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1299 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
1322 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
1300 {
1323 {
1301
1324
1302 // Sets the appropriate cursor shape
1325 // Sets the appropriate cursor shape
1303 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1326 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1304 setCursor(cursorShape);
1327 setCursor(cursorShape);
1305
1328
1306 // Manages the hovered zone
1329 // Manages the hovered zone
1307 if (selectionZoneItemUnderCursor != impl->m_HoveredZone)
1330 if (selectionZoneItemUnderCursor != impl->m_HoveredZone)
1308 {
1331 {
1309 if (impl->m_HoveredZone)
1332 if (impl->m_HoveredZone)
1310 {
1333 {
1311 impl->m_HoveredZone->setHovered(false);
1334 impl->m_HoveredZone->setHovered(false);
1312 }
1335 }
1313 selectionZoneItemUnderCursor->setHovered(true);
1336 selectionZoneItemUnderCursor->setHovered(true);
1314 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1337 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1315 plot().replot(QCustomPlot::rpQueuedReplot);
1338 plot().replot(QCustomPlot::rpQueuedReplot);
1316 }
1339 }
1317 }
1340 }
1318 else
1341 else
1319 {
1342 {
1320 // There is no zone under the mouse or the interaction mode is not "selection zones"
1343 // There is no zone under the mouse or the interaction mode is not "selection zones"
1321 if (impl->m_HoveredZone)
1344 if (impl->m_HoveredZone)
1322 {
1345 {
1323 impl->m_HoveredZone->setHovered(false);
1346 impl->m_HoveredZone->setHovered(false);
1324 impl->m_HoveredZone = nullptr;
1347 impl->m_HoveredZone = nullptr;
1325 }
1348 }
1326
1349
1327 setCursor(Qt::ArrowCursor);
1350 setCursor(Qt::ArrowCursor);
1328 }
1351 }
1329
1352
1330 impl->m_HasMovedMouse = true;
1353 impl->m_HasMovedMouse = true;
1331 VisualizationDragWidget::mouseMoveEvent(event);
1354 VisualizationDragWidget::mouseMoveEvent(event);
1332 }
1355 }
1333
1356
1334 void VisualizationGraphWidget::onMouseWheel(QWheelEvent* event) noexcept
1357 void VisualizationGraphWidget::onMouseWheel(QWheelEvent* event) noexcept
1335 {
1358 {
1336 // Processes event only if the wheel occurs on axis rect
1359 // Processes event only if the wheel occurs on axis rect
1337 if (!dynamic_cast<QCPAxisRect*>(impl->m_plot->layoutElementAt(event->posF())))
1360 if (!dynamic_cast<QCPAxisRect*>(impl->m_plot->layoutElementAt(event->posF())))
1338 {
1361 {
1339 return;
1362 return;
1340 }
1363 }
1341
1364
1342 auto value = event->angleDelta().x() + event->angleDelta().y();
1365 auto value = event->angleDelta().x() + event->angleDelta().y();
1343 if (value != 0)
1366 if (value != 0)
1344 {
1367 {
1345
1368
1346 auto direction = value > 0 ? 1.0 : -1.0;
1369 auto direction = value > 0 ? 1.0 : -1.0;
1347 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1370 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1348 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1371 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1349 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1372 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1350
1373
1351 auto zoomOrientations = QFlags<Qt::Orientation> {};
1374 auto zoomOrientations = QFlags<Qt::Orientation> {};
1352 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1375 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1353 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1376 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1354
1377
1355 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1378 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1356
1379
1357 if (!isZoomX && !isZoomY)
1380 if (!isZoomX && !isZoomY)
1358 {
1381 {
1359 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1382 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1360 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1383 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1361
1384
1362 axis->setRange(axis->range() + diff);
1385 axis->setRange(axis->range() + diff);
1363
1386
1364 if (plot().noAntialiasingOnDrag())
1387 if (plot().noAntialiasingOnDrag())
1365 {
1388 {
1366 plot().setNotAntialiasedElements(QCP::aeAll);
1389 plot().setNotAntialiasedElements(QCP::aeAll);
1367 }
1390 }
1368
1391
1369 // plot().replot(QCustomPlot::rpQueuedReplot);
1392 // plot().replot(QCustomPlot::rpQueuedReplot);
1370 }
1393 }
1371 }
1394 }
1372 }
1395 }
1373
1396
1374 void VisualizationGraphWidget::onMousePress(QMouseEvent* event) noexcept
1397 void VisualizationGraphWidget::onMousePress(QMouseEvent* event) noexcept
1375 {
1398 {
1376 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1399 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1377 auto isSelectionZoneMode
1400 auto isSelectionZoneMode
1378 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1401 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1379 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1402 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1380
1403
1381 if (!isDragDropClick && isLeftClick)
1404 if (!isDragDropClick && isLeftClick)
1382 {
1405 {
1383 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox)
1406 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox)
1384 {
1407 {
1385 // Starts a zoom box
1408 // Starts a zoom box
1386 impl->startDrawingRect(event->pos());
1409 impl->startDrawingRect(event->pos());
1387 }
1410 }
1388 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr)
1411 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr)
1389 {
1412 {
1390 // Starts a new selection zone
1413 // Starts a new selection zone
1391 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1414 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1392 if (!zoneAtPos)
1415 if (!zoneAtPos)
1393 {
1416 {
1394 impl->startDrawingZone(event->pos());
1417 impl->startDrawingZone(event->pos());
1395 }
1418 }
1396 }
1419 }
1397 }
1420 }
1398
1421
1399
1422
1400 // Allows zone edition only in selection zone mode without drag&drop
1423 // Allows zone edition only in selection zone mode without drag&drop
1401 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1424 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1402
1425
1403 // Selection / Deselection
1426 // Selection / Deselection
1404 if (isSelectionZoneMode)
1427 if (isSelectionZoneMode)
1405 {
1428 {
1406 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1429 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1407 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1430 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1408
1431
1409
1432
1410 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1433 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1411 && !isMultiSelectionClick)
1434 && !isMultiSelectionClick)
1412 {
1435 {
1413 parentVisualizationWidget()->selectionZoneManager().select(
1436 parentVisualizationWidget()->selectionZoneManager().select(
1414 { selectionZoneItemUnderCursor });
1437 { selectionZoneItemUnderCursor });
1415 }
1438 }
1416 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick)
1439 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick)
1417 {
1440 {
1418 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1441 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1419 }
1442 }
1420 else
1443 else
1421 {
1444 {
1422 // No selection change
1445 // No selection change
1423 }
1446 }
1424
1447
1425 if (selectionZoneItemUnderCursor && isLeftClick)
1448 if (selectionZoneItemUnderCursor && isLeftClick)
1426 {
1449 {
1427 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1450 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1428 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1451 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1429 }
1452 }
1430 }
1453 }
1431
1454
1432
1455
1433 impl->m_HasMovedMouse = false;
1456 impl->m_HasMovedMouse = false;
1434 VisualizationDragWidget::mousePressEvent(event);
1457 VisualizationDragWidget::mousePressEvent(event);
1435 }
1458 }
1436
1459
1437 void VisualizationGraphWidget::onMouseRelease(QMouseEvent* event) noexcept
1460 void VisualizationGraphWidget::onMouseRelease(QMouseEvent* event) noexcept
1438 {
1461 {
1439 if (impl->m_DrawingZoomRect)
1462 if (impl->m_DrawingZoomRect)
1440 {
1463 {
1441
1464
1442 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1465 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1443 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1466 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1444
1467
1445 auto newAxisXRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().x(),
1468 auto newAxisXRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().x(),
1446 impl->m_DrawingZoomRect->bottomRight->coords().x() };
1469 impl->m_DrawingZoomRect->bottomRight->coords().x() };
1447
1470
1448 auto newAxisYRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().y(),
1471 auto newAxisYRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().y(),
1449 impl->m_DrawingZoomRect->bottomRight->coords().y() };
1472 impl->m_DrawingZoomRect->bottomRight->coords().y() };
1450
1473
1451 impl->removeDrawingRect();
1474 impl->removeDrawingRect();
1452
1475
1453 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1476 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1454 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
1477 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
1455 {
1478 {
1456 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1479 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1457 axisX->setRange(newAxisXRange);
1480 axisX->setRange(newAxisXRange);
1458 axisY->setRange(newAxisYRange);
1481 axisY->setRange(newAxisYRange);
1459
1482
1460 plot().replot(QCustomPlot::rpQueuedReplot);
1483 plot().replot(QCustomPlot::rpQueuedReplot);
1461 }
1484 }
1462 }
1485 }
1463
1486
1464 impl->endDrawingZone();
1487 impl->endDrawingZone();
1465
1488
1466 // Selection / Deselection
1489 // Selection / Deselection
1467 auto isSelectionZoneMode
1490 auto isSelectionZoneMode
1468 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1491 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1469 if (isSelectionZoneMode)
1492 if (isSelectionZoneMode)
1470 {
1493 {
1471 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1494 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1472 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1495 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1473 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1496 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1474 && !impl->m_HasMovedMouse)
1497 && !impl->m_HasMovedMouse)
1475 {
1498 {
1476
1499
1477 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1500 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1478 if (zonesUnderCursor.count() > 1)
1501 if (zonesUnderCursor.count() > 1)
1479 {
1502 {
1480 // There are multiple zones under the mouse.
1503 // There are multiple zones under the mouse.
1481 // Performs the selection with a selection dialog.
1504 // Performs the selection with a selection dialog.
1482 VisualizationMultiZoneSelectionDialog dialog { this };
1505 VisualizationMultiZoneSelectionDialog dialog { this };
1483 dialog.setZones(zonesUnderCursor);
1506 dialog.setZones(zonesUnderCursor);
1484 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1507 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1485 dialog.activateWindow();
1508 dialog.activateWindow();
1486 dialog.raise();
1509 dialog.raise();
1487 if (dialog.exec() == QDialog::Accepted)
1510 if (dialog.exec() == QDialog::Accepted)
1488 {
1511 {
1489 auto selection = dialog.selectedZones();
1512 auto selection = dialog.selectedZones();
1490
1513
1491 if (!isMultiSelectionClick)
1514 if (!isMultiSelectionClick)
1492 {
1515 {
1493 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1516 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1494 }
1517 }
1495
1518
1496 for (auto it = selection.cbegin(); it != selection.cend(); ++it)
1519 for (auto it = selection.cbegin(); it != selection.cend(); ++it)
1497 {
1520 {
1498 auto zone = it.key();
1521 auto zone = it.key();
1499 auto isSelected = it.value();
1522 auto isSelected = it.value();
1500 parentVisualizationWidget()->selectionZoneManager().setSelected(
1523 parentVisualizationWidget()->selectionZoneManager().setSelected(
1501 zone, isSelected);
1524 zone, isSelected);
1502
1525
1503 if (isSelected)
1526 if (isSelected)
1504 {
1527 {
1505 // Puts the zone on top of the stack so it can be moved or resized
1528 // Puts the zone on top of the stack so it can be moved or resized
1506 impl->moveSelectionZoneOnTop(zone, plot());
1529 impl->moveSelectionZoneOnTop(zone, plot());
1507 }
1530 }
1508 }
1531 }
1509 }
1532 }
1510 }
1533 }
1511 else
1534 else
1512 {
1535 {
1513 if (!isMultiSelectionClick)
1536 if (!isMultiSelectionClick)
1514 {
1537 {
1515 parentVisualizationWidget()->selectionZoneManager().select(
1538 parentVisualizationWidget()->selectionZoneManager().select(
1516 { selectionZoneItemUnderCursor });
1539 { selectionZoneItemUnderCursor });
1517 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1540 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1518 }
1541 }
1519 else
1542 else
1520 {
1543 {
1521 parentVisualizationWidget()->selectionZoneManager().setSelected(
1544 parentVisualizationWidget()->selectionZoneManager().setSelected(
1522 selectionZoneItemUnderCursor,
1545 selectionZoneItemUnderCursor,
1523 !selectionZoneItemUnderCursor->selected()
1546 !selectionZoneItemUnderCursor->selected()
1524 || event->button() == Qt::RightButton);
1547 || event->button() == Qt::RightButton);
1525 }
1548 }
1526 }
1549 }
1527 }
1550 }
1528 else
1551 else
1529 {
1552 {
1530 // No selection change
1553 // No selection change
1531 }
1554 }
1532 }
1555 }
1533 }
1556 }
1534
1557
1535 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1558 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1536 {
1559 {
1537 auto graphRange = impl->m_plot->xAxis->range();
1560 auto graphRange = impl->m_plot->xAxis->range();
1538 auto dateTime = DateTimeRange { graphRange.lower, graphRange.upper };
1561 auto dateTime = DateTimeRange { graphRange.lower, graphRange.upper };
1539
1562
1540 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
1563 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
1541 {
1564 {
1542 auto variable = variableEntry.first;
1565 auto variable = variableEntry.first;
1543 qCDebug(LOG_VisualizationGraphWidget())
1566 qCDebug(LOG_VisualizationGraphWidget())
1544 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1567 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1545 qCDebug(LOG_VisualizationGraphWidget())
1568 qCDebug(LOG_VisualizationGraphWidget())
1546 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1569 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1547 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range()))
1570 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range()))
1548 {
1571 {
1549 impl->updateData(variableEntry.second, variable, variable->range());
1572 impl->updateData(variableEntry.second, variable, variable->range());
1550 }
1573 }
1551 }
1574 }
1552 }
1575 }
1553
1576
1554 void VisualizationGraphWidget::onUpdateVarDisplaying(
1577 void VisualizationGraphWidget::onUpdateVarDisplaying(
1555 std::shared_ptr<Variable2> variable, const DateTimeRange& range)
1578 std::shared_ptr<Variable2> variable, const DateTimeRange& range)
1556 {
1579 {
1557 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1580 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1558 if (it != impl->m_VariableToPlotMultiMap.end())
1581 if (it != impl->m_VariableToPlotMultiMap.end())
1559 {
1582 {
1560 impl->updateData(it->second, variable, range);
1583 impl->updateData(it->second, variable, range);
1561 }
1584 }
1562 }
1585 }
1563
1586
1564 void VisualizationGraphWidget::variableUpdated(QUuid id)
1587 void VisualizationGraphWidget::variableUpdated(QUuid id)
1565 {
1588 {
1566 for (auto& [var, plotables] : impl->m_VariableToPlotMultiMap)
1589 for (auto& [var, plotables] : impl->m_VariableToPlotMultiMap)
1567 {
1590 {
1568 if (var->ID() == id)
1591 if (var->ID() == id)
1569 {
1592 {
1570 impl->updateData(plotables, var, this->graphRange());
1593 impl->updateData(plotables, var, this->graphRange());
1571 }
1594 }
1572 }
1595 }
1573 }
1596 }
1574
1597
1575 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable2>& variable)
1598 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable2>& variable)
1576 {
1599 {
1577 this->removeVariable(variable);
1600 this->removeVariable(variable);
1578 }
1601 }
General Comments 0
You need to be logged in to leave comments. Login now