summaryrefslogtreecommitdiffstats
path: root/src/charts/boxplotchart/qboxplotseries.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/charts/boxplotchart/qboxplotseries.cpp')
-rw-r--r--src/charts/boxplotchart/qboxplotseries.cpp690
1 files changed, 690 insertions, 0 deletions
diff --git a/src/charts/boxplotchart/qboxplotseries.cpp b/src/charts/boxplotchart/qboxplotseries.cpp
new file mode 100644
index 00000000..2f444b40
--- /dev/null
+++ b/src/charts/boxplotchart/qboxplotseries.cpp
@@ -0,0 +1,690 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the Qt Enterprise Charts Add-on.
+**
+** $QT_BEGIN_LICENSE$
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qboxplotseries.h"
+#include "qboxplotseries_p.h"
+#include "qboxplotlegendmarker.h"
+#include "qbarcategoryaxis.h"
+#include "boxplotchartitem_p.h"
+#include "chartdataset_p.h"
+#include "charttheme_p.h"
+#include "qvalueaxis.h"
+#include "charttheme_p.h"
+#include "boxplotanimation_p.h"
+#include "qchart_p.h"
+#include "qboxset.h"
+#include "qboxset_p.h"
+
+QT_CHARTS_BEGIN_NAMESPACE
+
+/*!
+ \class QBoxPlotSeries
+ \inmodule Qt Charts
+ \brief Series for creating box-and-whiskers chart.
+ \mainclass
+
+ QBoxPlotSeries represents a series of data shown as box-and-whisker bars. The purpose of this class is to act as
+ a container for single box-and-whisker items. Each item is drawn to own slot. If chart includes multiple instances of
+ QBoxPlotSeries then box-and-whiskers items with the same index are drawn to same slot.
+
+ See the \l {Box and Whiskers Example} {box-and-whiskers chart example} to learn how to create a box-and-whiskers chart.
+ \image examples_boxplotchart.png
+
+ \sa QBoxSet
+*/
+/*!
+ \fn QBoxPlotSeries::boxsetsAdded(QList<QBoxSet *> sets)
+ \brief Signal is emitted when a new \a sets of box-and-whiskers data is added to the series.
+*/
+/*!
+ \fn QBoxPlotSeries::boxsetsRemoved(QList<QBoxSet *> sets)
+ \brief Signal is emitted when \a sets of box-and-whiskers data is removed from the series.
+*/
+/*!
+ \fn QBoxPlotSeries::clicked(QBoxSet *boxset)
+ \brief Signal is emitted when the user clicks the \a boxset on the chart.
+*/
+/*!
+ \fn QBoxPlotSeries::hovered(bool status, QBoxSet *boxset)
+ \brief Signal is emitted when there is change in hover \a status over \a boxset.
+*/
+/*!
+ \fn QBoxPlotSeries::countChanged()
+ \brief Signal is emitted when there is change in count of box-and-whiskers items in the series.
+*/
+/*!
+ \property QBoxPlotSeries::boxOutlineVisible
+ \brief This property configures the visibility of the middle box outline.
+*/
+/*!
+ \property QBoxPlotSeries::boxWidth
+ \brief This property configures the width of the box-and-whiskers item. The value signifies the relative
+ width of the box-and-whiskers item inside its own slot. The value can between 0.0 and 1.0. Negative values
+ are clamped to 0.0 and values over 1.0 are clamped to 1.0.
+*/
+/*!
+ \property QBoxPlotSeries::pen
+ \brief This property configures the pen of the box-and-whiskers items.
+*/
+/*!
+ \property QBoxPlotSeries::brush
+ \brief This property configures the brush of the box-and-whiskers items.
+*/
+/*!
+ \property QBoxPlotSeries::count
+ \brief The count of sets in series.
+*/
+
+/*!
+ \qmlproperty QString BoxPlotSeries::brushFilename
+ The name of the file used as a brush for the series.
+*/
+
+/*!
+ \fn void QBoxPlotSeries::boxOutlineVisibilityChanged()
+ Signal is emitted when the middle box outline visibility is changed.
+*/
+/*!
+ \fn void QBoxPlotSeries::boxWidthChanged()
+ Signal is emitted when the width of the box-and-whiskers item is changed.
+*/
+/*!
+ \fn void QBoxPlotSeries::penChanged()
+ This signal is emitted when the pen of the box-and-whiskers has changed.
+ \sa brush
+*/
+/*!
+ \fn void QBoxPlotSeries::brushChanged()
+ This signal is emitted when the brush of the box-and-whiskers has changed.
+ \sa brush
+*/
+/*!
+ \fn virtual SeriesType QBoxPlotSeries::type() const
+ \brief Returns type of series.
+ \sa QAbstractSeries, SeriesType
+*/
+
+/*!
+ Constructs empty QBoxPlotSeries.
+ QBoxPlotSeries is QObject which is a child of a \a parent.
+*/
+QBoxPlotSeries::QBoxPlotSeries(QObject *parent)
+ : QAbstractSeries(*new QBoxPlotSeriesPrivate(this), parent)
+{
+}
+
+/*!
+ Destructor. Removes series from chart.
+*/
+QBoxPlotSeries::~QBoxPlotSeries()
+{
+ Q_D(QBoxPlotSeries);
+ if (d->m_chart)
+ d->m_chart->removeSeries(this);
+}
+
+/*!
+ Adds a single box and whiskers set to series. Takes ownership of the \a set. If the set is null or is already in series, it won't be appended.
+ Returns true, if appending succeeded.
+*/
+bool QBoxPlotSeries::append(QBoxSet *set)
+{
+ Q_D(QBoxPlotSeries);
+
+ bool success = d->append(set);
+ if (success) {
+ QList<QBoxSet *> sets;
+ sets.append(set);
+ set->setParent(this);
+ emit boxsetsAdded(sets);
+ emit countChanged();
+ }
+ return success;
+}
+
+/*!
+ Removes boxset from the series. Deletes the \a set and returns true if successful.
+*/
+bool QBoxPlotSeries::remove(QBoxSet *set)
+{
+ Q_D(QBoxPlotSeries);
+ bool success = d->remove(set);
+ if (success) {
+ QList<QBoxSet *> sets;
+ sets.append(set);
+ set->setParent(0);
+ emit boxsetsRemoved(sets);
+ emit countChanged();
+ delete set;
+ set = 0;
+ }
+ return success;
+}
+
+/*!
+ Takes a single \a set from the series. Does not delete the boxset object.
+
+ NOTE: The series remains as the boxset's parent object. You must set the
+ parent object to take full ownership.
+
+ Returns true if take was successful.
+*/
+bool QBoxPlotSeries::take(QBoxSet *set)
+{
+ Q_D(QBoxPlotSeries);
+
+ bool success = d->remove(set);
+ if (success) {
+ QList<QBoxSet *> sets;
+ sets.append(set);
+ emit boxsetsRemoved(sets);
+ emit countChanged();
+ }
+ return success;
+}
+
+/*!
+ Adds a list of boxsets to series. Takes ownership of the \a sets.
+ Returns true, if all sets were appended successfully. If any of the sets is null or is already appended to series,
+ nothing is appended and function returns false. If any of the sets is in list more than once, nothing is appended
+ and function returns false.
+*/
+bool QBoxPlotSeries::append(QList<QBoxSet *> sets)
+{
+ Q_D(QBoxPlotSeries);
+ bool success = d->append(sets);
+ if (success) {
+ emit boxsetsAdded(sets);
+ emit countChanged();
+ }
+ return success;
+}
+
+/*!
+ Insert a box-and-whiskers set to the series at \a index postion. Takes ownership of the \a set. If the set is null or
+ is already in series, it won't be appended. Returns true, if inserting succeeded.
+
+*/
+bool QBoxPlotSeries::insert(int index, QBoxSet *set)
+{
+ Q_D(QBoxPlotSeries);
+ bool success = d->insert(index, set);
+ if (success) {
+ QList<QBoxSet *> sets;
+ sets.append(set);
+ emit boxsetsAdded(sets);
+ emit countChanged();
+ }
+ return success;
+}
+
+/*!
+ Removes all boxsets from the series. Deletes removed sets.
+*/
+void QBoxPlotSeries::clear()
+{
+ Q_D(QBoxPlotSeries);
+ QList<QBoxSet *> sets = boxSets();
+ bool success = d->remove(sets);
+ if (success) {
+ emit boxsetsRemoved(sets);
+ emit countChanged();
+ foreach (QBoxSet *set, sets)
+ delete set;
+ }
+}
+
+/*!
+ Returns number of sets in series.
+*/
+int QBoxPlotSeries::count() const
+{
+ Q_D(const QBoxPlotSeries);
+ return d->m_boxSets.count();
+}
+
+/*!
+ Returns a list of sets in series. Keeps ownership of sets.
+ */
+QList<QBoxSet *> QBoxPlotSeries::boxSets() const
+{
+ Q_D(const QBoxPlotSeries);
+ return d->m_boxSets;
+}
+
+/*
+ Returns QAbstractSeries::SeriesTypeBoxPlot.
+*/
+QAbstractSeries::SeriesType QBoxPlotSeries::type() const
+{
+ return QAbstractSeries::SeriesTypeBoxPlot;
+}
+
+void QBoxPlotSeries::setBoxOutlineVisible(bool visible)
+{
+ Q_D(QBoxPlotSeries);
+
+ if (d->m_boxOutlineVisible != visible) {
+ d->m_boxOutlineVisible = visible;
+ emit d->updated();
+ emit boxOutlineVisibilityChanged();
+ }
+}
+
+bool QBoxPlotSeries::boxOutlineVisible()
+{
+ Q_D(QBoxPlotSeries);
+
+ return d->m_boxOutlineVisible;
+}
+
+void QBoxPlotSeries::setBoxWidth(qreal width)
+{
+ Q_D(QBoxPlotSeries);
+
+ if (width != d->m_boxWidth) {
+ if (width < 0.0)
+ width = 0.0;
+ if (width > 1.0)
+ width = 1.0;
+ d->m_boxWidth = width;
+ emit d->updatedLayout();
+ emit boxWidthChanged();
+ }
+}
+
+qreal QBoxPlotSeries::boxWidth()
+{
+ Q_D(QBoxPlotSeries);
+
+ return d->m_boxWidth;
+}
+
+void QBoxPlotSeries::setBrush(const QBrush &brush)
+{
+ Q_D(QBoxPlotSeries);
+
+ if (d->m_brush != brush) {
+ d->m_brush = brush;
+ emit d->updated();
+ emit brushChanged();
+ }
+}
+
+QBrush QBoxPlotSeries::brush() const
+{
+ Q_D(const QBoxPlotSeries);
+
+ return d->m_brush;
+}
+
+void QBoxPlotSeries::setPen(const QPen &pen)
+{
+ Q_D(QBoxPlotSeries);
+
+ if (d->m_pen != pen) {
+ d->m_pen = pen;
+ emit d->updated();
+ emit penChanged();
+ }
+}
+
+QPen QBoxPlotSeries::pen() const
+{
+ Q_D(const QBoxPlotSeries);
+
+ return d->m_pen;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+QBoxPlotSeriesPrivate::QBoxPlotSeriesPrivate(QBoxPlotSeries *q)
+ : QAbstractSeriesPrivate(q),
+ m_pen(QChartPrivate::defaultPen()),
+ m_brush(QChartPrivate::defaultBrush()),
+ m_boxOutlineVisible(true),
+ m_boxWidth(0.5)
+{
+}
+
+QBoxPlotSeriesPrivate::~QBoxPlotSeriesPrivate()
+{
+ disconnect(this, 0, 0, 0);
+}
+
+void QBoxPlotSeriesPrivate::initializeDomain()
+{
+ qreal minX(domain()->minX());
+ qreal minY(domain()->minY());
+ qreal maxX(domain()->maxX());
+ qreal maxY(domain()->maxY());
+
+ qreal x = m_boxSets.count();
+ minX = qMin(minX, qreal(-0.5));
+ minY = qMin(minY, min());
+ maxX = qMax(maxX, x - qreal(0.5));
+ maxY = qMax(maxY, max());
+
+ domain()->setRange(minX, maxX, minY, maxY);
+}
+
+void QBoxPlotSeriesPrivate::initializeAxes()
+{
+ foreach (QAbstractAxis* axis, m_axes) {
+ if (axis->type() == QAbstractAxis::AxisTypeBarCategory) {
+ if (axis->orientation() == Qt::Horizontal)
+ populateCategories(qobject_cast<QBarCategoryAxis *>(axis));
+ }
+ }
+}
+
+QAbstractAxis::AxisType QBoxPlotSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
+{
+ if (orientation == Qt::Horizontal)
+ return QAbstractAxis::AxisTypeBarCategory;
+
+ return QAbstractAxis::AxisTypeValue;
+}
+
+QAbstractAxis* QBoxPlotSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const
+{
+ if (defaultAxisType(orientation) == QAbstractAxis::AxisTypeBarCategory)
+ return new QBarCategoryAxis;
+ else
+ return new QValueAxis;
+}
+
+void QBoxPlotSeriesPrivate::populateCategories(QBarCategoryAxis *axis)
+{
+ QStringList categories;
+ if (axis->categories().isEmpty()) {
+ for (int i(1); i < m_boxSets.count() + 1; i++) {
+ QBoxSet *set = m_boxSets.at(i - 1);
+ if (set->label().isEmpty())
+ categories << presenter()->numberToString(i);
+ else
+ categories << set->label();
+ }
+ axis->append(categories);
+ }
+}
+
+void QBoxPlotSeriesPrivate::initializeGraphics(QGraphicsItem *parent)
+{
+ Q_Q(QBoxPlotSeries);
+
+ BoxPlotChartItem *boxPlot = new BoxPlotChartItem(q, parent);
+ m_item.reset(boxPlot);
+ QAbstractSeriesPrivate::initializeGraphics(parent);
+
+ if (m_chart) {
+ connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), this, SLOT(handleSeriesChange(QAbstractSeries*)) );
+ connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries*)), this, SLOT(handleSeriesRemove(QAbstractSeries*)) );
+
+ QList<QAbstractSeries *> serieses = m_chart->series();
+
+ // Tries to find this series from the Chart's list of series and deduce the index
+ int index = 0;
+ foreach (QAbstractSeries *s, serieses) {
+ if (s->type() == QAbstractSeries::SeriesTypeBoxPlot) {
+ if (q == static_cast<QBoxPlotSeries *>(s)) {
+ boxPlot->m_seriesIndex = index;
+ m_index = index;
+ }
+ index++;
+ }
+ }
+ boxPlot->m_seriesCount = index;
+ }
+
+ // Make BoxPlotChartItem to instantiate box & whisker items
+ boxPlot->handleDataStructureChanged();
+}
+
+void QBoxPlotSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced)
+{
+ Q_Q(QBoxPlotSeries);
+
+ const QList<QGradient> gradients = theme->seriesGradients();
+
+ if (forced || QChartPrivate::defaultBrush() == m_brush) {
+ QColor brushColor = ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 0.5);
+ q->setBrush(brushColor);
+ }
+
+ if (forced || QChartPrivate::defaultPen() == m_pen) {
+ QPen pen = theme->outlinePen();
+ pen.setCosmetic(true);
+ q->setPen(pen);
+ }
+}
+
+void QBoxPlotSeriesPrivate::initializeAnimations(QChart::AnimationOptions options)
+{
+ BoxPlotChartItem *item = static_cast<BoxPlotChartItem *>(m_item.data());
+ Q_ASSERT(item);
+ if (item->animation())
+ item->animation()->stopAndDestroyLater();
+
+ if (options.testFlag(QChart::SeriesAnimations))
+ m_animation = new BoxPlotAnimation(item);
+ else
+ m_animation = 0;
+ item->setAnimation(m_animation);
+
+ QAbstractSeriesPrivate::initializeAnimations(options);
+}
+
+QList<QLegendMarker*> QBoxPlotSeriesPrivate::createLegendMarkers(QLegend *legend)
+{
+ Q_Q(QBoxPlotSeries);
+ QList<QLegendMarker *> list;
+ return list << new QBoxPlotLegendMarker(q, legend);
+}
+
+void QBoxPlotSeriesPrivate::handleSeriesRemove(QAbstractSeries *series)
+{
+ Q_Q(QBoxPlotSeries);
+
+ QBoxPlotSeries *removedSeries = static_cast<QBoxPlotSeries *>(series);
+
+ if (q == removedSeries && m_animation) {
+ m_animation->stopAll();
+ QObject::disconnect(m_chart->d_ptr->m_dataset, 0, removedSeries->d_func(), 0);
+ }
+
+ // Test if series removed is me, then don't do anything
+ if (q != removedSeries) {
+ BoxPlotChartItem *item = static_cast<BoxPlotChartItem *>(m_item.data());
+ if (item) {
+ item->m_seriesCount = item->m_seriesCount - 1;
+ if (removedSeries->d_func()->m_index < m_index) {
+ m_index--;
+ item->m_seriesIndex = m_index;
+ }
+
+ item->handleDataStructureChanged();
+ }
+ }
+}
+
+void QBoxPlotSeriesPrivate::handleSeriesChange(QAbstractSeries *series)
+{
+ Q_UNUSED(series);
+
+ Q_Q(QBoxPlotSeries);
+
+ BoxPlotChartItem *boxPlot = static_cast<BoxPlotChartItem *>(m_item.data());
+
+ if (m_chart) {
+ QList<QAbstractSeries *> serieses = m_chart->series();
+
+ // Tries to find this series from the Chart's list of series and deduce the index
+ int index = 0;
+ foreach (QAbstractSeries *s, serieses) {
+ if (s->type() == QAbstractSeries::SeriesTypeBoxPlot) {
+ if (q == static_cast<QBoxPlotSeries *>(s)) {
+ boxPlot->m_seriesIndex = index;
+ m_index = index;
+ }
+ index++;
+ }
+ }
+ boxPlot->m_seriesCount = index;
+ }
+
+ boxPlot->handleDataStructureChanged();
+}
+
+bool QBoxPlotSeriesPrivate::append(QBoxSet *set)
+{
+ if (m_boxSets.contains(set) || (set == 0) || set->d_ptr->m_series)
+ return false; // Fail if set is already in list or set is null.
+
+ m_boxSets.append(set);
+ QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
+ QObject::connect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
+ QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
+ set->d_ptr->m_series = this;
+
+ emit restructuredBoxes(); // this notifies boxplotchartitem
+ return true;
+}
+
+bool QBoxPlotSeriesPrivate::remove(QBoxSet *set)
+{
+ if (!m_boxSets.contains(set))
+ return false; // Fail if set is not in list
+
+ set->d_ptr->m_series = 0;
+ m_boxSets.removeOne(set);
+ QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
+ QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
+ QObject::disconnect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
+
+ emit restructuredBoxes(); // this notifies boxplotchartitem
+ return true;
+}
+
+bool QBoxPlotSeriesPrivate::append(QList<QBoxSet *> sets)
+{
+ foreach (QBoxSet *set, sets) {
+ if ((set == 0) || m_boxSets.contains(set) || set->d_ptr->m_series)
+ 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 (QBoxSet *set, sets) {
+ m_boxSets.append(set);
+ QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
+ QObject::connect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
+ QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
+ set->d_ptr->m_series = this;
+ }
+
+ emit restructuredBoxes(); // this notifies boxplotchartitem
+ return true;
+}
+
+bool QBoxPlotSeriesPrivate::remove(QList<QBoxSet *> sets)
+{
+ if (sets.count() == 0)
+ return false;
+
+ foreach (QBoxSet *set, sets) {
+ if ((set == 0) || (!m_boxSets.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 (QBoxSet *set, sets) {
+ set->d_ptr->m_series = 0;
+ m_boxSets.removeOne(set);
+ QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
+ QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
+ QObject::disconnect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
+ }
+
+ emit restructuredBoxes(); // this notifies boxplotchartitem
+
+ return true;
+}
+
+bool QBoxPlotSeriesPrivate::insert(int index, QBoxSet *set)
+{
+ if ((m_boxSets.contains(set)) || (set == 0) || set->d_ptr->m_series)
+ return false; // Fail if set is already in list or set is null.
+
+ m_boxSets.insert(index, set);
+ set->d_ptr->m_series = this;
+ QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
+ QObject::connect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
+ QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
+
+ emit restructuredBoxes(); // this notifies boxplotchartitem
+ return true;
+}
+
+QBoxSet *QBoxPlotSeriesPrivate::boxSetAt(int index)
+{
+ return m_boxSets.at(index);
+}
+
+qreal QBoxPlotSeriesPrivate::min()
+{
+ if (m_boxSets.count() <= 0)
+ return 0;
+
+ qreal min = m_boxSets.at(0)->at(0);
+
+ foreach (QBoxSet *set, m_boxSets) {
+ for (int i = 0; i < 5; i++) {
+ if (set->at(i) < min)
+ min = set->at(i);
+ }
+ }
+
+ return min;
+}
+
+qreal QBoxPlotSeriesPrivate::max()
+{
+ if (m_boxSets.count() <= 0)
+ return 0;
+
+ qreal max = m_boxSets.at(0)->at(0);
+
+ foreach (QBoxSet *set, m_boxSets) {
+ for (int i = 0; i < 5; i++) {
+ if (set->at(i) > max)
+ max = set->at(i);
+ }
+ }
+
+ return max;
+}
+
+#include "moc_qboxplotseries.cpp"
+#include "moc_qboxplotseries_p.cpp"
+
+QT_CHARTS_END_NAMESPACE
+