summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Varanka <sami.varanka@qt.io>2024-05-06 10:06:53 +0300
committerSami Varanka <sami.varanka@qt.io>2024-05-10 08:59:44 +0300
commit9aa8d47557f4e89b95622f3f1b03410458140b3d (patch)
treec3bae96d4cdd971985e55d6f828f7d1187f98b34
parentbe53d4ba6f4437ad9552da268f755f5e51f60ba4 (diff)
Add PieModelMapper
Task-number: QTBUG-124545 Change-Id: Ia615a85725af1f2ace8c87d2d28bac7b6f8de4e4 Reviewed-by: Kaj Grönholm <kaj.gronholm@qt.io>
-rw-r--r--src/graphs2d/CMakeLists.txt1
-rw-r--r--src/graphs2d/piechart/qpiemodelmapper.cpp628
-rw-r--r--src/graphs2d/piechart/qpiemodelmapper.h72
-rw-r--r--src/graphs2d/piechart/qpiemodelmapper_p.h74
-rw-r--r--tests/auto/cpp2dtest/CMakeLists.txt1
-rw-r--r--tests/auto/cpp2dtest/qgpiemodelmapper/CMakeLists.txt10
-rw-r--r--tests/auto/cpp2dtest/qgpiemodelmapper/tst_piemodelmapper.cpp565
7 files changed, 1351 insertions, 0 deletions
diff --git a/src/graphs2d/CMakeLists.txt b/src/graphs2d/CMakeLists.txt
index 3005db6..318cd74 100644
--- a/src/graphs2d/CMakeLists.txt
+++ b/src/graphs2d/CMakeLists.txt
@@ -12,6 +12,7 @@ qt_internal_extend_target(Graphs
linechart/qlineseries.cpp linechart/qlineseries.h linechart/qlineseries_p.h
piechart/qpieseries.cpp piechart/qpieseries.h piechart/qpieseries_p.h
piechart/qpieslice.cpp piechart/qpieslice.h piechart/qpieslice_p.h
+ piechart/qpiemodelmapper.cpp piechart/qpiemodelmapper.h piechart/qpiemodelmapper_p.h
scatterchart/qscatterseries.cpp scatterchart/qscatterseries.h scatterchart/qscatterseries_p.h
xychart/qxyseries.cpp xychart/qxyseries.h xychart/qxyseries_p.h
xychart/qxypoint.cpp xychart/qxypoint_p.h
diff --git a/src/graphs2d/piechart/qpiemodelmapper.cpp b/src/graphs2d/piechart/qpiemodelmapper.cpp
new file mode 100644
index 0000000..4280522
--- /dev/null
+++ b/src/graphs2d/piechart/qpiemodelmapper.cpp
@@ -0,0 +1,628 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QtCore/qabstractitemmodel.h>
+#include <QtGraphs/qpiemodelmapper.h>
+#include <QtGraphs/qpieseries.h>
+#include <private/qpiemodelmapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QPieModelMapper
+ \inmodule QtGraphs
+ \ingroup graphs_2D
+ \brief The QPieModelMapper is a model mapper for pie series.
+
+ 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 data model and QPieSeries, so that each row in the
+ data model defines a pie slice and each column maps to the label or the value
+ of the pie slice.
+
+ Both model and pie series properties can be used to manipulate the data. The
+ model mapper keeps the pie series and the data model in sync.
+*/
+/*!
+ \qmltype PieModelMapper
+ \instantiates QPieModelMapper
+ \inqmlmodule QtGraphs
+ \ingroup graphs_qml_2D
+
+ \brief Model mapper for pie series.
+
+ 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 data model and PieSeries, so that each row in the data
+ model defines a pie slice and each column maps to the label or the value of
+ the pie slice.
+
+ Both model and pie series properties can be used to manipulate the data. The
+ model mapper keeps the pie series and the data model in sync.
+
+ The following QML example creates a pie series with four slices (assuming
+ the model has at least five rows). Each slice gets a label from column 1 and
+ a value from column 2.
+ \code
+ PieModelMapper {
+ series: pieSeries
+ model: customModel
+ labelsSection: 1
+ valuesSection: 2
+ firstRow: 1
+ rowCount: 4
+ }
+ \endcode
+*/
+
+QPieModelMapper::~QPieModelMapper() {}
+
+QPieModelMapper::QPieModelMapper(QObject *parent)
+ : QObject{*(new QPieModelMapperPrivate), parent} {}
+
+QPieModelMapper::QPieModelMapper(QPieModelMapperPrivate &dd, QObject *parent)
+ : QObject(dd, parent) {}
+
+void QPieModelMapper::onSliceLabelChanged() {
+ Q_D(QPieModelMapper);
+ d->onSliceLabelChanged(qobject_cast<QPieSlice *>(sender()));
+}
+
+void QPieModelMapper::onSliceValueChanged() {
+ Q_D(QPieModelMapper);
+ d->onSliceValueChanged(qobject_cast<QPieSlice *>(sender()));
+}
+
+QAbstractItemModel *QPieModelMapper::model() const {
+ const Q_D(QPieModelMapper);
+ return d->m_model;
+}
+
+void QPieModelMapper::setModel(QAbstractItemModel *model) {
+ if (model == nullptr)
+ return;
+ Q_D(QPieModelMapper);
+ if (d->m_model) {
+ QObjectPrivate::disconnect(d->m_model,
+ &QAbstractItemModel::modelReset,
+ d,
+ &QPieModelMapperPrivate::initializePieFromModel);
+ QObjectPrivate::disconnect(d->m_model,
+ &QAbstractItemModel::dataChanged,
+ d,
+ &QPieModelMapperPrivate::onModelUpdated);
+ QObjectPrivate::disconnect(d->m_model,
+ &QAbstractItemModel::rowsInserted,
+ d,
+ &QPieModelMapperPrivate::onModelRowsAdded);
+ QObjectPrivate::disconnect(d->m_model,
+ &QAbstractItemModel::rowsRemoved,
+ d,
+ &QPieModelMapperPrivate::onModelRowsRemoved);
+ QObjectPrivate::disconnect(d->m_model,
+ &QAbstractItemModel::columnsInserted,
+ d,
+ &QPieModelMapperPrivate::onModelColumnsAdded);
+ QObjectPrivate::disconnect(d->m_model,
+ &QAbstractItemModel::columnsRemoved,
+ d,
+ &QPieModelMapperPrivate::onModelColumnsRemoved);
+ QObjectPrivate::disconnect(d->m_model,
+ &QAbstractItemModel::destroyed,
+ d,
+ &QPieModelMapperPrivate::handleModelDestroyed);
+ }
+ d->m_model = model;
+ d->initializePieFromModel();
+
+ QObjectPrivate::connect(d->m_model,
+ &QAbstractItemModel::modelReset,
+ d,
+ &QPieModelMapperPrivate::initializePieFromModel);
+ QObjectPrivate::connect(d->m_model,
+ &QAbstractItemModel::dataChanged,
+ d,
+ &QPieModelMapperPrivate::onModelUpdated);
+ QObjectPrivate::connect(d->m_model,
+ &QAbstractItemModel::rowsInserted,
+ d,
+ &QPieModelMapperPrivate::onModelRowsAdded);
+ QObjectPrivate::connect(d->m_model,
+ &QAbstractItemModel::rowsRemoved,
+ d,
+ &QPieModelMapperPrivate::onModelRowsRemoved);
+ QObjectPrivate::connect(d->m_model,
+ &QAbstractItemModel::columnsInserted,
+ d,
+ &QPieModelMapperPrivate::onModelColumnsAdded);
+ QObjectPrivate::connect(d->m_model,
+ &QAbstractItemModel::columnsRemoved,
+ d,
+ &QPieModelMapperPrivate::onModelColumnsRemoved);
+ QObjectPrivate::connect(d->m_model,
+ &QAbstractItemModel::destroyed,
+ d,
+ &QPieModelMapperPrivate::handleModelDestroyed);
+ Q_EMIT modelChanged();
+}
+
+QPieSeries *QPieModelMapper::series() const {
+ const Q_D(QPieModelMapper);
+ return d->m_series;
+}
+
+void QPieModelMapper::setSeries(QPieSeries *series) {
+ Q_D(QPieModelMapper);
+ if (d->m_series) {
+ QObjectPrivate::disconnect(d->m_series,
+ &QPieSeries::added,
+ d,
+ &QPieModelMapperPrivate::onSlicesAdded);
+ QObjectPrivate::disconnect(d->m_series,
+ &QPieSeries::removed,
+ d,
+ &QPieModelMapperPrivate::onSlicesRemoved);
+ QObjectPrivate::disconnect(d->m_series,
+ &QPieSeries::destroyed,
+ d,
+ &QPieModelMapperPrivate::handleSeriesDestroyed);
+ }
+
+ if (series == 0)
+ return;
+
+ d->m_series = series;
+ d->initializePieFromModel();
+ // connect the signals from the series
+ QObjectPrivate::connect(d->m_series,
+ &QPieSeries::added,
+ d,
+ &QPieModelMapperPrivate::onSlicesAdded);
+ QObjectPrivate::connect(d->m_series,
+ &QPieSeries::removed,
+ d,
+ &QPieModelMapperPrivate::onSlicesRemoved);
+ QObjectPrivate::connect(d->m_series,
+ &QPieSeries::destroyed,
+ d,
+ &QPieModelMapperPrivate::handleSeriesDestroyed);
+ Q_EMIT seriesChanged();
+}
+
+int QPieModelMapper::first() const {
+ Q_D(const QPieModelMapper);
+ return d->m_first;
+}
+
+void QPieModelMapper::setFirst(int first) {
+ Q_D(QPieModelMapper);
+ d->m_first = qMax(first, 0);
+ d->initializePieFromModel();
+ Q_EMIT firstChanged();
+}
+
+int QPieModelMapper::count() const {
+ Q_D(const QPieModelMapper);
+ return d->m_count;
+}
+
+void QPieModelMapper::setCount(int count) {
+ Q_D(QPieModelMapper);
+ d->m_count = qMax(count, -1);
+ d->initializePieFromModel();
+ Q_EMIT countChanged();
+}
+
+Qt::Orientation QPieModelMapper::orientation() const {
+ Q_D(const QPieModelMapper);
+ return d->m_orientation;
+}
+
+void QPieModelMapper::setOrientation(Qt::Orientation orientation) {
+ Q_D(QPieModelMapper);
+ d->m_orientation = orientation;
+ d->initializePieFromModel();
+ Q_EMIT orientationChanged();
+}
+
+int QPieModelMapper::valuesSection() const {
+ Q_D(const QPieModelMapper);
+ return d->m_valuesSection;
+}
+
+void QPieModelMapper::setValuesSection(int valuesSection) {
+ Q_D(QPieModelMapper);
+ d->m_valuesSection = qMax(-1, valuesSection);
+ d->initializePieFromModel();
+ Q_EMIT valuesSectionChanged();
+}
+
+int QPieModelMapper::labelsSection() const {
+ Q_D(const QPieModelMapper);
+ return d->m_labelsSection;
+}
+
+void QPieModelMapper::setLabelsSection(int labelsSection) {
+ Q_D(QPieModelMapper);
+ d->m_labelsSection = qMax(-1, labelsSection);
+ d->initializePieFromModel();
+ Q_EMIT labelsSectionChanged();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+QPieModelMapperPrivate::QPieModelMapperPrivate() {}
+
+QPieModelMapperPrivate::~QPieModelMapperPrivate() {}
+
+void QPieModelMapperPrivate::blockModelSignals(bool block) {
+ m_modelSignalsBlock = block;
+}
+
+void QPieModelMapperPrivate::blockSeriesSignals(bool block) {
+ m_seriesSignalsBlock = block;
+}
+
+QPieSlice *QPieModelMapperPrivate::pieSlice(QModelIndex index) const {
+ if (!index.isValid())
+ return 0; // index is invalid
+
+ if (m_orientation == Qt::Vertical
+ && (index.column() == m_valuesSection || index.column() == m_labelsSection)) {
+ if (index.row() >= m_first && (m_count == -1 || index.row() < m_first + m_count)) {
+ if (m_model->index(index.row(), m_valuesSection).isValid()
+ && m_model->index(index.row(), m_labelsSection).isValid())
+ return m_series->slices().at(index.row() - m_first);
+ else
+ return 0;
+ }
+ } else if (m_orientation == Qt::Horizontal
+ && (index.row() == m_valuesSection || index.row() == m_labelsSection)) {
+ if (index.column() >= m_first && (m_count == -1 || index.column() < m_first + m_count)) {
+ if (m_model->index(m_valuesSection, index.column()).isValid()
+ && m_model->index(m_labelsSection, index.column()).isValid())
+ return m_series->slices().at(index.column() - m_first);
+ else
+ return 0;
+ }
+ }
+ return 0; // This part of model has not been mapped to any slice
+}
+
+QModelIndex QPieModelMapperPrivate::valueModelIndex(int slicePos) {
+ if (m_count != -1 && slicePos >= m_count)
+ return QModelIndex(); // invalid
+
+ if (m_orientation == Qt::Vertical)
+ return m_model->index(slicePos + m_first, m_valuesSection);
+ else
+ return m_model->index(m_valuesSection, slicePos + m_first);
+}
+
+QModelIndex QPieModelMapperPrivate::labelModelIndex(int slicePos) {
+ if (m_count != -1 && slicePos >= m_count)
+ return QModelIndex(); // invalid
+
+ if (m_orientation == Qt::Vertical)
+ return m_model->index(slicePos + m_first, m_labelsSection);
+ else
+ return m_model->index(m_labelsSection, slicePos + m_first);
+}
+
+bool QPieModelMapperPrivate::isLabelIndex(QModelIndex index) const {
+ if (m_orientation == Qt::Vertical && index.column() == m_labelsSection)
+ return true;
+ else if (m_orientation == Qt::Horizontal && index.row() == m_labelsSection)
+ return true;
+
+ return false;
+}
+
+bool QPieModelMapperPrivate::isValueIndex(QModelIndex index) const {
+ if (m_orientation == Qt::Vertical && index.column() == m_valuesSection)
+ return true;
+ else if (m_orientation == Qt::Horizontal && index.row() == m_valuesSection)
+ return true;
+
+ return false;
+}
+
+void QPieModelMapperPrivate::onSlicesAdded(const QList<QPieSlice *> &slices) {
+ if (m_seriesSignalsBlock)
+ return;
+
+ if (slices.size() == 0)
+ return;
+
+ int firstIndex = m_series->slices().indexOf(slices.at(0));
+ if (firstIndex == -1)
+ return;
+
+ if (m_count != -1)
+ m_count += slices.size();
+ Q_Q(QPieModelMapper);
+ for (int i = firstIndex; i < firstIndex + slices.size(); i++) {
+ m_slices.insert(i, slices.at(i - firstIndex));
+ QObject::connect(slices.at(i - firstIndex),
+ &QPieSlice::labelChanged,
+ q,
+ &QPieModelMapper::onSliceLabelChanged);
+ QObject::connect(slices.at(i - firstIndex),
+ &QPieSlice::valueChanged,
+ q,
+ &QPieModelMapper::onSliceValueChanged);
+ }
+
+ blockModelSignals();
+ if (m_orientation == Qt::Vertical)
+ m_model->insertRows(firstIndex + m_first, slices.size());
+ else
+ m_model->insertColumns(firstIndex + m_first, slices.size());
+
+ for (int i = firstIndex; i < firstIndex + slices.size(); i++) {
+ m_model->setData(valueModelIndex(i), slices.at(i - firstIndex)->value());
+ m_model->setData(labelModelIndex(i), slices.at(i - firstIndex)->label());
+ }
+ blockModelSignals(false);
+}
+
+void QPieModelMapperPrivate::onSlicesRemoved(const QList<QPieSlice *> &slices) {
+ if (m_seriesSignalsBlock)
+ return;
+
+ if (slices.size() == 0)
+ return;
+
+ int firstIndex = m_slices.indexOf(slices.at(0));
+ if (firstIndex == -1)
+ return;
+
+ if (m_count != -1)
+ m_count -= slices.size();
+
+ for (int i = firstIndex + slices.size() - 1; i >= firstIndex; i--)
+ m_slices.removeAt(i);
+
+ blockModelSignals();
+ if (m_orientation == Qt::Vertical)
+ m_model->removeRows(firstIndex + m_first, slices.size());
+ else
+ m_model->removeColumns(firstIndex + m_first, slices.size());
+ blockModelSignals(false);
+}
+
+void QPieModelMapperPrivate::onSliceLabelChanged(const QPieSlice *slice) {
+ if (m_seriesSignalsBlock)
+ return;
+
+ blockModelSignals();
+ m_model->setData(labelModelIndex(m_series->slices().indexOf(slice)), slice->label());
+ blockModelSignals(false);
+}
+
+void QPieModelMapperPrivate::onSliceValueChanged(const QPieSlice *slice) {
+ if (m_seriesSignalsBlock)
+ return;
+
+ blockModelSignals();
+ m_model->setData(valueModelIndex(m_series->slices().indexOf(slice)), slice->value());
+ blockModelSignals(false);
+}
+
+void QPieModelMapperPrivate::handleSeriesDestroyed() { m_series = nullptr; }
+
+void QPieModelMapperPrivate::onModelUpdated(QModelIndex topLeft, QModelIndex bottomRight)
+{
+ if (m_model == nullptr || m_series == nullptr)
+ return;
+
+ if (m_modelSignalsBlock)
+ return;
+
+ blockSeriesSignals();
+ QModelIndex index;
+ QPieSlice *slice;
+ for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
+ for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
+ index = topLeft.sibling(row, column);
+ slice = pieSlice(index);
+ if (slice) {
+ if (isValueIndex(index))
+ slice->setValue(m_model->data(index, Qt::DisplayRole).toReal());
+ if (isLabelIndex(index))
+ slice->setLabel(m_model->data(index, Qt::DisplayRole).toString());
+ }
+ }
+ }
+ blockSeriesSignals(false);
+}
+
+void QPieModelMapperPrivate::onModelRowsAdded(QModelIndex parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ if (m_modelSignalsBlock)
+ return;
+
+ blockSeriesSignals();
+ if (m_orientation == Qt::Vertical) {
+ insertData(start, end);
+ } else if (start <= m_valuesSection
+ || start <= m_labelsSection) { // if the changes affect the map - reinitialize the pie
+ initializePieFromModel();
+ }
+ blockSeriesSignals(false);
+}
+
+void QPieModelMapperPrivate::onModelRowsRemoved(QModelIndex parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ if (m_modelSignalsBlock)
+ return;
+
+ blockSeriesSignals();
+ if (m_orientation == Qt::Vertical) {
+ removeData(start, end);
+ } else if (start <= m_valuesSection || start <= m_labelsSection) {
+ // if the changes affect the map - reinitialize the pie
+ initializePieFromModel();
+ }
+ blockSeriesSignals(false);
+}
+
+void QPieModelMapperPrivate::onModelColumnsAdded(QModelIndex parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ if (m_modelSignalsBlock)
+ return;
+
+ blockSeriesSignals();
+ if (m_orientation == Qt::Horizontal) {
+ insertData(start, end);
+ } else if (start <= m_valuesSection || start <= m_labelsSection) {
+ // if the changes affect the map - reinitialize the pie
+ initializePieFromModel();
+ }
+ blockSeriesSignals(false);
+}
+
+void QPieModelMapperPrivate::onModelColumnsRemoved(QModelIndex parent, int start, int end)
+{
+ Q_UNUSED(parent);
+ if (m_modelSignalsBlock)
+ return;
+
+ blockSeriesSignals();
+ if (m_orientation == Qt::Horizontal) {
+ removeData(start, end);
+ } else if (start <= m_valuesSection || start <= m_labelsSection) {
+ // if the changes affect the map - reinitialize the pie
+ initializePieFromModel();
+ }
+ blockSeriesSignals(false);
+}
+
+void QPieModelMapperPrivate::handleModelDestroyed() { m_model = nullptr; }
+
+void QPieModelMapperPrivate::insertData(int start, int end) {
+ if (m_model == nullptr || m_series == nullptr)
+ return;
+
+ if (m_count != -1 && start >= m_first + m_count) {
+ return;
+ } else {
+ int addedCount = end - start + 1;
+ if (m_count != -1 && addedCount > m_count)
+ addedCount = m_count;
+ int first = qMax(start, m_first);
+ int last = qMin(first + addedCount - 1,
+ m_orientation == Qt::Vertical ? m_model->rowCount() - 1
+ : m_model->columnCount() - 1);
+ Q_Q(QPieModelMapper);
+
+ for (int i = first; i <= last; i++) {
+ QModelIndex valueIndex = valueModelIndex(i - m_first);
+ QModelIndex labelIndex = labelModelIndex(i - m_first);
+ if (valueIndex.isValid() && labelIndex.isValid()) {
+ QPieSlice *slice = new QPieSlice;
+ slice->setValue(m_model->data(valueIndex, Qt::DisplayRole).toDouble());
+ slice->setLabel(m_model->data(labelIndex, Qt::DisplayRole).toString());
+ QObject::connect(slice,
+ &QPieSlice::labelChanged,
+ q,
+ &QPieModelMapper::onSliceLabelChanged);
+ QObject::connect(slice,
+ &QPieSlice::valueChanged,
+ q,
+ &QPieModelMapper::onSliceValueChanged);
+ m_series->insert(i - m_first, slice);
+ m_slices.insert(i - m_first, slice);
+ }
+ }
+
+ // remove excess of slices (abouve m_count)
+ if (m_count != -1 && m_series->slices().size() > m_count) {
+ for (int i = m_series->slices().size() - 1; i >= m_count; i--) {
+ m_series->remove(m_series->slices().at(i));
+ m_slices.removeAt(i);
+ }
+ }
+ }
+}
+
+void QPieModelMapperPrivate::removeData(int start, int end) {
+ if (m_model == 0 || m_series == 0)
+ return;
+
+ int removedCount = end - start + 1;
+ if (m_count != -1 && start >= m_first + m_count) {
+ return;
+ } else {
+ int toRemove = qMin(m_series->slices().size(),
+ removedCount); // first find how many items can actually be removed
+ int first = qMax(start,
+ m_first); // get the index of the first item that will be removed.
+ int last = qMin(first + toRemove - 1,
+ m_series->slices().size() + m_first
+ - 1); // get the index of the last item that will be removed.
+ for (int i = last; i >= first; i--) {
+ m_series->remove(m_series->slices().at(i - m_first));
+ m_slices.removeAt(i - m_first);
+ }
+
+ if (m_count != -1) {
+ int itemsAvailable; // check how many are available to be added
+ if (m_orientation == Qt::Vertical)
+ itemsAvailable = m_model->rowCount() - m_first - m_series->slices().size();
+ else
+ itemsAvailable = m_model->columnCount() - m_first - m_series->slices().size();
+ int toBeAdded = qMin(itemsAvailable,
+ m_count
+ - m_series->slices().size()); // add not more items than there
+ // is space left to be filled.
+ int currentSize = m_series->slices().size();
+ if (toBeAdded > 0) {
+ for (int i = m_series->slices().size(); i < currentSize + toBeAdded; i++) {
+ QModelIndex valueIndex = valueModelIndex(i - m_first);
+ QModelIndex labelIndex = labelModelIndex(i - m_first);
+ if (valueIndex.isValid() && labelIndex.isValid()) {
+ QPieSlice *slice = new QPieSlice;
+ slice->setValue(m_model->data(valueIndex, Qt::DisplayRole).toDouble());
+ slice->setLabel(m_model->data(labelIndex, Qt::DisplayRole).toString());
+ m_series->insert(i, slice);
+ m_slices.insert(i, slice);
+ }
+ }
+ }
+ }
+ }
+}
+
+void QPieModelMapperPrivate::initializePieFromModel() {
+ if (m_model == nullptr || m_series == nullptr)
+ return;
+
+ blockSeriesSignals();
+ // clear current content
+ m_series->clear();
+ m_slices.clear();
+
+ // create the initial slices set
+ int slicePos = 0;
+ QModelIndex valueIndex = valueModelIndex(slicePos);
+ QModelIndex labelIndex = labelModelIndex(slicePos);
+ Q_Q(QPieModelMapper);
+ while (valueIndex.isValid() && labelIndex.isValid()) {
+ QPieSlice *slice = new QPieSlice;
+ slice->setLabel(m_model->data(labelIndex, Qt::DisplayRole).toString());
+ slice->setValue(m_model->data(valueIndex, Qt::DisplayRole).toDouble());
+ QObject::connect(slice, &QPieSlice::labelChanged, q, &QPieModelMapper::onSliceLabelChanged);
+ QObject::connect(slice, &QPieSlice::valueChanged, q, &QPieModelMapper::onSliceValueChanged);
+ m_series->append(slice);
+ m_slices.append(slice);
+ slicePos++;
+ valueIndex = valueModelIndex(slicePos);
+ labelIndex = labelModelIndex(slicePos);
+ }
+ blockSeriesSignals(false);
+}
+
+QT_END_NAMESPACE
diff --git a/src/graphs2d/piechart/qpiemodelmapper.h b/src/graphs2d/piechart/qpiemodelmapper.h
new file mode 100644
index 0000000..f66b7aa
--- /dev/null
+++ b/src/graphs2d/piechart/qpiemodelmapper.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#ifndef QPIEMODELMAPPER_H
+#define QPIEMODELMAPPER_H
+
+#include <QtCore/qobject.h>
+#include <QtGraphs/qgraphsglobal.h>
+#include <QtQmlIntegration/qqmlintegration.h>
+Q_MOC_INCLUDE(<QtGraphs / qpieseries.h>)
+Q_MOC_INCLUDE(<QtCore / qabstractitemmodel.h>)
+QT_BEGIN_NAMESPACE
+class QAbstractItemModel;
+class QPieModelMapperPrivate;
+class QPieSeries;
+class Q_GRAPHS_EXPORT QPieModelMapper : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QPieSeries *series READ series WRITE setSeries NOTIFY seriesChanged)
+ Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(
+ int valuesSection READ valuesSection WRITE setValuesSection NOTIFY valuesSectionChanged)
+ Q_PROPERTY(
+ int labelsSection READ labelsSection WRITE setLabelsSection NOTIFY labelsSectionChanged)
+ Q_PROPERTY(int first READ first WRITE setFirst NOTIFY first)
+ Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY
+ orientationChanged FINAL)
+ QML_NAMED_ELEMENT(PieModelMapper)
+
+ Q_DECLARE_PRIVATE(QPieModelMapper)
+public:
+ explicit QPieModelMapper(QObject *parent = nullptr);
+ ~QPieModelMapper() override;
+
+ QPieSeries *series() const;
+ void setSeries(QPieSeries *series);
+
+ int first() const;
+ void setFirst(int first);
+
+ int count() const;
+ void setCount(int count);
+
+ int valuesSection() const;
+ void setValuesSection(int valuesSection);
+
+ int labelsSection() const;
+ void setLabelsSection(int labelsSection);
+
+ Qt::Orientation orientation() const;
+ void setOrientation(Qt::Orientation orientation);
+
+ QAbstractItemModel *model() const;
+ void setModel(QAbstractItemModel *model);
+Q_SIGNALS:
+ void seriesChanged();
+ void modelChanged();
+ void valuesSectionChanged();
+ void labelsSectionChanged();
+ void firstChanged();
+ void countChanged();
+ void orientationChanged();
+
+public Q_SLOTS:
+ void onSliceLabelChanged();
+ void onSliceValueChanged();
+
+protected:
+ QPieModelMapper(QPieModelMapperPrivate &dd, QObject *parent = nullptr);
+};
+QT_END_NAMESPACE
+#endif // QPIEMODELMAPPER_H
diff --git a/src/graphs2d/piechart/qpiemodelmapper_p.h b/src/graphs2d/piechart/qpiemodelmapper_p.h
new file mode 100644
index 0000000..c9a5603
--- /dev/null
+++ b/src/graphs2d/piechart/qpiemodelmapper_p.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtGraphs API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+#ifndef QPIEMODELMAPPER_P_H
+#define QPIEMODELMAPPER_P_H
+#include <QtGraphs/qpiemodelmapper.h>
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+class QPieSlice;
+class QPieSeries;
+class QAbstractItemModel;
+
+class QPieModelMapperPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QPieModelMapper)
+
+public:
+ explicit QPieModelMapperPrivate();
+ ~QPieModelMapperPrivate() override;
+
+ void onSliceLabelChanged(const QPieSlice *slice);
+ void onSliceValueChanged(const QPieSlice *slice);
+public Q_SLOTS:
+ // for the model
+ void onModelUpdated(QModelIndex topLeft, QModelIndex bottomRight);
+ void onModelRowsAdded(QModelIndex parent, int start, int end);
+ void onModelRowsRemoved(QModelIndex parent, int start, int end);
+ void onModelColumnsAdded(QModelIndex parent, int start, int end);
+ void onModelColumnsRemoved(QModelIndex parent, int start, int end);
+ void handleModelDestroyed();
+
+ // for the series
+ void onSlicesAdded(const QList<QPieSlice *> &slices);
+ void onSlicesRemoved(const QList<QPieSlice *> &slices);
+ void handleSeriesDestroyed();
+
+ void initializePieFromModel();
+
+private:
+ QPieSlice *pieSlice(QModelIndex index) const;
+ bool isLabelIndex(QModelIndex index) const;
+ bool isValueIndex(QModelIndex index) const;
+ QModelIndex valueModelIndex(int slicePos);
+ QModelIndex labelModelIndex(int slicePos);
+ void insertData(int start, int end);
+ void removeData(int start, int end);
+
+ void blockModelSignals(bool block = true);
+ void blockSeriesSignals(bool block = true);
+
+private:
+ QPieSeries *m_series = nullptr;
+ QList<QPieSlice *> m_slices;
+ QAbstractItemModel *m_model = nullptr;
+ int m_first = 0;
+ int m_count = -1;
+ Qt::Orientation m_orientation = Qt::Vertical;
+ int m_valuesSection = -1;
+ int m_labelsSection = -1;
+ bool m_seriesSignalsBlock = false;
+ bool m_modelSignalsBlock = false;
+};
+
+QT_END_NAMESPACE
+#endif // QPIEMODELMAPPER_P_H
diff --git a/tests/auto/cpp2dtest/CMakeLists.txt b/tests/auto/cpp2dtest/CMakeLists.txt
index 4237c4e..7b74dd1 100644
--- a/tests/auto/cpp2dtest/CMakeLists.txt
+++ b/tests/auto/cpp2dtest/CMakeLists.txt
@@ -15,3 +15,4 @@ add_subdirectory(qgxychart)
add_subdirectory(qgsplines)
add_subdirectory(qgarea)
add_subdirectory(qgbarmodelmapper)
+add_subdirectory(qgpiemodelmapper)
diff --git a/tests/auto/cpp2dtest/qgpiemodelmapper/CMakeLists.txt b/tests/auto/cpp2dtest/qgpiemodelmapper/CMakeLists.txt
new file mode 100644
index 0000000..1797808
--- /dev/null
+++ b/tests/auto/cpp2dtest/qgpiemodelmapper/CMakeLists.txt
@@ -0,0 +1,10 @@
+qt_internal_add_test(tst_qgpiemodelmapper
+ SOURCES
+ tst_piemodelmapper.cpp
+ INCLUDE_DIRECTORIES
+ ../common
+ LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::Graphs
+)
diff --git a/tests/auto/cpp2dtest/qgpiemodelmapper/tst_piemodelmapper.cpp b/tests/auto/cpp2dtest/qgpiemodelmapper/tst_piemodelmapper.cpp
new file mode 100644
index 0000000..0e5110d
--- /dev/null
+++ b/tests/auto/cpp2dtest/qgpiemodelmapper/tst_piemodelmapper.cpp
@@ -0,0 +1,565 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QStandardItemModel>
+#include <QtGraphs/QPieModelMapper>
+#include <QtGraphs/QPieSeries>
+#include <QtGraphs/QPieSlice>
+#include <QtTest/QtTest>
+
+class tst_piemodelmapper : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_piemodelmapper();
+ void createVerticalMapper();
+ void createHorizontalMapper();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+ void verticalMapper_data();
+ void verticalMapper();
+ void verticalMapperCustomMapping_data();
+ void verticalMapperCustomMapping();
+ void horizontalMapper_data();
+ void horizontalMapper();
+ void horizontalMapperCustomMapping_data();
+ void horizontalMapperCustomMapping();
+ void seriesUpdated();
+ void verticalModelInsertRows();
+ void verticalModelRemoveRows();
+ void verticalModelInsertColumns();
+ void verticalModelRemoveColumns();
+ void horizontalModelInsertRows();
+ void horizontalModelRemoveRows();
+ void horizontalModelInsertColumns();
+ void horizontalModelRemoveColumns();
+ void modelUpdateCell();
+ void verticalMapperSignals();
+ void horizontalMapperSignals();
+
+private:
+ QStandardItemModel *m_model = nullptr;
+ int m_modelRowCount = 10;
+ int m_modelColumnCount = 8;
+ QPieModelMapper *m_vMapper = nullptr;
+ QPieModelMapper *m_hMapper = nullptr;
+
+ QPieSeries *m_series = nullptr;
+};
+
+tst_piemodelmapper::tst_piemodelmapper() {}
+
+void tst_piemodelmapper::createVerticalMapper()
+{
+ m_vMapper = new QPieModelMapper;
+ QVERIFY(m_vMapper->model() == 0);
+ m_vMapper->setValuesSection(0);
+ m_vMapper->setLabelsSection(1);
+ m_vMapper->setModel(m_model);
+ m_vMapper->setSeries(m_series);
+}
+
+void tst_piemodelmapper::createHorizontalMapper()
+{
+ m_hMapper = new QPieModelMapper;
+ QVERIFY(m_hMapper->model() == 0);
+ m_hMapper->setOrientation(Qt::Horizontal);
+ m_hMapper->setValuesSection(0);
+ m_hMapper->setLabelsSection(1);
+ m_hMapper->setModel(m_model);
+ m_hMapper->setSeries(m_series);
+}
+
+void tst_piemodelmapper::init()
+{
+ m_series = new QPieSeries;
+
+ m_model = new QStandardItemModel(m_modelRowCount, m_modelColumnCount, this);
+ for (int row = 0; row < m_modelRowCount; ++row) {
+ for (int column = 0; column < m_modelColumnCount; column++) {
+ m_model->setData(m_model->index(row, column), row * column);
+ }
+ }
+}
+
+void tst_piemodelmapper::cleanup()
+{
+ m_series->deleteLater();
+ m_series = 0;
+
+ m_model->clear();
+ m_model->deleteLater();
+ m_model = 0;
+
+ if (m_vMapper) {
+ m_vMapper->deleteLater();
+ m_vMapper = 0;
+ }
+
+ if (m_hMapper) {
+ m_hMapper->deleteLater();
+ m_hMapper = 0;
+ }
+}
+
+void tst_piemodelmapper::initTestCase() {}
+
+void tst_piemodelmapper::cleanupTestCase() {}
+
+void tst_piemodelmapper::verticalMapper_data()
+{
+ QTest::addColumn<int>("valuesColumn");
+ QTest::addColumn<int>("labelsColumn");
+ QTest::addColumn<int>("expectedCount");
+ QTest::newRow("different values and labels columns") << 0 << 1 << m_modelRowCount;
+ QTest::newRow("same values and labels columns") << 1 << 1 << m_modelRowCount;
+ QTest::newRow("invalid values column and correct labels column") << -3 << 1 << 0;
+ QTest::newRow("values column beyond the size of model and correct labels column")
+ << m_modelColumnCount << 1 << 0;
+ QTest::newRow("values column beyond the size of model and invalid labels column")
+ << m_modelColumnCount << -1 << 0;
+}
+
+void tst_piemodelmapper::verticalMapper()
+{
+ QFETCH(int, valuesColumn);
+ QFETCH(int, labelsColumn);
+ QFETCH(int, expectedCount);
+
+ auto mapper = new QPieModelMapper;
+ mapper->setValuesSection(valuesColumn);
+ mapper->setLabelsSection(labelsColumn);
+ mapper->setModel(m_model);
+ mapper->setSeries(m_series);
+
+ QCOMPARE(m_series->count(), expectedCount);
+ QCOMPARE(mapper->valuesSection(), qMax(-1, valuesColumn));
+ QCOMPARE(mapper->labelsSection(), qMax(-1, labelsColumn));
+
+ delete mapper;
+ mapper = 0;
+}
+
+void tst_piemodelmapper::verticalMapperCustomMapping_data()
+{
+ QTest::addColumn<int>("first");
+ QTest::addColumn<int>("countLimit");
+ QTest::addColumn<int>("expectedCount");
+ QTest::newRow("first: 0, unlimited count") << 0 << -1 << m_modelRowCount;
+ QTest::newRow("first: 3, unlimited count") << 3 << -1 << m_modelRowCount - 3;
+ QTest::newRow("first: 0, count: 5") << 0 << 5 << qMin(5, m_modelRowCount);
+ QTest::newRow("first: 3, count: 5") << 3 << 5 << qMin(5, m_modelRowCount - 3);
+ QTest::newRow("first: +1 greater then the number of rows in the model, unlimited count")
+ << m_modelRowCount + 1 << -1 << 0;
+ QTest::newRow("first: +1 greater then the number of rows in the model, count: 5")
+ << m_modelRowCount + 1 << 5 << 0;
+ QTest::newRow("first: 0, count: +3 greater than the number of rows in the model (should limit "
+ "to the size of model)")
+ << 0 << m_modelRowCount + 3 << m_modelRowCount;
+ QTest::newRow("first: -3(invalid - should default to 0), unlimited count")
+ << -3 << -1 << m_modelRowCount;
+ QTest::newRow("first: 0, count: -3 (invalid - shlould default to -1)")
+ << 0 << -3 << m_modelRowCount;
+ QTest::newRow(
+ "first: -3(invalid - should default to 0), count: -3 (invalid - shlould default to -1)")
+ << -3 << -3 << m_modelRowCount;
+}
+
+void tst_piemodelmapper::verticalMapperCustomMapping()
+{
+ QFETCH(int, first);
+ QFETCH(int, countLimit);
+ QFETCH(int, expectedCount);
+
+ QCOMPARE(m_series->count(), 0);
+
+ auto mapper = new QPieModelMapper;
+ mapper->setValuesSection(0);
+ mapper->setLabelsSection(1);
+ mapper->setModel(m_model);
+ mapper->setSeries(m_series);
+ mapper->setFirst(first);
+ mapper->setCount(countLimit);
+
+ QCOMPARE(m_series->count(), expectedCount);
+
+ // change values column mapping to invalid
+ mapper->setValuesSection(-1);
+ mapper->setLabelsSection(1);
+
+ QCOMPARE(m_series->count(), 0);
+
+ delete mapper;
+ mapper = 0;
+}
+
+void tst_piemodelmapper::horizontalMapper_data()
+{
+ QTest::addColumn<int>("valuesRow");
+ QTest::addColumn<int>("labelsRow");
+ QTest::addColumn<int>("expectedCount");
+ QTest::newRow("different values and labels rows") << 0 << 1 << m_modelColumnCount;
+ QTest::newRow("same values and labels rows") << 1 << 1 << m_modelColumnCount;
+ QTest::newRow("invalid values row and correct labels row") << -3 << 1 << 0;
+ QTest::newRow("values row beyond the size of model and correct labels row")
+ << m_modelRowCount << 1 << 0;
+ QTest::newRow("values row beyond the size of model and invalid labels row")
+ << m_modelRowCount << -1 << 0;
+}
+
+void tst_piemodelmapper::horizontalMapper()
+{
+ QFETCH(int, valuesRow);
+ QFETCH(int, labelsRow);
+ QFETCH(int, expectedCount);
+
+ auto mapper = new QPieModelMapper;
+ mapper->setOrientation(Qt::Horizontal);
+ mapper->setValuesSection(valuesRow);
+ mapper->setLabelsSection(labelsRow);
+ mapper->setModel(m_model);
+ mapper->setSeries(m_series);
+
+ QCOMPARE(m_series->count(), expectedCount);
+ QCOMPARE(mapper->valuesSection(), qMax(-1, valuesRow));
+ QCOMPARE(mapper->labelsSection(), qMax(-1, labelsRow));
+
+ delete mapper;
+ mapper = 0;
+}
+
+void tst_piemodelmapper::horizontalMapperCustomMapping_data()
+{
+ QTest::addColumn<int>("first");
+ QTest::addColumn<int>("countLimit");
+ QTest::addColumn<int>("expectedCount");
+ QTest::newRow("first: 0, unlimited count") << 0 << -1 << m_modelColumnCount;
+ QTest::newRow("first: 3, unlimited count") << 3 << -1 << m_modelColumnCount - 3;
+ QTest::newRow("first: 0, count: 5") << 0 << 5 << qMin(5, m_modelColumnCount);
+ QTest::newRow("first: 3, count: 5") << 3 << 5 << qMin(5, m_modelColumnCount - 3);
+ QTest::newRow("first: +1 greater then the number of columns in the model, unlimited count")
+ << m_modelColumnCount + 1 << -1 << 0;
+ QTest::newRow("first: +1 greater then the number of columns in the model, count: 5")
+ << m_modelColumnCount + 1 << 5 << 0;
+ QTest::newRow("first: 0, count: +3 greater than the number of columns in the model (should "
+ "limit to the size of model)")
+ << 0 << m_modelColumnCount + 3 << m_modelColumnCount;
+ QTest::newRow("first: -3(invalid - should default to 0), unlimited count")
+ << -3 << -1 << m_modelColumnCount;
+ QTest::newRow("first: 0, count: -3 (invalid - shlould default to -1)")
+ << 0 << -3 << m_modelColumnCount;
+ QTest::newRow(
+ "first: -3(invalid - should default to 0), count: -3 (invalid - shlould default to -1)")
+ << -3 << -3 << m_modelColumnCount;
+}
+
+void tst_piemodelmapper::horizontalMapperCustomMapping()
+{
+ QFETCH(int, first);
+ QFETCH(int, countLimit);
+ QFETCH(int, expectedCount);
+
+ QCOMPARE(m_series->count(), 0);
+
+ auto mapper = new QPieModelMapper;
+ mapper->setOrientation(Qt::Horizontal);
+ mapper->setValuesSection(0);
+ mapper->setLabelsSection(1);
+ mapper->setModel(m_model);
+ mapper->setSeries(m_series);
+ mapper->setFirst(first);
+ mapper->setCount(countLimit);
+
+ QCOMPARE(m_series->count(), expectedCount);
+
+ // change values row mapping to invalid
+ mapper->setValuesSection(-1);
+ mapper->setLabelsSection(1);
+
+ QCOMPARE(m_series->count(), 0);
+
+ delete mapper;
+ mapper = 0;
+}
+
+void tst_piemodelmapper::seriesUpdated()
+{
+ // setup the mapper
+ createVerticalMapper();
+ QCOMPARE(m_series->count(), m_modelRowCount);
+ QCOMPARE(m_vMapper->count(), -1);
+
+ m_series->append("1000", 1000);
+ QCOMPARE(m_series->count(), m_modelRowCount + 1);
+ QCOMPARE(m_vMapper->count(),
+ -1); // the value should not change as it indicates 'all' items there are in the model
+
+ m_series->remove(m_series->slices().last());
+ QCOMPARE(m_series->count(), m_modelRowCount);
+ QCOMPARE(m_vMapper->count(),
+ -1); // the value should not change as it indicates 'all' items there are in the model
+
+ QPieSlice *slice = m_series->slices().first();
+ slice->setValue(25.0);
+ slice->setLabel(QString("25.0"));
+ QCOMPARE(m_model->data(m_model->index(0, 0)).toReal(), 25.0);
+ QCOMPARE(m_model->data(m_model->index(0, 1)).toString(), QString("25.0"));
+}
+
+void tst_piemodelmapper::verticalModelInsertRows()
+{
+ // setup the mapper
+ createVerticalMapper();
+ QCOMPARE(m_series->count(), m_modelRowCount);
+ QVERIFY(m_vMapper->model() != 0);
+
+ int insertCount = 4;
+ m_model->insertRows(3, insertCount);
+ QCOMPARE(m_series->count(), m_modelRowCount + insertCount);
+
+ int first = 3;
+ m_vMapper->setFirst(3);
+ QCOMPARE(m_series->count(), m_modelRowCount + insertCount - first);
+
+ m_model->insertRows(3, insertCount);
+ QCOMPARE(m_series->count(), m_modelRowCount + 2 * insertCount - first);
+
+ int countLimit = 6;
+ m_vMapper->setCount(countLimit);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelRowCount + 2 * insertCount - first));
+
+ m_model->insertRows(3, insertCount);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelRowCount + 3 * insertCount - first));
+
+ m_vMapper->setFirst(0);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelRowCount + 3 * insertCount));
+
+ m_vMapper->setCount(-1);
+ QCOMPARE(m_series->count(), m_modelRowCount + 3 * insertCount);
+}
+
+void tst_piemodelmapper::verticalModelRemoveRows()
+{
+ // setup the mapper
+ createVerticalMapper();
+ QCOMPARE(m_series->count(), m_modelRowCount);
+ QVERIFY(m_vMapper->model() != 0);
+
+ int removeCount = 2;
+ m_model->removeRows(1, removeCount);
+ QCOMPARE(m_series->count(), m_modelRowCount - removeCount);
+
+ int first = 1;
+ m_vMapper->setFirst(first);
+ QCOMPARE(m_series->count(), m_modelRowCount - removeCount - first);
+
+ m_model->removeRows(1, removeCount);
+ QCOMPARE(m_series->count(), m_modelRowCount - 2 * removeCount - first);
+
+ int countLimit = 3;
+ m_vMapper->setCount(countLimit);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelRowCount - 2 * removeCount - first));
+
+ m_model->removeRows(1, removeCount);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelRowCount - 3 * removeCount - first));
+
+ m_vMapper->setFirst(0);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelRowCount - 3 * removeCount));
+
+ m_vMapper->setCount(-1);
+ QCOMPARE(m_series->count(), m_modelRowCount - 3 * removeCount);
+}
+
+void tst_piemodelmapper::verticalModelInsertColumns()
+{
+ // setup the mapper
+ createVerticalMapper();
+ QCOMPARE(m_series->count(), m_modelRowCount);
+ QVERIFY(m_vMapper->model() != 0);
+
+ int insertCount = 4;
+ m_model->insertColumns(3, insertCount);
+ QCOMPARE(m_series->count(), m_modelRowCount);
+}
+
+void tst_piemodelmapper::verticalModelRemoveColumns()
+{
+ // setup the mapper
+ createVerticalMapper();
+ QCOMPARE(m_series->count(), m_modelRowCount);
+ QVERIFY(m_vMapper->model() != 0);
+
+ int removeCount = m_modelColumnCount - 2;
+ m_model->removeColumns(0, removeCount);
+ QCOMPARE(m_series->count(), m_modelRowCount);
+
+ // leave only one column
+ m_model->removeColumns(0, m_modelColumnCount - removeCount - 1);
+ QCOMPARE(m_series->count(), 0);
+}
+
+void tst_piemodelmapper::horizontalModelInsertRows()
+{
+ // setup the mapper
+ createHorizontalMapper();
+ QCOMPARE(m_series->count(), m_modelColumnCount);
+ QVERIFY(m_hMapper->model() != 0);
+
+ int insertCount = 4;
+ m_model->insertRows(3, insertCount);
+ QCOMPARE(m_series->count(), m_modelColumnCount);
+}
+
+void tst_piemodelmapper::horizontalModelRemoveRows()
+{
+ // setup the mapper
+ createHorizontalMapper();
+ QCOMPARE(m_series->count(), m_modelColumnCount);
+ QVERIFY(m_hMapper->model() != 0);
+
+ int removeCount = m_modelRowCount - 2;
+ m_model->removeRows(0, removeCount);
+ QCOMPARE(m_series->count(), m_modelColumnCount);
+
+ // leave only one column
+ m_model->removeRows(0, m_modelRowCount - removeCount - 1);
+ QCOMPARE(m_series->count(), 0);
+}
+
+void tst_piemodelmapper::horizontalModelInsertColumns()
+{
+ // setup the mapper
+ createHorizontalMapper();
+ QCOMPARE(m_series->count(), m_modelColumnCount);
+ QVERIFY(m_hMapper->model() != 0);
+
+ int insertCount = 4;
+ m_model->insertColumns(3, insertCount);
+ QCOMPARE(m_series->count(), m_modelColumnCount + insertCount);
+
+ int first = 3;
+ m_hMapper->setFirst(3);
+ QCOMPARE(m_series->count(), m_modelColumnCount + insertCount - first);
+
+ m_model->insertColumns(3, insertCount);
+ QCOMPARE(m_series->count(), m_modelColumnCount + 2 * insertCount - first);
+
+ int countLimit = 6;
+ m_hMapper->setCount(countLimit);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelColumnCount + 2 * insertCount - first));
+
+ m_model->insertColumns(3, insertCount);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelColumnCount + 3 * insertCount - first));
+
+ m_hMapper->setFirst(0);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelColumnCount + 3 * insertCount));
+
+ m_hMapper->setCount(-1);
+ QCOMPARE(m_series->count(), m_modelColumnCount + 3 * insertCount);
+}
+
+void tst_piemodelmapper::horizontalModelRemoveColumns()
+{
+ // setup the mapper
+ createHorizontalMapper();
+ QCOMPARE(m_series->count(), m_modelColumnCount);
+ QVERIFY(m_hMapper->model() != 0);
+
+ int removeCount = 2;
+ m_model->removeColumns(1, removeCount);
+ QCOMPARE(m_series->count(), m_modelColumnCount - removeCount);
+
+ int first = 1;
+ m_hMapper->setFirst(first);
+ QCOMPARE(m_series->count(), m_modelColumnCount - removeCount - first);
+
+ m_model->removeColumns(1, removeCount);
+ QCOMPARE(m_series->count(), m_modelColumnCount - 2 * removeCount - first);
+
+ int countLimit = 3;
+ m_hMapper->setCount(countLimit);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelColumnCount - 2 * removeCount - first));
+
+ m_model->removeColumns(1, removeCount);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelColumnCount - 3 * removeCount - first));
+
+ m_hMapper->setFirst(0);
+ QCOMPARE(m_series->count(), qMin(countLimit, m_modelColumnCount - 3 * removeCount));
+
+ m_hMapper->setCount(-1);
+ QCOMPARE(m_series->count(), m_modelColumnCount - 3 * removeCount);
+}
+
+void tst_piemodelmapper::modelUpdateCell()
+{
+ // setup the mapper
+ createVerticalMapper();
+
+ QVERIFY(m_model->setData(m_model->index(1, 0), 44));
+ QCOMPARE(m_series->slices().at(1)->value(), 44.0);
+ QCOMPARE(m_model->data(m_model->index(1, 0)).toReal(), 44.0);
+}
+
+void tst_piemodelmapper::verticalMapperSignals()
+{
+ auto mapper = new QPieModelMapper;
+
+ QSignalSpy spy0(mapper, SIGNAL(firstChanged()));
+ QSignalSpy spy1(mapper, SIGNAL(countChanged()));
+ QSignalSpy spy2(mapper, SIGNAL(valuesSectionChanged()));
+ QSignalSpy spy3(mapper, SIGNAL(labelsSectionChanged()));
+ QSignalSpy spy4(mapper, SIGNAL(modelChanged()));
+ QSignalSpy spy5(mapper, SIGNAL(seriesChanged()));
+
+ mapper->setValuesSection(0);
+ mapper->setLabelsSection(1);
+ mapper->setModel(m_model);
+ mapper->setSeries(m_series);
+ mapper->setFirst(1);
+ mapper->setCount(5);
+
+ QCOMPARE(spy0.size(), 1);
+ QCOMPARE(spy1.size(), 1);
+ QCOMPARE(spy2.size(), 1);
+ QCOMPARE(spy3.size(), 1);
+ QCOMPARE(spy4.size(), 1);
+ QCOMPARE(spy5.size(), 1);
+
+ delete mapper;
+}
+
+void tst_piemodelmapper::horizontalMapperSignals()
+{
+ auto mapper = new QPieModelMapper;
+
+ QSignalSpy spy0(mapper, SIGNAL(firstChanged()));
+ QSignalSpy spy1(mapper, SIGNAL(countChanged()));
+ QSignalSpy spy2(mapper, SIGNAL(valuesSectionChanged()));
+ QSignalSpy spy3(mapper, SIGNAL(labelsSectionChanged()));
+ QSignalSpy spy4(mapper, SIGNAL(modelChanged()));
+ QSignalSpy spy5(mapper, SIGNAL(seriesChanged()));
+
+ mapper->setOrientation(Qt::Horizontal);
+ mapper->setValuesSection(0);
+ mapper->setLabelsSection(1);
+ mapper->setModel(m_model);
+ mapper->setSeries(m_series);
+ mapper->setFirst(1);
+ mapper->setCount(5);
+
+ QCOMPARE(spy0.size(), 1);
+ QCOMPARE(spy1.size(), 1);
+ QCOMPARE(spy2.size(), 1);
+ QCOMPARE(spy3.size(), 1);
+ QCOMPARE(spy4.size(), 1);
+ QCOMPARE(spy5.size(), 1);
+}
+
+QTEST_MAIN(tst_piemodelmapper)
+
+#include "tst_piemodelmapper.moc"