From 73ffd814281d9fa07f5d25b2b3a8ce04a011780c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 13 Sep 2013 13:56:22 +0300 Subject: Automatic row/col categories for bar and surface item model mappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTRD-2271 Change-Id: I571e4010f3780722564f06c06bcc346d5803c646 Reviewed-by: Tomi Korpipää --- src/datavisualization/axis/q3dvalueaxis.cpp | 2 +- src/datavisualization/data/baritemmodelhandler.cpp | 64 +++++--- .../data/qitemmodelbardatamapping.cpp | 160 ++++++++++++++++--- .../data/qitemmodelbardatamapping.h | 14 ++ .../data/qitemmodelbardatamapping_p.h | 5 + .../data/qitemmodelscatterdatamapping_p.h | 6 +- .../data/qitemmodelsurfacedatamapping.cpp | 169 +++++++++++++++++++-- .../data/qitemmodelsurfacedatamapping.h | 14 ++ .../data/qitemmodelsurfacedatamapping_p.h | 5 + .../data/surfaceitemmodelhandler.cpp | 45 ++++-- .../doc/snippets/doc_src_qmldatavisualization.cpp | 2 - .../doc/snippets/doc_src_qtdatavisualization.cpp | 9 ++ src/datavisualizationqml2/declarativebars.cpp | 2 + src/datavisualizationqml2/declarativebars_p.h | 3 +- 14 files changed, 431 insertions(+), 69 deletions(-) (limited to 'src') diff --git a/src/datavisualization/axis/q3dvalueaxis.cpp b/src/datavisualization/axis/q3dvalueaxis.cpp index fb430428..86b16fd9 100644 --- a/src/datavisualization/axis/q3dvalueaxis.cpp +++ b/src/datavisualization/axis/q3dvalueaxis.cpp @@ -295,7 +295,7 @@ void Q3DValueAxisPrivate::setRange(qreal min, qreal max) m_min = min; dirty = true; } - if (m_max != max) { + if (m_max != max || min >= max) { if (min >= max) { m_max = min + 1.0; qWarning() << "Warning: Tried to set invalid range for value axis." diff --git a/src/datavisualization/data/baritemmodelhandler.cpp b/src/datavisualization/data/baritemmodelhandler.cpp index 07a802cd..f456604b 100644 --- a/src/datavisualization/data/baritemmodelhandler.cpp +++ b/src/datavisualization/data/baritemmodelhandler.cpp @@ -17,7 +17,7 @@ ****************************************************************************/ #include "baritemmodelhandler_p.h" -#include +#include "qitemmodelbardatamapping_p.h" QT_DATAVISUALIZATION_BEGIN_NAMESPACE @@ -40,33 +40,47 @@ void BarItemModelHandler::resolveModel() return; } - bool useModelRows(false); - if (!mapping->rowCategories().size() || !mapping->columnCategories().size()) { - useModelRows = true; - } else if (mapping->rowRole().isEmpty() || mapping->columnRole().isEmpty()) { + if (!mapping->useModelCategories() + && (mapping->rowRole().isEmpty() || mapping->columnRole().isEmpty())) { m_proxy->resetArray(0); return; } + QStringList rowLabels; + QStringList columnLabels; + QBarDataArray *newProxyArray = new QBarDataArray; QHash roleHash = m_itemModel->roleNames(); + // Default to display role if no mapping int valueRole = roleHash.key(mapping->valueRole().toLatin1(), Qt::DisplayRole); int rowCount = m_itemModel->rowCount(); int columnCount = m_itemModel->columnCount(); - if (useModelRows) { + if (mapping->useModelCategories()) { for (int i = 0; i < rowCount; i++) { QBarDataRow *newProxyRow = new QBarDataRow(columnCount); for (int j = 0; j < columnCount; j++) (*newProxyRow)[j].setValue(m_itemModel->index(i, j).data(valueRole).toReal()); newProxyArray->append(newProxyRow); } + // Generate labels from headers if using model rows/columns + for (int i = 0; i < rowCount; i++) + rowLabels << m_itemModel->headerData(i, Qt::Vertical).toString(); + for (int i = 0; i < columnCount; i++) + columnLabels << m_itemModel->headerData(i, Qt::Horizontal).toString(); } else { int rowRole = roleHash.key(mapping->rowRole().toLatin1()); int columnRole = roleHash.key(mapping->columnRole().toLatin1()); - const QStringList &rowList = mapping->rowCategories(); - const QStringList &columnList = mapping->columnCategories(); + + bool generateRows = mapping->autoRowCategories(); + bool generateColumns = mapping->autoColumnCategories(); + QStringList rowList; + QStringList columnList; + // For detecting duplicates in categories generation, using QHashes should be faster than + // simple QStringList::contains() check. + QHash rowListHash; + QHash columnListHash; // Sort values into rows and columns typedef QHash ColumnValueMap; @@ -74,11 +88,30 @@ void BarItemModelHandler::resolveModel() for (int i = 0; i < rowCount; i++) { for (int j = 0; j < columnCount; j++) { QModelIndex index = m_itemModel->index(i, j); - itemValueMap[index.data(rowRole).toString()][index.data(columnRole).toString()] - = index.data(valueRole).toReal(); + QString rowRoleStr = index.data(rowRole).toString(); + QString columnRoleStr = index.data(columnRole).toString(); + itemValueMap[rowRoleStr][columnRoleStr] = index.data(valueRole).toReal(); + if (generateRows && !rowListHash.value(rowRoleStr, false)) { + rowListHash.insert(rowRoleStr, true); + rowList << rowRoleStr; + } + if (generateColumns && !columnListHash.value(columnRoleStr, false)) { + columnListHash.insert(columnRoleStr, true); + columnList << columnRoleStr; + } } } + if (generateRows) + mapping->dptr()->m_rowCategories = rowList; + else + rowList = mapping->rowCategories(); + + if (generateColumns) + mapping->dptr()->m_columnCategories = columnList; + else + columnList = mapping->columnCategories(); + // Create new data array from itemValueMap foreach (QString rowKey, rowList) { QBarDataRow *newProxyRow = new QBarDataRow(columnList.size()); @@ -86,16 +119,9 @@ void BarItemModelHandler::resolveModel() (*newProxyRow)[i].setValue(itemValueMap[rowKey][columnList.at(i)]); newProxyArray->append(newProxyRow); } - } - // Generate labels from headers if using model rows/columns - QStringList rowLabels; - QStringList columnLabels; - if (useModelRows) { - for (int i = 0; i < rowCount; i++) - rowLabels << m_itemModel->headerData(i, Qt::Vertical).toString(); - for (int i = 0; i < columnCount; i++) - columnLabels << m_itemModel->headerData(i, Qt::Horizontal).toString(); + rowLabels = rowList; + columnLabels = columnList; } m_proxy->resetArray(newProxyArray, rowLabels, columnLabels); diff --git a/src/datavisualization/data/qitemmodelbardatamapping.cpp b/src/datavisualization/data/qitemmodelbardatamapping.cpp index 2c47bda4..6a60ff1e 100644 --- a/src/datavisualization/data/qitemmodelbardatamapping.cpp +++ b/src/datavisualization/data/qitemmodelbardatamapping.cpp @@ -27,15 +27,22 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * \since 1.0.0 * * QItemModelBarDataMapping is used to map roles of QAbstractItemModel to rows, columns, and values - * of Q3DBars. There are two ways to use QItemModelBarDataMapping: + * of Q3DBars. There are three ways to use QItemModelBarDataMapping: * - * 1) By default, the QItemModelBarDataMapping will map the rows and columns of QAbstractItemModel - * to rows and columns of Q3DBars, and uses the value returned for Qt::DisplayRole as bar value. + * 1) If useModelCategories property is set to true, QItemModelBarDataMapping will map rows and + * columns of QAbstractItemModel to rows and columns of Q3DBars, and uses the value returned for + * Qt::DisplayRole as bar value by default. * The value role to be used can be redefined if Qt::DisplayRole is not suitable. * * 2) For models that do not have data already neatly sorted into rows and columns, such as - * QAbstractListModel based models, you can define a list of categories for both rows and columns, - * and define a role to map for each of row, column and value. + * QAbstractListModel based models, you can define a role from the model to map for each of row, + * column and value. + * + * 3) If you do not want to include all data contained in the model, or the autogenerated rows and + * columns are not ordered as you wish, you can specify which rows and columns should be included + * and in which order by defining an explicit list of categories for either or both of rows and + * columns. + * * For example, assume that you have a custom QAbstractItemModel for storing various monthly values * related to a business. * Each item in the model has roles "year", "month", "income", and "expenses". @@ -85,6 +92,26 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * The column categories of the mapping. */ +/*! + * \qmlproperty list BarDataMapping::useModelCategories + * When set to true, the mapping ignores row and column roles and categories, and uses + * the rows and columns from the model instead. Defaults to false. + */ + +/*! + * \qmlproperty list BarDataMapping::autoRowCategories + * When set to true, the mapping ignores any explicitly set row categories + * and overwrites them with automatically generated ones whenever the + * data from model is resolved. Defaults to true. + */ + +/*! + * \qmlproperty list BarDataMapping::autoColumnCategories + * When set to true, the mapping ignores any explicitly set column categories + * and overwrites them with automatically generated ones whenever the + * data from model is resolved. Defaults to true. + */ + /*! * Constructs QItemModelBarDataMapping with the given \a parent. */ @@ -95,16 +122,35 @@ QItemModelBarDataMapping::QItemModelBarDataMapping(QObject *parent) /*! * Constructs QItemModelBarDataMapping with \a valueRole and the given \a parent. + * This constructor is meant to be used with models that have data properly sorted + * in rows and columns already, so it also sets useModelCategories property to true. */ QItemModelBarDataMapping::QItemModelBarDataMapping(const QString &valueRole, QObject *parent) : QAbstractDataMapping(new QItemModelBarDataMappingPrivate(this), parent) { dptr()->m_valueRole = valueRole; + dptr()->m_useModelCategories = true; +} + +/*! + * Constructs QItemModelBarDataMapping with \a rowRole, \a columnRole, \a valueRole + * and the given \a parent. + */ +QItemModelBarDataMapping::QItemModelBarDataMapping(const QString &rowRole, + const QString &columnRole, + const QString &valueRole, + QObject *parent) + : QAbstractDataMapping(new QItemModelBarDataMappingPrivate(this), parent) +{ + dptr()->m_rowRole = rowRole; + dptr()->m_columnRole = columnRole; + dptr()->m_valueRole = valueRole; } /*! * Constructs QItemModelBarDataMapping with \a rowRole, \a columnRole, \a valueRole, - * \a rowCategories, \a columnCategories and the given \a parent. + * \a rowCategories, \a columnCategories and the given \a parent. This constructor + * also sets autoRowCategories and autoColumnCategories to false. */ QItemModelBarDataMapping::QItemModelBarDataMapping(const QString &rowRole, const QString &columnRole, @@ -119,6 +165,8 @@ QItemModelBarDataMapping::QItemModelBarDataMapping(const QString &rowRole, dptr()->m_valueRole = valueRole; dptr()->m_rowCategories = rowCategories; dptr()->m_columnCategories = columnCategories; + dptr()->m_autoRowCategories = false; + dptr()->m_autoColumnCategories = false; } /*! @@ -135,8 +183,10 @@ QItemModelBarDataMapping::~QItemModelBarDataMapping() */ void QItemModelBarDataMapping::setRowRole(const QString &role) { - dptr()->m_rowRole = role; - emit mappingChanged(); + if (dptr()->m_rowRole != role) { + dptr()->m_rowRole = role; + emit mappingChanged(); + } } QString QItemModelBarDataMapping::rowRole() const @@ -151,8 +201,10 @@ QString QItemModelBarDataMapping::rowRole() const */ void QItemModelBarDataMapping::setColumnRole(const QString &role) { - dptr()->m_columnRole = role; - emit mappingChanged(); + if (dptr()->m_columnRole != role) { + dptr()->m_columnRole = role; + emit mappingChanged(); + } } QString QItemModelBarDataMapping::columnRole() const @@ -167,8 +219,10 @@ QString QItemModelBarDataMapping::columnRole() const */ void QItemModelBarDataMapping::setValueRole(const QString &role) { - dptr()->m_valueRole = role; - emit mappingChanged(); + if (dptr()->m_valueRole != role) { + dptr()->m_valueRole = role; + emit mappingChanged(); + } } QString QItemModelBarDataMapping::valueRole() const @@ -183,8 +237,10 @@ QString QItemModelBarDataMapping::valueRole() const */ void QItemModelBarDataMapping::setRowCategories(const QStringList &categories) { - dptr()->m_rowCategories = categories; - emit mappingChanged(); + if (dptr()->m_rowCategories != categories) { + dptr()->m_rowCategories = categories; + emit mappingChanged(); + } } QStringList QItemModelBarDataMapping::rowCategories() const @@ -199,8 +255,10 @@ QStringList QItemModelBarDataMapping::rowCategories() const */ void QItemModelBarDataMapping::setColumnCategories(const QStringList &categories) { - dptr()->m_columnCategories = categories; - emit mappingChanged(); + if (dptr()->m_columnCategories != categories) { + dptr()->m_columnCategories = categories; + emit mappingChanged(); + } } QStringList QItemModelBarDataMapping::columnCategories() const @@ -208,6 +266,65 @@ QStringList QItemModelBarDataMapping::columnCategories() const return dptrc()->m_columnCategories; } +/*! + * \property QItemModelBarDataMapping::useModelCategories + * + * When set to true, the mapping ignores row and column roles and categories, and uses + * the rows and columns from the model instead. Defaults to false. + */ +void QItemModelBarDataMapping::setUseModelCategories(bool enable) +{ + if (dptr()->m_useModelCategories != enable) { + dptr()->m_useModelCategories = enable; + emit mappingChanged(); + } +} + +bool QItemModelBarDataMapping::useModelCategories() const +{ + return dptrc()->m_useModelCategories; +} + +/*! + * \property QItemModelBarDataMapping::autoRowCategories + * + * When set to true, the mapping ignores any explicitly set row categories + * and overwrites them with automatically generated ones whenever the + * data from model is resolved. Defaults to true. + */ +void QItemModelBarDataMapping::setAutoRowCategories(bool enable) +{ + if (dptr()->m_autoRowCategories != enable) { + dptr()->m_autoRowCategories = enable; + emit mappingChanged(); + } +} + +bool QItemModelBarDataMapping::autoRowCategories() const +{ + return dptrc()->m_autoRowCategories; +} + +/*! + * \property QItemModelBarDataMapping::autoColumnCategories + * + * When set to true, the mapping ignores any explicitly set column categories + * and overwrites them with automatically generated ones whenever the + * data from model is resolved. Defaults to true. + */ +void QItemModelBarDataMapping::setAutoColumnCategories(bool enable) +{ + if (dptr()->m_autoColumnCategories != enable) { + dptr()->m_autoColumnCategories = enable; + emit mappingChanged(); + } +} + +bool QItemModelBarDataMapping::autoColumnCategories() const +{ + return dptrc()->m_autoColumnCategories; +} + /*! * Changes \a rowRole, \a columnRole, \a valueRole, \a rowCategories and \a columnCategories to the * mapping. @@ -229,7 +346,9 @@ void QItemModelBarDataMapping::remap(const QString &rowRole, /*! * /return index of the specified \a category in row categories list. - * If the category is not found, -1 is returned. + * If the row categories list is empty, -1 is returned. + * \note If the automatic row categories generation is in use, this method will + * not return valid index before the data in the model is resolved for the first time. */ int QItemModelBarDataMapping::rowCategoryIndex(const QString &category) { @@ -239,6 +358,8 @@ int QItemModelBarDataMapping::rowCategoryIndex(const QString &category) /*! * /return index of the specified \a category in column categories list. * If the category is not found, -1 is returned. + * \note If the automatic column categories generation is in use, this method will + * not return valid index before the data in the model is resolved for the first time. */ int QItemModelBarDataMapping::columnCategoryIndex(const QString &category) { @@ -264,7 +385,10 @@ const QItemModelBarDataMappingPrivate *QItemModelBarDataMapping::dptrc() const // QItemModelBarDataMappingPrivate QItemModelBarDataMappingPrivate::QItemModelBarDataMappingPrivate(QItemModelBarDataMapping *q) - : QAbstractDataMappingPrivate(q, QAbstractDataProxy::DataTypeBar) + : QAbstractDataMappingPrivate(q, QAbstractDataProxy::DataTypeBar), + m_useModelCategories(false), + m_autoRowCategories(true), + m_autoColumnCategories(true) { } diff --git a/src/datavisualization/data/qitemmodelbardatamapping.h b/src/datavisualization/data/qitemmodelbardatamapping.h index a781f020..a5ef33b8 100644 --- a/src/datavisualization/data/qitemmodelbardatamapping.h +++ b/src/datavisualization/data/qitemmodelbardatamapping.h @@ -35,10 +35,15 @@ class QT_DATAVISUALIZATION_EXPORT QItemModelBarDataMapping : public QAbstractDat Q_PROPERTY(QString valueRole READ valueRole WRITE setValueRole) Q_PROPERTY(QStringList rowCategories READ rowCategories WRITE setRowCategories) Q_PROPERTY(QStringList columnCategories READ columnCategories WRITE setColumnCategories) + Q_PROPERTY(bool useModelCategories READ useModelCategories WRITE setUseModelCategories) + Q_PROPERTY(bool autoRowCategories READ autoRowCategories WRITE setAutoRowCategories) + Q_PROPERTY(bool autoColumnCategories READ autoColumnCategories WRITE setAutoColumnCategories) public: explicit QItemModelBarDataMapping(QObject *parent = 0); QItemModelBarDataMapping(const QString &valueRole, QObject *parent = 0); + QItemModelBarDataMapping(const QString &rowRole, const QString &columnRole, + const QString &valueRole, QObject *parent = 0); QItemModelBarDataMapping(const QString &rowRole, const QString &columnRole, const QString &valueRole, const QStringList &rowCategories, const QStringList &columnCategories, QObject *parent = 0); @@ -56,6 +61,13 @@ public: void setColumnCategories(const QStringList &categories); QStringList columnCategories() const; + void setUseModelCategories(bool enable); + bool useModelCategories() const; + void setAutoRowCategories(bool enable); + bool autoRowCategories() const; + void setAutoColumnCategories(bool enable); + bool autoColumnCategories() const; + void remap(const QString &rowRole, const QString &columnRole, const QString &valueRole, const QStringList &rowCategories, const QStringList &columnCategories); @@ -69,6 +81,8 @@ protected: private: Q_DISABLE_COPY(QItemModelBarDataMapping) + + friend class BarItemModelHandler; }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/data/qitemmodelbardatamapping_p.h b/src/datavisualization/data/qitemmodelbardatamapping_p.h index b444e384..90a17fdb 100644 --- a/src/datavisualization/data/qitemmodelbardatamapping_p.h +++ b/src/datavisualization/data/qitemmodelbardatamapping_p.h @@ -50,7 +50,12 @@ private: QStringList m_rowCategories; QStringList m_columnCategories; + bool m_useModelCategories; + bool m_autoRowCategories; + bool m_autoColumnCategories; + friend class QItemModelBarDataMapping; + friend class BarItemModelHandler; }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/data/qitemmodelscatterdatamapping_p.h b/src/datavisualization/data/qitemmodelscatterdatamapping_p.h index 9a8c1b6e..62ff42b4 100644 --- a/src/datavisualization/data/qitemmodelscatterdatamapping_p.h +++ b/src/datavisualization/data/qitemmodelscatterdatamapping_p.h @@ -26,12 +26,12 @@ // // We mean it. -#include "qitemmodelscatterdatamapping.h" -#include "qabstractdatamapping_p.h" - #ifndef QITEMMODELSCATTERDATAMAPPING_P_H #define QITEMMODELSCATTERDATAMAPPING_P_H +#include "qitemmodelscatterdatamapping.h" +#include "qabstractdatamapping_p.h" + QT_DATAVISUALIZATION_BEGIN_NAMESPACE class QItemModelScatterDataMappingPrivate : public QAbstractDataMappingPrivate diff --git a/src/datavisualization/data/qitemmodelsurfacedatamapping.cpp b/src/datavisualization/data/qitemmodelsurfacedatamapping.cpp index 79f310ea..2fd66b30 100644 --- a/src/datavisualization/data/qitemmodelsurfacedatamapping.cpp +++ b/src/datavisualization/data/qitemmodelsurfacedatamapping.cpp @@ -26,7 +26,30 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * \brief Data model mapping for Q3DSurface. * \since 1.0.0 * - * DOCUMENTATION GOES HERE + * QItemModelSurfaceDataMapping is used to map roles of QAbstractItemModel to rows, columns, and values + * of Q3DSurface. There are three ways to use QItemModelSurfaceDataMapping: + * + * 1) If useModelCategories property is set to true, QItemModelSurfaceDataMapping will map rows and + * columns of QAbstractItemModel to rows and columns of Q3DSurface, and uses the value returned for + * Qt::DisplayRole as bar value by default. + * The value role to be used can be redefined if Qt::DisplayRole is not suitable. + * + * 2) For models that do not have data already neatly sorted into rows and columns, such as + * QAbstractListModel based models, you can define a role from the model to map for each of row, + * column and value. + * + * 3) If you do not want to include all data contained in the model, or the autogenerated rows and + * columns are not ordered as you wish, you can specify which rows and columns should be included + * and in which order by defining an explicit list of categories for either or both of rows and + * columns. + * + * For example, assume that you have a custom QAbstractItemModel storing surface topography data. + * Each item in the model has roles "longitude", "latitude" and "height". The item model already + * contains the data properly sorted so that longitudes and latitudes are first encountered in + * correct order, which enables us to utilize the row and column category autogeneration. + * You could do the following to display the data in a surface graph: + * + * \snippet doc_src_qtdatavisualization.cpp 5 * * \sa QItemModelSurfaceDataProxy */ @@ -70,6 +93,26 @@ QT_DATAVISUALIZATION_BEGIN_NAMESPACE * The column categories of the mapping. */ +/*! + * \qmlproperty list SurfaceDataMapping::useModelCategories + * When set to true, the mapping ignores row and column roles and categories, and uses + * the rows and columns from the model instead. Defaults to false. + */ + +/*! + * \qmlproperty list SurfaceDataMapping::autoRowCategories + * When set to true, the mapping ignores any explicitly set row categories + * and overwrites them with automatically generated ones whenever the + * data from model is resolved. Defaults to true. + */ + +/*! + * \qmlproperty list SurfaceDataMapping::autoColumnCategories + * When set to true, the mapping ignores any explicitly set column categories + * and overwrites them with automatically generated ones whenever the + * data from model is resolved. Defaults to true. + */ + /*! * Constructs QItemModelSurfaceDataMapping with the given \a parent. */ @@ -80,16 +123,35 @@ QItemModelSurfaceDataMapping::QItemModelSurfaceDataMapping(QObject *parent) /*! * Constructs QItemModelSurfaceDataMapping with \a valueRole and the given \a parent. + * This constructor is meant to be used with models that have data properly sorted + * in rows and columns already, so it also sets useModelCategories property to true. */ QItemModelSurfaceDataMapping::QItemModelSurfaceDataMapping(const QString &valueRole, QObject *parent) : QAbstractDataMapping(new QItemModelSurfaceDataMappingPrivate(this), parent) { dptr()->m_valueRole = valueRole; + dptr()->m_useModelCategories = true; +} + +/*! + * Constructs QItemModelSurfaceDataMapping with \a rowRole, \a columnRole, \a valueRole + * and the given \a parent. + */ +QItemModelSurfaceDataMapping::QItemModelSurfaceDataMapping(const QString &rowRole, + const QString &columnRole, + const QString &valueRole, + QObject *parent) + : QAbstractDataMapping(new QItemModelSurfaceDataMappingPrivate(this), parent) +{ + dptr()->m_rowRole = rowRole; + dptr()->m_columnRole = columnRole; + dptr()->m_valueRole = valueRole; } /*! * Constructs QItemModelSurfaceDataMapping with \a rowRole, \a columnRole, \a valueRole, - * \a rowCategories, \a columnCategories and the given \a parent. + * \a rowCategories, \a columnCategories and the given \a parent. This constructor + * also sets autoRowCategories and autoColumnCategories to false. */ QItemModelSurfaceDataMapping::QItemModelSurfaceDataMapping(const QString &rowRole, const QString &columnRole, @@ -104,6 +166,8 @@ QItemModelSurfaceDataMapping::QItemModelSurfaceDataMapping(const QString &rowRol dptr()->m_valueRole = valueRole; dptr()->m_rowCategories = rowCategories; dptr()->m_columnCategories = columnCategories; + dptr()->m_autoRowCategories = false; + dptr()->m_autoColumnCategories = false; } /*! @@ -120,8 +184,10 @@ QItemModelSurfaceDataMapping::~QItemModelSurfaceDataMapping() */ void QItemModelSurfaceDataMapping::setRowRole(const QString &role) { - dptr()->m_rowRole = role; - emit mappingChanged(); + if (dptr()->m_rowRole != role) { + dptr()->m_rowRole = role; + emit mappingChanged(); + } } QString QItemModelSurfaceDataMapping::rowRole() const @@ -136,8 +202,10 @@ QString QItemModelSurfaceDataMapping::rowRole() const */ void QItemModelSurfaceDataMapping::setColumnRole(const QString &role) { - dptr()->m_columnRole = role; - emit mappingChanged(); + if (dptr()->m_columnRole != role) { + dptr()->m_columnRole = role; + emit mappingChanged(); + } } QString QItemModelSurfaceDataMapping::columnRole() const @@ -152,8 +220,10 @@ QString QItemModelSurfaceDataMapping::columnRole() const */ void QItemModelSurfaceDataMapping::setValueRole(const QString &role) { - dptr()->m_valueRole = role; - emit mappingChanged(); + if (dptr()->m_valueRole != role) { + dptr()->m_valueRole = role; + emit mappingChanged(); + } } QString QItemModelSurfaceDataMapping::valueRole() const @@ -168,8 +238,10 @@ QString QItemModelSurfaceDataMapping::valueRole() const */ void QItemModelSurfaceDataMapping::setRowCategories(const QStringList &categories) { - dptr()->m_rowCategories = categories; - emit mappingChanged(); + if (dptr()->m_rowCategories != categories) { + dptr()->m_rowCategories = categories; + emit mappingChanged(); + } } QStringList QItemModelSurfaceDataMapping::rowCategories() const @@ -184,8 +256,10 @@ QStringList QItemModelSurfaceDataMapping::rowCategories() const */ void QItemModelSurfaceDataMapping::setColumnCategories(const QStringList &categories) { - dptr()->m_columnCategories = categories; - emit mappingChanged(); + if (dptr()->m_columnCategories != categories) { + dptr()->m_columnCategories = categories; + emit mappingChanged(); + } } QStringList QItemModelSurfaceDataMapping::columnCategories() const @@ -193,6 +267,65 @@ QStringList QItemModelSurfaceDataMapping::columnCategories() const return dptrc()->m_columnCategories; } +/*! + * \property QItemModelSurfaceDataMapping::useModelCategories + * + * When set to true, the mapping ignores row and column roles and categories, and uses + * the rows and columns from the model instead. Defaults to false. + */ +void QItemModelSurfaceDataMapping::setUseModelCategories(bool enable) +{ + if (dptr()->m_useModelCategories != enable) { + dptr()->m_useModelCategories = enable; + emit mappingChanged(); + } +} + +bool QItemModelSurfaceDataMapping::useModelCategories() const +{ + return dptrc()->m_useModelCategories; +} + +/*! + * \property QItemModelSurfaceDataMapping::autoRowCategories + * + * When set to true, the mapping ignores any explicitly set row categories + * and overwrites them with automatically generated ones whenever the + * data from model is resolved. Defaults to true. + */ +void QItemModelSurfaceDataMapping::setAutoRowCategories(bool enable) +{ + if (dptr()->m_autoRowCategories != enable) { + dptr()->m_autoRowCategories = enable; + emit mappingChanged(); + } +} + +bool QItemModelSurfaceDataMapping::autoRowCategories() const +{ + return dptrc()->m_autoRowCategories; +} + +/*! + * \property QItemModelSurfaceDataMapping::autoColumnCategories + * + * When set to true, the mapping ignores any explicitly set column categories + * and overwrites them with automatically generated ones whenever the + * data from model is resolved. Defaults to true. + */ +void QItemModelSurfaceDataMapping::setAutoColumnCategories(bool enable) +{ + if (dptr()->m_autoColumnCategories != enable) { + dptr()->m_autoColumnCategories = enable; + emit mappingChanged(); + } +} + +bool QItemModelSurfaceDataMapping::autoColumnCategories() const +{ + return dptrc()->m_autoColumnCategories; +} + /*! * Changes \a rowRole, \a columnRole, \a valueRole, \a rowCategories and \a columnCategories to the * mapping. @@ -214,7 +347,9 @@ void QItemModelSurfaceDataMapping::remap(const QString &rowRole, /*! * /return index of the specified \a category in row categories list. - * If the category is not found, -1 is returned. + * If the row categories list is empty, -1 is returned. + * \note If the automatic row categories generation is in use, this method will + * not return valid index before the data in the model is resolved for the first time. */ int QItemModelSurfaceDataMapping::rowCategoryIndex(const QString &category) { @@ -223,7 +358,8 @@ int QItemModelSurfaceDataMapping::rowCategoryIndex(const QString &category) /*! * /return index of the specified \a category in column categories list. - * If the category is not found, -1 is returned. + * \note If the automatic column categories generation is in use, this method will + * not return valid index before the data in the model is resolved for the first time. */ int QItemModelSurfaceDataMapping::columnCategoryIndex(const QString &category) { @@ -249,7 +385,10 @@ const QItemModelSurfaceDataMappingPrivate *QItemModelSurfaceDataMapping::dptrc() // QItemModelSurfaceDataMappingPrivate QItemModelSurfaceDataMappingPrivate::QItemModelSurfaceDataMappingPrivate(QItemModelSurfaceDataMapping *q) - : QAbstractDataMappingPrivate(q, QAbstractDataProxy::DataTypeSurface) + : QAbstractDataMappingPrivate(q, QAbstractDataProxy::DataTypeSurface), + m_useModelCategories(false), + m_autoRowCategories(true), + m_autoColumnCategories(true) { } diff --git a/src/datavisualization/data/qitemmodelsurfacedatamapping.h b/src/datavisualization/data/qitemmodelsurfacedatamapping.h index 1d937806..7e8817bf 100644 --- a/src/datavisualization/data/qitemmodelsurfacedatamapping.h +++ b/src/datavisualization/data/qitemmodelsurfacedatamapping.h @@ -35,10 +35,15 @@ class QT_DATAVISUALIZATION_EXPORT QItemModelSurfaceDataMapping : public QAbstrac Q_PROPERTY(QString valueRole READ valueRole WRITE setValueRole) Q_PROPERTY(QStringList rowCategories READ rowCategories WRITE setRowCategories) Q_PROPERTY(QStringList columnCategories READ columnCategories WRITE setColumnCategories) + Q_PROPERTY(bool useModelCategories READ useModelCategories WRITE setUseModelCategories) + Q_PROPERTY(bool autoRowCategories READ autoRowCategories WRITE setAutoRowCategories) + Q_PROPERTY(bool autoColumnCategories READ autoColumnCategories WRITE setAutoColumnCategories) public: explicit QItemModelSurfaceDataMapping(QObject *parent = 0); QItemModelSurfaceDataMapping(const QString &valueRole, QObject *parent = 0); + QItemModelSurfaceDataMapping(const QString &rowRole, const QString &columnRole, + const QString &valueRole, QObject *parent = 0); QItemModelSurfaceDataMapping(const QString &rowRole, const QString &columnRole, const QString &valueRole, const QStringList &rowCategories, const QStringList &columnCategories, QObject *parent = 0); @@ -56,6 +61,13 @@ public: void setColumnCategories(const QStringList &categories); QStringList columnCategories() const; + void setUseModelCategories(bool enable); + bool useModelCategories() const; + void setAutoRowCategories(bool enable); + bool autoRowCategories() const; + void setAutoColumnCategories(bool enable); + bool autoColumnCategories() const; + void remap(const QString &rowRole, const QString &columnRole, const QString &valueRole, const QStringList &rowCategories, const QStringList &columnCategories); @@ -69,6 +81,8 @@ protected: private: Q_DISABLE_COPY(QItemModelSurfaceDataMapping) + + friend class SurfaceItemModelHandler; }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/data/qitemmodelsurfacedatamapping_p.h b/src/datavisualization/data/qitemmodelsurfacedatamapping_p.h index 69470423..9896f868 100644 --- a/src/datavisualization/data/qitemmodelsurfacedatamapping_p.h +++ b/src/datavisualization/data/qitemmodelsurfacedatamapping_p.h @@ -50,7 +50,12 @@ private: QStringList m_rowCategories; QStringList m_columnCategories; + bool m_useModelCategories; + bool m_autoRowCategories; + bool m_autoColumnCategories; + friend class QItemModelSurfaceDataMapping; + friend class SurfaceItemModelHandler; }; QT_DATAVISUALIZATION_END_NAMESPACE diff --git a/src/datavisualization/data/surfaceitemmodelhandler.cpp b/src/datavisualization/data/surfaceitemmodelhandler.cpp index 911e2995..124fe640 100644 --- a/src/datavisualization/data/surfaceitemmodelhandler.cpp +++ b/src/datavisualization/data/surfaceitemmodelhandler.cpp @@ -17,7 +17,7 @@ ****************************************************************************/ #include "surfaceitemmodelhandler_p.h" -#include +#include "qitemmodelsurfacedatamapping_p.h" QT_DATAVISUALIZATION_BEGIN_NAMESPACE @@ -40,22 +40,21 @@ void SurfaceItemModelHandler::resolveModel() return; } - bool useModelRows(false); - if (!mapping->rowCategories().size() || !mapping->columnCategories().size()) { - useModelRows = true; - } else if (mapping->rowRole().isEmpty() || mapping->columnRole().isEmpty()) { + if (!mapping->useModelCategories() + && (mapping->rowRole().isEmpty() || mapping->columnRole().isEmpty())) { m_proxy->resetArray(0); return; } QSurfaceDataArray *newProxyArray = new QSurfaceDataArray; QHash roleHash = m_itemModel->roleNames(); + // Default to display role if no mapping int valueRole = roleHash.key(mapping->valueRole().toLatin1(), Qt::DisplayRole); int rowCount = m_itemModel->rowCount(); int columnCount = m_itemModel->columnCount(); - if (useModelRows) { + if (mapping->useModelCategories()) { for (int i = 0; i < rowCount; i++) { QSurfaceDataRow *newProxyRow = new QSurfaceDataRow(columnCount); for (int j = 0; j < columnCount; j++) @@ -65,8 +64,15 @@ void SurfaceItemModelHandler::resolveModel() } else { int rowRole = roleHash.key(mapping->rowRole().toLatin1()); int columnRole = roleHash.key(mapping->columnRole().toLatin1()); - const QStringList &rowList = mapping->rowCategories(); - const QStringList &columnList = mapping->columnCategories(); + + bool generateRows = mapping->autoRowCategories(); + bool generateColumns = mapping->autoColumnCategories(); + QStringList rowList; + QStringList columnList; + // For detecting duplicates in categories generation, using QHashes should be faster than + // simple QStringList::contains() check. + QHash rowListHash; + QHash columnListHash; // Sort values into rows and columns typedef QHash ColumnValueMap; @@ -74,11 +80,30 @@ void SurfaceItemModelHandler::resolveModel() for (int i = 0; i < rowCount; i++) { for (int j = 0; j < columnCount; j++) { QModelIndex index = m_itemModel->index(i, j); - itemValueMap[index.data(rowRole).toString()][index.data(columnRole).toString()] - = index.data(valueRole).toReal(); + QString rowRoleStr = index.data(rowRole).toString(); + QString columnRoleStr = index.data(columnRole).toString(); + itemValueMap[rowRoleStr][columnRoleStr] = index.data(valueRole).toReal(); + if (generateRows && !rowListHash.value(rowRoleStr, false)) { + rowListHash.insert(rowRoleStr, true); + rowList << rowRoleStr; + } + if (generateColumns && !columnListHash.value(columnRoleStr, false)) { + columnListHash.insert(columnRoleStr, true); + columnList << columnRoleStr; + } } } + if (generateRows) + mapping->dptr()->m_rowCategories = rowList; + else + rowList = mapping->rowCategories(); + + if (generateColumns) + mapping->dptr()->m_columnCategories = columnList; + else + columnList = mapping->columnCategories(); + // Create new data array from itemValueMap foreach (QString rowKey, rowList) { QSurfaceDataRow *newProxyRow = new QSurfaceDataRow(columnList.size()); diff --git a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp index 12496168..f4446e17 100644 --- a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp +++ b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp @@ -93,8 +93,6 @@ SurfaceDataMapping { rowRole: "latitude" columnRole: "longitude" valueRole: "population" - rowCategories: ["64.5", "64.6", "64.7", "64.8", "64.9", "65.0", "65.1", "65.2", "65.3", "65.4"] - columnCategories: ["24.5", "24.75", "25.0", "25.25", "25.5", "25.75", "26.0", "26.25", "26.5", "26.75"] } //! [6] diff --git a/src/datavisualization/doc/snippets/doc_src_qtdatavisualization.cpp b/src/datavisualization/doc/snippets/doc_src_qtdatavisualization.cpp index f4df32d5..54f64ef0 100644 --- a/src/datavisualization/doc/snippets/doc_src_qtdatavisualization.cpp +++ b/src/datavisualization/doc/snippets/doc_src_qtdatavisualization.cpp @@ -65,5 +65,14 @@ proxy->activeMapping()->setValueRole(QStringLiteral("expenses")); QItemModelScatterDataMapping *mapping = new QItemModelScatterDataMapping(QStringLiteral("density"), QStringLiteral("hardness"), QStringLiteral("conductivity")) + QItemModelScatterDataProxy *proxy = new QItemModelScatterDataProxy(customModel, mapping); //! [4] + +//! [5] +QItemModelSurfaceDataMapping *mapping = new QItemModelSurfaceDataMapping(QStringLiteral("longitude"), // Row role + QStringLiteral("latitude"), // Column role + QStringLiteral("height")); // value role + +QItemModelSurfaceDataProxy *proxy = new QItemModelSurfaceDataProxy(customModel, mapping); +//! [5] diff --git a/src/datavisualizationqml2/declarativebars.cpp b/src/datavisualizationqml2/declarativebars.cpp index 7eee3e6d..010409e8 100644 --- a/src/datavisualizationqml2/declarativebars.cpp +++ b/src/datavisualizationqml2/declarativebars.cpp @@ -42,6 +42,8 @@ DeclarativeBars::DeclarativeBars(QQuickItem *parent) m_shared = new Bars3DController(boundingRect().toRect()); QObject::connect(m_shared, &Abstract3DController::shadowQualityChanged, this, &DeclarativeBars::handleShadowQualityUpdate); + QObject::connect(m_shared, &Bars3DController::selectedBarPosChanged, this, + &DeclarativeBars::selectedBarPosChanged); QItemModelBarDataProxy *proxy = new QItemModelBarDataProxy; m_shared->setActiveDataProxy(proxy); diff --git a/src/datavisualizationqml2/declarativebars_p.h b/src/datavisualizationqml2/declarativebars_p.h index a85bc2da..ebf565fe 100644 --- a/src/datavisualizationqml2/declarativebars_p.h +++ b/src/datavisualizationqml2/declarativebars_p.h @@ -67,7 +67,7 @@ class DeclarativeBars : public QQuickItem Q_PROPERTY(int rows READ rows WRITE setRows) Q_PROPERTY(int columns READ columns WRITE setColumns) Q_PROPERTY(QString itemLabelFormat READ itemLabelFormat WRITE setItemLabelFormat) - Q_PROPERTY(QPointF selectedBarPos READ selectedBarPos WRITE setSelectedBarPos) + Q_PROPERTY(QPointF selectedBarPos READ selectedBarPos WRITE setSelectedBarPos NOTIFY selectedBarPosChanged) Q_ENUMS(QtDataVisualization::QDataVis::SelectionMode) Q_ENUMS(QtDataVisualization::QDataVis::ShadowQuality) Q_ENUMS(QtDataVisualization::QDataVis::LabelTransparency) @@ -174,6 +174,7 @@ public: signals: // Signals shadow quality changes. void shadowQualityChanged(QDataVis::ShadowQuality quality); + void selectedBarPosChanged(const QPointF &position); protected: Bars3DController *m_shared; -- cgit v1.2.3