diff options
Diffstat (limited to 'examples/datavisualization/graphgallery')
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 |