diff --git a/example/piechart/main.cpp b/example/piechart/main.cpp index 9695b70..4e52351 100644 --- a/example/piechart/main.cpp +++ b/example/piechart/main.cpp @@ -17,10 +17,10 @@ int main(int argc, char *argv[]) Q_ASSERT(series); // Add test data to the series - series->add(QPieSlice(1, "test1", Qt::red)); + series->add(QPieSlice(1, "test1", Qt::red, true)); series->add(QPieSlice(2, "test2", Qt::green)); series->add(QPieSlice(3, "test3", Qt::blue)); - series->add(QPieSlice(4, "test4", Qt::darkRed)); + series->add(QPieSlice(4, "test4", Qt::darkRed, true)); series->add(QPieSlice(5, "test5", Qt::darkGreen)); // Use the chart widget as the central widget diff --git a/src/chartpresenter.cpp b/src/chartpresenter.cpp index 909cb3f..f3ba54a 100644 --- a/src/chartpresenter.cpp +++ b/src/chartpresenter.cpp @@ -147,7 +147,7 @@ void ChartPresenter::handleSeriesAdded(QChartSeries* series) case QChartSeries::SeriesTypePie: { QPieSeries *pieSeries = qobject_cast(series); PiePresenter* pie = new PiePresenter(m_chart, pieSeries); - pieSeries->m_piePresenter = pie; // TODO: remove this pointer passing use signals&slots + QObject::connect(pieSeries, SIGNAL(changed(const PieChangeSet&)), pie, SLOT(handleSeriesChanged(const PieChangeSet&))); QObject::connect(this, SIGNAL(geometryChanged(const QRectF&)), pie, SLOT(handleGeometryChanged(const QRectF&))); QObject::connect(m_dataset, SIGNAL(domainChanged(const Domain&)), pie, SLOT(handleDomainChanged(const Domain&))); m_chartItems.insert(series, pie); diff --git a/src/piechart/piepresenter.cpp b/src/piechart/piepresenter.cpp index b8db4d6..eb8c435 100644 --- a/src/piechart/piepresenter.cpp +++ b/src/piechart/piepresenter.cpp @@ -21,7 +21,7 @@ PiePresenter::~PiePresenter() delete m_slices.takeLast(); } -void PiePresenter::seriesChanged() +void PiePresenter::handleSeriesChanged(const PieChangeSet& changeSet) { const qreal fullPie = 360; qreal total = 0; @@ -43,25 +43,25 @@ void PiePresenter::seriesChanged() m_slices.append(slice); angle += span; } - - resize(); } -void PiePresenter::resize() +void PiePresenter::updateGeometry() { + prepareGeometryChange(); + m_pieRect = m_rect; if (m_pieRect.width() < m_pieRect.height()) { - m_pieRect.setWidth(m_pieRect.width() * m_pieSeries->m_sizeFactor); + m_pieRect.setWidth(m_pieRect.width() * m_pieSeries->sizeFactor()); m_pieRect.setHeight(m_pieRect.width()); m_pieRect.moveCenter(m_rect.center()); } else { - m_pieRect.setHeight(m_pieRect.height() * m_pieSeries->m_sizeFactor); + m_pieRect.setHeight(m_pieRect.height() * m_pieSeries->sizeFactor()); m_pieRect.setWidth(m_pieRect.height()); m_pieRect.moveCenter(m_rect.center()); } - switch (m_pieSeries->m_position) { + switch (m_pieSeries->position()) { case QPieSeries::PiePositionTopLeft: { m_pieRect.setHeight(m_pieRect.height() / 2); m_pieRect.setWidth(m_pieRect.height()); @@ -92,6 +92,8 @@ void PiePresenter::resize() qDebug() << "presentation rect:" << m_rect; qDebug() << "pie rect:" << m_pieRect; + foreach (PieSlice *slice, m_slices) + slice->updateGeometry(); } void PiePresenter::handleDomainChanged(const Domain& domain) @@ -101,10 +103,9 @@ void PiePresenter::handleDomainChanged(const Domain& domain) void PiePresenter::handleGeometryChanged(const QRectF& rect) { - // TODO: allow user setting the size? - // TODO: allow user defining the margins? - m_rect.setSize(rect.size()); - resize(); + m_rect = rect; + updateGeometry(); + } #include "moc_piepresenter.cpp" diff --git a/src/piechart/piepresenter.h b/src/piechart/piepresenter.h index 50bca99..4083d1d 100644 --- a/src/piechart/piepresenter.h +++ b/src/piechart/piepresenter.h @@ -17,16 +17,16 @@ public: PiePresenter(QGraphicsItem *parent, QPieSeries *series); ~PiePresenter(); -public: // from ChartItem +public: // from QGraphicsItem QRectF boundingRect() const { return m_rect; } void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) {} public: - void seriesChanged(); - void resize(); + void updateGeometry(); QRectF pieRect() const { return m_pieRect; } public Q_SLOTS: + void handleSeriesChanged(const PieChangeSet& changeSet); void handleDomainChanged(const Domain& domain); void handleGeometryChanged(const QRectF& rect); diff --git a/src/piechart/pieslice.cpp b/src/piechart/pieslice.cpp index 468185f..64bea43 100644 --- a/src/piechart/pieslice.cpp +++ b/src/piechart/pieslice.cpp @@ -1,19 +1,37 @@ #include "pieslice.h" #include "piepresenter.h" +#include "qpieseries.h" #include #include +#include QTCOMMERCIALCHART_BEGIN_NAMESPACE -PieSlice::PieSlice(PiePresenter *piePresentation, int seriesIndex, qreal startAngle, qreal span) - :QGraphicsItem(piePresentation), +#define PI 3.14159265 +#define EXPLODE_OFFSET 20 + +QPointF offset(qreal angle, qreal length) +{ + qreal dx = qSin(angle*(PI/180)) * length; + qreal dy = qCos(angle*(PI/180)) * length; + return QPointF(dx, -dy); +} + +PieSlice::PieSlice(PiePresenter *presenter, int seriesIndex, qreal startAngle, qreal span) + :QGraphicsItem(presenter), + m_label(new QGraphicsTextItem(this)), m_seriesIndex(seriesIndex), m_startAngle(startAngle), m_span(span) { - Q_ASSERT(piePresentation); + Q_ASSERT(presenter); setAcceptHoverEvents(true); setAcceptedMouseButtons(Qt::LeftButton); + updateGeometry(); + + // TODO: use themes + m_pen = QPen(Qt::black); + m_brush = QBrush(sliceData().m_color); } PieSlice::~PieSlice() @@ -22,70 +40,85 @@ PieSlice::~PieSlice() QRectF PieSlice::boundingRect() const { - return shape().boundingRect(); + return m_rect; } QPainterPath PieSlice::shape() const { - QRectF rect = (static_cast(parentItem()))->pieRect(); - qreal angle = (-m_startAngle) + (90); - qreal span = -m_span; - - QPainterPath path; - path.moveTo(rect.center()); - path.arcTo(rect, angle, span); - - // TODO: draw the shape so that it might have a hole in the center - // - Sin & Cos will be needed to find inner/outer arc endpoints - - // dx, dy are offsets from the center - //qreal l = boundingRect().height(); - //qreal dx = qSin(angle*(M_PI/180)) * l; - //qreal dy = qCos(angle*(M_PI/180)) * l; - - // TODO: exploded slice? - - return path; + return m_path; } void PieSlice::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { painter->setRenderHint(QPainter::Antialiasing); - // TODO: how to map theme settings to a pie slice? Now we - //painter->setPen(m_theme.linePen); - // TODO: - - QPieSlice data = (static_cast(parentItem()))->m_pieSeries->slice(m_seriesIndex); - painter->setBrush(data.m_color); - - //painter->setBrush(m_theme.linePen.color()); - - // From Qt docs: - // The startAngle and spanAngle must be specified in 1/16th of a degree, i.e. a full circle equals 5760 (16 * 360). - // Positive values for the angles mean counter-clockwise while negative values mean the clockwise direction. - // Zero degrees is at the 3 o'clock position. - // - // For sake of simplicity convert this so that zero degrees is at 12 o'clock and full circle is 360. - //qreal angle = (-m_startAngle*16) + (90*16); - //qreal span = -m_span*16; - //painter->drawPie(boundingRect(), angle, span); - - painter->drawPath(shape()); + // TODO: themes + painter->setPen(m_pen); + painter->setBrush(m_brush); + painter->drawPath(m_path); // Draw the label // TODO: do this better - painter->drawText(boundingRect().center(), data.m_label); + //QTextItem text; + painter->drawText(m_center, sliceData().m_label); } void PieSlice::hoverEnterEvent(QGraphicsSceneHoverEvent * event) { QGraphicsItem::hoverEnterEvent(event); - qDebug() << "hover" << m_seriesIndex << m_startAngle << m_span; + m_brush = QBrush(sliceData().m_color.lighter()); + update(); +} + +void PieSlice::hoverLeaveEvent(QGraphicsSceneHoverEvent * event) +{ + QGraphicsItem::hoverLeaveEvent(event); + m_brush = QBrush(sliceData().m_color); + update(); } void PieSlice::mousePressEvent(QGraphicsSceneMouseEvent *event) { QGraphicsItem::mousePressEvent(event); + QPieSlice data = sliceData(); + data.m_isExploded = !data.m_isExploded; + (static_cast(parentItem()))->m_pieSeries->update(m_seriesIndex, data); +} + +void PieSlice::updateGeometry() +{ + prepareGeometryChange(); + + PiePresenter *presenter = static_cast(parentItem()); + QRectF rect = presenter->pieRect(); + rect.adjust(EXPLODE_OFFSET, EXPLODE_OFFSET, -EXPLODE_OFFSET ,-EXPLODE_OFFSET); + + qreal centerAngle = m_startAngle + (m_span/2); + + if (presenter->m_pieSeries->slice(m_seriesIndex).m_isExploded) { + QPointF d = offset((centerAngle), EXPLODE_OFFSET); + rect.translate(d.x(), d.y()); + } + + qreal angle = (-m_startAngle) + (90); + qreal span = -m_span; + + QPainterPath path; + path.moveTo(rect.center()); + path.arcTo(rect, angle, span); + + // TODO: draw the shape so that it might have a hole in the center + + m_path = path; + m_rect = path.boundingRect(); + + qreal radius = rect.height() / 2; + m_center = rect.center() + offset(centerAngle, radius / 2); } + +QPieSlice PieSlice::sliceData() +{ + return (static_cast(parentItem()))->m_pieSeries->slice(m_seriesIndex); +} + QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/piechart/pieslice.h b/src/piechart/pieslice.h index 09a9e32..5795394 100644 --- a/src/piechart/pieslice.h +++ b/src/piechart/pieslice.h @@ -3,9 +3,11 @@ #include "qchartglobal.h" #include "charttheme_p.h" +#include "qpieseries.h" #include #include #include +#include QTCOMMERCIALCHART_BEGIN_NAMESPACE class PiePresenter; @@ -13,7 +15,7 @@ class PiePresenter; class PieSlice : public QGraphicsItem { public: - PieSlice(PiePresenter *piePresentation, int seriesIndex, qreal startAngle, qreal span); + PieSlice(PiePresenter *piePresenter, int seriesIndex, qreal startAngle, qreal span); ~PieSlice(); public: // from QGraphicsItem @@ -21,14 +23,25 @@ public: // from QGraphicsItem QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); void mousePressEvent(QGraphicsSceneMouseEvent *event); +public: + void updateGeometry(); + +private: + QPieSlice sliceData(); + private: + QGraphicsTextItem* m_label; int m_seriesIndex; qreal m_startAngle; qreal m_span; + QPainterPath m_path; QRectF m_rect; - //SeriesTheme m_theme; + QPointF m_center; + QPen m_pen; + QBrush m_brush; }; QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/piechart/qpieseries.cpp b/src/piechart/qpieseries.cpp index e46f90a..a636363 100644 --- a/src/piechart/qpieseries.cpp +++ b/src/piechart/qpieseries.cpp @@ -7,7 +7,6 @@ QTCOMMERCIALCHART_BEGIN_NAMESPACE QPieSeries::QPieSeries(QObject *parent) : QChartSeries(parent), - m_piePresenter(0), m_sizeFactor(1.0), m_position(PiePositionMaximized) { @@ -20,21 +19,30 @@ QPieSeries::~QPieSeries() void QPieSeries::set(QList slices) { - m_slices = slices; - if (m_piePresenter) { - m_piePresenter->seriesChanged(); - m_piePresenter->update(); + PieChangeSet changeSet; + + for (int i=slices.count(); i slices) { + PieChangeSet changeSet; + for (int i=0; iseriesChanged(); - // TODO: m_piePresenter->seriesAppended()?? - m_piePresenter->update(); - } + emit changed(changeSet); } void QPieSeries::add(QPieSlice slice) @@ -53,12 +61,9 @@ bool QPieSeries::update(int index, QPieSlice slice) { if ((index >= 0) && (index < m_slices.count())) { m_slices[index] = slice; - if (m_piePresenter) { - m_piePresenter->seriesChanged(); - // TODO: for a nice animation we need something like - // m_piePresenter->sliceChanged(index, oldslice, newslice) - m_piePresenter->update(); - } + PieChangeSet changeSet; + changeSet.m_changed << index; + emit changed(changeSet); return true; } return false; @@ -68,24 +73,13 @@ void QPieSeries::setSizeFactor(qreal factor) { if (factor > 0.0) m_sizeFactor = factor; - - if (m_piePresenter) { - m_piePresenter->resize(); - m_piePresenter->update(); - // TODO: do we have to update the parent item also? - // - potential issue: what if this function is called from the parent context? - } + emit sizeFactorChanged(); } void QPieSeries::setPosition(PiePosition position) { m_position = position; - if (m_piePresenter) { - m_piePresenter->resize(); - m_piePresenter->update(); - // TODO: do we have to update the parent item also? - // - potential issue: what if this function is called from the parent context? - } + emit positionChanged(); } #include "moc_qpieseries.cpp" diff --git a/src/piechart/qpieseries.h b/src/piechart/qpieseries.h index f0a916d..4cd4bdb 100644 --- a/src/piechart/qpieseries.h +++ b/src/piechart/qpieseries.h @@ -26,6 +26,14 @@ public: bool m_isExploded; }; +class PieChangeSet +{ +public: + QList m_added; + QList m_removed; + QList m_changed; +}; + class QTCOMMERCIALCHART_EXPORT QPieSeries : public QChartSeries { Q_OBJECT @@ -69,13 +77,14 @@ public: void setPosition(PiePosition position); PiePosition position() const { return m_position; } +Q_SIGNALS: + void changed(const PieChangeSet& changeSet); + void sizeFactorChanged(); + void positionChanged(); + private: Q_DISABLE_COPY(QPieSeries) // TODO: use PIML - friend class ChartPresenter; - friend class ChartDataSet; - friend class PiePresenter; - PiePresenter *m_piePresenter; QList m_slices; qreal m_sizeFactor; PiePosition m_position;