diff options
author | Sami Varanka <sami.varanka@qt.io> | 2024-05-02 17:18:30 +0300 |
---|---|---|
committer | Sami Varanka <sami.varanka@qt.io> | 2024-05-10 10:57:39 +0300 |
commit | 09f0342e64e29244c003a06467f0d1f2ee54567b (patch) | |
tree | 3864765f1cdc11f86d9fa75f0a3e1e8d9584574d | |
parent | 34734ae3f8c8b2bad9b9ec98a810de869fedcc57 (diff) |
Add XYModelMapper
Task-number: QTBUG-125027
Change-Id: Ic0ea8bb216abe3dd5a5732958ab5dfe2fdc7e610
Reviewed-by: Kaj Grönholm <kaj.gronholm@qt.io>
-rw-r--r-- | src/graphs2d/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/graphs2d/xychart/qxymodelmapper.cpp | 650 | ||||
-rw-r--r-- | src/graphs2d/xychart/qxymodelmapper.h | 67 | ||||
-rw-r--r-- | src/graphs2d/xychart/qxymodelmapper_p.h | 68 | ||||
-rw-r--r-- | tests/auto/cpp2dtest/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/cpp2dtest/qgxymodelmapper/CMakeLists.txt | 10 | ||||
-rw-r--r-- | tests/auto/cpp2dtest/qgxymodelmapper/tst_xymodelmapper.cpp | 584 |
7 files changed, 1381 insertions, 0 deletions
diff --git a/src/graphs2d/CMakeLists.txt b/src/graphs2d/CMakeLists.txt index 318cd74..4e5759a 100644 --- a/src/graphs2d/CMakeLists.txt +++ b/src/graphs2d/CMakeLists.txt @@ -16,6 +16,7 @@ qt_internal_extend_target(Graphs 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 + xychart/qxymodelmapper.cpp xychart/qxymodelmapper.h xychart/qxymodelmapper_p.h axis/qabstractaxis.cpp axis/qabstractaxis.h axis/qabstractaxis_p.h axis/barcategoryaxis/qbarcategoryaxis.cpp axis/barcategoryaxis/qbarcategoryaxis.h axis/barcategoryaxis/qbarcategoryaxis_p.h axis/axisgrid.cpp axis/axisgrid_p.h diff --git a/src/graphs2d/xychart/qxymodelmapper.cpp b/src/graphs2d/xychart/qxymodelmapper.cpp new file mode 100644 index 0000000..22ec057 --- /dev/null +++ b/src/graphs2d/xychart/qxymodelmapper.cpp @@ -0,0 +1,650 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <QtCore/QAbstractItemModel> +#include <QtGraphs/QXYModelMapper> +#include <QtGraphs/QXYSeries> +#include "qxymodelmapper_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QXYModelMapper + \inmodule QtGraphs + \ingroup graphs_2D + + \brief The QXYModelMapper class is a model mapper for line, + spline, and scatter 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 line, spline, or scatter series and the data + 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 QXYSeries +*/ +/*! + \qmltype XYModelMapper + \instantiates QXYModelMapper + \inqmlmodule QtGraphs + \ingroup graphs_qml_2D + + \brief A model mapper for XYSeries. + + 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 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 XYSeries +*/ + +QXYModelMapper::~QXYModelMapper() {} + +QXYModelMapper::QXYModelMapper(QObject *parent) + : QObject{*(new QXYModelMapperPrivate), parent} +{} + +QXYModelMapper::QXYModelMapper(QXYModelMapperPrivate &dd, QObject *parent) + : QObject(dd, parent) +{} + +QAbstractItemModel *QXYModelMapper::model() const +{ + Q_D(const QXYModelMapper); + return d->m_model; +} + +void QXYModelMapper::setModel(QAbstractItemModel *model) +{ + if (model == 0) + return; + + Q_D(QXYModelMapper); + if (d->m_model) { + QObjectPrivate::disconnect(d->m_model, + &QAbstractItemModel::dataChanged, + d, + &QXYModelMapperPrivate::onModelUpdated); + QObjectPrivate::disconnect(d->m_model, + &QAbstractItemModel::rowsInserted, + d, + &QXYModelMapperPrivate::onModelRowsAdded); + QObjectPrivate::disconnect(d->m_model, + &QAbstractItemModel::rowsRemoved, + d, + &QXYModelMapperPrivate::onModelRowsRemoved); + QObjectPrivate::disconnect(d->m_model, + &QAbstractItemModel::columnsInserted, + d, + &QXYModelMapperPrivate::onModelColumnsAdded); + QObjectPrivate::disconnect(d->m_model, + &QAbstractItemModel::columnsRemoved, + d, + &QXYModelMapperPrivate::onModelColumnsRemoved); + QObjectPrivate::disconnect(d->m_model, + &QAbstractItemModel::modelReset, + d, + &QXYModelMapperPrivate::initializeXYFromModel); + QObjectPrivate::disconnect(d->m_model, + &QAbstractItemModel::layoutChanged, + d, + &QXYModelMapperPrivate::initializeXYFromModel); + QObjectPrivate::disconnect(d->m_model, + &QAbstractItemModel::destroyed, + d, + &QXYModelMapperPrivate::handleModelDestroyed); + } + + d->m_model = model; + d->initializeXYFromModel(); + // connect signals from the model + QObjectPrivate::connect(d->m_model, + &QAbstractItemModel::dataChanged, + d, + &QXYModelMapperPrivate::onModelUpdated); + QObjectPrivate::connect(d->m_model, + &QAbstractItemModel::rowsInserted, + d, + &QXYModelMapperPrivate::onModelRowsAdded); + QObjectPrivate::connect(d->m_model, + &QAbstractItemModel::rowsRemoved, + d, + &QXYModelMapperPrivate::onModelRowsRemoved); + QObjectPrivate::connect(d->m_model, + &QAbstractItemModel::columnsInserted, + d, + &QXYModelMapperPrivate::onModelColumnsAdded); + QObjectPrivate::connect(d->m_model, + &QAbstractItemModel::columnsRemoved, + d, + &QXYModelMapperPrivate::onModelColumnsRemoved); + + QObjectPrivate::connect(d->m_model, + &QAbstractItemModel::modelReset, + d, + &QXYModelMapperPrivate::initializeXYFromModel); + QObjectPrivate::connect(d->m_model, + &QAbstractItemModel::layoutChanged, + d, + &QXYModelMapperPrivate::initializeXYFromModel); + QObjectPrivate::connect(d->m_model, + &QAbstractItemModel::destroyed, + d, + &QXYModelMapperPrivate::handleModelDestroyed); + Q_EMIT modelChanged(); +} + +QXYSeries *QXYModelMapper::series() const +{ + Q_D(const QXYModelMapper); + return d->m_series; +} + +void QXYModelMapper::setSeries(QXYSeries *series) +{ + Q_D(QXYModelMapper); + if (d->m_series) { + QObjectPrivate::disconnect(d->m_series, + &QXYSeries::pointAdded, + d, + &QXYModelMapperPrivate::onPointAdded); + QObjectPrivate::disconnect(d->m_series, + &QXYSeries::pointRemoved, + d, + &QXYModelMapperPrivate::onPointRemoved); + QObjectPrivate::disconnect(d->m_series, + &QXYSeries::pointReplaced, + d, + &QXYModelMapperPrivate::onPointReplaced); + QObjectPrivate::disconnect(d->m_series, + &QXYSeries::destroyed, + d, + &QXYModelMapperPrivate::handleSeriesDestroyed); + QObjectPrivate::disconnect(d->m_series, + &QXYSeries::pointsRemoved, + d, + &QXYModelMapperPrivate::onPointsRemoved); + } + + if (series == 0) + return; + + d->m_series = series; + d->initializeXYFromModel(); + // connect the signals from the series + QObjectPrivate::connect(d->m_series, + &QXYSeries::pointAdded, + d, + &QXYModelMapperPrivate::onPointAdded); + QObjectPrivate::connect(d->m_series, + &QXYSeries::pointRemoved, + d, + &QXYModelMapperPrivate::onPointRemoved); + QObjectPrivate::connect(d->m_series, + &QXYSeries::pointReplaced, + d, + &QXYModelMapperPrivate::onPointReplaced); + QObjectPrivate::connect(d->m_series, + &QXYSeries::destroyed, + d, + &QXYModelMapperPrivate::handleSeriesDestroyed); + QObjectPrivate::connect(d->m_series, + &QXYSeries::pointsRemoved, + d, + &QXYModelMapperPrivate::onPointsRemoved); + Q_EMIT seriesChanged(); +} + +int QXYModelMapper::first() const +{ + Q_D(const QXYModelMapper); + return d->m_first; +} + +void QXYModelMapper::setFirst(int first) +{ + Q_D(QXYModelMapper); + d->m_first = qMax(first, 0); + d->initializeXYFromModel(); + Q_EMIT firstChanged(); +} + +int QXYModelMapper::count() const +{ + Q_D(const QXYModelMapper); + return d->m_count; +} + +void QXYModelMapper::setCount(int count) +{ + Q_D(QXYModelMapper); + d->m_count = qMax(count, -1); + d->initializeXYFromModel(); + Q_EMIT countChanged(); +} + +Qt::Orientation QXYModelMapper::orientation() const +{ + Q_D(const QXYModelMapper); + return d->m_orientation; +} + +void QXYModelMapper::setOrientation(Qt::Orientation orientation) +{ + Q_D(QXYModelMapper); + d->m_orientation = orientation; + d->initializeXYFromModel(); + Q_EMIT orientationChanged(); +} + +int QXYModelMapper::xSection() const +{ + Q_D(const QXYModelMapper); + return d->m_xSection; +} + +void QXYModelMapper::setXSection(int xSection) +{ + Q_D(QXYModelMapper); + d->m_xSection = qMax(-1, xSection); + d->initializeXYFromModel(); + Q_EMIT xSectionChanged(); +} + +int QXYModelMapper::ySection() const +{ + Q_D(const QXYModelMapper); + return d->m_ySection; +} + +void QXYModelMapper::setYSection(int ySection) +{ + Q_D(QXYModelMapper); + d->m_ySection = qMax(-1, ySection); + d->initializeXYFromModel(); + Q_EMIT ySectionChanged(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +QXYModelMapperPrivate::QXYModelMapperPrivate() {} + +QXYModelMapperPrivate::~QXYModelMapperPrivate() {} + +void QXYModelMapperPrivate::blockModelSignals(bool block) +{ + m_modelSignalsBlock = block; +} + +void QXYModelMapperPrivate::blockSeriesSignals(bool block) +{ + m_seriesSignalsBlock = block; +} + +QModelIndex QXYModelMapperPrivate::xModelIndex(int xPos) +{ + if (m_count != -1 && xPos >= m_count) + return QModelIndex(); // invalid + + if (m_orientation == Qt::Vertical) + return m_model->index(xPos + m_first, m_xSection); + else + return m_model->index(m_xSection, xPos + m_first); +} + +QModelIndex QXYModelMapperPrivate::yModelIndex(int yPos) +{ + if (m_count != -1 && yPos >= m_count) + return QModelIndex(); // invalid + + if (m_orientation == Qt::Vertical) + return m_model->index(yPos + m_first, m_ySection); + else + return m_model->index(m_ySection, yPos + m_first); +} + +qreal QXYModelMapperPrivate::valueFromModel(QModelIndex index) +{ + QVariant value = m_model->data(index, Qt::DisplayRole); + switch (value.metaType().id()) { + case QMetaType::QDateTime: + return value.toDateTime().toMSecsSinceEpoch(); + case QMetaType::QDate: + return value.toDate().startOfDay().toMSecsSinceEpoch(); + default: + return value.toReal(); + } +} + +void QXYModelMapperPrivate::setValueToModel(QModelIndex index, qreal value) +{ + QVariant oldValue = m_model->data(index, Qt::DisplayRole); + switch (oldValue.metaType().id()) { + case QMetaType::QDateTime: + m_model->setData(index, QDateTime::fromMSecsSinceEpoch(value)); + break; + case QMetaType::QDate: + m_model->setData(index, QDateTime::fromMSecsSinceEpoch(value).date()); + break; + default: + m_model->setData(index, value); + } +} + +void QXYModelMapperPrivate::onPointAdded(int pointPos) +{ + if (m_seriesSignalsBlock) + return; + + if (m_count != -1) + m_count += 1; + + blockModelSignals(); + if (m_orientation == Qt::Vertical) + m_model->insertRows(pointPos + m_first, 1); + else + m_model->insertColumns(pointPos + m_first, 1); + + setValueToModel(xModelIndex(pointPos), m_series->points().at(pointPos).x()); + setValueToModel(yModelIndex(pointPos), m_series->points().at(pointPos).y()); + blockModelSignals(false); +} + +void QXYModelMapperPrivate::onPointRemoved(int pointPos) +{ + if (m_seriesSignalsBlock) + return; + + if (m_count != -1) + m_count -= 1; + + blockModelSignals(); + if (m_orientation == Qt::Vertical) + m_model->removeRow(pointPos + m_first); + else + m_model->removeColumn(pointPos + m_first); + blockModelSignals(false); +} + +void QXYModelMapperPrivate::onPointsRemoved(int pointPos, int count) +{ + if (m_seriesSignalsBlock) + return; + + m_count -= count; + + if (m_count < -1) + m_count = -1; + + blockModelSignals(); + if (m_orientation == Qt::Vertical) + m_model->removeRows(pointPos + m_first, count); + else + m_model->removeColumns(pointPos + m_first, count); + blockModelSignals(false); +} + +void QXYModelMapperPrivate::onPointReplaced(int pointPos) +{ + if (m_seriesSignalsBlock) + return; + + blockModelSignals(); + setValueToModel(xModelIndex(pointPos), m_series->points().at(pointPos).x()); + setValueToModel(yModelIndex(pointPos), m_series->points().at(pointPos).y()); + blockModelSignals(false); +} + +void QXYModelMapperPrivate::handleSeriesDestroyed() +{ + m_series = 0; +} + +void QXYModelMapperPrivate::onModelUpdated(QModelIndex topLeft, QModelIndex bottomRight) +{ + if (m_model == 0 || m_series == 0) + return; + + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + QModelIndex index; + 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); + 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()) { + newPoint.setX(valueFromModel(xIndex)); + newPoint.setY(valueFromModel(yIndex)); + m_series->replace(indexRow - m_first, newPoint); + } + } + } 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()) { + newPoint.setX(valueFromModel(xIndex)); + newPoint.setY(valueFromModel(yIndex)); + m_series->replace(indexColumn - m_first, newPoint); + } + } + } + } + } + blockSeriesSignals(false); +} + +void QXYModelMapperPrivate::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_xSection || start <= m_ySection) { + // if the changes affect the map - reinitialize the xy + initializeXYFromModel(); + } + blockSeriesSignals(false); +} + +void QXYModelMapperPrivate::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_xSection || start <= m_ySection) { + // if the changes affect the map - reinitialize the xy + initializeXYFromModel(); + } + blockSeriesSignals(false); +} + +void QXYModelMapperPrivate::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_xSection || start <= m_ySection) { + // if the changes affect the map - reinitialize the xy + initializeXYFromModel(); + } + blockSeriesSignals(false); +} + +void QXYModelMapperPrivate::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_xSection || start <= m_ySection) { + // if the changes affect the map - reinitialize the xy + initializeXYFromModel(); + } + blockSeriesSignals(false); +} + +void QXYModelMapperPrivate::handleModelDestroyed() +{ + m_model = 0; +} + +void QXYModelMapperPrivate::insertData(int start, int end) +{ + if (m_model == 0 || m_series == 0) + 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); + for (int i = first; i <= last; i++) { + QPointF point; + QModelIndex xIndex = xModelIndex(i - m_first); + QModelIndex yIndex = yModelIndex(i - m_first); + if (xIndex.isValid() && yIndex.isValid()) { + point.setX(valueFromModel(xIndex)); + point.setY(valueFromModel(yIndex)); + m_series->insert(i - m_first, point); + } + } + + // remove excess of points (above m_count) + if (m_count != -1 && m_series->points().size() > m_count) { + for (int i = m_series->points().size() - 1; i >= m_count; i--) + m_series->remove(m_series->points().at(i)); + } + } +} + +void QXYModelMapperPrivate::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->count(), + 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->count() + 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->points().at(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->count(); + else + itemsAvailable = m_model->columnCount() - m_first - m_series->count(); + int toBeAdded = qMin( + itemsAvailable, + m_count + - m_series->count()); // add not more items than there is space left to be filled. + int currentSize = m_series->count(); + if (toBeAdded > 0) { + for (int i = m_series->count(); i < currentSize + toBeAdded; i++) { + QPointF point; + QModelIndex xIndex = xModelIndex(i); + QModelIndex yIndex = yModelIndex(i); + if (xIndex.isValid() && yIndex.isValid()) { + point.setX(valueFromModel(xIndex)); + point.setY(valueFromModel(yIndex)); + m_series->insert(i, point); + } + } + } + } + } +} + +void QXYModelMapperPrivate::initializeXYFromModel() +{ + if (m_model == 0 || m_series == 0) + return; + + blockSeriesSignals(); + // clear current content + m_series->clear(); + + // create the initial points set + int pointPos = 0; + QModelIndex xIndex = xModelIndex(pointPos); + QModelIndex yIndex = yModelIndex(pointPos); + + if (xIndex.isValid() && yIndex.isValid()) { + while (xIndex.isValid() && yIndex.isValid()) { + QPointF point; + point.setX(valueFromModel(xIndex)); + point.setY(valueFromModel(yIndex)); + m_series->append(point); + pointPos++; + xIndex = xModelIndex(pointPos); + yIndex = yModelIndex(pointPos); + // Don't warn about invalid index after the first, those are valid and used to + // determine when we should end looping. + } + } else { + // Invalid index right off the bat means series will be left empty, so output a warning, + // unless model is also empty + int count = m_orientation == Qt::Vertical ? m_model->rowCount() : m_model->columnCount(); + if (count > 0) { + if (!xIndex.isValid()) { + qWarning() << __FUNCTION__ + << QStringLiteral("Invalid X coordinate index in model mapper."); + } else if (!yIndex.isValid()) { + qWarning() << __FUNCTION__ + << QStringLiteral("Invalid Y coordinate index in model mapper."); + } + } + } + + blockSeriesSignals(false); +} +QT_END_NAMESPACE diff --git a/src/graphs2d/xychart/qxymodelmapper.h b/src/graphs2d/xychart/qxymodelmapper.h new file mode 100644 index 0000000..1aaeba4 --- /dev/null +++ b/src/graphs2d/xychart/qxymodelmapper.h @@ -0,0 +1,67 @@ +// Copyright (C) 2024 The Qt Company Ltd. + +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#ifndef QXYMODELMAPPER_H +#define QXYMODELMAPPER_H +#include <QtCore/qobject.h> +#include <QtGraphs/qgraphsglobal.h> +#include <QtQmlIntegration/qqmlintegration.h> + +Q_MOC_INCLUDE(<QtGraphs / qxyseries.h>) +Q_MOC_INCLUDE(<QtCore / qabstractitemmodel.h>) +QT_BEGIN_NAMESPACE + +class QXYModelMapperPrivate; +class QXYSeries; +class QAbstractItemModel; +class Q_GRAPHS_EXPORT QXYModelMapper : public QObject +{ + Q_OBJECT + Q_PROPERTY(QXYSeries *series READ series WRITE setSeries NOTIFY seriesChanged) + Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(int xSection READ xSection WRITE setXSection NOTIFY xSectionChanged) + Q_PROPERTY(int ySection READ ySection WRITE setYSection NOTIFY ySectionChanged) + Q_PROPERTY(int first READ first WRITE setFirst NOTIFY firstChanged) + 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(XYModelMapper) + Q_DECLARE_PRIVATE(QXYModelMapper) +public: + explicit QXYModelMapper(QObject *parent = nullptr); + ~QXYModelMapper() override; + + QAbstractItemModel *model() const; + void setModel(QAbstractItemModel *model); + + QXYSeries *series() const; + void setSeries(QXYSeries *series); + + int first() const; + void setFirst(int first); + + int count() const; + void setCount(int count); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + + int xSection() const; + void setXSection(int xSection); + + int ySection() const; + void setYSection(int ySection); +Q_SIGNALS: + void seriesChanged(); + void modelChanged(); + void xSectionChanged(); + void ySectionChanged(); + void firstChanged(); + void countChanged(); + void orientationChanged(); + +protected: + QXYModelMapper(QXYModelMapperPrivate &dd, QObject *parent = nullptr); +}; +QT_END_NAMESPACE +#endif // QXYMODELMAPPER_H diff --git a/src/graphs2d/xychart/qxymodelmapper_p.h b/src/graphs2d/xychart/qxymodelmapper_p.h new file mode 100644 index 0000000..3b035b7 --- /dev/null +++ b/src/graphs2d/xychart/qxymodelmapper_p.h @@ -0,0 +1,68 @@ +// 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 QXYMODELMAPPER_P_H +#define QXYMODELMAPPER_P_H + +#include <QtGraphs/QXYModelMapper> +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QXYModelMapperPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QXYModelMapper) +public: + QXYModelMapperPrivate(); + ~QXYModelMapperPrivate() override; + +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 onPointAdded(int pointPos); + void onPointRemoved(int pointPos); + void onPointsRemoved(int pointPos, int count); + void onPointReplaced(int pointPos); + void handleSeriesDestroyed(); + + void initializeXYFromModel(); + +private: + QModelIndex xModelIndex(int xPos); + QModelIndex yModelIndex(int yPos); + void insertData(int start, int end); + void removeData(int start, int end); + void blockModelSignals(bool block = true); + void blockSeriesSignals(bool block = true); + qreal valueFromModel(QModelIndex index); + void setValueToModel(QModelIndex index, qreal value); + +private: + QXYSeries *m_series = nullptr; + QAbstractItemModel *m_model = nullptr; + int m_first = 0; + int m_count = -1; + Qt::Orientation m_orientation = Qt::Vertical; + int m_xSection = -1; + int m_ySection = -1; + bool m_seriesSignalsBlock = false; + bool m_modelSignalsBlock = false; +}; + +QT_END_NAMESPACE +#endif // QXYMODELMAPPER_P_H diff --git a/tests/auto/cpp2dtest/CMakeLists.txt b/tests/auto/cpp2dtest/CMakeLists.txt index 7b74dd1..bbee3a8 100644 --- a/tests/auto/cpp2dtest/CMakeLists.txt +++ b/tests/auto/cpp2dtest/CMakeLists.txt @@ -16,3 +16,4 @@ add_subdirectory(qgsplines) add_subdirectory(qgarea) add_subdirectory(qgbarmodelmapper) add_subdirectory(qgpiemodelmapper) +add_subdirectory(qgxymodelmapper) diff --git a/tests/auto/cpp2dtest/qgxymodelmapper/CMakeLists.txt b/tests/auto/cpp2dtest/qgxymodelmapper/CMakeLists.txt new file mode 100644 index 0000000..43e2970 --- /dev/null +++ b/tests/auto/cpp2dtest/qgxymodelmapper/CMakeLists.txt @@ -0,0 +1,10 @@ +qt_internal_add_test(tst_qgxymodelmapper + SOURCES + tst_xymodelmapper.cpp + INCLUDE_DIRECTORIES + ../common + LIBRARIES + Qt::Gui + Qt::GuiPrivate + Qt::Graphs +) diff --git a/tests/auto/cpp2dtest/qgxymodelmapper/tst_xymodelmapper.cpp b/tests/auto/cpp2dtest/qgxymodelmapper/tst_xymodelmapper.cpp new file mode 100644 index 0000000..bc2830c --- /dev/null +++ b/tests/auto/cpp2dtest/qgxymodelmapper/tst_xymodelmapper.cpp @@ -0,0 +1,584 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore/QString> +#include <QtTest/QtTest> + +#include <QtGraphs/QLineSeries> +#include <QtGraphs/QXYModelMapper> +#include <QtGraphs/QXYSeries> +#include <QtGui/QStandardItemModel> + +QT_USE_NAMESPACE + +class tst_qgxymodelmapper : public QObject +{ + Q_OBJECT + +public: + tst_qgxymodelmapper(); + 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; + int m_modelRowCount; + int m_modelColumnCount; + + QXYModelMapper *m_hMapper; + QXYModelMapper *m_vMapper; + + QXYSeries *m_series; +}; + +tst_qgxymodelmapper::tst_qgxymodelmapper() + : m_model(0) + , m_modelRowCount(10) + , m_modelColumnCount(8) + , m_hMapper(0) + , m_vMapper(0) + , m_series(0) +{} + +void tst_qgxymodelmapper::createVerticalMapper() +{ + m_vMapper = new QXYModelMapper; + QVERIFY(m_vMapper->model() == 0); + m_vMapper->setXSection(0); + m_vMapper->setYSection(1); + m_vMapper->setModel(m_model); + m_vMapper->setSeries(m_series); +} + +void tst_qgxymodelmapper::createHorizontalMapper() +{ + m_hMapper = new QXYModelMapper; + QVERIFY(m_hMapper->model() == 0); + m_hMapper->setOrientation(Qt::Horizontal); + m_hMapper->setXSection(0); + m_hMapper->setYSection(1); + m_hMapper->setModel(m_model); + m_hMapper->setSeries(m_series); +} + +void tst_qgxymodelmapper::init() +{ + m_series = new QLineSeries; + + 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_qgxymodelmapper::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_qgxymodelmapper::initTestCase() {} + +void tst_qgxymodelmapper::cleanupTestCase() +{ + QTest::qWait(1); // Allow final deleteLaters to run +} + +void tst_qgxymodelmapper::verticalMapper_data() +{ + QTest::addColumn<int>("xColumn"); + QTest::addColumn<int>("yColumn"); + QTest::addColumn<int>("expectedCount"); + QTest::newRow("different x and y columns") << 0 << 1 << m_modelRowCount; + QTest::newRow("same x and y columns") << 1 << 1 << m_modelRowCount; + QTest::newRow("invalid x column and correct y column") << -3 << 1 << 0; + QTest::newRow("x column beyond the size of model and correct y column") + << m_modelColumnCount << 1 << 0; + QTest::newRow("x column beyond the size of model and invalid y column") + << m_modelColumnCount << -1 << 0; +} + +void tst_qgxymodelmapper::verticalMapper() +{ + QFETCH(int, xColumn); + QFETCH(int, yColumn); + QFETCH(int, expectedCount); + + QXYModelMapper *mapper = new QXYModelMapper; + QVERIFY(mapper->model() == 0); + + mapper->setXSection(xColumn); + mapper->setYSection(yColumn); + mapper->setModel(m_model); + mapper->setSeries(m_series); + + QCOMPARE(m_series->count(), expectedCount); + QCOMPARE(mapper->xSection(), qMax(-1, xColumn)); + QCOMPARE(mapper->ySection(), qMax(-1, yColumn)); + + delete mapper; + mapper = 0; +} + +void tst_qgxymodelmapper::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_qgxymodelmapper::verticalMapperCustomMapping() +{ + QFETCH(int, first); + QFETCH(int, countLimit); + QFETCH(int, expectedCount); + + QCOMPARE(m_series->count(), 0); + + QXYModelMapper *mapper = new QXYModelMapper; + mapper->setXSection(0); + mapper->setYSection(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->setXSection(-1); + mapper->setYSection(1); + + QCOMPARE(m_series->count(), 0); + + delete mapper; + mapper = 0; +} + +void tst_qgxymodelmapper::horizontalMapper_data() +{ + QTest::addColumn<int>("xRow"); + QTest::addColumn<int>("yRow"); + QTest::addColumn<int>("expectedCount"); + QTest::newRow("different x and y rows") << 0 << 1 << m_modelColumnCount; + QTest::newRow("same x and y rows") << 1 << 1 << m_modelColumnCount; + QTest::newRow("invalid x row and correct y row") << -3 << 1 << 0; + QTest::newRow("x row beyond the size of model and correct y row") << m_modelRowCount << 1 << 0; + QTest::newRow("x row beyond the size of model and invalid y row") << m_modelRowCount << -1 << 0; +} + +void tst_qgxymodelmapper::horizontalMapper() +{ + QFETCH(int, xRow); + QFETCH(int, yRow); + QFETCH(int, expectedCount); + + QXYModelMapper *mapper = new QXYModelMapper; + mapper->setOrientation(Qt::Horizontal); + mapper->setXSection(xRow); + mapper->setYSection(yRow); + mapper->setModel(m_model); + mapper->setSeries(m_series); + + QCOMPARE(m_series->count(), expectedCount); + QCOMPARE(mapper->xSection(), qMax(-1, xRow)); + QCOMPARE(mapper->ySection(), qMax(-1, yRow)); + + delete mapper; + mapper = 0; +} + +void tst_qgxymodelmapper::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_qgxymodelmapper::horizontalMapperCustomMapping() +{ + QFETCH(int, first); + QFETCH(int, countLimit); + QFETCH(int, expectedCount); + + QCOMPARE(m_series->count(), 0); + + QXYModelMapper *mapper = new QXYModelMapper; + mapper->setOrientation(Qt::Horizontal); + mapper->setXSection(0); + mapper->setYSection(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->setXSection(-1); + mapper->setYSection(1); + + QCOMPARE(m_series->count(), 0); + + delete mapper; + mapper = 0; +} + +void tst_qgxymodelmapper::seriesUpdated() +{ + // setup the mapper + createVerticalMapper(); + QCOMPARE(m_series->count(), m_modelRowCount); + QCOMPARE(m_vMapper->count(), -1); + + m_series->append(QPointF(100, 100)); + 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->points().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 + + m_series->removePoints(1, m_modelRowCount - 4); + QCOMPARE(m_series->count(), 4); + QCOMPARE(m_vMapper->count(), + -1); // the value should not change as it indicates 'all' items there are in the model + + m_series->replace(m_series->points().first(), QPointF(25.0, 75.0)); + QCOMPARE(m_model->data(m_model->index(0, 0)).toReal(), 25.0); + QCOMPARE(m_model->data(m_model->index(0, 1)).toReal(), 75.0); +} + +void tst_qgxymodelmapper::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_qgxymodelmapper::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_qgxymodelmapper::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_qgxymodelmapper::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_qgxymodelmapper::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_qgxymodelmapper::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_qgxymodelmapper::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_qgxymodelmapper::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_qgxymodelmapper::modelUpdateCell() +{ + // setup the mapper + createVerticalMapper(); + + QVERIFY(m_model->setData(m_model->index(1, 0), 44)); + QCOMPARE(m_series->points().at(1).x(), 44.0); + QCOMPARE(m_model->data(m_model->index(1, 0)).toReal(), 44.0); +} + +void tst_qgxymodelmapper::verticalMapperSignals() +{ + QXYModelMapper *mapper = new QXYModelMapper; + + QSignalSpy spy0(mapper, SIGNAL(firstChanged())); + QSignalSpy spy1(mapper, SIGNAL(countChanged())); + QSignalSpy spy2(mapper, SIGNAL(xSectionChanged())); + QSignalSpy spy3(mapper, SIGNAL(ySectionChanged())); + QSignalSpy spy4(mapper, SIGNAL(modelChanged())); + QSignalSpy spy5(mapper, SIGNAL(seriesChanged())); + + mapper->setXSection(0); + mapper->setYSection(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_qgxymodelmapper::horizontalMapperSignals() +{ + QXYModelMapper *mapper = new QXYModelMapper; + + QSignalSpy spy0(mapper, SIGNAL(firstChanged())); + QSignalSpy spy1(mapper, SIGNAL(countChanged())); + QSignalSpy spy2(mapper, SIGNAL(xSectionChanged())); + QSignalSpy spy3(mapper, SIGNAL(ySectionChanged())); + QSignalSpy spy4(mapper, SIGNAL(modelChanged())); + QSignalSpy spy5(mapper, SIGNAL(seriesChanged())); + + mapper->setXSection(0); + mapper->setYSection(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; +} + +QTEST_MAIN(tst_qgxymodelmapper) + +#include "tst_xymodelmapper.moc" |