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