summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2016-09-29 13:48:09 +0300
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2016-10-04 12:58:40 +0000
commit4e2698c9aee2594faaacb4d3f835e2189c7631ef (patch)
tree68177f4a6d0cf25515ee3d2984af589c8abca809
parent3cb2c5e7572a75a4fae8883c9c0f3141cdf207b9 (diff)
Add support for more than one bar series
Now charts properly positions bars if there are more than one bar series in the chart. Theming was changed to account for more than one bar series in the chart. This patch also fixes various animation errors that were exposed by adding/removing the series to/from an existing chart. Task-number: QTBUG-52379 Change-Id: I4a8bc7e1675191ff87966ec4ca3d01e2b7cb815d Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
-rw-r--r--src/charts/barchart/abstractbarchartitem.cpp226
-rw-r--r--src/charts/barchart/abstractbarchartitem_p.h10
-rw-r--r--src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp78
-rw-r--r--src/charts/barchart/horizontal/bar/horizontalbarchartitem_p.h2
-rw-r--r--src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem.cpp85
-rw-r--r--src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h4
-rw-r--r--src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem.cpp119
-rw-r--r--src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem_p.h2
-rw-r--r--src/charts/barchart/qabstractbarseries.cpp60
-rw-r--r--src/charts/barchart/vertical/bar/barchartitem.cpp70
-rw-r--r--src/charts/barchart/vertical/bar/barchartitem_p.h5
-rw-r--r--src/charts/barchart/vertical/percent/percentbarchartitem.cpp86
-rw-r--r--src/charts/barchart/vertical/percent/percentbarchartitem_p.h4
-rw-r--r--src/charts/barchart/vertical/stacked/stackedbarchartitem.cpp119
-rw-r--r--src/charts/barchart/vertical/stacked/stackedbarchartitem_p.h2
-rw-r--r--src/charts/chartthememanager_p.h1
-rw-r--r--src/charts/qchart.h1
-rw-r--r--tests/manual/barcharttester/chart-widget.cpp327
-rw-r--r--tests/manual/barcharttester/chart-widget.h9
19 files changed, 809 insertions, 401 deletions
diff --git a/src/charts/barchart/abstractbarchartitem.cpp b/src/charts/barchart/abstractbarchartitem.cpp
index 1af93df8..9395eef0 100644
--- a/src/charts/barchart/abstractbarchartitem.cpp
+++ b/src/charts/barchart/abstractbarchartitem.cpp
@@ -33,7 +33,7 @@
#include <private/qbarset_p.h>
#include <QtCharts/QAbstractBarSeries>
#include <private/qabstractbarseries_p.h>
-#include <QtCharts/QChart>
+#include <private/qchart_p.h>
#include <private/chartpresenter_p.h>
#include <private/charttheme_p.h>
#include <private/baranimation_p.h>
@@ -75,7 +75,12 @@ AbstractBarChartItem::AbstractBarChartItem(QAbstractBarSeries *series, QGraphics
connect(series, SIGNAL(labelsPositionChanged(QAbstractBarSeries::LabelsPosition)),
this, SLOT(handleLabelsPositionChanged()));
connect(series, SIGNAL(labelsAngleChanged(qreal)), this, SLOT(positionLabels()));
+ connect(series->chart()->d_ptr->m_dataset, &ChartDataSet::seriesAdded,
+ this, &AbstractBarChartItem::handleSeriesAdded);
+ connect(series->chart()->d_ptr->m_dataset, &ChartDataSet::seriesRemoved,
+ this, &AbstractBarChartItem::handleSeriesRemoved);
setZValue(ChartPresenter::BarSeriesZValue);
+ calculateSeriesPositionAdjustmentAndWidth();
handleDataStructureChanged();
}
@@ -99,12 +104,15 @@ void AbstractBarChartItem::initializeFullLayout()
{
qreal setCount = m_series->count();
- int index = 0;
for (int set = 0; set < setCount; set++) {
QBarSet *barSet = m_series->barSets().at(set);
const QList<Bar *> bars = m_barMap.value(barSet);
- for (int category = 0; category < m_categoryCount; category++)
- initializeLayout(set, bars.at(category)->index(), index++, true);
+ for (int i = 0; i < bars.size(); i++) {
+ Bar *bar = bars.at(i);
+ initializeLayout(set, bar->index(), bar->layoutIndex(), true);
+ // Make bar initially hidden to avoid artifacts, layout setting will show it
+ bar->setVisible(false);
+ }
}
}
@@ -113,9 +121,16 @@ void AbstractBarChartItem::applyLayout(const QVector<QRectF> &layout)
QSizeF size = geometry().size();
if (geometry().size().isValid()) {
if (m_animation) {
- if (m_resetAnimation || m_oldSize != size) {
+ // If geometry changes along the value axis, do full animation reset, as
+ // it can cause "ungrounded" bars otherwise.
+ // Changes to axis labels on bar axis can occur naturally with scrolling,
+ // which can cause geometry changes that shouldn't trigger animation reset.
+ const bool sizeChanged = m_orientation == Qt::Horizontal
+ ? m_oldSize.width() != size.width()
+ : m_oldSize.height() != size.height();
+ m_oldSize = size;
+ if (m_resetAnimation || sizeChanged) {
initializeFullLayout();
- m_oldSize = size;
m_resetAnimation = false;
}
m_animation->setup(m_layout, layout);
@@ -135,23 +150,33 @@ void AbstractBarChartItem::setAnimation(BarAnimation *animation)
void AbstractBarChartItem::setLayout(const QVector<QRectF> &layout)
{
- if (layout.size() != m_layout.size())
+ int setCount = m_series->count();
+ if (layout.size() != m_layout.size() || m_barMap.size() != setCount)
return;
m_layout = layout;
- int index = 0;
- int setCount = m_series->count();
+ const bool visible = m_series->isVisible();
for (int set = 0; set < setCount; set++) {
QBarSet *barSet = m_series->d_func()->barsetAt(set);
const QList<Bar *> bars = m_barMap.value(barSet);
- for (int category = 0; category < m_categoryCount; category++)
- bars.at(category)->setRect(layout.at(index++));
+ for (int i = 0; i < bars.size(); i++) {
+ Bar *bar = bars.at(i);
+ const QRectF &rect = layout.at(bar->layoutIndex());
+ bar->setRect(rect);
+ // Hide empty bars to avoid artifacts at animation start when adding a new series
+ // as it doesn't have correct axes yet
+ bar->setVisible(visible && !rect.isEmpty());
+ }
}
positionLabels();
}
-//handlers
+
+void AbstractBarChartItem::resetAnimation()
+{
+ m_resetAnimation = true;
+}
void AbstractBarChartItem::handleDomainUpdated()
{
@@ -182,7 +207,7 @@ void AbstractBarChartItem::handleLabelsVisibleChanged(bool visible)
while (i.hasNext()) {
i.next();
const QList<Bar *> &bars = i.value();
- for (int j = 0; j < m_categoryCount; j++) {
+ for (int j = 0; j < bars.size(); j++) {
QGraphicsTextItem *label = bars.at(j)->labelItem();
if (label)
label->setVisible(newVisible);
@@ -210,7 +235,7 @@ void AbstractBarChartItem::handleVisibleChanged()
while (i.hasNext()) {
i.next();
const QList<Bar *> &bars = i.value();
- for (int j = 0; j < m_categoryCount; j++) {
+ for (int j = 0; j < bars.size(); j++) {
Bar *bar = bars.at(j);
bar->setVisible(visible && i.key()->at(bar->index()) != 0.0);
}
@@ -247,28 +272,27 @@ void AbstractBarChartItem::handleUpdatedBars()
barSetP->setVisualsDirty(false);
if (updateLabels)
barSetP->setLabelsDirty(false);
- const QList<Bar *> bars = m_barMap.value(barSet);
const int actualBarCount = barSet->count();
- const int categoryCount = bars.size();
- for (int category = 0; category < categoryCount; category++) {
- Bar *bar = bars.at(category);
+ const QList<Bar *> bars = m_barMap.value(barSet);
+ for (int i = 0; i < bars.size(); i++) {
+ Bar *bar = bars.at(i);
if (seriesVisualsDirty || setVisualsDirty || bar->visualsDirty()) {
bar->setPen(barSetP->m_pen);
bar->setBrush(barSetP->m_brush);
bar->setVisualsDirty(false);
bar->update();
}
- if (updateLabels && actualBarCount > category) {
+ if (updateLabels && actualBarCount > bar->index()) {
if (seriesLabelsDirty || setLabelsDirty || bar->labelDirty()) {
bar->setLabelDirty(false);
- QGraphicsTextItem *label = bars.at(category)->labelItem();
+ QGraphicsTextItem *label = bar->labelItem();
QString valueLabel;
- qreal value = barSetP->value(category + m_firstCategory);
+ qreal value = barSetP->value(bar->index());
if (value == 0.0) {
label->setVisible(false);
} else {
label->setVisible(m_series->isLabelsVisible());
- valueLabel = generateLabelText(set, category + m_firstCategory, value);
+ valueLabel = generateLabelText(set, bar->index(), value);
}
label->setHtml(valueLabel);
label->setFont(barSetP->m_labelFont);
@@ -300,19 +324,19 @@ void AbstractBarChartItem::positionLabels()
if (angle != 0.0)
transform.rotate(angle);
- int index = 0;
int setCount = m_series->count();
for (int set = 0; set < setCount; set++) {
QBarSet *barSet = m_series->d_func()->barsetAt(set);
const QList<Bar *> bars = m_barMap.value(barSet);
- for (int category = 0; category < m_categoryCount; category++) {
- QGraphicsTextItem *label = bars.at(category)->labelItem();
+ for (int i = 0; i < bars.size(); i++) {
+ Bar *bar = bars.at(i);
+ QGraphicsTextItem *label = bar->labelItem();
QRectF labelRect = label->boundingRect();
QPointF center = labelRect.center();
qreal xPos = 0;
- qreal yPos = m_layout.at(index).center().y() - center.y();
+ qreal yPos = m_layout.at(bar->layoutIndex()).center().y() - center.y();
int xDiff = 0;
if (angle != 0.0) {
@@ -323,20 +347,20 @@ void AbstractBarChartItem::positionLabels()
xDiff = (labelRect.width() - oldWidth) / 2;
}
- int offset = bars.at(category)->pen().width() / 2 + 2;
+ int offset = bar->pen().width() / 2 + 2;
switch (m_series->labelsPosition()) {
case QAbstractBarSeries::LabelsCenter:
- xPos = m_layout.at(index).center().x() - center.x();
+ xPos = m_layout.at(bar->layoutIndex()).center().x() - center.x();
break;
case QAbstractBarSeries::LabelsInsideEnd:
- xPos = m_layout.at(index).right() - labelRect.width() - offset + xDiff;
+ xPos = m_layout.at(bar->layoutIndex()).right() - labelRect.width() - offset + xDiff;
break;
case QAbstractBarSeries::LabelsInsideBase:
- xPos = m_layout.at(index).left() + offset + xDiff;
+ xPos = m_layout.at(bar->layoutIndex()).left() + offset + xDiff;
break;
case QAbstractBarSeries::LabelsOutsideEnd:
- xPos = m_layout.at(index).right() + offset + xDiff;
+ xPos = m_layout.at(bar->layoutIndex()).right() + offset + xDiff;
break;
default:
// Invalid position, never comes here
@@ -345,15 +369,13 @@ void AbstractBarChartItem::positionLabels()
label->setPos(xPos, yPos);
label->setZValue(zValue() + 1);
-
- index++;
}
}
}
void AbstractBarChartItem::handleBarValueChange(int index, QtCharts::QBarSet *barset)
{
- markLabelsDirty(barset, index - m_firstCategory, 1);
+ markLabelsDirty(barset, index, 1);
handleLayoutChanged();
}
@@ -362,8 +384,7 @@ void AbstractBarChartItem::handleBarValueAdd(int index, int count, QBarSet *bars
Q_UNUSED(count)
// Value insertions into middle of barset need to dirty the rest of the labels of the set
- int visualIndex = qMax(0, index - m_firstCategory);
- markLabelsDirty(barset, visualIndex, -1);
+ markLabelsDirty(barset, index, -1);
handleLayoutChanged();
}
@@ -372,11 +393,38 @@ void AbstractBarChartItem::handleBarValueRemove(int index, int count, QBarSet *b
Q_UNUSED(count)
// Value removals from the middle of barset need to dirty the rest of the labels of the set.
- int visualIndex = qMax(0, index - m_firstCategory);
- markLabelsDirty(barset, visualIndex, -1);
+ markLabelsDirty(barset, index, -1);
handleLayoutChanged();
}
+void AbstractBarChartItem::handleSeriesAdded(QAbstractSeries *series)
+{
+ Q_UNUSED(series)
+
+ // If the parent series was added, do nothing, as series pos and width calculations will
+ // happen anyway.
+ QAbstractBarSeries *addedSeries = static_cast<QAbstractBarSeries *>(series);
+ if (addedSeries != m_series) {
+ calculateSeriesPositionAdjustmentAndWidth();
+ handleLayoutChanged();
+ }
+}
+
+void AbstractBarChartItem::handleSeriesRemoved(QAbstractSeries *series)
+{
+ // If the parent series was removed, disconnect everything connected by this item,
+ // as the item will be scheduled for deletion but it is done asynchronously with deleteLater.
+ QAbstractBarSeries *removedSeries = static_cast<QAbstractBarSeries *>(series);
+ if (removedSeries == m_series) {
+ disconnect(m_series->d_func(), 0, this, 0);
+ disconnect(m_series, 0, this, 0);
+ disconnect(m_series->chart()->d_ptr->m_dataset, 0, this, 0);
+ } else {
+ calculateSeriesPositionAdjustmentAndWidth();
+ handleLayoutChanged();
+ }
+}
+
void AbstractBarChartItem::positionLabelsVertical()
{
if (!m_series->isLabelsVisible())
@@ -388,18 +436,18 @@ void AbstractBarChartItem::positionLabelsVertical()
if (angle != 0.0)
transform.rotate(angle);
- int index = 0;
int setCount = m_series->count();
for (int set = 0; set < setCount; set++) {
QBarSet *barSet = m_series->d_func()->barsetAt(set);
const QList<Bar *> bars = m_barMap.value(barSet);
- for (int category = 0; category < m_categoryCount; category++) {
- QGraphicsTextItem *label = bars.at(category)->labelItem();
+ for (int i = 0; i < bars.size(); i++) {
+ Bar *bar = bars.at(i);
+ QGraphicsTextItem *label = bar->labelItem();
QRectF labelRect = label->boundingRect();
QPointF center = labelRect.center();
- qreal xPos = m_layout.at(index).center().x() - center.x();
+ qreal xPos = m_layout.at(bar->layoutIndex()).center().x() - center.x();
qreal yPos = 0;
int yDiff = 0;
@@ -411,20 +459,20 @@ void AbstractBarChartItem::positionLabelsVertical()
yDiff = (labelRect.height() - oldHeight) / 2;
}
- int offset = bars.at(category)->pen().width() / 2 + 2;
+ int offset = bar->pen().width() / 2 + 2;
switch (m_series->labelsPosition()) {
case QAbstractBarSeries::LabelsCenter:
- yPos = m_layout.at(index).center().y() - center.y();
+ yPos = m_layout.at(bar->layoutIndex()).center().y() - center.y();
break;
case QAbstractBarSeries::LabelsInsideEnd:
- yPos = m_layout.at(index).top() + offset + yDiff;
+ yPos = m_layout.at(bar->layoutIndex()).top() + offset + yDiff;
break;
case QAbstractBarSeries::LabelsInsideBase:
- yPos = m_layout.at(index).bottom() - labelRect.height() - offset + yDiff;
+ yPos = m_layout.at(bar->layoutIndex()).bottom() - labelRect.height() - offset + yDiff;
break;
case QAbstractBarSeries::LabelsOutsideEnd:
- yPos = m_layout.at(index).top() - labelRect.height() - offset + yDiff;
+ yPos = m_layout.at(bar->layoutIndex()).top() - labelRect.height() - offset + yDiff;
break;
default:
// Invalid position, never comes here
@@ -433,8 +481,6 @@ void AbstractBarChartItem::positionLabelsVertical()
label->setPos(xPos, yPos);
label->setZValue(zValue() + 1);
-
- index++;
}
}
}
@@ -483,8 +529,7 @@ void AbstractBarChartItem::handleSetStructureChange()
m_barMap.insert(set, bars);
} else {
// Dirty the old set labels to ensure labels are updated correctly on all series types
- QBarSetPrivate *barSetP = set->d_ptr.data();
- barSetP->setLabelsDirty(true);
+ markLabelsDirty(set, 0, -1);
}
}
@@ -520,7 +565,6 @@ void AbstractBarChartItem::updateBarItems()
}
int lastBarIndex = m_series->d_func()->categoryCount() - 1;
- int oldFirstCategory = m_firstCategory;
if (lastBarIndex < 0) {
// Indicate invalid categories by negatives
@@ -534,8 +578,6 @@ void AbstractBarChartItem::updateBarItems()
m_categoryCount = m_lastCategory - m_firstCategory + 1;
}
- bool dirtyAllLabels = oldFirstCategory != m_firstCategory;
-
QList<QBarSet *> newSets = m_series->barSets();
QList<QBarSet *> oldSets = m_barMap.keys();
@@ -547,17 +589,10 @@ void AbstractBarChartItem::updateBarItems()
if (layoutSize != m_layout.size())
m_layout.resize(layoutSize);
- bool visible = m_series->isVisible();
-
// Create new graphic items for bars or remove excess ones
int layoutIndex = 0;
for (int s = 0; s < newSets.size(); s++) {
QBarSet *set = newSets.at(s);
- if (dirtyAllLabels) {
- QBarSetPrivate *barSetP = set->d_ptr.data();
- barSetP->setLabelsDirty(true);
- }
-
QList<Bar *> bars = m_barMap.value(set);
int addCount = m_categoryCount - bars.size();
if (addCount > 0) {
@@ -576,7 +611,6 @@ void AbstractBarChartItem::updateBarItems()
connect(bar, &Bar::released, set, &QBarSet::released);
connect(bar, &Bar::doubleClicked, set, &QBarSet::doubleClicked);
- bar->setVisible(m_series->isVisible());
m_labelItemsMissing = true;
}
}
@@ -613,33 +647,67 @@ void AbstractBarChartItem::updateBarItems()
Bar *bar = indexMap.value(c);
if (!bar) {
bar = unassignedBars.at(unassignedIndex++);
- if (bar) {
- bar->setIndex(c);
- if (m_animation) {
- int layoutIndex = bar->layoutIndex();
- initializeLayout(s, c, layoutIndex, m_resetAnimation);
- bar->setRect(m_layout.at(layoutIndex));
- }
- }
+ bar->setIndex(c);
+ indexMap.insert(bar->index(), bar);
+ }
+ }
+
+ m_indexForBarMap.insert(set, indexMap);
+
+ if (m_animation) {
+ for (int i = 0; i < unassignedIndex; i++) {
+ Bar *bar = unassignedBars.at(i);
+ initializeLayout(s, bar->index(), bar->layoutIndex(), m_resetAnimation);
+ bar->setRect(m_layout.at(bar->layoutIndex()));
+ // Make bar initially hidden to avoid artifacts, layout setting will show it
+ bar->setVisible(false);
}
- if (bar)
- bar->setVisible(visible && set->at(c) != 0.0);
}
m_barMap.insert(set, newBars);
}
}
-void AbstractBarChartItem::markLabelsDirty(QBarSet *barset, int visualIndex, int count)
+void AbstractBarChartItem::markLabelsDirty(QBarSet *barset, int index, int count)
{
Q_ASSERT(barset);
- QList<Bar *> bars = m_barMap.value(barset);
- int firstIndex = qMax(0, visualIndex);
- int lastIndex = count < 0 ? bars.size() - 1 : visualIndex + count - 1;
- lastIndex = qMin(bars.size() - 1, lastIndex);
- for (int i = firstIndex; i <= lastIndex; i++)
- bars.at(i)->setLabelDirty(true);
+ if (index <= 0 && count < 0) {
+ barset->d_ptr.data()->setLabelsDirty(true);
+ } else {
+ const QList<Bar *> bars = m_barMap.value(barset);
+ const int maxIndex = count > 0 ? index + count : barset->count();
+ for (int i = 0; i < bars.size(); i++) {
+ Bar *bar = bars.at(i);
+ if (bar->index() >= index && bar->index() < maxIndex)
+ bar->setLabelDirty(true);
+ }
+ }
+}
+
+void AbstractBarChartItem::calculateSeriesPositionAdjustmentAndWidth()
+{
+ m_seriesPosAdjustment = 0.0;
+ m_seriesWidth = 1.0;
+
+ if (!m_series->chart())
+ return;
+
+ // Find out total number of bar series in chart and the index of this series among them
+ const QList<QAbstractSeries *> seriesList = m_series->chart()->series();
+ int index = -1;
+ int count = 0;
+ for (QAbstractSeries *series : seriesList) {
+ if (qobject_cast<QAbstractBarSeries *>(series)){
+ if (series == m_series)
+ index = count;
+ count++;
+ }
+ }
+ if (index > -1 && count > 1) {
+ m_seriesWidth = 1.0 / count;
+ m_seriesPosAdjustment = (m_seriesWidth * index) + (m_seriesWidth / 2.0) - 0.5;
+ }
}
#include "moc_abstractbarchartitem_p.cpp"
diff --git a/src/charts/barchart/abstractbarchartitem_p.h b/src/charts/barchart/abstractbarchartitem_p.h
index 927a5631..d65b1bc9 100644
--- a/src/charts/barchart/abstractbarchartitem_p.h
+++ b/src/charts/barchart/abstractbarchartitem_p.h
@@ -52,6 +52,7 @@ class QAxisCategories;
class QChart;
class BarAnimation;
class QBarSetPrivate;
+class QAbstractAxis;
class AbstractBarChartItem : public ChartItem
{
@@ -71,6 +72,7 @@ public:
virtual void setAnimation(BarAnimation *animation);
void setLayout(const QVector<QRectF> &layout);
QRectF geometry() const { return m_rect;}
+ void resetAnimation();
public Q_SLOTS:
void handleDomainUpdated();
@@ -85,6 +87,8 @@ public Q_SLOTS:
void handleBarValueChange(int index, QBarSet *barset);
void handleBarValueAdd(int index, int count, QBarSet *barset);
void handleBarValueRemove(int index, int count, QBarSet *barset);
+ void handleSeriesAdded(QAbstractSeries *series);
+ void handleSeriesRemoved(QAbstractSeries *series);
protected:
void positionLabelsVertical();
@@ -92,7 +96,8 @@ protected:
void handleSetStructureChange();
virtual QString generateLabelText(int set, int category, qreal value);
void updateBarItems();
- virtual void markLabelsDirty(QBarSet *barset, int visualIndex, int count);
+ virtual void markLabelsDirty(QBarSet *barset, int index, int count);
+ void calculateSeriesPositionAdjustmentAndWidth();
QRectF m_rect;
QVector<QRectF> m_layout;
@@ -101,6 +106,7 @@ protected:
QAbstractBarSeries *m_series; // Not owned.
QMap<QBarSet *, QList<Bar *> > m_barMap;
+ QMap<QBarSet *, QHash<int, Bar *> > m_indexForBarMap;
int m_firstCategory;
int m_lastCategory;
int m_categoryCount;
@@ -108,6 +114,8 @@ protected:
bool m_labelItemsMissing;
Qt::Orientation m_orientation;
bool m_resetAnimation;
+ qreal m_seriesPosAdjustment;
+ qreal m_seriesWidth;
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp b/src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp
index 6b9b7192..ee0822a9 100644
--- a/src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp
+++ b/src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp
@@ -39,14 +39,15 @@ HorizontalBarChartItem::HorizontalBarChartItem(QAbstractBarSeries *series, QGrap
{
}
-void HorizontalBarChartItem::initializeLayout(int set, int category, int layoutIndex,
- bool resetAnimation)
+void HorizontalBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
{
QRectF rect;
- int previousSetIndex = layoutIndex - m_categoryCount;
- if (previousSetIndex >= 0) {
- rect = m_layout.at(previousSetIndex);
+ if (set > 0) {
+ QBarSet *barSet = m_series->barSets().at(set - 1);
+ Bar *bar = m_indexForBarMap.value(barSet).value(category);
+ rect = m_layout.at(bar->layoutIndex());
qreal oldTop = rect.top();
if (resetAnimation)
rect.setTop(oldTop - rect.height());
@@ -55,23 +56,15 @@ void HorizontalBarChartItem::initializeLayout(int set, int category, int layoutI
} else {
QPointF topLeft;
QPointF bottomRight;
- qreal barWidth = m_series->d_func()->barWidth();
- qreal setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
+ const int setCount = m_series->count();
if (domain()->type() == AbstractDomain::LogXYDomain
|| domain()->type() == AbstractDomain::LogXLogYDomain) {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(domain()->minX(),
- category - barWidth / 2 + set/setCount * barWidth), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(domain()->minX(),
- category - barWidth / 2 + (set + 1)/setCount * barWidth),
- m_validData);
+ topLeft = topLeftPoint(set, setCount, category, barWidth, domain()->minX());
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, domain()->minX());
} else {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(0, category - barWidth / 2 + set/setCount * barWidth), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(0, category - barWidth / 2 + (set + 1)/setCount * barWidth),
- m_validData);
+ topLeft = topLeftPoint(set, setCount, category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, 0.0);
}
if (m_validData) {
@@ -82,31 +75,52 @@ void HorizontalBarChartItem::initializeLayout(int set, int category, int layoutI
m_layout[layoutIndex] = rect.normalized();
}
+QPointF HorizontalBarChartItem::topLeftPoint(int set, int setCount, int category,
+ qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(value, m_seriesPosAdjustment + category - (barWidth / 2.0)
+ + (qreal(set)/setCount) * barWidth),
+ m_validData);
+}
+
+QPointF HorizontalBarChartItem::bottomRightPoint(int set, int setCount, int category,
+ qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(value, m_seriesPosAdjustment + category - (barWidth / 2.0)
+ + (qreal(set + 1)/setCount) * barWidth),
+ m_validData);
+}
+
QVector<QRectF> HorizontalBarChartItem::calculateLayout()
{
QVector<QRectF> layout;
- layout.reserve(m_layout.size());
+ layout.resize(m_layout.size());
- // Use temporary qreals for accuracy
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ const int setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
for (int set = 0; set < setCount; set++) {
- const QBarSet *barSet = m_series->barSets().at(set);
- for (int category = m_firstCategory; category <= m_lastCategory; category++) {
+ QBarSet *barSet = m_series->barSets().at(set);
+ const QList<Bar *> bars = m_barMap.value(barSet);
+ for (int i = 0; i < m_categoryCount; i++) {
+ Bar *bar = bars.at(i);
+ const int category = bar->index();
qreal value = barSet->at(category);
QRectF rect;
QPointF topLeft;
- if (domain()->type() == AbstractDomain::LogXYDomain || domain()->type() == AbstractDomain::LogXLogYDomain)
- topLeft = domain()->calculateGeometryPoint(QPointF(domain()->minX(), category - barWidth / 2 + set/setCount * barWidth), m_validData);
- else
- topLeft = domain()->calculateGeometryPoint(QPointF(0, category - barWidth / 2 + set/setCount * barWidth), m_validData);
-
- QPointF bottomRight = domain()->calculateGeometryPoint(QPointF(value, category - barWidth / 2 + (set + 1)/setCount * barWidth), m_validData);
+ if (domain()->type() == AbstractDomain::LogXYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ topLeft = topLeftPoint(set, setCount, category, barWidth, domain()->minX());
+ } else {
+ topLeft = topLeftPoint(set, setCount, category, barWidth, 0.0);
+ }
+ QPointF bottomRight = bottomRightPoint(set, setCount, category, barWidth, value);
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- layout.append(rect.normalized());
+ layout[bar->layoutIndex()] = rect.normalized();
}
}
return layout;
diff --git a/src/charts/barchart/horizontal/bar/horizontalbarchartitem_p.h b/src/charts/barchart/horizontal/bar/horizontalbarchartitem_p.h
index 6a80cf1a..9f616768 100644
--- a/src/charts/barchart/horizontal/bar/horizontalbarchartitem_p.h
+++ b/src/charts/barchart/horizontal/bar/horizontalbarchartitem_p.h
@@ -53,6 +53,8 @@ public:
private:
virtual QVector<QRectF> calculateLayout();
void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation);
+ QPointF topLeftPoint(int set, int setCount, int category, qreal barWidth, qreal value);
+ QPointF bottomRightPoint(int set, int setCount, int category, qreal barWidth, qreal value);
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem.cpp b/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem.cpp
index 326c5f45..8bda21ed 100644
--- a/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem.cpp
+++ b/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem.cpp
@@ -58,33 +58,30 @@ QString HorizontalPercentBarChartItem::generateLabelText(int set, int category,
return valueLabel;
}
-void HorizontalPercentBarChartItem::initializeLayout(int set, int category, int layoutIndex,
- bool resetAnimation)
+void HorizontalPercentBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
{
Q_UNUSED(set)
Q_UNUSED(resetAnimation)
QRectF rect;
- int previousSetIndex = layoutIndex - m_categoryCount;
- if (previousSetIndex >= 0) {
- rect = m_layout.at(previousSetIndex);
+ if (set > 0) {
+ QBarSet *barSet = m_series->barSets().at(set - 1);
+ Bar *bar = m_indexForBarMap.value(barSet).value(category);
+ rect = m_layout.at(bar->layoutIndex());
rect.setLeft(rect.right());
} else {
QPointF topLeft;
QPointF bottomRight;
- qreal barWidth = m_series->d_func()->barWidth();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
if (domain()->type() == AbstractDomain::LogXYDomain
|| domain()->type() == AbstractDomain::LogXLogYDomain) {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(domain()->minX(), category - barWidth / 2), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(domain()->minX(), category + barWidth / 2), m_validData);
+ topLeft = topLeftPoint(category, barWidth, domain()->minX());
+ bottomRight = bottomRightPoint(category, barWidth, domain()->minX());
} else {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(0, category - barWidth / 2), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(0, category + barWidth / 2), m_validData);
+ topLeft = topLeftPoint(category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(category, barWidth, 0.0);
}
if (m_validData) {
rect.setTopLeft(topLeft);
@@ -94,52 +91,72 @@ void HorizontalPercentBarChartItem::initializeLayout(int set, int category, int
m_layout[layoutIndex] = rect.normalized();
}
-void HorizontalPercentBarChartItem::markLabelsDirty(QBarSet *barset, int visualIndex, int count)
+void HorizontalPercentBarChartItem::markLabelsDirty(QBarSet *barset, int index, int count)
{
Q_UNUSED(barset)
// Percent series need to dirty all labels of the stack
QList<QBarSet *> sets = m_barMap.keys();
for (int set = 0; set < sets.size(); set++)
- AbstractBarChartItem::markLabelsDirty(sets.at(set), visualIndex, count);
+ AbstractBarChartItem::markLabelsDirty(sets.at(set), index, count);
+}
+
+QPointF HorizontalPercentBarChartItem::topLeftPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(value, m_seriesPosAdjustment + category - (barWidth / 2.0)), m_validData);
+}
+
+QPointF HorizontalPercentBarChartItem::bottomRightPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(value, m_seriesPosAdjustment + category + (barWidth / 2.0)), m_validData);
}
QVector<QRectF> HorizontalPercentBarChartItem::calculateLayout()
{
QVector<QRectF> layout;
- layout.reserve(m_layout.size());
+ layout.resize(m_layout.size());
- // Use temporary qreals for accuracy
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ const int setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
QVector<qreal> categorySums(m_categoryCount);
QVector<qreal> tempSums(m_categoryCount, 0.0);
+
for (int category = 0; category < m_categoryCount; category++)
categorySums[category] = m_series->d_func()->categorySum(category + m_firstCategory);
+
for (int set = 0; set < setCount; set++) {
- const QBarSet *barSet = m_series->barSets().at(set);
- for (int category = m_firstCategory; category <= m_lastCategory; category++) {
+ QBarSet *barSet = m_series->barSets().at(set);
+ const QList<Bar *> bars = m_barMap.value(barSet);
+ for (int i = 0; i < m_categoryCount; i++) {
+ Bar *bar = bars.at(i);
+ const int category = bar->index();
qreal &sum = tempSums[category - m_firstCategory];
const qreal &categorySum = categorySums.at(category - m_firstCategory);
qreal value = barSet->at(category);
QRectF rect;
- qreal topX = 0;
- if (sum > 0)
- topX = 100 * sum / categorySum;
- qreal bottomX = 0;
+ qreal topX = 0.0;
+ qreal bottomX = 0.0;
qreal newSum = value + sum;
- if (newSum > 0)
- bottomX = 100 * newSum / categorySum;
+ if (categorySum != 0.0) {
+ if (sum > 0.0)
+ topX = 100.0 * sum / categorySum;
+ if (newSum > 0.0)
+ bottomX = 100.0 * newSum / categorySum;
+ }
QPointF topLeft;
- if (domain()->type() == AbstractDomain::LogXYDomain || domain()->type() == AbstractDomain::LogXLogYDomain)
- topLeft = domain()->calculateGeometryPoint(QPointF(set ? topX : domain()->minX(), category - barWidth/2), m_validData);
- else
- topLeft = domain()->calculateGeometryPoint(QPointF(set ? topX : 0, category - barWidth/2), m_validData);
- QPointF bottomRight = domain()->calculateGeometryPoint(QPointF(bottomX, category + barWidth/2), m_validData);
+ if (domain()->type() == AbstractDomain::LogXYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ topLeft = topLeftPoint(category, barWidth, set ? topX : domain()->minX());
+ } else {
+ topLeft = topLeftPoint(category, barWidth, set ? topX : 0.0);
+ }
+ QPointF bottomRight = bottomRightPoint(category, barWidth, bottomX);
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- layout.append(rect.normalized());
+ layout[bar->layoutIndex()] = rect.normalized();
sum = newSum;
}
}
diff --git a/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h b/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h
index afd8e40d..51180258 100644
--- a/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h
+++ b/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h
@@ -54,7 +54,9 @@ public:
private:
virtual QVector<QRectF> calculateLayout();
void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation);
- void markLabelsDirty(QBarSet *barset, int visualIndex, int count);
+ void markLabelsDirty(QBarSet *barset, int index, int count);
+ QPointF topLeftPoint(int category, qreal barWidth, qreal value);
+ QPointF bottomRightPoint(int category, qreal barWidth, qreal value);
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem.cpp b/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem.cpp
index e38186e4..211aa4e5 100644
--- a/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem.cpp
+++ b/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem.cpp
@@ -39,34 +39,53 @@ HorizontalStackedBarChartItem::HorizontalStackedBarChartItem(QAbstractBarSeries
{
}
-void HorizontalStackedBarChartItem::initializeLayout(int set, int category, int layoutIndex,
- bool resetAnimation)
+void HorizontalStackedBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
{
Q_UNUSED(set)
Q_UNUSED(resetAnimation)
QRectF rect;
-
- int previousSetIndex = layoutIndex - m_categoryCount;
- if (previousSetIndex >= 0) {
- rect = m_layout.at(previousSetIndex);
- rect.setLeft(rect.right());
+ if (set > 0) {
+ const QBarSet *barSet = m_series->barSets().at(set);
+ const qreal value = barSet->at(category);
+ int checkIndex = set;
+ bool found = false;
+ // Negative values stack to negative side and positive values to positive side, so we need
+ // to find the previous set that stacks to the same side
+ while (checkIndex > 0 && !found) {
+ checkIndex--;
+ QBarSet *checkSet = m_series->barSets().at(checkIndex);
+ const qreal checkValue = checkSet->at(category);
+ if (value < 0.0 == checkValue < 0.0) {
+ Bar *checkBar = m_indexForBarMap.value(checkSet).value(category);
+ rect = m_layout.at(checkBar->layoutIndex());
+ found = true;
+ break;
+ }
+ }
+ // If we didn't find a previous set to the same direction, just stack next to the first set
+ if (!found) {
+ QBarSet *firsSet = m_series->barSets().at(0);
+ Bar *firstBar = m_indexForBarMap.value(firsSet).value(category);
+ rect = m_layout.at(firstBar->layoutIndex());
+ }
+ if (value < 0)
+ rect.setRight(rect.left());
+ else
+ rect.setLeft(rect.right());
} else {
QPointF topLeft;
QPointF bottomRight;
- qreal barWidth = m_series->d_func()->barWidth();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
if (domain()->type() == AbstractDomain::LogXYDomain
|| domain()->type() == AbstractDomain::LogXLogYDomain) {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(domain()->minX(), category - barWidth / 2), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(domain()->minX(), category + barWidth / 2), m_validData);
+ topLeft = topLeftPoint(category, barWidth, domain()->minX());
+ bottomRight = bottomRightPoint(category, barWidth, domain()->minX());
} else {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(0, category - barWidth / 2), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(0, category + barWidth / 2), m_validData);
+ topLeft = topLeftPoint(category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(category, barWidth, 0.0);
}
if (m_validData) {
@@ -77,21 +96,35 @@ void HorizontalStackedBarChartItem::initializeLayout(int set, int category, int
m_layout[layoutIndex] = rect.normalized();
}
+QPointF HorizontalStackedBarChartItem::topLeftPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(value, m_seriesPosAdjustment + category - (barWidth / 2)), m_validData);
+}
+
+QPointF HorizontalStackedBarChartItem::bottomRightPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(value, m_seriesPosAdjustment + category + (barWidth / 2)), m_validData);
+}
+
QVector<QRectF> HorizontalStackedBarChartItem::calculateLayout()
{
QVector<QRectF> layout;
- layout.reserve(m_layout.size());
+ layout.resize(m_layout.size());
- // Use temporary qreals for accuracy
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ const int setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
QVector<qreal> positiveSums(m_categoryCount, 0.0);
QVector<qreal> negativeSums(m_categoryCount, 0.0);
for (int set = 0; set < setCount; set++) {
- const QBarSet *barSet = m_series->barSets().at(set);
- for (int category = m_firstCategory; category <= m_lastCategory; category++) {
+ QBarSet *barSet = m_series->barSets().at(set);
+ const QList<Bar *> bars = m_barMap.value(barSet);
+ for (int i = 0; i < m_categoryCount; i++) {
+ Bar *bar = bars.at(i);
+ const int category = bar->index();
qreal &positiveSum = positiveSums[category - m_firstCategory];
qreal &negativeSum = negativeSums[category - m_firstCategory];
qreal value = barSet->at(category);
@@ -99,24 +132,44 @@ QVector<QRectF> HorizontalStackedBarChartItem::calculateLayout()
QPointF topLeft;
QPointF bottomRight;
if (value < 0) {
- bottomRight = domain()->calculateGeometryPoint(QPointF(value + negativeSum, category - barWidth / 2), m_validData);
- if (domain()->type() == AbstractDomain::LogXYDomain || domain()->type() == AbstractDomain::LogXLogYDomain)
- topLeft = domain()->calculateGeometryPoint(QPointF(set ? negativeSum : domain()->minX(), category + barWidth / 2), m_validData);
- else
- topLeft = domain()->calculateGeometryPoint(QPointF(set ? negativeSum : 0, category + barWidth / 2), m_validData);
+ bottomRight = bottomRightPoint(category, barWidth, value + negativeSum);
+ if (domain()->type() == AbstractDomain::XLogYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ topLeft = topLeftPoint(category, barWidth,
+ set ? negativeSum : domain()->minX());
+ } else {
+ topLeft = topLeftPoint(category, barWidth, set ? negativeSum : 0.0);
+ }
negativeSum += value;
} else {
- bottomRight = domain()->calculateGeometryPoint(QPointF(value + positiveSum, category - barWidth / 2), m_validData);
- if (domain()->type() == AbstractDomain::LogXYDomain || domain()->type() == AbstractDomain::LogXLogYDomain)
- topLeft = domain()->calculateGeometryPoint(QPointF(set ? positiveSum : domain()->minX(), category + barWidth / 2), m_validData);
- else
- topLeft = domain()->calculateGeometryPoint(QPointF(set ? positiveSum : 0, category + barWidth / 2), m_validData);
+ bottomRight = bottomRightPoint(category, barWidth, value + positiveSum);
+ if (domain()->type() == AbstractDomain::XLogYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ topLeft = topLeftPoint(category, barWidth,
+ set ? positiveSum : domain()->minX());
+ } else {
+ topLeft = topLeftPoint(category, barWidth,
+ set ? positiveSum : 0.0);
+ }
positiveSum += value;
}
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- layout.append(rect.normalized());
+ rect = rect.normalized();
+ layout[bar->layoutIndex()] = rect;
+
+ // If animating, we need to reinitialize ~zero size bars with non-zero values
+ // so the bar growth animation starts at correct spot. We shouldn't reset if rect
+ // is already at correct position horizontally, so we check for that.
+ if (m_animation && value != 0.0) {
+ const QRectF &checkRect = m_layout.at(bar->layoutIndex());
+ if (checkRect.isEmpty() &&
+ (value < 0.0 && !qFuzzyCompare(checkRect.right(), rect.right())
+ || value > 0.0 && !qFuzzyCompare(checkRect.left(), rect.left()))) {
+ initializeLayout(set, category, bar->layoutIndex(), true);
+ }
+ }
}
}
return layout;
diff --git a/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem_p.h b/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem_p.h
index bbbc6bd8..4fb8ce27 100644
--- a/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem_p.h
+++ b/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem_p.h
@@ -53,6 +53,8 @@ public:
private:
virtual QVector<QRectF> calculateLayout();
void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation);
+ QPointF topLeftPoint(int category, qreal barWidth, qreal value);
+ QPointF bottomRightPoint(int category, qreal barWidth, qreal value);
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/barchart/qabstractbarseries.cpp b/src/charts/barchart/qabstractbarseries.cpp
index 1b371feb..be62a0d3 100644
--- a/src/charts/barchart/qabstractbarseries.cpp
+++ b/src/charts/barchart/qabstractbarseries.cpp
@@ -999,7 +999,6 @@ void QAbstractBarSeriesPrivate::initializeAxes()
Q_Q(QAbstractBarSeries);
foreach(QAbstractAxis* axis, m_axes) {
-
if (axis->type() == QAbstractAxis::AxisTypeBarCategory) {
switch (q->type()) {
case QAbstractSeries::SeriesTypeHorizontalBar:
@@ -1022,6 +1021,11 @@ void QAbstractBarSeriesPrivate::initializeAxes()
}
}
}
+
+ // Make sure series animations are reset when axes change
+ AbstractBarChartItem *item = qobject_cast<AbstractBarChartItem *>(m_item.data());
+ if (item)
+ item->resetAnimation();
}
QAbstractAxis::AxisType QAbstractBarSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
@@ -1096,19 +1100,53 @@ void QAbstractBarSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bo
const QList<QGradient> gradients = theme->seriesGradients();
+ // Since each bar series uses different number of colors, we need to account for other
+ // bar series in the chart that are also themed when choosing colors.
+ // First series count is used to determine the color stepping to keep old applications
+ // with single bar series with a lot of sets colored as they always have been.
+ int actualIndex = 0;
+ int firstSeriesSetCount = m_barSets.count();
+ if (!m_item.isNull()) {
+ auto seriesMap = m_item->themeManager()->seriesMap();
+ int lowestSeries = index;
+ for (auto it = seriesMap.cbegin(), end = seriesMap.cend(); it != end; ++it) {
+ if (it.value() != index) {
+ auto barSeries = qobject_cast<QAbstractBarSeries *>(it.key());
+ if (barSeries) {
+ actualIndex += barSeries->count();
+ if (it.value() < lowestSeries) {
+ firstSeriesSetCount = qMax(barSeries->count(), gradients.count());
+ lowestSeries = it.value();
+ }
+ }
+ }
+ }
+ }
+
qreal takeAtPos = 0.5;
qreal step = 0.2;
- if (m_barSets.count() > 1) {
- step = 1.0 / (qreal) m_barSets.count();
- if (m_barSets.count() % gradients.count())
- step *= gradients.count();
+ if (firstSeriesSetCount > 1) {
+ step = 1.0 / qreal(firstSeriesSetCount);
+ if (firstSeriesSetCount % gradients.count())
+ step *= gradients.count();
else
- step *= (gradients.count() - 1);
+ step *= (gradients.count() - 1);
+ if (index > 0) {
+ // Take necessary amount of initial steps
+ int initialStepper = actualIndex;
+ while (initialStepper > gradients.count()) {
+ initialStepper -= gradients.count();
+ takeAtPos += step;
+ if (takeAtPos == 1.0)
+ takeAtPos += step;
+ takeAtPos -= int(takeAtPos);
+ }
+ }
}
for (int i(0); i < m_barSets.count(); i++) {
- int colorIndex = (index + i) % gradients.count();
- if (i > 0 && i %gradients.count() == 0) {
+ int colorIndex = (actualIndex + i) % gradients.count();
+ if ((actualIndex + i) > 0 && (actualIndex + i) % gradients.count() == 0) {
// There is no dedicated base color for each sets, generate more colors
takeAtPos += step;
if (takeAtPos == 1.0)
@@ -1122,12 +1160,12 @@ void QAbstractBarSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bo
// 0.3 as a boundary seems to work well.
if (forced || QChartPrivate::defaultBrush() == m_barSets.at(i)->d_ptr->m_labelBrush) {
if (takeAtPos < 0.3)
- m_barSets.at(i)->setLabelBrush(ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 1));
+ m_barSets.at(i)->setLabelBrush(ChartThemeManager::colorAt(gradients.at(actualIndex % gradients.size()), 1));
else
- m_barSets.at(i)->setLabelBrush(ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 0));
+ m_barSets.at(i)->setLabelBrush(ChartThemeManager::colorAt(gradients.at(actualIndex % gradients.size()), 0));
}
if (forced || QChartPrivate::defaultPen() == m_barSets.at(i)->d_ptr->m_pen) {
- QColor c = ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 0.0);
+ QColor c = ChartThemeManager::colorAt(gradients.at(actualIndex % gradients.size()), 0.0);
m_barSets.at(i)->setPen(c);
}
}
diff --git a/src/charts/barchart/vertical/bar/barchartitem.cpp b/src/charts/barchart/vertical/bar/barchartitem.cpp
index 69bd481b..f9c867b3 100644
--- a/src/charts/barchart/vertical/bar/barchartitem.cpp
+++ b/src/charts/barchart/vertical/bar/barchartitem.cpp
@@ -48,9 +48,10 @@ void BarChartItem::initializeLayout(int set, int category, int layoutIndex, bool
{
QRectF rect;
- int previousSetIndex = layoutIndex - m_categoryCount;
- if (previousSetIndex >= 0) {
- rect = m_layout.at(previousSetIndex);
+ if (set > 0) {
+ QBarSet *barSet = m_series->barSets().at(set - 1);
+ Bar *bar = m_indexForBarMap.value(barSet).value(category);
+ rect = m_layout.at(bar->layoutIndex());
qreal oldRight = rect.right();
if (resetAnimation)
rect.setRight(oldRight + rect.width());
@@ -59,22 +60,15 @@ void BarChartItem::initializeLayout(int set, int category, int layoutIndex, bool
} else {
QPointF topLeft;
QPointF bottomRight;
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ const int setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
if (domain()->type() == AbstractDomain::XLogYDomain
|| domain()->type() == AbstractDomain::LogXLogYDomain) {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(category - barWidth / 2 + set/setCount * barWidth,
- domain()->minY()), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(category - barWidth / 2 + (set + 1)/setCount * barWidth,
- domain()->minY()), m_validData);
+ topLeft = topLeftPoint(set, setCount, category, barWidth, domain()->minY());
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, domain()->minY());
} else {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(category - barWidth / 2 + set/setCount * barWidth, 0), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(category - barWidth / 2 + (set + 1)/setCount * barWidth, 0),
- m_validData);
+ topLeft = topLeftPoint(set, setCount, category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, 0.0);
}
if (m_validData) {
@@ -85,32 +79,52 @@ void BarChartItem::initializeLayout(int set, int category, int layoutIndex, bool
m_layout[layoutIndex] = rect.normalized();
}
+QPointF BarChartItem::topLeftPoint(int set, int setCount, int category,
+ qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category - (barWidth / 2.0)
+ + (qreal(set)/setCount) * barWidth,
+ value), m_validData);
+}
+
+QPointF BarChartItem::bottomRightPoint(int set, int setCount,
+ int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category - (barWidth / 2.0)
+ + (qreal(set + 1)/setCount) * barWidth,
+ value), m_validData);
+}
+
QVector<QRectF> BarChartItem::calculateLayout()
{
QVector<QRectF> layout;
- layout.reserve(m_layout.size());
+ layout.resize(m_layout.size());
- // Use temporary qreals for accuracy
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ const int setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
for (int set = 0; set < setCount; set++) {
QBarSet *barSet = m_series->barSets().at(set);
const QList<Bar *> bars = m_barMap.value(barSet);
- for (int c = 0; c < m_categoryCount; c++) {
- int category = bars.at(c)->index();
+ for (int i = 0; i < m_categoryCount; i++) {
+ Bar *bar = bars.at(i);
+ const int category = bar->index();
qreal value = barSet->at(category);
QRectF rect;
- QPointF topLeft = domain()->calculateGeometryPoint(QPointF(category - barWidth / 2 + (set)/(setCount) * barWidth, value), m_validData);
+ QPointF topLeft = topLeftPoint(set, setCount, category, barWidth, value);
QPointF bottomRight;
- if (domain()->type() == AbstractDomain::XLogYDomain || domain()->type() == AbstractDomain::LogXLogYDomain)
- bottomRight = domain()->calculateGeometryPoint(QPointF(category - barWidth / 2 + (set + 1)/(setCount) * barWidth, domain()->minY()), m_validData);
- else
- bottomRight = domain()->calculateGeometryPoint(QPointF(category - barWidth / 2 + (set + 1)/(setCount) * barWidth, 0), m_validData);
+ if (domain()->type() == AbstractDomain::XLogYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, domain()->minY());
+ } else {
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, 0.0);
+ }
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- layout.append(rect.normalized());
+ layout[bar->layoutIndex()] = rect.normalized();
}
}
diff --git a/src/charts/barchart/vertical/bar/barchartitem_p.h b/src/charts/barchart/vertical/bar/barchartitem_p.h
index bb499ff4..ef5f5220 100644
--- a/src/charts/barchart/vertical/bar/barchartitem_p.h
+++ b/src/charts/barchart/vertical/bar/barchartitem_p.h
@@ -58,7 +58,10 @@ private Q_SLOTS:
private:
virtual QVector<QRectF> calculateLayout();
- void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation);
+ void initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation);
+ QPointF topLeftPoint(int set, int setCount, int category, qreal barWidth, qreal value);
+ QPointF bottomRightPoint(int set, int setCount, int category, qreal barWidth, qreal value);
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/barchart/vertical/percent/percentbarchartitem.cpp b/src/charts/barchart/vertical/percent/percentbarchartitem.cpp
index 2cd06f51..95867c78 100644
--- a/src/charts/barchart/vertical/percent/percentbarchartitem.cpp
+++ b/src/charts/barchart/vertical/percent/percentbarchartitem.cpp
@@ -63,33 +63,30 @@ QString PercentBarChartItem::generateLabelText(int set, int category, qreal valu
return valueLabel;
}
-void PercentBarChartItem::initializeLayout(int set, int category, int layoutIndex,
- bool resetAnimation)
+void PercentBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
{
Q_UNUSED(set)
Q_UNUSED(resetAnimation)
QRectF rect;
- int previousSetIndex = layoutIndex - m_categoryCount;
- if (previousSetIndex >= 0) {
- rect = m_layout.at(previousSetIndex);
+ if (set > 0) {
+ QBarSet *barSet = m_series->barSets().at(set - 1);
+ Bar *bar = m_indexForBarMap.value(barSet).value(category);
+ rect = m_layout.at(bar->layoutIndex());
rect.setBottom(rect.top());
} else {
QPointF topLeft;
QPointF bottomRight;
- qreal barWidth = m_series->d_func()->barWidth();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
if (domain()->type() == AbstractDomain::XLogYDomain
|| domain()->type() == AbstractDomain::LogXLogYDomain) {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(category - barWidth / 2, domain()->minY()), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(category + barWidth / 2, domain()->minY()), m_validData);
+ topLeft = topLeftPoint(category, barWidth, domain()->minY());
+ bottomRight = bottomRightPoint(category, barWidth, domain()->minY());
} else {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(category - barWidth / 2, 0), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(category + barWidth / 2, 0), m_validData);
+ topLeft = topLeftPoint(category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(category, barWidth, 0.0);
}
if (m_validData) {
rect.setTopLeft(topLeft);
@@ -100,52 +97,73 @@ void PercentBarChartItem::initializeLayout(int set, int category, int layoutInde
m_layout[layoutIndex] = rect.normalized();
}
-void PercentBarChartItem::markLabelsDirty(QBarSet *barset, int visualIndex, int count)
+void PercentBarChartItem::markLabelsDirty(QBarSet *barset, int index, int count)
{
Q_UNUSED(barset)
// Percent series need to dirty all labels of the stack
QList<QBarSet *> sets = m_barMap.keys();
for (int set = 0; set < sets.size(); set++)
- AbstractBarChartItem::markLabelsDirty(sets.at(set), visualIndex, count);
+ AbstractBarChartItem::markLabelsDirty(sets.at(set), index, count);
+}
+
+QPointF PercentBarChartItem::topLeftPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category - (barWidth / 2.0), value), m_validData);
+}
+
+QPointF PercentBarChartItem::bottomRightPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category + (barWidth / 2.0), value), m_validData);
}
QVector<QRectF> PercentBarChartItem::calculateLayout()
{
QVector<QRectF> layout;
- layout.reserve(m_layout.size());
+ layout.resize(m_layout.size());
- // Use temporary qreals for accuracy
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ const int setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
QVector<qreal> categorySums(m_categoryCount);
QVector<qreal> tempSums(m_categoryCount, 0.0);
+
for (int category = 0; category < m_categoryCount; category++)
categorySums[category] = m_series->d_func()->categorySum(category + m_firstCategory);
+
for (int set = 0; set < setCount; set++) {
- const QBarSet *barSet = m_series->barSets().at(set);
- for (int category = m_firstCategory; category <= m_lastCategory; category++) {
+ QBarSet *barSet = m_series->barSets().at(set);
+ const QList<Bar *> bars = m_barMap.value(barSet);
+ for (int i = 0; i < m_categoryCount; i++) {
+ Bar *bar = bars.at(i);
+ const int category = bar->index();
qreal &sum = tempSums[category - m_firstCategory];
const qreal &categorySum = categorySums.at(category - m_firstCategory);
qreal value = barSet->at(category);
QRectF rect;
- qreal topY = 0;
+ qreal topY = 0.0;
qreal newSum = value + sum;
- if (newSum > 0)
- topY = 100 * newSum / categorySum;
- qreal bottomY = 0;
- if (sum > 0)
- bottomY = 100 * sum / categorySum;
- QPointF topLeft = domain()->calculateGeometryPoint(QPointF(category - barWidth/2, topY), m_validData);
+ qreal bottomY = 0.0;
+ if (categorySum != 0.0) {
+ if (newSum > 0.0)
+ topY = 100.0 * newSum / categorySum;
+ if (sum > 0.0)
+ bottomY = 100.0 * sum / categorySum;
+ }
+ QPointF topLeft = topLeftPoint(category, barWidth, topY);
QPointF bottomRight;
- if (domain()->type() == AbstractDomain::XLogYDomain || domain()->type() == AbstractDomain::LogXLogYDomain)
- bottomRight = domain()->calculateGeometryPoint(QPointF(category + barWidth/2, set ? bottomY : domain()->minY()), m_validData);
- else
- bottomRight = domain()->calculateGeometryPoint(QPointF(category + barWidth/2, set ? bottomY : 0), m_validData);
+ if (domain()->type() == AbstractDomain::XLogYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ bottomRight = bottomRightPoint(category, barWidth,
+ set ? bottomY : domain()->minY());
+ } else {
+ bottomRight = bottomRightPoint(category, barWidth, bottomY);
+ }
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- layout.append(rect.normalized());
+ layout[bar->layoutIndex()] = rect.normalized();
sum = newSum;
}
}
diff --git a/src/charts/barchart/vertical/percent/percentbarchartitem_p.h b/src/charts/barchart/vertical/percent/percentbarchartitem_p.h
index c46697ef..80016613 100644
--- a/src/charts/barchart/vertical/percent/percentbarchartitem_p.h
+++ b/src/charts/barchart/vertical/percent/percentbarchartitem_p.h
@@ -61,7 +61,9 @@ private Q_SLOTS:
private:
virtual QVector<QRectF> calculateLayout();
void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation);
- void markLabelsDirty(QBarSet *barset, int visualIndex, int count);
+ void markLabelsDirty(QBarSet *barset, int index, int count);
+ QPointF topLeftPoint(int category, qreal barWidth, qreal value);
+ QPointF bottomRightPoint(int category, qreal barWidth, qreal value);
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/barchart/vertical/stacked/stackedbarchartitem.cpp b/src/charts/barchart/vertical/stacked/stackedbarchartitem.cpp
index 678de741..51ab680d 100644
--- a/src/charts/barchart/vertical/stacked/stackedbarchartitem.cpp
+++ b/src/charts/barchart/vertical/stacked/stackedbarchartitem.cpp
@@ -44,33 +44,53 @@ StackedBarChartItem::StackedBarChartItem(QAbstractBarSeries *series, QGraphicsIt
connect(series, SIGNAL(labelsFormatChanged(QString)), this, SLOT(positionLabels()));
}
-void StackedBarChartItem::initializeLayout(int set, int category, int layoutIndex,
- bool resetAnimation)
+void StackedBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
{
Q_UNUSED(set)
Q_UNUSED(resetAnimation)
QRectF rect;
- int previousSetIndex = layoutIndex - m_categoryCount;
- if (previousSetIndex >= 0) {
- rect = m_layout.at(previousSetIndex);
- rect.setBottom(rect.top());
+ if (set > 0) {
+ const QBarSet *barSet = m_series->barSets().at(set);
+ const qreal value = barSet->at(category);
+ int checkIndex = set;
+ bool found = false;
+ // Negative values stack to negative side and positive values to positive side, so we need
+ // to find the previous set that stacks to the same side
+ while (checkIndex > 0 && !found) {
+ checkIndex--;
+ QBarSet *checkSet = m_series->barSets().at(checkIndex);
+ const qreal checkValue = checkSet->at(category);
+ if (value < 0.0 == checkValue < 0.0) {
+ Bar *checkBar = m_indexForBarMap.value(checkSet).value(category);
+ rect = m_layout.at(checkBar->layoutIndex());
+ found = true;
+ break;
+ }
+ }
+ // If we didn't find a previous set to the same direction, just stack next to the first set
+ if (!found) {
+ QBarSet *firsSet = m_series->barSets().at(0);
+ Bar *firstBar = m_indexForBarMap.value(firsSet).value(category);
+ rect = m_layout.at(firstBar->layoutIndex());
+ }
+ if (value < 0)
+ rect.setTop(rect.bottom());
+ else
+ rect.setBottom(rect.top());
} else {
QPointF topLeft;
QPointF bottomRight;
- qreal barWidth = m_series->d_func()->barWidth();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
if (domain()->type() == AbstractDomain::XLogYDomain
|| domain()->type() == AbstractDomain::LogXLogYDomain) {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(category - barWidth / 2, domain()->minY()), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(category + barWidth / 2, domain()->minY()), m_validData);
+ topLeft = topLeftPoint(category, barWidth, domain()->minY());
+ bottomRight = bottomRightPoint(category, barWidth, domain()->minY());
} else {
- topLeft = domain()->calculateGeometryPoint(
- QPointF(category - barWidth / 2, 0), m_validData);
- bottomRight = domain()->calculateGeometryPoint(
- QPointF(category + barWidth / 2, 0), m_validData);
+ topLeft = topLeftPoint(category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(category, barWidth, 0.0);
}
if (m_validData) {
@@ -81,46 +101,79 @@ void StackedBarChartItem::initializeLayout(int set, int category, int layoutInde
m_layout[layoutIndex] = rect.normalized();
}
+QPointF StackedBarChartItem::topLeftPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category - (barWidth / 2), value), m_validData);
+}
+
+QPointF StackedBarChartItem::bottomRightPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category + (barWidth / 2), value), m_validData);
+}
+
QVector<QRectF> StackedBarChartItem::calculateLayout()
{
QVector<QRectF> layout;
- layout.reserve(m_layout.size());
+ layout.resize(m_layout.size());
- // Use temporary qreals for accuracy
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ const int setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
QVector<qreal> positiveSums(m_categoryCount, 0.0);
QVector<qreal> negativeSums(m_categoryCount, 0.0);
for (int set = 0; set < setCount; set++) {
- const QBarSet *barSet = m_series->barSets().at(set);
- for (int category = m_firstCategory; category <= m_lastCategory; category++) {
+ QBarSet *barSet = m_series->barSets().at(set);
+ const QList<Bar *> bars = m_barMap.value(barSet);
+ for (int i = 0; i < m_categoryCount; i++) {
+ Bar *bar = bars.at(i);
+ const int category = bar->index();
qreal &positiveSum = positiveSums[category - m_firstCategory];
qreal &negativeSum = negativeSums[category - m_firstCategory];
qreal value = barSet->at(category);
QRectF rect;
QPointF topLeft;
QPointF bottomRight;
- if (value < 0) {
- bottomRight = domain()->calculateGeometryPoint(QPointF(category - barWidth / 2, value + negativeSum), m_validData);
- if (domain()->type() == AbstractDomain::XLogYDomain || domain()->type() == AbstractDomain::LogXLogYDomain)
- topLeft = domain()->calculateGeometryPoint(QPointF(category + barWidth / 2, set ? negativeSum : domain()->minY()), m_validData);
- else
- topLeft = domain()->calculateGeometryPoint(QPointF(category + barWidth / 2, set ? negativeSum : 0), m_validData);
+ if (value < 0.0) {
+ topLeft = topLeftPoint(category, barWidth, value + negativeSum);
+ if (domain()->type() == AbstractDomain::XLogYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ bottomRight = bottomRightPoint(category, barWidth,
+ set ? negativeSum : domain()->minY());
+ } else {
+ bottomRight = bottomRightPoint(category, barWidth, set ? negativeSum : 0.0);
+ }
negativeSum += value;
} else {
- topLeft = domain()->calculateGeometryPoint(QPointF(category - barWidth / 2, value + positiveSum), m_validData);
- if (domain()->type() == AbstractDomain::XLogYDomain || domain()->type() == AbstractDomain::LogXLogYDomain)
- bottomRight = domain()->calculateGeometryPoint(QPointF(category + barWidth / 2, set ? positiveSum : domain()->minY()), m_validData);
- else
- bottomRight = domain()->calculateGeometryPoint(QPointF(category + barWidth / 2, set ? positiveSum : 0), m_validData);
+ topLeft = topLeftPoint(category, barWidth, value + positiveSum);
+ if (domain()->type() == AbstractDomain::XLogYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ bottomRight = bottomRightPoint(category, barWidth,
+ set ? positiveSum : domain()->minY());
+ } else {
+ bottomRight = bottomRightPoint(category, barWidth, set ? positiveSum : 0.0);
+ }
positiveSum += value;
}
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- layout.append(rect.normalized());
+ rect = rect.normalized();
+ layout[bar->layoutIndex()] = rect;
+
+ // If animating, we need to reinitialize ~zero size bars with non-zero values
+ // so the bar growth animation starts at correct spot. We shouldn't reset if rect
+ // is already at correct position vertically, so we check for that.
+ if (m_animation && value != 0.0) {
+ const QRectF &checkRect = m_layout.at(bar->layoutIndex());
+ if (checkRect.isEmpty() &&
+ (value < 0.0 && !qFuzzyCompare(checkRect.top(), rect.top())
+ || value > 0.0 && !qFuzzyCompare(checkRect.bottom(), rect.bottom()))) {
+ initializeLayout(set, category, bar->layoutIndex(), true);
+ }
+ }
}
}
return layout;
diff --git a/src/charts/barchart/vertical/stacked/stackedbarchartitem_p.h b/src/charts/barchart/vertical/stacked/stackedbarchartitem_p.h
index 580741e6..03010e16 100644
--- a/src/charts/barchart/vertical/stacked/stackedbarchartitem_p.h
+++ b/src/charts/barchart/vertical/stacked/stackedbarchartitem_p.h
@@ -59,6 +59,8 @@ private Q_SLOTS:
private:
virtual QVector<QRectF> calculateLayout();
void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation);
+ QPointF topLeftPoint(int category, qreal barWidth, qreal value);
+ QPointF bottomRightPoint(int category, qreal barWidth, qreal value);
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/chartthememanager_p.h b/src/charts/chartthememanager_p.h
index cc24fa0c..0a7d2f81 100644
--- a/src/charts/chartthememanager_p.h
+++ b/src/charts/chartthememanager_p.h
@@ -65,6 +65,7 @@ public:
void decorateChart(QChart *chart, ChartTheme* theme) const;
void decorateLegend(QLegend *legend, ChartTheme* theme) const;
void updateSeries(QAbstractSeries *series);
+ QMap<QAbstractSeries *, int> seriesMap() const { return m_seriesMap; }
public:
static QList<QGradient> generateSeriesGradients(const QList<QColor>& colors);
diff --git a/src/charts/qchart.h b/src/charts/qchart.h
index cf2efb4f..225d296a 100644
--- a/src/charts/qchart.h
+++ b/src/charts/qchart.h
@@ -192,6 +192,7 @@ protected:
friend class QAbstractSeries;
friend class QBoxPlotSeriesPrivate;
friend class QCandlestickSeriesPrivate;
+ friend class AbstractBarChartItem;
private:
Q_DISABLE_COPY(QChart)
diff --git a/tests/manual/barcharttester/chart-widget.cpp b/tests/manual/barcharttester/chart-widget.cpp
index a2f15b42..6e7157b1 100644
--- a/tests/manual/barcharttester/chart-widget.cpp
+++ b/tests/manual/barcharttester/chart-widget.cpp
@@ -31,7 +31,7 @@
#include <QtCharts/QLineSeries>
#include <QtCharts/QLegend>
-#include <QtCharts/QCategoryAxis>
+#include <QtCharts/QBarCategoryAxis>
#include <QtCharts/QValueAxis>
#include <QtCharts/QLogValueAxis>
#include <QtCharts/QBarSet>
@@ -44,26 +44,39 @@
#include <QElapsedTimer>
#include <QDebug>
-const int initialCount = 20;
-const int visibleCount = 40;
+//#define VALUE_LOGGING 1
+
+const int initialCount = 100000;
+const int visibleCount = 20;
const int storeCount = 100000000;
-const int interval = 600;
+const int interval = 500;
+
+const int initialSeriesCount = 2;
+const int maxSeriesCount = 2;
+const bool removeSeries = false;
+const int extraSeriesFrequency = 7;
+
const int initialSetCount = 3;
const int maxSetCount = 3;
-const bool removeSets = true;
-const int extraSetFrequency = 30;
-const bool initialLabels = false;
+const bool removeSets = false;
+const int extraSetFrequency = 3;
+
+const bool initialLabels = true;
const int labelsTrigger = -1;
const int visibleTrigger = -1;
const int appendFrequency = 1;
const int animationTrigger = -1;
const bool sameNumberOfBars = false;
+
const bool animation = true;
-const int animationDuration = 500;
+const int animationDuration = 300;
const bool horizontal = false;
const bool percent = false;
-const bool stacked = false;
+const bool stacked = true;
+
+const bool negativeValues = false;
+const bool mixedValues = false;
// Negative indexes are counted from end of the set.
const bool doReplace = false;
@@ -77,20 +90,24 @@ const int replaceIndex = -3;
const int insertIndex = -5;
const bool logarithmic = false;
+const bool barCategories = false;
+
+const qreal barWidth = 0.9;
+
// Note: reverse axes are not fully supported for bars (animation and label positioning break a bit)
const bool reverseBar = false;
const bool reverseValue = false;
static int counter = 1;
-static const QString nameTemplate = QStringLiteral("Set %1");
+static const QString nameTemplate = QStringLiteral("Set %1/%2");
ChartWidget::ChartWidget(QWidget *parent) :
QWidget(parent),
m_chart(new QChart()),
m_chartView(new QChartView(this)),
- m_barAxis(new QValueAxis()),
- m_series(nullptr),
- m_setCount(initialSetCount)
+ m_setCount(initialSetCount),
+ m_seriesCount(initialSeriesCount),
+ m_extraScroll(0.0)
{
m_elapsedTimer.start();
@@ -102,23 +119,37 @@ ChartWidget::ChartWidget(QWidget *parent) :
m_valueAxis = new QValueAxis;
}
+ if (barCategories)
+ m_barAxis = new QBarCategoryAxis;
+ else
+ m_barAxis = new QValueAxis;
+
m_barAxis->setReverse(reverseBar);
m_valueAxis->setReverse(reverseValue);
- if (horizontal) {
- if (percent)
- m_series = new QHorizontalPercentBarSeries;
- else if (stacked)
- m_series = new QHorizontalStackedBarSeries;
- else
- m_series = new QHorizontalBarSeries;
- } else {
- if (percent)
- m_series = new QPercentBarSeries;
- else if (stacked)
- m_series = new QStackedBarSeries;
- else
- m_series = new QBarSeries;
+ for (int i = 0; i < maxSeriesCount; i++) {
+ if (horizontal) {
+ if (percent)
+ m_series.append(new QHorizontalPercentBarSeries);
+ else if (stacked)
+ m_series.append(new QHorizontalStackedBarSeries);
+ else
+ m_series.append(new QHorizontalBarSeries);
+ } else {
+ if (percent)
+ m_series.append(new QPercentBarSeries);
+ else if (stacked)
+ m_series.append(new QStackedBarSeries);
+ else
+ m_series.append(new QBarSeries);
+ }
+ QAbstractBarSeries *series =
+ qobject_cast<QAbstractBarSeries *>(m_series.at(m_series.size() - 1));
+ QString seriesNameTemplate = QStringLiteral("bar %1");
+ series->setName(seriesNameTemplate.arg(i));
+ series->setLabelsPosition(QAbstractBarSeries::LabelsInsideEnd);
+ series->setLabelsVisible(initialLabels);
+ series->setBarWidth(barWidth);
}
qsrand((uint) QTime::currentTime().msec());
@@ -155,50 +186,89 @@ void ChartWidget::handleTimeout()
bool doScroll = false;
if (counter % appendFrequency == 0) {
- for (int i = 0; i < maxSetCount; i++) {
- QBarSet *set = m_sets.at(i);
- qreal value = (counter % 100) / qreal(i+1);
- set->append(value);
- if (set->count() > storeCount)
- set->remove(5, set->count() - storeCount);
- if (set->count() > visibleCount)
- doScroll = true;
+ for (int i = 0; i < maxSeriesCount; i++) {
+ for (int j = 0; j < maxSetCount; j++) {
+ QBarSet *set = m_sets.value(m_series.at(i)).at(j);
+ qreal value = 5 * i + (counter % 100) / qreal(j+1);
+ if (negativeValues)
+ set->append(-value);
+ else if (mixedValues)
+ set->append(counter % 2 ? value : -value);
+ else
+ set->append(value);
+#ifdef VALUE_LOGGING
+ qDebug() << "Appended value:" << set->at(set->count() - 1)
+ << "to series:" << i
+ << "to set:" << j
+ << "at index:" << set->count() - 1;
+#endif
+ if (set->count() > storeCount)
+ set->remove(0, set->count() - storeCount);
+ if (set->count() > visibleCount)
+ doScroll = true;
+ }
}
qDebug() << "Append time:" << m_elapsedTimer.restart();
}
if (doScroll) {
doScroll = false;
- if (horizontal)
- m_chart->scroll(0, m_chart->plotArea().height() / visibleCount);
- else
- m_chart->scroll(m_chart->plotArea().width() / visibleCount, 0);
+ qreal scrollAmount = horizontal ? m_chart->plotArea().height() / visibleCount
+ : m_chart->plotArea().width() / visibleCount;
+ // Charts can't scroll without any series, so store the required scroll in those cases
+ if (m_seriesCount == 0) {
+ m_extraScroll += scrollAmount;
+ } else {
+ if (horizontal)
+ m_chart->scroll(0, scrollAmount + m_extraScroll);
+ else
+ m_chart->scroll(scrollAmount + m_extraScroll, 0);
+ m_extraScroll = 0.0;
+ }
qDebug() << "Scroll time:" << m_elapsedTimer.restart();
}
if (doRemove || doReplace || doInsert) {
- for (int i = 0; i < m_setCount; i++) {
- QBarSet *set = m_sets.at(i);
- qreal value = ((counter + 20) % 100) / qreal(i+1);
- if (doReplace && (!singleReplace || i == 0)) {
- int index = replaceIndex < 0 ? set->count() + replaceIndex : replaceIndex;
- set->replace(index, value);
- }
- if (doRemove && (!singleRemove || i == 0)) {
- int index = removeIndex < 0 ? set->count() + removeIndex : removeIndex;
- set->remove(index, 1);
- }
- if (doInsert && (!singleInsert || i == 0)) {
- int index = insertIndex < 0 ? set->count() + insertIndex : insertIndex;
- set->insert(index, value);
+ for (int i = 0; i < maxSeriesCount; i++) {
+ for (int j = 0; j < m_setCount; j++) {
+ QBarSet *set = m_sets.value(m_series.at(i)).at(j);
+ qreal value = ((counter + 20) % 100) / qreal(j + 1);
+ if (doReplace && (!singleReplace || j == 0)) {
+ int index = replaceIndex < 0 ? set->count() + replaceIndex : replaceIndex;
+ set->replace(index, value);
+ }
+ if (doRemove && (!singleRemove || j == 0)) {
+ int index = removeIndex < 0 ? set->count() + removeIndex : removeIndex;
+ set->remove(index, 1);
+ }
+ if (doInsert && (!singleInsert || j == 0)) {
+ int index = insertIndex < 0 ? set->count() + insertIndex : insertIndex;
+ set->insert(index, value);
+ }
}
}
qDebug() << "R/R time:" << m_elapsedTimer.restart();
}
-// if (counter % 5 == 0) {
-// m_sets.at(0)->setPen(QPen(QColor(counter % 255, counter % 255, counter % 255), 2));
-// }
+ if (counter % extraSeriesFrequency == 0) {
+ if (m_seriesCount <= maxSeriesCount) {
+ qDebug() << "Adjusting series count, current count:" << m_seriesCount;
+ static int seriesCountAdder = 1;
+ if (m_seriesCount == maxSeriesCount) {
+ if (removeSeries)
+ seriesCountAdder = -1;
+ else
+ seriesCountAdder = 0;
+ } else if (m_seriesCount == 0) {
+ seriesCountAdder = 1;
+ }
+ if (seriesCountAdder < 0)
+ m_chart->removeSeries(m_series.at(m_seriesCount - 1));
+ else if (m_seriesCount < maxSeriesCount)
+ addSeriesToChart(m_series.at(m_seriesCount));
+ m_seriesCount += seriesCountAdder;
+ }
+ }
if (counter % extraSetFrequency == 0) {
if (m_setCount <= maxSetCount) {
@@ -212,34 +282,43 @@ void ChartWidget::handleTimeout()
} else if (m_setCount == 0) {
setCountAdder = 1;
}
- if (setCountAdder < 0) {
- int barCount = m_sets.at(m_setCount - 1)->count();
- m_series->remove(m_sets.at(m_setCount - 1));
- // Since remove deletes the set, recreate it in our list
- QBarSet *set = new QBarSet(nameTemplate.arg(m_setCount - 1));
- m_sets[m_setCount - 1] = set;
- set->setLabelBrush(QColor("black"));
- set->setPen(QPen(QColor("black"), 0.3));
- QList<qreal> valueList;
- valueList.reserve(barCount);
- for (int j = 0; j < barCount; ++j)
- valueList.append(counter % 100);
- set->append(valueList);
-
- } else if (m_setCount < maxSetCount) {
- m_series->append(m_sets.at(m_setCount));
+ for (int i = 0; i < maxSeriesCount; i++) {
+ if (setCountAdder < 0) {
+ int barCount = m_sets.value(m_series.at(i)).at(m_setCount - 1)->count();
+ m_series.at(i)->remove(m_sets.value(m_series.at(i)).at(m_setCount - 1));
+ // Since remove deletes the set, recreate it in our list
+ QBarSet *set = new QBarSet(nameTemplate.arg(i).arg(m_setCount - 1));
+ m_sets[m_series.at(i)][m_setCount - 1] = set;
+ set->setLabelBrush(QColor("black"));
+ set->setPen(QPen(QColor("black"), 0.3));
+ QList<qreal> valueList;
+ valueList.reserve(barCount);
+ for (int j = 0; j < barCount; ++j) {
+ qreal value = counter % 100;
+ if (negativeValues)
+ valueList.append(-value);
+ else if (mixedValues)
+ valueList.append(counter % 2 ? value : -value);
+ else
+ valueList.append(value);
+ }
+ set->append(valueList);
+
+ } else if (m_setCount < maxSetCount) {
+ m_series.at(i)->append(m_sets.value(m_series.at(i)).at(m_setCount));
+ }
}
m_setCount += setCountAdder;
}
}
if (labelsTrigger > 0 && counter % labelsTrigger == 0) {
- m_series->setLabelsVisible(!m_series->isLabelsVisible());
+ m_series.at(0)->setLabelsVisible(!m_series.at(0)->isLabelsVisible());
qDebug() << "Label visibility changed";
}
if (visibleTrigger > 0 && counter % visibleTrigger == 0) {
- m_series->setVisible(!m_series->isVisible());
+ m_series.at(0)->setVisible(!m_series.at(0)->isVisible());
qDebug() << "Series visibility changed";
}
@@ -251,9 +330,9 @@ void ChartWidget::handleTimeout()
qDebug() << "Series animation changed";
}
- qDebug() << "Restof time:" << m_elapsedTimer.restart();
+ qDebug() << "Rest of time:" << m_elapsedTimer.restart();
- qDebug() << "Item Count:" << m_chart->scene()->items().size();
+ qDebug() << "GraphicsItem Count:" << m_chart->scene()->items().size();
counter++;
}
@@ -263,57 +342,85 @@ void ChartWidget::createChart()
QList<qreal> valueList;
valueList.reserve(initialCount);
- for (int j = 0; j < initialCount; ++j)
- valueList.append(counter++ % 100);
-
- for (int i = 0; i < maxSetCount; i++) {
- QBarSet *set = new QBarSet(nameTemplate.arg(i));
- m_sets.append(set);
- set->setLabelBrush(QColor("black"));
- set->setPen(QPen(QColor("black"), 0.3));
- if (sameNumberOfBars) {
- set->append(valueList);
- } else {
- QList<qreal> tempList = valueList;
- for (int j = 0; j < i; j++) {
- tempList.removeLast();
- tempList.removeLast();
- tempList.removeLast();
+ for (int j = 0; j < initialCount; ++j) {
+ qreal value = counter++ % 100;
+ if (negativeValues)
+ valueList.append(-value);
+ else if (mixedValues)
+ valueList.append(counter % 2 ? value : -value);
+ else
+ valueList.append(value);
+ }
+
+ for (int i = 0; i < maxSeriesCount; i++) {
+ for (int j = 0; j < maxSetCount; j++) {
+ QBarSet *set = new QBarSet(nameTemplate.arg(i).arg(j));
+ m_sets[m_series.at(i)].append(set);
+ set->setLabelBrush(QColor("black"));
+ set->setPen(QPen(QColor("black"), 0.3));
+ if (sameNumberOfBars) {
+ set->append(valueList);
+ } else {
+ QList<qreal> tempList = valueList;
+ for (int k = 0; k < j; k++)
+ tempList.removeLast();
+ set->append(tempList);
}
- set->append(tempList);
+ if (j < m_setCount)
+ m_series.at(i)->append(set);
}
}
+ for (int i = 0; i < initialSeriesCount; i++)
+ addSeriesToChart(m_series.at(i));
- m_series->setName("bar");
- for (int i = 0; i < initialSetCount; i++)
- m_series->append(m_sets.at(i));
-
- m_series->append(m_sets.at(0));
- m_series->setLabelsPosition(QAbstractBarSeries::LabelsInsideEnd);
- m_series->setLabelsVisible(initialLabels);
- m_series->setBarWidth(0.9);
-
- m_chart->addSeries(m_series);
m_chart->setTitle("Chart");
if (animation) {
m_chart->setAnimationOptions(QChart::SeriesAnimations);
m_chart->setAnimationDuration(animationDuration);
}
- if (horizontal) {
- m_chart->setAxisX(m_valueAxis, m_series);
- m_chart->setAxisY(m_barAxis, m_series);
+ if (barCategories) {
+ QBarCategoryAxis *barCatAxis = qobject_cast<QBarCategoryAxis *>(m_barAxis);
+ QStringList categories;
+ const int count = qMax(initialCount, visibleCount);
+ for (int i = 0; i < count; i++)
+ categories.append(QString::number(i));
+ barCatAxis->setCategories(categories);
} else {
- m_chart->setAxisX(m_barAxis, m_series);
- m_chart->setAxisY(m_valueAxis, m_series);
+ qobject_cast<QValueAxis *>(m_barAxis)->setTickCount(11);
}
- m_barAxis->setTickCount(11);
+
if (initialCount > visibleCount)
m_barAxis->setRange(initialCount - visibleCount, initialCount);
else
m_barAxis->setRange(0, visibleCount);
- m_valueAxis->setRange(logarithmic ? 1 : 0, stacked ? 200 : 100);
+ qreal rangeValue = stacked ? 200.0 : 100.0;
+ m_valueAxis->setRange(logarithmic ? 1.0 : (negativeValues || mixedValues) ? -rangeValue : 0.0,
+ (!negativeValues || mixedValues) ? rangeValue : 0.0);
m_chartView->setChart(m_chart);
}
+
+void ChartWidget::addSeriesToChart(QAbstractBarSeries *series)
+{
+ qDebug() << "Adding series:" << series->name();
+
+ // HACK: Temporarily take the sets out of the series until axes are set.
+ // This is done because added series defaults to a domain that displays all bars, which can
+ // get extremely slow as the bar count increases.
+ QList<QBarSet *> sets = series->barSets();
+ for (auto set : sets)
+ series->take(set);
+
+ m_chart->addSeries(series);
+ if (horizontal) {
+ m_chart->setAxisX(m_valueAxis,series);
+ m_chart->setAxisY(m_barAxis, series);
+ } else {
+ m_chart->setAxisX(m_barAxis, series);
+ m_chart->setAxisY(m_valueAxis, series);
+ }
+
+ series->append(sets);
+}
diff --git a/tests/manual/barcharttester/chart-widget.h b/tests/manual/barcharttester/chart-widget.h
index 989c48a9..0b788172 100644
--- a/tests/manual/barcharttester/chart-widget.h
+++ b/tests/manual/barcharttester/chart-widget.h
@@ -55,18 +55,21 @@ public slots:
private:
void createChart();
+ void addSeriesToChart(QAbstractBarSeries *series);
private:
QChart *m_chart;
QChartView *m_chartView;
- QValueAxis *m_barAxis;
+ QAbstractAxis *m_barAxis;
QAbstractAxis *m_valueAxis;
- QAbstractBarSeries *m_series;
- QVector<QBarSet *> m_sets;
+ QVector<QAbstractBarSeries *> m_series;
+ QMap<const QAbstractBarSeries *, QVector<QBarSet *> > m_sets;
QTimer m_timer;
QElapsedTimer m_elapsedTimer;
QHBoxLayout *m_horizontalLayout;
int m_setCount;
+ int m_seriesCount;
+ qreal m_extraScroll;
};
#endif // CHARTWIDGET_H