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

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