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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtgraphs/src/graphs2d/axis/barcategoryaxis/qbarcategoryaxis.cpp