##// END OF EJS Templates
Merge branch 'feature/MockSpectrogram2' into develop
Alexandre Leroux -
r909:c6a1fc96dbd2 merge
parent child
Show More
@@ -1,482 +1,483
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
7
7
8 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
9 #include <Data/DataSeriesMergeHelper.h>
9 #include <Data/DataSeriesMergeHelper.h>
10 #include <Data/IDataSeries.h>
10 #include <Data/IDataSeries.h>
11 #include <Data/OptionalAxis.h>
11 #include <Data/OptionalAxis.h>
12
12
13 #include <QLoggingCategory>
13 #include <QLoggingCategory>
14 #include <QReadLocker>
14 #include <QReadLocker>
15 #include <QReadWriteLock>
15 #include <QReadWriteLock>
16 #include <memory>
16 #include <memory>
17
17
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
19 // definitions with inheritance. Inline method is used instead
19 // definitions with inheritance. Inline method is used instead
20 inline const QLoggingCategory &LOG_DataSeries()
20 inline const QLoggingCategory &LOG_DataSeries()
21 {
21 {
22 static const QLoggingCategory category{"DataSeries"};
22 static const QLoggingCategory category{"DataSeries"};
23 return category;
23 return category;
24 }
24 }
25
25
26 template <int Dim>
26 template <int Dim>
27 class DataSeries;
27 class DataSeries;
28
28
29 namespace dataseries_detail {
29 namespace dataseries_detail {
30
30
31 template <int Dim, bool IsConst>
31 template <int Dim, bool IsConst>
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
33 public:
33 public:
34 friend class DataSeries<Dim>;
34 friend class DataSeries<Dim>;
35
35
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
40 {
40 {
41 }
41 }
42
42
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
47 : dataSeries.valuesData()->cend())
47 : dataSeries.valuesData()->cend())
48 {
48 {
49 }
49 }
50
50
51 IteratorValue(const IteratorValue &other) = default;
51 IteratorValue(const IteratorValue &other) = default;
52
52
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
54 {
54 {
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
56 }
56 }
57
57
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
60 return m_XIt->distance(*otherImpl.m_XIt);
60 return m_XIt->distance(*otherImpl.m_XIt);
61 }
61 }
62 catch (const std::bad_cast &) {
62 catch (const std::bad_cast &) {
63 return 0;
63 return 0;
64 }
64 }
65
65
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
69 }
69 }
70 catch (const std::bad_cast &) {
70 catch (const std::bad_cast &) {
71 return false;
71 return false;
72 }
72 }
73
73
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
77 }
77 }
78 catch (const std::bad_cast &) {
78 catch (const std::bad_cast &) {
79 return false;
79 return false;
80 }
80 }
81
81
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
83 {
83 {
84 auto result = clone();
84 auto result = clone();
85 result->next(offset);
85 result->next(offset);
86 return result;
86 return result;
87 }
87 }
88
88
89 void next(int offset) override
89 void next(int offset) override
90 {
90 {
91 m_XIt->next(offset);
91 m_XIt->next(offset);
92 m_ValuesIt->next(offset);
92 m_ValuesIt->next(offset);
93 }
93 }
94
94
95 void prev() override
95 void prev() override
96 {
96 {
97 --m_XIt;
97 --m_XIt;
98 --m_ValuesIt;
98 --m_ValuesIt;
99 }
99 }
100
100
101 double x() const override { return m_XIt->at(0); }
101 double x() const override { return m_XIt->at(0); }
102 double value() const override { return m_ValuesIt->at(0); }
102 double value() const override { return m_ValuesIt->at(0); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
104 double minValue() const override { return m_ValuesIt->min(); }
104 double minValue() const override { return m_ValuesIt->min(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
107
107
108 void swap(DataSeriesIteratorValue::Impl &other) override
108 void swap(DataSeriesIteratorValue::Impl &other) override
109 {
109 {
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
113 }
113 }
114
114
115 private:
115 private:
116 ArrayDataIterator m_XIt;
116 ArrayDataIterator m_XIt;
117 ArrayDataIterator m_ValuesIt;
117 ArrayDataIterator m_ValuesIt;
118 };
118 };
119 } // namespace dataseries_detail
119 } // namespace dataseries_detail
120
120
121 /**
121 /**
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
123 *
123 *
124 * The DataSeries represents values on one or two axes, according to these rules:
124 * The DataSeries represents values on one or two axes, according to these rules:
125 * - the x-axis is always defined
125 * - the x-axis is always defined
126 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
126 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
127 * below)
127 * below)
128 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
128 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
129 * distributed into components (for example, a vector defines three components)
129 * distributed into components (for example, a vector defines three components)
130 * - New values can be added to the series, on the x-axis.
130 * - New values can be added to the series, on the x-axis.
131 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
131 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
132 * - Data representing values and axes are associated with a unit
132 * - Data representing values and axes are associated with a unit
133 * - The data series is always sorted in ascending order on the x-axis.
133 * - The data series is always sorted in ascending order on the x-axis.
134 *
134 *
135 * Consistency checks are carried out between the axes and the values. These controls are provided
135 * Consistency checks are carried out between the axes and the values. These controls are provided
136 * throughout the DataSeries lifecycle:
136 * throughout the DataSeries lifecycle:
137 * - the number of data on the x-axis must be equal to the number of values (in the case of
137 * - the number of data on the x-axis must be equal to the number of values (in the case of
138 * 2-dim ArrayData for values, the test is performed on the number of values per component)
138 * 2-dim ArrayData for values, the test is performed on the number of values per component)
139 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
139 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
140 * number of data on the y-axis.
140 * number of data on the y-axis.
141 *
141 *
142 * Examples:
142 * Examples:
143 * 1)
143 * 1)
144 * - x-axis: [1 ; 2 ; 3]
144 * - x-axis: [1 ; 2 ; 3]
145 * - y-axis: not defined
145 * - y-axis: not defined
146 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
146 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
147 * => the DataSeries is valid, as x-axis and values have the same number of data
147 * => the DataSeries is valid, as x-axis and values have the same number of data
148 *
148 *
149 * 2)
149 * 2)
150 * - x-axis: [1 ; 2 ; 3]
150 * - x-axis: [1 ; 2 ; 3]
151 * - y-axis: not defined
151 * - y-axis: not defined
152 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
152 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
153 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
153 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
154 *
154 *
155 * 3)
155 * 3)
156 * - x-axis: [1 ; 2 ; 3]
156 * - x-axis: [1 ; 2 ; 3]
157 * - y-axis: not defined
157 * - y-axis: not defined
158 * - values: [10 ; 20 ; 30
158 * - values: [10 ; 20 ; 30
159 * 40 ; 50 ; 60] (2-dim ArrayData)
159 * 40 ; 50 ; 60] (2-dim ArrayData)
160 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
160 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
161 * data each
161 * data each
162 *
162 *
163 * 4)
163 * 4)
164 * - x-axis: [1 ; 2 ; 3]
164 * - x-axis: [1 ; 2 ; 3]
165 * - y-axis: [1 ; 2]
165 * - y-axis: [1 ; 2]
166 * - values: [10 ; 20 ; 30
166 * - values: [10 ; 20 ; 30
167 * 40 ; 50 ; 60] (2-dim ArrayData)
167 * 40 ; 50 ; 60] (2-dim ArrayData)
168 * => the DataSeries is valid, as:
168 * => the DataSeries is valid, as:
169 * - x-axis has 3 data and values contains 2 components with 3 data each AND
169 * - x-axis has 3 data and values contains 2 components with 3 data each AND
170 * - y-axis has 2 data and values contains 2 components
170 * - y-axis has 2 data and values contains 2 components
171 *
171 *
172 * 5)
172 * 5)
173 * - x-axis: [1 ; 2 ; 3]
173 * - x-axis: [1 ; 2 ; 3]
174 * - y-axis: [1 ; 2 ; 3]
174 * - y-axis: [1 ; 2 ; 3]
175 * - values: [10 ; 20 ; 30
175 * - values: [10 ; 20 ; 30
176 * 40 ; 50 ; 60] (2-dim ArrayData)
176 * 40 ; 50 ; 60] (2-dim ArrayData)
177 * => the DataSeries is invalid, as:
177 * => the DataSeries is invalid, as:
178 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
178 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
179 * - y-axis has 3 data and values contains only 2 components
179 * - y-axis has 3 data and values contains only 2 components
180 *
180 *
181 * @tparam Dim The dimension of the values data
181 * @tparam Dim The dimension of the values data
182 *
182 *
183 */
183 */
184 template <int Dim>
184 template <int Dim>
185 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
185 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
186 friend class DataSeriesMergeHelper;
186 friend class DataSeriesMergeHelper;
187
187
188 public:
188 public:
189 /// @sa IDataSeries::xAxisData()
189 /// @sa IDataSeries::xAxisData()
190 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
190 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
191 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
191 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
192
192
193 /// @sa IDataSeries::xAxisUnit()
193 /// @sa IDataSeries::xAxisUnit()
194 Unit xAxisUnit() const override { return m_XAxisUnit; }
194 Unit xAxisUnit() const override { return m_XAxisUnit; }
195
195
196 /// @return the values dataset
196 /// @return the values dataset
197 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
197 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
198 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
198 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
199
199
200 /// @sa IDataSeries::valuesUnit()
200 /// @sa IDataSeries::valuesUnit()
201 Unit valuesUnit() const override { return m_ValuesUnit; }
201 Unit valuesUnit() const override { return m_ValuesUnit; }
202
202
203 int nbPoints() const override { return m_ValuesData->totalSize(); }
203 int nbPoints() const override { return m_ValuesData->totalSize(); }
204
204
205 void clear()
205 void clear()
206 {
206 {
207 m_XAxisData->clear();
207 m_XAxisData->clear();
208 m_ValuesData->clear();
208 m_ValuesData->clear();
209 }
209 }
210
210
211 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
211 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
212
212
213 /// Merges into the data series an other data series.
213 /// Merges into the data series an other data series.
214 ///
214 ///
215 /// The two dataseries:
215 /// The two dataseries:
216 /// - must be of the same dimension
216 /// - must be of the same dimension
217 /// - must have the same y-axis (if defined)
217 /// - must have the same y-axis (if defined)
218 ///
218 ///
219 /// If the prerequisites are not valid, the method does nothing
219 /// If the prerequisites are not valid, the method does nothing
220 ///
220 ///
221 /// @remarks the data series to merge with is cleared after the operation
221 /// @remarks the data series to merge with is cleared after the operation
222 void merge(IDataSeries *dataSeries) override
222 void merge(IDataSeries *dataSeries) override
223 {
223 {
224 dataSeries->lockWrite();
224 dataSeries->lockWrite();
225 lockWrite();
225 lockWrite();
226
226
227 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
227 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
228 if (m_YAxis == other->m_YAxis) {
228 if (m_YAxis == other->m_YAxis) {
229 DataSeriesMergeHelper::merge(*other, *this);
229 DataSeriesMergeHelper::merge(*other, *this);
230 }
230 }
231 else {
231 else {
232 qCWarning(LOG_DataSeries())
232 qCWarning(LOG_DataSeries())
233 << QObject::tr("Can't merge data series that have not the same y-axis");
233 << QObject::tr("Can't merge data series that have not the same y-axis");
234 }
234 }
235 }
235 }
236 else {
236 else {
237 qCWarning(LOG_DataSeries())
237 qCWarning(LOG_DataSeries())
238 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
238 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
239 }
239 }
240 unlock();
240 unlock();
241 dataSeries->unlock();
241 dataSeries->unlock();
242 }
242 }
243
243
244 void purge(double min, double max) override
244 void purge(double min, double max) override
245 {
245 {
246 // Nothing to purge if series is empty
246 // Nothing to purge if series is empty
247 if (isEmpty()) {
247 if (isEmpty()) {
248 return;
248 return;
249 }
249 }
250
250
251 if (min > max) {
251 if (min > max) {
252 std::swap(min, max);
252 std::swap(min, max);
253 }
253 }
254
254
255 // Nothing to purge if series min/max are inside purge range
255 // Nothing to purge if series min/max are inside purge range
256 auto xMin = cbegin()->x();
256 auto xMin = cbegin()->x();
257 auto xMax = (--cend())->x();
257 auto xMax = (--cend())->x();
258 if (xMin >= min && xMax <= max) {
258 if (xMin >= min && xMax <= max) {
259 return;
259 return;
260 }
260 }
261
261
262 auto lowerIt = std::lower_bound(
262 auto lowerIt = std::lower_bound(
263 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
263 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
264 erase(begin(), lowerIt);
264 erase(begin(), lowerIt);
265 auto upperIt = std::upper_bound(
265 auto upperIt = std::upper_bound(
266 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
266 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
267 erase(upperIt, end());
267 erase(upperIt, end());
268 }
268 }
269
269
270 // ///////// //
270 // ///////// //
271 // Iterators //
271 // Iterators //
272 // ///////// //
272 // ///////// //
273
273
274 DataSeriesIterator begin() override
274 DataSeriesIterator begin() override
275 {
275 {
276 return DataSeriesIterator{DataSeriesIteratorValue{
276 return DataSeriesIterator{DataSeriesIteratorValue{
277 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
277 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
278 }
278 }
279
279
280 DataSeriesIterator end() override
280 DataSeriesIterator end() override
281 {
281 {
282 return DataSeriesIterator{DataSeriesIteratorValue{
282 return DataSeriesIterator{DataSeriesIteratorValue{
283 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
283 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
284 }
284 }
285
285
286 DataSeriesIterator cbegin() const override
286 DataSeriesIterator cbegin() const override
287 {
287 {
288 return DataSeriesIterator{DataSeriesIteratorValue{
288 return DataSeriesIterator{DataSeriesIteratorValue{
289 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
289 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
290 }
290 }
291
291
292 DataSeriesIterator cend() const override
292 DataSeriesIterator cend() const override
293 {
293 {
294 return DataSeriesIterator{DataSeriesIteratorValue{
294 return DataSeriesIterator{DataSeriesIteratorValue{
295 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
295 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
296 }
296 }
297
297
298 void erase(DataSeriesIterator first, DataSeriesIterator last)
298 void erase(DataSeriesIterator first, DataSeriesIterator last)
299 {
299 {
300 auto firstImpl
300 auto firstImpl
301 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
301 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
302 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
302 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
303
303
304 if (firstImpl && lastImpl) {
304 if (firstImpl && lastImpl) {
305 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
305 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
306 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
306 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
307 }
307 }
308 }
308 }
309
309
310 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
310 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
311 {
311 {
312 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
312 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
313 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
313 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
314
314
315 if (firstImpl && lastImpl) {
315 if (firstImpl && lastImpl) {
316 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
316 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
317 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
317 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
318 }
318 }
319 }
319 }
320
320
321 /// @sa IDataSeries::minXAxisData()
321 /// @sa IDataSeries::minXAxisData()
322 DataSeriesIterator minXAxisData(double minXAxisData) const override
322 DataSeriesIterator minXAxisData(double minXAxisData) const override
323 {
323 {
324 return std::lower_bound(
324 return std::lower_bound(
325 cbegin(), cend(), minXAxisData,
325 cbegin(), cend(), minXAxisData,
326 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
326 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
327 }
327 }
328
328
329 /// @sa IDataSeries::maxXAxisData()
329 /// @sa IDataSeries::maxXAxisData()
330 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
330 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
331 {
331 {
332 // Gets the first element that greater than max value
332 // Gets the first element that greater than max value
333 auto it = std::upper_bound(
333 auto it = std::upper_bound(
334 cbegin(), cend(), maxXAxisData,
334 cbegin(), cend(), maxXAxisData,
335 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
335 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
336
336
337 return it == cbegin() ? cend() : --it;
337 return it == cbegin() ? cend() : --it;
338 }
338 }
339
339
340 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
340 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
341 double maxXAxisData) const override
341 double maxXAxisData) const override
342 {
342 {
343 if (minXAxisData > maxXAxisData) {
343 if (minXAxisData > maxXAxisData) {
344 std::swap(minXAxisData, maxXAxisData);
344 std::swap(minXAxisData, maxXAxisData);
345 }
345 }
346
346
347 auto begin = cbegin();
347 auto begin = cbegin();
348 auto end = cend();
348 auto end = cend();
349
349
350 auto lowerIt = std::lower_bound(
350 auto lowerIt = std::lower_bound(
351 begin, end, minXAxisData,
351 begin, end, minXAxisData,
352 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
352 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
353 auto upperIt = std::upper_bound(
353 auto upperIt = std::upper_bound(
354 lowerIt, end, maxXAxisData,
354 lowerIt, end, maxXAxisData,
355 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
355 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
356
356
357 return std::make_pair(lowerIt, upperIt);
357 return std::make_pair(lowerIt, upperIt);
358 }
358 }
359
359
360 std::pair<DataSeriesIterator, DataSeriesIterator>
360 std::pair<DataSeriesIterator, DataSeriesIterator>
361 valuesBounds(double minXAxisData, double maxXAxisData) const override
361 valuesBounds(double minXAxisData, double maxXAxisData) const override
362 {
362 {
363 // Places iterators to the correct x-axis range
363 // Places iterators to the correct x-axis range
364 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
364 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
365
365
366 // Returns end iterators if the range is empty
366 // Returns end iterators if the range is empty
367 if (xAxisRangeIts.first == xAxisRangeIts.second) {
367 if (xAxisRangeIts.first == xAxisRangeIts.second) {
368 return std::make_pair(cend(), cend());
368 return std::make_pair(cend(), cend());
369 }
369 }
370
370
371 // Gets the iterator on the min of all values data
371 // Gets the iterator on the min of all values data
372 auto minIt = std::min_element(
372 auto minIt = std::min_element(
373 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
373 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
374 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
374 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
375 });
375 });
376
376
377 // Gets the iterator on the max of all values data
377 // Gets the iterator on the max of all values data
378 auto maxIt = std::max_element(
378 auto maxIt = std::max_element(
379 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
379 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
380 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
380 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
381 });
381 });
382
382
383 return std::make_pair(minIt, maxIt);
383 return std::make_pair(minIt, maxIt);
384 }
384 }
385
385
386 /// @return the y-axis associated to the data series
387 /// @todo pass getter as protected and use iterators to access the y-axis data
388 OptionalAxis yAxis() const { return m_YAxis; }
389
386 // /////// //
390 // /////// //
387 // Mutexes //
391 // Mutexes //
388 // /////// //
392 // /////// //
389
393
390 virtual void lockRead() { m_Lock.lockForRead(); }
394 virtual void lockRead() { m_Lock.lockForRead(); }
391 virtual void lockWrite() { m_Lock.lockForWrite(); }
395 virtual void lockWrite() { m_Lock.lockForWrite(); }
392 virtual void unlock() { m_Lock.unlock(); }
396 virtual void unlock() { m_Lock.unlock(); }
393
397
394 protected:
398 protected:
395 /// Protected ctor (DataSeries is abstract).
399 /// Protected ctor (DataSeries is abstract).
396 ///
400 ///
397 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
401 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
398 /// class description for consistent rules)
402 /// class description for consistent rules)
399 /// @remarks data series is automatically sorted on its x-axis data
403 /// @remarks data series is automatically sorted on its x-axis data
400 /// @throws std::invalid_argument if the data are inconsistent with each other
404 /// @throws std::invalid_argument if the data are inconsistent with each other
401 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
405 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
402 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
406 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
403 OptionalAxis yAxis = OptionalAxis{})
407 OptionalAxis yAxis = OptionalAxis{})
404 : m_XAxisData{xAxisData},
408 : m_XAxisData{xAxisData},
405 m_XAxisUnit{xAxisUnit},
409 m_XAxisUnit{xAxisUnit},
406 m_ValuesData{valuesData},
410 m_ValuesData{valuesData},
407 m_ValuesUnit{valuesUnit},
411 m_ValuesUnit{valuesUnit},
408 m_YAxis{std::move(yAxis)}
412 m_YAxis{std::move(yAxis)}
409 {
413 {
410 if (m_XAxisData->size() != m_ValuesData->size()) {
414 if (m_XAxisData->size() != m_ValuesData->size()) {
411 throw std::invalid_argument{
415 throw std::invalid_argument{
412 "The number of values by component must be equal to the number of x-axis data"};
416 "The number of values by component must be equal to the number of x-axis data"};
413 }
417 }
414
418
415 // Validates y-axis (if defined)
419 // Validates y-axis (if defined)
416 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
420 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
417 throw std::invalid_argument{
421 throw std::invalid_argument{
418 "As the y-axis is defined, the number of value components must be equal to the "
422 "As the y-axis is defined, the number of value components must be equal to the "
419 "number of y-axis data"};
423 "number of y-axis data"};
420 }
424 }
421
425
422 // Sorts data if it's not the case
426 // Sorts data if it's not the case
423 const auto &xAxisCData = m_XAxisData->cdata();
427 const auto &xAxisCData = m_XAxisData->cdata();
424 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
428 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
425 sort();
429 sort();
426 }
430 }
427 }
431 }
428
432
429 /// Copy ctor
433 /// Copy ctor
430 explicit DataSeries(const DataSeries<Dim> &other)
434 explicit DataSeries(const DataSeries<Dim> &other)
431 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
435 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
432 m_XAxisUnit{other.m_XAxisUnit},
436 m_XAxisUnit{other.m_XAxisUnit},
433 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
437 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
434 m_ValuesUnit{other.m_ValuesUnit},
438 m_ValuesUnit{other.m_ValuesUnit},
435 m_YAxis{other.m_YAxis}
439 m_YAxis{other.m_YAxis}
436 {
440 {
437 // Since a series is ordered from its construction and is always ordered, it is not
441 // Since a series is ordered from its construction and is always ordered, it is not
438 // necessary to call the sort method here ('other' is sorted)
442 // necessary to call the sort method here ('other' is sorted)
439 }
443 }
440
444
441 /// @return the y-axis associated to the data series
442 OptionalAxis yAxis() const { return m_YAxis; }
443
444 /// Assignment operator
445 /// Assignment operator
445 template <int D>
446 template <int D>
446 DataSeries &operator=(DataSeries<D> other)
447 DataSeries &operator=(DataSeries<D> other)
447 {
448 {
448 std::swap(m_XAxisData, other.m_XAxisData);
449 std::swap(m_XAxisData, other.m_XAxisData);
449 std::swap(m_XAxisUnit, other.m_XAxisUnit);
450 std::swap(m_XAxisUnit, other.m_XAxisUnit);
450 std::swap(m_ValuesData, other.m_ValuesData);
451 std::swap(m_ValuesData, other.m_ValuesData);
451 std::swap(m_ValuesUnit, other.m_ValuesUnit);
452 std::swap(m_ValuesUnit, other.m_ValuesUnit);
452 std::swap(m_YAxis, other.m_YAxis);
453 std::swap(m_YAxis, other.m_YAxis);
453
454
454 return *this;
455 return *this;
455 }
456 }
456
457
457 private:
458 private:
458 /**
459 /**
459 * Sorts data series on its x-axis data
460 * Sorts data series on its x-axis data
460 */
461 */
461 void sort() noexcept
462 void sort() noexcept
462 {
463 {
463 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
464 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
464 m_XAxisData = m_XAxisData->sort(permutation);
465 m_XAxisData = m_XAxisData->sort(permutation);
465 m_ValuesData = m_ValuesData->sort(permutation);
466 m_ValuesData = m_ValuesData->sort(permutation);
466 }
467 }
467
468
468 // x-axis
469 // x-axis
469 std::shared_ptr<ArrayData<1> > m_XAxisData;
470 std::shared_ptr<ArrayData<1> > m_XAxisData;
470 Unit m_XAxisUnit;
471 Unit m_XAxisUnit;
471
472
472 // values
473 // values
473 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
474 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
474 Unit m_ValuesUnit;
475 Unit m_ValuesUnit;
475
476
476 // y-axis (optional)
477 // y-axis (optional)
477 OptionalAxis m_YAxis;
478 OptionalAxis m_YAxis;
478
479
479 QReadWriteLock m_Lock;
480 QReadWriteLock m_Lock;
480 };
481 };
481
482
482 #endif // SCIQLOP_DATASERIES_H
483 #endif // SCIQLOP_DATASERIES_H
@@ -1,58 +1,62
1 #ifndef SCIQLOP_OPTIONALAXIS_H
1 #ifndef SCIQLOP_OPTIONALAXIS_H
2 #define SCIQLOP_OPTIONALAXIS_H
2 #define SCIQLOP_OPTIONALAXIS_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5 #include "Unit.h"
5 #include "Unit.h"
6
6
7 #include <memory>
7 #include <memory>
8
8
9 template <int Dim>
9 template <int Dim>
10 class ArrayData;
10 class ArrayData;
11
11
12 /**
12 /**
13 * @brief The OptionalAxis class defines an optional data axis for a series of data.
13 * @brief The OptionalAxis class defines an optional data axis for a series of data.
14 *
14 *
15 * An optional data axis is an axis that can be defined or not for a data series. If defined, it
15 * An optional data axis is an axis that can be defined or not for a data series. If defined, it
16 * contains a unit and data (1-dim ArrayData). It is then possible to access the data or the unit.
16 * contains a unit and data (1-dim ArrayData). It is then possible to access the data or the unit.
17 * In the case of an undefined axis, the axis has no data and no unit. The methods for accessing the
17 * In the case of an undefined axis, the axis has no data and no unit. The methods for accessing the
18 * data or the unit are always callable but will return undefined values.
18 * data or the unit are always callable but will return undefined values.
19 *
19 *
20 * @sa DataSeries
20 * @sa DataSeries
21 * @sa ArrayData
21 * @sa ArrayData
22 */
22 */
23 class SCIQLOP_CORE_EXPORT OptionalAxis {
23 class SCIQLOP_CORE_EXPORT OptionalAxis {
24 public:
24 public:
25 /// Ctor for an undefined axis
25 /// Ctor for an undefined axis
26 explicit OptionalAxis();
26 explicit OptionalAxis();
27 /// Ctor for a defined axis
27 /// Ctor for a defined axis
28 /// @param data the axis' data
28 /// @param data the axis' data
29 /// @param unit the axis' unit
29 /// @param unit the axis' unit
30 /// @throws std::invalid_argument if no data is associated to the axis
30 /// @throws std::invalid_argument if no data is associated to the axis
31 explicit OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit);
31 explicit OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit);
32
32
33 /// Copy ctor
33 /// Copy ctor
34 OptionalAxis(const OptionalAxis &other);
34 OptionalAxis(const OptionalAxis &other);
35 /// Assignment operator
35 /// Assignment operator
36 OptionalAxis &operator=(OptionalAxis other);
36 OptionalAxis &operator=(OptionalAxis other);
37
37
38 /// @return the flag that indicates if the axis is defined or not
38 /// @return the flag that indicates if the axis is defined or not
39 bool isDefined() const;
39 bool isDefined() const;
40
40
41 /// @return gets the data at the index passed in parameter, NaN if the index is outside the
41 /// @return gets the data at the index passed in parameter, NaN if the index is outside the
42 /// bounds of the axis, or if the axis is undefined
42 /// bounds of the axis, or if the axis is undefined
43 double at(int index) const;
43 double at(int index) const;
44
45 ///@return the min and max values of the data on the axis, NaN values if there is no data
46 std::pair<double, double> bounds() const;
47
44 /// @return the number of data on the axis, 0 if the axis is not defined
48 /// @return the number of data on the axis, 0 if the axis is not defined
45 int size() const;
49 int size() const;
46 /// @return the unit of the axis, an empty unit if the axis is not defined
50 /// @return the unit of the axis, an empty unit if the axis is not defined
47 Unit unit() const;
51 Unit unit() const;
48
52
49 bool operator==(const OptionalAxis &other);
53 bool operator==(const OptionalAxis &other);
50 bool operator!=(const OptionalAxis &other);
54 bool operator!=(const OptionalAxis &other);
51
55
52 private:
56 private:
53 bool m_Defined; ///< Axis is defined or not
57 bool m_Defined; ///< Axis is defined or not
54 std::shared_ptr<ArrayData<1> > m_Data; ///< Axis' data
58 std::shared_ptr<ArrayData<1> > m_Data; ///< Axis' data
55 Unit m_Unit; ///< Axis' unit
59 Unit m_Unit; ///< Axis' unit
56 };
60 };
57
61
58 #endif // SCIQLOP_OPTIONALAXIS_H
62 #endif // SCIQLOP_OPTIONALAXIS_H
@@ -1,74 +1,99
1 #include <Data/OptionalAxis.h>
1 #include <Data/OptionalAxis.h>
2
2
3 #include "Data/ArrayData.h"
3 #include "Data/ArrayData.h"
4
4
5 OptionalAxis::OptionalAxis() : m_Defined{false}, m_Data{nullptr}, m_Unit{}
5 OptionalAxis::OptionalAxis() : m_Defined{false}, m_Data{nullptr}, m_Unit{}
6 {
6 {
7 }
7 }
8
8
9 OptionalAxis::OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit)
9 OptionalAxis::OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit)
10 : m_Defined{true}, m_Data{data}, m_Unit{std::move(unit)}
10 : m_Defined{true}, m_Data{data}, m_Unit{std::move(unit)}
11 {
11 {
12 if (m_Data == nullptr) {
12 if (m_Data == nullptr) {
13 throw std::invalid_argument{"Data can't be null for a defined axis"};
13 throw std::invalid_argument{"Data can't be null for a defined axis"};
14 }
14 }
15 }
15 }
16
16
17 OptionalAxis::OptionalAxis(const OptionalAxis &other)
17 OptionalAxis::OptionalAxis(const OptionalAxis &other)
18 : m_Defined{other.m_Defined},
18 : m_Defined{other.m_Defined},
19 m_Data{other.m_Data ? std::make_shared<ArrayData<1> >(*other.m_Data) : nullptr},
19 m_Data{other.m_Data ? std::make_shared<ArrayData<1> >(*other.m_Data) : nullptr},
20 m_Unit{other.m_Unit}
20 m_Unit{other.m_Unit}
21 {
21 {
22 }
22 }
23
23
24 OptionalAxis &OptionalAxis::operator=(OptionalAxis other)
24 OptionalAxis &OptionalAxis::operator=(OptionalAxis other)
25 {
25 {
26 std::swap(m_Defined, other.m_Defined);
26 std::swap(m_Defined, other.m_Defined);
27 std::swap(m_Data, other.m_Data);
27 std::swap(m_Data, other.m_Data);
28 std::swap(m_Unit, other.m_Unit);
28 std::swap(m_Unit, other.m_Unit);
29 }
29 }
30
30
31 bool OptionalAxis::isDefined() const
31 bool OptionalAxis::isDefined() const
32 {
32 {
33 return m_Defined;
33 return m_Defined;
34 }
34 }
35
35
36 double OptionalAxis::at(int index) const
36 double OptionalAxis::at(int index) const
37 {
37 {
38 if (m_Defined) {
38 if (m_Defined) {
39 return (index >= 0 && index < m_Data->size()) ? m_Data->at(index)
39 return (index >= 0 && index < m_Data->size()) ? m_Data->at(index)
40 : std::numeric_limits<double>::quiet_NaN();
40 : std::numeric_limits<double>::quiet_NaN();
41 }
41 }
42 else {
42 else {
43 return std::numeric_limits<double>::quiet_NaN();
43 return std::numeric_limits<double>::quiet_NaN();
44 }
44 }
45 }
45 }
46
46
47 std::pair<double, double> OptionalAxis::bounds() const
48 {
49 if (!m_Defined || m_Data->size() == 0) {
50 return std::make_pair(std::numeric_limits<double>::quiet_NaN(),
51 std::numeric_limits<double>::quiet_NaN());
52 }
53 else {
54
55 auto minIt = std::min_element(
56 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
57 return SortUtils::minCompareWithNaN(it1.first(), it2.first());
58 });
59
60 // Gets the iterator on the max of all values data
61 auto maxIt = std::max_element(
62 m_Data->cbegin(), m_Data->cend(), [](const auto &it1, const auto &it2) {
63 return SortUtils::maxCompareWithNaN(it1.first(), it2.first());
64 });
65
66 auto pair = std::make_pair(minIt->first(), maxIt->first());
67
68 return std::make_pair(minIt->first(), maxIt->first());
69 }
70 }
71
47 int OptionalAxis::size() const
72 int OptionalAxis::size() const
48 {
73 {
49 return m_Defined ? m_Data->size() : 0;
74 return m_Defined ? m_Data->size() : 0;
50 }
75 }
51
76
52 Unit OptionalAxis::unit() const
77 Unit OptionalAxis::unit() const
53 {
78 {
54 return m_Defined ? m_Unit : Unit{};
79 return m_Defined ? m_Unit : Unit{};
55 }
80 }
56
81
57 bool OptionalAxis::operator==(const OptionalAxis &other)
82 bool OptionalAxis::operator==(const OptionalAxis &other)
58 {
83 {
59 // Axis not defined
84 // Axis not defined
60 if (!m_Defined) {
85 if (!m_Defined) {
61 return !other.m_Defined;
86 return !other.m_Defined;
62 }
87 }
63
88
64 // Axis defined
89 // Axis defined
65 return m_Unit == other.m_Unit
90 return m_Unit == other.m_Unit
66 && std::equal(
91 && std::equal(
67 m_Data->cbegin(), m_Data->cend(), other.m_Data->cbegin(), other.m_Data->cend(),
92 m_Data->cbegin(), m_Data->cend(), other.m_Data->cbegin(), other.m_Data->cend(),
68 [](const auto &it1, const auto &it2) { return it1.values() == it2.values(); });
93 [](const auto &it1, const auto &it2) { return it1.values() == it2.values(); });
69 }
94 }
70
95
71 bool OptionalAxis::operator!=(const OptionalAxis &other)
96 bool OptionalAxis::operator!=(const OptionalAxis &other)
72 {
97 {
73 return !(*this == other);
98 return !(*this == other);
74 }
99 }
@@ -1,199 +1,198
1 #include "Data/SpectrogramSeries.h"
1 #include "Data/SpectrogramSeries.h"
2
2
3 #include "DataSeriesBuilders.h"
3 #include "DataSeriesBuilders.h"
4 #include "DataSeriesUtils.h"
4 #include "DataSeriesUtils.h"
5
5
6 #include <QObject>
6 #include <QObject>
7 #include <QtTest>
7 #include <QtTest>
8
8
9 namespace {
9 namespace {
10
10
11 // Aliases used to facilitate reading of test inputs
11 // Aliases used to facilitate reading of test inputs
12 using X = DataContainer;
12 using X = DataContainer;
13 using Y = DataContainer;
13 using Y = DataContainer;
14 using Values = DataContainer;
14 using Values = DataContainer;
15 using Components = std::vector<DataContainer>;
15 using Components = std::vector<DataContainer>;
16
16
17 } // namespace
17 } // namespace
18
18
19 /**
19 /**
20 * @brief The TestSpectrogramSeries class defines unit tests on spectrogram series.
20 * @brief The TestSpectrogramSeries class defines unit tests on spectrogram series.
21 *
21 *
22 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
22 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
23 */
23 */
24 class TestSpectrogramSeries : public QObject {
24 class TestSpectrogramSeries : public QObject {
25 Q_OBJECT
25 Q_OBJECT
26 private slots:
26 private slots:
27
27
28 /// Tests construction of a spectrogram series
28 /// Tests construction of a spectrogram series
29 void testCtor_data();
29 void testCtor_data();
30 void testCtor();
30 void testCtor();
31
31
32 /// Tests merge of two spectrogram series
32 /// Tests merge of two spectrogram series
33 void testMerge_data();
33 void testMerge_data();
34 void testMerge();
34 void testMerge();
35
35
36 /// @todo ALX: test subdataseries
37 /// Tests get subdata of a spectrogram series
36 /// Tests get subdata of a spectrogram series
38 void testSubDataSeries_data();
37 void testSubDataSeries_data();
39 void testSubDataSeries();
38 void testSubDataSeries();
40 };
39 };
41
40
42 void TestSpectrogramSeries::testCtor_data()
41 void TestSpectrogramSeries::testCtor_data()
43 {
42 {
44 // x-axis data
43 // x-axis data
45 QTest::addColumn<X>("xAxisData");
44 QTest::addColumn<X>("xAxisData");
46 // y-axis data
45 // y-axis data
47 QTest::addColumn<Y>("yAxisData");
46 QTest::addColumn<Y>("yAxisData");
48 // values data
47 // values data
49 QTest::addColumn<Values>("valuesData");
48 QTest::addColumn<Values>("valuesData");
50
49
51 // construction expected to be valid
50 // construction expected to be valid
52 QTest::addColumn<bool>("expectOK");
51 QTest::addColumn<bool>("expectOK");
53 // expected x-axis data (when construction is valid)
52 // expected x-axis data (when construction is valid)
54 QTest::addColumn<X>("expectedXAxisData");
53 QTest::addColumn<X>("expectedXAxisData");
55 // expected components data (when construction is valid)
54 // expected components data (when construction is valid)
56 QTest::addColumn<Components>("expectedComponentsData");
55 QTest::addColumn<Components>("expectedComponentsData");
57
56
58 QTest::newRow(
57 QTest::newRow(
59 "invalidData (number of values by component aren't equal to the number of x-axis data)")
58 "invalidData (number of values by component aren't equal to the number of x-axis data)")
60 << X{1., 2., 3., 4., 5.} << Y{1., 2., 3.} << Values{1., 2., 3.} << false << X{}
59 << X{1., 2., 3., 4., 5.} << Y{1., 2., 3.} << Values{1., 2., 3.} << false << X{}
61 << Components{};
60 << Components{};
62
61
63 QTest::newRow("invalidData (number of components aren't equal to the number of y-axis data)")
62 QTest::newRow("invalidData (number of components aren't equal to the number of y-axis data)")
64 << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
63 << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
65 << Values{1., 2., 3., 4., 5.} // 1 component
64 << Values{1., 2., 3., 4., 5.} // 1 component
66 << false << X{} << Components{};
65 << false << X{} << Components{};
67
66
68 QTest::newRow("sortedData") << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
67 QTest::newRow("sortedData") << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
69 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} // 2 components
68 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} // 2 components
70 << true << X{1., 2., 3., 4., 5.}
69 << true << X{1., 2., 3., 4., 5.}
71 << Components{{1., 3., 5., 7., 9.}, {2., 4., 6., 8., 10.}};
70 << Components{{1., 3., 5., 7., 9.}, {2., 4., 6., 8., 10.}};
72
71
73 QTest::newRow("unsortedData") << X{5., 4., 3., 2., 1.} << Y{1., 2.}
72 QTest::newRow("unsortedData") << X{5., 4., 3., 2., 1.} << Y{1., 2.}
74 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << true
73 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << true
75 << X{1., 2., 3., 4., 5.}
74 << X{1., 2., 3., 4., 5.}
76 << Components{{9., 7., 5., 3., 1.}, {10., 8., 6., 4., 2.}};
75 << Components{{9., 7., 5., 3., 1.}, {10., 8., 6., 4., 2.}};
77 }
76 }
78
77
79 void TestSpectrogramSeries::testCtor()
78 void TestSpectrogramSeries::testCtor()
80 {
79 {
81 // Creates series
80 // Creates series
82 QFETCH(X, xAxisData);
81 QFETCH(X, xAxisData);
83 QFETCH(Y, yAxisData);
82 QFETCH(Y, yAxisData);
84 QFETCH(Values, valuesData);
83 QFETCH(Values, valuesData);
85 QFETCH(bool, expectOK);
84 QFETCH(bool, expectOK);
86
85
87 if (expectOK) {
86 if (expectOK) {
88 auto series = SpectrogramBuilder{}
87 auto series = SpectrogramBuilder{}
89 .setX(std::move(xAxisData))
88 .setX(std::move(xAxisData))
90 .setY(std::move(yAxisData))
89 .setY(std::move(yAxisData))
91 .setValues(std::move(valuesData))
90 .setValues(std::move(valuesData))
92 .build();
91 .build();
93
92
94 // Validates results
93 // Validates results
95 QFETCH(X, expectedXAxisData);
94 QFETCH(X, expectedXAxisData);
96 QFETCH(Components, expectedComponentsData);
95 QFETCH(Components, expectedComponentsData);
97 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedComponentsData);
96 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedComponentsData);
98 }
97 }
99 else {
98 else {
100 QVERIFY_EXCEPTION_THROWN(SpectrogramBuilder{}
99 QVERIFY_EXCEPTION_THROWN(SpectrogramBuilder{}
101 .setX(std::move(xAxisData))
100 .setX(std::move(xAxisData))
102 .setY(std::move(yAxisData))
101 .setY(std::move(yAxisData))
103 .setValues(std::move(valuesData))
102 .setValues(std::move(valuesData))
104 .build(),
103 .build(),
105 std::invalid_argument);
104 std::invalid_argument);
106 }
105 }
107 }
106 }
108
107
109 void TestSpectrogramSeries::testMerge_data()
108 void TestSpectrogramSeries::testMerge_data()
110 {
109 {
111 testMerge_struct<SpectrogramSeries, Components>();
110 testMerge_struct<SpectrogramSeries, Components>();
112
111
113 QTest::newRow("sortedMerge") << SpectrogramBuilder{}
112 QTest::newRow("sortedMerge") << SpectrogramBuilder{}
114 .setX({1., 2., 3.})
113 .setX({1., 2., 3.})
115 .setY({1., 2.})
114 .setY({1., 2.})
116 .setValues({10., 11., 20., 21., 30., 31})
115 .setValues({10., 11., 20., 21., 30., 31})
117 .build()
116 .build()
118 << SpectrogramBuilder{}
117 << SpectrogramBuilder{}
119 .setX({4., 5., 6.})
118 .setX({4., 5., 6.})
120 .setY({1., 2.})
119 .setY({1., 2.})
121 .setValues({40., 41., 50., 51., 60., 61})
120 .setValues({40., 41., 50., 51., 60., 61})
122 .build()
121 .build()
123 << DataContainer{1., 2., 3., 4., 5., 6.}
122 << DataContainer{1., 2., 3., 4., 5., 6.}
124 << Components{{10., 20., 30., 40., 50., 60.},
123 << Components{{10., 20., 30., 40., 50., 60.},
125 {11., 21., 31., 41., 51., 61}};
124 {11., 21., 31., 41., 51., 61}};
126
125
127 QTest::newRow(
126 QTest::newRow(
128 "unsortedMerge (merge not made because the two data series have different y-axes)")
127 "unsortedMerge (merge not made because the two data series have different y-axes)")
129 << SpectrogramBuilder{}
128 << SpectrogramBuilder{}
130 .setX({4., 5., 6.})
129 .setX({4., 5., 6.})
131 .setY({1., 2.})
130 .setY({1., 2.})
132 .setValues({40., 41., 50., 51., 60., 61})
131 .setValues({40., 41., 50., 51., 60., 61})
133 .build()
132 .build()
134 << SpectrogramBuilder{}
133 << SpectrogramBuilder{}
135 .setX({1., 2., 3.})
134 .setX({1., 2., 3.})
136 .setY({3., 4.})
135 .setY({3., 4.})
137 .setValues({10., 11., 20., 21., 30., 31})
136 .setValues({10., 11., 20., 21., 30., 31})
138 .build()
137 .build()
139 << DataContainer{4., 5., 6.} << Components{{40., 50., 60.}, {41., 51., 61}};
138 << DataContainer{4., 5., 6.} << Components{{40., 50., 60.}, {41., 51., 61}};
140
139
141 QTest::newRow(
140 QTest::newRow(
142 "unsortedMerge (unsortedMerge (merge is made because the two data series have the same "
141 "unsortedMerge (unsortedMerge (merge is made because the two data series have the same "
143 "y-axis)")
142 "y-axis)")
144 << SpectrogramBuilder{}
143 << SpectrogramBuilder{}
145 .setX({4., 5., 6.})
144 .setX({4., 5., 6.})
146 .setY({1., 2.})
145 .setY({1., 2.})
147 .setValues({40., 41., 50., 51., 60., 61})
146 .setValues({40., 41., 50., 51., 60., 61})
148 .build()
147 .build()
149 << SpectrogramBuilder{}
148 << SpectrogramBuilder{}
150 .setX({1., 2., 3.})
149 .setX({1., 2., 3.})
151 .setY({1., 2.})
150 .setY({1., 2.})
152 .setValues({10., 11., 20., 21., 30., 31})
151 .setValues({10., 11., 20., 21., 30., 31})
153 .build()
152 .build()
154 << DataContainer{1., 2., 3., 4., 5., 6.}
153 << DataContainer{1., 2., 3., 4., 5., 6.}
155 << Components{{10., 20., 30., 40., 50., 60.}, {11., 21., 31., 41., 51., 61}};
154 << Components{{10., 20., 30., 40., 50., 60.}, {11., 21., 31., 41., 51., 61}};
156 }
155 }
157
156
158 void TestSpectrogramSeries::testMerge()
157 void TestSpectrogramSeries::testMerge()
159 {
158 {
160 testMerge_t<SpectrogramSeries, Components>();
159 testMerge_t<SpectrogramSeries, Components>();
161 }
160 }
162
161
163 void TestSpectrogramSeries::testSubDataSeries_data()
162 void TestSpectrogramSeries::testSubDataSeries_data()
164 {
163 {
165 testSubDataSeries_struct<SpectrogramSeries, Components>();
164 testSubDataSeries_struct<SpectrogramSeries, Components>();
166
165
167 QTest::newRow("subDataSeries (the range includes all data)")
166 QTest::newRow("subDataSeries (the range includes all data)")
168 << SpectrogramBuilder{}
167 << SpectrogramBuilder{}
169 .setX({1., 2., 3.})
168 .setX({1., 2., 3.})
170 .setY({1., 2.})
169 .setY({1., 2.})
171 .setValues({10., 11., 20., 21., 30., 31})
170 .setValues({10., 11., 20., 21., 30., 31})
172 .build()
171 .build()
173 << SqpRange{0., 5.} << DataContainer{1., 2., 3.}
172 << SqpRange{0., 5.} << DataContainer{1., 2., 3.}
174 << Components{{10., 20., 30.}, {11., 21., 31.}};
173 << Components{{10., 20., 30.}, {11., 21., 31.}};
175
174
176 QTest::newRow("subDataSeries (the range includes no data)")
175 QTest::newRow("subDataSeries (the range includes no data)")
177 << SpectrogramBuilder{}
176 << SpectrogramBuilder{}
178 .setX({1., 2., 3.})
177 .setX({1., 2., 3.})
179 .setY({1., 2.})
178 .setY({1., 2.})
180 .setValues({10., 11., 20., 21., 30., 31})
179 .setValues({10., 11., 20., 21., 30., 31})
181 .build()
180 .build()
182 << SqpRange{4., 5.} << DataContainer{} << Components{{}, {}};
181 << SqpRange{4., 5.} << DataContainer{} << Components{{}, {}};
183
182
184 QTest::newRow("subDataSeries (the range includes some data)")
183 QTest::newRow("subDataSeries (the range includes some data)")
185 << SpectrogramBuilder{}
184 << SpectrogramBuilder{}
186 .setX({1., 2., 3.})
185 .setX({1., 2., 3.})
187 .setY({1., 2.})
186 .setY({1., 2.})
188 .setValues({10., 11., 20., 21., 30., 31})
187 .setValues({10., 11., 20., 21., 30., 31})
189 .build()
188 .build()
190 << SqpRange{1.1, 3} << DataContainer{2., 3.} << Components{{20., 30.}, {21., 31.}};
189 << SqpRange{1.1, 3} << DataContainer{2., 3.} << Components{{20., 30.}, {21., 31.}};
191 }
190 }
192
191
193 void TestSpectrogramSeries::testSubDataSeries()
192 void TestSpectrogramSeries::testSubDataSeries()
194 {
193 {
195 testSubDataSeries_t<SpectrogramSeries, Components>();
194 testSubDataSeries_t<SpectrogramSeries, Components>();
196 }
195 }
197
196
198 QTEST_MAIN(TestSpectrogramSeries)
197 QTEST_MAIN(TestSpectrogramSeries)
199 #include "TestSpectrogramSeries.moc"
198 #include "TestSpectrogramSeries.moc"
@@ -1,39 +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<IDataSeries> dataSeries,
36 const SqpRange &dateTime);
36 const SqpRange &dateTime);
37
38 static void setYAxisRange(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept;
37 };
39 };
38
40
39 #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H
41 #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H
@@ -1,108 +1,109
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3
3
4 #include "Visualization/IVisualizationWidget.h"
4 #include "Visualization/IVisualizationWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
6
6
7 #include <QLoggingCategory>
7 #include <QLoggingCategory>
8 #include <QWidget>
8 #include <QWidget>
9
9
10 #include <memory>
10 #include <memory>
11
11
12 #include <Common/spimpl.h>
12 #include <Common/spimpl.h>
13
13
14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15
15
16 class QCPRange;
16 class QCPRange;
17 class QCustomPlot;
17 class QCustomPlot;
18 class SqpRange;
18 class SqpRange;
19 class Variable;
19 class Variable;
20 class VisualizationZoneWidget;
20 class VisualizationZoneWidget;
21
21
22 namespace Ui {
22 namespace Ui {
23 class VisualizationGraphWidget;
23 class VisualizationGraphWidget;
24 } // namespace Ui
24 } // namespace Ui
25
25
26 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
26 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
27 Q_OBJECT
27 Q_OBJECT
28
28
29 friend class QCustomPlotSynchronizer;
29 friend class QCustomPlotSynchronizer;
30 friend class VisualizationGraphRenderingDelegate;
30 friend class VisualizationGraphRenderingDelegate;
31
31
32 public:
32 public:
33 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
33 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
34 virtual ~VisualizationGraphWidget();
34 virtual ~VisualizationGraphWidget();
35
35
36 VisualizationZoneWidget *parentZoneWidget() const noexcept;
36 VisualizationZoneWidget *parentZoneWidget() const noexcept;
37
37
38 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
38 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
39 void enableAcquisition(bool enable);
39 void enableAcquisition(bool enable);
40
40
41 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
41 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
42
42
43 /// Removes a variable from the graph
43 /// Removes a variable from the graph
44 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
44 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
45
45
46 /// Returns the list of all variables used in the graph
46 /// Returns the list of all variables used in the graph
47 QList<std::shared_ptr<Variable> > variables() const;
47 QList<std::shared_ptr<Variable> > variables() const;
48
48
49 void setYRange(const SqpRange &range);
49 /// Sets the y-axis range based on the data of a variable
50 void setYRange(std::shared_ptr<Variable> variable);
50 SqpRange graphRange() const noexcept;
51 SqpRange graphRange() const noexcept;
51 void setGraphRange(const SqpRange &range);
52 void setGraphRange(const SqpRange &range);
52
53
53 // IVisualizationWidget interface
54 // IVisualizationWidget interface
54 void accept(IVisualizationWidgetVisitor *visitor) override;
55 void accept(IVisualizationWidgetVisitor *visitor) override;
55 bool canDrop(const Variable &variable) const override;
56 bool canDrop(const Variable &variable) const override;
56 bool contains(const Variable &variable) const override;
57 bool contains(const Variable &variable) const override;
57 QString name() const override;
58 QString name() const override;
58
59
59 // VisualisationDragWidget
60 // VisualisationDragWidget
60 QMimeData *mimeData() const override;
61 QMimeData *mimeData() const override;
61 bool isDragAllowed() const override;
62 bool isDragAllowed() const override;
62 void highlightForMerge(bool highlighted) override;
63 void highlightForMerge(bool highlighted) override;
63
64
64 signals:
65 signals:
65 void synchronize(const SqpRange &range, const SqpRange &oldRange);
66 void synchronize(const SqpRange &range, const SqpRange &oldRange);
66 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
67 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
67 bool synchronise);
68 bool synchronise);
68
69
69 /// Signal emitted when the variable is about to be removed from the graph
70 /// Signal emitted when the variable is about to be removed from the graph
70 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
71 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
71 /// Signal emitted when the variable has been added to the graph
72 /// Signal emitted when the variable has been added to the graph
72 void variableAdded(std::shared_ptr<Variable> var);
73 void variableAdded(std::shared_ptr<Variable> var);
73
74
74 protected:
75 protected:
75 void closeEvent(QCloseEvent *event) override;
76 void closeEvent(QCloseEvent *event) override;
76 void enterEvent(QEvent *event) override;
77 void enterEvent(QEvent *event) override;
77 void leaveEvent(QEvent *event) override;
78 void leaveEvent(QEvent *event) override;
78
79
79 QCustomPlot &plot() noexcept;
80 QCustomPlot &plot() noexcept;
80
81
81 private:
82 private:
82 Ui::VisualizationGraphWidget *ui;
83 Ui::VisualizationGraphWidget *ui;
83
84
84 class VisualizationGraphWidgetPrivate;
85 class VisualizationGraphWidgetPrivate;
85 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
86 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
86
87
87 private slots:
88 private slots:
88 /// Slot called when right clicking on the graph (displays a menu)
89 /// Slot called when right clicking on the graph (displays a menu)
89 void onGraphMenuRequested(const QPoint &pos) noexcept;
90 void onGraphMenuRequested(const QPoint &pos) noexcept;
90
91
91 /// Rescale the X axe to range parameter
92 /// Rescale the X axe to range parameter
92 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
93 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
93
94
94 /// Slot called when a mouse move was made
95 /// Slot called when a mouse move was made
95 void onMouseMove(QMouseEvent *event) noexcept;
96 void onMouseMove(QMouseEvent *event) noexcept;
96 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
97 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
97 void onMouseWheel(QWheelEvent *event) noexcept;
98 void onMouseWheel(QWheelEvent *event) noexcept;
98 /// Slot called when a mouse press was made, to activate the calibration of a graph
99 /// Slot called when a mouse press was made, to activate the calibration of a graph
99 void onMousePress(QMouseEvent *event) noexcept;
100 void onMousePress(QMouseEvent *event) noexcept;
100 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
101 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
101 void onMouseRelease(QMouseEvent *event) noexcept;
102 void onMouseRelease(QMouseEvent *event) noexcept;
102
103
103 void onDataCacheVariableUpdated();
104 void onDataCacheVariableUpdated();
104
105
105 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
106 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
106 };
107 };
107
108
108 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
109 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,204 +1,342
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Common/ColorUtils.h>
4 #include <Common/ColorUtils.h>
5
5
6 #include <Data/ScalarSeries.h>
6 #include <Data/ScalarSeries.h>
7 #include <Data/SpectrogramSeries.h>
7 #include <Data/VectorSeries.h>
8 #include <Data/VectorSeries.h>
8
9
9 #include <Variable/Variable.h>
10 #include <Variable/Variable.h>
10
11
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12
13
13 namespace {
14 namespace {
14
15
15 class SqpDataContainer : public QCPGraphDataContainer {
16 class SqpDataContainer : public QCPGraphDataContainer {
16 public:
17 public:
17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
18 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
18 };
19 };
19
20
20 /**
21 /**
21 * Struct used to create plottables, depending on the type of the data series from which to create
22 * Struct used to create plottables, depending on the type of the data series from which to create
22 * them
23 * them
23 * @tparam T the data series' type
24 * @tparam T the data series' type
24 * @remarks Default implementation can't create plottables
25 * @remarks Default implementation can't create plottables
25 */
26 */
26 template <typename T, typename Enabled = void>
27 template <typename T, typename Enabled = void>
27 struct PlottablesCreator {
28 struct PlottablesCreator {
28 static PlottablesMap createPlottables(T &, QCustomPlot &)
29 static PlottablesMap createPlottables(T &, QCustomPlot &)
29 {
30 {
30 qCCritical(LOG_DataSeries())
31 qCCritical(LOG_DataSeries())
31 << QObject::tr("Can't create plottables: unmanaged data series type");
32 << QObject::tr("Can't create plottables: unmanaged data series type");
32 return {};
33 return {};
33 }
34 }
34 };
35 };
35
36
36 /**
37 /**
37 * Specialization of PlottablesCreator for scalars and vectors
38 * Specialization of PlottablesCreator for scalars and vectors
38 * @sa ScalarSeries
39 * @sa ScalarSeries
39 * @sa VectorSeries
40 * @sa VectorSeries
40 */
41 */
41 template <typename T>
42 template <typename T>
42 struct PlottablesCreator<T,
43 struct PlottablesCreator<T,
43 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
44 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
44 or std::is_base_of<VectorSeries, T>::value> > {
45 or std::is_base_of<VectorSeries, T>::value> > {
45 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
46 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
46 {
47 {
47 PlottablesMap result{};
48 PlottablesMap result{};
48
49
49 // Gets the number of components of the data series
50 // Gets the number of components of the data series
50 dataSeries.lockRead();
51 dataSeries.lockRead();
51 auto componentCount = dataSeries.valuesData()->componentCount();
52 auto componentCount = dataSeries.valuesData()->componentCount();
52 dataSeries.unlock();
53 dataSeries.unlock();
53
54
54 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
55 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
55
56
56 // For each component of the data series, creates a QCPGraph to add to the plot
57 // For each component of the data series, creates a QCPGraph to add to the plot
57 for (auto i = 0; i < componentCount; ++i) {
58 for (auto i = 0; i < componentCount; ++i) {
58 auto graph = plot.addGraph();
59 auto graph = plot.addGraph();
59 graph->setPen(QPen{colors.at(i)});
60 graph->setPen(QPen{colors.at(i)});
60
61
61 result.insert({i, graph});
62 result.insert({i, graph});
62 }
63 }
63
64
64 plot.replot();
65 plot.replot();
65
66
66 return result;
67 return result;
67 }
68 }
68 };
69 };
69
70
70 /**
71 /**
72 * Specialization of PlottablesCreator for spectrograms
73 * @sa SpectrogramSeries
74 */
75 template <typename T>
76 struct PlottablesCreator<T,
77 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
78 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
79 {
80 PlottablesMap result{};
81 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
82
83 plot.replot();
84
85 return result;
86 }
87 };
88
89 /**
71 * Struct used to update plottables, depending on the type of the data series from which to update
90 * Struct used to update plottables, depending on the type of the data series from which to update
72 * them
91 * them
73 * @tparam T the data series' type
92 * @tparam T the data series' type
74 * @remarks Default implementation can't update plottables
93 * @remarks Default implementation can't update plottables
75 */
94 */
76 template <typename T, typename Enabled = void>
95 template <typename T, typename Enabled = void>
77 struct PlottablesUpdater {
96 struct PlottablesUpdater {
97 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
98 {
99 qCCritical(LOG_VisualizationGraphHelper())
100 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
101 }
102
78 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
103 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
79 {
104 {
80 qCCritical(LOG_DataSeries())
105 qCCritical(LOG_VisualizationGraphHelper())
81 << QObject::tr("Can't update plottables: unmanaged data series type");
106 << QObject::tr("Can't update plottables: unmanaged data series type");
82 }
107 }
83 };
108 };
84
109
85 /**
110 /**
86 * Specialization of PlottablesUpdater for scalars and vectors
111 * Specialization of PlottablesUpdater for scalars and vectors
87 * @sa ScalarSeries
112 * @sa ScalarSeries
88 * @sa VectorSeries
113 * @sa VectorSeries
89 */
114 */
90 template <typename T>
115 template <typename T>
91 struct PlottablesUpdater<T,
116 struct PlottablesUpdater<T,
92 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
117 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
93 or std::is_base_of<VectorSeries, T>::value> > {
118 or std::is_base_of<VectorSeries, T>::value> > {
119 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
120 {
121 auto minValue = 0., maxValue = 0.;
122
123 dataSeries.lockRead();
124 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
125 auto end = dataSeries.cend();
126 if (valuesBounds.first != end && valuesBounds.second != end) {
127 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
128
129 minValue = rangeValue(valuesBounds.first->minValue());
130 maxValue = rangeValue(valuesBounds.second->maxValue());
131 }
132 dataSeries.unlock();
133
134 plot.yAxis->setRange(QCPRange{minValue, maxValue});
135 }
136
94 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
137 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
95 bool rescaleAxes)
138 bool rescaleAxes)
96 {
139 {
97
140
98 // For each plottable to update, resets its data
141 // For each plottable to update, resets its data
99 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
142 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
100 for (const auto &plottable : plottables) {
143 for (const auto &plottable : plottables) {
101 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
144 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
102 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
145 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
103 graph->setData(dataContainer);
146 graph->setData(dataContainer);
104
147
105 dataContainers.insert({plottable.first, dataContainer});
148 dataContainers.insert({plottable.first, dataContainer});
106 }
149 }
107 }
150 }
108 dataSeries.lockRead();
151 dataSeries.lockRead();
109
152
110 // - Gets the data of the series included in the current range
153 // - Gets the data of the series included in the current range
111 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
154 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
112 // and value data. The correct value is retrieved according to the index of the component
155 // and value data. The correct value is retrieved according to the index of the component
113 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
156 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
114 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
157 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
115 for (const auto &dataContainer : dataContainers) {
158 for (const auto &dataContainer : dataContainers) {
116 auto componentIndex = dataContainer.first;
159 auto componentIndex = dataContainer.first;
117 dataContainer.second->appendGraphData(
160 dataContainer.second->appendGraphData(
118 QCPGraphData(it->x(), it->value(componentIndex)));
161 QCPGraphData(it->x(), it->value(componentIndex)));
119 }
162 }
120 }
163 }
121
164
122 dataSeries.unlock();
165 dataSeries.unlock();
123
166
124 if (!plottables.empty()) {
167 if (!plottables.empty()) {
125 auto plot = plottables.begin()->second->parentPlot();
168 auto plot = plottables.begin()->second->parentPlot();
126
169
127 if (rescaleAxes) {
170 if (rescaleAxes) {
128 plot->rescaleAxes();
171 plot->rescaleAxes();
129 }
172 }
130
173
131 plot->replot();
174 plot->replot();
132 }
175 }
133 }
176 }
134 };
177 };
135
178
136 /**
179 /**
180 * Specialization of PlottablesUpdater for spectrograms
181 * @sa SpectrogramSeries
182 */
183 template <typename T>
184 struct PlottablesUpdater<T,
185 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
186 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
187 {
188 double min, max;
189 /// @todo ALX: use iterators here
190 std::tie(min, max) = dataSeries.yAxis().bounds();
191
192 if (!std::isnan(min) && !std::isnan(max)) {
193 plot.yAxis->setRange(QCPRange{min, max});
194 }
195 }
196
197 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
198 bool rescaleAxes)
199 {
200 if (plottables.empty()) {
201 qCDebug(LOG_VisualizationGraphHelper())
202 << QObject::tr("Can't update spectrogram: no colormap has been associated");
203 return;
204 }
205
206 // Gets the colormap to update (normally there is only one colormap)
207 Q_ASSERT(plottables.size() == 1);
208 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
209 Q_ASSERT(colormap != nullptr);
210
211 dataSeries.lockRead();
212
213 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
214 /// @todo ALX: use iterators here
215 auto yAxis = dataSeries.yAxis();
216
217 // Gets properties of x-axis and y-axis to set size and range of the colormap
218 auto nbX = std::distance(its.first, its.second);
219 auto xMin = nbX != 0 ? its.first->x() : 0.;
220 auto xMax = nbX != 0 ? (its.second - 1)->x() : 0.;
221
222 auto nbY = yAxis.size();
223 auto yMin = 0., yMax = 0.;
224 if (nbY != 0) {
225 std::tie(yMin, yMax) = yAxis.bounds();
226 }
227
228 colormap->data()->setSize(nbX, nbY);
229 colormap->data()->setRange(QCPRange{xMin, xMax}, QCPRange{yMin, yMax});
230
231 // Sets values
232 auto xIndex = 0;
233 for (auto it = its.first; it != its.second; ++it, ++xIndex) {
234 for (auto yIndex = 0; yIndex < nbY; ++yIndex) {
235 colormap->data()->setCell(xIndex, yIndex, it->value(yIndex));
236 }
237 }
238
239 dataSeries.unlock();
240
241 // Rescales axes
242 auto plot = colormap->parentPlot();
243
244 if (rescaleAxes) {
245 plot->rescaleAxes();
246 }
247
248 plot->replot();
249 }
250 };
251
252 /**
137 * Helper used to create/update plottables
253 * Helper used to create/update plottables
138 */
254 */
139 struct IPlottablesHelper {
255 struct IPlottablesHelper {
140 virtual ~IPlottablesHelper() noexcept = default;
256 virtual ~IPlottablesHelper() noexcept = default;
141 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
257 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
258 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
142 virtual void update(PlottablesMap &plottables, const SqpRange &range,
259 virtual void update(PlottablesMap &plottables, const SqpRange &range,
143 bool rescaleAxes = false) const = 0;
260 bool rescaleAxes = false) const = 0;
144 };
261 };
145
262
146 /**
263 /**
147 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
264 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
148 * @tparam T the data series' type
265 * @tparam T the data series' type
149 */
266 */
150 template <typename T>
267 template <typename T>
151 struct PlottablesHelper : public IPlottablesHelper {
268 struct PlottablesHelper : public IPlottablesHelper {
152 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
269 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
153
270
154 PlottablesMap create(QCustomPlot &plot) const override
271 PlottablesMap create(QCustomPlot &plot) const override
155 {
272 {
156 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
273 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
157 }
274 }
158
275
159 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
276 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
160 {
277 {
161 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
278 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
162 }
279 }
163
280
281 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
282 {
283 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
284 }
285
164 T &m_DataSeries;
286 T &m_DataSeries;
165 };
287 };
166
288
167 /// Creates IPlottablesHelper according to a data series
289 /// Creates IPlottablesHelper according to a data series
168 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
290 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
169 {
291 {
170 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
292 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
171 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
293 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
172 }
294 }
295 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
296 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
297 }
173 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
298 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
174 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
299 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
175 }
300 }
176 else {
301 else {
177 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
302 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
178 }
303 }
179 }
304 }
180
305
181 } // namespace
306 } // namespace
182
307
183 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
308 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
184 QCustomPlot &plot) noexcept
309 QCustomPlot &plot) noexcept
185 {
310 {
186 if (variable) {
311 if (variable) {
187 auto helper = createHelper(variable->dataSeries());
312 auto helper = createHelper(variable->dataSeries());
188 auto plottables = helper->create(plot);
313 auto plottables = helper->create(plot);
189 return plottables;
314 return plottables;
190 }
315 }
191 else {
316 else {
192 qCDebug(LOG_VisualizationGraphHelper())
317 qCDebug(LOG_VisualizationGraphHelper())
193 << QObject::tr("Can't create graph plottables : the variable is null");
318 << QObject::tr("Can't create graph plottables : the variable is null");
194 return PlottablesMap{};
319 return PlottablesMap{};
195 }
320 }
196 }
321 }
197
322
323 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
324 QCustomPlot &plot) noexcept
325 {
326 if (variable) {
327 auto helper = createHelper(variable->dataSeries());
328 helper->setYAxisRange(variable->range(), plot);
329 }
330 else {
331 qCDebug(LOG_VisualizationGraphHelper())
332 << QObject::tr("Can't set y-axis range of plot: the variable is null");
333 }
334 }
335
198 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
336 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
199 std::shared_ptr<IDataSeries> dataSeries,
337 std::shared_ptr<IDataSeries> dataSeries,
200 const SqpRange &dateTime)
338 const SqpRange &dateTime)
201 {
339 {
202 auto helper = createHelper(dataSeries);
340 auto helper = createHelper(dataSeries);
203 helper->update(plottables, dateTime);
341 helper->update(plottables, dateTime);
204 }
342 }
@@ -1,401 +1,405
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationDefs.h"
3 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationGraphHelper.h"
4 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphRenderingDelegate.h"
5 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationZoneWidget.h"
6 #include "Visualization/VisualizationZoneWidget.h"
7 #include "ui_VisualizationGraphWidget.h"
7 #include "ui_VisualizationGraphWidget.h"
8
8
9 #include <Common/MimeTypesDef.h>
9 #include <Common/MimeTypesDef.h>
10 #include <Data/ArrayData.h>
10 #include <Data/ArrayData.h>
11 #include <Data/IDataSeries.h>
11 #include <Data/IDataSeries.h>
12 #include <DragAndDrop/DragDropHelper.h>
12 #include <DragAndDrop/DragDropHelper.h>
13 #include <Settings/SqpSettingsDefs.h>
13 #include <Settings/SqpSettingsDefs.h>
14 #include <SqpApplication.h>
14 #include <SqpApplication.h>
15 #include <Time/TimeController.h>
15 #include <Time/TimeController.h>
16 #include <Variable/Variable.h>
16 #include <Variable/Variable.h>
17 #include <Variable/VariableController.h>
17 #include <Variable/VariableController.h>
18
18
19 #include <unordered_map>
19 #include <unordered_map>
20
20
21 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
21 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
22
22
23 namespace {
23 namespace {
24
24
25 /// Key pressed to enable zoom on horizontal axis
25 /// Key pressed to enable zoom on horizontal axis
26 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
26 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
27
27
28 /// Key pressed to enable zoom on vertical axis
28 /// Key pressed to enable zoom on vertical axis
29 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
29 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
30
30
31 } // namespace
31 } // namespace
32
32
33 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
33 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
34
34
35 explicit VisualizationGraphWidgetPrivate(const QString &name)
35 explicit VisualizationGraphWidgetPrivate(const QString &name)
36 : m_Name{name},
36 : m_Name{name},
37 m_DoAcquisition{true},
37 m_DoAcquisition{true},
38 m_IsCalibration{false},
38 m_IsCalibration{false},
39 m_RenderingDelegate{nullptr}
39 m_RenderingDelegate{nullptr}
40 {
40 {
41 }
41 }
42
42
43 QString m_Name;
43 QString m_Name;
44 // 1 variable -> n qcpplot
44 // 1 variable -> n qcpplot
45 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
45 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
46 bool m_DoAcquisition;
46 bool m_DoAcquisition;
47 bool m_IsCalibration;
47 bool m_IsCalibration;
48 QCPItemTracer *m_TextTracer;
49 /// Delegate used to attach rendering features to the plot
48 /// Delegate used to attach rendering features to the plot
50 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
49 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
51 };
50 };
52
51
53 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
52 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
54 : VisualizationDragWidget{parent},
53 : VisualizationDragWidget{parent},
55 ui{new Ui::VisualizationGraphWidget},
54 ui{new Ui::VisualizationGraphWidget},
56 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
55 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
57 {
56 {
58 ui->setupUi(this);
57 ui->setupUi(this);
59
58
60 // 'Close' options : widget is deleted when closed
59 // 'Close' options : widget is deleted when closed
61 setAttribute(Qt::WA_DeleteOnClose);
60 setAttribute(Qt::WA_DeleteOnClose);
62
61
63 // Set qcpplot properties :
62 // Set qcpplot properties :
64 // - Drag (on x-axis) and zoom are enabled
63 // - Drag (on x-axis) and zoom are enabled
65 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
64 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
66 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
65 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
67 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
66 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
68
67
69 // The delegate must be initialized after the ui as it uses the plot
68 // The delegate must be initialized after the ui as it uses the plot
70 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
69 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
71
70
72 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
71 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
73 connect(ui->widget, &QCustomPlot::mouseRelease, this,
72 connect(ui->widget, &QCustomPlot::mouseRelease, this,
74 &VisualizationGraphWidget::onMouseRelease);
73 &VisualizationGraphWidget::onMouseRelease);
75 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
74 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
76 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
75 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
77 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
76 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
78 &QCPAxis::rangeChanged),
77 &QCPAxis::rangeChanged),
79 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
78 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
80
79
81 // Activates menu when right clicking on the graph
80 // Activates menu when right clicking on the graph
82 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
81 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
83 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
82 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
84 &VisualizationGraphWidget::onGraphMenuRequested);
83 &VisualizationGraphWidget::onGraphMenuRequested);
85
84
86 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
85 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
87 &VariableController::onRequestDataLoading);
86 &VariableController::onRequestDataLoading);
88
87
89 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
88 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
90 &VisualizationGraphWidget::onUpdateVarDisplaying);
89 &VisualizationGraphWidget::onUpdateVarDisplaying);
91 }
90 }
92
91
93
92
94 VisualizationGraphWidget::~VisualizationGraphWidget()
93 VisualizationGraphWidget::~VisualizationGraphWidget()
95 {
94 {
96 delete ui;
95 delete ui;
97 }
96 }
98
97
99 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
98 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
100 {
99 {
101 auto parent = parentWidget();
100 auto parent = parentWidget();
102 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
101 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
103 parent = parent->parentWidget();
102 parent = parent->parentWidget();
104 }
103 }
105
104
106 return qobject_cast<VisualizationZoneWidget *>(parent);
105 return qobject_cast<VisualizationZoneWidget *>(parent);
107 }
106 }
108
107
109 void VisualizationGraphWidget::enableAcquisition(bool enable)
108 void VisualizationGraphWidget::enableAcquisition(bool enable)
110 {
109 {
111 impl->m_DoAcquisition = enable;
110 impl->m_DoAcquisition = enable;
112 }
111 }
113
112
114 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
113 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
115 {
114 {
116 // Uses delegate to create the qcpplot components according to the variable
115 // Uses delegate to create the qcpplot components according to the variable
117 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
116 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
118 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
117 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
119
118
120 // Set axes properties according to the units of the data series
119 // Set axes properties according to the units of the data series
121 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
120 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
122 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
121 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
123 auto xAxisUnit = Unit{};
122 auto xAxisUnit = Unit{};
124 auto valuesUnit = Unit{};
123 auto valuesUnit = Unit{};
125
124
126 if (auto dataSeries = variable->dataSeries()) {
125 if (auto dataSeries = variable->dataSeries()) {
127 dataSeries->lockRead();
126 dataSeries->lockRead();
128 xAxisUnit = dataSeries->xAxisUnit();
127 xAxisUnit = dataSeries->xAxisUnit();
129 valuesUnit = dataSeries->valuesUnit();
128 valuesUnit = dataSeries->valuesUnit();
130 dataSeries->unlock();
129 dataSeries->unlock();
131 }
130 }
132 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
131 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
133
132
134 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
133 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
135
134
136 this->enableAcquisition(false);
135 this->enableAcquisition(false);
137 this->setGraphRange(range);
136 this->setGraphRange(range);
138 this->enableAcquisition(true);
137 this->enableAcquisition(true);
139
138
140 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
139 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
141
140
142 emit variableAdded(variable);
141 emit variableAdded(variable);
143 }
142 }
144
143
145 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
144 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
146 {
145 {
147 // Each component associated to the variable :
146 // Each component associated to the variable :
148 // - is removed from qcpplot (which deletes it)
147 // - is removed from qcpplot (which deletes it)
149 // - is no longer referenced in the map
148 // - is no longer referenced in the map
150 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
149 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
151 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
150 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
152 emit variableAboutToBeRemoved(variable);
151 emit variableAboutToBeRemoved(variable);
153
152
154 auto &plottablesMap = variableIt->second;
153 auto &plottablesMap = variableIt->second;
155
154
156 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
155 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
157 plottableIt != plottableEnd;) {
156 plottableIt != plottableEnd;) {
158 ui->widget->removePlottable(plottableIt->second);
157 ui->widget->removePlottable(plottableIt->second);
159 plottableIt = plottablesMap.erase(plottableIt);
158 plottableIt = plottablesMap.erase(plottableIt);
160 }
159 }
161
160
162 impl->m_VariableToPlotMultiMap.erase(variableIt);
161 impl->m_VariableToPlotMultiMap.erase(variableIt);
163 }
162 }
164
163
165 // Updates graph
164 // Updates graph
166 ui->widget->replot();
165 ui->widget->replot();
167 }
166 }
168
167
169 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
168 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
170 {
169 {
171 auto variables = QList<std::shared_ptr<Variable> >{};
170 auto variables = QList<std::shared_ptr<Variable> >{};
172 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
171 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
173 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
172 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
174 variables << it->first;
173 variables << it->first;
175 }
174 }
176
175
177 return variables;
176 return variables;
178 }
177 }
179
178
180 void VisualizationGraphWidget::setYRange(const SqpRange &range)
179 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
181 {
180 {
182 ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd);
181 if (!variable) {
182 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
183 return;
184 }
185
186 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
183 }
187 }
184
188
185 SqpRange VisualizationGraphWidget::graphRange() const noexcept
189 SqpRange VisualizationGraphWidget::graphRange() const noexcept
186 {
190 {
187 auto graphRange = ui->widget->xAxis->range();
191 auto graphRange = ui->widget->xAxis->range();
188 return SqpRange{graphRange.lower, graphRange.upper};
192 return SqpRange{graphRange.lower, graphRange.upper};
189 }
193 }
190
194
191 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
195 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
192 {
196 {
193 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
197 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
194 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
198 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
195 ui->widget->replot();
199 ui->widget->replot();
196 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
200 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
197 }
201 }
198
202
199 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
203 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
200 {
204 {
201 if (visitor) {
205 if (visitor) {
202 visitor->visit(this);
206 visitor->visit(this);
203 }
207 }
204 else {
208 else {
205 qCCritical(LOG_VisualizationGraphWidget())
209 qCCritical(LOG_VisualizationGraphWidget())
206 << tr("Can't visit widget : the visitor is null");
210 << tr("Can't visit widget : the visitor is null");
207 }
211 }
208 }
212 }
209
213
210 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
214 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
211 {
215 {
212 /// @todo : for the moment, a graph can always accomodate a variable
216 /// @todo : for the moment, a graph can always accomodate a variable
213 Q_UNUSED(variable);
217 Q_UNUSED(variable);
214 return true;
218 return true;
215 }
219 }
216
220
217 bool VisualizationGraphWidget::contains(const Variable &variable) const
221 bool VisualizationGraphWidget::contains(const Variable &variable) const
218 {
222 {
219 // Finds the variable among the keys of the map
223 // Finds the variable among the keys of the map
220 auto variablePtr = &variable;
224 auto variablePtr = &variable;
221 auto findVariable
225 auto findVariable
222 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
226 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
223
227
224 auto end = impl->m_VariableToPlotMultiMap.cend();
228 auto end = impl->m_VariableToPlotMultiMap.cend();
225 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
229 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
226 return it != end;
230 return it != end;
227 }
231 }
228
232
229 QString VisualizationGraphWidget::name() const
233 QString VisualizationGraphWidget::name() const
230 {
234 {
231 return impl->m_Name;
235 return impl->m_Name;
232 }
236 }
233
237
234 QMimeData *VisualizationGraphWidget::mimeData() const
238 QMimeData *VisualizationGraphWidget::mimeData() const
235 {
239 {
236 auto mimeData = new QMimeData;
240 auto mimeData = new QMimeData;
237 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
241 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
238
242
239 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
243 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
240 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
244 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
241
245
242 return mimeData;
246 return mimeData;
243 }
247 }
244
248
245 bool VisualizationGraphWidget::isDragAllowed() const
249 bool VisualizationGraphWidget::isDragAllowed() const
246 {
250 {
247 return true;
251 return true;
248 }
252 }
249
253
250 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
254 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
251 {
255 {
252 if (highlighted) {
256 if (highlighted) {
253 plot().setBackground(QBrush(QColor("#BBD5EE")));
257 plot().setBackground(QBrush(QColor("#BBD5EE")));
254 }
258 }
255 else {
259 else {
256 plot().setBackground(QBrush(Qt::white));
260 plot().setBackground(QBrush(Qt::white));
257 }
261 }
258
262
259 plot().update();
263 plot().update();
260 }
264 }
261
265
262 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
266 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
263 {
267 {
264 Q_UNUSED(event);
268 Q_UNUSED(event);
265
269
266 // Prevents that all variables will be removed from graph when it will be closed
270 // Prevents that all variables will be removed from graph when it will be closed
267 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
271 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
268 emit variableAboutToBeRemoved(variableEntry.first);
272 emit variableAboutToBeRemoved(variableEntry.first);
269 }
273 }
270 }
274 }
271
275
272 void VisualizationGraphWidget::enterEvent(QEvent *event)
276 void VisualizationGraphWidget::enterEvent(QEvent *event)
273 {
277 {
274 Q_UNUSED(event);
278 Q_UNUSED(event);
275 impl->m_RenderingDelegate->showGraphOverlay(true);
279 impl->m_RenderingDelegate->showGraphOverlay(true);
276 }
280 }
277
281
278 void VisualizationGraphWidget::leaveEvent(QEvent *event)
282 void VisualizationGraphWidget::leaveEvent(QEvent *event)
279 {
283 {
280 Q_UNUSED(event);
284 Q_UNUSED(event);
281 impl->m_RenderingDelegate->showGraphOverlay(false);
285 impl->m_RenderingDelegate->showGraphOverlay(false);
282 }
286 }
283
287
284 QCustomPlot &VisualizationGraphWidget::plot() noexcept
288 QCustomPlot &VisualizationGraphWidget::plot() noexcept
285 {
289 {
286 return *ui->widget;
290 return *ui->widget;
287 }
291 }
288
292
289 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
293 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
290 {
294 {
291 QMenu graphMenu{};
295 QMenu graphMenu{};
292
296
293 // Iterates on variables (unique keys)
297 // Iterates on variables (unique keys)
294 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
298 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
295 end = impl->m_VariableToPlotMultiMap.cend();
299 end = impl->m_VariableToPlotMultiMap.cend();
296 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
300 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
297 // 'Remove variable' action
301 // 'Remove variable' action
298 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
302 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
299 [ this, var = it->first ]() { removeVariable(var); });
303 [ this, var = it->first ]() { removeVariable(var); });
300 }
304 }
301
305
302 if (!graphMenu.isEmpty()) {
306 if (!graphMenu.isEmpty()) {
303 graphMenu.exec(QCursor::pos());
307 graphMenu.exec(QCursor::pos());
304 }
308 }
305 }
309 }
306
310
307 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
311 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
308 {
312 {
309 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
313 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
310 << QThread::currentThread()->objectName() << "DoAcqui"
314 << QThread::currentThread()->objectName() << "DoAcqui"
311 << impl->m_DoAcquisition;
315 << impl->m_DoAcquisition;
312
316
313 auto graphRange = SqpRange{t1.lower, t1.upper};
317 auto graphRange = SqpRange{t1.lower, t1.upper};
314 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
318 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
315
319
316 if (impl->m_DoAcquisition) {
320 if (impl->m_DoAcquisition) {
317 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
321 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
318
322
319 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
323 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
320 end = impl->m_VariableToPlotMultiMap.end();
324 end = impl->m_VariableToPlotMultiMap.end();
321 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
325 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
322 variableUnderGraphVector.push_back(it->first);
326 variableUnderGraphVector.push_back(it->first);
323 }
327 }
324 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
328 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
325 !impl->m_IsCalibration);
329 !impl->m_IsCalibration);
326
330
327 if (!impl->m_IsCalibration) {
331 if (!impl->m_IsCalibration) {
328 qCDebug(LOG_VisualizationGraphWidget())
332 qCDebug(LOG_VisualizationGraphWidget())
329 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
333 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
330 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
334 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
331 emit synchronize(graphRange, oldGraphRange);
335 emit synchronize(graphRange, oldGraphRange);
332 }
336 }
333 }
337 }
334 }
338 }
335
339
336 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
340 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
337 {
341 {
338 // Handles plot rendering when mouse is moving
342 // Handles plot rendering when mouse is moving
339 impl->m_RenderingDelegate->onMouseMove(event);
343 impl->m_RenderingDelegate->onMouseMove(event);
340
344
341 VisualizationDragWidget::mouseMoveEvent(event);
345 VisualizationDragWidget::mouseMoveEvent(event);
342 }
346 }
343
347
344 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
348 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
345 {
349 {
346 auto zoomOrientations = QFlags<Qt::Orientation>{};
350 auto zoomOrientations = QFlags<Qt::Orientation>{};
347
351
348 // Lambda that enables a zoom orientation if the key modifier related to this orientation
352 // Lambda that enables a zoom orientation if the key modifier related to this orientation
349 // has
353 // has
350 // been pressed
354 // been pressed
351 auto enableOrientation
355 auto enableOrientation
352 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
356 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
353 auto orientationEnabled = event->modifiers().testFlag(modifier);
357 auto orientationEnabled = event->modifiers().testFlag(modifier);
354 zoomOrientations.setFlag(orientation, orientationEnabled);
358 zoomOrientations.setFlag(orientation, orientationEnabled);
355 };
359 };
356 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
360 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
357 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
361 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
358
362
359 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
363 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
360 }
364 }
361
365
362 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
366 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
363 {
367 {
364 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
368 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
365
369
366 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
370 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
367
371
368 VisualizationDragWidget::mousePressEvent(event);
372 VisualizationDragWidget::mousePressEvent(event);
369 }
373 }
370
374
371 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
375 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
372 {
376 {
373 impl->m_IsCalibration = false;
377 impl->m_IsCalibration = false;
374 }
378 }
375
379
376 void VisualizationGraphWidget::onDataCacheVariableUpdated()
380 void VisualizationGraphWidget::onDataCacheVariableUpdated()
377 {
381 {
378 auto graphRange = ui->widget->xAxis->range();
382 auto graphRange = ui->widget->xAxis->range();
379 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
383 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
380
384
381 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
385 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
382 auto variable = variableEntry.first;
386 auto variable = variableEntry.first;
383 qCDebug(LOG_VisualizationGraphWidget())
387 qCDebug(LOG_VisualizationGraphWidget())
384 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
388 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
385 qCDebug(LOG_VisualizationGraphWidget())
389 qCDebug(LOG_VisualizationGraphWidget())
386 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
390 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
387 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
391 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
388 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
392 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
389 variable->range());
393 variable->range());
390 }
394 }
391 }
395 }
392 }
396 }
393
397
394 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
398 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
395 const SqpRange &range)
399 const SqpRange &range)
396 {
400 {
397 auto it = impl->m_VariableToPlotMultiMap.find(variable);
401 auto it = impl->m_VariableToPlotMultiMap.find(variable);
398 if (it != impl->m_VariableToPlotMultiMap.end()) {
402 if (it != impl->m_VariableToPlotMultiMap.end()) {
399 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
403 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
400 }
404 }
401 }
405 }
@@ -1,516 +1,500
1 #include "Visualization/VisualizationZoneWidget.h"
1 #include "Visualization/VisualizationZoneWidget.h"
2
2
3 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 #include "Visualization/QCustomPlotSynchronizer.h"
4 #include "Visualization/QCustomPlotSynchronizer.h"
5 #include "Visualization/VisualizationGraphWidget.h"
5 #include "Visualization/VisualizationGraphWidget.h"
6 #include "Visualization/VisualizationWidget.h"
6 #include "Visualization/VisualizationWidget.h"
7 #include "ui_VisualizationZoneWidget.h"
7 #include "ui_VisualizationZoneWidget.h"
8
8
9 #include "Common/MimeTypesDef.h"
9 #include "Common/MimeTypesDef.h"
10 #include "Common/VisualizationDef.h"
10 #include "Common/VisualizationDef.h"
11
11
12 #include <Data/SqpRange.h>
12 #include <Data/SqpRange.h>
13 #include <Time/TimeController.h>
13 #include <Time/TimeController.h>
14 #include <Variable/Variable.h>
14 #include <Variable/Variable.h>
15 #include <Variable/VariableController.h>
15 #include <Variable/VariableController.h>
16
16
17 #include <Visualization/operations/FindVariableOperation.h>
17 #include <Visualization/operations/FindVariableOperation.h>
18
18
19 #include <DragAndDrop/DragDropHelper.h>
19 #include <DragAndDrop/DragDropHelper.h>
20 #include <QUuid>
20 #include <QUuid>
21 #include <SqpApplication.h>
21 #include <SqpApplication.h>
22 #include <cmath>
22 #include <cmath>
23
23
24 #include <QLayout>
24 #include <QLayout>
25
25
26 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
26 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
27
27
28 namespace {
28 namespace {
29
29
30
30
31 /// Generates a default name for a new graph, according to the number of graphs already displayed in
31 /// Generates a default name for a new graph, according to the number of graphs already displayed in
32 /// the zone
32 /// the zone
33 QString defaultGraphName(const QLayout &layout)
33 QString defaultGraphName(const QLayout &layout)
34 {
34 {
35 auto count = 0;
35 auto count = 0;
36 for (auto i = 0; i < layout.count(); ++i) {
36 for (auto i = 0; i < layout.count(); ++i) {
37 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
37 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
38 count++;
38 count++;
39 }
39 }
40 }
40 }
41
41
42 return QObject::tr("Graph %1").arg(count + 1);
42 return QObject::tr("Graph %1").arg(count + 1);
43 }
43 }
44
44
45 /**
45 /**
46 * Applies a function to all graphs of the zone represented by its layout
46 * Applies a function to all graphs of the zone represented by its layout
47 * @param layout the layout that contains graphs
47 * @param layout the layout that contains graphs
48 * @param fun the function to apply to each graph
48 * @param fun the function to apply to each graph
49 */
49 */
50 template <typename Fun>
50 template <typename Fun>
51 void processGraphs(QLayout &layout, Fun fun)
51 void processGraphs(QLayout &layout, Fun fun)
52 {
52 {
53 for (auto i = 0; i < layout.count(); ++i) {
53 for (auto i = 0; i < layout.count(); ++i) {
54 if (auto item = layout.itemAt(i)) {
54 if (auto item = layout.itemAt(i)) {
55 if (auto visualizationGraphWidget
55 if (auto visualizationGraphWidget
56 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
56 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
57 fun(*visualizationGraphWidget);
57 fun(*visualizationGraphWidget);
58 }
58 }
59 }
59 }
60 }
60 }
61 }
61 }
62
62
63 } // namespace
63 } // namespace
64
64
65 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
65 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
66
66
67 explicit VisualizationZoneWidgetPrivate()
67 explicit VisualizationZoneWidgetPrivate()
68 : m_SynchronisationGroupId{QUuid::createUuid()},
68 : m_SynchronisationGroupId{QUuid::createUuid()},
69 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
69 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
70 {
70 {
71 }
71 }
72 QUuid m_SynchronisationGroupId;
72 QUuid m_SynchronisationGroupId;
73 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
73 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
74
74
75 // Returns the first graph in the zone or nullptr if there is no graph inside
75 // Returns the first graph in the zone or nullptr if there is no graph inside
76 VisualizationGraphWidget *firstGraph(const VisualizationZoneWidget *zoneWidget) const
76 VisualizationGraphWidget *firstGraph(const VisualizationZoneWidget *zoneWidget) const
77 {
77 {
78 VisualizationGraphWidget *firstGraph = nullptr;
78 VisualizationGraphWidget *firstGraph = nullptr;
79 auto layout = zoneWidget->ui->dragDropContainer->layout();
79 auto layout = zoneWidget->ui->dragDropContainer->layout();
80 if (layout->count() > 0) {
80 if (layout->count() > 0) {
81 if (auto visualizationGraphWidget
81 if (auto visualizationGraphWidget
82 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
82 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
83 firstGraph = visualizationGraphWidget;
83 firstGraph = visualizationGraphWidget;
84 }
84 }
85 }
85 }
86
86
87 return firstGraph;
87 return firstGraph;
88 }
88 }
89
89
90 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
90 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
91 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
91 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
92 VisualizationZoneWidget *zoneWidget);
92 VisualizationZoneWidget *zoneWidget);
93 };
93 };
94
94
95 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
95 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
96 : VisualizationDragWidget{parent},
96 : VisualizationDragWidget{parent},
97 ui{new Ui::VisualizationZoneWidget},
97 ui{new Ui::VisualizationZoneWidget},
98 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
98 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
99 {
99 {
100 ui->setupUi(this);
100 ui->setupUi(this);
101
101
102 ui->zoneNameLabel->setText(name);
102 ui->zoneNameLabel->setText(name);
103
103
104 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Graph);
104 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Graph);
105 ui->dragDropContainer->addAcceptedMimeType(
105 ui->dragDropContainer->addAcceptedMimeType(
106 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
106 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
107 ui->dragDropContainer->addAcceptedMimeType(
107 ui->dragDropContainer->addAcceptedMimeType(
108 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
108 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
109 ui->dragDropContainer->addAcceptedMimeType(
109 ui->dragDropContainer->addAcceptedMimeType(
110 MIME_TYPE_TIME_RANGE, VisualizationDragDropContainer::DropBehavior::Merged);
110 MIME_TYPE_TIME_RANGE, VisualizationDragDropContainer::DropBehavior::Merged);
111 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
111 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
112 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
112 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
113 ui->dragDropContainer);
113 ui->dragDropContainer);
114 });
114 });
115
115
116 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
116 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
117 &VisualizationZoneWidget::dropMimeData);
117 &VisualizationZoneWidget::dropMimeData);
118 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
118 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
119 &VisualizationZoneWidget::dropMimeDataOnGraph);
119 &VisualizationZoneWidget::dropMimeDataOnGraph);
120
120
121 // 'Close' options : widget is deleted when closed
121 // 'Close' options : widget is deleted when closed
122 setAttribute(Qt::WA_DeleteOnClose);
122 setAttribute(Qt::WA_DeleteOnClose);
123 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
123 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
124 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
124 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
125
125
126 // Synchronisation id
126 // Synchronisation id
127 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
127 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
128 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
128 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
129 }
129 }
130
130
131 VisualizationZoneWidget::~VisualizationZoneWidget()
131 VisualizationZoneWidget::~VisualizationZoneWidget()
132 {
132 {
133 delete ui;
133 delete ui;
134 }
134 }
135
135
136 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
136 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
137 {
137 {
138 // Synchronize new graph with others in the zone
138 // Synchronize new graph with others in the zone
139 impl->m_Synchronizer->addGraph(*graphWidget);
139 impl->m_Synchronizer->addGraph(*graphWidget);
140
140
141 ui->dragDropContainer->addDragWidget(graphWidget);
141 ui->dragDropContainer->addDragWidget(graphWidget);
142 }
142 }
143
143
144 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
144 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
145 {
145 {
146 // Synchronize new graph with others in the zone
146 // Synchronize new graph with others in the zone
147 impl->m_Synchronizer->addGraph(*graphWidget);
147 impl->m_Synchronizer->addGraph(*graphWidget);
148
148
149 ui->dragDropContainer->insertDragWidget(index, graphWidget);
149 ui->dragDropContainer->insertDragWidget(index, graphWidget);
150 }
150 }
151
151
152 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
152 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
153 {
153 {
154 return createGraph(variable, -1);
154 return createGraph(variable, -1);
155 }
155 }
156
156
157 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
157 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
158 int index)
158 int index)
159 {
159 {
160 auto graphWidget
160 auto graphWidget
161 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
161 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
162
162
163
163
164 // Set graph properties
164 // Set graph properties
165 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
165 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
166 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
166 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
167
167
168
168
169 // Lambda to synchronize zone widget
169 // Lambda to synchronize zone widget
170 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
170 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
171 const SqpRange &oldGraphRange) {
171 const SqpRange &oldGraphRange) {
172
172
173 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
173 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
174 auto frameLayout = ui->dragDropContainer->layout();
174 auto frameLayout = ui->dragDropContainer->layout();
175 for (auto i = 0; i < frameLayout->count(); ++i) {
175 for (auto i = 0; i < frameLayout->count(); ++i) {
176 auto graphChild
176 auto graphChild
177 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
177 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
178 if (graphChild && (graphChild != graphWidget)) {
178 if (graphChild && (graphChild != graphWidget)) {
179
179
180 auto graphChildRange = graphChild->graphRange();
180 auto graphChildRange = graphChild->graphRange();
181 switch (zoomType) {
181 switch (zoomType) {
182 case AcquisitionZoomType::ZoomIn: {
182 case AcquisitionZoomType::ZoomIn: {
183 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
183 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
184 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
184 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
185 graphChildRange.m_TStart += deltaLeft;
185 graphChildRange.m_TStart += deltaLeft;
186 graphChildRange.m_TEnd -= deltaRight;
186 graphChildRange.m_TEnd -= deltaRight;
187 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
187 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
188 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
188 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
189 << deltaLeft;
189 << deltaLeft;
190 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
190 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
191 << deltaRight;
191 << deltaRight;
192 qCDebug(LOG_VisualizationZoneWidget())
192 qCDebug(LOG_VisualizationZoneWidget())
193 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
193 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
194
194
195 break;
195 break;
196 }
196 }
197
197
198 case AcquisitionZoomType::ZoomOut: {
198 case AcquisitionZoomType::ZoomOut: {
199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
200 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
200 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
201 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
201 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
202 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
202 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
203 << deltaLeft;
203 << deltaLeft;
204 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
204 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
205 << deltaRight;
205 << deltaRight;
206 qCDebug(LOG_VisualizationZoneWidget())
206 qCDebug(LOG_VisualizationZoneWidget())
207 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
207 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
208 graphChildRange.m_TStart -= deltaLeft;
208 graphChildRange.m_TStart -= deltaLeft;
209 graphChildRange.m_TEnd += deltaRight;
209 graphChildRange.m_TEnd += deltaRight;
210 break;
210 break;
211 }
211 }
212 case AcquisitionZoomType::PanRight: {
212 case AcquisitionZoomType::PanRight: {
213 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
213 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
214 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
214 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
215 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
215 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
216 graphChildRange.m_TStart += deltaLeft;
216 graphChildRange.m_TStart += deltaLeft;
217 graphChildRange.m_TEnd += deltaRight;
217 graphChildRange.m_TEnd += deltaRight;
218 qCDebug(LOG_VisualizationZoneWidget())
218 qCDebug(LOG_VisualizationZoneWidget())
219 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
219 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
220 break;
220 break;
221 }
221 }
222 case AcquisitionZoomType::PanLeft: {
222 case AcquisitionZoomType::PanLeft: {
223 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
223 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
224 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
224 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
225 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
225 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
226 graphChildRange.m_TStart -= deltaLeft;
226 graphChildRange.m_TStart -= deltaLeft;
227 graphChildRange.m_TEnd -= deltaRight;
227 graphChildRange.m_TEnd -= deltaRight;
228 break;
228 break;
229 }
229 }
230 case AcquisitionZoomType::Unknown: {
230 case AcquisitionZoomType::Unknown: {
231 qCDebug(LOG_VisualizationZoneWidget())
231 qCDebug(LOG_VisualizationZoneWidget())
232 << tr("Impossible to synchronize: zoom type unknown");
232 << tr("Impossible to synchronize: zoom type unknown");
233 break;
233 break;
234 }
234 }
235 default:
235 default:
236 qCCritical(LOG_VisualizationZoneWidget())
236 qCCritical(LOG_VisualizationZoneWidget())
237 << tr("Impossible to synchronize: zoom type not take into account");
237 << tr("Impossible to synchronize: zoom type not take into account");
238 // No action
238 // No action
239 break;
239 break;
240 }
240 }
241 graphChild->enableAcquisition(false);
241 graphChild->enableAcquisition(false);
242 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
242 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
243 << graphChild->graphRange();
243 << graphChild->graphRange();
244 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
244 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
245 << graphChildRange;
245 << graphChildRange;
246 qCDebug(LOG_VisualizationZoneWidget())
246 qCDebug(LOG_VisualizationZoneWidget())
247 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
247 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
248 graphChild->setGraphRange(graphChildRange);
248 graphChild->setGraphRange(graphChildRange);
249 graphChild->enableAcquisition(true);
249 graphChild->enableAcquisition(true);
250 }
250 }
251 }
251 }
252 };
252 };
253
253
254 // connection for synchronization
254 // connection for synchronization
255 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
255 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
256 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
256 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
257 &VisualizationZoneWidget::onVariableAdded);
257 &VisualizationZoneWidget::onVariableAdded);
258 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
258 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
259 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
259 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
260
260
261 auto range = SqpRange{};
261 auto range = SqpRange{};
262 if (auto firstGraph = impl->firstGraph(this)) {
262 if (auto firstGraph = impl->firstGraph(this)) {
263 // Case of a new graph in a existant zone
263 // Case of a new graph in a existant zone
264 range = firstGraph->graphRange();
264 range = firstGraph->graphRange();
265 }
265 }
266 else {
266 else {
267 // Case of a new graph as the first of the zone
267 // Case of a new graph as the first of the zone
268 range = variable->range();
268 range = variable->range();
269 }
269 }
270
270
271 this->insertGraph(index, graphWidget);
271 this->insertGraph(index, graphWidget);
272
272
273 graphWidget->addVariable(variable, range);
273 graphWidget->addVariable(variable, range);
274
274 graphWidget->setYRange(variable);
275 // get y using variable range
276 if (auto dataSeries = variable->dataSeries()) {
277 dataSeries->lockRead();
278 auto valuesBounds
279 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
280 auto end = dataSeries->cend();
281 if (valuesBounds.first != end && valuesBounds.second != end) {
282 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
283
284 auto minValue = rangeValue(valuesBounds.first->minValue());
285 auto maxValue = rangeValue(valuesBounds.second->maxValue());
286
287 graphWidget->setYRange(SqpRange{minValue, maxValue});
288 }
289 dataSeries->unlock();
290 }
291
275
292 return graphWidget;
276 return graphWidget;
293 }
277 }
294
278
295 VisualizationGraphWidget *
279 VisualizationGraphWidget *
296 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
280 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
297 {
281 {
298 if (variables.isEmpty()) {
282 if (variables.isEmpty()) {
299 return nullptr;
283 return nullptr;
300 }
284 }
301
285
302 auto graphWidget = createGraph(variables.first(), index);
286 auto graphWidget = createGraph(variables.first(), index);
303 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
287 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
304 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
288 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
305 }
289 }
306
290
307 return graphWidget;
291 return graphWidget;
308 }
292 }
309
293
310 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
294 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
311 {
295 {
312 if (visitor) {
296 if (visitor) {
313 visitor->visitEnter(this);
297 visitor->visitEnter(this);
314
298
315 // Apply visitor to graph children: widgets different from graphs are not visited (no
299 // Apply visitor to graph children: widgets different from graphs are not visited (no
316 // action)
300 // action)
317 processGraphs(
301 processGraphs(
318 *ui->dragDropContainer->layout(),
302 *ui->dragDropContainer->layout(),
319 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
303 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
320
304
321 visitor->visitLeave(this);
305 visitor->visitLeave(this);
322 }
306 }
323 else {
307 else {
324 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
308 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
325 }
309 }
326 }
310 }
327
311
328 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
312 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
329 {
313 {
330 // A tab can always accomodate a variable
314 // A tab can always accomodate a variable
331 Q_UNUSED(variable);
315 Q_UNUSED(variable);
332 return true;
316 return true;
333 }
317 }
334
318
335 bool VisualizationZoneWidget::contains(const Variable &variable) const
319 bool VisualizationZoneWidget::contains(const Variable &variable) const
336 {
320 {
337 Q_UNUSED(variable);
321 Q_UNUSED(variable);
338 return false;
322 return false;
339 }
323 }
340
324
341 QString VisualizationZoneWidget::name() const
325 QString VisualizationZoneWidget::name() const
342 {
326 {
343 return ui->zoneNameLabel->text();
327 return ui->zoneNameLabel->text();
344 }
328 }
345
329
346 QMimeData *VisualizationZoneWidget::mimeData() const
330 QMimeData *VisualizationZoneWidget::mimeData() const
347 {
331 {
348 auto mimeData = new QMimeData;
332 auto mimeData = new QMimeData;
349 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
333 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
350
334
351 return mimeData;
335 return mimeData;
352 }
336 }
353
337
354 bool VisualizationZoneWidget::isDragAllowed() const
338 bool VisualizationZoneWidget::isDragAllowed() const
355 {
339 {
356 return true;
340 return true;
357 }
341 }
358
342
359 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
343 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
360 {
344 {
361 // Closes graphs in the zone
345 // Closes graphs in the zone
362 processGraphs(*ui->dragDropContainer->layout(),
346 processGraphs(*ui->dragDropContainer->layout(),
363 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
347 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
364
348
365 // Delete synchronization group from variable controller
349 // Delete synchronization group from variable controller
366 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
350 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
367 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
351 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
368
352
369 QWidget::closeEvent(event);
353 QWidget::closeEvent(event);
370 }
354 }
371
355
372 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
356 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
373 {
357 {
374 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
358 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
375 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
359 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
376 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
360 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
377 }
361 }
378
362
379 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
363 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
380 {
364 {
381 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
365 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
382 Q_ARG(std::shared_ptr<Variable>, variable),
366 Q_ARG(std::shared_ptr<Variable>, variable),
383 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
367 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
384 }
368 }
385
369
386 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
370 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
387 {
371 {
388 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
372 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
389 impl->dropGraph(index, this);
373 impl->dropGraph(index, this);
390 }
374 }
391 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
375 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
392 auto variables = sqpApp->variableController().variablesForMimeData(
376 auto variables = sqpApp->variableController().variablesForMimeData(
393 mimeData->data(MIME_TYPE_VARIABLE_LIST));
377 mimeData->data(MIME_TYPE_VARIABLE_LIST));
394 impl->dropVariables(variables, index, this);
378 impl->dropVariables(variables, index, this);
395 }
379 }
396 else {
380 else {
397 qCWarning(LOG_VisualizationZoneWidget())
381 qCWarning(LOG_VisualizationZoneWidget())
398 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
382 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
399 }
383 }
400 }
384 }
401
385
402 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
386 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
403 const QMimeData *mimeData)
387 const QMimeData *mimeData)
404 {
388 {
405 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
389 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
406 if (!graphWidget) {
390 if (!graphWidget) {
407 qCWarning(LOG_VisualizationZoneWidget())
391 qCWarning(LOG_VisualizationZoneWidget())
408 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
392 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
409 "drop aborted");
393 "drop aborted");
410 Q_ASSERT(false);
394 Q_ASSERT(false);
411 return;
395 return;
412 }
396 }
413
397
414 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
398 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
415 auto variables = sqpApp->variableController().variablesForMimeData(
399 auto variables = sqpApp->variableController().variablesForMimeData(
416 mimeData->data(MIME_TYPE_VARIABLE_LIST));
400 mimeData->data(MIME_TYPE_VARIABLE_LIST));
417 for (const auto &var : variables) {
401 for (const auto &var : variables) {
418 graphWidget->addVariable(var, graphWidget->graphRange());
402 graphWidget->addVariable(var, graphWidget->graphRange());
419 }
403 }
420 }
404 }
421 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
405 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
422 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
406 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
423 graphWidget->setGraphRange(range);
407 graphWidget->setGraphRange(range);
424 }
408 }
425 else {
409 else {
426 qCWarning(LOG_VisualizationZoneWidget())
410 qCWarning(LOG_VisualizationZoneWidget())
427 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
411 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
428 }
412 }
429 }
413 }
430
414
431 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
415 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
432 int index, VisualizationZoneWidget *zoneWidget)
416 int index, VisualizationZoneWidget *zoneWidget)
433 {
417 {
434 auto &helper = sqpApp->dragDropHelper();
418 auto &helper = sqpApp->dragDropHelper();
435
419
436 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
420 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
437 if (!graphWidget) {
421 if (!graphWidget) {
438 qCWarning(LOG_VisualizationZoneWidget())
422 qCWarning(LOG_VisualizationZoneWidget())
439 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
423 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
440 "found or invalid.");
424 "found or invalid.");
441 Q_ASSERT(false);
425 Q_ASSERT(false);
442 return;
426 return;
443 }
427 }
444
428
445 auto parentDragDropContainer
429 auto parentDragDropContainer
446 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
430 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
447 if (!parentDragDropContainer) {
431 if (!parentDragDropContainer) {
448 qCWarning(LOG_VisualizationZoneWidget())
432 qCWarning(LOG_VisualizationZoneWidget())
449 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
433 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
450 "the dropped graph is not found.");
434 "the dropped graph is not found.");
451 Q_ASSERT(false);
435 Q_ASSERT(false);
452 return;
436 return;
453 }
437 }
454
438
455 const auto &variables = graphWidget->variables();
439 const auto &variables = graphWidget->variables();
456
440
457 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
441 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
458 // The drop didn't occur in the same zone
442 // The drop didn't occur in the same zone
459
443
460 // Abort the requests for the variables (if any)
444 // Abort the requests for the variables (if any)
461 // Commented, because it's not sure if it's needed or not
445 // Commented, because it's not sure if it's needed or not
462 // for (const auto& var : variables)
446 // for (const auto& var : variables)
463 //{
447 //{
464 // sqpApp->variableController().onAbortProgressRequested(var);
448 // sqpApp->variableController().onAbortProgressRequested(var);
465 //}
449 //}
466
450
467 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
451 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
468 auto nbGraph = parentDragDropContainer->countDragWidget();
452 auto nbGraph = parentDragDropContainer->countDragWidget();
469 if (nbGraph == 1) {
453 if (nbGraph == 1) {
470 // This is the only graph in the previous zone, close the zone
454 // This is the only graph in the previous zone, close the zone
471 previousParentZoneWidget->close();
455 previousParentZoneWidget->close();
472 }
456 }
473 else {
457 else {
474 // Close the graph
458 // Close the graph
475 graphWidget->close();
459 graphWidget->close();
476 }
460 }
477
461
478 // Creates the new graph in the zone
462 // Creates the new graph in the zone
479 zoneWidget->createGraph(variables, index);
463 zoneWidget->createGraph(variables, index);
480 }
464 }
481 else {
465 else {
482 // The drop occurred in the same zone or the graph is empty
466 // The drop occurred in the same zone or the graph is empty
483 // Simple move of the graph, no variable operation associated
467 // Simple move of the graph, no variable operation associated
484 parentDragDropContainer->layout()->removeWidget(graphWidget);
468 parentDragDropContainer->layout()->removeWidget(graphWidget);
485
469
486 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
470 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
487 // The graph is empty and dropped in a different zone.
471 // The graph is empty and dropped in a different zone.
488 // Take the range of the first graph in the zone (if existing).
472 // Take the range of the first graph in the zone (if existing).
489 auto layout = zoneWidget->ui->dragDropContainer->layout();
473 auto layout = zoneWidget->ui->dragDropContainer->layout();
490 if (layout->count() > 0) {
474 if (layout->count() > 0) {
491 if (auto visualizationGraphWidget
475 if (auto visualizationGraphWidget
492 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
476 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
493 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
477 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
494 }
478 }
495 }
479 }
496 }
480 }
497
481
498 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
482 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
499 }
483 }
500 }
484 }
501
485
502 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
486 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
503 const QList<std::shared_ptr<Variable> > &variables, int index,
487 const QList<std::shared_ptr<Variable> > &variables, int index,
504 VisualizationZoneWidget *zoneWidget)
488 VisualizationZoneWidget *zoneWidget)
505 {
489 {
506 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
490 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
507 // compatible variable here
491 // compatible variable here
508 if (variables.count() > 1) {
492 if (variables.count() > 1) {
509 qCWarning(LOG_VisualizationZoneWidget())
493 qCWarning(LOG_VisualizationZoneWidget())
510 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
494 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
511 "aborted.");
495 "aborted.");
512 return;
496 return;
513 }
497 }
514
498
515 zoneWidget->createGraph(variables, index);
499 zoneWidget->createGraph(variables, index);
516 }
500 }
General Comments 0
You need to be logged in to leave comments. Login now