qpieseries.cpp
538 lines
| 12.0 KiB
| text/x-c
|
CppLexer
|
r142 | #include "qpieseries.h" | ||
|
r203 | #include "qpieslice.h" | ||
|
r146 | #include "piepresenter.h" | ||
|
r142 | #include "pieslice.h" | ||
|
r289 | #include <QFontMetrics> | ||
|
r142 | #include <QDebug> | ||
QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||||
|
r320 | |||
|
r314 | /*! | ||
|
r320 | \class QPieSeries::ChangeSet | ||
\brief Defines the changes in the series. | ||||
|
r314 | |||
|
r320 | Contains the changes that have occurred in the series. Lists of added, changed and removed slices. | ||
|
r314 | |||
|
r320 | \sa QPieSeries::changed() | ||
|
r314 | */ | ||
/*! | ||||
|
r320 | \internal | ||
|
r314 | */ | ||
|
r203 | void QPieSeries::ChangeSet::appendAdded(QPieSlice* slice) | ||
|
r142 | { | ||
|
r203 | if (!m_added.contains(slice)) | ||
m_added << slice; | ||||
|
r142 | } | ||
|
r320 | /*! | ||
\internal | ||||
*/ | ||||
|
r289 | void QPieSeries::ChangeSet::appendAdded(QList<QPieSlice*> slices) | ||
{ | ||||
foreach (QPieSlice* s, slices) | ||||
appendAdded(s); | ||||
} | ||||
|
r320 | /*! | ||
\internal | ||||
*/ | ||||
|
r203 | void QPieSeries::ChangeSet::appendChanged(QPieSlice* slice) | ||
|
r142 | { | ||
|
r203 | if (!m_changed.contains(slice)) | ||
m_changed << slice; | ||||
} | ||||
|
r142 | |||
|
r320 | /*! | ||
\internal | ||||
*/ | ||||
|
r203 | void QPieSeries::ChangeSet::appendRemoved(QPieSlice* slice) | ||
{ | ||||
if (!m_removed.contains(slice)) | ||||
m_removed << slice; | ||||
|
r142 | } | ||
|
r314 | /*! | ||
Returns a list of slices that have been added to the series. | ||||
\sa QPieSeries::changed() | ||||
*/ | ||||
|
r203 | QList<QPieSlice*> QPieSeries::ChangeSet::added() const | ||
|
r142 | { | ||
|
r203 | return m_added; | ||
|
r163 | } | ||
|
r314 | /*! | ||
Returns a list of slices that have been changed in the series. | ||||
\sa QPieSeries::changed() | ||||
*/ | ||||
|
r203 | QList<QPieSlice*> QPieSeries::ChangeSet::changed() const | ||
|
r163 | { | ||
|
r203 | return m_changed; | ||
} | ||||
|
r163 | |||
|
r314 | /*! | ||
Returns a list of slices that have been removed from the series. | ||||
\sa QPieSeries::changed() | ||||
*/ | ||||
|
r203 | QList<QPieSlice*> QPieSeries::ChangeSet::removed() const | ||
{ | ||||
return m_removed; | ||||
} | ||||
|
r157 | |||
|
r314 | |||
/*! | ||||
Returns true if there are no added/changed or removed slices in the change set. | ||||
*/ | ||||
|
r203 | bool QPieSeries::ChangeSet::isEmpty() const | ||
{ | ||||
if (m_added.count() || m_changed.count() || m_removed.count()) | ||||
return false; | ||||
return true; | ||||
} | ||||
|
r157 | |||
|
r320 | /*! | ||
\enum QPieSeries::PiePosition | ||||
This enum describes pie position within its bounding rectangle | ||||
\value PiePositionMaximized | ||||
\value PiePositionTopLeft | ||||
\value PiePositionTopRight | ||||
\value PiePositionBottomLeft | ||||
\value PiePositionBottomRight | ||||
*/ | ||||
/*! | ||||
\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. | ||||
The actual slice size (span) is determined by that relative value. | ||||
By default the pie is defined as full but it can be a partial pie. | ||||
This can be done by setting a starting angle and angle span to the series. | ||||
|
r341 | |||
Example on how to create a chart with pie series: | ||||
\snippet ../example/piechart/main.cpp 1 | ||||
|
r345 | |||
To help with the most common user intercation scenarions there some convenience functions. Specifically | ||||
exploding and higlighting: | ||||
\snippet ../example/piechart/main.cpp 2 | ||||
|
r320 | */ | ||
|
r314 | /*! | ||
Constructs a series object which is a child of \a parent. | ||||
*/ | ||||
|
r203 | QPieSeries::QPieSeries(QObject *parent) : | ||
QChartSeries(parent), | ||||
m_sizeFactor(1.0), | ||||
m_position(PiePositionMaximized), | ||||
m_pieStartAngle(0), | ||||
|
r314 | m_pieAngleSpan(360) | ||
|
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 | */ | ||
QChartSeries::QChartSeriesType QPieSeries::type() const | ||||
{ | ||||
return QChartSeries::SeriesTypePie; | ||||
} | ||||
/*! | ||||
Sets an array of values to the series. | ||||
TO BE REMOVED | ||||
*/ | ||||
|
r203 | bool QPieSeries::setData(QList<qreal> data) | ||
{ | ||||
// TODO: remove this function | ||||
QList<QPieSlice*> slices; | ||||
|
r278 | foreach (qreal value, data) | ||
|
r203 | slices << new QPieSlice(value, QString::number(value)); | ||
set(slices); | ||||
|
r163 | return true; | ||
|
r142 | } | ||
|
r314 | /*! | ||
Sets an array of \a slices to the series. | ||||
Slice ownership is passed to the series. | ||||
*/ | ||||
|
r203 | void QPieSeries::set(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) | ||
{ | ||||
|
r174 | ChangeSet changeSet; | ||
|
r203 | foreach (QPieSlice* s, slices) { | ||
s->setParent(this); | ||||
m_slices << s; | ||||
changeSet.appendAdded(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())); | ||||
} | ||||
emit changed(changeSet); | ||||
|
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 | } | ||
|
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 | |||
|
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 | |||
ChangeSet changeSet; | ||||
|
r203 | changeSet.appendRemoved(slice); | ||
|
r174 | emit changed(changeSet); | ||
|
r203 | delete slice; | ||
slice = NULL; | ||||
updateDerivativeData(); | ||||
|
r142 | } | ||
|
r314 | /*! | ||
Clears all slices from the series. | ||||
*/ | ||||
|
r203 | void QPieSeries::clear() | ||
|
r142 | { | ||
|
r203 | if (m_slices.count() == 0) | ||
return; | ||||
|
r174 | |||
ChangeSet changeSet; | ||||
|
r203 | foreach (QPieSlice* s, m_slices) { | ||
changeSet.appendRemoved(s); | ||||
m_slices.removeOne(s); | ||||
delete s; | ||||
} | ||||
|
r174 | emit changed(changeSet); | ||
|
r203 | updateDerivativeData(); | ||
|
r142 | } | ||
|
r314 | /*! | ||
Counts the number of the slices in this series. | ||||
*/ | ||||
int QPieSeries::count() const | ||||
{ | ||||
return m_slices.count(); | ||||
} | ||||
/*! | ||||
Returns a list of slices that belong to this series. | ||||
*/ | ||||
QList<QPieSlice*> QPieSeries::slices() const | ||||
{ | ||||
return m_slices; | ||||
} | ||||
/*! | ||||
Sets the size \a factor of the pie. 1.0 is the default value. | ||||
Note that the pie will not grow beyond its absolute maximum size. | ||||
In practice its use is to make the pie appear smaller. | ||||
\sa sizeFactor() | ||||
*/ | ||||
|
r142 | void QPieSeries::setSizeFactor(qreal factor) | ||
{ | ||||
|
r163 | if (factor < 0.0) | ||
return; | ||||
if (m_sizeFactor != factor) { | ||||
|
r142 | m_sizeFactor = factor; | ||
|
r163 | emit sizeFactorChanged(); | ||
} | ||||
|
r142 | } | ||
|
r314 | /*! | ||
Gets the size factor of the pie. | ||||
\sa setSizeFactor() | ||||
*/ | ||||
qreal QPieSeries::sizeFactor() const | ||||
{ | ||||
return m_sizeFactor; | ||||
} | ||||
/*! | ||||
Sets the \a position of the pie within its bounding rectangle. | ||||
\sa PiePosition, position() | ||||
*/ | ||||
|
r142 | void QPieSeries::setPosition(PiePosition position) | ||
{ | ||||
|
r163 | if (m_position != position) { | ||
m_position = position; | ||||
emit positionChanged(); | ||||
} | ||||
|
r142 | } | ||
|
r314 | /*! | ||
Gets the position of the pie within its bounding rectangle. | ||||
\sa PiePosition, setPosition() | ||||
*/ | ||||
QPieSeries::PiePosition QPieSeries::position() const | ||||
{ | ||||
return m_position; | ||||
} | ||||
/*! | ||||
Sets the \a startAngle and \a angleSpan of this series. | ||||
|
r320 | Full pie is 360 degrees where 0 degrees is at 12 a'clock. | ||
|
r314 | */ | ||
void QPieSeries::setSpan(qreal startAngle, qreal angleSpan) | ||||
|
r203 | { | ||
if (startAngle >= 0 && startAngle < 360 && | ||||
|
r314 | angleSpan > 0 && angleSpan <= 360) { | ||
|
r203 | m_pieStartAngle = startAngle; | ||
|
r314 | m_pieAngleSpan = angleSpan; | ||
|
r203 | updateDerivativeData(); | ||
} | ||||
} | ||||
|
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); | ||||
} | ||||
|
r314 | /*! | ||
|
r347 | Convenience method for exploding a slice when user clicks the pie. Set \a enable to true to | ||
explode slices by clicking. | ||||
|
r314 | |||
|
r345 | \sa QPieSlice::isExploded(), QPieSlice::setExploded(), QPieSlice::setExplodeDistance() | ||
|
r314 | */ | ||
|
r203 | void QPieSeries::enableClickExplodes(bool enable) | ||
{ | ||||
if (enable) | ||||
connect(this, SIGNAL(clicked(QPieSlice*)), this, SLOT(toggleExploded(QPieSlice*))); | ||||
else | ||||
disconnect(this, SLOT(toggleExploded(QPieSlice*))); | ||||
} | ||||
|
r314 | /*! | ||
Convenience method for highlighting a slice when user hovers over the slice. | ||||
It changes the slice color to be lighter and shows the label of the slice. | ||||
|
r347 | Set \a enable to true to highlight a slice when user hovers on top of it. | ||
|
r314 | |||
\sa QPieSlice::isExploded(), QPieSlice::setExploded() | ||||
*/ | ||||
|
r203 | void QPieSeries::enableHoverHighlight(bool enable) | ||
{ | ||||
if (enable) { | ||||
connect(this, SIGNAL(hoverEnter(QPieSlice*)), this, SLOT(highlightOn(QPieSlice*))); | ||||
connect(this, SIGNAL(hoverLeave(QPieSlice*)), this, SLOT(highlightOff(QPieSlice*))); | ||||
} else { | ||||
disconnect(this, SLOT(hoverEnter(QPieSlice*))); | ||||
disconnect(this, SLOT(hoverLeave(QPieSlice*))); | ||||
} | ||||
} | ||||
|
r314 | /*! | ||
\fn void QPieSeries::changed(const QPieSeries::ChangeSet& changeSet) | ||||
This signal emitted when something has changed in the series. | ||||
The \a changeSet contains the details of which slices have been added, changed or removed. | ||||
\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() | ||||
*/ | ||||
/*! | ||||
\fn void QPieSeries::sizeFactorChanged() | ||||
This signal is emitted when size factor has been changed. | ||||
\sa sizeFactor(), setSizeFactor() | ||||
*/ | ||||
/*! | ||||
\fn void QPieSeries::positionChanged() | ||||
This signal is emitted when position of the pie has been changed. | ||||
\sa position(), setPosition() | ||||
*/ | ||||
|
r203 | void QPieSeries::sliceChanged() | ||
{ | ||||
QPieSlice* slice = qobject_cast<QPieSlice *>(sender()); | ||||
Q_ASSERT(m_slices.contains(slice)); | ||||
ChangeSet changeSet; | ||||
changeSet.appendChanged(slice); | ||||
emit changed(changeSet); | ||||
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); | ||||
} | ||||
void QPieSeries::toggleExploded(QPieSlice* slice) | ||||
{ | ||||
Q_ASSERT(slice); | ||||
slice->setExploded(!slice->isExploded()); | ||||
} | ||||
void QPieSeries::highlightOn(QPieSlice* slice) | ||||
{ | ||||
Q_ASSERT(slice); | ||||
QColor c = slice->brush().color().lighter(); | ||||
slice->setBrush(c); | ||||
|
r294 | slice->setLabelVisible(true); | ||
|
r203 | } | ||
void QPieSeries::highlightOff(QPieSlice* slice) | ||||
{ | ||||
Q_ASSERT(slice); | ||||
QColor c = slice->brush().color().darker(150); | ||||
slice->setBrush(c); | ||||
|
r294 | slice->setLabelVisible(false); | ||
|
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(); | ||||
// we must have some values | ||||
|
r289 | Q_ASSERT(m_total > 0); // TODO: is this the correct way to handle this? | ||
|
r174 | |||
|
r203 | // update slice attributes | ||
qreal sliceAngle = m_pieStartAngle; | ||||
foreach (QPieSlice* s, m_slices) { | ||||
bool changed = false; | ||||
qreal percentage = s->value() / m_total; | ||||
if (s->m_percentage != percentage) { | ||||
s->m_percentage = percentage; | ||||
changed = true; | ||||
} | ||||
|
r314 | qreal sliceSpan = m_pieAngleSpan * percentage; | ||
|
r289 | if (s->m_angleSpan != sliceSpan) { | ||
s->m_angleSpan = sliceSpan; | ||||
|
r203 | changed = true; | ||
} | ||||
if (s->m_angle != sliceAngle) { | ||||
s->m_angle = sliceAngle; | ||||
changed = true; | ||||
} | ||||
sliceAngle += sliceSpan; | ||||
if (changed) | ||||
emit s->changed(); | ||||
} | ||||
|
r174 | } | ||
|
r142 | #include "moc_qpieseries.cpp" | ||
QTCOMMERCIALCHART_END_NAMESPACE | ||||