qbarseries.cpp
456 lines
| 12.7 KiB
| text/x-c
|
CppLexer
Jani Honkonen
|
r794 | /**************************************************************************** | ||
** | ||||
** 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$ | ||||
** | ||||
****************************************************************************/ | ||||
sauimone
|
r338 | #include "qbarseries.h" | ||
Michal Klocek
|
r938 | #include "qbarseries_p.h" | ||
sauimone
|
r172 | #include "qbarset.h" | ||
Michal Klocek
|
r938 | #include "qbarset_p.h" | ||
sauimone
|
r172 | #include "barchartmodel_p.h" | ||
Michal Klocek
|
r943 | #include "domain_p.h" | ||
Michal Klocek
|
r950 | #include "legendmarker_p.h" | ||
Michal Klocek
|
r943 | #include "chartdataset_p.h" | ||
#include "charttheme_p.h" | ||||
#include "chartanimator_p.h" | ||||
Michal Klocek
|
r938 | |||
Marek Rosa
|
r862 | #include <QAbstractItemModel> | ||
Marek Rosa
|
r877 | #include <QModelIndex> | ||
sauimone
|
r126 | |||
sauimone
|
r56 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||
sauimone
|
r313 | /*! | ||
sauimone
|
r338 | \class QBarSeries | ||
sauimone
|
r313 | \brief part of QtCommercial chart API. | ||
Tero Ahola
|
r995 | \mainclass | ||
sauimone
|
r313 | |||
Michal Klocek
|
r974 | QBarSeries represents a series of data shown as bars. One QBarSeries can contain multiple | ||
sauimone
|
r338 | QBarSet data sets. QBarSeries groups the data from sets to categories, which are defined | ||
sauimone
|
r377 | by QStringList. | ||
sauimone
|
r313 | |||
Tero Ahola
|
r995 | See the \l {BarChart Example} {bar chart example} to learn how to create a simple bar chart. | ||
\image examples_barchart.png | ||||
sauimone
|
r319 | |||
sauimone
|
r377 | \sa QBarSet, QStackedBarSeries, QPercentBarSeries | ||
sauimone
|
r313 | */ | ||
/*! | ||||
Tero Ahola
|
r973 | \fn void QBarSeries::clicked(QBarSet *barset, QString category, Qt::MouseButtons button) | ||
The signal is emitted if the user clicks with a mouse \a button on top of QBarSet \a barset of category \a category | ||||
contained by the series. | ||||
*/ | ||||
/*! | ||||
\fn void QBarSeries::selected() | ||||
The signal is emitted if the user selects/deselects the series. The logic for storing selections should be | ||||
implemented by the user of QBarSeries API. | ||||
sauimone
|
r313 | */ | ||
sauimone
|
r980 | /*! | ||
\fn void QBarSeries::hovered(QBarSet* barset, bool status) | ||||
The signal is emitted if mouse is hovered on top of series. | ||||
Parameter \a barset is the pointer of barset, where hover happened. | ||||
Parameter \a status is true, if mouse entered on top of series, false if mouse left from top of series. | ||||
*/ | ||||
sauimone
|
r313 | /*! | ||
sauimone
|
r387 | Constructs empty QBarSeries. Parameter \a categories defines the categories for chart. | ||
sauimone
|
r338 | QBarSeries is QObject which is a child of a \a parent. | ||
sauimone
|
r313 | */ | ||
Tero Ahola
|
r973 | QBarSeries::QBarSeries(QBarCategories categories, QObject *parent) : | ||
Tero Ahola
|
r988 | QAbstractSeries(*new QBarSeriesPrivate(categories, this),parent) | ||
Michal Klocek
|
r938 | { | ||
} | ||||
sauimone
|
r980 | /*! | ||
Destructs barseries and owned barsets. | ||||
*/ | ||||
QBarSeries::~QBarSeries() | ||||
{ | ||||
// NOTE: d_ptr destroyed by QObject | ||||
} | ||||
Tero Ahola
|
r973 | /*! | ||
\internal | ||||
*/ | ||||
QBarSeries::QBarSeries(QBarSeriesPrivate &d, QObject *parent) : | ||||
Tero Ahola
|
r988 | QAbstractSeries(d,parent) | ||
Michal Klocek
|
r943 | { | ||
} | ||||
sauimone
|
r980 | /*! | ||
Returns the type of series. Derived classes override this. | ||||
*/ | ||||
Tero Ahola
|
r988 | QAbstractSeries::QSeriesType QBarSeries::type() const | ||
sauimone
|
r71 | { | ||
Tero Ahola
|
r988 | return QAbstractSeries::SeriesTypeBar; | ||
sauimone
|
r71 | } | ||
sauimone
|
r313 | /*! | ||
sauimone
|
r425 | Adds a set of bars to series. Takes ownership of \a set. | ||
sauimone
|
r812 | Connects the clicked(QString, Qt::MouseButtons) signal | ||
sauimone
|
r425 | of \a set to this series | ||
sauimone
|
r313 | */ | ||
sauimone
|
r776 | void QBarSeries::appendBarSet(QBarSet *set) | ||
sauimone
|
r171 | { | ||
sauimone
|
r934 | Q_D(QBarSeries); | ||
Michal Klocek
|
r938 | d->m_internalModel->appendBarSet(set); | ||
QObject::connect(set->d_ptr.data(), SIGNAL(valueChanged()), d, SLOT(barsetChanged())); | ||||
emit d->restructuredBars(); | ||||
sauimone
|
r171 | } | ||
sauimone
|
r313 | /*! | ||
Michal Klocek
|
r974 | Removes a set of bars from series. Releases ownership of \a set. Doesn't delete \a set. | ||
sauimone
|
r812 | Disconnects the clicked(QString, Qt::MouseButtons) signal | ||
sauimone
|
r425 | of \a set from this series | ||
sauimone
|
r313 | */ | ||
sauimone
|
r338 | void QBarSeries::removeBarSet(QBarSet *set) | ||
sauimone
|
r171 | { | ||
sauimone
|
r934 | Q_D(QBarSeries); | ||
Michal Klocek
|
r938 | d->m_internalModel->removeBarSet(set); | ||
emit d->restructuredBars(); | ||||
sauimone
|
r850 | } | ||
/*! | ||||
Adds a list of barsets to series. Takes ownership of \a sets. | ||||
Connects the clicked(QString, Qt::MouseButtons) signals | ||||
of \a sets to this series | ||||
*/ | ||||
void QBarSeries::appendBarSets(QList<QBarSet* > sets) | ||||
{ | ||||
sauimone
|
r934 | Q_D(QBarSeries); | ||
sauimone
|
r850 | foreach (QBarSet* barset, sets) { | ||
Michal Klocek
|
r938 | d->m_internalModel->appendBarSet(barset); | ||
QObject::connect(barset, SIGNAL(valueChanged()), this, SLOT(barsetChanged())); | ||||
sauimone
|
r850 | } | ||
Michal Klocek
|
r938 | emit d->restructuredBars(); | ||
sauimone
|
r850 | } | ||
/*! | ||||
Michal Klocek
|
r974 | Removes a list of barsets from series. Releases ownership of \a sets. Doesn't delete \a sets. | ||
sauimone
|
r850 | Disconnects the clicked(QString, Qt::MouseButtons) signal | ||
of \a sets from this series | ||||
*/ | ||||
void QBarSeries::removeBarSets(QList<QBarSet* > sets) | ||||
{ | ||||
sauimone
|
r934 | Q_D(QBarSeries); | ||
Michal Klocek
|
r938 | |||
sauimone
|
r850 | foreach (QBarSet* barset, sets) { | ||
Michal Klocek
|
r938 | d->m_internalModel->removeBarSet(barset); | ||
sauimone
|
r850 | } | ||
Michal Klocek
|
r938 | emit d->restructuredBars(); | ||
sauimone
|
r172 | } | ||
Marek Rosa
|
r901 | /*! | ||
Inserts new \a set on the \a i position. | ||||
The barset that is currently at this postion is moved to postion i + 1 | ||||
*/ | ||||
Marek Rosa
|
r662 | void QBarSeries::insertBarSet(int i, QBarSet *set) | ||
{ | ||||
sauimone
|
r934 | Q_D(QBarSeries); | ||
Michal Klocek
|
r938 | d->m_internalModel->insertBarSet(i, set); | ||
emit d->barsetChanged(); | ||||
Marek Rosa
|
r662 | } | ||
sauimone
|
r313 | /*! | ||
Returns number of sets in series. | ||||
*/ | ||||
sauimone
|
r776 | int QBarSeries::barsetCount() const | ||
sauimone
|
r214 | { | ||
sauimone
|
r934 | Q_D(const QBarSeries); | ||
Michal Klocek
|
r938 | return d->m_internalModel->barsetCount(); | ||
sauimone
|
r214 | } | ||
sauimone
|
r323 | /*! | ||
Returns number of categories in series | ||||
*/ | ||||
sauimone
|
r776 | int QBarSeries::categoryCount() const | ||
sauimone
|
r323 | { | ||
sauimone
|
r934 | Q_D(const QBarSeries); | ||
Michal Klocek
|
r938 | return d->m_internalModel->categoryCount(); | ||
sauimone
|
r323 | } | ||
sauimone
|
r313 | /*! | ||
sauimone
|
r357 | Returns a list of sets in series. Keeps ownership of sets. | ||
*/ | ||||
sauimone
|
r776 | QList<QBarSet*> QBarSeries::barSets() const | ||
sauimone
|
r214 | { | ||
sauimone
|
r934 | Q_D(const QBarSeries); | ||
Michal Klocek
|
r938 | return d->m_internalModel->barSets(); | ||
sauimone
|
r214 | } | ||
Marek Rosa
|
r879 | /*! | ||
\fn bool QBarSeries::setModel(QAbstractItemModel *model) | ||||
Sets the \a model to be used as a data source | ||||
*/ | ||||
Tero Ahola
|
r737 | bool QBarSeries::setModel(QAbstractItemModel *model) | ||
Marek Rosa
|
r527 | { | ||
sauimone
|
r934 | Q_D(QBarSeries); | ||
return d->setModel(model); | ||||
Marek Rosa
|
r527 | } | ||
Marek Rosa
|
r900 | /*! | ||
\fn bool QBarSeries::setModelMapping(int categories, int bottomBoundry, int topBoundry, Qt::Orientation orientation) | ||||
Sets column/row specified by \a categories to be used as a list of bar series categories. | ||||
Parameter \a bottomBoundry indicates the column/row where the first bar set is located in the model. | ||||
Parameter \a topBoundry indicates the column/row where the last bar set is located in the model. | ||||
All the columns/rows inbetween those two values are also used as data for bar sets. | ||||
Michal Klocek
|
r974 | The \a orientation parameter specifies whether the data is in columns or in rows. | ||
Marek Rosa
|
r900 | */ | ||
sauimone
|
r934 | void QBarSeries::setModelMapping(int categories, int bottomBoundary, int topBoundary, Qt::Orientation orientation) | ||
Marek Rosa
|
r527 | { | ||
sauimone
|
r934 | Q_D(QBarSeries); | ||
d->setModelMapping(categories,bottomBoundary,topBoundary,orientation); | ||||
Marek Rosa
|
r527 | } | ||
Tero Ahola
|
r973 | /*! | ||
Returns the bar categories of the series. | ||||
*/ | ||||
Michal Klocek
|
r703 | QBarCategories QBarSeries::categories() const | ||
{ | ||||
sauimone
|
r934 | Q_D(const QBarSeries); | ||
Michal Klocek
|
r938 | |||
Michal Klocek
|
r703 | QBarCategories categories; | ||
Michal Klocek
|
r938 | int count = d->m_internalModel->categoryCount(); | ||
Tero Ahola
|
r737 | for (int i=1; i <= count; i++) { | ||
Marek Rosa
|
r990 | categories.insert(i, d->m_internalModel->categoryName(i - 1)); | ||
Michal Klocek
|
r703 | } | ||
return categories; | ||||
Michal Klocek
|
r938 | |||
Michal Klocek
|
r703 | } | ||
sauimone
|
r839 | /*! | ||
Sets the visibility of labels in series to \a visible | ||||
*/ | ||||
sauimone
|
r820 | void QBarSeries::setLabelsVisible(bool visible) | ||
sauimone
|
r813 | { | ||
foreach (QBarSet* s, barSets()) { | ||||
sauimone
|
r820 | s->setLabelsVisible(visible); | ||
sauimone
|
r813 | } | ||
} | ||||
Michal Klocek
|
r938 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
Tero Ahola
|
r988 | QBarSeriesPrivate::QBarSeriesPrivate(QBarCategories categories, QBarSeries *q) : | ||
QAbstractSeriesPrivate(q), | ||||
Michal Klocek
|
r938 | m_internalModel(new BarChartModel(categories,this)), | ||
m_model(0), | ||||
m_mapCategories(-1), | ||||
m_mapBarBottom(-1), | ||||
m_mapBarTop(-1), | ||||
m_mapOrientation(Qt::Vertical) | ||||
{ | ||||
} | ||||
QBarSet* QBarSeriesPrivate::barsetAt(int index) | ||||
{ | ||||
return m_internalModel->barsetAt(index); | ||||
} | ||||
QString QBarSeriesPrivate::categoryName(int category) | ||||
{ | ||||
return m_internalModel->categoryName(category); | ||||
} | ||||
qreal QBarSeriesPrivate::min() | ||||
{ | ||||
return m_internalModel->min(); | ||||
} | ||||
qreal QBarSeriesPrivate::max() | ||||
{ | ||||
return m_internalModel->max(); | ||||
} | ||||
qreal QBarSeriesPrivate::valueAt(int set, int category) | ||||
{ | ||||
return m_internalModel->valueAt(set, category); | ||||
} | ||||
qreal QBarSeriesPrivate::percentageAt(int set, int category) | ||||
{ | ||||
return m_internalModel->percentageAt(set, category); | ||||
} | ||||
qreal QBarSeriesPrivate::categorySum(int category) | ||||
{ | ||||
return m_internalModel->categorySum(category); | ||||
} | ||||
qreal QBarSeriesPrivate::absoluteCategorySum(int category) | ||||
{ | ||||
return m_internalModel->absoluteCategorySum(category); | ||||
} | ||||
qreal QBarSeriesPrivate::maxCategorySum() | ||||
{ | ||||
return m_internalModel->maxCategorySum(); | ||||
} | ||||
BarChartModel& QBarSeriesPrivate::modelInternal() | ||||
{ | ||||
return *m_internalModel; | ||||
} | ||||
bool QBarSeriesPrivate::setModel(QAbstractItemModel *model) | ||||
{ | ||||
// disconnect signals from old model | ||||
if(m_model) | ||||
{ | ||||
disconnect(m_model, 0, this, 0); | ||||
m_mapCategories = -1; | ||||
m_mapBarBottom = -1; | ||||
m_mapBarTop = -1; | ||||
m_mapOrientation = Qt::Vertical; | ||||
} | ||||
// set new model | ||||
if(model) | ||||
{ | ||||
m_model = model; | ||||
return true; | ||||
} | ||||
else | ||||
{ | ||||
m_model = 0; | ||||
return false; | ||||
} | ||||
} | ||||
void QBarSeriesPrivate::setModelMapping(int categories, int bottomBoundry, int topBoundry, Qt::Orientation orientation) | ||||
{ | ||||
Q_Q(QBarSeries); | ||||
Marek Rosa
|
r947 | if (m_model == 0) | ||
Michal Klocek
|
r938 | return; | ||
m_mapCategories = categories; | ||||
m_mapBarBottom = bottomBoundry; | ||||
m_mapBarTop = topBoundry; | ||||
m_mapOrientation = orientation; | ||||
// connect the signals | ||||
Marek Rosa
|
r990 | connect(m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), | ||
this, SLOT(modelUpdated(QModelIndex,QModelIndex))); | ||||
Michal Klocek
|
r938 | |||
// create the initial bars | ||||
delete m_internalModel; | ||||
if (m_mapOrientation == Qt::Vertical) { | ||||
QStringList categories; | ||||
Marek Rosa
|
r990 | for (int k = 0; k < m_model->rowCount(); k++) | ||
Michal Klocek
|
r938 | categories << m_model->data(m_model->index(k, m_mapCategories), Qt::DisplayRole).toString(); | ||
m_internalModel = new BarChartModel(categories, this); | ||||
for (int i = m_mapBarBottom; i <= m_mapBarTop; i++) { | ||||
QBarSet* barSet = new QBarSet(QString("Column: %1").arg(i + 1)); | ||||
Marek Rosa
|
r990 | for(int m = 0; m < m_model->rowCount(); m++) | ||
Michal Klocek
|
r938 | *barSet << m_model->data(m_model->index(m, i), Qt::DisplayRole).toDouble(); | ||
q->appendBarSet(barSet); | ||||
} | ||||
} else { | ||||
QStringList categories; | ||||
Marek Rosa
|
r990 | for (int k = 0; k < m_model->columnCount(); k++) | ||
Michal Klocek
|
r938 | categories << m_model->data(m_model->index(m_mapCategories, k), Qt::DisplayRole).toString(); | ||
m_internalModel = new BarChartModel(categories, this); | ||||
for (int i = m_mapBarBottom; i <= m_mapBarTop; i++) { | ||||
QBarSet* barSet = new QBarSet(QString("Row: %1").arg(i + 1)); | ||||
Marek Rosa
|
r990 | for(int m = 0; m < m_model->columnCount(); m++) | ||
Michal Klocek
|
r938 | *barSet << m_model->data(m_model->index(i, m), Qt::DisplayRole).toDouble(); | ||
q->appendBarSet(barSet); | ||||
} | ||||
} | ||||
} | ||||
void QBarSeriesPrivate::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight) | ||||
{ | ||||
Q_UNUSED(bottomRight) | ||||
if (m_mapOrientation == Qt::Vertical) | ||||
{ | ||||
// model update is relevant to BarSeries if the change was made to the part of the model that was mapped to BarSeries | ||||
Marek Rosa
|
r990 | if (topLeft.column() >= m_mapBarBottom && topLeft.column() <= m_mapBarTop) | ||
sauimone
|
r993 | barsetAt(topLeft.column() - m_mapBarBottom)->replace(topLeft.row(), m_model->data(topLeft, Qt::DisplayRole).toDouble()); | ||
Michal Klocek
|
r938 | } | ||
else | ||||
{ | ||||
// model update is relevant to BarSeries if the change was made to the part of the model that was mapped to BarSeries | ||||
Marek Rosa
|
r990 | if (topLeft.row() >= m_mapBarBottom && topLeft.row() <= m_mapBarTop) | ||
sauimone
|
r993 | barsetAt(topLeft.row() - m_mapBarBottom)->replace(topLeft.column(), m_model->data(topLeft, Qt::DisplayRole).toDouble()); | ||
Michal Klocek
|
r938 | } | ||
} | ||||
void QBarSeriesPrivate::barsetChanged() | ||||
{ | ||||
emit updatedBars(); | ||||
} | ||||
sauimone
|
r813 | |||
Michal Klocek
|
r943 | void QBarSeriesPrivate::scaleDomain(Domain& domain) | ||
{ | ||||
qreal minX(domain.minX()); | ||||
qreal minY(domain.minY()); | ||||
qreal maxX(domain.maxX()); | ||||
qreal maxY(domain.maxY()); | ||||
int tickXCount(domain.tickXCount()); | ||||
int tickYCount(domain.tickYCount()); | ||||
sauimone
|
r962 | qreal x = m_internalModel->categoryCount(); | ||
qreal y = max(); | ||||
Michal Klocek
|
r943 | minX = qMin(minX, x); | ||
minY = qMin(minY, y); | ||||
maxX = qMax(maxX, x); | ||||
maxY = qMax(maxY, y); | ||||
tickXCount = x+1; | ||||
domain.setRangeX(minX,maxX,tickXCount); | ||||
domain.setRangeY(minY,maxY,tickYCount); | ||||
} | ||||
Chart* QBarSeriesPrivate::createGraphics(ChartPresenter* presenter) | ||||
{ | ||||
Q_Q(QBarSeries); | ||||
BarChartItem* bar = new BarChartItem(q,presenter); | ||||
if(presenter->animationOptions().testFlag(QChart::SeriesAnimations)) { | ||||
presenter->animator()->addAnimation(bar); | ||||
} | ||||
presenter->chartTheme()->decorate(q, presenter->dataSet()->seriesIndex(q)); | ||||
return bar; | ||||
} | ||||
Michal Klocek
|
r950 | QList<LegendMarker*> QBarSeriesPrivate::createLegendMarker(QLegend* legend) | ||
{ | ||||
Q_Q(QBarSeries); | ||||
QList<LegendMarker*> markers; | ||||
foreach(QBarSet* set, q->barSets()) { | ||||
BarLegendMarker* marker = new BarLegendMarker(q,set,legend); | ||||
markers << marker; | ||||
} | ||||
return markers; | ||||
} | ||||
sauimone
|
r338 | #include "moc_qbarseries.cpp" | ||
Michal Klocek
|
r938 | #include "moc_qbarseries_p.cpp" | ||
sauimone
|
r71 | |||
sauimone
|
r56 | QTCOMMERCIALCHART_END_NAMESPACE | ||