From 6d31e42632f0779212b2a265f3c9a7c97a0e63b0 Mon Sep 17 00:00:00 2001 From: Sami Varanka Date: Wed, 29 Sep 2021 14:53:35 +0300 Subject: Add row colors for Bar3DSeries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added rowColors property for Bar3DSeries. The property can be used draw the individual rows in the series in different colors. Autotests and a manualtest for the property were added also. Fixes: QTBUG-96682 Change-Id: I5c4f778189d079206500a9289490792584eab2b9 Reviewed-by: Miikka Heikkinen Reviewed-by: Tomi Korpipää --- src/datavisualization/data/qbar3dseries.cpp | 43 +++ src/datavisualization/data/qbar3dseries.h | 5 + src/datavisualization/data/qbar3dseries_p.h | 4 + src/datavisualization/engine/bars3dcontroller.cpp | 5 + src/datavisualization/engine/bars3dcontroller_p.h | 1 + src/datavisualization/engine/bars3drenderer.cpp | 13 +- src/datavisualizationqml/declarativeseries.cpp | 101 ++++- src/datavisualizationqml/declarativeseries_p.h | 19 + tests/auto/cpptest/q3dbars-series/tst_series.cpp | 13 + tests/auto/qmltest/bars3d/tst_barseries.qml | 23 ++ tests/manual/CMakeLists.txt | 1 + tests/manual/qmlbarsrowcolors/CMakeLists.txt | 52 +++ tests/manual/qmlbarsrowcolors/main.cpp | 59 +++ .../qmlbarsrowcolors/qml/qmlbarsrowcolors/Axes.qml | 67 ++++ .../qmlbarsrowcolors/qml/qmlbarsrowcolors/Data.qml | 144 +++++++ .../qmlbarsrowcolors/qml/qmlbarsrowcolors/main.qml | 418 +++++++++++++++++++++ 16 files changed, 964 insertions(+), 4 deletions(-) create mode 100644 tests/manual/qmlbarsrowcolors/CMakeLists.txt create mode 100644 tests/manual/qmlbarsrowcolors/main.cpp create mode 100644 tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/Axes.qml create mode 100644 tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/Data.qml create mode 100644 tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/main.qml diff --git a/src/datavisualization/data/qbar3dseries.cpp b/src/datavisualization/data/qbar3dseries.cpp index afc3b3ba..1cd4c754 100644 --- a/src/datavisualization/data/qbar3dseries.cpp +++ b/src/datavisualization/data/qbar3dseries.cpp @@ -146,6 +146,18 @@ QT_BEGIN_NAMESPACE * \sa {Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation} */ +/*! + * \qmlproperty list Bar3DSeries::rowColors + * \since 6.3 + * This property can be used to draw the rows of the series in different colors. + * The \l{Theme3D::colorStyle}{Theme3D.colorStyle} must be set to + * \c ColorStyleUniform to use this property. + * \note If the property is set and the theme is changed, + * the rowColors list is not cleared automatically. + * + * \sa Q3DTheme::ColorStyleUniform + */ + /*! * Constructsa bar 3D series with the parent \a parent. */ @@ -281,6 +293,27 @@ float QBar3DSeries::meshAngle() const return quaternionAngle(rotation); } +/*! + * \property QBar3DSeries::rowColors + * \since 6.3 + * + * \brief The list of row colors in the series. + * + * This property can be used to color + * the rows of the series in different colors. + * The Q3DTheme::ColorStyle must be set to + * Q3DTheme::ColorStyleUniform to use this property. + * + * \sa Q3DTheme::ColorStyleUniform + */ +void QBar3DSeries::setRowColors(const QList &colors) +{ + dptr()->setRowColors(colors); +} +QList QBar3DSeries::rowColors() const +{ + return dptrc()->m_rowColors; +} /*! * \internal */ @@ -355,6 +388,8 @@ void QBar3DSeriesPrivate::connectControllerAndProxy(Abstract3DController *newCon &Bars3DController::handleDataColumnLabelsChanged); QObject::connect(qptr(), &QBar3DSeries::dataProxyChanged, controller, &Bars3DController::handleArrayReset); + QObject::connect(qptr(), &QBar3DSeries::rowColorsChanged, controller, + &Bars3DController::handleRowColorsChanged); } } @@ -432,4 +467,12 @@ void QBar3DSeriesPrivate::connectSignals() &QBar3DSeriesPrivate::handleMeshRotationChanged); } +void QBar3DSeriesPrivate::setRowColors(const QList &colors) +{ + if (m_rowColors != colors) { + m_rowColors = colors; + emit qptr()->rowColorsChanged(m_rowColors); + } +} + QT_END_NAMESPACE diff --git a/src/datavisualization/data/qbar3dseries.h b/src/datavisualization/data/qbar3dseries.h index 99fbad77..a174f0f5 100644 --- a/src/datavisualization/data/qbar3dseries.h +++ b/src/datavisualization/data/qbar3dseries.h @@ -44,6 +44,7 @@ class Q_DATAVISUALIZATION_EXPORT QBar3DSeries : public QAbstract3DSeries Q_PROPERTY(QBarDataProxy *dataProxy READ dataProxy WRITE setDataProxy NOTIFY dataProxyChanged) Q_PROPERTY(QPoint selectedBar READ selectedBar WRITE setSelectedBar NOTIFY selectedBarChanged) Q_PROPERTY(float meshAngle READ meshAngle WRITE setMeshAngle NOTIFY meshAngleChanged) + Q_PROPERTY(QList rowColors READ rowColors WRITE setRowColors NOTIFY rowColorsChanged REVISION(6, 3)) public: explicit QBar3DSeries(QObject *parent = nullptr); @@ -60,10 +61,14 @@ public: void setMeshAngle(float angle); float meshAngle() const; + QList rowColors() const; + void setRowColors(const QList &colors); + Q_SIGNALS: void dataProxyChanged(QBarDataProxy *proxy); void selectedBarChanged(const QPoint &position); void meshAngleChanged(float angle); + Q_REVISION(6, 3) void rowColorsChanged(const QList &rowcolors); protected: QBar3DSeriesPrivate *dptr(); diff --git a/src/datavisualization/data/qbar3dseries_p.h b/src/datavisualization/data/qbar3dseries_p.h index 0f5bb43a..cd94b7f4 100644 --- a/src/datavisualization/data/qbar3dseries_p.h +++ b/src/datavisualization/data/qbar3dseries_p.h @@ -62,11 +62,15 @@ public: void connectSignals(); + void setRowColors(const QList &colors); + private: QBar3DSeries *qptr(); QPoint m_selectedBar; + QList m_rowColors; + private: friend class QBar3DSeries; }; diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp index 74ea1076..b5910c8e 100644 --- a/src/datavisualization/engine/bars3dcontroller.cpp +++ b/src/datavisualization/engine/bars3dcontroller.cpp @@ -328,6 +328,11 @@ void Bars3DController::handleDataColumnLabelsChanged() } } +void Bars3DController::handleRowColorsChanged() +{ + emitNeedRender(); +} + void Bars3DController::handleAxisAutoAdjustRangeChangedInOrientation( QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust) { diff --git a/src/datavisualization/engine/bars3dcontroller_p.h b/src/datavisualization/engine/bars3dcontroller_p.h index 818eea4f..fb028e92 100644 --- a/src/datavisualization/engine/bars3dcontroller_p.h +++ b/src/datavisualization/engine/bars3dcontroller_p.h @@ -165,6 +165,7 @@ public Q_SLOTS: void handleItemChanged(int rowIndex, int columnIndex); void handleDataRowLabelsChanged(); void handleDataColumnLabelsChanged(); + void handleRowColorsChanged(); Q_SIGNALS: void primarySeriesChanged(QBar3DSeries *series); diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 0c51d187..a0c58b45 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -1628,10 +1628,17 @@ bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, } case Bars3DController::SelectionNone: { // Current bar is not selected, nor on a row or column - if (colorStyleIsUniform) - barColor = baseColor; - else + if (colorStyleIsUniform) { + QList rowColors = cache->series()->rowColors(); + if (rowColors.size() == 0) { + barColor = baseColor; + } else { + int rowColorIndex = row % rowColors.size(); + barColor = Utils::vectorFromColor(rowColors[rowColorIndex]); + } + } else { gradientTexture = cache->baseGradientTexture(); + } break; } } diff --git a/src/datavisualizationqml/declarativeseries.cpp b/src/datavisualizationqml/declarativeseries.cpp index 38263a32..2afa3450 100644 --- a/src/datavisualizationqml/declarativeseries.cpp +++ b/src/datavisualizationqml/declarativeseries.cpp @@ -103,7 +103,8 @@ DeclarativeBar3DSeries::DeclarativeBar3DSeries(QObject *parent) : QBar3DSeries(parent), m_baseGradient(0), m_singleHighlightGradient(0), - m_multiHighlightGradient(0) + m_multiHighlightGradient(0), + m_dummyColors(false) { QObject::connect(this, &QBar3DSeries::selectedBarChanged, this, &DeclarativeBar3DSeries::selectedBarChanged); @@ -171,6 +172,37 @@ ColorGradient *DeclarativeBar3DSeries::multiHighlightGradient() const return m_multiHighlightGradient; } +QQmlListProperty DeclarativeBar3DSeries::rowColors() +{ + return QQmlListProperty(this, this, + &DeclarativeBar3DSeries::appendRowColorsFunc, + &DeclarativeBar3DSeries::countRowColorsFunc, + &DeclarativeBar3DSeries::atRowColorsFunc, + &DeclarativeBar3DSeries::clearRowColorsFunc); +} + +void DeclarativeBar3DSeries::appendRowColorsFunc(QQmlListProperty *list, + DeclarativeColor *color) +{ + reinterpret_cast(list->data)->addColor(color); +} + +qsizetype DeclarativeBar3DSeries::countRowColorsFunc(QQmlListProperty *list) +{ + return reinterpret_cast(list->data)->colorList().count(); +} + +DeclarativeColor *DeclarativeBar3DSeries::atRowColorsFunc(QQmlListProperty *list, + qsizetype index) +{ + return reinterpret_cast(list->data)->colorList().at(index); +} + +void DeclarativeBar3DSeries::clearRowColorsFunc(QQmlListProperty *list) +{ + reinterpret_cast(list->data)->clearColors(); +} + void DeclarativeBar3DSeries::handleBaseGradientUpdate() { if (m_baseGradient) @@ -189,6 +221,73 @@ void DeclarativeBar3DSeries::handleMultiHighlightGradientUpdate() setSeriesGradient(this, *m_multiHighlightGradient, GradientTypeMulti); } +void DeclarativeBar3DSeries::handleRowColorUpdate() +{ + int colorCount = m_rowColors.size(); + int changed = 0; + + DeclarativeColor *color = qobject_cast(QObject::sender()); + for (int i = 0; i < colorCount; i++) { + if (color == m_rowColors.at(i)) { + changed = i; + break; + } + } + QList list = QBar3DSeries::rowColors(); + list[changed] = m_rowColors.at(changed)->color(); + QBar3DSeries::setRowColors(list); +} + +void DeclarativeBar3DSeries::addColor(DeclarativeColor *color) +{ + if (!color) { + qWarning("Color is invalid, use ThemeColor"); + return; + } + clearDummyColors(); + m_rowColors.append(color); + connect(color, &DeclarativeColor::colorChanged, this, + &DeclarativeBar3DSeries::handleRowColorUpdate); + QList list = QBar3DSeries::rowColors(); + list.append(color->color()); + QBar3DSeries::setRowColors(list); +} + +QList DeclarativeBar3DSeries::colorList() +{ + if (m_rowColors.isEmpty()) { + m_dummyColors = true; + const QList list = QBar3DSeries::rowColors(); + for (const QColor &item : list) { + DeclarativeColor *color = new DeclarativeColor(this); + color->setColor(item); + m_rowColors.append(color); + connect(color, &DeclarativeColor::colorChanged, this, + &DeclarativeBar3DSeries::handleRowColorUpdate); + } + } + return m_rowColors; +} + +void DeclarativeBar3DSeries::clearColors() +{ + clearDummyColors(); + for (const auto color : qAsConst(m_rowColors)) + disconnect(color, 0, this, 0); + + m_rowColors.clear(); + QBar3DSeries::setRowColors(QList()); +} + +void DeclarativeBar3DSeries::clearDummyColors() +{ + if (m_dummyColors) { + qDeleteAll(m_rowColors); + m_rowColors.clear(); + m_dummyColors = false; + } +} + DeclarativeScatter3DSeries::DeclarativeScatter3DSeries(QObject *parent) : QScatter3DSeries(parent), m_baseGradient(0), diff --git a/src/datavisualizationqml/declarativeseries_p.h b/src/datavisualizationqml/declarativeseries_p.h index da45cb3f..3e1593d5 100644 --- a/src/datavisualizationqml/declarativeseries_p.h +++ b/src/datavisualizationqml/declarativeseries_p.h @@ -44,6 +44,7 @@ #include "qscatter3dseries.h" #include "qsurface3dseries.h" #include "colorgradient_p.h" +#include "declarativecolor_p.h" #include #include @@ -68,6 +69,7 @@ class DeclarativeBar3DSeries : public QBar3DSeries Q_PROPERTY(ColorGradient *baseGradient READ baseGradient WRITE setBaseGradient NOTIFY baseGradientChanged) Q_PROPERTY(ColorGradient *singleHighlightGradient READ singleHighlightGradient WRITE setSingleHighlightGradient NOTIFY singleHighlightGradientChanged) Q_PROPERTY(ColorGradient *multiHighlightGradient READ multiHighlightGradient WRITE setMultiHighlightGradient NOTIFY multiHighlightGradientChanged) + Q_PROPERTY(QQmlListProperty rowColors READ rowColors REVISION(6, 3)) Q_CLASSINFO("DefaultProperty", "seriesChildren") QML_NAMED_ELEMENT(Bar3DSeries) @@ -91,10 +93,19 @@ public: void setMultiHighlightGradient(ColorGradient *gradient); ColorGradient *multiHighlightGradient() const; + QQmlListProperty rowColors(); + static void appendRowColorsFunc(QQmlListProperty *list, + DeclarativeColor *color); + static qsizetype countRowColorsFunc(QQmlListProperty *list); + static DeclarativeColor *atRowColorsFunc(QQmlListProperty *list, + qsizetype index); + static void clearRowColorsFunc(QQmlListProperty *list); + public Q_SLOTS: void handleBaseGradientUpdate(); void handleSingleHighlightGradientUpdate(); void handleMultiHighlightGradientUpdate(); + void handleRowColorUpdate(); Q_SIGNALS: void selectedBarChanged(const QPointF &position); @@ -106,6 +117,14 @@ private: ColorGradient *m_baseGradient; // Not owned ColorGradient *m_singleHighlightGradient; // Not owned ColorGradient *m_multiHighlightGradient; // Not owned + + QList m_rowColors; + bool m_dummyColors; + + void addColor(DeclarativeColor *color); + QList colorList(); + void clearColors(); + void clearDummyColors(); }; class DeclarativeScatter3DSeries : public QScatter3DSeries diff --git a/tests/auto/cpptest/q3dbars-series/tst_series.cpp b/tests/auto/cpptest/q3dbars-series/tst_series.cpp index 11dfd3f2..74e375c5 100644 --- a/tests/auto/cpptest/q3dbars-series/tst_series.cpp +++ b/tests/auto/cpptest/q3dbars-series/tst_series.cpp @@ -90,6 +90,7 @@ void tst_series::initialProperties() QVERIFY(m_series->dataProxy()); QCOMPARE(m_series->meshAngle(), 0.0f); QCOMPARE(m_series->selectedBar(), m_series->invalidSelectionPosition()); + QCOMPARE(m_series->rowColors().size(), 0); // Common properties QCOMPARE(m_series->baseColor(), QColor(Qt::black)); @@ -132,6 +133,13 @@ void tst_series::initializeProperties() gradient3.setColorAt(0.0, Qt::white); gradient3.setColorAt(1.0, Qt::gray); + QList rowColors; + rowColors.append(QColor(Qt::green)); + rowColors.append(QColor(Qt::blue)); + rowColors.append(QColor(Qt::red)); + + m_series->setRowColors(rowColors); + // Common properties m_series->setBaseColor(QColor(Qt::blue)); m_series->setBaseGradient(gradient1); @@ -165,6 +173,11 @@ void tst_series::initializeProperties() QCOMPARE(m_series->singleHighlightGradient().stops().at(0).second, QColor(Qt::white)); QCOMPARE(m_series->userDefinedMesh(), QString(":/customitem.obj")); QCOMPARE(m_series->isVisible(), false); + + QCOMPARE(m_series->rowColors().size(), 3); + QCOMPARE(m_series->rowColors().at(0), QColor(Qt::green)); + QCOMPARE(m_series->rowColors().at(1), QColor(Qt::blue)); + QCOMPARE(m_series->rowColors().at(2), QColor(Qt::red)); } void tst_series::invalidProperties() diff --git a/tests/auto/qmltest/bars3d/tst_barseries.qml b/tests/auto/qmltest/bars3d/tst_barseries.qml index 77eba61a..77dd0895 100644 --- a/tests/auto/qmltest/bars3d/tst_barseries.qml +++ b/tests/auto/qmltest/bars3d/tst_barseries.qml @@ -64,6 +64,20 @@ Item { ] } + ThemeColor { + id: rowColor1 + color: "green" + } + + ThemeColor { + id: rowColor2 + color: "blue" + } + ThemeColor { + id: rowColor3 + color: "red" + } + Bar3DSeries { id: initialized dataProxy: ItemModelBarDataProxy { @@ -92,6 +106,7 @@ Item { singleHighlightGradient: gradient3 userDefinedMesh: ":/customitem.obj" visible: false + rowColors: [ rowColor1, rowColor2, rowColor3 ] } ItemModelBarDataProxy { @@ -118,6 +133,7 @@ Item { compare(initial.invalidSelectionPosition, Qt.point(-1, -1)) compare(initial.meshAngle, 0) compare(initial.selectedBar, Qt.point(-1, -1)) + compare(initial.rowColors.length, 0) } function test_2_initial_common() { @@ -149,6 +165,7 @@ Item { compare(initialized.dataProxy.rowCount, 2) fuzzyCompare(initialized.meshAngle, 15.0, 0.01) compare(initialized.selectedBar, Qt.point(0, 0)) + compare(initialized.rowColors.length, 3) } function test_2_initialized_common() { @@ -177,6 +194,7 @@ Item { change.dataProxy = proxy1 change.meshAngle = 15.0 change.selectedBar = Qt.point(0, 0) + change.rowColors = [rowColor1, rowColor2, rowColor3] } function test_2_test_change() { @@ -222,5 +240,10 @@ Item { gradient1.stops[0].color = "yellow" compare(change.baseGradient.stops[0].color, "#ffff00") } + + function test_5_change_rowColors() { + rowColor2.color = "purple" + compare(change.rowColors[1].color, "#800080") + } } } diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index a1ca1dba..922fe921 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -5,6 +5,7 @@ if(TARGET Qt::Quick) add_subdirectory(qmlperf) add_subdirectory(qmlgradient) add_subdirectory(qmlheightmap) + add_subdirectory(qmlbarsrowcolors) endif() if(NOT ANDROID AND NOT IOS AND NOT WINRT) add_subdirectory(barstest) diff --git a/tests/manual/qmlbarsrowcolors/CMakeLists.txt b/tests/manual/qmlbarsrowcolors/CMakeLists.txt new file mode 100644 index 00000000..bd8da958 --- /dev/null +++ b/tests/manual/qmlbarsrowcolors/CMakeLists.txt @@ -0,0 +1,52 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +qt_internal_add_manual_test(qmlbarsrowcolors + GUI + SOURCES + main.cpp + ) + +find_package(Qt6 COMPONENTS Core) +find_package(Qt6 COMPONENTS Gui) +find_package(Qt6 COMPONENTS Qml) +find_package(Qt6 COMPONENTS Quick) +find_package(Qt6 COMPONENTS DataVisualization) + +set_target_properties(qmlbarsrowcolors PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) +target_compile_definitions(qmlbarsrowcolors PUBLIC + QT_DISABLE_DEPRECATED_BEFORE=0x050F00 +) + +target_link_libraries(qmlbarsrowcolors PUBLIC + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick + Qt::DataVisualization +) + +set(qmlbarsrowcolors_resource_files + "qml/qmlbarsrowcolors/Axes.qml" + "qml/qmlbarsrowcolors/Data.qml" + "qml/qmlbarsrowcolors/main.qml" +) + +qt6_add_resources(qmlbarsrowcolors "qmlbarsrowcolors" + PREFIX + "/" + FILES + ${qmlbarsrowcolors_resource_files} +) + +install(TARGETS qmlbarsrowcolors + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/qmlbarsrowcolors/main.cpp b/tests/manual/qmlbarsrowcolors/main.cpp new file mode 100644 index 00000000..25ed1447 --- /dev/null +++ b/tests/manual/qmlbarsrowcolors/main.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 +#include +#include +#include + +int main(int argc, char *argv[]) +{ + qputenv("QSG_RHI_BACKEND", "opengl"); + QGuiApplication app(argc, argv); + + QQuickView viewer; + + // The following are needed to make examples run without having to install the module + // in desktop environments. +#ifdef Q_OS_WIN + QString extraImportPath(QStringLiteral("%1/../../../../%2")); +#else + QString extraImportPath(QStringLiteral("%1/../../../%2")); +#endif + viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(), + QString::fromLatin1("qml"))); + + viewer.setTitle(QStringLiteral("Monthly income/expenses")); + + viewer.setSource(QUrl("qrc:/qml/qmlbarsrowcolors/main.qml")); + viewer.setResizeMode(QQuickView::SizeRootObjectToView); + viewer.show(); + + return app.exec(); +} diff --git a/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/Axes.qml b/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/Axes.qml new file mode 100644 index 00000000..e17ca521 --- /dev/null +++ b/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/Axes.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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$ +** +****************************************************************************/ + +import QtQuick +import QtDataVisualization + +Item { + property alias column: columnAxis + property alias row: rowAxis + property alias value: valueAxis + property alias total: totalAxis + + // Custom labels for columns, since the data contains abbreviated month names. + //! [0] + CategoryAxis3D { + id: columnAxis + labels: ["January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"] + labelAutoRotation: 30 + } + //! [0] + CategoryAxis3D { + id: totalAxis + labels: ["Yearly total"] + labelAutoRotation: 30 + } + CategoryAxis3D { + // For row labels we can use row labels from data proxy, no labels defined for rows. + id: rowAxis + labelAutoRotation: 30 + } + + ValueAxis3D { + id: valueAxis + min: 0 + max: 35 + labelFormat: "%.2f M\u20AC" + title: "Monthly income" + labelAutoRotation: 90 + } +} diff --git a/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/Data.qml b/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/Data.qml new file mode 100644 index 00000000..cabb6522 --- /dev/null +++ b/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/Data.qml @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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$ +** +****************************************************************************/ + +import QtQuick +import QtQml.Models + +Item { + property alias model: dataModel + + property var modelAsJsArray: { + var arr = [] + for (var i = 0; i < dataModel.count; i++) { + var row = dataModel.get(i) + arr.push({ + timestamp: row.timestamp, + expenses: row.expenses, + income: row.income + }) + } + return arr + } + + //! [0] + ListModel { + id: dataModel + ListElement{ timestamp: "2006-01"; expenses: "-4"; income: "5" } + ListElement{ timestamp: "2006-02"; expenses: "-5"; income: "6" } + ListElement{ timestamp: "2006-03"; expenses: "-7"; income: "4" } + //! [0] + ListElement{ timestamp: "2006-04"; expenses: "-3"; income: "2" } + ListElement{ timestamp: "2006-05"; expenses: "-4"; income: "1" } + ListElement{ timestamp: "2006-06"; expenses: "-2"; income: "2" } + ListElement{ timestamp: "2006-07"; expenses: "-1"; income: "3" } + ListElement{ timestamp: "2006-08"; expenses: "-5"; income: "1" } + ListElement{ timestamp: "2006-09"; expenses: "-2"; income: "3" } + ListElement{ timestamp: "2006-10"; expenses: "-5"; income: "2" } + ListElement{ timestamp: "2006-11"; expenses: "-8"; income: "5" } + ListElement{ timestamp: "2006-12"; expenses: "-3"; income: "3" } + + ListElement{ timestamp: "2007-01"; expenses: "-3"; income: "1" } + ListElement{ timestamp: "2007-02"; expenses: "-4"; income: "2" } + ListElement{ timestamp: "2007-03"; expenses: "-12"; income: "4" } + ListElement{ timestamp: "2007-04"; expenses: "-13"; income: "6" } + ListElement{ timestamp: "2007-05"; expenses: "-14"; income: "11" } + ListElement{ timestamp: "2007-06"; expenses: "-7"; income: "7" } + ListElement{ timestamp: "2007-07"; expenses: "-6"; income: "4" } + ListElement{ timestamp: "2007-08"; expenses: "-4"; income: "15" } + ListElement{ timestamp: "2007-09"; expenses: "-2"; income: "18" } + ListElement{ timestamp: "2007-10"; expenses: "-29"; income: "25" } + ListElement{ timestamp: "2007-11"; expenses: "-23"; income: "29" } + ListElement{ timestamp: "2007-12"; expenses: "-5"; income: "9" } + + ListElement{ timestamp: "2008-01"; expenses: "-3"; income: "8" } + ListElement{ timestamp: "2008-02"; expenses: "-8"; income: "14" } + ListElement{ timestamp: "2008-03"; expenses: "-10"; income: "20" } + ListElement{ timestamp: "2008-04"; expenses: "-12"; income: "24" } + ListElement{ timestamp: "2008-05"; expenses: "-10"; income: "19" } + ListElement{ timestamp: "2008-06"; expenses: "-5"; income: "8" } + ListElement{ timestamp: "2008-07"; expenses: "-1"; income: "4" } + ListElement{ timestamp: "2008-08"; expenses: "-7"; income: "12" } + ListElement{ timestamp: "2008-09"; expenses: "-4"; income: "16" } + ListElement{ timestamp: "2008-10"; expenses: "-22"; income: "33" } + ListElement{ timestamp: "2008-11"; expenses: "-16"; income: "25" } + ListElement{ timestamp: "2008-12"; expenses: "-2"; income: "7" } + + ListElement{ timestamp: "2009-01"; expenses: "-4"; income: "5" } + ListElement{ timestamp: "2009-02"; expenses: "-4"; income: "7" } + ListElement{ timestamp: "2009-03"; expenses: "-11"; income: "14" } + ListElement{ timestamp: "2009-04"; expenses: "-16"; income: "22" } + ListElement{ timestamp: "2009-05"; expenses: "-3"; income: "5" } + ListElement{ timestamp: "2009-06"; expenses: "-4"; income: "8" } + ListElement{ timestamp: "2009-07"; expenses: "-7"; income: "9" } + ListElement{ timestamp: "2009-08"; expenses: "-9"; income: "13" } + ListElement{ timestamp: "2009-09"; expenses: "-1"; income: "6" } + ListElement{ timestamp: "2009-10"; expenses: "-14"; income: "25" } + ListElement{ timestamp: "2009-11"; expenses: "-19"; income: "29" } + ListElement{ timestamp: "2009-12"; expenses: "-5"; income: "7" } + + ListElement{ timestamp: "2010-01"; expenses: "-14"; income: "22" } + ListElement{ timestamp: "2010-02"; expenses: "-5"; income: "7" } + ListElement{ timestamp: "2010-03"; expenses: "-1"; income: "9" } + ListElement{ timestamp: "2010-04"; expenses: "-1"; income: "12" } + ListElement{ timestamp: "2010-05"; expenses: "-5"; income: "9" } + ListElement{ timestamp: "2010-06"; expenses: "-5"; income: "8" } + ListElement{ timestamp: "2010-07"; expenses: "-3"; income: "7" } + ListElement{ timestamp: "2010-08"; expenses: "-1"; income: "5" } + ListElement{ timestamp: "2010-09"; expenses: "-2"; income: "4" } + ListElement{ timestamp: "2010-10"; expenses: "-10"; income: "13" } + ListElement{ timestamp: "2010-11"; expenses: "-12"; income: "17" } + ListElement{ timestamp: "2010-12"; expenses: "-6"; income: "9" } + + ListElement{ timestamp: "2011-01"; expenses: "-2"; income: "6" } + ListElement{ timestamp: "2011-02"; expenses: "-4"; income: "8" } + ListElement{ timestamp: "2011-03"; expenses: "-7"; income: "12" } + ListElement{ timestamp: "2011-04"; expenses: "-9"; income: "15" } + ListElement{ timestamp: "2011-05"; expenses: "-7"; income: "19" } + ListElement{ timestamp: "2011-06"; expenses: "-9"; income: "18" } + ListElement{ timestamp: "2011-07"; expenses: "-13"; income: "17" } + ListElement{ timestamp: "2011-08"; expenses: "-5"; income: "9" } + ListElement{ timestamp: "2011-09"; expenses: "-3"; income: "8" } + ListElement{ timestamp: "2011-10"; expenses: "-13"; income: "15" } + ListElement{ timestamp: "2011-11"; expenses: "-8"; income: "17" } + ListElement{ timestamp: "2011-12"; expenses: "-7"; income: "10" } + + ListElement{ timestamp: "2012-01"; expenses: "-12"; income: "16" } + ListElement{ timestamp: "2012-02"; expenses: "-24"; income: "28" } + ListElement{ timestamp: "2012-03"; expenses: "-27"; income: "22" } + ListElement{ timestamp: "2012-04"; expenses: "-29"; income: "25" } + ListElement{ timestamp: "2012-05"; expenses: "-27"; income: "29" } + ListElement{ timestamp: "2012-06"; expenses: "-19"; income: "18" } + ListElement{ timestamp: "2012-07"; expenses: "-13"; income: "17" } + ListElement{ timestamp: "2012-08"; expenses: "-15"; income: "19" } + ListElement{ timestamp: "2012-09"; expenses: "-3"; income: "8" } + ListElement{ timestamp: "2012-10"; expenses: "-3"; income: "6" } + ListElement{ timestamp: "2012-11"; expenses: "-4"; income: "8" } + ListElement{ timestamp: "2012-12"; expenses: "-5"; income: "9" } + } +} diff --git a/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/main.qml b/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/main.qml new file mode 100644 index 00000000..0d72369b --- /dev/null +++ b/tests/manual/qmlbarsrowcolors/qml/qmlbarsrowcolors/main.qml @@ -0,0 +1,418 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtDataVisualization +import QtQuick.Window +import Qt.labs.qmlmodels +import "." + +Item { + id: mainview + + property int buttonLayoutHeight: 180; + property int currentRow + property Bar3DSeries selectedSeries + + function toggleRowColorsForBarSeries(enable) { + if (enable) + barSeries.rowColors = [color1, color2, color3] + else + barSeries.rowColors = [] + } + + function toggleRowColorsForSecondarySeries(enable) { + if (enable) + secondarySeries.rowColors = [color4, color5, color6] + else + secondarySeries.rowColors = [] + } + + function handleSelectionChange(series, position) { + if (position !== series.invalidSelectionPosition) + selectedSeries = series + + // Set tableView current row to selected bar + var rowRole = series.dataProxy.rowLabels[position.x]; + var colRole + if (barGraph.columnAxis === graphAxes.total) + colRole = "01"; + else + colRole = series.dataProxy.columnLabels[position.y]; + var checkTimestamp = rowRole + "-" + colRole + + if (currentRow === -1 || checkTimestamp !== graphData.model.get(currentRow).timestamp) { + var totalRows = tableView.rows; + for (var i = 0; i < totalRows; i++) { + var modelTimestamp = graphData.model.get(i).timestamp + if (modelTimestamp === checkTimestamp) { + currentRow = i + break + } + } + } + } + + width: 1280 + height: 1024 + + state: Screen.width < Screen.height ? "portrait" : "landscape" + selectedSeries: barSeries + + onCurrentRowChanged: { + var timestamp = graphData.model.get(currentRow).timestamp + var pattern = /(\d\d\d\d)-(\d\d)/ + var matches = pattern.exec(timestamp) + var rowIndex = modelProxy.rowCategoryIndex(matches[1]) + var colIndex + if (barGraph.columnAxis === graphAxes.total) + colIndex = 0 // Just one column when showing yearly totals + else + colIndex = modelProxy.columnCategoryIndex(matches[2]) + if (selectedSeries.visible) + mainview.selectedSeries.selectedBar = Qt.point(rowIndex, colIndex) + else if (barSeries.visible) + barSeries.selectedBar = Qt.point(rowIndex, colIndex) + else + secondarySeries.selectedBar = Qt.point(rowIndex, colIndex) + } + + Data { + id: graphData + } + + Axes { + id: graphAxes + } + + ThemeColor { + id: color1 + color: "green" + } + + ThemeColor { + id: color2 + color: "blue" + } + + ThemeColor { + id: color3 + color: "red" + } + + ThemeColor { + id: color4 + color: "yellow" + } + + ThemeColor { + id: color5 + color: "purple" + } + + ThemeColor { + id: color6 + color: "orange" + } + + Theme3D { + id: theme1 + type: Theme3D.ThemeRetro + labelBorderEnabled: true + font.pointSize: 35 + labelBackgroundEnabled: true + colorStyle: Theme3D.ColorStyleUniform + } + + Theme3D { + id: theme2 + type: Theme3D.ThemeArmyBlue + labelBorderEnabled: true + font.pointSize: 35 + labelBackgroundEnabled: true + colorStyle: Theme3D.ColorStyleUniform + } + + Item { + id: dataView + anchors.right: mainview.right; + anchors.bottom: mainview.bottom + + Bars3D { + id: barGraph + width: dataView.width + height: dataView.height + shadowQuality: AbstractGraph3D.ShadowQualityMedium + selectionMode: AbstractGraph3D.SelectionItem + theme: theme1 + barThickness: 0.7 + barSpacing: Qt.size(0.5, 0.5) + barSpacingRelative: false + scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricLeftHigh + columnAxis: graphAxes.column + rowAxis: graphAxes.row + valueAxis: graphAxes.value + + Bar3DSeries { + id: secondarySeries + visible: false + itemLabelFormat: "Expenses, @colLabel, @rowLabel: -@valueLabel" + rowColors: [color4 , color5, color6] + + onSelectedBarChanged: (position)=> handleSelectionChange(secondarySeries, position) + + ItemModelBarDataProxy { + id: secondaryProxy + itemModel: graphData.model + rowRole: "timestamp" + columnRole: "timestamp" + valueRole: "expenses" + rowRolePattern: /^(\d\d\d\d).*$/ + columnRolePattern: /^.*-(\d\d)$/ + valueRolePattern: /-/ + rowRoleReplace: "\\1" + columnRoleReplace: "\\1" + multiMatchBehavior: ItemModelBarDataProxy.MMBCumulative + } + } + + Bar3DSeries { + id: barSeries + itemLabelFormat: "Income, @colLabel, @rowLabel: @valueLabel" + rowColors: [color1, color2, color3] + + onSelectedBarChanged: (position)=> handleSelectionChange(barSeries, position) + + ItemModelBarDataProxy { + id: modelProxy + itemModel: graphData.model + rowRole: "timestamp" + columnRole: "timestamp" + valueRole: "income" + rowRolePattern: /^(\d\d\d\d).*$/ + columnRolePattern: /^.*-(\d\d)$/ + rowRoleReplace: "\\1" + columnRoleReplace: "\\1" + multiMatchBehavior: ItemModelBarDataProxy.MMBCumulative + } + } + } + } + + ColumnLayout { + id: tableViewLayout + + anchors.top: parent.top + anchors.left: parent.left + + HorizontalHeaderView { + id: header + property var columnNames: ["Month", "Expenses", "Income"] + + syncView: tableView + Layout.fillWidth: true + delegate: Text { + padding: 3 + text: header.columnNames[index] + } + + } + + TableView { + id: tableView + Layout.fillWidth: true + Layout.fillHeight: true + + reuseItems: false + clip: true + + model: TableModel { + id: tableModel + TableModelColumn { display: "timestamp" } + TableModelColumn { display: "expenses" } + TableModelColumn { display: "income" } + + rows: graphData.modelAsJsArray + } + + delegate: Rectangle { + implicitHeight: 30 + implicitWidth: tableView.width / 3 + color: row === currentRow ? "#e0e0e0" : "#ffffff" + MouseArea { + anchors.fill: parent + onClicked: currentRow = row + } + + Text { + id: delegateText + anchors.verticalCenter: parent.verticalCenter + width: parent.width + anchors.leftMargin: 4 + anchors.left: parent.left + anchors.right: parent.right + text: formattedText + property string formattedText: { + if (column === 0) { + if (display !== "") { + var pattern = /(\d\d\d\d)-(\d\d)/ + var matches = pattern.exec(display) + var colIndex = parseInt(matches[2], 10) - 1 + return matches[1] + " - " + graphAxes.column.labels[colIndex] + } + } else { + return display + } + } + } + } + } + } + + ColumnLayout { + id: controlLayout + spacing: 0 + + Button { + id: seriesToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Show Expenses" + clip: true + + onClicked: { + if (text === "Show Expenses") { + barSeries.visible = false + secondarySeries.visible = true + barGraph.valueAxis.labelFormat = "-%.2f M\u20AC" + secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: @valueLabel" + text = "Show Both" + } else if (text === "Show Both") { + barSeries.visible = true + barGraph.valueAxis.labelFormat = "%.2f M\u20AC" + secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: -@valueLabel" + text = "Show Income" + } else { // text === "Show Income" + secondarySeries.visible = false + text = "Show Expenses" + } + } + } + + Button { + id: themeToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Use theme 2" + clip: true + + onClicked: { + if (text === "Use theme 2") { + barGraph.theme = theme2 + text = "Use theme 1" + } else { + barGraph.theme = theme1 + text = "Use theme 2" + } + } + } + + Button { + id: barSeriesRowColorToggle + Layout.fillWidth: true + Layout.fillHeight: true + text: "Disable row colors" + + onClicked: { + if (text === "Disable row colors") { + toggleRowColorsForBarSeries(false) + toggleRowColorsForSecondarySeries(false) + text = "Enable row colors" + } else { + toggleRowColorsForBarSeries(true) + toggleRowColorsForSecondarySeries(true) + text = "Disable row colors" + } + } + } + } + + states: [ + State { + name: "landscape" + PropertyChanges { + target: dataView + width: mainview.width / 4 * 3 + height: mainview.height + } + PropertyChanges { + target: tableViewLayout + height: mainview.height - buttonLayoutHeight + anchors.right: dataView.left + anchors.left: mainview.left + anchors.bottom: undefined + } + PropertyChanges { + target: controlLayout + width: mainview.width / 4 + height: buttonLayoutHeight + anchors.top: tableViewLayout.bottom + anchors.bottom: mainview.bottom + anchors.left: mainview.left + anchors.right: dataView.left + } + }, + State { + name: "portrait" + PropertyChanges { + target: dataView + width: mainview.height / 4 * 3 + height: mainview.width + } + PropertyChanges { + target: tableViewLayout + height: mainview.width + anchors.right: controlLayout.left + anchors.left: mainview.left + anchors.bottom: dataView.top + } + PropertyChanges { + target: controlLayout + width: mainview.height / 4 + height: mainview.width / 4 + anchors.top: mainview.top + anchors.bottom: dataView.top + anchors.left: undefined + anchors.right: mainview.right + } + } + ] +} -- cgit v1.2.3