##// END OF EJS Templates
Updates VisualizationGraphHelper to use variable's type instead of dataseries
Alexandre Leroux -
r1334:5b0b3c10ce1e
parent child
Show More
@@ -1,41 +1,41
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHHELPER_H
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHHELPER_H
2 #define SCIQLOP_VISUALIZATIONGRAPHHELPER_H
2 #define SCIQLOP_VISUALIZATIONGRAPHHELPER_H
3
3
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5
5
6 #include <Data/SqpRange.h>
6 #include <Data/SqpRange.h>
7
7
8 #include <QLoggingCategory>
8 #include <QLoggingCategory>
9 #include <QVector>
9 #include <QVector>
10
10
11 #include <memory>
11 #include <memory>
12
12
13 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphHelper)
13 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphHelper)
14
14
15 class IDataSeries;
15 class IDataSeries;
16 class QCPAbstractPlottable;
16 class QCPAbstractPlottable;
17 class QCustomPlot;
17 class QCustomPlot;
18 class Variable;
18 class Variable;
19
19
20 /**
20 /**
21 * @brief The VisualizationGraphHelper class aims to create the QCustomPlot components relative to a
21 * @brief The VisualizationGraphHelper class aims to create the QCustomPlot components relative to a
22 * variable, depending on the data series of this variable
22 * variable, depending on the data series of this variable
23 */
23 */
24 struct VisualizationGraphHelper {
24 struct VisualizationGraphHelper {
25 /**
25 /**
26 * Creates (if possible) the QCustomPlot components relative to the variable passed in
26 * Creates (if possible) the QCustomPlot components relative to the variable passed in
27 * parameter, and adds these to the plot passed in parameter.
27 * parameter, and adds these to the plot passed in parameter.
28 * @param variable the variable for which to create the components
28 * @param variable the variable for which to create the components
29 * @param plot the plot in which to add the created components. It takes ownership of these
29 * @param plot the plot in which to add the created components. It takes ownership of these
30 * components.
30 * components.
31 * @return the list of the components created
31 * @return the list of the components created
32 */
32 */
33 static PlottablesMap create(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept;
33 static PlottablesMap create(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept;
34
34
35 static void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
35 static void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
36 const SqpRange &dateTime);
36 const SqpRange &dateTime);
37
37
38 static void setYAxisRange(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept;
38 static void setYAxisRange(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept;
39 };
39 };
40
40
41 #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H
41 #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H
@@ -1,340 +1,361
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Data/DataSeriesUtils.h>
4 #include <Data/DataSeriesUtils.h>
5 #include <Data/ScalarSeries.h>
5 #include <Data/ScalarSeries.h>
6 #include <Data/SpectrogramSeries.h>
6 #include <Data/SpectrogramSeries.h>
7 #include <Data/VectorSeries.h>
7 #include <Data/VectorSeries.h>
8
8
9 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
10
10
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12
12
13 namespace {
13 namespace {
14
14
15 class SqpDataContainer : public QCPGraphDataContainer {
15 class SqpDataContainer : public QCPGraphDataContainer {
16 public:
16 public:
17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
18 };
18 };
19
19
20 /**
20 /**
21 * Struct used to create plottables, depending on the type of the data series from which to create
21 * Struct used to create plottables, depending on the type of the data series from which to create
22 * them
22 * them
23 * @tparam T the data series' type
23 * @tparam T the data series' type
24 * @remarks Default implementation can't create plottables
24 * @remarks Default implementation can't create plottables
25 */
25 */
26 template <typename T, typename Enabled = void>
26 template <typename T, typename Enabled = void>
27 struct PlottablesCreator {
27 struct PlottablesCreator {
28 static PlottablesMap createPlottables(T &, QCustomPlot &)
28 static PlottablesMap createPlottables(QCustomPlot &)
29 {
29 {
30 qCCritical(LOG_DataSeries())
30 qCCritical(LOG_DataSeries())
31 << QObject::tr("Can't create plottables: unmanaged data series type");
31 << QObject::tr("Can't create plottables: unmanaged data series type");
32 return {};
32 return {};
33 }
33 }
34 };
34 };
35
35
36 PlottablesMap createGraphs(QCustomPlot &plot, int nbGraphs)
37 {
38 PlottablesMap result{};
39
40 // Creates {nbGraphs} QCPGraph to add to the plot
41 for (auto i = 0; i < nbGraphs; ++i) {
42 auto graph = plot.addGraph();
43 result.insert({i, graph});
44 }
45
46 plot.replot();
47
48 return result;
49 }
50
36 /**
51 /**
37 * Specialization of PlottablesCreator for scalars and vectors
52 * Specialization of PlottablesCreator for scalars
38 * @sa ScalarSeries
53 * @sa ScalarSeries
39 * @sa VectorSeries
40 */
54 */
41 template <typename T>
55 template <typename T>
42 struct PlottablesCreator<T,
56 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value> > {
43 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
57 static PlottablesMap createPlottables(QCustomPlot &plot) { return createGraphs(plot, 1); }
44 or std::is_base_of<VectorSeries, T>::value> > {
58 };
45 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
46 {
47 PlottablesMap result{};
48
49 // Gets the number of components of the data series
50 dataSeries.lockRead();
51 auto componentCount = dataSeries.valuesData()->componentCount();
52 dataSeries.unlock();
53
54 // For each component of the data series, creates a QCPGraph to add to the plot
55 for (auto i = 0; i < componentCount; ++i) {
56 auto graph = plot.addGraph();
57 result.insert({i, graph});
58 }
59
60 plot.replot();
61
59
62 return result;
60 /**
63 }
61 * Specialization of PlottablesCreator for vectors
62 * @sa VectorSeries
63 */
64 template <typename T>
65 struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorSeries, T>::value> > {
66 static PlottablesMap createPlottables(QCustomPlot &plot) { return createGraphs(plot, 3); }
64 };
67 };
65
68
66 /**
69 /**
67 * Specialization of PlottablesCreator for spectrograms
70 * Specialization of PlottablesCreator for spectrograms
68 * @sa SpectrogramSeries
71 * @sa SpectrogramSeries
69 */
72 */
70 template <typename T>
73 template <typename T>
71 struct PlottablesCreator<T,
74 struct PlottablesCreator<T,
72 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
75 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
73 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
76 static PlottablesMap createPlottables(QCustomPlot &plot)
74 {
77 {
75 PlottablesMap result{};
78 PlottablesMap result{};
76 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
79 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
77
80
78 plot.replot();
81 plot.replot();
79
82
80 return result;
83 return result;
81 }
84 }
82 };
85 };
83
86
84 /**
87 /**
85 * Struct used to update plottables, depending on the type of the data series from which to update
88 * Struct used to update plottables, depending on the type of the data series from which to update
86 * them
89 * them
87 * @tparam T the data series' type
90 * @tparam T the data series' type
88 * @remarks Default implementation can't update plottables
91 * @remarks Default implementation can't update plottables
89 */
92 */
90 template <typename T, typename Enabled = void>
93 template <typename T, typename Enabled = void>
91 struct PlottablesUpdater {
94 struct PlottablesUpdater {
92 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
95 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
93 {
96 {
94 qCCritical(LOG_VisualizationGraphHelper())
97 qCCritical(LOG_VisualizationGraphHelper())
95 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
98 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
96 }
99 }
97
100
98 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
101 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
99 {
102 {
100 qCCritical(LOG_VisualizationGraphHelper())
103 qCCritical(LOG_VisualizationGraphHelper())
101 << QObject::tr("Can't update plottables: unmanaged data series type");
104 << QObject::tr("Can't update plottables: unmanaged data series type");
102 }
105 }
103 };
106 };
104
107
105 /**
108 /**
106 * Specialization of PlottablesUpdater for scalars and vectors
109 * Specialization of PlottablesUpdater for scalars and vectors
107 * @sa ScalarSeries
110 * @sa ScalarSeries
108 * @sa VectorSeries
111 * @sa VectorSeries
109 */
112 */
110 template <typename T>
113 template <typename T>
111 struct PlottablesUpdater<T,
114 struct PlottablesUpdater<T,
112 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
115 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
113 or std::is_base_of<VectorSeries, T>::value> > {
116 or std::is_base_of<VectorSeries, T>::value> > {
114 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
117 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
115 {
118 {
116 auto minValue = 0., maxValue = 0.;
119 auto minValue = 0., maxValue = 0.;
117
120
118 dataSeries.lockRead();
121 dataSeries.lockRead();
119 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
122 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
120 auto end = dataSeries.cend();
123 auto end = dataSeries.cend();
121 if (valuesBounds.first != end && valuesBounds.second != end) {
124 if (valuesBounds.first != end && valuesBounds.second != end) {
122 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
125 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
123
126
124 minValue = rangeValue(valuesBounds.first->minValue());
127 minValue = rangeValue(valuesBounds.first->minValue());
125 maxValue = rangeValue(valuesBounds.second->maxValue());
128 maxValue = rangeValue(valuesBounds.second->maxValue());
126 }
129 }
127 dataSeries.unlock();
130 dataSeries.unlock();
128
131
129 plot.yAxis->setRange(QCPRange{minValue, maxValue});
132 plot.yAxis->setRange(QCPRange{minValue, maxValue});
130 }
133 }
131
134
132 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
135 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
133 bool rescaleAxes)
136 bool rescaleAxes)
134 {
137 {
135
138
136 // For each plottable to update, resets its data
139 // For each plottable to update, resets its data
137 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
140 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
138 for (const auto &plottable : plottables) {
141 for (const auto &plottable : plottables) {
139 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
142 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
140 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
143 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
141 graph->setData(dataContainer);
144 graph->setData(dataContainer);
142
145
143 dataContainers.insert({plottable.first, dataContainer});
146 dataContainers.insert({plottable.first, dataContainer});
144 }
147 }
145 }
148 }
146 dataSeries.lockRead();
149 dataSeries.lockRead();
147
150
148 // - Gets the data of the series included in the current range
151 // - Gets the data of the series included in the current range
149 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
152 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
150 // and value data. The correct value is retrieved according to the index of the component
153 // and value data. The correct value is retrieved according to the index of the component
151 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
154 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
152 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
155 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
153 for (const auto &dataContainer : dataContainers) {
156 for (const auto &dataContainer : dataContainers) {
154 auto componentIndex = dataContainer.first;
157 auto componentIndex = dataContainer.first;
155 dataContainer.second->appendGraphData(
158 dataContainer.second->appendGraphData(
156 QCPGraphData(it->x(), it->value(componentIndex)));
159 QCPGraphData(it->x(), it->value(componentIndex)));
157 }
160 }
158 }
161 }
159
162
160 dataSeries.unlock();
163 dataSeries.unlock();
161
164
162 if (!plottables.empty()) {
165 if (!plottables.empty()) {
163 auto plot = plottables.begin()->second->parentPlot();
166 auto plot = plottables.begin()->second->parentPlot();
164
167
165 if (rescaleAxes) {
168 if (rescaleAxes) {
166 plot->rescaleAxes();
169 plot->rescaleAxes();
167 }
170 }
168 }
171 }
169 }
172 }
170 };
173 };
171
174
172 /**
175 /**
173 * Specialization of PlottablesUpdater for spectrograms
176 * Specialization of PlottablesUpdater for spectrograms
174 * @sa SpectrogramSeries
177 * @sa SpectrogramSeries
175 */
178 */
176 template <typename T>
179 template <typename T>
177 struct PlottablesUpdater<T,
180 struct PlottablesUpdater<T,
178 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
181 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
179 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
182 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
180 {
183 {
181 double min, max;
184 double min, max;
182 std::tie(min, max) = dataSeries.yBounds();
185 std::tie(min, max) = dataSeries.yBounds();
183
186
184 if (!std::isnan(min) && !std::isnan(max)) {
187 if (!std::isnan(min) && !std::isnan(max)) {
185 plot.yAxis->setRange(QCPRange{min, max});
188 plot.yAxis->setRange(QCPRange{min, max});
186 }
189 }
187 }
190 }
188
191
189 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
192 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
190 bool rescaleAxes)
193 bool rescaleAxes)
191 {
194 {
192 if (plottables.empty()) {
195 if (plottables.empty()) {
193 qCDebug(LOG_VisualizationGraphHelper())
196 qCDebug(LOG_VisualizationGraphHelper())
194 << QObject::tr("Can't update spectrogram: no colormap has been associated");
197 << QObject::tr("Can't update spectrogram: no colormap has been associated");
195 return;
198 return;
196 }
199 }
197
200
198 // Gets the colormap to update (normally there is only one colormap)
201 // Gets the colormap to update (normally there is only one colormap)
199 Q_ASSERT(plottables.size() == 1);
202 Q_ASSERT(plottables.size() == 1);
200 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
203 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
201 Q_ASSERT(colormap != nullptr);
204 Q_ASSERT(colormap != nullptr);
202
205
203 dataSeries.lockRead();
206 dataSeries.lockRead();
204
207
205 // Processing spectrogram data for display in QCustomPlot
208 // Processing spectrogram data for display in QCustomPlot
206 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
209 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
207
210
208 // Computes logarithmic y-axis resolution for the spectrogram
211 // Computes logarithmic y-axis resolution for the spectrogram
209 auto yData = its.first->y();
212 auto yData = its.first->y();
210 auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true);
213 auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true);
211
214
212 // Generates mesh for colormap
215 // Generates mesh for colormap
213 auto mesh = DataSeriesUtils::regularMesh(
216 auto mesh = DataSeriesUtils::regularMesh(
214 its.first, its.second, DataSeriesUtils::Resolution{dataSeries.xResolution()},
217 its.first, its.second, DataSeriesUtils::Resolution{dataSeries.xResolution()},
215 yResolution);
218 yResolution);
216
219
217 dataSeries.unlock();
220 dataSeries.unlock();
218
221
219 colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY);
222 colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY);
220 if (!mesh.isEmpty()) {
223 if (!mesh.isEmpty()) {
221 colormap->data()->setRange(
224 colormap->data()->setRange(
222 QCPRange{mesh.m_XMin, mesh.xMax()},
225 QCPRange{mesh.m_XMin, mesh.xMax()},
223 // y-axis range is converted to linear values
226 // y-axis range is converted to linear values
224 QCPRange{std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax())});
227 QCPRange{std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax())});
225
228
226 // Sets values
229 // Sets values
227 auto index = 0;
230 auto index = 0;
228 for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it, ++index) {
231 for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it, ++index) {
229 auto xIndex = index % mesh.m_NbX;
232 auto xIndex = index % mesh.m_NbX;
230 auto yIndex = index / mesh.m_NbX;
233 auto yIndex = index / mesh.m_NbX;
231
234
232 colormap->data()->setCell(xIndex, yIndex, *it);
235 colormap->data()->setCell(xIndex, yIndex, *it);
233
236
234 // Makes the NaN values to be transparent in the colormap
237 // Makes the NaN values to be transparent in the colormap
235 if (std::isnan(*it)) {
238 if (std::isnan(*it)) {
236 colormap->data()->setAlpha(xIndex, yIndex, 0);
239 colormap->data()->setAlpha(xIndex, yIndex, 0);
237 }
240 }
238 }
241 }
239 }
242 }
240
243
241 // Rescales axes
244 // Rescales axes
242 auto plot = colormap->parentPlot();
245 auto plot = colormap->parentPlot();
243
246
244 if (rescaleAxes) {
247 if (rescaleAxes) {
245 plot->rescaleAxes();
248 plot->rescaleAxes();
246 }
249 }
247 }
250 }
248 };
251 };
249
252
250 /**
253 /**
251 * Helper used to create/update plottables
254 * Helper used to create/update plottables
252 */
255 */
253 struct IPlottablesHelper {
256 struct IPlottablesHelper {
254 virtual ~IPlottablesHelper() noexcept = default;
257 virtual ~IPlottablesHelper() noexcept = default;
255 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
258 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
256 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
259 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
257 virtual void update(PlottablesMap &plottables, const SqpRange &range,
260 virtual void update(PlottablesMap &plottables, const SqpRange &range,
258 bool rescaleAxes = false) const = 0;
261 bool rescaleAxes = false) const = 0;
259 };
262 };
260
263
261 /**
264 /**
262 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
265 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
263 * @tparam T the data series' type
266 * @tparam T the data series' type
264 */
267 */
265 template <typename T>
268 template <typename T>
266 struct PlottablesHelper : public IPlottablesHelper {
269 struct PlottablesHelper : public IPlottablesHelper {
267 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
270 explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries{dataSeries} {}
268
271
269 PlottablesMap create(QCustomPlot &plot) const override
272 PlottablesMap create(QCustomPlot &plot) const override
270 {
273 {
271 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
274 return PlottablesCreator<T>::createPlottables(plot);
272 }
275 }
273
276
274 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
277 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
275 {
278 {
276 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
279 if (m_DataSeries) {
280 PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes);
281 }
282 else {
283 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
284 "between the type of data series and the "
285 "type supposed";
286 }
277 }
287 }
278
288
279 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
289 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
280 {
290 {
281 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
291 if (m_DataSeries) {
292 PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot);
293 }
294 else {
295 qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency "
296 "between the type of data series and the "
297 "type supposed";
298 }
282 }
299 }
283
300
284 T &m_DataSeries;
301 std::shared_ptr<T> m_DataSeries;
285 };
302 };
286
303
287 /// Creates IPlottablesHelper according to a data series
304 /// Creates IPlottablesHelper according to the type of data series a variable holds
288 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
305 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable> variable) noexcept
289 {
306 {
290 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
307 switch (variable->type()) {
291 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
308 case DataSeriesType::SCALAR:
292 }
309 return std::make_unique<PlottablesHelper<ScalarSeries> >(
293 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
310 std::dynamic_pointer_cast<ScalarSeries>(variable->dataSeries()));
294 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
311 case DataSeriesType::SPECTROGRAM:
295 }
312 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(
296 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
313 std::dynamic_pointer_cast<SpectrogramSeries>(variable->dataSeries()));
297 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
314 case DataSeriesType::VECTOR:
298 }
315 return std::make_unique<PlottablesHelper<VectorSeries> >(
299 else {
316 std::dynamic_pointer_cast<VectorSeries>(variable->dataSeries()));
300 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
317 default:
318 // Creates default helper
319 break;
301 }
320 }
321
322 return std::make_unique<PlottablesHelper<IDataSeries> >(nullptr);
302 }
323 }
303
324
304 } // namespace
325 } // namespace
305
326
306 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
327 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
307 QCustomPlot &plot) noexcept
328 QCustomPlot &plot) noexcept
308 {
329 {
309 if (variable) {
330 if (variable) {
310 auto helper = createHelper(variable->dataSeries());
331 auto helper = createHelper(variable);
311 auto plottables = helper->create(plot);
332 auto plottables = helper->create(plot);
312 return plottables;
333 return plottables;
313 }
334 }
314 else {
335 else {
315 qCDebug(LOG_VisualizationGraphHelper())
336 qCDebug(LOG_VisualizationGraphHelper())
316 << QObject::tr("Can't create graph plottables : the variable is null");
337 << QObject::tr("Can't create graph plottables : the variable is null");
317 return PlottablesMap{};
338 return PlottablesMap{};
318 }
339 }
319 }
340 }
320
341
321 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
342 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
322 QCustomPlot &plot) noexcept
343 QCustomPlot &plot) noexcept
323 {
344 {
324 if (variable) {
345 if (variable) {
325 auto helper = createHelper(variable->dataSeries());
346 auto helper = createHelper(variable);
326 helper->setYAxisRange(variable->range(), plot);
347 helper->setYAxisRange(variable->range(), plot);
327 }
348 }
328 else {
349 else {
329 qCDebug(LOG_VisualizationGraphHelper())
350 qCDebug(LOG_VisualizationGraphHelper())
330 << QObject::tr("Can't set y-axis range of plot: the variable is null");
351 << QObject::tr("Can't set y-axis range of plot: the variable is null");
331 }
352 }
332 }
353 }
333
354
334 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
355 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
335 std::shared_ptr<IDataSeries> dataSeries,
356 std::shared_ptr<Variable> variable,
336 const SqpRange &dateTime)
357 const SqpRange &dateTime)
337 {
358 {
338 auto helper = createHelper(dataSeries);
359 auto helper = createHelper(variable);
339 helper->update(plottables, dateTime);
360 helper->update(plottables, dateTime);
340 }
361 }
@@ -1,1004 +1,1004
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 <Common/MimeTypesDef.h>
15 #include <Common/MimeTypesDef.h>
16 #include <Data/ArrayData.h>
16 #include <Data/ArrayData.h>
17 #include <Data/IDataSeries.h>
17 #include <Data/IDataSeries.h>
18 #include <Data/SpectrogramSeries.h>
18 #include <Data/SpectrogramSeries.h>
19 #include <DragAndDrop/DragDropGuiController.h>
19 #include <DragAndDrop/DragDropGuiController.h>
20 #include <Settings/SqpSettingsDefs.h>
20 #include <Settings/SqpSettingsDefs.h>
21 #include <SqpApplication.h>
21 #include <SqpApplication.h>
22 #include <Time/TimeController.h>
22 #include <Time/TimeController.h>
23 #include <Variable/Variable.h>
23 #include <Variable/Variable.h>
24 #include <Variable/VariableController.h>
24 #include <Variable/VariableController.h>
25
25
26 #include <unordered_map>
26 #include <unordered_map>
27
27
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29
29
30 namespace {
30 namespace {
31
31
32 /// Key pressed to enable drag&drop in all modes
32 /// Key pressed to enable drag&drop in all modes
33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34
34
35 /// Key pressed to enable zoom on horizontal axis
35 /// Key pressed to enable zoom on horizontal axis
36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37
37
38 /// Key pressed to enable zoom on vertical axis
38 /// Key pressed to enable zoom on vertical axis
39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40
40
41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 const auto PAN_SPEED = 5;
42 const auto PAN_SPEED = 5;
43
43
44 /// Key pressed to enable a calibration pan
44 /// Key pressed to enable a calibration pan
45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46
46
47 /// Key pressed to enable multi selection of selection zones
47 /// Key pressed to enable multi selection of selection zones
48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49
49
50 /// Minimum size for the zoom box, in percentage of the axis range
50 /// Minimum size for the zoom box, in percentage of the axis range
51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52
52
53 /// Format of the dates appearing in the label of a cursor
53 /// Format of the dates appearing in the label of a cursor
54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55
55
56 } // namespace
56 } // namespace
57
57
58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59
59
60 explicit VisualizationGraphWidgetPrivate(const QString &name)
60 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 : m_Name{name},
61 : m_Name{name},
62 m_Flags{GraphFlag::EnableAll},
62 m_Flags{GraphFlag::EnableAll},
63 m_IsCalibration{false},
63 m_IsCalibration{false},
64 m_RenderingDelegate{nullptr}
64 m_RenderingDelegate{nullptr}
65 {
65 {
66 }
66 }
67
67
68 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
68 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
69 const SqpRange &range)
69 const SqpRange &range)
70 {
70 {
71 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
71 VisualizationGraphHelper::updateData(plottables, variable, range);
72
72
73 // Prevents that data has changed to update rendering
73 // Prevents that data has changed to update rendering
74 m_RenderingDelegate->onPlotUpdated();
74 m_RenderingDelegate->onPlotUpdated();
75 }
75 }
76
76
77 QString m_Name;
77 QString m_Name;
78 // 1 variable -> n qcpplot
78 // 1 variable -> n qcpplot
79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 GraphFlags m_Flags;
80 GraphFlags m_Flags;
81 bool m_IsCalibration;
81 bool m_IsCalibration;
82 /// Delegate used to attach rendering features to the plot
82 /// Delegate used to attach rendering features to the plot
83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84
84
85 QCPItemRect *m_DrawingZoomRect = nullptr;
85 QCPItemRect *m_DrawingZoomRect = nullptr;
86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87
87
88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90
90
91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94
94
95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96
96
97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
98 {
98 {
99 removeDrawingRect(plot);
99 removeDrawingRect(plot);
100
100
101 auto axisPos = posToAxisPos(pos, plot);
101 auto axisPos = posToAxisPos(pos, plot);
102
102
103 m_DrawingZoomRect = new QCPItemRect{&plot};
103 m_DrawingZoomRect = new QCPItemRect{&plot};
104 QPen p;
104 QPen p;
105 p.setWidth(2);
105 p.setWidth(2);
106 m_DrawingZoomRect->setPen(p);
106 m_DrawingZoomRect->setPen(p);
107
107
108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
110 }
110 }
111
111
112 void removeDrawingRect(QCustomPlot &plot)
112 void removeDrawingRect(QCustomPlot &plot)
113 {
113 {
114 if (m_DrawingZoomRect) {
114 if (m_DrawingZoomRect) {
115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
116 m_DrawingZoomRect = nullptr;
116 m_DrawingZoomRect = nullptr;
117 plot.replot(QCustomPlot::rpQueuedReplot);
117 plot.replot(QCustomPlot::rpQueuedReplot);
118 }
118 }
119 }
119 }
120
120
121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
122 {
122 {
123 endDrawingZone(graph);
123 endDrawingZone(graph);
124
124
125 auto axisPos = posToAxisPos(pos, graph->plot());
125 auto axisPos = posToAxisPos(pos, graph->plot());
126
126
127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
129 m_DrawingZone->setEditionEnabled(false);
129 m_DrawingZone->setEditionEnabled(false);
130 }
130 }
131
131
132 void endDrawingZone(VisualizationGraphWidget *graph)
132 void endDrawingZone(VisualizationGraphWidget *graph)
133 {
133 {
134 if (m_DrawingZone) {
134 if (m_DrawingZone) {
135 auto drawingZoneRange = m_DrawingZone->range();
135 auto drawingZoneRange = m_DrawingZone->range();
136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
137 m_DrawingZone->setEditionEnabled(true);
137 m_DrawingZone->setEditionEnabled(true);
138 addSelectionZone(m_DrawingZone);
138 addSelectionZone(m_DrawingZone);
139 }
139 }
140 else {
140 else {
141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
142 }
142 }
143
143
144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
145 m_DrawingZone = nullptr;
145 m_DrawingZone = nullptr;
146 }
146 }
147 }
147 }
148
148
149 void setSelectionZonesEditionEnabled(bool value)
149 void setSelectionZonesEditionEnabled(bool value)
150 {
150 {
151 for (auto s : m_SelectionZones) {
151 for (auto s : m_SelectionZones) {
152 s->setEditionEnabled(value);
152 s->setEditionEnabled(value);
153 }
153 }
154 }
154 }
155
155
156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
157
157
158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
159 const QCustomPlot &plot) const
159 const QCustomPlot &plot) const
160 {
160 {
161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
162 auto minDistanceToZone = -1;
162 auto minDistanceToZone = -1;
163 for (auto zone : m_SelectionZones) {
163 for (auto zone : m_SelectionZones) {
164 auto distanceToZone = zone->selectTest(pos, false);
164 auto distanceToZone = zone->selectTest(pos, false);
165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
167 selectionZoneItemUnderCursor = zone;
167 selectionZoneItemUnderCursor = zone;
168 }
168 }
169 }
169 }
170
170
171 return selectionZoneItemUnderCursor;
171 return selectionZoneItemUnderCursor;
172 }
172 }
173
173
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
175 const QCustomPlot &plot) const
175 const QCustomPlot &plot) const
176 {
176 {
177 QVector<VisualizationSelectionZoneItem *> zones;
177 QVector<VisualizationSelectionZoneItem *> zones;
178 for (auto zone : m_SelectionZones) {
178 for (auto zone : m_SelectionZones) {
179 auto distanceToZone = zone->selectTest(pos, false);
179 auto distanceToZone = zone->selectTest(pos, false);
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
181 zones << zone;
181 zones << zone;
182 }
182 }
183 }
183 }
184
184
185 return zones;
185 return zones;
186 }
186 }
187
187
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
189 {
189 {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
191 zone->moveToTop();
191 zone->moveToTop();
192 m_SelectionZones.removeAll(zone);
192 m_SelectionZones.removeAll(zone);
193 m_SelectionZones.append(zone);
193 m_SelectionZones.append(zone);
194 }
194 }
195 }
195 }
196
196
197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
198 {
198 {
199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
202 }
202 }
203
203
204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
205 {
205 {
206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
209 }
209 }
210 };
210 };
211
211
212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
213 : VisualizationDragWidget{parent},
213 : VisualizationDragWidget{parent},
214 ui{new Ui::VisualizationGraphWidget},
214 ui{new Ui::VisualizationGraphWidget},
215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
216 {
216 {
217 ui->setupUi(this);
217 ui->setupUi(this);
218
218
219 // 'Close' options : widget is deleted when closed
219 // 'Close' options : widget is deleted when closed
220 setAttribute(Qt::WA_DeleteOnClose);
220 setAttribute(Qt::WA_DeleteOnClose);
221
221
222 // Set qcpplot properties :
222 // Set qcpplot properties :
223 // - zoom is enabled
223 // - zoom is enabled
224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
225 ui->widget->setInteractions(QCP::iRangeZoom);
225 ui->widget->setInteractions(QCP::iRangeZoom);
226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
227
227
228 // The delegate must be initialized after the ui as it uses the plot
228 // The delegate must be initialized after the ui as it uses the plot
229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
230
230
231 // Init the cursors
231 // Init the cursors
232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
236
236
237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
239 &VisualizationGraphWidget::onMouseRelease);
239 &VisualizationGraphWidget::onMouseRelease);
240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
243 &VisualizationGraphWidget::onMouseDoubleClick);
243 &VisualizationGraphWidget::onMouseDoubleClick);
244 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
244 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
245 &QCPAxis::rangeChanged),
245 &QCPAxis::rangeChanged),
246 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
246 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
247
247
248 // Activates menu when right clicking on the graph
248 // Activates menu when right clicking on the graph
249 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
249 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
250 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
250 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
251 &VisualizationGraphWidget::onGraphMenuRequested);
251 &VisualizationGraphWidget::onGraphMenuRequested);
252
252
253 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
253 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
254 &VariableController::onRequestDataLoading);
254 &VariableController::onRequestDataLoading);
255
255
256 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
256 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
257 &VisualizationGraphWidget::onUpdateVarDisplaying);
257 &VisualizationGraphWidget::onUpdateVarDisplaying);
258
258
259 #ifdef Q_OS_MAC
259 #ifdef Q_OS_MAC
260 plot().setPlottingHint(QCP::phFastPolylines, true);
260 plot().setPlottingHint(QCP::phFastPolylines, true);
261 #endif
261 #endif
262 }
262 }
263
263
264
264
265 VisualizationGraphWidget::~VisualizationGraphWidget()
265 VisualizationGraphWidget::~VisualizationGraphWidget()
266 {
266 {
267 delete ui;
267 delete ui;
268 }
268 }
269
269
270 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
270 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
271 {
271 {
272 auto parent = parentWidget();
272 auto parent = parentWidget();
273 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
273 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
274 parent = parent->parentWidget();
274 parent = parent->parentWidget();
275 }
275 }
276
276
277 return qobject_cast<VisualizationZoneWidget *>(parent);
277 return qobject_cast<VisualizationZoneWidget *>(parent);
278 }
278 }
279
279
280 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
280 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
281 {
281 {
282 auto parent = parentWidget();
282 auto parent = parentWidget();
283 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
283 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
284 parent = parent->parentWidget();
284 parent = parent->parentWidget();
285 }
285 }
286
286
287 return qobject_cast<VisualizationWidget *>(parent);
287 return qobject_cast<VisualizationWidget *>(parent);
288 }
288 }
289
289
290 void VisualizationGraphWidget::setFlags(GraphFlags flags)
290 void VisualizationGraphWidget::setFlags(GraphFlags flags)
291 {
291 {
292 impl->m_Flags = std::move(flags);
292 impl->m_Flags = std::move(flags);
293 }
293 }
294
294
295 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
295 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
296 {
296 {
297 // Uses delegate to create the qcpplot components according to the variable
297 // Uses delegate to create the qcpplot components according to the variable
298 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
298 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
299
299
300 if (auto dataSeries = variable->dataSeries()) {
300 if (auto dataSeries = variable->dataSeries()) {
301 // Set axes properties according to the units of the data series
301 // Set axes properties according to the units of the data series
302 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
302 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
303
303
304 // Sets rendering properties for the new plottables
304 // Sets rendering properties for the new plottables
305 // Warning: this method must be called after setAxesProperties(), as it can access to some
305 // Warning: this method must be called after setAxesProperties(), as it can access to some
306 // axes properties that have to be initialized
306 // axes properties that have to be initialized
307 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
307 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
308 }
308 }
309
309
310 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
310 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
311
311
312 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
312 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
313
313
314 this->setFlags(GraphFlag::DisableAll);
314 this->setFlags(GraphFlag::DisableAll);
315 this->setGraphRange(range);
315 this->setGraphRange(range);
316 this->setFlags(GraphFlag::EnableAll);
316 this->setFlags(GraphFlag::EnableAll);
317
317
318 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
318 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
319
319
320 emit variableAdded(variable);
320 emit variableAdded(variable);
321 }
321 }
322
322
323 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
323 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
324 {
324 {
325 // Each component associated to the variable :
325 // Each component associated to the variable :
326 // - is removed from qcpplot (which deletes it)
326 // - is removed from qcpplot (which deletes it)
327 // - is no longer referenced in the map
327 // - is no longer referenced in the map
328 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
328 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
329 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
329 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
330 emit variableAboutToBeRemoved(variable);
330 emit variableAboutToBeRemoved(variable);
331
331
332 auto &plottablesMap = variableIt->second;
332 auto &plottablesMap = variableIt->second;
333
333
334 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
334 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
335 plottableIt != plottableEnd;) {
335 plottableIt != plottableEnd;) {
336 ui->widget->removePlottable(plottableIt->second);
336 ui->widget->removePlottable(plottableIt->second);
337 plottableIt = plottablesMap.erase(plottableIt);
337 plottableIt = plottablesMap.erase(plottableIt);
338 }
338 }
339
339
340 impl->m_VariableToPlotMultiMap.erase(variableIt);
340 impl->m_VariableToPlotMultiMap.erase(variableIt);
341 }
341 }
342
342
343 // Updates graph
343 // Updates graph
344 ui->widget->replot();
344 ui->widget->replot();
345 }
345 }
346
346
347 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
347 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
348 {
348 {
349 auto variables = QList<std::shared_ptr<Variable> >{};
349 auto variables = QList<std::shared_ptr<Variable> >{};
350 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
350 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
351 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
351 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
352 variables << it->first;
352 variables << it->first;
353 }
353 }
354
354
355 return variables;
355 return variables;
356 }
356 }
357
357
358 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
358 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
359 {
359 {
360 if (!variable) {
360 if (!variable) {
361 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
361 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
362 return;
362 return;
363 }
363 }
364
364
365 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
365 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
366 }
366 }
367
367
368 SqpRange VisualizationGraphWidget::graphRange() const noexcept
368 SqpRange VisualizationGraphWidget::graphRange() const noexcept
369 {
369 {
370 auto graphRange = ui->widget->xAxis->range();
370 auto graphRange = ui->widget->xAxis->range();
371 return SqpRange{graphRange.lower, graphRange.upper};
371 return SqpRange{graphRange.lower, graphRange.upper};
372 }
372 }
373
373
374 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
374 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
375 {
375 {
376 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
376 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
377 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
377 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
378 ui->widget->replot();
378 ui->widget->replot();
379 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
379 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
380 }
380 }
381
381
382 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
382 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
383 {
383 {
384 QVector<SqpRange> ranges;
384 QVector<SqpRange> ranges;
385 for (auto zone : impl->m_SelectionZones) {
385 for (auto zone : impl->m_SelectionZones) {
386 ranges << zone->range();
386 ranges << zone->range();
387 }
387 }
388
388
389 return ranges;
389 return ranges;
390 }
390 }
391
391
392 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
392 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
393 {
393 {
394 for (const auto &range : ranges) {
394 for (const auto &range : ranges) {
395 // note: ownership is transfered to QCustomPlot
395 // note: ownership is transfered to QCustomPlot
396 auto zone = new VisualizationSelectionZoneItem(&plot());
396 auto zone = new VisualizationSelectionZoneItem(&plot());
397 zone->setRange(range.m_TStart, range.m_TEnd);
397 zone->setRange(range.m_TStart, range.m_TEnd);
398 impl->addSelectionZone(zone);
398 impl->addSelectionZone(zone);
399 }
399 }
400
400
401 plot().replot(QCustomPlot::rpQueuedReplot);
401 plot().replot(QCustomPlot::rpQueuedReplot);
402 }
402 }
403
403
404 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
404 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
405 {
405 {
406 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
406 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
407
407
408 if (impl->m_HoveredZone == selectionZone) {
408 if (impl->m_HoveredZone == selectionZone) {
409 impl->m_HoveredZone = nullptr;
409 impl->m_HoveredZone = nullptr;
410 setCursor(Qt::ArrowCursor);
410 setCursor(Qt::ArrowCursor);
411 }
411 }
412
412
413 impl->m_SelectionZones.removeAll(selectionZone);
413 impl->m_SelectionZones.removeAll(selectionZone);
414 plot().removeItem(selectionZone);
414 plot().removeItem(selectionZone);
415 plot().replot(QCustomPlot::rpQueuedReplot);
415 plot().replot(QCustomPlot::rpQueuedReplot);
416 }
416 }
417
417
418 void VisualizationGraphWidget::undoZoom()
418 void VisualizationGraphWidget::undoZoom()
419 {
419 {
420 auto zoom = impl->m_ZoomStack.pop();
420 auto zoom = impl->m_ZoomStack.pop();
421 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
421 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
422 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
422 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
423
423
424 axisX->setRange(zoom.first);
424 axisX->setRange(zoom.first);
425 axisY->setRange(zoom.second);
425 axisY->setRange(zoom.second);
426
426
427 plot().replot(QCustomPlot::rpQueuedReplot);
427 plot().replot(QCustomPlot::rpQueuedReplot);
428 }
428 }
429
429
430 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
430 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
431 {
431 {
432 if (visitor) {
432 if (visitor) {
433 visitor->visit(this);
433 visitor->visit(this);
434 }
434 }
435 else {
435 else {
436 qCCritical(LOG_VisualizationGraphWidget())
436 qCCritical(LOG_VisualizationGraphWidget())
437 << tr("Can't visit widget : the visitor is null");
437 << tr("Can't visit widget : the visitor is null");
438 }
438 }
439 }
439 }
440
440
441 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
441 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
442 {
442 {
443 auto isSpectrogram = [](const auto &variable) {
443 auto isSpectrogram = [](const auto &variable) {
444 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
444 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
445 };
445 };
446
446
447 // - A spectrogram series can't be dropped on graph with existing plottables
447 // - A spectrogram series can't be dropped on graph with existing plottables
448 // - No data series can be dropped on graph with existing spectrogram series
448 // - No data series can be dropped on graph with existing spectrogram series
449 return isSpectrogram(variable)
449 return isSpectrogram(variable)
450 ? impl->m_VariableToPlotMultiMap.empty()
450 ? impl->m_VariableToPlotMultiMap.empty()
451 : std::none_of(
451 : std::none_of(
452 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
452 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
453 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
453 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
454 }
454 }
455
455
456 bool VisualizationGraphWidget::contains(const Variable &variable) const
456 bool VisualizationGraphWidget::contains(const Variable &variable) const
457 {
457 {
458 // Finds the variable among the keys of the map
458 // Finds the variable among the keys of the map
459 auto variablePtr = &variable;
459 auto variablePtr = &variable;
460 auto findVariable
460 auto findVariable
461 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
461 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
462
462
463 auto end = impl->m_VariableToPlotMultiMap.cend();
463 auto end = impl->m_VariableToPlotMultiMap.cend();
464 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
464 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
465 return it != end;
465 return it != end;
466 }
466 }
467
467
468 QString VisualizationGraphWidget::name() const
468 QString VisualizationGraphWidget::name() const
469 {
469 {
470 return impl->m_Name;
470 return impl->m_Name;
471 }
471 }
472
472
473 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
473 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
474 {
474 {
475 auto mimeData = new QMimeData;
475 auto mimeData = new QMimeData;
476
476
477 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
477 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
478 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
478 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
479 && selectionZoneItemUnderCursor) {
479 && selectionZoneItemUnderCursor) {
480 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
480 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
481 selectionZoneItemUnderCursor->range()));
481 selectionZoneItemUnderCursor->range()));
482 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
482 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
483 selectionZoneItemUnderCursor->range()));
483 selectionZoneItemUnderCursor->range()));
484 }
484 }
485 else {
485 else {
486 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
486 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
487
487
488 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
488 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
489 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
489 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
490 }
490 }
491
491
492 return mimeData;
492 return mimeData;
493 }
493 }
494
494
495 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
495 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
496 {
496 {
497 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
497 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
498 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
498 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
499 && selectionZoneItemUnderCursor) {
499 && selectionZoneItemUnderCursor) {
500
500
501 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
501 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
502 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
502 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
503
503
504 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
504 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
505 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
505 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
506 .toSize();
506 .toSize();
507
507
508 auto pixmap = QPixmap(zoneSize);
508 auto pixmap = QPixmap(zoneSize);
509 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
509 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
510
510
511 return pixmap;
511 return pixmap;
512 }
512 }
513
513
514 return QPixmap();
514 return QPixmap();
515 }
515 }
516
516
517 bool VisualizationGraphWidget::isDragAllowed() const
517 bool VisualizationGraphWidget::isDragAllowed() const
518 {
518 {
519 return true;
519 return true;
520 }
520 }
521
521
522 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
522 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
523 {
523 {
524 if (highlighted) {
524 if (highlighted) {
525 plot().setBackground(QBrush(QColor("#BBD5EE")));
525 plot().setBackground(QBrush(QColor("#BBD5EE")));
526 }
526 }
527 else {
527 else {
528 plot().setBackground(QBrush(Qt::white));
528 plot().setBackground(QBrush(Qt::white));
529 }
529 }
530
530
531 plot().update();
531 plot().update();
532 }
532 }
533
533
534 void VisualizationGraphWidget::addVerticalCursor(double time)
534 void VisualizationGraphWidget::addVerticalCursor(double time)
535 {
535 {
536 impl->m_VerticalCursor->setPosition(time);
536 impl->m_VerticalCursor->setPosition(time);
537 impl->m_VerticalCursor->setVisible(true);
537 impl->m_VerticalCursor->setVisible(true);
538
538
539 auto text
539 auto text
540 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
540 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
541 impl->m_VerticalCursor->setLabelText(text);
541 impl->m_VerticalCursor->setLabelText(text);
542 }
542 }
543
543
544 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
544 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
545 {
545 {
546 impl->m_VerticalCursor->setAbsolutePosition(position);
546 impl->m_VerticalCursor->setAbsolutePosition(position);
547 impl->m_VerticalCursor->setVisible(true);
547 impl->m_VerticalCursor->setVisible(true);
548
548
549 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
549 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
550 auto text
550 auto text
551 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
551 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
552 impl->m_VerticalCursor->setLabelText(text);
552 impl->m_VerticalCursor->setLabelText(text);
553 }
553 }
554
554
555 void VisualizationGraphWidget::removeVerticalCursor()
555 void VisualizationGraphWidget::removeVerticalCursor()
556 {
556 {
557 impl->m_VerticalCursor->setVisible(false);
557 impl->m_VerticalCursor->setVisible(false);
558 plot().replot(QCustomPlot::rpQueuedReplot);
558 plot().replot(QCustomPlot::rpQueuedReplot);
559 }
559 }
560
560
561 void VisualizationGraphWidget::addHorizontalCursor(double value)
561 void VisualizationGraphWidget::addHorizontalCursor(double value)
562 {
562 {
563 impl->m_HorizontalCursor->setPosition(value);
563 impl->m_HorizontalCursor->setPosition(value);
564 impl->m_HorizontalCursor->setVisible(true);
564 impl->m_HorizontalCursor->setVisible(true);
565 impl->m_HorizontalCursor->setLabelText(QString::number(value));
565 impl->m_HorizontalCursor->setLabelText(QString::number(value));
566 }
566 }
567
567
568 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
568 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
569 {
569 {
570 impl->m_HorizontalCursor->setAbsolutePosition(position);
570 impl->m_HorizontalCursor->setAbsolutePosition(position);
571 impl->m_HorizontalCursor->setVisible(true);
571 impl->m_HorizontalCursor->setVisible(true);
572
572
573 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
573 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
574 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
574 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
575 }
575 }
576
576
577 void VisualizationGraphWidget::removeHorizontalCursor()
577 void VisualizationGraphWidget::removeHorizontalCursor()
578 {
578 {
579 impl->m_HorizontalCursor->setVisible(false);
579 impl->m_HorizontalCursor->setVisible(false);
580 plot().replot(QCustomPlot::rpQueuedReplot);
580 plot().replot(QCustomPlot::rpQueuedReplot);
581 }
581 }
582
582
583 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
583 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
584 {
584 {
585 Q_UNUSED(event);
585 Q_UNUSED(event);
586
586
587 // Prevents that all variables will be removed from graph when it will be closed
587 // Prevents that all variables will be removed from graph when it will be closed
588 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
588 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
589 emit variableAboutToBeRemoved(variableEntry.first);
589 emit variableAboutToBeRemoved(variableEntry.first);
590 }
590 }
591 }
591 }
592
592
593 void VisualizationGraphWidget::enterEvent(QEvent *event)
593 void VisualizationGraphWidget::enterEvent(QEvent *event)
594 {
594 {
595 Q_UNUSED(event);
595 Q_UNUSED(event);
596 impl->m_RenderingDelegate->showGraphOverlay(true);
596 impl->m_RenderingDelegate->showGraphOverlay(true);
597 }
597 }
598
598
599 void VisualizationGraphWidget::leaveEvent(QEvent *event)
599 void VisualizationGraphWidget::leaveEvent(QEvent *event)
600 {
600 {
601 Q_UNUSED(event);
601 Q_UNUSED(event);
602 impl->m_RenderingDelegate->showGraphOverlay(false);
602 impl->m_RenderingDelegate->showGraphOverlay(false);
603
603
604 if (auto parentZone = parentZoneWidget()) {
604 if (auto parentZone = parentZoneWidget()) {
605 parentZone->notifyMouseLeaveGraph(this);
605 parentZone->notifyMouseLeaveGraph(this);
606 }
606 }
607 else {
607 else {
608 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
608 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
609 }
609 }
610
610
611 if (impl->m_HoveredZone) {
611 if (impl->m_HoveredZone) {
612 impl->m_HoveredZone->setHovered(false);
612 impl->m_HoveredZone->setHovered(false);
613 impl->m_HoveredZone = nullptr;
613 impl->m_HoveredZone = nullptr;
614 }
614 }
615 }
615 }
616
616
617 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
617 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
618 {
618 {
619 return *ui->widget;
619 return *ui->widget;
620 }
620 }
621
621
622 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
622 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
623 {
623 {
624 QMenu graphMenu{};
624 QMenu graphMenu{};
625
625
626 // Iterates on variables (unique keys)
626 // Iterates on variables (unique keys)
627 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
627 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
628 end = impl->m_VariableToPlotMultiMap.cend();
628 end = impl->m_VariableToPlotMultiMap.cend();
629 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
629 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
630 // 'Remove variable' action
630 // 'Remove variable' action
631 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
631 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
632 [ this, var = it->first ]() { removeVariable(var); });
632 [ this, var = it->first ]() { removeVariable(var); });
633 }
633 }
634
634
635 if (!impl->m_ZoomStack.isEmpty()) {
635 if (!impl->m_ZoomStack.isEmpty()) {
636 if (!graphMenu.isEmpty()) {
636 if (!graphMenu.isEmpty()) {
637 graphMenu.addSeparator();
637 graphMenu.addSeparator();
638 }
638 }
639
639
640 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
640 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
641 }
641 }
642
642
643 // Selection Zone Actions
643 // Selection Zone Actions
644 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
644 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
645 if (selectionZoneItem) {
645 if (selectionZoneItem) {
646 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
646 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
647 selectedItems.removeAll(selectionZoneItem);
647 selectedItems.removeAll(selectionZoneItem);
648 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
648 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
649
649
650 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
650 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
651 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
651 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
652 graphMenu.addSeparator();
652 graphMenu.addSeparator();
653 }
653 }
654
654
655 QHash<QString, QMenu *> subMenus;
655 QHash<QString, QMenu *> subMenus;
656 QHash<QString, bool> subMenusEnabled;
656 QHash<QString, bool> subMenusEnabled;
657
657
658 for (auto zoneAction : zoneActions) {
658 for (auto zoneAction : zoneActions) {
659
659
660 auto isEnabled = zoneAction->isEnabled(selectedItems);
660 auto isEnabled = zoneAction->isEnabled(selectedItems);
661
661
662 auto menu = &graphMenu;
662 auto menu = &graphMenu;
663 for (auto subMenuName : zoneAction->subMenuList()) {
663 for (auto subMenuName : zoneAction->subMenuList()) {
664 if (!subMenus.contains(subMenuName)) {
664 if (!subMenus.contains(subMenuName)) {
665 menu = menu->addMenu(subMenuName);
665 menu = menu->addMenu(subMenuName);
666 subMenus[subMenuName] = menu;
666 subMenus[subMenuName] = menu;
667 subMenusEnabled[subMenuName] = isEnabled;
667 subMenusEnabled[subMenuName] = isEnabled;
668 }
668 }
669 else {
669 else {
670 menu = subMenus.value(subMenuName);
670 menu = subMenus.value(subMenuName);
671 if (isEnabled) {
671 if (isEnabled) {
672 // The sub menu is enabled if at least one of its actions is enabled
672 // The sub menu is enabled if at least one of its actions is enabled
673 subMenusEnabled[subMenuName] = true;
673 subMenusEnabled[subMenuName] = true;
674 }
674 }
675 }
675 }
676 }
676 }
677
677
678 auto action = menu->addAction(zoneAction->name());
678 auto action = menu->addAction(zoneAction->name());
679 action->setEnabled(isEnabled);
679 action->setEnabled(isEnabled);
680 action->setShortcut(zoneAction->displayedShortcut());
680 action->setShortcut(zoneAction->displayedShortcut());
681 QObject::connect(action, &QAction::triggered,
681 QObject::connect(action, &QAction::triggered,
682 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
682 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
683 }
683 }
684
684
685 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
685 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
686 it.value()->setEnabled(subMenusEnabled[it.key()]);
686 it.value()->setEnabled(subMenusEnabled[it.key()]);
687 }
687 }
688 }
688 }
689
689
690 if (!graphMenu.isEmpty()) {
690 if (!graphMenu.isEmpty()) {
691 graphMenu.exec(QCursor::pos());
691 graphMenu.exec(QCursor::pos());
692 }
692 }
693 }
693 }
694
694
695 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
695 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
696 {
696 {
697 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
697 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
698 << QThread::currentThread()->objectName() << "DoAcqui"
698 << QThread::currentThread()->objectName() << "DoAcqui"
699 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
699 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
700
700
701 auto graphRange = SqpRange{t1.lower, t1.upper};
701 auto graphRange = SqpRange{t1.lower, t1.upper};
702 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
702 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
703
703
704 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
704 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
705 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
705 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
706
706
707 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
707 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
708 end = impl->m_VariableToPlotMultiMap.end();
708 end = impl->m_VariableToPlotMultiMap.end();
709 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
709 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
710 variableUnderGraphVector.push_back(it->first);
710 variableUnderGraphVector.push_back(it->first);
711 }
711 }
712 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
712 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
713 !impl->m_IsCalibration);
713 !impl->m_IsCalibration);
714 }
714 }
715
715
716 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
716 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
717 qCDebug(LOG_VisualizationGraphWidget())
717 qCDebug(LOG_VisualizationGraphWidget())
718 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
718 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
719 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
719 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
720 emit synchronize(graphRange, oldGraphRange);
720 emit synchronize(graphRange, oldGraphRange);
721 }
721 }
722
722
723 auto pos = mapFromGlobal(QCursor::pos());
723 auto pos = mapFromGlobal(QCursor::pos());
724 auto axisPos = impl->posToAxisPos(pos, plot());
724 auto axisPos = impl->posToAxisPos(pos, plot());
725 if (auto parentZone = parentZoneWidget()) {
725 if (auto parentZone = parentZoneWidget()) {
726 if (impl->pointIsInAxisRect(axisPos, plot())) {
726 if (impl->pointIsInAxisRect(axisPos, plot())) {
727 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
727 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
728 }
728 }
729 else {
729 else {
730 parentZone->notifyMouseLeaveGraph(this);
730 parentZone->notifyMouseLeaveGraph(this);
731 }
731 }
732 }
732 }
733 else {
733 else {
734 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
734 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
735 }
735 }
736
736
737 // Quits calibration
737 // Quits calibration
738 impl->m_IsCalibration = false;
738 impl->m_IsCalibration = false;
739 }
739 }
740
740
741 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
741 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
742 {
742 {
743 impl->m_RenderingDelegate->onMouseDoubleClick(event);
743 impl->m_RenderingDelegate->onMouseDoubleClick(event);
744 }
744 }
745
745
746 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
746 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
747 {
747 {
748 // Handles plot rendering when mouse is moving
748 // Handles plot rendering when mouse is moving
749 impl->m_RenderingDelegate->onMouseMove(event);
749 impl->m_RenderingDelegate->onMouseMove(event);
750
750
751 auto axisPos = impl->posToAxisPos(event->pos(), plot());
751 auto axisPos = impl->posToAxisPos(event->pos(), plot());
752
752
753 // Zoom box and zone drawing
753 // Zoom box and zone drawing
754 if (impl->m_DrawingZoomRect) {
754 if (impl->m_DrawingZoomRect) {
755 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
755 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
756 }
756 }
757 else if (impl->m_DrawingZone) {
757 else if (impl->m_DrawingZone) {
758 impl->m_DrawingZone->setEnd(axisPos.x());
758 impl->m_DrawingZone->setEnd(axisPos.x());
759 }
759 }
760
760
761 // Cursor
761 // Cursor
762 if (auto parentZone = parentZoneWidget()) {
762 if (auto parentZone = parentZoneWidget()) {
763 if (impl->pointIsInAxisRect(axisPos, plot())) {
763 if (impl->pointIsInAxisRect(axisPos, plot())) {
764 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
764 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
765 }
765 }
766 else {
766 else {
767 parentZone->notifyMouseLeaveGraph(this);
767 parentZone->notifyMouseLeaveGraph(this);
768 }
768 }
769 }
769 }
770 else {
770 else {
771 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
771 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
772 }
772 }
773
773
774 // Search for the selection zone under the mouse
774 // Search for the selection zone under the mouse
775 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
775 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
776 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
776 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
777 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
777 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
778
778
779 // Sets the appropriate cursor shape
779 // Sets the appropriate cursor shape
780 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
780 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
781 setCursor(cursorShape);
781 setCursor(cursorShape);
782
782
783 // Manages the hovered zone
783 // Manages the hovered zone
784 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
784 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
785 if (impl->m_HoveredZone) {
785 if (impl->m_HoveredZone) {
786 impl->m_HoveredZone->setHovered(false);
786 impl->m_HoveredZone->setHovered(false);
787 }
787 }
788 selectionZoneItemUnderCursor->setHovered(true);
788 selectionZoneItemUnderCursor->setHovered(true);
789 impl->m_HoveredZone = selectionZoneItemUnderCursor;
789 impl->m_HoveredZone = selectionZoneItemUnderCursor;
790 plot().replot(QCustomPlot::rpQueuedReplot);
790 plot().replot(QCustomPlot::rpQueuedReplot);
791 }
791 }
792 }
792 }
793 else {
793 else {
794 // There is no zone under the mouse or the interaction mode is not "selection zones"
794 // There is no zone under the mouse or the interaction mode is not "selection zones"
795 if (impl->m_HoveredZone) {
795 if (impl->m_HoveredZone) {
796 impl->m_HoveredZone->setHovered(false);
796 impl->m_HoveredZone->setHovered(false);
797 impl->m_HoveredZone = nullptr;
797 impl->m_HoveredZone = nullptr;
798 }
798 }
799
799
800 setCursor(Qt::ArrowCursor);
800 setCursor(Qt::ArrowCursor);
801 }
801 }
802
802
803 impl->m_HasMovedMouse = true;
803 impl->m_HasMovedMouse = true;
804 VisualizationDragWidget::mouseMoveEvent(event);
804 VisualizationDragWidget::mouseMoveEvent(event);
805 }
805 }
806
806
807 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
807 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
808 {
808 {
809 auto value = event->angleDelta().x() + event->angleDelta().y();
809 auto value = event->angleDelta().x() + event->angleDelta().y();
810 if (value != 0) {
810 if (value != 0) {
811
811
812 auto direction = value > 0 ? 1.0 : -1.0;
812 auto direction = value > 0 ? 1.0 : -1.0;
813 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
813 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
814 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
814 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
815 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
815 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
816
816
817 auto zoomOrientations = QFlags<Qt::Orientation>{};
817 auto zoomOrientations = QFlags<Qt::Orientation>{};
818 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
818 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
819 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
819 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
820
820
821 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
821 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
822
822
823 if (!isZoomX && !isZoomY) {
823 if (!isZoomX && !isZoomY) {
824 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
824 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
825 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
825 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
826
826
827 axis->setRange(axis->range() + diff);
827 axis->setRange(axis->range() + diff);
828
828
829 if (plot().noAntialiasingOnDrag()) {
829 if (plot().noAntialiasingOnDrag()) {
830 plot().setNotAntialiasedElements(QCP::aeAll);
830 plot().setNotAntialiasedElements(QCP::aeAll);
831 }
831 }
832
832
833 plot().replot(QCustomPlot::rpQueuedReplot);
833 plot().replot(QCustomPlot::rpQueuedReplot);
834 }
834 }
835 }
835 }
836 }
836 }
837
837
838 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
838 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
839 {
839 {
840 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
840 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
841 auto isSelectionZoneMode
841 auto isSelectionZoneMode
842 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
842 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
843 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
843 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
844
844
845 if (!isDragDropClick && isLeftClick) {
845 if (!isDragDropClick && isLeftClick) {
846 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
846 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
847 // Starts a zoom box
847 // Starts a zoom box
848 impl->startDrawingRect(event->pos(), plot());
848 impl->startDrawingRect(event->pos(), plot());
849 }
849 }
850 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
850 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
851 // Starts a new selection zone
851 // Starts a new selection zone
852 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
852 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
853 if (!zoneAtPos) {
853 if (!zoneAtPos) {
854 impl->startDrawingZone(event->pos(), this);
854 impl->startDrawingZone(event->pos(), this);
855 }
855 }
856 }
856 }
857 }
857 }
858
858
859 // Allows mouse panning only in default mode
859 // Allows mouse panning only in default mode
860 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
860 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
861 == SqpApplication::PlotsInteractionMode::None
861 == SqpApplication::PlotsInteractionMode::None
862 && !isDragDropClick);
862 && !isDragDropClick);
863
863
864 // Allows zone edition only in selection zone mode without drag&drop
864 // Allows zone edition only in selection zone mode without drag&drop
865 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
865 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
866
866
867 // Selection / Deselection
867 // Selection / Deselection
868 if (isSelectionZoneMode) {
868 if (isSelectionZoneMode) {
869 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
869 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
870 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
870 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
871
871
872
872
873 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
873 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
874 && !isMultiSelectionClick) {
874 && !isMultiSelectionClick) {
875 parentVisualizationWidget()->selectionZoneManager().select(
875 parentVisualizationWidget()->selectionZoneManager().select(
876 {selectionZoneItemUnderCursor});
876 {selectionZoneItemUnderCursor});
877 }
877 }
878 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
878 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
879 parentVisualizationWidget()->selectionZoneManager().clearSelection();
879 parentVisualizationWidget()->selectionZoneManager().clearSelection();
880 }
880 }
881 else {
881 else {
882 // No selection change
882 // No selection change
883 }
883 }
884
884
885 if (selectionZoneItemUnderCursor && isLeftClick) {
885 if (selectionZoneItemUnderCursor && isLeftClick) {
886 selectionZoneItemUnderCursor->setAssociatedEditedZones(
886 selectionZoneItemUnderCursor->setAssociatedEditedZones(
887 parentVisualizationWidget()->selectionZoneManager().selectedItems());
887 parentVisualizationWidget()->selectionZoneManager().selectedItems());
888 }
888 }
889 }
889 }
890
890
891
891
892 impl->m_HasMovedMouse = false;
892 impl->m_HasMovedMouse = false;
893 VisualizationDragWidget::mousePressEvent(event);
893 VisualizationDragWidget::mousePressEvent(event);
894 }
894 }
895
895
896 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
896 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
897 {
897 {
898 if (impl->m_DrawingZoomRect) {
898 if (impl->m_DrawingZoomRect) {
899
899
900 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
900 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
901 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
901 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
902
902
903 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
903 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
904 impl->m_DrawingZoomRect->bottomRight->coords().x()};
904 impl->m_DrawingZoomRect->bottomRight->coords().x()};
905
905
906 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
906 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
907 impl->m_DrawingZoomRect->bottomRight->coords().y()};
907 impl->m_DrawingZoomRect->bottomRight->coords().y()};
908
908
909 impl->removeDrawingRect(plot());
909 impl->removeDrawingRect(plot());
910
910
911 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
911 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
912 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
912 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
913 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
913 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
914 axisX->setRange(newAxisXRange);
914 axisX->setRange(newAxisXRange);
915 axisY->setRange(newAxisYRange);
915 axisY->setRange(newAxisYRange);
916
916
917 plot().replot(QCustomPlot::rpQueuedReplot);
917 plot().replot(QCustomPlot::rpQueuedReplot);
918 }
918 }
919 }
919 }
920
920
921 impl->endDrawingZone(this);
921 impl->endDrawingZone(this);
922
922
923 // Selection / Deselection
923 // Selection / Deselection
924 auto isSelectionZoneMode
924 auto isSelectionZoneMode
925 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
925 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
926 if (isSelectionZoneMode) {
926 if (isSelectionZoneMode) {
927 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
927 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
928 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
928 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
929 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
929 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
930 && !impl->m_HasMovedMouse) {
930 && !impl->m_HasMovedMouse) {
931
931
932 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
932 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
933 if (zonesUnderCursor.count() > 1) {
933 if (zonesUnderCursor.count() > 1) {
934 // There are multiple zones under the mouse.
934 // There are multiple zones under the mouse.
935 // Performs the selection with a selection dialog.
935 // Performs the selection with a selection dialog.
936 VisualizationMultiZoneSelectionDialog dialog{this};
936 VisualizationMultiZoneSelectionDialog dialog{this};
937 dialog.setZones(zonesUnderCursor);
937 dialog.setZones(zonesUnderCursor);
938 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
938 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
939 dialog.activateWindow();
939 dialog.activateWindow();
940 dialog.raise();
940 dialog.raise();
941 if (dialog.exec() == QDialog::Accepted) {
941 if (dialog.exec() == QDialog::Accepted) {
942 auto selection = dialog.selectedZones();
942 auto selection = dialog.selectedZones();
943
943
944 if (!isMultiSelectionClick) {
944 if (!isMultiSelectionClick) {
945 parentVisualizationWidget()->selectionZoneManager().clearSelection();
945 parentVisualizationWidget()->selectionZoneManager().clearSelection();
946 }
946 }
947
947
948 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
948 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
949 auto zone = it.key();
949 auto zone = it.key();
950 auto isSelected = it.value();
950 auto isSelected = it.value();
951 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
951 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
952 isSelected);
952 isSelected);
953
953
954 if (isSelected) {
954 if (isSelected) {
955 // Puts the zone on top of the stack so it can be moved or resized
955 // Puts the zone on top of the stack so it can be moved or resized
956 impl->moveSelectionZoneOnTop(zone, plot());
956 impl->moveSelectionZoneOnTop(zone, plot());
957 }
957 }
958 }
958 }
959 }
959 }
960 }
960 }
961 else {
961 else {
962 if (!isMultiSelectionClick) {
962 if (!isMultiSelectionClick) {
963 parentVisualizationWidget()->selectionZoneManager().select(
963 parentVisualizationWidget()->selectionZoneManager().select(
964 {selectionZoneItemUnderCursor});
964 {selectionZoneItemUnderCursor});
965 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
965 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
966 }
966 }
967 else {
967 else {
968 parentVisualizationWidget()->selectionZoneManager().setSelected(
968 parentVisualizationWidget()->selectionZoneManager().setSelected(
969 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
969 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
970 || event->button() == Qt::RightButton);
970 || event->button() == Qt::RightButton);
971 }
971 }
972 }
972 }
973 }
973 }
974 else {
974 else {
975 // No selection change
975 // No selection change
976 }
976 }
977 }
977 }
978 }
978 }
979
979
980 void VisualizationGraphWidget::onDataCacheVariableUpdated()
980 void VisualizationGraphWidget::onDataCacheVariableUpdated()
981 {
981 {
982 auto graphRange = ui->widget->xAxis->range();
982 auto graphRange = ui->widget->xAxis->range();
983 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
983 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
984
984
985 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
985 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
986 auto variable = variableEntry.first;
986 auto variable = variableEntry.first;
987 qCDebug(LOG_VisualizationGraphWidget())
987 qCDebug(LOG_VisualizationGraphWidget())
988 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
988 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
989 qCDebug(LOG_VisualizationGraphWidget())
989 qCDebug(LOG_VisualizationGraphWidget())
990 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
990 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
991 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
991 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
992 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
992 impl->updateData(variableEntry.second, variable, variable->range());
993 }
993 }
994 }
994 }
995 }
995 }
996
996
997 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
997 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
998 const SqpRange &range)
998 const SqpRange &range)
999 {
999 {
1000 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1000 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1001 if (it != impl->m_VariableToPlotMultiMap.end()) {
1001 if (it != impl->m_VariableToPlotMultiMap.end()) {
1002 impl->updateData(it->second, variable->dataSeries(), range);
1002 impl->updateData(it->second, variable, range);
1003 }
1003 }
1004 }
1004 }
General Comments 0
You need to be logged in to leave comments. Login now