##// END OF EJS Templates
Updates sqp color scale thresholds (2)...
Alexandre Leroux -
r1061:edadf7db2f1e
parent child
Show More
@@ -1,40 +1,42
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
3 3
4 4 #include <Common/spimpl.h>
5 5
6 6 #include <Visualization/VisualizationDefs.h>
7 7
8 8 class IDataSeries;
9 9 class QCustomPlot;
10 10 class QMouseEvent;
11 11 class Unit;
12 12 class VisualizationGraphWidget;
13 13
14 14 class VisualizationGraphRenderingDelegate {
15 15 public:
16 16 /// Ctor
17 17 /// @param graphWidget the graph widget to which the delegate is associated
18 18 /// @remarks the graph widget must exist throughout the life cycle of the delegate
19 19 explicit VisualizationGraphRenderingDelegate(VisualizationGraphWidget &graphWidget);
20 20
21 21 void onMouseDoubleClick(QMouseEvent *event) noexcept;
22 22 void onMouseMove(QMouseEvent *event) noexcept;
23 /// Updates rendering when data of plot changed
24 void onPlotUpdated() noexcept;
23 25
24 26 /// Sets properties of the plot's axes from the data series passed as parameter
25 27 void setAxesProperties(std::shared_ptr<IDataSeries> dataSeries) noexcept;
26 28
27 29 /// Sets rendering properties of the plottables passed as parameter, from the data series that
28 30 /// generated these
29 31 void setPlottablesProperties(std::shared_ptr<IDataSeries> dataSeries,
30 32 PlottablesMap &plottables) noexcept;
31 33
32 34 /// Shows or hides graph overlay (name, close button, etc.)
33 35 void showGraphOverlay(bool show) noexcept;
34 36
35 37 private:
36 38 class VisualizationGraphRenderingDelegatePrivate;
37 39 spimpl::unique_impl_ptr<VisualizationGraphRenderingDelegatePrivate> impl;
38 40 };
39 41
40 42 #endif // SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
@@ -1,344 +1,340
1 1 #include "Visualization/VisualizationGraphHelper.h"
2 2 #include "Visualization/qcustomplot.h"
3 3
4 4 #include <Data/DataSeriesUtils.h>
5 5 #include <Data/ScalarSeries.h>
6 6 #include <Data/SpectrogramSeries.h>
7 7 #include <Data/VectorSeries.h>
8 8
9 9 #include <Variable/Variable.h>
10 10
11 11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12 12
13 13 namespace {
14 14
15 15 class SqpDataContainer : public QCPGraphDataContainer {
16 16 public:
17 17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
18 18 };
19 19
20 20 /**
21 21 * Struct used to create plottables, depending on the type of the data series from which to create
22 22 * them
23 23 * @tparam T the data series' type
24 24 * @remarks Default implementation can't create plottables
25 25 */
26 26 template <typename T, typename Enabled = void>
27 27 struct PlottablesCreator {
28 28 static PlottablesMap createPlottables(T &, QCustomPlot &)
29 29 {
30 30 qCCritical(LOG_DataSeries())
31 31 << QObject::tr("Can't create plottables: unmanaged data series type");
32 32 return {};
33 33 }
34 34 };
35 35
36 36 /**
37 37 * Specialization of PlottablesCreator for scalars and vectors
38 38 * @sa ScalarSeries
39 39 * @sa VectorSeries
40 40 */
41 41 template <typename T>
42 42 struct PlottablesCreator<T,
43 43 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
44 44 or std::is_base_of<VectorSeries, T>::value> > {
45 45 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
46 46 {
47 47 PlottablesMap result{};
48 48
49 49 // Gets the number of components of the data series
50 50 dataSeries.lockRead();
51 51 auto componentCount = dataSeries.valuesData()->componentCount();
52 52 dataSeries.unlock();
53 53
54 54 // For each component of the data series, creates a QCPGraph to add to the plot
55 55 for (auto i = 0; i < componentCount; ++i) {
56 56 auto graph = plot.addGraph();
57 57 result.insert({i, graph});
58 58 }
59 59
60 60 plot.replot();
61 61
62 62 return result;
63 63 }
64 64 };
65 65
66 66 /**
67 67 * Specialization of PlottablesCreator for spectrograms
68 68 * @sa SpectrogramSeries
69 69 */
70 70 template <typename T>
71 71 struct PlottablesCreator<T,
72 72 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
73 73 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
74 74 {
75 75 PlottablesMap result{};
76 76 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
77 77
78 78 plot.replot();
79 79
80 80 return result;
81 81 }
82 82 };
83 83
84 84 /**
85 85 * Struct used to update plottables, depending on the type of the data series from which to update
86 86 * them
87 87 * @tparam T the data series' type
88 88 * @remarks Default implementation can't update plottables
89 89 */
90 90 template <typename T, typename Enabled = void>
91 91 struct PlottablesUpdater {
92 92 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
93 93 {
94 94 qCCritical(LOG_VisualizationGraphHelper())
95 95 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
96 96 }
97 97
98 98 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
99 99 {
100 100 qCCritical(LOG_VisualizationGraphHelper())
101 101 << QObject::tr("Can't update plottables: unmanaged data series type");
102 102 }
103 103 };
104 104
105 105 /**
106 106 * Specialization of PlottablesUpdater for scalars and vectors
107 107 * @sa ScalarSeries
108 108 * @sa VectorSeries
109 109 */
110 110 template <typename T>
111 111 struct PlottablesUpdater<T,
112 112 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
113 113 or std::is_base_of<VectorSeries, T>::value> > {
114 114 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
115 115 {
116 116 auto minValue = 0., maxValue = 0.;
117 117
118 118 dataSeries.lockRead();
119 119 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
120 120 auto end = dataSeries.cend();
121 121 if (valuesBounds.first != end && valuesBounds.second != end) {
122 122 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
123 123
124 124 minValue = rangeValue(valuesBounds.first->minValue());
125 125 maxValue = rangeValue(valuesBounds.second->maxValue());
126 126 }
127 127 dataSeries.unlock();
128 128
129 129 plot.yAxis->setRange(QCPRange{minValue, maxValue});
130 130 }
131 131
132 132 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
133 133 bool rescaleAxes)
134 134 {
135 135
136 136 // For each plottable to update, resets its data
137 137 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
138 138 for (const auto &plottable : plottables) {
139 139 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
140 140 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
141 141 graph->setData(dataContainer);
142 142
143 143 dataContainers.insert({plottable.first, dataContainer});
144 144 }
145 145 }
146 146 dataSeries.lockRead();
147 147
148 148 // - Gets the data of the series included in the current range
149 149 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
150 150 // and value data. The correct value is retrieved according to the index of the component
151 151 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
152 152 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
153 153 for (const auto &dataContainer : dataContainers) {
154 154 auto componentIndex = dataContainer.first;
155 155 dataContainer.second->appendGraphData(
156 156 QCPGraphData(it->x(), it->value(componentIndex)));
157 157 }
158 158 }
159 159
160 160 dataSeries.unlock();
161 161
162 162 if (!plottables.empty()) {
163 163 auto plot = plottables.begin()->second->parentPlot();
164 164
165 165 if (rescaleAxes) {
166 166 plot->rescaleAxes();
167 167 }
168
169 plot->replot();
170 168 }
171 169 }
172 170 };
173 171
174 172 /**
175 173 * Specialization of PlottablesUpdater for spectrograms
176 174 * @sa SpectrogramSeries
177 175 */
178 176 template <typename T>
179 177 struct PlottablesUpdater<T,
180 178 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
181 179 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
182 180 {
183 181 double min, max;
184 182 std::tie(min, max) = dataSeries.yBounds();
185 183
186 184 if (!std::isnan(min) && !std::isnan(max)) {
187 185 plot.yAxis->setRange(QCPRange{min, max});
188 186 }
189 187 }
190 188
191 189 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
192 190 bool rescaleAxes)
193 191 {
194 192 if (plottables.empty()) {
195 193 qCDebug(LOG_VisualizationGraphHelper())
196 194 << QObject::tr("Can't update spectrogram: no colormap has been associated");
197 195 return;
198 196 }
199 197
200 198 // Gets the colormap to update (normally there is only one colormap)
201 199 Q_ASSERT(plottables.size() == 1);
202 200 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
203 201 Q_ASSERT(colormap != nullptr);
204 202
205 203 dataSeries.lockRead();
206 204
207 205 // Processing spectrogram data for display in QCustomPlot
208 206 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
209 207
210 208 // Computes logarithmic y-axis resolution for the spectrogram
211 209 auto yData = its.first->y();
212 210 auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true);
213 211
214 212 // Generates mesh for colormap
215 213 auto mesh = DataSeriesUtils::regularMesh(
216 214 its.first, its.second, DataSeriesUtils::Resolution{dataSeries.xResolution()},
217 215 yResolution);
218 216
219 217 dataSeries.unlock();
220 218
221 219 colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY);
222 220 if (!mesh.isEmpty()) {
223 221 colormap->data()->setRange(
224 222 QCPRange{mesh.m_XMin, mesh.xMax()},
225 223 // y-axis range is converted to linear values
226 224 QCPRange{std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax())});
227 225
228 226 // Sets values
229 227 auto index = 0;
230 228 for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it, ++index) {
231 229 auto xIndex = index % mesh.m_NbX;
232 230 auto yIndex = index / mesh.m_NbX;
233 231
234 232 colormap->data()->setCell(xIndex, yIndex, *it);
235 233
236 234 // Makes the NaN values to be transparent in the colormap
237 235 if (std::isnan(*it)) {
238 236 colormap->data()->setAlpha(xIndex, yIndex, 0);
239 237 }
240 238 }
241 239 }
242 240
243 241 // Rescales axes
244 242 auto plot = colormap->parentPlot();
245 243
246 244 if (rescaleAxes) {
247 245 plot->rescaleAxes();
248 246 }
249
250 plot->replot();
251 247 }
252 248 };
253 249
254 250 /**
255 251 * Helper used to create/update plottables
256 252 */
257 253 struct IPlottablesHelper {
258 254 virtual ~IPlottablesHelper() noexcept = default;
259 255 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
260 256 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
261 257 virtual void update(PlottablesMap &plottables, const SqpRange &range,
262 258 bool rescaleAxes = false) const = 0;
263 259 };
264 260
265 261 /**
266 262 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
267 263 * @tparam T the data series' type
268 264 */
269 265 template <typename T>
270 266 struct PlottablesHelper : public IPlottablesHelper {
271 267 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
272 268
273 269 PlottablesMap create(QCustomPlot &plot) const override
274 270 {
275 271 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
276 272 }
277 273
278 274 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
279 275 {
280 276 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
281 277 }
282 278
283 279 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
284 280 {
285 281 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
286 282 }
287 283
288 284 T &m_DataSeries;
289 285 };
290 286
291 287 /// Creates IPlottablesHelper according to a data series
292 288 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
293 289 {
294 290 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
295 291 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
296 292 }
297 293 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
298 294 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
299 295 }
300 296 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
301 297 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
302 298 }
303 299 else {
304 300 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
305 301 }
306 302 }
307 303
308 304 } // namespace
309 305
310 306 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
311 307 QCustomPlot &plot) noexcept
312 308 {
313 309 if (variable) {
314 310 auto helper = createHelper(variable->dataSeries());
315 311 auto plottables = helper->create(plot);
316 312 return plottables;
317 313 }
318 314 else {
319 315 qCDebug(LOG_VisualizationGraphHelper())
320 316 << QObject::tr("Can't create graph plottables : the variable is null");
321 317 return PlottablesMap{};
322 318 }
323 319 }
324 320
325 321 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
326 322 QCustomPlot &plot) noexcept
327 323 {
328 324 if (variable) {
329 325 auto helper = createHelper(variable->dataSeries());
330 326 helper->setYAxisRange(variable->range(), plot);
331 327 }
332 328 else {
333 329 qCDebug(LOG_VisualizationGraphHelper())
334 330 << QObject::tr("Can't set y-axis range of plot: the variable is null");
335 331 }
336 332 }
337 333
338 334 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
339 335 std::shared_ptr<IDataSeries> dataSeries,
340 336 const SqpRange &dateTime)
341 337 {
342 338 auto helper = createHelper(dataSeries);
343 339 helper->update(plottables, dateTime);
344 340 }
@@ -1,258 +1,265
1 1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 2 #include "Visualization/AxisRenderingUtils.h"
3 3 #include "Visualization/ColorScaleEditor.h"
4 4 #include "Visualization/PlottablesRenderingUtils.h"
5 5 #include "Visualization/SqpColorScale.h"
6 6 #include "Visualization/VisualizationGraphWidget.h"
7 7 #include "Visualization/qcustomplot.h"
8 8
9 9 #include <Common/DateUtils.h>
10 10
11 11 #include <Data/IDataSeries.h>
12 12
13 13 #include <SqpApplication.h>
14 14
15 15 namespace {
16 16
17 17 /// Name of the axes layer in QCustomPlot
18 18 const auto AXES_LAYER = QStringLiteral("axes");
19 19
20 20 /// Icon used to show x-axis properties
21 21 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
22 22
23 23 /// Name of the overlay layer in QCustomPlot
24 24 const auto OVERLAY_LAYER = QStringLiteral("overlay");
25 25
26 26 /// Pixmap used to show x-axis properties
27 27 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
28 28
29 29 const auto TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
30 30
31 31 /// Offset used to shift the tooltip of the mouse
32 32 const auto TOOLTIP_OFFSET = QPoint{20, 20};
33 33
34 34 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
35 35 const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
36 36
37 37 /// Timeout after which the tooltip is displayed
38 38 const auto TOOLTIP_TIMEOUT = 500;
39 39
40 40 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
41 41 {
42 42 tracer.setInterpolating(false);
43 43 tracer.setStyle(QCPItemTracer::tsCircle);
44 44 tracer.setSize(3);
45 45 tracer.setPen(QPen(Qt::black));
46 46 tracer.setBrush(Qt::black);
47 47 }
48 48
49 49 QPixmap pixmap(const QString &iconPath) noexcept
50 50 {
51 51 return QIcon{iconPath}.pixmap(QSize{16, 16});
52 52 }
53 53
54 54 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
55 55 {
56 56 // Icon
57 57 pixmap.setPixmap(
58 58 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
59 59
60 60 // Position
61 61 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
62 62 pixmap.topLeft->setCoords(1, 0);
63 63 pixmap.setClipToAxisRect(false);
64 64
65 65 // Can be selected
66 66 pixmap.setSelectable(true);
67 67 }
68 68
69 69 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
70 70 {
71 71 // Icon
72 72 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
73 73
74 74 // Position
75 75 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
76 76 itemPixmap.topLeft->setCoords(0, 1);
77 77 itemPixmap.setClipToAxisRect(false);
78 78
79 79 // Can be selected
80 80 itemPixmap.setSelectable(true);
81 81 }
82 82
83 83 void initTitleTextStyle(QCPItemText &text) noexcept
84 84 {
85 85 // Font and background styles
86 86 text.setColor(Qt::gray);
87 87 text.setBrush(Qt::white);
88 88
89 89 // Position
90 90 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
91 91 text.position->setType(QCPItemPosition::ptAxisRectRatio);
92 92 text.position->setCoords(0.5, 0);
93 93 }
94 94
95 95 } // namespace
96 96
97 97 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
98 98 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
99 99 : m_Plot{graphWidget.plot()},
100 100 m_PointTracer{new QCPItemTracer{&m_Plot}},
101 101 m_TracerTimer{},
102 102 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
103 103 m_TitleText{new QCPItemText{&m_Plot}},
104 104 m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
105 105 m_ShowXAxis{true},
106 106 m_XAxisLabel{},
107 107 m_ColorScale{SqpColorScale{m_Plot}}
108 108 {
109 109 initPointTracerStyle(*m_PointTracer);
110 110
111 111 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
112 112 m_TracerTimer.setSingleShot(true);
113 113
114 114 // Inits "close button" in plot overlay
115 115 m_ClosePixmap->setLayer(OVERLAY_LAYER);
116 116 initClosePixmapStyle(*m_ClosePixmap);
117 117
118 118 // Connects pixmap selection to graph widget closing
119 119 QObject::connect(m_ClosePixmap, &QCPItemPixmap::selectionChanged,
120 120 [&graphWidget](bool selected) {
121 121 if (selected) {
122 122 graphWidget.close();
123 123 }
124 124 });
125 125
126 126 // Inits graph name in plot overlay
127 127 m_TitleText->setLayer(OVERLAY_LAYER);
128 128 m_TitleText->setText(graphWidget.name());
129 129 initTitleTextStyle(*m_TitleText);
130 130
131 131 // Inits "show x-axis button" in plot overlay
132 132 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
133 133 initXAxisPixmapStyle(*m_XAxisPixmap);
134 134
135 135 // Connects pixmap selection to graph x-axis showing/hiding
136 136 QObject::connect(m_XAxisPixmap, &QCPItemPixmap::selectionChanged, [this]() {
137 137 if (m_XAxisPixmap->selected()) {
138 138 // Changes the selection state and refreshes the x-axis
139 139 m_ShowXAxis = !m_ShowXAxis;
140 140 updateXAxisState();
141 141 m_Plot.layer(AXES_LAYER)->replot();
142 142
143 143 // Deselects the x-axis pixmap and updates icon
144 144 m_XAxisPixmap->setSelected(false);
145 145 m_XAxisPixmap->setPixmap(
146 146 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
147 147 m_Plot.layer(OVERLAY_LAYER)->replot();
148 148 }
149 149 });
150 150 }
151 151
152 152 /// Updates state of x-axis according to the current selection of x-axis pixmap
153 153 /// @remarks the method doesn't call plot refresh
154 154 void updateXAxisState() noexcept
155 155 {
156 156 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
157 157 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
158 158 }
159 159
160 160 QCustomPlot &m_Plot;
161 161 QCPItemTracer *m_PointTracer;
162 162 QTimer m_TracerTimer;
163 163 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
164 164 QCPItemText *m_TitleText; /// Graph's title
165 165 QCPItemPixmap *m_XAxisPixmap;
166 166 bool m_ShowXAxis; /// X-axis properties are shown or hidden
167 167 QString m_XAxisLabel;
168 168 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
169 169 };
170 170
171 171 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
172 172 VisualizationGraphWidget &graphWidget)
173 173 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
174 174 {
175 175 }
176 176
177 177 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) noexcept
178 178 {
179 179 // Opens color scale editor if color scale is double clicked
180 180 auto colorScale = dynamic_cast<QCPColorScale *>(impl->m_Plot.layoutElementAt(event->pos()));
181 181 if (impl->m_ColorScale.m_Scale == colorScale) {
182 182 if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) {
183 183 impl->m_Plot.replot();
184 184 }
185 185 }
186 186 }
187 187
188 188 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
189 189 {
190 190 // Cancels pending refresh
191 191 impl->m_TracerTimer.disconnect();
192 192
193 193 // Reinits tracers
194 194 impl->m_PointTracer->setGraph(nullptr);
195 195 impl->m_PointTracer->setVisible(false);
196 196 impl->m_Plot.replot();
197 197
198 198 // Gets the graph under the mouse position
199 199 auto eventPos = event->pos();
200 200 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
201 201 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
202 202 auto graphData = graph->data();
203 203
204 204 // Gets the closest data point to the mouse
205 205 auto graphDataIt = graphData->findBegin(mouseKey);
206 206 if (graphDataIt != graphData->constEnd()) {
207 207 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
208 208 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
209 209
210 210 // Displays point tracer
211 211 impl->m_PointTracer->setGraph(graph);
212 212 impl->m_PointTracer->setGraphKey(graphDataIt->key);
213 213 impl->m_PointTracer->setLayer(
214 214 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
215 215 impl->m_PointTracer->setVisible(true);
216 216 impl->m_Plot.replot();
217 217
218 218 // Starts timer to show tooltip after timeout
219 219 auto showTooltip = [ tooltip = TOOLTIP_FORMAT.arg(key, value), eventPos, this ]()
220 220 {
221 221 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
222 222 &impl->m_Plot, TOOLTIP_RECT);
223 223 };
224 224
225 225 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
226 226 impl->m_TracerTimer.start();
227 227 }
228 228 }
229 229 }
230 230
231 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
232 {
233 // Updates color scale bounds
234 impl->m_ColorScale.updateDataRange();
235 impl->m_Plot.replot();
236 }
237
231 238 void VisualizationGraphRenderingDelegate::setAxesProperties(
232 239 std::shared_ptr<IDataSeries> dataSeries) noexcept
233 240 {
234 241 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
235 242 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
236 243
237 244 auto axisHelper = IAxisHelperFactory::create(dataSeries);
238 245 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
239 246
240 247 // Updates x-axis state
241 248 impl->updateXAxisState();
242 249
243 250 impl->m_Plot.layer(AXES_LAYER)->replot();
244 251 }
245 252
246 253 void VisualizationGraphRenderingDelegate::setPlottablesProperties(
247 254 std::shared_ptr<IDataSeries> dataSeries, PlottablesMap &plottables) noexcept
248 255 {
249 256 auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries);
250 257 plottablesHelper->setProperties(plottables);
251 258 }
252 259
253 260 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
254 261 {
255 262 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
256 263 overlay->setVisible(show);
257 264 overlay->replot();
258 265 }
@@ -1,604 +1,612
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 7 #include "Visualization/VisualizationZoneWidget.h"
8 8 #include "ui_VisualizationGraphWidget.h"
9 9
10 10 #include <Common/MimeTypesDef.h>
11 11 #include <Data/ArrayData.h>
12 12 #include <Data/IDataSeries.h>
13 13 #include <DragAndDrop/DragDropHelper.h>
14 14 #include <Settings/SqpSettingsDefs.h>
15 15 #include <SqpApplication.h>
16 16 #include <Time/TimeController.h>
17 17 #include <Variable/Variable.h>
18 18 #include <Variable/VariableController.h>
19 19
20 20 #include <unordered_map>
21 21
22 22 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
23 23
24 24 namespace {
25 25
26 26 /// Key pressed to enable zoom on horizontal axis
27 27 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
28 28
29 29 /// Key pressed to enable zoom on vertical axis
30 30 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
31 31
32 32 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
33 33 const auto PAN_SPEED = 5;
34 34
35 35 /// Key pressed to enable a calibration pan
36 36 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
37 37
38 38 /// Minimum size for the zoom box, in percentage of the axis range
39 39 const auto ZOOM_BOX_MIN_SIZE = 0.8;
40 40
41 41 /// Format of the dates appearing in the label of a cursor
42 42 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
43 43
44 44 } // namespace
45 45
46 46 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
47 47
48 48 explicit VisualizationGraphWidgetPrivate(const QString &name)
49 49 : m_Name{name},
50 50 m_DoAcquisition{true},
51 51 m_IsCalibration{false},
52 52 m_RenderingDelegate{nullptr}
53 53 {
54 54 }
55 55
56 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
57 const SqpRange &range)
58 {
59 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
60
61 // Prevents that data has changed to update rendering
62 m_RenderingDelegate->onPlotUpdated();
63 }
64
56 65 QString m_Name;
57 66 // 1 variable -> n qcpplot
58 67 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
59 68 bool m_DoAcquisition;
60 69 bool m_IsCalibration;
61 70 /// Delegate used to attach rendering features to the plot
62 71 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
63 72
64 73 QCPItemRect *m_DrawingRect = nullptr;
65 74 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
66 75 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
67 76
68 77 void configureDrawingRect()
69 78 {
70 79 if (m_DrawingRect) {
71 80 QPen p;
72 81 p.setWidth(2);
73 82 m_DrawingRect->setPen(p);
74 83 }
75 84 }
76 85
77 86 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
78 87 {
79 88 removeDrawingRect(plot);
80 89
81 90 auto axisPos = posToAxisPos(pos, plot);
82 91
83 92 m_DrawingRect = new QCPItemRect{&plot};
84 93 configureDrawingRect();
85 94
86 95 m_DrawingRect->topLeft->setCoords(axisPos);
87 96 m_DrawingRect->bottomRight->setCoords(axisPos);
88 97 }
89 98
90 99 void removeDrawingRect(QCustomPlot &plot)
91 100 {
92 101 if (m_DrawingRect) {
93 102 plot.removeItem(m_DrawingRect); // the item is deleted by QCustomPlot
94 103 m_DrawingRect = nullptr;
95 104 plot.replot(QCustomPlot::rpQueuedReplot);
96 105 }
97 106 }
98 107
99 108 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
100 109 {
101 110 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
102 111 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
103 112 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
104 113 }
105 114
106 115 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
107 116 {
108 117 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
109 118 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
110 119
111 120 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
112 121 }
113 122 };
114 123
115 124 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
116 125 : VisualizationDragWidget{parent},
117 126 ui{new Ui::VisualizationGraphWidget},
118 127 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
119 128 {
120 129 ui->setupUi(this);
121 130
122 131 // 'Close' options : widget is deleted when closed
123 132 setAttribute(Qt::WA_DeleteOnClose);
124 133
125 134 // Set qcpplot properties :
126 135 // - Drag (on x-axis) and zoom are enabled
127 136 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
128 137 ui->widget->setInteractions(QCP::iRangeZoom | QCP::iSelectItems);
129 138
130 139 // The delegate must be initialized after the ui as it uses the plot
131 140 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
132 141
133 142 // Init the cursors
134 143 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
135 144 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
136 145 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
137 146 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
138 147
139 148 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
140 149 connect(ui->widget, &QCustomPlot::mouseRelease, this,
141 150 &VisualizationGraphWidget::onMouseRelease);
142 151 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
143 152 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
144 153 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
145 154 &VisualizationGraphWidget::onMouseDoubleClick);
146 155 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
147 156 &QCPAxis::rangeChanged),
148 157 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
149 158
150 159 // Activates menu when right clicking on the graph
151 160 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
152 161 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
153 162 &VisualizationGraphWidget::onGraphMenuRequested);
154 163
155 164 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
156 165 &VariableController::onRequestDataLoading);
157 166
158 167 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
159 168 &VisualizationGraphWidget::onUpdateVarDisplaying);
160 169
161 170 #ifdef Q_OS_MAC
162 171 plot().setPlottingHint(QCP::phFastPolylines, true);
163 172 #endif
164 173 }
165 174
166 175
167 176 VisualizationGraphWidget::~VisualizationGraphWidget()
168 177 {
169 178 delete ui;
170 179 }
171 180
172 181 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
173 182 {
174 183 auto parent = parentWidget();
175 184 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
176 185 parent = parent->parentWidget();
177 186 }
178 187
179 188 return qobject_cast<VisualizationZoneWidget *>(parent);
180 189 }
181 190
182 191 void VisualizationGraphWidget::enableAcquisition(bool enable)
183 192 {
184 193 impl->m_DoAcquisition = enable;
185 194 }
186 195
187 196 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
188 197 {
189 198 // Uses delegate to create the qcpplot components according to the variable
190 199 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
191 200
192 201 if (auto dataSeries = variable->dataSeries()) {
193 202 // Set axes properties according to the units of the data series
194 203 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
195 204
196 205 // Sets rendering properties for the new plottables
197 206 // Warning: this method must be called after setAxesProperties(), as it can access to some
198 207 // axes properties that have to be initialized
199 208 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
200 209 }
201 210
202 211 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
203 212
204 213 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
205 214
206 215 this->enableAcquisition(false);
207 216 this->setGraphRange(range);
208 217 this->enableAcquisition(true);
209 218
210 219 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
211 220
212 221 emit variableAdded(variable);
213 222 }
214 223
215 224 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
216 225 {
217 226 // Each component associated to the variable :
218 227 // - is removed from qcpplot (which deletes it)
219 228 // - is no longer referenced in the map
220 229 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
221 230 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
222 231 emit variableAboutToBeRemoved(variable);
223 232
224 233 auto &plottablesMap = variableIt->second;
225 234
226 235 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
227 236 plottableIt != plottableEnd;) {
228 237 ui->widget->removePlottable(plottableIt->second);
229 238 plottableIt = plottablesMap.erase(plottableIt);
230 239 }
231 240
232 241 impl->m_VariableToPlotMultiMap.erase(variableIt);
233 242 }
234 243
235 244 // Updates graph
236 245 ui->widget->replot();
237 246 }
238 247
239 248 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
240 249 {
241 250 auto variables = QList<std::shared_ptr<Variable> >{};
242 251 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
243 252 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
244 253 variables << it->first;
245 254 }
246 255
247 256 return variables;
248 257 }
249 258
250 259 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
251 260 {
252 261 if (!variable) {
253 262 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
254 263 return;
255 264 }
256 265
257 266 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
258 267 }
259 268
260 269 SqpRange VisualizationGraphWidget::graphRange() const noexcept
261 270 {
262 271 auto graphRange = ui->widget->xAxis->range();
263 272 return SqpRange{graphRange.lower, graphRange.upper};
264 273 }
265 274
266 275 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
267 276 {
268 277 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
269 278 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
270 279 ui->widget->replot();
271 280 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
272 281 }
273 282
274 283 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
275 284 {
276 285 if (visitor) {
277 286 visitor->visit(this);
278 287 }
279 288 else {
280 289 qCCritical(LOG_VisualizationGraphWidget())
281 290 << tr("Can't visit widget : the visitor is null");
282 291 }
283 292 }
284 293
285 294 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
286 295 {
287 296 /// @todo : for the moment, a graph can always accomodate a variable
288 297 Q_UNUSED(variable);
289 298 return true;
290 299 }
291 300
292 301 bool VisualizationGraphWidget::contains(const Variable &variable) const
293 302 {
294 303 // Finds the variable among the keys of the map
295 304 auto variablePtr = &variable;
296 305 auto findVariable
297 306 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
298 307
299 308 auto end = impl->m_VariableToPlotMultiMap.cend();
300 309 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
301 310 return it != end;
302 311 }
303 312
304 313 QString VisualizationGraphWidget::name() const
305 314 {
306 315 return impl->m_Name;
307 316 }
308 317
309 318 QMimeData *VisualizationGraphWidget::mimeData() const
310 319 {
311 320 auto mimeData = new QMimeData;
312 321 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
313 322
314 323 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
315 324 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
316 325
317 326 return mimeData;
318 327 }
319 328
320 329 bool VisualizationGraphWidget::isDragAllowed() const
321 330 {
322 331 return true;
323 332 }
324 333
325 334 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
326 335 {
327 336 if (highlighted) {
328 337 plot().setBackground(QBrush(QColor("#BBD5EE")));
329 338 }
330 339 else {
331 340 plot().setBackground(QBrush(Qt::white));
332 341 }
333 342
334 343 plot().update();
335 344 }
336 345
337 346 void VisualizationGraphWidget::addVerticalCursor(double time)
338 347 {
339 348 impl->m_VerticalCursor->setPosition(time);
340 349 impl->m_VerticalCursor->setVisible(true);
341 350
342 351 auto text
343 352 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
344 353 impl->m_VerticalCursor->setLabelText(text);
345 354 }
346 355
347 356 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
348 357 {
349 358 impl->m_VerticalCursor->setAbsolutePosition(position);
350 359 impl->m_VerticalCursor->setVisible(true);
351 360
352 361 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
353 362 auto text
354 363 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
355 364 impl->m_VerticalCursor->setLabelText(text);
356 365 }
357 366
358 367 void VisualizationGraphWidget::removeVerticalCursor()
359 368 {
360 369 impl->m_VerticalCursor->setVisible(false);
361 370 plot().replot(QCustomPlot::rpQueuedReplot);
362 371 }
363 372
364 373 void VisualizationGraphWidget::addHorizontalCursor(double value)
365 374 {
366 375 impl->m_HorizontalCursor->setPosition(value);
367 376 impl->m_HorizontalCursor->setVisible(true);
368 377 impl->m_HorizontalCursor->setLabelText(QString::number(value));
369 378 }
370 379
371 380 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
372 381 {
373 382 impl->m_HorizontalCursor->setAbsolutePosition(position);
374 383 impl->m_HorizontalCursor->setVisible(true);
375 384
376 385 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
377 386 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
378 387 }
379 388
380 389 void VisualizationGraphWidget::removeHorizontalCursor()
381 390 {
382 391 impl->m_HorizontalCursor->setVisible(false);
383 392 plot().replot(QCustomPlot::rpQueuedReplot);
384 393 }
385 394
386 395 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
387 396 {
388 397 Q_UNUSED(event);
389 398
390 399 // Prevents that all variables will be removed from graph when it will be closed
391 400 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
392 401 emit variableAboutToBeRemoved(variableEntry.first);
393 402 }
394 403 }
395 404
396 405 void VisualizationGraphWidget::enterEvent(QEvent *event)
397 406 {
398 407 Q_UNUSED(event);
399 408 impl->m_RenderingDelegate->showGraphOverlay(true);
400 409 }
401 410
402 411 void VisualizationGraphWidget::leaveEvent(QEvent *event)
403 412 {
404 413 Q_UNUSED(event);
405 414 impl->m_RenderingDelegate->showGraphOverlay(false);
406 415
407 416 if (auto parentZone = parentZoneWidget()) {
408 417 parentZone->notifyMouseLeaveGraph(this);
409 418 }
410 419 else {
411 420 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
412 421 }
413 422 }
414 423
415 424 QCustomPlot &VisualizationGraphWidget::plot() noexcept
416 425 {
417 426 return *ui->widget;
418 427 }
419 428
420 429 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
421 430 {
422 431 QMenu graphMenu{};
423 432
424 433 // Iterates on variables (unique keys)
425 434 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
426 435 end = impl->m_VariableToPlotMultiMap.cend();
427 436 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
428 437 // 'Remove variable' action
429 438 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
430 439 [ this, var = it->first ]() { removeVariable(var); });
431 440 }
432 441
433 442 if (!graphMenu.isEmpty()) {
434 443 graphMenu.exec(QCursor::pos());
435 444 }
436 445 }
437 446
438 447 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
439 448 {
440 449 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
441 450 << QThread::currentThread()->objectName() << "DoAcqui"
442 451 << impl->m_DoAcquisition;
443 452
444 453 auto graphRange = SqpRange{t1.lower, t1.upper};
445 454 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
446 455
447 456 if (impl->m_DoAcquisition) {
448 457 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
449 458
450 459 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
451 460 end = impl->m_VariableToPlotMultiMap.end();
452 461 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
453 462 variableUnderGraphVector.push_back(it->first);
454 463 }
455 464 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
456 465 !impl->m_IsCalibration);
457 466
458 467 if (!impl->m_IsCalibration) {
459 468 qCDebug(LOG_VisualizationGraphWidget())
460 469 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
461 470 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
462 471 emit synchronize(graphRange, oldGraphRange);
463 472 }
464 473 }
465 474
466 475 auto pos = mapFromGlobal(QCursor::pos());
467 476 auto axisPos = impl->posToAxisPos(pos, plot());
468 477 if (auto parentZone = parentZoneWidget()) {
469 478 if (impl->pointIsInAxisRect(axisPos, plot())) {
470 479 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
471 480 }
472 481 else {
473 482 parentZone->notifyMouseLeaveGraph(this);
474 483 }
475 484 }
476 485 else {
477 486 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
478 487 }
479 488 }
480 489
481 490 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
482 491 {
483 492 impl->m_RenderingDelegate->onMouseDoubleClick(event);
484 493 }
485 494
486 495 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
487 496 {
488 497 // Handles plot rendering when mouse is moving
489 498 impl->m_RenderingDelegate->onMouseMove(event);
490 499
491 500 auto axisPos = impl->posToAxisPos(event->pos(), plot());
492 501
493 502 if (impl->m_DrawingRect) {
494 503 impl->m_DrawingRect->bottomRight->setCoords(axisPos);
495 504 }
496 505
497 506 if (auto parentZone = parentZoneWidget()) {
498 507 if (impl->pointIsInAxisRect(axisPos, plot())) {
499 508 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
500 509 }
501 510 else {
502 511 parentZone->notifyMouseLeaveGraph(this);
503 512 }
504 513 }
505 514 else {
506 515 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
507 516 }
508 517
509 518 VisualizationDragWidget::mouseMoveEvent(event);
510 519 }
511 520
512 521 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
513 522 {
514 523 auto value = event->angleDelta().x() + event->angleDelta().y();
515 524 if (value != 0) {
516 525
517 526 auto direction = value > 0 ? 1.0 : -1.0;
518 527 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
519 528 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
520 529 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
521 530
522 531 auto zoomOrientations = QFlags<Qt::Orientation>{};
523 532 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
524 533 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
525 534
526 535 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
527 536
528 537 if (!isZoomX && !isZoomY) {
529 538 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
530 539 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
531 540
532 541 axis->setRange(axis->range() + diff);
533 542
534 543 if (plot().noAntialiasingOnDrag()) {
535 544 plot().setNotAntialiasedElements(QCP::aeAll);
536 545 }
537 546
538 547 plot().replot(QCustomPlot::rpQueuedReplot);
539 548 }
540 549 }
541 550 }
542 551
543 552 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
544 553 {
545 554 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
546 555 impl->startDrawingRect(event->pos(), plot());
547 556 }
548 557
549 558 VisualizationDragWidget::mousePressEvent(event);
550 559 }
551 560
552 561 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
553 562 {
554 563 if (impl->m_DrawingRect) {
555 564
556 565 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
557 566 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
558 567
559 568 auto newAxisXRange = QCPRange{impl->m_DrawingRect->topLeft->coords().x(),
560 569 impl->m_DrawingRect->bottomRight->coords().x()};
561 570
562 571 auto newAxisYRange = QCPRange{impl->m_DrawingRect->topLeft->coords().y(),
563 572 impl->m_DrawingRect->bottomRight->coords().y()};
564 573
565 574 impl->removeDrawingRect(plot());
566 575
567 576 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
568 577 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
569 578 axisX->setRange(newAxisXRange);
570 579 axisY->setRange(newAxisYRange);
571 580
572 581 plot().replot(QCustomPlot::rpQueuedReplot);
573 582 }
574 583 }
575 584
576 585 impl->m_IsCalibration = false;
577 586 }
578 587
579 588 void VisualizationGraphWidget::onDataCacheVariableUpdated()
580 589 {
581 590 auto graphRange = ui->widget->xAxis->range();
582 591 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
583 592
584 593 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
585 594 auto variable = variableEntry.first;
586 595 qCDebug(LOG_VisualizationGraphWidget())
587 596 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
588 597 qCDebug(LOG_VisualizationGraphWidget())
589 598 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
590 599 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
591 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
592 variable->range());
600 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
593 601 }
594 602 }
595 603 }
596 604
597 605 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
598 606 const SqpRange &range)
599 607 {
600 608 auto it = impl->m_VariableToPlotMultiMap.find(variable);
601 609 if (it != impl->m_VariableToPlotMultiMap.end()) {
602 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
610 impl->updateData(it->second, variable->dataSeries(), range);
603 611 }
604 612 }
General Comments 0
You need to be logged in to leave comments. Login now