diff --git a/src/boxplotchart/boxplotchart.pri b/src/boxplotchart/boxplotchart.pri new file mode 100644 index 0000000..57e1772 --- /dev/null +++ b/src/boxplotchart/boxplotchart.pri @@ -0,0 +1,16 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +SOURCES += \ + $$PWD/boxplotchartitem.cpp \ + $$PWD/qboxplotseries.cpp \ + $$PWD/boxwhiskers.cpp + +PRIVATE_HEADERS += \ + $$PWD/boxplotchartitem_p.h \ + $$PWD/qboxplotseries_p.h \ + $$PWD/boxwhiskers_p.h \ + $$PWD/boxwhiskersdata_p.h + +PUBLIC_HEADERS += \ + $$PWD/qboxplotseries.h diff --git a/src/boxplotchart/boxplotchartitem.cpp b/src/boxplotchart/boxplotchartitem.cpp new file mode 100644 index 0000000..03f1f40 --- /dev/null +++ b/src/boxplotchart/boxplotchartitem.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial 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 "boxplotchartitem_p.h" +#include "qboxplotseries_p.h" +#include "bar_p.h" +#include "qbarset_p.h" +#include "qabstractbarseries_p.h" +#include "qbarset.h" +#include "boxwhiskers_p.h" +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +BoxPlotChartItem::BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem* item) : + ChartItem(series->d_func(), item), + m_series(series), + m_animation(0), + m_animate(0) +{ + connect(series->d_func(), SIGNAL(restructuredBars()), this, SLOT(handleDataStructureChanged())); + connect(series->d_func(), SIGNAL(updatedLayout()), this, SLOT(handleLayoutChanged())); + connect(series->d_func(), SIGNAL(updatedBars()), this, SLOT(handleUpdatedBars())); + connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdatedBars())); + // QBoxPlotSeriesPrivate calls handleDataStructureChanged(), don't do it here + setZValue(ChartPresenter::BoxPlotSeriesZValue); + + m_barSets = m_series->barSets(); +} + +BoxPlotChartItem::~BoxPlotChartItem() +{ + qDebug() << "BoxPlotChartItem::~BoxPlotChartItem() " << m_seriesIndex; +} + +void BoxPlotChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(widget); + + //painter->setClipRect(QRectF(QPointF(0,0),domain()->size())); + + //qDebug() << "ALERT EMPTY: BoxPlotChartItem::paint"; +} + +void BoxPlotChartItem::setAnimation(BoxPlotAnimation *animation) +{ + //qDebug() << "BoxPlotChartItem::setAnimation :" << animation; + + m_animation = animation; + if (m_animation) { + foreach (BoxWhiskers *item, m_boxTable.values()) { + m_animation->addBox(item); + } + handleDomainUpdated(); + } +} + +void BoxPlotChartItem::handleDataStructureChanged() +{ + //qDebug() << "BoxPlotChartItem::handleDataStructureChanged()"; + int setCount = m_series->count(); + + for (int s = 0; s < setCount; s++) { + QBarSet *set = m_series->d_func()->barsetAt(s); + + BoxWhiskers *boxWhiskersItem = m_boxTable.value(set); + if (boxWhiskersItem == 0) { + // Item is not yet created, make a box and add it to hash table + boxWhiskersItem = new BoxWhiskers(domain(), this); + m_boxTable.insert(set, boxWhiskersItem); + + boxWhiskersItem->setBrush(m_series->brush()); + boxWhiskersItem->setPen(m_series->pen()); + } + updateBoxGeometry(boxWhiskersItem, s); + + boxWhiskersItem->updateGeometry(); + + if (m_animation) + m_animation->addBox(boxWhiskersItem); + } + + // + handleDomainUpdated(); +} + +void BoxPlotChartItem::handleUpdatedBars() +{ + //qDebug() << "BoxPlotChartItem::handleUpdatedBars()"; + + foreach (BoxWhiskers *item, m_boxTable.values()) { + item->setBrush(m_series->brush()); + item->setPen(m_series->pen()); + } +} + +void BoxPlotChartItem::handleDomainUpdated() +{ + //qDebug() << "BoxPlotChartItem::handleDomainUpdated() domain()->size() = " << domain()->size(); + + if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0)) + return; + + // Set my bounding rect to same as domain size + m_boundingRect.setRect(0.0, 0.0, domain()->size().width(), domain()->size().height()); + + foreach (BoxWhiskers *item, m_boxTable.values()) { + // Update the domain size for each BoxWhisker item + item->setDomainSize(domain()->size()); + + // If the animation is set, start the animation for each BoxWhisker item + if (m_animation) { + presenter()->startAnimation(m_animation->boxAnimation(item)); + } + } +} + +void BoxPlotChartItem::handleLayoutChanged() +{ + //qDebug() << "BoxPlotChartItem::handleLayoutChanged() domain()->size() = " << domain()->size(); + //foreach (BoxWhiskers *boxWhiskersItem, m_boxes) + // boxWhiskersItem->updateGeometry(); +} + +void BoxPlotChartItem::handleSeriesRemove(QAbstractSeries *series) +{ + qDebug() << "BoxPlotChartItem::handleSeriesRemove " << m_seriesIndex; + QBoxPlotSeries *removedSeries = static_cast(series); + if (m_series == removedSeries) { + return; + } + + m_seriesCount = m_seriesCount - 1; + + handleDataStructureChanged(); +} + +QRectF BoxPlotChartItem::boundingRect() const +{ + return m_boundingRect; +} + +void BoxPlotChartItem::initializeLayout() +{ + qDebug() << "ALERT EMPTY: BoxPlotChartItem::initializeLayout"; +} + +QVector BoxPlotChartItem::calculateLayout() +{ + return QVector(); +} + +void BoxPlotChartItem::updateBoxGeometry(BoxWhiskers *box, int index) +{ + QBarSet *set = m_series->d_func()->barsetAt(index); + //QBarSet *set = m_barSets.at(index); + BoxWhiskersData &data = box->m_data; + data.m_lowerExtreme = set->at(0); + data.m_lowerQuartile = set->at(1); + data.m_median = set->at(2); + data.m_upperQuartile = set->at(3); + data.m_upperExtreme = set->at(4); + data.m_index = index; + data.m_boxItems = m_series->count(); + + data.m_maxX = domain()->maxX(); + data.m_minX = domain()->minX(); + data.m_maxY = domain()->maxY(); + data.m_minY = domain()->minY(); + + data.m_seriesIndex = m_seriesIndex; + data.m_seriesCount = m_seriesCount; +} + +#include "moc_boxplotchartitem_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/boxplotchart/boxplotchartitem_p.h b/src/boxplotchart/boxplotchartitem_p.h new file mode 100644 index 0000000..40813f4 --- /dev/null +++ b/src/boxplotchart/boxplotchartitem_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + + +#ifndef BOXPLOTCHARTITEM_H +#define BOXPLOTCHARTITEM_H + +#include "boxwhiskers_p.h" +#include "qboxplotseries.h" +#include "chartitem_p.h" +#include "boxplotanimation_p.h" +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class BoxPlotSeriesPrivate; + +class BoxPlotChartItem : public ChartItem +{ + Q_OBJECT +public: + BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem* item =0); + ~BoxPlotChartItem(); + + void setAnimation(BoxPlotAnimation *animation); + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + QRectF boundingRect() const; + +public Q_SLOTS: + void handleDataStructureChanged(); + void handleDomainUpdated(); + void handleLayoutChanged(); + void handleUpdatedBars(); + void handleSeriesRemove(QAbstractSeries *series); + +private: + virtual QVector calculateLayout(); + void initializeLayout(); + void updateBoxGeometry(BoxWhiskers *box, int index); + +protected: + friend class QBoxPlotSeriesPrivate; + QBoxPlotSeries *m_series; // Not owned. + QList m_boxes; + QHash m_boxTable; + QList m_barSets; + int m_seriesIndex; + int m_seriesCount; + + BoxPlotAnimation *m_animation; + bool m_animate; + + QRectF m_boundingRect; + + //QList m_animations; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // BOXPLOTCHARTITEM_H diff --git a/src/boxplotchart/boxwhiskers.cpp b/src/boxplotchart/boxwhiskers.cpp new file mode 100644 index 0000000..b975ce1 --- /dev/null +++ b/src/boxplotchart/boxwhiskers.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 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 Commercial Charts Add-on. + ** + ** $QT_BEGIN_LICENSE$ + ** Licensees holding valid Qt Commercial licenses may use this file in + ** accordance with the Qt Commercial 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 "boxwhiskers_p.h" +#include +#include +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +BoxWhiskers::BoxWhiskers(AbstractDomain *domain, QGraphicsObject *parent) : + QGraphicsObject(parent), + m_domain(domain) +{ +} + +BoxWhiskers::~BoxWhiskers() +{ + //qDebug() << "BoxWhiskers::~BoxWhiskers()"; +} + +void BoxWhiskers::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_UNUSED(event) + + qDebug() << "BoxWhiskers::mousePressEvent"; +} + +void BoxWhiskers::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event) + + qDebug() << "BoxWhiskers::hoverEnterEvent"; +} + +void BoxWhiskers::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event) + + qDebug() << "BoxWhiskers::hoverLeaveEvent"; +} + +void BoxWhiskers::setBrush(const QBrush &brush) +{ + m_brush = brush; +} + +void BoxWhiskers::setPen(const QPen &pen) +{ + m_pen = pen; +} + +void BoxWhiskers::setLayout(const BoxWhiskersData &data) +{ + m_data = data; +// if (m_data.m_index == 1) +// qDebug() << "BoxWhiskers::setLayout"; + updateGeometry(); + //update(0.0, 0.0, m_data.m_domainSize.width(), m_data.m_domainSize.height()); + update(); +} + + +QSizeF BoxWhiskers::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + //Q_UNUSED(which) + Q_UNUSED(constraint) + + qDebug() << "BoxWhiskers::sizeHint, which = " << which; + + return QSizeF(); +} + +void BoxWhiskers::setGeometry(const QRectF &rect) // TODO: Unused? +{ + Q_UNUSED(rect) + + qDebug() << "BoxWhiskers::setGeometry"; +} + +void BoxWhiskers::setDomainSize(const QSizeF &size) +{ + m_domainSize = size; + + updateBoundingRect(); +} + +QRectF BoxWhiskers::boundingRect() const +{ + return m_boundingRect; +} + +void BoxWhiskers::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option) + Q_UNUSED(widget) + //Q_UNUSED(painter) + + //painter->save(); + //painter->setClipRect(parentItem()->boundingRect()); + painter->setPen(m_pen); + painter->setBrush(m_brush); + qreal spanY = m_data.m_maxY - m_data.m_minY; + //painter->setClipRect(parentItem()->boundingRect()); + painter->scale(m_domainSize.width() / m_data.m_boxItems, m_domainSize.height() / spanY); + painter->drawPath(m_boxPath); + //painter->restore(); +} + +void BoxWhiskers::updateGeometry() +{ + prepareGeometryChange(); + + QPainterPath path; + + // TODO: Dirty hack + if (m_data.m_seriesCount == 0) m_data.m_seriesCount = 1; + + qreal columnWidth = 1.0 / m_data.m_seriesCount; + qreal left = 0.25 * columnWidth + columnWidth * m_data.m_seriesIndex; + qreal right = 0.75 * columnWidth + columnWidth * m_data.m_seriesIndex; + qreal middle = 0.5 * columnWidth + columnWidth * m_data.m_seriesIndex; + + //whisker = 0.35 0.75 + + // Upper whisker + path.moveTo(left + m_data.m_index, m_data.m_maxY - m_data.m_upperExtreme); + path.lineTo(right + m_data.m_index, m_data.m_maxY - m_data.m_upperExtreme); + path.moveTo(middle + m_data.m_index, m_data.m_maxY - m_data.m_upperExtreme); + path.lineTo(middle + m_data.m_index, m_data.m_maxY - m_data.m_upperQuartile); + path.closeSubpath(); + + // Middle Box + path.addRect(left + m_data.m_index, m_data.m_maxY - m_data.m_upperQuartile, + 0.5 * columnWidth, m_data.m_upperQuartile - m_data.m_lowerQuartile); + + // Median/mean line + path.moveTo(left + m_data.m_index, m_data.m_maxY - m_data.m_median); + path.lineTo(right + m_data.m_index, m_data.m_maxY - m_data.m_median); + + // Lower whisker + path.moveTo(left + m_data.m_index, m_data.m_maxY - m_data.m_lowerExtreme); + path.lineTo(right + m_data.m_index, m_data.m_maxY - m_data.m_lowerExtreme); + path.moveTo(middle + m_data.m_index, m_data.m_maxY - m_data.m_lowerExtreme); + path.lineTo(middle + m_data.m_index, m_data.m_maxY - m_data.m_lowerQuartile); + path.closeSubpath(); + + m_boxPath = path; + + updateBoundingRect(); + +// qreal scaleY = m_domainSize.height() / (m_data.m_maxY - m_data.m_minY); +// qreal scaleX = m_domainSize.width() / m_data.m_boxItems; +// QRectF br = path.boundingRect(); +// m_boundingRect = QRectF( br.x() * scaleX, br.y() * scaleY, br.width() * scaleX, br.height() * scaleY); + + if (m_data.m_index == 5) { + //qDebug() << "myValue = " << myValue; + //qDebug() << "m_data.m_upperExtreme" << m_data.m_upperExtreme; + //qDebug() << "m_boundingRect = " << m_boundingRect; +// qDebug() << "x = " << m_boundingRect.x() << ", y = " << m_boundingRect.y() << ", width = " +// << m_boundingRect.width() << ", height = " << m_boundingRect.height(); + } +} + +void BoxWhiskers::updateBoundingRect() +{ + qreal scaleY = m_domainSize.height() / (m_data.m_maxY - m_data.m_minY); + qreal scaleX = m_domainSize.width() / m_data.m_boxItems; + QRectF br = m_boxPath.boundingRect(); + m_boundingRect = QRectF( br.x() * scaleX, br.y() * scaleY, br.width() * scaleX, br.height() * scaleY); +} + +#include "moc_boxwhiskers_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/boxplotchart/boxwhiskers_p.h b/src/boxplotchart/boxwhiskers_p.h new file mode 100644 index 0000000..b1fb268 --- /dev/null +++ b/src/boxplotchart/boxwhiskers_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef BOXWHISKERS_H +#define BOXWHISKERS_H + +#include "boxwhiskersdata_p.h" +#include "qchartglobal.h" +#include "qbarset.h" +#include "abstractdomain_p.h" +#include +#include +#include +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QBarSet; + +class BoxWhiskers : public QGraphicsObject/*, public QGraphicsLayoutItem*/ +{ + Q_OBJECT + //Q_INTERFACES(QGraphicsLayoutItem) +public: + BoxWhiskers(AbstractDomain *domain, QGraphicsObject *parent); + ~BoxWhiskers(); + + void setBrush(const QBrush &brush); + void setPen(const QPen &pen); + void setLayout(const BoxWhiskersData &data); + void setDomainSize(const QSizeF &size); + + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + // From QGraphicsLayoutItem // TODO tarkista n�m� jollei ole QGraphicsLayoutItem + void updateGeometry(); +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; + void setGeometry(const QRectF &rect); + +private: + void updateBoundingRect(); + +Q_SIGNALS: + void clicked(int index, QBarSet *barset); + void hovered(bool status, QBarSet *barset); + +private: + friend class BoxPlotChartItem; + friend class BoxPlotAnimation; + + AbstractDomain *m_domain; + QPainterPath m_boxPath; + QRectF m_boundingRect; + bool m_hovering; + bool m_validData; + QBrush m_brush; + QPen m_pen; + BoxWhiskersData m_data; + QSizeF m_domainSize; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // BOXWHISKERS_H diff --git a/src/boxplotchart/boxwhiskersdata_p.h b/src/boxplotchart/boxwhiskersdata_p.h new file mode 100644 index 0000000..fa5f8e1 --- /dev/null +++ b/src/boxplotchart/boxwhiskersdata_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef BOXWHISKERSDATA_P_H +#define BOXWHISKERSDATA_P_H + +#include "qchartglobal.h" +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class BoxWhiskersData +{ +public: + BoxWhiskersData() : + m_lowerExtreme(0.0), + m_lowerQuartile(0.0), + m_median(0.0), + m_upperQuartile(0.0), + m_upperExtreme(0.0), + m_index(0), + m_boxItems(0), + m_maxX(0.0), + m_minX(0.0), + m_maxY(0.0), + m_minY(0.0), + m_seriesIndex(0), + m_seriesCount(0) + { + } + + // Box related statistics + qreal m_lowerExtreme; + qreal m_lowerQuartile; + qreal m_median; + qreal m_upperQuartile; + qreal m_upperExtreme; + int m_index; + int m_boxItems; + + // Domain boundaries, axis + qreal m_maxX; + qreal m_minX; + qreal m_maxY; + qreal m_minY; + + // Serieses related data + int m_seriesIndex; + int m_seriesCount; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // BOXWHISKERSDATA_P_H diff --git a/src/boxplotchart/qboxplotseries.cpp b/src/boxplotchart/qboxplotseries.cpp new file mode 100644 index 0000000..0de3a82 --- /dev/null +++ b/src/boxplotchart/qboxplotseries.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial 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 "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 + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +/*! + \class QBoxPlotSeries + \brief Series for creating stacked bar chart + \mainclass + + QBoxPlotSeries represents a series of data shown as bars. The purpose of this class is to draw bars + as stacks, where bars in same category are stacked on top of each other. + QBoxPlotSeries groups the data from sets to categories, which are defined by QStringList. + + See the \l {BoxPlotChart Example} {stacked bar chart example} to learn how to create a stacked bar chart. + \image examples_boxplotchart.png + + \sa QBarSet, QPercentBarSeries, QAbstractBarSeries +*/ + +/*! + \qmlclass BoxPlotSeries QBoxPlotSeries + \inherits AbstractBarSeries + + The following QML shows how to create a simple stacked bar chart: + \snippet ../demos/qmlchart/qml/qmlchart/View7.qml 1 + \beginfloatleft + \image demos_qmlchart7.png + \endfloat + \clearfloat +*/ + +/*! + Constructs empty QBoxPlotSeries. + QBoxPlotSeries is QObject which is a child of a \a parent. +*/ +QBoxPlotSeries::QBoxPlotSeries(QObject *parent) + : QAbstractBarSeries(*new QBoxPlotSeriesPrivate(this), parent) +{ +} + +/*! + Destructor. Removes series from chart. +*/ +QBoxPlotSeries::~QBoxPlotSeries() +{ + qDebug() << "QBoxPlotSeries::~QBoxPlotSeries"; + + Q_D(QBoxPlotSeries); + if (d->m_chart) + d->m_chart->removeSeries(this); +} + +/*! + Returns QChartSeries::SeriesTypeBoxPlot. +*/ +QAbstractSeries::SeriesType QBoxPlotSeries::type() const +{ + return QAbstractSeries::SeriesTypeBoxPlot; +} + +void QBoxPlotSeries::setBrush(const QBrush &brush) +{ + Q_D(QBoxPlotSeries); + + if (d->m_brush != brush) { + d->m_brush = brush; + emit d->updated(); + } +} + +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(); + } +} + +QPen QBoxPlotSeries::pen() const +{ + Q_D(const QBoxPlotSeries); + + return d->m_pen; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +QBoxPlotSeriesPrivate::QBoxPlotSeriesPrivate(QBoxPlotSeries *q) + : QAbstractBarSeriesPrivate(q), + m_pen(QPen(Qt::NoPen)), + m_brush(QBrush(Qt::NoBrush)) +{ +} + +QBoxPlotSeriesPrivate::~QBoxPlotSeriesPrivate() +{ + qDebug() << "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 = categoryCount(); + minX = qMin(minX, - (qreal)0.5); + minY = qMin(minY, bottom()); + maxX = qMax(maxX, x - (qreal)0.5); + //maxY = qMax(maxY, top()); + maxY = qMax(maxY, max()); + + domain()->setRange(minX, maxX, minY, maxY); +} + +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 serieses = m_chart->series(); + boxPlot->m_seriesCount = serieses.count(); + + // Tries to find this series from the Chart's list of serieses and deduce the index + int index = 0; + foreach (QAbstractSeries *s, serieses) { + if (q == static_cast(s)) { + boxPlot->m_seriesIndex = index; + m_index = index; + } + index++; + } + } + + // Make BoxPlotChartItem to instantiate box & whisker items + boxPlot->handleDataStructureChanged(); +} + +void QBoxPlotSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced) +{ + Q_Q(QBoxPlotSeries); + + const QList gradients = theme->seriesGradients(); + + if (forced || m_brush == QBrush(Qt::NoBrush)) { + QColor brushColor = ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 0.1); + q->setBrush(brushColor); + } + + if (forced || m_pen == QPen(Qt::NoPen)) { + QPen pen; + pen.setColor(ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 1.0)); + pen.setWidthF(1.0); + pen.setCosmetic(true); + q->setPen(pen); + } +} + +void QBoxPlotSeriesPrivate::initializeAnimations(QtCommercialChart::QChart::AnimationOptions options) +{ + BoxPlotChartItem *item = static_cast(m_item.data()); + Q_ASSERT(item); + if (options.testFlag(QChart::SeriesAnimations)) { + item->setAnimation(new BoxPlotAnimation(item)); + }else{ + item->setAnimation((BoxPlotAnimation *)0); + } + QAbstractSeriesPrivate::initializeAnimations(options); +} + +QList QBoxPlotSeriesPrivate::createLegendMarkers(QLegend *legend) +{ + Q_Q(QBoxPlotSeries); + QList list; + return list << new QBoxPlotLegendMarker(q, legend); +} + +void QBoxPlotSeriesPrivate::handleSeriesRemove(QAbstractSeries *series) +{ + qDebug() << "QBoxPlotSeriesPrivate::handleSeriesRemove"; + Q_Q(QBoxPlotSeries); + + QBoxPlotSeries *removedSeries = static_cast(series); + 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(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(m_item.data()); + + if (m_chart) { + QList serieses = m_chart->series(); + boxPlot->m_seriesCount = serieses.count(); + + // Tries to find this series from the Chart's list of serieses and deduce the index + int index = 0; + foreach (QAbstractSeries *s, serieses) { + if (q == static_cast(s)) { + boxPlot->m_seriesIndex = index; + m_index = index; + } + index++; + } + } + + boxPlot->handleDataStructureChanged(); +} + +#include "moc_qboxplotseries.cpp" +#include "moc_qboxplotseries_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE + diff --git a/src/boxplotchart/qboxplotseries.h b/src/boxplotchart/qboxplotseries.h new file mode 100644 index 0000000..b273a51 --- /dev/null +++ b/src/boxplotchart/qboxplotseries.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial 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$ +** +****************************************************************************/ + +#ifndef QBOXPLOTSERIES_H +#define QBOXPLOTSERIES_H + +#include +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QBoxPlotSeriesPrivate; +//class QBarSet; + +class QTCOMMERCIALCHART_EXPORT QBoxPlotSeries : public QAbstractBarSeries +{ + Q_OBJECT +public: + explicit QBoxPlotSeries(QObject *parent = 0); + ~QBoxPlotSeries(); + + QAbstractSeries::SeriesType type() const; + + void mika(); + + void setBrush(const QBrush &brush); + QBrush brush() const; + void setPen(const QPen &pen); + QPen pen() const; + +private: + Q_DECLARE_PRIVATE(QBoxPlotSeries) + Q_DISABLE_COPY(QBoxPlotSeries) + friend class BoxPlotChartItem; + friend class QBoxPlotLegendMarkerPrivate; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // QBOXPLOTSERIES_H diff --git a/src/boxplotchart/qboxplotseries_p.h b/src/boxplotchart/qboxplotseries_p.h new file mode 100644 index 0000000..a0207ce --- /dev/null +++ b/src/boxplotchart/qboxplotseries_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 Commercial Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the QtCommercial Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef QBOXPLOTSERIES_P_H +#define QBOXPLOTSERIES_P_H + +#include "qboxplotseries.h" +#include "qabstractbarseries_p.h" +#include "abstractdomain_p.h" +#include "qbarset.h" + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class QBoxPlotSeriesPrivate : public QAbstractBarSeriesPrivate +{ + Q_OBJECT + +public: + QBoxPlotSeriesPrivate(QBoxPlotSeries *q); + ~QBoxPlotSeriesPrivate(); + + void initializeGraphics(QGraphicsItem* parent); + void initializeDomain(); + void initializeAnimations(QChart::AnimationOptions options); + void initializeTheme(int index, ChartTheme* theme, bool forced = false); + + QList createLegendMarkers(QLegend *legend); + +Q_SIGNALS: + void updated(); + +private slots: + void handleSeriesChange(QAbstractSeries *series); + void handleSeriesRemove(QAbstractSeries *series); + +protected: + QPen m_pen; + QBrush m_brush; + int m_index; + +private: + Q_DECLARE_PUBLIC(QBoxPlotSeries) +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif diff --git a/src/src.pro b/src/src.pro index bfb90ba..8f88510 100644 --- a/src/src.pro +++ b/src/src.pro @@ -75,6 +75,7 @@ include(scatterchart/scatter.pri) include(splinechart/splinechart.pri) include(themes/themes.pri) include(xychart/xychart.pri) +include(boxplotchart/boxplotchart.pri) HEADERS += $$PUBLIC_HEADERS HEADERS += $$PRIVATE_HEADERS