##// END OF EJS Templates
Separate the initialization of the properties of the graph of the update of the units of the graph....
Alexandre Leroux -
r1308:41b7c6aab8be
parent child
Show More
@@ -1,42 +1,44
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 class Variable;
12 13 class VisualizationGraphWidget;
13 14
14 15 class VisualizationGraphRenderingDelegate {
15 16 public:
16 17 /// Ctor
17 18 /// @param graphWidget the graph widget to which the delegate is associated
18 19 /// @remarks the graph widget must exist throughout the life cycle of the delegate
19 20 explicit VisualizationGraphRenderingDelegate(VisualizationGraphWidget &graphWidget);
20 21
21 22 void onMouseDoubleClick(QMouseEvent *event) noexcept;
22 23 void onMouseMove(QMouseEvent *event) noexcept;
23 24 /// Updates rendering when data of plot changed
24 25 void onPlotUpdated() noexcept;
25 26
26 /// Sets properties of the plot's axes from the data series passed as parameter
27 void setAxesProperties(std::shared_ptr<IDataSeries> dataSeries) noexcept;
27 /// Sets units of the plot's axes according to the properties of the variable passed as
28 /// parameter
29 void setAxesUnits(const Variable &variable) noexcept;
28 30
29 /// Sets rendering properties of the plottables passed as parameter, from the data series that
31 /// Sets graph properties of the plottables passed as parameter, from the variable that
30 32 /// generated these
31 void setPlottablesProperties(std::shared_ptr<IDataSeries> dataSeries,
32 PlottablesMap &plottables) noexcept;
33 void setGraphProperties(const Variable &variable, PlottablesMap &plottables) noexcept;
34
33 35
34 36 /// Shows or hides graph overlay (name, close button, etc.)
35 37 void showGraphOverlay(bool show) noexcept;
36 38
37 39 private:
38 40 class VisualizationGraphRenderingDelegatePrivate;
39 41 spimpl::unique_impl_ptr<VisualizationGraphRenderingDelegatePrivate> impl;
40 42 };
41 43
42 44 #endif // SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
@@ -1,332 +1,338
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 #include <Variable/Variable.h>
12 13
13 14 #include <SqpApplication.h>
14 15
15 16 namespace {
16 17
17 18 /// Name of the axes layer in QCustomPlot
18 19 const auto AXES_LAYER = QStringLiteral("axes");
19 20
20 21 /// Icon used to show x-axis properties
21 22 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
22 23
23 24 /// Name of the overlay layer in QCustomPlot
24 25 const auto OVERLAY_LAYER = QStringLiteral("overlay");
25 26
26 27 /// Pixmap used to show x-axis properties
27 28 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
28 29
29 30 /// Tooltip format for graphs
30 31 const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
31 32
32 33 /// Tooltip format for colormaps
33 34 const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
34 35
35 36 /// Offset used to shift the tooltip of the mouse
36 37 const auto TOOLTIP_OFFSET = QPoint{20, 20};
37 38
38 39 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
39 40 const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
40 41
41 42 /// Timeout after which the tooltip is displayed
42 43 const auto TOOLTIP_TIMEOUT = 500;
43 44
44 45 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
45 46 {
46 47 tracer.setInterpolating(false);
47 48 tracer.setStyle(QCPItemTracer::tsCircle);
48 49 tracer.setSize(3);
49 50 tracer.setPen(QPen(Qt::black));
50 51 tracer.setBrush(Qt::black);
51 52 tracer.setSelectable(false);
52 53 }
53 54
54 55 QPixmap pixmap(const QString &iconPath) noexcept
55 56 {
56 57 return QIcon{iconPath}.pixmap(QSize{16, 16});
57 58 }
58 59
59 60 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
60 61 {
61 62 // Icon
62 63 pixmap.setPixmap(
63 64 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
64 65
65 66 // Position
66 67 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
67 68 pixmap.topLeft->setCoords(1, 0);
68 69 pixmap.setClipToAxisRect(false);
69 70
70 71 // Can be selected
71 72 pixmap.setSelectable(true);
72 73 }
73 74
74 75 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
75 76 {
76 77 // Icon
77 78 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
78 79
79 80 // Position
80 81 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
81 82 itemPixmap.topLeft->setCoords(0, 1);
82 83 itemPixmap.setClipToAxisRect(false);
83 84
84 85 // Can be selected
85 86 itemPixmap.setSelectable(true);
86 87 }
87 88
88 89 void initTitleTextStyle(QCPItemText &text) noexcept
89 90 {
90 91 // Font and background styles
91 92 text.setColor(Qt::gray);
92 93 text.setBrush(Qt::white);
93 94
94 95 // Position
95 96 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
96 97 text.position->setType(QCPItemPosition::ptAxisRectRatio);
97 98 text.position->setCoords(0.5, 0);
98 99 text.setSelectable(false);
99 100 }
100 101
101 102 /**
102 103 * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
103 104 * This method handles the fact that a colormap axis can be logarithmic or linear.
104 105 * @param colormap the colormap for which to calculate the index
105 106 * @param coord the coord to convert to cell index
106 107 * @param xCoord calculates the x index if true, calculates y index if false
107 108 * @return the cell index
108 109 */
109 110 int colorMapCellIndex(const QCPColorMap &colormap, double coord, bool xCoord)
110 111 {
111 112 // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
112 113 auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
113 114 == QCPAxis::stLogarithmic;
114 115
115 116 if (isLogarithmic) {
116 117 // For a logarithmic axis we can't use the conversion method of colormap, so we calculate
117 118 // the index manually based on the position of the coordinate on the axis
118 119
119 120 // Gets the axis range and the number of values between range bounds to calculate the step
120 121 // between each value of the range
121 122 auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
122 123 auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
123 124 auto valueStep
124 125 = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
125 126
126 127 // According to the coord position, calculates the closest index in the range
127 128 return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
128 129 }
129 130 else {
130 131 // For a linear axis, we use the conversion method of colormap
131 132 int index;
132 133 if (xCoord) {
133 134 colormap.data()->coordToCell(coord, 0., &index, nullptr);
134 135 }
135 136 else {
136 137 colormap.data()->coordToCell(0., coord, nullptr, &index);
137 138 }
138 139
139 140 return index;
140 141 }
141 142 }
142 143
143 144 } // namespace
144 145
145 146 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
146 147 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
147 148 : m_Plot{graphWidget.plot()},
148 149 m_PointTracer{new QCPItemTracer{&m_Plot}},
149 150 m_TracerTimer{},
150 151 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
151 152 m_TitleText{new QCPItemText{&m_Plot}},
152 153 m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
153 154 m_ShowXAxis{true},
154 155 m_XAxisLabel{},
155 156 m_ColorScale{SqpColorScale{m_Plot}}
156 157 {
157 158 initPointTracerStyle(*m_PointTracer);
158 159
159 160 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
160 161 m_TracerTimer.setSingleShot(true);
161 162
162 163 // Inits "close button" in plot overlay
163 164 m_ClosePixmap->setLayer(OVERLAY_LAYER);
164 165 initClosePixmapStyle(*m_ClosePixmap);
165 166
166 167 // Connects pixmap selection to graph widget closing
167 168 QObject::connect(&m_Plot, &QCustomPlot::itemClick,
168 169 [&graphWidget, this](auto item, auto mouseEvent) {
169 170 if (item == m_ClosePixmap) {
170 171 graphWidget.close();
171 172 }
172 173 });
173 174
174 175 // Inits graph name in plot overlay
175 176 m_TitleText->setLayer(OVERLAY_LAYER);
176 177 m_TitleText->setText(graphWidget.name());
177 178 initTitleTextStyle(*m_TitleText);
178 179
179 180 // Inits "show x-axis button" in plot overlay
180 181 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
181 182 initXAxisPixmapStyle(*m_XAxisPixmap);
182 183
183 184 // Connects pixmap selection to graph x-axis showing/hiding
184 185 QObject::connect(&m_Plot, &QCustomPlot::itemClick, [this](auto item, auto mouseEvent) {
185 186 if (m_XAxisPixmap == item) {
186 187 // Changes the selection state and refreshes the x-axis
187 188 m_ShowXAxis = !m_ShowXAxis;
188 189 this->updateXAxisState();
189 190 m_Plot.layer(AXES_LAYER)->replot();
190 191
191 192 // Deselects the x-axis pixmap and updates icon
192 193 m_XAxisPixmap->setPixmap(
193 194 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
194 195 m_Plot.layer(OVERLAY_LAYER)->replot();
195 196 }
196 197 });
197 198 }
198 199
199 200 /// Updates state of x-axis according to the current selection of x-axis pixmap
200 201 /// @remarks the method doesn't call plot refresh
201 202 void updateXAxisState() noexcept
202 203 {
203 204 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
204 205 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
205 206 }
206 207
207 208 QCustomPlot &m_Plot;
208 209 QCPItemTracer *m_PointTracer;
209 210 QTimer m_TracerTimer;
210 211 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
211 212 QCPItemText *m_TitleText; /// Graph's title
212 213 QCPItemPixmap *m_XAxisPixmap;
213 214 bool m_ShowXAxis; /// X-axis properties are shown or hidden
214 215 QString m_XAxisLabel;
215 216 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
216 217 };
217 218
218 219 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
219 220 VisualizationGraphWidget &graphWidget)
220 221 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
221 222 {
222 223 }
223 224
224 225 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) noexcept
225 226 {
226 227 // Opens color scale editor if color scale is double clicked
227 228 auto colorScale = static_cast<QCPColorScale *>(impl->m_Plot.layoutElementAt(event->pos()));
228 229 if (impl->m_ColorScale.m_Scale == colorScale) {
229 230 if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) {
230 231 impl->m_Plot.replot();
231 232 }
232 233 }
233 234 }
234 235
235 236 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
236 237 {
237 238 // Cancels pending refresh
238 239 impl->m_TracerTimer.disconnect();
239 240
240 241 // Reinits tracers
241 242 impl->m_PointTracer->setGraph(nullptr);
242 243 impl->m_PointTracer->setVisible(false);
243 244 impl->m_Plot.replot();
244 245
245 246 QString tooltip{};
246 247
247 248 // Gets the graph under the mouse position
248 249 auto eventPos = event->pos();
249 250 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
250 251 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
251 252 auto graphData = graph->data();
252 253
253 254 // Gets the closest data point to the mouse
254 255 auto graphDataIt = graphData->findBegin(mouseKey);
255 256 if (graphDataIt != graphData->constEnd()) {
256 257 // Sets tooltip
257 258 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
258 259 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
259 260 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
260 261
261 262 // Displays point tracer
262 263 impl->m_PointTracer->setGraph(graph);
263 264 impl->m_PointTracer->setGraphKey(graphDataIt->key);
264 265 impl->m_PointTracer->setLayer(
265 266 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
266 267 impl->m_PointTracer->setVisible(true);
267 268 impl->m_Plot.replot();
268 269 }
269 270 }
270 271 else if (auto colorMap = qobject_cast<QCPColorMap *>(impl->m_Plot.plottableAt(eventPos))) {
271 272 // Gets x and y coords
272 273 auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
273 274 auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
274 275
275 276 // Calculates x and y cell indexes, and retrieves the underlying value
276 277 auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
277 278 auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
278 279 auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
279 280
280 281 // Sets tooltips
281 282 tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
282 283 formatValue(y, *colorMap->valueAxis()),
283 284 formatValue(value, *colorMap->colorScale()->axis()));
284 285 }
285 286
286 287 if (!tooltip.isEmpty()) {
287 288 // Starts timer to show tooltip after timeout
288 289 auto showTooltip = [tooltip, eventPos, this]() {
289 290 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
290 291 &impl->m_Plot, TOOLTIP_RECT);
291 292 };
292 293
293 294 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
294 295 impl->m_TracerTimer.start();
295 296 }
296 297 }
297 298
298 299 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
299 300 {
300 301 // Updates color scale bounds
301 302 impl->m_ColorScale.updateDataRange();
302 303 impl->m_Plot.replot();
303 304 }
304 305
305 void VisualizationGraphRenderingDelegate::setAxesProperties(
306 std::shared_ptr<IDataSeries> dataSeries) noexcept
306 void VisualizationGraphRenderingDelegate::setAxesUnits(const Variable &variable) noexcept
307 307 {
308 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
309 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
310 308
311 auto axisHelper = IAxisHelperFactory::create(dataSeries);
312 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
309 auto axisHelper = IAxisHelperFactory::create(variable);
310 axisHelper->setUnits(impl->m_Plot, impl->m_ColorScale);
311
312 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
313 impl->m_XAxisLabel = impl->m_Plot.xAxis->label();
313 314
314 315 // Updates x-axis state
315 316 impl->updateXAxisState();
316 317
317 318 impl->m_Plot.layer(AXES_LAYER)->replot();
318 319 }
319 320
320 void VisualizationGraphRenderingDelegate::setPlottablesProperties(
321 std::shared_ptr<IDataSeries> dataSeries, PlottablesMap &plottables) noexcept
321 void VisualizationGraphRenderingDelegate::setGraphProperties(const Variable &variable,
322 PlottablesMap &plottables) noexcept
322 323 {
323 auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries);
324 // Axes' properties
325 auto axisHelper = IAxisHelperFactory::create(variable);
326 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
327
328 // Plottables' properties
329 auto plottablesHelper = IPlottablesHelperFactory::create(variable);
324 330 plottablesHelper->setProperties(plottables);
325 331 }
326 332
327 333 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
328 334 {
329 335 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
330 336 overlay->setVisible(show);
331 337 overlay->replot();
332 338 }
General Comments 0
You need to be logged in to leave comments. Login now