##// END OF EJS Templates
(Minor) Removes obsolete code
Alexandre Leroux -
r905:9ee11b23a95d
parent child
Show More
@@ -1,199 +1,198
1 1 #include "Data/SpectrogramSeries.h"
2 2
3 3 #include "DataSeriesBuilders.h"
4 4 #include "DataSeriesUtils.h"
5 5
6 6 #include <QObject>
7 7 #include <QtTest>
8 8
9 9 namespace {
10 10
11 11 // Aliases used to facilitate reading of test inputs
12 12 using X = DataContainer;
13 13 using Y = DataContainer;
14 14 using Values = DataContainer;
15 15 using Components = std::vector<DataContainer>;
16 16
17 17 } // namespace
18 18
19 19 /**
20 20 * @brief The TestSpectrogramSeries class defines unit tests on spectrogram series.
21 21 *
22 22 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
23 23 */
24 24 class TestSpectrogramSeries : public QObject {
25 25 Q_OBJECT
26 26 private slots:
27 27
28 28 /// Tests construction of a spectrogram series
29 29 void testCtor_data();
30 30 void testCtor();
31 31
32 32 /// Tests merge of two spectrogram series
33 33 void testMerge_data();
34 34 void testMerge();
35 35
36 /// @todo ALX: test subdataseries
37 36 /// Tests get subdata of a spectrogram series
38 37 void testSubDataSeries_data();
39 38 void testSubDataSeries();
40 39 };
41 40
42 41 void TestSpectrogramSeries::testCtor_data()
43 42 {
44 43 // x-axis data
45 44 QTest::addColumn<X>("xAxisData");
46 45 // y-axis data
47 46 QTest::addColumn<Y>("yAxisData");
48 47 // values data
49 48 QTest::addColumn<Values>("valuesData");
50 49
51 50 // construction expected to be valid
52 51 QTest::addColumn<bool>("expectOK");
53 52 // expected x-axis data (when construction is valid)
54 53 QTest::addColumn<X>("expectedXAxisData");
55 54 // expected components data (when construction is valid)
56 55 QTest::addColumn<Components>("expectedComponentsData");
57 56
58 57 QTest::newRow(
59 58 "invalidData (number of values by component aren't equal to the number of x-axis data)")
60 59 << X{1., 2., 3., 4., 5.} << Y{1., 2., 3.} << Values{1., 2., 3.} << false << X{}
61 60 << Components{};
62 61
63 62 QTest::newRow("invalidData (number of components aren't equal to the number of y-axis data)")
64 63 << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
65 64 << Values{1., 2., 3., 4., 5.} // 1 component
66 65 << false << X{} << Components{};
67 66
68 67 QTest::newRow("sortedData") << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
69 68 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} // 2 components
70 69 << true << X{1., 2., 3., 4., 5.}
71 70 << Components{{1., 3., 5., 7., 9.}, {2., 4., 6., 8., 10.}};
72 71
73 72 QTest::newRow("unsortedData") << X{5., 4., 3., 2., 1.} << Y{1., 2.}
74 73 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << true
75 74 << X{1., 2., 3., 4., 5.}
76 75 << Components{{9., 7., 5., 3., 1.}, {10., 8., 6., 4., 2.}};
77 76 }
78 77
79 78 void TestSpectrogramSeries::testCtor()
80 79 {
81 80 // Creates series
82 81 QFETCH(X, xAxisData);
83 82 QFETCH(Y, yAxisData);
84 83 QFETCH(Values, valuesData);
85 84 QFETCH(bool, expectOK);
86 85
87 86 if (expectOK) {
88 87 auto series = SpectrogramBuilder{}
89 88 .setX(std::move(xAxisData))
90 89 .setY(std::move(yAxisData))
91 90 .setValues(std::move(valuesData))
92 91 .build();
93 92
94 93 // Validates results
95 94 QFETCH(X, expectedXAxisData);
96 95 QFETCH(Components, expectedComponentsData);
97 96 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedComponentsData);
98 97 }
99 98 else {
100 99 QVERIFY_EXCEPTION_THROWN(SpectrogramBuilder{}
101 100 .setX(std::move(xAxisData))
102 101 .setY(std::move(yAxisData))
103 102 .setValues(std::move(valuesData))
104 103 .build(),
105 104 std::invalid_argument);
106 105 }
107 106 }
108 107
109 108 void TestSpectrogramSeries::testMerge_data()
110 109 {
111 110 testMerge_struct<SpectrogramSeries, Components>();
112 111
113 112 QTest::newRow("sortedMerge") << SpectrogramBuilder{}
114 113 .setX({1., 2., 3.})
115 114 .setY({1., 2.})
116 115 .setValues({10., 11., 20., 21., 30., 31})
117 116 .build()
118 117 << SpectrogramBuilder{}
119 118 .setX({4., 5., 6.})
120 119 .setY({1., 2.})
121 120 .setValues({40., 41., 50., 51., 60., 61})
122 121 .build()
123 122 << DataContainer{1., 2., 3., 4., 5., 6.}
124 123 << Components{{10., 20., 30., 40., 50., 60.},
125 124 {11., 21., 31., 41., 51., 61}};
126 125
127 126 QTest::newRow(
128 127 "unsortedMerge (merge not made because the two data series have different y-axes)")
129 128 << SpectrogramBuilder{}
130 129 .setX({4., 5., 6.})
131 130 .setY({1., 2.})
132 131 .setValues({40., 41., 50., 51., 60., 61})
133 132 .build()
134 133 << SpectrogramBuilder{}
135 134 .setX({1., 2., 3.})
136 135 .setY({3., 4.})
137 136 .setValues({10., 11., 20., 21., 30., 31})
138 137 .build()
139 138 << DataContainer{4., 5., 6.} << Components{{40., 50., 60.}, {41., 51., 61}};
140 139
141 140 QTest::newRow(
142 141 "unsortedMerge (unsortedMerge (merge is made because the two data series have the same "
143 142 "y-axis)")
144 143 << SpectrogramBuilder{}
145 144 .setX({4., 5., 6.})
146 145 .setY({1., 2.})
147 146 .setValues({40., 41., 50., 51., 60., 61})
148 147 .build()
149 148 << SpectrogramBuilder{}
150 149 .setX({1., 2., 3.})
151 150 .setY({1., 2.})
152 151 .setValues({10., 11., 20., 21., 30., 31})
153 152 .build()
154 153 << DataContainer{1., 2., 3., 4., 5., 6.}
155 154 << Components{{10., 20., 30., 40., 50., 60.}, {11., 21., 31., 41., 51., 61}};
156 155 }
157 156
158 157 void TestSpectrogramSeries::testMerge()
159 158 {
160 159 testMerge_t<SpectrogramSeries, Components>();
161 160 }
162 161
163 162 void TestSpectrogramSeries::testSubDataSeries_data()
164 163 {
165 164 testSubDataSeries_struct<SpectrogramSeries, Components>();
166 165
167 166 QTest::newRow("subDataSeries (the range includes all data)")
168 167 << SpectrogramBuilder{}
169 168 .setX({1., 2., 3.})
170 169 .setY({1., 2.})
171 170 .setValues({10., 11., 20., 21., 30., 31})
172 171 .build()
173 172 << SqpRange{0., 5.} << DataContainer{1., 2., 3.}
174 173 << Components{{10., 20., 30.}, {11., 21., 31.}};
175 174
176 175 QTest::newRow("subDataSeries (the range includes no data)")
177 176 << SpectrogramBuilder{}
178 177 .setX({1., 2., 3.})
179 178 .setY({1., 2.})
180 179 .setValues({10., 11., 20., 21., 30., 31})
181 180 .build()
182 181 << SqpRange{4., 5.} << DataContainer{} << Components{{}, {}};
183 182
184 183 QTest::newRow("subDataSeries (the range includes some data)")
185 184 << SpectrogramBuilder{}
186 185 .setX({1., 2., 3.})
187 186 .setY({1., 2.})
188 187 .setValues({10., 11., 20., 21., 30., 31})
189 188 .build()
190 189 << SqpRange{1.1, 3} << DataContainer{2., 3.} << Components{{20., 30.}, {21., 31.}};
191 190 }
192 191
193 192 void TestSpectrogramSeries::testSubDataSeries()
194 193 {
195 194 testSubDataSeries_t<SpectrogramSeries, Components>();
196 195 }
197 196
198 197 QTEST_MAIN(TestSpectrogramSeries)
199 198 #include "TestSpectrogramSeries.moc"
@@ -1,342 +1,342
1 1 #include "Visualization/VisualizationGraphHelper.h"
2 2 #include "Visualization/qcustomplot.h"
3 3
4 4 #include <Common/ColorUtils.h>
5 5
6 6 #include <Data/ScalarSeries.h>
7 7 #include <Data/SpectrogramSeries.h>
8 8 #include <Data/VectorSeries.h>
9 9
10 10 #include <Variable/Variable.h>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
13 13
14 14 namespace {
15 15
16 16 class SqpDataContainer : public QCPGraphDataContainer {
17 17 public:
18 18 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
19 19 };
20 20
21 21 /**
22 22 * Struct used to create plottables, depending on the type of the data series from which to create
23 23 * them
24 24 * @tparam T the data series' type
25 25 * @remarks Default implementation can't create plottables
26 26 */
27 27 template <typename T, typename Enabled = void>
28 28 struct PlottablesCreator {
29 29 static PlottablesMap createPlottables(T &, QCustomPlot &)
30 30 {
31 31 qCCritical(LOG_DataSeries())
32 32 << QObject::tr("Can't create plottables: unmanaged data series type");
33 33 return {};
34 34 }
35 35 };
36 36
37 37 /**
38 38 * Specialization of PlottablesCreator for scalars and vectors
39 39 * @sa ScalarSeries
40 40 * @sa VectorSeries
41 41 */
42 42 template <typename T>
43 43 struct PlottablesCreator<T,
44 44 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
45 45 or std::is_base_of<VectorSeries, T>::value> > {
46 46 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
47 47 {
48 48 PlottablesMap result{};
49 49
50 50 // Gets the number of components of the data series
51 51 dataSeries.lockRead();
52 52 auto componentCount = dataSeries.valuesData()->componentCount();
53 53 dataSeries.unlock();
54 54
55 55 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
56 56
57 57 // For each component of the data series, creates a QCPGraph to add to the plot
58 58 for (auto i = 0; i < componentCount; ++i) {
59 59 auto graph = plot.addGraph();
60 60 graph->setPen(QPen{colors.at(i)});
61 61
62 62 result.insert({i, graph});
63 63 }
64 64
65 65 plot.replot();
66 66
67 67 return result;
68 68 }
69 69 };
70 70
71 71 /**
72 72 * Specialization of PlottablesCreator for spectrograms
73 73 * @sa SpectrogramSeries
74 74 */
75 75 template <typename T>
76 76 struct PlottablesCreator<T,
77 77 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
78 78 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
79 79 {
80 80 PlottablesMap result{};
81 81 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
82 82
83 83 plot.replot();
84 84
85 85 return result;
86 86 }
87 87 };
88 88
89 89 /**
90 90 * Struct used to update plottables, depending on the type of the data series from which to update
91 91 * them
92 92 * @tparam T the data series' type
93 93 * @remarks Default implementation can't update plottables
94 94 */
95 95 template <typename T, typename Enabled = void>
96 96 struct PlottablesUpdater {
97 97 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
98 98 {
99 qCCritical(LOG_DataSeries())
99 qCCritical(LOG_VisualizationGraphHelper())
100 100 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
101 101 }
102 102
103 103 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
104 104 {
105 qCCritical(LOG_DataSeries())
105 qCCritical(LOG_VisualizationGraphHelper())
106 106 << QObject::tr("Can't update plottables: unmanaged data series type");
107 107 }
108 108 };
109 109
110 110 /**
111 111 * Specialization of PlottablesUpdater for scalars and vectors
112 112 * @sa ScalarSeries
113 113 * @sa VectorSeries
114 114 */
115 115 template <typename T>
116 116 struct PlottablesUpdater<T,
117 117 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
118 118 or std::is_base_of<VectorSeries, T>::value> > {
119 119 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
120 120 {
121 121 auto minValue = 0., maxValue = 0.;
122 122
123 123 dataSeries.lockRead();
124 124 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
125 125 auto end = dataSeries.cend();
126 126 if (valuesBounds.first != end && valuesBounds.second != end) {
127 127 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
128 128
129 129 minValue = rangeValue(valuesBounds.first->minValue());
130 130 maxValue = rangeValue(valuesBounds.second->maxValue());
131 131 }
132 132 dataSeries.unlock();
133 133
134 134 plot.yAxis->setRange(QCPRange{minValue, maxValue});
135 135 }
136 136
137 137 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
138 138 bool rescaleAxes)
139 139 {
140 140
141 141 // For each plottable to update, resets its data
142 142 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
143 143 for (const auto &plottable : plottables) {
144 144 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
145 145 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
146 146 graph->setData(dataContainer);
147 147
148 148 dataContainers.insert({plottable.first, dataContainer});
149 149 }
150 150 }
151 151 dataSeries.lockRead();
152 152
153 153 // - Gets the data of the series included in the current range
154 154 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
155 155 // and value data. The correct value is retrieved according to the index of the component
156 156 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
157 157 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
158 158 for (const auto &dataContainer : dataContainers) {
159 159 auto componentIndex = dataContainer.first;
160 160 dataContainer.second->appendGraphData(
161 161 QCPGraphData(it->x(), it->value(componentIndex)));
162 162 }
163 163 }
164 164
165 165 dataSeries.unlock();
166 166
167 167 if (!plottables.empty()) {
168 168 auto plot = plottables.begin()->second->parentPlot();
169 169
170 170 if (rescaleAxes) {
171 171 plot->rescaleAxes();
172 172 }
173 173
174 174 plot->replot();
175 175 }
176 176 }
177 177 };
178 178
179 179 /**
180 180 * Specialization of PlottablesUpdater for spectrograms
181 181 * @sa SpectrogramSeries
182 182 */
183 183 template <typename T>
184 184 struct PlottablesUpdater<T,
185 185 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
186 186 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
187 187 {
188 188 double min, max;
189 189 /// @todo ALX: use iterators here
190 190 std::tie(min, max) = dataSeries.yAxis().bounds();
191 191
192 192 if (!std::isnan(min) && !std::isnan(max)) {
193 193 plot.yAxis->setRange(QCPRange{min, max});
194 194 }
195 195 }
196 196
197 197 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
198 198 bool rescaleAxes)
199 199 {
200 200 if (plottables.empty()) {
201 201 qCDebug(LOG_VisualizationGraphHelper())
202 202 << QObject::tr("Can't update spectrogram: no colormap has been associated");
203 203 return;
204 204 }
205 205
206 206 // Gets the colormap to update (normally there is only one colormap)
207 207 Q_ASSERT(plottables.size() == 1);
208 208 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
209 209 Q_ASSERT(colormap != nullptr);
210 210
211 211 dataSeries.lockRead();
212 212
213 213 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
214 214 /// @todo ALX: use iterators here
215 215 auto yAxis = dataSeries.yAxis();
216 216
217 217 // Gets properties of x-axis and y-axis to set size and range of the colormap
218 218 auto nbX = std::distance(its.first, its.second);
219 219 auto xMin = nbX != 0 ? its.first->x() : 0.;
220 220 auto xMax = nbX != 0 ? (its.second - 1)->x() : 0.;
221 221
222 222 auto nbY = yAxis.size();
223 223 auto yMin = 0., yMax = 0.;
224 224 if (nbY != 0) {
225 225 std::tie(yMin, yMax) = yAxis.bounds();
226 226 }
227 227
228 228 colormap->data()->setSize(nbX, nbY);
229 229 colormap->data()->setRange(QCPRange{xMin, xMax}, QCPRange{yMin, yMax});
230 230
231 231 // Sets values
232 232 auto xIndex = 0;
233 233 for (auto it = its.first; it != its.second; ++it, ++xIndex) {
234 234 for (auto yIndex = 0; yIndex < nbY; ++yIndex) {
235 235 colormap->data()->setCell(xIndex, yIndex, it->value(yIndex));
236 236 }
237 237 }
238 238
239 239 dataSeries.unlock();
240 240
241 241 // Rescales axes
242 242 auto plot = colormap->parentPlot();
243 243
244 244 if (rescaleAxes) {
245 245 plot->rescaleAxes();
246 246 }
247 247
248 248 plot->replot();
249 249 }
250 250 };
251 251
252 252 /**
253 253 * Helper used to create/update plottables
254 254 */
255 255 struct IPlottablesHelper {
256 256 virtual ~IPlottablesHelper() noexcept = default;
257 257 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
258 258 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
259 259 virtual void update(PlottablesMap &plottables, const SqpRange &range,
260 260 bool rescaleAxes = false) const = 0;
261 261 };
262 262
263 263 /**
264 264 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
265 265 * @tparam T the data series' type
266 266 */
267 267 template <typename T>
268 268 struct PlottablesHelper : public IPlottablesHelper {
269 269 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
270 270
271 271 PlottablesMap create(QCustomPlot &plot) const override
272 272 {
273 273 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
274 274 }
275 275
276 276 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
277 277 {
278 278 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
279 279 }
280 280
281 281 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
282 282 {
283 283 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
284 284 }
285 285
286 286 T &m_DataSeries;
287 287 };
288 288
289 289 /// Creates IPlottablesHelper according to a data series
290 290 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
291 291 {
292 292 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
293 293 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
294 294 }
295 295 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
296 296 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
297 297 }
298 298 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
299 299 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
300 300 }
301 301 else {
302 302 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
303 303 }
304 304 }
305 305
306 306 } // namespace
307 307
308 308 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
309 309 QCustomPlot &plot) noexcept
310 310 {
311 311 if (variable) {
312 312 auto helper = createHelper(variable->dataSeries());
313 313 auto plottables = helper->create(plot);
314 314 return plottables;
315 315 }
316 316 else {
317 317 qCDebug(LOG_VisualizationGraphHelper())
318 318 << QObject::tr("Can't create graph plottables : the variable is null");
319 319 return PlottablesMap{};
320 320 }
321 321 }
322 322
323 323 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
324 324 QCustomPlot &plot) noexcept
325 325 {
326 326 if (variable) {
327 327 auto helper = createHelper(variable->dataSeries());
328 328 helper->setYAxisRange(variable->range(), plot);
329 329 }
330 330 else {
331 331 qCDebug(LOG_VisualizationGraphHelper())
332 332 << QObject::tr("Can't set y-axis range of plot: the variable is null");
333 333 }
334 334 }
335 335
336 336 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
337 337 std::shared_ptr<IDataSeries> dataSeries,
338 338 const SqpRange &dateTime)
339 339 {
340 340 auto helper = createHelper(dataSeries);
341 341 helper->update(plottables, dateTime);
342 342 }
@@ -1,406 +1,405
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationDefs.h"
4 4 #include "Visualization/VisualizationGraphHelper.h"
5 5 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7 #include "ui_VisualizationGraphWidget.h"
8 8
9 9 #include <Common/MimeTypesDef.h>
10 10 #include <Data/ArrayData.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <DragAndDrop/DragDropHelper.h>
13 13 #include <Settings/SqpSettingsDefs.h>
14 14 #include <SqpApplication.h>
15 15 #include <Time/TimeController.h>
16 16 #include <Variable/Variable.h>
17 17 #include <Variable/VariableController.h>
18 18
19 19 #include <unordered_map>
20 20
21 21 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
22 22
23 23 namespace {
24 24
25 25 /// Key pressed to enable zoom on horizontal axis
26 26 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
27 27
28 28 /// Key pressed to enable zoom on vertical axis
29 29 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
30 30
31 31 } // namespace
32 32
33 33 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
34 34
35 35 explicit VisualizationGraphWidgetPrivate(const QString &name)
36 36 : m_Name{name},
37 37 m_DoAcquisition{true},
38 38 m_IsCalibration{false},
39 39 m_RenderingDelegate{nullptr}
40 40 {
41 41 }
42 42
43 43 QString m_Name;
44 44 // 1 variable -> n qcpplot
45 45 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
46 46 bool m_DoAcquisition;
47 47 bool m_IsCalibration;
48 QCPItemTracer *m_TextTracer;
49 48 /// Delegate used to attach rendering features to the plot
50 49 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
51 50 };
52 51
53 52 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
54 53 : VisualizationDragWidget{parent},
55 54 ui{new Ui::VisualizationGraphWidget},
56 55 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
57 56 {
58 57 ui->setupUi(this);
59 58
60 59 // 'Close' options : widget is deleted when closed
61 60 setAttribute(Qt::WA_DeleteOnClose);
62 61
63 62 // Set qcpplot properties :
64 63 // - Drag (on x-axis) and zoom are enabled
65 64 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
66 65 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
67 66 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
68 67
69 68 // The delegate must be initialized after the ui as it uses the plot
70 69 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
71 70
72 71 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
73 72 connect(ui->widget, &QCustomPlot::mouseRelease, this,
74 73 &VisualizationGraphWidget::onMouseRelease);
75 74 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
76 75 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
77 76 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
78 77 &QCPAxis::rangeChanged),
79 78 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
80 79
81 80 // Activates menu when right clicking on the graph
82 81 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
83 82 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
84 83 &VisualizationGraphWidget::onGraphMenuRequested);
85 84
86 85 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
87 86 &VariableController::onRequestDataLoading);
88 87
89 88 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
90 89 &VisualizationGraphWidget::onUpdateVarDisplaying);
91 90 }
92 91
93 92
94 93 VisualizationGraphWidget::~VisualizationGraphWidget()
95 94 {
96 95 delete ui;
97 96 }
98 97
99 98 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
100 99 {
101 100 auto parent = parentWidget();
102 101 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
103 102 parent = parent->parentWidget();
104 103 }
105 104
106 105 return qobject_cast<VisualizationZoneWidget *>(parent);
107 106 }
108 107
109 108 void VisualizationGraphWidget::enableAcquisition(bool enable)
110 109 {
111 110 impl->m_DoAcquisition = enable;
112 111 }
113 112
114 113 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
115 114 {
116 115 // Uses delegate to create the qcpplot components according to the variable
117 116 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
118 117 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
119 118
120 119 // Set axes properties according to the units of the data series
121 120 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
122 121 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
123 122 auto xAxisUnit = Unit{};
124 123 auto valuesUnit = Unit{};
125 124
126 125 if (auto dataSeries = variable->dataSeries()) {
127 126 dataSeries->lockRead();
128 127 xAxisUnit = dataSeries->xAxisUnit();
129 128 valuesUnit = dataSeries->valuesUnit();
130 129 dataSeries->unlock();
131 130 }
132 131 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
133 132
134 133 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
135 134
136 135 this->enableAcquisition(false);
137 136 this->setGraphRange(range);
138 137 this->enableAcquisition(true);
139 138
140 139 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
141 140
142 141 emit variableAdded(variable);
143 142 }
144 143
145 144 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
146 145 {
147 146 // Each component associated to the variable :
148 147 // - is removed from qcpplot (which deletes it)
149 148 // - is no longer referenced in the map
150 149 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
151 150 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
152 151 emit variableAboutToBeRemoved(variable);
153 152
154 153 auto &plottablesMap = variableIt->second;
155 154
156 155 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
157 156 plottableIt != plottableEnd;) {
158 157 ui->widget->removePlottable(plottableIt->second);
159 158 plottableIt = plottablesMap.erase(plottableIt);
160 159 }
161 160
162 161 impl->m_VariableToPlotMultiMap.erase(variableIt);
163 162 }
164 163
165 164 // Updates graph
166 165 ui->widget->replot();
167 166 }
168 167
169 168 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
170 169 {
171 170 auto variables = QList<std::shared_ptr<Variable> >{};
172 171 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
173 172 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
174 173 variables << it->first;
175 174 }
176 175
177 176 return variables;
178 177 }
179 178
180 179 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
181 180 {
182 181 if (!variable) {
183 182 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
184 183 return;
185 184 }
186 185
187 186 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
188 187 }
189 188
190 189 SqpRange VisualizationGraphWidget::graphRange() const noexcept
191 190 {
192 191 auto graphRange = ui->widget->xAxis->range();
193 192 return SqpRange{graphRange.lower, graphRange.upper};
194 193 }
195 194
196 195 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
197 196 {
198 197 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
199 198 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
200 199 ui->widget->replot();
201 200 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
202 201 }
203 202
204 203 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
205 204 {
206 205 if (visitor) {
207 206 visitor->visit(this);
208 207 }
209 208 else {
210 209 qCCritical(LOG_VisualizationGraphWidget())
211 210 << tr("Can't visit widget : the visitor is null");
212 211 }
213 212 }
214 213
215 214 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
216 215 {
217 216 /// @todo : for the moment, a graph can always accomodate a variable
218 217 Q_UNUSED(variable);
219 218 return true;
220 219 }
221 220
222 221 bool VisualizationGraphWidget::contains(const Variable &variable) const
223 222 {
224 223 // Finds the variable among the keys of the map
225 224 auto variablePtr = &variable;
226 225 auto findVariable
227 226 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
228 227
229 228 auto end = impl->m_VariableToPlotMultiMap.cend();
230 229 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
231 230 return it != end;
232 231 }
233 232
234 233 QString VisualizationGraphWidget::name() const
235 234 {
236 235 return impl->m_Name;
237 236 }
238 237
239 238 QMimeData *VisualizationGraphWidget::mimeData() const
240 239 {
241 240 auto mimeData = new QMimeData;
242 241 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
243 242
244 243 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
245 244 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
246 245
247 246 return mimeData;
248 247 }
249 248
250 249 bool VisualizationGraphWidget::isDragAllowed() const
251 250 {
252 251 return true;
253 252 }
254 253
255 254 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
256 255 {
257 256 if (highlighted) {
258 257 plot().setBackground(QBrush(QColor("#BBD5EE")));
259 258 }
260 259 else {
261 260 plot().setBackground(QBrush(Qt::white));
262 261 }
263 262
264 263 plot().update();
265 264 }
266 265
267 266 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
268 267 {
269 268 Q_UNUSED(event);
270 269
271 270 // Prevents that all variables will be removed from graph when it will be closed
272 271 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
273 272 emit variableAboutToBeRemoved(variableEntry.first);
274 273 }
275 274 }
276 275
277 276 void VisualizationGraphWidget::enterEvent(QEvent *event)
278 277 {
279 278 Q_UNUSED(event);
280 279 impl->m_RenderingDelegate->showGraphOverlay(true);
281 280 }
282 281
283 282 void VisualizationGraphWidget::leaveEvent(QEvent *event)
284 283 {
285 284 Q_UNUSED(event);
286 285 impl->m_RenderingDelegate->showGraphOverlay(false);
287 286 }
288 287
289 288 QCustomPlot &VisualizationGraphWidget::plot() noexcept
290 289 {
291 290 return *ui->widget;
292 291 }
293 292
294 293 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
295 294 {
296 295 QMenu graphMenu{};
297 296
298 297 // Iterates on variables (unique keys)
299 298 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
300 299 end = impl->m_VariableToPlotMultiMap.cend();
301 300 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
302 301 // 'Remove variable' action
303 302 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
304 303 [ this, var = it->first ]() { removeVariable(var); });
305 304 }
306 305
307 306 if (!graphMenu.isEmpty()) {
308 307 graphMenu.exec(QCursor::pos());
309 308 }
310 309 }
311 310
312 311 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
313 312 {
314 313 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
315 314 << QThread::currentThread()->objectName() << "DoAcqui"
316 315 << impl->m_DoAcquisition;
317 316
318 317 auto graphRange = SqpRange{t1.lower, t1.upper};
319 318 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
320 319
321 320 if (impl->m_DoAcquisition) {
322 321 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
323 322
324 323 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
325 324 end = impl->m_VariableToPlotMultiMap.end();
326 325 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
327 326 variableUnderGraphVector.push_back(it->first);
328 327 }
329 328 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
330 329 !impl->m_IsCalibration);
331 330
332 331 if (!impl->m_IsCalibration) {
333 332 qCDebug(LOG_VisualizationGraphWidget())
334 333 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
335 334 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
336 335 emit synchronize(graphRange, oldGraphRange);
337 336 }
338 337 }
339 338 }
340 339
341 340 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
342 341 {
343 342 // Handles plot rendering when mouse is moving
344 343 impl->m_RenderingDelegate->onMouseMove(event);
345 344
346 345 VisualizationDragWidget::mouseMoveEvent(event);
347 346 }
348 347
349 348 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
350 349 {
351 350 auto zoomOrientations = QFlags<Qt::Orientation>{};
352 351
353 352 // Lambda that enables a zoom orientation if the key modifier related to this orientation
354 353 // has
355 354 // been pressed
356 355 auto enableOrientation
357 356 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
358 357 auto orientationEnabled = event->modifiers().testFlag(modifier);
359 358 zoomOrientations.setFlag(orientation, orientationEnabled);
360 359 };
361 360 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
362 361 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
363 362
364 363 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
365 364 }
366 365
367 366 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
368 367 {
369 368 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
370 369
371 370 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
372 371
373 372 VisualizationDragWidget::mousePressEvent(event);
374 373 }
375 374
376 375 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
377 376 {
378 377 impl->m_IsCalibration = false;
379 378 }
380 379
381 380 void VisualizationGraphWidget::onDataCacheVariableUpdated()
382 381 {
383 382 auto graphRange = ui->widget->xAxis->range();
384 383 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
385 384
386 385 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
387 386 auto variable = variableEntry.first;
388 387 qCDebug(LOG_VisualizationGraphWidget())
389 388 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
390 389 qCDebug(LOG_VisualizationGraphWidget())
391 390 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
392 391 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
393 392 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
394 393 variable->range());
395 394 }
396 395 }
397 396 }
398 397
399 398 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
400 399 const SqpRange &range)
401 400 {
402 401 auto it = impl->m_VariableToPlotMultiMap.find(variable);
403 402 if (it != impl->m_VariableToPlotMultiMap.end()) {
404 403 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
405 404 }
406 405 }
General Comments 0
You need to be logged in to leave comments. Login now