1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
5
6#include <QtCharts/QAbstractBarSeries>
7#include <private/qabstractbarseries_p.h>
8#include <QtCharts/QBarSet>
9#include <private/qbarset_p.h>
10#include <private/abstractdomain_p.h>
11#include <private/chartdataset_p.h>
12#include <private/charttheme_p.h>
13#include <QtCharts/QValueAxis>
14#include <QtCharts/QBarCategoryAxis>
15#include <QtCharts/QBarLegendMarker>
16#include <private/baranimation_p.h>
17#include <private/abstractbarchartitem_p.h>
18#include <private/qchart_p.h>
19
20QT_BEGIN_NAMESPACE
21
22/*!
23 \class QAbstractBarSeries
24 \inmodule QtCharts
25 \brief The QAbstractBarSeries class is an abstract parent class for all bar series classes.
26
27 In bar charts, bars are defined as bar sets that contain one data value for each category.
28 The position of a bar is specified by the category and its height by the data value. Bar
29 series that contain multiple bar sets group together bars that belong to the same category.
30 The way the bars are displayed is determined by the subclass of this class chosen to create
31 the bar chart.
32
33 If a QValueAxis is used instead of QBarCategoryAxis for the main bar axis, the bars are
34 grouped around the index value of the category.
35
36 See the \l {Charts with Widgets Gallery} to learn how to use the QBarSeries class to create a simple bar chart.
37 \image examples_barchart.png
38
39 \sa QBarSet, QBarSeries, QStackedBarSeries, QPercentBarSeries
40 \sa QHorizontalBarSeries, QHorizontalStackedBarSeries, QHorizontalPercentBarSeries
41*/
42/*!
43 \qmltype AbstractBarSeries
44 \nativetype QAbstractBarSeries
45 \inqmlmodule QtCharts
46
47 \inherits AbstractSeries
48
49 \brief An abstract parent type for all bar series types.
50
51 In bar charts, bars are defined as bar sets that contain one data value for each category.
52 The position of a bar is specified by the category and its height by the data value. Bar
53 series that contain multiple bar sets group together bars that belong to the same category.
54 The way the bars are displayed is determined by the type chosen to create the bar chart:
55 BarSeries, StackedBarSeries, PercentBarSeries, HorizontalBarSeries, HorizontalStackedBarSeries,
56 or HorizontalPercentBarSeries.
57
58 If a ValueAxis type is used instead of a BarCategoryAxis type for the main bar axis, the
59 bars are grouped around the index value of the category.
60
61 The following QML code snippet shows how to use the BarSeries and BarCategoryAxis type
62 to create a simple bar chart:
63 \snippet qmlchartsgallery/qml/BarSeries.qml 1
64
65 \beginfloatleft
66 \image examples_qmlchart6.png
67 \endfloat
68 \clearfloat
69*/
70
71/*!
72 \qmlproperty AbstractAxis AbstractBarSeries::axisX
73 The x-axis used for the series. If you leave both axisX and axisXTop undefined, a
74 BarCategoryAxis is created for the series.
75 \sa axisXTop
76*/
77
78/*!
79 \qmlproperty AbstractAxis AbstractBarSeries::axisY
80 The y-axis used for the series. If you leave both axisY and axisYRight undefined, a
81 ValueAxis is created for the series.
82 \sa axisYRight
83*/
84
85/*!
86 \qmlproperty AbstractAxis AbstractBarSeries::axisXTop
87 The x-axis used for the series, drawn on top of the chart view.
88
89 \note You can only provide either axisX or axisXTop, but not both.
90 \sa axisX
91*/
92
93/*!
94 \qmlproperty AbstractAxis AbstractBarSeries::axisYRight
95 The y-axis used for the series, drawn to the right of the chart view.
96
97 \note You can only provide either axisY or axisYRight, but not both.
98 \sa axisY
99*/
100
101/*!
102 \property QAbstractBarSeries::barWidth
103 \brief The width of the bars of the series.
104
105 The unit of width is the unit of the x-axis. The minimum width for bars is zero, and negative
106 values are treated as zero. Setting the width to zero means that the width of the bar on the
107 screen is one pixel regardless of the scale of the x-axis. Bars wider than zero are scaled
108 using the x-axis scale.
109
110 \note When used with QBarSeries, this value specifies the width of a group of bars instead of
111 that of a single bar.
112 \sa QBarSeries
113*/
114/*!
115 \qmlproperty real AbstractBarSeries::barWidth
116 The unit of width is the unit of the x-axis. The minimum width for bars is zero, and negative
117 values are treated as zero. Setting the width to zero means that the width of the bar on the
118 screen is one pixel regardless of the scale of the x-axis. Bars wider than zero are scaled
119 using the x-axis scale.
120
121 \note When used with the BarSeries type, this value specifies the width of a group of bars
122 instead of that of a single bar.
123*/
124
125/*!
126 \property QAbstractBarSeries::count
127 \brief The number of bar sets in a bar series.
128*/
129/*!
130 \qmlproperty int AbstractBarSeries::count
131 The number of bar sets in a bar series.
132*/
133
134/*!
135 \property QAbstractBarSeries::labelsVisible
136 \brief The visibility of the labels in a bar series.
137*/
138/*!
139 \qmlproperty bool AbstractBarSeries::labelsVisible
140 The visibility of the labels in a bar series.
141*/
142
143/*!
144 \property QAbstractBarSeries::labelsFormat
145 \brief The format used for showing labels in a bar series.
146
147 QAbstractBarSeries supports the following format tag:
148 \table
149 \row
150 \li @value \li The value of the bar
151 \endtable
152
153 For example, the following usage of the format tags would produce labels that show the value
154 followed by the unit (u):
155 \code
156 series->setLabelsFormat("@value u");
157 \endcode
158
159 By default, the labels show the value of the bar. For the percent bar series, \e % is added
160 after the value. The labels are shown on the plot area, if the bars are close to each other,
161 the labels may overlap.
162
163 \sa labelsVisible, labelsPosition, labelsPrecision
164*/
165/*!
166 \qmlproperty string AbstractBarSeries::labelsFormat
167 The format used for showing labels in a bar series.
168
169 \sa QAbstractBarSeries::labelsFormat, labelsVisible, labelsPosition
170*/
171/*!
172 \fn void QAbstractBarSeries::labelsFormatChanged(const QString &format)
173 This signal is emitted when the \a format of data value labels changes.
174*/
175
176/*!
177 \enum QAbstractBarSeries::LabelsPosition
178
179 This enum value describes the position of the data value labels:
180
181 \value LabelsCenter Label is located in the center of the bar.
182 \value LabelsInsideEnd Label is located inside the bar at the top.
183 \value LabelsInsideBase Label is located inside the bar at the bottom.
184 \value LabelsOutsideEnd Label is located outside the bar at the top.
185 */
186
187/*!
188 \property QAbstractBarSeries::labelsPosition
189 \brief The position of value labels.
190
191 \sa labelsVisible, labelsFormat
192*/
193/*!
194 \qmlproperty enumeration AbstractBarSeries::labelsPosition
195
196 The position of the data value labels:
197
198 \value AbstractBarSeries.LabelsCenter
199 Label is located in the center of the bar.
200 \value AbstractBarSeries.LabelsInsideEnd
201 Label is located inside the bar at the top.
202 \value AbstractBarSeries.LabelsInsideBase
203 Label is located inside the bar at the bottom.
204 \value AbstractBarSeries.LabelsOutsideEnd
205 Label is located outside the bar at the top.
206
207 \sa labelsVisible, labelsFormat
208*/
209/*!
210 \fn void QAbstractBarSeries::labelsPositionChanged(QAbstractBarSeries::LabelsPosition position)
211 This signal is emitted when the \a position of value labels changes.
212*/
213
214/*!
215 \property QAbstractBarSeries::labelsAngle
216 \brief The angle of the value labels in degrees.
217*/
218/*!
219 \qmlproperty real AbstractBarSeries::labelsAngle
220 The angle of the value labels in degrees.
221*/
222/*!
223 \fn void QAbstractBarSeries::labelsAngleChanged(qreal angle)
224 This signal is emitted when the \a angle of the value labels changes.
225*/
226
227/*!
228 \property QAbstractBarSeries::labelsPrecision
229 \brief The maximum amount of significant digits shown in value labels.
230
231 Default value is 6.
232*/
233/*!
234 \qmlproperty real AbstractBarSeries::labelsPrecision
235 The maximum amount of significant digits shown in value labels.
236
237 Default value is 6.
238*/
239/*!
240 \fn void QAbstractBarSeries::labelsPrecisionChanged(int precision)
241 This signal is emitted when the \a precision of the value labels changes.
242*/
243
244/*!
245 \fn void QAbstractBarSeries::clicked(int index, QBarSet *barset)
246 This signal is emitted when the user clicks the bar specified by \a index
247 in the bar set specified by \a barset.
248*/
249/*!
250 \qmlsignal AbstractBarSeries::clicked(int index, BarSet barset)
251 This signal is emitted when the user clicks the bar specified by \a index
252 in the bar set specified by \a barset.
253
254 The corresponding signal handler is \c onClicked.
255*/
256
257/*!
258 \fn void QAbstractBarSeries::pressed(int index, QBarSet *barset)
259 This signal is emitted when the user clicks the bar specified by \a index
260 in the bar set specified by \a barset and holds down the mouse button.
261*/
262/*!
263 \qmlsignal AbstractBarSeries::pressed(int index, BarSet barset)
264 This signal is emitted when the user clicks the bar specified by \a index
265 in the bar set specified by \a barset and holds down the mouse button.
266
267 The corresponding signal handler is \c onPressed.
268*/
269
270/*!
271 \fn void QAbstractBarSeries::released(int index, QBarSet *barset)
272 This signal is emitted when the user releases the mouse press on the bar
273 specified by \a index in the bar set specified by \a barset.
274*/
275/*!
276 \qmlsignal AbstractBarSeries::released(int index, BarSet barset)
277 This signal is emitted when the user releases the mouse press on the bar
278 specified by \a index in the bar set specified by \a barset.
279
280 The corresponding signal handler is \c onReleased.
281*/
282
283/*!
284 \fn void QAbstractBarSeries::doubleClicked(int index, QBarSet *barset)
285 This signal is emitted when the user double-clicks the bar specified by \a index
286 in the bar set specified by \a barset.
287*/
288/*!
289 \qmlsignal AbstractBarSeries::doubleClicked(int index, BarSet barset)
290 This signal is emitted when the user double-clicks the bar specified by \a index
291 in the bar set specified by \a barset.
292
293 The corresponding signal handler is \c onDoubleClicked.
294*/
295
296/*!
297 \fn void QAbstractBarSeries::hovered(bool status, int index, QBarSet* barset)
298
299 This signal is emitted when a mouse is hovered over the bar specified by \a index in the
300 bar set specified by \a barset. When the mouse moves over the bar, \a status turns \c true,
301 and when the mouse moves away again, it turns \c false.
302*/
303/*!
304 \qmlsignal AbstractBarSeries::hovered(bool status, int index, BarSet barset)
305
306 This signal is emitted when a mouse is hovered over the bar specified by \a index in the
307 bar set specified by \a barset. When the mouse moves over the bar, \a status turns \c true,
308 and when the mouse moves away again, it turns \c false.
309
310 The corresponding signal handler is \c onHovered.
311*/
312
313/*!
314 \fn void QAbstractBarSeries::countChanged()
315 This signal is emitted when the number of bar sets is changed, for example by append() or
316 remove().
317*/
318
319/*!
320 \fn void QAbstractBarSeries::labelsVisibleChanged()
321 This signal is emitted when the labels' visibility changes.
322 \sa isLabelsVisible(), setLabelsVisible()
323*/
324
325/*!
326 \fn void QAbstractBarSeries::barsetsAdded(const QList<QBarSet *> &sets)
327 This signal is emitted when the bar sets specified by \a sets are added to the series.
328 \sa append(), insert()
329*/
330/*!
331 \qmlsignal AbstractBarSeries::barsetsAdded()
332 This signal is emitted when bar sets are added to the series.
333
334 The corresponding signal handler is \c onBarsetsAdded.
335*/
336
337/*!
338 \fn void QAbstractBarSeries::barsetsRemoved(const QList<QBarSet *> &sets)
339 This signal is emitted when the bar sets specified by \a sets are removed from the series.
340 \sa remove()
341*/
342/*!
343 \qmlsignal AbstractBarSeries::barsetsRemoved()
344 This signal is emitted when bar sets are removed from the series.
345
346 The corresponding signal handler is \c onBarsetsRemoved.
347*/
348
349/*!
350 \qmlmethod BarSet AbstractBarSeries::at(int index)
351 Returns the bar set at \a index. Returns null if the index is not valid.
352*/
353
354/*!
355 \qmlmethod BarSet AbstractBarSeries::append(string label, VariantList values)
356 Adds a new bar set with \a label and \a values to the index. \a values is
357 a list of real values.
358
359 For example:
360 \code
361 myBarSeries.append("set 1", [0, 0.2, 0.2, 0.5, 0.4, 1.5, 0.9]);
362 \endcode
363*/
364
365/*!
366 \qmlmethod BarSet AbstractBarSeries::insert(int index, string label, VariantList values)
367 Adds a new bar set with \a label and \a values to \a index. \a values can be a list
368 of real values or a list of XYPoint types.
369
370 If the index value is equal to or less than zero, the new bar set is prepended to the bar
371 series. If the index value is equal to or greater than the number of bar sets in the bar
372 series, the new bar set is appended to the bar series.
373
374 \sa append()
375*/
376
377/*!
378 \qmlmethod bool AbstractBarSeries::remove(BarSet barset)
379 Removes the bar set specified by \a barset from the series. Returns \c true if successful,
380 \c false otherwise.
381*/
382
383/*!
384 \qmlmethod AbstractBarSeries::clear()
385 Removes all bar sets from the series.
386*/
387
388/*!
389 Removes the abstract bar series and the bar sets owned by it.
390*/
391QAbstractBarSeries::~QAbstractBarSeries()
392{
393
394}
395
396/*!
397 \internal
398*/
399QAbstractBarSeries::QAbstractBarSeries(QAbstractBarSeriesPrivate &o, QObject *parent)
400 : QAbstractSeries(o, parent)
401{
402 Q_D(QAbstractSeries);
403 QObject::connect(sender: this, SIGNAL(countChanged()), receiver: d, SIGNAL(countChanged()));
404}
405
406/*!
407 Sets the width of the bars of the series to \a width.
408*/
409void QAbstractBarSeries::setBarWidth(qreal width)
410{
411 Q_D(QAbstractBarSeries);
412 d->setBarWidth(width);
413}
414
415/*!
416 Returns the width of the bars of the series.
417 \sa setBarWidth()
418*/
419qreal QAbstractBarSeries::barWidth() const
420{
421 Q_D(const QAbstractBarSeries);
422 return d->barWidth();
423}
424
425/*!
426 Adds a set of bars specified by \a set to the bar series and takes ownership of it. If the set
427 is null or it already belongs to the series, it will not be appended.
428 Returns \c true if appending succeeded.
429*/
430bool QAbstractBarSeries::append(QBarSet *set)
431{
432 Q_D(QAbstractBarSeries);
433 bool success = d->append(set);
434 if (success) {
435 QList<QBarSet *> sets;
436 sets.append(t: set);
437 set->setParent(this);
438 emit barsetsAdded(sets);
439 emit countChanged();
440 }
441 return success;
442}
443
444/*!
445 Removes the bar set specified by \a set from the series and permanently deletes it if
446 the removal succeeds. Returns \c true if the set was removed.
447*/
448bool QAbstractBarSeries::remove(QBarSet *set)
449{
450 Q_D(QAbstractBarSeries);
451 bool success = d->remove(set);
452 if (success) {
453 QList<QBarSet *> sets;
454 sets.append(t: set);
455 set->setParent(0);
456 emit barsetsRemoved(sets);
457 emit countChanged();
458 delete set;
459 set = 0;
460 }
461 return success;
462}
463
464/*!
465 Takes a single \a set from the series. Does not delete the bar set object.
466 \note The series remains the barset's parent object. You must set the
467 parent object to take full ownership.
468
469 Returns \c true if the take operation succeeds.
470*/
471bool QAbstractBarSeries::take(QBarSet *set)
472{
473 Q_D(QAbstractBarSeries);
474 bool success = d->remove(set);
475 if (success) {
476 QList<QBarSet *> sets;
477 sets.append(t: set);
478 emit barsetsRemoved(sets);
479 emit countChanged();
480 }
481 return success;
482}
483
484/*!
485 Adds a list of bar sets specified by \a sets to a bar series and takes ownership of the sets.
486 Returns \c true if all sets were appended successfully. If any of the sets is null or was
487 previously appended to the series, nothing is appended and this function returns \c false.
488 If any of the sets appears in the list more than once, nothing is appended and this function
489 returns \c false.
490*/
491bool QAbstractBarSeries::append(const QList<QBarSet *> &sets)
492{
493 Q_D(QAbstractBarSeries);
494 if (!d->append(sets))
495 return false;
496
497 for (auto *set : sets)
498 set->setParent(this);
499
500 emit barsetsAdded(sets);
501 emit countChanged();
502
503 return true;
504}
505
506/*!
507 Inserts a bar set specified by \a set to a series at the position specified by \a index
508 and takes ownership of the set. If the set is null or already belongs to the series, it will
509 not be appended. Returns \c true if inserting succeeds.
510*/
511bool QAbstractBarSeries::insert(int index, QBarSet *set)
512{
513 Q_D(QAbstractBarSeries);
514 bool success = d->insert(index, set);
515 if (success) {
516 QList<QBarSet *> sets;
517 sets.append(t: set);
518 set->setParent(this);
519 emit barsetsAdded(sets);
520 emit countChanged();
521 }
522 return success;
523}
524
525/*!
526 Removes all bar sets from the series and permanently deletes them.
527*/
528void QAbstractBarSeries::clear()
529{
530 Q_D(QAbstractBarSeries);
531 QList<QBarSet *> sets = barSets();
532 bool success = d->remove(sets);
533 if (success) {
534 emit barsetsRemoved(sets);
535 emit countChanged();
536 foreach (QBarSet *set, sets)
537 delete set;
538 }
539}
540
541/*!
542 Returns the number of bar sets in a bar series.
543*/
544int QAbstractBarSeries::count() const
545{
546 Q_D(const QAbstractBarSeries);
547 return d->m_barSets.size();
548}
549
550/*!
551 Returns a list of bar sets in a bar series. Keeps the ownership of the bar sets.
552 */
553QList<QBarSet *> QAbstractBarSeries::barSets() const
554{
555 Q_D(const QAbstractBarSeries);
556 return d->m_barSets;
557}
558
559/*!
560 Sets the visibility of labels in a bar series to \a visible.
561*/
562void QAbstractBarSeries::setLabelsVisible(bool visible)
563{
564 Q_D(QAbstractBarSeries);
565 if (d->m_labelsVisible != visible) {
566 d->setLabelsVisible(visible);
567 emit labelsVisibleChanged();
568 }
569}
570
571/*!
572 Returns the visibility of labels.
573*/
574bool QAbstractBarSeries::isLabelsVisible() const
575{
576 Q_D(const QAbstractBarSeries);
577 return d->m_labelsVisible;
578}
579
580void QAbstractBarSeries::setLabelsFormat(const QString &format)
581{
582 Q_D(QAbstractBarSeries);
583 if (d->m_labelsFormat != format) {
584 d->m_labelsFormat = format;
585 d->setLabelsDirty(true);
586 emit labelsFormatChanged(format);
587 }
588}
589
590QString QAbstractBarSeries::labelsFormat() const
591{
592 Q_D(const QAbstractBarSeries);
593 return d->m_labelsFormat;
594}
595
596void QAbstractBarSeries::setLabelsAngle(qreal angle)
597{
598 Q_D(QAbstractBarSeries);
599 if (d->m_labelsAngle != angle) {
600 d->m_labelsAngle = angle;
601 d->setLabelsDirty(true);
602 emit labelsAngleChanged(angle);
603 }
604}
605
606qreal QAbstractBarSeries::labelsAngle() const
607{
608 Q_D(const QAbstractBarSeries);
609 return d->m_labelsAngle;
610}
611
612void QAbstractBarSeries::setLabelsPosition(QAbstractBarSeries::LabelsPosition position)
613{
614 Q_D(QAbstractBarSeries);
615 if (d->m_labelsPosition != position) {
616 d->m_labelsPosition = position;
617 emit labelsPositionChanged(position);
618 }
619}
620
621QAbstractBarSeries::LabelsPosition QAbstractBarSeries::labelsPosition() const
622{
623 Q_D(const QAbstractBarSeries);
624 return d->m_labelsPosition;
625}
626
627void QAbstractBarSeries::setLabelsPrecision(int precision)
628{
629 Q_D(QAbstractBarSeries);
630 if (d->m_labelsPrecision != precision) {
631 d->m_labelsPrecision = precision;
632 d->setLabelsDirty(true);
633 emit labelsPrecisionChanged(precision);
634 }
635}
636
637int QAbstractBarSeries::labelsPrecision() const
638{
639 Q_D(const QAbstractBarSeries);
640 return d->m_labelsPrecision;
641}
642
643///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
644
645QAbstractBarSeriesPrivate::QAbstractBarSeriesPrivate(QAbstractBarSeries *q) :
646 QAbstractSeriesPrivate(q),
647 m_barWidth(0.5), // Default value is 50% of category width
648 m_labelsVisible(false),
649 m_visible(true),
650 m_blockBarUpdate(false),
651 m_labelsFormat(),
652 m_labelsPosition(QAbstractBarSeries::LabelsCenter),
653 m_labelsAngle(0),
654 m_labelsPrecision(6),
655 m_visualsDirty(true),
656 m_labelsDirty(true)
657{
658}
659
660int QAbstractBarSeriesPrivate::categoryCount() const
661{
662 // No categories defined. return count of longest set.
663 int count = 0;
664 for (int i = 0; i < m_barSets.size(); i++) {
665 if (m_barSets.at(i)->count() > count)
666 count = m_barSets.at(i)->count();
667 }
668
669 return count;
670}
671
672void QAbstractBarSeriesPrivate::setBarWidth(qreal width)
673{
674 if (width < 0.0)
675 width = 0.0;
676 m_barWidth = width;
677 emit updatedLayout();
678}
679
680qreal QAbstractBarSeriesPrivate::barWidth() const
681{
682 return m_barWidth;
683}
684
685QBarSet *QAbstractBarSeriesPrivate::barsetAt(int index)
686{
687 return m_barSets.at(i: index);
688}
689
690void QAbstractBarSeriesPrivate::setVisible(bool visible)
691{
692 m_visible = visible;
693 emit visibleChanged();
694}
695
696void QAbstractBarSeriesPrivate::setLabelsVisible(bool visible)
697{
698 m_labelsVisible = visible;
699 emit labelsVisibleChanged(visible);
700}
701
702qreal QAbstractBarSeriesPrivate::min()
703{
704 if (m_barSets.size() <= 0)
705 return 0;
706
707 qreal min = INT_MAX;
708
709 for (int i = 0; i < m_barSets.size(); i++) {
710 int categoryCount = m_barSets.at(i)->count();
711 for (int j = 0; j < categoryCount; j++) {
712 qreal temp = m_barSets.at(i)->at(index: j);
713 if (temp < min)
714 min = temp;
715 }
716 }
717 return min;
718}
719
720qreal QAbstractBarSeriesPrivate::max()
721{
722 if (m_barSets.size() <= 0)
723 return 0;
724
725 qreal max = INT_MIN;
726
727 for (int i = 0; i < m_barSets.size(); i++) {
728 int categoryCount = m_barSets.at(i)->count();
729 for (int j = 0; j < categoryCount; j++) {
730 qreal temp = m_barSets.at(i)->at(index: j);
731 if (temp > max)
732 max = temp;
733 }
734 }
735
736 return max;
737}
738
739qreal QAbstractBarSeriesPrivate::valueAt(int set, int category)
740{
741 if ((set < 0) || (set >= m_barSets.size()))
742 return 0; // No set, no value.
743 else if ((category < 0) || (category >= m_barSets.at(i: set)->count()))
744 return 0; // No category, no value.
745
746 return m_barSets.at(i: set)->at(index: category);
747}
748
749qreal QAbstractBarSeriesPrivate::percentageAt(int set, int category)
750{
751 if ((set < 0) || (set >= m_barSets.size()))
752 return 0; // No set, no value.
753 else if ((category < 0) || (category >= m_barSets.at(i: set)->count()))
754 return 0; // No category, no value.
755
756 qreal value = m_barSets.at(i: set)->at(index: category);
757 qreal sum = categorySum(category);
758 if (qFuzzyCompare(p1: sum, p2: 0))
759 return 0;
760
761 return value / sum;
762}
763
764qreal QAbstractBarSeriesPrivate::categorySum(int category)
765{
766 qreal sum(0);
767 int count = m_barSets.size(); // Count sets
768 for (int set = 0; set < count; set++) {
769 if (category < m_barSets.at(i: set)->count())
770 sum += m_barSets.at(i: set)->at(index: category);
771 }
772 return sum;
773}
774
775qreal QAbstractBarSeriesPrivate::absoluteCategorySum(int category)
776{
777 qreal sum(0);
778 int count = m_barSets.size(); // Count sets
779 for (int set = 0; set < count; set++) {
780 if (category < m_barSets.at(i: set)->count())
781 sum += qAbs(t: m_barSets.at(i: set)->at(index: category));
782 }
783 return sum;
784}
785
786qreal QAbstractBarSeriesPrivate::maxCategorySum()
787{
788 qreal max = INT_MIN;
789 int count = categoryCount();
790 for (int i = 0; i < count; i++) {
791 qreal sum = categorySum(category: i);
792 if (sum > max)
793 max = sum;
794 }
795 return max;
796}
797
798qreal QAbstractBarSeriesPrivate::minX()
799{
800 if (m_barSets.size() <= 0)
801 return 0;
802
803 qreal min = INT_MAX;
804
805 for (int i = 0; i < m_barSets.size(); i++) {
806 int categoryCount = m_barSets.at(i)->count();
807 for (int j = 0; j < categoryCount; j++) {
808 qreal temp = m_barSets.at(i)->d_ptr.data()->m_values.at(i: j).x();
809 if (temp < min)
810 min = temp;
811 }
812 }
813 return min;
814}
815
816qreal QAbstractBarSeriesPrivate::maxX()
817{
818 if (m_barSets.size() <= 0)
819 return 0;
820
821 qreal max = INT_MIN;
822
823 for (int i = 0; i < m_barSets.size(); i++) {
824 int categoryCount = m_barSets.at(i)->count();
825 for (int j = 0; j < categoryCount; j++) {
826 qreal temp = m_barSets.at(i)->d_ptr.data()->m_values.at(i: j).x();
827 if (temp > max)
828 max = temp;
829 }
830 }
831
832 return max;
833}
834
835qreal QAbstractBarSeriesPrivate::categoryTop(int category)
836{
837 // Returns top (sum of all positive values) of category.
838 // Returns 0, if all values are negative
839 qreal top(0);
840 int count = m_barSets.size();
841 for (int set = 0; set < count; set++) {
842 if (category < m_barSets.at(i: set)->count()) {
843 qreal temp = m_barSets.at(i: set)->at(index: category);
844 if (temp > 0) {
845 top += temp;
846 }
847 }
848 }
849 return top;
850}
851
852qreal QAbstractBarSeriesPrivate::categoryBottom(int category)
853{
854 // Returns bottom (sum of all negative values) of category
855 // Returns 0, if all values are positive
856 qreal bottom(0);
857 int count = m_barSets.size();
858 for (int set = 0; set < count; set++) {
859 if (category < m_barSets.at(i: set)->count()) {
860 qreal temp = m_barSets.at(i: set)->at(index: category);
861 if (temp < 0) {
862 bottom += temp;
863 }
864 }
865 }
866 return bottom;
867}
868
869qreal QAbstractBarSeriesPrivate::top()
870{
871 // Returns top of all categories
872 qreal top(0);
873 int count = categoryCount();
874 for (int i = 0; i < count; i++) {
875 qreal temp = categoryTop(category: i);
876 if (temp > top)
877 top = temp;
878 }
879 return top;
880}
881
882qreal QAbstractBarSeriesPrivate::bottom()
883{
884 // Returns bottom of all categories
885 qreal bottom(0);
886 int count = categoryCount();
887 for (int i = 0; i < count; i++) {
888 qreal temp = categoryBottom(category: i);
889 if (temp < bottom)
890 bottom = temp;
891 }
892 return bottom;
893}
894
895bool QAbstractBarSeriesPrivate::blockBarUpdate()
896{
897 return m_blockBarUpdate;
898}
899
900qreal QAbstractBarSeriesPrivate::labelsAngle() const
901{
902 return m_labelsAngle;
903}
904
905void QAbstractBarSeriesPrivate::initializeDomain()
906{
907 qreal minX(domain()->minX());
908 qreal minY(domain()->minY());
909 qreal maxX(domain()->maxX());
910 qreal maxY(domain()->maxY());
911
912 qreal seriesMinX = this->minX();
913 qreal seriesMaxX = this->maxX();
914 qreal y = max();
915 minX = qMin(a: minX, b: seriesMinX - (qreal)0.5);
916 minY = qMin(a: minY, b: y);
917 maxX = qMax(a: maxX, b: seriesMaxX + (qreal)0.5);
918 maxY = qMax(a: maxY, b: y);
919
920 domain()->setRange(minX, maxX, minY, maxY);
921}
922
923QList<QLegendMarker*> QAbstractBarSeriesPrivate::createLegendMarkers(QLegend* legend)
924{
925 Q_Q(QAbstractBarSeries);
926 QList<QLegendMarker*> markers;
927
928 const auto barSets = q->barSets();
929 for (QBarSet *set : barSets) {
930 QBarLegendMarker* marker = new QBarLegendMarker(q,set,legend);
931 markers << marker;
932 }
933 return markers;
934}
935
936
937bool QAbstractBarSeriesPrivate::append(QBarSet *set)
938{
939 if ((m_barSets.contains(t: set)) || (set == 0))
940 return false; // Fail if set is already in list or set is null.
941
942 m_barSets.append(t: set);
943 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::updatedBars,
944 context: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
945 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueChanged,
946 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueChange);
947 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueAdded,
948 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueAdd);
949 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueRemoved,
950 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueRemove);
951 connect(sender: set, signal: &QBarSet::selectedBarsChanged,
952 context: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
953
954 emit restructuredBars(); // this notifies barchartitem
955 return true;
956}
957
958bool QAbstractBarSeriesPrivate::remove(QBarSet *set)
959{
960 if (!m_barSets.contains(t: set))
961 return false; // Fail if set is not in list
962
963 m_barSets.removeOne(t: set);
964 QObject::disconnect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::updatedBars,
965 receiver: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
966 QObject::disconnect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueChanged,
967 receiver: this, slot: &QAbstractBarSeriesPrivate::handleSetValueChange);
968 QObject::disconnect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueAdded,
969 receiver: this, slot: &QAbstractBarSeriesPrivate::handleSetValueAdd);
970 QObject::disconnect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueRemoved,
971 receiver: this, slot: &QAbstractBarSeriesPrivate::handleSetValueRemove);
972 disconnect(sender: set, signal: &QBarSet::selectedBarsChanged,
973 receiver: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
974
975 emit restructuredBars(); // this notifies barchartitem
976 return true;
977}
978
979bool QAbstractBarSeriesPrivate::append(const QList<QBarSet *> &sets)
980{
981 for (auto *set : sets) {
982 if ((set == 0) || (m_barSets.contains(t: set)))
983 return false; // Fail if any of the sets is null or is already appended.
984 if (sets.count(t: set) != 1)
985 return false; // Also fail if same set is more than once in given list.
986 }
987
988 for (auto *set : sets) {
989 m_barSets.append(t: set);
990 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::updatedBars,
991 context: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
992 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueChanged,
993 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueChange);
994 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueAdded,
995 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueAdd);
996 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueRemoved,
997 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueRemove);
998 connect(sender: set, signal: &QBarSet::selectedBarsChanged,
999 context: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
1000 }
1001
1002 emit restructuredBars(); // this notifies barchartitem
1003 return true;
1004}
1005
1006bool QAbstractBarSeriesPrivate::remove(const QList<QBarSet *> &sets)
1007{
1008 if (sets.size() == 0)
1009 return false;
1010
1011 foreach (QBarSet *set, sets) {
1012 if ((set == 0) || (!m_barSets.contains(t: set)))
1013 return false; // Fail if any of the sets is null or is not in series
1014 if (sets.count(t: set) != 1)
1015 return false; // Also fail if same set is more than once in given list.
1016 }
1017
1018 foreach (QBarSet *set, sets) {
1019 m_barSets.removeOne(t: set);
1020 QObject::disconnect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::updatedBars,
1021 receiver: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
1022 QObject::disconnect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueChanged,
1023 receiver: this, slot: &QAbstractBarSeriesPrivate::handleSetValueChange);
1024 QObject::disconnect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueAdded,
1025 receiver: this, slot: &QAbstractBarSeriesPrivate::handleSetValueAdd);
1026 QObject::disconnect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueRemoved,
1027 receiver: this, slot: &QAbstractBarSeriesPrivate::handleSetValueRemove);
1028 disconnect(sender: set, signal: &QBarSet::selectedBarsChanged,
1029 receiver: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
1030 }
1031
1032 emit restructuredBars(); // this notifies barchartitem
1033
1034 return true;
1035}
1036
1037bool QAbstractBarSeriesPrivate::insert(int index, QBarSet *set)
1038{
1039 if ((m_barSets.contains(t: set)) || (set == 0))
1040 return false; // Fail if set is already in list or set is null.
1041
1042 m_barSets.insert(i: index, t: set);
1043 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::updatedBars,
1044 context: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
1045 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueChanged,
1046 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueChange);
1047 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueAdded,
1048 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueAdd);
1049 QObject::connect(sender: set->d_ptr.data(), signal: &QBarSetPrivate::valueRemoved,
1050 context: this, slot: &QAbstractBarSeriesPrivate::handleSetValueRemove);
1051 connect(sender: set, signal: &QBarSet::selectedBarsChanged,
1052 context: this, slot: &QAbstractBarSeriesPrivate::updatedBars);
1053
1054 emit restructuredBars(); // this notifies barchartitem
1055 return true;
1056}
1057
1058void QAbstractBarSeriesPrivate::initializeAxes()
1059{
1060 Q_Q(QAbstractBarSeries);
1061
1062 foreach(QAbstractAxis* axis, m_axes) {
1063 if (axis->type() == QAbstractAxis::AxisTypeBarCategory) {
1064 switch (q->type()) {
1065 case QAbstractSeries::SeriesTypeHorizontalBar:
1066 case QAbstractSeries::SeriesTypeHorizontalPercentBar:
1067 case QAbstractSeries::SeriesTypeHorizontalStackedBar:
1068 if (axis->orientation() == Qt::Vertical)
1069 populateCategories(axis: qobject_cast<QBarCategoryAxis *>(object: axis));
1070 break;
1071 case QAbstractSeries::SeriesTypeBar:
1072 case QAbstractSeries::SeriesTypePercentBar:
1073 case QAbstractSeries::SeriesTypeStackedBar:
1074 case QAbstractSeries::SeriesTypeBoxPlot:
1075 case QAbstractSeries::SeriesTypeCandlestick:
1076 if (axis->orientation() == Qt::Horizontal)
1077 populateCategories(axis: qobject_cast<QBarCategoryAxis *>(object: axis));
1078 break;
1079 default:
1080 qWarning() << "Unexpected series type";
1081 break;
1082 }
1083 }
1084 }
1085
1086 // Make sure series animations are reset when axes change
1087 AbstractBarChartItem *item = qobject_cast<AbstractBarChartItem *>(object: m_item.get());
1088 if (item)
1089 item->resetAnimation();
1090}
1091
1092QAbstractAxis::AxisType QAbstractBarSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
1093{
1094 Q_Q(const QAbstractBarSeries);
1095
1096 switch (q->type()) {
1097 case QAbstractSeries::SeriesTypeHorizontalBar:
1098 case QAbstractSeries::SeriesTypeHorizontalPercentBar:
1099 case QAbstractSeries::SeriesTypeHorizontalStackedBar:
1100 if (orientation == Qt::Vertical)
1101 return QAbstractAxis::AxisTypeBarCategory;
1102 break;
1103 case QAbstractSeries::SeriesTypeBar:
1104 case QAbstractSeries::SeriesTypePercentBar:
1105 case QAbstractSeries::SeriesTypeStackedBar:
1106 case QAbstractSeries::SeriesTypeBoxPlot:
1107 case QAbstractSeries::SeriesTypeCandlestick:
1108 if (orientation == Qt::Horizontal)
1109 return QAbstractAxis::AxisTypeBarCategory;
1110 break;
1111 default:
1112 qWarning() << "Unexpected series type";
1113 break;
1114 }
1115 return QAbstractAxis::AxisTypeValue;
1116
1117}
1118
1119void QAbstractBarSeriesPrivate::handleSetValueChange(int index)
1120{
1121 QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(object: sender());
1122 if (priv)
1123 emit setValueChanged(index, barset: priv->q_ptr);
1124}
1125
1126void QAbstractBarSeriesPrivate::handleSetValueAdd(int index, int count)
1127{
1128 QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(object: sender());
1129 if (priv)
1130 emit setValueAdded(index, count, barset: priv->q_ptr);
1131}
1132
1133void QAbstractBarSeriesPrivate::handleSetValueRemove(int index, int count)
1134{
1135 QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(object: sender());
1136 if (priv)
1137 emit setValueRemoved(index, count, barset: priv->q_ptr);
1138}
1139
1140void QAbstractBarSeriesPrivate::populateCategories(QBarCategoryAxis *axis)
1141{
1142 QStringList categories;
1143 if (axis->categories().isEmpty()) {
1144 for (int i(1); i < categoryCount() + 1; i++)
1145 categories << presenter()->numberToString(value: i);
1146 axis->append(categories);
1147 }
1148}
1149
1150QAbstractAxis* QAbstractBarSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const
1151{
1152 if (defaultAxisType(orientation) == QAbstractAxis::AxisTypeBarCategory)
1153 return new QBarCategoryAxis;
1154 else
1155 return new QValueAxis;
1156}
1157
1158void QAbstractBarSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced)
1159{
1160 m_blockBarUpdate = true; // Ensures that the bars are not updated before the theme is ready
1161
1162 const QList<QGradient> gradients = theme->seriesGradients();
1163
1164 // Since each bar series uses different number of colors, we need to account for other
1165 // bar series in the chart that are also themed when choosing colors.
1166 // First series count is used to determine the color stepping to keep old applications
1167 // with single bar series with a lot of sets colored as they always have been.
1168 int actualIndex = 0;
1169 int firstSeriesSetCount = m_barSets.size();
1170 if (m_item) {
1171 auto seriesMap = m_item->themeManager()->seriesMap();
1172 int lowestSeries = index;
1173 for (auto it = seriesMap.cbegin(), end = seriesMap.cend(); it != end; ++it) {
1174 if (it.value() != index) {
1175 auto barSeries = qobject_cast<QAbstractBarSeries *>(object: it.key());
1176 if (barSeries) {
1177 actualIndex += barSeries->count();
1178 if (it.value() < lowestSeries) {
1179 firstSeriesSetCount = qMax(a: barSeries->count(), b: gradients.size());
1180 lowestSeries = it.value();
1181 }
1182 }
1183 }
1184 }
1185 }
1186
1187 qreal takeAtPos = 0.5;
1188 qreal step = 0.2;
1189 if (firstSeriesSetCount > 1) {
1190 step = 1.0 / qreal(firstSeriesSetCount);
1191 if (firstSeriesSetCount % gradients.size())
1192 step *= gradients.size();
1193 else
1194 step *= (gradients.size() - 1);
1195 if (index > 0) {
1196 // Take necessary amount of initial steps
1197 int initialStepper = actualIndex;
1198 while (initialStepper > gradients.size()) {
1199 initialStepper -= gradients.size();
1200 takeAtPos += step;
1201 if (takeAtPos == 1.0)
1202 takeAtPos += step;
1203 takeAtPos -= int(takeAtPos);
1204 }
1205 }
1206 }
1207
1208 for (int i(0); i < m_barSets.size(); i++) {
1209 int colorIndex = (actualIndex + i) % gradients.size();
1210 if ((actualIndex + i) > 0 && (actualIndex + i) % gradients.size() == 0) {
1211 // There is no dedicated base color for each sets, generate more colors
1212 takeAtPos += step;
1213 if (takeAtPos == 1.0)
1214 takeAtPos += step;
1215 takeAtPos -= (int) takeAtPos;
1216 }
1217 if (forced || QChartPrivate::defaultBrush() == m_barSets.at(i)->d_ptr->m_brush)
1218 m_barSets.at(i)->setBrush(ChartThemeManager::colorAt(gradient: gradients.at(i: colorIndex), pos: takeAtPos));
1219
1220 // Pick label color from the opposite end of the gradient.
1221 // 0.3 as a boundary seems to work well.
1222 if (forced || QChartPrivate::defaultBrush() == m_barSets.at(i)->d_ptr->m_labelBrush) {
1223 if (takeAtPos < 0.3)
1224 m_barSets.at(i)->setLabelBrush(ChartThemeManager::colorAt(gradient: gradients.at(i: actualIndex % gradients.size()), pos: 1));
1225 else
1226 m_barSets.at(i)->setLabelBrush(ChartThemeManager::colorAt(gradient: gradients.at(i: actualIndex % gradients.size()), pos: 0));
1227 }
1228 if (forced || QChartPrivate::defaultPen() == m_barSets.at(i)->d_ptr->m_pen) {
1229 QColor c = ChartThemeManager::colorAt(gradient: gradients.at(i: actualIndex % gradients.size()), pos: 0.0);
1230 m_barSets.at(i)->setPen(c);
1231 }
1232 }
1233 m_blockBarUpdate = false;
1234 emit updatedBars();
1235}
1236
1237void QAbstractBarSeriesPrivate::initializeAnimations(QChart::AnimationOptions options, int duration,
1238 QEasingCurve &curve)
1239{
1240 AbstractBarChartItem *bar = static_cast<AbstractBarChartItem *>(m_item.get());
1241 Q_ASSERT(bar);
1242 if (bar->animation())
1243 bar->animation()->stopAndDestroyLater();
1244
1245 if (options.testFlag(flag: QChart::SeriesAnimations))
1246 bar->setAnimation(new BarAnimation(bar, duration, curve));
1247 else
1248 bar->setAnimation(0);
1249 QAbstractSeriesPrivate::initializeAnimations(options, duration, curve);
1250}
1251
1252QT_END_NAMESPACE
1253
1254#include "moc_qabstractbarseries.cpp"
1255#include "moc_qabstractbarseries_p.cpp"
1256

source code of qtcharts/src/charts/barchart/qabstractbarseries.cpp