summaryrefslogtreecommitdiffstats
path: root/examples/datavisualization/graphgallery
diff options
context:
space:
mode:
Diffstat (limited to 'examples/datavisualization/graphgallery')
-rw-r--r--examples/datavisualization/graphgallery/CMakeLists.txt59
-rw-r--r--examples/datavisualization/graphgallery/bargraph.cpp338
-rw-r--r--examples/datavisualization/graphgallery/bargraph.h26
-rw-r--r--examples/datavisualization/graphgallery/data/raindata.txt158
-rw-r--r--examples/datavisualization/graphgallery/doc/src/graphgallery.qdoc282
-rw-r--r--examples/datavisualization/graphgallery/graphgallery.pro31
-rw-r--r--examples/datavisualization/graphgallery/graphgallery.qrc5
-rw-r--r--examples/datavisualization/graphgallery/graphmodifier.cpp439
-rw-r--r--examples/datavisualization/graphgallery/graphmodifier.h87
-rw-r--r--examples/datavisualization/graphgallery/main.cpp30
-rw-r--r--examples/datavisualization/graphgallery/rainfalldata.cpp115
-rw-r--r--examples/datavisualization/graphgallery/rainfalldata.h43
-rw-r--r--examples/datavisualization/graphgallery/variantbardatamapping.cpp109
-rw-r--r--examples/datavisualization/graphgallery/variantbardatamapping.h70
-rw-r--r--examples/datavisualization/graphgallery/variantbardataproxy.cpp125
-rw-r--r--examples/datavisualization/graphgallery/variantbardataproxy.h51
-rw-r--r--examples/datavisualization/graphgallery/variantdataset.cpp48
-rw-r--r--examples/datavisualization/graphgallery/variantdataset.h41
18 files changed, 2057 insertions, 0 deletions
diff --git a/examples/datavisualization/graphgallery/CMakeLists.txt b/examples/datavisualization/graphgallery/CMakeLists.txt
new file mode 100644
index 00000000..9a53ece6
--- /dev/null
+++ b/examples/datavisualization/graphgallery/CMakeLists.txt
@@ -0,0 +1,59 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(graphgallery LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}")
+
+find_package(Qt6 COMPONENTS Core)
+find_package(Qt6 COMPONENTS Gui)
+find_package(Qt6 COMPONENTS Widgets)
+find_package(Qt6 COMPONENTS DataVisualization)
+
+qt_add_executable(graphgallery
+ main.cpp
+ bargraph.cpp bargraph.h
+ graphmodifier.cpp graphmodifier.h
+ rainfalldata.cpp rainfalldata.h
+ variantbardatamapping.cpp variantbardatamapping.h
+ variantbardataproxy.cpp variantbardataproxy.h
+ variantdataset.cpp variantdataset.h
+)
+set_target_properties(graphgallery PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+target_link_libraries(graphgallery PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Widgets
+ Qt::DataVisualization
+)
+
+set(graphgallery_resource_files
+ "data/raindata.txt"
+)
+
+qt6_add_resources(graphgallery "graphgallery"
+ PREFIX
+ "/"
+ FILES
+ ${graphgallery_resource_files}
+)
+
+install(TARGETS graphgallery
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/datavisualization/graphgallery/bargraph.cpp b/examples/datavisualization/graphgallery/bargraph.cpp
new file mode 100644
index 00000000..b8d450f9
--- /dev/null
+++ b/examples/datavisualization/graphgallery/bargraph.cpp
@@ -0,0 +1,338 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "bargraph.h"
+#include "graphmodifier.h"
+
+#include <QtWidgets/qboxlayout.h>
+#include <QtWidgets/qpushbutton.h>
+#include <QtWidgets/qcheckbox.h>
+#include <QtWidgets/qslider.h>
+#include <QtWidgets/qfontcombobox.h>
+#include <QtWidgets/qlabel.h>
+#include <QtWidgets/qmessagebox.h>
+#include <QtWidgets/qradiobutton.h>
+#include <QtWidgets/qbuttongroup.h>
+#include <QtGui/qfontdatabase.h>
+
+BarGraph::BarGraph()
+{
+ //! [0]
+ m_barsGraph = new Q3DBars();
+ m_container = QWidget::createWindowContainer(m_barsGraph);
+ //! [0]
+}
+
+BarGraph::~BarGraph()
+{
+ delete m_container;
+}
+
+bool BarGraph::initialize()
+{
+ if (!m_barsGraph->hasContext()) {
+ QMessageBox msgBox;
+ msgBox.setText("Couldn't initialize the OpenGL context.");
+ msgBox.exec();
+ return false;
+ }
+
+ QSize screenSize = m_barsGraph->screen()->size();
+ m_container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5));
+ m_container->setMaximumSize(screenSize);
+ m_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_container->setFocusPolicy(Qt::StrongFocus);
+
+ //! [1]
+ m_barsWidget = new QWidget;
+ QHBoxLayout *hLayout = new QHBoxLayout(m_barsWidget);
+ QVBoxLayout *vLayout = new QVBoxLayout();
+ hLayout->addWidget(m_container, 1);
+ hLayout->addLayout(vLayout);
+ //! [1]
+
+ // TODO: Add a label into the main widget to show the title of the current example?
+ //m_barsWidget->setWindowTitle(QStringLiteral("Average temperatures in Oulu and Helsinki, Finland (2015-2022)"));
+
+ QComboBox *themeList = new QComboBox(m_barsWidget);
+ themeList->addItem(QStringLiteral("Qt"));
+ themeList->addItem(QStringLiteral("Primary Colors"));
+ themeList->addItem(QStringLiteral("Digia"));
+ themeList->addItem(QStringLiteral("Stone Moss"));
+ themeList->addItem(QStringLiteral("Army Blue"));
+ themeList->addItem(QStringLiteral("Retro"));
+ themeList->addItem(QStringLiteral("Ebony"));
+ themeList->addItem(QStringLiteral("Isabelle"));
+ themeList->setCurrentIndex(0);
+
+ QPushButton *labelButton = new QPushButton(m_barsWidget);
+ labelButton->setText(QStringLiteral("Change label style"));
+
+ QCheckBox *smoothCheckBox = new QCheckBox(m_barsWidget);
+ smoothCheckBox->setText(QStringLiteral("Smooth bars"));
+ smoothCheckBox->setChecked(false);
+
+ QComboBox *barStyleList = new QComboBox(m_barsWidget);
+ barStyleList->addItem(QStringLiteral("Bar"), int(QAbstract3DSeries::MeshBar));
+ barStyleList->addItem(QStringLiteral("Pyramid"), int(QAbstract3DSeries::MeshPyramid));
+ barStyleList->addItem(QStringLiteral("Cone"), int(QAbstract3DSeries::MeshCone));
+ barStyleList->addItem(QStringLiteral("Cylinder"), int(QAbstract3DSeries::MeshCylinder));
+ barStyleList->addItem(QStringLiteral("Bevel bar"), int(QAbstract3DSeries::MeshBevelBar));
+ barStyleList->addItem(QStringLiteral("Sphere"), int(QAbstract3DSeries::MeshSphere));
+ barStyleList->setCurrentIndex(4);
+
+ QPushButton *cameraButton = new QPushButton(m_barsWidget);
+ cameraButton->setText(QStringLiteral("Change camera preset"));
+
+ QPushButton *zoomToSelectedButton = new QPushButton(m_barsWidget);
+ zoomToSelectedButton->setText(QStringLiteral("Zoom to selected bar"));
+
+ QComboBox *selectionModeList = new QComboBox(m_barsWidget);
+ selectionModeList->addItem(QStringLiteral("None"),
+ int(QAbstract3DGraph::SelectionNone));
+ selectionModeList->addItem(QStringLiteral("Bar"),
+ int(QAbstract3DGraph::SelectionItem));
+ selectionModeList->addItem(QStringLiteral("Row"),
+ int(QAbstract3DGraph::SelectionRow));
+ selectionModeList->addItem(QStringLiteral("Bar and Row"),
+ int(QAbstract3DGraph::SelectionItemAndRow));
+ selectionModeList->addItem(QStringLiteral("Column"),
+ int(QAbstract3DGraph::SelectionColumn));
+ selectionModeList->addItem(QStringLiteral("Bar and Column"),
+ int(QAbstract3DGraph::SelectionItemAndColumn));
+ selectionModeList->addItem(QStringLiteral("Row and Column"),
+ int(QAbstract3DGraph::SelectionRowAndColumn));
+ selectionModeList->addItem(QStringLiteral("Bar, Row and Column"),
+ int(QAbstract3DGraph::SelectionItemRowAndColumn));
+ selectionModeList->addItem(QStringLiteral("Slice into Row"),
+ int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionRow));
+ selectionModeList->addItem(QStringLiteral("Slice into Row and Item"),
+ int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndRow));
+ selectionModeList->addItem(QStringLiteral("Slice into Column"),
+ int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionColumn));
+ selectionModeList->addItem(QStringLiteral("Slice into Column and Item"),
+ int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndColumn));
+ selectionModeList->addItem(QStringLiteral("Multi: Bar, Row, Col"),
+ int(QAbstract3DGraph::SelectionItemRowAndColumn
+ | QAbstract3DGraph::SelectionMultiSeries));
+ selectionModeList->addItem(QStringLiteral("Multi, Slice: Row, Item"),
+ int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndRow
+ | QAbstract3DGraph::SelectionMultiSeries));
+ selectionModeList->addItem(QStringLiteral("Multi, Slice: Col, Item"),
+ int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndColumn
+ | QAbstract3DGraph::SelectionMultiSeries));
+ selectionModeList->setCurrentIndex(1);
+
+ QCheckBox *backgroundCheckBox = new QCheckBox(m_barsWidget);
+ backgroundCheckBox->setText(QStringLiteral("Show background"));
+ backgroundCheckBox->setChecked(false);
+
+ QCheckBox *gridCheckBox = new QCheckBox(m_barsWidget);
+ gridCheckBox->setText(QStringLiteral("Show grid"));
+ gridCheckBox->setChecked(true);
+
+ QCheckBox *seriesCheckBox = new QCheckBox(m_barsWidget);
+ seriesCheckBox->setText(QStringLiteral("Show second series"));
+ seriesCheckBox->setChecked(false);
+
+ QCheckBox *reverseValueAxisCheckBox = new QCheckBox(m_barsWidget);
+ reverseValueAxisCheckBox->setText(QStringLiteral("Reverse value axis"));
+ reverseValueAxisCheckBox->setChecked(false);
+
+ QCheckBox *reflectionCheckBox = new QCheckBox(m_barsWidget);
+ reflectionCheckBox->setText(QStringLiteral("Show reflections"));
+ reflectionCheckBox->setChecked(false);
+
+ //! [3]
+ QSlider *rotationSliderX = new QSlider(Qt::Horizontal, m_barsWidget);
+ rotationSliderX->setTickInterval(30);
+ rotationSliderX->setTickPosition(QSlider::TicksBelow);
+ rotationSliderX->setMinimum(-180);
+ rotationSliderX->setValue(0);
+ rotationSliderX->setMaximum(180);
+ //! [3]
+ QSlider *rotationSliderY = new QSlider(Qt::Horizontal, m_barsWidget);
+ rotationSliderY->setTickInterval(15);
+ rotationSliderY->setTickPosition(QSlider::TicksAbove);
+ rotationSliderY->setMinimum(-90);
+ rotationSliderY->setValue(0);
+ rotationSliderY->setMaximum(90);
+
+ QSlider *fontSizeSlider = new QSlider(Qt::Horizontal, m_barsWidget);
+ fontSizeSlider->setTickInterval(10);
+ fontSizeSlider->setTickPosition(QSlider::TicksBelow);
+ fontSizeSlider->setMinimum(1);
+ fontSizeSlider->setValue(30);
+ fontSizeSlider->setMaximum(100);
+
+ QFontComboBox *fontList = new QFontComboBox(m_barsWidget);
+ fontList->setCurrentFont(QFont("Times New Roman"));
+
+ QComboBox *shadowQuality = new QComboBox(m_barsWidget);
+ shadowQuality->addItem(QStringLiteral("None"));
+ shadowQuality->addItem(QStringLiteral("Low"));
+ shadowQuality->addItem(QStringLiteral("Medium"));
+ shadowQuality->addItem(QStringLiteral("High"));
+ shadowQuality->addItem(QStringLiteral("Low Soft"));
+ shadowQuality->addItem(QStringLiteral("Medium Soft"));
+ shadowQuality->addItem(QStringLiteral("High Soft"));
+ shadowQuality->setCurrentIndex(5);
+
+ QComboBox *rangeList = new QComboBox(m_barsWidget);
+ rangeList->addItem(QStringLiteral("2015"));
+ rangeList->addItem(QStringLiteral("2016"));
+ rangeList->addItem(QStringLiteral("2017"));
+ rangeList->addItem(QStringLiteral("2018"));
+ rangeList->addItem(QStringLiteral("2019"));
+ rangeList->addItem(QStringLiteral("2020"));
+ rangeList->addItem(QStringLiteral("2021"));
+ rangeList->addItem(QStringLiteral("2022"));
+ rangeList->addItem(QStringLiteral("All"));
+ rangeList->setCurrentIndex(8);
+
+ QCheckBox *axisTitlesVisibleCB = new QCheckBox(m_barsWidget);
+ axisTitlesVisibleCB->setText(QStringLiteral("Axis titles visible"));
+ axisTitlesVisibleCB->setChecked(true);
+
+ QCheckBox *axisTitlesFixedCB = new QCheckBox(m_barsWidget);
+ axisTitlesFixedCB->setText(QStringLiteral("Axis titles fixed"));
+ axisTitlesFixedCB->setChecked(true);
+
+ QSlider *axisLabelRotationSlider = new QSlider(Qt::Horizontal, m_barsWidget);
+ axisLabelRotationSlider->setTickInterval(10);
+ axisLabelRotationSlider->setTickPosition(QSlider::TicksBelow);
+ axisLabelRotationSlider->setMinimum(0);
+ axisLabelRotationSlider->setValue(30);
+ axisLabelRotationSlider->setMaximum(90);
+
+ QButtonGroup *modeGroup = new QButtonGroup(m_barsWidget);
+ QRadioButton *modeWeather = new QRadioButton(QStringLiteral("Temperature Data"), m_barsWidget);
+ modeWeather->setChecked(true);
+ QRadioButton *modeCustomProxy = new QRadioButton(QStringLiteral("Custom Proxy Data"), m_barsWidget);
+ modeGroup->addButton(modeWeather);
+ modeGroup->addButton(modeCustomProxy);
+
+ //! [4]
+ vLayout->addWidget(new QLabel(QStringLiteral("Rotate horizontally")));
+ vLayout->addWidget(rotationSliderX, 0, Qt::AlignTop);
+ //! [4]
+ vLayout->addWidget(new QLabel(QStringLiteral("Rotate vertically")));
+ vLayout->addWidget(rotationSliderY, 0, Qt::AlignTop);
+ vLayout->addWidget(labelButton, 0, Qt::AlignTop);
+ vLayout->addWidget(cameraButton, 0, Qt::AlignTop);
+ vLayout->addWidget(zoomToSelectedButton, 0, Qt::AlignTop);
+ vLayout->addWidget(backgroundCheckBox);
+ vLayout->addWidget(gridCheckBox);
+ vLayout->addWidget(smoothCheckBox);
+ vLayout->addWidget(reflectionCheckBox);
+ vLayout->addWidget(seriesCheckBox);
+ vLayout->addWidget(reverseValueAxisCheckBox);
+ vLayout->addWidget(axisTitlesVisibleCB);
+ vLayout->addWidget(axisTitlesFixedCB);
+ vLayout->addWidget(new QLabel(QStringLiteral("Show year")));
+ vLayout->addWidget(rangeList);
+ vLayout->addWidget(new QLabel(QStringLiteral("Change bar style")));
+ vLayout->addWidget(barStyleList);
+ vLayout->addWidget(new QLabel(QStringLiteral("Change selection mode")));
+ vLayout->addWidget(selectionModeList);
+ vLayout->addWidget(new QLabel(QStringLiteral("Change theme")));
+ vLayout->addWidget(themeList);
+ vLayout->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")));
+ vLayout->addWidget(shadowQuality);
+ vLayout->addWidget(new QLabel(QStringLiteral("Change font")));
+ vLayout->addWidget(fontList);
+ vLayout->addWidget(new QLabel(QStringLiteral("Adjust font size")));
+ vLayout->addWidget(fontSizeSlider);
+ vLayout->addWidget(new QLabel(QStringLiteral("Axis label rotation")));
+ vLayout->addWidget(axisLabelRotationSlider, 0, Qt::AlignTop);
+ vLayout->addWidget(modeWeather, 0, Qt::AlignTop);
+ vLayout->addWidget(modeCustomProxy, 1, Qt::AlignTop);
+
+ //! [2]
+ GraphModifier *modifier = new GraphModifier(m_barsGraph);
+ //! [2]
+
+ //! [5]
+ QObject::connect(rotationSliderX, &QSlider::valueChanged, modifier, &GraphModifier::rotateX);
+ //! [5]
+ QObject::connect(rotationSliderY, &QSlider::valueChanged, modifier, &GraphModifier::rotateY);
+
+ QObject::connect(labelButton, &QPushButton::clicked, modifier,
+ &GraphModifier::changeLabelBackground);
+ QObject::connect(cameraButton, &QPushButton::clicked, modifier,
+ &GraphModifier::changePresetCamera);
+ QObject::connect(zoomToSelectedButton, &QPushButton::clicked, modifier,
+ &GraphModifier::zoomToSelectedBar);
+
+ QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setBackgroundEnabled);
+ QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setGridEnabled);
+ QObject::connect(smoothCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setSmoothBars);
+ QObject::connect(seriesCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setSeriesVisibility);
+ QObject::connect(reverseValueAxisCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setReverseValueAxis);
+ QObject::connect(reflectionCheckBox, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setReflection);
+
+ QObject::connect(modifier, &GraphModifier::backgroundEnabledChanged,
+ backgroundCheckBox, &QCheckBox::setChecked);
+ QObject::connect(modifier, &GraphModifier::gridEnabledChanged,
+ gridCheckBox, &QCheckBox::setChecked);
+
+ QObject::connect(rangeList, &QComboBox::currentIndexChanged, modifier,
+ &GraphModifier::changeRange);
+
+ QObject::connect(barStyleList, &QComboBox::currentIndexChanged, modifier,
+ &GraphModifier::changeStyle);
+
+ QObject::connect(selectionModeList, &QComboBox::currentIndexChanged, modifier,
+ &GraphModifier::changeSelectionMode);
+
+ QObject::connect(themeList, &QComboBox::currentIndexChanged, modifier,
+ &GraphModifier::changeTheme);
+
+ QObject::connect(shadowQuality, &QComboBox::currentIndexChanged, modifier,
+ &GraphModifier::changeShadowQuality);
+
+ QObject::connect(modifier, &GraphModifier::shadowQualityChanged, shadowQuality,
+ &QComboBox::setCurrentIndex);
+ QObject::connect(m_barsGraph, &Q3DBars::shadowQualityChanged, modifier,
+ &GraphModifier::shadowQualityUpdatedByVisual);
+
+ QObject::connect(fontSizeSlider, &QSlider::valueChanged, modifier,
+ &GraphModifier::changeFontSize);
+ QObject::connect(fontList, &QFontComboBox::currentFontChanged, modifier,
+ &GraphModifier::changeFont);
+
+ QObject::connect(modifier, &GraphModifier::fontSizeChanged, fontSizeSlider,
+ &QSlider::setValue);
+ QObject::connect(modifier, &GraphModifier::fontChanged, fontList,
+ &QFontComboBox::setCurrentFont);
+
+ QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setAxisTitleVisibility);
+ QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged, modifier,
+ &GraphModifier::setAxisTitleFixed);
+ QObject::connect(axisLabelRotationSlider, &QSlider::valueChanged, modifier,
+ &GraphModifier::changeLabelRotation);
+
+ QObject::connect(modeWeather, &QRadioButton::toggled, modifier,
+ &GraphModifier::setDataModeToWeather);
+ QObject::connect(modeCustomProxy, &QRadioButton::toggled, modifier,
+ &GraphModifier::setDataModeToCustom);
+ QObject::connect(modeWeather, &QRadioButton::toggled, seriesCheckBox,
+ &QCheckBox::setEnabled);
+ QObject::connect(modeWeather, &QRadioButton::toggled, rangeList,
+ &QComboBox::setEnabled);
+ QObject::connect(modeWeather, &QRadioButton::toggled, axisTitlesVisibleCB,
+ &QCheckBox::setEnabled);
+ QObject::connect(modeWeather, &QRadioButton::toggled, axisTitlesFixedCB,
+ &QCheckBox::setEnabled);
+ QObject::connect(modeWeather, &QRadioButton::toggled, axisLabelRotationSlider,
+ &QSlider::setEnabled);
+
+ return true;
+}
diff --git a/examples/datavisualization/graphgallery/bargraph.h b/examples/datavisualization/graphgallery/bargraph.h
new file mode 100644
index 00000000..dae991f9
--- /dev/null
+++ b/examples/datavisualization/graphgallery/bargraph.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef BARGRAPH_H
+#define BARGRAPH_H
+
+#include <QtDataVisualization/q3dbars.h>
+#include <QtCore/qobject.h>
+
+class BarGraph : public QObject
+{
+ Q_OBJECT
+public:
+ explicit BarGraph();
+ ~BarGraph();
+
+ bool initialize();
+ QWidget *barsWidget() { return m_barsWidget; }
+
+private:
+ Q3DBars *m_barsGraph = nullptr;
+ QWidget *m_container = nullptr;
+ QWidget *m_barsWidget = nullptr;
+};
+
+#endif
diff --git a/examples/datavisualization/graphgallery/data/raindata.txt b/examples/datavisualization/graphgallery/data/raindata.txt
new file mode 100644
index 00000000..d9558921
--- /dev/null
+++ b/examples/datavisualization/graphgallery/data/raindata.txt
@@ -0,0 +1,158 @@
+# Rainfall per month from 2010 to 2022 in Northern Finland (Oulu)
+# Format: year, month, rainfall
+2010,1, 0,
+2010,2, 3.4,
+2010,3, 52,
+2010,4, 33.8,
+2010,5, 45.6,
+2010,6, 43.8,
+2010,7, 104.6,
+2010,8, 105.4,
+2010,9, 107.2,
+2010,10,38.6,
+2010,11,17.8,
+2010,12,0,
+2011,1, 8.2,
+2011,2, 1.6,
+2011,3, 27.4,
+2011,4, 15.8,
+2011,5, 57.6,
+2011,6, 85.2,
+2011,7, 127,
+2011,8, 72.2,
+2011,9, 82.2,
+2011,10,62.4,
+2011,11,31.6,
+2011,12,53.8,
+2012,1, 0,
+2012,2, 5,
+2012,3, 32.4,
+2012,4, 57.6,
+2012,5, 71.4,
+2012,6, 60.8,
+2012,7, 109,
+2012,8, 43.6,
+2012,9, 79.4,
+2012,10,117.2,
+2012,11,59,
+2012,12,0.2,
+2013,1, 28,
+2013,2, 19,
+2013,3, 0,
+2013,4, 37.6,
+2013,5, 44.2,
+2013,6, 104.8,
+2013,7, 84.2,
+2013,8, 57.2,
+2013,9, 37.2,
+2013,10,64.6,
+2013,11,77.8,
+2013,12,92.8,
+2014,1, 23.8,
+2014,2, 23.6,
+2014,3, 15.4,
+2014,4, 13.2,
+2014,5, 36.4,
+2014,6, 26.4,
+2014,7, 95.8,
+2014,8, 81.8,
+2014,9, 13.8,
+2014,10,94.6,
+2014,11,44.6,
+2014,12,31,
+2015,1, 37.4,
+2015,2, 21,
+2015,3, 42,
+2015,4, 8.8,
+2015,5, 82.4,
+2015,6, 150,
+2015,7, 56.8,
+2015,8, 67.2,
+2015,9, 131.2,
+2015,10,38.4,
+2015,11,83.4,
+2015,12,47.8,
+2016,1, 12.4,
+2016,2, 34.8,
+2016,3, 29,
+2016,4, 40.4,
+2016,5, 32.4,
+2016,6, 80.2,
+2016,7, 102.6,
+2016,8, 95.6,
+2016,9, 40.2,
+2016,10,7.8,
+2016,11,39.6,
+2016,12,8.8,
+2017,1, 9.4,
+2017,2, 6.6,
+2017,3, 29,
+2017,4, 46.2,
+2017,5, 43.2,
+2017,6, 25.2,
+2017,7, 72.4,
+2017,8, 58.8,
+2017,9, 68.8,
+2017,10,45.8,
+2017,11,36.8,
+2017,12,29.6,
+2018,1, 19.8,
+2018,2, 0.8,
+2018,3, 4,
+2018,4, 23.2,
+2018,5, 13.2,
+2018,6, 62.8,
+2018,7, 33,
+2018,8, 96.6,
+2018,9, 72.6,
+2018,10,48.8,
+2018,11,31.8,
+2018,12,12.8,
+2019,1, 0.2,
+2019,2, 24.8,
+2019,3, 32,
+2019,4, 8.8,
+2019,5, 71.4,
+2019,6, 65.8,
+2019,7, 17.6,
+2019,8, 90,
+2019,9, 50,
+2019,10,77,
+2019,11,27,
+2019,12,43.2,
+2020,1, 28.8,
+2020,2, 45,
+2020,3, 18.6,
+2020,4, 13,
+2020,5, 30.8,
+2020,6, 21.4,
+2020,7, 163.6,
+2020,8, 12,
+2020,9, 102.4,
+2020,10,133.2,
+2020,11,69.8,
+2020,12,40.6,
+2021,1, 0.4,
+2021,2, 21.6,
+2021,3, 24,
+2021,4, 51.4,
+2021,5, 76.4,
+2021,6, 29.2,
+2021,7, 36.4,
+2021,8, 116,
+2021,9, 72.4,
+2021,10,93.4,
+2021,11,21,
+2021,12,10.2,
+2022,1, 8.6,
+2022,2, 6.6,
+2022,3, 5.2,
+2022,4, 15.2,
+2022,5, 37.6,
+2022,6, 45,
+2022,7, 67.4,
+2022,8, 161.6,
+2022,9, 22.8,
+2022,10,75.2,
+2022,11,21.8,
+2022,12,0.2
diff --git a/examples/datavisualization/graphgallery/doc/src/graphgallery.qdoc b/examples/datavisualization/graphgallery/doc/src/graphgallery.qdoc
new file mode 100644
index 00000000..e2695592
--- /dev/null
+++ b/examples/datavisualization/graphgallery/doc/src/graphgallery.qdoc
@@ -0,0 +1,282 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example graphgallery
+ \meta tags {DataVisualization, Q3DBars, Bar Graph, Custom Proxy}
+ \meta category {Graphics}
+ \title Graph Gallery
+ \ingroup qtdatavisualization_examples
+ \brief Gallery of Bar, Scatter, and Surface graphs.
+
+ \e {Graph Gallery} demonstrates all three graph types and some of their special features.
+ The graphs have their own tabs in the application.
+
+ \image qmlgraphgallery-example.png
+
+ \include examples-run.qdocinc
+
+ \section1 Bar Graph
+
+ In the \uicontrol {Bar Graph} tab, create a 3D bar graph using Q3DBars and combine the use of
+ widgets to adjust various bar graph qualities. The example shows how to:
+
+ \list
+ \li Create an application with Q3DBars and some widgets
+ \li Use QBar3DSeries and QBarDataProxy to set data to the graph
+ \li Adjust some graph and series properties using widget controls
+ \li Select a row or a column by clicking an axis label
+ \li Create a custom proxy to use with Q3DBars
+ \endlist
+
+ For information about interacting with the graph, see
+ \l{Qt Data Visualization Interacting with Data}{this page}.
+
+ \section2 Creating the Application
+
+ First, in \c{bargraph.cpp}, instantiate Q3DBars and a window container for it:
+
+ \snippet graphgallery/bargraph.cpp 0
+
+ The call to QWidget::createWindowContainer is required, as all data visualization graph classes
+ (Q3DBars, Q3DScatter, Q3DSurface) inherit QWindow. This is the only way to use a class that
+ inherits QWindow as a widget.
+
+ Then, create horizontal and vertical layouts. Add the graph and the vertical layout to the
+ horizontal one:
+
+ \snippet graphgallery/bargraph.cpp 1
+
+ Next, create another class to handle the data addition and other interaction with the
+ graph:
+
+ \snippet graphgallery/bargraph.cpp 2
+
+ \section2 Setting up the Graph
+
+ Set up the graph in the constructor of the \c GraphModifier class:
+
+ \snippet graphgallery/graphmodifier.cpp 0
+
+ First, create the axes and the series into member variables to support changing them easily:
+
+ \snippet graphgallery/graphmodifier.cpp 1
+
+ Then, set some visual qualities for the graph:
+
+ \snippet graphgallery/graphmodifier.cpp 2
+
+ Set up the axes and make them the active axes of the graph:
+
+ \snippet graphgallery/graphmodifier.cpp 3
+
+ Give axis labels a small autorotation angle with setLabelAutoRotation() to make them orient
+ slightly toward the camera. This improves axis label readability at extreme camera angles.
+
+ Next, initialize the visual properties of the series. Note that the second series is initially
+ not visible:
+
+ \snippet graphgallery/graphmodifier.cpp 4
+
+ Add the series to the graph:
+
+ \snippet graphgallery/graphmodifier.cpp 5
+
+ Finally, set the camera angle by calling the same method the camera angle change button
+ in the UI uses to cycle through various camera angles:
+
+ \snippet graphgallery/graphmodifier.cpp 6
+
+ The camera is controlled via the scene object of the graph:
+
+ \snippet graphgallery/graphmodifier.cpp 7
+
+ For more information about using scene and cameras, see Q3DScene and Q3DCamera.
+
+ \section2 Adding Data to the Graph
+
+ At the end of the constructor, call a method that sets up the data:
+
+ \snippet graphgallery/graphmodifier.cpp 8
+
+ This method adds data to the proxies of the two series:
+
+ \snippet graphgallery/graphmodifier.cpp 9a
+ \dots 0
+ \snippet graphgallery/graphmodifier.cpp 9b
+
+ \section2 Using Widgets to Control the Graph
+
+ Continue by adding some widgets in \c{bargraph.cpp}. Add a slider:
+
+ \snippet graphgallery/bargraph.cpp 3
+
+ Use the slider to rotate the graph instead of just using a mouse or touch. Add it to the
+ vertical layout:
+
+ \snippet graphgallery/bargraph.cpp 4
+
+ Then, connect it to a method in \c GraphModifier:
+
+ \snippet graphgallery/bargraph.cpp 5
+
+ Create a slot in \c GraphModifier for the signal connection. The camera is controlled via the
+ scene object. This time, specify the actual camera position along the orbit around the center
+ point, instead of specifying a preset camera angle:
+
+ \snippet graphgallery/graphmodifier.cpp 10
+
+ You can now use the slider to rotate the graph.
+
+ Add more widgets to the vertical layout to control:
+
+ \list
+ \li Graph rotation
+ \li Label style
+ \li Camera preset
+ \li Background visibility
+ \li Grid visibility
+ \li Bar shading smoothness
+ \li Visibility of the second bar series
+ \li Value axis direction
+ \li Axis title visibility and rotation
+ \li Data range to be shown
+ \li Bar style
+ \li Selection mode
+ \li Theme
+ \li Shadow quality
+ \li Font
+ \li Font size
+ \li Axis label rotation
+ \li Data mode
+ \endlist
+
+ Some widget controls are intentionally disabled when in the \uicontrol {Custom Proxy Data}
+ data mode.
+
+ \section2 Selecting a Row or Column by Clicking an Axis Label
+
+ Selection by axis label is default functionality for bar graphs. As an example, you can select
+ rows by clicking an axis label in the following way:
+
+ \list 1
+ \li Change selection mode to \c SelectionRow
+ \li Click a year label
+ \li The row with the clicked year is selected
+ \endlist
+
+ The same method works with \c SelectionSlice and \c SelectionItem flags, as long as
+ either \c SelectionRow or \c SelectionColumn is set as well.
+
+ \section2 Zooming to Selection
+
+ As an example of adjusting the camera target, implement an animation of zooming to
+ selection via a button press. Animation initializations are done in the constructor:
+
+ \snippet graphgallery/graphmodifier.cpp 11
+
+ Function \c{GraphModifier::zoomToSelectedBar()} contains the zooming functionality.
+ QPropertyAnimation \c m_animationCameraTarget targets Q3DCamera::target property,
+ which takes a value normalized to the range (-1, 1).
+
+ Figure out where the selected bar is relative to axes, and use that as the end value for
+ \c{m_animationCameraTarget}:
+
+ \snippet graphgallery/graphmodifier.cpp 12
+ \dots 0
+ \snippet graphgallery/graphmodifier.cpp 13
+
+ Then, rotate the camera so that it always points approximately to the center of
+ the graph at the end of the animation:
+
+ \snippet graphgallery/graphmodifier.cpp 14
+
+ \section2 Custom Proxy for Data
+
+ By toggling \uicontrol {Custom Proxy Data} data mode on, a custom dataset and the corresponding
+ proxy are taken into use.
+
+ Define a simple flexible data set, \c{VariantDataSet}, where each data item is
+ a variant list. Each item can have multiple values, identified by their index in
+ the list. In this case, the data set is storing monthly rainfall data, where the value in
+ index zero is the year, the value in index one is the month, and the value in index two is
+ the amount of rainfall in that month.
+
+ The custom proxy is similar to itemmodel-based proxies provided by Qt Data Visualization, and
+ it requires mapping to interpret the data.
+
+ \section3 VariantDataSet
+
+ Define the data items as QVariantList objects. Add functionality for clearing the data set and
+ querying for a reference to the data contained in the set. Also, add signals to be emitted when
+ data is added or the set is cleared:
+
+ \snippet graphgallery/variantdataset.h 0
+ \dots 0
+ \codeline
+ \snippet graphgallery/variantdataset.h 1
+
+ \section3 VariantBarDataProxy
+
+ Subclass \c VariantBarDataProxy from QBarDataProxy and provide a simple API of getters and
+ setters for the data set and the mapping:
+
+ \snippet graphgallery/variantbardataproxy.h 0
+ \dots 0
+ \codeline
+ \snippet graphgallery/variantbardataproxy.h 1
+
+ The proxy listens for the changes in the data set and the mapping, and resolves the data set
+ if any changes are detected. This is not a particularly efficient implementation, as any change
+ will cause re-resolving of the entire data set, but that is not an issue for this example.
+
+ In \c resolveDataSet() method, sort the variant data values into rows and columns based on the
+ mapping. This is very similar to how QItemModelBarDataProxy handles mapping, except you use
+ list indexes instead of item model roles here. Once the values are sorted, generate
+ \c QBarDataArray out of them, and call the \c resetArray() method in the parent class:
+
+ \snippet graphgallery/variantbardataproxy.cpp 0
+
+ \section3 VariantBarDataMapping
+
+ Store the mapping information between \c VariantDataSet data item indexes and rows, columns,
+ and values of \c QBarDataArray in \c VariantBarDataMapping. It contains the lists of rows and
+ columns to be included in the resolved data:
+
+ \snippet graphgallery/variantbardatamapping.h 0
+ \dots 0
+ \codeline
+ \snippet graphgallery/variantbardatamapping.h 1
+ \dots 0
+ \codeline
+ \snippet graphgallery/variantbardatamapping.h 2
+ \dots 0
+ \codeline
+ \snippet graphgallery/variantbardatamapping.h 3
+
+ The primary way to use a \c VariantBarDataMapping object is to give the mappings in the
+ constructor, though you can use the \c remap() method to set them later, either individually or
+ all together. Emit a signal if mapping changes. The outcome is a simplified version of the
+ mapping functionality of QItemModelBarDataProxy, adapted to work with variant lists instead of
+ item models.
+
+ \section3 RainfallData
+
+ Handle the setup of QBar3DSeries with the custom proxy in the \c RainfallData class:
+
+ \snippet graphgallery/rainfalldata.cpp 0
+
+ Populate the variant data set in the \c addDataSet() method:
+
+ \snippet graphgallery/rainfalldata.cpp 1
+ \dots
+
+ Add the data set to the custom proxy and set the mapping:
+
+ \snippet graphgallery/rainfalldata.cpp 2
+
+ Finally, add a function for getting the created series for displaying:
+
+ \snippet graphgallery/rainfalldata.h 0
+
+*/
diff --git a/examples/datavisualization/graphgallery/graphgallery.pro b/examples/datavisualization/graphgallery/graphgallery.pro
new file mode 100644
index 00000000..1f8ce428
--- /dev/null
+++ b/examples/datavisualization/graphgallery/graphgallery.pro
@@ -0,0 +1,31 @@
+android|ios|winrt {
+ error( "This example is not supported for android, ios, or winrt." )
+}
+
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+QT += widgets
+requires(qtConfig(fontcombobox))
+requires(qtConfig(combobox))
+
+SOURCES += main.cpp \
+ graphmodifier.cpp \
+ rainfalldata.cpp \
+ variantdataset.cpp \
+ variantbardataproxy.cpp \
+ variantbardatamapping.cpp \
+
+HEADERS += \
+ graphmodifier.h \
+ rainfalldata.h \
+ variantdataset.h \
+ variantbardataproxy.h \
+ variantbardatamapping.h
+
+RESOURCES += graphgallery.qrc
+
+OTHER_FILES += doc/src/* \
+ doc/images/* \
+ data/raindata.txt
diff --git a/examples/datavisualization/graphgallery/graphgallery.qrc b/examples/datavisualization/graphgallery/graphgallery.qrc
new file mode 100644
index 00000000..53cd4915
--- /dev/null
+++ b/examples/datavisualization/graphgallery/graphgallery.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/raindata.txt</file>
+ </qresource>
+</RCC>
diff --git a/examples/datavisualization/graphgallery/graphmodifier.cpp b/examples/datavisualization/graphgallery/graphmodifier.cpp
new file mode 100644
index 00000000..56ec102a
--- /dev/null
+++ b/examples/datavisualization/graphgallery/graphmodifier.cpp
@@ -0,0 +1,439 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "graphmodifier.h"
+#include "rainfalldata.h"
+
+#include <QtDataVisualization/qcategory3daxis.h>
+#include <QtDataVisualization/qvalue3daxis.h>
+#include <QtDataVisualization/qbardataproxy.h>
+#include <QtDataVisualization/q3dscene.h>
+#include <QtDataVisualization/q3dcamera.h>
+#include <QtDataVisualization/qbar3dseries.h>
+#include <QtDataVisualization/q3dtheme.h>
+#include <QtCore/qmath.h>
+#include <QtWidgets/qcombobox.h>
+
+// TODO: Many of the values do not affect custom proxy series now - should be fixed
+
+//! [0]
+GraphModifier::GraphModifier(Q3DBars *bargraph)
+ : m_graph(bargraph),
+ //! [0]
+ //! [1]
+ m_temperatureAxis(new QValue3DAxis),
+ m_yearAxis(new QCategory3DAxis),
+ m_monthAxis(new QCategory3DAxis),
+ m_primarySeries(new QBar3DSeries),
+ m_secondarySeries(new QBar3DSeries)
+ //! [1]
+{
+ //! [2]
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftMedium);
+ m_graph->activeTheme()->setBackgroundEnabled(false);
+ m_graph->activeTheme()->setFont(QFont("Times New Roman", m_fontSize));
+ m_graph->activeTheme()->setLabelBackgroundEnabled(true);
+ m_graph->setMultiSeriesUniform(true);
+ //! [2]
+
+ m_months << "January" << "February" << "March" << "April" << "May" << "June" << "July" << "August" << "September" << "October" << "November" << "December";
+ m_years << "2015" << "2016" << "2017" << "2018" << "2019" << "2020" << "2021" << "2022";
+
+ //! [3]
+ m_temperatureAxis->setTitle("Average temperature");
+ m_temperatureAxis->setSegmentCount(m_segments);
+ m_temperatureAxis->setSubSegmentCount(m_subSegments);
+ m_temperatureAxis->setRange(m_minval, m_maxval);
+ m_temperatureAxis->setLabelFormat(QString(QStringLiteral("%.1f ") + m_celsiusString));
+ m_temperatureAxis->setLabelAutoRotation(30.0f);
+ m_temperatureAxis->setTitleVisible(true);
+
+ m_yearAxis->setTitle("Year");
+ m_yearAxis->setLabelAutoRotation(30.0f);
+ m_yearAxis->setTitleVisible(true);
+ m_monthAxis->setTitle("Month");
+ m_monthAxis->setLabelAutoRotation(30.0f);
+ m_monthAxis->setTitleVisible(true);
+
+ m_graph->setValueAxis(m_temperatureAxis);
+ m_graph->setRowAxis(m_yearAxis);
+ m_graph->setColumnAxis(m_monthAxis);
+ //! [3]
+
+ //! [4]
+ m_primarySeries->setItemLabelFormat(QStringLiteral("Oulu - @colLabel @rowLabel: @valueLabel"));
+ m_primarySeries->setMesh(QAbstract3DSeries::MeshBevelBar);
+ m_primarySeries->setMeshSmooth(false);
+
+ m_secondarySeries->setItemLabelFormat(QStringLiteral("Helsinki - @colLabel @rowLabel: @valueLabel"));
+ m_secondarySeries->setMesh(QAbstract3DSeries::MeshBevelBar);
+ m_secondarySeries->setMeshSmooth(false);
+ m_secondarySeries->setVisible(false);
+ //! [4]
+
+ //! [5]
+ m_graph->addSeries(m_primarySeries);
+ m_graph->addSeries(m_secondarySeries);
+ //! [5]
+
+ //! [6]
+ changePresetCamera();
+ //! [6]
+
+ //! [8]
+ resetTemperatureData();
+ //! [8]
+
+ // Set up property animations for zooming to the selected bar
+ //! [11]
+ Q3DCamera *camera = m_graph->scene()->activeCamera();
+ m_defaultAngleX = camera->xRotation();
+ m_defaultAngleY = camera->yRotation();
+ m_defaultZoom = camera->zoomLevel();
+ m_defaultTarget = camera->target();
+
+ m_animationCameraX.setTargetObject(camera);
+ m_animationCameraY.setTargetObject(camera);
+ m_animationCameraZoom.setTargetObject(camera);
+ m_animationCameraTarget.setTargetObject(camera);
+
+ m_animationCameraX.setPropertyName("xRotation");
+ m_animationCameraY.setPropertyName("yRotation");
+ m_animationCameraZoom.setPropertyName("zoomLevel");
+ m_animationCameraTarget.setPropertyName("target");
+
+ int duration = 1700;
+ m_animationCameraX.setDuration(duration);
+ m_animationCameraY.setDuration(duration);
+ m_animationCameraZoom.setDuration(duration);
+ m_animationCameraTarget.setDuration(duration);
+
+ // The zoom always first zooms out above the graph and then zooms in
+ qreal zoomOutFraction = 0.3;
+ m_animationCameraX.setKeyValueAt(zoomOutFraction, QVariant::fromValue(0.0f));
+ m_animationCameraY.setKeyValueAt(zoomOutFraction, QVariant::fromValue(90.0f));
+ m_animationCameraZoom.setKeyValueAt(zoomOutFraction, QVariant::fromValue(50.0f));
+ m_animationCameraTarget.setKeyValueAt(zoomOutFraction,
+ QVariant::fromValue(QVector3D(0.0f, 0.0f, 0.0f)));
+ //! [11]
+
+ m_customData = new RainfallData();
+}
+
+GraphModifier::~GraphModifier()
+{
+ delete m_customData;
+ delete m_graph;
+}
+
+void GraphModifier::resetTemperatureData()
+{
+ //! [9a]
+ // Set up data
+ static const float tempOulu[8][12] = {
+ {-7.4f, -2.4f, 0.0f, 3.0f, 8.2f, 11.6f, 14.7f, 15.4f, 11.4f, 4.2f, 2.1f, -2.3f}, // 2015
+ {-13.4f, -3.9f, -1.8f, 3.1f, 10.6f, 13.7f, 17.8f, 13.6f, 10.7f, 3.5f, -3.1f, -4.2f}, // 2016
+ //! [9a]
+ {-5.7f, -6.7f, -3.0f, -0.1f, 4.7f, 12.4f, 16.1f, 14.1f, 9.4f, 3.0f, -0.3f, -3.2f}, // 2017
+ {-6.4f, -11.9f, -7.4f, 1.9f, 11.4f, 12.4f, 21.5f, 16.1f, 11.0f, 4.4f, 2.1f, -4.1f}, // 2018
+ {-11.7f, -6.1f, -2.4f, 3.9f, 7.2f, 14.5f, 15.6f, 14.4f, 8.5f, 2.0f, -3.0f, -1.5f}, // 2019
+ {-2.1f, -3.4f, -1.8f, 0.6f, 7.0f, 17.1f, 15.6f, 15.4f, 11.1f, 5.6f, 1.9f, -1.7f}, // 2020
+ {-9.6f, -11.6f, -3.2f, 2.4f, 7.8f, 17.3f, 19.4f, 14.2f, 8.0f, 5.2f, -2.2f, -8.6f}, // 2021
+ {-7.3f, -6.4f, -1.8f, 1.3f, 8.1f, 15.5f, 17.6f, 17.6f, 9.1f, 5.4f, -1.5f, -4.4f} // 2022
+ };
+
+ static const float tempHelsinki[8][12] = {
+ {-2.0f, -0.1f, 1.8f, 5.1f, 9.7f, 13.7f, 16.3f, 17.3f, 12.7f, 5.4f, 4.6f, 2.1f}, // 2015
+ {-10.3f, -0.6f, 0.0f, 4.9f, 14.3f, 15.7f, 17.7f, 16.0f, 12.7f, 4.6f, -1.0f, -0.9f}, // 2016
+ {-2.9f, -3.3f, 0.7f, 2.3f, 9.9f, 13.8f, 16.1f, 15.9f, 11.4f, 5.0f, 2.7f, 0.7f}, // 2017
+ {-2.2f, -8.4f, -4.7f, 5.0f, 15.3f, 15.8f, 21.2f, 18.2f, 13.3f, 6.7f, 2.8f, -2.0f}, // 2018
+ {-6.2f, -0.5f, -0.3f, 6.8f, 10.6f, 17.9f, 17.5f, 16.8f, 11.3f, 5.2f, 1.8f, 1.4f}, // 2019
+ {1.9f, 0.5f, 1.7f, 4.5f, 9.5f, 18.4f, 16.5f, 16.8f, 13.0f, 8.2f, 4.4f, 0.9f}, // 2020
+ {-4.7f, -8.1f, -0.9f, 4.5f, 10.4f, 19.2f, 20.9f, 15.4f, 9.5f, 8.0f, 1.5f, -6.7f}, // 2021
+ {-3.3f, -2.2f, -0.2f, 3.3f, 9.6f, 16.9f, 18.1f, 18.9f, 9.2f, 7.6f, 2.3f, -3.4f} // 2022
+ };
+
+ // Create data arrays
+ //! [9b]
+ QBarDataArray *dataSet = new QBarDataArray;
+ QBarDataArray *dataSet2 = new QBarDataArray;
+ QBarDataRow *dataRow = nullptr;
+ QBarDataRow *dataRow2= nullptr;
+
+ dataSet->reserve(m_years.size());
+ for (int year = 0; year < m_years.size(); year++) {
+ // Create a data row
+ dataRow = new QBarDataRow(m_months.size());
+ dataRow2 = new QBarDataRow(m_months.size());
+ for (int month = 0; month < m_months.size(); month++) {
+ // Add data to the row
+ (*dataRow)[month].setValue(tempOulu[year][month]);
+ (*dataRow2)[month].setValue(tempHelsinki[year][month]);
+ }
+ // Add the row to the set
+ dataSet->append(dataRow);
+ dataSet2->append(dataRow2);
+ }
+
+ // Add data to the data proxy (the data proxy assumes ownership of it)
+ m_primarySeries->dataProxy()->resetArray(dataSet, m_years, m_months);
+ m_secondarySeries->dataProxy()->resetArray(dataSet2, m_years, m_months);
+ //! [9b]
+}
+
+void GraphModifier::changeRange(int range)
+{
+ if (range >= m_years.count())
+ m_yearAxis->setRange(0, m_years.count() - 1);
+ else
+ m_yearAxis->setRange(range, range);
+}
+
+void GraphModifier::changeStyle(int style)
+{
+ QComboBox *comboBox = qobject_cast<QComboBox *>(sender());
+ if (comboBox) {
+ m_barMesh = QAbstract3DSeries::Mesh(comboBox->itemData(style).toInt());
+ m_primarySeries->setMesh(m_barMesh);
+ m_secondarySeries->setMesh(m_barMesh);
+ m_customData->customSeries()->setMesh(m_barMesh);
+ }
+}
+
+void GraphModifier::changePresetCamera()
+{
+ m_animationCameraX.stop();
+ m_animationCameraY.stop();
+ m_animationCameraZoom.stop();
+ m_animationCameraTarget.stop();
+
+ // Restore camera target in case animation has changed it
+ m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));
+
+ //! [7]
+ static int preset = Q3DCamera::CameraPresetFront;
+
+ m_graph->scene()->activeCamera()->setCameraPreset((Q3DCamera::CameraPreset)preset);
+
+ if (++preset > Q3DCamera::CameraPresetDirectlyBelow)
+ preset = Q3DCamera::CameraPresetFrontLow;
+ //! [7]
+}
+
+void GraphModifier::changeTheme(int theme)
+{
+ Q3DTheme *currentTheme = m_graph->activeTheme();
+ currentTheme->setType(Q3DTheme::Theme(theme));
+ emit backgroundEnabledChanged(currentTheme->isBackgroundEnabled());
+ emit gridEnabledChanged(currentTheme->isGridEnabled());
+ emit fontChanged(currentTheme->font());
+ emit fontSizeChanged(currentTheme->font().pointSize());
+}
+
+void GraphModifier::changeLabelBackground()
+{
+ m_graph->activeTheme()->setLabelBackgroundEnabled(!m_graph->activeTheme()->isLabelBackgroundEnabled());
+}
+
+void GraphModifier::changeSelectionMode(int selectionMode)
+{
+ QComboBox *comboBox = qobject_cast<QComboBox *>(sender());
+ if (comboBox) {
+ int flags = comboBox->itemData(selectionMode).toInt();
+ m_graph->setSelectionMode(QAbstract3DGraph::SelectionFlags(flags));
+ }
+}
+
+void GraphModifier::changeFont(const QFont &font)
+{
+ QFont newFont = font;
+ m_graph->activeTheme()->setFont(newFont);
+}
+
+void GraphModifier::changeFontSize(int fontsize)
+{
+ m_fontSize = fontsize;
+ QFont font = m_graph->activeTheme()->font();
+ font.setPointSize(m_fontSize);
+ m_graph->activeTheme()->setFont(font);
+}
+
+void GraphModifier::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality sq)
+{
+ int quality = int(sq);
+ // Updates the UI component to show correct shadow quality
+ emit shadowQualityChanged(quality);
+}
+
+void GraphModifier::changeLabelRotation(int rotation)
+{
+ m_temperatureAxis->setLabelAutoRotation(float(rotation));
+ m_monthAxis->setLabelAutoRotation(float(rotation));
+ m_yearAxis->setLabelAutoRotation(float(rotation));
+}
+
+void GraphModifier::setAxisTitleVisibility(bool enabled)
+{
+ m_temperatureAxis->setTitleVisible(enabled);
+ m_monthAxis->setTitleVisible(enabled);
+ m_yearAxis->setTitleVisible(enabled);
+}
+
+void GraphModifier::setAxisTitleFixed(bool enabled)
+{
+ m_temperatureAxis->setTitleFixed(enabled);
+ m_monthAxis->setTitleFixed(enabled);
+ m_yearAxis->setTitleFixed(enabled);
+}
+
+void GraphModifier::zoomToSelectedBar()
+{
+ m_animationCameraX.stop();
+ m_animationCameraY.stop();
+ m_animationCameraZoom.stop();
+ m_animationCameraTarget.stop();
+
+ Q3DCamera *camera = m_graph->scene()->activeCamera();
+ float currentX = camera->xRotation();
+ float currentY = camera->yRotation();
+ float currentZoom = camera->zoomLevel();
+ QVector3D currentTarget = camera->target();
+
+ m_animationCameraX.setStartValue(QVariant::fromValue(currentX));
+ m_animationCameraY.setStartValue(QVariant::fromValue(currentY));
+ m_animationCameraZoom.setStartValue(QVariant::fromValue(currentZoom));
+ m_animationCameraTarget.setStartValue(QVariant::fromValue(currentTarget));
+
+ QPoint selectedBar = m_graph->selectedSeries()
+ ? m_graph->selectedSeries()->selectedBar()
+ : QBar3DSeries::invalidSelectionPosition();
+
+ if (selectedBar != QBar3DSeries::invalidSelectionPosition()) {
+ // Normalize selected bar position within axis range to determine target coordinates
+ //! [12]
+ QVector3D endTarget;
+ float xMin = m_graph->columnAxis()->min();
+ float xRange = m_graph->columnAxis()->max() - xMin;
+ float zMin = m_graph->rowAxis()->min();
+ float zRange = m_graph->rowAxis()->max() - zMin;
+ endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0f - 1.0f);
+ endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0f - 1.0f);
+ //! [12]
+
+ // Rotate the camera so that it always points approximately to the graph center
+ //! [14]
+ qreal endAngleX = 90.0 - qRadiansToDegrees(qAtan(qreal(endTarget.z() / endTarget.x())));
+ if (endTarget.x() > 0.0f)
+ endAngleX -= 180.0f;
+ float barValue = m_graph->selectedSeries()->dataProxy()->itemAt(selectedBar.x(),
+ selectedBar.y())->value();
+ float endAngleY = barValue >= 0.0f ? 30.0f : -30.0f;
+ if (m_graph->valueAxis()->reversed())
+ endAngleY *= -1.0f;
+ //! [14]
+
+ m_animationCameraX.setEndValue(QVariant::fromValue(float(endAngleX)));
+ m_animationCameraY.setEndValue(QVariant::fromValue(endAngleY));
+ m_animationCameraZoom.setEndValue(QVariant::fromValue(250));
+ //! [13]
+ m_animationCameraTarget.setEndValue(QVariant::fromValue(endTarget));
+ //! [13]
+ } else {
+ // No selected bar, so return to the default view
+ m_animationCameraX.setEndValue(QVariant::fromValue(m_defaultAngleX));
+ m_animationCameraY.setEndValue(QVariant::fromValue(m_defaultAngleY));
+ m_animationCameraZoom.setEndValue(QVariant::fromValue(m_defaultZoom));
+ m_animationCameraTarget.setEndValue(QVariant::fromValue(m_defaultTarget));
+ }
+
+ m_animationCameraX.start();
+ m_animationCameraY.start();
+ m_animationCameraZoom.start();
+ m_animationCameraTarget.start();
+}
+
+void GraphModifier::setDataModeToWeather(bool enabled)
+{
+ if (enabled)
+ changeDataMode(false);
+}
+
+void GraphModifier::setDataModeToCustom(bool enabled)
+{
+ if (enabled)
+ changeDataMode(true);
+}
+
+void GraphModifier::changeShadowQuality(int quality)
+{
+ QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality);
+ m_graph->setShadowQuality(sq);
+ emit shadowQualityChanged(quality);
+}
+
+//! [10]
+void GraphModifier::rotateX(int rotation)
+{
+ m_xRotation = rotation;
+ m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation);
+}
+//! [10]
+
+void GraphModifier::rotateY(int rotation)
+{
+ m_yRotation = rotation;
+ m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation);
+}
+
+void GraphModifier::setBackgroundEnabled(int enabled)
+{
+ m_graph->activeTheme()->setBackgroundEnabled(bool(enabled));
+}
+
+void GraphModifier::setGridEnabled(int enabled)
+{
+ m_graph->activeTheme()->setGridEnabled(bool(enabled));
+}
+
+void GraphModifier::setSmoothBars(int smooth)
+{
+ m_smooth = bool(smooth);
+ m_primarySeries->setMeshSmooth(m_smooth);
+ m_secondarySeries->setMeshSmooth(m_smooth);
+ m_customData->customSeries()->setMeshSmooth(m_smooth);
+}
+
+void GraphModifier::setSeriesVisibility(int enabled)
+{
+ m_secondarySeries->setVisible(bool(enabled));
+}
+
+void GraphModifier::setReverseValueAxis(int enabled)
+{
+ m_graph->valueAxis()->setReversed(enabled);
+}
+
+void GraphModifier::setReflection(bool enabled)
+{
+ m_graph->setReflection(enabled);
+}
+
+void GraphModifier::changeDataMode(bool customData)
+{
+ // Change between weather data and data from custom proxy
+ if (customData) {
+ m_graph->removeSeries(m_primarySeries);
+ m_graph->removeSeries(m_secondarySeries);
+ m_graph->addSeries(m_customData->customSeries());
+ m_graph->setValueAxis(m_customData->valueAxis());
+ m_graph->setRowAxis(m_customData->rowAxis());
+ m_graph->setColumnAxis(m_customData->colAxis());
+ } else {
+ m_graph->removeSeries(m_customData->customSeries());
+ m_graph->addSeries(m_primarySeries);
+ m_graph->addSeries(m_secondarySeries);
+ m_graph->setValueAxis(m_temperatureAxis);
+ m_graph->setRowAxis(m_yearAxis);
+ m_graph->setColumnAxis(m_monthAxis);
+ }
+}
diff --git a/examples/datavisualization/graphgallery/graphmodifier.h b/examples/datavisualization/graphgallery/graphmodifier.h
new file mode 100644
index 00000000..09110768
--- /dev/null
+++ b/examples/datavisualization/graphgallery/graphmodifier.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef GRAPHMODIFIER_H
+#define GRAPHMODIFIER_H
+
+#include <QtDataVisualization/q3dbars.h>
+#include <QtDataVisualization/qbardataproxy.h>
+#include <QtDataVisualization/qabstract3dseries.h>
+#include <QtCore/qpropertyanimation.h>
+
+class RainfallData;
+
+class GraphModifier : public QObject
+{
+ Q_OBJECT
+public:
+ explicit GraphModifier(Q3DBars *bargraph);
+ ~GraphModifier();
+
+ void resetTemperatureData();
+ void changePresetCamera();
+ void changeLabelBackground();
+ void changeFont(const QFont &font);
+ void changeFontSize(int fontsize);
+ void rotateX(int rotation);
+ void rotateY(int rotation);
+ void setBackgroundEnabled(int enabled);
+ void setGridEnabled(int enabled);
+ void setSmoothBars(int smooth);
+ void setSeriesVisibility(int enabled);
+ void setReverseValueAxis(int enabled);
+ void setReflection(bool enabled);
+ void changeDataMode(bool customData);
+
+public Q_SLOTS:
+ void changeRange(int range);
+ void changeStyle(int style);
+ void changeSelectionMode(int selectionMode);
+ void changeTheme(int theme);
+ void changeShadowQuality(int quality);
+ void shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality shadowQuality);
+ void changeLabelRotation(int rotation);
+ void setAxisTitleVisibility(bool enabled);
+ void setAxisTitleFixed(bool enabled);
+ void zoomToSelectedBar();
+ void setDataModeToWeather(bool enabled);
+ void setDataModeToCustom(bool enabled);
+
+Q_SIGNALS:
+ void shadowQualityChanged(int quality);
+ void backgroundEnabledChanged(bool enabled);
+ void gridEnabledChanged(bool enabled);
+ void fontChanged(const QFont &font);
+ void fontSizeChanged(int size);
+
+private:
+ Q3DBars *m_graph = nullptr;
+ float m_xRotation = 0.f;
+ float m_yRotation = 0.f;
+ int m_fontSize = 30;
+ int m_segments = 4;
+ int m_subSegments = 3;
+ float m_minval = -20.f;
+ float m_maxval = 20.f;
+ QStringList m_months = {};
+ QStringList m_years = {};
+ QValue3DAxis *m_temperatureAxis = nullptr;
+ QCategory3DAxis *m_yearAxis = nullptr;
+ QCategory3DAxis *m_monthAxis = nullptr;
+ QBar3DSeries *m_primarySeries = nullptr;
+ QBar3DSeries *m_secondarySeries = nullptr;
+ QAbstract3DSeries::Mesh m_barMesh = QAbstract3DSeries::MeshBevelBar;
+ bool m_smooth = false;
+ QPropertyAnimation m_animationCameraX = {};
+ QPropertyAnimation m_animationCameraY = {};
+ QPropertyAnimation m_animationCameraZoom = {};
+ QPropertyAnimation m_animationCameraTarget = {};
+ float m_defaultAngleX = 0.f;
+ float m_defaultAngleY = 0.f;
+ float m_defaultZoom = 0.f;
+ QVector3D m_defaultTarget = {};
+ const QString m_celsiusString = QString(QChar(0xB0)) + QLatin1String("C");
+ RainfallData *m_customData = nullptr;
+};
+
+#endif
diff --git a/examples/datavisualization/graphgallery/main.cpp b/examples/datavisualization/graphgallery/main.cpp
new file mode 100644
index 00000000..ad8eeefd
--- /dev/null
+++ b/examples/datavisualization/graphgallery/main.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "bargraph.h"
+
+#include <QtWidgets/qapplication.h>
+#include <QtWidgets/qwidget.h>
+#include <QtWidgets/qtabwidget.h>
+
+int main(int argc, char **argv)
+{
+ qputenv("QSG_RHI_BACKEND", "opengl");
+
+ QApplication app(argc, argv);
+
+ // Create bar graph
+ BarGraph *bars = new BarGraph();
+ if (!bars->initialize())
+ return -1;
+
+ // Create a tab widget for creating own tabs for Bars3D, Scatter3D, and Surface3D
+ QTabWidget *tabWidget = new QTabWidget();
+ tabWidget->setWindowTitle(QLatin1String("Graph Gallery"));
+
+ // Add bars widget
+ tabWidget->addTab(bars->barsWidget(), QLatin1String("Bar Graph"));
+
+ tabWidget->show();
+ return app.exec();
+}
diff --git a/examples/datavisualization/graphgallery/rainfalldata.cpp b/examples/datavisualization/graphgallery/rainfalldata.cpp
new file mode 100644
index 00000000..941e2171
--- /dev/null
+++ b/examples/datavisualization/graphgallery/rainfalldata.cpp
@@ -0,0 +1,115 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "rainfalldata.h"
+#include <QtDataVisualization/q3dscene.h>
+#include <QtDataVisualization/q3dcamera.h>
+#include <QtDataVisualization/qbar3dseries.h>
+#include <QtDataVisualization/q3dtheme.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qfile.h>
+
+RainfallData::RainfallData()
+{
+ // In data file the months are in numeric format, so create custom list
+ for (int i = 1; i <= 12; i++)
+ m_numericMonths << QString::number(i);
+
+ m_columnCount = m_numericMonths.size();
+
+ updateYearsList(2010, 2022);
+
+ // Create proxy and series
+ //! [0]
+ m_proxy = new VariantBarDataProxy;
+ m_series = new QBar3DSeries(m_proxy);
+ //! [0]
+
+ m_series->setItemLabelFormat(QString(QStringLiteral("%.1f mm")));
+
+ // Create the axes
+ m_rowAxis = new QCategory3DAxis(this);
+ m_colAxis = new QCategory3DAxis(this);
+ m_valueAxis = new QValue3DAxis(this);
+ m_rowAxis->setAutoAdjustRange(true);
+ m_colAxis->setAutoAdjustRange(true);
+ m_valueAxis->setAutoAdjustRange(true);
+
+ // Set axis labels and titles
+ QStringList months;
+ months << "January" << "February" << "March" << "April" << "May" << "June" << "July" << "August" << "September" << "October" << "November" << "December";
+ m_rowAxis->setTitle("Year");
+ m_colAxis->setTitle("Month");
+ m_valueAxis->setTitle("rainfall");
+ m_valueAxis->setSegmentCount(5);
+ m_rowAxis->setLabels(m_years);
+ m_colAxis->setLabels(months);
+ m_rowAxis->setTitleVisible(true);
+ m_colAxis->setTitleVisible(true);
+ m_valueAxis->setTitleVisible(true);
+
+ addDataSet();
+}
+
+RainfallData::~RainfallData()
+{
+ delete m_mapping;
+ delete m_dataSet;
+ delete m_proxy;
+}
+
+void RainfallData::updateYearsList(int start, int end)
+{
+ m_years.clear();
+
+ for (int i = start; i <= end; i++)
+ m_years << QString::number(i);
+
+ m_rowCount = m_years.size();
+}
+
+//! [1]
+void RainfallData::addDataSet()
+{
+ // Create a new variant data set and data item list
+ m_dataSet = new VariantDataSet;
+ VariantDataItemList *itemList = new VariantDataItemList;
+
+ // Read data from a data file into the data item list
+ QTextStream stream;
+ QFile dataFile(":/data/raindata.txt");
+ if (dataFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ stream.setDevice(&dataFile);
+ while (!stream.atEnd()) {
+ QString line = stream.readLine();
+ if (line.startsWith("#")) // Ignore comments
+ continue;
+ QStringList strList = line.split(",", Qt::SkipEmptyParts);
+ // Each line has three data items: Year, month, and rainfall value
+ if (strList.size() < 3) {
+ qWarning() << "Invalid row read from data:" << line;
+ continue;
+ }
+ // Store year and month as strings, and rainfall value as double
+ // into a variant data item and add the item to the item list.
+ VariantDataItem *newItem = new VariantDataItem;
+ for (int i = 0; i < 2; i++)
+ newItem->append(strList.at(i).trimmed());
+ newItem->append(strList.at(2).trimmed().toDouble());
+ itemList->append(newItem);
+ }
+ } else {
+ qWarning() << "Unable to open data file:" << dataFile.fileName();
+ }
+ //! [1]
+
+ //! [2]
+ // Add items to the data set and set it to the proxy
+ m_dataSet->addItems(itemList);
+ m_proxy->setDataSet(m_dataSet);
+
+ // Create new mapping for the data and set it to the proxy
+ m_mapping = new VariantBarDataMapping(0, 1, 2, m_years, m_numericMonths);
+ m_proxy->setMapping(m_mapping);
+ //! [2]
+}
diff --git a/examples/datavisualization/graphgallery/rainfalldata.h b/examples/datavisualization/graphgallery/rainfalldata.h
new file mode 100644
index 00000000..cf4866b1
--- /dev/null
+++ b/examples/datavisualization/graphgallery/rainfalldata.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef RAINFALLDATA_H
+#define RAINFALLDATA_H
+
+#include "variantbardataproxy.h"
+#include <QtDataVisualization/qcategory3daxis.h>
+#include <QtDataVisualization/qvalue3daxis.h>
+
+class RainfallData : public QObject
+{
+ Q_OBJECT
+public:
+ explicit RainfallData();
+ ~RainfallData();
+
+ void addDataSet();
+
+ //! [0]
+ QBar3DSeries *customSeries() { return m_series; }
+ //! [0]
+
+ QValue3DAxis *valueAxis() { return m_valueAxis; }
+ QCategory3DAxis *rowAxis() { return m_rowAxis; }
+ QCategory3DAxis *colAxis() { return m_colAxis; }
+
+private:
+ void updateYearsList(int start, int end);
+ int m_columnCount;
+ int m_rowCount;
+ QStringList m_years;
+ QStringList m_numericMonths;
+ VariantBarDataProxy *m_proxy;
+ VariantBarDataMapping *m_mapping;
+ VariantDataSet *m_dataSet;
+ QBar3DSeries *m_series;
+ QValue3DAxis *m_valueAxis;
+ QCategory3DAxis *m_rowAxis;
+ QCategory3DAxis *m_colAxis;
+};
+
+#endif
diff --git a/examples/datavisualization/graphgallery/variantbardatamapping.cpp b/examples/datavisualization/graphgallery/variantbardatamapping.cpp
new file mode 100644
index 00000000..2e375f10
--- /dev/null
+++ b/examples/datavisualization/graphgallery/variantbardatamapping.cpp
@@ -0,0 +1,109 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "variantbardatamapping.h"
+
+VariantBarDataMapping::VariantBarDataMapping()
+ : QObject(0)
+{
+}
+
+VariantBarDataMapping::VariantBarDataMapping(const VariantBarDataMapping &other)
+ : QObject(0)
+{
+ operator=(other);
+}
+
+VariantBarDataMapping::VariantBarDataMapping(int rowIndex, int columnIndex, int valueIndex,
+ const QStringList &rowCategories,
+ const QStringList &columnCategories)
+ : QObject(0)
+{
+ m_rowIndex = rowIndex;
+ m_columnIndex = columnIndex;
+ m_valueIndex = valueIndex;
+ m_rowCategories = rowCategories;
+ m_columnCategories = columnCategories;
+}
+
+VariantBarDataMapping::~VariantBarDataMapping()
+{
+}
+
+VariantBarDataMapping &VariantBarDataMapping::operator=(const VariantBarDataMapping &other)
+{
+ m_rowIndex = other.m_rowIndex;
+ m_columnIndex = other.m_columnIndex;
+ m_valueIndex = other.m_valueIndex;
+ m_rowCategories = other.m_rowCategories;
+ m_columnCategories = other.m_columnCategories;
+
+ return *this;
+}
+
+void VariantBarDataMapping::setRowIndex(int index)
+{
+ m_rowIndex = index;
+ emit mappingChanged();
+}
+
+int VariantBarDataMapping::rowIndex() const
+{
+ return m_rowIndex;
+}
+
+void VariantBarDataMapping::setColumnIndex(int index)
+{
+ m_columnIndex = index;
+ emit mappingChanged();
+}
+
+int VariantBarDataMapping::columnIndex() const
+{
+ return m_columnIndex;
+}
+
+void VariantBarDataMapping::setValueIndex(int index)
+{
+ m_valueIndex = index;
+ emit mappingChanged();
+}
+
+int VariantBarDataMapping::valueIndex() const
+{
+ return m_valueIndex;
+}
+
+void VariantBarDataMapping::setRowCategories(const QStringList &categories)
+{
+ m_rowCategories = categories;
+ emit mappingChanged();
+}
+
+const QStringList &VariantBarDataMapping::rowCategories() const
+{
+ return m_rowCategories;
+}
+
+void VariantBarDataMapping::setColumnCategories(const QStringList &categories)
+{
+ m_columnCategories = categories;
+ emit mappingChanged();
+}
+
+const QStringList &VariantBarDataMapping::columnCategories() const
+{
+ return m_columnCategories;
+}
+
+void VariantBarDataMapping::remap(int rowIndex, int columnIndex, int valueIndex,
+ const QStringList &rowCategories,
+ const QStringList &columnCategories)
+{
+ m_rowIndex = rowIndex;
+ m_columnIndex = columnIndex;
+ m_valueIndex = valueIndex;
+ m_rowCategories = rowCategories;
+ m_columnCategories = columnCategories;
+ emit mappingChanged();
+}
diff --git a/examples/datavisualization/graphgallery/variantbardatamapping.h b/examples/datavisualization/graphgallery/variantbardatamapping.h
new file mode 100644
index 00000000..bd4749a3
--- /dev/null
+++ b/examples/datavisualization/graphgallery/variantbardatamapping.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef VARIANTBARDATAMAPPING_H
+#define VARIANTBARDATAMAPPING_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstringlist.h>
+
+class VariantBarDataMapping : public QObject
+{
+ Q_OBJECT
+ //! [0]
+ Q_PROPERTY(int rowIndex READ rowIndex WRITE setRowIndex NOTIFY rowIndexChanged)
+ Q_PROPERTY(int columnIndex READ columnIndex WRITE setColumnIndex NOTIFY columnIndexChanged)
+ Q_PROPERTY(int valueIndex READ valueIndex WRITE setValueIndex NOTIFY valueIndexChanged)
+ Q_PROPERTY(QStringList rowCategories READ rowCategories WRITE setRowCategories NOTIFY rowCategoriesChanged)
+ Q_PROPERTY(QStringList columnCategories READ columnCategories WRITE setColumnCategories NOTIFY columnCategoriesChanged)
+ //! [0]
+public:
+ explicit VariantBarDataMapping();
+ VariantBarDataMapping(const VariantBarDataMapping &other);
+ //! [1]
+ VariantBarDataMapping(int rowIndex, int columnIndex, int valueIndex,
+ const QStringList &rowCategories,
+ const QStringList &columnCategories);
+ //! [1]
+ virtual ~VariantBarDataMapping();
+
+ VariantBarDataMapping &operator=(const VariantBarDataMapping &other);
+
+ void setRowIndex(int index);
+ int rowIndex() const;
+ void setColumnIndex(int index);
+ int columnIndex() const;
+ void setValueIndex(int index);
+ int valueIndex() const;
+
+ void setRowCategories(const QStringList &categories);
+ const QStringList &rowCategories() const;
+ void setColumnCategories(const QStringList &categories);
+ const QStringList &columnCategories() const;
+
+ //! [2]
+ void remap(int rowIndex, int columnIndex, int valueIndex,
+ const QStringList &rowCategories,
+ const QStringList &columnCategories);
+ //! [2]
+Q_SIGNALS:
+ void rowIndexChanged();
+ void columnIndexChanged();
+ void valueIndexChanged();
+ void rowCategoriesChanged();
+ void columnCategoriesChanged();
+ //! [3]
+ void mappingChanged();
+ //! [3]
+
+private:
+ // Indexes of the mapped items in the VariantDataItem
+ int m_rowIndex = 0;
+ int m_columnIndex = 1;
+ int m_valueIndex = 2;
+
+ // For row/column items, sort items into these categories. Other categories are ignored.
+ QStringList m_rowCategories = {};
+ QStringList m_columnCategories = {};
+};
+
+#endif
diff --git a/examples/datavisualization/graphgallery/variantbardataproxy.cpp b/examples/datavisualization/graphgallery/variantbardataproxy.cpp
new file mode 100644
index 00000000..e15a8148
--- /dev/null
+++ b/examples/datavisualization/graphgallery/variantbardataproxy.cpp
@@ -0,0 +1,125 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "variantbardataproxy.h"
+
+VariantBarDataProxy::VariantBarDataProxy() :
+ QBarDataProxy()
+{
+}
+
+VariantBarDataProxy::VariantBarDataProxy(VariantDataSet *newSet,
+ VariantBarDataMapping *mapping) :
+ QBarDataProxy()
+{
+ setDataSet(newSet);
+ setMapping(mapping);
+}
+
+VariantBarDataProxy::~VariantBarDataProxy()
+{
+ delete m_dataSet;
+}
+
+void VariantBarDataProxy::setDataSet(VariantDataSet *newSet)
+{
+ if (!m_dataSet.isNull())
+ QObject::disconnect(m_dataSet.data(), 0, this, 0);
+
+ m_dataSet = newSet;
+
+ if (!m_dataSet.isNull()) {
+ QObject::connect(m_dataSet.data(), &VariantDataSet::itemsAdded, this,
+ &VariantBarDataProxy::handleItemsAdded);
+ QObject::connect(m_dataSet.data(), &VariantDataSet::dataCleared, this,
+ &VariantBarDataProxy::handleDataCleared);
+ }
+ resolveDataSet();
+}
+
+VariantDataSet *VariantBarDataProxy::dataSet()
+{
+ return m_dataSet.data();
+}
+
+void VariantBarDataProxy::setMapping(VariantBarDataMapping *mapping)
+{
+ if (!m_mapping.isNull()) {
+ QObject::disconnect(m_mapping.data(), &VariantBarDataMapping::mappingChanged, this,
+ &VariantBarDataProxy::handleMappingChanged);
+ }
+
+ m_mapping = mapping;
+
+ if (!m_mapping.isNull()) {
+ QObject::connect(m_mapping.data(), &VariantBarDataMapping::mappingChanged, this,
+ &VariantBarDataProxy::handleMappingChanged);
+ }
+
+ resolveDataSet();
+}
+
+VariantBarDataMapping *VariantBarDataProxy::mapping()
+{
+ return m_mapping.data();
+}
+
+void VariantBarDataProxy::handleItemsAdded(int index, int count)
+{
+ Q_UNUSED(index);
+ Q_UNUSED(count);
+
+ // Resolve new items
+ resolveDataSet();
+}
+
+void VariantBarDataProxy::handleDataCleared()
+{
+ // Data cleared, reset array
+ resetArray(0);
+}
+
+void VariantBarDataProxy::handleMappingChanged()
+{
+ resolveDataSet();
+}
+
+// Resolve entire dataset into QBarDataArray.
+//! [0]
+void VariantBarDataProxy::resolveDataSet()
+{
+ // If we have no data or mapping, or the categories are not defined, simply clear the array
+ if (m_dataSet.isNull() || m_mapping.isNull() || !m_mapping->rowCategories().size()
+ || !m_mapping->columnCategories().size()) {
+ resetArray(0);
+ return;
+ }
+ const VariantDataItemList &itemList = m_dataSet->itemList();
+
+ int rowIndex = m_mapping->rowIndex();
+ int columnIndex = m_mapping->columnIndex();
+ int valueIndex = m_mapping->valueIndex();
+ const QStringList &rowList = m_mapping->rowCategories();
+ const QStringList &columnList = m_mapping->columnCategories();
+
+ // Sort values into rows and columns
+ typedef QHash<QString, float> ColumnValueMap;
+ QHash <QString, ColumnValueMap> itemValueMap;
+ for (const VariantDataItem *item : itemList) {
+ itemValueMap[item->at(rowIndex).toString()][item->at(columnIndex).toString()]
+ = item->at(valueIndex).toReal();
+ }
+
+ // Create a new data array in format the parent class understands
+ QBarDataArray *newProxyArray = new QBarDataArray;
+ for (const QString &rowKey : rowList) {
+ QBarDataRow *newProxyRow = new QBarDataRow(columnList.size());
+ for (int i = 0; i < columnList.size(); i++)
+ (*newProxyRow)[i].setValue(itemValueMap[rowKey][columnList.at(i)]);
+ newProxyArray->append(newProxyRow);
+ }
+
+ // Finally, reset the data array in the parent class
+ resetArray(newProxyArray);
+}
+//! [0]
diff --git a/examples/datavisualization/graphgallery/variantbardataproxy.h b/examples/datavisualization/graphgallery/variantbardataproxy.h
new file mode 100644
index 00000000..d2ed185a
--- /dev/null
+++ b/examples/datavisualization/graphgallery/variantbardataproxy.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef VARIANTBARDATAPROXY_H
+#define VARIANTBARDATAPROXY_H
+
+#include "variantdataset.h"
+#include "variantbardatamapping.h"
+#include <QtDataVisualization/qbardataproxy.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpointer.h>
+
+//! [0]
+class VariantBarDataProxy : public QBarDataProxy
+//! [0]
+{
+ Q_OBJECT
+
+public:
+ explicit VariantBarDataProxy();
+ explicit VariantBarDataProxy(VariantDataSet *newSet, VariantBarDataMapping *mapping);
+ virtual ~VariantBarDataProxy();
+
+ //! [1]
+ // Doesn't gain ownership of the dataset, but does connect to it to listen for data changes.
+ void setDataSet(VariantDataSet *newSet);
+ VariantDataSet *dataSet();
+
+ // Map key (row, column, value) to value index in data item (VariantItem).
+ // Doesn't gain ownership of mapping, but does connect to it to listen for mapping changes.
+ // Modifying mapping that is set to proxy will trigger dataset re-resolving.
+ void setMapping(VariantBarDataMapping *mapping);
+ VariantBarDataMapping *mapping();
+ //! [1]
+
+public Q_SLOTS:
+ void handleItemsAdded(int index, int count);
+ void handleDataCleared();
+ void handleMappingChanged();
+
+private:
+ void resolveDataSet();
+
+ QPointer<VariantDataSet> m_dataSet;
+ QPointer<VariantBarDataMapping> m_mapping;
+
+ Q_DISABLE_COPY(VariantBarDataProxy)
+};
+
+#endif
diff --git a/examples/datavisualization/graphgallery/variantdataset.cpp b/examples/datavisualization/graphgallery/variantdataset.cpp
new file mode 100644
index 00000000..400ff2a4
--- /dev/null
+++ b/examples/datavisualization/graphgallery/variantdataset.cpp
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "variantdataset.h"
+
+VariantDataSet::VariantDataSet()
+ : QObject(0)
+{
+}
+
+VariantDataSet::~VariantDataSet()
+{
+ clear();
+}
+
+void VariantDataSet::clear()
+{
+ for (VariantDataItem *item : m_variantData) {
+ item->clear();
+ delete item;
+ }
+ m_variantData.clear();
+ emit dataCleared();
+}
+
+int VariantDataSet::addItem(VariantDataItem *item)
+{
+ m_variantData.append(item);
+ int addIndex = m_variantData.size();
+
+ emit itemsAdded(addIndex, 1);
+ return addIndex;
+}
+
+int VariantDataSet::addItems(VariantDataItemList *itemList)
+{
+ int newCount = itemList->size();
+ int addIndex = m_variantData.size();
+ m_variantData.append(*itemList);
+ delete itemList;
+ emit itemsAdded(addIndex, newCount);
+ return addIndex;
+}
+
+const VariantDataItemList &VariantDataSet::itemList() const
+{
+ return m_variantData;
+}
diff --git a/examples/datavisualization/graphgallery/variantdataset.h b/examples/datavisualization/graphgallery/variantdataset.h
new file mode 100644
index 00000000..ce52db10
--- /dev/null
+++ b/examples/datavisualization/graphgallery/variantdataset.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef VARIANTDATASET_H
+#define VARIANTDATASET_H
+
+#include <QtCore/qvariantlist.h>
+
+//! [0]
+typedef QVariantList VariantDataItem;
+typedef QList<VariantDataItem *> VariantDataItemList;
+//! [0]
+
+class VariantDataSet : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit VariantDataSet();
+ ~VariantDataSet();
+
+ //! [1]
+ void clear();
+
+ int addItem(VariantDataItem *item);
+ int addItems(VariantDataItemList *itemList);
+
+ const VariantDataItemList &itemList() const;
+
+Q_SIGNALS:
+ void itemsAdded(int index, int count);
+ void dataCleared();
+ //! [1]
+
+private:
+ VariantDataItemList m_variantData;
+
+ Q_DISABLE_COPY(VariantDataSet)
+};
+
+#endif