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

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