1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <QtCharts/QValueAxis> |
5 | #include <private/qvalueaxis_p.h> |
6 | #include <private/chartvalueaxisx_p.h> |
7 | #include <private/chartvalueaxisy_p.h> |
8 | #include <private/abstractdomain_p.h> |
9 | #include <private/polarchartvalueaxisangular_p.h> |
10 | #include <private/polarchartvalueaxisradial_p.h> |
11 | #include <private/chartdataset_p.h> |
12 | #include <private/chartpresenter_p.h> |
13 | #include <private/charttheme_p.h> |
14 | #include <private/charthelpers_p.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | /*! |
18 | \class QValueAxis |
19 | \inmodule QtCharts |
20 | \brief The QValueAxis class adds values to a chart's axes. |
21 | |
22 | A value axis can be set up to show an axis line with tick marks, grid lines, and shades. |
23 | The values on the axis are drawn at the positions of tick marks. |
24 | |
25 | The following example code illustrates how to use the QValueAxis class: |
26 | \code |
27 | QChartView *chartView = new QChartView; |
28 | QLineSeries *series = new QLineSeries; |
29 | // ... |
30 | chartView->chart()->addSeries(series); |
31 | |
32 | QValueAxis *axisX = new QValueAxis; |
33 | axisX->setRange(10, 20.5); |
34 | axisX->setTickCount(10); |
35 | axisX->setLabelFormat("%.2f"); |
36 | chartView->chart()->setAxisX(axisX, series); |
37 | \endcode |
38 | */ |
39 | /*! |
40 | \qmltype ValueAxis |
41 | \instantiates QValueAxis |
42 | \inqmlmodule QtCharts |
43 | |
44 | \inherits AbstractAxis |
45 | \brief Adds values to a chart's axes. |
46 | |
47 | The ValueAxis type can be set up to show an axis line with tick marks, grid lines, and shades. |
48 | The values on the axis are drawn at the positions of tick marks. |
49 | |
50 | The following example code illustrates how to use the ValueAxis type: |
51 | \code |
52 | ChartView { |
53 | ValueAxis { |
54 | id: xAxis |
55 | min: 0 |
56 | max: 10 |
57 | } |
58 | // Add a few series... |
59 | } |
60 | \endcode |
61 | */ |
62 | |
63 | /*! |
64 | \property QValueAxis::min |
65 | \brief The minimum value on the axis. |
66 | |
67 | When setting this property, the maximum value is adjusted if necessary, to ensure that |
68 | the range remains valid. |
69 | */ |
70 | /*! |
71 | \qmlproperty real ValueAxis::min |
72 | The minimum value on the axis. |
73 | |
74 | When setting this property, the maximum value is adjusted if necessary, to ensure that |
75 | the range remains valid. |
76 | */ |
77 | |
78 | /*! |
79 | \property QValueAxis::max |
80 | \brief The maximum value on the axis. |
81 | |
82 | When setting this property, the minimum value is adjusted if necessary, to ensure that |
83 | the range remains valid. |
84 | */ |
85 | /*! |
86 | \qmlproperty real ValueAxis::max |
87 | The maximum value on the axis. |
88 | |
89 | When setting this property, the minimum value is adjusted if necessary, to ensure that |
90 | the range remains valid. |
91 | */ |
92 | |
93 | /*! |
94 | \property QValueAxis::tickCount |
95 | \brief The number of tick marks on the axis. This indicates how many grid lines are drawn on the |
96 | chart. The default value is 5, and the number cannot be less than 2. |
97 | */ |
98 | /*! |
99 | \qmlproperty int ValueAxis::tickCount |
100 | The number of tick marks on the axis. This indicates how many grid lines are drawn on the |
101 | chart. The default value is 5, and the number cannot be less than 2. |
102 | */ |
103 | |
104 | /*! |
105 | \property QValueAxis::minorTickCount |
106 | \brief The number of minor tick marks on the axis. This indicates how many grid lines are drawn |
107 | between major ticks on the chart. Labels are not drawn for minor ticks. The default value is 0. |
108 | */ |
109 | /*! |
110 | \qmlproperty int ValueAxis::minorTickCount |
111 | The number of minor tick marks on the axis. This indicates how many grid lines are drawn |
112 | between major ticks on the chart. Labels are not drawn for minor ticks. The default value is 0. |
113 | */ |
114 | |
115 | /*! |
116 | \property QValueAxis::tickAnchor |
117 | \since 5.12 |
118 | \brief The base value where the dynamically placed tick marks and labels are started from. |
119 | */ |
120 | /*! |
121 | \qmlproperty real ValueAxis::tickAnchor |
122 | \since QtCharts 2.3 |
123 | The base value where the dynamically placed tick marks and labels are started from. |
124 | */ |
125 | |
126 | /*! |
127 | \property QValueAxis::tickInterval |
128 | \since 5.12 |
129 | \brief The interval between dynamically placed tick marks and labels. |
130 | */ |
131 | /*! |
132 | \qmlproperty real ValueAxis::tickInterval |
133 | \since QtCharts 2.3 |
134 | The interval between dynamically placed tick marks and labels. |
135 | */ |
136 | |
137 | /*! |
138 | \enum QValueAxis::TickType |
139 | |
140 | This enum describes how the ticks and labels are positioned on the axis. |
141 | |
142 | \value TicksDynamic Ticks are placed according to tickAnchor and tickInterval values. |
143 | \value TicksFixed Ticks are placed evenly across the axis range. The tickCount value |
144 | specifies the number of ticks. |
145 | */ |
146 | /*! |
147 | \property QValueAxis::tickType |
148 | \since 5.12 |
149 | \brief The positioning method of tick and labels. |
150 | */ |
151 | /*! |
152 | \qmlproperty enumeration ValueAxis::tickType |
153 | \since QtCharts 2.3 |
154 | |
155 | The positioning method of tick and labels. |
156 | |
157 | \value ValueAxis.TicksDynamic |
158 | Ticks are placed according to tickAnchor and tickInterval values. |
159 | \value ValueAxis.TicksFixed |
160 | Ticks are placed evenly across the axis range. The tickCount value specifies the number of ticks. |
161 | */ |
162 | |
163 | /*! |
164 | \property QValueAxis::labelFormat |
165 | \brief The label format of the axis. |
166 | |
167 | The format string supports the following conversion specifiers, length modifiers, and flags |
168 | provided by \c printf() in the standard C++ library: d, i, o, x, X, f, F, e, E, g, G, c. |
169 | |
170 | If QChart::localizeNumbers is \c true, the supported specifiers are limited to: |
171 | d, e, E, f, g, G, and i. Also, only the precision modifier is supported. The rest of the |
172 | formatting comes from the default QLocale of the application. |
173 | |
174 | \sa QString::asprintf() |
175 | */ |
176 | /*! |
177 | \qmlproperty string ValueAxis::labelFormat |
178 | |
179 | The format string supports the following conversion specifiers, length modifiers, and flags |
180 | provided by \c printf() in the standard C++ library: d, i, o, x, X, f, F, e, E, g, G, c. |
181 | |
182 | If \l{ChartView::localizeNumbers}{ChartView.localizeNumbers} is \c true, the supported |
183 | specifiers are limited to: d, e, E, f, g, G, and i. Also, only the precision modifier is |
184 | supported. The rest of the formatting comes from the default QLocale of the application. |
185 | |
186 | \sa QString::asprintf() |
187 | */ |
188 | |
189 | /*! |
190 | \fn void QValueAxis::minChanged(qreal min) |
191 | This signal is emitted when the minimum value of the axis, specified by \a min, changes. |
192 | */ |
193 | |
194 | /*! |
195 | \fn void QValueAxis::maxChanged(qreal max) |
196 | This signal is emitted when the maximum value of the axis, specified by \a max, changes. |
197 | */ |
198 | |
199 | /*! |
200 | \fn void QValueAxis::tickCountChanged(int tickCount) |
201 | This signal is emitted when the number of tick marks on the axis, specified by \a tickCount, |
202 | changes. |
203 | */ |
204 | |
205 | /*! |
206 | \fn void QValueAxis::minorTickCountChanged(int minorTickCount) |
207 | This signal is emitted when the number of minor tick marks on the axis, specified by |
208 | \a minorTickCount, changes. |
209 | */ |
210 | |
211 | /*! |
212 | \fn void QValueAxis::rangeChanged(qreal min, qreal max) |
213 | This signal is emitted when the minimum or maximum value of the axis, specified by \a min |
214 | and \a max, changes. |
215 | */ |
216 | |
217 | /*! |
218 | \qmlsignal ValueAxis::rangeChanged(string min, string max) |
219 | This signal is emitted when \a min or \a max value of the axis changes. |
220 | |
221 | The corresponding signal handler is \c onRangeChanged. |
222 | */ |
223 | |
224 | /*! |
225 | \fn void QValueAxis::labelFormatChanged(const QString &format) |
226 | This signal is emitted when the \a format of axis labels changes. |
227 | */ |
228 | |
229 | /*! |
230 | Constructs an axis object that is a child of \a parent. |
231 | */ |
232 | QValueAxis::QValueAxis(QObject *parent) : |
233 | QAbstractAxis(*new QValueAxisPrivate(this), parent) |
234 | { |
235 | |
236 | } |
237 | |
238 | /*! |
239 | \internal |
240 | */ |
241 | QValueAxis::QValueAxis(QValueAxisPrivate &d, QObject *parent) |
242 | : QAbstractAxis(d, parent) |
243 | { |
244 | |
245 | } |
246 | |
247 | /*! |
248 | Destroys the object. |
249 | */ |
250 | QValueAxis::~QValueAxis() |
251 | { |
252 | Q_D(QValueAxis); |
253 | if (d->m_chart) |
254 | d->m_chart->removeAxis(axis: this); |
255 | } |
256 | |
257 | void QValueAxis::setMin(qreal min) |
258 | { |
259 | Q_D(QValueAxis); |
260 | setRange(min, max: qMax(a: d->m_max, b: min)); |
261 | } |
262 | |
263 | qreal QValueAxis::min() const |
264 | { |
265 | Q_D(const QValueAxis); |
266 | return d->m_min; |
267 | } |
268 | |
269 | void QValueAxis::setMax(qreal max) |
270 | { |
271 | Q_D(QValueAxis); |
272 | setRange(min: qMin(a: d->m_min, b: max), max); |
273 | } |
274 | |
275 | qreal QValueAxis::max() const |
276 | { |
277 | Q_D(const QValueAxis); |
278 | return d->m_max; |
279 | } |
280 | |
281 | /*! |
282 | Sets the range from \a min to \a max on the axis. |
283 | If \a min is greater than \a max, this function returns without making any changes. |
284 | */ |
285 | void QValueAxis::setRange(qreal min, qreal max) |
286 | { |
287 | Q_D(QValueAxis); |
288 | d->setRange(min,max); |
289 | } |
290 | |
291 | void QValueAxis::setTickCount(int count) |
292 | { |
293 | Q_D(QValueAxis); |
294 | if (d->m_tickCount != count && count >= 2) { |
295 | d->m_tickCount = count; |
296 | emit tickCountChanged(tickCount: count); |
297 | } |
298 | } |
299 | |
300 | int QValueAxis::tickCount() const |
301 | { |
302 | Q_D(const QValueAxis); |
303 | return d->m_tickCount; |
304 | } |
305 | |
306 | void QValueAxis::setMinorTickCount(int count) |
307 | { |
308 | Q_D(QValueAxis); |
309 | if (d->m_minorTickCount != count && count >= 0) { |
310 | d->m_minorTickCount = count; |
311 | emit minorTickCountChanged(tickCount: count); |
312 | } |
313 | } |
314 | |
315 | int QValueAxis::minorTickCount() const |
316 | { |
317 | Q_D(const QValueAxis); |
318 | return d->m_minorTickCount; |
319 | } |
320 | |
321 | |
322 | void QValueAxis::setTickInterval(qreal interval) |
323 | { |
324 | Q_D(QValueAxis); |
325 | if (d->m_tickInterval != interval) { |
326 | d->m_tickInterval = interval; |
327 | emit tickIntervalChanged(interval); |
328 | } |
329 | } |
330 | |
331 | qreal QValueAxis::tickInterval() const |
332 | { |
333 | Q_D(const QValueAxis); |
334 | return d->m_tickInterval; |
335 | } |
336 | |
337 | void QValueAxis::setTickAnchor(qreal anchor) |
338 | { |
339 | Q_D(QValueAxis); |
340 | if (d->m_tickAnchor != anchor) { |
341 | d->m_tickAnchor = anchor; |
342 | emit tickAnchorChanged(anchor); |
343 | } |
344 | } |
345 | |
346 | qreal QValueAxis::tickAnchor() const |
347 | { |
348 | Q_D(const QValueAxis); |
349 | return d->m_tickAnchor; |
350 | } |
351 | |
352 | void QValueAxis::setTickType(QValueAxis::TickType type) |
353 | { |
354 | Q_D(QValueAxis); |
355 | if (d->m_tickType != type) { |
356 | d->m_tickType = type; |
357 | emit tickTypeChanged(type); |
358 | } |
359 | } |
360 | |
361 | QValueAxis::TickType QValueAxis::tickType() const |
362 | { |
363 | Q_D(const QValueAxis); |
364 | return d->m_tickType; |
365 | } |
366 | |
367 | void QValueAxis::setLabelFormat(const QString &format) |
368 | { |
369 | Q_D(QValueAxis); |
370 | d->m_format = format; |
371 | emit labelFormatChanged(format); |
372 | } |
373 | |
374 | QString QValueAxis::labelFormat() const |
375 | { |
376 | Q_D(const QValueAxis); |
377 | return d->m_format; |
378 | } |
379 | |
380 | /*! |
381 | Returns the type of the axis. |
382 | */ |
383 | QAbstractAxis::AxisType QValueAxis::type() const |
384 | { |
385 | return AxisTypeValue; |
386 | } |
387 | |
388 | /*! |
389 | \qmlmethod ValueAxis::applyNiceNumbers() |
390 | Modifies the current range and number of tick marks on the axis to look |
391 | \e nice. The algorithm considers numbers that can be expressed as a form of |
392 | 1*10^n, 2* 10^n, or 5*10^n to be nice numbers. These numbers are used for |
393 | setting spacing for the tick marks. |
394 | */ |
395 | |
396 | /*! |
397 | Modifies the current range and number of tick marks on the axis to look \e nice. The algorithm |
398 | considers numbers that can be expressed as a form of 1*10^n, 2* 10^n, or 5*10^n to be |
399 | nice numbers. These numbers are used for setting spacing for the tick marks. |
400 | |
401 | \sa setRange(), setTickCount() |
402 | */ |
403 | void QValueAxis::applyNiceNumbers() |
404 | { |
405 | Q_D(QValueAxis); |
406 | if(d->m_applying) return; |
407 | qreal min = d->m_min; |
408 | qreal max = d->m_max; |
409 | int ticks = d->m_tickCount; |
410 | AbstractDomain::looseNiceNumbers(min,max,ticksCount&: ticks); |
411 | d->m_applying=true; |
412 | d->setRange(min,max); |
413 | setTickCount(ticks); |
414 | d->m_applying=false; |
415 | } |
416 | |
417 | ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
418 | |
419 | QValueAxisPrivate::QValueAxisPrivate(QValueAxis *q) |
420 | : QAbstractAxisPrivate(q), |
421 | m_min(0), |
422 | m_max(0), |
423 | m_tickCount(5), |
424 | m_minorTickCount(0), |
425 | m_format(), |
426 | m_applying(false), |
427 | m_tickInterval(0.0), |
428 | m_tickAnchor(0.0), |
429 | m_tickType(QValueAxis::TicksFixed) |
430 | { |
431 | |
432 | } |
433 | |
434 | QValueAxisPrivate::~QValueAxisPrivate() |
435 | { |
436 | |
437 | } |
438 | |
439 | void QValueAxisPrivate::setMin(const QVariant &min) |
440 | { |
441 | Q_Q(QValueAxis); |
442 | bool ok; |
443 | qreal value = min.toReal(ok: &ok); |
444 | if (ok) |
445 | q->setMin(value); |
446 | } |
447 | |
448 | void QValueAxisPrivate::setMax(const QVariant &max) |
449 | { |
450 | Q_Q(QValueAxis); |
451 | bool ok; |
452 | qreal value = max.toReal(ok: &ok); |
453 | if (ok) |
454 | q->setMax(value); |
455 | } |
456 | |
457 | void QValueAxisPrivate::setRange(const QVariant &min, const QVariant &max) |
458 | { |
459 | Q_Q(QValueAxis); |
460 | bool ok1; |
461 | bool ok2; |
462 | qreal value1 = min.toReal(ok: &ok1); |
463 | qreal value2 = max.toReal(ok: &ok2); |
464 | if (ok1 && ok2) |
465 | q->setRange(min: value1, max: value2); |
466 | } |
467 | |
468 | void QValueAxisPrivate::setRange(qreal min, qreal max) |
469 | { |
470 | Q_Q(QValueAxis); |
471 | bool changed = false; |
472 | |
473 | if (min > max) |
474 | return; |
475 | |
476 | if (!isValidValue(x: min, y: max)) { |
477 | qWarning() << "Attempting to set invalid range for value axis: [" |
478 | << min << " - " << max << "]" ; |
479 | return; |
480 | } |
481 | |
482 | if (m_min != min) { |
483 | m_min = min; |
484 | changed = true; |
485 | emit q->minChanged(min); |
486 | } |
487 | |
488 | if (m_max != max) { |
489 | m_max = max; |
490 | changed = true; |
491 | emit q->maxChanged(max); |
492 | } |
493 | |
494 | if (changed) { |
495 | emit rangeChanged(min,max); |
496 | emit q->rangeChanged(min, max); |
497 | } |
498 | } |
499 | |
500 | void QValueAxisPrivate::initializeGraphics(QGraphicsItem *parent) |
501 | { |
502 | Q_Q(QValueAxis); |
503 | ChartAxisElement *axis(0); |
504 | |
505 | if (m_chart->chartType() == QChart::ChartTypeCartesian) { |
506 | if (orientation() == Qt::Vertical) |
507 | axis = new ChartValueAxisY(q,parent); |
508 | if (orientation() == Qt::Horizontal) |
509 | axis = new ChartValueAxisX(q,parent); |
510 | axis->setLabelsEditable(q->labelsEditable()); |
511 | } |
512 | |
513 | if (m_chart->chartType() == QChart::ChartTypePolar) { |
514 | if (orientation() == Qt::Vertical) |
515 | axis = new PolarChartValueAxisRadial(q, parent); |
516 | if (orientation() == Qt::Horizontal) |
517 | axis = new PolarChartValueAxisAngular(q, parent); |
518 | } |
519 | |
520 | m_item.reset(p: axis); |
521 | QAbstractAxisPrivate::initializeGraphics(parent); |
522 | } |
523 | |
524 | |
525 | void QValueAxisPrivate::initializeDomain(AbstractDomain *domain) |
526 | { |
527 | if (orientation() == Qt::Vertical) { |
528 | if (!qFuzzyIsNull(d: m_max - m_min)) |
529 | domain->setRangeY(min: m_min, max: m_max); |
530 | else |
531 | setRange(min: domain->minY(), max: domain->maxY()); |
532 | } |
533 | if (orientation() == Qt::Horizontal) { |
534 | if (!qFuzzyIsNull(d: m_max - m_min)) |
535 | domain->setRangeX(min: m_min, max: m_max); |
536 | else |
537 | setRange(min: domain->minX(), max: domain->maxX()); |
538 | } |
539 | } |
540 | |
541 | QT_END_NAMESPACE |
542 | |
543 | #include "moc_qvalueaxis.cpp" |
544 | #include "moc_qvalueaxis_p.cpp" |
545 | |