// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qitemmodelscatterdataproxy_p.h" #include "scatteritemmodelhandler_p.h" QT_BEGIN_NAMESPACE /*! * \class QItemModelScatterDataProxy * \inmodule QtGraphs * \ingroup graphs_3D * \brief Proxy class for presenting data in item models with Q3DScatterWidgetItem. * * QItemModelScatterDataProxy allows you to use QAbstractItemModel derived * models as a data source for Q3DScatterWidgetItem. It maps roles of QAbstractItemModel * to the XYZ-values of Q3DScatterWidgetItem points. * * The data is resolved asynchronously whenever the mapping or the model * changes. QScatterDataProxy::arrayReset() is emitted when the data has been * resolved. However, inserts, removes, and single data item changes after the * model initialization are resolved synchronously, unless the same frame also * contains a change that causes the whole model to be resolved. * * Mapping ignores rows and columns of the QAbstractItemModel and treats * all items equally. It requires the model to provide roles for the data items * that can be mapped to X, Y, and Z-values for the scatter points. * * For example, assume that you have a custom QAbstractItemModel for storing * various measurements done on material samples, providing data for roles such * as "density", "hardness", and "conductivity". You could visualize these * properties on a scatter graph using this proxy: * * \snippet doc_src_qtgraphs.cpp scattermodelproxy * * If the fields of the model do not contain the data in the exact format you * need, you can specify a search pattern regular expression and a replace rule * for each role to get the value in a format you need. For more information on * how the replacement using regular expressions works, see the * QString::replace(const QRegularExpression &rx, const QString &after) * function documentation. Note that using regular expressions has an impact on * performance, so it's more efficient to utilize item models where doing search * and replace is not necessary to get the desired values. * * For example about using the search patterns in conjunction with the roles, * see ItemModelBarDataProxy usage in \l{Simple Bar Graph}. * * \sa {Qt Graphs Data Handling with 3D} */ /*! * \qmltype ItemModelScatterDataProxy * \inqmlmodule QtGraphs * \ingroup graphs_qml_3D * \nativetype QItemModelScatterDataProxy * \inherits ScatterDataProxy * \brief Proxy class for presenting data in item models with Scatter3D. * * This type allows you to use AbstractItemModel derived models as a data source * for Scatter3D. * * The data is resolved asynchronously whenever the mapping or the model * changes. QScatterDataProxy::arrayReset() is emitted when the data has been * resolved. * * For more details, see QItemModelScatterDataProxy documentation. * * Usage example: * * \snippet doc_src_qmlgraphs.cpp 8 * * \sa ScatterDataProxy, {Qt Graphs Data Handling with 3D} */ /*! * \qmlproperty model ItemModelScatterDataProxy::itemModel * The item model to use as a data source for Scatter3D. */ /*! * \qmlproperty string ItemModelScatterDataProxy::xPosRole * The item model role to map into the X position. */ /*! * \qmlproperty string ItemModelScatterDataProxy::yPosRole * The item model role to map into the Y position. */ /*! * \qmlproperty string ItemModelScatterDataProxy::zPosRole * The item model role to map into the Z position. */ /*! * \qmlproperty string ItemModelScatterDataProxy::rotationRole * * The item model role to map into item rotation. * The model may supply the value for rotation as either variant that is * directly convertible to \l [QtQuick] quaternion, or as one of the string * representations: \c{"scalar,x,y,z"} or \c{"@angle,x,y,z"}. The first format * will construct the \l [QtQuick] quaternion directly with given values, and * the second one will construct the \l [QtQuick] quaternion using * QQuaternion::fromAxisAndAngle() method. */ /*! * \qmlproperty regExp ItemModelScatterDataProxy::xPosRolePattern * * When set, a search and replace is done on the value mapped by the x-position * role before it is used as * an item position value. This property specifies the regular expression to * find the portion of the mapped value to replace, and xPosRoleReplace property * contains the replacement string. * * \sa xPosRole, xPosRoleReplace */ /*! * \qmlproperty regExp ItemModelScatterDataProxy::yPosRolePattern * * When set, a search and replace is done on the value mapped by the y-position * role before it is used as * an item position value. This property specifies the regular expression to * find the portion of the mapped value to replace, and yPosRoleReplace property * contains the replacement string. * * \sa yPosRole, yPosRoleReplace */ /*! * \qmlproperty regExp ItemModelScatterDataProxy::zPosRolePattern * * When set, a search and replace is done on the value mapped by the z-position * role before it is used as * an item position value. This property specifies the regular expression to * find the portion of the mapped value to replace, and zPosRoleReplace property * contains the replacement string. * * \sa zPosRole, zPosRoleReplace */ /*! * \qmlproperty regExp ItemModelScatterDataProxy::rotationRolePattern * When set, a search and replace is done on the value mapped by the rotation * role before it is used * as item rotation. This property specifies the regular expression to find the * portion of the mapped value to replace, and rotationRoleReplace property * contains the replacement string. * * \sa rotationRole, rotationRoleReplace */ /*! * \qmlproperty string ItemModelScatterDataProxy::xPosRoleReplace * * This property defines the replacement content to be used in conjunction with * xPosRolePattern. Defaults to an empty string. For more information on how the * search and replace using regular expressions works, see * the QString::replace(const QRegularExpression &rx, const QString &after) * function documentation. * * \sa xPosRole, xPosRolePattern */ /*! * \qmlproperty string ItemModelScatterDataProxy::yPosRoleReplace * * This property defines the replacement content to be used in conjunction with * yPosRolePattern. Defaults to an empty string. For more information on how the * search and replace using regular expressions works, see * the QString::replace(const QRegularExpression &rx, const QString &after) * function documentation. * * \sa yPosRole, yPosRolePattern */ /*! * \qmlproperty string ItemModelScatterDataProxy::zPosRoleReplace * * This property defines the replacement content to be used in conjunction with * zPosRolePattern. Defaults to an empty string. For more information on how the * search and replace using regular expressions works, see * the QString::replace(const QRegularExpression &rx, const QString &after) * function documentation. * * \sa zPosRole, zPosRolePattern */ /*! * \qmlproperty string ItemModelScatterDataProxy::rotationRoleReplace * This property defines the replacement content to be used in conjunction with * rotationRolePattern. Defaults to an empty string. For more information on how * the search and replace using regular expressions works, see * the QString::replace(const QRegularExpression &rx, const QString &after) * function documentation. * * \sa rotationRole, rotationRolePattern */ /*! \qmlsignal ItemModelScatterDataProxy::itemModelChanged(model itemModel) This signal is emitted when itemModel changes to \a itemModel. */ /*! \qmlsignal ItemModelScatterDataProxy::xPosRoleChanged(string role) This signal is emitted when xPosRole changes to \a role. */ /*! \qmlsignal ItemModelScatterDataProxy::yPosRoleChanged(string role) This signal is emitted when yPosRole changes to \a role. */ /*! \qmlsignal ItemModelScatterDataProxy::zPosRoleChanged(string role) This signal is emitted when zPosRole changes to \a role. */ /*! \qmlsignal ItemModelScatterDataProxy::rotationRoleChanged(string role) This signal is emitted when rotationRole changes to \a role. */ /*! \qmlsignal ItemModelScatterDataProxy::xPosRolePatternChanged(regExp pattern) This signal is emitted when xPosRolePattern changes to \a pattern. */ /*! \qmlsignal ItemModelScatterDataProxy::yPosRolePatternChanged(regExp pattern) This signal is emitted when yPosRolePattern changes to \a pattern. */ /*! \qmlsignal ItemModelScatterDataProxy::zPosRolePatternChanged(regExp pattern) This signal is emitted when zPosRolePattern changes to \a pattern. */ /*! \qmlsignal ItemModelScatterDataProxy::rotationRolePatternChanged(regExp pattern) This signal is emitted when rotationRolePattern changes to \a pattern. */ /*! \qmlsignal ItemModelScatterDataProxy::rotationRoleReplaceChanged(string replace) This signal is emitted when rotationRoleReplace changes to \a replace. */ /*! \qmlsignal ItemModelScatterDataProxy::xPosRoleReplaceChanged(string replace) This signal is emitted when xPosRoleReplace changes to \a replace. */ /*! \qmlsignal ItemModelScatterDataProxy::yPosRoleReplaceChanged(string replace) This signal is emitted when yPosRoleReplace changes to \a replace. */ /*! \qmlsignal ItemModelScatterDataProxy::zPosRoleReplaceChanged(string replace) This signal is emitted when zPosRoleReplace changes to \a replace. */ /*! * Constructs QItemModelScatterDataProxy with optional \a parent. */ QItemModelScatterDataProxy::QItemModelScatterDataProxy(QObject *parent) : QScatterDataProxy(*(new QItemModelScatterDataProxyPrivate(this)), parent) { Q_D(QItemModelScatterDataProxy); d->connectItemModelHandler(); } /*! * Constructs QItemModelScatterDataProxy with \a itemModel and an optional \a * parent. Proxy doesn't take ownership of the \a itemModel, as typically item * models are owned by other controls. */ QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel, QObject *parent) : QScatterDataProxy(*(new QItemModelScatterDataProxyPrivate(this)), parent) { Q_D(QItemModelScatterDataProxy); d->m_itemModelHandler->setItemModel(itemModel); d->connectItemModelHandler(); } /*! * Constructs QItemModelScatterDataProxy with \a itemModel and an optional * \a parent. The proxy doesn't take ownership of the \a itemModel, as item * models are typically owned by other controls. The xPosRole property is set * to \a xPosRole, the yPosRole property to \a yPosRole, and the zPosRole * property to \a zPosRole. */ QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel, const QString &xPosRole, const QString &yPosRole, const QString &zPosRole, QObject *parent) : QScatterDataProxy(*(new QItemModelScatterDataProxyPrivate(this)), parent) { Q_D(QItemModelScatterDataProxy); d->m_itemModelHandler->setItemModel(itemModel); d->m_xPosRole = xPosRole; d->m_yPosRole = yPosRole; d->m_zPosRole = zPosRole; d->connectItemModelHandler(); } /*! * Constructs QItemModelScatterDataProxy with \a itemModel and an optional \a * parent. The proxy doesn't take ownership of the \a itemModel, as item models * are typically owned by other controls. The xPosRole property is set to \a * xPosRole, the yPosRole property to \a yPosRole, the zPosRole property to \a * zPosRole, and the rotationRole property to \a rotationRole. */ QItemModelScatterDataProxy::QItemModelScatterDataProxy(QAbstractItemModel *itemModel, const QString &xPosRole, const QString &yPosRole, const QString &zPosRole, const QString &rotationRole, QObject *parent) : QScatterDataProxy(*(new QItemModelScatterDataProxyPrivate(this)), parent) { Q_D(QItemModelScatterDataProxy); d->m_itemModelHandler->setItemModel(itemModel); d->m_xPosRole = xPosRole; d->m_yPosRole = yPosRole; d->m_zPosRole = zPosRole; d->m_rotationRole = rotationRole; d->connectItemModelHandler(); } /*! * Destroys QItemModelScatterDataProxy. */ QItemModelScatterDataProxy::~QItemModelScatterDataProxy() {} /*! * \property QItemModelScatterDataProxy::itemModel * * \brief The item model to use as a data source for a 3D scatter series. */ /*! * Sets \a itemModel as the item model for Q3DScatterWidgetItem. Does not take * ownership of the model, but does connect to it to listen for changes. */ void QItemModelScatterDataProxy::setItemModel(QAbstractItemModel *itemModel) { Q_D(QItemModelScatterDataProxy); d->m_itemModelHandler->setItemModel(itemModel); } QAbstractItemModel *QItemModelScatterDataProxy::itemModel() const { Q_D(const QItemModelScatterDataProxy); return d->m_itemModelHandler->itemModel(); } /*! * \property QItemModelScatterDataProxy::xPosRole * * \brief The item model role to map into the X position. */ void QItemModelScatterDataProxy::setXPosRole(const QString &role) { Q_D(QItemModelScatterDataProxy); if (d->m_xPosRole != role) { d->m_xPosRole = role; emit xPosRoleChanged(role); } } QString QItemModelScatterDataProxy::xPosRole() const { Q_D(const QItemModelScatterDataProxy); return d->m_xPosRole; } /*! * \property QItemModelScatterDataProxy::yPosRole * * \brief The item model role to map into the Y position. */ void QItemModelScatterDataProxy::setYPosRole(const QString &role) { Q_D(QItemModelScatterDataProxy); if (d->m_yPosRole != role) { d->m_yPosRole = role; emit yPosRoleChanged(role); } } QString QItemModelScatterDataProxy::yPosRole() const { Q_D(const QItemModelScatterDataProxy); return d->m_yPosRole; } /*! * \property QItemModelScatterDataProxy::zPosRole * * \brief The item model role to map into the Z position. */ void QItemModelScatterDataProxy::setZPosRole(const QString &role) { Q_D(QItemModelScatterDataProxy); if (d->m_zPosRole != role) { d->m_zPosRole = role; emit zPosRoleChanged(role); } } QString QItemModelScatterDataProxy::zPosRole() const { Q_D(const QItemModelScatterDataProxy); return d->m_zPosRole; } /*! * \property QItemModelScatterDataProxy::rotationRole * * \brief The item model role to map into item rotation. * * The model may supply the value for rotation as either a variant that is * directly convertible to QQuaternion or as one of the string representations: * \c{"scalar,x,y,z"} or \c{"@angle,x,y,z"}. The first will construct the * quaternion directly with the given values, and the second one will construct * the quaternion using the QQuaternion::fromAxisAndAngle() method. */ void QItemModelScatterDataProxy::setRotationRole(const QString &role) { Q_D(QItemModelScatterDataProxy); if (d->m_rotationRole != role) { d->m_rotationRole = role; emit rotationRoleChanged(role); } } QString QItemModelScatterDataProxy::rotationRole() const { Q_D(const QItemModelScatterDataProxy); return d->m_rotationRole; } /*! * \property QItemModelScatterDataProxy::xPosRolePattern * * \brief Whether search and replace is done on the value mapped by the x * position role before it is used as an item position value. * * This property specifies the regular expression to find the portion of the * mapped value to replace and xPosRoleReplace property contains the replacement * string. * * \sa xPosRole, xPosRoleReplace */ void QItemModelScatterDataProxy::setXPosRolePattern(const QRegularExpression &pattern) { Q_D(QItemModelScatterDataProxy); if (d->m_xPosRolePattern != pattern) { d->m_xPosRolePattern = pattern; emit xPosRolePatternChanged(pattern); } } QRegularExpression QItemModelScatterDataProxy::xPosRolePattern() const { Q_D(const QItemModelScatterDataProxy); return d->m_xPosRolePattern; } /*! * \property QItemModelScatterDataProxy::yPosRolePattern * * \brief Whether a search and replace is done on the value mapped by the * y position role before it is used as an item position value. * * This property specifies the regular expression to find the portion of the * mapped value to replace and yPosRoleReplace property contains the replacement * string. * * \sa yPosRole, yPosRoleReplace */ void QItemModelScatterDataProxy::setYPosRolePattern(const QRegularExpression &pattern) { Q_D(QItemModelScatterDataProxy); if (d->m_yPosRolePattern != pattern) { d->m_yPosRolePattern = pattern; emit yPosRolePatternChanged(pattern); } } QRegularExpression QItemModelScatterDataProxy::yPosRolePattern() const { Q_D(const QItemModelScatterDataProxy); return d->m_yPosRolePattern; } /*! * \property QItemModelScatterDataProxy::zPosRolePattern * * \brief Whether a search and replace is done on the value mapped by the z * position role before it is used as an item position value. * * This property specifies the regular expression to find the portion of the * mapped value to replace and zPosRoleReplace property contains the replacement * string. * * \sa zPosRole, zPosRoleReplace */ void QItemModelScatterDataProxy::setZPosRolePattern(const QRegularExpression &pattern) { Q_D(QItemModelScatterDataProxy); if (d->m_zPosRolePattern != pattern) { d->m_zPosRolePattern = pattern; emit zPosRolePatternChanged(pattern); } } QRegularExpression QItemModelScatterDataProxy::zPosRolePattern() const { Q_D(const QItemModelScatterDataProxy); return d->m_zPosRolePattern; } /*! * \property QItemModelScatterDataProxy::rotationRolePattern * * \brief Whether a search and replace is done on the value mapped by the * rotation role before it is used as item rotation. * * This property specifies the regular expression to find the portion * of the mapped value to replace and rotationRoleReplace property contains the * replacement string. * * \sa rotationRole, rotationRoleReplace */ void QItemModelScatterDataProxy::setRotationRolePattern(const QRegularExpression &pattern) { Q_D(QItemModelScatterDataProxy); if (d->m_rotationRolePattern != pattern) { d->m_rotationRolePattern = pattern; emit rotationRolePatternChanged(pattern); } } QRegularExpression QItemModelScatterDataProxy::rotationRolePattern() const { Q_D(const QItemModelScatterDataProxy); return d->m_rotationRolePattern; } /*! * \property QItemModelScatterDataProxy::xPosRoleReplace * * \brief The replace content to be used in conjunction with the x position role * pattern. * * Defaults to an empty string. For more information on how the search and * replace using regular expressions works, see QString::replace(const * QRegularExpression &rx, const QString &after) function documentation. * * \sa xPosRole, xPosRolePattern */ void QItemModelScatterDataProxy::setXPosRoleReplace(const QString &replace) { Q_D(QItemModelScatterDataProxy); if (d->m_xPosRoleReplace != replace) { d->m_xPosRoleReplace = replace; emit xPosRoleReplaceChanged(replace); } } QString QItemModelScatterDataProxy::xPosRoleReplace() const { Q_D(const QItemModelScatterDataProxy); return d->m_xPosRoleReplace; } /*! * \property QItemModelScatterDataProxy::yPosRoleReplace * * \brief The replace content to be used in conjunction with the y position role * pattern. * * Defaults to an empty string. For more information on how the search and * replace using regular expressions works, see QString::replace(const * QRegularExpression &rx, const QString &after) function documentation. * * \sa yPosRole, yPosRolePattern */ void QItemModelScatterDataProxy::setYPosRoleReplace(const QString &replace) { Q_D(QItemModelScatterDataProxy); if (d->m_yPosRoleReplace != replace) { d->m_yPosRoleReplace = replace; emit yPosRoleReplaceChanged(replace); } } QString QItemModelScatterDataProxy::yPosRoleReplace() const { Q_D(const QItemModelScatterDataProxy); return d->m_yPosRoleReplace; } /*! * \property QItemModelScatterDataProxy::zPosRoleReplace * * \brief The replace content to be used in conjunction with the z position role * pattern. * * Defaults to an empty string. For more information on how the search and * replace using regular expressions works, see QString::replace(const * QRegularExpression &rx, const QString &after) function documentation. * * \sa zPosRole, zPosRolePattern */ void QItemModelScatterDataProxy::setZPosRoleReplace(const QString &replace) { Q_D(QItemModelScatterDataProxy); if (d->m_zPosRoleReplace != replace) { d->m_zPosRoleReplace = replace; emit zPosRoleReplaceChanged(replace); } } QString QItemModelScatterDataProxy::zPosRoleReplace() const { Q_D(const QItemModelScatterDataProxy); return d->m_zPosRoleReplace; } /*! * \property QItemModelScatterDataProxy::rotationRoleReplace * * \brief The replace content to be used in conjunction with the rotation role * pattern. * * Defaults to an empty string. For more information on how the search and * replace using regular expressions works, see QString::replace(const * QRegularExpression &rx, const QString &after) function documentation. * * \sa rotationRole, rotationRolePattern */ void QItemModelScatterDataProxy::setRotationRoleReplace(const QString &replace) { Q_D(QItemModelScatterDataProxy); if (d->m_rotationRoleReplace != replace) { d->m_rotationRoleReplace = replace; emit rotationRoleReplaceChanged(replace); } } QString QItemModelScatterDataProxy::rotationRoleReplace() const { Q_D(const QItemModelScatterDataProxy); return d->m_rotationRoleReplace; } /*! * Changes \a xPosRole, \a yPosRole, \a zPosRole, and \a rotationRole mapping. */ void QItemModelScatterDataProxy::remap(const QString &xPosRole, const QString &yPosRole, const QString &zPosRole, const QString &rotationRole) { setXPosRole(xPosRole); setYPosRole(yPosRole); setZPosRole(zPosRole); setRotationRole(rotationRole); } // QItemModelScatterDataProxyPrivate QItemModelScatterDataProxyPrivate::QItemModelScatterDataProxyPrivate(QItemModelScatterDataProxy *q) : m_itemModelHandler(new ScatterItemModelHandler(q)) {} QItemModelScatterDataProxyPrivate::~QItemModelScatterDataProxyPrivate() { delete m_itemModelHandler; } void QItemModelScatterDataProxyPrivate::connectItemModelHandler() { Q_Q(QItemModelScatterDataProxy); QObject::connect(m_itemModelHandler, &ScatterItemModelHandler::itemModelChanged, q, &QItemModelScatterDataProxy::itemModelChanged); QObject::connect(q, &QItemModelScatterDataProxy::xPosRoleChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::yPosRoleChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::zPosRoleChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::rotationRoleChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::xPosRolePatternChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::yPosRolePatternChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::zPosRolePatternChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::rotationRolePatternChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::xPosRoleReplaceChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::yPosRoleReplaceChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::zPosRoleReplaceChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); QObject::connect(q, &QItemModelScatterDataProxy::rotationRoleReplaceChanged, m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged); } QT_END_NAMESPACE