summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--dist/changes-5.15.237
-rw-r--r--src/charts/axis/cartesianchartaxis.cpp27
-rw-r--r--src/charts/axis/chartaxiselement.cpp28
-rw-r--r--src/charts/axis/horizontalaxis.cpp21
-rw-r--r--src/charts/axis/valueaxis/chartvalueaxisx.cpp13
-rw-r--r--src/charts/axis/valueaxis/chartvalueaxisy.cpp13
-rw-r--r--src/charts/axis/valueaxis/qvalueaxis.cpp2
-rw-r--r--src/charts/chartpresenter.cpp53
-rw-r--r--src/charts/domain/abstractdomain.cpp3
-rw-r--r--src/charts/layout/abstractchartlayout.cpp15
-rw-r--r--src/charts/legend/legendmarkeritem.cpp2
-rw-r--r--src/charts/legend/qlegend.cpp2
-rw-r--r--src/charts/piechart/qpieslice.cpp2
-rw-r--r--src/charts/xychart/qhxymodelmapper.cpp12
-rw-r--r--src/charts/xychart/qvxymodelmapper.cpp12
-rw-r--r--src/charts/xychart/qxymodelmapper.cpp27
-rw-r--r--src/chartsqml2/declarativechart.cpp8
-rw-r--r--src/chartsqml2/declarativeopenglrendernode.cpp3
-rw-r--r--tests/auto/qbarseries/BLACKLIST2
20 files changed, 196 insertions, 88 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 05561703..4a55f521 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -2,4 +2,4 @@ load(qt_build_config)
DEFINES += QT_NO_JAVA_STYLE_ITERATORS QT_NO_LINKED_LIST
-MODULE_VERSION = 5.15.3
+MODULE_VERSION = 5.15.13
diff --git a/dist/changes-5.15.2 b/dist/changes-5.15.2
new file mode 100644
index 00000000..564c6cf9
--- /dev/null
+++ b/dist/changes-5.15.2
@@ -0,0 +1,37 @@
+Qt 5.15.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.15.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+ https://doc.qt.io/qt-5.15/index.html
+
+The Qt version 5.15 series is binary compatible with the 5.14.x series.
+Applications compiled for 5.14 will continue to run with 5.15.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+ https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Important Behavior Changes *
+****************************************************************************
+
+****************************************************************************
+* Library *
+****************************************************************************
+
+ - [QTBUG-75500] Prevent zoom-out beyond infinite linear max double values
+ When zooming out domains that include log axes, prevent zoom-out when
+ the new zoom level would result in a max value that is above the upper
+ range of double (represented as inf). Additionally, prevent zoom-out
+ when the log value exceeds the number of pixels in the log axis
+ dimension. Major gridlines are created at each integral log value, and
+ it does not make sense to zoom out beyond such point.
+ The bug was: Stack overflow on high zoom out of QChart with a
+ logarithmic axis
+
diff --git a/src/charts/axis/cartesianchartaxis.cpp b/src/charts/axis/cartesianchartaxis.cpp
index 935a9601..6389c17e 100644
--- a/src/charts/axis/cartesianchartaxis.cpp
+++ b/src/charts/axis/cartesianchartaxis.cpp
@@ -130,15 +130,16 @@ void CartesianChartAxis::updateMinorTickItems()
expectedCount = qMax(expectedCount, 0);
} else {
const qreal interval = valueAxis->tickInterval();
- qreal firstMajorTick = valueAxis->tickAnchor();
+ const qreal anchor = valueAxis->tickAnchor();
const qreal max = valueAxis->max();
const qreal min = valueAxis->min();
const int _minorTickCount = valueAxis->minorTickCount();
- if (min < firstMajorTick)
- firstMajorTick = firstMajorTick - qCeil((firstMajorTick - min) / interval) * interval;
- else
- firstMajorTick = firstMajorTick + int((min - firstMajorTick) / interval) * interval;
+ // Find the closest major tick <= the min of the range, even if it's not drawn!
+ // This is where we'll start counting minor ticks from, because minor ticks
+ // might need to be drawn even before the first major tick.
+ const qreal ticksFromAnchor = (anchor - min) / interval;
+ const qreal firstMajorTick = anchor - std::ceil(ticksFromAnchor) * interval;
const qreal deltaMinor = interval / qreal(_minorTickCount + 1);
qreal minorTick = firstMajorTick + deltaMinor;
@@ -265,7 +266,7 @@ bool CartesianChartAxis::isEmpty()
{
return axisGeometry().isEmpty()
|| gridGeometry().isEmpty()
- || qFuzzyCompare(min(), max());
+ || qFuzzyIsNull(max() - min());
}
void CartesianChartAxis::setGeometry(const QRectF &axis, const QRectF &grid)
@@ -371,16 +372,16 @@ void CartesianChartAxis::updateLabelsValues(QValueAxis *axis)
static_cast<ValueAxisLabel *>(labelItems().at(i))->setValue(value);
}
} else {
- qreal value = axis->tickAnchor();
- if (value > min())
- value = value - int((value - min()) / axis->tickInterval()) * axis->tickInterval();
- else
- value = value + qCeil((min() - value) / axis->tickInterval()) * axis->tickInterval();
+ const qreal anchor = axis->tickAnchor();
+ const qreal interval = axis->tickInterval();
+ const qreal ticksFromAnchor = (anchor - min()) / interval;
+ const qreal firstMajorTick = anchor - std::floor(ticksFromAnchor) * interval;
int i = axis->isReverse() ? labelItems().count()-1 : 0;
- while (value <= max() || qFuzzyCompare(value, max())) {
+ qreal value = firstMajorTick;
+ while (value <= max()) {
static_cast<ValueAxisLabel *>(labelItems().at(i))->setValue(value);
- value += axis->tickInterval();
+ value += interval;
i += axis->isReverse() ? -1 : 1;
}
}
diff --git a/src/charts/axis/chartaxiselement.cpp b/src/charts/axis/chartaxiselement.cpp
index 8db5ef36..2a874216 100644
--- a/src/charts/axis/chartaxiselement.cpp
+++ b/src/charts/axis/chartaxiselement.cpp
@@ -339,7 +339,7 @@ bool ChartAxisElement::isEmpty()
{
return axisGeometry().isEmpty()
|| gridGeometry().isEmpty()
- || qFuzzyCompare(min(), max());
+ || qFuzzyIsNull(max() - min());
}
qreal ChartAxisElement::min() const
@@ -419,20 +419,22 @@ QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks,
return labels;
if (format.isEmpty()) {
- int n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0) + 1;
+ // Calculate how many decimal digits are needed to show difference between ticks,
+ // for example tick marks 1.002 and 1.003 have a difference of 0.001 and need 3 decimals.
+ // For differences >= 1 (positive log10) use always 1 decimal.
+ double l10 = std::log10((max - min) / (ticks - 1));
+ int n = qMax(int(-qFloor(l10)), 0) + 1;
if (tickType == QValueAxis::TicksFixed) {
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << presenter()->numberToString(value, 'f', n);
}
} else {
- qreal value = tickAnchor;
- if (value > min)
- value = value - int((value - min) / tickInterval) * tickInterval;
- else
- value = value + qCeil((min - value) / tickInterval) * tickInterval;
+ const qreal ticksFromAnchor = (tickAnchor - min) / tickInterval;
+ const qreal firstMajorTick = tickAnchor - std::floor(ticksFromAnchor) * tickInterval;
- while (value <= max || qFuzzyCompare(value, max)) {
+ qreal value = firstMajorTick;
+ while (value <= max) {
labels << presenter()->numberToString(value, 'f', n);
value += tickInterval;
}
@@ -468,13 +470,11 @@ QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks,
labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
}
} else {
- qreal value = tickAnchor;
- if (value > min)
- value = value - int((value - min) / tickInterval) * tickInterval;
- else
- value = value + qCeil((min - value) / tickInterval) * tickInterval;
+ const qreal ticksFromAnchor = (tickAnchor - min) / tickInterval;
+ const qreal firstMajorTick = tickAnchor - std::floor(ticksFromAnchor) * tickInterval;
- while (value <= max || qFuzzyCompare(value, max)) {
+ qreal value = firstMajorTick;
+ while (value <= max) {
labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
value += tickInterval;
}
diff --git a/src/charts/axis/horizontalaxis.cpp b/src/charts/axis/horizontalaxis.cpp
index 3bbc5ebe..a54339aa 100644
--- a/src/charts/axis/horizontalaxis.cpp
+++ b/src/charts/axis/horizontalaxis.cpp
@@ -102,7 +102,6 @@ void HorizontalAxis::updateGeometry()
else if (axis()->alignment() == Qt::AlignBottom)
arrowItem->setLine(gridRect.left(), axisRect.top(), gridRect.right(), axisRect.top());
- qreal width = 0;
const QLatin1String ellipsis("...");
//title
@@ -133,6 +132,8 @@ void HorizontalAxis::updateGeometry()
QList<QGraphicsItem *> lines = gridItems();
QList<QGraphicsItem *> shades = shadeItems();
+ qreal last_label_max_x = 0;
+
for (int i = 0; i < layout.size(); ++i) {
//items
QGraphicsLineItem *gridItem = static_cast<QGraphicsLineItem*>(lines.at(i));
@@ -160,6 +161,7 @@ void HorizontalAxis::updateGeometry()
labelItem->setHtml(text);
} else {
qreal labelWidth = axisRect.width() / layout.count() - (2 * labelPadding());
+ // Replace digits with ellipsis "..." if number does not fit
QString truncatedText = ChartPresenter::truncatedText(axis()->labelsFont(), text,
axis()->labelsAngle(),
labelWidth,
@@ -255,13 +257,14 @@ void HorizontalAxis::updateGeometry()
labelItem->setPos(labelPos.toPoint());
//label overlap detection - compensate one pixel for rounding errors
- if ((labelItem->pos().x() < width && labelItem->toPlainText() == ellipsis) || forceHide ||
- (labelItem->pos().x() + (widthDiff / 2.0)) < (axisRect.left() - 1.0) ||
- (labelItem->pos().x() + (widthDiff / 2.0) - 1.0) > axisRect.right()) {
+ if ((labelItem->pos().x() < last_label_max_x && labelItem->toPlainText() == ellipsis)
+ || forceHide
+ || (labelItem->pos().x() + (widthDiff / 2.0)) < (axisRect.left() - 1.0)
+ || (labelItem->pos().x() + (widthDiff / 2.0) - 1.0) > axisRect.right()) {
labelItem->setVisible(false);
} else {
labelItem->setVisible(true);
- width = boundingRect.width() + labelItem->pos().x();
+ last_label_max_x = boundingRect.width() + labelItem->pos().x();
}
//shades
@@ -433,12 +436,12 @@ void HorizontalAxis::updateMinorTickGeometry()
qreal minorArrowLineItemY2;
switch (axis()->alignment()) {
case Qt::AlignTop:
- minorArrowLineItemY1 = gridGeometry().bottom();
- minorArrowLineItemY2 = gridGeometry().bottom() - labelPadding() / 2.0;
+ minorArrowLineItemY1 = gridGeometry().top();
+ minorArrowLineItemY2 = gridGeometry().top() - labelPadding() / 2.0;
break;
case Qt::AlignBottom:
- minorArrowLineItemY1 = gridGeometry().top();
- minorArrowLineItemY2 = gridGeometry().top() + labelPadding() / 2.0;
+ minorArrowLineItemY1 = gridGeometry().bottom();
+ minorArrowLineItemY2 = gridGeometry().bottom() + labelPadding() / 2.0;
break;
default:
minorArrowLineItemY1 = 0.0;
diff --git a/src/charts/axis/valueaxis/chartvalueaxisx.cpp b/src/charts/axis/valueaxis/chartvalueaxisx.cpp
index 3eac86e0..db943480 100644
--- a/src/charts/axis/valueaxis/chartvalueaxisx.cpp
+++ b/src/charts/axis/valueaxis/chartvalueaxisx.cpp
@@ -75,22 +75,21 @@ QVector<qreal> ChartValueAxisX::calculateLayout() const
return points;
} else { // QValueAxis::TicksDynamic
const qreal interval = m_axis->tickInterval();
- qreal value = m_axis->tickAnchor();
+ const qreal anchor = m_axis->tickAnchor();
const qreal maxValue = max();
const qreal minValue = min();
- // Find the first major tick right after the min of range
- if (value > minValue)
- value = value - int((value - minValue) / interval) * interval;
- else
- value = value + qCeil((minValue - value) / interval) * interval;
+ // Find the first major tick right after the min of the range
+ const qreal ticksFromAnchor = (anchor - minValue) / interval;
+ const qreal firstMajorTick = anchor - std::floor(ticksFromAnchor) * interval;
const QRectF &gridRect = gridGeometry();
const qreal deltaX = gridRect.width() / (maxValue - minValue);
QVector<qreal> points;
const qreal leftPos = gridRect.left();
- while (value <= maxValue || qFuzzyCompare(value, maxValue)) {
+ qreal value = firstMajorTick;
+ while (value <= maxValue) {
points << (value - minValue) * deltaX + leftPos;
value += interval;
}
diff --git a/src/charts/axis/valueaxis/chartvalueaxisy.cpp b/src/charts/axis/valueaxis/chartvalueaxisy.cpp
index c4868fc2..3ffeddf9 100644
--- a/src/charts/axis/valueaxis/chartvalueaxisy.cpp
+++ b/src/charts/axis/valueaxis/chartvalueaxisy.cpp
@@ -76,22 +76,21 @@ QVector<qreal> ChartValueAxisY::calculateLayout() const
return points;
} else {
const qreal interval = m_axis->tickInterval();
- qreal value = m_axis->tickAnchor();
+ const qreal anchor = m_axis->tickAnchor();
const qreal maxValue = max();
const qreal minValue = min();
- // Find the first major tick right after the min of range
- if (value > minValue)
- value = value - int((value - minValue) / interval) * interval;
- else
- value = value + qCeil((minValue - value) / interval) * interval;
+ // Find the first major tick right after the min of the range
+ const qreal ticksFromAnchor = (anchor - minValue) / interval;
+ const qreal firstMajorTick = anchor - std::floor(ticksFromAnchor) * interval;
const QRectF &gridRect = gridGeometry();
const qreal deltaY = gridRect.height() / (maxValue - minValue);
QVector<qreal> points;
const qreal bottomPos = gridRect.bottom();
- while (value <= maxValue || qFuzzyCompare(value, maxValue)) {
+ qreal value = firstMajorTick;
+ while (value <= maxValue) {
points << (value - minValue) * -deltaY + bottomPos;
value += interval;
}
diff --git a/src/charts/axis/valueaxis/qvalueaxis.cpp b/src/charts/axis/valueaxis/qvalueaxis.cpp
index 905168d7..90954483 100644
--- a/src/charts/axis/valueaxis/qvalueaxis.cpp
+++ b/src/charts/axis/valueaxis/qvalueaxis.cpp
@@ -200,7 +200,7 @@ QT_CHARTS_BEGIN_NAMESPACE
\sa QString::asprintf()
*/
/*!
- \qmlproperty real ValueAxis::labelFormat
+ \qmlproperty string ValueAxis::labelFormat
The format string supports the following conversion specifiers, length modifiers, and flags
provided by \c printf() in the standard C++ library: d, i, o, x, X, f, F, e, E, g, G, c.
diff --git a/src/charts/chartpresenter.cpp b/src/charts/chartpresenter.cpp
index 6be0b141..f482adf3 100644
--- a/src/charts/chartpresenter.cpp
+++ b/src/charts/chartpresenter.cpp
@@ -98,7 +98,7 @@ void ChartPresenter::setFixedGeometry(const QRectF &rect)
void ChartPresenter::setGeometry(const QRectF rect)
{
- if (m_rect != rect) {
+ if (rect.isValid() && m_rect != rect) {
m_rect = rect;
if (!m_fixedRect.isNull())
return;
@@ -478,18 +478,59 @@ ChartTitle *ChartPresenter::titleElement()
return m_title;
}
+template <int TSize>
+struct TextBoundCache
+{
+ struct element
+ {
+ quint32 lastUsed;
+ QRectF bounds;
+ };
+ QHash<QString, element> elements;
+ quint32 usedCounter = 0;
+ QGraphicsTextItem dummyText;
+
+ QRectF bounds(const QFont &font, const QString &text)
+ {
+ const QString key = font.key() + text;
+ auto elem = elements.find(key);
+ if (elem != elements.end()) {
+ usedCounter++;
+ elem->lastUsed = usedCounter;
+ return elem->bounds;
+ }
+ dummyText.setFont(font);
+ dummyText.setHtml(text);
+ const QRectF bounds = dummyText.boundingRect();
+ if (elements.size() >= TSize) {
+ auto elem = std::min_element(elements.begin(), elements.end(),
+ [](const element &a, const element &b) {
+ return a.lastUsed < b.lastUsed;
+ });
+ if (elem != elements.end()) {
+ const QString key = elem.key();
+ elements.remove(key);
+ }
+ }
+ elements.insert(key, {usedCounter++, bounds});
+ return bounds;
+ }
+ QTextDocument *document()
+ {
+ return dummyText.document();
+ }
+};
+
QRectF ChartPresenter::textBoundingRect(const QFont &font, const QString &text, qreal angle)
{
- static QGraphicsTextItem dummyTextItem;
+ static TextBoundCache<32> textBoundCache;
static bool initMargin = true;
if (initMargin) {
- dummyTextItem.document()->setDocumentMargin(textMargin());
+ textBoundCache.document()->setDocumentMargin(textMargin());
initMargin = false;
}
- dummyTextItem.setFont(font);
- dummyTextItem.setHtml(text);
- QRectF boundingRect = dummyTextItem.boundingRect();
+ QRectF boundingRect = textBoundCache.bounds(font, text);
// Take rotation into account
if (angle) {
diff --git a/src/charts/domain/abstractdomain.cpp b/src/charts/domain/abstractdomain.cpp
index 36e4974a..be50d7cf 100644
--- a/src/charts/domain/abstractdomain.cpp
+++ b/src/charts/domain/abstractdomain.cpp
@@ -58,6 +58,9 @@ AbstractDomain::~AbstractDomain()
void AbstractDomain::setSize(const QSizeF &size)
{
+ if (!size.isValid())
+ return;
+
if (m_size != size) {
m_size=size;
emit updated();
diff --git a/src/charts/layout/abstractchartlayout.cpp b/src/charts/layout/abstractchartlayout.cpp
index fd3c5e97..c612d197 100644
--- a/src/charts/layout/abstractchartlayout.cpp
+++ b/src/charts/layout/abstractchartlayout.cpp
@@ -53,6 +53,7 @@ void AbstractChartLayout::setGeometry(const QRectF &rect)
{
if (!rect.isValid())
return;
+
// If the chart has a fixed geometry then don't update visually
const bool updateLayout = (!m_presenter->isFixedGeometry() || m_presenter->geometry() == rect);
if (m_presenter->chart()->isVisible()) {
@@ -73,12 +74,14 @@ void AbstractChartLayout::setGeometry(const QRectF &rect)
contentGeometry = calculateAxisGeometry(contentGeometry, axes, updateLayout);
- m_presenter->setGeometry(contentGeometry);
- if (updateLayout) {
- if (m_presenter->chart()->chartType() == QChart::ChartTypeCartesian)
- static_cast<QGraphicsRectItem *>(m_presenter->plotAreaElement())->setRect(contentGeometry);
- else
- static_cast<QGraphicsEllipseItem *>(m_presenter->plotAreaElement())->setRect(contentGeometry);
+ if (contentGeometry.isValid()) {
+ m_presenter->setGeometry(contentGeometry);
+ if (updateLayout) {
+ if (m_presenter->chart()->chartType() == QChart::ChartTypeCartesian)
+ static_cast<QGraphicsRectItem *>(m_presenter->plotAreaElement())->setRect(contentGeometry);
+ else
+ static_cast<QGraphicsEllipseItem *>(m_presenter->plotAreaElement())->setRect(contentGeometry);
+ }
}
}
diff --git a/src/charts/legend/legendmarkeritem.cpp b/src/charts/legend/legendmarkeritem.cpp
index 2986b687..7121783d 100644
--- a/src/charts/legend/legendmarkeritem.cpp
+++ b/src/charts/legend/legendmarkeritem.cpp
@@ -126,7 +126,7 @@ QFont LegendMarkerItem::font() const
void LegendMarkerItem::setLabel(const QString label)
{
m_label = label;
- updateGeometry();
+ m_marker->invalidateLegend();
}
QString LegendMarkerItem::label() const
diff --git a/src/charts/legend/qlegend.cpp b/src/charts/legend/qlegend.cpp
index 6f5c2bb1..1a4f0ea2 100644
--- a/src/charts/legend/qlegend.cpp
+++ b/src/charts/legend/qlegend.cpp
@@ -772,7 +772,7 @@ void QLegendPrivate::handleCountChanged()
}
// Re-insert createdMarkers into m_markers in correct order.
- if (pos != -1 || pos == m_markers.size()) {
+ if (pos == -1 || pos == m_markers.size()) {
m_markers.append(createdMarkers);
} else {
for (int c = createdMarkers.size() - 1; c >= 0; --c)
diff --git a/src/charts/piechart/qpieslice.cpp b/src/charts/piechart/qpieslice.cpp
index f36f93a3..8ed844b6 100644
--- a/src/charts/piechart/qpieslice.cpp
+++ b/src/charts/piechart/qpieslice.cpp
@@ -108,11 +108,13 @@ QT_CHARTS_BEGIN_NAMESPACE
/*!
\property QPieSlice::label
\brief The label of the slice.
+ \note The string can be HTML formatted.
\sa labelVisible, labelBrush, labelFont, labelArmLengthFactor
*/
/*!
\qmlproperty string PieSlice::label
The label of the slice.
+ \note The string can be HTML formatted.
*/
/*!
diff --git a/src/charts/xychart/qhxymodelmapper.cpp b/src/charts/xychart/qhxymodelmapper.cpp
index f11201c2..b6f907b7 100644
--- a/src/charts/xychart/qhxymodelmapper.cpp
+++ b/src/charts/xychart/qhxymodelmapper.cpp
@@ -40,12 +40,14 @@ QT_CHARTS_BEGIN_NAMESPACE
Model mappers enable using a data model derived from the QAbstractItemModel
class as a data source for a chart. A horizontal model mapper is used to
create a connection between a line, spline, or scatter series and the data
- model that holds the consecutive data point coordinates on rows.
+ model that has \e X and \e Y rows for the coordinates and holds the data
+ points for the XYSeries as columns. A \e TableModel is a natural choice
+ for the model.
Both model and series properties can be used to manipulate the data. The
model mapper keeps the series and the data model in sync.
- \sa QVXYModelMapper, QXYSeries
+ \sa QVXYModelMapper, QXYSeries, {Model Data Example}
*/
/*!
\qmltype HXYModelMapper
@@ -57,12 +59,14 @@ QT_CHARTS_BEGIN_NAMESPACE
Model mappers enable using a data model derived from the QAbstractItemModel
class as a data source for a chart. A horizontal model mapper is used to
create a connection between a line, spline, or scatter series and the data
- model that holds the consecutive data point coordinates on rows.
+ model that has \e X and \e Y rows for the coordinates and holds the data
+ points for the XYSeries as columns. A \e TableModel is a natural choice
+ for the model.
Both model and series properties can be used to manipulate the data. The
model mapper keeps the series and the data model in sync.
- \sa VXYModelMapper, XYSeries
+ \sa VXYModelMapper, XYSeries, {Model Data Example}
*/
/*!
diff --git a/src/charts/xychart/qvxymodelmapper.cpp b/src/charts/xychart/qvxymodelmapper.cpp
index 9b5655b7..eaad1796 100644
--- a/src/charts/xychart/qvxymodelmapper.cpp
+++ b/src/charts/xychart/qvxymodelmapper.cpp
@@ -40,12 +40,14 @@ QT_CHARTS_BEGIN_NAMESPACE
Model mappers enable using a data model derived from the QAbstractItemModel
class as a data source for a chart. A vertical model mapper is used to
create a connection between a line, spline, or scatter series and the data
- model that holds the consecutive data point coordinates in columns.
+ model that has \e X and \e Y columns for the coordinates and holds the data
+ points for the XYSeries as rows. A \e TableModel is a natural choice
+ for the model.
Both model and series properties can be used to manipulate the data. The
model mapper keeps the series and the data model in sync.
- \sa QHXYModelMapper, QXYSeries
+ \sa QHXYModelMapper, QXYSeries, {Model Data Example}
*/
/*!
\qmltype VXYModelMapper
@@ -57,12 +59,14 @@ QT_CHARTS_BEGIN_NAMESPACE
Model mappers enable using a data model derived from the QAbstractItemModel
class as a data source for a chart. A vertical model mapper is used to
create a connection between a line, spline, or scatter series and the data
- model that holds the consecutive data point coordinates in columns.
+ model that has \e X and \e Y columns for the coordinates and holds the data
+ points for the XYSeries as rows. A \e TableModel is a natural choice
+ for the model.
Both model and series properties can be used to manipulate the data. The
model mapper keeps the series and the data model in sync.
- \sa HXYModelMapper, XYSeries
+ \sa HXYModelMapper, XYSeries, {Model Data Example}
*/
/*!
diff --git a/src/charts/xychart/qxymodelmapper.cpp b/src/charts/xychart/qxymodelmapper.cpp
index d1f2e9f5..553b67a6 100644
--- a/src/charts/xychart/qxymodelmapper.cpp
+++ b/src/charts/xychart/qxymodelmapper.cpp
@@ -367,31 +367,32 @@ void QXYModelMapperPrivate::modelUpdated(QModelIndex topLeft, QModelIndex bottom
blockSeriesSignals();
QModelIndex index;
- QPointF oldPoint;
QPointF newPoint;
+ int indexColumn = 0;
+ int indexRow = 0;
for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
index = topLeft.sibling(row, column);
- if (m_orientation == Qt::Vertical && (index.column() == m_xSection || index.column() == m_ySection)) {
- if (index.row() >= m_first && (m_count == - 1 || index.row() < m_first + m_count)) {
- QModelIndex xIndex = xModelIndex(index.row() - m_first);
- QModelIndex yIndex = yModelIndex(index.row() - m_first);
+ indexColumn = index.column();
+ indexRow = index.row();
+ if (m_orientation == Qt::Vertical && (indexColumn == m_xSection || indexColumn == m_ySection)) {
+ if (indexRow >= m_first && (m_count == - 1 || indexRow < m_first + m_count)) {
+ QModelIndex xIndex = xModelIndex(indexRow - m_first);
+ QModelIndex yIndex = yModelIndex(indexRow - m_first);
if (xIndex.isValid() && yIndex.isValid()) {
- oldPoint = m_series->points().at(index.row() - m_first);
newPoint.setX(valueFromModel(xIndex));
newPoint.setY(valueFromModel(yIndex));
- m_series->replace(index.row() - m_first, newPoint);
+ m_series->replace(indexRow - m_first, newPoint);
}
}
- } else if (m_orientation == Qt::Horizontal && (index.row() == m_xSection || index.row() == m_ySection)) {
- if (index.column() >= m_first && (m_count == - 1 || index.column() < m_first + m_count)) {
- QModelIndex xIndex = xModelIndex(index.column() - m_first);
- QModelIndex yIndex = yModelIndex(index.column() - m_first);
+ } else if (m_orientation == Qt::Horizontal && (indexRow == m_xSection || indexRow == m_ySection)) {
+ if (indexColumn >= m_first && (m_count == - 1 || indexColumn < m_first + m_count)) {
+ QModelIndex xIndex = xModelIndex(indexColumn - m_first);
+ QModelIndex yIndex = yModelIndex(indexColumn - m_first);
if (xIndex.isValid() && yIndex.isValid()) {
- oldPoint = m_series->points().at(index.column() - m_first);
newPoint.setX(valueFromModel(xIndex));
newPoint.setY(valueFromModel(yIndex));
- m_series->replace(index.column() - m_first, newPoint);
+ m_series->replace(indexColumn - m_first, newPoint);
}
}
}
diff --git a/src/chartsqml2/declarativechart.cpp b/src/chartsqml2/declarativechart.cpp
index a58e67c7..84fc9871 100644
--- a/src/chartsqml2/declarativechart.cpp
+++ b/src/chartsqml2/declarativechart.cpp
@@ -61,6 +61,7 @@
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <QtQuick/QQuickWindow>
+#include <QtWidgets/QGraphicsLayout>
QT_CHARTS_BEGIN_NAMESPACE
@@ -632,9 +633,12 @@ void DeclarativeChart::seriesAxisAttachHelper(QAbstractSeries *series, QAbstract
{
if (!series->attachedAxes().contains(axis)) {
// Remove & delete old axes that are not attached to any other series
+ // Detach old axis from series so that if it is shared with other series
+ // It can be deleted.
foreach (QAbstractAxis* oldAxis, m_chart->axes(orientation, series)) {
bool otherAttachments = false;
if (oldAxis != axis) {
+ series->detachAxis(oldAxis);
foreach (QAbstractSeries *oldSeries, m_chart->series()) {
if (oldSeries != series && oldSeries->attachedAxes().contains(oldAxis)) {
otherAttachments = true;
@@ -1496,6 +1500,10 @@ QPointF DeclarativeChart::mapToPosition(const QPointF &value, QAbstractSeries *s
void DeclarativeChart::setPlotArea(const QRectF &rect)
{
m_chart->setPlotArea(rect);
+
+ // If plotArea is set inside ChartView, contentGeometry is updated too early and we end up
+ // with incorrect plotArea. Invalidate the layout to correct the geometry.
+ m_chart->layout()->invalidate();
}
QT_CHARTS_END_NAMESPACE
diff --git a/src/chartsqml2/declarativeopenglrendernode.cpp b/src/chartsqml2/declarativeopenglrendernode.cpp
index 58f7aef6..ff15034c 100644
--- a/src/chartsqml2/declarativeopenglrendernode.cpp
+++ b/src/chartsqml2/declarativeopenglrendernode.cpp
@@ -229,7 +229,8 @@ void DeclarativeOpenGLRenderNode::setSeriesData(bool mapDirty, const GLXYDataMap
GLXYSeriesData *data = oldMap.take(i.key());
const GLXYSeriesData *newData = i.value();
if (!data || newData->dirty) {
- data = new GLXYSeriesData;
+ if (!data)
+ data = new GLXYSeriesData;
*data = *newData;
}
m_xyDataMap.insert(i.key(), data);
diff --git a/tests/auto/qbarseries/BLACKLIST b/tests/auto/qbarseries/BLACKLIST
index a1b10c85..c8b10b7d 100644
--- a/tests/auto/qbarseries/BLACKLIST
+++ b/tests/auto/qbarseries/BLACKLIST
@@ -1,2 +1,4 @@
[mousehovered]
macos
+# QTBUG-111293
+sles-15