From 93cea193b2c20bccdb06ab5a6b2cf00837418fdf 2013-06-05 12:17:27 From: Mika Salmela Date: 2013-06-05 12:17:27 Subject: [PATCH] Changed BoxPlot to use domain for calculating geometry points. Change-Id: I14e5db7a4be7afd406e36eac758d8341d707f37f Reviewed-by: Mika Salmela --- diff --git a/src/boxplotchart/boxplotchartitem.cpp b/src/boxplotchart/boxplotchartitem.cpp index 0ddaa44..14e1fad 100644 --- a/src/boxplotchart/boxplotchartitem.cpp +++ b/src/boxplotchart/boxplotchartitem.cpp @@ -33,9 +33,7 @@ BoxPlotChartItem::BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem *item) ChartItem(series->d_func(), item), m_series(series), m_animation(0), - m_animate(0), - m_domainMaxY(0.0), - m_domainMinY(0.0) + m_animate(0) { connect(series, SIGNAL(boxsetsRemoved(QList)), this, SLOT(handleBoxsetRemove(QList))); connect(series->d_func(), SIGNAL(restructuredBoxes()), this, SLOT(handleDataStructureChanged())); @@ -90,7 +88,7 @@ void BoxPlotChartItem::handleDataStructureChanged() } updateBoxGeometry(box, s); - box->updateGeometry(); + box->updateGeometry(domain()); if (m_animation) m_animation->addBox(box); @@ -128,17 +126,12 @@ void BoxPlotChartItem::handleDomainUpdated() if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0)) return; - // Set my bounding rect to same as domain size - m_boundingRect.setRect(0.0, 0.0, domain()->size().width(), domain()->size().height()); + // Set my bounding rect to same as domain size. Add one pixel at the top (-1.0) and the bottom as 0.0 would + // snip a bit off from the whisker at the grid line + m_boundingRect.setRect(0.0, -1.0, domain()->size().width(), domain()->size().height() + 1.0); foreach (BoxWhiskers *item, m_boxTable.values()) { - // Update the domain size for each BoxWhisker item - item->setDomainSize(domain()->size()); - if (domain()->minY() != m_domainMinY || domain()->maxY() != m_domainMaxY) { - item->updateGeometry(); - m_domainMinY = domain()->minY(); - m_domainMaxY = domain()->maxY(); - } + item->updateGeometry(domain()); // If the animation is set, start the animation for each BoxWhisker item if (m_animation) @@ -156,7 +149,7 @@ void BoxPlotChartItem::handleLayoutChanged() if (dirty && m_animation) presenter()->startAnimation(m_animation->boxChangeAnimation(item)); else - item->updateGeometry(); + item->updateGeometry(domain()); } } diff --git a/src/boxplotchart/boxplotchartitem_p.h b/src/boxplotchart/boxplotchartitem_p.h index dafaf36..2f64790 100644 --- a/src/boxplotchart/boxplotchartitem_p.h +++ b/src/boxplotchart/boxplotchartitem_p.h @@ -76,8 +76,6 @@ protected: BoxPlotAnimation *m_animation; bool m_animate; - qreal m_domainMaxY; - qreal m_domainMinY; QRectF m_boundingRect; }; diff --git a/src/boxplotchart/boxwhiskers.cpp b/src/boxplotchart/boxwhiskers.cpp index 7bc378d..a74fe16 100644 --- a/src/boxplotchart/boxwhiskers.cpp +++ b/src/boxplotchart/boxwhiskers.cpp @@ -71,11 +71,10 @@ void BoxWhiskers::setLayout(const BoxWhiskersData &data) { m_data = data; - updateGeometry(); + updateGeometry(m_domain); update(); } - QSizeF BoxWhiskers::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_UNUSED(which) @@ -89,13 +88,6 @@ void BoxWhiskers::setGeometry(const QRectF &rect) Q_UNUSED(rect) } -void BoxWhiskers::setDomainSize(const QSizeF &size) -{ - m_domainSize = size; - - updateBoundingRect(); -} - QRectF BoxWhiskers::boundingRect() const { return m_boundingRect; @@ -109,57 +101,69 @@ void BoxWhiskers::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio painter->setPen(m_pen); painter->setBrush(m_brush); painter->setClipRect(parentItem()->boundingRect()); - painter->scale(m_domainSize.width() / m_data.m_boxItems, m_domainSize.height() / m_domain->spanY()); painter->drawPath(m_boxPath); } -void BoxWhiskers::updateGeometry() +void BoxWhiskers::updateGeometry(AbstractDomain *domain) { + m_domain = domain; + prepareGeometryChange(); QPainterPath path; + m_boxPath = path; + m_boundingRect = m_boxPath.boundingRect(); qreal columnWidth = 1.0 / m_data.m_seriesCount; - qreal left = 0.25 * columnWidth + columnWidth * m_data.m_seriesIndex; - qreal right = 0.75 * columnWidth + columnWidth * m_data.m_seriesIndex; - qreal middle = 0.5 * columnWidth + columnWidth * m_data.m_seriesIndex; - qreal domainMaxY = m_domain->maxY(); + qreal left = 0.25 * columnWidth + columnWidth * m_data.m_seriesIndex + m_data.m_index - 0.5; + qreal barWidth = columnWidth / 2.0; + + QPointF geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_upperExtreme), m_validData); + if (!m_validData) + return; + qreal geometryLeft = geometryPoint.x(); + qreal geometryUpperExtreme = geometryPoint.y(); + geometryPoint = m_domain->calculateGeometryPoint(QPointF(left + barWidth, m_data.m_upperQuartile), m_validData); + if (!m_validData) + return; + qreal geometryRight = geometryPoint.x(); + qreal geometryUpperQuartile = geometryPoint.y(); + geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_lowerQuartile), m_validData); + if (!m_validData) + return; + qreal geometryLowerQuartile = geometryPoint.y(); + geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_lowerExtreme), m_validData); + if (!m_validData) + return; + qreal geometryLowerExtreme = geometryPoint.y(); + geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_median), m_validData); + if (!m_validData) + return; + qreal geometryMedian = geometryPoint.y(); // Upper whisker - path.moveTo(left + m_data.m_index, domainMaxY - m_data.m_upperExtreme); - path.lineTo(right + m_data.m_index, domainMaxY - m_data.m_upperExtreme); - path.moveTo(middle + m_data.m_index, domainMaxY - m_data.m_upperExtreme); - path.lineTo(middle + m_data.m_index, domainMaxY - m_data.m_upperQuartile); + path.moveTo(geometryLeft, geometryUpperExtreme); + path.lineTo(geometryRight, geometryUpperExtreme); + path.moveTo((geometryLeft + geometryRight) / 2.0, geometryUpperExtreme); + path.lineTo((geometryLeft + geometryRight) / 2.0, geometryUpperQuartile); // Middle Box - path.addRect(left + m_data.m_index, domainMaxY - m_data.m_upperQuartile, - 0.5 * columnWidth, m_data.m_upperQuartile - m_data.m_lowerQuartile); + path.addRect(geometryLeft, geometryUpperQuartile, geometryRight - geometryLeft, geometryLowerQuartile - geometryUpperQuartile); - // Median/mean line - path.moveTo(left + m_data.m_index, domainMaxY - m_data.m_median); - path.lineTo(right + m_data.m_index, domainMaxY - m_data.m_median); + // Median line + path.moveTo(geometryLeft, geometryMedian); + path.lineTo(geometryRight, geometryMedian); // Lower whisker - path.moveTo(left + m_data.m_index, domainMaxY - m_data.m_lowerExtreme); - path.lineTo(right + m_data.m_index, domainMaxY - m_data.m_lowerExtreme); - path.moveTo(middle + m_data.m_index, domainMaxY - m_data.m_lowerExtreme); - path.lineTo(middle + m_data.m_index, domainMaxY - m_data.m_lowerQuartile); + path.moveTo(geometryLeft, geometryLowerExtreme); + path.lineTo(geometryRight, geometryLowerExtreme); + path.moveTo((geometryLeft + geometryRight) / 2.0, geometryLowerQuartile); + path.lineTo((geometryLeft + geometryRight) / 2.0, geometryLowerExtreme); path.closeSubpath(); m_boxPath = path; - - updateBoundingRect(); -} - -void BoxWhiskers::updateBoundingRect() -{ - qreal scaleY = m_domainSize.height() / (m_domain->maxY() - m_domain->minY()); - qreal scaleX = m_domainSize.width() / m_data.m_boxItems; - QRectF br = m_boxPath.boundingRect(); - - m_boundingRect = QRectF(br.x() * scaleX, br.y() * scaleY, - br.width() * scaleX, br.height() * scaleY); + m_boundingRect = m_boxPath.boundingRect(); } #include "moc_boxwhiskers_p.cpp" diff --git a/src/boxplotchart/boxwhiskers_p.h b/src/boxplotchart/boxwhiskers_p.h index 63cf225..17b32c6 100644 --- a/src/boxplotchart/boxwhiskers_p.h +++ b/src/boxplotchart/boxwhiskers_p.h @@ -54,7 +54,6 @@ public: void setBrush(const QBrush &brush); void setPen(const QPen &pen); void setLayout(const BoxWhiskersData &data); - void setDomainSize(const QSizeF &size); void mousePressEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); @@ -63,14 +62,11 @@ public: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); - void updateGeometry(); + void updateGeometry(AbstractDomain *domain); protected: QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const; void setGeometry(const QRectF &rect); -private: - void updateBoundingRect(); - Q_SIGNALS: void clicked(QBoxSet *boxset); void hovered(bool status, QBoxSet *boxset); diff --git a/src/boxplotchart/qboxplotseries.cpp b/src/boxplotchart/qboxplotseries.cpp index 842a9eb..fca2c45 100644 --- a/src/boxplotchart/qboxplotseries.cpp +++ b/src/boxplotchart/qboxplotseries.cpp @@ -382,9 +382,9 @@ void QBoxPlotSeriesPrivate::initializeDomain() qreal maxY(domain()->maxY()); qreal x = m_boxSets.count(); - minX = qMin(minX, - (qreal)0.5); + minX = qMin(minX, qreal(-0.5)); minY = qMin(minY, min()); - maxX = qMax(maxX, x - (qreal)0.5); + maxX = qMax(maxX, x - qreal(0.5)); maxY = qMax(maxY, max()); domain()->setRange(minX, maxX, minY, maxY); diff --git a/tests/boxplottester/mainwidget.cpp b/tests/boxplottester/mainwidget.cpp index 17227ae..25b5245 100644 --- a/tests/boxplottester/mainwidget.cpp +++ b/tests/boxplottester/mainwidget.cpp @@ -43,15 +43,17 @@ #include #include #include - +#include QTCOMMERCIALCHART_USE_NAMESPACE QString addCategories[] = {"Jul", "Aug", "Sep", "Nov", "Dec"}; +static const int maxAddCategories = 5; MainWidget::MainWidget(QWidget *parent) : QWidget(parent), m_chart(0), + m_axis(0), rowPos(0), nSeries(0), nNewBoxes(0) @@ -124,7 +126,6 @@ MainWidget::MainWidget(QWidget *parent) : // Create chart view with the chart m_chartView = new QChartView(m_chart, this); - //m_chartView->setRubberBand(QChartView::HorizonalRubberBand); // Another grid layout as a main layout QGridLayout *mainLayout = new QGridLayout(); @@ -187,7 +188,6 @@ void MainWidget::addSeries() return; // Initial data - //![1] QBoxSet *set0 = new QBoxSet(); QBoxSet *set1 = new QBoxSet(); QBoxSet *set2 = new QBoxSet(); @@ -196,7 +196,7 @@ void MainWidget::addSeries() QBoxSet *set5 = new QBoxSet(); // low bot med top upp - *set0 << 1 << 2 << 4.4 << 13 << 15; + *set0 << -1 << 2 << 4 << 13 << 15; *set1 << 5 << 6 << 7.5 << 8 << 12; *set2 << 3 << 5 << 5.7 << 8 << 9; *set3 << 5 << 6 << 6.8 << 7 << 8; @@ -219,14 +219,14 @@ void MainWidget::addSeries() m_chart->addSeries(m_series[nSeries]); - if (nSeries == 0) { + if (!m_axis) { QStringList categories; categories << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun"; m_axis = new QBarCategoryAxis(); m_axis->append(categories); m_chart->createDefaultAxes(); - m_chart->setAxisX(m_axis, m_series[nSeries]); } + m_chart->setAxisX(m_axis, m_series[nSeries]); nSeries++; } @@ -248,7 +248,7 @@ void MainWidget::addBox() { qDebug() << "BoxPlotTester::MainWidget::addBox()"; - if (nSeries > 0) { + if (nSeries > 0 && nNewBoxes < maxAddCategories) { QBoxSet *newSet = new QBoxSet(); newSet->setValue(QBoxSet::LowerExtreme, 5.0); newSet->setValue(QBoxSet::LowerQuartile, 6.0); @@ -256,8 +256,7 @@ void MainWidget::addBox() newSet->setValue(QBoxSet::UpperQuartile, 7.0); newSet->setValue(QBoxSet::UpperExtreme, 8.0); - for (int i = 0; i < nSeries; i++) - m_series[i]->append(newSet); + m_series[0]->append(newSet); m_axis->append(addCategories[nNewBoxes]); @@ -288,11 +287,15 @@ void MainWidget::removeBox() if (nSeries > 0) { for (int i = 0; i < nSeries; i++) { - QList sets = m_series[i]->boxSets(); - m_series[i]->remove(sets.at(m_series[i]->count() - 3)); + qDebug() << "m_series[i]->count() = " << m_series[i]->count(); + if (m_series[i]->count() > 3) { + QList sets = m_series[i]->boxSets(); + m_series[i]->remove(sets.at(m_series[i]->count() - 3)); + } } - m_axis->remove(m_axis->at(1)); + if (m_axis->count() > 3) + m_axis->remove(m_axis->at(1)); } else { qDebug() << "Create a series first"; }