piepresenter.cpp
210 lines
| 6.5 KiB
| text/x-c
|
CppLexer
Jani Honkonen
|
r142 | |||
Jani Honkonen
|
r353 | #include "piepresenter_p.h" | ||
#include "pieslice_p.h" | ||||
Jani Honkonen
|
r203 | #include "qpieslice.h" | ||
Jani Honkonen
|
r353 | #include "pieslicelabel_p.h" | ||
Jani Honkonen
|
r289 | #include "qpieseries.h" | ||
#include <qmath.h> | ||||
Jani Honkonen
|
r142 | #include <QDebug> | ||
Jani Honkonen
|
r289 | #include <QFontMetrics> | ||
Jani Honkonen
|
r142 | |||
QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||||
Jani Honkonen
|
r174 | PiePresenter::PiePresenter(QGraphicsItem *parent, QPieSeries *series) | ||
:ChartItem(parent), | ||||
m_series(series) | ||||
Jani Honkonen
|
r142 | { | ||
Q_ASSERT(series); | ||||
Jani Honkonen
|
r174 | connect(series, SIGNAL(changed(const QPieSeries::ChangeSet&)), this, SLOT(handleSeriesChanged(const QPieSeries::ChangeSet&))); | ||
Jani Honkonen
|
r163 | connect(series, SIGNAL(sizeFactorChanged()), this, SLOT(updateGeometry())); | ||
connect(series, SIGNAL(positionChanged()), this, SLOT(updateGeometry())); | ||||
Jani Honkonen
|
r289 | |||
if (m_series->count()) { | ||||
QPieSeries::ChangeSet changeSet; | ||||
changeSet.appendAdded(m_series->m_slices); | ||||
handleSeriesChanged(changeSet); | ||||
} | ||||
Jani Honkonen
|
r142 | } | ||
Jani Honkonen
|
r146 | PiePresenter::~PiePresenter() | ||
Jani Honkonen
|
r142 | { | ||
Jani Honkonen
|
r174 | // slices deleted automatically through QGraphicsItem | ||
Jani Honkonen
|
r142 | } | ||
Jani Honkonen
|
r289 | void PiePresenter::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) | ||
Jani Honkonen
|
r142 | { | ||
Jani Honkonen
|
r174 | // TODO: paint shadows for all components | ||
// - get paths from items & merge & offset and draw with shadow color? | ||||
} | ||||
void PiePresenter::handleSeriesChanged(const QPieSeries::ChangeSet& changeSet) | ||||
{ | ||||
Jani Honkonen
|
r203 | //qDebug() << "PiePresenter::handleSeriesChanged()"; | ||
//qDebug() << " added : " << changeSet.added(); | ||||
//qDebug() << " changed: " << changeSet.changed(); | ||||
//qDebug() << " removed: " << changeSet.removed(); | ||||
Jani Honkonen
|
r174 | |||
Jani Honkonen
|
r289 | foreach (QPieSlice* s, changeSet.added()) | ||
addSlice(s); | ||||
foreach (QPieSlice* s, changeSet.changed()) | ||||
updateSlice(s); | ||||
Jani Honkonen
|
r142 | |||
Jani Honkonen
|
r203 | foreach (QPieSlice* s, changeSet.removed()) | ||
deleteSlice(s); | ||||
Jani Honkonen
|
r142 | |||
Jani Honkonen
|
r289 | // every change possibly changes the actual pie size | ||
updateGeometry(); | ||||
Jani Honkonen
|
r203 | } | ||
Jani Honkonen
|
r174 | |||
Jani Honkonen
|
r203 | void PiePresenter::handleDomainChanged(const Domain& domain) | ||
{ | ||||
// TODO | ||||
} | ||||
void PiePresenter::handleGeometryChanged(const QRectF& rect) | ||||
{ | ||||
m_rect = rect; | ||||
Jani Honkonen
|
r289 | prepareGeometryChange(); | ||
Jani Honkonen
|
r203 | updateGeometry(); | ||
Jani Honkonen
|
r142 | } | ||
Jani Honkonen
|
r157 | void PiePresenter::updateGeometry() | ||
Jani Honkonen
|
r142 | { | ||
Jani Honkonen
|
r289 | if (!m_rect.isValid() || m_rect.isEmpty()) | ||
return; | ||||
Jani Honkonen
|
r157 | |||
Jani Honkonen
|
r289 | // calculate maximum rectangle for pie | ||
Jani Honkonen
|
r203 | QRectF pieRect = m_rect; | ||
if (pieRect.width() < pieRect.height()) { | ||||
pieRect.setWidth(pieRect.width() * m_series->sizeFactor()); | ||||
pieRect.setHeight(pieRect.width()); | ||||
pieRect.moveCenter(m_rect.center()); | ||||
Jani Honkonen
|
r142 | } else { | ||
Jani Honkonen
|
r203 | pieRect.setHeight(pieRect.height() * m_series->sizeFactor()); | ||
pieRect.setWidth(pieRect.height()); | ||||
pieRect.moveCenter(m_rect.center()); | ||||
Jani Honkonen
|
r142 | } | ||
Jani Honkonen
|
r289 | // position the pie rectangle | ||
Jani Honkonen
|
r174 | switch (m_series->position()) { | ||
Jani Honkonen
|
r142 | case QPieSeries::PiePositionTopLeft: { | ||
Jani Honkonen
|
r203 | pieRect.setHeight(pieRect.height() / 2); | ||
pieRect.setWidth(pieRect.height()); | ||||
pieRect.moveCenter(QPointF(m_rect.center().x() / 2, m_rect.center().y() / 2)); | ||||
Jani Honkonen
|
r142 | break; | ||
} | ||||
case QPieSeries::PiePositionTopRight: { | ||||
Jani Honkonen
|
r203 | pieRect.setHeight(pieRect.height() / 2); | ||
pieRect.setWidth(pieRect.height()); | ||||
pieRect.moveCenter(QPointF((m_rect.center().x() / 2) * 3, m_rect.center().y() / 2)); | ||||
Jani Honkonen
|
r142 | break; | ||
} | ||||
case QPieSeries::PiePositionBottomLeft: { | ||||
Jani Honkonen
|
r203 | pieRect.setHeight(pieRect.height() / 2); | ||
pieRect.setWidth(pieRect.height()); | ||||
pieRect.moveCenter(QPointF(m_rect.center().x() / 2, (m_rect.center().y() / 2) * 3)); | ||||
Jani Honkonen
|
r142 | break; | ||
} | ||||
case QPieSeries::PiePositionBottomRight: { | ||||
Jani Honkonen
|
r203 | pieRect.setHeight(pieRect.height() / 2); | ||
pieRect.setWidth(pieRect.height()); | ||||
pieRect.moveCenter(QPointF((m_rect.center().x() / 2) * 3, (m_rect.center().y() / 2) * 3)); | ||||
Jani Honkonen
|
r142 | break; | ||
} | ||||
default: | ||||
break; | ||||
} | ||||
Jani Honkonen
|
r289 | // calculate how much space we need around the pie rectangle (labels & exploding) | ||
qreal delta = 0; | ||||
qreal pieRadius = pieRect.height() / 2; | ||||
foreach (QPieSlice* s, m_series->m_slices) { | ||||
// calculate the farthest point in the slice from the pie center | ||||
qreal centerAngle = s->angle() + (s->angleSpan() / 2); | ||||
qreal len = pieRadius + s->labelArmLength() + s->explodeDistance(); | ||||
QPointF dp(qSin(centerAngle*(PI/180)) * len, -qCos(centerAngle*(PI/180)) * len); | ||||
QPointF p = pieRect.center() + dp; | ||||
// TODO: consider the label text | ||||
// calculate how much the radius must get smaller to fit that point in the base rectangle | ||||
qreal dt = m_rect.top() - p.y(); | ||||
if (dt > delta) delta = dt; | ||||
qreal dl = m_rect.left() - p.x(); | ||||
if (dl > delta) delta = dl; | ||||
qreal dr = p.x() - m_rect.right(); | ||||
if (dr > delta) delta = dr; | ||||
qreal db = p.y() - m_rect.bottom(); | ||||
if (db > delta) delta = db; | ||||
//if (!m_rect.contains(p)) qDebug() << s->label() << dt << dl << dr << db << "delta" << delta; | ||||
} | ||||
// shrink the pie rectangle so that everything outside it fits the base rectangle | ||||
pieRect.adjust(delta, delta, -delta, -delta); | ||||
// update slices | ||||
Jani Honkonen
|
r203 | if (m_pieRect != pieRect) { | ||
m_pieRect = pieRect; | ||||
Jani Honkonen
|
r289 | //qDebug() << "PiePresenter::updateGeometry()" << m_rect << m_pieRect; | ||
Jani Honkonen
|
r203 | foreach (PieSlice* s, m_slices.values()) { | ||
s->setPieRect(m_pieRect); | ||||
s->updateGeometry(); | ||||
Jani Honkonen
|
r289 | s->update(); | ||
Jani Honkonen
|
r203 | } | ||
Jani Honkonen
|
r174 | } | ||
Jani Honkonen
|
r142 | } | ||
Jani Honkonen
|
r203 | void PiePresenter::addSlice(QPieSlice* sliceData) | ||
Jani Honkonen
|
r142 | { | ||
Jani Honkonen
|
r203 | //qDebug() << "PiePresenter::addSlice()" << sliceData; | ||
Jani Honkonen
|
r142 | |||
Jani Honkonen
|
r203 | if (m_slices.keys().contains(sliceData)) { | ||
Jani Honkonen
|
r289 | Q_ASSERT(0); // TODO: how to handle this nicely? | ||
Jani Honkonen
|
r174 | return; | ||
} | ||||
// create slice | ||||
Jani Honkonen
|
r203 | PieSlice *slice = new PieSlice(this); | ||
Jani Honkonen
|
r208 | slice->setPieRect(m_pieRect); | ||
Jani Honkonen
|
r203 | slice->updateData(sliceData); | ||
Jani Honkonen
|
r209 | slice->updateGeometry(); | ||
slice->update(); | ||||
Jani Honkonen
|
r203 | m_slices.insert(sliceData, slice); | ||
// connect signals | ||||
connect(slice, SIGNAL(clicked()), sliceData, SIGNAL(clicked())); | ||||
connect(slice, SIGNAL(hoverEnter()), sliceData, SIGNAL(hoverEnter())); | ||||
connect(slice, SIGNAL(hoverLeave()), sliceData, SIGNAL(hoverLeave())); | ||||
Jani Honkonen
|
r174 | } | ||
Jani Honkonen
|
r289 | void PiePresenter::updateSlice(QPieSlice* sliceData) | ||
{ | ||||
//qDebug() << "PiePresenter::updateSlice()" << sliceData; | ||||
if (!m_slices.contains(sliceData)) { | ||||
Q_ASSERT(0); // TODO: how to handle this nicely? | ||||
return; | ||||
} | ||||
m_slices[sliceData]->updateData(sliceData); | ||||
} | ||||
Jani Honkonen
|
r203 | void PiePresenter::deleteSlice(QPieSlice* sliceData) | ||
Jani Honkonen
|
r174 | { | ||
Jani Honkonen
|
r203 | //qDebug() << "PiePresenter::deleteSlice()" << sliceData; | ||
Jani Honkonen
|
r174 | |||
Jani Honkonen
|
r289 | if (!m_slices.contains(sliceData)) { | ||
Q_ASSERT(0); // TODO: how to handle this nicely? | ||||
return; | ||||
Jani Honkonen
|
r174 | } | ||
Jani Honkonen
|
r289 | |||
delete m_slices.take(sliceData); | ||||
Jani Honkonen
|
r142 | } | ||
Jani Honkonen
|
r353 | #include "moc_piepresenter_p.cpp" | ||
Jani Honkonen
|
r142 | |||
QTCOMMERCIALCHART_END_NAMESPACE | ||||