summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--examples/charts/logvalueaxis/main.cpp27
-rw-r--r--examples/charts/scatterchart/chartview.cpp11
-rw-r--r--src/charts/axis/cartesianchartaxis.cpp82
-rw-r--r--src/charts/axis/horizontalaxis.cpp232
-rw-r--r--src/charts/axis/horizontalaxis_p.h16
-rw-r--r--src/charts/axis/logvalueaxis/chartlogvalueaxisx.cpp28
-rw-r--r--src/charts/axis/logvalueaxis/chartlogvalueaxisy.cpp27
-rw-r--r--src/charts/axis/logvalueaxis/polarchartlogvalueaxisangular.cpp32
-rw-r--r--src/charts/axis/logvalueaxis/polarchartlogvalueaxisradial.cpp32
-rw-r--r--src/charts/axis/logvalueaxis/qlogvalueaxis.cpp142
-rw-r--r--src/charts/axis/logvalueaxis/qlogvalueaxis.h9
-rw-r--r--src/charts/axis/logvalueaxis/qlogvalueaxis_p.h9
-rw-r--r--src/charts/axis/polarchartaxis.cpp5
-rw-r--r--src/charts/axis/polarchartaxisangular.cpp226
-rw-r--r--src/charts/axis/polarchartaxisangular_p.h4
-rw-r--r--src/charts/axis/polarchartaxisradial.cpp220
-rw-r--r--src/charts/axis/polarchartaxisradial_p.h6
-rw-r--r--src/charts/axis/verticalaxis.cpp242
-rw-r--r--src/charts/axis/verticalaxis_p.h16
-rw-r--r--src/charts/barchart/abstractbarchartitem.cpp667
-rw-r--r--src/charts/barchart/abstractbarchartitem_p.h37
-rw-r--r--src/charts/barchart/bar.cpp11
-rw-r--r--src/charts/barchart/bar_p.h20
-rw-r--r--src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp98
-rw-r--r--src/charts/barchart/horizontal/bar/horizontalbarchartitem_p.h4
-rw-r--r--src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem.cpp183
-rw-r--r--src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h7
-rw-r--r--src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem.cpp143
-rw-r--r--src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem_p.h4
-rw-r--r--src/charts/barchart/qabstractbarseries.cpp142
-rw-r--r--src/charts/barchart/qabstractbarseries_p.h14
-rw-r--r--src/charts/barchart/qbarset.cpp33
-rw-r--r--src/charts/barchart/qbarset_p.h13
-rw-r--r--src/charts/barchart/vertical/bar/barchartitem.cpp103
-rw-r--r--src/charts/barchart/vertical/bar/barchartitem_p.h5
-rw-r--r--src/charts/barchart/vertical/percent/percentbarchartitem.cpp187
-rw-r--r--src/charts/barchart/vertical/percent/percentbarchartitem_p.h7
-rw-r--r--src/charts/barchart/vertical/stacked/stackedbarchartitem.cpp149
-rw-r--r--src/charts/barchart/vertical/stacked/stackedbarchartitem_p.h5
-rw-r--r--src/charts/chartdataset.cpp10
-rw-r--r--src/charts/chartdataset_p.h1
-rw-r--r--src/charts/chartthememanager_p.h1
-rw-r--r--src/charts/doc/images/examples_scatterchart.pngbin30702 -> 25298 bytes
-rw-r--r--src/charts/doc/src/examples-scatterchart.qdoc3
-rw-r--r--src/charts/layout/abstractchartlayout.cpp21
-rw-r--r--src/charts/layout/abstractchartlayout_p.h1
-rw-r--r--src/charts/legend/legendmarkeritem.cpp198
-rw-r--r--src/charts/legend/legendmarkeritem_p.h32
-rw-r--r--src/charts/legend/qlegend.cpp72
-rw-r--r--src/charts/legend/qlegend.h14
-rw-r--r--src/charts/legend/qlegend_p.h2
-rw-r--r--src/charts/legend/qlegendmarker.cpp38
-rw-r--r--src/charts/legend/qlegendmarker.h6
-rw-r--r--src/charts/legend/qlegendmarker_p.h2
-rw-r--r--src/charts/legend/qxylegendmarker.cpp16
-rw-r--r--src/charts/qchart.h1
-rw-r--r--tests/auto/chartdataset/tst_chartdataset.cpp94
-rw-r--r--tests/auto/qabstractaxis/tst_qabstractaxis.cpp1
-rw-r--r--tests/auto/qbarmodelmapper/tst_qbarmodelmapper.cpp1
-rw-r--r--tests/auto/qcandlestickmodelmapper/tst_qcandlestickmodelmapper.cpp1
-rw-r--r--tests/auto/qchart/tst_qchart.cpp1
-rw-r--r--tests/auto/qchartview/tst_qchartview.cpp1
-rw-r--r--tests/auto/qdatetimeaxis/tst_qdatetimeaxis.cpp1
-rw-r--r--tests/auto/qhorizontalpercentbarseries/tst_qhorizontalpercentbarseries.cpp1
-rw-r--r--tests/auto/qlineseries/tst_qlineseries.cpp3
-rw-r--r--tests/auto/qlogvalueaxis/tst_qlogvalueaxis.cpp17
-rw-r--r--tests/auto/qpercentbarseries/tst_qpercentbarseries.cpp1
-rw-r--r--tests/auto/qpiemodelmapper/tst_qpiemodelmapper.cpp1
-rw-r--r--tests/auto/qpieseries/tst_qpieseries.cpp1
-rw-r--r--tests/auto/qpieslice/tst_qpieslice.cpp5
-rw-r--r--tests/auto/qscatterseries/tst_qscatterseries.cpp3
-rw-r--r--tests/auto/qsplineseries/tst_qsplineseries.cpp3
-rw-r--r--tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp1
-rw-r--r--tests/auto/qxyseries/tst_qxyseries.cpp1
-rw-r--r--tests/manual/barcharttester/barcharttester.pro9
-rw-r--r--tests/manual/barcharttester/chart-widget.cpp426
-rw-r--r--tests/manual/barcharttester/chart-widget.h75
-rw-r--r--tests/manual/barcharttester/main.cpp42
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/manual/qmlchartproperties/qml/qmlchartproperties/ChartEditorLegend.qml12
81 files changed, 3268 insertions, 1054 deletions
diff --git a/.qmake.conf b/.qmake.conf
index fec66b73..b1c22d3b 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,3 +1,3 @@
load(qt_build_config)
-MODULE_VERSION = 5.8.1
+MODULE_VERSION = 5.9.0
diff --git a/examples/charts/logvalueaxis/main.cpp b/examples/charts/logvalueaxis/main.cpp
index 8d3d618e..ae5d92fc 100644
--- a/examples/charts/logvalueaxis/main.cpp
+++ b/examples/charts/logvalueaxis/main.cpp
@@ -27,22 +27,23 @@
**
****************************************************************************/
-#include <QtWidgets/QApplication>
-#include <QtWidgets/QMainWindow>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
-#include <QtCharts/QValueAxis>
#include <QtCharts/QLogValueAxis>
+#include <QtCharts/QValueAxis>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QMainWindow>
QT_CHARTS_USE_NAMESPACE
int main(int argc, char *argv[])
{
- QApplication a(argc, argv);
+ QApplication app(argc, argv);
//![1]
QLineSeries *series = new QLineSeries();
- *series << QPointF(1, 1) << QPointF(2, 73) << QPointF(3, 268) << QPointF(4, 17) << QPointF(5, 4325) << QPointF(6, 723);
+ *series << QPointF(1.0, 1.0) << QPointF(2.0, 73.0) << QPointF(3.0, 268.0) << QPointF(4.0, 17.0)
+ << QPointF(5.0, 4325.0) << QPointF(6.0, 723.0);
//![1]
//![2]
@@ -53,17 +54,18 @@ int main(int argc, char *argv[])
//![2]
//![3]
- QValueAxis *axisX = new QValueAxis;
+ QValueAxis *axisX = new QValueAxis();
axisX->setTitleText("Data point");
- axisX->setTickCount(6);
axisX->setLabelFormat("%i");
+ axisX->setTickCount(series->count());
chart->addAxis(axisX, Qt::AlignBottom);
series->attachAxis(axisX);
- QLogValueAxis *axisY = new QLogValueAxis;
- axisY->setLabelFormat("%g");
+ QLogValueAxis *axisY = new QLogValueAxis();
axisY->setTitleText("Values");
- axisY->setBase(8);
+ axisY->setLabelFormat("%g");
+ axisY->setBase(8.0);
+ axisY->setMinorTickCount(-1);
chart->addAxis(axisY, Qt::AlignLeft);
series->attachAxis(axisY);
//![3]
@@ -76,10 +78,9 @@ int main(int argc, char *argv[])
//![5]
QMainWindow window;
window.setCentralWidget(chartView);
- window.resize(400, 300);
+ window.resize(800, 600);
window.show();
-
//![5]
- return a.exec();
+ return app.exec();
}
diff --git a/examples/charts/scatterchart/chartview.cpp b/examples/charts/scatterchart/chartview.cpp
index 3a6c9240..c2f099c1 100644
--- a/examples/charts/scatterchart/chartview.cpp
+++ b/examples/charts/scatterchart/chartview.cpp
@@ -69,10 +69,10 @@ ChartView::ChartView(QWidget *parent) :
//![3]
QPainterPath starPath;
- starPath.moveTo(30, 15);
+ starPath.moveTo(28, 15);
for (int i = 1; i < 5; ++i) {
- starPath.lineTo(15 + 15 * qCos(0.8 * i * Pi),
- 15 + 15 * qSin(0.8 * i * Pi));
+ starPath.lineTo(14 + 14 * qCos(0.8 * i * Pi),
+ 15 + 14 * qSin(0.8 * i * Pi));
}
starPath.closeSubpath();
@@ -86,6 +86,7 @@ ChartView::ChartView(QWidget *parent) :
painter.drawPath(starPath);
series2->setBrush(star);
+ series2->setPen(QColor(Qt::transparent));
//![3]
//![4]
@@ -100,8 +101,6 @@ ChartView::ChartView(QWidget *parent) :
//![4]
//![5]
- QList<QLegendMarker *> markers = chart()->legend()->markers(series2);
- for (int i = 0; i < markers.count(); i++)
- markers.at(i)->setBrush(painter.pen().color());
+ chart()->legend()->setMarkerShape(QLegend::MarkerShapeFromSeries);
//![5]
}
diff --git a/src/charts/axis/cartesianchartaxis.cpp b/src/charts/axis/cartesianchartaxis.cpp
index 9e815922..0dcb4860 100644
--- a/src/charts/axis/cartesianchartaxis.cpp
+++ b/src/charts/axis/cartesianchartaxis.cpp
@@ -27,17 +27,18 @@
**
****************************************************************************/
-#include <private/cartesianchartaxis_p.h>
-#include <QtCharts/QAbstractAxis>
-#include <private/qabstractaxis_p.h>
-#include <private/chartpresenter_p.h>
+#include <QtCharts/qabstractaxis.h>
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCharts/qvalueaxis.h>
+#include <QtCore/qmath.h>
+#include <QtGui/qtextdocument.h>
+#include <QtWidgets/qgraphicslayout.h>
#include <private/abstractchartlayout_p.h>
#include <private/abstractdomain_p.h>
+#include <private/cartesianchartaxis_p.h>
+#include <private/chartpresenter_p.h>
#include <private/linearrowitem_p.h>
-#include <QtCharts/QValueAxis>
-#include <QtCharts/QLogValueAxis>
-#include <QtWidgets/QGraphicsLayout>
-#include <QtGui/QTextDocument>
+#include <private/qabstractaxis_p.h>
QT_CHARTS_BEGIN_NAMESPACE
@@ -102,29 +103,46 @@ void CartesianChartAxis::createItems(int count)
void CartesianChartAxis::updateMinorTickItems()
{
- QValueAxis *valueAxis = qobject_cast<QValueAxis *>(this->axis());
- if (valueAxis) {
- int currentCount = minorArrowItems().size();
- int expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
- int diff = expectedCount - currentCount;
- if (diff > 0) {
- for (int i = 0; i < diff; i++) {
- QGraphicsLineItem *minorArrow = new QGraphicsLineItem(this);
- QGraphicsLineItem *minorGrid = new QGraphicsLineItem(this);
- minorArrow->setPen(valueAxis->linePen());
- minorGrid->setPen(valueAxis->minorGridLinePen());
- minorArrowGroup()->addToGroup(minorArrow);
- minorGridGroup()->addToGroup(minorGrid);
- }
- } else {
- QList<QGraphicsItem *> minorGridLines = minorGridItems();
- QList<QGraphicsItem *> minorArrows = minorArrowItems();
- for (int i = 0; i > diff; i--) {
- if (!minorGridLines.isEmpty())
- delete(minorGridLines.takeLast());
- if (!minorArrows.isEmpty())
- delete(minorArrows.takeLast());
- }
+ int currentCount = minorArrowItems().size();
+ int expectedCount = 0;
+ if (axis()->type() == QAbstractAxis::AxisTypeValue) {
+ QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
+ expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
+ expectedCount = qMax(expectedCount, 0);
+ } else if (axis()->type() == QAbstractAxis::AxisTypeLogValue) {
+ QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+
+ int minorTickCount = logValueAxis->minorTickCount();
+ if (minorTickCount < 0)
+ minorTickCount = qMax(int(qFloor(logValueAxis->base()) - 2.0), 0);
+
+ expectedCount = minorTickCount * (logValueAxis->tickCount() + 1);
+ expectedCount = qMax(expectedCount, logValueAxis->minorTickCount());
+ } else {
+ // minor ticks are not supported
+ return;
+ }
+
+ int diff = expectedCount - currentCount;
+ if (diff > 0) {
+ for (int i = 0; i < diff; ++i) {
+ QGraphicsLineItem *minorGridLineItem = new QGraphicsLineItem(this);
+ minorGridLineItem->setPen(axis()->minorGridLinePen());
+ minorGridGroup()->addToGroup(minorGridLineItem);
+
+ QGraphicsLineItem *minorArrowLineItem = new QGraphicsLineItem(this);
+ minorArrowLineItem->setPen(axis()->linePen());
+ minorArrowGroup()->addToGroup(minorArrowLineItem);
+ }
+ } else {
+ QList<QGraphicsItem *> minorGridItemsList = minorGridItems();
+ QList<QGraphicsItem *> minorArrowItemsList = minorArrowItems();
+ for (int i = 0; i > diff; --i) {
+ if (!minorGridItemsList.isEmpty())
+ delete minorGridItemsList.takeLast();
+
+ if (!minorArrowItemsList.isEmpty())
+ delete minorArrowItemsList.takeLast();
}
}
}
@@ -151,7 +169,7 @@ void CartesianChartAxis::updateLayout(QVector<qreal> &layout)
if (diff > 0)
deleteItems(diff);
- else if (diff < 0)
+ else if (diff <= 0)
createItems(-diff);
updateMinorTickItems();
diff --git a/src/charts/axis/horizontalaxis.cpp b/src/charts/axis/horizontalaxis.cpp
index ba81175a..e8455703 100644
--- a/src/charts/axis/horizontalaxis.cpp
+++ b/src/charts/axis/horizontalaxis.cpp
@@ -27,12 +27,11 @@
**
****************************************************************************/
-#include <private/horizontalaxis_p.h>
-#include <private/qabstractaxis_p.h>
+#include <QtCharts/qcategoryaxis.h>
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCore/qmath.h>
#include <private/chartpresenter_p.h>
-#include <QtCharts/QCategoryAxis>
-#include <QtCore/QtMath>
-#include <QtCore/QDebug>
+#include <private/horizontalaxis_p.h>
QT_CHARTS_BEGIN_NAMESPACE
@@ -45,6 +44,34 @@ HorizontalAxis::~HorizontalAxis()
{
}
+QSizeF HorizontalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_UNUSED(constraint);
+ QSizeF sh(0,0);
+
+ if (axis()->titleText().isEmpty() || !titleItem()->isVisible())
+ return sh;
+
+ switch (which) {
+ case Qt::MinimumSize: {
+ QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(),
+ QStringLiteral("..."));
+ sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2.0));
+ break;
+ }
+ case Qt::MaximumSize:
+ case Qt::PreferredSize: {
+ QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
+ sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2.0));
+ break;
+ }
+ default:
+ break;
+ }
+
+ return sh;
+}
+
void HorizontalAxis::updateGeometry()
{
const QVector<qreal> &layout = ChartAxisElement::layout();
@@ -100,13 +127,8 @@ void HorizontalAxis::updateGeometry()
availableSpace -= titleBoundingRect.height();
}
- if (layout.isEmpty() && axis()->type() == QAbstractAxis::AxisTypeLogValue)
- return;
-
QList<QGraphicsItem *> lines = gridItems();
QList<QGraphicsItem *> shades = shadeItems();
- QList<QGraphicsItem *> minorLines = minorGridItems();
- QList<QGraphicsItem *> minorArrows = minorArrowItems();
for (int i = 0; i < layout.size(); ++i) {
//items
@@ -286,61 +308,15 @@ void HorizontalAxis::updateGeometry()
}
// check if the grid line and the axis tick should be shown
- qreal x = gridItem->line().p1().x();
- if (x < gridRect.left() || x > gridRect.right()) {
- gridItem->setVisible(false);
- tickItem->setVisible(false);
- } else {
- gridItem->setVisible(true);
- tickItem->setVisible(true);
- }
-
- // add minor ticks
- QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
- if ((i + 1) != layout.size() && valueAxis) {
- int minorTickCount = valueAxis->minorTickCount();
- if (minorTickCount != 0) {
- qreal minorTickDistance = (layout[i] - layout[i + 1]) / qreal(minorTickCount + 1);
- if (axis()->isReverse())
- minorTickDistance = -minorTickDistance;
- for (int j = 0; j < minorTickCount; j++) {
- QGraphicsLineItem *minorGridItem =
- static_cast<QGraphicsLineItem *>(minorLines.at(i * minorTickCount + j));
- QGraphicsLineItem *minorArrowItem =
- static_cast<QGraphicsLineItem *>(minorArrows.at(i * minorTickCount + j));
- minorGridItem->setLine(gridItem->line().p1().x()
- - minorTickDistance * qreal(j + 1),
- gridRect.top(),
- gridItem->line().p1().x()
- - minorTickDistance * qreal(j + 1),
- gridRect.bottom());
- if (axis()->alignment() == Qt::AlignTop) {
- minorArrowItem->setLine(minorGridItem->line().p1().x(),
- axisRect.bottom(),
- minorGridItem->line().p1().x(),
- axisRect.bottom() - labelPadding() / 2);
- } else if (axis()->alignment() == Qt::AlignBottom){
- minorArrowItem->setLine(minorGridItem->line().p1().x(),
- axisRect.top(),
- minorGridItem->line().p1().x(),
- axisRect.top() + labelPadding() / 2);
- }
-
- // check if the minor grid line and the axis tick should be shown
- qreal minorXPos = minorGridItem->line().p1().x();
- if (minorXPos < gridRect.left() || minorXPos > gridRect.right()) {
- minorGridItem->setVisible(false);
- minorArrowItem->setVisible(false);
- } else {
- minorGridItem->setVisible(true);
- minorArrowItem->setVisible(true);
- }
- }
- }
- }
+ const bool gridLineVisible = (gridItem->line().p1().x() >= gridRect.left()
+ && gridItem->line().p1().x() <= gridRect.right());
+ gridItem->setVisible(gridLineVisible);
+ tickItem->setVisible(gridLineVisible);
}
- //begin/end grid line in case labels between
+ updateMinorTickGeometry();
+
+ // begin/end grid line in case labels between
if (intervalAxis()) {
QGraphicsLineItem *gridLine;
gridLine = static_cast<QGraphicsLineItem *>(lines.at(layout.size()));
@@ -352,32 +328,132 @@ void HorizontalAxis::updateGeometry()
}
}
-QSizeF HorizontalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+void HorizontalAxis::updateMinorTickGeometry()
{
- Q_UNUSED(constraint);
- QSizeF sh(0,0);
+ if (!axis())
+ return;
- if (axis()->titleText().isEmpty() || !titleItem()->isVisible())
- return sh;
+ QVector<qreal> layout = ChartAxisElement::layout();
+ int minorTickCount = 0;
+ qreal tickSpacing = 0.0;
+ QVector<qreal> minorTickSpacings;
+ switch (axis()->type()) {
+ case QAbstractAxis::AxisTypeValue: {
+ const QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
- switch (which) {
- case Qt::MinimumSize: {
- QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(),
- QStringLiteral("..."));
- sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2.0));
+ minorTickCount = valueAxis->minorTickCount();
+
+ if (valueAxis->tickCount() >= 2)
+ tickSpacing = layout.at(0) - layout.at(1);
+
+ for (int i = 0; i < minorTickCount; ++i) {
+ const qreal ratio = (1.0 / qreal(minorTickCount + 1)) * qreal(i + 1);
+ minorTickSpacings.append(tickSpacing * ratio);
+ }
break;
}
- case Qt::MaximumSize:
- case Qt::PreferredSize: {
- QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
- sh = QSizeF(titleRect.width(), titleRect.height() + (titlePadding() * 2.0));
+ case QAbstractAxis::AxisTypeLogValue: {
+ const QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+ const qreal base = logValueAxis->base();
+ const qreal logBase = qLn(base);
+
+ minorTickCount = logValueAxis->minorTickCount();
+ if (minorTickCount < 0)
+ minorTickCount = qMax(int(qFloor(base) - 2.0), 0);
+
+ // Two "virtual" ticks are required to make sure that all minor ticks
+ // are displayed properly (even for the partially visible segments of
+ // the chart).
+ if (layout.size() >= 2) {
+ // Calculate tickSpacing as a difference between visible ticks
+ // whenever it is possible. Virtual ticks will not be correctly
+ // positioned when the layout is animating.
+ tickSpacing = layout.at(0) - layout.at(1);
+ layout.prepend(layout.at(0) + tickSpacing);
+ layout.append(layout.at(layout.size() - 1) - tickSpacing);
+ } else {
+ const qreal logMax = qLn(logValueAxis->max());
+ const qreal logMin = qLn(logValueAxis->min());
+ const qreal logExtraMaxTick = qLn(qPow(base, qFloor(logMax / logBase) + 1.0));
+ const qreal logExtraMinTick = qLn(qPow(base, qCeil(logMin / logBase) - 1.0));
+ const qreal edge = gridGeometry().left();
+ const qreal delta = gridGeometry().width() / qAbs(logMax - logMin);
+ const qreal extraMaxTick = edge + (logExtraMaxTick - qMin(logMin, logMax)) * delta;
+ const qreal extraMinTick = edge + (logExtraMinTick - qMin(logMin, logMax)) * delta;
+
+ // Calculate tickSpacing using one (if layout.size() == 1) or two
+ // (if layout.size() == 0) "virtual" ticks. In both cases animation
+ // will not work as expected. This should be fixed later.
+ layout.prepend(extraMinTick);
+ layout.append(extraMaxTick);
+ tickSpacing = layout.at(0) - layout.at(1);
+ }
+
+ const qreal minorTickStepValue = qFabs(base - 1.0) / qreal(minorTickCount + 1);
+ for (int i = 0; i < minorTickCount; ++i) {
+ const qreal x = minorTickStepValue * qreal(i + 1) + 1.0;
+ const qreal minorTickSpacing = tickSpacing * (qLn(x) / logBase);
+ minorTickSpacings.append(minorTickSpacing);
+ }
break;
}
default:
+ // minor ticks are not supported
break;
}
- return sh;
+ if (minorTickCount < 1 || tickSpacing == 0.0 || minorTickSpacings.count() != minorTickCount)
+ return;
+
+ for (int i = 0; i < layout.size() - 1; ++i) {
+ for (int j = 0; j < minorTickCount; ++j) {
+ const int minorItemIndex = i * minorTickCount + j;
+ QGraphicsLineItem *minorGridLineItem =
+ static_cast<QGraphicsLineItem *>(minorGridItems().value(minorItemIndex));
+ QGraphicsLineItem *minorArrowLineItem =
+ static_cast<QGraphicsLineItem *>(minorArrowItems().value(minorItemIndex));
+ if (!minorGridLineItem || !minorArrowLineItem)
+ continue;
+
+ const qreal minorTickSpacing = minorTickSpacings.value(j, 0.0);
+
+ qreal minorGridLineItemX = 0.0;
+ if (axis()->isReverse()) {
+ minorGridLineItemX = qFloor(gridGeometry().left() + gridGeometry().right()
+ - layout.at(i) + minorTickSpacing);
+ } else {
+ minorGridLineItemX = qCeil(layout.at(i) - minorTickSpacing);
+ }
+
+ qreal minorArrowLineItemY1;
+ qreal minorArrowLineItemY2;
+ switch (axis()->alignment()) {
+ case Qt::AlignTop:
+ minorArrowLineItemY1 = gridGeometry().bottom();
+ minorArrowLineItemY2 = gridGeometry().bottom() - labelPadding() / 2.0;
+ break;
+ case Qt::AlignBottom:
+ minorArrowLineItemY1 = gridGeometry().top();
+ minorArrowLineItemY2 = gridGeometry().top() + labelPadding() / 2.0;
+ break;
+ default:
+ minorArrowLineItemY1 = 0.0;
+ minorArrowLineItemY2 = 0.0;
+ break;
+ }
+
+ minorGridLineItem->setLine(minorGridLineItemX, gridGeometry().top(),
+ minorGridLineItemX, gridGeometry().bottom());
+ minorArrowLineItem->setLine(minorGridLineItemX, minorArrowLineItemY1,
+ minorGridLineItemX, minorArrowLineItemY2);
+
+ // check if the minor grid line and the minor axis arrow should be shown
+ bool minorGridLineVisible = (minorGridLineItemX >= gridGeometry().left()
+ && minorGridLineItemX <= gridGeometry().right());
+ minorGridLineItem->setVisible(minorGridLineVisible);
+ minorArrowLineItem->setVisible(minorGridLineVisible);
+ }
+ }
}
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/axis/horizontalaxis_p.h b/src/charts/axis/horizontalaxis_p.h
index 5b7d7701..1fe0c405 100644
--- a/src/charts/axis/horizontalaxis_p.h
+++ b/src/charts/axis/horizontalaxis_p.h
@@ -36,8 +36,8 @@
//
// We mean it.
-#ifndef HORIZONTALAXIS_P_H_
-#define HORIZONTALAXIS_P_H_
+#ifndef HORIZONTALAXIS_P_H
+#define HORIZONTALAXIS_P_H
#include <private/cartesianchartaxis_p.h>
@@ -46,14 +46,18 @@ QT_CHARTS_BEGIN_NAMESPACE
class HorizontalAxis : public CartesianChartAxis
{
public:
- HorizontalAxis(QAbstractAxis *axis, QGraphicsItem *item = 0, bool intervalAxis = false);
+ HorizontalAxis(QAbstractAxis *axis, QGraphicsItem *item = nullptr, bool intervalAxis = false);
~HorizontalAxis();
- QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const Q_DECL_OVERRIDE;
+
protected:
- void updateGeometry();
+ void updateGeometry() Q_DECL_OVERRIDE;
+private:
+ void updateMinorTickGeometry();
};
QT_CHARTS_END_NAMESPACE
-#endif
+#endif // HORIZONTALAXIS_P_H
diff --git a/src/charts/axis/logvalueaxis/chartlogvalueaxisx.cpp b/src/charts/axis/logvalueaxis/chartlogvalueaxisx.cpp
index 2e4641c3..4b33cc9c 100644
--- a/src/charts/axis/logvalueaxis/chartlogvalueaxisx.cpp
+++ b/src/charts/axis/logvalueaxis/chartlogvalueaxisx.cpp
@@ -27,14 +27,12 @@
**
****************************************************************************/
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCore/qmath.h>
+#include <QtWidgets/qgraphicslayout.h>
+#include <private/abstractchartlayout_p.h>
#include <private/chartlogvalueaxisx_p.h>
#include <private/chartpresenter_p.h>
-#include <QtCharts/QLogValueAxis>
-#include <private/abstractchartlayout_p.h>
-#include <QtWidgets/QGraphicsLayout>
-#include <QtCore/QtMath>
-#include <QtCore/QDebug>
-#include <cmath>
QT_CHARTS_BEGIN_NAMESPACE
@@ -53,22 +51,16 @@ ChartLogValueAxisX::~ChartLogValueAxisX()
QVector<qreal> ChartLogValueAxisX::calculateLayout() const
{
QVector<qreal> points;
+ points.resize(m_axis->tickCount());
- qreal logMax = std::log10(m_axis->max()) / std::log10(m_axis->base());
- qreal logMin = std::log10(m_axis->min()) / std::log10(m_axis->base());
- qreal leftEdge = logMin < logMax ? logMin : logMax;
- qreal ceilEdge = qCeil(leftEdge);
- int tickCount = qAbs(qCeil(logMax) - qCeil(logMin));
-
- // If the high edge sits exactly on the tick value, add a tick
- qreal highValue = logMin < logMax ? logMax : logMin;
- if (qFuzzyCompare(highValue, qreal(qCeil(highValue))))
- tickCount++;
+ const qreal logMax = std::log10(m_axis->max()) / std::log10(m_axis->base());
+ const qreal logMin = std::log10(m_axis->min()) / std::log10(m_axis->base());
+ const qreal leftEdge = qMin(logMin, logMax);
+ const qreal ceilEdge = qCeil(leftEdge);
- points.resize(tickCount);
const QRectF &gridRect = gridGeometry();
const qreal deltaX = gridRect.width() / qAbs(logMax - logMin);
- for (int i = 0; i < tickCount; ++i)
+ for (int i = 0; i < m_axis->tickCount(); ++i)
points[i] = (ceilEdge + qreal(i)) * deltaX - leftEdge * deltaX + gridRect.left();
return points;
diff --git a/src/charts/axis/logvalueaxis/chartlogvalueaxisy.cpp b/src/charts/axis/logvalueaxis/chartlogvalueaxisy.cpp
index 3884a81c..4eea7035 100644
--- a/src/charts/axis/logvalueaxis/chartlogvalueaxisy.cpp
+++ b/src/charts/axis/logvalueaxis/chartlogvalueaxisy.cpp
@@ -27,14 +27,12 @@
**
****************************************************************************/
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCore/qmath.h>
+#include <QtWidgets/qgraphicslayout.h>
+#include <private/abstractchartlayout_p.h>
#include <private/chartlogvalueaxisy_p.h>
#include <private/chartpresenter_p.h>
-#include <QtCharts/QLogValueAxis>
-#include <private/abstractchartlayout_p.h>
-#include <QtWidgets/QGraphicsLayout>
-#include <QtCore/QtMath>
-#include <QtCore/QDebug>
-#include <cmath>
QT_CHARTS_BEGIN_NAMESPACE
@@ -53,21 +51,16 @@ ChartLogValueAxisY::~ChartLogValueAxisY()
QVector<qreal> ChartLogValueAxisY::calculateLayout() const
{
QVector<qreal> points;
- qreal logMax = std::log10(m_axis->max()) / std::log10(m_axis->base());
- qreal logMin = std::log10(m_axis->min()) / std::log10(m_axis->base());
- qreal leftEdge = logMin < logMax ? logMin : logMax;
- qreal ceilEdge = qCeil(leftEdge);
- int tickCount = qAbs(qCeil(logMax) - qCeil(logMin));
+ points.resize(m_axis->tickCount());
- // If the high edge sits exactly on the tick value, add a tick
- qreal highValue = logMin < logMax ? logMax : logMin;
- if (qFuzzyCompare(highValue, qreal(qCeil(highValue))))
- tickCount++;
+ const qreal logMax = std::log10(m_axis->max()) / std::log10(m_axis->base());
+ const qreal logMin = std::log10(m_axis->min()) / std::log10(m_axis->base());
+ const qreal leftEdge = qMin(logMin, logMax);
+ const qreal ceilEdge = qCeil(leftEdge);
- points.resize(tickCount);
const QRectF &gridRect = gridGeometry();
const qreal deltaY = gridRect.height() / qAbs(logMax - logMin);
- for (int i = 0; i < tickCount; ++i)
+ for (int i = 0; i < m_axis->tickCount(); ++i)
points[i] = (ceilEdge + qreal(i)) * -deltaY - leftEdge * -deltaY + gridRect.bottom();
return points;
diff --git a/src/charts/axis/logvalueaxis/polarchartlogvalueaxisangular.cpp b/src/charts/axis/logvalueaxis/polarchartlogvalueaxisangular.cpp
index 7cc21090..2312b809 100644
--- a/src/charts/axis/logvalueaxis/polarchartlogvalueaxisangular.cpp
+++ b/src/charts/axis/logvalueaxis/polarchartlogvalueaxisangular.cpp
@@ -27,21 +27,21 @@
**
****************************************************************************/
-#include <private/polarchartlogvalueaxisangular_p.h>
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCore/qmath.h>
#include <private/abstractchartlayout_p.h>
#include <private/chartpresenter_p.h>
-#include <QtCharts/QLogValueAxis>
-#include <QtCore/QtMath>
-#include <QtCore/QDebug>
-#include <cmath>
+#include <private/polarchartlogvalueaxisangular_p.h>
QT_CHARTS_BEGIN_NAMESPACE
-PolarChartLogValueAxisAngular::PolarChartLogValueAxisAngular(QLogValueAxis *axis, QGraphicsItem *item)
+PolarChartLogValueAxisAngular::PolarChartLogValueAxisAngular(QLogValueAxis *axis,
+ QGraphicsItem *item)
: PolarChartAxisAngular(axis, item)
{
QObject::connect(axis, SIGNAL(baseChanged(qreal)), this, SLOT(handleBaseChanged(qreal)));
- QObject::connect(axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString)));
+ QObject::connect(axis, SIGNAL(labelFormatChanged(QString)),
+ this, SLOT(handleLabelFormatChanged(QString)));
}
PolarChartLogValueAxisAngular::~PolarChartLogValueAxisAngular()
@@ -50,21 +50,19 @@ PolarChartLogValueAxisAngular::~PolarChartLogValueAxisAngular()
QVector<qreal> PolarChartLogValueAxisAngular::calculateLayout() const
{
- QLogValueAxis *logValueAxis = static_cast<QLogValueAxis *>(axis());
+ QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+
+ QVector<qreal> points;
+ points.resize(logValueAxis->tickCount());
+
const qreal logMax = std::log10(logValueAxis->max()) / std::log10(logValueAxis->base());
const qreal logMin = std::log10(logValueAxis->min()) / std::log10(logValueAxis->base());
- const qreal startEdge = logMin < logMax ? logMin : logMax;
+ const qreal startEdge = qMin(logMin, logMax);
const qreal delta = 360.0 / qAbs(logMax - logMin);
const qreal initialSpan = (qCeil(startEdge) - startEdge) * delta;
- int tickCount = qAbs(qCeil(logMax) - qCeil(logMin));
-
- QVector<qreal> points;
- points.resize(tickCount);
- for (int i = 0; i < tickCount; ++i) {
- qreal angularCoordinate = initialSpan + (delta * qreal(i));
- points[i] = angularCoordinate;
- }
+ for (int i = 0; i < logValueAxis->tickCount(); ++i)
+ points[i] = initialSpan + (delta * qreal(i));
return points;
}
diff --git a/src/charts/axis/logvalueaxis/polarchartlogvalueaxisradial.cpp b/src/charts/axis/logvalueaxis/polarchartlogvalueaxisradial.cpp
index cdebeb13..98ae52e1 100644
--- a/src/charts/axis/logvalueaxis/polarchartlogvalueaxisradial.cpp
+++ b/src/charts/axis/logvalueaxis/polarchartlogvalueaxisradial.cpp
@@ -27,13 +27,11 @@
**
****************************************************************************/
-#include <private/polarchartlogvalueaxisradial_p.h>
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCore/qmath.h>
#include <private/abstractchartlayout_p.h>
#include <private/chartpresenter_p.h>
-#include <QtCharts/QLogValueAxis>
-#include <QtCore/QtMath>
-#include <QtCore/QDebug>
-#include <cmath>
+#include <private/polarchartlogvalueaxisradial_p.h>
QT_CHARTS_BEGIN_NAMESPACE
@@ -41,7 +39,8 @@ PolarChartLogValueAxisRadial::PolarChartLogValueAxisRadial(QLogValueAxis *axis,
: PolarChartAxisRadial(axis, item)
{
QObject::connect(axis, SIGNAL(baseChanged(qreal)), this, SLOT(handleBaseChanged(qreal)));
- QObject::connect(axis, SIGNAL(labelFormatChanged(QString)), this, SLOT(handleLabelFormatChanged(QString)));
+ QObject::connect(axis, SIGNAL(labelFormatChanged(QString)),
+ this, SLOT(handleLabelFormatChanged(QString)));
}
PolarChartLogValueAxisRadial::~PolarChartLogValueAxisRadial()
@@ -50,26 +49,19 @@ PolarChartLogValueAxisRadial::~PolarChartLogValueAxisRadial()
QVector<qreal> PolarChartLogValueAxisRadial::calculateLayout() const
{
- QLogValueAxis *logValueAxis = static_cast<QLogValueAxis *>(axis());
+ QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+
+ QVector<qreal> points;
+ points.resize(logValueAxis->tickCount());
+
const qreal logMax = std::log10(logValueAxis->max()) / std::log10(logValueAxis->base());
const qreal logMin = std::log10(logValueAxis->min()) / std::log10(logValueAxis->base());
const qreal innerEdge = logMin < logMax ? logMin : logMax;
- const qreal outerEdge = logMin > logMax ? logMin : logMax;
const qreal delta = (axisGeometry().width() / 2.0) / qAbs(logMax - logMin);
const qreal initialSpan = (qCeil(innerEdge) - innerEdge) * delta;
- int tickCount = qAbs(qCeil(logMax) - qCeil(logMin));
-
- // Extra tick if outer edge is exactly at the tick
- if (outerEdge == qCeil(outerEdge))
- tickCount++;
-
- QVector<qreal> points;
- points.resize(tickCount);
- for (int i = 0; i < tickCount; ++i) {
- qreal radialCoordinate = initialSpan + (delta * qreal(i));
- points[i] = radialCoordinate;
- }
+ for (int i = 0; i < logValueAxis->tickCount(); ++i)
+ points[i] = initialSpan + (delta * qreal(i));
return points;
}
diff --git a/src/charts/axis/logvalueaxis/qlogvalueaxis.cpp b/src/charts/axis/logvalueaxis/qlogvalueaxis.cpp
index 75e6a27f..8efe1139 100644
--- a/src/charts/axis/logvalueaxis/qlogvalueaxis.cpp
+++ b/src/charts/axis/logvalueaxis/qlogvalueaxis.cpp
@@ -27,17 +27,16 @@
**
****************************************************************************/
-#include <QtCharts/QLogValueAxis>
-#include <private/qlogvalueaxis_p.h>
+#include <QtCore/qmath.h>
+#include <private/abstractdomain_p.h>
#include <private/chartlogvalueaxisx_p.h>
#include <private/chartlogvalueaxisy_p.h>
#include <private/polarchartlogvalueaxisangular_p.h>
#include <private/polarchartlogvalueaxisradial_p.h>
-#include <private/abstractdomain_p.h>
-#include <float.h>
-#include <cmath>
+#include <private/qlogvalueaxis_p.h>
QT_CHARTS_BEGIN_NAMESPACE
+
/*!
\class QLogValueAxis
\inmodule Qt Charts
@@ -117,6 +116,31 @@ QT_CHARTS_BEGIN_NAMESPACE
*/
/*!
+ \property QLogValueAxis::tickCount
+ \brief The number of tick marks on the axis. This indicates how many grid lines are drawn on the
+ chart. This value is read-only.
+*/
+/*!
+ \qmlproperty int LogValueAxis::tickCount
+ The number of tick marks on the axis. This indicates how many grid lines are drawn on the
+ chart. This value is read-only.
+*/
+
+/*!
+ \property QLogValueAxis::minorTickCount
+ \brief The number of minor tick marks on the axis. This indicates how many grid lines are drawn
+ between major ticks on the chart. Labels are not drawn for minor ticks. The default value is 0.
+ Set the value to -1 and the number of grid lines between major ticks will be calculated
+ automatically.
+*/
+/*!
+ \qmlproperty int LogValueAxis::minorTickCount
+ The number of minor tick marks on the axis. This indicates how many grid lines are drawn between
+ major ticks on the chart. Labels are not drawn for minor ticks. The default value is 0. Set the
+ value to -1 and the number of grid lines between major ticks will be calculated automatically.
+*/
+
+/*!
\property QLogValueAxis::labelFormat
\brief The label format of the axis.
@@ -160,6 +184,28 @@ QT_CHARTS_BEGIN_NAMESPACE
*/
/*!
+ \fn void QLogValueAxis::tickCountChanged(int tickCount)
+ This signal is emitted when the number of tick marks on the axis, specified by \a tickCount,
+ changes.
+*/
+/*!
+ \qmlsignal LogValueAxis::tickCountChanged(int tickCount)
+ This signal is emitted when the number of tick marks on the axis, specified by \a tickCount,
+ changes.
+*/
+
+/*!
+ \fn void QLogValueAxis::minorTickCountChanged(int minorTickCount)
+ This signal is emitted when the number of minor tick marks on the axis, specified by
+ \a minorTickCount, changes.
+*/
+/*!
+ \qmlsignal LogValueAxis::minorTickCountChanged(int minorTickCount)
+ This signal is emitted when the number of minor tick marks on the axis, specified by
+ \a minorTickCount, changes.
+*/
+
+/*!
\fn void QLogValueAxis::labelFormatChanged(const QString &format)
This signal is emitted when the \a format of axis labels changes.
*/
@@ -227,12 +273,13 @@ qreal QLogValueAxis::max() const
void QLogValueAxis::setRange(qreal min, qreal max)
{
Q_D(QLogValueAxis);
- bool changed = false;
if (min > max)
return;
if (min > 0) {
+ bool changed = false;
+
if (!qFuzzyCompare(d->m_min, min)) {
d->m_min = min;
changed = true;
@@ -246,6 +293,7 @@ void QLogValueAxis::setRange(qreal min, qreal max)
}
if (changed) {
+ d->updateTickCount();
emit rangeChanged(min, max);
emit d->rangeChanged(min,max);
}
@@ -255,27 +303,32 @@ void QLogValueAxis::setRange(qreal min, qreal max)
void QLogValueAxis::setLabelFormat(const QString &format)
{
Q_D(QLogValueAxis);
- d->m_format = format;
- emit labelFormatChanged(format);
+
+ if (d->m_labelFormat == format)
+ return;
+
+ d->m_labelFormat = format;
+ emit labelFormatChanged(d->m_labelFormat);
}
QString QLogValueAxis::labelFormat() const
{
Q_D(const QLogValueAxis);
- return d->m_format;
+ return d->m_labelFormat;
}
void QLogValueAxis::setBase(qreal base)
{
- // check if base is correct
- if (qFuzzyCompare(base, 1))
- return;
+ Q_D(QLogValueAxis);
- if (base > 0) {
- Q_D(QLogValueAxis);
- d->m_base = base;
- emit baseChanged(base);
+ if (base < 0.0 || qFuzzyIsNull(base) || qFuzzyCompare(base, 1.0) // check if base is correct
+ || qFuzzyCompare(d->m_base, base)) {
+ return;
}
+
+ d->m_base = base;
+ d->updateTickCount();
+ emit baseChanged(d->m_base);
}
qreal QLogValueAxis::base() const
@@ -284,6 +337,32 @@ qreal QLogValueAxis::base() const
return d->m_base;
}
+int QLogValueAxis::tickCount() const
+{
+ Q_D(const QLogValueAxis);
+ return d->m_tickCount;
+}
+
+void QLogValueAxis::setMinorTickCount(int minorTickCount)
+{
+ Q_D(QLogValueAxis);
+
+ if (minorTickCount < 0)
+ minorTickCount = -1;
+
+ if (d->m_minorTickCount == minorTickCount)
+ return;
+
+ d->m_minorTickCount = minorTickCount;
+ emit minorTickCountChanged(minorTickCount);
+}
+
+int QLogValueAxis::minorTickCount() const
+{
+ Q_D(const QLogValueAxis);
+ return d->m_minorTickCount;
+}
+
/*!
Returns the type of the axis.
*/
@@ -292,20 +371,21 @@ QAbstractAxis::AxisType QLogValueAxis::type() const
return AxisTypeLogValue;
}
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////
QLogValueAxisPrivate::QLogValueAxisPrivate(QLogValueAxis *q)
: QAbstractAxisPrivate(q),
m_min(1),
m_max(1),
m_base(10),
- m_format(QString::null)
+ m_tickCount(0),
+ m_minorTickCount(0),
+ m_labelFormat(QString::null)
{
}
QLogValueAxisPrivate::~QLogValueAxisPrivate()
{
-
}
void QLogValueAxisPrivate::setMin(const QVariant &min)
@@ -341,12 +421,13 @@ void QLogValueAxisPrivate::setRange(const QVariant &min, const QVariant &max)
void QLogValueAxisPrivate::setRange(qreal min, qreal max)
{
Q_Q(QLogValueAxis);
- bool changed = false;
if (min > max)
return;
if (min > 0) {
+ bool changed = false;
+
if (!qFuzzyCompare(m_min, min)) {
m_min = min;
changed = true;
@@ -360,12 +441,33 @@ void QLogValueAxisPrivate::setRange(qreal min, qreal max)
}
if (changed) {
+ updateTickCount();
emit rangeChanged(min,max);
emit q->rangeChanged(min, max);
}
}
}
+void QLogValueAxisPrivate::updateTickCount()
+{
+ Q_Q(QLogValueAxis);
+
+ const qreal logMax = qLn(m_max) / qLn(m_base);
+ const qreal logMin = qLn(m_min) / qLn(m_base);
+ int tickCount = qAbs(qCeil(logMax) - qCeil(logMin));
+
+ // If the high edge sits exactly on the tick value, add a tick
+ qreal highValue = logMin < logMax ? logMax : logMin;
+ if (qFuzzyCompare(highValue, qreal(qCeil(highValue))))
+ ++tickCount;
+
+ if (m_tickCount == tickCount)
+ return;
+
+ m_tickCount = tickCount;
+ emit q->tickCountChanged(m_tickCount);
+}
+
void QLogValueAxisPrivate::initializeGraphics(QGraphicsItem *parent)
{
Q_Q(QLogValueAxis);
diff --git a/src/charts/axis/logvalueaxis/qlogvalueaxis.h b/src/charts/axis/logvalueaxis/qlogvalueaxis.h
index 3d8fd842..2614cff3 100644
--- a/src/charts/axis/logvalueaxis/qlogvalueaxis.h
+++ b/src/charts/axis/logvalueaxis/qlogvalueaxis.h
@@ -47,6 +47,8 @@ class QT_CHARTS_EXPORT QLogValueAxis : public QAbstractAxis
Q_PROPERTY(qreal max READ max WRITE setMax NOTIFY maxChanged)
Q_PROPERTY(QString labelFormat READ labelFormat WRITE setLabelFormat NOTIFY labelFormatChanged)
Q_PROPERTY(qreal base READ base WRITE setBase NOTIFY baseChanged)
+ Q_PROPERTY(int tickCount READ tickCount NOTIFY tickCountChanged)
+ Q_PROPERTY(int minorTickCount READ minorTickCount WRITE setMinorTickCount NOTIFY minorTickCountChanged)
public:
explicit QLogValueAxis(QObject *parent = Q_NULLPTR);
@@ -71,12 +73,19 @@ public:
void setBase(qreal base);
qreal base() const;
+ int tickCount() const;
+
+ void setMinorTickCount(int minorTickCount);
+ int minorTickCount() const;
+
Q_SIGNALS:
void minChanged(qreal min);
void maxChanged(qreal max);
void rangeChanged(qreal min, qreal max);
void labelFormatChanged(const QString &format);
void baseChanged(qreal base);
+ void tickCountChanged(int tickCount);
+ void minorTickCountChanged(int minorTickCount);
private:
Q_DECLARE_PRIVATE(QLogValueAxis)
diff --git a/src/charts/axis/logvalueaxis/qlogvalueaxis_p.h b/src/charts/axis/logvalueaxis/qlogvalueaxis_p.h
index 814b92fa..1c00c7eb 100644
--- a/src/charts/axis/logvalueaxis/qlogvalueaxis_p.h
+++ b/src/charts/axis/logvalueaxis/qlogvalueaxis_p.h
@@ -39,7 +39,7 @@
#ifndef QLOGVALUEAXIS_P_H
#define QLOGVALUEAXIS_P_H
-#include <QtCharts/QLogValueAxis>
+#include <QtCharts/qlogvalueaxis.h>
#include <private/qabstractaxis_p.h>
QT_CHARTS_BEGIN_NAMESPACE
@@ -59,17 +59,20 @@ class QLogValueAxisPrivate : public QAbstractAxisPrivate
qreal max() { return m_max; }
void setRange(qreal min,qreal max);
+ void updateTickCount();
+
protected:
void setMin(const QVariant &min);
void setMax(const QVariant &max);
void setRange(const QVariant &min, const QVariant &max);
- int tickCount() const;
protected:
qreal m_min;
qreal m_max;
qreal m_base;
- QString m_format;
+ int m_tickCount;
+ int m_minorTickCount;
+ QString m_labelFormat;
Q_DECLARE_PUBLIC(QLogValueAxis)
};
diff --git a/src/charts/axis/polarchartaxis.cpp b/src/charts/axis/polarchartaxis.cpp
index b9a1c042..b37041fb 100644
--- a/src/charts/axis/polarchartaxis.cpp
+++ b/src/charts/axis/polarchartaxis.cpp
@@ -83,12 +83,13 @@ void PolarChartAxis::updateLayout(QVector<qreal> &layout)
// axes lingering in wrong position compared to series plot before animation can kick in.
// Note that the position mismatch still exists even with this update, but it will be
// far less ugly.
- updateGeometry();
+ if (ChartAxisElement::layout().size() != 0)
+ updateGeometry();
}
if (diff > 0)
deleteItems(diff);
- else if (diff < 0)
+ else if (diff <= 0)
createItems(-diff);
updateMinorTickItems();
diff --git a/src/charts/axis/polarchartaxisangular.cpp b/src/charts/axis/polarchartaxisangular.cpp
index bda2e086..17f00ed7 100644
--- a/src/charts/axis/polarchartaxisangular.cpp
+++ b/src/charts/axis/polarchartaxisangular.cpp
@@ -27,19 +27,17 @@
**
****************************************************************************/
-#include <private/polarchartaxisangular_p.h>
+#include <QtCharts/qcategoryaxis.h>
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCore/qmath.h>
+#include <QtGui/qtextdocument.h>
#include <private/chartpresenter_p.h>
-#include <private/abstractchartlayout_p.h>
-#include <QtCharts/QAbstractAxis>
-#include <QtCharts/QCategoryAxis>
-#include <private/qabstractaxis_p.h>
-#include <QtCore/QDebug>
-#include <QtCore/QtMath>
-#include <QtGui/QTextDocument>
+#include <private/polarchartaxisangular_p.h>
QT_CHARTS_BEGIN_NAMESPACE
-PolarChartAxisAngular::PolarChartAxisAngular(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
+PolarChartAxisAngular::PolarChartAxisAngular(QAbstractAxis *axis, QGraphicsItem *item,
+ bool intervalAxis)
: PolarChartAxis(axis, item, intervalAxis)
{
}
@@ -53,7 +51,7 @@ void PolarChartAxisAngular::updateGeometry()
QGraphicsLayoutItem::updateGeometry();
const QVector<qreal> &layout = this->layout();
- if (layout.isEmpty())
+ if (layout.isEmpty() && axis()->type() != QAbstractAxis::AxisTypeLogValue)
return;
createAxisLabels(layout);
@@ -242,38 +240,10 @@ void PolarChartAxisAngular::updateGeometry()
shadeItem->setVisible(true);
firstShade = false;
}
-
- // Minor ticks
- QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
- if ((i + 1) != layout.size() && valueAxis) {
- int minorTickCount = valueAxis->minorTickCount();
- if (minorTickCount != 0) {
- qreal minorAngularCoordinate = (layout[i + 1] - layout[i])
- / qreal(minorTickCount + 1);
- for (int j = 0; j < minorTickCount; j++) {
- QGraphicsLineItem *minorGridItem =
- static_cast<QGraphicsLineItem *>(minorGridItemList.at(i * minorTickCount + j));
- QGraphicsLineItem *minorTickItem =
- static_cast<QGraphicsLineItem *>(minorArrowItemList.at(i * minorTickCount + j));
- qreal minorAngle = 90.0 - angularCoordinate
- - minorAngularCoordinate * qreal(j + 1);
- QLineF minorGridLine = QLineF::fromPolar(radius, minorAngle);
- minorGridLine.translate(center);
- minorGridItem->setLine(minorGridLine);
- minorGridItem->setVisible(true);
-
- QLineF minorTickLine(QLineF::fromPolar(radius - tickWidth() + 1,
- minorAngle).p2(),
- QLineF::fromPolar(radius + tickWidth() - 1,
- minorAngle).p2());
- minorTickLine.translate(center);
- minorTickItem->setLine(minorTickLine);
- minorTickItem->setVisible(true);
- }
- }
- }
}
+ updateMinorTickGeometry();
+
// Title, centered above the chart
QString titleText = axis()->titleText();
if (!titleText.isEmpty() && axis()->isTitleVisible()) {
@@ -514,31 +484,161 @@ QRectF PolarChartAxisAngular::moveLabelToPosition(qreal angularCoordinate, QPoin
return labelRect;
}
-void PolarChartAxisAngular::updateMinorTickItems()
+void PolarChartAxisAngular::updateMinorTickGeometry()
{
- QValueAxis *valueAxis = qobject_cast<QValueAxis *>(this->axis());
- if (valueAxis) {
- int currentCount = minorArrowItems().size();
- int expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
- int diff = expectedCount - currentCount;
- if (diff > 0) {
- for (int i = 0; i < diff; i++) {
- QGraphicsLineItem *minorArrow = new QGraphicsLineItem(presenter()->rootItem());
- QGraphicsLineItem *minorGrid = new QGraphicsLineItem(presenter()->rootItem());
- minorArrow->setPen(valueAxis->linePen());
- minorGrid->setPen(valueAxis->minorGridLinePen());
- minorArrowGroup()->addToGroup(minorArrow);
- minorGridGroup()->addToGroup(minorGrid);
- }
+ if (!axis())
+ return;
+
+ QVector<qreal> layout = ChartAxisElement::layout();
+ int minorTickCount = 0;
+ qreal tickAngle = 0.0;
+ QVector<qreal> minorTickAngles;
+ switch (axis()->type()) {
+ case QAbstractAxis::AxisTypeValue: {
+ const QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
+
+ minorTickCount = valueAxis->minorTickCount();
+
+ if (valueAxis->tickCount() >= 2)
+ tickAngle = layout.at(1) - layout.at(0);
+
+ for (int i = 0; i < minorTickCount; ++i) {
+ const qreal ratio = (1.0 / qreal(minorTickCount + 1)) * qreal(i + 1);
+ minorTickAngles.append(tickAngle * ratio);
+ }
+ break;
+ }
+ case QAbstractAxis::AxisTypeLogValue: {
+ const QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+ const qreal base = logValueAxis->base();
+ const qreal logBase = qLn(base);
+
+ minorTickCount = logValueAxis->minorTickCount();
+ if (minorTickCount < 0)
+ minorTickCount = qMax(int(qFloor(base) - 2.0), 0);
+
+ // Two "virtual" ticks are required to make sure that all minor ticks
+ // are displayed properly (even for the partially visible segments of
+ // the chart).
+ if (layout.size() >= 2) {
+ // Calculate tickAngle as a difference between visible ticks
+ // whenever it is possible. Virtual ticks will not be correctly
+ // positioned when the layout is animating.
+ tickAngle = layout.at(1) - layout.at(0);
+ layout.prepend(layout.at(0) - tickAngle);
+ layout.append(layout.at(layout.size() - 1) + tickAngle);
} else {
- QList<QGraphicsItem *> minorGridLines = minorGridItems();
- QList<QGraphicsItem *> minorArrows = minorArrowItems();
- for (int i = 0; i > diff; i--) {
- if (!minorGridLines.isEmpty())
- delete(minorGridLines.takeLast());
- if (!minorArrows.isEmpty())
- delete(minorArrows.takeLast());
- }
+ const qreal logMax = qLn(logValueAxis->max());
+ const qreal logMin = qLn(logValueAxis->min());
+ const qreal logExtraMaxTick = qLn(qPow(base, qFloor(logMax / logBase) + 1.0));
+ const qreal logExtraMinTick = qLn(qPow(base, qCeil(logMin / logBase) - 1.0));
+ const qreal edge = qMin(logMin, logMax);
+ const qreal delta = 360.0 / qAbs(logMax - logMin);
+ const qreal extraMaxTick = edge + (logExtraMaxTick - edge) * delta;
+ const qreal extraMinTick = edge + (logExtraMinTick - edge) * delta;
+
+ // Calculate tickAngle using one (if layout.size() == 1) or two
+ // (if layout.size() == 0) "virtual" ticks. In both cases animation
+ // will not work as expected. This should be fixed later.
+ layout.prepend(extraMinTick);
+ layout.append(extraMaxTick);
+ tickAngle = layout.at(1) - layout.at(0);
+ }
+
+ const qreal minorTickStepValue = qFabs(base - 1.0) / qreal(minorTickCount + 1);
+ for (int i = 0; i < minorTickCount; ++i) {
+ const qreal x = minorTickStepValue * qreal(i + 1) + 1.0;
+ const qreal minorTickAngle = tickAngle * (qLn(x) / logBase);
+ minorTickAngles.append(minorTickAngle);
+ }
+ break;
+ }
+ default:
+ // minor ticks are not supported
+ break;
+ }
+
+ if (minorTickCount < 1 || tickAngle == 0.0 || minorTickAngles.count() != minorTickCount)
+ return;
+
+ const QPointF axisCenter = axisGeometry().center();
+ const qreal axisRadius = axisGeometry().height() / 2.0;
+
+ for (int i = 0; i < layout.size() - 1; ++i) {
+ for (int j = 0; j < minorTickCount; ++j) {
+ const int minorItemIndex = i * minorTickCount + j;
+ QGraphicsLineItem *minorGridLineItem =
+ static_cast<QGraphicsLineItem *>(minorGridItems().at(minorItemIndex));
+ QGraphicsLineItem *minorArrowLineItem =
+ static_cast<QGraphicsLineItem *>(minorArrowItems().at(minorItemIndex));
+ if (!minorGridLineItem || !minorArrowLineItem)
+ continue;
+
+ const qreal minorTickAngle = 90.0 - layout.at(i) - minorTickAngles.value(j, 0.0);
+
+ const QPointF minorArrowLinePt1 = QLineF::fromPolar(axisRadius - tickWidth() + 1,
+ minorTickAngle).p2();
+ const QPointF minorArrowLinePt2 = QLineF::fromPolar(axisRadius + tickWidth() - 1,
+ minorTickAngle).p2();
+
+ QLineF minorGridLine = QLineF::fromPolar(axisRadius, minorTickAngle);
+ minorGridLine.translate(axisCenter);
+ minorGridLineItem->setLine(minorGridLine);
+
+ QLineF minorArrowLine(minorArrowLinePt1, minorArrowLinePt2);
+ minorArrowLine.translate(axisCenter);
+ minorArrowLineItem->setLine(minorArrowLine);
+
+ // check if the minor grid line and the minor axis arrow should be shown
+ const bool minorGridLineVisible = (minorTickAngle >= -270.0 && minorTickAngle <= 90.0);
+ minorGridLineItem->setVisible(minorGridLineVisible);
+ minorArrowLineItem->setVisible(minorGridLineVisible);
+ }
+ }
+}
+
+void PolarChartAxisAngular::updateMinorTickItems()
+{
+ int currentCount = minorArrowItems().size();
+ int expectedCount = 0;
+ if (axis()->type() == QAbstractAxis::AxisTypeValue) {
+ QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
+ expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
+ expectedCount = qMax(expectedCount, 0);
+ } else if (axis()->type() == QAbstractAxis::AxisTypeLogValue) {
+ QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+
+ int minorTickCount = logValueAxis->minorTickCount();
+ if (minorTickCount < 0)
+ minorTickCount = qMax(int(qFloor(logValueAxis->base()) - 2.0), 0);
+
+ expectedCount = minorTickCount * (logValueAxis->tickCount() + 1);
+ expectedCount = qMax(expectedCount, logValueAxis->minorTickCount());
+ } else {
+ // minor ticks are not supported
+ return;
+ }
+
+ int diff = expectedCount - currentCount;
+ if (diff > 0) {
+ for (int i = 0; i < diff; ++i) {
+ QGraphicsLineItem *minorGridLineItem = new QGraphicsLineItem(this);
+ minorGridLineItem->setPen(axis()->minorGridLinePen());
+ minorGridGroup()->addToGroup(minorGridLineItem);
+
+ QGraphicsLineItem *minorArrowLineItem = new QGraphicsLineItem(this);
+ minorArrowLineItem->setPen(axis()->linePen());
+ minorArrowGroup()->addToGroup(minorArrowLineItem);
+ }
+ } else {
+ QList<QGraphicsItem *> minorGridItemsList = minorGridItems();
+ QList<QGraphicsItem *> minorArrowItemsList = minorArrowItems();
+ for (int i = 0; i > diff; --i) {
+ if (!minorGridItemsList.isEmpty())
+ delete minorGridItemsList.takeLast();
+
+ if (!minorArrowItemsList.isEmpty())
+ delete minorArrowItemsList.takeLast();
}
}
}
diff --git a/src/charts/axis/polarchartaxisangular_p.h b/src/charts/axis/polarchartaxisangular_p.h
index cb902816..1cb43ca6 100644
--- a/src/charts/axis/polarchartaxisangular_p.h
+++ b/src/charts/axis/polarchartaxisangular_p.h
@@ -39,14 +39,15 @@
#ifndef POLARCHARTAXISANGULAR_P_H
#define POLARCHARTAXISANGULAR_P_H
+#include <QtCharts/qvalueaxis.h>
#include <private/polarchartaxis_p.h>
-#include <QtCharts/QValueAxis>
QT_CHARTS_BEGIN_NAMESPACE
class PolarChartAxisAngular : public PolarChartAxis
{
Q_OBJECT
+
public:
PolarChartAxisAngular(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis = false);
~PolarChartAxisAngular();
@@ -70,6 +71,7 @@ public Q_SLOTS:
private:
QRectF moveLabelToPosition(qreal angularCoordinate, QPointF labelPoint, QRectF labelRect) const;
+ void updateMinorTickGeometry();
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/axis/polarchartaxisradial.cpp b/src/charts/axis/polarchartaxisradial.cpp
index c20fcb47..23ba1e41 100644
--- a/src/charts/axis/polarchartaxisradial.cpp
+++ b/src/charts/axis/polarchartaxisradial.cpp
@@ -27,17 +27,18 @@
**
****************************************************************************/
-#include <private/polarchartaxisradial_p.h>
+#include <QtCharts/qcategoryaxis.h>
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCore/qmath.h>
+#include <QtGui/qtextdocument.h>
#include <private/chartpresenter_p.h>
-#include <private/abstractchartlayout_p.h>
-#include <private/qabstractaxis_p.h>
#include <private/linearrowitem_p.h>
-#include <QtCharts/QCategoryAxis>
-#include <QtGui/QTextDocument>
+#include <private/polarchartaxisradial_p.h>
QT_CHARTS_BEGIN_NAMESPACE
-PolarChartAxisRadial::PolarChartAxisRadial(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
+PolarChartAxisRadial::PolarChartAxisRadial(QAbstractAxis *axis, QGraphicsItem *item,
+ bool intervalAxis)
: PolarChartAxis(axis, item, intervalAxis)
{
}
@@ -49,7 +50,7 @@ PolarChartAxisRadial::~PolarChartAxisRadial()
void PolarChartAxisRadial::updateGeometry()
{
const QVector<qreal> &layout = this->layout();
- if (layout.isEmpty())
+ if (layout.isEmpty() && axis()->type() != QAbstractAxis::AxisTypeLogValue)
return;
createAxisLabels(layout);
@@ -225,39 +226,10 @@ void PolarChartAxisRadial::updateGeometry()
shadeItem->setVisible(true);
firstShade = false;
}
-
- // Minor ticks
- QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
- if ((i + 1) != layout.size() && valueAxis) {
- int minorTickCount = valueAxis->minorTickCount();
- if (minorTickCount != 0) {
- qreal minorRadialCoordinate = (layout[i + 1] - layout[i])
- / qreal(minorTickCount + 1) * 2.0;
- for (int j = 0; j < minorTickCount; j++) {
- QGraphicsEllipseItem *minorGridItem =
- static_cast<QGraphicsEllipseItem *>(minorGridItemList.at(i * minorTickCount + j));
- QGraphicsLineItem *minorTickItem =
- static_cast<QGraphicsLineItem *>(minorArrowItemList.at(i * minorTickCount + j));
-
- QRectF minorGridRect;
- minorGridRect.setWidth(minorRadialCoordinate * qreal(i + 1)
- + minorRadialCoordinate * qreal(i * minorTickCount + j));
- minorGridRect.setHeight(minorRadialCoordinate * qreal(i + 1)
- + minorRadialCoordinate
- * qreal(i * minorTickCount + j));
- minorGridRect.moveCenter(center);
- minorGridItem->setRect(minorGridRect);
- minorGridItem->setVisible(true);
-
- QLineF minorTickLine(-tickWidth() + 1, 0.0, tickWidth() - 1, 0.0);
- tickLine.translate(center.rx(), minorGridRect.top());
- minorTickItem->setLine(minorTickLine);
- minorTickItem->setVisible(true);
- }
- }
- }
}
+ updateMinorTickGeometry();
+
// Title, along the 0 axis
QString titleText = axis()->titleText();
if (!titleText.isEmpty() && axis()->isTitleVisible()) {
@@ -378,31 +350,157 @@ qreal PolarChartAxisRadial::preferredAxisRadius(const QSizeF &maxSize)
return radius;
}
-void PolarChartAxisRadial::updateMinorTickItems()
+void PolarChartAxisRadial::updateMinorTickGeometry()
{
- QValueAxis *valueAxis = qobject_cast<QValueAxis *>(this->axis());
- if (valueAxis) {
- int currentCount = minorArrowItems().size();
- int expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
- int diff = expectedCount - currentCount;
- if (diff > 0) {
- for (int i = 0; i < diff; i++) {
- QGraphicsLineItem *minorArrow = new QGraphicsLineItem(presenter()->rootItem());
- QGraphicsEllipseItem *minorGrid = new QGraphicsEllipseItem(presenter()->rootItem());
- minorArrow->setPen(valueAxis->linePen());
- minorGrid->setPen(valueAxis->minorGridLinePen());
- minorArrowGroup()->addToGroup(minorArrow);
- minorGridGroup()->addToGroup(minorGrid);
- }
+ if (!axis())
+ return;
+
+ QVector<qreal> layout = ChartAxisElement::layout();
+ int minorTickCount = 0;
+ qreal tickRadius = 0.0;
+ QVector<qreal> minorTickRadiuses;
+ switch (axis()->type()) {
+ case QAbstractAxis::AxisTypeValue: {
+ const QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
+
+ minorTickCount = valueAxis->minorTickCount();
+
+ if (valueAxis->tickCount() >= 2)
+ tickRadius = layout.at(1) - layout.at(0);
+
+ for (int i = 0; i < minorTickCount; ++i) {
+ const qreal ratio = (1.0 / qreal(minorTickCount + 1)) * qreal(i + 1);
+ minorTickRadiuses.append(tickRadius * ratio);
+ }
+ break;
+ }
+ case QAbstractAxis::AxisTypeLogValue: {
+ const QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+ const qreal base = logValueAxis->base();
+ const qreal logBase = qLn(base);
+
+ minorTickCount = logValueAxis->minorTickCount();
+ if (minorTickCount < 0)
+ minorTickCount = qMax(int(qFloor(base) - 2.0), 0);
+
+ // Two "virtual" ticks are required to make sure that all minor ticks
+ // are displayed properly (even for the partially visible segments of
+ // the chart).
+ if (layout.size() >= 2) {
+ // Calculate tickRadius as a difference between visible ticks
+ // whenever it is possible. Virtual ticks will not be correctly
+ // positioned when the layout is animating.
+ tickRadius = layout.at(1) - layout.at(0);
+ layout.prepend(layout.at(0) - tickRadius);
+ layout.append(layout.at(layout.size() - 1) + tickRadius);
} else {
- QList<QGraphicsItem *> minorGridLines = minorGridItems();
- QList<QGraphicsItem *> minorArrows = minorArrowItems();
- for (int i = 0; i > diff; i--) {
- if (!minorGridLines.isEmpty())
- delete(minorGridLines.takeLast());
- if (!minorArrows.isEmpty())
- delete(minorArrows.takeLast());
- }
+ const qreal logMax = qLn(logValueAxis->max());
+ const qreal logMin = qLn(logValueAxis->min());
+ const qreal logExtraMaxTick = qLn(qPow(base, qFloor(logMax / logBase) + 1.0));
+ const qreal logExtraMinTick = qLn(qPow(base, qCeil(logMin / logBase) - 1.0));
+ const qreal edge = qMin(logMin, logMax);
+ const qreal delta = (axisGeometry().width() / 2.0) / qAbs(logMax - logMin);
+ const qreal extraMaxTick = edge + (logExtraMaxTick - edge) * delta;
+ const qreal extraMinTick = edge + (logExtraMinTick - edge) * delta;
+
+ // Calculate tickRadius using one (if layout.size() == 1) or two
+ // (if layout.size() == 0) "virtual" ticks. In both cases animation
+ // will not work as expected. This should be fixed later.
+ layout.prepend(extraMinTick);
+ layout.append(extraMaxTick);
+ tickRadius = layout.at(1) - layout.at(0);
+ }
+
+ const qreal minorTickStepValue = qFabs(base - 1.0) / qreal(minorTickCount + 1);
+ for (int i = 0; i < minorTickCount; ++i) {
+ const qreal x = minorTickStepValue * qreal(i + 1) + 1.0;
+ const qreal minorTickSpacing = tickRadius * (qLn(x) / logBase);
+ minorTickRadiuses.append(minorTickSpacing);
+ }
+ break;
+ }
+ default:
+ // minor ticks are not supported
+ break;
+ }
+
+ if (minorTickCount < 1 || tickRadius == 0.0 || minorTickRadiuses.count() != minorTickCount)
+ return;
+
+ const QPointF axisCenter = axisGeometry().center();
+ const qreal axisRadius = axisGeometry().height() / 2.0;
+
+ for (int i = 0; i < layout.size() - 1; ++i) {
+ for (int j = 0; j < minorTickCount; ++j) {
+ const int minorItemIndex = i * minorTickCount + j;
+ QGraphicsEllipseItem *minorGridLineItem =
+ static_cast<QGraphicsEllipseItem *>(minorGridItems().at(minorItemIndex));
+ QGraphicsLineItem *minorArrowLineItem =
+ static_cast<QGraphicsLineItem *>(minorArrowItems().at(minorItemIndex));
+ if (!minorGridLineItem || !minorArrowLineItem)
+ continue;
+
+ const qreal minorTickRadius = layout.at(i) + minorTickRadiuses.value(j, 0.0);
+ const qreal minorTickDiameter = minorTickRadius * 2.0;
+
+ QRectF minorGridLine(0.0, 0.0, minorTickDiameter, minorTickDiameter);
+ minorGridLine.moveCenter(axisCenter);
+ minorGridLineItem->setRect(minorGridLine);
+
+ QLineF minorArrowLine(-tickWidth() + 1.0, 0.0, tickWidth() - 1.0, 0.0);
+ minorArrowLine.translate(axisCenter.x(), minorGridLine.top());
+ minorArrowLineItem->setLine(minorArrowLine);
+
+ // check if the minor grid line and the minor axis arrow should be shown
+ bool minorGridLineVisible = (minorTickRadius >= 0.0 && minorTickRadius <= axisRadius);
+ minorGridLineItem->setVisible(minorGridLineVisible);
+ minorArrowLineItem->setVisible(minorGridLineVisible);
+ }
+ }
+}
+
+void PolarChartAxisRadial::updateMinorTickItems()
+{
+ int currentCount = minorArrowItems().size();
+ int expectedCount = 0;
+ if (axis()->type() == QAbstractAxis::AxisTypeValue) {
+ QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
+ expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
+ expectedCount = qMax(expectedCount, 0);
+ } else if (axis()->type() == QAbstractAxis::AxisTypeLogValue) {
+ QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+
+ int minorTickCount = logValueAxis->minorTickCount();
+ if (minorTickCount < 0)
+ minorTickCount = qMax(int(qFloor(logValueAxis->base()) - 2.0), 0);
+
+ expectedCount = minorTickCount * (logValueAxis->tickCount() + 1);
+ expectedCount = qMax(expectedCount, logValueAxis->minorTickCount());
+ } else {
+ // minor ticks are not supported
+ return;
+ }
+
+ int diff = expectedCount - currentCount;
+ if (diff > 0) {
+ for (int i = 0; i < diff; ++i) {
+ QGraphicsEllipseItem *minorGridItem = new QGraphicsEllipseItem(presenter()->rootItem());
+ minorGridItem->setPen(axis()->minorGridLinePen());
+ minorGridGroup()->addToGroup(minorGridItem);
+
+ QGraphicsLineItem *minorArrowItem = new QGraphicsLineItem(presenter()->rootItem());
+ minorArrowItem->setPen(axis()->linePen());
+ minorArrowGroup()->addToGroup(minorArrowItem);
+ }
+ } else {
+ QList<QGraphicsItem *> minorGridItemsList = minorGridItems();
+ QList<QGraphicsItem *> minorArrowItemsList = minorArrowItems();
+ for (int i = 0; i > diff; --i) {
+ if (!minorGridItemsList.isEmpty())
+ delete minorGridItemsList.takeLast();
+
+ if (!minorArrowItemsList.isEmpty())
+ delete minorArrowItemsList.takeLast();
}
}
}
diff --git a/src/charts/axis/polarchartaxisradial_p.h b/src/charts/axis/polarchartaxisradial_p.h
index 9233645a..fd7fe7f2 100644
--- a/src/charts/axis/polarchartaxisradial_p.h
+++ b/src/charts/axis/polarchartaxisradial_p.h
@@ -39,14 +39,15 @@
#ifndef POLARCHARTAXISRADIAL_P_H
#define POLARCHARTAXISRADIAL_P_H
+#include <QtCharts/qvalueaxis.h>
#include <private/polarchartaxis_p.h>
-#include <QtCharts/QValueAxis>
QT_CHARTS_BEGIN_NAMESPACE
class PolarChartAxisRadial : public PolarChartAxis
{
Q_OBJECT
+
public:
PolarChartAxisRadial(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis = false);
~PolarChartAxisRadial();
@@ -67,6 +68,9 @@ public Q_SLOTS:
virtual void handleMinorGridPenChanged(const QPen &pen);
virtual void handleGridLineColorChanged(const QColor &color);
virtual void handleMinorGridLineColorChanged(const QColor &color);
+
+private:
+ void updateMinorTickGeometry();
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/axis/verticalaxis.cpp b/src/charts/axis/verticalaxis.cpp
index c01b89a7..d59d92b9 100644
--- a/src/charts/axis/verticalaxis.cpp
+++ b/src/charts/axis/verticalaxis.cpp
@@ -27,11 +27,11 @@
**
****************************************************************************/
-#include <private/verticalaxis_p.h>
-#include <QtCharts/QAbstractAxis>
+#include <QtCharts/qcategoryaxis.h>
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCore/qmath.h>
#include <private/chartpresenter_p.h>
-#include <QtCharts/QCategoryAxis>
-#include <QtCore/QDebug>
+#include <private/verticalaxis_p.h>
QT_CHARTS_BEGIN_NAMESPACE
@@ -44,6 +44,34 @@ VerticalAxis::~VerticalAxis()
{
}
+QSizeF VerticalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_UNUSED(constraint);
+ QSizeF sh(0, 0);
+
+ if (axis()->titleText().isEmpty() || !titleItem()->isVisible())
+ return sh;
+
+ switch (which) {
+ case Qt::MinimumSize: {
+ QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(),
+ QStringLiteral("..."));
+ sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width());
+ break;
+ }
+ case Qt::MaximumSize:
+ case Qt::PreferredSize: {
+ QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
+ sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width());
+ break;
+ }
+ default:
+ break;
+ }
+
+ return sh;
+}
+
void VerticalAxis::updateGeometry()
{
const QVector<qreal> &layout = ChartAxisElement::layout();
@@ -91,10 +119,13 @@ void VerticalAxis::updateGeometry()
titleBoundingRect = title->boundingRect();
QPointF center = gridRect.center() - titleBoundingRect.center();
- if (axis()->alignment() == Qt::AlignLeft)
- title->setPos(axisRect.left() - titleBoundingRect.width() / 2.0 + titleBoundingRect.height() / 2.0 + titlePadding(), center.y());
- else if (axis()->alignment() == Qt::AlignRight)
- title->setPos(axisRect.right() - titleBoundingRect.width() / 2.0 - titleBoundingRect.height() / 2.0 - titlePadding(), center.y());
+ if (axis()->alignment() == Qt::AlignLeft) {
+ title->setPos(axisRect.left() - titleBoundingRect.width() / 2.0
+ + titleBoundingRect.height() / 2.0 + titlePadding(), center.y());
+ } else if (axis()->alignment() == Qt::AlignRight) {
+ title->setPos(axisRect.right() - titleBoundingRect.width() / 2.0
+ - titleBoundingRect.height() / 2.0 - titlePadding(), center.y());
+ }
title->setTransformOriginPoint(titleBoundingRect.center());
title->setRotation(270);
@@ -102,13 +133,8 @@ void VerticalAxis::updateGeometry()
availableSpace -= titleBoundingRect.height();
}
- if (layout.isEmpty() && axis()->type() == QAbstractAxis::AxisTypeLogValue)
- return;
-
QList<QGraphicsItem *> lines = gridItems();
QList<QGraphicsItem *> shades = shadeItems();
- QList<QGraphicsItem *> minorLines = minorGridItems();
- QList<QGraphicsItem *> minorArrows = minorArrowItems();
for (int i = 0; i < layout.size(); ++i) {
//items
@@ -301,61 +327,15 @@ void VerticalAxis::updateGeometry()
}
// check if the grid line and the axis tick should be shown
- qreal y = gridItem->line().p1().y();
- if ((y < gridRect.top() || y > gridRect.bottom()))
- {
- gridItem->setVisible(false);
- tickItem->setVisible(false);
- }else{
- gridItem->setVisible(true);
- tickItem->setVisible(true);
- }
+ const bool gridLineVisible = (gridItem->line().p1().y() >= gridRect.top()
+ && gridItem->line().p1().y() <= gridRect.bottom());
+ gridItem->setVisible(gridLineVisible);
+ tickItem->setVisible(gridLineVisible);
+ }
- // add minor ticks
- QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
- if ((i + 1) != layout.size() && valueAxis) {
- int minorTickCount = valueAxis->minorTickCount();
- if (minorTickCount != 0) {
- qreal minorTickDistance = (layout[i] - layout[i + 1]) / qreal(minorTickCount + 1);
- if (axis()->isReverse())
- minorTickDistance = -minorTickDistance;
- for (int j = 0; j < minorTickCount; j++) {
- QGraphicsLineItem *minorGridItem =
- static_cast<QGraphicsLineItem *>(minorLines.at(i * minorTickCount + j));
- QGraphicsLineItem *minorArrowItem =
- static_cast<QGraphicsLineItem *>(minorArrows.at(i * minorTickCount + j));
- minorGridItem->setLine(gridRect.left(),
- gridItem->line().p1().y()
- - minorTickDistance * qreal(j + 1),
- gridRect.right(),
- gridItem->line().p1().y()
- - minorTickDistance * qreal(j + 1));
- if (axis()->alignment() == Qt::AlignLeft) {
- minorArrowItem->setLine(gridRect.left() - labelPadding() / 2,
- minorGridItem->line().p1().y(),
- gridRect.left(),
- minorGridItem->line().p1().y());
- } else if (axis()->alignment() == Qt::AlignRight){
- minorArrowItem->setLine(gridRect.right(),
- minorGridItem->line().p1().y(),
- gridRect.right() + labelPadding() / 2,
- minorGridItem->line().p1().y());
- }
+ updateMinorTickGeometry();
- // check if the minor grid line and the axis tick should be shown
- qreal minorYPos = minorGridItem->line().p1().y();
- if (minorYPos < gridRect.top() || minorYPos > gridRect.bottom()) {
- minorGridItem->setVisible(false);
- minorArrowItem->setVisible(false);
- } else {
- minorGridItem->setVisible(true);
- minorArrowItem->setVisible(true);
- }
- }
- }
- }
- }
- //begin/end grid line in case labels between
+ // begin/end grid line in case labels between
if (intervalAxis()) {
QGraphicsLineItem *gridLine;
gridLine = static_cast<QGraphicsLineItem *>(lines.at(layout.size()));
@@ -367,32 +347,132 @@ void VerticalAxis::updateGeometry()
}
}
-QSizeF VerticalAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+void VerticalAxis::updateMinorTickGeometry()
{
- Q_UNUSED(constraint);
- QSizeF sh(0, 0);
+ if (!axis())
+ return;
- if (axis()->titleText().isEmpty() || !titleItem()->isVisible())
- return sh;
+ QVector<qreal> layout = ChartAxisElement::layout();
+ int minorTickCount = 0;
+ qreal tickSpacing = 0.0;
+ QVector<qreal> minorTickSpacings;
+ switch (axis()->type()) {
+ case QAbstractAxis::AxisTypeValue: {
+ const QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
- switch (which) {
- case Qt::MinimumSize: {
- QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(),
- QStringLiteral("..."));
- sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width());
+ minorTickCount = valueAxis->minorTickCount();
+
+ if (valueAxis->tickCount() >= 2)
+ tickSpacing = layout.at(0) - layout.at(1);
+
+ for (int i = 0; i < minorTickCount; ++i) {
+ const qreal ratio = (1.0 / qreal(minorTickCount + 1)) * qreal(i + 1);
+ minorTickSpacings.append(tickSpacing * ratio);
+ }
break;
}
- case Qt::MaximumSize:
- case Qt::PreferredSize: {
- QRectF titleRect = ChartPresenter::textBoundingRect(axis()->titleFont(), axis()->titleText());
- sh = QSizeF(titleRect.height() + (titlePadding() * 2.0), titleRect.width());
+ case QAbstractAxis::AxisTypeLogValue: {
+ const QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
+ const qreal base = logValueAxis->base();
+ const qreal logBase = qLn(base);
+
+ minorTickCount = logValueAxis->minorTickCount();
+ if (minorTickCount < 0)
+ minorTickCount = qMax(int(qFloor(base) - 2.0), 0);
+
+ // Two "virtual" ticks are required to make sure that all minor ticks
+ // are displayed properly (even for the partially visible segments of
+ // the chart).
+ if (layout.size() >= 2) {
+ // Calculate tickSpacing as a difference between visible ticks
+ // whenever it is possible. Virtual ticks will not be correctly
+ // positioned when the layout is animating.
+ tickSpacing = layout.at(0) - layout.at(1);
+ layout.prepend(layout.at(0) + tickSpacing);
+ layout.append(layout.at(layout.size() - 1) - tickSpacing);
+ } else {
+ const qreal logMax = qLn(logValueAxis->max());
+ const qreal logMin = qLn(logValueAxis->min());
+ const qreal logExtraMaxTick = qLn(qPow(base, qFloor(logMax / logBase) + 1.0));
+ const qreal logExtraMinTick = qLn(qPow(base, qCeil(logMin / logBase) - 1.0));
+ const qreal edge = gridGeometry().bottom();
+ const qreal delta = gridGeometry().height() / qAbs(logMax - logMin);
+ const qreal extraMaxTick = edge - (logExtraMaxTick - qMin(logMin, logMax)) * delta;
+ const qreal extraMinTick = edge - (logExtraMinTick - qMin(logMin, logMax)) * delta;
+
+ // Calculate tickSpacing using one (if layout.size() == 1) or two
+ // (if layout.size() == 0) "virtual" ticks. In both cases animation
+ // will not work as expected. This should be fixed later.
+ layout.prepend(extraMinTick);
+ layout.append(extraMaxTick);
+ tickSpacing = layout.at(0) - layout.at(1);
+ }
+
+ const qreal minorTickStepValue = qFabs(base - 1.0) / qreal(minorTickCount + 1);
+ for (int i = 0; i < minorTickCount; ++i) {
+ const qreal x = minorTickStepValue * qreal(i + 1) + 1.0;
+ const qreal minorTickSpacing = tickSpacing * (qLn(x) / logBase);
+ minorTickSpacings.append(minorTickSpacing);
+ }
break;
}
default:
+ // minor ticks are not supported
break;
}
- return sh;
+ if (minorTickCount < 1 || tickSpacing == 0.0 || minorTickSpacings.count() != minorTickCount)
+ return;
+
+ for (int i = 0; i < layout.size() - 1; ++i) {
+ for (int j = 0; j < minorTickCount; ++j) {
+ const int minorItemIndex = i * minorTickCount + j;
+ QGraphicsLineItem *minorGridLineItem =
+ static_cast<QGraphicsLineItem *>(minorGridItems().value(minorItemIndex));
+ QGraphicsLineItem *minorArrowLineItem =
+ static_cast<QGraphicsLineItem *>(minorArrowItems().value(minorItemIndex));
+ if (!minorGridLineItem || !minorArrowLineItem)
+ continue;
+
+ const qreal minorTickSpacing = minorTickSpacings.value(j, 0.0);
+
+ qreal minorGridLineItemY = 0.0;
+ if (axis()->isReverse()) {
+ minorGridLineItemY = qFloor(gridGeometry().top() + gridGeometry().bottom()
+ - layout.at(i) + minorTickSpacing);
+ } else {
+ minorGridLineItemY = qCeil(layout.at(i) - minorTickSpacing);
+ }
+
+ qreal minorArrowLineItemX1;
+ qreal minorArrowLineItemX2;
+ switch (axis()->alignment()) {
+ case Qt::AlignLeft:
+ minorArrowLineItemX1 = gridGeometry().left() - labelPadding() / 2.0;
+ minorArrowLineItemX2 = gridGeometry().left();
+ break;
+ case Qt::AlignRight:
+ minorArrowLineItemX1 = gridGeometry().right();
+ minorArrowLineItemX2 = gridGeometry().right() + labelPadding() / 2.0;
+ break;
+ default:
+ minorArrowLineItemX1 = 0.0;
+ minorArrowLineItemX2 = 0.0;
+ break;
+ }
+
+ minorGridLineItem->setLine(gridGeometry().left(), minorGridLineItemY,
+ gridGeometry().right(), minorGridLineItemY);
+ minorArrowLineItem->setLine(minorArrowLineItemX1, minorGridLineItemY,
+ minorArrowLineItemX2, minorGridLineItemY);
+
+ // check if the minor grid line and the minor axis arrow should be shown
+ bool minorGridLineVisible = (minorGridLineItemY >= gridGeometry().top()
+ && minorGridLineItemY <= gridGeometry().bottom());
+ minorGridLineItem->setVisible(minorGridLineVisible);
+ minorArrowLineItem->setVisible(minorGridLineVisible);
+ }
+ }
}
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/axis/verticalaxis_p.h b/src/charts/axis/verticalaxis_p.h
index 17c82f7f..687d03d2 100644
--- a/src/charts/axis/verticalaxis_p.h
+++ b/src/charts/axis/verticalaxis_p.h
@@ -36,8 +36,8 @@
//
// We mean it.
-#ifndef VERTICALAXIS_P_H_
-#define VERTICALAXIS_P_H_
+#ifndef VERTICALAXIS_P_H
+#define VERTICALAXIS_P_H
#include <private/cartesianchartaxis_p.h>
@@ -46,14 +46,18 @@ QT_CHARTS_BEGIN_NAMESPACE
class VerticalAxis : public CartesianChartAxis
{
public:
- VerticalAxis(QAbstractAxis *axis, QGraphicsItem *item = 0, bool intervalAxis = false);
+ VerticalAxis(QAbstractAxis *axis, QGraphicsItem *item = nullptr, bool intervalAxis = false);
~VerticalAxis();
- QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
+
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const Q_DECL_OVERRIDE;
+
protected:
- void updateGeometry();
+ void updateGeometry() Q_DECL_OVERRIDE;
+private:
+ void updateMinorTickGeometry();
};
QT_CHARTS_END_NAMESPACE
-#endif /* VERTICALAXIS_P_H_ */
+#endif // VERTICALAXIS_P_H
diff --git a/src/charts/barchart/abstractbarchartitem.cpp b/src/charts/barchart/abstractbarchartitem.cpp
index 1305ea44..9395eef0 100644
--- a/src/charts/barchart/abstractbarchartitem.cpp
+++ b/src/charts/barchart/abstractbarchartitem.cpp
@@ -33,12 +33,12 @@
#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>
-
#include <private/chartdataset_p.h>
+#include <QtCore/QtMath>
#include <QtGui/QPainter>
#include <QtGui/QTextDocument>
@@ -47,7 +47,13 @@ QT_CHARTS_BEGIN_NAMESPACE
AbstractBarChartItem::AbstractBarChartItem(QAbstractBarSeries *series, QGraphicsItem* item) :
ChartItem(series->d_func(),item),
m_animation(0),
- m_series(series)
+ m_series(series),
+ m_firstCategory(-1),
+ m_lastCategory(-2),
+ m_categoryCount(0),
+ m_labelItemsMissing(false),
+ m_orientation(Qt::Horizontal),
+ m_resetAnimation(true)
{
setFlag(ItemClipsChildrenToShape);
@@ -56,6 +62,12 @@ AbstractBarChartItem::AbstractBarChartItem(QAbstractBarSeries *series, QGraphics
connect(series->d_func(), SIGNAL(updatedBars()), this, SLOT(handleUpdatedBars()));
connect(series->d_func(), SIGNAL(labelsVisibleChanged(bool)), this, SLOT(handleLabelsVisibleChanged(bool)));
connect(series->d_func(), SIGNAL(restructuredBars()), this, SLOT(handleDataStructureChanged()));
+ connect(series->d_func(), &QAbstractBarSeriesPrivate::setValueChanged,
+ this, &AbstractBarChartItem::handleBarValueChange);
+ connect(series->d_func(), &QAbstractBarSeriesPrivate::setValueAdded,
+ this, &AbstractBarChartItem::handleBarValueAdd);
+ connect(series->d_func(), &QAbstractBarSeriesPrivate::setValueRemoved,
+ this, &AbstractBarChartItem::handleBarValueRemove);
connect(series, SIGNAL(visibleChanged()), this, SLOT(handleVisibleChanged()));
connect(series, SIGNAL(opacityChanged()), this, SLOT(handleOpacityChanged()));
connect(series, SIGNAL(labelsFormatChanged(QString)), this, SLOT(handleUpdatedBars()));
@@ -63,10 +75,13 @@ 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();
- handleVisibleChanged();
- handleUpdatedBars();
}
AbstractBarChartItem::~AbstractBarChartItem()
@@ -85,14 +100,38 @@ QRectF AbstractBarChartItem::boundingRect() const
return m_rect;
}
+void AbstractBarChartItem::initializeFullLayout()
+{
+ qreal setCount = m_series->count();
+
+ for (int set = 0; set < setCount; set++) {
+ QBarSet *barSet = m_series->barSets().at(set);
+ const QList<Bar *> bars = m_barMap.value(barSet);
+ 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);
+ }
+ }
+}
+
void AbstractBarChartItem::applyLayout(const QVector<QRectF> &layout)
{
QSizeF size = geometry().size();
if (geometry().size().isValid()) {
if (m_animation) {
- if (m_layout.count() == 0 || m_oldSize != size) {
- initializeLayout();
- 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_resetAnimation = false;
}
m_animation->setup(m_layout, layout);
presenter()->startAnimation(m_animation);
@@ -106,29 +145,41 @@ void AbstractBarChartItem::applyLayout(const QVector<QRectF> &layout)
void AbstractBarChartItem::setAnimation(BarAnimation *animation)
{
m_animation = animation;
+ m_resetAnimation = true;
}
void AbstractBarChartItem::setLayout(const QVector<QRectF> &layout)
{
- if (layout.count() != m_bars.count())
+ int setCount = m_series->count();
+ if (layout.size() != m_layout.size() || m_barMap.size() != setCount)
return;
m_layout = layout;
- for (int i = 0; i < m_bars.count(); i++)
- m_bars.at(i)->setRect(layout.at(i));
+ 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 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::handleDomainUpdated()
+void AbstractBarChartItem::resetAnimation()
{
- m_domainMinX = domain()->minX();
- m_domainMaxX = domain()->maxX();
- m_domainMinY = domain()->minY();
- m_domainMaxY = domain()->maxY();
+ m_resetAnimation = true;
+}
+void AbstractBarChartItem::handleDomainUpdated()
+{
QRectF rect(QPointF(0,0),domain()->size());
if(m_rect != rect){
@@ -143,71 +194,52 @@ void AbstractBarChartItem::handleLayoutChanged()
{
if ((m_rect.width() <= 0) || (m_rect.height() <= 0))
return; // rect size zero.
+ updateBarItems();
QVector<QRectF> layout = calculateLayout();
- applyLayout(layout);
handleUpdatedBars();
+ applyLayout(layout);
}
void AbstractBarChartItem::handleLabelsVisibleChanged(bool visible)
{
- foreach (QGraphicsTextItem *label, m_labels)
- label->setVisible(visible);
+ bool newVisible = visible && m_series->isVisible();
+ QMapIterator<QBarSet *, QList<Bar *> > i(m_barMap);
+ while (i.hasNext()) {
+ i.next();
+ const QList<Bar *> &bars = i.value();
+ for (int j = 0; j < bars.size(); j++) {
+ QGraphicsTextItem *label = bars.at(j)->labelItem();
+ if (label)
+ label->setVisible(newVisible);
+ }
+ }
+ if (newVisible) {
+ handleUpdatedBars();
+ positionLabels();
+ }
update();
}
void AbstractBarChartItem::handleDataStructureChanged()
{
- foreach (QGraphicsItem *item, childItems())
- delete item;
-
- m_bars.clear();
- m_labels.clear();
- m_layout.clear();
-
- // Create new graphic items for bars
- for (int c = 0; c < m_series->d_func()->categoryCount(); c++) {
- for (int s = 0; s < m_series->count(); s++) {
- QBarSet *set = m_series->d_func()->barsetAt(s);
-
- // Bars
- Bar *bar = new Bar(set, c, this);
- m_bars.append(bar);
- connect(bar, SIGNAL(clicked(int,QBarSet*)), m_series, SIGNAL(clicked(int,QBarSet*)));
- connect(bar, SIGNAL(hovered(bool, int, QBarSet*)), m_series, SIGNAL(hovered(bool, int, QBarSet*)));
- connect(bar, SIGNAL(pressed(int, QBarSet*)), m_series, SIGNAL(pressed(int, QBarSet*)));
- connect(bar, SIGNAL(released(int, QBarSet*)),
- m_series, SIGNAL(released(int, QBarSet*)));
- connect(bar, SIGNAL(doubleClicked(int, QBarSet*)),
- m_series, SIGNAL(doubleClicked(int, QBarSet*)));
- connect(bar, SIGNAL(clicked(int,QBarSet*)), set, SIGNAL(clicked(int)));
- connect(bar, SIGNAL(hovered(bool, int, QBarSet*)), set, SIGNAL(hovered(bool, int)));
- connect(bar, SIGNAL(pressed(int, QBarSet*)), set, SIGNAL(pressed(int)));
- connect(bar, SIGNAL(released(int, QBarSet*)), set, SIGNAL(released(int)));
- connect(bar, SIGNAL(doubleClicked(int, QBarSet*)), set, SIGNAL(doubleClicked(int)));
- // m_layout.append(QRectF(0, 0, 1, 1));
-
- // Labels
- QGraphicsTextItem *newLabel = new QGraphicsTextItem(this);
- newLabel->document()->setDocumentMargin(ChartPresenter::textMargin());
- m_labels.append(newLabel);
- }
- }
-
- if(themeManager()) themeManager()->updateSeries(m_series);
+ handleSetStructureChange();
handleLayoutChanged();
- handleVisibleChanged();
}
void AbstractBarChartItem::handleVisibleChanged()
{
bool visible = m_series->isVisible();
- if (visible)
- handleLabelsVisibleChanged(m_series->isLabelsVisible());
- else
- handleLabelsVisibleChanged(visible);
-
- foreach (QGraphicsItem *bar, m_bars)
- bar->setVisible(visible);
+ handleLabelsVisibleChanged(m_series->isLabelsVisible());
+
+ QMapIterator<QBarSet *, QList<Bar *> > i(m_barMap);
+ while (i.hasNext()) {
+ i.next();
+ const QList<Bar *> &bars = i.value();
+ for (int j = 0; j < bars.size(); j++) {
+ Bar *bar = bars.at(j);
+ bar->setVisible(visible && i.key()->at(bar->index()) != 0.0);
+ }
+ }
}
void AbstractBarChartItem::handleOpacityChanged()
@@ -220,40 +252,54 @@ void AbstractBarChartItem::handleUpdatedBars()
{
if (!m_series->d_func()->blockBarUpdate()) {
// Handle changes in pen, brush, labels etc.
- int categoryCount = m_series->d_func()->categoryCount();
int setCount = m_series->count();
- int itemIndex(0);
- static const QString valueTag(QLatin1String("@value"));
-
- for (int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QBarSetPrivate *barSet = m_series->d_func()->barsetAt(set)->d_ptr.data();
- Bar *bar = m_bars.at(itemIndex);
- bar->setPen(barSet->m_pen);
- bar->setBrush(barSet->m_brush);
- bar->update();
-
- QGraphicsTextItem *label = m_labels.at(itemIndex);
- QString valueLabel;
- if (presenter()) { // At startup presenter is not yet set, yet somehow update comes
- if (barSet->value(category) == 0) {
- label->setVisible(false);
- } else {
- label->setVisible(m_series->isLabelsVisible());
- if (m_series->labelsFormat().isEmpty()) {
- valueLabel = presenter()->numberToString(barSet->value(category));
+ const bool seriesVisualsDirty = m_series->d_func()->visualsDirty();
+ const bool seriesLabelsDirty = m_series->d_func()->labelsDirty();
+ m_series->d_func()->setVisualsDirty(false);
+
+ const bool updateLabels =
+ m_series->isLabelsVisible() && m_series->isVisible() && presenter();
+ if (updateLabels) {
+ createLabelItems();
+ m_series->d_func()->setLabelsDirty(false);
+ }
+
+ for (int set = 0; set < setCount; set++) {
+ QBarSet *barSet = m_series->d_func()->barsetAt(set);
+ QBarSetPrivate *barSetP = barSet->d_ptr.data();
+ const bool setVisualsDirty = barSetP->visualsDirty();
+ const bool setLabelsDirty = barSetP->labelsDirty();
+ barSetP->setVisualsDirty(false);
+ if (updateLabels)
+ barSetP->setLabelsDirty(false);
+ const int actualBarCount = barSet->count();
+ 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 > bar->index()) {
+ if (seriesLabelsDirty || setLabelsDirty || bar->labelDirty()) {
+ bar->setLabelDirty(false);
+ QGraphicsTextItem *label = bar->labelItem();
+ QString valueLabel;
+ qreal value = barSetP->value(bar->index());
+ if (value == 0.0) {
+ label->setVisible(false);
} else {
- valueLabel = m_series->labelsFormat();
- valueLabel.replace(valueTag,
- presenter()->numberToString(barSet->value(category)));
+ label->setVisible(m_series->isLabelsVisible());
+ valueLabel = generateLabelText(set, bar->index(), value);
}
+ label->setHtml(valueLabel);
+ label->setFont(barSetP->m_labelFont);
+ label->setDefaultTextColor(barSetP->m_labelBrush.color());
+ label->update();
}
}
- label->setHtml(valueLabel);
- label->setFont(barSet->m_labelFont);
- label->setDefaultTextColor(barSet->m_labelBrush.color());
- label->update();
- itemIndex++;
}
}
}
@@ -269,101 +315,398 @@ void AbstractBarChartItem::positionLabels()
// By default position labels on horizontal bar series
// Vertical bar series overload positionLabels() to call positionLabelsVertical()
+ if (!m_series->isLabelsVisible())
+ return;
+ createLabelItems();
+
QTransform transform;
const qreal angle = m_series->d_func()->labelsAngle();
if (angle != 0.0)
transform.rotate(angle);
- for (int i = 0; i < m_layout.count(); i++) {
- QGraphicsTextItem *label = m_labels.at(i);
-
- QRectF labelRect = label->boundingRect();
- QPointF center = labelRect.center();
+ 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 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(bar->layoutIndex()).center().y() - center.y();
+
+ int xDiff = 0;
+ if (angle != 0.0) {
+ label->setTransformOriginPoint(center.x(), center.y());
+ label->setRotation(m_series->d_func()->labelsAngle());
+ qreal oldWidth = labelRect.width();
+ labelRect = transform.mapRect(labelRect);
+ xDiff = (labelRect.width() - oldWidth) / 2;
+ }
- qreal xPos = 0;
- qreal yPos = m_layout.at(i).center().y() - center.y();
+ int offset = bar->pen().width() / 2 + 2;
+
+ switch (m_series->labelsPosition()) {
+ case QAbstractBarSeries::LabelsCenter:
+ xPos = m_layout.at(bar->layoutIndex()).center().x() - center.x();
+ break;
+ case QAbstractBarSeries::LabelsInsideEnd:
+ xPos = m_layout.at(bar->layoutIndex()).right() - labelRect.width() - offset + xDiff;
+ break;
+ case QAbstractBarSeries::LabelsInsideBase:
+ xPos = m_layout.at(bar->layoutIndex()).left() + offset + xDiff;
+ break;
+ case QAbstractBarSeries::LabelsOutsideEnd:
+ xPos = m_layout.at(bar->layoutIndex()).right() + offset + xDiff;
+ break;
+ default:
+ // Invalid position, never comes here
+ break;
+ }
- int xDiff = 0;
- if (angle != 0.0) {
- label->setTransformOriginPoint(center.x(), center.y());
- label->setRotation(m_series->d_func()->labelsAngle());
- qreal oldWidth = labelRect.width();
- labelRect = transform.mapRect(labelRect);
- xDiff = (labelRect.width() - oldWidth) / 2;
+ label->setPos(xPos, yPos);
+ label->setZValue(zValue() + 1);
}
+ }
+}
- int offset = m_bars.at(i)->pen().width() / 2 + 2;
-
- switch (m_series->labelsPosition()) {
- case QAbstractBarSeries::LabelsCenter:
- xPos = m_layout.at(i).center().x() - center.x();
- break;
- case QAbstractBarSeries::LabelsInsideEnd:
- xPos = m_layout.at(i).right() - labelRect.width() - offset + xDiff;
- break;
- case QAbstractBarSeries::LabelsInsideBase:
- xPos = m_layout.at(i).left() + offset + xDiff;
- break;
- case QAbstractBarSeries::LabelsOutsideEnd:
- xPos = m_layout.at(i).right() + offset + xDiff;
- break;
- default:
- // Invalid position, never comes here
- break;
- }
+void AbstractBarChartItem::handleBarValueChange(int index, QtCharts::QBarSet *barset)
+{
+ markLabelsDirty(barset, index, 1);
+ handleLayoutChanged();
+}
+
+void AbstractBarChartItem::handleBarValueAdd(int index, int count, QBarSet *barset)
+{
+ Q_UNUSED(count)
+
+ // Value insertions into middle of barset need to dirty the rest of the labels of the set
+ markLabelsDirty(barset, index, -1);
+ handleLayoutChanged();
+}
+
+void AbstractBarChartItem::handleBarValueRemove(int index, int count, QBarSet *barset)
+{
+ Q_UNUSED(count)
+
+ // Value removals from the middle of barset need to dirty the rest of the labels of the set.
+ markLabelsDirty(barset, index, -1);
+ handleLayoutChanged();
+}
- label->setPos(xPos, yPos);
- label->setZValue(zValue() + 1);
+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())
+ return;
+ createLabelItems();
+
QTransform transform;
const qreal angle = m_series->d_func()->labelsAngle();
if (angle != 0.0)
transform.rotate(angle);
- for (int i = 0; i < m_layout.count(); i++) {
- QGraphicsTextItem *label = m_labels.at(i);
+ 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 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(bar->layoutIndex()).center().x() - center.x();
+ qreal yPos = 0;
+
+ int yDiff = 0;
+ if (angle != 0.0) {
+ label->setTransformOriginPoint(center.x(), center.y());
+ label->setRotation(m_series->d_func()->labelsAngle());
+ qreal oldHeight = labelRect.height();
+ labelRect = transform.mapRect(labelRect);
+ yDiff = (labelRect.height() - oldHeight) / 2;
+ }
+
+ int offset = bar->pen().width() / 2 + 2;
+
+ switch (m_series->labelsPosition()) {
+ case QAbstractBarSeries::LabelsCenter:
+ yPos = m_layout.at(bar->layoutIndex()).center().y() - center.y();
+ break;
+ case QAbstractBarSeries::LabelsInsideEnd:
+ yPos = m_layout.at(bar->layoutIndex()).top() + offset + yDiff;
+ break;
+ case QAbstractBarSeries::LabelsInsideBase:
+ yPos = m_layout.at(bar->layoutIndex()).bottom() - labelRect.height() - offset + yDiff;
+ break;
+ case QAbstractBarSeries::LabelsOutsideEnd:
+ yPos = m_layout.at(bar->layoutIndex()).top() - labelRect.height() - offset + yDiff;
+ break;
+ default:
+ // Invalid position, never comes here
+ break;
+ }
+
+ label->setPos(xPos, yPos);
+ label->setZValue(zValue() + 1);
+ }
+ }
+}
- QRectF labelRect = label->boundingRect();
- QPointF center = labelRect.center();
+void AbstractBarChartItem::createLabelItems()
+{
+ if (!m_labelItemsMissing)
+ return;
- qreal xPos = m_layout.at(i).center().x() - center.x();
- qreal yPos = 0;
+ m_labelItemsMissing = false;
+
+ QMapIterator<QBarSet *, QList<Bar *> > i(m_barMap);
+ while (i.hasNext()) {
+ i.next();
+ const QList<Bar *> &bars = i.value();
+ for (int j = 0; j < bars.size(); j++) {
+ QGraphicsTextItem *label = bars.at(j)->labelItem();
+ if (!label) {
+ QGraphicsTextItem *newLabel = new QGraphicsTextItem(this);
+ newLabel->document()->setDocumentMargin(ChartPresenter::textMargin());
+ bars.at(j)->setLabelItem(newLabel);
+ }
+ }
+ }
+}
- int yDiff = 0;
- if (angle != 0.0) {
- label->setTransformOriginPoint(center.x(), center.y());
- label->setRotation(m_series->d_func()->labelsAngle());
- qreal oldHeight = labelRect.height();
- labelRect = transform.mapRect(labelRect);
- yDiff = (labelRect.height() - oldHeight) / 2;
+// This function is called whenever barsets change
+void AbstractBarChartItem::handleSetStructureChange()
+{
+ QList<QBarSet *> newSets = m_series->barSets();
+ QList<QBarSet *> oldSets = m_barMap.keys();
+
+ // Remove obsolete sets
+ for (int i = 0; i < oldSets.size(); i++) {
+ if (!newSets.contains(oldSets.at(i))) {
+ qDeleteAll(m_barMap.value(oldSets.at(i)));
+ m_barMap.remove(oldSets.at(i));
}
+ }
- int offset = m_bars.at(i)->pen().width() / 2 + 2;
-
- switch (m_series->labelsPosition()) {
- case QAbstractBarSeries::LabelsCenter:
- yPos = m_layout.at(i).center().y() - center.y();
- break;
- case QAbstractBarSeries::LabelsInsideEnd:
- yPos = m_layout.at(i).top() + offset + yDiff;
- break;
- case QAbstractBarSeries::LabelsInsideBase:
- yPos = m_layout.at(i).bottom() - labelRect.height() - offset + yDiff;
- break;
- case QAbstractBarSeries::LabelsOutsideEnd:
- yPos = m_layout.at(i).top() - labelRect.height() - offset + yDiff;
- break;
- default:
- // Invalid position, never comes here
- break;
+ // Create new sets
+ for (int s = 0; s < newSets.size(); s++) {
+ QBarSet *set = newSets.at(s);
+ if (!m_barMap.contains(set)) {
+ QList<Bar *> bars;
+ m_barMap.insert(set, bars);
+ } else {
+ // Dirty the old set labels to ensure labels are updated correctly on all series types
+ markLabelsDirty(set, 0, -1);
}
+ }
+
+ if (themeManager())
+ themeManager()->updateSeries(m_series);
+}
+
+QString AbstractBarChartItem::generateLabelText(int set, int category, qreal value)
+{
+ Q_UNUSED(set);
+ Q_UNUSED(category);
+ static const QString valueTag(QLatin1String("@value"));
+ QString valueLabel;
+ if (m_series->labelsFormat().isEmpty()) {
+ valueLabel = presenter()->numberToString(value);
+ } else {
+ valueLabel = m_series->labelsFormat();
+ valueLabel.replace(valueTag, presenter()->numberToString(value));
+ }
+ return valueLabel;
+}
+
+void AbstractBarChartItem::updateBarItems()
+{
+ int min(0);
+ int max(0);
+ if (m_orientation == Qt::Vertical) {
+ min = qFloor(domain()->minX()) - 1;
+ max = qCeil(domain()->maxX()) + 1;
+ } else {
+ min = qFloor(domain()->minY()) - 1;
+ max = qCeil(domain()->maxY()) + 1;
+ }
+
+ int lastBarIndex = m_series->d_func()->categoryCount() - 1;
+
+ if (lastBarIndex < 0) {
+ // Indicate invalid categories by negatives
+ // Last should be one less than the first to make loops work right in case of no categories
+ m_firstCategory = -1;
+ m_lastCategory = -2;
+ m_categoryCount = 0;
+ } else {
+ m_firstCategory = qMax(qMin(min, lastBarIndex), 0);
+ m_lastCategory = qMax(qMin(max, lastBarIndex), m_firstCategory);
+ m_categoryCount = m_lastCategory - m_firstCategory + 1;
+ }
- label->setPos(xPos, yPos);
- label->setZValue(zValue() + 1);
+ QList<QBarSet *> newSets = m_series->barSets();
+ QList<QBarSet *> oldSets = m_barMap.keys();
+
+ Q_ASSERT(newSets.size() == oldSets.size());
+
+ int layoutSize = m_categoryCount * newSets.size();
+
+ QVector<QRectF> oldLayout = m_layout;
+ if (layoutSize != m_layout.size())
+ m_layout.resize(layoutSize);
+
+ // 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);
+ QList<Bar *> bars = m_barMap.value(set);
+ int addCount = m_categoryCount - bars.size();
+ if (addCount > 0) {
+ for (int c = 0; c < addCount; c++) {
+ Bar *bar = new Bar(set, this);
+ bars.append(bar);
+ connect(bar, &Bar::clicked, m_series, &QAbstractBarSeries::clicked);
+ connect(bar, &Bar::hovered, m_series, &QAbstractBarSeries::hovered);
+ connect(bar, &Bar::pressed, m_series, &QAbstractBarSeries::pressed);
+ connect(bar, &Bar::released, m_series, &QAbstractBarSeries::released);
+ connect(bar, &Bar::doubleClicked, m_series, &QAbstractBarSeries::doubleClicked);
+
+ connect(bar, &Bar::clicked, set, &QBarSet::clicked);
+ connect(bar, &Bar::hovered, set, &QBarSet::hovered);
+ connect(bar, &Bar::pressed, set, &QBarSet::pressed);
+ connect(bar, &Bar::released, set, &QBarSet::released);
+ connect(bar, &Bar::doubleClicked, set, &QBarSet::doubleClicked);
+
+ m_labelItemsMissing = true;
+ }
+ }
+ // Update bar indexes
+ QHash<int, Bar*> indexMap;
+ QVector<Bar *> unassignedBars(m_categoryCount, nullptr);
+ int unassignedIndex(0);
+ QList<Bar *> newBars;
+ newBars.reserve(m_categoryCount);
+ for (int c = 0; c < bars.size(); c++) {
+ Bar *bar = bars.at(c);
+ if (bar->index() < m_firstCategory || bar->index() > m_lastCategory) {
+ // Delete excess unassigned bars first
+ if (addCount < 0) {
+ addCount++;
+ delete bar;
+ bar = nullptr;
+ } else {
+ unassignedBars[unassignedIndex++] = bar;
+ bar->setLayoutIndex(layoutIndex);
+ newBars.append(bar);
+ layoutIndex++;
+ }
+ } else {
+ indexMap.insert(bar->index(), bar);
+ newBars.append(bar);
+ m_layout[layoutIndex] = oldLayout.at(bar->layoutIndex());
+ bar->setLayoutIndex(layoutIndex);
+ layoutIndex++;
+ }
+ }
+ unassignedIndex = 0;
+ for (int c = m_firstCategory; c <= m_lastCategory; c++) {
+ Bar *bar = indexMap.value(c);
+ if (!bar) {
+ bar = unassignedBars.at(unassignedIndex++);
+ 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);
+ }
+ }
+
+ m_barMap.insert(set, newBars);
+ }
+}
+
+void AbstractBarChartItem::markLabelsDirty(QBarSet *barset, int index, int count)
+{
+ Q_ASSERT(barset);
+
+ 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;
}
}
diff --git a/src/charts/barchart/abstractbarchartitem_p.h b/src/charts/barchart/abstractbarchartitem_p.h
index 3998312e..d65b1bc9 100644
--- a/src/charts/barchart/abstractbarchartitem_p.h
+++ b/src/charts/barchart/abstractbarchartitem_p.h
@@ -51,6 +51,8 @@ class Bar;
class QAxisCategories;
class QChart;
class BarAnimation;
+class QBarSetPrivate;
+class QAbstractAxis;
class AbstractBarChartItem : public ChartItem
{
@@ -64,12 +66,13 @@ public:
QRectF boundingRect() const;
virtual QVector<QRectF> calculateLayout() = 0;
- virtual void initializeLayout() = 0;
+ void initializeFullLayout();
+ virtual void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation) = 0;
virtual void applyLayout(const QVector<QRectF> &layout);
virtual void setAnimation(BarAnimation *animation);
void setLayout(const QVector<QRectF> &layout);
- void updateLayout(const QVector<QRectF> &layout);
QRectF geometry() const { return m_rect;}
+ void resetAnimation();
public Q_SLOTS:
void handleDomainUpdated();
@@ -78,17 +81,23 @@ public Q_SLOTS:
void handleDataStructureChanged(); // structure of of series has changed, recreate graphic items
void handleVisibleChanged();
void handleOpacityChanged();
- virtual void handleUpdatedBars();
+ void handleUpdatedBars();
void handleLabelsPositionChanged();
virtual void positionLabels();
+ 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();
-
- qreal m_domainMinX;
- qreal m_domainMaxX;
- qreal m_domainMinY;
- qreal m_domainMaxY;
+ void createLabelItems();
+ void handleSetStructureChange();
+ virtual QString generateLabelText(int set, int category, qreal value);
+ void updateBarItems();
+ virtual void markLabelsDirty(QBarSet *barset, int index, int count);
+ void calculateSeriesPositionAdjustmentAndWidth();
QRectF m_rect;
QVector<QRectF> m_layout;
@@ -96,9 +105,17 @@ protected:
BarAnimation *m_animation;
QAbstractBarSeries *m_series; // Not owned.
- QList<Bar *> m_bars;
- QList<QGraphicsTextItem *> m_labels;
+ QMap<QBarSet *, QList<Bar *> > m_barMap;
+ QMap<QBarSet *, QHash<int, Bar *> > m_indexForBarMap;
+ int m_firstCategory;
+ int m_lastCategory;
+ int m_categoryCount;
QSizeF m_oldSize;
+ 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/bar.cpp b/src/charts/barchart/bar.cpp
index 7177b10d..58af2ba4 100644
--- a/src/charts/barchart/bar.cpp
+++ b/src/charts/barchart/bar.cpp
@@ -35,11 +35,15 @@
QT_CHARTS_BEGIN_NAMESPACE
-Bar::Bar(QBarSet *barset, int index, QGraphicsItem *parent) : QGraphicsRectItem(parent),
- m_index(index),
+Bar::Bar(QBarSet *barset, QGraphicsItem *parent) : QGraphicsRectItem(parent),
+ m_index(-255),
+ m_layoutIndex(-255),
m_barset(barset),
+ m_labelItem(nullptr),
m_hovering(false),
- m_mousePressed(false)
+ m_mousePressed(false),
+ m_visualsDirty(true),
+ m_labelDirty(true)
{
setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
setAcceptHoverEvents(true);
@@ -51,6 +55,7 @@ Bar::~Bar()
// End hover event, if bar is deleted during it
if (m_hovering)
emit hovered(false, m_index, m_barset);
+ delete m_labelItem;
}
void Bar::mousePressEvent(QGraphicsSceneMouseEvent *event)
diff --git a/src/charts/barchart/bar_p.h b/src/charts/barchart/bar_p.h
index f7c59ccf..03fd19a7 100644
--- a/src/charts/barchart/bar_p.h
+++ b/src/charts/barchart/bar_p.h
@@ -41,6 +41,7 @@
#include <QtCharts/QChartGlobal>
#include <QtWidgets/QGraphicsRectItem>
+#include <QtWidgets/QGraphicsTextItem>
QT_CHARTS_BEGIN_NAMESPACE
@@ -51,7 +52,7 @@ class Bar : public QObject, public QGraphicsRectItem
{
Q_OBJECT
public:
- Bar(QBarSet *barset, int index, QGraphicsItem *parent = 0);
+ Bar(QBarSet *barset, QGraphicsItem *parent = 0);
~Bar();
public:
@@ -62,6 +63,18 @@ public:
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+ void setVisualsDirty(bool dirty) { m_visualsDirty = dirty; }
+ bool visualsDirty() const { return m_visualsDirty; }
+ void setLabelDirty(bool dirty) { m_labelDirty = dirty; }
+ bool labelDirty() const { return m_labelDirty; }
+
+ void setLabelItem(QGraphicsTextItem *labelItem) { m_labelItem = labelItem; }
+ QGraphicsTextItem *labelItem() const { return m_labelItem; }
+
+ void setIndex(int index) { m_index = index; }
+ int index() const { return m_index; }
+ void setLayoutIndex(int index) { m_layoutIndex = index; }
+ int layoutIndex() const { return m_layoutIndex; }
Q_SIGNALS:
void clicked(int index, QBarSet *barset);
@@ -72,10 +85,13 @@ Q_SIGNALS:
private:
int m_index;
+ int m_layoutIndex;
QBarSet *m_barset;
+ QGraphicsTextItem *m_labelItem;
bool m_hovering;
-
bool m_mousePressed;
+ bool m_visualsDirty;
+ bool m_labelDirty;
};
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp b/src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp
index d0fd83d4..ee0822a9 100644
--- a/src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp
+++ b/src/charts/barchart/horizontal/bar/horizontalbarchartitem.cpp
@@ -39,60 +39,88 @@ HorizontalBarChartItem::HorizontalBarChartItem(QAbstractBarSeries *series, QGrap
{
}
-void HorizontalBarChartItem::initializeLayout()
+void HorizontalBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
{
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ QRectF rect;
- m_layout.clear();
- for(int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QRectF rect;
- QPointF topLeft;
- QPointF bottomRight;
- 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);
- } 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);
- }
-
- if (!m_validData)
- return;
+ 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());
+ rect.setBottom(oldTop);
+ rect.setRight(rect.left());
+ } else {
+ QPointF topLeft;
+ QPointF bottomRight;
+ 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 = topLeftPoint(set, setCount, category, barWidth, domain()->minX());
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, domain()->minX());
+ } else {
+ topLeft = topLeftPoint(set, setCount, category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, 0.0);
+ }
+ if (m_validData) {
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- m_layout.append(rect.normalized());
}
}
+ 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.resize(m_layout.size());
- // Use temporary qreals for accuracy
- qreal categoryCount = m_series->d_func()->categoryCount();
- 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 category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- qreal value = m_series->barSets().at(set)->at(category);
+ for (int set = 0; set < setCount; set++) {
+ 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 720eadb7..9f616768 100644
--- a/src/charts/barchart/horizontal/bar/horizontalbarchartitem_p.h
+++ b/src/charts/barchart/horizontal/bar/horizontalbarchartitem_p.h
@@ -52,7 +52,9 @@ public:
private:
virtual QVector<QRectF> calculateLayout();
- void initializeLayout();
+ 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 ffebbaae..8bda21ed 100644
--- a/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem.cpp
+++ b/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem.cpp
@@ -39,115 +39,130 @@ HorizontalPercentBarChartItem::HorizontalPercentBarChartItem(QAbstractBarSeries
{
}
-void HorizontalPercentBarChartItem::initializeLayout()
+QString HorizontalPercentBarChartItem::generateLabelText(int set, int category, qreal value)
{
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ Q_UNUSED(value)
- m_layout.clear();
- for(int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QRectF rect;
- QPointF topLeft;
- QPointF bottomRight;
- 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);
- } else {
- topLeft = domain()->calculateGeometryPoint(QPointF(0, category - barWidth / 2), m_validData);
- bottomRight = domain()->calculateGeometryPoint(QPointF(0, category + barWidth / 2), m_validData);
- }
+ static const QString valueTag(QLatin1String("@value"));
+ qreal p = m_series->d_func()->percentageAt(set, category) * 100.0;
+ QString vString(presenter()->numberToString(p, 'f', 0));
+ QString valueLabel;
+ if (m_series->labelsFormat().isEmpty()) {
+ vString.append(QStringLiteral("%"));
+ valueLabel = vString;
+ } else {
+ valueLabel = m_series->labelsFormat();
+ valueLabel.replace(valueTag, vString);
+ }
- if (!m_validData)
- return;
+ return valueLabel;
+}
+void HorizontalPercentBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
+{
+ Q_UNUSED(set)
+ Q_UNUSED(resetAnimation)
+
+ QRectF rect;
+
+ 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;
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
+ if (domain()->type() == AbstractDomain::LogXYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ topLeft = topLeftPoint(category, barWidth, domain()->minX());
+ bottomRight = bottomRightPoint(category, barWidth, domain()->minX());
+ } else {
+ topLeft = topLeftPoint(category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(category, barWidth, 0.0);
+ }
+ if (m_validData) {
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- m_layout.append(rect.normalized());
}
}
+ m_layout[layoutIndex] = rect.normalized();
}
-QVector<QRectF> HorizontalPercentBarChartItem::calculateLayout()
+void HorizontalPercentBarChartItem::markLabelsDirty(QBarSet *barset, int index, int count)
{
- QVector<QRectF> layout;
+ 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), index, count);
+}
- // Use temporary qreals for accuracy
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+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);
+}
- for(int category = 0; category < categoryCount; category++) {
- qreal sum = 0;
- qreal categorySum = m_series->d_func()->categorySum(category);
- for (int set = 0; set < setCount; set++) {
- qreal value = m_series->barSets().at(set)->at(category);
+QVector<QRectF> HorizontalPercentBarChartItem::calculateLayout()
+{
+ QVector<QRectF> layout;
+ layout.resize(m_layout.size());
+
+ 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++) {
+ 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;
}
}
return layout;
}
-void HorizontalPercentBarChartItem::handleUpdatedBars()
-{
- // Handle changes in pen, brush, labels etc.
- int categoryCount = m_series->d_func()->categoryCount();
- int setCount = m_series->count();
- int itemIndex(0);
- static const QString valueTag(QLatin1String("@value"));
-
- for (int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QBarSetPrivate *barSet = m_series->d_func()->barsetAt(set)->d_ptr.data();
- Bar *bar = m_bars.at(itemIndex);
- bar->setPen(barSet->m_pen);
- bar->setBrush(barSet->m_brush);
- bar->update();
-
- QGraphicsTextItem *label = m_labels.at(itemIndex);
- qreal p = m_series->d_func()->percentageAt(set, category) * 100.0;
- QString vString(presenter()->numberToString(p, 'f', 0));
- QString valueLabel;
- if (p == 0) {
- label->setVisible(false);
- } else {
- label->setVisible(m_series->isLabelsVisible());
- if (m_series->labelsFormat().isEmpty()) {
- vString.append(QStringLiteral("%"));
- valueLabel = vString;
- } else {
- valueLabel = m_series->labelsFormat();
- valueLabel.replace(valueTag, vString);
- }
- }
- label->setHtml(valueLabel);
- label->setFont(barSet->m_labelFont);
- label->setDefaultTextColor(barSet->m_labelBrush.color());
- label->update();
- itemIndex++;
- }
- }
-}
-
#include "moc_horizontalpercentbarchartitem_p.cpp"
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h b/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h
index ed6f2c3a..51180258 100644
--- a/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h
+++ b/src/charts/barchart/horizontal/percent/horizontalpercentbarchartitem_p.h
@@ -49,11 +49,14 @@ class HorizontalPercentBarChartItem : public AbstractBarChartItem
Q_OBJECT
public:
HorizontalPercentBarChartItem(QAbstractBarSeries *series, QGraphicsItem* item = 0);
- void handleUpdatedBars();
+ virtual QString generateLabelText(int set, int category, qreal value);
private:
virtual QVector<QRectF> calculateLayout();
- void initializeLayout();
+ void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation);
+ 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 78ccd9f4..211aa4e5 100644
--- a/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem.cpp
+++ b/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem.cpp
@@ -39,72 +39,137 @@ HorizontalStackedBarChartItem::HorizontalStackedBarChartItem(QAbstractBarSeries
{
}
-void HorizontalStackedBarChartItem::initializeLayout()
+void HorizontalStackedBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
{
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ Q_UNUSED(set)
+ Q_UNUSED(resetAnimation)
- m_layout.clear();
- for(int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QRectF rect;
- QPointF topLeft;
- QPointF bottomRight;
- 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);
- } else {
- topLeft = domain()->calculateGeometryPoint(QPointF(0, category - barWidth / 2), m_validData);
- bottomRight = domain()->calculateGeometryPoint(QPointF(0, category + barWidth / 2), m_validData);
+ QRectF rect;
+ 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;
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
- if (!m_validData)
- return;
+ if (domain()->type() == AbstractDomain::LogXYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ topLeft = topLeftPoint(category, barWidth, domain()->minX());
+ bottomRight = bottomRightPoint(category, barWidth, domain()->minX());
+ } else {
+ topLeft = topLeftPoint(category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(category, barWidth, 0.0);
+ }
+ if (m_validData) {
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- m_layout.append(rect.normalized());
}
}
+ 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.resize(m_layout.size());
+
+ const int setCount = m_series->count();
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
- // Use temporary qreals for accuracy
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ QVector<qreal> positiveSums(m_categoryCount, 0.0);
+ QVector<qreal> negativeSums(m_categoryCount, 0.0);
- for(int category = 0; category < categoryCount; category++) {
- qreal positiveSum = 0;
- qreal negativeSum = 0;
- for (int set = 0; set < setCount; set++) {
- qreal value = m_series->barSets().at(set)->at(category);
+ for (int set = 0; set < setCount; set++) {
+ 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(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 6f1b6640..4fb8ce27 100644
--- a/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem_p.h
+++ b/src/charts/barchart/horizontal/stacked/horizontalstackedbarchartitem_p.h
@@ -52,7 +52,9 @@ public:
private:
virtual QVector<QRectF> calculateLayout();
- void initializeLayout();
+ 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 1cf9528b..cd27cd1c 100644
--- a/src/charts/barchart/qabstractbarseries.cpp
+++ b/src/charts/barchart/qabstractbarseries.cpp
@@ -587,6 +587,7 @@ void QAbstractBarSeries::setLabelsFormat(const QString &format)
Q_D(QAbstractBarSeries);
if (d->m_labelsFormat != format) {
d->m_labelsFormat = format;
+ d->setLabelsDirty(true);
emit labelsFormatChanged(format);
}
}
@@ -602,6 +603,7 @@ void QAbstractBarSeries::setLabelsAngle(qreal angle)
Q_D(QAbstractBarSeries);
if (d->m_labelsAngle != angle) {
d->m_labelsAngle = angle;
+ d->setLabelsDirty(true);
emit labelsAngleChanged(angle);
}
}
@@ -637,7 +639,9 @@ QAbstractBarSeriesPrivate::QAbstractBarSeriesPrivate(QAbstractBarSeries *q) :
m_blockBarUpdate(false),
m_labelsFormat(),
m_labelsPosition(QAbstractBarSeries::LabelsCenter),
- m_labelsAngle(0)
+ m_labelsAngle(0),
+ m_visualsDirty(true),
+ m_labelsDirty(true)
{
}
@@ -923,9 +927,14 @@ bool QAbstractBarSeriesPrivate::append(QBarSet *set)
return false; // Fail if set is already in list or set is null.
m_barSets.append(set);
- QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
- QObject::connect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
- QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::updatedBars,
+ this, &QAbstractBarSeriesPrivate::updatedBars);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueChanged,
+ this, &QAbstractBarSeriesPrivate::handleSetValueChange);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueAdded,
+ this, &QAbstractBarSeriesPrivate::handleSetValueAdd);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved,
+ this, &QAbstractBarSeriesPrivate::handleSetValueRemove);
emit restructuredBars(); // this notifies barchartitem
return true;
@@ -937,9 +946,14 @@ bool QAbstractBarSeriesPrivate::remove(QBarSet *set)
return false; // Fail if set is not in list
m_barSets.removeOne(set);
- QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
- QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
- QObject::disconnect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
+ QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::updatedBars,
+ this, &QAbstractBarSeriesPrivate::updatedBars);
+ QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueChanged,
+ this, &QAbstractBarSeriesPrivate::handleSetValueChange);
+ QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueAdded,
+ this, &QAbstractBarSeriesPrivate::handleSetValueAdd);
+ QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved,
+ this, &QAbstractBarSeriesPrivate::handleSetValueRemove);
emit restructuredBars(); // this notifies barchartitem
return true;
@@ -956,9 +970,14 @@ bool QAbstractBarSeriesPrivate::append(QList<QBarSet * > sets)
foreach (QBarSet *set, sets) {
m_barSets.append(set);
- QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
- QObject::connect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
- QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::updatedBars,
+ this, &QAbstractBarSeriesPrivate::updatedBars);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueChanged,
+ this, &QAbstractBarSeriesPrivate::handleSetValueChange);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueAdded,
+ this, &QAbstractBarSeriesPrivate::handleSetValueAdd);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved,
+ this, &QAbstractBarSeriesPrivate::handleSetValueRemove);
}
emit restructuredBars(); // this notifies barchartitem
@@ -979,9 +998,14 @@ bool QAbstractBarSeriesPrivate::remove(QList<QBarSet * > sets)
foreach (QBarSet *set, sets) {
m_barSets.removeOne(set);
- QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
- QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
- QObject::disconnect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
+ QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::updatedBars,
+ this, &QAbstractBarSeriesPrivate::updatedBars);
+ QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueChanged,
+ this, &QAbstractBarSeriesPrivate::handleSetValueChange);
+ QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueAdded,
+ this, &QAbstractBarSeriesPrivate::handleSetValueAdd);
+ QObject::disconnect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved,
+ this, &QAbstractBarSeriesPrivate::handleSetValueRemove);
}
emit restructuredBars(); // this notifies barchartitem
@@ -995,9 +1019,14 @@ bool QAbstractBarSeriesPrivate::insert(int index, QBarSet *set)
return false; // Fail if set is already in list or set is null.
m_barSets.insert(index, set);
- QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
- QObject::connect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
- QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::updatedBars,
+ this, &QAbstractBarSeriesPrivate::updatedBars);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueChanged,
+ this, &QAbstractBarSeriesPrivate::handleSetValueChange);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueAdded,
+ this, &QAbstractBarSeriesPrivate::handleSetValueAdd);
+ QObject::connect(set->d_ptr.data(), &QBarSetPrivate::valueRemoved,
+ this, &QAbstractBarSeriesPrivate::handleSetValueRemove);
emit restructuredBars(); // this notifies barchartitem
return true;
@@ -1008,7 +1037,6 @@ void QAbstractBarSeriesPrivate::initializeAxes()
Q_Q(QAbstractBarSeries);
foreach(QAbstractAxis* axis, m_axes) {
-
if (axis->type() == QAbstractAxis::AxisTypeBarCategory) {
switch (q->type()) {
case QAbstractSeries::SeriesTypeHorizontalBar:
@@ -1031,6 +1059,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
@@ -1060,6 +1093,27 @@ QAbstractAxis::AxisType QAbstractBarSeriesPrivate::defaultAxisType(Qt::Orientati
}
+void QAbstractBarSeriesPrivate::handleSetValueChange(int index)
+{
+ QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(sender());
+ if (priv)
+ emit setValueChanged(index, priv->q_ptr);
+}
+
+void QAbstractBarSeriesPrivate::handleSetValueAdd(int index, int count)
+{
+ QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(sender());
+ if (priv)
+ emit setValueAdded(index, count, priv->q_ptr);
+}
+
+void QAbstractBarSeriesPrivate::handleSetValueRemove(int index, int count)
+{
+ QBarSetPrivate *priv = qobject_cast<QBarSetPrivate *>(sender());
+ if (priv)
+ emit setValueRemoved(index, count, priv->q_ptr);
+}
+
void QAbstractBarSeriesPrivate::populateCategories(QBarCategoryAxis *axis)
{
QStringList categories;
@@ -1084,19 +1138,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)
@@ -1110,12 +1198,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/qabstractbarseries_p.h b/src/charts/barchart/qabstractbarseries_p.h
index 2dd7325a..62ddb420 100644
--- a/src/charts/barchart/qabstractbarseries_p.h
+++ b/src/charts/barchart/qabstractbarseries_p.h
@@ -97,6 +97,10 @@ public:
bool blockBarUpdate();
qreal labelsAngle() const;
+ void setVisualsDirty(bool dirty) { m_visualsDirty = dirty; }
+ bool visualsDirty() const { return m_visualsDirty; }
+ void setLabelsDirty(bool dirty) { m_labelsDirty = dirty; }
+ bool labelsDirty() const { return m_labelsDirty; }
Q_SIGNALS:
void clicked(int index, QBarSet *barset);
@@ -108,6 +112,14 @@ Q_SIGNALS:
void restructuredBars();
void labelsVisibleChanged(bool visible);
void visibleChanged();
+ void setValueChanged(int index, QBarSet *barset);
+ void setValueAdded(int index, int count, QBarSet *barset);
+ void setValueRemoved(int index, int count, QBarSet *barset);
+
+private Q_SLOTS:
+ void handleSetValueChange(int index);
+ void handleSetValueAdd(int index, int count);
+ void handleSetValueRemove(int index, int count);
private:
void populateCategories(QBarCategoryAxis *axis);
@@ -121,6 +133,8 @@ protected:
QString m_labelsFormat;
QAbstractBarSeries::LabelsPosition m_labelsPosition;
qreal m_labelsAngle;
+ bool m_visualsDirty;
+ bool m_labelsDirty;
private:
Q_DECLARE_PUBLIC(QAbstractBarSeries)
diff --git a/src/charts/barchart/qbarset.cpp b/src/charts/barchart/qbarset.cpp
index 7538bb50..bba1cac7 100644
--- a/src/charts/barchart/qbarset.cpp
+++ b/src/charts/barchart/qbarset.cpp
@@ -310,6 +310,7 @@ QBarSet::~QBarSet()
void QBarSet::setLabel(const QString label)
{
d_ptr->m_label = label;
+ d_ptr->setLabelsDirty(true);
emit labelChanged();
}
@@ -463,6 +464,7 @@ void QBarSet::setPen(const QPen &pen)
{
if (d_ptr->m_pen != pen) {
d_ptr->m_pen = pen;
+ d_ptr->setVisualsDirty(true);
emit d_ptr->updatedBars();
emit penChanged();
}
@@ -486,6 +488,7 @@ void QBarSet::setBrush(const QBrush &brush)
{
if (d_ptr->m_brush != brush) {
d_ptr->m_brush = brush;
+ d_ptr->setVisualsDirty(true);
emit d_ptr->updatedBars();
emit brushChanged();
}
@@ -509,6 +512,7 @@ void QBarSet::setLabelBrush(const QBrush &brush)
{
if (d_ptr->m_labelBrush != brush) {
d_ptr->m_labelBrush = brush;
+ d_ptr->setLabelsDirty(true);
emit d_ptr->updatedBars();
emit labelBrushChanged();
}
@@ -532,10 +536,10 @@ void QBarSet::setLabelFont(const QFont &font)
{
if (d_ptr->m_labelFont != font) {
d_ptr->m_labelFont = font;
+ d_ptr->setLabelsDirty(true);
emit d_ptr->updatedBars();
emit labelFontChanged();
}
-
}
/*!
@@ -626,7 +630,8 @@ QBarSetPrivate::QBarSetPrivate(const QString label, QBarSet *parent) : QObject(p
m_label(label),
m_pen(QChartPrivate::defaultPen()),
m_brush(QChartPrivate::defaultBrush()),
- m_labelBrush(QChartPrivate::defaultBrush())
+ m_labelBrush(QChartPrivate::defaultBrush()),
+ m_visualsDirty(true)
{
}
@@ -638,41 +643,43 @@ void QBarSetPrivate::append(QPointF value)
{
if (isValidValue(value)) {
m_values.append(value);
- emit restructuredBars();
+ emit valueAdded(m_values.size() - 1, 1);
}
}
void QBarSetPrivate::append(QList<QPointF> values)
{
+ int originalIndex = m_values.count();
for (int i = 0; i < values.count(); i++) {
if (isValidValue(values.at(i)))
m_values.append(values.at(i));
}
- emit restructuredBars();
+ emit valueAdded(originalIndex, values.size());
}
void QBarSetPrivate::append(QList<qreal> values)
{
- int index = m_values.count();
+ int originalIndex = m_values.count();
+ int index = originalIndex;
for (int i = 0; i < values.count(); i++) {
if (isValidValue(values.at(i))) {
m_values.append(QPointF(index, values.at(i)));
index++;
}
}
- emit restructuredBars();
+ emit valueAdded(originalIndex, values.size());
}
void QBarSetPrivate::insert(const int index, const qreal value)
{
m_values.insert(index, QPointF(index, value));
- emit restructuredBars();
+ emit valueAdded(index, 1);
}
void QBarSetPrivate::insert(const int index, const QPointF value)
{
m_values.insert(index, value);
- emit restructuredBars();
+ emit valueAdded(index, 1);
}
int QBarSetPrivate::remove(const int index, const int count)
@@ -689,20 +696,14 @@ int QBarSetPrivate::remove(const int index, const int count)
m_values.removeAt(index);
c++;
}
- emit restructuredBars();
+ emit valueRemoved(index, removeCount);
return removeCount;
}
void QBarSetPrivate::replace(const int index, const qreal value)
{
m_values.replace(index, QPointF(index, value));
- emit updatedLayout();
-}
-
-void QBarSetPrivate::replace(const int index, const QPointF value)
-{
- m_values.replace(index, value);
- emit updatedLayout();
+ emit valueChanged(index);
}
qreal QBarSetPrivate::pos(const int index)
diff --git a/src/charts/barchart/qbarset_p.h b/src/charts/barchart/qbarset_p.h
index 6a1639e1..bcecffe0 100644
--- a/src/charts/barchart/qbarset_p.h
+++ b/src/charts/barchart/qbarset_p.h
@@ -64,15 +64,20 @@ public:
int remove(const int index, const int count);
void replace(const int index, const qreal value);
- void replace(const int index, const QPointF value);
qreal pos(const int index);
qreal value(const int index);
+ void setVisualsDirty(bool dirty) { m_visualsDirty = dirty; }
+ bool visualsDirty() const { return m_visualsDirty; }
+ void setLabelsDirty(bool dirty) { m_labelsDirty = dirty; }
+ bool labelsDirty() const { return m_labelsDirty; }
+
Q_SIGNALS:
- void restructuredBars();
void updatedBars();
- void updatedLayout();
+ void valueChanged(int index);
+ void valueAdded(int index, int count);
+ void valueRemoved(int index, int count);
public:
QBarSet * const q_ptr;
@@ -82,6 +87,8 @@ public:
QBrush m_brush;
QBrush m_labelBrush;
QFont m_labelFont;
+ bool m_visualsDirty;
+ bool m_labelsDirty;
friend class QBarSet;
};
diff --git a/src/charts/barchart/vertical/bar/barchartitem.cpp b/src/charts/barchart/vertical/bar/barchartitem.cpp
index 9274e5d2..f9c867b3 100644
--- a/src/charts/barchart/vertical/bar/barchartitem.cpp
+++ b/src/charts/barchart/vertical/bar/barchartitem.cpp
@@ -38,64 +38,93 @@ QT_CHARTS_BEGIN_NAMESPACE
BarChartItem::BarChartItem(QAbstractBarSeries *series, QGraphicsItem* item) :
AbstractBarChartItem(series, item)
{
+ m_orientation = Qt::Vertical;
connect(series, SIGNAL(labelsPositionChanged(QAbstractBarSeries::LabelsPosition)),
this, SLOT(handleLabelsPositionChanged()));
connect(series, SIGNAL(labelsFormatChanged(QString)), this, SLOT(positionLabels()));
}
-void BarChartItem::initializeLayout()
+void BarChartItem::initializeLayout(int set, int category, int layoutIndex, bool resetAnimation)
{
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
-
- m_layout.clear();
- for(int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QRectF rect;
- QPointF topLeft;
- QPointF bottomRight;
-
- 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);
- } 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);
- }
+ QRectF rect;
+
+ 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());
+ rect.setLeft(oldRight);
+ rect.setTop(rect.bottom());
+ } else {
+ QPointF topLeft;
+ QPointF bottomRight;
+ 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 = topLeftPoint(set, setCount, category, barWidth, domain()->minY());
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, domain()->minY());
+ } else {
+ topLeft = topLeftPoint(set, setCount, category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(set, setCount, category, barWidth, 0.0);
+ }
- if (!m_validData)
- return;
+ if (m_validData) {
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- m_layout.append(rect.normalized());
}
}
+ m_layout[layoutIndex] = rect.normalized();
}
-QVector<QRectF> BarChartItem::calculateLayout()
+QPointF BarChartItem::topLeftPoint(int set, int setCount, int category,
+ qreal barWidth, qreal value)
{
- QVector<QRectF> layout;
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category - (barWidth / 2.0)
+ + (qreal(set)/setCount) * barWidth,
+ value), m_validData);
+}
- // Use temporary qreals for accuracy
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+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);
+}
- for(int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- qreal value = m_series->barSets().at(set)->at(category);
+QVector<QRectF> BarChartItem::calculateLayout()
+{
+ QVector<QRectF> layout;
+ layout.resize(m_layout.size());
+
+ 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 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 795d757f..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();
+ 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 9f587448..95867c78 100644
--- a/src/charts/barchart/vertical/percent/percentbarchartitem.cpp
+++ b/src/charts/barchart/vertical/percent/percentbarchartitem.cpp
@@ -38,121 +38,138 @@ QT_CHARTS_BEGIN_NAMESPACE
PercentBarChartItem::PercentBarChartItem(QAbstractBarSeries *series, QGraphicsItem* item) :
AbstractBarChartItem(series, item)
{
+ m_orientation = Qt::Vertical;
connect(series, SIGNAL(labelsPositionChanged(QAbstractBarSeries::LabelsPosition)),
this, SLOT(handleLabelsPositionChanged()));
connect(series, SIGNAL(labelsFormatChanged(QString)), this, SLOT(positionLabels()));
}
-void PercentBarChartItem::initializeLayout()
+QString PercentBarChartItem::generateLabelText(int set, int category, qreal value)
{
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ Q_UNUSED(value)
- m_layout.clear();
- for(int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QRectF rect;
- QPointF topLeft;
- QPointF bottomRight;
-
- 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);
- } else {
- topLeft = domain()->calculateGeometryPoint(QPointF(category - barWidth / 2, 0), m_validData);
- bottomRight = domain()->calculateGeometryPoint(QPointF(category + barWidth / 2, 0), m_validData);
- }
+ static const QString valueTag(QLatin1String("@value"));
+ qreal p = m_series->d_func()->percentageAt(set, category) * 100.0;
+ QString vString(presenter()->numberToString(p, 'f', 0));
+ QString valueLabel;
+ if (m_series->labelsFormat().isEmpty()) {
+ vString.append(QStringLiteral("%"));
+ valueLabel = vString;
+ } else {
+ valueLabel = m_series->labelsFormat();
+ valueLabel.replace(valueTag, vString);
+ }
- if (!m_validData)
- return;
+ return valueLabel;
+}
+void PercentBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
+{
+ Q_UNUSED(set)
+ Q_UNUSED(resetAnimation)
+
+ QRectF rect;
+
+ 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;
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
+ if (domain()->type() == AbstractDomain::XLogYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ topLeft = topLeftPoint(category, barWidth, domain()->minY());
+ bottomRight = bottomRightPoint(category, barWidth, domain()->minY());
+ } else {
+ topLeft = topLeftPoint(category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(category, barWidth, 0.0);
+ }
+ if (m_validData) {
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- m_layout.append(rect.normalized());
}
}
+
+ m_layout[layoutIndex] = rect.normalized();
}
-QVector<QRectF> PercentBarChartItem::calculateLayout()
+void PercentBarChartItem::markLabelsDirty(QBarSet *barset, int index, int count)
{
- QVector<QRectF> layout;
+ 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), index, count);
+}
+
+QPointF PercentBarChartItem::topLeftPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category - (barWidth / 2.0), value), m_validData);
+}
- // Use temporary qreals for accuracy
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+QPointF PercentBarChartItem::bottomRightPoint(int category, qreal barWidth, qreal value)
+{
+ return domain()->calculateGeometryPoint(
+ QPointF(m_seriesPosAdjustment + category + (barWidth / 2.0), value), m_validData);
+}
- for(int category = 0; category < categoryCount; category++) {
- qreal sum = 0;
- qreal categorySum = m_series->d_func()->categorySum(category);
- for (int set = 0; set < setCount; set++) {
- qreal value = m_series->barSets().at(set)->at(category);
+QVector<QRectF> PercentBarChartItem::calculateLayout()
+{
+ QVector<QRectF> layout;
+ layout.resize(m_layout.size());
+
+ 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++) {
+ 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;
}
}
return layout;
}
-void PercentBarChartItem::handleUpdatedBars()
-{
- // Handle changes in pen, brush, labels etc.
- int categoryCount = m_series->d_func()->categoryCount();
- int setCount = m_series->count();
- int itemIndex(0);
- static const QString valueTag(QLatin1String("@value"));
-
- for (int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QBarSetPrivate *barSet = m_series->d_func()->barsetAt(set)->d_ptr.data();
- Bar *bar = m_bars.at(itemIndex);
- bar->setPen(barSet->m_pen);
- bar->setBrush(barSet->m_brush);
- bar->update();
-
- QGraphicsTextItem *label = m_labels.at(itemIndex);
- qreal p = m_series->d_func()->percentageAt(set, category) * 100.0;
- QString vString(presenter()->numberToString(p, 'f', 0));
- QString valueLabel;
- if (p == 0) {
- label->setVisible(false);
- } else {
- label->setVisible(m_series->isLabelsVisible());
- if (m_series->labelsFormat().isEmpty()) {
- vString.append(QStringLiteral("%"));
- valueLabel = vString;
- } else {
- valueLabel = m_series->labelsFormat();
- valueLabel.replace(valueTag, vString);
- }
- }
- label->setHtml(valueLabel);
- label->setFont(barSet->m_labelFont);
- label->setDefaultTextColor(barSet->m_labelBrush.color());
- label->update();
- itemIndex++;
- }
- }
-}
-
void PercentBarChartItem::handleLabelsPositionChanged()
{
positionLabels();
diff --git a/src/charts/barchart/vertical/percent/percentbarchartitem_p.h b/src/charts/barchart/vertical/percent/percentbarchartitem_p.h
index 3a248d07..80016613 100644
--- a/src/charts/barchart/vertical/percent/percentbarchartitem_p.h
+++ b/src/charts/barchart/vertical/percent/percentbarchartitem_p.h
@@ -52,7 +52,7 @@ class PercentBarChartItem : public AbstractBarChartItem
Q_OBJECT
public:
PercentBarChartItem(QAbstractBarSeries *series, QGraphicsItem* item = 0);
- void handleUpdatedBars();
+ QString generateLabelText(int set, int category, qreal value);
private Q_SLOTS:
void handleLabelsPositionChanged();
@@ -60,7 +60,10 @@ private Q_SLOTS:
private:
virtual QVector<QRectF> calculateLayout();
- void initializeLayout();
+ void initializeLayout(int set, int category, int layoutIndex, bool resetAnimation);
+ 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 08295c66..51ab680d 100644
--- a/src/charts/barchart/vertical/stacked/stackedbarchartitem.cpp
+++ b/src/charts/barchart/vertical/stacked/stackedbarchartitem.cpp
@@ -38,77 +38,142 @@ QT_CHARTS_BEGIN_NAMESPACE
StackedBarChartItem::StackedBarChartItem(QAbstractBarSeries *series, QGraphicsItem* item) :
AbstractBarChartItem(series, item)
{
+ m_orientation = Qt::Vertical;
connect(series, SIGNAL(labelsPositionChanged(QAbstractBarSeries::LabelsPosition)),
this, SLOT(handleLabelsPositionChanged()));
connect(series, SIGNAL(labelsFormatChanged(QString)), this, SLOT(positionLabels()));
}
-void StackedBarChartItem::initializeLayout()
+void StackedBarChartItem::initializeLayout(int set, int category,
+ int layoutIndex, bool resetAnimation)
{
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
+ Q_UNUSED(set)
+ Q_UNUSED(resetAnimation)
- m_layout.clear();
- for(int category = 0; category < categoryCount; category++) {
- for (int set = 0; set < setCount; set++) {
- QRectF rect;
- QPointF topLeft;
- QPointF bottomRight;
+ QRectF rect;
- 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);
- } else {
- topLeft = domain()->calculateGeometryPoint(QPointF(category - barWidth / 2, 0), m_validData);
- bottomRight = domain()->calculateGeometryPoint(QPointF(category + barWidth / 2, 0), m_validData);
+ 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;
+ const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
+ if (domain()->type() == AbstractDomain::XLogYDomain
+ || domain()->type() == AbstractDomain::LogXLogYDomain) {
+ topLeft = topLeftPoint(category, barWidth, domain()->minY());
+ bottomRight = bottomRightPoint(category, barWidth, domain()->minY());
+ } else {
+ topLeft = topLeftPoint(category, barWidth, 0.0);
+ bottomRight = bottomRightPoint(category, barWidth, 0.0);
+ }
- if (!m_validData)
- return;
-
+ if (m_validData) {
rect.setTopLeft(topLeft);
rect.setBottomRight(bottomRight);
- m_layout.append(rect.normalized());
}
}
+ 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;
- // Use temporary qreals for accuracy
- qreal categoryCount = m_series->d_func()->categoryCount();
- qreal setCount = m_series->count();
- qreal barWidth = m_series->d_func()->barWidth();
-
- for(int category = 0; category < categoryCount; category++) {
- qreal positiveSum = 0;
- qreal negativeSum = 0;
- for (int set = 0; set < setCount; set++) {
- qreal value = m_series->barSets().at(set)->at(category);
+ layout.resize(m_layout.size());
+
+ 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++) {
+ 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 b156b4ae..03010e16 100644
--- a/src/charts/barchart/vertical/stacked/stackedbarchartitem_p.h
+++ b/src/charts/barchart/vertical/stacked/stackedbarchartitem_p.h
@@ -58,8 +58,9 @@ private Q_SLOTS:
private:
virtual QVector<QRectF> calculateLayout();
- void initializeLayout();
-
+ 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/chartdataset.cpp b/src/charts/chartdataset.cpp
index 97602b57..702dc8dd 100644
--- a/src/charts/chartdataset.cpp
+++ b/src/charts/chartdataset.cpp
@@ -272,6 +272,11 @@ bool ChartDataSet::attachAxis(QAbstractSeries *series,QAbstractAxis *axis)
}
series->d_ptr->setDomain(domain);
series->d_ptr->initializeDomain();
+
+ // Reinitialize domain based on old axes, as the series domain initialization above
+ // has trashed the old ranges, if there were any.
+ for (QAbstractAxis *oldAxis : series->d_ptr->m_axes)
+ oldAxis->d_ptr->initializeDomain(domain);
}
series->d_ptr->m_axes<<axis;
@@ -644,6 +649,11 @@ AbstractDomain* ChartDataSet::createDomain(AbstractDomain::DomainType type)
}
}
+AbstractDomain *ChartDataSet::domainForSeries(QAbstractSeries *series) const
+{
+ return series->d_ptr->domain();
+}
+
void ChartDataSet::reverseChanged()
{
QAbstractAxis *axis = qobject_cast<QAbstractAxis *>(sender());
diff --git a/src/charts/chartdataset_p.h b/src/charts/chartdataset_p.h
index 4552ad20..f8a1070c 100644
--- a/src/charts/chartdataset_p.h
+++ b/src/charts/chartdataset_p.h
@@ -82,6 +82,7 @@ public:
GLXYSeriesDataManager *glXYSeriesDataManager() { return m_glXYSeriesDataManager; }
AbstractDomain* createDomain(AbstractDomain::DomainType type);
+ AbstractDomain* domainForSeries(QAbstractSeries *series) const;
Q_SIGNALS:
void axisAdded(QAbstractAxis* axis);
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/doc/images/examples_scatterchart.png b/src/charts/doc/images/examples_scatterchart.png
index 41900f66..6ffaed35 100644
--- a/src/charts/doc/images/examples_scatterchart.png
+++ b/src/charts/doc/images/examples_scatterchart.png
Binary files differ
diff --git a/src/charts/doc/src/examples-scatterchart.qdoc b/src/charts/doc/src/examples-scatterchart.qdoc
index add34f28..08c12c09 100644
--- a/src/charts/doc/src/examples-scatterchart.qdoc
+++ b/src/charts/doc/src/examples-scatterchart.qdoc
@@ -60,8 +60,7 @@
\snippet scatterchart/chartview.cpp 4
- The image used as a brush for the scatter series may contain several colors. Therefore we need
- to set the desired color explicitly by setting the brush for the legend marker items.
+ We can also use the scatter points as legend markers.
\snippet scatterchart/chartview.cpp 5
diff --git a/src/charts/layout/abstractchartlayout.cpp b/src/charts/layout/abstractchartlayout.cpp
index 46689cab..ad3274a0 100644
--- a/src/charts/layout/abstractchartlayout.cpp
+++ b/src/charts/layout/abstractchartlayout.cpp
@@ -41,8 +41,7 @@ static const qreal golden_ratio = 0.4;
AbstractChartLayout::AbstractChartLayout(ChartPresenter *presenter)
: m_presenter(presenter),
- m_margins(20, 20, 20, 20),
- m_minChartRect(0, 0, 200, 200)
+ m_margins(20, 20, 20, 20)
{
}
@@ -160,8 +159,12 @@ QRectF AbstractChartLayout::calculateLegendGeometry(const QRectF &geometry, QLeg
QRectF AbstractChartLayout::calculateLegendMinimum(const QRectF &geometry, QLegend *legend) const
{
- QSizeF minSize = legend->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, -1));
- return geometry.adjusted(0, 0, minSize.width(), minSize.height());
+ if (!legend->isAttachedToChart() || !legend->isVisible()) {
+ return geometry;
+ } else {
+ QSizeF minSize = legend->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, -1));
+ return geometry.adjusted(0, 0, minSize.width(), minSize.height());
+ }
}
QRectF AbstractChartLayout::calculateTitleGeometry(const QRectF &geometry, ChartTitle *title) const
@@ -180,8 +183,12 @@ QRectF AbstractChartLayout::calculateTitleGeometry(const QRectF &geometry, Chart
QRectF AbstractChartLayout::calculateTitleMinimum(const QRectF &minimum, ChartTitle *title) const
{
- QSizeF min = title->sizeHint(Qt::MinimumSize);
- return minimum.adjusted(0, 0, min.width(), min.height());
+ if (!title->isVisible() || title->text().isEmpty()) {
+ return minimum;
+ } else {
+ QSizeF min = title->sizeHint(Qt::MinimumSize);
+ return minimum.adjusted(0, 0, min.width(), min.height());
+ }
}
QSizeF AbstractChartLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
@@ -197,7 +204,7 @@ QSizeF AbstractChartLayout::sizeHint(Qt::SizeHint which, const QSizeF &constrain
minimumRect = calculateTitleMinimum(minimumRect, title);
minimumRect = calculateLegendMinimum(minimumRect, legend);
minimumRect = calculateAxisMinimum(minimumRect, axes);
- return minimumRect.united(m_minChartRect).size().toSize();
+ return minimumRect.size().toSize();
}
return QSize(-1, -1);
}
diff --git a/src/charts/layout/abstractchartlayout_p.h b/src/charts/layout/abstractchartlayout_p.h
index 82043b68..f2ef7861 100644
--- a/src/charts/layout/abstractchartlayout_p.h
+++ b/src/charts/layout/abstractchartlayout_p.h
@@ -82,7 +82,6 @@ protected:
ChartPresenter *m_presenter;
QMargins m_margins;
- QRectF m_minChartRect;
QRectF m_minAxisRect;
};
diff --git a/src/charts/legend/legendmarkeritem.cpp b/src/charts/legend/legendmarkeritem.cpp
index 1c0b5901..ec75659f 100644
--- a/src/charts/legend/legendmarkeritem.cpp
+++ b/src/charts/legend/legendmarkeritem.cpp
@@ -30,9 +30,16 @@
#include <QtGui/QPainter>
#include <QtWidgets/QGraphicsSceneEvent>
#include <QtWidgets/QGraphicsTextItem>
+#include <QtWidgets/QGraphicsEllipseItem>
+#include <QtWidgets/QGraphicsRectItem>
+#include <QtWidgets/QGraphicsLineItem>
#include <QtGui/QTextDocument>
+#include <QtCore/QtMath>
#include <QtCharts/QLegend>
+#include <QtCharts/QScatterSeries>
+#include <QtCharts/QLineSeries>
+#include <QtCharts/QSplineSeries>
#include <private/qlegend_p.h>
#include <QtCharts/QLegendMarker>
#include <private/qlegendmarker_p.h>
@@ -44,16 +51,18 @@ QT_CHARTS_BEGIN_NAMESPACE
LegendMarkerItem::LegendMarkerItem(QLegendMarkerPrivate *marker, QGraphicsObject *parent) :
QGraphicsObject(parent),
m_marker(marker),
- m_markerRect(0,0,10.0,10.0),
+ m_defaultMarkerRect(0.0, 0.0, 10.0, 10.0),
+ m_markerRect(0.0, 0.0, -1.0, -1.0),
m_boundingRect(0,0,0,0),
m_textItem(new QGraphicsTextItem(this)),
- m_rectItem(new QGraphicsRectItem(this)),
+ m_markerItem(nullptr),
m_margin(3),
m_space(4),
+ m_markerShape(QLegend::MarkerShapeDefault),
m_hovering(false),
- m_pressPos(0, 0)
+ m_itemType(TypeRect)
{
- m_rectItem->setRect(m_markerRect);
+ updateMarkerShapeAndSize();
m_textItem->document()->setDocumentMargin(ChartPresenter::textMargin());
setAcceptHoverEvents(true);
}
@@ -67,41 +76,52 @@ LegendMarkerItem::~LegendMarkerItem()
void LegendMarkerItem::setPen(const QPen &pen)
{
- m_rectItem->setPen(pen);
+ m_pen = pen;
+ setItemBrushAndPen();
}
QPen LegendMarkerItem::pen() const
{
- return m_rectItem->pen();
+ return m_pen;
}
void LegendMarkerItem::setBrush(const QBrush &brush)
{
- m_rectItem->setBrush(brush);
+ m_brush = brush;
+ setItemBrushAndPen();
}
QBrush LegendMarkerItem::brush() const
{
- return m_rectItem->brush();
+ return m_brush;
}
-void LegendMarkerItem::setFont(const QFont &font)
+void LegendMarkerItem::setSeriesPen(const QPen &pen)
+{
+ m_seriesPen = pen;
+ setItemBrushAndPen();
+}
+
+void LegendMarkerItem::setSeriesBrush(const QBrush &brush)
{
- m_textItem->setFont(font);
+ m_seriesBrush = brush;
+ setItemBrushAndPen();
+}
+void LegendMarkerItem::setFont(const QFont &font)
+{
QFontMetrics fn(font);
- QRectF markerRect = QRectF(0, 0, fn.height() / 2, fn.height() / 2);
- if (m_markerRect != markerRect) {
- m_markerRect = markerRect;
- emit markerRectChanged();
- }
+ m_font = font;
- updateGeometry();
+ m_defaultMarkerRect = QRectF(0, 0, fn.height() / 2, fn.height() / 2);
+ if (effectiveMarkerShape() != QLegend::MarkerShapeFromSeries)
+ updateMarkerShapeAndSize();
+ m_marker->invalidateLegend();
}
QFont LegendMarkerItem::font() const
{
- return m_textItem->font();
+ return m_font;
}
void LegendMarkerItem::setLabel(const QString label)
@@ -127,10 +147,11 @@ QBrush LegendMarkerItem::labelBrush() const
void LegendMarkerItem::setGeometry(const QRectF &rect)
{
- qreal width = rect.width();
- qreal x = m_margin + m_markerRect.width() + m_space + m_margin;
+ const qreal width = rect.width();
+ const qreal markerWidth = effectiveMarkerWidth();
+ const qreal x = m_margin + markerWidth + m_space + m_margin;
QRectF truncatedRect;
- const QString html = ChartPresenter::truncatedText(m_textItem->font(), m_label, qreal(0.0),
+ const QString html = ChartPresenter::truncatedText(m_font, m_label, qreal(0.0),
width - x, rect.height(), truncatedRect);
m_textItem->setHtml(html);
if (m_marker->m_legend->showToolTips() && html != m_label)
@@ -138,6 +159,7 @@ void LegendMarkerItem::setGeometry(const QRectF &rect)
else
m_textItem->setToolTip(QString());
+ m_textItem->setFont(m_font);
m_textItem->setTextWidth(truncatedRect.width());
qreal y = qMax(m_markerRect.height() + 2 * m_margin, truncatedRect.height() + 2 * m_margin);
@@ -145,9 +167,12 @@ void LegendMarkerItem::setGeometry(const QRectF &rect)
const QRectF &textRect = m_textItem->boundingRect();
m_textItem->setPos(x - m_margin, y / 2 - textRect.height() / 2);
- m_rectItem->setRect(m_markerRect);
+ setItemRect();
+
// The textMargin adjustments to position are done to make default case rects less blurry with anti-aliasing
- m_rectItem->setPos(m_margin - ChartPresenter::textMargin(), y / 2.0 - m_markerRect.height() / 2.0 + ChartPresenter::textMargin());
+ m_markerItem->setPos(m_margin - ChartPresenter::textMargin()
+ + (markerWidth - m_markerRect.width()) / 2.0,
+ y / 2.0 - m_markerRect.height() / 2.0 + ChartPresenter::textMargin());
prepareGeometryChange();
m_boundingRect = QRectF(0, 0, x + textRect.width() + m_margin, y);
@@ -175,18 +200,18 @@ QSizeF LegendMarkerItem::sizeHint(Qt::SizeHint which, const QSizeF& constraint)
Q_UNUSED(constraint)
QSizeF sh;
+ const qreal markerWidth = effectiveMarkerWidth();
switch (which) {
case Qt::MinimumSize: {
- QRectF labelRect = ChartPresenter::textBoundingRect(m_textItem->font(),
- QStringLiteral("..."));
- sh = QSizeF(labelRect.width() + (2.0 * m_margin) + m_space + m_markerRect.width(),
+ const QRectF labelRect = ChartPresenter::textBoundingRect(m_font, QStringLiteral("..."));
+ sh = QSizeF(labelRect.width() + (2.0 * m_margin) + m_space + markerWidth,
qMax(m_markerRect.height(), labelRect.height()) + (2.0 * m_margin));
break;
}
case Qt::PreferredSize: {
- QRectF labelRect = ChartPresenter::textBoundingRect(m_textItem->font(), m_label);
- sh = QSizeF(labelRect.width() + (2.0 * m_margin) + m_space + m_markerRect.width(),
+ const QRectF labelRect = ChartPresenter::textBoundingRect(m_font, m_label);
+ sh = QSizeF(labelRect.width() + (2.0 * m_margin) + m_space + markerWidth,
qMax(m_markerRect.height(), labelRect.height()) + (2.0 * m_margin));
break;
}
@@ -221,6 +246,125 @@ void LegendMarkerItem::setToolTip(const QString &tip)
m_textItem->setToolTip(tip);
}
+QLegend::MarkerShape LegendMarkerItem::markerShape() const
+{
+ return m_markerShape;
+}
+
+void LegendMarkerItem::setMarkerShape(QLegend::MarkerShape shape)
+{
+ m_markerShape = shape;
+}
+
+void LegendMarkerItem::updateMarkerShapeAndSize()
+{
+ const QLegend::MarkerShape shape = effectiveMarkerShape();
+
+ ItemType itemType = TypeRect;
+ QRectF newRect = m_defaultMarkerRect;
+ if (shape == QLegend::MarkerShapeFromSeries) {
+ QScatterSeries *scatter = qobject_cast<QScatterSeries *>(m_marker->series());
+ if (scatter) {
+ newRect.setSize(QSizeF(scatter->markerSize(), scatter->markerSize()));
+ if (scatter->markerShape() == QScatterSeries::MarkerShapeCircle)
+ itemType = TypeCircle;
+ } else if (qobject_cast<QLineSeries *>(m_marker->series())
+ || qobject_cast<QSplineSeries *>(m_marker->series())) {
+ newRect.setHeight(m_seriesPen.width());
+ newRect.setWidth(qRound(m_defaultMarkerRect.width() * 1.5));
+ itemType = TypeLine;
+ }
+ } else if (shape == QLegend::MarkerShapeCircle) {
+ itemType = TypeCircle;
+ }
+
+ if (!m_markerItem || m_itemType != itemType) {
+ m_itemType = itemType;
+ QPointF oldPos;
+ if (m_markerItem) {
+ oldPos = m_markerItem->pos();
+ delete m_markerItem;
+ }
+ if (itemType == TypeRect)
+ m_markerItem = new QGraphicsRectItem(this);
+ else if (itemType == TypeCircle)
+ m_markerItem = new QGraphicsEllipseItem(this);
+ else
+ m_markerItem = new QGraphicsLineItem(this);
+ // Immediately update the position to the approximate correct position to avoid marker
+ // jumping around when changing markers
+ m_markerItem->setPos(oldPos);
+ }
+ setItemBrushAndPen();
+
+ if (newRect != m_markerRect) {
+ if (useMaxWidth() && m_marker->m_legend->d_ptr->maxMarkerWidth() < newRect.width())
+ m_marker->invalidateAllItems();
+ m_markerRect = newRect;
+ setItemRect();
+ emit markerRectChanged();
+ updateGeometry();
+ }
+}
+
+QLegend::MarkerShape LegendMarkerItem::effectiveMarkerShape() const
+{
+ QLegend::MarkerShape shape = m_markerShape;
+ if (shape == QLegend::MarkerShapeDefault)
+ shape = m_marker->m_legend->markerShape();
+ return shape;
+}
+
+qreal LegendMarkerItem::effectiveMarkerWidth() const
+{
+ return useMaxWidth() ? m_marker->m_legend->d_ptr->maxMarkerWidth()
+ : m_markerRect.width();
+}
+
+void LegendMarkerItem::setItemBrushAndPen()
+{
+ if (m_markerItem) {
+ QAbstractGraphicsShapeItem *shapeItem =
+ qgraphicsitem_cast<QGraphicsRectItem *>(m_markerItem);
+ if (!shapeItem)
+ shapeItem = qgraphicsitem_cast<QGraphicsEllipseItem *>(m_markerItem);
+ if (shapeItem) {
+ if (effectiveMarkerShape() == QLegend::MarkerShapeFromSeries) {
+ shapeItem->setPen(m_seriesPen);
+ shapeItem->setBrush(m_seriesBrush);
+ } else {
+ shapeItem->setPen(m_pen);
+ shapeItem->setBrush(m_brush);
+ }
+ } else {
+ // Must be line item, it has no brush.
+ QGraphicsLineItem *lineItem =
+ qgraphicsitem_cast<QGraphicsLineItem *>(m_markerItem);
+ if (lineItem)
+ lineItem->setPen(m_seriesPen);
+ }
+ }
+}
+
+void LegendMarkerItem::setItemRect()
+{
+ if (m_itemType == TypeRect) {
+ static_cast<QGraphicsRectItem *>(m_markerItem)->setRect(m_markerRect);
+ } else if (m_itemType == TypeCircle) {
+ static_cast<QGraphicsEllipseItem *>(m_markerItem)->setRect(m_markerRect);
+ } else {
+ qreal y = m_markerRect.height() / 2.0;
+ QLineF line(0.0, y, m_markerRect.width(), y);
+ static_cast<QGraphicsLineItem *>(m_markerItem)->setLine(line);
+ }
+}
+
+bool LegendMarkerItem::useMaxWidth() const
+{
+ return (m_marker->m_legend->alignment() == Qt::AlignLeft
+ || m_marker->m_legend->alignment() == Qt::AlignRight);
+}
+
#include "moc_legendmarkeritem_p.cpp"
QT_CHARTS_END_NAMESPACE
diff --git a/src/charts/legend/legendmarkeritem_p.h b/src/charts/legend/legendmarkeritem_p.h
index c3371742..5325f8d1 100644
--- a/src/charts/legend/legendmarkeritem_p.h
+++ b/src/charts/legend/legendmarkeritem_p.h
@@ -40,6 +40,7 @@
#define LEGENDMARKERITEM_P_H
#include <QtCharts/QChartGlobal>
+#include <QtCharts/QLegend>
#include <QGraphicsObject>
#include <QtGui/QFont>
#include <QtGui/QBrush>
@@ -56,6 +57,12 @@ class LegendMarkerItem : public QGraphicsObject, public QGraphicsLayoutItem
Q_OBJECT
Q_INTERFACES(QGraphicsLayoutItem)
public:
+ enum ItemType {
+ TypeRect,
+ TypeLine,
+ TypeCircle
+ };
+
explicit LegendMarkerItem(QLegendMarkerPrivate *marker, QGraphicsObject *parent = nullptr);
~LegendMarkerItem();
@@ -65,6 +72,9 @@ public:
void setBrush(const QBrush &brush);
QBrush brush() const;
+ void setSeriesPen(const QPen &pen);
+ void setSeriesBrush(const QBrush &brush);
+
void setFont(const QFont &font);
QFont font() const;
@@ -87,25 +97,43 @@ public:
QString displayedLabel() const;
void setToolTip(const QString &tooltip);
+ QLegend::MarkerShape markerShape() const;
+ void setMarkerShape(QLegend::MarkerShape shape);
+
+ void updateMarkerShapeAndSize();
+ QLegend::MarkerShape effectiveMarkerShape() const;
+ qreal effectiveMarkerWidth() const;
+
+ ItemType itemType() const { return m_itemType; }
+
Q_SIGNALS:
void markerRectChanged();
protected:
+ void setItemBrushAndPen();
+ void setItemRect();
+ bool useMaxWidth() const;
+
QLegendMarkerPrivate *m_marker; // Knows
+ QRectF m_defaultMarkerRect;
QRectF m_markerRect;
QRectF m_boundingRect;
QGraphicsTextItem *m_textItem;
- QGraphicsRectItem *m_rectItem;
+ QGraphicsItem *m_markerItem;
qreal m_margin;
qreal m_space;
QString m_label;
+ QLegend::MarkerShape m_markerShape;
QBrush m_labelBrush;
QPen m_pen;
QBrush m_brush;
+ QPen m_seriesPen;
+ QBrush m_seriesBrush;
+ QFont m_font;
bool m_hovering;
- QPointF m_pressPos;
+ ItemType m_itemType;
friend class QLegendMarker;
friend class QLegendMarkerPrivate;
diff --git a/src/charts/legend/qlegend.cpp b/src/charts/legend/qlegend.cpp
index 665dcc44..219d790e 100644
--- a/src/charts/legend/qlegend.cpp
+++ b/src/charts/legend/qlegend.cpp
@@ -174,6 +174,47 @@ QT_CHARTS_BEGIN_NAMESPACE
*/
/*!
+ \enum QLegend::MarkerShape
+
+ This enum describes the shape used when rendering legend marker items.
+
+ \value MarkerShapeDefault Default shape determined by QLegend is used for the marker.
+ This value is supported only for individual QLegendMarker items.
+ \value MarkerShapeRectangle Rectangular markers are used.
+ Marker size is determined by font size.
+ \value MarkerShapeCircle Circular markers are used.
+ Marker size is determined by font size.
+ \value MarkerShapeFromSeries The marker shape is determined by the series.
+ In case of a scatter series, the legend marker looks like a scatter dot and is the same
+ size as the dot. In case of a line or spline series, the legend marker looks like a small
+ segment of the line. For other series types, rectangular markers are shown.
+
+ \sa markerShape
+*/
+
+/*!
+ \qmlproperty enumeration Legend::markerShape
+ \since 5.9
+
+ The default shape of the legend markers.
+ The default value is \c{MarkerShapeRectangle}.
+
+ \value Legend.MarkerShapeRectangle Legend markers are rectangular
+ \value Legend.MarkerShapeCircle Legend markers are circular
+ \value Legend.MarkerShapeFromSeries Legend marker shape is determined by the series
+
+ \sa QLegend::MarkerShape
+*/
+
+/*!
+ \property QLegend::markerShape
+ \since 5.9
+
+ The default shape of the legend markers.
+ The default value is \c{MarkerShapeRectangle}.
+*/
+
+/*!
\qmlproperty bool Legend::showToolTips
Whether tooltips are shown when the text is truncated. This property is \c false by default.
This property currently has no effect as there is no support for tooltips in QML.
@@ -503,6 +544,23 @@ void QLegend::setShowToolTips(bool show)
}
}
+QLegend::MarkerShape QLegend::markerShape() const
+{
+ return d_ptr->m_markerShape;
+}
+
+void QLegend::setMarkerShape(QLegend::MarkerShape shape)
+{
+ QLegend::MarkerShape newShape = shape;
+ if (newShape == MarkerShapeDefault)
+ newShape = MarkerShapeRectangle;
+ if (d_ptr->m_markerShape != newShape) {
+ d_ptr->m_markerShape = newShape;
+ layout()->invalidate();
+ emit markerShapeChanged(newShape);
+ }
+}
+
/*!
\internal
\a event, see QGraphicsWidget for details.
@@ -541,7 +599,8 @@ QLegendPrivate::QLegendPrivate(ChartPresenter *presenter, QChart *chart, QLegend
m_attachedToChart(true),
m_backgroundVisible(false),
m_reverseMarkers(false),
- m_showToolTips(false)
+ m_showToolTips(false),
+ m_markerShape(QLegend::MarkerShapeRectangle)
{
m_items->setHandlesChildEvents(false);
}
@@ -583,6 +642,17 @@ QList<QLegendMarker*> QLegendPrivate::markers(QAbstractSeries *series)
return markers;
}
+qreal QLegendPrivate::maxMarkerWidth() const
+{
+ qreal maxWidth = 0.0;
+ for (int i = 0; i < m_markers.size(); i++) {
+ LegendMarkerItem *item = m_markers.at(i)->d_ptr->item();
+ if (item)
+ maxWidth = qMax(item->markerRect().width(), maxWidth);
+ }
+ return maxWidth;
+}
+
void QLegendPrivate::handleSeriesAdded(QAbstractSeries *series)
{
if (m_series.contains(series)) {
diff --git a/src/charts/legend/qlegend.h b/src/charts/legend/qlegend.h
index cafbefba..fa3a8258 100644
--- a/src/charts/legend/qlegend.h
+++ b/src/charts/legend/qlegend.h
@@ -53,11 +53,20 @@ class QT_CHARTS_EXPORT QLegend : public QGraphicsWidget
Q_PROPERTY(QColor labelColor READ labelColor WRITE setLabelColor NOTIFY labelColorChanged)
Q_PROPERTY(bool reverseMarkers READ reverseMarkers WRITE setReverseMarkers NOTIFY reverseMarkersChanged)
Q_PROPERTY(bool showToolTips READ showToolTips WRITE setShowToolTips NOTIFY showToolTipsChanged)
+ Q_PROPERTY(MarkerShape markerShape READ markerShape WRITE setMarkerShape NOTIFY markerShapeChanged)
private:
explicit QLegend(QChart *chart);
public:
+ enum MarkerShape {
+ MarkerShapeDefault,
+ MarkerShapeRectangle,
+ MarkerShapeCircle,
+ MarkerShapeFromSeries
+ };
+ Q_ENUMS(MarkerShape)
+
~QLegend();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = Q_NULLPTR);
@@ -97,6 +106,10 @@ public:
bool showToolTips() const;
void setShowToolTips(bool show);
+
+ MarkerShape markerShape() const;
+ void setMarkerShape(MarkerShape shape);
+
protected:
void hideEvent(QHideEvent *event);
void showEvent(QShowEvent *event);
@@ -109,6 +122,7 @@ Q_SIGNALS:
void labelColorChanged(QColor color);
void reverseMarkersChanged(bool reverseMarkers);
void showToolTipsChanged(bool showToolTips);
+ void markerShapeChanged(MarkerShape shape);
private:
QScopedPointer<QLegendPrivate> d_ptr;
diff --git a/src/charts/legend/qlegend_p.h b/src/charts/legend/qlegend_p.h
index 7806015e..83dd0c0b 100644
--- a/src/charts/legend/qlegend_p.h
+++ b/src/charts/legend/qlegend_p.h
@@ -63,6 +63,7 @@ public:
QGraphicsItemGroup* items() { return m_items; }
QList<QLegendMarker*> markers(QAbstractSeries *series = 0);
+ qreal maxMarkerWidth() const;
public Q_SLOTS:
void handleSeriesAdded(QAbstractSeries *series);
@@ -94,6 +95,7 @@ private:
bool m_backgroundVisible;
bool m_reverseMarkers;
bool m_showToolTips;
+ QLegend::MarkerShape m_markerShape;
QList<QLegendMarker *> m_markers;
QList<QAbstractSeries *> m_series;
diff --git a/src/charts/legend/qlegendmarker.cpp b/src/charts/legend/qlegendmarker.cpp
index 4ed9d737..eb5530c4 100644
--- a/src/charts/legend/qlegendmarker.cpp
+++ b/src/charts/legend/qlegendmarker.cpp
@@ -161,6 +161,12 @@ QT_CHARTS_BEGIN_NAMESPACE
The visibility affects both the legend marker label and the icon.
*/
+/*!
+ \property QLegendMarker::shape
+
+ The shape of the legend marker. Defaults to QLegend::MarkerShapeDefault, which indicates
+ the shape is determined by QLegend::markerShape property.
+*/
/*!
\internal
@@ -293,6 +299,20 @@ void QLegendMarker::setVisible(bool visible)
d_ptr->m_item->setVisible(visible);
}
+QLegend::MarkerShape QLegendMarker::shape() const
+{
+ return d_ptr->m_item->markerShape();
+}
+
+void QLegendMarker::setShape(QLegend::MarkerShape shape)
+{
+ if (shape != d_ptr->m_item->markerShape()) {
+ d_ptr->m_item->setMarkerShape(shape);
+ d_ptr->handleShapeChange();
+ emit shapeChanged();
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QLegendMarkerPrivate::QLegendMarkerPrivate(QLegendMarker *q, QLegend *legend) :
m_legend(legend),
@@ -302,6 +322,9 @@ QLegendMarkerPrivate::QLegendMarkerPrivate(QLegendMarker *q, QLegend *legend) :
q_ptr(q)
{
m_item = new LegendMarkerItem(this);
+
+ connect(legend, &QLegend::markerShapeChanged, this,
+ &QLegendMarkerPrivate::handleShapeChange);
}
QLegendMarkerPrivate::~QLegendMarkerPrivate()
@@ -311,6 +334,21 @@ QLegendMarkerPrivate::~QLegendMarkerPrivate()
void QLegendMarkerPrivate::invalidateLegend()
{
+ m_item->updateGeometry();
+ m_legend->d_ptr->m_layout->invalidate();
+}
+
+void QLegendMarkerPrivate::invalidateAllItems()
+{
+ QList<QLegendMarker *> markers = m_legend->markers();
+ for (int i = 0; i < markers.size(); i++)
+ markers.at(i)->d_ptr->m_item->updateGeometry();
+ m_legend->d_ptr->m_layout->invalidate();
+}
+
+void QLegendMarkerPrivate::handleShapeChange()
+{
+ m_item->updateMarkerShapeAndSize();
m_legend->d_ptr->m_layout->invalidate();
}
diff --git a/src/charts/legend/qlegendmarker.h b/src/charts/legend/qlegendmarker.h
index 86721440..72ad384c 100644
--- a/src/charts/legend/qlegendmarker.h
+++ b/src/charts/legend/qlegendmarker.h
@@ -31,6 +31,7 @@
#define QLEGENDMARKER_H
#include <QtCharts/QChartGlobal>
+#include <QtCharts/QLegend>
#include <QtCore/QObject>
#include <QtGui/QPen>
#include <QtGui/QBrush>
@@ -62,6 +63,7 @@ public:
Q_PROPERTY(QPen pen READ pen WRITE setPen NOTIFY penChanged)
Q_PROPERTY(QBrush brush READ brush WRITE setBrush NOTIFY brushChanged)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
+ Q_PROPERTY(QLegend::MarkerShape shape READ shape WRITE setShape NOTIFY shapeChanged)
Q_ENUMS(LegendMarkerType)
public:
@@ -86,6 +88,9 @@ public:
bool isVisible() const;
void setVisible(bool visible);
+ QLegend::MarkerShape shape() const;
+ void setShape(QLegend::MarkerShape shape);
+
virtual QAbstractSeries* series() = 0;
Q_SIGNALS:
@@ -97,6 +102,7 @@ Q_SIGNALS:
void penChanged();
void brushChanged();
void visibleChanged();
+ void shapeChanged();
protected:
explicit QLegendMarker(QLegendMarkerPrivate &d, QObject *parent = Q_NULLPTR);
diff --git a/src/charts/legend/qlegendmarker_p.h b/src/charts/legend/qlegendmarker_p.h
index 7a97623d..00d24542 100644
--- a/src/charts/legend/qlegendmarker_p.h
+++ b/src/charts/legend/qlegendmarker_p.h
@@ -67,9 +67,11 @@ public:
virtual QObject* relatedObject() = 0;
void invalidateLegend();
+ void invalidateAllItems();
public Q_SLOTS:
virtual void updated() = 0;
+ void handleShapeChange();
protected:
LegendMarkerItem *m_item;
diff --git a/src/charts/legend/qxylegendmarker.cpp b/src/charts/legend/qxylegendmarker.cpp
index c3ffefbd..5f1859c4 100644
--- a/src/charts/legend/qxylegendmarker.cpp
+++ b/src/charts/legend/qxylegendmarker.cpp
@@ -31,6 +31,7 @@
#include <private/qxylegendmarker_p.h>
#include <private/qxyseries_p.h>
#include <QtCharts/QXYSeries>
+#include <QtCharts/QScatterSeries>
QT_CHARTS_BEGIN_NAMESPACE
@@ -123,6 +124,18 @@ void QXYLegendMarkerPrivate::updated()
m_item->setBrush(m_series->brush());
brushChanged = true;
}
+ if (m_item->effectiveMarkerShape() == QLegend::MarkerShapeFromSeries) {
+ QScatterSeries *scatter = static_cast<QScatterSeries *>(m_series);
+ if (scatter) {
+ const bool shapeChangeNeeded =
+ (scatter->markerShape() == QScatterSeries::MarkerShapeCircle
+ && m_item->itemType() != LegendMarkerItem::TypeCircle)
+ || (scatter->markerShape() == QScatterSeries::MarkerShapeRectangle
+ && m_item->itemType() != LegendMarkerItem::TypeRect);
+ if (shapeChangeNeeded || scatter->markerSize() != m_item->markerRect().width())
+ m_item->updateMarkerShapeAndSize();
+ }
+ }
} else {
QBrush emptyBrush;
if (!m_customBrush
@@ -132,6 +145,9 @@ void QXYLegendMarkerPrivate::updated()
brushChanged = true;
}
}
+ m_item->setSeriesBrush(m_series->brush());
+ m_item->setSeriesPen(m_series->pen());
+
invalidateLegend();
if (labelChanged)
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/auto/chartdataset/tst_chartdataset.cpp b/tests/auto/chartdataset/tst_chartdataset.cpp
index fc1ac1d3..b3f40198 100644
--- a/tests/auto/chartdataset/tst_chartdataset.cpp
+++ b/tests/auto/chartdataset/tst_chartdataset.cpp
@@ -28,21 +28,22 @@
****************************************************************************/
#include <QtTest/QtTest>
-#include <QtCharts/QAbstractAxis>
-#include <QtCharts/QValueAxis>
-#include <QtCharts/QBarCategoryAxis>
-#include <QtCharts/QCategoryAxis>
+#include <QtCharts/qabstractaxis.h>
+#include <QtCharts/qvalueaxis.h>
+#include <QtCharts/qlogvalueaxis.h>
+#include <QtCharts/qbarcategoryaxis.h>
+#include <QtCharts/qcategoryaxis.h>
#ifndef QT_QREAL_IS_FLOAT
-#include <QtCharts/QDateTimeAxis>
+#include <QtCharts/qdatetimeaxis.h>
#endif
-#include <QtCharts/QLineSeries>
-#include <QtCharts/QAreaSeries>
-#include <QtCharts/QScatterSeries>
-#include <QtCharts/QSplineSeries>
-#include <QtCharts/QPieSeries>
-#include <QtCharts/QBarSeries>
-#include <QtCharts/QPercentBarSeries>
-#include <QtCharts/QStackedBarSeries>
+#include <QtCharts/qlineseries.h>
+#include <QtCharts/qareaseries.h>
+#include <QtCharts/qscatterseries.h>
+#include <QtCharts/qsplineseries.h>
+#include <QtCharts/qpieseries.h>
+#include <QtCharts/qbarseries.h>
+#include <QtCharts/qpercentbarseries.h>
+#include <QtCharts/qstackedbarseries.h>
#include <private/chartdataset_p.h>
#include <private/abstractdomain_p.h>
#include <tst_definitions.h>
@@ -85,8 +86,11 @@ private Q_SLOTS:
void attachAxis();
void detachAxis_data();
void detachAxis();
+ void domainChangePreservesRanges();
private:
+ void compareDomain(QAbstractSeries *series, qreal minX, qreal maxX,
+ qreal minY, qreal maxY) const;
ChartDataSet* m_dataset;
};
@@ -206,6 +210,7 @@ void tst_ChartDataSet::addAxis_data()
{
QTest::addColumn<QAbstractAxis*>("axis");
QAbstractAxis* value = new QValueAxis(this);
+ QAbstractAxis* logvalue = new QLogValueAxis(this);
QAbstractAxis* category = new QCategoryAxis(this);
QAbstractAxis* barcategory = new QBarCategoryAxis(this);
#ifndef QT_QREAL_IS_FLOAT
@@ -213,6 +218,7 @@ void tst_ChartDataSet::addAxis_data()
#endif
QTest::newRow("value") << value;
+ QTest::newRow("logvalue") << logvalue;
QTest::newRow("category") << category;
QTest::newRow("barcategory") << barcategory;
#ifndef QT_QREAL_IS_FLOAT
@@ -381,5 +387,67 @@ void tst_ChartDataSet::detachAxis()
QCOMPARE(m_dataset->detachAxis(detachSeries,detachAxis),success);
}
+void tst_ChartDataSet::domainChangePreservesRanges()
+{
+ // This test checks that domain ranges stay correct after an axis attachment causes domain
+ // to be recreated.
+ QVERIFY(m_dataset->series().isEmpty());
+ QVERIFY(m_dataset->axes().isEmpty());
+
+ QLineSeries* line = new QLineSeries(this);
+ QValueAxis* value1 = new QValueAxis(this);
+ QValueAxis* value2 = new QValueAxis(this);
+ QLogValueAxis* logValue1 = new QLogValueAxis(this);
+ QLogValueAxis* logValue2 = new QLogValueAxis(this);
+ (*line) << QPointF(1.0, 2.0) << QPointF(10.0, 20.0);
+
+ value1->setRange(2.0, 6.0);
+ value2->setRange(3.0, 7.0);
+ logValue1->setRange(4.0, 8.0);
+ logValue2->setRange(5.0, 9.0);
+
+ m_dataset->addSeries(line);
+ compareDomain(line, 1.0, 10.0, 2.0, 20.0);
+ m_dataset->addAxis(value1, Qt::AlignBottom);
+ m_dataset->addAxis(value2, Qt::AlignLeft);
+ m_dataset->addAxis(logValue1, Qt::AlignBottom);
+ m_dataset->addAxis(logValue2, Qt::AlignLeft);
+ compareDomain(line, 1.0, 10.0, 2.0, 20.0);
+
+ // Start with two value axes
+ m_dataset->attachAxis(line, value1);
+ compareDomain(line, 2.0, 6.0, 2.0, 20.0);
+ m_dataset->attachAxis(line, value2);
+ compareDomain(line, 2.0, 6.0, 3.0, 7.0);
+
+ // Detach y value and attach y logvalue
+ m_dataset->detachAxis(line, value2);
+ compareDomain(line, 2.0, 6.0, 3.0, 7.0); // Detach doesn't change domain ranges
+ m_dataset->attachAxis(line, logValue2);
+ compareDomain(line, 2.0, 6.0, 5.0, 9.0);
+
+ // Detach x value and attach x logvalue
+ m_dataset->detachAxis(line, value1);
+ compareDomain(line, 2.0, 6.0, 5.0, 9.0); // Detach doesn't change domain ranges
+ m_dataset->attachAxis(line, logValue1);
+ compareDomain(line, 4.0, 8.0, 5.0, 9.0);
+
+ // Detach y logvalue and attach y value
+ m_dataset->detachAxis(line, logValue2);
+ compareDomain(line, 4.0, 8.0, 5.0, 9.0); // Detach doesn't change domain ranges
+ m_dataset->attachAxis(line, value2);
+ compareDomain(line, 4.0, 8.0, 3.0, 7.0);
+}
+
+void tst_ChartDataSet::compareDomain(QAbstractSeries *series, qreal minX, qreal maxX,
+ qreal minY, qreal maxY) const
+{
+ AbstractDomain *domain = m_dataset->domainForSeries(series);
+ QCOMPARE(domain->minX(), minX);
+ QCOMPARE(domain->maxX(), maxX);
+ QCOMPARE(domain->minY(), minY);
+ QCOMPARE(domain->maxY(), maxY);
+}
+
QTEST_MAIN(tst_ChartDataSet)
#include "tst_chartdataset.moc"
diff --git a/tests/auto/qabstractaxis/tst_qabstractaxis.cpp b/tests/auto/qabstractaxis/tst_qabstractaxis.cpp
index b0297b14..08122187 100644
--- a/tests/auto/qabstractaxis/tst_qabstractaxis.cpp
+++ b/tests/auto/qabstractaxis/tst_qabstractaxis.cpp
@@ -46,6 +46,7 @@ void tst_QAbstractAxis::init(QAbstractAxis* axis, QAbstractSeries* series)
m_axis = axis;
m_series = series;
m_view = new QChartView(newQChartOrQPolarChart());
+ m_view->resize(200, 200);
m_chart = m_view->chart();
}
diff --git a/tests/auto/qbarmodelmapper/tst_qbarmodelmapper.cpp b/tests/auto/qbarmodelmapper/tst_qbarmodelmapper.cpp
index f4b12221..badbd67d 100644
--- a/tests/auto/qbarmodelmapper/tst_qbarmodelmapper.cpp
+++ b/tests/auto/qbarmodelmapper/tst_qbarmodelmapper.cpp
@@ -158,6 +158,7 @@ void tst_qbarmodelmapper::initTestCase()
{
m_chart = new QChart;
m_chartView = new QChartView(m_chart);
+ m_chartView->resize(200, 200);
m_chartView->show();
}
diff --git a/tests/auto/qcandlestickmodelmapper/tst_qcandlestickmodelmapper.cpp b/tests/auto/qcandlestickmodelmapper/tst_qcandlestickmodelmapper.cpp
index 32959116..cf3531ed 100644
--- a/tests/auto/qcandlestickmodelmapper/tst_qcandlestickmodelmapper.cpp
+++ b/tests/auto/qcandlestickmodelmapper/tst_qcandlestickmodelmapper.cpp
@@ -135,6 +135,7 @@ void tst_qcandlestickmodelmapper::initTestCase()
{
m_chart = new QChart();
m_chartView = new QChartView(m_chart);
+ m_chartView->resize(200, 200);
m_chartView->show();
}
diff --git a/tests/auto/qchart/tst_qchart.cpp b/tests/auto/qchart/tst_qchart.cpp
index 87487a50..75a395a2 100644
--- a/tests/auto/qchart/tst_qchart.cpp
+++ b/tests/auto/qchart/tst_qchart.cpp
@@ -143,6 +143,7 @@ void tst_QChart::cleanupTestCase()
void tst_QChart::init()
{
m_view = new QChartView(newQChartOrQPolarChart());
+ m_view->resize(200, 200);
m_chart = m_view->chart();
}
diff --git a/tests/auto/qchartview/tst_qchartview.cpp b/tests/auto/qchartview/tst_qchartview.cpp
index 6a300391..4f36dbc1 100644
--- a/tests/auto/qchartview/tst_qchartview.cpp
+++ b/tests/auto/qchartview/tst_qchartview.cpp
@@ -78,6 +78,7 @@ void tst_QChartView::cleanupTestCase()
void tst_QChartView::init()
{
m_view = new QChartView(newQChartOrQPolarChart());
+ m_view->resize(200, 200);
m_view->chart()->legend()->setVisible(false);
}
diff --git a/tests/auto/qdatetimeaxis/tst_qdatetimeaxis.cpp b/tests/auto/qdatetimeaxis/tst_qdatetimeaxis.cpp
index 832deb18..fa2f5447 100644
--- a/tests/auto/qdatetimeaxis/tst_qdatetimeaxis.cpp
+++ b/tests/auto/qdatetimeaxis/tst_qdatetimeaxis.cpp
@@ -91,6 +91,7 @@ void tst_QDateTimeAxis::init()
// tst_QAbstractAxis::init(m_datetimeaxis, m_series);
m_view = new QChartView;
+ m_view->resize(200, 200);
m_chart = m_view->chart();
m_chart->addSeries(m_series);
m_chart->setAxisY(m_dateTimeAxisY, m_series);
diff --git a/tests/auto/qhorizontalpercentbarseries/tst_qhorizontalpercentbarseries.cpp b/tests/auto/qhorizontalpercentbarseries/tst_qhorizontalpercentbarseries.cpp
index a7359288..c01cb58f 100644
--- a/tests/auto/qhorizontalpercentbarseries/tst_qhorizontalpercentbarseries.cpp
+++ b/tests/auto/qhorizontalpercentbarseries/tst_qhorizontalpercentbarseries.cpp
@@ -624,6 +624,7 @@ void tst_QHorizontalPercentBarSeries::zeroValuesInSeries()
series->append(set2);
QChartView view(new QChart());
+ view.resize(400, 300);
view.chart()->addSeries(series);
view.chart()->createDefaultAxes();
view.show();
diff --git a/tests/auto/qlineseries/tst_qlineseries.cpp b/tests/auto/qlineseries/tst_qlineseries.cpp
index 2662a896..995abc04 100644
--- a/tests/auto/qlineseries/tst_qlineseries.cpp
+++ b/tests/auto/qlineseries/tst_qlineseries.cpp
@@ -128,6 +128,7 @@ void tst_QLineSeries::pressedSignal()
lineSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(lineSeries);
view.show();
@@ -159,6 +160,7 @@ void tst_QLineSeries::releasedSignal()
lineSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(lineSeries);
view.show();
@@ -212,6 +214,7 @@ void tst_QLineSeries::doubleClickedSignal()
lineSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(lineSeries);
view.show();
diff --git a/tests/auto/qlogvalueaxis/tst_qlogvalueaxis.cpp b/tests/auto/qlogvalueaxis/tst_qlogvalueaxis.cpp
index 3016007f..26523b4b 100644
--- a/tests/auto/qlogvalueaxis/tst_qlogvalueaxis.cpp
+++ b/tests/auto/qlogvalueaxis/tst_qlogvalueaxis.cpp
@@ -61,6 +61,8 @@ private slots:
void range_raw();
void range_data();
void range();
+ void range_before_show_data();
+ void range_before_show();
void range_animation_data();
void range_animation();
void noautoscale_data();
@@ -253,6 +255,7 @@ void tst_QLogValueAxis::range_raw_data()
QTest::newRow("0.1 - 1.0") << (qreal)0.1 << (qreal)1.0 << (qreal)0.1 << (qreal)1.0 << true << false;
QTest::newRow("25.0 - 75.0") << (qreal)25.0 << (qreal)75.0 << (qreal)25.0 << (qreal)75.0 << true << true;
QTest::newRow("10.0 - 5.0") << (qreal)10.0 << (qreal)5.0 << (qreal)1.0 << (qreal)1.0 << false << false;
+ QTest::newRow("2.0 - 7.0") << (qreal)2.0 << (qreal)7.0 << (qreal)2.0 << (qreal)7.0 << true << true;
}
void tst_QLogValueAxis::range_raw()
@@ -292,6 +295,20 @@ void tst_QLogValueAxis::range()
range_raw();
}
+void tst_QLogValueAxis::range_before_show_data()
+{
+ range_raw_data();
+}
+
+void tst_QLogValueAxis::range_before_show()
+{
+ range_raw();
+
+ m_chart->setAxisX(m_logvaluesaxis, m_series);
+ m_view->show();
+ QTest::qWaitForWindowShown(m_view);
+}
+
void tst_QLogValueAxis::range_animation_data()
{
range_data();
diff --git a/tests/auto/qpercentbarseries/tst_qpercentbarseries.cpp b/tests/auto/qpercentbarseries/tst_qpercentbarseries.cpp
index 12c97b4b..cd16c7e9 100644
--- a/tests/auto/qpercentbarseries/tst_qpercentbarseries.cpp
+++ b/tests/auto/qpercentbarseries/tst_qpercentbarseries.cpp
@@ -628,6 +628,7 @@ void tst_QPercentBarSeries::zeroValuesInSeries()
series->append(set2);
QChartView view(new QChart());
+ view.resize(400, 300);
view.chart()->addSeries(series);
view.chart()->createDefaultAxes();
view.show();
diff --git a/tests/auto/qpiemodelmapper/tst_qpiemodelmapper.cpp b/tests/auto/qpiemodelmapper/tst_qpiemodelmapper.cpp
index 0ebb7608..e7b50795 100644
--- a/tests/auto/qpiemodelmapper/tst_qpiemodelmapper.cpp
+++ b/tests/auto/qpiemodelmapper/tst_qpiemodelmapper.cpp
@@ -157,6 +157,7 @@ void tst_qpiemodelmapper::initTestCase()
{
m_chart = new QChart;
m_chartView = new QChartView(m_chart);
+ m_chartView->resize(200, 200);
m_chartView->show();
}
diff --git a/tests/auto/qpieseries/tst_qpieseries.cpp b/tests/auto/qpieseries/tst_qpieseries.cpp
index b3fc6d12..022aa5b7 100644
--- a/tests/auto/qpieseries/tst_qpieseries.cpp
+++ b/tests/auto/qpieseries/tst_qpieseries.cpp
@@ -93,6 +93,7 @@ void tst_qpieseries::cleanupTestCase()
void tst_qpieseries::init()
{
m_view = new QChartView();
+ m_view->resize(200, 200);
m_series = new QPieSeries(m_view);
m_view->show();
QTest::qWaitForWindowShown(m_view);
diff --git a/tests/auto/qpieslice/tst_qpieslice.cpp b/tests/auto/qpieslice/tst_qpieslice.cpp
index 5e1dd099..bc68ddd5 100644
--- a/tests/auto/qpieslice/tst_qpieslice.cpp
+++ b/tests/auto/qpieslice/tst_qpieslice.cpp
@@ -253,6 +253,7 @@ void tst_qpieslice::clickedSignal()
// add series to the chart
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(series);
view.show();
@@ -290,6 +291,7 @@ void tst_qpieslice::hoverSignal()
// add series to the chart
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(series);
view.show();
@@ -369,6 +371,7 @@ void tst_qpieslice::pressedSignal()
// add series to the chart
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(series);
view.show();
@@ -409,6 +412,7 @@ void tst_qpieslice::releasedSignal()
// add series to the chart
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(series);
view.show();
@@ -443,6 +447,7 @@ void tst_qpieslice::doubleClickedSignal()
// add series to the chart
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(series);
view.show();
diff --git a/tests/auto/qscatterseries/tst_qscatterseries.cpp b/tests/auto/qscatterseries/tst_qscatterseries.cpp
index b3e9c3f2..fdd91198 100644
--- a/tests/auto/qscatterseries/tst_qscatterseries.cpp
+++ b/tests/auto/qscatterseries/tst_qscatterseries.cpp
@@ -151,6 +151,7 @@ void tst_QScatterSeries::pressedSignal()
scatterSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(scatterSeries);
view.show();
@@ -182,6 +183,7 @@ void tst_QScatterSeries::releasedSignal()
scatterSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(scatterSeries);
view.show();
@@ -213,6 +215,7 @@ void tst_QScatterSeries::doubleClickedSignal()
scatterSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(scatterSeries);
view.show();
diff --git a/tests/auto/qsplineseries/tst_qsplineseries.cpp b/tests/auto/qsplineseries/tst_qsplineseries.cpp
index d390db40..0b979658 100644
--- a/tests/auto/qsplineseries/tst_qsplineseries.cpp
+++ b/tests/auto/qsplineseries/tst_qsplineseries.cpp
@@ -120,6 +120,7 @@ void tst_QSplineSeries::pressedSignal()
splineSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(splineSeries);
view.show();
@@ -151,6 +152,7 @@ void tst_QSplineSeries::releasedSignal()
splineSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(splineSeries);
view.show();
@@ -182,6 +184,7 @@ void tst_QSplineSeries::doubleClickedSignal()
splineSeries->append(QPointF(6, 12));
QChartView view;
+ view.resize(200, 200);
view.chart()->legend()->setVisible(false);
view.chart()->addSeries(splineSeries);
view.show();
diff --git a/tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp b/tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp
index 3e891168..d990926d 100644
--- a/tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp
+++ b/tests/auto/qxymodelmapper/tst_qxymodelmapper.cpp
@@ -159,6 +159,7 @@ void tst_qxymodelmapper::initTestCase()
{
m_chart = newQChartOrQPolarChart();
m_chartView = new QChartView(m_chart);
+ m_chartView->resize(200, 200);
m_chartView->show();
}
diff --git a/tests/auto/qxyseries/tst_qxyseries.cpp b/tests/auto/qxyseries/tst_qxyseries.cpp
index da1fd3db..767caca2 100644
--- a/tests/auto/qxyseries/tst_qxyseries.cpp
+++ b/tests/auto/qxyseries/tst_qxyseries.cpp
@@ -43,6 +43,7 @@ void tst_QXYSeries::cleanupTestCase()
void tst_QXYSeries::init()
{
m_view = new QChartView(newQChartOrQPolarChart());
+ m_view->resize(200, 200);
m_chart = m_view->chart();
}
diff --git a/tests/manual/barcharttester/barcharttester.pro b/tests/manual/barcharttester/barcharttester.pro
new file mode 100644
index 00000000..09b28bba
--- /dev/null
+++ b/tests/manual/barcharttester/barcharttester.pro
@@ -0,0 +1,9 @@
+QT += core gui charts widgets
+
+TARGET = barcharttester
+TEMPLATE = app
+
+SOURCES += main.cpp\
+ chart-widget.cpp
+
+HEADERS += chart-widget.h
diff --git a/tests/manual/barcharttester/chart-widget.cpp b/tests/manual/barcharttester/chart-widget.cpp
new file mode 100644
index 00000000..6e7157b1
--- /dev/null
+++ b/tests/manual/barcharttester/chart-widget.cpp
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Charts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "chart-widget.h"
+
+#include <QtCharts/QLineSeries>
+#include <QtCharts/QLegend>
+#include <QtCharts/QBarCategoryAxis>
+#include <QtCharts/QValueAxis>
+#include <QtCharts/QLogValueAxis>
+#include <QtCharts/QBarSet>
+#include <QtCharts/QHorizontalBarSeries>
+#include <QtCharts/QHorizontalPercentBarSeries>
+#include <QtCharts/QHorizontalStackedBarSeries>
+#include <QtCharts/QPercentBarSeries>
+#include <QtCharts/QStackedBarSeries>
+#include <QtCore/QTime>
+#include <QElapsedTimer>
+#include <QDebug>
+
+//#define VALUE_LOGGING 1
+
+const int initialCount = 100000;
+const int visibleCount = 20;
+const int storeCount = 100000000;
+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 = 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 = 300;
+
+const bool horizontal = false;
+const bool percent = 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;
+const bool doRemove = false;
+const bool doInsert = false;
+const bool singleReplace = false;
+const bool singleRemove = false;
+const bool singleInsert = false;
+const int removeIndex = -7;
+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/%2");
+
+ChartWidget::ChartWidget(QWidget *parent) :
+ QWidget(parent),
+ m_chart(new QChart()),
+ m_chartView(new QChartView(this)),
+ m_setCount(initialSetCount),
+ m_seriesCount(initialSeriesCount),
+ m_extraScroll(0.0)
+{
+ m_elapsedTimer.start();
+
+ if (logarithmic) {
+ QLogValueAxis *logAxis = new QLogValueAxis;
+ logAxis->setBase(2);
+ m_valueAxis = logAxis;
+ } else {
+ m_valueAxis = new QValueAxis;
+ }
+
+ if (barCategories)
+ m_barAxis = new QBarCategoryAxis;
+ else
+ m_barAxis = new QValueAxis;
+
+ m_barAxis->setReverse(reverseBar);
+ m_valueAxis->setReverse(reverseValue);
+
+ 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());
+
+ resize(800, 300);
+ m_horizontalLayout = new QHBoxLayout(this);
+ m_horizontalLayout->setSpacing(6);
+ m_horizontalLayout->setContentsMargins(11, 11, 11, 11);
+ m_horizontalLayout->addWidget(m_chartView);
+
+ qDebug() << "UI setup time:"<< m_elapsedTimer.restart();
+
+ m_chartView->setRenderHint(QPainter::Antialiasing);
+
+ createChart();
+ qDebug() << "Chart creation time:"<< m_elapsedTimer.restart();
+
+ QObject::connect(&m_timer, SIGNAL(timeout()), this, SLOT(handleTimeout()));
+
+ m_timer.setInterval(interval);
+ m_timer.start();
+}
+
+ChartWidget::~ChartWidget()
+{
+}
+
+void ChartWidget::handleTimeout()
+{
+ qDebug() << "Intervening time:" << m_elapsedTimer.restart();
+
+ qDebug() << "----------" << counter << "----------";
+
+ bool doScroll = false;
+
+ if (counter % appendFrequency == 0) {
+ 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;
+ 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 < 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 % 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) {
+ qDebug() << "Adjusting setcount, current count:" << m_setCount;
+ static int setCountAdder = 1;
+ if (m_setCount == maxSetCount) {
+ if (removeSets)
+ setCountAdder = -1;
+ else
+ setCountAdder = 0;
+ } else if (m_setCount == 0) {
+ setCountAdder = 1;
+ }
+ 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.at(0)->setLabelsVisible(!m_series.at(0)->isLabelsVisible());
+ qDebug() << "Label visibility changed";
+ }
+
+ if (visibleTrigger > 0 && counter % visibleTrigger == 0) {
+ m_series.at(0)->setVisible(!m_series.at(0)->isVisible());
+ qDebug() << "Series visibility changed";
+ }
+
+ if (animationTrigger > 0 && counter % animationTrigger == 0) {
+ if (m_chart->animationOptions() == QChart::SeriesAnimations)
+ m_chart->setAnimationOptions(QChart::NoAnimation);
+ else
+ m_chart->setAnimationOptions(QChart::SeriesAnimations);
+ qDebug() << "Series animation changed";
+ }
+
+ qDebug() << "Rest of time:" << m_elapsedTimer.restart();
+
+ qDebug() << "GraphicsItem Count:" << m_chart->scene()->items().size();
+ counter++;
+}
+
+void ChartWidget::createChart()
+{
+ qDebug() << "Initial bar count:" << initialCount;
+
+ QList<qreal> valueList;
+ valueList.reserve(initialCount);
+ 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);
+ }
+ if (j < m_setCount)
+ m_series.at(i)->append(set);
+ }
+ }
+ for (int i = 0; i < initialSeriesCount; i++)
+ addSeriesToChart(m_series.at(i));
+
+ m_chart->setTitle("Chart");
+ if (animation) {
+ m_chart->setAnimationOptions(QChart::SeriesAnimations);
+ m_chart->setAnimationDuration(animationDuration);
+ }
+
+ 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 {
+ qobject_cast<QValueAxis *>(m_barAxis)->setTickCount(11);
+ }
+
+ if (initialCount > visibleCount)
+ m_barAxis->setRange(initialCount - visibleCount, initialCount);
+ else
+ m_barAxis->setRange(0, visibleCount);
+
+ 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
new file mode 100644
index 00000000..0b788172
--- /dev/null
+++ b/tests/manual/barcharttester/chart-widget.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Charts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CHARTWIDGET_H
+#define CHARTWIDGET_H
+
+#include <QtWidgets/QHBoxLayout>
+#include <QtCharts/QChart>
+#include <QtCharts/QChartView>
+#include <QtCharts/QValueAxis>
+#include <QtCharts/QBarSeries>
+#include <QtCharts/QBarSet>
+#include <QtCore/QTimer>
+#include <QtCore/QElapsedTimer>
+
+QT_CHARTS_USE_NAMESPACE
+
+
+class ChartWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ChartWidget(QWidget *parent = 0);
+ ~ChartWidget();
+
+public slots:
+ void handleTimeout();
+
+private:
+ void createChart();
+ void addSeriesToChart(QAbstractBarSeries *series);
+
+private:
+ QChart *m_chart;
+ QChartView *m_chartView;
+ QAbstractAxis *m_barAxis;
+ QAbstractAxis *m_valueAxis;
+ 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
diff --git a/tests/manual/barcharttester/main.cpp b/tests/manual/barcharttester/main.cpp
new file mode 100644
index 00000000..eb4cd99f
--- /dev/null
+++ b/tests/manual/barcharttester/main.cpp
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Charts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "chart-widget.h"
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+
+ ChartWidget w;
+ w.resize(1200, 800);
+ w.show();
+
+ return a.exec();
+}
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 0e02e8c2..507fd47c 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -3,7 +3,8 @@ SUBDIRS += \
presenterchart \
polarcharttest \
boxplottester \
- candlesticktester
+ candlesticktester \
+ barcharttester
contains(QT_CONFIG, opengl) {
SUBDIRS += chartwidgettest \
diff --git a/tests/manual/qmlchartproperties/qml/qmlchartproperties/ChartEditorLegend.qml b/tests/manual/qmlchartproperties/qml/qmlchartproperties/ChartEditorLegend.qml
index 81bc82dd..534127b2 100644
--- a/tests/manual/qmlchartproperties/qml/qmlchartproperties/ChartEditorLegend.qml
+++ b/tests/manual/qmlchartproperties/qml/qmlchartproperties/ChartEditorLegend.qml
@@ -28,6 +28,7 @@
****************************************************************************/
import QtQuick 2.0
+import QtCharts 2.2
Row {
anchors.fill: parent
@@ -78,6 +79,17 @@ Row {
text: "legend use reverse order"
onClicked: chartLegend.reverseMarkers = !chartLegend.reverseMarkers;
}
+ Button {
+ text: "legend marker shape"
+ onClicked: {
+ if (chartLegend.markerShape === Legend.MarkerShapeRectangle)
+ chartLegend.markerShape = Legend.MarkerShapeCircle
+ else if (chartLegend.markerShape === Legend.MarkerShapeCircle)
+ chartLegend.markerShape = Legend.MarkerShapeFromSeries
+ else
+ chartLegend.markerShape = Legend.MarkerShapeRectangle
+ }
+ }
}
FontEditor {