/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Data Visualization module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qbar3dseries_p.h" #include "bars3dcontroller_p.h" #include QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \class QBar3DSeries * \inmodule QtDataVisualization * \brief The QBar3DSeries class represents a data series in a 3D bar graph. * \since QtDataVisualization 1.0 * * This class manages the series specific visual elements, as well as the series * data (via a data proxy). * * If no data proxy is set explicitly for the series, the series creates a default * proxy. Setting another proxy will destroy the existing proxy and all data added to it. * * QBar3DSeries supports the following format tags for QAbstract3DSeries::setItemLabelFormat(): * \table * \row * \li @rowTitle \li Title from row axis * \row * \li @colTitle \li Title from column axis * \row * \li @valueTitle \li Title from value axis * \row * \li @rowIdx \li Visible row index. Localized using the graph locale. * \row * \li @colIdx \li Visible column index. Localized using the graph locale. * \row * \li @rowLabel \li Label from row axis * \row * \li @colLabel \li Label from column axis * \row * \li @valueLabel \li Item value formatted using the format of the value * axis attached to the graph. For more information, * see \l{QValue3DAxis::labelFormat}. * \row * \li @seriesName \li Name of the series * \row * \li % \li Item value in the specified format. Formatted * using the same rules as \l{QValue3DAxis::labelFormat}. * \endtable * * For example: * \snippet doc_src_qtdatavisualization.cpp 1 * * \sa {Qt Data Visualization Data Handling}, QAbstract3DGraph::locale */ /*! * \qmltype Bar3DSeries * \inqmlmodule QtDataVisualization * \since QtDataVisualization 1.0 * \ingroup datavisualization_qml * \instantiates QBar3DSeries * \inherits Abstract3DSeries * \brief Represents a data series in a 3D bar graph. * * This type manages the series specific visual elements, as well as the series * data (via a data proxy). * * For a more complete description, see QBar3DSeries. * * \sa {Qt Data Visualization Data Handling} */ /*! * \qmlproperty BarDataProxy Bar3DSeries::dataProxy * * The active data proxy. The series assumes ownership of any proxy set to * it and deletes any previously set proxy when a new one is added. The proxy cannot be null or * set to another series. */ /*! * \qmlproperty point Bar3DSeries::selectedBar * * The bar in the series that is selected. * * The position of the selected bar is specified as a row and column in the * data array of the series. * * Only one bar can be selected at a time. * * To clear selection from this series, set invalidSelectionPosition as the position. * * If this series is added to a graph, the graph can adjust the selection according to user * interaction or if it becomes invalid. Selecting a bar on another added series will also * clear the selection. * * Removing rows from or inserting rows to the series before the row of the selected bar * will adjust the selection so that the same bar will stay selected. * * \sa {AbstractGraph3D::clearSelection()}{AbstractGraph3D.clearSelection()} */ /*! * \qmlproperty point Bar3DSeries::invalidSelectionPosition * A constant property providing an invalid position for selection. This * position is set to the selectedBar property to clear the selection from this * series. * * \sa {AbstractGraph3D::clearSelection()}{AbstractGraph3D.clearSelection()} */ /*! * \qmlproperty real Bar3DSeries::meshAngle * * A convenience property for defining the series rotation angle in degrees. * * \note When reading this property, it is calculated from the * \l{Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation} value * using floating point precision and always returns a value from zero to 360 degrees. * * \sa {Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation} */ /*! * Constructsa bar 3D series with the parent \a parent. */ QBar3DSeries::QBar3DSeries(QObject *parent) : QAbstract3DSeries(new QBar3DSeriesPrivate(this), parent) { // Default proxy dptr()->setDataProxy(new QBarDataProxy); dptr()->connectSignals(); } /*! * Constructs a bar 3D series with the data proxy \a dataProxy and the parent * \a parent. */ QBar3DSeries::QBar3DSeries(QBarDataProxy *dataProxy, QObject *parent) : QAbstract3DSeries(new QBar3DSeriesPrivate(this), parent) { dptr()->setDataProxy(dataProxy); dptr()->connectSignals(); } /*! * Deletes a bar 3D series. */ QBar3DSeries::~QBar3DSeries() { } /*! * \property QBar3DSeries::dataProxy * * \brief The active data proxy. * * The series assumes ownership of any proxy set to it and deletes any * previously set proxy when a new one is added. The proxy cannot be null or * set to another series. */ void QBar3DSeries::setDataProxy(QBarDataProxy *proxy) { d_ptr->setDataProxy(proxy); } QBarDataProxy *QBar3DSeries::dataProxy() const { return static_cast(d_ptr->dataProxy()); } /*! * \property QBar3DSeries::selectedBar * * \brief The bar in the series that is selected. * */ /*! * Selects the bar at the \a position position, specified as a row and column in * the data array of the series. * * Only one bar can be selected at a time. * * To clear selection from this series, invalidSelectionPosition() is set as * \a position. * * If this series is added to a graph, the graph can adjust the selection according to user * interaction or if it becomes invalid. Selecting a bar on another added series will also * clear the selection. * * Removing rows from or inserting rows to the series before the row of the selected bar * will adjust the selection so that the same bar will stay selected. * * \sa QAbstract3DGraph::clearSelection() */ void QBar3DSeries::setSelectedBar(const QPoint &position) { // Don't do this in private to avoid loops, as that is used for callback from controller. if (d_ptr->m_controller) static_cast(d_ptr->m_controller)->setSelectedBar(position, this, true); else dptr()->setSelectedBar(position); } QPoint QBar3DSeries::selectedBar() const { return dptrc()->m_selectedBar; } /*! * Returns an invalid position for selection. This position is set to the * selectedBar property to clear the selection from this series. * * \sa QAbstract3DGraph::clearSelection() */ QPoint QBar3DSeries::invalidSelectionPosition() { return Bars3DController::invalidSelectionPosition(); } static inline float quaternionAngle(const QQuaternion &rotation) { return qAcos(rotation.scalar()) * 360.0f / M_PI; } /*! * \property QBar3DSeries::meshAngle * * \brief The series rotation angle in degrees. * * Setting this property is equivalent to the following call: * \code setMeshRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, angle)) \endcode * * \note When reading this property, it is calculated from the * QAbstract3DSeries::meshRotation value * using floating point precision and always returns a value from zero to 360 degrees. * * \sa QAbstract3DSeries::meshRotation */ void QBar3DSeries::setMeshAngle(float angle) { setMeshRotation(QQuaternion::fromAxisAndAngle(upVector, angle)); } float QBar3DSeries::meshAngle() const { QQuaternion rotation = meshRotation(); if (rotation.isIdentity() || rotation.x() != 0.0f || rotation.z() != 0.0f) return 0.0f; else return quaternionAngle(rotation); } /*! * \internal */ QBar3DSeriesPrivate *QBar3DSeries::dptr() { return static_cast(d_ptr.data()); } /*! * \internal */ const QBar3DSeriesPrivate *QBar3DSeries::dptrc() const { return static_cast(d_ptr.data()); } // QBar3DSeriesPrivate QBar3DSeriesPrivate::QBar3DSeriesPrivate(QBar3DSeries *q) : QAbstract3DSeriesPrivate(q, QAbstract3DSeries::SeriesTypeBar), m_selectedBar(Bars3DController::invalidSelectionPosition()) { m_itemLabelFormat = QStringLiteral("@valueLabel"); m_mesh = QAbstract3DSeries::MeshBevelBar; } QBar3DSeriesPrivate::~QBar3DSeriesPrivate() { } QBar3DSeries *QBar3DSeriesPrivate::qptr() { return static_cast(q_ptr); } void QBar3DSeriesPrivate::setDataProxy(QAbstractDataProxy *proxy) { Q_ASSERT(proxy->type() == QAbstractDataProxy::DataTypeBar); QAbstract3DSeriesPrivate::setDataProxy(proxy); emit qptr()->dataProxyChanged(static_cast(proxy)); } void QBar3DSeriesPrivate::connectControllerAndProxy(Abstract3DController *newController) { QBarDataProxy *barDataProxy = static_cast(m_dataProxy); if (m_controller && barDataProxy) { // Disconnect old controller/old proxy QObject::disconnect(barDataProxy, 0, m_controller, 0); QObject::disconnect(q_ptr, 0, m_controller, 0); } if (newController && barDataProxy) { Bars3DController *controller = static_cast(newController); QObject::connect(barDataProxy, &QBarDataProxy::arrayReset, controller, &Bars3DController::handleArrayReset); QObject::connect(barDataProxy, &QBarDataProxy::rowsAdded, controller, &Bars3DController::handleRowsAdded); QObject::connect(barDataProxy, &QBarDataProxy::rowsChanged, controller, &Bars3DController::handleRowsChanged); QObject::connect(barDataProxy, &QBarDataProxy::rowsRemoved, controller, &Bars3DController::handleRowsRemoved); QObject::connect(barDataProxy, &QBarDataProxy::rowsInserted, controller, &Bars3DController::handleRowsInserted); QObject::connect(barDataProxy, &QBarDataProxy::itemChanged, controller, &Bars3DController::handleItemChanged); QObject::connect(barDataProxy, &QBarDataProxy::rowLabelsChanged, controller, &Bars3DController::handleDataRowLabelsChanged); QObject::connect(barDataProxy, &QBarDataProxy::columnLabelsChanged, controller, &Bars3DController::handleDataColumnLabelsChanged); QObject::connect(qptr(), &QBar3DSeries::dataProxyChanged, controller, &Bars3DController::handleArrayReset); } } void QBar3DSeriesPrivate::createItemLabel() { static const QString rowIndexTag(QStringLiteral("@rowIdx")); static const QString rowLabelTag(QStringLiteral("@rowLabel")); static const QString rowTitleTag(QStringLiteral("@rowTitle")); static const QString colIndexTag(QStringLiteral("@colIdx")); static const QString colLabelTag(QStringLiteral("@colLabel")); static const QString colTitleTag(QStringLiteral("@colTitle")); static const QString valueTitleTag(QStringLiteral("@valueTitle")); static const QString valueLabelTag(QStringLiteral("@valueLabel")); static const QString seriesNameTag(QStringLiteral("@seriesName")); if (m_selectedBar == QBar3DSeries::invalidSelectionPosition()) { m_itemLabel = QString(); return; } QLocale locale(QLocale::c()); if (m_controller) locale = m_controller->locale(); QCategory3DAxis *categoryAxisZ = static_cast(m_controller->axisZ()); QCategory3DAxis *categoryAxisX = static_cast(m_controller->axisX()); QValue3DAxis *valueAxis = static_cast(m_controller->axisY()); qreal selectedBarValue = qreal(qptr()->dataProxy()->itemAt(m_selectedBar)->value()); // Custom format expects printf format specifier. There is no tag for it. m_itemLabel = valueAxis->formatter()->stringForValue(selectedBarValue, m_itemLabelFormat); int selBarPosRow = m_selectedBar.x(); int selBarPosCol = m_selectedBar.y(); m_itemLabel.replace(rowIndexTag, locale.toString(selBarPosRow)); if (categoryAxisZ->labels().size() > selBarPosRow) m_itemLabel.replace(rowLabelTag, categoryAxisZ->labels().at(selBarPosRow)); else m_itemLabel.replace(rowLabelTag, QString()); m_itemLabel.replace(rowTitleTag, categoryAxisZ->title()); m_itemLabel.replace(colIndexTag, locale.toString(selBarPosCol)); if (categoryAxisX->labels().size() > selBarPosCol) m_itemLabel.replace(colLabelTag, categoryAxisX->labels().at(selBarPosCol)); else m_itemLabel.replace(colLabelTag, QString()); m_itemLabel.replace(colTitleTag, categoryAxisX->title()); m_itemLabel.replace(valueTitleTag, valueAxis->title()); if (m_itemLabel.contains(valueLabelTag)) { QString valueLabelText = valueAxis->formatter()->stringForValue(selectedBarValue, valueAxis->labelFormat()); m_itemLabel.replace(valueLabelTag, valueLabelText); } m_itemLabel.replace(seriesNameTag, m_name); } void QBar3DSeriesPrivate::handleMeshRotationChanged(const QQuaternion &rotation) { emit qptr()->meshAngleChanged(quaternionAngle(rotation)); } void QBar3DSeriesPrivate::setSelectedBar(const QPoint &position) { if (position != m_selectedBar) { markItemLabelDirty(); m_selectedBar = position; emit qptr()->selectedBarChanged(m_selectedBar); } } void QBar3DSeriesPrivate::connectSignals() { QObject::connect(q_ptr, &QAbstract3DSeries::meshRotationChanged, this, &QBar3DSeriesPrivate::handleMeshRotationChanged); } QT_END_NAMESPACE_DATAVISUALIZATION