| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Charts module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <QtCharts/QAbstractBarSeries> |
| #include <private/qabstractbarseries_p.h> |
| #include <QtCharts/QBarSet> |
| #include <private/qbarset_p.h> |
| #include <private/abstractdomain_p.h> |
| #include <private/chartdataset_p.h> |
| #include <private/charttheme_p.h> |
| #include <QtCharts/QValueAxis> |
| #include <QtCharts/QBarCategoryAxis> |
| #include <QtCharts/QBarLegendMarker> |
| #include <private/baranimation_p.h> |
| #include <private/abstractbarchartitem_p.h> |
| #include <private/qchart_p.h> |
| |
| QT_CHARTS_BEGIN_NAMESPACE |
| |
| /*! |
| \class QAbstractBarSeries |
| \inmodule QtCharts |
| \brief The QAbstractBarSeries class is an abstract parent class for all bar series classes. |
| |
| In bar charts, bars are defined as bar sets that contain one data value for each category. |
| The position of a bar is specified by the category and its height by the data value. Bar |
| series that contain multiple bar sets group together bars that belong to the same category. |
| The way the bars are displayed is determined by the subclass of this class chosen to create |
| the bar chart. |
| |
| If a QValueAxis is used instead of QBarCategoryAxis for the main bar axis, the bars are |
| grouped around the index value of the category. |
| |
| See the \l {BarChart Example} {bar chart example} to learn how to use the QBarSeries class |
| to create a simple bar chart. |
| \image examples_barchart.png |
| |
| \sa QBarSet, QBarSeries, QStackedBarSeries, QPercentBarSeries |
| \sa QHorizontalBarSeries, QHorizontalStackedBarSeries, QHorizontalPercentBarSeries |
| */ |
| /*! |
| \qmltype AbstractBarSeries |
| \instantiates QAbstractBarSeries |
| \inqmlmodule QtCharts |
| |
| \inherits AbstractSeries |
| |
| \brief An abstract parent type for all bar series types. |
| |
| In bar charts, bars are defined as bar sets that contain one data value for each category. |
| The position of a bar is specified by the category and its height by the data value. Bar |
| series that contain multiple bar sets group together bars that belong to the same category. |
| The way the bars are displayed is determined by the type chosen to create the bar chart: |
| BarSeries, StackedBarSeries, PercentBarSeries, HorizontalBarSeries, HorizontalStackedBarSeries, |
| or HorizontalPercentBarSeries. |
| |
| If a ValueAxis type is used instead of a BarCategoryAxis type for the main bar axis, the |
| bars are grouped around the index value of the category. |
| |
| The following QML code snippet shows how to use the BarSeries and BarCategoryAxis type |
| to create a simple bar chart: |
| \snippet qmlchart/qml/qmlchart/View6.qml 1 |
| |
| \beginfloatleft |
| \image examples_qmlchart6.png |
| \endfloat |
| \clearfloat |
| */ |
| |
| /*! |
| \qmlproperty AbstractAxis AbstractBarSeries::axisX |
| The x-axis used for the series. If you leave both axisX and axisXTop undefined, a |
| BarCategoryAxis is created for the series. |
| \sa axisXTop |
| */ |
| |
| /*! |
| \qmlproperty AbstractAxis AbstractBarSeries::axisY |
| The y-axis used for the series. If you leave both axisY and axisYRight undefined, a |
| ValueAxis is created for the series. |
| \sa axisYRight |
| */ |
| |
| /*! |
| \qmlproperty AbstractAxis AbstractBarSeries::axisXTop |
| The x-axis used for the series, drawn on top of the chart view. |
| |
| \note You can only provide either axisX or axisXTop, but not both. |
| \sa axisX |
| */ |
| |
| /*! |
| \qmlproperty AbstractAxis AbstractBarSeries::axisYRight |
| The y-axis used for the series, drawn to the right of the chart view. |
| |
| \note You can only provide either axisY or axisYRight, but not both. |
| \sa axisY |
| */ |
| |
| /*! |
| \property QAbstractBarSeries::barWidth |
| \brief The width of the bars of the series. |
| |
| The unit of width is the unit of the x-axis. The minimum width for bars is zero, and negative |
| values are treated as zero. Setting the width to zero means that the width of the bar on the |
| screen is one pixel regardless of the scale of the x-axis. Bars wider than zero are scaled |
| using the x-axis scale. |
| |
| \note When used with QBarSeries, this value specifies the width of a group of bars instead of |
| that of a single bar. |
| \sa QBarSeries |
| */ |
| /*! |
| \qmlproperty real AbstractBarSeries::barWidth |
| The unit of width is the unit of the x-axis. The minimum width for bars is zero, and negative |
| values are treated as zero. Setting the width to zero means that the width of the bar on the |
| screen is one pixel regardless of the scale of the x-axis. Bars wider than zero are scaled |
| using the x-axis scale. |
| |
| \note When used with the BarSeries type, this value specifies the width of a group of bars |
| instead of that of a single bar. |
| */ |
| |
| /*! |
| \property QAbstractBarSeries::count |
| \brief The number of bar sets in a bar series. |
| */ |
| /*! |
| \qmlproperty int AbstractBarSeries::count |
| The number of bar sets in a bar series. |
| */ |
| |
| /*! |
| \property QAbstractBarSeries::labelsVisible |
| \brief The visibility of the labels in a bar series. |
| */ |
| /*! |
| \qmlproperty bool AbstractBarSeries::labelsVisible |
| The visibility of the labels in a bar series. |
| */ |
| |
| /*! |
| \property QAbstractBarSeries::labelsFormat |
| \brief The format used for showing labels in a bar series. |
| |
| QAbstractBarSeries supports the following format tag: |
| \table |
| \row |
| \li @value \li The value of the bar |
| \endtable |
| |
| For example, the following usage of the format tags would produce labels that show the value |
| followed by the unit (u): |
| \code |
| series->setLabelsFormat("@value u"); |
| \endcode |
| |
| By default, the labels show the value of the bar. For the percent bar series, \e % is added |
| after the value. The labels are shown on the plot area, whereas labels on the edge of the plot |
| area are cut. If the bars are close to each other, the labels may overlap. |
| |
| \sa labelsVisible, labelsPosition, labelsPrecision |
| */ |
| /*! |
| \qmlproperty string AbstractBarSeries::labelsFormat |
| The format used for showing labels in a bar series. |
| |
| \sa QAbstractBarSeries::labelsFormat, labelsVisible, labelsPosition |
| */ |
| /*! |
| \fn void QAbstractBarSeries::labelsFormatChanged(const QString &format) |
| This signal is emitted when the \a format of data value labels changes. |
| */ |
| |
| /*! |
| \enum QAbstractBarSeries::LabelsPosition |
| |
| This enum value describes the position of the data value labels: |
| |
| \value LabelsCenter Label is located in the center of the bar. |
| \value LabelsInsideEnd Label is located inside the bar at the top. |
| \value LabelsInsideBase Label is located inside the bar at the bottom. |
| \value LabelsOutsideEnd Label is located outside the bar at the top. |
| */ |
| |
| /*! |
| \property QAbstractBarSeries::labelsPosition |
| \brief The position of value labels. |
| |
| \sa labelsVisible, labelsFormat |
| */ |
| /*! |
| \qmlproperty enumeration AbstractBarSeries::labelsPosition |
| |
| The position of the data value labels: |
| |
| \value AbstractBarSeries.LabelsCenter |
| Label is located in the center of the bar. |
| \value AbstractBarSeries.LabelsInsideEnd |
| Label is located inside the bar at the top. |
| \value AbstractBarSeries.LabelsInsideBase |
| Label is located inside the bar at the bottom. |
| \value AbstractBarSeries.LabelsOutsideEnd |
| Label is located outside the bar at the top. |
| |
| \sa labelsVisible, labelsFormat |
| */ |
| /*! |
| \fn void QAbstractBarSeries::labelsPositionChanged(QAbstractBarSeries::LabelsPosition position) |
| This signal is emitted when the \a position of value labels changes. |
| */ |
| |
| /*! |
| \property QAbstractBarSeries::labelsAngle |
| \brief The angle of the value labels in degrees. |
| */ |
| /*! |
| \qmlproperty real AbstractBarSeries::labelsAngle |
| The angle of the value labels in degrees. |
| */ |
| /*! |
| \fn void QAbstractBarSeries::labelsAngleChanged(qreal angle) |
| This signal is emitted when the \a angle of the value labels changes. |
| */ |
| |
| /*! |
| \property QAbstractBarSeries::labelsPrecision |
| \brief The maximum amount of significant digits shown in value labels. |
| |
| Default value is 6. |
| */ |
| /*! |
| \qmlproperty real AbstractBarSeries::labelsPrecision |
| The maximum amount of significant digits shown in value labels. |
| |
| Default value is 6. |
| */ |
| /*! |
| \fn void QAbstractBarSeries::labelsPrecisionChanged(int precision) |
| This signal is emitted when the \a precision of the value labels changes. |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::clicked(int index, QBarSet *barset) |
| This signal is emitted when the user clicks the bar specified by \a index |
| in the bar set specified by \a barset. |
| */ |
| /*! |
| \qmlsignal AbstractBarSeries::clicked(int index, BarSet barset) |
| This signal is emitted when the user clicks the bar specified by \a index |
| in the bar set specified by \a barset. |
| |
| The corresponding signal handler is \c onClicked. |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::pressed(int index, QBarSet *barset) |
| This signal is emitted when the user clicks the bar specified by \a index |
| in the bar set specified by \a barset and holds down the mouse button. |
| */ |
| /*! |
| \qmlsignal AbstractBarSeries::pressed(int index, BarSet barset) |
| This signal is emitted when the user clicks the bar specified by \a index |
| in the bar set specified by \a barset and holds down the mouse button. |
| |
| The corresponding signal handler is \c onPressed. |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::released(int index, QBarSet *barset) |
| This signal is emitted when the user releases the mouse press on the bar |
| specified by \a index in the bar set specified by \a barset. |
| */ |
| /*! |
| \qmlsignal AbstractBarSeries::released(int index, BarSet barset) |
| This signal is emitted when the user releases the mouse press on the bar |
| specified by \a index in the bar set specified by \a barset. |
| |
| The corresponding signal handler is \c onReleased. |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::doubleClicked(int index, QBarSet *barset) |
| This signal is emitted when the user double-clicks the bar specified by \a index |
| in the bar set specified by \a barset. |
| */ |
| /*! |
| \qmlsignal AbstractBarSeries::doubleClicked(int index, BarSet barset) |
| This signal is emitted when the user double-clicks the bar specified by \a index |
| in the bar set specified by \a barset. |
| |
| The corresponding signal handler is \c onDoubleClicked. |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::hovered(bool status, int index, QBarSet* barset) |
| |
| This signal is emitted when a mouse is hovered over the bar specified by \a index in the |
| bar set specified by \a barset. When the mouse moves over the bar, \a status turns \c true, |
| and when the mouse moves away again, it turns \c false. |
| */ |
| /*! |
| \qmlsignal AbstractBarSeries::hovered(bool status, int index, BarSet barset) |
| |
| This signal is emitted when a mouse is hovered over the bar specified by \a index in the |
| bar set specified by \a barset. When the mouse moves over the bar, \a status turns \c true, |
| and when the mouse moves away again, it turns \c false. |
| |
| The corresponding signal handler is \c onHovered. |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::countChanged() |
| This signal is emitted when the number of bar sets is changed, for example by append() or |
| remove(). |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::labelsVisibleChanged() |
| This signal is emitted when the labels' visibility changes. |
| \sa isLabelsVisible(), setLabelsVisible() |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::barsetsAdded(QList<QBarSet*> sets) |
| This signal is emitted when the bar sets specified by \a sets are added to the series. |
| \sa append(), insert() |
| */ |
| /*! |
| \qmlsignal AbstractBarSeries::barsetsAdded() |
| This signal is emitted when bar sets are added to the series. |
| |
| The corresponding signal handler is \c onBarsetsAdded. |
| */ |
| |
| /*! |
| \fn void QAbstractBarSeries::barsetsRemoved(QList<QBarSet*> sets) |
| This signal is emitted when the bar sets specified by \a sets are removed from the series. |
| \sa remove() |
| */ |
| /*! |
| \qmlsignal AbstractBarSeries::barsetsRemoved() |
| This signal is emitted when bar sets are removed from the series. |
| |
| The corresponding signal handler is \c onBarsetsRemoved. |
| */ |
| |
| /*! |
| \qmlmethod BarSet AbstractBarSeries::at(int index) |
| Returns the bar set at \a index. Returns null if the index is not valid. |
| */ |
| |
| /*! |
| \qmlmethod BarSet AbstractBarSeries::append(string label, VariantList values) |
| Adds a new bar set with \a label and \a values to the index. \a values is |
| a list of real values. |
| |
| For example: |
| \code |
| myBarSeries.append("set 1", [0, 0.2, 0.2, 0.5, 0.4, 1.5, 0.9]); |
| \endcode |
| */ |
| |
| /*! |
| \qmlmethod BarSet AbstractBarSeries::insert(int index, string label, VariantList values) |
| Adds a new bar set with \a label and \a values to \a index. \a values can be a list |
| of real values or a list of XYPoint types. |
| |
| If the index value is equal to or less than zero, the new bar set is prepended to the bar |
| series. If the index value is equal to or greater than the number of bar sets in the bar |
| series, the new bar set is appended to the bar series. |
| |
| \sa append() |
| */ |
| |
| /*! |
| \qmlmethod bool AbstractBarSeries::remove(BarSet barset) |
| Removes the bar set specified by \a barset from the series. Returns \c true if successful, |
| \c false otherwise. |
| */ |
| |
| /*! |
| \qmlmethod AbstractBarSeries::clear() |
| Removes all bar sets from the series. |
| */ |
| |
| /*! |
| Removes the abstract bar series and the bar sets owned by it. |
| */ |
| QAbstractBarSeries::~QAbstractBarSeries() |
| { |
| |
| } |
| |
| /*! |
| \internal |
| */ |
| QAbstractBarSeries::QAbstractBarSeries(QAbstractBarSeriesPrivate &o, QObject *parent) |
| : QAbstractSeries(o, parent) |
| { |
| Q_D(QAbstractSeries); |
| QObject::connect(this, SIGNAL(countChanged()), d, SIGNAL(countChanged())); |
| } |
| |
| /*! |
| Sets the width of the bars of the series to \a width. |
| */ |
| void QAbstractBarSeries::setBarWidth(qreal width) |
| { |
| Q_D(QAbstractBarSeries); |
| d->setBarWidth(width); |
| } |
| |
| /*! |
| Returns the width of the bars of the series. |
| \sa setBarWidth() |
| */ |
| qreal QAbstractBarSeries::barWidth() const |
| { |
| Q_D(const QAbstractBarSeries); |
| return d->barWidth(); |
| } |
| |
| /*! |
| Adds a set of bars specified by \a set to the bar series and takes ownership of it. If the set |
| is null or it already belongs to the series, it will not be appended. |
| Returns \c true if appending succeeded. |
| */ |
| bool QAbstractBarSeries::append(QBarSet *set) |
| { |
| Q_D(QAbstractBarSeries); |
| bool success = d->append(set); |
| if (success) { |
| QList<QBarSet *> sets; |
| sets.append(set); |
| set->setParent(this); |
| emit barsetsAdded(sets); |
| emit countChanged(); |
| } |
| return success; |
| } |
| |
| /*! |
| Removes the bar set specified by \a set from the series and permanently deletes it if |
| the removal succeeds. Returns \c true if the set was removed. |
| */ |
| bool QAbstractBarSeries::remove(QBarSet *set) |
| { |
| Q_D(QAbstractBarSeries); |
| bool success = d->remove(set); |
| if (success) { |
| QList<QBarSet *> sets; |
| sets.append(set); |
| set->setParent(0); |
| emit barsetsRemoved(sets); |
| emit countChanged(); |
| delete set; |
| set = 0; |
| } |
| return success; |
| } |
| |
| /*! |
| Takes a single \a set from the series. Does not delete the bar set object. |
| \note The series remains the barset's parent object. You must set the |
| parent object to take full ownership. |
| |
| Returns \c true if the take operation succeeds. |
| */ |
| bool QAbstractBarSeries::take(QBarSet *set) |
| { |
| Q_D(QAbstractBarSeries); |
| bool success = d->remove(set); |
| if (success) { |
| QList<QBarSet *> sets; |
| sets.append(set); |
| emit barsetsRemoved(sets); |
| emit countChanged(); |
| } |
| return success; |
| } |
| |
| /*! |
| Adds a list of bar sets specified by \a sets to a bar series and takes ownership of the sets. |
| Returns \c true if all sets were appended successfully. If any of the sets is null or was |
| previously appended to the series, nothing is appended and this function returns \c false. |
| If any of the sets appears in the list more than once, nothing is appended and this function |
| returns \c false. |
| */ |
| bool QAbstractBarSeries::append(QList<QBarSet *> sets) |
| { |
| Q_D(QAbstractBarSeries); |
| bool success = d->append(sets); |
| if (success) { |
| foreach (QBarSet *set, sets) |
| set->setParent(this); |
| emit barsetsAdded(sets); |
| emit countChanged(); |
| } |
| return success; |
| } |
| |
| /*! |
| Inserts a bar set specified by \a set to a series at the position specified by \a index |
| and takes ownership of the set. If the set is null or already belongs to the series, it will |
| not be appended. Returns \c true if inserting succeeds. |
| */ |
| bool QAbstractBarSeries::insert(int index, QBarSet *set) |
| { |
| Q_D(QAbstractBarSeries); |
| bool success = d->insert(index, set); |
| if (success) { |
| QList<QBarSet *> sets; |
| sets.append(set); |
| emit barsetsAdded(sets); |
| emit countChanged(); |
| } |
| return success; |
| } |
| |
| /*! |
| Removes all bar sets from the series and permanently deletes them. |
| */ |
| void QAbstractBarSeries::clear() |
| { |
| Q_D(QAbstractBarSeries); |
| QList<QBarSet *> sets = barSets(); |
| bool success = d->remove(sets); |
| if (success) { |
| emit barsetsRemoved(sets); |
| emit countChanged(); |
| foreach (QBarSet *set, sets) |
| delete set; |
| } |
| } |
| |
| /*! |
| Returns the number of bar sets in a bar series. |
| */ |
| int QAbstractBarSeries::count() const |
| { |
| Q_D(const QAbstractBarSeries); |
| return d->m_barSets.count(); |
| } |
| |
| /*! |
| Returns a list of bar sets in a bar series. Keeps the ownership of the bar sets. |
| */ |
| QList<QBarSet *> QAbstractBarSeries::barSets() const |
| { |
| Q_D(const QAbstractBarSeries); |
| return d->m_barSets; |
| } |
| |
| /*! |
| Sets the visibility of labels in a bar series to \a visible. |
| */ |
| void QAbstractBarSeries::setLabelsVisible(bool visible) |
| { |
| Q_D(QAbstractBarSeries); |
| if (d->m_labelsVisible != visible) { |
| d->setLabelsVisible(visible); |
| emit labelsVisibleChanged(); |
| } |
| } |
| |
| /*! |
| Returns the visibility of labels. |
| */ |
| bool QAbstractBarSeries::isLabelsVisible() const |
| { |
| Q_D(const QAbstractBarSeries); |
| return d->m_labelsVisible; |
| } |
| |
| void QAbstractBarSeries::setLabelsFormat(const QString &format) |
| { |
| Q_D(QAbstractBarSeries); |
| if (d->m_labelsFormat != format) { |
| d->m_labelsFormat = format; |
| d->setLabelsDirty(true); |
| emit labelsFormatChanged(format); |
| } |
| } |
| |
| QString QAbstractBarSeries::labelsFormat() const |
| { |
| Q_D(const QAbstractBarSeries); |
| return d->m_labelsFormat; |
| } |
| |
| void QAbstractBarSeries::setLabelsAngle(qreal angle) |
| { |
| Q_D(QAbstractBarSeries); |
| if (d->m_labelsAngle != angle) { |
| d->m_labelsAngle = angle; |
| d->setLabelsDirty(true); |
| emit labelsAngleChanged(angle); |
| } |
| } |
| |
| qreal QAbstractBarSeries::labelsAngle() const |
| { |
| Q_D(const QAbstractBarSeries); |
| return d->m_labelsAngle; |
| } |
| |
| void QAbstractBarSeries::setLabelsPosition(QAbstractBarSeries::LabelsPosition position) |
| { |
| Q_D(QAbstractBarSeries); |
| if (d->m_labelsPosition != position) { |
| d->m_labelsPosition = position; |
| emit labelsPositionChanged(position); |
| } |
| } |
| |
| QAbstractBarSeries::LabelsPosition QAbstractBarSeries::labelsPosition() const |
| { |
| Q_D(const QAbstractBarSeries); |
| return d->m_labelsPosition; |
| } |
| |
| void QAbstractBarSeries::setLabelsPrecision(int precision) |
| { |
| Q_D(QAbstractBarSeries); |
| if (d->m_labelsPrecision != precision) { |
| d->m_labelsPrecision = precision; |
| d->setLabelsDirty(true); |
| emit labelsPrecisionChanged(precision); |
| } |
| } |
| |
| int QAbstractBarSeries::labelsPrecision() const |
| { |
| Q_D(const QAbstractBarSeries); |
| return d->m_labelsPrecision; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| QAbstractBarSeriesPrivate::QAbstractBarSeriesPrivate(QAbstractBarSeries *q) : |
| QAbstractSeriesPrivate(q), |
| m_barWidth(0.5), // Default value is 50% of category width |
| m_labelsVisible(false), |
| m_visible(true), |
| m_blockBarUpdate(false), |
| m_labelsFormat(), |
| m_labelsPosition(QAbstractBarSeries::LabelsCenter), |
| m_labelsAngle(0), |
| m_labelsPrecision(6), |
| m_visualsDirty(true), |
| m_labelsDirty(true) |
| { |
| } |
| |
| int QAbstractBarSeriesPrivate::categoryCount() const |
| { |
| // No categories defined. return count of longest set. |
| int count = 0; |
| for (int i = 0; i < m_barSets.count(); i++) { |
| if (m_barSets.at(i)->count() > count) |
| count = m_barSets.at(i)->count(); |
| } |
| |
| return count; |
| } |
| |
| void QAbstractBarSeriesPrivate::setBarWidth(qreal width) |
| { |
| if (width < 0.0) |
| width = 0.0; |
| m_barWidth = width; |
| emit updatedLayout(); |
| } |
| |
| qreal QAbstractBarSeriesPrivate::barWidth() const |
| { |
| return m_barWidth; |
| } |
| |
| QBarSet *QAbstractBarSeriesPrivate::barsetAt(int index) |
| { |
| return m_barSets.at(index); |
| } |
| |
| void QAbstractBarSeriesPrivate::setVisible(bool visible) |
| { |
| m_visible = visible; |
| emit visibleChanged(); |
| } |
| |
| void QAbstractBarSeriesPrivate::setLabelsVisible(bool visible) |
| { |
| m_labelsVisible = visible; |
| emit labelsVisibleChanged(visible); |
| } |
| |
| qreal QAbstractBarSeriesPrivate::min() |
| { |
| if (m_barSets.count() <= 0) |
| return 0; |
| |
| qreal min = INT_MAX; |
| |
| for (int i = 0; i < m_barSets.count(); i++) { |
| int categoryCount = m_barSets.at(i)->count(); |
| for (int j = 0; j < categoryCount; j++) { |
| qreal temp = m_barSets.at(i)->at(j); |
| if (temp < min) |
| min = temp; |
| } |
| } |
| return min; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::max() |
| { |
| if (m_barSets.count() <= 0) |
| return 0; |
| |
| qreal max = INT_MIN; |
| |
| for (int i = 0; i < m_barSets.count(); i++) { |
| int categoryCount = m_barSets.at(i)->count(); |
| for (int j = 0; j < categoryCount; j++) { |
| qreal temp = m_barSets.at(i)->at(j); |
| if (temp > max) |
| max = temp; |
| } |
| } |
| |
| return max; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::valueAt(int set, int category) |
| { |
| if ((set < 0) || (set >= m_barSets.count())) |
| return 0; // No set, no value. |
| else if ((category < 0) || (category >= m_barSets.at(set)->count())) |
| return 0; // No category, no value. |
| |
| return m_barSets.at(set)->at(category); |
| } |
| |
| qreal QAbstractBarSeriesPrivate::percentageAt(int set, int category) |
| { |
| if ((set < 0) || (set >= m_barSets.count())) |
| return 0; // No set, no value. |
| else if ((category < 0) || (category >= m_barSets.at(set)->count())) |
| return 0; // No category, no value. |
| |
| qreal value = m_barSets.at(set)->at(category); |
| qreal sum = categorySum(category); |
| if (qFuzzyCompare(sum, 0)) |
| return 0; |
| |
| return value / sum; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::categorySum(int category) |
| { |
| qreal sum(0); |
| int count = m_barSets.count(); // Count sets |
| for (int set = 0; set < count; set++) { |
| if (category < m_barSets.at(set)->count()) |
| sum += m_barSets.at(set)->at(category); |
| } |
| return sum; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::absoluteCategorySum(int category) |
| { |
| qreal sum(0); |
| int count = m_barSets.count(); // Count sets |
| for (int set = 0; set < count; set++) { |
| if (category < m_barSets.at(set)->count()) |
| sum += qAbs(m_barSets.at(set)->at(category)); |
| } |
| return sum; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::maxCategorySum() |
| { |
| qreal max = INT_MIN; |
| int count = categoryCount(); |
| for (int i = 0; i < count; i++) { |
| qreal sum = categorySum(i); |
| if (sum > max) |
| max = sum; |
| } |
| return max; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::minX() |
| { |
| if (m_barSets.count() <= 0) |
| return 0; |
| |
| qreal min = INT_MAX; |
| |
| for (int i = 0; i < m_barSets.count(); i++) { |
| int categoryCount = m_barSets.at(i)->count(); |
| for (int j = 0; j < categoryCount; j++) { |
| qreal temp = m_barSets.at(i)->d_ptr.data()->m_values.at(j).x(); |
| if (temp < min) |
| min = temp; |
| } |
| } |
| return min; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::maxX() |
| { |
| if (m_barSets.count() <= 0) |
| return 0; |
| |
| qreal max = INT_MIN; |
| |
| for (int i = 0; i < m_barSets.count(); i++) { |
| int categoryCount = m_barSets.at(i)->count(); |
| for (int j = 0; j < categoryCount; j++) { |
| qreal temp = m_barSets.at(i)->d_ptr.data()->m_values.at(j).x(); |
| if (temp > max) |
| max = temp; |
| } |
| } |
| |
| return max; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::categoryTop(int category) |
| { |
| // Returns top (sum of all positive values) of category. |
| // Returns 0, if all values are negative |
| qreal top(0); |
| int count = m_barSets.count(); |
| for (int set = 0; set < count; set++) { |
| if (category < m_barSets.at(set)->count()) { |
| qreal temp = m_barSets.at(set)->at(category); |
| if (temp > 0) { |
| top += temp; |
| } |
| } |
| } |
| return top; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::categoryBottom(int category) |
| { |
| // Returns bottom (sum of all negative values) of category |
| // Returns 0, if all values are positive |
| qreal bottom(0); |
| int count = m_barSets.count(); |
| for (int set = 0; set < count; set++) { |
| if (category < m_barSets.at(set)->count()) { |
| qreal temp = m_barSets.at(set)->at(category); |
| if (temp < 0) { |
| bottom += temp; |
| } |
| } |
| } |
| return bottom; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::top() |
| { |
| // Returns top of all categories |
| qreal top(0); |
| int count = categoryCount(); |
| for (int i = 0; i < count; i++) { |
| qreal temp = categoryTop(i); |
| if (temp > top) |
| top = temp; |
| } |
| return top; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::bottom() |
| { |
| // Returns bottom of all categories |
| qreal bottom(0); |
| int count = categoryCount(); |
| for (int i = 0; i < count; i++) { |
| qreal temp = categoryBottom(i); |
| if (temp < bottom) |
| bottom = temp; |
| } |
| return bottom; |
| } |
| |
| bool QAbstractBarSeriesPrivate::blockBarUpdate() |
| { |
| return m_blockBarUpdate; |
| } |
| |
| qreal QAbstractBarSeriesPrivate::labelsAngle() const |
| { |
| return m_labelsAngle; |
| } |
| |
| void QAbstractBarSeriesPrivate::initializeDomain() |
| { |
| qreal minX(domain()->minX()); |
| qreal minY(domain()->minY()); |
| qreal maxX(domain()->maxX()); |
| qreal maxY(domain()->maxY()); |
| |
| qreal seriesMinX = this->minX(); |
| qreal seriesMaxX = this->maxX(); |
| qreal y = max(); |
| minX = qMin(minX, seriesMinX - (qreal)0.5); |
| minY = qMin(minY, y); |
| maxX = qMax(maxX, seriesMaxX + (qreal)0.5); |
| maxY = qMax(maxY, y); |
| |
| domain()->setRange(minX, maxX, minY, maxY); |
| } |
| |
| QList<QLegendMarker*> QAbstractBarSeriesPrivate::createLegendMarkers(QLegend* legend) |
| { |
| Q_Q(QAbstractBarSeries); |
| QList<QLegendMarker*> markers; |
| |
| foreach(QBarSet* set, q->barSets()) { |
| QBarLegendMarker* marker = new QBarLegendMarker(q,set,legend); |
| markers << marker; |
| } |
| return markers; |
| } |
| |
| |
| bool QAbstractBarSeriesPrivate::append(QBarSet *set) |
| { |
| if ((m_barSets.contains(set)) || (set == 0)) |
| return false; // Fail if set is already in list or set is null. |
| |
| m_barSets.append(set); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::updatedBars, |
| this, &QAbstractBarSeriesPrivate::updatedBars); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueChanged, |
| this, &QAbstractBarSeriesPrivate::handleSetValueChange); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueAdded, |
| this, &QAbstractBarSeriesPrivate::handleSetValueAdd); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved, |
| this, &QAbstractBarSeriesPrivate::handleSetValueRemove); |
| |
| emit restructuredBars(); // this notifies barchartitem |
| return true; |
| } |
| |
| bool QAbstractBarSeriesPrivate::remove(QBarSet *set) |
| { |
| if (!m_barSets.contains(set)) |
| return false; // Fail if set is not in list |
| |
| m_barSets.removeOne(set); |
| QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::updatedBars, |
| this, &QAbstractBarSeriesPrivate::updatedBars); |
| QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueChanged, |
| this, &QAbstractBarSeriesPrivate::handleSetValueChange); |
| QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueAdded, |
| this, &QAbstractBarSeriesPrivate::handleSetValueAdd); |
| QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved, |
| this, &QAbstractBarSeriesPrivate::handleSetValueRemove); |
| |
| emit restructuredBars(); // this notifies barchartitem |
| return true; |
| } |
| |
| bool QAbstractBarSeriesPrivate::append(QList<QBarSet * > sets) |
| { |
| foreach (QBarSet *set, sets) { |
| if ((set == 0) || (m_barSets.contains(set))) |
| return false; // Fail if any of the sets is null or is already appended. |
| if (sets.count(set) != 1) |
| return false; // Also fail if same set is more than once in given list. |
| } |
| |
| foreach (QBarSet *set, sets) { |
| m_barSets.append(set); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::updatedBars, |
| this, &QAbstractBarSeriesPrivate::updatedBars); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueChanged, |
| this, &QAbstractBarSeriesPrivate::handleSetValueChange); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueAdded, |
| this, &QAbstractBarSeriesPrivate::handleSetValueAdd); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved, |
| this, &QAbstractBarSeriesPrivate::handleSetValueRemove); |
| } |
| |
| emit restructuredBars(); // this notifies barchartitem |
| return true; |
| } |
| |
| bool QAbstractBarSeriesPrivate::remove(QList<QBarSet * > sets) |
| { |
| if (sets.count() == 0) |
| return false; |
| |
| foreach (QBarSet *set, sets) { |
| if ((set == 0) || (!m_barSets.contains(set))) |
| return false; // Fail if any of the sets is null or is not in series |
| if (sets.count(set) != 1) |
| return false; // Also fail if same set is more than once in given list. |
| } |
| |
| foreach (QBarSet *set, sets) { |
| m_barSets.removeOne(set); |
| QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::updatedBars, |
| this, &QAbstractBarSeriesPrivate::updatedBars); |
| QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueChanged, |
| this, &QAbstractBarSeriesPrivate::handleSetValueChange); |
| QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueAdded, |
| this, &QAbstractBarSeriesPrivate::handleSetValueAdd); |
| QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved, |
| this, &QAbstractBarSeriesPrivate::handleSetValueRemove); |
| } |
| |
| emit restructuredBars(); // this notifies barchartitem |
| |
| return true; |
| } |
| |
| bool QAbstractBarSeriesPrivate::insert(int index, QBarSet *set) |
| { |
| if ((m_barSets.contains(set)) || (set == 0)) |
| return false; // Fail if set is already in list or set is null. |
| |
| m_barSets.insert(index, set); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::updatedBars, |
| this, &QAbstractBarSeriesPrivate::updatedBars); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueChanged, |
| this, &QAbstractBarSeriesPrivate::handleSetValueChange); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueAdded, |
| this, &QAbstractBarSeriesPrivate::handleSetValueAdd); |
| QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved, |
| this, &QAbstractBarSeriesPrivate::handleSetValueRemove); |
| |
| emit restructuredBars(); // this notifies barchartitem |
| return true; |
| } |
| |
| void QAbstractBarSeriesPrivate::initializeAxes() |
| { |
| Q_Q(QAbstractBarSeries); |
| |
| foreach(QAbstractAxis* axis, m_axes) { |
| if (axis->type() == QAbstractAxis::AxisTypeBarCategory) { |
| switch (q->type()) { |
| case QAbstractSeries::SeriesTypeHorizontalBar: |
| case QAbstractSeries::SeriesTypeHorizontalPercentBar: |
| case QAbstractSeries::SeriesTypeHorizontalStackedBar: |
| if (axis->orientation() == Qt::Vertical) |
| populateCategories(qobject_cast<QBarCategoryAxis *>(axis)); |
| break; |
| case QAbstractSeries::SeriesTypeBar: |
| case QAbstractSeries::SeriesTypePercentBar: |
| case QAbstractSeries::SeriesTypeStackedBar: |
| case QAbstractSeries::SeriesTypeBoxPlot: |
| case QAbstractSeries::SeriesTypeCandlestick: |
| if (axis->orientation() == Qt::Horizontal) |
| populateCategories(qobject_cast<QBarCategoryAxis *>(axis)); |
| break; |
| default: |
| qWarning() << "Unexpected series type"; |
| break; |
| } |
| } |
| } |
| |
| // Make sure series animations are reset when axes change |
| AbstractBarChartItem *item = qobject_cast<AbstractBarChartItem *>(m_item.data()); |
| if (item) |
| item->resetAnimation(); |
| } |
| |
| QAbstractAxis::AxisType QAbstractBarSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const |
| { |
| Q_Q(const QAbstractBarSeries); |
| |
| switch (q->type()) { |
| case QAbstractSeries::SeriesTypeHorizontalBar: |
| case QAbstractSeries::SeriesTypeHorizontalPercentBar: |
| case QAbstractSeries::SeriesTypeHorizontalStackedBar: |
| if (orientation == Qt::Vertical) |
| return QAbstractAxis::AxisTypeBarCategory; |
| break; |
| case QAbstractSeries::SeriesTypeBar: |
| case QAbstractSeries::SeriesTypePercentBar: |
| case QAbstractSeries::SeriesTypeStackedBar: |
| case QAbstractSeries::SeriesTypeBoxPlot: |
| case QAbstractSeries::SeriesTypeCandlestick: |
| if (orientation == Qt::Horizontal) |
| return QAbstractAxis::AxisTypeBarCategory; |
| break; |
| default: |
| qWarning() << "Unexpected series type"; |
| break; |
| } |
| return QAbstractAxis::AxisTypeValue; |
| |
| } |
| |
| void QAbstractBarSeriesPrivate::handleSetValueChange(int index) |
| { |
| QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(sender()); |
| if (priv) |
| emit setValueChanged(index, priv->q_ptr); |
| } |
| |
| void QAbstractBarSeriesPrivate::handleSetValueAdd(int index, int count) |
| { |
| QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(sender()); |
| if (priv) |
| emit setValueAdded(index, count, priv->q_ptr); |
| } |
| |
| void QAbstractBarSeriesPrivate::handleSetValueRemove(int index, int count) |
| { |
| QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(sender()); |
| if (priv) |
| emit setValueRemoved(index, count, priv->q_ptr); |
| } |
| |
| void QAbstractBarSeriesPrivate::populateCategories(QBarCategoryAxis *axis) |
| { |
| QStringList categories; |
| if (axis->categories().isEmpty()) { |
| for (int i(1); i < categoryCount() + 1; i++) |
| categories << presenter()->numberToString(i); |
| axis->append(categories); |
| } |
| } |
| |
| QAbstractAxis* QAbstractBarSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const |
| { |
| if (defaultAxisType(orientation) == QAbstractAxis::AxisTypeBarCategory) |
| return new QBarCategoryAxis; |
| else |
| return new QValueAxis; |
| } |
| |
| void QAbstractBarSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced) |
| { |
| m_blockBarUpdate = true; // Ensures that the bars are not updated before the theme is ready |
| |
| const QList<QGradient> gradients = theme->seriesGradients(); |
| |
| // Since each bar series uses different number of colors, we need to account for other |
| // bar series in the chart that are also themed when choosing colors. |
| // First series count is used to determine the color stepping to keep old applications |
| // with single bar series with a lot of sets colored as they always have been. |
| int actualIndex = 0; |
| int firstSeriesSetCount = m_barSets.count(); |
| if (!m_item.isNull()) { |
| auto seriesMap = m_item->themeManager()->seriesMap(); |
| int lowestSeries = index; |
| for (auto it = seriesMap.cbegin(), end = seriesMap.cend(); it != end; ++it) { |
| if (it.value() != index) { |
| auto barSeries = qobject_cast<QAbstractBarSeries *>(it.key()); |
| if (barSeries) { |
| actualIndex += barSeries->count(); |
| if (it.value() < lowestSeries) { |
| firstSeriesSetCount = qMax(barSeries->count(), gradients.count()); |
| lowestSeries = it.value(); |
| } |
| } |
| } |
| } |
| } |
| |
| qreal takeAtPos = 0.5; |
| qreal step = 0.2; |
| if (firstSeriesSetCount > 1) { |
| step = 1.0 / qreal(firstSeriesSetCount); |
| if (firstSeriesSetCount % gradients.count()) |
| step *= gradients.count(); |
| else |
| step *= (gradients.count() - 1); |
| if (index > 0) { |
| // Take necessary amount of initial steps |
| int initialStepper = actualIndex; |
| while (initialStepper > gradients.count()) { |
| initialStepper -= gradients.count(); |
| takeAtPos += step; |
| if (takeAtPos == 1.0) |
| takeAtPos += step; |
| takeAtPos -= int(takeAtPos); |
| } |
| } |
| } |
| |
| for (int i(0); i < m_barSets.count(); i++) { |
| int colorIndex = (actualIndex + i) % gradients.count(); |
| if ((actualIndex + i) > 0 && (actualIndex + i) % gradients.count() == 0) { |
| // There is no dedicated base color for each sets, generate more colors |
| takeAtPos += step; |
| if (takeAtPos == 1.0) |
| takeAtPos += step; |
| takeAtPos -= (int) takeAtPos; |
| } |
| if (forced || QChartPrivate::defaultBrush() == m_barSets.at(i)->d_ptr->m_brush) |
| m_barSets.at(i)->setBrush(ChartThemeManager::colorAt(gradients.at(colorIndex), takeAtPos)); |
| |
| // Pick label color from the opposite end of the gradient. |
| // 0.3 as a boundary seems to work well. |
| if (forced || QChartPrivate::defaultBrush() == m_barSets.at(i)->d_ptr->m_labelBrush) { |
| if (takeAtPos < 0.3) |
| m_barSets.at(i)->setLabelBrush(ChartThemeManager::colorAt(gradients.at(actualIndex % gradients.size()), 1)); |
| else |
| m_barSets.at(i)->setLabelBrush(ChartThemeManager::colorAt(gradients.at(actualIndex % gradients.size()), 0)); |
| } |
| if (forced || QChartPrivate::defaultPen() == m_barSets.at(i)->d_ptr->m_pen) { |
| QColor c = ChartThemeManager::colorAt(gradients.at(actualIndex % gradients.size()), 0.0); |
| m_barSets.at(i)->setPen(c); |
| } |
| } |
| m_blockBarUpdate = false; |
| emit updatedBars(); |
| } |
| |
| void QAbstractBarSeriesPrivate::initializeAnimations(QChart::AnimationOptions options, int duration, |
| QEasingCurve &curve) |
| { |
| AbstractBarChartItem *bar = static_cast<AbstractBarChartItem *>(m_item.data()); |
| Q_ASSERT(bar); |
| if (bar->animation()) |
| bar->animation()->stopAndDestroyLater(); |
| |
| if (options.testFlag(QChart::SeriesAnimations)) |
| bar->setAnimation(new BarAnimation(bar, duration, curve)); |
| else |
| bar->setAnimation(0); |
| QAbstractSeriesPrivate::initializeAnimations(options, duration, curve); |
| } |
| |
| QT_CHARTS_END_NAMESPACE |
| |
| #include "moc_qabstractbarseries.cpp" |
| #include "moc_qabstractbarseries_p.cpp" |