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