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