1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCharts/QBarCategoryAxis>
5#include <private/qbarcategoryaxis_p.h>
6#include <private/chartbarcategoryaxisx_p.h>
7#include <private/chartbarcategoryaxisy_p.h>
8#include <private/abstractdomain_p.h>
9#include <QtCharts/QChart>
10#include <QtCore/QtMath>
11
12QT_BEGIN_NAMESPACE
13/*!
14 \class QBarCategoryAxis
15 \inmodule QtCharts
16 \brief The QBarCategoryAxis class adds categories to a chart's axes.
17
18 QBarCategoryAxis can be set up to show an axis line with tick marks, grid lines, and shades.
19 Categories are drawn between the ticks. It can be used also with a line series, as demonstrated
20 by the \l {Charts with Widgets Gallery}.
21
22 The following code illustrates how to use QBarCategoryAxis:
23 \code
24 QChartView *chartView = new QChartView;
25 QBarSeries *series = new QBarSeries;
26 // ...
27 chartView->chart()->addSeries(series);
28 chartView->chart()->createDefaultAxes();
29
30 QBarCategoryAxis *axisX = new QBarCategoryAxis;
31 QStringList categories;
32 categories << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun";
33 axisX->append(categories);
34 axisX->setRange("Feb", "May");
35 chartView->chart()->setAxisX(axisX, series);
36 \endcode
37*/
38
39/*!
40 \qmltype BarCategoryAxis
41 \instantiates QBarCategoryAxis
42 \inqmlmodule QtCharts
43
44 \inherits AbstractAxis
45
46 \brief Adds categories to a chart's axes.
47
48 The BarCategoryAxis type can be set up to show an axis line with tick marks, grid lines, and
49 shades. Categories are drawn between the ticks. It can be used also with a line series.
50
51 The following QML snippet illustrates how to use BarCategoryAxis:
52 \code
53 ChartView {
54 BarCategoryAxis {
55 id: categoryAxis
56 categories: ["Jan", "Feb", "Mar", "Apr", "May", "Jun" ]
57 }
58 // Add a few series...
59 }
60 \endcode
61*/
62
63/*!
64 \property QBarCategoryAxis::categories
65 \brief The categories of an axis.
66*/
67/*!
68 \qmlproperty QStringList BarCategoryAxis::categories
69 The categories of an axis.
70*/
71
72/*!
73 \property QBarCategoryAxis::min
74 \brief The minimum value on the axis.
75*/
76/*!
77 \qmlproperty string BarCategoryAxis::min
78 The minimum value on the axis.
79*/
80
81/*!
82 \property QBarCategoryAxis::max
83 \brief The maximum value on the axis.
84*/
85/*!
86 \qmlproperty string BarCategoryAxis::max
87 The maximum value on the axis.
88*/
89
90/*!
91 \property QBarCategoryAxis::count
92 \brief The number of categories of an axis.
93*/
94/*!
95 \qmlproperty int BarCategoryAxis::count
96 The number of categories of an axis.
97*/
98
99/*!
100 \fn void QBarCategoryAxis::categoriesChanged()
101 This signal is emitted when the categories of the axis change.
102*/
103
104/*!
105 \fn void QBarCategoryAxis::minChanged(const QString &min)
106 This signal is emitted when the \a min value of the axis changes.
107*/
108
109/*!
110 \fn void QBarCategoryAxis::maxChanged(const QString &max)
111 This signal is emitted when the \a max value of the axis changes.
112*/
113
114/*!
115 \fn void QBarCategoryAxis::countChanged()
116 This signal is emitted when the number of categories of an axis changes.
117*/
118
119/*!
120 \fn void QBarCategoryAxis::rangeChanged(const QString &min, const QString &max)
121 This signal is emitted when \a min or \a max value of the axis changes.
122*/
123
124/*!
125 \qmlsignal BarCategoryAxis::rangeChanged(string min, string max)
126 This signal is emitted when \a min or \a max value of the axis changes.
127
128 The corresponding signal handler is \c onRangeChanged.
129*/
130
131/*!
132 \qmlmethod void BarCategoryAxis::clear()
133 Removes all categories. Sets the maximum and minimum values of the axis range to QString::null.
134*/
135
136/*!
137 Constructs an axis object that is the child of \a parent.
138*/
139QBarCategoryAxis::QBarCategoryAxis(QObject *parent):
140 QAbstractAxis(*new QBarCategoryAxisPrivate(this), parent)
141{
142}
143
144/*!
145 Destroys the axis object.
146*/
147QBarCategoryAxis::~QBarCategoryAxis()
148{
149 Q_D(QBarCategoryAxis);
150 if (d->m_chart)
151 d->m_chart->removeAxis(axis: this);
152}
153
154/*!
155 \internal
156*/
157QBarCategoryAxis::QBarCategoryAxis(QBarCategoryAxisPrivate &d, QObject *parent)
158 : QAbstractAxis(d, parent)
159{
160
161}
162
163/*!
164 Appends \a categories to an axis. The maximum value on the axis will be changed
165 to match the last category in \a categories. If no categories were previously defined,
166 the minimum value on the axis will also be changed to match the first category in
167 \a categories.
168
169 A category has to be a valid QString and it cannot be duplicated. Duplicated
170 categories will not be appended.
171*/
172void QBarCategoryAxis::append(const QStringList &categories)
173{
174 if (categories.isEmpty())
175 return;
176
177 Q_D(QBarCategoryAxis);
178
179 int count = d->m_categories.size();
180
181 foreach(QString category, categories) {
182 if (!d->m_categories.contains(str: category) && !category.isNull()) {
183 d->m_categories.append(t: category);
184 }
185 }
186
187 if (d->m_categories.size() == count)
188 return;
189
190 if (count == 0)
191 setRange(minCategory: d->m_categories.first(), maxCategory: d->m_categories.last());
192 else
193 setRange(minCategory: d->m_minCategory, maxCategory: d->m_categories.last());
194
195 emit categoriesChanged();
196 emit countChanged();
197}
198
199/*!
200 Appends \a category to an axis. The maximum value on the axis will be changed
201 to match the last \a category. If no categories were previously defined, the minimum
202 value on the axis will also be changed to match \a category.
203
204 A category has to be a valid QString and it cannot be duplicated. Duplicated
205 categories will not be appended.
206*/
207void QBarCategoryAxis::append(const QString &category)
208{
209 Q_D(QBarCategoryAxis);
210
211 int count = d->m_categories.size();
212
213 if (!d->m_categories.contains(str: category) && !category.isNull())
214 d->m_categories.append(t: category);
215
216 if (d->m_categories.size() == count)
217 return;
218
219 if (count == 0)
220 setRange(minCategory: d->m_categories.last(), maxCategory: d->m_categories.last());
221 else
222 setRange(minCategory: d->m_minCategory, maxCategory: d->m_categories.last());
223
224 emit categoriesChanged();
225 emit countChanged();
226}
227
228/*!
229 Removes \a category from the axis. Removing a category that currently sets the
230 maximum or minimum value on the axis will affect the axis range.
231*/
232void QBarCategoryAxis::remove(const QString &category)
233{
234 Q_D(QBarCategoryAxis);
235
236 if (d->m_categories.contains(str: category)) {
237 d->m_categories.removeAt(i: d->m_categories.indexOf(str: category));
238 if (!d->m_categories.isEmpty()) {
239 if (d->m_minCategory == category) {
240 setRange(minCategory: d->m_categories.first(), maxCategory: d->m_maxCategory);
241 } else if (d->m_maxCategory == category) {
242 setRange(minCategory: d->m_minCategory, maxCategory: d->m_categories.last());
243 } else {
244 d->updateCategoryDomain();
245 }
246 } else {
247 setRange(minCategory: QString(), maxCategory: QString());
248 }
249 emit categoriesChanged();
250 emit countChanged();
251 }
252}
253
254/*!
255 Inserts \a category to the axis at \a index. \a category has to be a valid QString
256 and it cannot be duplicated. If \a category is prepended or appended to other
257 categories, the minimum and maximum values on the axis are updated accordingly.
258*/
259void QBarCategoryAxis::insert(int index, const QString &category)
260{
261 Q_D(QBarCategoryAxis);
262
263 int count = d->m_categories.size();
264
265 if (!d->m_categories.contains(str: category) && !category.isNull())
266 d->m_categories.insert(i: index, t: category);
267
268 if (d->m_categories.size() == count)
269 return;
270
271 if (count == 0) {
272 setRange(minCategory: d->m_categories.first(), maxCategory: d->m_categories.first());
273 } else if (index == 0) {
274 setRange(minCategory: d->m_categories.first(), maxCategory: d->m_maxCategory);
275 } else if (index == count) {
276 setRange(minCategory: d->m_minCategory, maxCategory: d->m_categories.last());
277 } else {
278 d->updateCategoryDomain();
279 }
280
281 emit categoriesChanged();
282 emit countChanged();
283}
284
285/*!
286 Replaces \a oldCategory with \a newCategory. If \a oldCategory does not exist on the axis,
287 nothing is done. \a newCategory has to be a valid QString and it cannot be duplicated. If
288 the minimum or maximum category is replaced, the minimum and maximum values on the axis are
289 updated accordingly.
290*/
291void QBarCategoryAxis::replace(const QString &oldCategory, const QString &newCategory)
292{
293 Q_D(QBarCategoryAxis);
294
295 int pos = d->m_categories.indexOf(str: oldCategory);
296
297 if (pos != -1 && !d->m_categories.contains(str: newCategory) && !newCategory.isNull()) {
298 d->m_categories.replace(i: pos, t: newCategory);
299 if (d->m_minCategory == oldCategory)
300 setRange(minCategory: newCategory, maxCategory: d->m_maxCategory);
301 else if (d->m_maxCategory == oldCategory)
302 setRange(minCategory: d->m_minCategory, maxCategory: newCategory);
303
304 emit categoriesChanged();
305 emit countChanged();
306 }
307}
308
309/*!
310 Removes all categories. Sets the maximum and minimum values of the axis range to QString::null.
311 */
312void QBarCategoryAxis::clear()
313{
314 Q_D(QBarCategoryAxis);
315 d->m_categories.clear();
316 setRange(minCategory: QString(), maxCategory: QString());
317 emit categoriesChanged();
318 emit countChanged();
319}
320
321/*!
322 Sets \a categories and discards the old ones. The axis range is adjusted to match the
323 first and last category in \a categories.
324
325 A category has to be a valid QString and it cannot be duplicated.
326*/
327void QBarCategoryAxis::setCategories(const QStringList &categories)
328{
329 Q_D(QBarCategoryAxis);
330 d->m_categories.clear();
331 d->m_minCategory = QString();
332 d->m_maxCategory = QString();
333 d->m_min = 0;
334 d->m_max = 0;
335 d->m_count = 0;
336 append(categories);
337}
338
339/*!
340 Returns categories.
341*/
342QStringList QBarCategoryAxis::categories()
343{
344 Q_D(QBarCategoryAxis);
345 return d->m_categories;
346}
347
348/*!
349 Returns the number of categories.
350 */
351int QBarCategoryAxis::count() const
352{
353 Q_D(const QBarCategoryAxis);
354 return d->m_categories.size();
355}
356
357/*!
358 Returns the category at \a index. The index must be valid.
359*/
360QString QBarCategoryAxis::at(int index) const
361{
362 Q_D(const QBarCategoryAxis);
363 return d->m_categories.at(i: index);
364}
365
366/*!
367 Sets the minimum category to \a min.
368*/
369void QBarCategoryAxis::setMin(const QString &min)
370{
371 Q_D(QBarCategoryAxis);
372 d->setRange(minCategory: min, maxCategory: d->m_maxCategory);
373}
374
375/*!
376 Returns the minimum category.
377*/
378QString QBarCategoryAxis::min() const
379{
380 Q_D(const QBarCategoryAxis);
381 return d->m_minCategory;
382}
383
384/*!
385 Sets the maximum category to \a max.
386*/
387void QBarCategoryAxis::setMax(const QString &max)
388{
389 Q_D(QBarCategoryAxis);
390 d->setRange(minCategory: d->m_minCategory, maxCategory: max);
391}
392
393/*!
394 Returns the maximum category.
395*/
396QString QBarCategoryAxis::max() const
397{
398 Q_D(const QBarCategoryAxis);
399 return d->m_maxCategory;
400}
401
402/*!
403 Sets the axis range from \a minCategory to \a maxCategory.
404*/
405void QBarCategoryAxis::setRange(const QString &minCategory, const QString &maxCategory)
406{
407 Q_D(QBarCategoryAxis);
408 d->setRange(minCategory,maxCategory);
409}
410
411/*!
412 Returns the type of the axis.
413*/
414QAbstractAxis::AxisType QBarCategoryAxis::type() const
415{
416 return AxisTypeBarCategory;
417}
418
419//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
420
421QBarCategoryAxisPrivate::QBarCategoryAxisPrivate(QBarCategoryAxis *q)
422 : QAbstractAxisPrivate(q),
423 m_min(0.0),
424 m_max(0.0),
425 m_count(0)
426{
427
428}
429
430QBarCategoryAxisPrivate::~QBarCategoryAxisPrivate()
431{
432
433}
434
435void QBarCategoryAxisPrivate::setMin(const QVariant &min)
436{
437 setRange(min, max: m_maxCategory);
438}
439
440void QBarCategoryAxisPrivate::setMax(const QVariant &max)
441{
442 setRange(min: m_minCategory, max);
443}
444
445void QBarCategoryAxisPrivate::setRange(const QVariant &min, const QVariant &max)
446{
447 QString value1 = min.toString();
448 QString value2 = max.toString();
449 setRange(minCategory: value1, maxCategory: value2);
450}
451
452void QBarCategoryAxisPrivate::setRange(qreal min, qreal max)
453{
454 Q_Q(QBarCategoryAxis);
455
456 bool categoryChanged = false;
457 bool changed = false;
458
459 if (min > max)
460 return;
461
462 if (!qFuzzyIsNull(d: m_min - min)) {
463 m_min = min;
464 changed = true;
465
466 int imin = m_min + 0.5;
467 if (imin >= 0 && imin < m_categories.size()) {
468 QString minCategory = m_categories.at(i: imin);
469 if (m_minCategory != minCategory && !minCategory.isEmpty()) {
470 m_minCategory = minCategory;
471 categoryChanged = true;
472 emit q->minChanged(min: minCategory);
473 }
474 }
475
476 }
477
478 if (!qFuzzyIsNull(d: m_max - max)) {
479 m_max = max;
480 changed = true;
481
482 int imax = m_max - 0.5;
483 if (imax >= 0 && imax < m_categories.size()) {
484 QString maxCategory = m_categories.at(i: imax);
485 if (m_maxCategory != maxCategory && !maxCategory.isEmpty()) {
486 m_maxCategory = maxCategory;
487 categoryChanged = true;
488 emit q->maxChanged(max: maxCategory);
489 }
490 }
491 }
492
493 if (categoryChanged){
494 emit q->rangeChanged(min: m_minCategory, max: m_maxCategory);
495 }
496
497 if (changed) {
498 emit rangeChanged(min: m_min,max: m_max);
499 }
500}
501
502void QBarCategoryAxisPrivate::setRange(const QString &minCategory, const QString &maxCategory)
503{
504 Q_Q(QBarCategoryAxis);
505 bool changed = false;
506
507 //special case in case or clearing all categories
508 if (minCategory.isNull() && maxCategory.isNull()) {
509 m_minCategory = minCategory;
510 m_maxCategory = maxCategory;
511 m_min = 0;
512 m_max = 0;
513 m_count = 0;
514 emit q->minChanged(min: minCategory);
515 emit q->maxChanged(max: maxCategory);
516 emit q->rangeChanged(min: m_minCategory, max: m_maxCategory);
517 emit rangeChanged(min: m_min,max: m_max);
518 return;
519 }
520
521 if (m_categories.indexOf(str: maxCategory) < m_categories.indexOf(str: minCategory))
522 return;
523
524 if (!minCategory.isNull() && (m_minCategory != minCategory || m_minCategory.isNull())
525 && m_categories.contains(str: minCategory)) {
526 m_minCategory = minCategory;
527 m_min = m_categories.indexOf(str: m_minCategory) - 0.5;
528 changed = true;
529 emit q->minChanged(min: minCategory);
530 }
531
532 if (!maxCategory.isNull() && (m_maxCategory != maxCategory || m_maxCategory.isNull())
533 && m_categories.contains(str: maxCategory)) {
534 m_maxCategory = maxCategory;
535 m_max = m_categories.indexOf(str: m_maxCategory) + 0.5;
536 changed = true;
537 emit q->maxChanged(max: maxCategory);
538 }
539
540 if (changed) {
541 m_count = m_max - m_min;
542 emit q->rangeChanged(min: m_minCategory, max: m_maxCategory);
543 emit rangeChanged(min: m_min,max: m_max);
544 }
545}
546
547void QBarCategoryAxisPrivate::initializeGraphics(QGraphicsItem* parent)
548{
549 Q_Q(QBarCategoryAxis);
550 ChartAxisElement* axis(0);
551 if (orientation() == Qt::Vertical)
552 axis = new ChartBarCategoryAxisY(q,parent);
553 if (orientation() == Qt::Horizontal)
554 axis = new ChartBarCategoryAxisX(q,parent);
555
556 m_item.reset(p: axis);
557 QAbstractAxisPrivate::initializeGraphics(parent);
558}
559
560void QBarCategoryAxisPrivate::updateCategoryDomain()
561{
562 bool changed = false;
563
564 qreal tmpMin = m_categories.indexOf(str: m_minCategory) - 0.5;
565 if (!qFuzzyIsNull(d: m_min - tmpMin)) {
566 m_min = tmpMin;
567 changed = true;
568 }
569 qreal tmpMax = m_categories.indexOf(str: m_maxCategory) + 0.5;
570 if (!qFuzzyIsNull(d: m_max - tmpMax)) {
571 m_max = tmpMax;
572 changed = true;
573 }
574 m_count = m_max - m_min;
575
576 if (changed)
577 emit rangeChanged(min: m_min,max: m_max);
578}
579
580
581void QBarCategoryAxisPrivate::initializeDomain(AbstractDomain *domain)
582{
583 Q_Q(QBarCategoryAxis);
584 if (m_max == m_min) {
585 int min;
586 int max;
587 if (orientation() == Qt::Vertical) {
588 min = domain->minY() + 0.5;
589 max = domain->maxY() - 0.5;
590 } else {
591 min = domain->minX() + 0.5;
592 max = domain->maxX() - 0.5;
593 }
594
595 if (min > 0 && min < m_categories.size() && max > 0 && max < m_categories.size())
596 q->setRange(minCategory: m_categories.at(i: min), maxCategory: m_categories.at(i: max));
597 } else {
598 if (orientation() == Qt::Vertical)
599 domain->setRangeY(min: m_min, max: m_max);
600 else
601 domain->setRangeX(min: m_min, max: m_max);
602 }
603}
604
605QT_END_NAMESPACE
606
607#include "moc_qbarcategoryaxis.cpp"
608#include "moc_qbarcategoryaxis_p.cpp"
609

source code of qtcharts/src/charts/axis/barcategoryaxis/qbarcategoryaxis.cpp