##// END OF EJS Templates
Fix the close button on the graphs and multi selection in the same graph
trabillard -
r1094:f391b1d9fb19
parent child
Show More
@@ -1,333 +1,332
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 #include "Visualization/AxisRenderingUtils.h"
2 #include "Visualization/AxisRenderingUtils.h"
3 #include "Visualization/ColorScaleEditor.h"
3 #include "Visualization/ColorScaleEditor.h"
4 #include "Visualization/PlottablesRenderingUtils.h"
4 #include "Visualization/PlottablesRenderingUtils.h"
5 #include "Visualization/SqpColorScale.h"
5 #include "Visualization/SqpColorScale.h"
6 #include "Visualization/VisualizationGraphWidget.h"
6 #include "Visualization/VisualizationGraphWidget.h"
7 #include "Visualization/qcustomplot.h"
7 #include "Visualization/qcustomplot.h"
8
8
9 #include <Common/DateUtils.h>
9 #include <Common/DateUtils.h>
10
10
11 #include <Data/IDataSeries.h>
11 #include <Data/IDataSeries.h>
12
12
13 #include <SqpApplication.h>
13 #include <SqpApplication.h>
14
14
15 namespace {
15 namespace {
16
16
17 /// Name of the axes layer in QCustomPlot
17 /// Name of the axes layer in QCustomPlot
18 const auto AXES_LAYER = QStringLiteral("axes");
18 const auto AXES_LAYER = QStringLiteral("axes");
19
19
20 /// Icon used to show x-axis properties
20 /// Icon used to show x-axis properties
21 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
21 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
22
22
23 /// Name of the overlay layer in QCustomPlot
23 /// Name of the overlay layer in QCustomPlot
24 const auto OVERLAY_LAYER = QStringLiteral("overlay");
24 const auto OVERLAY_LAYER = QStringLiteral("overlay");
25
25
26 /// Pixmap used to show x-axis properties
26 /// Pixmap used to show x-axis properties
27 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
27 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
28
28
29 /// Tooltip format for graphs
29 /// Tooltip format for graphs
30 const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
30 const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
31
31
32 /// Tooltip format for colormaps
32 /// Tooltip format for colormaps
33 const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
33 const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
34
34
35 /// Offset used to shift the tooltip of the mouse
35 /// Offset used to shift the tooltip of the mouse
36 const auto TOOLTIP_OFFSET = QPoint{20, 20};
36 const auto TOOLTIP_OFFSET = QPoint{20, 20};
37
37
38 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
38 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
39 const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
39 const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
40
40
41 /// Timeout after which the tooltip is displayed
41 /// Timeout after which the tooltip is displayed
42 const auto TOOLTIP_TIMEOUT = 500;
42 const auto TOOLTIP_TIMEOUT = 500;
43
43
44 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
44 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
45 {
45 {
46 tracer.setInterpolating(false);
46 tracer.setInterpolating(false);
47 tracer.setStyle(QCPItemTracer::tsCircle);
47 tracer.setStyle(QCPItemTracer::tsCircle);
48 tracer.setSize(3);
48 tracer.setSize(3);
49 tracer.setPen(QPen(Qt::black));
49 tracer.setPen(QPen(Qt::black));
50 tracer.setBrush(Qt::black);
50 tracer.setBrush(Qt::black);
51 tracer.setSelectable(false);
51 tracer.setSelectable(false);
52 }
52 }
53
53
54 QPixmap pixmap(const QString &iconPath) noexcept
54 QPixmap pixmap(const QString &iconPath) noexcept
55 {
55 {
56 return QIcon{iconPath}.pixmap(QSize{16, 16});
56 return QIcon{iconPath}.pixmap(QSize{16, 16});
57 }
57 }
58
58
59 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
59 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
60 {
60 {
61 // Icon
61 // Icon
62 pixmap.setPixmap(
62 pixmap.setPixmap(
63 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
63 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
64
64
65 // Position
65 // Position
66 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
66 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
67 pixmap.topLeft->setCoords(1, 0);
67 pixmap.topLeft->setCoords(1, 0);
68 pixmap.setClipToAxisRect(false);
68 pixmap.setClipToAxisRect(false);
69
69
70 // Can be selected
70 // Can be selected
71 pixmap.setSelectable(true);
71 pixmap.setSelectable(true);
72 }
72 }
73
73
74 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
74 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
75 {
75 {
76 // Icon
76 // Icon
77 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
77 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
78
78
79 // Position
79 // Position
80 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
80 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
81 itemPixmap.topLeft->setCoords(0, 1);
81 itemPixmap.topLeft->setCoords(0, 1);
82 itemPixmap.setClipToAxisRect(false);
82 itemPixmap.setClipToAxisRect(false);
83
83
84 // Can be selected
84 // Can be selected
85 itemPixmap.setSelectable(true);
85 itemPixmap.setSelectable(true);
86 }
86 }
87
87
88 void initTitleTextStyle(QCPItemText &text) noexcept
88 void initTitleTextStyle(QCPItemText &text) noexcept
89 {
89 {
90 // Font and background styles
90 // Font and background styles
91 text.setColor(Qt::gray);
91 text.setColor(Qt::gray);
92 text.setBrush(Qt::white);
92 text.setBrush(Qt::white);
93
93
94 // Position
94 // Position
95 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
95 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
96 text.position->setType(QCPItemPosition::ptAxisRectRatio);
96 text.position->setType(QCPItemPosition::ptAxisRectRatio);
97 text.position->setCoords(0.5, 0);
97 text.position->setCoords(0.5, 0);
98 text.setSelectable(false);
98 text.setSelectable(false);
99 }
99 }
100
100
101 /**
101 /**
102 * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
102 * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
103 * This method handles the fact that a colormap axis can be logarithmic or linear.
103 * This method handles the fact that a colormap axis can be logarithmic or linear.
104 * @param colormap the colormap for which to calculate the index
104 * @param colormap the colormap for which to calculate the index
105 * @param coord the coord to convert to cell index
105 * @param coord the coord to convert to cell index
106 * @param xCoord calculates the x index if true, calculates y index if false
106 * @param xCoord calculates the x index if true, calculates y index if false
107 * @return the cell index
107 * @return the cell index
108 */
108 */
109 int colorMapCellIndex(const QCPColorMap &colormap, double coord, bool xCoord)
109 int colorMapCellIndex(const QCPColorMap &colormap, double coord, bool xCoord)
110 {
110 {
111 // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
111 // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
112 auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
112 auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
113 == QCPAxis::stLogarithmic;
113 == QCPAxis::stLogarithmic;
114
114
115 if (isLogarithmic) {
115 if (isLogarithmic) {
116 // For a logarithmic axis we can't use the conversion method of colormap, so we calculate
116 // For a logarithmic axis we can't use the conversion method of colormap, so we calculate
117 // the index manually based on the position of the coordinate on the axis
117 // the index manually based on the position of the coordinate on the axis
118
118
119 // Gets the axis range and the number of values between range bounds to calculate the step
119 // Gets the axis range and the number of values between range bounds to calculate the step
120 // between each value of the range
120 // between each value of the range
121 auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
121 auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
122 auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
122 auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
123 auto valueStep
123 auto valueStep
124 = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
124 = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
125
125
126 // According to the coord position, calculates the closest index in the range
126 // According to the coord position, calculates the closest index in the range
127 return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
127 return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
128 }
128 }
129 else {
129 else {
130 // For a linear axis, we use the conversion method of colormap
130 // For a linear axis, we use the conversion method of colormap
131 int index;
131 int index;
132 if (xCoord) {
132 if (xCoord) {
133 colormap.data()->coordToCell(coord, 0., &index, nullptr);
133 colormap.data()->coordToCell(coord, 0., &index, nullptr);
134 }
134 }
135 else {
135 else {
136 colormap.data()->coordToCell(0., coord, nullptr, &index);
136 colormap.data()->coordToCell(0., coord, nullptr, &index);
137 }
137 }
138
138
139 return index;
139 return index;
140 }
140 }
141 }
141 }
142
142
143 } // namespace
143 } // namespace
144
144
145 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
145 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
146 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
146 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
147 : m_Plot{graphWidget.plot()},
147 : m_Plot{graphWidget.plot()},
148 m_PointTracer{new QCPItemTracer{&m_Plot}},
148 m_PointTracer{new QCPItemTracer{&m_Plot}},
149 m_TracerTimer{},
149 m_TracerTimer{},
150 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
150 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
151 m_TitleText{new QCPItemText{&m_Plot}},
151 m_TitleText{new QCPItemText{&m_Plot}},
152 m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
152 m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
153 m_ShowXAxis{true},
153 m_ShowXAxis{true},
154 m_XAxisLabel{},
154 m_XAxisLabel{},
155 m_ColorScale{SqpColorScale{m_Plot}}
155 m_ColorScale{SqpColorScale{m_Plot}}
156 {
156 {
157 initPointTracerStyle(*m_PointTracer);
157 initPointTracerStyle(*m_PointTracer);
158
158
159 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
159 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
160 m_TracerTimer.setSingleShot(true);
160 m_TracerTimer.setSingleShot(true);
161
161
162 // Inits "close button" in plot overlay
162 // Inits "close button" in plot overlay
163 m_ClosePixmap->setLayer(OVERLAY_LAYER);
163 m_ClosePixmap->setLayer(OVERLAY_LAYER);
164 initClosePixmapStyle(*m_ClosePixmap);
164 initClosePixmapStyle(*m_ClosePixmap);
165
165
166 // Connects pixmap selection to graph widget closing
166 // Connects pixmap selection to graph widget closing
167 QObject::connect(m_ClosePixmap, &QCPItemPixmap::selectionChanged,
167 QObject::connect(&m_Plot, &QCustomPlot::itemClick,
168 [&graphWidget](bool selected) {
168 [&graphWidget, this](auto item, auto mouseEvent) {
169 if (selected) {
169 if (item == m_ClosePixmap) {
170 graphWidget.close();
170 graphWidget.close();
171 }
171 }
172 });
172 });
173
173
174 // Inits graph name in plot overlay
174 // Inits graph name in plot overlay
175 m_TitleText->setLayer(OVERLAY_LAYER);
175 m_TitleText->setLayer(OVERLAY_LAYER);
176 m_TitleText->setText(graphWidget.name());
176 m_TitleText->setText(graphWidget.name());
177 initTitleTextStyle(*m_TitleText);
177 initTitleTextStyle(*m_TitleText);
178
178
179 // Inits "show x-axis button" in plot overlay
179 // Inits "show x-axis button" in plot overlay
180 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
180 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
181 initXAxisPixmapStyle(*m_XAxisPixmap);
181 initXAxisPixmapStyle(*m_XAxisPixmap);
182
182
183 // Connects pixmap selection to graph x-axis showing/hiding
183 // Connects pixmap selection to graph x-axis showing/hiding
184 QObject::connect(m_XAxisPixmap, &QCPItemPixmap::selectionChanged, [this]() {
184 QObject::connect(&m_Plot, &QCustomPlot::itemClick, [this](auto item, auto mouseEvent) {
185 if (m_XAxisPixmap->selected()) {
185 if (m_XAxisPixmap == item) {
186 // Changes the selection state and refreshes the x-axis
186 // Changes the selection state and refreshes the x-axis
187 m_ShowXAxis = !m_ShowXAxis;
187 m_ShowXAxis = !m_ShowXAxis;
188 updateXAxisState();
188 this->updateXAxisState();
189 m_Plot.layer(AXES_LAYER)->replot();
189 m_Plot.layer(AXES_LAYER)->replot();
190
190
191 // Deselects the x-axis pixmap and updates icon
191 // Deselects the x-axis pixmap and updates icon
192 m_XAxisPixmap->setSelected(false);
193 m_XAxisPixmap->setPixmap(
192 m_XAxisPixmap->setPixmap(
194 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
193 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
195 m_Plot.layer(OVERLAY_LAYER)->replot();
194 m_Plot.layer(OVERLAY_LAYER)->replot();
196 }
195 }
197 });
196 });
198 }
197 }
199
198
200 /// Updates state of x-axis according to the current selection of x-axis pixmap
199 /// Updates state of x-axis according to the current selection of x-axis pixmap
201 /// @remarks the method doesn't call plot refresh
200 /// @remarks the method doesn't call plot refresh
202 void updateXAxisState() noexcept
201 void updateXAxisState() noexcept
203 {
202 {
204 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
203 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
205 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
204 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
206 }
205 }
207
206
208 QCustomPlot &m_Plot;
207 QCustomPlot &m_Plot;
209 QCPItemTracer *m_PointTracer;
208 QCPItemTracer *m_PointTracer;
210 QTimer m_TracerTimer;
209 QTimer m_TracerTimer;
211 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
210 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
212 QCPItemText *m_TitleText; /// Graph's title
211 QCPItemText *m_TitleText; /// Graph's title
213 QCPItemPixmap *m_XAxisPixmap;
212 QCPItemPixmap *m_XAxisPixmap;
214 bool m_ShowXAxis; /// X-axis properties are shown or hidden
213 bool m_ShowXAxis; /// X-axis properties are shown or hidden
215 QString m_XAxisLabel;
214 QString m_XAxisLabel;
216 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
215 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
217 };
216 };
218
217
219 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
218 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
220 VisualizationGraphWidget &graphWidget)
219 VisualizationGraphWidget &graphWidget)
221 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
220 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
222 {
221 {
223 }
222 }
224
223
225 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) noexcept
224 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) noexcept
226 {
225 {
227 // Opens color scale editor if color scale is double clicked
226 // Opens color scale editor if color scale is double clicked
228 auto colorScale = static_cast<QCPColorScale *>(impl->m_Plot.layoutElementAt(event->pos()));
227 auto colorScale = static_cast<QCPColorScale *>(impl->m_Plot.layoutElementAt(event->pos()));
229 if (impl->m_ColorScale.m_Scale == colorScale) {
228 if (impl->m_ColorScale.m_Scale == colorScale) {
230 if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) {
229 if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) {
231 impl->m_Plot.replot();
230 impl->m_Plot.replot();
232 }
231 }
233 }
232 }
234 }
233 }
235
234
236 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
235 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
237 {
236 {
238 // Cancels pending refresh
237 // Cancels pending refresh
239 impl->m_TracerTimer.disconnect();
238 impl->m_TracerTimer.disconnect();
240
239
241 // Reinits tracers
240 // Reinits tracers
242 impl->m_PointTracer->setGraph(nullptr);
241 impl->m_PointTracer->setGraph(nullptr);
243 impl->m_PointTracer->setVisible(false);
242 impl->m_PointTracer->setVisible(false);
244 impl->m_Plot.replot();
243 impl->m_Plot.replot();
245
244
246 QString tooltip{};
245 QString tooltip{};
247
246
248 // Gets the graph under the mouse position
247 // Gets the graph under the mouse position
249 auto eventPos = event->pos();
248 auto eventPos = event->pos();
250 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
249 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
251 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
250 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
252 auto graphData = graph->data();
251 auto graphData = graph->data();
253
252
254 // Gets the closest data point to the mouse
253 // Gets the closest data point to the mouse
255 auto graphDataIt = graphData->findBegin(mouseKey);
254 auto graphDataIt = graphData->findBegin(mouseKey);
256 if (graphDataIt != graphData->constEnd()) {
255 if (graphDataIt != graphData->constEnd()) {
257 // Sets tooltip
256 // Sets tooltip
258 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
257 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
259 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
258 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
260 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
259 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
261
260
262 // Displays point tracer
261 // Displays point tracer
263 impl->m_PointTracer->setGraph(graph);
262 impl->m_PointTracer->setGraph(graph);
264 impl->m_PointTracer->setGraphKey(graphDataIt->key);
263 impl->m_PointTracer->setGraphKey(graphDataIt->key);
265 impl->m_PointTracer->setLayer(
264 impl->m_PointTracer->setLayer(
266 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
265 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
267 impl->m_PointTracer->setVisible(true);
266 impl->m_PointTracer->setVisible(true);
268 impl->m_Plot.replot();
267 impl->m_Plot.replot();
269 }
268 }
270 }
269 }
271 else if (auto colorMap = qobject_cast<QCPColorMap *>(impl->m_Plot.plottableAt(eventPos))) {
270 else if (auto colorMap = qobject_cast<QCPColorMap *>(impl->m_Plot.plottableAt(eventPos))) {
272 // Gets x and y coords
271 // Gets x and y coords
273 auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
272 auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
274 auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
273 auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
275
274
276 // Calculates x and y cell indexes, and retrieves the underlying value
275 // Calculates x and y cell indexes, and retrieves the underlying value
277 auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
276 auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
278 auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
277 auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
279 auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
278 auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
280
279
281 // Sets tooltips
280 // Sets tooltips
282 tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
281 tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
283 formatValue(y, *colorMap->valueAxis()),
282 formatValue(y, *colorMap->valueAxis()),
284 formatValue(value, *colorMap->colorScale()->axis()));
283 formatValue(value, *colorMap->colorScale()->axis()));
285 }
284 }
286
285
287 if (!tooltip.isEmpty()) {
286 if (!tooltip.isEmpty()) {
288 // Starts timer to show tooltip after timeout
287 // Starts timer to show tooltip after timeout
289 auto showTooltip = [tooltip, eventPos, this]() {
288 auto showTooltip = [tooltip, eventPos, this]() {
290 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
289 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
291 &impl->m_Plot, TOOLTIP_RECT);
290 &impl->m_Plot, TOOLTIP_RECT);
292 };
291 };
293
292
294 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
293 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
295 impl->m_TracerTimer.start();
294 impl->m_TracerTimer.start();
296 }
295 }
297 }
296 }
298
297
299 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
298 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
300 {
299 {
301 // Updates color scale bounds
300 // Updates color scale bounds
302 impl->m_ColorScale.updateDataRange();
301 impl->m_ColorScale.updateDataRange();
303 impl->m_Plot.replot();
302 impl->m_Plot.replot();
304 }
303 }
305
304
306 void VisualizationGraphRenderingDelegate::setAxesProperties(
305 void VisualizationGraphRenderingDelegate::setAxesProperties(
307 std::shared_ptr<IDataSeries> dataSeries) noexcept
306 std::shared_ptr<IDataSeries> dataSeries) noexcept
308 {
307 {
309 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
308 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
310 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
309 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
311
310
312 auto axisHelper = IAxisHelperFactory::create(dataSeries);
311 auto axisHelper = IAxisHelperFactory::create(dataSeries);
313 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
312 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
314
313
315 // Updates x-axis state
314 // Updates x-axis state
316 impl->updateXAxisState();
315 impl->updateXAxisState();
317
316
318 impl->m_Plot.layer(AXES_LAYER)->replot();
317 impl->m_Plot.layer(AXES_LAYER)->replot();
319 }
318 }
320
319
321 void VisualizationGraphRenderingDelegate::setPlottablesProperties(
320 void VisualizationGraphRenderingDelegate::setPlottablesProperties(
322 std::shared_ptr<IDataSeries> dataSeries, PlottablesMap &plottables) noexcept
321 std::shared_ptr<IDataSeries> dataSeries, PlottablesMap &plottables) noexcept
323 {
322 {
324 auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries);
323 auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries);
325 plottablesHelper->setProperties(plottables);
324 plottablesHelper->setProperties(plottables);
326 }
325 }
327
326
328 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
327 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
329 {
328 {
330 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
329 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
331 overlay->setVisible(show);
330 overlay->setVisible(show);
332 overlay->replot();
331 overlay->replot();
333 }
332 }
@@ -1,876 +1,876
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/VisualizationSelectionZoneItem.h"
7 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneManager.h"
8 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationWidget.h"
9 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationZoneWidget.h"
10 #include "Visualization/VisualizationZoneWidget.h"
11 #include "ui_VisualizationGraphWidget.h"
11 #include "ui_VisualizationGraphWidget.h"
12
12
13 #include <Common/MimeTypesDef.h>
13 #include <Common/MimeTypesDef.h>
14 #include <Data/ArrayData.h>
14 #include <Data/ArrayData.h>
15 #include <Data/IDataSeries.h>
15 #include <Data/IDataSeries.h>
16 #include <Data/SpectrogramSeries.h>
16 #include <Data/SpectrogramSeries.h>
17 #include <DragAndDrop/DragDropHelper.h>
17 #include <DragAndDrop/DragDropHelper.h>
18 #include <Settings/SqpSettingsDefs.h>
18 #include <Settings/SqpSettingsDefs.h>
19 #include <SqpApplication.h>
19 #include <SqpApplication.h>
20 #include <Time/TimeController.h>
20 #include <Time/TimeController.h>
21 #include <Variable/Variable.h>
21 #include <Variable/Variable.h>
22 #include <Variable/VariableController.h>
22 #include <Variable/VariableController.h>
23
23
24 #include <unordered_map>
24 #include <unordered_map>
25
25
26 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
26 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
27
27
28 namespace {
28 namespace {
29
29
30 /// Key pressed to enable drag&drop in all modes
30 /// Key pressed to enable drag&drop in all modes
31 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
31 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
32
32
33 /// Key pressed to enable zoom on horizontal axis
33 /// Key pressed to enable zoom on horizontal axis
34 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
34 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
35
35
36 /// Key pressed to enable zoom on vertical axis
36 /// Key pressed to enable zoom on vertical axis
37 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
37 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
38
38
39 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
39 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
40 const auto PAN_SPEED = 5;
40 const auto PAN_SPEED = 5;
41
41
42 /// Key pressed to enable a calibration pan
42 /// Key pressed to enable a calibration pan
43 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
43 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
44
44
45 /// Key pressed to enable multi selection of selection zones
45 /// Key pressed to enable multi selection of selection zones
46 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
46 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
47
47
48 /// Minimum size for the zoom box, in percentage of the axis range
48 /// Minimum size for the zoom box, in percentage of the axis range
49 const auto ZOOM_BOX_MIN_SIZE = 0.8;
49 const auto ZOOM_BOX_MIN_SIZE = 0.8;
50
50
51 /// Format of the dates appearing in the label of a cursor
51 /// Format of the dates appearing in the label of a cursor
52 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
52 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
53
53
54 } // namespace
54 } // namespace
55
55
56 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
56 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
57
57
58 explicit VisualizationGraphWidgetPrivate(const QString &name)
58 explicit VisualizationGraphWidgetPrivate(const QString &name)
59 : m_Name{name},
59 : m_Name{name},
60 m_DoAcquisition{true},
60 m_DoAcquisition{true},
61 m_IsCalibration{false},
61 m_IsCalibration{false},
62 m_RenderingDelegate{nullptr}
62 m_RenderingDelegate{nullptr}
63 {
63 {
64 }
64 }
65
65
66 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
66 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
67 const SqpRange &range)
67 const SqpRange &range)
68 {
68 {
69 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
69 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
70
70
71 // Prevents that data has changed to update rendering
71 // Prevents that data has changed to update rendering
72 m_RenderingDelegate->onPlotUpdated();
72 m_RenderingDelegate->onPlotUpdated();
73 }
73 }
74
74
75 QString m_Name;
75 QString m_Name;
76 // 1 variable -> n qcpplot
76 // 1 variable -> n qcpplot
77 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
77 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
78 bool m_DoAcquisition;
78 bool m_DoAcquisition;
79 bool m_IsCalibration;
79 bool m_IsCalibration;
80 /// Delegate used to attach rendering features to the plot
80 /// Delegate used to attach rendering features to the plot
81 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
81 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
82
82
83 QCPItemRect *m_DrawingZoomRect = nullptr;
83 QCPItemRect *m_DrawingZoomRect = nullptr;
84 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
84 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
85
85
86 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
86 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
87 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
87 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
88
88
89 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
89 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
90 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
90 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
91 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
91 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
92
92
93 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
93 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
94
94
95 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
95 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
96 {
96 {
97 removeDrawingRect(plot);
97 removeDrawingRect(plot);
98
98
99 auto axisPos = posToAxisPos(pos, plot);
99 auto axisPos = posToAxisPos(pos, plot);
100
100
101 m_DrawingZoomRect = new QCPItemRect{&plot};
101 m_DrawingZoomRect = new QCPItemRect{&plot};
102 QPen p;
102 QPen p;
103 p.setWidth(2);
103 p.setWidth(2);
104 m_DrawingZoomRect->setPen(p);
104 m_DrawingZoomRect->setPen(p);
105
105
106 m_DrawingZoomRect->topLeft->setCoords(axisPos);
106 m_DrawingZoomRect->topLeft->setCoords(axisPos);
107 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
107 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
108 }
108 }
109
109
110 void removeDrawingRect(QCustomPlot &plot)
110 void removeDrawingRect(QCustomPlot &plot)
111 {
111 {
112 if (m_DrawingZoomRect) {
112 if (m_DrawingZoomRect) {
113 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
113 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
114 m_DrawingZoomRect = nullptr;
114 m_DrawingZoomRect = nullptr;
115 plot.replot(QCustomPlot::rpQueuedReplot);
115 plot.replot(QCustomPlot::rpQueuedReplot);
116 }
116 }
117 }
117 }
118
118
119 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
119 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
120 {
120 {
121 endDrawingZone(graph);
121 endDrawingZone(graph);
122
122
123 auto axisPos = posToAxisPos(pos, graph->plot());
123 auto axisPos = posToAxisPos(pos, graph->plot());
124
124
125 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
125 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
126 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
126 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
127 m_DrawingZone->setEditionEnabled(false);
127 m_DrawingZone->setEditionEnabled(false);
128 }
128 }
129
129
130 void endDrawingZone(VisualizationGraphWidget *graph)
130 void endDrawingZone(VisualizationGraphWidget *graph)
131 {
131 {
132 if (m_DrawingZone) {
132 if (m_DrawingZone) {
133 auto drawingZoneRange = m_DrawingZone->range();
133 auto drawingZoneRange = m_DrawingZone->range();
134 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
134 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
135 m_DrawingZone->setEditionEnabled(true);
135 m_DrawingZone->setEditionEnabled(true);
136 addSelectionZone(m_DrawingZone);
136 addSelectionZone(m_DrawingZone);
137 }
137 }
138 else {
138 else {
139 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
139 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
140 }
140 }
141
141
142 graph->plot().replot(QCustomPlot::rpQueuedReplot);
142 graph->plot().replot(QCustomPlot::rpQueuedReplot);
143 m_DrawingZone = nullptr;
143 m_DrawingZone = nullptr;
144 }
144 }
145 }
145 }
146
146
147 void setSelectionZonesEditionEnabled(bool value)
147 void setSelectionZonesEditionEnabled(bool value)
148 {
148 {
149 for (auto s : m_SelectionZones) {
149 for (auto s : m_SelectionZones) {
150 s->setEditionEnabled(value);
150 s->setEditionEnabled(value);
151 }
151 }
152 }
152 }
153
153
154 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
154 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
155
155
156 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
156 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
157 const QCustomPlot &plot) const
157 const QCustomPlot &plot) const
158 {
158 {
159 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
159 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
160 auto minDistanceToZone = -1;
160 auto minDistanceToZone = -1;
161 for (auto zone : m_SelectionZones) {
161 for (auto zone : m_SelectionZones) {
162 auto distanceToZone = zone->selectTest(pos, false);
162 auto distanceToZone = zone->selectTest(pos, false);
163 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
163 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
164 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
164 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
165 selectionZoneItemUnderCursor = zone;
165 selectionZoneItemUnderCursor = zone;
166 }
166 }
167 }
167 }
168
168
169 return selectionZoneItemUnderCursor;
169 return selectionZoneItemUnderCursor;
170 }
170 }
171
171
172 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
172 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
173 {
173 {
174 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
174 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
175 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
175 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
176 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
176 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
177 }
177 }
178
178
179 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
179 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
180 {
180 {
181 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
181 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
182 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
182 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
183 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
183 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
184 }
184 }
185 };
185 };
186
186
187 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
187 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
188 : VisualizationDragWidget{parent},
188 : VisualizationDragWidget{parent},
189 ui{new Ui::VisualizationGraphWidget},
189 ui{new Ui::VisualizationGraphWidget},
190 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
190 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
191 {
191 {
192 ui->setupUi(this);
192 ui->setupUi(this);
193
193
194 // 'Close' options : widget is deleted when closed
194 // 'Close' options : widget is deleted when closed
195 setAttribute(Qt::WA_DeleteOnClose);
195 setAttribute(Qt::WA_DeleteOnClose);
196
196
197 // Set qcpplot properties :
197 // Set qcpplot properties :
198 // - zoom is enabled
198 // - zoom is enabled
199 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
199 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
200 ui->widget->setInteractions(QCP::iRangeZoom | QCP::iSelectItems);
200 ui->widget->setInteractions(QCP::iRangeZoom);
201 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
201 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
202
202
203 // The delegate must be initialized after the ui as it uses the plot
203 // The delegate must be initialized after the ui as it uses the plot
204 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
204 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
205
205
206 // Init the cursors
206 // Init the cursors
207 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
207 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
208 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
208 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
209 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
209 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
210 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
210 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
211
211
212 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
212 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
213 connect(ui->widget, &QCustomPlot::mouseRelease, this,
213 connect(ui->widget, &QCustomPlot::mouseRelease, this,
214 &VisualizationGraphWidget::onMouseRelease);
214 &VisualizationGraphWidget::onMouseRelease);
215 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
215 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
216 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
216 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
217 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
217 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
218 &VisualizationGraphWidget::onMouseDoubleClick);
218 &VisualizationGraphWidget::onMouseDoubleClick);
219 connect(
219 connect(
220 ui->widget->xAxis,
220 ui->widget->xAxis,
221 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
221 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
222 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
222 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
223
223
224 // Activates menu when right clicking on the graph
224 // Activates menu when right clicking on the graph
225 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
225 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
226 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
226 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
227 &VisualizationGraphWidget::onGraphMenuRequested);
227 &VisualizationGraphWidget::onGraphMenuRequested);
228
228
229 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
229 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
230 &VariableController::onRequestDataLoading);
230 &VariableController::onRequestDataLoading);
231
231
232 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
232 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
233 &VisualizationGraphWidget::onUpdateVarDisplaying);
233 &VisualizationGraphWidget::onUpdateVarDisplaying);
234
234
235 #ifdef Q_OS_MAC
235 #ifdef Q_OS_MAC
236 plot().setPlottingHint(QCP::phFastPolylines, true);
236 plot().setPlottingHint(QCP::phFastPolylines, true);
237 #endif
237 #endif
238 }
238 }
239
239
240
240
241 VisualizationGraphWidget::~VisualizationGraphWidget()
241 VisualizationGraphWidget::~VisualizationGraphWidget()
242 {
242 {
243 delete ui;
243 delete ui;
244 }
244 }
245
245
246 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
246 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
247 {
247 {
248 auto parent = parentWidget();
248 auto parent = parentWidget();
249 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
249 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
250 parent = parent->parentWidget();
250 parent = parent->parentWidget();
251 }
251 }
252
252
253 return qobject_cast<VisualizationZoneWidget *>(parent);
253 return qobject_cast<VisualizationZoneWidget *>(parent);
254 }
254 }
255
255
256 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
256 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
257 {
257 {
258 auto parent = parentWidget();
258 auto parent = parentWidget();
259 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
259 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
260 parent = parent->parentWidget();
260 parent = parent->parentWidget();
261 }
261 }
262
262
263 return qobject_cast<VisualizationWidget *>(parent);
263 return qobject_cast<VisualizationWidget *>(parent);
264 }
264 }
265
265
266 void VisualizationGraphWidget::enableAcquisition(bool enable)
266 void VisualizationGraphWidget::enableAcquisition(bool enable)
267 {
267 {
268 impl->m_DoAcquisition = enable;
268 impl->m_DoAcquisition = enable;
269 }
269 }
270
270
271 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
271 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
272 {
272 {
273 // Uses delegate to create the qcpplot components according to the variable
273 // Uses delegate to create the qcpplot components according to the variable
274 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
274 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
275
275
276 if (auto dataSeries = variable->dataSeries()) {
276 if (auto dataSeries = variable->dataSeries()) {
277 // Set axes properties according to the units of the data series
277 // Set axes properties according to the units of the data series
278 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
278 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
279
279
280 // Sets rendering properties for the new plottables
280 // Sets rendering properties for the new plottables
281 // Warning: this method must be called after setAxesProperties(), as it can access to some
281 // Warning: this method must be called after setAxesProperties(), as it can access to some
282 // axes properties that have to be initialized
282 // axes properties that have to be initialized
283 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
283 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
284 }
284 }
285
285
286 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
286 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
287
287
288 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
288 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
289
289
290 this->enableAcquisition(false);
290 this->enableAcquisition(false);
291 this->setGraphRange(range);
291 this->setGraphRange(range);
292 this->enableAcquisition(true);
292 this->enableAcquisition(true);
293
293
294 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
294 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
295
295
296 emit variableAdded(variable);
296 emit variableAdded(variable);
297 }
297 }
298
298
299 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
299 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
300 {
300 {
301 // Each component associated to the variable :
301 // Each component associated to the variable :
302 // - is removed from qcpplot (which deletes it)
302 // - is removed from qcpplot (which deletes it)
303 // - is no longer referenced in the map
303 // - is no longer referenced in the map
304 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
304 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
305 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
305 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
306 emit variableAboutToBeRemoved(variable);
306 emit variableAboutToBeRemoved(variable);
307
307
308 auto &plottablesMap = variableIt->second;
308 auto &plottablesMap = variableIt->second;
309
309
310 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
310 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
311 plottableIt != plottableEnd;) {
311 plottableIt != plottableEnd;) {
312 ui->widget->removePlottable(plottableIt->second);
312 ui->widget->removePlottable(plottableIt->second);
313 plottableIt = plottablesMap.erase(plottableIt);
313 plottableIt = plottablesMap.erase(plottableIt);
314 }
314 }
315
315
316 impl->m_VariableToPlotMultiMap.erase(variableIt);
316 impl->m_VariableToPlotMultiMap.erase(variableIt);
317 }
317 }
318
318
319 // Updates graph
319 // Updates graph
320 ui->widget->replot();
320 ui->widget->replot();
321 }
321 }
322
322
323 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
323 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
324 {
324 {
325 auto variables = QList<std::shared_ptr<Variable> >{};
325 auto variables = QList<std::shared_ptr<Variable> >{};
326 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
326 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
327 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
327 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
328 variables << it->first;
328 variables << it->first;
329 }
329 }
330
330
331 return variables;
331 return variables;
332 }
332 }
333
333
334 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
334 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
335 {
335 {
336 if (!variable) {
336 if (!variable) {
337 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
337 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
338 return;
338 return;
339 }
339 }
340
340
341 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
341 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
342 }
342 }
343
343
344 SqpRange VisualizationGraphWidget::graphRange() const noexcept
344 SqpRange VisualizationGraphWidget::graphRange() const noexcept
345 {
345 {
346 auto graphRange = ui->widget->xAxis->range();
346 auto graphRange = ui->widget->xAxis->range();
347 return SqpRange{graphRange.lower, graphRange.upper};
347 return SqpRange{graphRange.lower, graphRange.upper};
348 }
348 }
349
349
350 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
350 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
351 {
351 {
352 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
352 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
353 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
353 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
354 ui->widget->replot();
354 ui->widget->replot();
355 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
355 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
356 }
356 }
357
357
358 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
358 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
359 {
359 {
360 QVector<SqpRange> ranges;
360 QVector<SqpRange> ranges;
361 for (auto zone : impl->m_SelectionZones) {
361 for (auto zone : impl->m_SelectionZones) {
362 ranges << zone->range();
362 ranges << zone->range();
363 }
363 }
364
364
365 return ranges;
365 return ranges;
366 }
366 }
367
367
368 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
368 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
369 {
369 {
370 for (const auto &range : ranges) {
370 for (const auto &range : ranges) {
371 // note: ownership is transfered to QCustomPlot
371 // note: ownership is transfered to QCustomPlot
372 auto zone = new VisualizationSelectionZoneItem(&plot());
372 auto zone = new VisualizationSelectionZoneItem(&plot());
373 zone->setRange(range.m_TStart, range.m_TEnd);
373 zone->setRange(range.m_TStart, range.m_TEnd);
374 impl->addSelectionZone(zone);
374 impl->addSelectionZone(zone);
375 }
375 }
376
376
377 plot().replot(QCustomPlot::rpQueuedReplot);
377 plot().replot(QCustomPlot::rpQueuedReplot);
378 }
378 }
379
379
380 void VisualizationGraphWidget::undoZoom()
380 void VisualizationGraphWidget::undoZoom()
381 {
381 {
382 auto zoom = impl->m_ZoomStack.pop();
382 auto zoom = impl->m_ZoomStack.pop();
383 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
383 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
384 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
384 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
385
385
386 axisX->setRange(zoom.first);
386 axisX->setRange(zoom.first);
387 axisY->setRange(zoom.second);
387 axisY->setRange(zoom.second);
388
388
389 plot().replot(QCustomPlot::rpQueuedReplot);
389 plot().replot(QCustomPlot::rpQueuedReplot);
390 }
390 }
391
391
392 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
392 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
393 {
393 {
394 if (visitor) {
394 if (visitor) {
395 visitor->visit(this);
395 visitor->visit(this);
396 }
396 }
397 else {
397 else {
398 qCCritical(LOG_VisualizationGraphWidget())
398 qCCritical(LOG_VisualizationGraphWidget())
399 << tr("Can't visit widget : the visitor is null");
399 << tr("Can't visit widget : the visitor is null");
400 }
400 }
401 }
401 }
402
402
403 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
403 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
404 {
404 {
405 auto isSpectrogram = [](const auto &variable) {
405 auto isSpectrogram = [](const auto &variable) {
406 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
406 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
407 };
407 };
408
408
409 // - A spectrogram series can't be dropped on graph with existing plottables
409 // - A spectrogram series can't be dropped on graph with existing plottables
410 // - No data series can be dropped on graph with existing spectrogram series
410 // - No data series can be dropped on graph with existing spectrogram series
411 return isSpectrogram(variable)
411 return isSpectrogram(variable)
412 ? impl->m_VariableToPlotMultiMap.empty()
412 ? impl->m_VariableToPlotMultiMap.empty()
413 : std::none_of(
413 : std::none_of(
414 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
414 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
415 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
415 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
416 }
416 }
417
417
418 bool VisualizationGraphWidget::contains(const Variable &variable) const
418 bool VisualizationGraphWidget::contains(const Variable &variable) const
419 {
419 {
420 // Finds the variable among the keys of the map
420 // Finds the variable among the keys of the map
421 auto variablePtr = &variable;
421 auto variablePtr = &variable;
422 auto findVariable
422 auto findVariable
423 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
423 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
424
424
425 auto end = impl->m_VariableToPlotMultiMap.cend();
425 auto end = impl->m_VariableToPlotMultiMap.cend();
426 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
426 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
427 return it != end;
427 return it != end;
428 }
428 }
429
429
430 QString VisualizationGraphWidget::name() const
430 QString VisualizationGraphWidget::name() const
431 {
431 {
432 return impl->m_Name;
432 return impl->m_Name;
433 }
433 }
434
434
435 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
435 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
436 {
436 {
437 auto mimeData = new QMimeData;
437 auto mimeData = new QMimeData;
438
438
439 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
439 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
440 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
440 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
441 && selectionZoneItemUnderCursor) {
441 && selectionZoneItemUnderCursor) {
442 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
442 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
443 selectionZoneItemUnderCursor->range()));
443 selectionZoneItemUnderCursor->range()));
444 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
444 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
445 selectionZoneItemUnderCursor->range()));
445 selectionZoneItemUnderCursor->range()));
446 }
446 }
447 else {
447 else {
448 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
448 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
449
449
450 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
450 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
451 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
451 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
452 }
452 }
453
453
454 return mimeData;
454 return mimeData;
455 }
455 }
456
456
457 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
457 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
458 {
458 {
459 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
459 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
460 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
460 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
461 && selectionZoneItemUnderCursor) {
461 && selectionZoneItemUnderCursor) {
462
462
463 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
463 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
464 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
464 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
465
465
466 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
466 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
467 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
467 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
468 .toSize();
468 .toSize();
469
469
470 auto pixmap = QPixmap(zoneSize);
470 auto pixmap = QPixmap(zoneSize);
471 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
471 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
472
472
473 return pixmap;
473 return pixmap;
474 }
474 }
475
475
476 return QPixmap();
476 return QPixmap();
477 }
477 }
478
478
479 bool VisualizationGraphWidget::isDragAllowed() const
479 bool VisualizationGraphWidget::isDragAllowed() const
480 {
480 {
481 return true;
481 return true;
482 }
482 }
483
483
484 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
484 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
485 {
485 {
486 if (highlighted) {
486 if (highlighted) {
487 plot().setBackground(QBrush(QColor("#BBD5EE")));
487 plot().setBackground(QBrush(QColor("#BBD5EE")));
488 }
488 }
489 else {
489 else {
490 plot().setBackground(QBrush(Qt::white));
490 plot().setBackground(QBrush(Qt::white));
491 }
491 }
492
492
493 plot().update();
493 plot().update();
494 }
494 }
495
495
496 void VisualizationGraphWidget::addVerticalCursor(double time)
496 void VisualizationGraphWidget::addVerticalCursor(double time)
497 {
497 {
498 impl->m_VerticalCursor->setPosition(time);
498 impl->m_VerticalCursor->setPosition(time);
499 impl->m_VerticalCursor->setVisible(true);
499 impl->m_VerticalCursor->setVisible(true);
500
500
501 auto text
501 auto text
502 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
502 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
503 impl->m_VerticalCursor->setLabelText(text);
503 impl->m_VerticalCursor->setLabelText(text);
504 }
504 }
505
505
506 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
506 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
507 {
507 {
508 impl->m_VerticalCursor->setAbsolutePosition(position);
508 impl->m_VerticalCursor->setAbsolutePosition(position);
509 impl->m_VerticalCursor->setVisible(true);
509 impl->m_VerticalCursor->setVisible(true);
510
510
511 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
511 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
512 auto text
512 auto text
513 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
513 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
514 impl->m_VerticalCursor->setLabelText(text);
514 impl->m_VerticalCursor->setLabelText(text);
515 }
515 }
516
516
517 void VisualizationGraphWidget::removeVerticalCursor()
517 void VisualizationGraphWidget::removeVerticalCursor()
518 {
518 {
519 impl->m_VerticalCursor->setVisible(false);
519 impl->m_VerticalCursor->setVisible(false);
520 plot().replot(QCustomPlot::rpQueuedReplot);
520 plot().replot(QCustomPlot::rpQueuedReplot);
521 }
521 }
522
522
523 void VisualizationGraphWidget::addHorizontalCursor(double value)
523 void VisualizationGraphWidget::addHorizontalCursor(double value)
524 {
524 {
525 impl->m_HorizontalCursor->setPosition(value);
525 impl->m_HorizontalCursor->setPosition(value);
526 impl->m_HorizontalCursor->setVisible(true);
526 impl->m_HorizontalCursor->setVisible(true);
527 impl->m_HorizontalCursor->setLabelText(QString::number(value));
527 impl->m_HorizontalCursor->setLabelText(QString::number(value));
528 }
528 }
529
529
530 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
530 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
531 {
531 {
532 impl->m_HorizontalCursor->setAbsolutePosition(position);
532 impl->m_HorizontalCursor->setAbsolutePosition(position);
533 impl->m_HorizontalCursor->setVisible(true);
533 impl->m_HorizontalCursor->setVisible(true);
534
534
535 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
535 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
536 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
536 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
537 }
537 }
538
538
539 void VisualizationGraphWidget::removeHorizontalCursor()
539 void VisualizationGraphWidget::removeHorizontalCursor()
540 {
540 {
541 impl->m_HorizontalCursor->setVisible(false);
541 impl->m_HorizontalCursor->setVisible(false);
542 plot().replot(QCustomPlot::rpQueuedReplot);
542 plot().replot(QCustomPlot::rpQueuedReplot);
543 }
543 }
544
544
545 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
545 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
546 {
546 {
547 Q_UNUSED(event);
547 Q_UNUSED(event);
548
548
549 // Prevents that all variables will be removed from graph when it will be closed
549 // Prevents that all variables will be removed from graph when it will be closed
550 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
550 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
551 emit variableAboutToBeRemoved(variableEntry.first);
551 emit variableAboutToBeRemoved(variableEntry.first);
552 }
552 }
553 }
553 }
554
554
555 void VisualizationGraphWidget::enterEvent(QEvent *event)
555 void VisualizationGraphWidget::enterEvent(QEvent *event)
556 {
556 {
557 Q_UNUSED(event);
557 Q_UNUSED(event);
558 impl->m_RenderingDelegate->showGraphOverlay(true);
558 impl->m_RenderingDelegate->showGraphOverlay(true);
559 }
559 }
560
560
561 void VisualizationGraphWidget::leaveEvent(QEvent *event)
561 void VisualizationGraphWidget::leaveEvent(QEvent *event)
562 {
562 {
563 Q_UNUSED(event);
563 Q_UNUSED(event);
564 impl->m_RenderingDelegate->showGraphOverlay(false);
564 impl->m_RenderingDelegate->showGraphOverlay(false);
565
565
566 if (auto parentZone = parentZoneWidget()) {
566 if (auto parentZone = parentZoneWidget()) {
567 parentZone->notifyMouseLeaveGraph(this);
567 parentZone->notifyMouseLeaveGraph(this);
568 }
568 }
569 else {
569 else {
570 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
570 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
571 }
571 }
572
572
573 if (impl->m_HoveredZone) {
573 if (impl->m_HoveredZone) {
574 impl->m_HoveredZone->setHovered(false);
574 impl->m_HoveredZone->setHovered(false);
575 impl->m_HoveredZone = nullptr;
575 impl->m_HoveredZone = nullptr;
576 }
576 }
577 }
577 }
578
578
579 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
579 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
580 {
580 {
581 return *ui->widget;
581 return *ui->widget;
582 }
582 }
583
583
584 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
584 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
585 {
585 {
586 QMenu graphMenu{};
586 QMenu graphMenu{};
587
587
588 // Iterates on variables (unique keys)
588 // Iterates on variables (unique keys)
589 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
589 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
590 end = impl->m_VariableToPlotMultiMap.cend();
590 end = impl->m_VariableToPlotMultiMap.cend();
591 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
591 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
592 // 'Remove variable' action
592 // 'Remove variable' action
593 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
593 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
594 [ this, var = it->first ]() { removeVariable(var); });
594 [ this, var = it->first ]() { removeVariable(var); });
595 }
595 }
596
596
597 if (!impl->m_ZoomStack.isEmpty()) {
597 if (!impl->m_ZoomStack.isEmpty()) {
598 if (!graphMenu.isEmpty()) {
598 if (!graphMenu.isEmpty()) {
599 graphMenu.addSeparator();
599 graphMenu.addSeparator();
600 }
600 }
601
601
602 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
602 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
603 }
603 }
604
604
605 if (!graphMenu.isEmpty()) {
605 if (!graphMenu.isEmpty()) {
606 graphMenu.exec(QCursor::pos());
606 graphMenu.exec(QCursor::pos());
607 }
607 }
608 }
608 }
609
609
610 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
610 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
611 {
611 {
612 qCDebug(LOG_VisualizationGraphWidget())
612 qCDebug(LOG_VisualizationGraphWidget())
613 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
613 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
614 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
614 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
615
615
616 auto graphRange = SqpRange{t1.lower, t1.upper};
616 auto graphRange = SqpRange{t1.lower, t1.upper};
617 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
617 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
618
618
619 if (impl->m_DoAcquisition) {
619 if (impl->m_DoAcquisition) {
620 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
620 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
621
621
622 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
622 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
623 end = impl->m_VariableToPlotMultiMap.end();
623 end = impl->m_VariableToPlotMultiMap.end();
624 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
624 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
625 variableUnderGraphVector.push_back(it->first);
625 variableUnderGraphVector.push_back(it->first);
626 }
626 }
627 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
627 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
628 !impl->m_IsCalibration);
628 !impl->m_IsCalibration);
629
629
630 if (!impl->m_IsCalibration) {
630 if (!impl->m_IsCalibration) {
631 qCDebug(LOG_VisualizationGraphWidget())
631 qCDebug(LOG_VisualizationGraphWidget())
632 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
632 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
633 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
633 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
634 emit synchronize(graphRange, oldGraphRange);
634 emit synchronize(graphRange, oldGraphRange);
635 }
635 }
636 }
636 }
637
637
638 auto pos = mapFromGlobal(QCursor::pos());
638 auto pos = mapFromGlobal(QCursor::pos());
639 auto axisPos = impl->posToAxisPos(pos, plot());
639 auto axisPos = impl->posToAxisPos(pos, plot());
640 if (auto parentZone = parentZoneWidget()) {
640 if (auto parentZone = parentZoneWidget()) {
641 if (impl->pointIsInAxisRect(axisPos, plot())) {
641 if (impl->pointIsInAxisRect(axisPos, plot())) {
642 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
642 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
643 }
643 }
644 else {
644 else {
645 parentZone->notifyMouseLeaveGraph(this);
645 parentZone->notifyMouseLeaveGraph(this);
646 }
646 }
647 }
647 }
648 else {
648 else {
649 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
649 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
650 }
650 }
651 }
651 }
652
652
653 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
653 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
654 {
654 {
655 impl->m_RenderingDelegate->onMouseDoubleClick(event);
655 impl->m_RenderingDelegate->onMouseDoubleClick(event);
656 }
656 }
657
657
658 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
658 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
659 {
659 {
660 // Handles plot rendering when mouse is moving
660 // Handles plot rendering when mouse is moving
661 impl->m_RenderingDelegate->onMouseMove(event);
661 impl->m_RenderingDelegate->onMouseMove(event);
662
662
663 auto axisPos = impl->posToAxisPos(event->pos(), plot());
663 auto axisPos = impl->posToAxisPos(event->pos(), plot());
664
664
665 // Zoom box and zone drawing
665 // Zoom box and zone drawing
666 if (impl->m_DrawingZoomRect) {
666 if (impl->m_DrawingZoomRect) {
667 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
667 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
668 }
668 }
669 else if (impl->m_DrawingZone) {
669 else if (impl->m_DrawingZone) {
670 impl->m_DrawingZone->setEnd(axisPos.x());
670 impl->m_DrawingZone->setEnd(axisPos.x());
671 }
671 }
672
672
673 // Cursor
673 // Cursor
674 if (auto parentZone = parentZoneWidget()) {
674 if (auto parentZone = parentZoneWidget()) {
675 if (impl->pointIsInAxisRect(axisPos, plot())) {
675 if (impl->pointIsInAxisRect(axisPos, plot())) {
676 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
676 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
677 }
677 }
678 else {
678 else {
679 parentZone->notifyMouseLeaveGraph(this);
679 parentZone->notifyMouseLeaveGraph(this);
680 }
680 }
681 }
681 }
682 else {
682 else {
683 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
683 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
684 }
684 }
685
685
686 // Search for the selection zone under the mouse
686 // Search for the selection zone under the mouse
687 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
687 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
688 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
688 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
689 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
689 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
690
690
691 // Sets the appropriate cursor shape
691 // Sets the appropriate cursor shape
692 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
692 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
693 setCursor(cursorShape);
693 setCursor(cursorShape);
694
694
695 // Manages the hovered zone
695 // Manages the hovered zone
696 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
696 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
697 if (impl->m_HoveredZone) {
697 if (impl->m_HoveredZone) {
698 impl->m_HoveredZone->setHovered(false);
698 impl->m_HoveredZone->setHovered(false);
699 }
699 }
700 selectionZoneItemUnderCursor->setHovered(true);
700 selectionZoneItemUnderCursor->setHovered(true);
701 impl->m_HoveredZone = selectionZoneItemUnderCursor;
701 impl->m_HoveredZone = selectionZoneItemUnderCursor;
702 plot().replot(QCustomPlot::rpQueuedReplot);
702 plot().replot(QCustomPlot::rpQueuedReplot);
703 }
703 }
704 }
704 }
705 else {
705 else {
706 // There is no zone under the mouse or the interaction mode is not "selection zones"
706 // There is no zone under the mouse or the interaction mode is not "selection zones"
707 if (impl->m_HoveredZone) {
707 if (impl->m_HoveredZone) {
708 impl->m_HoveredZone->setHovered(false);
708 impl->m_HoveredZone->setHovered(false);
709 impl->m_HoveredZone = nullptr;
709 impl->m_HoveredZone = nullptr;
710 }
710 }
711
711
712 setCursor(Qt::ArrowCursor);
712 setCursor(Qt::ArrowCursor);
713 }
713 }
714
714
715 impl->m_HasMovedMouse = true;
715 impl->m_HasMovedMouse = true;
716 VisualizationDragWidget::mouseMoveEvent(event);
716 VisualizationDragWidget::mouseMoveEvent(event);
717 }
717 }
718
718
719 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
719 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
720 {
720 {
721 auto value = event->angleDelta().x() + event->angleDelta().y();
721 auto value = event->angleDelta().x() + event->angleDelta().y();
722 if (value != 0) {
722 if (value != 0) {
723
723
724 auto direction = value > 0 ? 1.0 : -1.0;
724 auto direction = value > 0 ? 1.0 : -1.0;
725 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
725 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
726 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
726 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
727 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
727 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
728
728
729 auto zoomOrientations = QFlags<Qt::Orientation>{};
729 auto zoomOrientations = QFlags<Qt::Orientation>{};
730 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
730 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
731 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
731 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
732
732
733 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
733 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
734
734
735 if (!isZoomX && !isZoomY) {
735 if (!isZoomX && !isZoomY) {
736 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
736 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
737 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
737 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
738
738
739 axis->setRange(axis->range() + diff);
739 axis->setRange(axis->range() + diff);
740
740
741 if (plot().noAntialiasingOnDrag()) {
741 if (plot().noAntialiasingOnDrag()) {
742 plot().setNotAntialiasedElements(QCP::aeAll);
742 plot().setNotAntialiasedElements(QCP::aeAll);
743 }
743 }
744
744
745 plot().replot(QCustomPlot::rpQueuedReplot);
745 plot().replot(QCustomPlot::rpQueuedReplot);
746 }
746 }
747 }
747 }
748 }
748 }
749
749
750 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
750 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
751 {
751 {
752 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
752 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
753 auto isSelectionZoneMode
753 auto isSelectionZoneMode
754 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
754 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
755 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
755 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
756
756
757 if (!isDragDropClick && isLeftClick) {
757 if (!isDragDropClick && isLeftClick) {
758 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
758 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
759 // Starts a zoom box
759 // Starts a zoom box
760 impl->startDrawingRect(event->pos(), plot());
760 impl->startDrawingRect(event->pos(), plot());
761 }
761 }
762 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
762 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
763 // Starts a new selection zone
763 // Starts a new selection zone
764 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
764 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
765 if (!zoneAtPos) {
765 if (!zoneAtPos) {
766 impl->startDrawingZone(event->pos(), this);
766 impl->startDrawingZone(event->pos(), this);
767 }
767 }
768 }
768 }
769 }
769 }
770
770
771 // Allows mouse panning only in default mode
771 // Allows mouse panning only in default mode
772 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
772 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
773 == SqpApplication::PlotsInteractionMode::None
773 == SqpApplication::PlotsInteractionMode::None
774 && !isDragDropClick);
774 && !isDragDropClick);
775
775
776 // Allows zone edition only in selection zone mode without drag&drop
776 // Allows zone edition only in selection zone mode without drag&drop
777 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
777 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
778
778
779 // Selection / Deselection
779 // Selection / Deselection
780 if (isSelectionZoneMode) {
780 if (isSelectionZoneMode) {
781 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
781 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
782 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
782 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
783 if (selectionZoneItemUnderCursor && isLeftClick) {
783 if (selectionZoneItemUnderCursor && isLeftClick) {
784 selectionZoneItemUnderCursor->setAssociatedEditedZones(
784 selectionZoneItemUnderCursor->setAssociatedEditedZones(
785 parentVisualizationWidget()->selectionZoneManager().selectedItems());
785 parentVisualizationWidget()->selectionZoneManager().selectedItems());
786 }
786 }
787 else if (!isMultiSelectionClick && isLeftClick) {
787 else if (!isMultiSelectionClick && isLeftClick) {
788 parentVisualizationWidget()->selectionZoneManager().clearSelection();
788 parentVisualizationWidget()->selectionZoneManager().clearSelection();
789 }
789 }
790 else {
790 else {
791 // No selection change
791 // No selection change
792 }
792 }
793 }
793 }
794
794
795
795
796 impl->m_HasMovedMouse = false;
796 impl->m_HasMovedMouse = false;
797 VisualizationDragWidget::mousePressEvent(event);
797 VisualizationDragWidget::mousePressEvent(event);
798 }
798 }
799
799
800 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
800 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
801 {
801 {
802 if (impl->m_DrawingZoomRect) {
802 if (impl->m_DrawingZoomRect) {
803
803
804 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
804 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
805 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
805 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
806
806
807 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
807 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
808 impl->m_DrawingZoomRect->bottomRight->coords().x()};
808 impl->m_DrawingZoomRect->bottomRight->coords().x()};
809
809
810 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
810 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
811 impl->m_DrawingZoomRect->bottomRight->coords().y()};
811 impl->m_DrawingZoomRect->bottomRight->coords().y()};
812
812
813 impl->removeDrawingRect(plot());
813 impl->removeDrawingRect(plot());
814
814
815 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
815 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
816 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
816 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
817 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
817 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
818 axisX->setRange(newAxisXRange);
818 axisX->setRange(newAxisXRange);
819 axisY->setRange(newAxisYRange);
819 axisY->setRange(newAxisYRange);
820
820
821 plot().replot(QCustomPlot::rpQueuedReplot);
821 plot().replot(QCustomPlot::rpQueuedReplot);
822 }
822 }
823 }
823 }
824
824
825 impl->endDrawingZone(this);
825 impl->endDrawingZone(this);
826
826
827 impl->m_IsCalibration = false;
827 impl->m_IsCalibration = false;
828
828
829 // Selection / Deselection
829 // Selection / Deselection
830 auto isSelectionZoneMode
830 auto isSelectionZoneMode
831 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
831 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
832 if (isSelectionZoneMode) {
832 if (isSelectionZoneMode) {
833 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
833 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
834 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
834 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
835 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
835 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
836 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
836 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
837 parentVisualizationWidget()->selectionZoneManager().select(
837 parentVisualizationWidget()->selectionZoneManager().select(
838 {selectionZoneItemUnderCursor});
838 {selectionZoneItemUnderCursor});
839 }
839 }
840 else if (!impl->m_HasMovedMouse) {
840 else if (!impl->m_HasMovedMouse) {
841 parentVisualizationWidget()->selectionZoneManager().setSelected(
841 parentVisualizationWidget()->selectionZoneManager().setSelected(
842 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
842 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
843 || event->button() == Qt::RightButton);
843 || event->button() == Qt::RightButton);
844 }
844 }
845 }
845 }
846 else {
846 else {
847 // No selection change
847 // No selection change
848 }
848 }
849 }
849 }
850 }
850 }
851
851
852 void VisualizationGraphWidget::onDataCacheVariableUpdated()
852 void VisualizationGraphWidget::onDataCacheVariableUpdated()
853 {
853 {
854 auto graphRange = ui->widget->xAxis->range();
854 auto graphRange = ui->widget->xAxis->range();
855 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
855 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
856
856
857 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
857 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
858 auto variable = variableEntry.first;
858 auto variable = variableEntry.first;
859 qCDebug(LOG_VisualizationGraphWidget())
859 qCDebug(LOG_VisualizationGraphWidget())
860 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
860 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
861 qCDebug(LOG_VisualizationGraphWidget())
861 qCDebug(LOG_VisualizationGraphWidget())
862 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
862 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
863 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
863 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
864 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
864 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
865 }
865 }
866 }
866 }
867 }
867 }
868
868
869 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
869 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
870 const SqpRange &range)
870 const SqpRange &range)
871 {
871 {
872 auto it = impl->m_VariableToPlotMultiMap.find(variable);
872 auto it = impl->m_VariableToPlotMultiMap.find(variable);
873 if (it != impl->m_VariableToPlotMultiMap.end()) {
873 if (it != impl->m_VariableToPlotMultiMap.end()) {
874 impl->updateData(it->second, variable->dataSeries(), range);
874 impl->updateData(it->second, variable->dataSeries(), range);
875 }
875 }
876 }
876 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved

Status change > Approved

You need to be logged in to leave comments. Login now