qpieseries.cpp
572 lines
| 14.7 KiB
| text/x-c
|
CppLexer
|
r142 | #include "qpieseries.h" | ||
|
r203 | #include "qpieslice.h" | ||
|
r142 | #include <QDebug> | ||
QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||||
|
r320 | /*! | ||
\class QPieSeries | ||||
\brief Pie series API for QtCommercial Charts | ||||
The pie series defines a pie chart which consists of pie slices which are QPieSlice objects. | ||||
The slices can have any values as the QPieSeries will calculate its relative value to the sum of all slices. | ||||
|
r386 | The actual slice size is determined by that relative value. | ||
|
r320 | |||
|
r386 | By default the pie is defined as a full pie but it can be a partial pie. | ||
|
r320 | This can be done by setting a starting angle and angle span to the series. | ||
*/ | ||||
|
r314 | /*! | ||
Constructs a series object which is a child of \a parent. | ||||
*/ | ||||
|
r203 | QPieSeries::QPieSeries(QObject *parent) : | ||
|
r360 | QSeries(parent), | ||
|
r498 | m_pieRelativeHorPos(0.5), | ||
m_pieRelativeVerPos(0.5), | ||||
m_pieRelativeSize(0.7), | ||||
|
r203 | m_pieStartAngle(0), | ||
|
r454 | m_pieEndAngle(360), | ||
m_total(0) | ||||
|
r203 | { | ||
|
r157 | |||
|
r203 | } | ||
|
r174 | |||
|
r314 | /*! | ||
Destroys the object. Note that adding series to QChart transfers the ownership to the chart. | ||||
*/ | ||||
|
r203 | QPieSeries::~QPieSeries() | ||
{ | ||||
|
r204 | |||
|
r203 | } | ||
|
r314 | /*! | ||
|
r320 | Returns QChartSeries::SeriesTypePie. | ||
|
r314 | */ | ||
|
r360 | QSeries::QSeriesType QPieSeries::type() const | ||
|
r314 | { | ||
|
r360 | return QSeries::SeriesTypePie; | ||
|
r314 | } | ||
/*! | ||||
|
r354 | Sets an array of \a slices to the series replacing the existing slices. | ||
|
r314 | Slice ownership is passed to the series. | ||
*/ | ||||
|
r354 | void QPieSeries::replace(QList<QPieSlice*> slices) | ||
|
r142 | { | ||
|
r203 | clear(); | ||
add(slices); | ||||
} | ||||
|
r163 | |||
|
r314 | /*! | ||
|
r345 | Adds an array of \a slices to the series. | ||
|
r314 | Slice ownership is passed to the series. | ||
*/ | ||||
|
r203 | void QPieSeries::add(QList<QPieSlice*> slices) | ||
{ | ||||
foreach (QPieSlice* s, slices) { | ||||
s->setParent(this); | ||||
m_slices << s; | ||||
|
r174 | } | ||
|
r157 | |||
|
r174 | updateDerivativeData(); | ||
|
r203 | foreach (QPieSlice* s, slices) { | ||
connect(s, SIGNAL(changed()), this, SLOT(sliceChanged())); | ||||
connect(s, SIGNAL(clicked()), this, SLOT(sliceClicked())); | ||||
connect(s, SIGNAL(hoverEnter()), this, SLOT(sliceHoverEnter())); | ||||
connect(s, SIGNAL(hoverLeave()), this, SLOT(sliceHoverLeave())); | ||||
} | ||||
|
r621 | emit added(slices); | ||
|
r142 | } | ||
|
r314 | /*! | ||
Adds a single \a slice to the series. | ||||
Slice ownership is passed to the series. | ||||
*/ | ||||
|
r203 | void QPieSeries::add(QPieSlice* slice) | ||
|
r142 | { | ||
|
r203 | add(QList<QPieSlice*>() << slice); | ||
|
r142 | } | ||
|
r415 | /*! | ||
Adds a single \a slice to the series and returns a reference to the series. | ||||
Slice ownership is passed to the series. | ||||
*/ | ||||
|
r406 | QPieSeries& QPieSeries::operator << (QPieSlice* slice) | ||
{ | ||||
add(slice); | ||||
return *this; | ||||
} | ||||
|
r314 | |||
/*! | ||||
Adds a single slice to the series with give \a value and \a name. | ||||
Slice ownership is passed to the series. | ||||
*/ | ||||
|
r203 | QPieSlice* QPieSeries::add(qreal value, QString name) | ||
|
r142 | { | ||
|
r203 | QPieSlice* slice = new QPieSlice(value, name); | ||
add(slice); | ||||
return slice; | ||||
} | ||||
|
r174 | |||
|
r604 | void QPieSeries::insert(int i, QPieSlice* slice) | ||
{ | ||||
Q_ASSERT(i <= m_slices.count()); | ||||
slice->setParent(this); | ||||
m_slices.insert(i, slice); | ||||
updateDerivativeData(); | ||||
connect(slice, SIGNAL(changed()), this, SLOT(sliceChanged())); | ||||
connect(slice, SIGNAL(clicked()), this, SLOT(sliceClicked())); | ||||
connect(slice, SIGNAL(hoverEnter()), this, SLOT(sliceHoverEnter())); | ||||
connect(slice, SIGNAL(hoverLeave()), this, SLOT(sliceHoverLeave())); | ||||
|
r621 | emit added(QList<QPieSlice*>() << slice); | ||
|
r604 | } | ||
|
r314 | /*! | ||
Removes a single \a slice from the series and deletes the slice. | ||||
|
r320 | |||
|
r314 | Do not reference this pointer after this call. | ||
*/ | ||||
|
r203 | void QPieSeries::remove(QPieSlice* slice) | ||
{ | ||||
if (!m_slices.removeOne(slice)) { | ||||
|
r289 | Q_ASSERT(0); // TODO: how should this be reported? | ||
|
r203 | return; | ||
} | ||||
|
r174 | |||
|
r566 | updateDerivativeData(); | ||
|
r174 | |||
|
r621 | emit removed(QList<QPieSlice*>() << slice); | ||
|
r203 | delete slice; | ||
slice = NULL; | ||||
|
r142 | } | ||
|
r314 | /*! | ||
Clears all slices from the series. | ||||
*/ | ||||
|
r203 | void QPieSeries::clear() | ||
|
r142 | { | ||
|
r203 | if (m_slices.count() == 0) | ||
return; | ||||
|
r174 | |||
|
r621 | QList<QPieSlice*> slices = m_slices; | ||
|
r203 | foreach (QPieSlice* s, m_slices) { | ||
m_slices.removeOne(s); | ||||
delete s; | ||||
} | ||||
|
r566 | |||
|
r203 | updateDerivativeData(); | ||
|
r621 | |||
emit removed(slices); | ||||
|
r142 | } | ||
|
r314 | /*! | ||
Counts the number of the slices in this series. | ||||
*/ | ||||
int QPieSeries::count() const | ||||
{ | ||||
return m_slices.count(); | ||||
} | ||||
|
r621 | /*! | ||
Returns true is the series is empty. | ||||
*/ | ||||
bool QPieSeries::isEmpty() const | ||||
{ | ||||
return m_slices.isEmpty(); | ||||
} | ||||
|
r314 | /*! | ||
Returns a list of slices that belong to this series. | ||||
*/ | ||||
QList<QPieSlice*> QPieSeries::slices() const | ||||
{ | ||||
return m_slices; | ||||
} | ||||
/*! | ||||
|
r498 | Sets the center position of the pie by \a relativeHorizontalPosition and \a relativeVerticalPosition. | ||
|
r454 | |||
The factors are relative to the chart rectangle where: | ||||
|
r498 | \a relativeHorizontalPosition 0.0 means the absolute left. | ||
\a relativeHorizontalPosition 1.0 means the absolute right. | ||||
\a relativeVerticalPosition 0.0 means the absolute top. | ||||
\a relativeVerticalPosition 1.0 means the absolute bottom. | ||||
|
r454 | |||
|
r498 | By default both values are 0.5 which puts the pie in the middle of the chart rectangle. | ||
|
r454 | |||
|
r498 | \sa pieHorizontalPosition(), pieVerticalPosition(), setPieSize() | ||
|
r314 | */ | ||
|
r498 | void QPieSeries::setPiePosition(qreal relativeHorizontalPosition, qreal relativeVerticalPosition) | ||
|
r142 | { | ||
|
r498 | if (relativeHorizontalPosition < 0.0 || relativeHorizontalPosition > 1.0 || | ||
|
r597 | relativeVerticalPosition < 0.0 || relativeVerticalPosition > 1.0) | ||
|
r163 | return; | ||
|
r498 | if (m_pieRelativeHorPos != relativeHorizontalPosition || m_pieRelativeVerPos != relativeVerticalPosition) { | ||
m_pieRelativeHorPos = relativeHorizontalPosition; | ||||
m_pieRelativeVerPos = relativeVerticalPosition; | ||||
|
r621 | emit piePositionChanged(); | ||
|
r163 | } | ||
|
r142 | } | ||
|
r314 | /*! | ||
|
r498 | Gets the horizontal position of the pie. | ||
|
r314 | |||
|
r498 | The returned value is relative to the chart rectangle where: | ||
|
r454 | |||
|
r498 | 0.0 means the absolute left. | ||
1.0 means the absolute right. | ||||
|
r454 | |||
|
r498 | By default it is 0.5 which puts the pie in the horizontal middle of the chart rectangle. | ||
|
r454 | |||
|
r498 | \sa setPiePosition(), pieVerticalPosition(), setPieSize() | ||
|
r314 | */ | ||
|
r498 | qreal QPieSeries::pieHorizontalPosition() const | ||
|
r142 | { | ||
|
r498 | return m_pieRelativeHorPos; | ||
|
r142 | } | ||
|
r314 | /*! | ||
|
r498 | Gets the vertical position position of the pie. | ||
|
r454 | |||
|
r498 | The returned value is relative to the chart rectangle where: | ||
|
r454 | |||
|
r498 | 0.0 means the absolute top. | ||
1.0 means the absolute bottom. | ||||
|
r454 | |||
|
r498 | By default it is 0.5 which puts the pie in the vertical middle of the chart rectangle. | ||
|
r454 | |||
|
r498 | \sa setPiePosition(), pieHorizontalPosition(), setPieSize() | ||
|
r314 | */ | ||
|
r498 | qreal QPieSeries::pieVerticalPosition() const | ||
|
r314 | { | ||
|
r498 | return m_pieRelativeVerPos; | ||
|
r314 | } | ||
/*! | ||||
|
r498 | Sets the relative size of the pie. | ||
|
r454 | |||
|
r498 | The \a relativeSize is defined so that the 1.0 is the maximum that can fit the given chart rectangle. | ||
|
r454 | |||
Default value is 0.7. | ||||
|
r498 | \sa pieSize(), setPiePosition(), pieVerticalPosition(), pieHorizontalPosition() | ||
|
r437 | */ | ||
|
r498 | void QPieSeries::setPieSize(qreal relativeSize) | ||
|
r437 | { | ||
|
r498 | if (relativeSize < 0.0 || relativeSize > 1.0) | ||
|
r454 | return; | ||
|
r498 | if (m_pieRelativeSize != relativeSize) { | ||
m_pieRelativeSize = relativeSize; | ||||
|
r621 | emit pieSizeChanged(); | ||
|
r437 | } | ||
} | ||||
|
r314 | |||
|
r437 | /*! | ||
|
r498 | Gets the relative size of the pie. | ||
|
r454 | |||
|
r498 | The size is defined so that the 1.0 is the maximum that can fit the given chart rectangle. | ||
|
r454 | |||
Default value is 0.7. | ||||
|
r498 | \sa setPieSize(), setPiePosition(), pieVerticalPosition(), pieHorizontalPosition() | ||
|
r314 | */ | ||
|
r498 | qreal QPieSeries::pieSize() const | ||
|
r203 | { | ||
|
r498 | return m_pieRelativeSize; | ||
|
r437 | } | ||
|
r454 | /*! | ||
Sets the end angle of the pie. | ||||
Full pie is 360 degrees where 0 degrees is at 12 a'clock. | ||||
|
r498 | \a angle must be less than pie end angle. Default value is 0. | ||
|
r454 | |||
|
r498 | \sa pieStartAngle(), pieEndAngle(), setPieEndAngle() | ||
|
r454 | */ | ||
|
r498 | void QPieSeries::setPieStartAngle(qreal angle) | ||
|
r437 | { | ||
|
r498 | if (angle >= 0 && angle <= 360 && angle != m_pieStartAngle && angle <= m_pieEndAngle) { | ||
m_pieStartAngle = angle; | ||||
|
r203 | updateDerivativeData(); | ||
} | ||||
} | ||||
|
r454 | /*! | ||
Gets the start angle of the pie. | ||||
Full pie is 360 degrees where 0 degrees is at 12 a'clock. Default value is 360. | ||||
|
r498 | \sa setPieStartAngle(), pieEndAngle(), setPieEndAngle() | ||
|
r454 | */ | ||
|
r498 | qreal QPieSeries::pieStartAngle() const | ||
|
r437 | { | ||
return m_pieStartAngle; | ||||
} | ||||
|
r454 | /*! | ||
Sets the end angle of the pie. | ||||
Full pie is 360 degrees where 0 degrees is at 12 a'clock. | ||||
|
r498 | \a angle must be greater than start angle. | ||
|
r454 | |||
|
r498 | \sa pieEndAngle(), pieStartAngle(), setPieStartAngle() | ||
|
r454 | */ | ||
|
r498 | void QPieSeries::setPieEndAngle(qreal angle) | ||
|
r437 | { | ||
|
r498 | if (angle >= 0 && angle <= 360 && angle != m_pieEndAngle && angle >= m_pieStartAngle) { | ||
m_pieEndAngle = angle; | ||||
|
r437 | updateDerivativeData(); | ||
} | ||||
} | ||||
|
r454 | /*! | ||
Returns the end angle of the pie. | ||||
Full pie is 360 degrees where 0 degrees is at 12 a'clock. | ||||
|
r498 | \sa setPieEndAngle(), pieStartAngle(), setPieStartAngle() | ||
|
r454 | */ | ||
|
r498 | qreal QPieSeries::pieEndAngle() const | ||
|
r437 | { | ||
return m_pieEndAngle; | ||||
} | ||||
|
r314 | /*! | ||
Sets the all the slice labels \a visible or invisible. | ||||
\sa QPieSlice::isLabelVisible(), QPieSlice::setLabelVisible() | ||||
*/ | ||||
|
r203 | void QPieSeries::setLabelsVisible(bool visible) | ||
{ | ||||
foreach (QPieSlice* s, m_slices) | ||||
s->setLabelVisible(visible); | ||||
} | ||||
|
r406 | /*! | ||
Returns the sum of all slice values in this series. | ||||
\sa QPieSlice::value(), QPieSlice::setValue() | ||||
*/ | ||||
qreal QPieSeries::total() const | ||||
{ | ||||
return m_total; | ||||
} | ||||
|
r314 | /*! | ||
|
r566 | \fn void QPieSeries::changed() | ||
|
r314 | |||
This signal emitted when something has changed in the series. | ||||
\sa QPieSeries::ChangeSet, QPieSlice::changed() | ||||
*/ | ||||
/*! | ||||
\fn void QPieSeries::clicked(QPieSlice* slice) | ||||
This signal is emitted when a \a slice has been clicked. | ||||
\sa QPieSlice::clicked() | ||||
*/ | ||||
/*! | ||||
\fn void QPieSeries::hoverEnter(QPieSlice* slice) | ||||
This signal is emitted when user has hovered over a \a slice. | ||||
\sa QPieSlice::hoverEnter() | ||||
*/ | ||||
/*! | ||||
\fn void QPieSeries::hoverLeave(QPieSlice* slice) | ||||
This signal is emitted when user has hovered away from a \a slice. | ||||
\sa QPieSlice::hoverLeave() | ||||
*/ | ||||
|
r203 | void QPieSeries::sliceChanged() | ||
{ | ||||
QPieSlice* slice = qobject_cast<QPieSlice *>(sender()); | ||||
Q_ASSERT(m_slices.contains(slice)); | ||||
updateDerivativeData(); | ||||
} | ||||
void QPieSeries::sliceClicked() | ||||
{ | ||||
QPieSlice* slice = qobject_cast<QPieSlice *>(sender()); | ||||
Q_ASSERT(m_slices.contains(slice)); | ||||
emit clicked(slice); | ||||
} | ||||
void QPieSeries::sliceHoverEnter() | ||||
{ | ||||
QPieSlice* slice = qobject_cast<QPieSlice *>(sender()); | ||||
Q_ASSERT(m_slices.contains(slice)); | ||||
emit hoverEnter(slice); | ||||
} | ||||
void QPieSeries::sliceHoverLeave() | ||||
|
r174 | { | ||
|
r203 | QPieSlice* slice = qobject_cast<QPieSlice *>(sender()); | ||
Q_ASSERT(m_slices.contains(slice)); | ||||
emit hoverLeave(slice); | ||||
} | ||||
|
r174 | void QPieSeries::updateDerivativeData() | ||
{ | ||||
m_total = 0; | ||||
|
r203 | // nothing to do? | ||
if (m_slices.count() == 0) | ||||
return; | ||||
// calculate total | ||||
foreach (QPieSlice* s, m_slices) | ||||
m_total += s->value(); | ||||
|
r621 | // nothing to show.. | ||
if (m_total == 0) | ||||
return; | ||||
|
r174 | |||
|
r203 | // update slice attributes | ||
qreal sliceAngle = m_pieStartAngle; | ||||
|
r437 | qreal pieSpan = m_pieEndAngle - m_pieStartAngle; | ||
|
r618 | QVector<QPieSlice*> changed; | ||
|
r203 | foreach (QPieSlice* s, m_slices) { | ||
|
r618 | bool isChanged = false; | ||
|
r203 | |||
qreal percentage = s->value() / m_total; | ||||
if (s->m_percentage != percentage) { | ||||
s->m_percentage = percentage; | ||||
|
r618 | isChanged = true; | ||
|
r203 | } | ||
|
r437 | qreal sliceSpan = pieSpan * percentage; | ||
|
r289 | if (s->m_angleSpan != sliceSpan) { | ||
s->m_angleSpan = sliceSpan; | ||||
|
r618 | isChanged = true; | ||
|
r203 | } | ||
|
r355 | if (s->m_startAngle != sliceAngle) { | ||
s->m_startAngle = sliceAngle; | ||||
|
r618 | isChanged = true; | ||
|
r203 | } | ||
sliceAngle += sliceSpan; | ||||
|
r618 | if (isChanged) | ||
changed << s; | ||||
|
r203 | } | ||
|
r618 | |||
|
r621 | // emit signals | ||
|
r618 | foreach (QPieSlice* s, changed) | ||
emit s->changed(); | ||||
|
r174 | } | ||
|
r597 | bool QPieSeries::setModel(QAbstractItemModel* model) | ||
{ | ||||
// disconnect signals from old model | ||||
if(m_model) | ||||
{ | ||||
disconnect(m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), 0, 0); | ||||
disconnect(m_model,SIGNAL(rowsInserted(QModelIndex, int, int)), 0, 0); | ||||
disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)), 0, 0); | ||||
} | ||||
// set new model if not NULL and connect necessary signals from it | ||||
if(model) | ||||
{ | ||||
m_model = model; | ||||
connect(m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(modelUpdated(QModelIndex, QModelIndex))); | ||||
connect(m_model,SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(modelDataAdded(QModelIndex,int,int))); | ||||
connect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(modelDataRemoved(QModelIndex,int,int))); | ||||
} | ||||
|
r605 | |||
return true; | ||||
|
r597 | } | ||
void QPieSeries::setModelMapping(int modelValuesLine, int modelLabelsLine, Qt::Orientation orientation) | ||||
{ | ||||
m_mapValues = modelValuesLine; | ||||
m_mapLabels = modelLabelsLine; | ||||
m_mapOrientation = orientation; | ||||
if (m_model == NULL) | ||||
return; | ||||
if (m_mapOrientation == Qt::Vertical) | ||||
for (int i = 0; i < m_model->rowCount(); i++) | ||||
add(m_model->data(m_model->index(i, m_mapValues), Qt::DisplayRole).toDouble(), m_model->data(m_model->index(i, m_mapLabels), Qt::DisplayRole).toString()); | ||||
else | ||||
for (int i = 0; i < m_model->columnCount(); i++) | ||||
add(m_model->data(m_model->index(m_mapValues, i), Qt::DisplayRole).toDouble(), m_model->data(m_model->index(m_mapLabels, i), Qt::DisplayRole).toString()); | ||||
} | ||||
void QPieSeries::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight) | ||||
{ | ||||
|
r611 | Q_UNUSED(bottomRight) | ||
|
r597 | if (m_mapOrientation == Qt::Vertical) | ||
|
r604 | { | ||
|
r597 | // slices().at(topLeft.row())->setValue(m_model->data(m_model->index(topLeft.row(), topLeft.column()), Qt::DisplayRole).toDouble()); | ||
if (topLeft.column() == m_mapValues) | ||||
slices().at(topLeft.row())->setValue(m_model->data(topLeft, Qt::DisplayRole).toDouble()); | ||||
else if (topLeft.column() == m_mapLabels) | ||||
slices().at(topLeft.row())->setLabel(m_model->data(topLeft, Qt::DisplayRole).toString()); | ||||
|
r604 | } | ||
else | ||||
{ | ||||
// slices().at(topLeft.column())->setValue(m_model->data(m_model->index(topLeft.row(), topLeft.column()), Qt::DisplayRole).toDouble()); | ||||
if (topLeft.column() == m_mapValues) | ||||
slices().at(topLeft.column())->setValue(m_model->data(topLeft, Qt::DisplayRole).toDouble()); | ||||
else if (topLeft.column() == m_mapLabels) | ||||
slices().at(topLeft.column())->setLabel(m_model->data(topLeft, Qt::DisplayRole).toString()); | ||||
} | ||||
|
r597 | } | ||
void QPieSeries::modelDataAdded(QModelIndex parent, int start, int end) | ||||
{ | ||||
|
r611 | Q_UNUSED(parent) | ||
Q_UNUSED(end) | ||||
|
r604 | QPieSlice* newSlice = new QPieSlice; | ||
newSlice->setLabelVisible(true); | ||||
if (m_mapOrientation == Qt::Vertical) | ||||
{ | ||||
newSlice->setValue(m_model->data(m_model->index(start, m_mapValues), Qt::DisplayRole).toDouble()); | ||||
newSlice->setLabel(m_model->data(m_model->index(start, m_mapLabels), Qt::DisplayRole).toString()); | ||||
} | ||||
else | ||||
{ | ||||
newSlice->setValue(m_model->data(m_model->index(m_mapValues, start), Qt::DisplayRole).toDouble()); | ||||
newSlice->setLabel(m_model->data(m_model->index(m_mapLabels, start), Qt::DisplayRole).toString()); | ||||
} | ||||
insert(start, newSlice); | ||||
|
r597 | } | ||
void QPieSeries::modelDataRemoved(QModelIndex parent, int start, int end) | ||||
{ | ||||
|
r611 | Q_UNUSED(parent) | ||
Q_UNUSED(end) | ||||
|
r597 | remove(slices().at(start)); | ||
} | ||||
|
r142 | #include "moc_qpieseries.cpp" | ||
QTCOMMERCIALCHART_END_NAMESPACE | ||||