diff options
Diffstat (limited to 'examples/charts/chartsgallery')
127 files changed, 7633 insertions, 0 deletions
diff --git a/examples/charts/chartsgallery/CMakeLists.txt b/examples/charts/chartsgallery/CMakeLists.txt new file mode 100644 index 00000000..b4fd03d6 --- /dev/null +++ b/examples/charts/chartsgallery/CMakeLists.txt @@ -0,0 +1,135 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(chartsgallery LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/charts/chartsgallery") + +find_package(Qt6 REQUIRED COMPONENTS Charts Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(chartsgallery + areawidget.cpp areawidget.h + barmodelmappermodel.cpp barmodelmappermodel.h + barmodelmapperwidget.cpp barmodelmapperwidget.h + boxplotdatareader.cpp boxplotdatareader.h + boxplotwidget.cpp boxplotwidget.h + barwidget.cpp barwidget.h + barpercentwidget.cpp barpercentwidget.h + barstackedwidget.cpp barstackedwidget.h + callout.cpp callout.h + calloutwidget.cpp calloutwidget.h + calloutview.cpp calloutview.h + candlestickwidget.cpp candlestickwidget.h + candlestickdatareader.cpp candlestickdatareader.h + contentwidget.cpp contentwidget.h + customchartwidget.cpp customchartwidget.h + datetimeaxiswidget.cpp datetimeaxiswidget.h + donutbreakdownchart.cpp donutbreakdownchart.h + donutbreakdownmainslice.cpp donutbreakdownmainslice.h + donutbreakdownwidget.cpp donutbreakdownwidget.h + donutwidget.cpp donutwidget.h + dynamicsplinewidget.cpp dynamicsplinewidget.h + horizontalbarwidget.cpp horizontalbarwidget.h + horizontalpercentbarwidget.cpp horizontalpercentbarwidget.h + horizontalstackedbarwidget.cpp horizontalstackedbarwidget.h + interactionswidget.cpp interactionswidget.h + interactionsview.cpp interactionsview.h + interactionschart.cpp interactionschart.h + legendwidget.cpp legendwidget.h + legendmarkerswidget.cpp legendmarkerswidget.h + lightmarkerswidget.cpp lightmarkerswidget.h + lineandbarwidget.cpp lineandbarwidget.h + linewidget.cpp linewidget.h + logaxiswidget.cpp logaxiswidget.h + modeldatamodel.cpp modeldatamodel.h + modeldatawidget.cpp modeldatawidget.h + multiaxiswidget.cpp multiaxiswidget.h + nesteddonutswidget.cpp nesteddonutswidget.h + main.cpp + mainwidget.cpp mainwidget.h + pccpentool.cpp pccpentool.h + pccbrushtool.cpp pccbrushtool.h + pcccustomslice.cpp pcccustomslice.h + pccwidget.cpp pccwidget.h + piedrilldownchart.cpp piedrilldownchart.h + piedrilldownslice.cpp piedrilldownslice.h + piedrilldownwidget.cpp piedrilldownwidget.h + piewidget.cpp piewidget.h + pointconfigurationwidget.cpp pointconfigurationwidget.h + polarchartview.cpp polarchartview.h + polarchartwidget.cpp polarchartwidget.h + scatterinteractionswidget.cpp scatterinteractionswidget.h + scatterwidget.cpp scatterwidget.h + selectedbarwidget.cpp selectedbarwidget.h + splinewidget.cpp splinewidget.h + stackeddrilldownchart.cpp stackeddrilldownchart.h + stackeddrilldownseries.cpp stackeddrilldownseries.h + stackeddrilldownwidget.cpp stackeddrilldownwidget.h + temperaturerecordswidget.cpp temperaturerecordswidget.h + themewidget.cpp themewidget.h themewidget.ui +) + +set_target_properties(chartsgallery PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(chartsgallery PRIVATE + Qt::Charts + Qt::Core + Qt::Gui + Qt::Widgets +) + +set_source_files_properties("data/boxplot_a.txt" + PROPERTIES QT_RESOURCE_ALIAS "boxplot_a" +) + +set_source_files_properties("data/boxplot_b.txt" + PROPERTIES QT_RESOURCE_ALIAS "boxplot_b" +) + +set_source_files_properties("data/candlestick.txt" + PROPERTIES QT_RESOURCE_ALIAS "candlestick" +) + +set_source_files_properties("data/sun_spots.txt" + PROPERTIES QT_RESOURCE_ALIAS "sun_spots" +) + +set_source_files_properties("data/blue_triangle.png" + PROPERTIES QT_RESOURCE_ALIAS "blue_triangle" +) + +set_source_files_properties("data/green_triangle.png" + PROPERTIES QT_RESOURCE_ALIAS "green_triangle" +) + +set(chartsgallery_resource_files + "data/boxplot_a.txt" + "data/boxplot_b.txt" + "data/candlestick.txt" + "data/sun_spots.txt" + "data/blue_triangle.png" + "data/green_triangle.png" +) + +qt6_add_resources(chartsgallery "chartsgallery" + PREFIX + "/" + FILES + ${chartsgallery_resource_files} +) + +install(TARGETS chartsgallery + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/charts/chartsgallery/areawidget.cpp b/examples/charts/chartsgallery/areawidget.cpp new file mode 100644 index 00000000..a16ef53d --- /dev/null +++ b/examples/charts/chartsgallery/areawidget.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "areawidget.h" + +#include <QAreaSeries> +#include <QChart> +#include <QLineSeries> + +AreaWidget::AreaWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto series0 = new QLineSeries; + auto series1 = new QLineSeries; + //![1] + + //![2] + *series0 << QPointF(1, 5) << QPointF(3, 7) << QPointF(7, 6) << QPointF(9, 7) << QPointF(12, 6) + << QPointF(16, 7) << QPointF(18, 5); + *series1 << QPointF(1, 3) << QPointF(3, 4) << QPointF(7, 3) << QPointF(8, 2) << QPointF(12, 3) + << QPointF(16, 4) << QPointF(18, 3); + //![2] + + //![3] + auto series = new QAreaSeries(series0, series1); + series->setName("Batman"); + QPen pen(0x059605); + pen.setWidth(3); + series->setPen(pen); + + QLinearGradient gradient(QPointF(0, 0), QPointF(0, 1)); + gradient.setColorAt(0.0, 0x3cc63c); + gradient.setColorAt(1.0, 0x26f626); + gradient.setCoordinateMode(QGradient::ObjectBoundingMode); + series->setBrush(gradient); + //![3] + + //![4] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Simple Area Chart"); + chart->createDefaultAxes(); + chart->axes(Qt::Horizontal).first()->setRange(0, 20); + chart->axes(Qt::Vertical).first()->setRange(0, 10); + //![4] + + //![5] + createDefaultChartView(chart); + //![5] +} diff --git a/examples/charts/chartsgallery/areawidget.h b/examples/charts/chartsgallery/areawidget.h new file mode 100644 index 00000000..201659c9 --- /dev/null +++ b/examples/charts/chartsgallery/areawidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef AREAWIDGET_H +#define AREAWIDGET_H + +#include "contentwidget.h" + +class AreaWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit AreaWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/barmodelmappermodel.cpp b/examples/charts/chartsgallery/barmodelmappermodel.cpp new file mode 100644 index 00000000..294d85fe --- /dev/null +++ b/examples/charts/chartsgallery/barmodelmappermodel.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "barmodelmappermodel.h" + +#include <QColor> +#include <QList> +#include <QRandomGenerator> +#include <QRect> + +BarModelMapperModel::BarModelMapperModel(QObject *parent) : + QAbstractTableModel(parent) +{ + m_columnCount = 6; + m_rowCount = 12; + + for (int i = 0; i < m_rowCount; i++) { + auto dataList = new QList<qreal>(m_columnCount); + for (int k = 0; k < dataList->size(); k++) { + if (k % 2 == 0) + dataList->replace(k, i * 50 + QRandomGenerator::global()->bounded(20)); + else + dataList->replace(k, QRandomGenerator::global()->bounded(100)); + } + m_data.append(dataList); + } +} + +BarModelMapperModel::~BarModelMapperModel() +{ + qDeleteAll(m_data); +} + +int BarModelMapperModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_data.count(); +} + +int BarModelMapperModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_columnCount; +} + +QVariant BarModelMapperModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return QString("201%1").arg(section); + else + return QString("%1").arg(section + 1); +} + +QVariant BarModelMapperModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) { + return m_data[index.row()]->at(index.column()); + } else if (role == Qt::EditRole) { + return m_data[index.row()]->at(index.column()); + } else if (role == Qt::BackgroundRole) { + for (const QRect &rect : m_mapping) { + if (rect.contains(index.column(), index.row())) + return QColor(m_mapping.key(rect)); + } + + // cell not mapped return white color + return QColor(Qt::white); + } + return QVariant(); +} + +bool BarModelMapperModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) { + m_data[index.row()]->replace(index.column(), value.toDouble()); + emit dataChanged(index, index); + return true; + } + return false; +} + +Qt::ItemFlags BarModelMapperModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +void BarModelMapperModel::addMapping(QString color, QRect area) +{ + m_mapping.insert(color, area); +} diff --git a/examples/charts/chartsgallery/barmodelmappermodel.h b/examples/charts/chartsgallery/barmodelmappermodel.h new file mode 100644 index 00000000..84d4664e --- /dev/null +++ b/examples/charts/chartsgallery/barmodelmappermodel.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef BARMODELMAPPERMODEL_H +#define BARMODELMAPPERMODEL_H + +#include <QAbstractTableModel> +#include <QMultiHash> +#include <QRect> + +class BarModelMapperModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit BarModelMapperModel(QObject *parent = nullptr); + virtual ~BarModelMapperModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) const; + + void addMapping(QString color, QRect area); + void clearMapping() { m_mapping.clear(); } + +private: + QList<QList<qreal> *> m_data; + QMultiHash<QString, QRect> m_mapping; + int m_columnCount; + int m_rowCount; +}; + +#endif diff --git a/examples/charts/chartsgallery/barmodelmapperwidget.cpp b/examples/charts/chartsgallery/barmodelmapperwidget.cpp new file mode 100644 index 00000000..1d73fe17 --- /dev/null +++ b/examples/charts/chartsgallery/barmodelmapperwidget.cpp @@ -0,0 +1,100 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "barmodelmappermodel.h" +#include "barmodelmapperwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSeries> +#include <QBarSet> +#include <QChart> +#include <QChartView> +#include <QGraphicsLayout> +#include <QGridLayout> +#include <QHeaderView> +#include <QLineSeries> +#include <QTableView> +#include <QValueAxis> +#include <QVBarModelMapper> +#include <QVXYModelMapper> + +BarModelMapperWidget::BarModelMapperWidget(QWidget *parent) + : ContentWidget(parent) +{ + // create simple model for storing user's data + //! [1] + auto model = new BarModelMapperModel(this); + //! [1] + + //! [2] + // create table view and add model to it + auto tableView = new QTableView(this); + tableView->setModel(model); + tableView->setMinimumWidth(300); + tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); + model->setParent(tableView); + //! [2] + + //! [3] + auto chart = new QChart; + chart->setAnimationOptions(QChart::AllAnimations); + //! [3] + + chart->layout()->setContentsMargins(0, 0, 0, 0); + + // series 1 + //! [4] + auto series = new QBarSeries; + + int first = 3; + int count = 5; + auto mapper = new QVBarModelMapper(this); + mapper->setFirstBarSetColumn(1); + mapper->setLastBarSetColumn(4); + mapper->setFirstRow(first); + mapper->setRowCount(count); + mapper->setSeries(series); + mapper->setModel(model); + chart->addSeries(series); + //! [4] + + //! [5] + // for storing color hex from the series + QString seriesColorHex = "#000000"; + + // get the color of the series and use it for showing the mapped area + QList<QBarSet *> barsets = series->barSets(); + for (int i = 0; i < barsets.count(); i++) { + seriesColorHex = "#" + QString::number(barsets.at(i)->brush().color().rgb(), 16).right(6).toUpper(); + model->addMapping(seriesColorHex, QRect(1 + i, first, 1, barsets.at(i)->count())); + } + //! [5] + + //! [6] + QStringList categories {"April", "May", "June", "July", "August"}; + auto axisX = new QBarCategoryAxis; + axisX->append(categories); + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + auto axisY = new QValueAxis; + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + //! [6] + + //! [7] + auto chartView = new QChartView(chart, this); + chartView->setRenderHint(QPainter::Antialiasing); + //! [7] + + //! [8] + // create main layout + auto mainLayout = new QGridLayout; + mainLayout->setHorizontalSpacing(10); + mainLayout->addWidget(tableView, 1, 0); + mainLayout->addWidget(chartView, 1, 1); + mainLayout->setColumnStretch(1, 1); + mainLayout->setColumnStretch(0, 0); + setLayout(mainLayout); + //! [8] +} diff --git a/examples/charts/chartsgallery/barmodelmapperwidget.h b/examples/charts/chartsgallery/barmodelmapperwidget.h new file mode 100644 index 00000000..17b4eb58 --- /dev/null +++ b/examples/charts/chartsgallery/barmodelmapperwidget.h @@ -0,0 +1,18 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef BARMODELMAPPERWIDGET_H +#define BARMODELMAPPERWIDGET_H + +#include "contentwidget.h" + +QT_FORWARD_DECLARE_CLASS(QChartView) + +class BarModelMapperWidget : public ContentWidget +{ + Q_OBJECT +public: + BarModelMapperWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/barpercentwidget.cpp b/examples/charts/chartsgallery/barpercentwidget.cpp new file mode 100644 index 00000000..d9f2fcb1 --- /dev/null +++ b/examples/charts/chartsgallery/barpercentwidget.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "barpercentwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSet> +#include <QChart> +#include <QLegend> +#include <QPercentBarSeries> +#include <QValueAxis> + +BarPercentWidget::BarPercentWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto set0 = new QBarSet("Jane"); + auto set1 = new QBarSet("John"); + auto set2 = new QBarSet("Axel"); + auto set3 = new QBarSet("Mary"); + auto set4 = new QBarSet("Samantha"); + + *set0 << 1 << 2 << 3 << 4 << 5 << 6; + *set1 << 5 << 0 << 0 << 4 << 0 << 7; + *set2 << 3 << 5 << 8 << 13 << 8 << 5; + *set3 << 5 << 6 << 7 << 3 << 4 << 5; + *set4 << 9 << 7 << 5 << 3 << 1 << 2; + //![1] + + //![2] + auto series = new QPercentBarSeries; + series->append(set0); + series->append(set1); + series->append(set2); + series->append(set3); + series->append(set4); + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Simple Percent Bar Chart"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //![3] + + //![4] + QStringList categories {"Jan", "Feb", "Mar", "Apr", "May", "Jun"}; + auto axisX = new QBarCategoryAxis; + axisX->append(categories); + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + auto axisY = new QValueAxis; + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + //![4] + + //![5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/barpercentwidget.h b/examples/charts/chartsgallery/barpercentwidget.h new file mode 100644 index 00000000..4372f278 --- /dev/null +++ b/examples/charts/chartsgallery/barpercentwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef BARPERCENTWIDGET_H +#define BARPERCENTWIDGET_H + +#include "contentwidget.h" + +class BarPercentWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit BarPercentWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/barstackedwidget.cpp b/examples/charts/chartsgallery/barstackedwidget.cpp new file mode 100644 index 00000000..6dc377e1 --- /dev/null +++ b/examples/charts/chartsgallery/barstackedwidget.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "barstackedwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSet> +#include <QChart> +#include <QLegend> +#include <QStackedBarSeries> +#include <QValueAxis> + +BarStackedWidget::BarStackedWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto set0 = new QBarSet("Jane"); + auto set1 = new QBarSet("John"); + auto set2 = new QBarSet("Axel"); + auto set3 = new QBarSet("Mary"); + auto set4 = new QBarSet("Samantha"); + + *set0 << 1 << 2 << 3 << 4 << 5 << 6; + *set1 << 5 << 0 << 0 << 4 << 0 << 7; + *set2 << 3 << 5 << 8 << 13 << 8 << 5; + *set3 << 5 << 6 << 7 << 3 << 4 << 5; + *set4 << 9 << 7 << 5 << 3 << 1 << 2; + //![1] + + //![2] + auto series = new QStackedBarSeries; + series->append(set0); + series->append(set1); + series->append(set2); + series->append(set3); + series->append(set4); + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Simple Stacked Bar Chart"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //![3] + + //![4] + QStringList categories {"Jan", "Feb", "Mar", "Apr", "May", "Jun"}; + auto axisX = new QBarCategoryAxis; + axisX->append(categories); + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + auto axisY = new QValueAxis; + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + //![4] + + //![5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/barstackedwidget.h b/examples/charts/chartsgallery/barstackedwidget.h new file mode 100644 index 00000000..c40941c1 --- /dev/null +++ b/examples/charts/chartsgallery/barstackedwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef BARSTACKEDWIDGET_H +#define BARSTACKEDWIDGET_H + +#include "contentwidget.h" + +class BarStackedWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit BarStackedWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/barwidget.cpp b/examples/charts/chartsgallery/barwidget.cpp new file mode 100644 index 00000000..78430cd1 --- /dev/null +++ b/examples/charts/chartsgallery/barwidget.cpp @@ -0,0 +1,67 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "barwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSeries> +#include <QBarSet> +#include <QChart> +#include <QLegend> +#include <QValueAxis> + +BarWidget::BarWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto set0 = new QBarSet("Jane"); + auto set1 = new QBarSet("John"); + auto set2 = new QBarSet("Axel"); + auto set3 = new QBarSet("Mary"); + auto set4 = new QBarSet("Samantha"); + + *set0 << 1 << 2 << 3 << 4 << 5 << 6; + *set1 << 5 << 0 << 0 << 4 << 0 << 7; + *set2 << 3 << 5 << 8 << 13 << 8 << 5; + *set3 << 5 << 6 << 7 << 3 << 4 << 5; + *set4 << 9 << 7 << 5 << 3 << 1 << 2; + //![1] + + //![2] + QBarSeries *series = new QBarSeries; + series->append(set0); + series->append(set1); + series->append(set2); + series->append(set3); + series->append(set4); + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Simple Bar Chart"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //![3] + + //![4] + QStringList categories {"Jan", "Feb", "Mar", "Apr", "May", "Jun"}; + auto axisX = new QBarCategoryAxis; + axisX->append(categories); + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + + auto axisY = new QValueAxis; + axisY->setRange(0,15); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + //![4] + + //![5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/barwidget.h b/examples/charts/chartsgallery/barwidget.h new file mode 100644 index 00000000..0de1785b --- /dev/null +++ b/examples/charts/chartsgallery/barwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef BARWIDGET_H +#define BARWIDGET_H + +#include "contentwidget.h" + +class BarWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit BarWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/boxplotdatareader.cpp b/examples/charts/chartsgallery/boxplotdatareader.cpp new file mode 100644 index 00000000..47172704 --- /dev/null +++ b/examples/charts/chartsgallery/boxplotdatareader.cpp @@ -0,0 +1,71 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "boxplotdatareader.h" + +#include <QBoxSet> + +#include <algorithm> + +BoxPlotDataReader::BoxPlotDataReader(QIODevice *device) + : m_textStream(device) +{ +} + +void BoxPlotDataReader::readFile(QIODevice *device) +{ + m_textStream.setDevice(device); +} + +bool BoxPlotDataReader::atEnd() const +{ + return m_textStream.atEnd(); +} + +QBoxSet *BoxPlotDataReader::readBox() +{ + //! [1] + QString line = m_textStream.readLine(); + if (line.startsWith("#")) + return nullptr; + //! [1] + + //! [2] + QStringList strList = line.split(QLatin1Char(' '), Qt::SkipEmptyParts); + //! [2] + + //! [3] + m_sortedList.clear(); + for (int i = 1; i < strList.count(); i++) + m_sortedList.append(strList.at(i).toDouble()); + + std::sort(m_sortedList.begin(), m_sortedList.end()); + //! [3] + + int count = m_sortedList.count(); + + //! [4] + auto box = new QBoxSet(strList.first()); + box->setValue(QBoxSet::LowerExtreme, m_sortedList.first()); + box->setValue(QBoxSet::UpperExtreme, m_sortedList.last()); + box->setValue(QBoxSet::Median, findMedian(0, count)); + box->setValue(QBoxSet::LowerQuartile, findMedian(0, count / 2)); + box->setValue(QBoxSet::UpperQuartile, findMedian(count / 2 + (count % 2), count)); + //! [4] + + return box; +} + +qreal BoxPlotDataReader::findMedian(int begin, int end) +{ + //! [5] + int count = end - begin; + if (count % 2) { + return m_sortedList.at(count / 2 + begin); + } else { + qreal right = m_sortedList.at(count / 2 + begin); + qreal left = m_sortedList.at(count / 2 - 1 + begin); + return (right + left) / 2.0; + } + //! [5] +} diff --git a/examples/charts/chartsgallery/boxplotdatareader.h b/examples/charts/chartsgallery/boxplotdatareader.h new file mode 100644 index 00000000..253b05ee --- /dev/null +++ b/examples/charts/chartsgallery/boxplotdatareader.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef BOXPLOTDATAREADER_H +#define BOXPLOTDATAREADER_H + +#include <QList> +#include <QTextStream> + +QT_FORWARD_DECLARE_CLASS(QBoxSet) + +class BoxPlotDataReader +{ +public: + explicit BoxPlotDataReader(QIODevice *device); + + QBoxSet *readBox(); + void readFile(QIODevice *device); + bool atEnd() const; + +protected: + qreal findMedian(int begin, int end); + +private: + QList<qreal> m_sortedList; + QTextStream m_textStream; +}; + +#endif diff --git a/examples/charts/chartsgallery/boxplotwidget.cpp b/examples/charts/chartsgallery/boxplotwidget.cpp new file mode 100644 index 00000000..c2ca2936 --- /dev/null +++ b/examples/charts/chartsgallery/boxplotwidget.cpp @@ -0,0 +1,84 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "boxplotdatareader.h" +#include "boxplotwidget.h" + +#include <QBarCategoryAxis> +#include <QBoxPlotSeries> +#include <QBoxSet> +#include <QChart> +#include <QFile> +#include <QLegend> + +BoxPlotWidget::BoxPlotWidget(QWidget *parent) + : ContentWidget(parent) +{ +} + +bool BoxPlotWidget::doLoad() +{ + //! [1] + auto acmeSeries = new QBoxPlotSeries; + acmeSeries->setName("Acme Ltd"); + + auto boxWhiskSeries = new QBoxPlotSeries; + boxWhiskSeries->setName("BoxWhisk Inc"); + //! [1] + + //! [2] + QFile acmeData(":boxplot_a"); + const QString errorTemplate = QStringLiteral("Failed to load '%1' file."); + if (!acmeData.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_loadError = errorTemplate.arg(acmeData.fileName()); + return false; + } + + BoxPlotDataReader dataReader(&acmeData); + while (!dataReader.atEnd()) { + QBoxSet *set = dataReader.readBox(); + if (set) + acmeSeries->append(set); + } + //! [2] + + //! [3] + QFile boxwhiskData(":boxplot_b"); + if (!boxwhiskData.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_loadError = errorTemplate.arg(acmeData.fileName()); + return false; + } + + dataReader.readFile(&boxwhiskData); + while (!dataReader.atEnd()) { + QBoxSet *set = dataReader.readBox(); + if (set) + boxWhiskSeries->append(set); + } + //! [3] + + //! [4] + auto chart = new QChart; + chart->addSeries(acmeSeries); + chart->addSeries(boxWhiskSeries); + chart->setTitle("Acme Ltd. and BoxWhisk Inc. share deviation in 2012"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //! [4] + + //! [5] + chart->createDefaultAxes(); + chart->axes(Qt::Vertical).first()->setMin(15.0); + chart->axes(Qt::Horizontal).first()->setMax(34.0); + //! [5] + + //! [6] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //! [6] + + //! [7] + createDefaultChartView(chart); + //! [7] + + return true; +} diff --git a/examples/charts/chartsgallery/boxplotwidget.h b/examples/charts/chartsgallery/boxplotwidget.h new file mode 100644 index 00000000..b498d438 --- /dev/null +++ b/examples/charts/chartsgallery/boxplotwidget.h @@ -0,0 +1,19 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef BOXPLOTWIDGET_H +#define BOXPLOTWIDGET_H + +#include "contentwidget.h" + +class BoxPlotWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit BoxPlotWidget(QWidget *parent = nullptr); + +protected: + bool doLoad() override; +}; + +#endif diff --git a/examples/charts/chartsgallery/callout.cpp b/examples/charts/chartsgallery/callout.cpp new file mode 100644 index 00000000..2169286c --- /dev/null +++ b/examples/charts/chartsgallery/callout.cpp @@ -0,0 +1,111 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "callout.h" + +#include <QChart> +#include <QFontMetrics> +#include <QGraphicsSceneMouseEvent> +#include <QMouseEvent> +#include <QPainter> + +Callout::Callout(QChart *chart) + : QGraphicsItem(chart) + , m_chart(chart) +{ +} + +QRectF Callout::boundingRect() const +{ + QPointF anchor = mapFromParent(m_chart->mapToPosition(m_anchor)); + QRectF rect; + rect.setLeft(qMin(m_rect.left(), anchor.x())); + rect.setRight(qMax(m_rect.right(), anchor.x())); + rect.setTop(qMin(m_rect.top(), anchor.y())); + rect.setBottom(qMax(m_rect.bottom(), anchor.y())); + return rect; +} + +void Callout::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + QPainterPath path; + path.addRoundedRect(m_rect, 5, 5); + + QPointF anchor = mapFromParent(m_chart->mapToPosition(m_anchor)); + if (!m_rect.contains(anchor) && !m_anchor.isNull()) { + QPointF point1, point2; + + // establish the position of the anchor point in relation to m_rect + bool above = anchor.y() <= m_rect.top(); + bool aboveCenter = anchor.y() > m_rect.top() && anchor.y() <= m_rect.center().y(); + bool belowCenter = anchor.y() > m_rect.center().y() && anchor.y() <= m_rect.bottom(); + bool below = anchor.y() > m_rect.bottom(); + + bool onLeft = anchor.x() <= m_rect.left(); + bool leftOfCenter = anchor.x() > m_rect.left() && anchor.x() <= m_rect.center().x(); + bool rightOfCenter = anchor.x() > m_rect.center().x() && anchor.x() <= m_rect.right(); + bool onRight = anchor.x() > m_rect.right(); + + // get the nearest m_rect corner. + qreal x = (onRight + rightOfCenter) * m_rect.width(); + qreal y = (below + belowCenter) * m_rect.height(); + bool cornerCase = (above && onLeft) || (above && onRight) || (below && onLeft) || (below && onRight); + bool vertical = qAbs(anchor.x() - x) > qAbs(anchor.y() - y); + + qreal x1 = x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * !vertical * (onLeft * 10 - onRight * 20); + qreal y1 = y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20);; + point1.setX(x1); + point1.setY(y1); + + qreal x2 = x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * !vertical * (onLeft * 20 - onRight * 10);; + qreal y2 = y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 - below * 10);; + point2.setX(x2); + point2.setY(y2); + + path.moveTo(point1); + path.lineTo(anchor); + path.lineTo(point2); + path = path.simplified(); + } + painter->setBrush(QColor(255, 255, 255)); + painter->drawPath(path); + painter->drawText(m_textRect, m_text); +} + +void Callout::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + event->setAccepted(true); +} + +void Callout::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton){ + setPos(mapToParent(event->pos() - event->buttonDownPos(Qt::LeftButton))); + event->setAccepted(true); + } else { + event->setAccepted(false); + } +} + +void Callout::setText(const QString &text) +{ + m_text = text; + QFontMetrics metrics(m_font); + m_textRect = metrics.boundingRect(QRect(0, 0, 150, 150), Qt::AlignLeft, m_text); + m_textRect.translate(5, 5); + prepareGeometryChange(); + m_rect = m_textRect.adjusted(-5, -5, 5, 5); +} + +void Callout::setAnchor(QPointF point) +{ + m_anchor = point; +} + +void Callout::updateGeometry() +{ + prepareGeometryChange(); + setPos(m_chart->mapToPosition(m_anchor) + QPoint(10, -50)); +} diff --git a/examples/charts/chartsgallery/callout.h b/examples/charts/chartsgallery/callout.h new file mode 100644 index 00000000..27ef57cb --- /dev/null +++ b/examples/charts/chartsgallery/callout.h @@ -0,0 +1,38 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CALLOUT_H +#define CALLOUT_H + +#include <QFont> +#include <QGraphicsItem> + +QT_FORWARD_DECLARE_CLASS(QChart) +QT_FORWARD_DECLARE_CLASS(QGraphicsSceneMouseEvent) + +class Callout : public QGraphicsItem +{ +public: + Callout(QChart *parent); + + void setText(const QString &text); + void setAnchor(QPointF point); + void updateGeometry(); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + +private: + QString m_text; + QRectF m_textRect; + QRectF m_rect; + QPointF m_anchor; + QFont m_font; + QChart *m_chart = nullptr; +}; + +#endif diff --git a/examples/charts/chartsgallery/calloutview.cpp b/examples/charts/chartsgallery/calloutview.cpp new file mode 100644 index 00000000..3ea4ef39 --- /dev/null +++ b/examples/charts/chartsgallery/calloutview.cpp @@ -0,0 +1,113 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "callout.h" +#include "calloutview.h" + +#include <QChart> +#include <QGraphicsLayout> +#include <QGraphicsScene> +#include <QGraphicsTextItem> +#include <QGraphicsView> +#include <QLineSeries> +#include <QMouseEvent> +#include <QSplineSeries> +#include <QResizeEvent> + +CalloutView::CalloutView(QWidget *parent) + : QGraphicsView(new QGraphicsScene, parent) +{ + setDragMode(QGraphicsView::NoDrag); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setFrameShape(QFrame::NoFrame); + setBackgroundRole(QPalette::Window); + setRenderHint(QPainter::Antialiasing); + setMouseTracking(true); + + m_chart = new QChart; + m_chart->setTitle("Hover the line to show callout. Click the line to make it stay."); + m_chart->legend()->hide(); + + auto series = new QLineSeries; + series->append(1, 3); + series->append(4, 5); + series->append(5, 4.5); + series->append(7, 1); + series->append(11, 2); + m_chart->addSeries(series); + + auto series2 = new QSplineSeries; + series2->append(1.6, 1.4); + series2->append(2.4, 3.5); + series2->append(3.7, 2.5); + series2->append(7, 4); + series2->append(10, 2); + m_chart->addSeries(series2); + + m_chart->createDefaultAxes(); + m_chart->setAcceptHoverEvents(true); + + scene()->addItem(m_chart); + + m_coordX = new QGraphicsSimpleTextItem(m_chart); + m_coordX->setText("X: "); + m_coordY = new QGraphicsSimpleTextItem(m_chart); + m_coordY->setText("Y: "); + + connect(series, &QLineSeries::clicked, this, &CalloutView::keepCallout); + connect(series, &QLineSeries::hovered, this, &CalloutView::tooltip); + + connect(series2, &QSplineSeries::clicked, this, &CalloutView::keepCallout); + connect(series2, &QSplineSeries::hovered, this, &CalloutView::tooltip); +} + +CalloutView::~CalloutView() +{ + delete scene(); +} + +void CalloutView::resizeEvent(QResizeEvent *event) +{ + if (scene()) { + scene()->setSceneRect(QRect(QPoint(0, 0), event->size())); + m_chart->resize(event->size()); + m_coordX->setPos(m_chart->size().width() / 2 - 70, m_chart->size().height() - 24); + m_coordY->setPos(m_chart->size().width() / 2 + 30, m_chart->size().height() - 24); + const auto callouts = m_callouts; + for (Callout *callout : callouts) + callout->updateGeometry(); + } + + resize(size()); +} + +void CalloutView::mouseMoveEvent(QMouseEvent *event) +{ + m_coordX->setText(QString("X: %1").arg(m_chart->mapToValue(event->pos()).x())); + m_coordY->setText(QString("Y: %1").arg(m_chart->mapToValue(event->pos()).y())); + + QGraphicsView::mouseMoveEvent(event); +} + +void CalloutView::keepCallout() +{ + m_callouts.append(m_tooltip); + m_tooltip = new Callout(m_chart); +} + +void CalloutView::tooltip(QPointF point, bool state) +{ + if (!m_tooltip) + m_tooltip = new Callout(m_chart); + + if (state) { + m_tooltip->setText(QString("X: %1 \nY: %2 ").arg(point.x()).arg(point.y())); + m_tooltip->setAnchor(point); + m_tooltip->setZValue(11); + m_tooltip->updateGeometry(); + m_tooltip->show(); + } else { + m_tooltip->hide(); + } +} diff --git a/examples/charts/chartsgallery/calloutview.h b/examples/charts/chartsgallery/calloutview.h new file mode 100644 index 00000000..866846a6 --- /dev/null +++ b/examples/charts/chartsgallery/calloutview.h @@ -0,0 +1,41 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CALLOUTVIEW_H +#define CALLOUTVIEW_H + +#include <QGraphicsView> + +QT_FORWARD_DECLARE_CLASS(QChart) +QT_FORWARD_DECLARE_CLASS(QGraphicsScene) +QT_FORWARD_DECLARE_CLASS(QGraphicsSimpleTextItem) +QT_FORWARD_DECLARE_CLASS(QGraphicsView) +QT_FORWARD_DECLARE_CLASS(QMouseEvent) +QT_FORWARD_DECLARE_CLASS(QResizeEvent) + +class Callout; + +class CalloutView : public QGraphicsView +{ + Q_OBJECT +public: + CalloutView(QWidget *parent = nullptr); + ~CalloutView(); + +protected: + void resizeEvent(QResizeEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + +public slots: + void keepCallout(); + void tooltip(QPointF point, bool state); + +private: + QGraphicsSimpleTextItem *m_coordX = nullptr; + QGraphicsSimpleTextItem *m_coordY = nullptr; + QChart *m_chart = nullptr; + Callout *m_tooltip = nullptr; + QList<Callout *> m_callouts; +}; + +#endif diff --git a/examples/charts/chartsgallery/calloutwidget.cpp b/examples/charts/chartsgallery/calloutwidget.cpp new file mode 100644 index 00000000..e3571f15 --- /dev/null +++ b/examples/charts/chartsgallery/calloutwidget.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "calloutview.h" +#include "calloutwidget.h" + +CalloutWidget::CalloutWidget(QWidget *parent) + : ContentWidget(parent) +{ + // To make mouse tracking work, we need to subclass the QGraphicsView instead of just having + // it as member of this class + m_view = new CalloutView(this); + m_view->resize(size()); +} + +void CalloutWidget::resizeEvent(QResizeEvent *) +{ + m_view->resize(size()); +} diff --git a/examples/charts/chartsgallery/calloutwidget.h b/examples/charts/chartsgallery/calloutwidget.h new file mode 100644 index 00000000..0040f4eb --- /dev/null +++ b/examples/charts/chartsgallery/calloutwidget.h @@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CALLOUTWIDGET_H +#define CALLOUTWIDGET_H + +#include "contentwidget.h" + +class CalloutView; + +class CalloutWidget : public ContentWidget +{ + Q_OBJECT +public: + CalloutWidget(QWidget *parent = nullptr); + +protected: + void resizeEvent(QResizeEvent *) override; + +private: + CalloutView *m_view; +}; + +#endif diff --git a/examples/charts/chartsgallery/candlestickdatareader.cpp b/examples/charts/chartsgallery/candlestickdatareader.cpp new file mode 100644 index 00000000..3913e3bd --- /dev/null +++ b/examples/charts/chartsgallery/candlestickdatareader.cpp @@ -0,0 +1,58 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "candlestickdatareader.h" + +#include <QCandlestickSet> + +CandlestickDataReader::CandlestickDataReader(QIODevice *device) + : m_textStream(device) +{ +} + +CandlestickDataReader::~CandlestickDataReader() +{ +} + +void CandlestickDataReader::readFile(QIODevice *device) +{ + m_textStream.setDevice(device); +} + +QCandlestickSet *CandlestickDataReader::readCandlestickSet() +{ + //! [1] + QString line = m_textStream.readLine(); + if (line.startsWith("#") || line.isEmpty()) + return nullptr; + //! [1] + + //! [2] + QStringList strList = line.split(QLatin1Char(' '), Qt::SkipEmptyParts); + if (strList.count() != 5) + return nullptr; + //! [2] + + //! [3] + const qreal timestamp = strList.at(0).toDouble(); + const qreal open = strList.at(1).toDouble(); + const qreal high = strList.at(2).toDouble(); + const qreal low = strList.at(3).toDouble(); + const qreal close = strList.at(4).toDouble(); + //! [3] + + //! [4] + auto candlestickSet = new QCandlestickSet(timestamp); + candlestickSet->setOpen(open); + candlestickSet->setHigh(high); + candlestickSet->setLow(low); + candlestickSet->setClose(close); + //! [4] + + return candlestickSet; +} + +bool CandlestickDataReader::atEnd() const +{ + return m_textStream.atEnd(); +} diff --git a/examples/charts/chartsgallery/candlestickdatareader.h b/examples/charts/chartsgallery/candlestickdatareader.h new file mode 100644 index 00000000..f7ee316d --- /dev/null +++ b/examples/charts/chartsgallery/candlestickdatareader.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CANDLESTICKDATAREADER_H +#define CANDLESTICKDATAREADER_H + +#include <QTextStream> + +QT_FORWARD_DECLARE_CLASS(QCandlestickSet) + +class CandlestickDataReader +{ +public: + explicit CandlestickDataReader(QIODevice *device); + ~CandlestickDataReader(); + + void readFile(QIODevice *device); + QCandlestickSet *readCandlestickSet(); + bool atEnd() const; + +private: + QTextStream m_textStream; +}; + +#endif diff --git a/examples/charts/chartsgallery/candlestickwidget.cpp b/examples/charts/chartsgallery/candlestickwidget.cpp new file mode 100644 index 00000000..f21037c9 --- /dev/null +++ b/examples/charts/chartsgallery/candlestickwidget.cpp @@ -0,0 +1,77 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "candlestickdatareader.h" +#include "candlestickwidget.h" + +#include <QBarCategoryAxis> +#include <QCandlestickSeries> +#include <QCandlestickSet> +#include <QChart> +#include <QDateTime> +#include <QFile> +#include <QLegend> +#include <QValueAxis> + +CandleStickWidget::CandleStickWidget(QWidget *parent) + : ContentWidget(parent) +{ +} + +bool CandleStickWidget::doLoad() +{ + //! [1] + auto acmeSeries = new QCandlestickSeries; + acmeSeries->setName("Acme Ltd"); + acmeSeries->setIncreasingColor(QColor(Qt::green)); + acmeSeries->setDecreasingColor(QColor(Qt::red)); + //! [1] + + //! [2] + QFile acmeData(":candlestick"); + if (!acmeData.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_loadError = QStringLiteral("Failed to load '%1' file.").arg(acmeData.fileName()); + return false; + } + + QStringList categories; + + CandlestickDataReader dataReader(&acmeData); + while (!dataReader.atEnd()) { + QCandlestickSet *set = dataReader.readCandlestickSet(); + if (set) { + acmeSeries->append(set); + categories << QDateTime::fromMSecsSinceEpoch(set->timestamp()).toString("dd"); + } + } + //! [2] + + //! [3] + auto chart = new QChart; + chart->addSeries(acmeSeries); + chart->setTitle("Acme Ltd. Historical Data (July 2015)"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //! [3] + + //! [4] + chart->createDefaultAxes(); + + auto axisX = qobject_cast<QBarCategoryAxis *>(chart->axes(Qt::Horizontal).at(0)); + axisX->setCategories(categories); + + auto axisY = qobject_cast<QValueAxis *>(chart->axes(Qt::Vertical).at(0)); + axisY->setMax(axisY->max() * 1.01); + axisY->setMin(axisY->min() * 0.99); + //! [4] + + //! [5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //! [5] + + //! [6] + createDefaultChartView(chart); + //! [6] + + return true; +} diff --git a/examples/charts/chartsgallery/candlestickwidget.h b/examples/charts/chartsgallery/candlestickwidget.h new file mode 100644 index 00000000..05bfac4e --- /dev/null +++ b/examples/charts/chartsgallery/candlestickwidget.h @@ -0,0 +1,19 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CANDLESTICKWIDGET_H +#define CANDLESTICKWIDGET_H + +#include "contentwidget.h" + +class CandleStickWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit CandleStickWidget(QWidget *parent = nullptr); + +protected: + bool doLoad() override; +}; + +#endif diff --git a/examples/charts/chartsgallery/chartsgallery.pro b/examples/charts/chartsgallery/chartsgallery.pro new file mode 100644 index 00000000..b362ffad --- /dev/null +++ b/examples/charts/chartsgallery/chartsgallery.pro @@ -0,0 +1,129 @@ +QT += charts + +HEADERS += \ + areawidget.h \ + barmodelmappermodel.h \ + barmodelmapperwidget.h \ + boxplotdatareader.h \ + boxplotwidget.h \ + barwidget.h \ + barpercentwidget.h \ + barstackedwidget.h \ + callout.h \ + calloutwidget.h \ + calloutview.h \ + candlestickwidget.h \ + candlestickdatareader.h \ + contentwidget.h \ + customchartwidget.h \ + datetimeaxiswidget.h \ + donutbreakdownchart.h \ + donutbreakdownmainslice.h \ + donutbreakdownwidget.h \ + donutwidget.h \ + dynamicsplinewidget.h \ + horizontalbarwidget.h \ + horizontalpercentbarwidget.h \ + horizontalstackedbarwidget.h \ + interactionswidget.h \ + interactionsview.h \ + interactionschart.h \ + legendwidget.h \ + legendmarkerswidget.h \ + lightmarkerswidget.h \ + lineandbarwidget.h \ + linewidget.h \ + logaxiswidget.h \ + modeldatamodel.h \ + modeldatawidget.h \ + multiaxiswidget.h \ + nesteddonutswidget.h \ + mainwidget.h \ + pccpentool.h \ + pccbrushtool.h \ + pcccustomslice.h \ + pccwidget.h \ + piedrilldownchart.h \ + piedrilldownslice.h \ + piedrilldownwidget.h \ + piewidget.h \ + pointconfigurationwidget.h \ + polarchartview.h \ + polarchartwidget.h \ + scatterinteractionswidget.h \ + scatterwidget.h \ + selectedbarwidget.h \ + splinewidget.h \ + stackeddrilldownchart.h \ + stackeddrilldownseries.h \ + stackeddrilldownwidget.h \ + temperaturerecordswidget.h \ + themewidget.h + +SOURCES += \ + areawidget.cpp \ + barmodelmappermodel.cpp \ + barmodelmapperwidget.cpp \ + boxplotdatareader.cpp \ + boxplotwidget.cpp \ + barwidget.cpp \ + barpercentwidget.cpp \ + barstackedwidget.cpp \ + callout.cpp \ + calloutwidget.cpp \ + calloutview.cpp \ + candlestickwidget.cpp \ + candlestickdatareader.cpp \ + contentwidget.cpp \ + customchartwidget.cpp \ + datetimeaxiswidget.cpp \ + donutbreakdownchart.cpp \ + donutbreakdownmainslice.cpp \ + donutbreakdownwidget.cpp \ + donutwidget.cpp \ + dynamicsplinewidget.cpp \ + horizontalbarwidget.cpp \ + horizontalpercentbarwidget.cpp \ + horizontalstackedbarwidget.cpp \ + interactionswidget.cpp \ + interactionsview.cpp \ + interactionschart.cpp \ + legendwidget.cpp \ + legendmarkerswidget.cpp \ + lightmarkerswidget.cpp \ + lineandbarwidget.cpp \ + linewidget.cpp \ + logaxiswidget.cpp \ + main.cpp \ + modeldatamodel.cpp \ + modeldatawidget.cpp \ + multiaxiswidget.cpp \ + nesteddonutswidget.cpp \ + mainwidget.cpp \ + pccpentool.cpp \ + pccbrushtool.cpp \ + pcccustomslice.cpp \ + pccwidget.cpp \ + piedrilldownchart.cpp \ + piedrilldownslice.cpp \ + piedrilldownwidget.cpp \ + piewidget.cpp \ + pointconfigurationwidget.cpp \ + polarchartview.cpp \ + polarchartwidget.cpp \ + scatterinteractionswidget.cpp \ + scatterwidget.cpp \ + selectedbarwidget.cpp \ + splinewidget.cpp \ + stackeddrilldownchart.cpp \ + stackeddrilldownseries.cpp \ + stackeddrilldownwidget.cpp \ + temperaturerecordswidget.cpp \ + themewidget.cpp + +RESOURCES += chartsgallery.qrc + +FORMS += themewidget.ui + +target.path = $$[QT_INSTALL_EXAMPLES]/charts/chartsgallery +INSTALLS += target diff --git a/examples/charts/chartsgallery/chartsgallery.qrc b/examples/charts/chartsgallery/chartsgallery.qrc new file mode 100644 index 00000000..016e2241 --- /dev/null +++ b/examples/charts/chartsgallery/chartsgallery.qrc @@ -0,0 +1,10 @@ +<RCC> + <qresource prefix="/"> + <file alias="boxplot_a">data/boxplot_a.txt</file> + <file alias="boxplot_b">data/boxplot_b.txt</file> + <file alias="candlestick">data/candlestick.txt</file> + <file alias="sun_spots">data/sun_spots.txt</file> + <file alias="blue_triangle">data/blue_triangle.png</file> + <file alias="green_triangle">data/green_triangle.png</file> + </qresource> +</RCC> diff --git a/examples/charts/chartsgallery/contentwidget.cpp b/examples/charts/chartsgallery/contentwidget.cpp new file mode 100644 index 00000000..06ea8314 --- /dev/null +++ b/examples/charts/chartsgallery/contentwidget.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "contentwidget.h" + +#include <QChart> +#include <QChartView> +#include <QFont> +#include <QLabel> +#include <QVBoxLayout> + +ContentWidget::ContentWidget(QWidget *parent) + : QWidget(parent) +{ +} + +void ContentWidget::load() +{ + if (m_loaded || layout()) + return; + + if (!doLoad()) { + auto errorLabel = new QLabel(this); + auto errorLayout = new QVBoxLayout(this); + errorLabel->setText(tr("Error loading the example:\n%1").arg(m_loadError)); + QFont font = errorLabel->font(); + font.setPointSize(20); + errorLabel->setFont(font); + errorLabel->setAlignment(Qt::AlignCenter); + errorLayout->addWidget(errorLabel); + setLayout(errorLayout); + } + + m_loaded = true; +} + +bool ContentWidget::doLoad() +{ + // Most examples do their initialization in constructor. + // Only those that can fail and show error message need to reimplement this method. + return true; +} + +void ContentWidget::resizeEvent(QResizeEvent *) +{ + if (m_defaultChartView) + m_defaultChartView->resize(size()); +} + +// Most examples are simple and need only basic chart view widget, so provide it in this base class +// to avoid duplicating code +void ContentWidget::createDefaultChartView(QChart *chart) +{ + m_defaultChartView = new QChartView(chart, this); + m_defaultChartView->setRenderHint(QPainter::Antialiasing); +} + +void ContentWidget::setDefaultChartView(QChartView *view) +{ + m_defaultChartView = view; + m_defaultChartView->setRenderHint(QPainter::Antialiasing); +} diff --git a/examples/charts/chartsgallery/contentwidget.h b/examples/charts/chartsgallery/contentwidget.h new file mode 100644 index 00000000..780877a5 --- /dev/null +++ b/examples/charts/chartsgallery/contentwidget.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CONTENTWIDGET_H +#define CONTENTWIDGET_H + +#include <QWidget> + +QT_FORWARD_DECLARE_CLASS(QChart) +QT_FORWARD_DECLARE_CLASS(QChartView) + +class ContentWidget : public QWidget +{ + Q_OBJECT +public: + explicit ContentWidget(QWidget *parent = nullptr); + + void load(); + +protected: + virtual bool doLoad(); + void resizeEvent(QResizeEvent *) override; + void createDefaultChartView(QChart *chart); + QChartView *defaultChartView() const { return m_defaultChartView; } + void setDefaultChartView(QChartView *view); + + QString m_loadError; + +private: + QChartView *m_defaultChartView = nullptr; + bool m_loaded = false; +}; + +#endif diff --git a/examples/charts/chartsgallery/customchartwidget.cpp b/examples/charts/chartsgallery/customchartwidget.cpp new file mode 100644 index 00000000..dbe4ba6b --- /dev/null +++ b/examples/charts/chartsgallery/customchartwidget.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "customchartwidget.h" + +#include <QCategoryAxis> +#include <QChart> +#include <QLineSeries> + +CustomChartWidget::CustomChartWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto series = new QLineSeries; + *series << QPointF(0, 6) << QPointF(9, 4) << QPointF(15, 20) << QPointF(25, 12) << QPointF(29, 26); + auto chart = new QChart; + chart->legend()->hide(); + chart->addSeries(series); + //![1] + + //![2] + // Customize series + QPen pen(QRgb(0xfdb157)); + pen.setWidth(5); + series->setPen(pen); + + // Customize chart title + QFont font; + font.setPixelSize(18); + chart->setTitleFont(font); + chart->setTitleBrush(QBrush(Qt::white)); + chart->setTitle("Custom Chart"); + + // Customize chart background + QLinearGradient backgroundGradient; + backgroundGradient.setStart(QPointF(0, 0)); + backgroundGradient.setFinalStop(QPointF(0, 1)); + backgroundGradient.setColorAt(0.0, QRgb(0xd2d0d1)); + backgroundGradient.setColorAt(1.0, QRgb(0x4c4547)); + backgroundGradient.setCoordinateMode(QGradient::ObjectBoundingMode); + chart->setBackgroundBrush(backgroundGradient); + + // Customize plot area background + QLinearGradient plotAreaGradient; + plotAreaGradient.setStart(QPointF(0, 1)); + plotAreaGradient.setFinalStop(QPointF(1, 0)); + plotAreaGradient.setColorAt(0.0, QRgb(0x555555)); + plotAreaGradient.setColorAt(1.0, QRgb(0x55aa55)); + plotAreaGradient.setCoordinateMode(QGradient::ObjectBoundingMode); + chart->setPlotAreaBackgroundBrush(plotAreaGradient); + chart->setPlotAreaBackgroundVisible(true); + //![2] + + //![3] + auto axisX = new QCategoryAxis; + auto axisY = new QCategoryAxis; + + // Customize axis label font + QFont labelsFont; + labelsFont.setPixelSize(12); + axisX->setLabelsFont(labelsFont); + axisY->setLabelsFont(labelsFont); + + // Customize axis colors + QPen axisPen(QRgb(0xd18952)); + axisPen.setWidth(2); + axisX->setLinePen(axisPen); + axisY->setLinePen(axisPen); + + // Customize axis label colors + QBrush axisBrush(Qt::white); + axisX->setLabelsBrush(axisBrush); + axisY->setLabelsBrush(axisBrush); + + // Customize grid lines and shades + axisX->setGridLineVisible(false); + axisY->setGridLineVisible(false); + axisY->setShadesPen(Qt::NoPen); + axisY->setShadesBrush(QBrush(QColor(0x99, 0xcc, 0xcc, 0x55))); + axisY->setShadesVisible(true); + //![3] + + //![4] + axisX->append("low", 10); + axisX->append("optimal", 20); + axisX->append("high", 30); + axisX->setRange(0, 30); + + axisY->append("slow", 10); + axisY->append("med", 20); + axisY->append("fast", 30); + axisY->setRange(0, 30); + + chart->addAxis(axisX, Qt::AlignBottom); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisX); + series->attachAxis(axisY); + //![4] + + //![5] + createDefaultChartView(chart); + //![5] +} diff --git a/examples/charts/chartsgallery/customchartwidget.h b/examples/charts/chartsgallery/customchartwidget.h new file mode 100644 index 00000000..dd8db49d --- /dev/null +++ b/examples/charts/chartsgallery/customchartwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef CUSTOMCHARTWIDGET_H +#define CUSTOMCHARTWIDGET_H + +#include "contentwidget.h" + +class CustomChartWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit CustomChartWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/data/blue_triangle.png b/examples/charts/chartsgallery/data/blue_triangle.png Binary files differnew file mode 100644 index 00000000..7790453c --- /dev/null +++ b/examples/charts/chartsgallery/data/blue_triangle.png diff --git a/examples/charts/chartsgallery/data/boxplot_a.txt b/examples/charts/chartsgallery/data/boxplot_a.txt new file mode 100644 index 00000000..9511418d --- /dev/null +++ b/examples/charts/chartsgallery/data/boxplot_a.txt @@ -0,0 +1,13 @@ +# Acme Ltd share deviation in 2012 +Jan 27.74 27.28 27.86 28.05 28.64 27.47 28.30 28.22 28.72 26.50 26.62 26.50 26.15 26.47 26.41 25.78 24.82 24.89 24.88 24.60 23.85 +Feb 31.79 30.62 30.67 31.37 31.16 31.22 32.02 32.70 31.60 31.24 30.98 30.79 31.10 30.79 31.53 30.92 30.00 30.58 30.37 29.40 28.60 +Mar 28.64 28.34 29.13 29.43 30.75 29.77 29.72 30.52 31.12 33.05 32.51 32.69 31.83 32.47 31.41 31.39 31.78 30.08 29.46 31.58 31.39 31.41 +Apr 25.96 26.62 26.19 30.37 28.78 27.50 28.90 28.40 28.86 28.90 27.91 27.32 27.99 26.86 26.68 27.57 27.50 28.96 28.50 +May 20.85 21.08 21.98 21.61 21.45 21.73 21.71 22.27 21.14 20.65 21.95 22.23 23.17 24.26 24.17 22.97 23.53 24.49 24.51 25.46 25.65 +Jun 18.08 17.19 17.36 17.21 17.31 18.19 18.30 17.53 17.35 17.80 17.17 16.95 18.25 20.52 20.61 21.40 20.45 19.43 19.11 19.74 +Jul 17.75 18.24 17.57 16.53 15.98 16.06 16.64 17.69 17.91 18.00 18.03 18.14 18.10 17.86 18.12 18.53 18.43 18.30 19.03 18.76 18.79 18.33 +Aug 18.69 18.54 18.39 18.49 18.96 18.72 19.25 19.70 20.13 19.74 19.27 18.25 17.72 18.02 18.20 18.24 18.60 18.22 18.60 17.98 17.27 16.70 17.19 +Sep 18.35 18.82 18.96 19.96 19.75 20.55 20.68 21.19 21.14 21.48 21.45 20.74 20.97 20.18 19.66 19.54 18.89 18.39 18.26 18.86 +Oct 16.95 16.80 16.45 16.89 17.38 17.12 16.85 17.59 17.65 17.46 17.43 17.30 17.87 18.61 18.55 18.59 19.27 19.54 20.02 19.23 18.05 18.52 18.71 +Now 19.36 19.29 18.22 18.74 19.05 19.13 18.67 18.19 17.94 18.04 17.49 17.53 17.64 18.00 18.21 18.19 18.30 18.11 18.17 17.76 17.80 17.52 +Dec 19.95 20.19 20.15 20.42 20.39 20.65 20.39 19.86 19.48 19.70 19.94 19.82 20.25 20.21 19.63 19.55 diff --git a/examples/charts/chartsgallery/data/boxplot_b.txt b/examples/charts/chartsgallery/data/boxplot_b.txt new file mode 100644 index 00000000..f1d49cf0 --- /dev/null +++ b/examples/charts/chartsgallery/data/boxplot_b.txt @@ -0,0 +1,13 @@ +# BoxWhisk Inc share deviation in 2012 +Jan 25.74 25.28 25.86 26.05 26.64 28.47 28.30 29.22 29.72 27.50 25.62 25.50 25.15 26.47 27.41 25.78 24.82 24.89 24.88 24.60 23.85 +Feb 30.79 29.62 29.67 30.37 30.16 30.22 31.02 33.70 32.60 32.24 31.98 31.79 31.10 30.79 31.53 30.92 29.00 29.58 30.37 29.40 28.60 +Mar 29.64 29.34 30.13 30.43 31.75 30.77 30.72 31.52 32.12 33.75 33.51 33.69 32.83 32.47 32.41 32.39 32.78 31.08 29.46 31.58 31.39 31.41 +Apr 24.96 25.62 25.19 28.37 28.78 26.50 25.90 26.40 26.86 26.90 27.91 27.32 27.99 26.86 26.68 27.57 27.50 26.96 26.50 +May 21.85 21.08 22.98 22.61 22.45 21.73 21.71 23.27 22.14 19.65 22.95 21.23 23.17 24.26 24.17 21.97 22.53 25.49 25.51 26.46 26.65 +Jun 15.08 15.19 15.36 15.21 15.31 18.19 18.30 15.53 15.35 15.80 15.17 16.95 18.25 20.52 20.61 20.40 20.45 19.43 19.11 19.74 +Jul 17.75 18.24 17.57 16.53 15.98 16.06 16.64 17.69 17.91 19.00 19.03 19.14 19.10 17.86 19.12 19.53 19.43 19.30 19.03 19.76 18.79 18.33 +Aug 19.69 19.54 19.39 19.49 19.96 19.72 20.25 20.70 21.13 20.74 20.27 19.25 18.72 19.02 19.20 19.24 19.60 19.22 19.60 18.98 18.27 17.70 18.19 +Sep 19.35 19.82 19.96 20.96 20.75 21.55 21.68 22.19 22.14 22.48 22.45 21.74 21.97 21.18 20.66 20.54 19.89 19.39 19.26 19.86 +Oct 17.95 17.80 17.45 17.89 18.38 18.12 17.85 18.59 18.65 18.46 18.43 18.30 18.87 19.61 19.55 19.59 20.27 20.54 21.02 20.23 19.05 19.52 19.71 +Now 20.36 20.29 19.22 19.74 20.05 20.13 19.67 19.19 18.94 19.04 18.49 18.53 18.64 19.00 19.21 19.19 19.30 19.11 19.17 18.76 18.80 18.52 +Dec 22.95 22.19 22.15 21.42 21.39 21.65 22.39 20.86 20.48 20.70 20.94 20.82 19.25 21.21 19.63 20.55 diff --git a/examples/charts/chartsgallery/data/candlestick.txt b/examples/charts/chartsgallery/data/candlestick.txt new file mode 100644 index 00000000..eaac318c --- /dev/null +++ b/examples/charts/chartsgallery/data/candlestick.txt @@ -0,0 +1,24 @@ +# Acme Ltd Historical Data (July 2015) +# timestamp, open, high, low, close +1435708800000 126.90 126.94 125.99 126.60 +1435795200000 126.69 126.69 126.69 126.69 +1436140800000 124.85 126.23 124.85 126.00 +1436227200000 125.89 126.15 123.77 125.69 +1436313600000 124.64 124.64 122.54 122.54 +1436400000000 123.85 124.06 119.22 120.07 +1436486400000 121.94 123.85 121.21 123.28 +1436745600000 125.03 125.76 124.32 125.66 +1436832000000 126.04 126.37 125.04 125.61 +1436918400000 125.72 127.15 125.58 126.82 +1437004800000 127.74 128.57 127.35 128.51 +1437091200000 129.08 129.62 128.31 129.62 +1437350400000 130.97 132.97 130.70 132.07 +1437436800000 132.85 132.92 130.32 130.75 +1437523200000 121.99 125.50 121.99 125.22 +1437609600000 126.20 127.09 125.06 125.16 +1437696000000 125.32 125.74 123.90 124.50 +1437955200000 123.09 123.61 122.12 122.77 +1438041600000 123.38 123.91 122.55 123.38 +1438128000000 123.15 123.50 122.27 122.99 +1438214400000 122.32 122.57 121.71 122.37 +1438300800000 122.60 122.64 120.91 121.30 diff --git a/examples/charts/chartsgallery/data/green_triangle.png b/examples/charts/chartsgallery/data/green_triangle.png Binary files differnew file mode 100644 index 00000000..29ae043f --- /dev/null +++ b/examples/charts/chartsgallery/data/green_triangle.png diff --git a/examples/charts/chartsgallery/data/sun_spots.txt b/examples/charts/chartsgallery/data/sun_spots.txt new file mode 100644 index 00000000..91ef5982 --- /dev/null +++ b/examples/charts/chartsgallery/data/sun_spots.txt @@ -0,0 +1,284 @@ +:Recent_Solar_Indices: RecentIndices.txt +:Created: 2012 Aug 06 0759 UTC +# Prepared by the U.S. Dept. of Commerce, NOAA, Space Weather Prediction Center (SWPC). +# Please send comments and suggestions to swpc.webmaster@noaa.gov +# +# Source SWO: SWPC Space Weather Operations (SWO). +# Source RI: S.I.D.C. Brussels International Sunspot Number. +# Source 10.7cm radio flux values (sfu): Penticton, B.C., Canada. +# +# Source Ap: GeoForschungsZentrum, Postdam, Germany +# Prior to January 1997, Institut fur Geophysik, Gottingen, Germany +# Source Ap for final month is NOAA/SWPC estimated Ap. +# +# Data not yet available or not calculable: -1.0 +# +# Values for most recent 6 months are considered preliminary. +# Final values from National Geophysical Data Center www.ngdc.noaa.gov +# +# Recent Solar Indices +# of Observed Monthly Mean Values +# +# -----------Sunspot Numbers--------- ----Radio Flux--- ---Geomagnetic--- +# ---Observed---- Ratio --Smoothed- Observed Smoothed Observed Smoothed +# YR MO SWO RI RI/SW SWO RI 10.7cm 10.7cm Ap Ap +#------------------------------------------------------------------------------- +1991 01 213.5 136.9 0.64 220.5 147.6 229.4 205.5 8 17.4 +1991 02 270.2 167.5 0.62 221.5 147.6 243.0 206.3 10 18.4 +1991 03 227.9 141.9 0.62 220.7 146.6 230.0 205.9 27 19.1 +1991 04 215.9 140.0 0.65 220.7 146.5 198.8 206.8 17 20.0 +1991 05 182.5 121.3 0.66 219.6 145.5 190.3 207.1 18 21.7 +1991 06 231.8 169.7 0.73 218.9 145.2 206.8 207.4 44 23.0 +1991 07 245.7 173.7 0.71 219.5 146.3 212.0 207.7 27 23.6 +1991 08 251.5 176.3 0.70 218.3 146.6 210.3 206.8 30 24.7 +1991 09 185.8 125.3 0.67 214.2 144.9 180.6 203.9 20 25.0 +1991 10 220.1 144.1 0.65 208.4 141.7 201.3 199.7 31 24.3 +1991 11 169.0 108.2 0.64 202.2 138.1 172.0 195.4 33 24.1 +1991 12 217.7 144.4 0.66 193.7 131.7 223.9 188.9 15 23.0 +1992 01 217.9 150.0 0.69 183.3 123.7 217.6 181.8 14 21.1 +1992 02 238.2 161.1 0.68 171.8 115.4 232.1 174.8 31 19.8 +1992 03 160.5 106.7 0.66 161.6 108.2 171.3 168.5 14 19.4 +1992 04 144.0 99.8 0.69 154.3 103.3 158.5 162.9 11 18.9 +1992 05 106.3 73.8 0.69 148.9 100.3 125.4 158.9 21 17.5 +1992 06 104.7 65.2 0.62 143.3 97.1 116.7 154.3 15 16.6 +1992 07 121.4 85.7 0.71 134.3 90.7 132.2 146.7 10 16.6 +1992 08 99.5 64.5 0.65 124.4 84.0 122.1 138.9 15 16.1 +1992 09 93.8 63.9 0.68 117.5 79.5 116.8 133.8 25 15.9 +1992 10 136.2 88.7 0.65 113.4 76.4 131.7 130.5 15 16.7 +1992 11 124.3 91.8 0.74 110.4 74.4 145.2 128.2 14 16.6 +1992 12 127.4 82.6 0.65 107.7 73.2 139.1 127.4 13 16.1 +1993 01 92.1 59.3 0.64 104.5 71.4 121.0 125.7 17 16.0 +1993 02 126.1 91.0 0.72 101.2 69.3 142.6 123.1 16 15.9 +1993 03 107.4 69.8 0.65 97.0 66.6 136.4 120.7 24 15.3 +1993 04 98.6 62.2 0.63 91.9 63.6 115.9 118.1 19 14.9 +1993 05 79.1 61.3 0.77 86.2 59.9 112.4 114.8 12 14.9 +1993 06 68.5 49.8 0.73 81.0 56.1 109.3 111.3 12 15.0 +1993 07 81.6 57.9 0.71 78.7 54.7 99.0 109.6 10 14.9 +1993 08 59.4 42.2 0.71 75.7 52.3 93.7 107.6 12 15.4 +1993 09 33.5 22.4 0.67 70.7 48.4 87.0 103.9 15 16.0 +1993 10 73.5 56.4 0.77 65.5 44.9 100.3 100.4 14 16.4 +1993 11 51.0 35.6 0.70 60.8 41.2 95.9 97.5 15 17.4 +1993 12 75.9 48.9 0.64 57.9 38.4 104.8 94.8 14 18.1 +1994 01 86.4 57.8 0.67 55.6 36.6 115.0 92.7 15 18.2 +1994 02 60.5 35.5 0.59 53.5 34.8 99.6 91.2 30 18.1 +1994 03 52.4 31.7 0.60 52.9 34.1 90.4 90.2 24 17.8 +1994 04 29.3 16.1 0.55 53.0 33.7 79.1 89.3 29 18.0 +1994 05 35.4 17.8 0.50 51.9 32.5 79.9 88.1 26 18.3 +1994 06 42.6 28.0 0.66 49.6 30.8 77.3 86.4 14 18.2 +1994 07 52.7 35.1 0.67 46.1 28.5 80.5 83.9 11 18.1 +1994 08 38.4 22.5 0.59 43.8 26.8 76.1 82.0 8 17.5 +1994 09 40.5 25.7 0.63 43.4 26.6 79.0 81.2 12 16.5 +1994 10 67.1 44.0 0.66 43.2 26.5 87.7 80.9 22 15.5 +1994 11 33.0 18.0 0.55 42.5 26.2 80.9 80.6 14 14.7 +1994 12 38.7 26.2 0.68 41.4 25.6 77.4 80.4 13 14.2 +1995 01 39.8 24.2 0.61 39.6 24.2 82.7 80.1 14 14.0 +1995 02 51.3 29.9 0.58 37.8 23.0 85.6 79.7 15 13.9 +1995 03 51.9 31.1 0.60 36.3 22.1 85.1 79.3 15 14.0 +1995 04 23.6 14.0 0.59 33.8 20.6 77.7 78.6 16 13.8 +1995 05 24.6 14.5 0.59 31.6 19.2 75.5 77.9 18 13.3 +1995 06 27.8 15.6 0.56 29.9 18.2 75.7 77.4 11 12.9 +1995 07 23.8 14.5 0.61 28.1 17.0 73.9 76.9 8 12.5 +1995 08 25.1 14.3 0.57 25.4 15.4 73.8 76.0 9 12.1 +1995 09 16.5 11.8 0.72 22.0 13.4 72.0 74.8 13 11.8 +1995 10 31.6 21.1 0.67 19.7 12.1 77.9 73.8 16 11.4 +1995 11 15.7 9.0 0.57 18.5 11.3 74.2 73.2 9 10.7 +1995 12 16.2 10.0 0.62 17.6 10.8 72.6 72.8 9 10.0 +1996 01 17.6 11.5 0.65 16.8 10.4 74.5 72.4 9 9.7 +1996 02 9.1 4.4 0.48 16.2 10.1 71.5 72.2 10 9.7 +1996 03 12.1 9.2 0.76 15.4 9.7 70.7 72.1 11 9.8 +1996 04 8.5 4.8 0.56 13.6 8.4 69.3 71.6 11 9.7 +1996 05 11.8 5.5 0.47 12.9 8.0 70.1 71.4 7 9.5 +1996 06 18.8 11.8 0.63 13.5 8.5 69.6 71.8 5 9.4 +1996 07 13.2 8.2 0.62 13.4 8.4 71.2 72.0 7 9.3 +1996 08 20.5 14.4 0.70 13.1 8.3 72.4 72.1 9 9.4 +1996 09 2.9 1.6 0.55 13.3 8.4 69.4 72.3 15 9.3 +1996 10 2.3 0.9 0.39 14.0 8.8 69.2 72.6 13 9.1 +1996 11 26.7 17.9 0.67 15.4 9.8 78.7 73.0 8 9.1 +1996 12 21.1 13.3 0.63 16.2 10.4 77.8 73.3 7 9.3 +1997 01 9.0 5.7 0.63 16.5 10.5 74.0 73.4 9 9.3 +1997 02 11.3 7.6 0.67 17.4 11.0 73.8 73.7 11 9.2 +1997 03 14.4 8.7 0.60 20.4 13.5 73.5 75.1 8 8.9 +1997 04 24.5 15.5 0.63 24.0 16.5 74.5 76.8 10 8.5 +1997 05 28.6 18.5 0.65 26.4 18.4 74.6 78.4 8 8.5 +1997 06 22.1 12.7 0.57 29.0 20.4 71.7 80.1 7 8.5 +1997 07 17.0 10.4 0.61 32.4 22.7 71.1 81.8 6 8.4 +1997 08 36.7 24.4 0.66 35.9 25.1 79.0 83.4 7 8.2 +1997 09 58.2 51.3 0.88 40.5 28.4 96.2 85.7 10 8.3 +1997 10 33.6 23.8 0.71 45.4 31.9 84.9 88.6 10 8.5 +1997 11 53.5 39.0 0.73 49.3 35.0 99.5 91.3 11 8.9 +1997 12 57.9 41.2 0.71 54.2 39.0 98.8 94.2 4 9.5 +1998 01 51.8 31.9 0.62 60.6 43.8 93.4 97.5 8 9.8 +1998 02 54.4 40.3 0.74 67.4 49.0 93.4 101.7 8 10.5 +1998 03 81.8 54.8 0.67 73.3 53.5 109.1 105.8 13 11.0 +1998 04 73.6 53.4 0.73 77.7 56.6 108.3 108.9 10 11.3 +1998 05 74.3 56.3 0.76 81.4 59.4 106.7 112.0 18 11.6 +1998 06 93.6 70.7 0.76 85.9 62.5 108.4 115.8 10 12.0 +1998 07 98.3 66.6 0.68 90.3 65.5 114.0 120.0 11 12.3 +1998 08 118.6 92.2 0.78 93.7 67.8 136.0 124.1 18 12.5 +1998 09 119.0 92.9 0.78 96.1 69.5 138.3 126.8 13 12.7 +1998 10 77.0 55.5 0.72 97.7 70.5 117.3 127.9 13 12.8 +1998 11 99.5 74.0 0.74 101.3 73.0 140.2 130.0 16 12.5 +1998 12 120.8 81.9 0.68 108.8 77.9 150.1 134.3 8 12.0 +1999 01 94.3 62.0 0.66 116.5 82.6 142.6 139.0 10 11.8 +1999 02 93.4 66.3 0.71 120.2 84.6 142.0 142.6 12 11.6 +1999 03 100.5 68.8 0.68 120.5 83.8 126.3 144.0 14 11.8 +1999 04 92.9 63.7 0.69 123.8 85.5 117.2 145.8 12 12.3 +1999 05 140.5 106.4 0.76 131.7 90.5 148.6 149.9 8 12.4 +1999 06 208.3 137.7 0.66 136.0 93.1 169.8 152.9 7 12.4 +1999 07 169.2 113.5 0.67 138.0 94.3 165.6 154.4 10 12.6 +1999 08 136.1 93.7 0.69 142.8 97.5 170.8 156.3 15 12.9 +1999 09 107.4 71.5 0.67 150.0 102.3 135.7 161.0 19 12.9 +1999 10 167.7 116.7 0.70 158.5 107.8 164.8 167.2 19 12.8 +1999 11 199.3 133.2 0.67 164.7 111.0 191.5 171.5 14 13.2 +1999 12 123.5 84.6 0.69 165.9 111.1 169.8 173.4 10 13.8 +2000 01 140.8 90.1 0.64 168.0 112.9 158.1 175.5 13 14.6 +2000 02 161.9 112.9 0.70 172.1 116.8 173.2 176.8 16 15.1 +2000 03 203.6 138.5 0.68 175.4 119.9 208.2 178.4 9 15.1 +2000 04 193.4 125.5 0.65 176.3 120.8 184.2 180.5 15 15.0 +2000 05 188.8 121.6 0.64 173.1 119.0 184.5 180.0 15 15.1 +2000 06 190.3 124.9 0.66 172.0 118.7 179.8 179.7 15 15.1 +2000 07 236.7 170.1 0.72 173.0 119.8 204.7 180.2 21 14.8 +2000 08 166.6 130.5 0.78 171.8 118.6 163.1 179.4 16 14.2 +2000 09 157.9 109.7 0.69 169.0 116.3 182.1 177.1 18 14.3 +2000 10 138.9 99.4 0.72 166.2 114.5 167.7 175.5 18 15.0 +2000 11 149.9 106.8 0.71 162.7 112.7 178.8 173.8 17 15.1 +2000 12 146.4 104.4 0.71 160.8 112.0 173.6 172.0 7 14.7 +2001 01 142.7 95.6 0.67 156.3 108.7 166.6 168.7 8 14.0 +2001 02 131.0 80.6 0.62 151.4 104.0 146.7 165.6 7 13.3 +2001 03 166.7 113.5 0.68 154.0 104.8 177.7 167.8 20 12.9 +2001 04 163.6 107.7 0.66 159.4 107.5 178.1 171.6 22 12.8 +2001 05 135.1 96.6 0.72 163.1 108.6 147.9 174.7 10 12.8 +2001 06 196.7 134.0 0.68 167.2 109.8 173.7 178.7 10 12.8 +2001 07 124.6 81.8 0.66 172.1 111.7 131.3 183.8 9 12.9 +2001 08 159.4 106.4 0.67 176.7 113.6 163.1 188.8 11 13.0 +2001 09 229.1 150.7 0.66 178.8 114.1 233.8 191.3 13 12.8 +2001 10 197.4 125.5 0.64 179.5 114.0 208.1 191.9 20 12.1 +2001 11 178.6 106.5 0.60 183.7 115.5 212.7 193.7 16 12.0 +2001 12 217.5 132.2 0.61 184.5 114.6 235.6 193.9 9 12.0 +2002 01 189.0 114.1 0.60 184.8 113.5 227.3 194.6 8 11.9 +2002 02 194.5 107.4 0.55 188.6 114.6 205.0 197.2 10 12.1 +2002 03 153.1 98.4 0.64 188.9 113.3 180.3 195.7 10 12.3 +2002 04 194.9 120.7 0.62 186.2 110.5 189.8 191.5 17 12.5 +2002 05 204.1 120.8 0.59 183.6 108.8 178.4 188.0 12 12.7 +2002 06 146.0 88.3 0.60 179.9 106.2 148.7 182.9 7 12.9 +2002 07 183.5 99.6 0.54 175.4 102.7 173.5 176.2 11 13.3 +2002 08 191.0 116.4 0.61 169.2 98.7 183.9 169.3 14 13.8 +2002 09 206.4 109.6 0.53 163.4 94.6 175.8 164.0 13 14.5 +2002 10 153.9 97.5 0.63 158.8 90.5 167.0 159.3 25 15.1 +2002 11 159.8 95.5 0.60 150.9 85.2 168.7 154.1 17 15.8 +2002 12 147.9 80.8 0.55 145.0 82.0 157.2 150.7 13 17.1 +2003 01 149.3 79.7 0.53 141.7 80.8 144.0 148.0 13 18.2 +2003 02 87.0 46.0 0.53 136.4 78.3 124.5 143.5 17 18.9 +2003 03 119.7 61.1 0.51 128.1 74.0 132.2 138.3 21 19.5 +2003 04 119.7 60.0 0.50 121.5 70.1 126.3 135.0 20 20.1 +2003 05 89.6 54.6 0.61 118.3 67.6 116.2 133.1 26 21.0 +2003 06 118.4 77.4 0.65 113.6 65.0 129.3 130.2 24 21.5 +2003 07 132.8 83.3 0.63 106.9 61.8 127.7 127.2 19 22.0 +2003 08 114.3 72.7 0.64 102.8 60.0 122.1 125.2 23 22.3 +2003 09 82.6 48.7 0.59 100.7 59.5 112.2 123.7 18 21.8 +2003 10 118.9 65.5 0.55 96.6 58.2 151.3 121.8 35 21.1 +2003 11 118.9 67.3 0.57 93.6 56.7 140.8 120.1 28 20.0 +2003 12 75.4 46.5 0.62 91.4 54.8 115.0 118.0 16 18.6 +2004 01 62.3 37.3 0.60 87.9 52.0 114.1 116.3 22 18.1 +2004 02 75.6 45.8 0.61 84.2 49.3 107.0 115.5 13 17.7 +2004 03 81.0 49.1 0.61 80.9 47.1 112.0 114.6 14 16.7 +2004 04 59.3 39.3 0.66 77.9 45.5 101.2 112.4 11 15.2 +2004 05 77.3 41.5 0.54 74.1 43.8 99.8 109.3 8 14.0 +2004 06 78.9 43.2 0.55 70.4 41.6 97.4 107.4 8 13.6 +2004 07 87.8 51.1 0.58 68.3 40.2 118.5 106.1 23 13.5 +2004 08 69.5 40.9 0.59 66.6 39.2 111.0 105.2 9 13.5 +2004 09 50.0 27.7 0.55 63.7 37.5 103.0 103.8 9 13.3 +2004 10 77.9 48.0 0.62 61.3 35.9 105.9 102.3 8 13.3 +2004 11 70.5 43.5 0.62 60.0 35.3 113.7 101.6 25 13.7 +2004 12 34.7 17.9 0.52 58.8 35.2 95.0 101.5 11 14.3 +2005 01 52.0 31.3 0.60 57.3 34.6 102.2 100.4 24 14.1 +2005 02 45.4 29.2 0.64 56.4 33.9 97.2 98.6 11 14.0 +2005 03 41.0 24.5 0.60 55.8 33.5 89.9 97.3 12 14.6 +2005 04 41.5 24.2 0.58 52.6 31.6 86.0 95.5 11 15.1 +2005 05 65.4 42.7 0.65 48.3 28.9 99.5 93.2 19 14.4 +2005 06 59.8 39.3 0.66 47.9 28.8 93.7 91.9 12 13.6 +2005 07 71.0 40.1 0.56 48.1 29.1 96.5 90.9 14 12.8 +2005 08 65.6 36.4 0.55 45.4 27.4 90.5 89.2 14 11.8 +2005 09 39.2 21.9 0.56 42.9 25.8 91.1 87.8 20 11.4 +2005 10 13.0 8.7 0.67 42.6 25.5 76.6 87.3 8 11.3 +2005 11 32.2 18.0 0.56 42.1 24.9 86.2 86.7 8 10.8 +2005 12 62.6 41.1 0.66 40.1 23.0 90.7 85.2 9 10.0 +2006 01 28.0 15.3 0.55 37.2 20.8 83.4 83.6 6 9.5 +2006 02 5.3 4.9 0.92 33.4 18.6 76.5 82.3 6 9.0 +2006 03 21.3 10.6 0.50 31.0 17.4 75.5 81.2 8 8.3 +2006 04 55.2 30.2 0.55 30.6 17.1 89.0 80.6 11 7.8 +2006 05 39.6 22.3 0.56 30.7 17.3 80.9 80.5 7 7.9 +2006 06 37.7 13.9 0.37 28.9 16.3 76.5 80.2 7 8.2 +2006 07 22.6 12.2 0.54 27.2 15.2 75.8 80.0 6 8.6 +2006 08 22.8 12.9 0.57 27.6 15.6 79.4 80.1 9 8.8 +2006 09 25.2 14.4 0.57 27.7 15.5 77.8 80.0 8 8.8 +2006 10 15.7 10.5 0.67 25.2 14.2 74.3 79.1 9 8.8 +2006 11 31.5 21.4 0.68 22.3 12.6 86.3 78.2 9 8.7 +2006 12 22.2 13.6 0.61 20.7 12.1 84.5 77.8 15 8.7 +2007 01 26.6 16.8 0.63 19.7 11.9 83.5 77.5 10 8.7 +2007 02 17.2 10.7 0.62 18.9 11.5 77.7 76.9 7 8.6 +2007 03 9.7 4.5 0.46 17.5 10.7 72.2 76.0 8 8.5 +2007 04 6.9 3.4 0.49 16.0 9.8 72.4 75.3 9 8.5 +2007 05 19.4 11.7 0.60 14.2 8.6 74.4 74.3 8 8.3 +2007 06 20.0 12.1 0.61 12.8 7.6 73.7 73.3 6 7.9 +2007 07 15.6 9.7 0.62 11.6 6.9 71.6 72.7 7 7.4 +2007 08 9.9 6.0 0.61 10.2 6.0 69.1 72.1 6 7.5 +2007 09 4.8 2.4 0.50 9.9 5.9 67.0 71.8 9 7.8 +2007 10 1.3 0.9 0.69 10.0 6.0 67.5 71.8 7 7.9 +2007 11 2.5 1.7 0.68 9.4 5.7 69.6 71.4 7 7.8 +2007 12 16.2 10.1 0.62 8.1 4.9 78.5 70.8 6 7.8 +2008 01 5.1 3.3 0.65 6.9 4.2 74.3 70.3 8 7.8 +2008 02 3.8 2.1 0.55 5.9 3.6 71.1 69.9 11 7.6 +2008 03 15.9 9.3 0.58 5.3 3.3 72.9 69.8 11 7.5 +2008 04 4.9 2.9 0.59 5.3 3.4 70.2 69.8 9 7.3 +2008 05 5.7 3.2 0.56 5.7 3.5 68.4 69.8 6 7.2 +2008 06 4.2 3.4 0.81 5.2 3.3 65.9 69.4 7 7.0 +2008 07 1.0 0.8 0.80 4.5 2.8 65.7 68.8 5 6.8 +2008 08 0.0 0.5 -1.00 4.4 2.7 66.3 68.6 5 6.3 +2008 09 1.5 1.1 0.73 3.7 2.3 67.1 68.4 6 5.8 +2008 10 5.2 2.9 0.56 2.9 1.8 68.3 68.2 7 5.4 +2008 11 6.8 4.1 0.60 2.7 1.7 68.6 68.3 4 5.1 +2008 12 1.3 0.8 0.62 2.7 1.7 69.2 68.5 4 4.9 +2009 01 2.8 1.3 0.46 3.0 1.8 69.8 68.7 4 4.7 +2009 02 2.5 1.4 0.56 3.1 1.9 70.0 68.8 5 4.7 +2009 03 0.7 0.7 1.00 3.4 2.0 69.2 69.0 5 4.6 +2009 04 1.2 0.8 1.00 3.7 2.2 69.7 69.3 4 4.3 +2009 05 3.9 2.9 0.74 3.8 2.3 70.5 69.7 4 4.1 +2009 06 6.6 2.9 0.39 4.4 2.7 68.6 70.2 4 4.0 +2009 07 5.0 3.2 0.70 5.8 3.6 68.2 71.0 4 3.9 +2009 08 0.3 0.0 0.00 7.7 4.8 67.4 72.1 5 3.8 +2009 09 6.6 4.3 0.64 9.9 6.2 70.5 73.3 4 3.8 +2009 10 7.0 4.8 0.66 11.3 7.1 72.3 74.1 3 4.1 +2009 11 7.7 4.1 0.55 12.4 7.6 73.6 74.5 3 4.5 +2009 12 15.7 10.8 0.68 13.6 8.3 76.8 74.9 2 4.8 +2010 01 21.3 13.2 0.62 14.8 9.3 81.1 75.5 3 5.0 +2010 02 31.0 18.8 0.60 16.7 10.6 84.7 76.5 5 5.1 +2010 03 24.7 15.4 0.62 19.1 12.3 83.3 77.5 5 5.3 +2010 04 11.2 8.0 0.71 21.4 14.0 75.9 78.3 10 5.5 +2010 05 19.9 8.7 0.44 23.8 15.5 73.8 79.0 8 5.7 +2010 06 17.9 13.6 0.75 25.2 16.4 72.6 79.7 7 5.8 +2010 07 23.1 16.1 0.70 25.9 16.7 79.9 80.1 5 6.0 +2010 08 28.2 19.6 0.70 27.3 17.4 79.7 80.7 8 6.2 +2010 09 35.6 25.2 0.71 30.6 19.6 81.1 82.4 5 6.3 +2010 10 35.0 23.5 0.67 35.9 23.2 81.6 85.3 6 6.4 +2010 11 36.1 21.5 0.60 40.5 26.5 82.5 87.7 5 6.4 +2010 12 22.0 14.4 0.66 43.8 28.8 84.3 89.6 4 6.5 +2011 01 32.1 18.8 0.59 47.2 30.9 83.7 91.2 6 6.7 +2011 02 53.2 29.6 0.55 50.6 33.4 94.5 92.7 6 6.8 +2011 03 81.0 55.8 0.69 55.2 36.9 115.3 95.8 7 7.2 +2011 04 81.7 54.4 0.67 61.5 41.8 112.6 100.4 9 7.5 +2011 05 61.4 41.5 0.68 69.0 47.6 95.9 105.6 9 7.5 +2011 06 55.5 37.0 0.67 76.5 53.2 95.8 110.9 8 7.4 +2011 07 67.0 43.8 0.66 82.5 57.2 94.2 115.4 9 7.3 +2011 08 66.1 50.6 0.77 84.9 59.0 101.7 117.9 8 7.4 +2011 09 106.4 78.0 0.73 84.6 59.5 134.5 118.4 13 7.7 +2011 10 116.8 88.0 0.75 84.6 59.9 137.2 118.4 7 8.0 +2011 11 133.1 96.7 0.73 86.3 61.1 153.1 119.5 3 8.0 +2011 12 106.3 73.0 0.69 89.2 63.4 141.2 121.6 3 8.0 +2012 01 91.3 58.3 0.64 92.0 65.5 133.1 124.4 6 8.3 +2012 02 50.1 32.9 0.66 -1.0 -1.0 106.7 -1.0 7 -1.0 +2012 03 77.9 64.3 0.82 -1.0 -1.0 115.1 -1.0 14 -1.0 +2012 04 84.4 55.2 0.65 -1.0 -1.0 113.1 -1.0 9 -1.0 +2012 05 99.5 69.0 0.69 -1.0 -1.0 121.5 -1.0 8 -1.0 +2012 06 88.6 64.5 0.73 -1.0 -1.0 120.5 -1.0 10 -1.0 +2012 07 99.6 66.5 0.67 -1.0 -1.0 135.6 -1.0 13 -1.0 diff --git a/examples/charts/chartsgallery/datetimeaxiswidget.cpp b/examples/charts/chartsgallery/datetimeaxiswidget.cpp new file mode 100644 index 00000000..c71e7291 --- /dev/null +++ b/examples/charts/chartsgallery/datetimeaxiswidget.cpp @@ -0,0 +1,75 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "datetimeaxiswidget.h" + +#include <QChart> +#include <QDateTime> +#include <QDateTimeAxis> +#include <QFile> +#include <QLineSeries> +#include <QTextStream> +#include <QValueAxis> + +DateTimeAxisWidget::DateTimeAxisWidget(QWidget *parent) + : ContentWidget(parent) +{ +} + +bool DateTimeAxisWidget::doLoad() +{ + //![1] + auto series = new QLineSeries; + //![1] + + //![2] + // data from http://www.swpc.noaa.gov/ftpdir/weekly/RecentIndices.txt + // http://www.swpc.noaa.gov/ftpdir/weekly/README + // http://www.weather.gov/disclaimer + QFile sunSpots(":sun_spots"); + if (!sunSpots.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_loadError = QStringLiteral("Failed to load '%1' file.").arg(sunSpots.fileName()); + return false; + } + + QTextStream stream(&sunSpots); + while (!stream.atEnd()) { + QString line = stream.readLine(); + if (line.startsWith("#") || line.startsWith(":")) + continue; + QStringList values = line.split(QLatin1Char(' '), Qt::SkipEmptyParts); + QDateTime momentInTime; + momentInTime.setDate(QDate(values[0].toInt(), values[1].toInt() , 15)); + series->append(momentInTime.toMSecsSinceEpoch(), values[2].toDouble()); + } + sunSpots.close(); + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->legend()->hide(); + chart->setTitle("Sunspots count (by Space Weather Prediction Center)"); + //![3] + + //![4] + auto axisX = new QDateTimeAxis; + axisX->setTickCount(10); + axisX->setFormat("MMM yyyy"); + axisX->setTitleText("Date"); + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + + auto axisY = new QValueAxis; + axisY->setLabelFormat("%i"); + axisY->setTitleText("Sunspots count"); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + //![4] + + //![5] + createDefaultChartView(chart); + //![5] + + return true; +} diff --git a/examples/charts/chartsgallery/datetimeaxiswidget.h b/examples/charts/chartsgallery/datetimeaxiswidget.h new file mode 100644 index 00000000..1baf6df9 --- /dev/null +++ b/examples/charts/chartsgallery/datetimeaxiswidget.h @@ -0,0 +1,19 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef DATETIMEAXISWIDGET_H +#define DATETIMEAXISWIDGET_H + +#include "contentwidget.h" + +class DateTimeAxisWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit DateTimeAxisWidget(QWidget *parent = nullptr); + +protected: + bool doLoad() override; +}; + +#endif diff --git a/examples/charts/chartsgallery/donutbreakdownchart.cpp b/examples/charts/chartsgallery/donutbreakdownchart.cpp new file mode 100644 index 00000000..7faf09e4 --- /dev/null +++ b/examples/charts/chartsgallery/donutbreakdownchart.cpp @@ -0,0 +1,100 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "donutbreakdownchart.h" +#include "donutbreakdownmainslice.h" + +#include <QPieLegendMarker> +#include <QPieSeries> +#include <QPieSlice> + +QT_USE_NAMESPACE + +//![1] +DonutBreakdownChart::DonutBreakdownChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) + : QChart(QChart::ChartTypeCartesian, parent, wFlags) +{ + // create the series for main center pie + m_mainSeries = new QPieSeries; + m_mainSeries->setPieSize(0.7); + QChart::addSeries(m_mainSeries); +} +//![1] + +//![2] +void DonutBreakdownChart::addBreakdownSeries(QPieSeries *breakdownSeries, QColor color) +{ + QFont font("Arial", 8); + + // add breakdown series as a slice to center pie + auto mainSlice = new DonutBreakdownMainSlice(breakdownSeries); + mainSlice->setName(breakdownSeries->name()); + mainSlice->setValue(breakdownSeries->sum()); + m_mainSeries->append(mainSlice); + + // customize the slice + mainSlice->setBrush(color); + mainSlice->setLabelVisible(); + mainSlice->setLabelColor(Qt::white); + mainSlice->setLabelPosition(QPieSlice::LabelInsideHorizontal); + mainSlice->setLabelFont(font); + + // position and customize the breakdown series + breakdownSeries->setPieSize(0.8); + breakdownSeries->setHoleSize(0.7); + breakdownSeries->setLabelsVisible(); + const auto slices = breakdownSeries->slices(); + for (QPieSlice *slice : slices) { + color = color.lighter(115); + slice->setBrush(color); + slice->setLabelFont(font); + } + + // add the series to the chart + QChart::addSeries(breakdownSeries); + + // recalculate breakdown donut segments + recalculateAngles(); + + // update customize legend markers + updateLegendMarkers(); +} +//![2] + +//![3] +void DonutBreakdownChart::recalculateAngles() +{ + qreal angle = 0; + const auto slices = m_mainSeries->slices(); + for (QPieSlice *slice : slices) { + QPieSeries *breakdownSeries = qobject_cast<DonutBreakdownMainSlice *>(slice)->breakdownSeries(); + breakdownSeries->setPieStartAngle(angle); + angle += slice->percentage() * 360.0; // full pie is 360.0 + breakdownSeries->setPieEndAngle(angle); + } +} +//![3] + +//![4] +void DonutBreakdownChart::updateLegendMarkers() +{ + // go through all markers + const auto allseries = series(); + for (QAbstractSeries *series : allseries) { + const auto markers = legend()->markers(series); + for (QLegendMarker *marker : markers) { + auto pieMarker = qobject_cast<QPieLegendMarker *>(marker); + if (series == m_mainSeries) { + // hide markers from main series + pieMarker->setVisible(false); + } else { + // modify markers from breakdown series + pieMarker->setLabel(QString("%1 %2%") + .arg(pieMarker->slice()->label()) + .arg(pieMarker->slice()->percentage() * 100, 0, 'f', 2)); + pieMarker->setFont(QFont("Arial", 8)); + } + } + } +} +//![4] diff --git a/examples/charts/chartsgallery/donutbreakdownchart.h b/examples/charts/chartsgallery/donutbreakdownchart.h new file mode 100644 index 00000000..5cfc574e --- /dev/null +++ b/examples/charts/chartsgallery/donutbreakdownchart.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef DONUTBREAKDOWNCHART_H +#define DONUTBREAKDOWNCHART_H + +#include <QChart> + +QT_FORWARD_DECLARE_CLASS(QPieSeries) + +class DonutBreakdownChart : public QChart +{ +public: + DonutBreakdownChart(QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = {}); + void addBreakdownSeries(QPieSeries *series, QColor color); + +private: + void recalculateAngles(); + void updateLegendMarkers(); + +private: + QPieSeries *m_mainSeries = nullptr; +}; + +#endif diff --git a/examples/charts/chartsgallery/donutbreakdownmainslice.cpp b/examples/charts/chartsgallery/donutbreakdownmainslice.cpp new file mode 100644 index 00000000..eb0dd4cb --- /dev/null +++ b/examples/charts/chartsgallery/donutbreakdownmainslice.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "donutbreakdownmainslice.h" + +//![1] +DonutBreakdownMainSlice::DonutBreakdownMainSlice(QPieSeries *breakdownSeries, QObject *parent) + : QPieSlice(parent), + m_breakdownSeries(breakdownSeries) +{ + connect(this, &DonutBreakdownMainSlice::percentageChanged, + this, &DonutBreakdownMainSlice::updateLabel); +} +//![1] + +QPieSeries *DonutBreakdownMainSlice::breakdownSeries() const +{ + return m_breakdownSeries; +} + +void DonutBreakdownMainSlice::setName(const QString &name) +{ + m_name = name; +} + +QString DonutBreakdownMainSlice::name() const +{ + return m_name; +} + +//![2] +void DonutBreakdownMainSlice::updateLabel() +{ + setLabel(QString("%1 %2%").arg(m_name).arg(percentage() * 100, 0, 'f', 2)); +} +//![2] diff --git a/examples/charts/chartsgallery/donutbreakdownmainslice.h b/examples/charts/chartsgallery/donutbreakdownmainslice.h new file mode 100644 index 00000000..f0088400 --- /dev/null +++ b/examples/charts/chartsgallery/donutbreakdownmainslice.h @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef DONUTBREAKDOWNMAINSLICE_H +#define DONUTBREAKDOWNMAINSLICE_H + +#include <QPieSlice> + +QT_FORWARD_DECLARE_CLASS(QPieSeries) + +class DonutBreakdownMainSlice : public QPieSlice +{ + Q_OBJECT +public: + DonutBreakdownMainSlice(QPieSeries *breakdownSeries, QObject *parent = nullptr); + + QPieSeries *breakdownSeries() const; + + void setName(const QString &name); + QString name() const; + +public slots: + void updateLabel(); + +private: + QPieSeries *m_breakdownSeries = nullptr; + QString m_name; +}; + +#endif diff --git a/examples/charts/chartsgallery/donutbreakdownwidget.cpp b/examples/charts/chartsgallery/donutbreakdownwidget.cpp new file mode 100644 index 00000000..63181601 --- /dev/null +++ b/examples/charts/chartsgallery/donutbreakdownwidget.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "donutbreakdownchart.h" +#include "donutbreakdownwidget.h" + +#include <QChart> +#include <QPieSeries> + +DonutBreakdownWidget::DonutBreakdownWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + // Graph is based on data of 'Total consumption of energy increased by 10 per cent in 2010' + // Statistics Finland, 13 December 2011 + // http://www.stat.fi/til/ekul/2010/ekul_2010_2011-12-13_tie_001_en.html + + auto series1 = new QPieSeries; + series1->setName("Fossil fuels"); + series1->append("Oil", 353295); + series1->append("Coal", 188500); + series1->append("Natural gas", 148680); + series1->append("Peat", 94545); + + auto series2 = new QPieSeries; + series2->setName("Renewables"); + series2->append("Wood fuels", 319663); + series2->append("Hydro power", 45875); + series2->append("Wind power", 1060); + + auto series3 = new QPieSeries; + series3->setName("Others"); + series3->append("Nuclear energy", 238789); + series3->append("Import energy", 37802); + series3->append("Other", 32441); + //![1] + + //![2] + auto donutBreakdown = new DonutBreakdownChart; + donutBreakdown->setAnimationOptions(QChart::AllAnimations); + donutBreakdown->setTitle("Total consumption of energy in Finland 2010"); + donutBreakdown->legend()->setAlignment(Qt::AlignRight); + donutBreakdown->addBreakdownSeries(series1, Qt::red); + donutBreakdown->addBreakdownSeries(series2, Qt::darkGreen); + donutBreakdown->addBreakdownSeries(series3, Qt::darkBlue); + //![2] + + //![3] + createDefaultChartView(donutBreakdown); + //![3] +} diff --git a/examples/charts/chartsgallery/donutbreakdownwidget.h b/examples/charts/chartsgallery/donutbreakdownwidget.h new file mode 100644 index 00000000..4b2f9b43 --- /dev/null +++ b/examples/charts/chartsgallery/donutbreakdownwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef DONUTBREAKDOWNWIDGET_H +#define DONUTBREAKDOWNWIDGET_H + +#include "contentwidget.h" + +class DonutBreakdownWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit DonutBreakdownWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/donutwidget.cpp b/examples/charts/chartsgallery/donutwidget.cpp new file mode 100644 index 00000000..2bee9974 --- /dev/null +++ b/examples/charts/chartsgallery/donutwidget.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "donutwidget.h" + +#include <QChart> +#include <QPieSeries> +#include <QPieSlice> + +DonutWidget::DonutWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto series = new QPieSeries; + series->setHoleSize(0.35); + series->append("Protein 4.2%", 4.2); + QPieSlice *slice = series->append("Fat 15.6%", 15.6); + slice->setExploded(); + slice->setLabelVisible(); + series->append("Other 23.8%", 23.8); + series->append("Carbs 56.4%", 56.4); + //![1] + + //![2] + auto chart = new QChart; + chart->setTitle("Donut with a lemon glaze (100g)"); + chart->addSeries(series); + chart->legend()->setAlignment(Qt::AlignBottom); + chart->setTheme(QChart::ChartThemeBlueCerulean); + chart->legend()->setFont(QFont("Arial", 7)); + + createDefaultChartView(chart); + //![2] +} diff --git a/examples/charts/chartsgallery/donutwidget.h b/examples/charts/chartsgallery/donutwidget.h new file mode 100644 index 00000000..cf27c2ae --- /dev/null +++ b/examples/charts/chartsgallery/donutwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef DONUTWIDGET_H +#define DONUTWIDGET_H + +#include "contentwidget.h" + +class DonutWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit DonutWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/dynamicsplinewidget.cpp b/examples/charts/chartsgallery/dynamicsplinewidget.cpp new file mode 100644 index 00000000..fce79d79 --- /dev/null +++ b/examples/charts/chartsgallery/dynamicsplinewidget.cpp @@ -0,0 +1,57 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "dynamicsplinewidget.h" + +#include <QChart> +#include <QChartView> +#include <QRandomGenerator> +#include <QSplineSeries> +#include <QValueAxis> + +DynamicSplineWidget::DynamicSplineWidget(QWidget *parent) + : ContentWidget(parent) +{ + auto chart = new QChart; + chart->setTitle("Dynamic Spline Chart"); + chart->legend()->hide(); + chart->setAnimationOptions(QChart::AllAnimations); + + m_axisX = new QValueAxis; + m_axisY = new QValueAxis; + + QObject::connect(&m_timer, &QTimer::timeout, this, &DynamicSplineWidget::handleTimeout); + m_timer.setInterval(1000); + + m_series = new QSplineSeries; + QPen green(Qt::red); + green.setWidth(3); + m_series->setPen(green); + m_series->append(m_x, m_y); + + chart->addSeries(m_series); + + chart->addAxis(m_axisX,Qt::AlignBottom); + chart->addAxis(m_axisY,Qt::AlignLeft); + m_series->attachAxis(m_axisX); + m_series->attachAxis(m_axisY); + m_axisX->setTickCount(5); + m_axisX->setRange(0, 10); + m_axisY->setRange(-5, 10); + + createDefaultChartView(chart); + + m_timer.start(); +} + +void DynamicSplineWidget::handleTimeout() +{ + qreal x = defaultChartView()->chart()->plotArea().width() / m_axisX->tickCount(); + qreal y = (m_axisX->max() - m_axisX->min()) / m_axisX->tickCount(); + m_x += y; + m_y = QRandomGenerator::global()->bounded(5) - 2.5; + m_series->append(m_x, m_y); + defaultChartView()->chart()->scroll(x, 0); + if (m_x >= 100) + m_timer.stop(); +} diff --git a/examples/charts/chartsgallery/dynamicsplinewidget.h b/examples/charts/chartsgallery/dynamicsplinewidget.h new file mode 100644 index 00000000..1389280b --- /dev/null +++ b/examples/charts/chartsgallery/dynamicsplinewidget.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef DYNAMICSPLINEWIDGET_H +#define DYNAMICSPLINEWIDGET_H + +#include "contentwidget.h" + +#include <QTimer> + +QT_FORWARD_DECLARE_CLASS(QSplineSeries) +QT_FORWARD_DECLARE_CLASS(QValueAxis) + +class DynamicSplineWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit DynamicSplineWidget(QWidget *parent = nullptr); + +public slots: + void handleTimeout(); + +private: + QSplineSeries *m_series = nullptr; + QValueAxis *m_axisX = nullptr; + QValueAxis *m_axisY = nullptr; + QTimer m_timer; + QStringList m_titles; + qreal m_step = 0.; + qreal m_x = 5; + qreal m_y = 1; +}; + +#endif diff --git a/examples/charts/chartsgallery/horizontalbarwidget.cpp b/examples/charts/chartsgallery/horizontalbarwidget.cpp new file mode 100644 index 00000000..5a903335 --- /dev/null +++ b/examples/charts/chartsgallery/horizontalbarwidget.cpp @@ -0,0 +1,67 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "horizontalbarwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSeries> +#include <QBarSet> +#include <QChart> +#include <QHorizontalBarSeries> +#include <QLegend> +#include <QValueAxis> + +HorizontalBarWidget::HorizontalBarWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto set0 = new QBarSet("Jane"); + auto set1 = new QBarSet("John"); + auto set2 = new QBarSet("Axel"); + auto set3 = new QBarSet("Mary"); + auto set4 = new QBarSet("Samantha"); + + *set0 << 1 << 2 << 3 << 4 << 5 << 6; + *set1 << 5 << 0 << 0 << 4 << 0 << 7; + *set2 << 3 << 5 << 8 << 13 << 8 << 5; + *set3 << 5 << 6 << 7 << 3 << 4 << 5; + *set4 << 9 << 7 << 5 << 3 << 1 << 2; + //![1] + + //![2] + auto series = new QHorizontalBarSeries; + series->append(set0); + series->append(set1); + series->append(set2); + series->append(set3); + series->append(set4); + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Simple Horizontal Bar Chart"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //![3] + + //![4] + QStringList categories {"Jan", "Feb", "Mar", "Apr", "May", "Jun"}; + auto axisY = new QBarCategoryAxis; + axisY->append(categories); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + auto axisX = new QValueAxis; + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + axisX->applyNiceNumbers(); + //![4] + + //![5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/horizontalbarwidget.h b/examples/charts/chartsgallery/horizontalbarwidget.h new file mode 100644 index 00000000..cd459050 --- /dev/null +++ b/examples/charts/chartsgallery/horizontalbarwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef HORIZONTALBARWIDGET_H +#define HORIZONTALBARWIDGET_H + +#include "contentwidget.h" + +class HorizontalBarWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit HorizontalBarWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/horizontalpercentbarwidget.cpp b/examples/charts/chartsgallery/horizontalpercentbarwidget.cpp new file mode 100644 index 00000000..374b3dd4 --- /dev/null +++ b/examples/charts/chartsgallery/horizontalpercentbarwidget.cpp @@ -0,0 +1,66 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "horizontalpercentbarwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSeries> +#include <QBarSet> +#include <QChart> +#include <QHorizontalPercentBarSeries> +#include <QLegend> +#include <QValueAxis> + +HorizontalPercentBarWidget::HorizontalPercentBarWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto set0 = new QBarSet("Jane"); + auto set1 = new QBarSet("John"); + auto set2 = new QBarSet("Axel"); + auto set3 = new QBarSet("Mary"); + auto set4 = new QBarSet("Samantha"); + + *set0 << 1 << 2 << 3 << 4 << 5 << 6; + *set1 << 5 << 0 << 0 << 4 << 0 << 7; + *set2 << 3 << 5 << 8 << 13 << 8 << 5; + *set3 << 5 << 6 << 7 << 3 << 4 << 5; + *set4 << 9 << 7 << 5 << 3 << 1 << 2; + //![1] + + //![2] + auto series = new QHorizontalPercentBarSeries; + series->append(set0); + series->append(set1); + series->append(set2); + series->append(set3); + series->append(set4); + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Simple Horizontal Percent Bar Chart"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //![3] + + //![4] + QStringList categories {"Jan", "Feb", "Mar", "Apr", "May", "Jun"}; + auto axisY = new QBarCategoryAxis; + axisY->append(categories); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + auto axisX = new QValueAxis; + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + //![4] + + //![5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/horizontalpercentbarwidget.h b/examples/charts/chartsgallery/horizontalpercentbarwidget.h new file mode 100644 index 00000000..33f7eb8f --- /dev/null +++ b/examples/charts/chartsgallery/horizontalpercentbarwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef HORIZONTALPERCENTBARWIDGET_H +#define HORIZONTALPERCENTBARWIDGET_H + +#include "contentwidget.h" + +class HorizontalPercentBarWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit HorizontalPercentBarWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/horizontalstackedbarwidget.cpp b/examples/charts/chartsgallery/horizontalstackedbarwidget.cpp new file mode 100644 index 00000000..647e0e34 --- /dev/null +++ b/examples/charts/chartsgallery/horizontalstackedbarwidget.cpp @@ -0,0 +1,67 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "horizontalstackedbarwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSeries> +#include <QBarSet> +#include <QChart> +#include <QHorizontalStackedBarSeries> +#include <QLegend> +#include <QValueAxis> + +HorizontalStackedBarWidget::HorizontalStackedBarWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto set0 = new QBarSet("Jane"); + auto set1 = new QBarSet("John"); + auto set2 = new QBarSet("Axel"); + auto set3 = new QBarSet("Mary"); + auto set4 = new QBarSet("Samantha"); + + *set0 << 1 << 2 << 3 << 4 << 5 << 6; + *set1 << 5 << 0 << 0 << 4 << 0 << 7; + *set2 << 3 << 5 << 8 << 13 << 8 << 5; + *set3 << 5 << 6 << 7 << 3 << 4 << 5; + *set4 << 9 << 7 << 5 << 3 << 1 << 2; + //![1] + + //![2] + auto series = new QHorizontalStackedBarSeries; + series->append(set0); + series->append(set1); + series->append(set2); + series->append(set3); + series->append(set4); + + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Simple Horizontal Stacked Bar Chart"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //![3] + + //![4] + QStringList categories {"Jan", "Feb", "Mar", "Apr", "May", "Jun"}; + auto axisY = new QBarCategoryAxis; + axisY->append(categories); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + auto axisX = new QValueAxis; + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + //![4] + + //![5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/horizontalstackedbarwidget.h b/examples/charts/chartsgallery/horizontalstackedbarwidget.h new file mode 100644 index 00000000..e36d9687 --- /dev/null +++ b/examples/charts/chartsgallery/horizontalstackedbarwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef HORIZONTALSTACKEDBARWIDGET_H +#define HORIZONTALSTACKEDBARWIDGET_H + +#include "contentwidget.h" + +class HorizontalStackedBarWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit HorizontalStackedBarWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/interactionschart.cpp b/examples/charts/chartsgallery/interactionschart.cpp new file mode 100644 index 00000000..740c9f64 --- /dev/null +++ b/examples/charts/chartsgallery/interactionschart.cpp @@ -0,0 +1,77 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "interactionschart.h" + +#include <QLineSeries> +#include <QtMath> +#include <QValueAxis> + +InteractionsChart::InteractionsChart(QGraphicsItem *parent, Qt::WindowFlags wFlags, QLineSeries *series) + : QChart(QChart::ChartTypeCartesian, parent, wFlags), m_series(series) +{ +} + +void InteractionsChart::clickPoint(const QPointF &point) +{ + // Find the closest data point + m_movingPoint = QPoint(); + m_clicked = false; + const auto points = m_series->points(); + for (QPointF p : points) { + if (distance(p, point) < distance(m_movingPoint, point)) { + m_movingPoint = p; + m_clicked = true; + } + } +} + +qreal InteractionsChart::distance(const QPointF &p1, const QPointF &p2) +{ + return qSqrt((p1.x() - p2.x()) * (p1.x() - p2.x()) + + (p1.y() - p2.y()) * (p1.y() - p2.y())); +} + +void InteractionsChart::setPointClicked(bool clicked) +{ + m_clicked = clicked; +} + +void InteractionsChart::handlePointMove(const QPoint &point) +{ + if (m_clicked) { + // Map the point clicked from the ChartView to the area occupied by the chart + QPoint mappedPoint = point; + mappedPoint.setX(point.x() - this->plotArea().x()); + mappedPoint.setY(point.y() - this->plotArea().y()); + + // Get the x and y axis to be able to convert the mapped coordinate point to the chart scale + QAbstractAxis *axisx = axes(Qt::Horizontal).first(); + QValueAxis *haxis = nullptr; + if (axisx->type() == QAbstractAxis::AxisTypeValue) + haxis = qobject_cast<QValueAxis *>(axisx); + + QAbstractAxis *axisy = axes(Qt::Vertical).first(); + QValueAxis *vaxis = nullptr; + if (axisy->type() == QAbstractAxis::AxisTypeValue) + vaxis = qobject_cast<QValueAxis *>(axisy); + + if (haxis && vaxis) { + // Calculate the "unit" between points on the x y axis + double xUnit = this->plotArea().width() / haxis->max(); + double yUnit = this->plotArea().height() / vaxis->max(); + + // Convert the mappedPoint to the actual chart scale + double x = mappedPoint.x() / xUnit; + double y = vaxis->max() - mappedPoint.y() / yUnit; + + // Replace the old point with the new one + m_series->replace(m_movingPoint, QPointF(x, y)); + + // Update the m_movingPoint so we are able to do the replace also during mousemoveEvent + m_movingPoint.setX(x); + m_movingPoint.setY(y); + } + } +} + diff --git a/examples/charts/chartsgallery/interactionschart.h b/examples/charts/chartsgallery/interactionschart.h new file mode 100644 index 00000000..dd087156 --- /dev/null +++ b/examples/charts/chartsgallery/interactionschart.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef INTERACTIONSCHART_H +#define INTERACTIONSCHART_H + +#include <QChart> + +QT_FORWARD_DECLARE_CLASS(QLineSeries) + +class InteractionsChart : public QChart +{ + Q_OBJECT +public: + explicit InteractionsChart(QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = {}, QLineSeries *series = nullptr); + +public slots: + void clickPoint(const QPointF &point); + +public: + void handlePointMove(const QPoint &point); + void setPointClicked(bool clicked); + +private: + qreal distance(const QPointF &p1, const QPointF &p2); + + QLineSeries *m_series = nullptr; + QPointF m_movingPoint; + + // Boolean value to determine if an actual point in the series is clicked + bool m_clicked = false; +}; + +#endif diff --git a/examples/charts/chartsgallery/interactionsview.cpp b/examples/charts/chartsgallery/interactionsview.cpp new file mode 100644 index 00000000..0bac0b77 --- /dev/null +++ b/examples/charts/chartsgallery/interactionsview.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "interactionschart.h" +#include "interactionsview.h" + +#include <QMouseEvent> + +InteractionsView::InteractionsView(InteractionsChart *chart, QWidget *parent) : + QChartView(chart, parent) +{ + m_chart = chart; +} + +void InteractionsView::mousePressEvent(QMouseEvent *event) +{ + m_mousePos = event->pos(); + QChartView::mousePressEvent(event); +} + +void InteractionsView::mouseMoveEvent(QMouseEvent *event) +{ + m_chart->handlePointMove(event->pos()); + QChartView::mouseMoveEvent(event); +} + +void InteractionsView::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->pos() != m_mousePos) { + m_chart->handlePointMove(event->pos()); + m_chart->setPointClicked(false); + } + QChartView::mouseReleaseEvent(event); +} diff --git a/examples/charts/chartsgallery/interactionsview.h b/examples/charts/chartsgallery/interactionsview.h new file mode 100644 index 00000000..04a27b41 --- /dev/null +++ b/examples/charts/chartsgallery/interactionsview.h @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef INTERACTIONSVIEW_H +#define INTERACTIONSVIEW_H + +#include <QChartView> +#include <QPoint> + +class InteractionsChart; + +class InteractionsView : public QChartView +{ +public: + InteractionsView(InteractionsChart *chart, QWidget *parent = nullptr); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +private: + InteractionsChart *m_chart = nullptr; + QPoint m_mousePos; +}; + +#endif diff --git a/examples/charts/chartsgallery/interactionswidget.cpp b/examples/charts/chartsgallery/interactionswidget.cpp new file mode 100644 index 00000000..6be473ac --- /dev/null +++ b/examples/charts/chartsgallery/interactionswidget.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "interactionschart.h" +#include "interactionsview.h" +#include "interactionswidget.h" + +#include <QChart> +#include <QLineSeries> +#include <QValueAxis> + +InteractionsWidget::InteractionsWidget(QWidget *parent) + : ContentWidget(parent) +{ + auto series = new QLineSeries; + + series->append(0, 6); + series->append(1, 3); + series->append(2, 4); + series->append(3, 8); + series->append(7, 13); + series->append(10, 5); + *series << QPointF(11, 1) << QPointF(13, 3) << QPointF(17, 6) << QPointF(18, 3) << QPointF(20, 2); + + auto chart = new InteractionsChart(0, {}, series); + chart->legend()->hide(); + chart->addSeries(series); + QPen p = series->pen(); + p.setWidth(5); + series->setPen(p); + chart->setTitle("Drag and drop data points to move them"); + + auto axisX = new QValueAxis; + chart->addAxis(axisX, Qt::AlignBottom); + axisX->setRange(0, 20); + series->attachAxis(axisX); + + auto axisY = new QValueAxis; + chart->addAxis(axisY, Qt::AlignLeft); + axisY->setRange(0, 13); + series->attachAxis(axisY); + + QObject::connect(series, &QLineSeries::pressed, chart, &InteractionsChart::clickPoint); + + auto view = new InteractionsView(chart, this); + setDefaultChartView(view); +} diff --git a/examples/charts/chartsgallery/interactionswidget.h b/examples/charts/chartsgallery/interactionswidget.h new file mode 100644 index 00000000..bc378a10 --- /dev/null +++ b/examples/charts/chartsgallery/interactionswidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef INTERACTIONSWIDGET_H +#define INTERACTIONSWIDGET_H + +#include "contentwidget.h" + +class InteractionsWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit InteractionsWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/legendmarkerswidget.cpp b/examples/charts/chartsgallery/legendmarkerswidget.cpp new file mode 100644 index 00000000..0d739bb1 --- /dev/null +++ b/examples/charts/chartsgallery/legendmarkerswidget.cpp @@ -0,0 +1,161 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "legendmarkerswidget.h" + +#include <QChart> +#include <QChartView> +#include <QGraphicsLayout> +#include <QGridLayout> +#include <QLegend> +#include <QLegendMarker> +#include <QLineSeries> +#include <QtMath> +#include <QXYLegendMarker> + +LegendMarkersWidget::LegendMarkersWidget(QWidget *parent) + : ContentWidget(parent) +{ + // Create chart view with the chart + m_chart = new QChart; + m_chartView = new QChartView(m_chart, this); + + // Create layout for grid and detached legend + m_mainLayout = new QGridLayout; + m_mainLayout->addWidget(m_chartView, 0, 1, 3, 1); + setLayout(m_mainLayout); + + // Add few series + addSeries(); + addSeries(); + addSeries(); + addSeries(); + + connectMarkers(); + + // Set the title and show legend + m_chart->setTitle("Legend Markers (Click on legend to interact)"); + m_chart->legend()->setVisible(true); + m_chart->legend()->setAlignment(Qt::AlignBottom); + + m_chartView->setRenderHint(QPainter::Antialiasing); + m_chart->layout()->setContentsMargins(0, 0, 0, 0); +} + +void LegendMarkersWidget::addSeries() +{ + auto series = new QLineSeries; + m_series.append(series); + + series->setName(QString("line " + QString::number(m_series.count()))); + + // Make some sine wave for data + QList<QPointF> data; + int offset = m_chart->series().count(); + for (int i = 0; i < 360; i++) { + qreal x = offset * 20 + i; + data.append(QPointF(i, qSin(qDegreesToRadians(x)))); + } + + series->append(data); + m_chart->addSeries(series); + + if (m_series.count() == 1) + m_chart->createDefaultAxes(); +} + +void LegendMarkersWidget::removeSeries() +{ + // Remove last series from chart + if (m_series.count() > 0) { + QLineSeries *series = m_series.last(); + m_chart->removeSeries(series); + m_series.removeLast(); + delete series; + } +} + +void LegendMarkersWidget::connectMarkers() +{ +//![1] + // Connect all markers to handler + const auto markers = m_chart->legend()->markers(); + for (QLegendMarker *marker : markers) { + // Disconnect possible existing connection to avoid multiple connections + QObject::disconnect(marker, &QLegendMarker::clicked, + this, &LegendMarkersWidget::handleMarkerClicked); + QObject::connect(marker, &QLegendMarker::clicked, + this, &LegendMarkersWidget::handleMarkerClicked); + } +//![1] +} + +void LegendMarkersWidget::disconnectMarkers() +{ +//![2] + const auto markers = m_chart->legend()->markers(); + for (QLegendMarker *marker : markers) { + QObject::disconnect(marker, &QLegendMarker::clicked, + this, &LegendMarkersWidget::handleMarkerClicked); + } +//![2] +} + +void LegendMarkersWidget::handleMarkerClicked() +{ +//![3] + auto marker = qobject_cast<QLegendMarker *>(sender()); + Q_ASSERT(marker); +//![3] + +//![4] + switch (marker->type()) +//![4] + { + case QLegendMarker::LegendMarkerTypeXY: + { +//![5] + // Toggle visibility of series + marker->series()->setVisible(!marker->series()->isVisible()); + + // Turn legend marker back to visible, since hiding series also hides the marker + // and we don't want it to happen now. + marker->setVisible(true); +//![5] + +//![6] + // Dim the marker, if series is not visible + qreal alpha = 1.0; + + if (!marker->series()->isVisible()) + alpha = 0.5; + + QColor color; + QBrush brush = marker->labelBrush(); + color = brush.color(); + color.setAlphaF(alpha); + brush.setColor(color); + marker->setLabelBrush(brush); + + brush = marker->brush(); + color = brush.color(); + color.setAlphaF(alpha); + brush.setColor(color); + marker->setBrush(brush); + + QPen pen = marker->pen(); + color = pen.color(); + color.setAlphaF(alpha); + pen.setColor(color); + marker->setPen(pen); + +//![6] + break; + } + default: + { + qDebug("Unknown marker type"); + break; + } + } +} diff --git a/examples/charts/chartsgallery/legendmarkerswidget.h b/examples/charts/chartsgallery/legendmarkerswidget.h new file mode 100644 index 00000000..78b26623 --- /dev/null +++ b/examples/charts/chartsgallery/legendmarkerswidget.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef LEGENDMARKERSWIDGET_H +#define LEGENDMARKERSWIDGET_H + +#include "contentwidget.h" + +QT_FORWARD_DECLARE_CLASS(QChart) +QT_FORWARD_DECLARE_CLASS(QChartView) +QT_FORWARD_DECLARE_CLASS(QGridLayout) +QT_FORWARD_DECLARE_CLASS(QLineSeries) + +class LegendMarkersWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit LegendMarkersWidget(QWidget *parent = nullptr); + +public slots: + void addSeries(); + void removeSeries(); + void connectMarkers(); + void disconnectMarkers(); + void handleMarkerClicked(); + +private: + QChart *m_chart = nullptr; + QChartView *m_chartView = nullptr; + QGridLayout *m_mainLayout = nullptr; + QGridLayout *m_fontLayout = nullptr; + QList<QLineSeries *> m_series; +}; + +#endif diff --git a/examples/charts/chartsgallery/legendwidget.cpp b/examples/charts/chartsgallery/legendwidget.cpp new file mode 100644 index 00000000..da5d3587 --- /dev/null +++ b/examples/charts/chartsgallery/legendwidget.cpp @@ -0,0 +1,285 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "legendwidget.h" + +#include <QBarSet> +#include <QBarSeries> +#include <QChart> +#include <QChartView> +#include <QCheckBox> +#include <QDoubleSpinBox> +#include <QFormLayout> +#include <QGraphicsLayout> +#include <QGridLayout> +#include <QGroupBox> +#include <QLegend> +#include <QPushButton> + +LegendWidget::LegendWidget(QWidget *parent) + : ContentWidget(parent) +{ + // Create chart view with the chart + m_chart = new QChart; + m_chartView = new QChartView(m_chart, this); + + m_chart->setTitle("Legend Options"); + + createUi(); + + connect(m_toggleAttachedButton, &QPushButton::clicked, this, &LegendWidget::toggleAttached); + connect(m_interactiveButton, &QPushButton::clicked, this, &LegendWidget::toggleInteractive); + connect(m_boldButton, &QPushButton::clicked, this, &LegendWidget::toggleBold); + connect(m_italicButton, &QPushButton::clicked, this, &LegendWidget::toggleItalic); + connect(m_addSetButton, &QPushButton::clicked, this, &LegendWidget::addBarset); + connect(m_removeSetButton, &QPushButton::clicked, this, &LegendWidget::removeBarset); + connect(m_alignmentButton, &QPushButton::clicked, this, &LegendWidget::setLegendAlignment); + connect(m_fontSize, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &LegendWidget::fontSizeChanged); + connect(m_legendPosX, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &LegendWidget::updateLegendLayout); + connect(m_legendPosY, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &LegendWidget::updateLegendLayout); + connect(m_legendWidth, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &LegendWidget::updateLegendLayout); + connect(m_legendHeight, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &LegendWidget::updateLegendLayout); + + QLegend *legend = m_chart->legend(); + legend->setShowToolTips(true); + legend->setBrush(QBrush(QColor(128, 128, 128, 128))); + legend->setPen(QPen(QColor(192, 192, 192, 192))); + //![5] + legend->setInteractive(true); + //![5] + + //![4] + connect(legend, &QLegend::attachedToChartChanged, [legend, this](bool attachedToChart) { + m_toggleAttachedButton->setChecked(attachedToChart); + legend->setBackgroundVisible(!attachedToChart); + m_geometrySettings->setDisabled(attachedToChart); + }); + //![4] + connect(legend, &QGraphicsWidget::geometryChanged, this, &LegendWidget::updateLegendSpinbox); + + createSeries(); + m_chart->createDefaultAxes(); + + //![1] + m_chart->legend()->setVisible(true); + m_chart->legend()->setAlignment(Qt::AlignBottom); + //![1] + + m_chartView->setRenderHint(QPainter::Antialiasing); + m_chart->layout()->setContentsMargins(0, 0, 0, 0); +} + +void LegendWidget::resizeEvent(QResizeEvent *) +{ + QRectF chartViewRect = m_chartView->rect(); + m_legendPosX->setMaximum(chartViewRect.width()); + m_legendPosY->setMaximum(chartViewRect.height()); + m_legendWidth->setMaximum(chartViewRect.width()); + m_legendHeight->setMaximum(chartViewRect.height()); +} + +void LegendWidget::createUi() +{ + QLegend *legend = m_chart->legend(); + // Create buttons for ui + auto buttonLayout = new QGridLayout; + m_toggleAttachedButton = new QCheckBox("Attached", this); + m_toggleAttachedButton->setChecked(true); + buttonLayout->addWidget(m_toggleAttachedButton, 0, 0); + + m_interactiveButton = new QCheckBox("Interactive", this); + m_interactiveButton->setChecked(true); + buttonLayout->addWidget(m_interactiveButton, 1, 0); + + m_boldButton = new QCheckBox("Bold", this); + buttonLayout->addWidget(m_boldButton, 2, 0); + + m_italicButton = new QCheckBox("Italic", this); + buttonLayout->addWidget(m_italicButton, 3, 0); + + m_addSetButton = new QPushButton("Add Barset", this); + buttonLayout->addWidget(m_addSetButton, 4, 0); + + m_removeSetButton = new QPushButton("Remove Barset", this); + buttonLayout->addWidget(m_removeSetButton, 5, 0); + + m_alignmentButton = new QPushButton("Align (Bottom)", this); + buttonLayout->addWidget(m_alignmentButton, 6, 0); + + buttonLayout->setRowStretch(7, 1); + + m_legendPosX = new QDoubleSpinBox(this); + m_legendPosY = new QDoubleSpinBox(this); + m_legendWidth = new QDoubleSpinBox(this); + m_legendHeight = new QDoubleSpinBox(this); + + auto legendLayout = new QFormLayout; + legendLayout->addRow("HPos", m_legendPosX); + legendLayout->addRow("VPos", m_legendPosY); + legendLayout->addRow("Width", m_legendWidth); + legendLayout->addRow("Height", m_legendHeight); + m_geometrySettings = new QGroupBox("Detached legend", this); + m_geometrySettings->setLayout(legendLayout); + buttonLayout->addWidget(m_geometrySettings, 8, 0); + m_geometrySettings->setDisabled(true); + + // Create spinbox to modify font size + m_fontSize = new QDoubleSpinBox(this); + QFont lfont = legend->font(); + lfont.setPointSizeF(12.0); + legend->setFont(lfont); + m_fontSize->setValue(legend->font().pointSizeF()); + m_fontSize->setMinimum(1.); + + auto fontLayout = new QFormLayout; + fontLayout->addRow("Legend font size", m_fontSize); + + // Create layout for grid and detached legend + auto mainLayout = new QGridLayout; + mainLayout->setHorizontalSpacing(10); + mainLayout->addLayout(buttonLayout, 0, 0); + mainLayout->addLayout(fontLayout, 1, 0); + mainLayout->addWidget(m_chartView, 0, 1, 3, 1); + setLayout(mainLayout); +} + +void LegendWidget::createSeries() +{ + m_series = new QBarSeries; + addBarset(); + addBarset(); + addBarset(); + addBarset(); + + m_chart->addSeries(m_series); +} + +void LegendWidget::updateLegendSpinbox() +{ + QLegend *legend = m_chart->legend(); + double newPosX = legend->x(); + double newPosY = legend->y(); + QSizeF newSize = legend->size(); + if (!qFuzzyCompare(m_legendPosX->value(), newPosX)) + m_legendPosX->setValue(newPosX); + if (!qFuzzyCompare(m_legendPosY->value(), newPosY)) + m_legendPosY->setValue(newPosY); + if (!qFuzzyCompare(m_legendWidth->value(), newSize.width())) + m_legendWidth->setValue(newSize.width()); + if (!qFuzzyCompare(m_legendHeight->value(), newSize.height())) + m_legendHeight->setValue(newSize.height()); +} + + +void LegendWidget::toggleAttached() +{ + QLegend *legend = m_chart->legend(); + if (legend->isAttachedToChart()) { + //![2] + legend->detachFromChart(); + //![2] + } else { + //![3] + legend->attachToChart(); + //![3] + } + update(); +} + +void LegendWidget::toggleInteractive() +{ + m_chart->legend()->setInteractive(!m_chart->legend()->isInteractive()); +} + +void LegendWidget::toggleBold() +{ + QFont font = m_chart->legend()->font(); + font.setBold(!font.bold()); + m_chart->legend()->setFont(font); +} + +void LegendWidget::toggleItalic() +{ + QFont font = m_chart->legend()->font(); + font.setItalic(!font.italic()); + m_chart->legend()->setFont(font); +} + +void LegendWidget::addBarset() +{ + auto barSet = new QBarSet(QString("set ") + QString::number(m_series->count())); + qreal delta = m_series->count() * 0.1; + *barSet << 1 + delta << 2 + delta << 3 + delta << 4 + delta; + m_series->append(barSet); +} + +void LegendWidget::removeBarset() +{ + QList<QBarSet *> sets = m_series->barSets(); + if (sets.count() > 0) { + m_series->remove(sets.at(sets.count() - 1)); + } +} + +void LegendWidget::setLegendAlignment() +{ + auto button = qobject_cast<QPushButton *>(sender()); + + switch (m_chart->legend()->alignment()) { + case Qt::AlignTop: + m_chart->legend()->setAlignment(Qt::AlignLeft); + if (button) + button->setText("Align (Left)"); + break; + case Qt::AlignLeft: + m_chart->legend()->setAlignment(Qt::AlignBottom); + if (button) + button->setText("Align (Bottom)"); + break; + case Qt::AlignBottom: + m_chart->legend()->setAlignment(Qt::AlignRight); + if (button) + button->setText("Align (Right)"); + break; + default: + if (button) + button->setText("Align (Top)"); + m_chart->legend()->setAlignment(Qt::AlignTop); + break; + } +} + +void LegendWidget::fontSizeChanged() +{ + QFont font = m_chart->legend()->font(); + font.setPointSizeF(m_fontSize->value()); + m_chart->legend()->setFont(font); +} + +void LegendWidget::updateLegendLayout() +{ +//![6] + QRectF geom = m_chart->legend()->geometry(); + if (qFuzzyCompare(geom.x(), m_legendPosX->value()) + && qFuzzyCompare(geom.y(), m_legendPosY->value()) + && qFuzzyCompare(geom.width(), m_legendWidth->value()) + && qFuzzyCompare(geom.height(), m_legendHeight->value())) + return; + + m_chart->legend()->setGeometry(QRectF(m_legendPosX->value(), + m_legendPosY->value(), + m_legendWidth->value(), + m_legendHeight->value())); + m_chart->legend()->update(); +//![6] +} diff --git a/examples/charts/chartsgallery/legendwidget.h b/examples/charts/chartsgallery/legendwidget.h new file mode 100644 index 00000000..2ca128be --- /dev/null +++ b/examples/charts/chartsgallery/legendwidget.h @@ -0,0 +1,61 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef LEGENDWIDGET_H +#define LEGENDWIDGET_H + +#include "contentwidget.h" + +#include <QBarSeries> +#include <QChart> +#include <QChartView> +#include <QCheckBox> +#include <QDoubleSpinBox> +#include <QGroupBox> +#include <QPushButton> + +class LegendWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit LegendWidget(QWidget *parent = nullptr); + void createUi(); + void createSeries(); + void updateLegendSpinbox(); + +public slots: + void toggleAttached(); + void toggleInteractive(); + void toggleBold(); + void toggleItalic(); + void addBarset(); + void removeBarset(); + void setLegendAlignment(); + void fontSizeChanged(); + void updateLegendLayout(); + +protected: + void resizeEvent(QResizeEvent *) override; + +private: + QChart *m_chart; + QChartView *m_chartView; + QBarSeries *m_series; + + QCheckBox *m_toggleAttachedButton; + QCheckBox *m_interactiveButton; + QCheckBox *m_boldButton; + QCheckBox *m_italicButton; + QPushButton *m_addSetButton; + QPushButton *m_removeSetButton; + QPushButton *m_alignmentButton; + QDoubleSpinBox *m_fontSize; + + QGroupBox *m_geometrySettings; + QDoubleSpinBox *m_legendPosX; + QDoubleSpinBox *m_legendPosY; + QDoubleSpinBox *m_legendWidth; + QDoubleSpinBox *m_legendHeight; +}; + +#endif diff --git a/examples/charts/chartsgallery/lightmarkerswidget.cpp b/examples/charts/chartsgallery/lightmarkerswidget.cpp new file mode 100644 index 00000000..ddb9b759 --- /dev/null +++ b/examples/charts/chartsgallery/lightmarkerswidget.cpp @@ -0,0 +1,211 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "lightmarkerswidget.h" + +#include <QBrush> +#include <QChart> +#include <QChartView> +#include <QCheckBox> +#include <QComboBox> +#include <QGraphicsLayout> +#include <QGridLayout> +#include <QHBoxLayout> +#include <QLabel> +#include <QList> +#include <QPainter> +#include <QPainterPath> +#include <QSplineSeries> + +LightMarkersWidget::LightMarkersWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + constexpr qreal marker_size = 20.; + + auto series = new QSplineSeries; + series->append({QPointF(0., 0.), + QPointF(0.5, 2.27), + QPointF(1.5, 2.2), + QPointF(3.3, 1.7), + QPointF(4.23, 3.1), + QPointF(5.3, 2.3), + QPointF(6.47, 4.1) + }); + series->setMarkerSize(marker_size); + series->setLightMarker(rectangle(marker_size, Qt::red)); + series->setSelectedLightMarker(blueTriangle(marker_size)); + + QObject::connect(series, &QXYSeries::clicked, series, [=](const QPointF &point) { + int index = series->points().indexOf(point); + if (index != -1) + series->toggleSelection({index}); + }); + //![1] + + //![2] + auto chart = new QChart; + chart->addSeries(series); + chart->createDefaultAxes(); + chart->legend()->setVisible(false); + chart->layout()->setContentsMargins(0, 0, 0, 0); + chart->setTitle("Select points with mouse click"); + + auto chartView = new QChartView(chart, this); + chartView->setRenderHint(QPainter::Antialiasing); + + auto controlWidget = new QWidget(this); + auto controlLayout = new QGridLayout(controlWidget); + //![2] + + //![3] + auto charPointCombobox = new QComboBox(controlWidget); + auto charPointSelectedCombobox = new QComboBox(controlWidget); + auto lineColorCombobox = new QComboBox(controlWidget); + auto showUnselectedPointsCheckbox = new QCheckBox(controlWidget); + //![3] + + //![4] + auto charPoint = new QLabel(tr("Char point: "), controlWidget); + charPointCombobox->addItems({tr("Red rectangle"), + tr("Green triangle"), + tr("Orange circle") + }); + QObject::connect(charPointCombobox, &QComboBox::currentIndexChanged, series, [=](const int index) { + if (showUnselectedPointsCheckbox->isChecked()) + series->setLightMarker(getPointRepresentation(PointType(index), marker_size)); + }); + //![4] + + //![5] + auto charPointSelected = new QLabel(tr("Char point selected: "), controlWidget); + charPointSelectedCombobox->addItems({tr("Blue triangle"), + tr("Yellow rectangle"), + tr("Lavender circle") + }); + QObject::connect(charPointSelectedCombobox, &QComboBox::currentIndexChanged, series, [=](const int index) { + series->setSelectedLightMarker(getSelectedPointRepresentation(SelectedPointType(index), marker_size)); + }); + + auto lineColorLabel = new QLabel(tr("Line color: "), controlWidget); + lineColorCombobox->addItems({tr("Blue"), + tr("Black"), + tr("Mint") + }); + QObject::connect(lineColorCombobox, &QComboBox::currentIndexChanged, series, [=](const int index) { + series->setColor(makeLineColor(LineColor(index))); + }); + //![5] + + //![6] + auto showUnselectedPointsLabel = new QLabel(tr("Display unselected points: "), controlWidget); + showUnselectedPointsCheckbox->setChecked(true); + QObject::connect(showUnselectedPointsCheckbox, &QCheckBox::stateChanged, series, [=](const int state) { + if (state) + series->setLightMarker(getPointRepresentation(PointType(charPointCombobox->currentIndex()), marker_size)); + else + series->setLightMarker(QImage()); + }); + //![6] + + //![7] + controlLayout->addWidget(charPoint, 0, 0); + controlLayout->addWidget(charPointCombobox, 0, 1); + + controlLayout->addWidget(charPointSelected, 1, 0); + controlLayout->addWidget(charPointSelectedCombobox, 1, 1); + + controlLayout->addWidget(lineColorLabel, 2, 0); + controlLayout->addWidget(lineColorCombobox, 2, 1); + + controlLayout->addWidget(showUnselectedPointsLabel, 3, 0); + controlLayout->addWidget(showUnselectedPointsCheckbox, 3, 1, 1, 2); + + m_mainWidget = new QWidget(this); + auto mainLayout = new QHBoxLayout(m_mainWidget); + mainLayout->addWidget(chartView); + mainLayout->addWidget(controlWidget); + //![7] +} + +void LightMarkersWidget::resizeEvent(QResizeEvent *) +{ + m_mainWidget->resize(size()); +} + +QImage LightMarkersWidget::rectangle(qreal imageSize, const QColor &color) +{ + QImage image(imageSize, imageSize, QImage::Format_RGB32); + QPainter painter; + painter.begin(&image); + painter.fillRect(0, 0, imageSize, imageSize, color); + painter.end(); + return image; +} + +QImage LightMarkersWidget::circle(qreal imageSize, const QColor &color) +{ + QImage image(imageSize, imageSize, QImage::Format_ARGB32); + image.fill(QColor(0, 0, 0, 0)); + QPainter paint; + paint.begin(&image); + paint.setBrush(color); + QPen pen = paint.pen(); + pen.setWidth(0); + paint.setPen(pen); + paint.drawEllipse(0, 0, imageSize * 0.9, imageSize * 0.9); + paint.end(); + return image; +} + +QImage LightMarkersWidget::blueTriangle(qreal imageSize) +{ + return QImage(":/blue_triangle").scaled(imageSize, imageSize); +} + +QImage LightMarkersWidget::greenTriangle(qreal imageSize) +{ + return QImage(":/green_triangle").scaled(imageSize, imageSize); +} + +QImage LightMarkersWidget::getPointRepresentation(PointType pointType, int imageSize) +{ + switch (pointType) { + case PointType::RedRectangle: + return rectangle(imageSize, Qt::red); + case PointType::GreenTriangle: + return greenTriangle(imageSize); + case PointType::OrangeCircle: + return circle(imageSize, QColor(255, 127, 80)); + default: + return rectangle(imageSize, Qt::red); + } +} + +QImage LightMarkersWidget::getSelectedPointRepresentation(SelectedPointType pointType, int imageSize) +{ + switch (pointType) { + case SelectedPointType::BlueTriangle: + return blueTriangle(imageSize); + case SelectedPointType::YellowRectangle: + return rectangle(imageSize, Qt::yellow); + case SelectedPointType::LavenderCircle: + return circle(imageSize, QColor(147, 112, 219)); + default: + return blueTriangle(imageSize); + } +} + +QColor LightMarkersWidget::makeLineColor(LineColor lineColor) +{ + switch (lineColor) { + case LineColor::Blue: + return QColor(65, 105, 225); + case LineColor::Black: + return QColor(0, 0, 0); + case LineColor::Mint: + return QColor(70, 203, 155); + default: + return QColor(0, 0, 0); + } +} diff --git a/examples/charts/chartsgallery/lightmarkerswidget.h b/examples/charts/chartsgallery/lightmarkerswidget.h new file mode 100644 index 00000000..9add2b77 --- /dev/null +++ b/examples/charts/chartsgallery/lightmarkerswidget.h @@ -0,0 +1,53 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef LIGHTMARKERSWIDGET_H +#define LIGHTMARKERSWIDGET_H + +#include "contentwidget.h" + +#include <QColor> +#include <QImage> + +QT_FORWARD_DECLARE_CLASS(QChartView) + +class LightMarkersWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit LightMarkersWidget(QWidget *parent = nullptr); + +protected: + void resizeEvent(QResizeEvent *) override; + +private: + enum class PointType { + RedRectangle, + GreenTriangle, + OrangeCircle + }; + + enum class SelectedPointType { + BlueTriangle, + YellowRectangle, + LavenderCircle + }; + + enum class LineColor { + Blue, + Black, + Mint + }; + + QImage rectangle(qreal imageSize, const QColor &color); + QImage circle(qreal imageSize, const QColor &color); + QImage blueTriangle(qreal imageSize); + QImage greenTriangle(qreal imageSize); + QImage getPointRepresentation(PointType pointType, int imageSize); + QImage getSelectedPointRepresentation(SelectedPointType pointType, int imageSize); + QColor makeLineColor(LineColor lineColor); + + QWidget *m_mainWidget = nullptr; +}; + +#endif diff --git a/examples/charts/chartsgallery/lineandbarwidget.cpp b/examples/charts/chartsgallery/lineandbarwidget.cpp new file mode 100644 index 00000000..49d1cfa1 --- /dev/null +++ b/examples/charts/chartsgallery/lineandbarwidget.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "lineandbarwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSeries> +#include <QBarSet> +#include <QChart> +#include <QLegend> +#include <QLineSeries> +#include <QValueAxis> + +LineAndBarWidget::LineAndBarWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto set0 = new QBarSet("Jane"); + auto set1 = new QBarSet("John"); + auto set2 = new QBarSet("Axel"); + auto set3 = new QBarSet("Mary"); + auto set4 = new QBarSet("Sam"); + + *set0 << 1 << 2 << 3 << 4 << 5 << 6; + *set1 << 5 << 0 << 0 << 4 << 0 << 7; + *set2 << 3 << 5 << 8 << 13 << 8 << 5; + *set3 << 5 << 6 << 7 << 3 << 4 << 5; + *set4 << 9 << 7 << 5 << 3 << 1 << 2; + //![1] + + //![2] + auto barseries = new QBarSeries; + barseries->append(set0); + barseries->append(set1); + barseries->append(set2); + barseries->append(set3); + barseries->append(set4); + //![2] + + //![8] + auto lineseries = new QLineSeries; + lineseries->setName("trend"); + lineseries->append(QPoint(0, 4)); + lineseries->append(QPoint(1, 15)); + lineseries->append(QPoint(2, 20)); + lineseries->append(QPoint(3, 4)); + lineseries->append(QPoint(4, 12)); + lineseries->append(QPoint(5, 17)); + //![8] + + //![3] + auto chart = new QChart; + chart->addSeries(barseries); + chart->addSeries(lineseries); + chart->setTitle("Line and Bar Chart"); + //![3] + + //![4] + QStringList categories; + categories << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun"; + auto axisX = new QBarCategoryAxis; + axisX->append(categories); + chart->addAxis(axisX, Qt::AlignBottom); + lineseries->attachAxis(axisX); + barseries->attachAxis(axisX); + axisX->setRange(QString("Jan"), QString("Jun")); + + auto axisY = new QValueAxis; + chart->addAxis(axisY, Qt::AlignLeft); + lineseries->attachAxis(axisY); + barseries->attachAxis(axisY); + axisY->setRange(0, 20); + //![4] + + //![5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/lineandbarwidget.h b/examples/charts/chartsgallery/lineandbarwidget.h new file mode 100644 index 00000000..53f64bee --- /dev/null +++ b/examples/charts/chartsgallery/lineandbarwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef LINEANDBARWIDGET_H +#define LINEANDBARWIDGET_H + +#include "contentwidget.h" + +class LineAndBarWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit LineAndBarWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/linewidget.cpp b/examples/charts/chartsgallery/linewidget.cpp new file mode 100644 index 00000000..5bbc9304 --- /dev/null +++ b/examples/charts/chartsgallery/linewidget.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "linewidget.h" + +#include <QChart> +#include <QLineSeries> + +LineWidget::LineWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto series = new QLineSeries; + //![1] + + //![2] + series->append(0, 6); + series->append(2, 4); + series->append(3, 8); + series->append(7, 4); + series->append(10, 5); + *series << QPointF(11, 1) << QPointF(13, 3) << QPointF(17, 6) << QPointF(18, 3) << QPointF(20, 2); + //![2] + + //![3] + auto chart = new QChart; + chart->legend()->hide(); + chart->addSeries(series); + chart->createDefaultAxes(); + chart->setTitle("Simple Line Chart"); + //![3] + + //![4] + createDefaultChartView(chart); + //![4] +} diff --git a/examples/charts/chartsgallery/linewidget.h b/examples/charts/chartsgallery/linewidget.h new file mode 100644 index 00000000..a4022463 --- /dev/null +++ b/examples/charts/chartsgallery/linewidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef LINEWIDGET_H +#define LINEWIDGET_H + +#include "contentwidget.h" + +class LineWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit LineWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/logaxiswidget.cpp b/examples/charts/chartsgallery/logaxiswidget.cpp new file mode 100644 index 00000000..ce72b2ce --- /dev/null +++ b/examples/charts/chartsgallery/logaxiswidget.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "logaxiswidget.h" + +#include <QChart> +#include <QLineSeries> +#include <QLogValueAxis> +#include <QValueAxis> + +LogAxisWidget::LogAxisWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto series = new QLineSeries; + *series << QPointF(1.0, 1.0) << QPointF(2.0, 73.0) << QPointF(3.0, 268.0) << QPointF(4.0, 17.0) + << QPointF(5.0, 4325.0) << QPointF(6.0, 723.0); + //![1] + + //![2] + auto chart = new QChart; + chart->addSeries(series); + chart->legend()->hide(); + chart->setTitle("Logarithmic Axis"); + //![2] + + //![3] + auto axisX = new QValueAxis; + axisX->setTitleText("Data point"); + axisX->setLabelFormat("%i"); + axisX->setTickCount(series->count()); + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + + auto axisY = new QLogValueAxis; + axisY->setTitleText("Values"); + axisY->setLabelFormat("%g"); + axisY->setBase(8.0); + axisY->setMinorTickCount(-1); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + //![3] + + //![4] + createDefaultChartView(chart); + //![4] +} diff --git a/examples/charts/chartsgallery/logaxiswidget.h b/examples/charts/chartsgallery/logaxiswidget.h new file mode 100644 index 00000000..860107ee --- /dev/null +++ b/examples/charts/chartsgallery/logaxiswidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef LOGAXISWIDGET_H +#define LOGAXISWIDGET_H + +#include "contentwidget.h" + +class LogAxisWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit LogAxisWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/main.cpp b/examples/charts/chartsgallery/main.cpp new file mode 100644 index 00000000..b6f7acae --- /dev/null +++ b/examples/charts/chartsgallery/main.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwidget.h" + +#include <QApplication> +#include <QListView> +#include <QStringListModel> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + MainWidget w; + w.show(); + + return a.exec(); +} diff --git a/examples/charts/chartsgallery/mainwidget.cpp b/examples/charts/chartsgallery/mainwidget.cpp new file mode 100644 index 00000000..cb535217 --- /dev/null +++ b/examples/charts/chartsgallery/mainwidget.cpp @@ -0,0 +1,262 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + + +#include "areawidget.h" +#include "boxplotwidget.h" +#include "barmodelmapperwidget.h" +#include "barwidget.h" +#include "barpercentwidget.h" +#include "barstackedwidget.h" +#include "calloutwidget.h" +#include "candlestickwidget.h" +#include "contentwidget.h" +#include "customchartwidget.h" +#include "datetimeaxiswidget.h" +#include "donutbreakdownwidget.h" +#include "donutwidget.h" +#include "dynamicsplinewidget.h" +#include "horizontalbarwidget.h" +#include "horizontalpercentbarwidget.h" +#include "horizontalstackedbarwidget.h" +#include "interactionswidget.h" +#include "legendwidget.h" +#include "legendmarkerswidget.h" +#include "lightmarkerswidget.h" +#include "lineandbarwidget.h" +#include "linewidget.h" +#include "logaxiswidget.h" +#include "mainwidget.h" +#include "modeldatawidget.h" +#include "multiaxiswidget.h" +#include "nesteddonutswidget.h" +#include "pccwidget.h" +#include "piewidget.h" +#include "piedrilldownwidget.h" +#include "pointconfigurationwidget.h" +#include "polarchartwidget.h" +#include "scatterwidget.h" +#include "scatterinteractionswidget.h" +#include "selectedbarwidget.h" +#include "splinewidget.h" +#include "stackeddrilldownwidget.h" +#include "temperaturerecordswidget.h" +#include "themewidget.h" + +#include <QApplication> +#include <QHBoxLayout> +#include <QListView> +#include <QModelIndex> +#include <QStringListModel> + +#include <algorithm> + +MainWidget::MainWidget(QWidget *parent) + : QWidget(parent) + , m_listView(new QListView(this)) + , m_listModel(new QStringListModel(this)) + , m_contentArea(new QWidget(this)) +{ + m_exampleMap.insert(tr("Area Chart"), AreaChart); + m_exampleMap.insert(tr("Bar Model Mapper"), BarModelMapper); + m_exampleMap.insert(tr("Bar Chart"), BarChart); + m_exampleMap.insert(tr("Bar Chart Percent"), BarChartPercent); + m_exampleMap.insert(tr("Bar Chart Stacked"), BarChartStacked); + m_exampleMap.insert(tr("Bar Chart Horizontal"), BarChartHorizontal); + m_exampleMap.insert(tr("Bar Chart Percent Horizontal"), BarChartHorizontalPercent); + m_exampleMap.insert(tr("Bar Chart Stacked Horizontal"), BarChartHorizontalStacked); + m_exampleMap.insert(tr("Box Plot Chart"), BoxPlotChart); + m_exampleMap.insert(tr("Callout"), Callout); + m_exampleMap.insert(tr("Candlestick Chart"), CandleStickChart); + m_exampleMap.insert(tr("Chart Interactions"), ChartInteractions); + m_exampleMap.insert(tr("Chart Themes"), ChartThemes); + m_exampleMap.insert(tr("Custom Chart"), CustomChart); + m_exampleMap.insert(tr("Datetime Axis"), DateTimeAxis); + m_exampleMap.insert(tr("Donut"), Donut); + m_exampleMap.insert(tr("Donut Breakdown"), DonutBreakdown); + m_exampleMap.insert(tr("Drilldown Bar Chart"), StackedDrilldown); + m_exampleMap.insert(tr("Drilldown Pie Chart"), PieDrilldown); + m_exampleMap.insert(tr("Dynamic Spline Chart"), DynamicSplineChart); + m_exampleMap.insert(tr("Legend"), Legend); + m_exampleMap.insert(tr("Legend Markers"), LegendMarkers); + m_exampleMap.insert(tr("Light Markers and Point Selection"), LightMarkersPointSelection); + m_exampleMap.insert(tr("Line and Bar Chart"), LineAndBarChart); + m_exampleMap.insert(tr("Line Chart"), LineChart); + m_exampleMap.insert(tr("Log Axis"), LogAxis); + m_exampleMap.insert(tr("Model Data"), ModelData); + m_exampleMap.insert(tr("Multiple Axes"), MultiAxis); + m_exampleMap.insert(tr("Nested Donuts"), NestedDonuts); + m_exampleMap.insert(tr("Pie Chart Customization"), PieChartCustomization); + m_exampleMap.insert(tr("Pie Chart"), PieChart); + m_exampleMap.insert(tr("Point Configuration"), PointConfiguration); + m_exampleMap.insert(tr("Polar Chart"), PolarChart); + m_exampleMap.insert(tr("Scatter Interactions"), ScatterInteractions); + m_exampleMap.insert(tr("Scatter Chart"), ScatterChart); + m_exampleMap.insert(tr("Selected Bar"), SelectedBar); + m_exampleMap.insert(tr("Spline Chart"), SplineChart); + m_exampleMap.insert(tr("Temperature Records"), TemperatureRecords); + + QStringList examples = m_exampleMap.keys(); + std::sort(examples.begin(), examples.end()); + m_listModel->setStringList(examples); + + m_listView->setMaximumWidth(220); + m_listView->setModel(m_listModel); + m_listView->setCurrentIndex(m_listModel->index(0)); + m_listView->setEditTriggers(QAbstractItemView::NoEditTriggers); + + setMinimumSize(800, 400); + resize(1200, 600); + + auto layout = new QHBoxLayout(this); + layout->addWidget(m_listView); + layout->addWidget(m_contentArea); + setLayout(layout); + + connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged, this, + [this](const QModelIndex &index) { + setActiveExample(m_exampleMap[m_listModel->data(index).toString()]); + }); + + setActiveExample(m_exampleMap[examples[0]]); + + setMouseTracking(true); + + qApp->setApplicationDisplayName(tr("Qt Charts Example Gallery")); +} + +void MainWidget::resizeEvent(QResizeEvent *) +{ + m_activeWidget->resize(m_contentArea->size()); +} + +void MainWidget::setActiveExample(Example example) +{ + // We only keep one example alive at the time to save resources. + // This also allows resetting the example by switching to another example and back. + if (m_activeWidget) { + m_activeWidget->setVisible(false); + m_activeWidget->deleteLater(); + } + + switch (example) { + case AreaChart: + m_activeWidget = new AreaWidget(m_contentArea); + break; + case BarModelMapper: + m_activeWidget = new BarModelMapperWidget(m_contentArea); + break; + case BarChart: + m_activeWidget = new BarWidget(m_contentArea); + break; + case BarChartPercent: + m_activeWidget = new BarPercentWidget(m_contentArea); + break; + case BarChartStacked: + m_activeWidget = new BarStackedWidget(m_contentArea); + break; + case BarChartHorizontal: + m_activeWidget = new HorizontalBarWidget(m_contentArea); + break; + case BarChartHorizontalPercent: + m_activeWidget = new HorizontalPercentBarWidget(m_contentArea); + break; + case BarChartHorizontalStacked: + m_activeWidget = new HorizontalStackedBarWidget(m_contentArea); + break; + case BoxPlotChart: + m_activeWidget = new BoxPlotWidget(m_contentArea); + break; + case Callout: + m_activeWidget = new CalloutWidget(m_contentArea); + break; + case CandleStickChart: + m_activeWidget = new CandleStickWidget(m_contentArea); + break; + case ChartInteractions: + m_activeWidget = new InteractionsWidget(m_contentArea); + break; + case ChartThemes: + m_activeWidget = new ThemeWidget(m_contentArea); + break; + case CustomChart: + m_activeWidget = new CustomChartWidget(m_contentArea); + break; + case DateTimeAxis: + m_activeWidget = new DateTimeAxisWidget(m_contentArea); + break; + case Donut: + m_activeWidget = new DonutWidget(m_contentArea); + break; + case DonutBreakdown: + m_activeWidget = new DonutBreakdownWidget(m_contentArea); + break; + case DynamicSplineChart: + m_activeWidget = new DynamicSplineWidget(m_contentArea); + break; + case Legend: + m_activeWidget = new LegendWidget(m_contentArea); + break; + case LegendMarkers: + m_activeWidget = new LegendMarkersWidget(m_contentArea); + break; + case LightMarkersPointSelection: + m_activeWidget = new LightMarkersWidget(m_contentArea); + break; + case LineAndBarChart: + m_activeWidget = new LineAndBarWidget(m_contentArea); + break; + case LineChart: + m_activeWidget = new LineWidget(m_contentArea); + break; + case LogAxis: + m_activeWidget = new LogAxisWidget(m_contentArea); + break; + case ModelData: + m_activeWidget = new ModelDataWidget(m_contentArea); + break; + case MultiAxis: + m_activeWidget = new MultiAxisWidget(m_contentArea); + break; + case NestedDonuts: + m_activeWidget = new NestedDonutsWidget(m_contentArea); + break; + case PieChartCustomization: + m_activeWidget = new PccWidget(m_contentArea); + break; + case PieDrilldown: + m_activeWidget = new PieDrilldownWidget(m_contentArea); + break; + case PieChart: + m_activeWidget = new PieWidget(m_contentArea); + break; + case PointConfiguration: + m_activeWidget = new PointConfigurationWidget(m_contentArea); + break; + case PolarChart: + m_activeWidget = new PolarChartWidget(m_contentArea); + break; + case ScatterInteractions: + m_activeWidget = new ScatterInteractionsWidget(m_contentArea); + break; + case ScatterChart: + m_activeWidget = new ScatterWidget(m_contentArea); + break; + case SelectedBar: + m_activeWidget = new SelectedBarWidget(m_contentArea); + break; + case SplineChart: + m_activeWidget = new SplineWidget(m_contentArea); + break; + case TemperatureRecords: + m_activeWidget = new TemperatureRecordsWidget(m_contentArea); + break; + case StackedDrilldown: + m_activeWidget = new StackedDrilldownWidget(m_contentArea); + break; + } + + m_activeWidget->load(); + m_activeWidget->resize(m_contentArea->size()); + m_activeWidget->setVisible(true); +} diff --git a/examples/charts/chartsgallery/mainwidget.h b/examples/charts/chartsgallery/mainwidget.h new file mode 100644 index 00000000..8082812c --- /dev/null +++ b/examples/charts/chartsgallery/mainwidget.h @@ -0,0 +1,75 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MAINWIDGET_H +#define MAINWIDGET_H + +#include <QHash> +#include <QWidget> + +QT_FORWARD_DECLARE_CLASS(QListView) +QT_FORWARD_DECLARE_CLASS(QStringListModel) + +class ContentWidget; + +class MainWidget : public QWidget +{ + Q_OBJECT +public: + explicit MainWidget(QWidget *parent = nullptr); + +protected: + void resizeEvent(QResizeEvent *) override; + +private: + enum Example { + AreaChart, + BarModelMapper, + BarChart, + BarChartPercent, + BarChartStacked, + BarChartHorizontal, + BarChartHorizontalPercent, + BarChartHorizontalStacked, + BoxPlotChart, + Callout, + CandleStickChart, + ChartInteractions, + ChartThemes, + CustomChart, + DateTimeAxis, + Donut, + DonutBreakdown, + DynamicSplineChart, + Legend, + LegendMarkers, + LightMarkersPointSelection, + LineAndBarChart, + LineChart, + LogAxis, + ModelData, + MultiAxis, + NestedDonuts, + PieChartCustomization, + PieDrilldown, + PieChart, + PointConfiguration, + PolarChart, + ScatterChart, + ScatterInteractions, + SelectedBar, + SplineChart, + StackedDrilldown, + TemperatureRecords + }; + + void setActiveExample(Example example); + + QListView *m_listView = nullptr; + QStringListModel *m_listModel = nullptr; + QWidget *m_contentArea = nullptr; + ContentWidget *m_activeWidget = nullptr; + QHash<QString, Example> m_exampleMap; +}; + +#endif diff --git a/examples/charts/chartsgallery/modeldatamodel.cpp b/examples/charts/chartsgallery/modeldatamodel.cpp new file mode 100644 index 00000000..af5bc9dd --- /dev/null +++ b/examples/charts/chartsgallery/modeldatamodel.cpp @@ -0,0 +1,92 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "modeldatamodel.h" + +#include <QColor> +#include <QList> +#include <QRandomGenerator> +#include <QRect> + +ModelDataModel::ModelDataModel(QObject *parent) : + QAbstractTableModel(parent) +{ + m_columnCount = 4; + m_rowCount = 15; + + // m_data + for (int i = 0; i < m_rowCount; i++) { + auto dataList = new QList<qreal>(m_columnCount); + for (int k = 0; k < dataList->size(); k++) { + if (k % 2 == 0) + dataList->replace(k, i * 50 + QRandomGenerator::global()->bounded(20)); + else + dataList->replace(k, QRandomGenerator::global()->bounded(100)); + } + m_data.append(dataList); + } +} + +int ModelDataModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_data.count(); +} + +int ModelDataModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_columnCount; +} + +QVariant ModelDataModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + if (section % 2 == 0) + return "x"; + else + return "y"; + } else { + return QString("%1").arg(section + 1); + } +} + +QVariant ModelDataModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) { + return m_data[index.row()]->at(index.column()); + } else if (role == Qt::EditRole) { + return m_data[index.row()]->at(index.column()); + } else if (role == Qt::BackgroundRole) { + for (const QRect &rect : m_mapping) { + if (rect.contains(index.column(), index.row())) + return QColor(m_mapping.key(rect)); + } + // cell not mapped return white color + return QColor(Qt::white); + } + return QVariant(); +} + +bool ModelDataModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) { + m_data[index.row()]->replace(index.column(), value.toDouble()); + emit dataChanged(index, index); + return true; + } + return false; +} + +Qt::ItemFlags ModelDataModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +void ModelDataModel::addMapping(const QString &color, const QRect &area) +{ + m_mapping.insert(color, area); +} diff --git a/examples/charts/chartsgallery/modeldatamodel.h b/examples/charts/chartsgallery/modeldatamodel.h new file mode 100644 index 00000000..e8b2e1d1 --- /dev/null +++ b/examples/charts/chartsgallery/modeldatamodel.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MODELDATAMODEL_H +#define MODELDATAMODEL_H + +#include <QAbstractTableModel> +#include <QMultiHash> +#include <QRect> + +class ModelDataModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit ModelDataModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) const; + + void addMapping(const QString &color, const QRect &area); + void clearMapping() { m_mapping.clear(); } + +private: + QList<QList<qreal> *> m_data; + QMultiHash<QString, QRect> m_mapping; + int m_columnCount; + int m_rowCount; +}; + +#endif diff --git a/examples/charts/chartsgallery/modeldatawidget.cpp b/examples/charts/chartsgallery/modeldatawidget.cpp new file mode 100644 index 00000000..20d50aa6 --- /dev/null +++ b/examples/charts/chartsgallery/modeldatawidget.cpp @@ -0,0 +1,95 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "modeldatawidget.h" +#include "modeldatamodel.h" + +#include <QChart> +#include <QChartView> +#include <QGraphicsLayout> +#include <QGridLayout> +#include <QHeaderView> +#include <QLineSeries> +#include <QTableView> +#include <QVXYModelMapper> + +QT_USE_NAMESPACE + +ModelDataWidget::ModelDataWidget(QWidget *parent) + : ContentWidget(parent) +{ + // create simple model for storing user's data + //! [1] + auto *model = new ModelDataModel; + //! [1] + + //! [2] + // create table view and add model to it + auto tableView = new QTableView; + tableView->setModel(model); + tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); + //! [2] + + //! [3] + auto chart = new QChart; + chart->setAnimationOptions(QChart::AllAnimations); + //! [3] + + // series 1 + //! [4] + auto series = new QLineSeries; + series->setName("Line 1"); + auto mapper = new QVXYModelMapper(this); + mapper->setXColumn(0); + mapper->setYColumn(1); + mapper->setSeries(series); + mapper->setModel(model); + chart->addSeries(series); + //! [4] + + //! [5] + // for storing color hex from the series + QString seriesColorHex = "#000000"; + + // get the color of the series and use it for showing the mapped area + seriesColorHex = "#" + QString::number(series->pen().color().rgb(), 16).right(6).toUpper(); + model->addMapping(seriesColorHex, QRect(0, 0, 2, model->rowCount())); + //! [5] + + // series 2 + //! [6] + series = new QLineSeries; + series->setName("Line 2"); + + mapper = new QVXYModelMapper(this); + mapper->setXColumn(2); + mapper->setYColumn(3); + mapper->setSeries(series); + mapper->setModel(model); + chart->addSeries(series); + //! [6] + + //! [7] + // get the color of the series and use it for showing the mapped area + seriesColorHex = "#" + QString::number(series->pen().color().rgb(), 16).right(6).toUpper(); + model->addMapping(seriesColorHex, QRect(2, 0, 2, model->rowCount())); + //! [7] + + //! [8] + chart->createDefaultAxes(); + chart->layout()->setContentsMargins(0, 0, 0, 0); + auto chartView = new QChartView(chart, this); + chartView->setRenderHint(QPainter::Antialiasing); + //! [8] + + //! [9] + // create main layout + auto mainLayout = new QGridLayout; + mainLayout->addWidget(tableView, 1, 0); + mainLayout->addWidget(chartView, 1, 1); + mainLayout->setColumnStretch(1, 1); + mainLayout->setColumnStretch(0, 0); + setLayout(mainLayout); + //! [9] +} diff --git a/examples/charts/chartsgallery/modeldatawidget.h b/examples/charts/chartsgallery/modeldatawidget.h new file mode 100644 index 00000000..245e6267 --- /dev/null +++ b/examples/charts/chartsgallery/modeldatawidget.h @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MODELDATAWIDGET_H +#define MODELDATAWIDGET_H + +#include "contentwidget.h" + +class ModelDataWidget : public ContentWidget +{ + Q_OBJECT + +public: + ModelDataWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/multiaxiswidget.cpp b/examples/charts/chartsgallery/multiaxiswidget.cpp new file mode 100644 index 00000000..1979a59e --- /dev/null +++ b/examples/charts/chartsgallery/multiaxiswidget.cpp @@ -0,0 +1,61 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "multiaxiswidget.h" + +#include <QCategoryAxis> +#include <QChart> +#include <QLineSeries> +#include <QSplineSeries> +#include <QValueAxis> + +MultiAxisWidget::MultiAxisWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto chart = new QChart; + chart->legend()->hide(); + chart->setTitle("Multiple Vertical Axes in Chart"); + //![1] + + //![2] + auto axisX = new QValueAxis; + axisX->setTickCount(10); + chart->addAxis(axisX, Qt::AlignBottom); + //![2] + + //![3] + auto series = new QSplineSeries; + *series << QPointF(1, 5) << QPointF(3.5, 18) << QPointF(4.8, 7.5) << QPointF(10, 2.5); + chart->addSeries(series); + + auto axisY = new QValueAxis; + axisY->setLinePenColor(series->pen().color()); + + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisX); + series->attachAxis(axisY); + //![3] + + //![4] + series = new QSplineSeries; + *series << QPointF(1, 0.5) << QPointF(1.5, 4.5) << QPointF(2.4, 2.5) << QPointF(4.3, 12.5) + << QPointF(5.2, 3.5) << QPointF(7.4, 16.5) << QPointF(8.3, 7.5) << QPointF(10, 17); + chart->addSeries(series); + + auto axisY3 = new QCategoryAxis; + axisY3->append("Low", 5); + axisY3->append("Medium", 12); + axisY3->append("High", 17); + axisY3->setLinePenColor(series->pen().color()); + axisY3->setGridLinePen((series->pen())); + + chart->addAxis(axisY3, Qt::AlignRight); + series->attachAxis(axisX); + series->attachAxis(axisY3); + //![4] + + //![5] + createDefaultChartView(chart); + //![5] +} diff --git a/examples/charts/chartsgallery/multiaxiswidget.h b/examples/charts/chartsgallery/multiaxiswidget.h new file mode 100644 index 00000000..e61cc368 --- /dev/null +++ b/examples/charts/chartsgallery/multiaxiswidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MULTIAXISWIDGET_H +#define MULTIAXISWIDGET_H + +#include "contentwidget.h" + +class MultiAxisWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit MultiAxisWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/nesteddonutswidget.cpp b/examples/charts/chartsgallery/nesteddonutswidget.cpp new file mode 100644 index 00000000..25c103c9 --- /dev/null +++ b/examples/charts/chartsgallery/nesteddonutswidget.cpp @@ -0,0 +1,105 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "nesteddonutswidget.h" + +#include <QChart> +#include <QChartView> +#include <QGraphicsLayout> +#include <QGridLayout> +#include <QLegend> +#include <QPieSeries> +#include <QPieSlice> +#include <QRandomGenerator> +#include <QTimer> + +NestedDonutsWidget::NestedDonutsWidget(QWidget *parent) + : ContentWidget(parent) +{ + //! [1] + auto chartView = new QChartView(this); + chartView->setRenderHint(QPainter::Antialiasing); + QChart *chart = chartView->chart(); + chart->legend()->setVisible(false); + chart->setTitle("Nested Donuts (Hover over segments to explode them)"); + chart->setAnimationOptions(QChart::AllAnimations); + chart->layout()->setContentsMargins(0, 0, 0, 0); + //! [1] + + //! [2] + qreal minSize = 0.1; + qreal maxSize = 0.9; + int donutCount = 5; + //! [2] + + //! [3] + for (int i = 0; i < donutCount; i++) { + auto donut = new QPieSeries; + int sliceCount = 3 + QRandomGenerator::global()->bounded(3); + for (int j = 0; j < sliceCount; j++) { + qreal value = 100 + QRandomGenerator::global()->bounded(100); + auto slice = new QPieSlice(QString("%1").arg(value), value); + slice->setLabelVisible(true); + slice->setLabelColor(Qt::white); + slice->setLabelPosition(QPieSlice::LabelInsideTangential); + connect(slice, &QPieSlice::hovered, this, &NestedDonutsWidget::explodeSlice); + donut->append(slice); + donut->setHoleSize(minSize + i * (maxSize - minSize) / donutCount); + donut->setPieSize(minSize + (i + 1) * (maxSize - minSize) / donutCount); + } + m_donuts.append(donut); + chartView->chart()->addSeries(donut); + } + //! [3] + + // create main layout + //! [4] + auto mainLayout = new QGridLayout; + mainLayout->addWidget(chartView, 1, 1); + setLayout(mainLayout); + //! [4] + + //! [5] + m_updateTimer = new QTimer(this); + connect(m_updateTimer, &QTimer::timeout, this, &NestedDonutsWidget::updateRotation); + m_updateTimer->start(1250); + //! [5] +} + +//! [6] +void NestedDonutsWidget::updateRotation() +{ + for (int i = 0; i < m_donuts.count(); i++) { + QPieSeries *donut = m_donuts.at(i); + qreal phaseShift = -50 + QRandomGenerator::global()->bounded(100); + donut->setPieStartAngle(donut->pieStartAngle() + phaseShift); + donut->setPieEndAngle(donut->pieEndAngle() + phaseShift); + } +} +//! [6] + +//! [7] +void NestedDonutsWidget::explodeSlice(bool exploded) +{ + auto slice = qobject_cast<QPieSlice *>(sender()); + if (exploded) { + m_updateTimer->stop(); + qreal sliceStartAngle = slice->startAngle(); + qreal sliceEndAngle = slice->startAngle() + slice->angleSpan(); + + QPieSeries *donut = slice->series(); + qreal seriesIndex = m_donuts.indexOf(donut); + for (int i = seriesIndex + 1; i < m_donuts.count(); i++) { + m_donuts.at(i)->setPieStartAngle(sliceEndAngle); + m_donuts.at(i)->setPieEndAngle(360 + sliceStartAngle); + } + } else { + for (int i = 0; i < m_donuts.count(); i++) { + m_donuts.at(i)->setPieStartAngle(0); + m_donuts.at(i)->setPieEndAngle(360); + } + m_updateTimer->start(); + } + slice->setExploded(exploded); +} +//! [7] diff --git a/examples/charts/chartsgallery/nesteddonutswidget.h b/examples/charts/chartsgallery/nesteddonutswidget.h new file mode 100644 index 00000000..7dfc95c1 --- /dev/null +++ b/examples/charts/chartsgallery/nesteddonutswidget.h @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef NESTEDDONUTSWIDGET_H +#define NESTEDDONUTSWIDGET_H + +#include "contentwidget.h" + +QT_FORWARD_DECLARE_CLASS(QTimer) +QT_FORWARD_DECLARE_CLASS(QPieSeries) + +class NestedDonutsWidget : public ContentWidget +{ + Q_OBJECT +public: + NestedDonutsWidget(QWidget *parent = nullptr); + +public slots: + void updateRotation(); + void explodeSlice(bool exploded); + +private: + QList<QPieSeries *> m_donuts; + QTimer *m_updateTimer = nullptr; +}; + +#endif diff --git a/examples/charts/chartsgallery/pccbrushtool.cpp b/examples/charts/chartsgallery/pccbrushtool.cpp new file mode 100644 index 00000000..63cdf0c3 --- /dev/null +++ b/examples/charts/chartsgallery/pccbrushtool.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "pccbrushtool.h" + +#include <QColorDialog> +#include <QComboBox> +#include <QFormLayout> +#include <QPushButton> + +PccBrushTool::PccBrushTool(const QString &title, QWidget *parent) + : QWidget(parent) +{ + setWindowTitle(title); + setWindowFlags(Qt::Tool); + + m_colorButton = new QPushButton(); + m_styleCombo = new QComboBox(); + m_styleCombo->addItem("Nobrush", (int) Qt::NoBrush); + m_styleCombo->addItem("Solidpattern", (int) Qt::SolidPattern); + m_styleCombo->addItem("Dense1pattern", (int) Qt::Dense1Pattern); + m_styleCombo->addItem("Dense2attern", (int) Qt::Dense2Pattern); + m_styleCombo->addItem("Dense3Pattern", (int) Qt::Dense3Pattern); + m_styleCombo->addItem("Dense4Pattern", (int) Qt::Dense4Pattern); + m_styleCombo->addItem("Dense5Pattern", (int) Qt::Dense5Pattern); + m_styleCombo->addItem("Dense6Pattern", (int) Qt::Dense6Pattern); + m_styleCombo->addItem("Dense7Pattern", (int) Qt::Dense7Pattern); + m_styleCombo->addItem("HorPattern", (int) Qt::HorPattern); + m_styleCombo->addItem("VerPattern", (int) Qt::VerPattern); + m_styleCombo->addItem("CrossPattern", (int) Qt::CrossPattern); + m_styleCombo->addItem("BDiagPattern", (int) Qt::BDiagPattern); + m_styleCombo->addItem("FDiagPattern", (int) Qt::FDiagPattern); + m_styleCombo->addItem("DiagCrossPattern", (int) Qt::DiagCrossPattern); + + auto layout = new QFormLayout; + layout->addRow("Color", m_colorButton); + layout->addRow("Style", m_styleCombo); + setLayout(layout); + + connect(m_colorButton, &QPushButton::clicked, this, &PccBrushTool::showColorDialog); + connect(m_styleCombo, &QComboBox::currentIndexChanged, this, &PccBrushTool::updateStyle); +} + +void PccBrushTool::setBrush(const QBrush &brush) +{ + m_brush = brush; + m_colorButton->setText(m_brush.color().name()); + m_styleCombo->setCurrentIndex(m_brush.style()); // index matches the enum +} + +QBrush PccBrushTool::brush() const +{ + return m_brush; +} + +QString PccBrushTool::name() const +{ + return name(m_brush); +} + +QString PccBrushTool::name(const QBrush &brush) +{ + return brush.color().name(); +} + +void PccBrushTool::showColorDialog() +{ + QColorDialog dialog(m_brush.color()); + dialog.show(); + dialog.exec(); + m_brush.setColor(dialog.selectedColor()); + m_colorButton->setText(m_brush.color().name()); + emit changed(); +} + +void PccBrushTool::updateStyle() +{ + Qt::BrushStyle style = (Qt::BrushStyle) m_styleCombo->itemData(m_styleCombo->currentIndex()).toInt(); + if (m_brush.style() != style) { + m_brush.setStyle(style); + emit changed(); + } +} diff --git a/examples/charts/chartsgallery/pccbrushtool.h b/examples/charts/chartsgallery/pccbrushtool.h new file mode 100644 index 00000000..2e392b6a --- /dev/null +++ b/examples/charts/chartsgallery/pccbrushtool.h @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PCCBRUSHTOOL_H +#define PCCBRUSHTOOL_H + +#include <QBrush> +#include <QWidget> + +QT_FORWARD_DECLARE_CLASS(QComboBox) +QT_FORWARD_DECLARE_CLASS(QPushButton) + +class PccBrushTool : public QWidget +{ + Q_OBJECT + +public: + explicit PccBrushTool(const QString &title, QWidget *parent = nullptr); + void setBrush(const QBrush &brush); + QBrush brush() const; + QString name() const; + static QString name(const QBrush &brush); + +signals: + void changed(); + +public slots: + void showColorDialog(); + void updateStyle(); + +private: + QBrush m_brush; + QPushButton *m_colorButton = nullptr; + QComboBox *m_styleCombo = nullptr; +}; + +#endif diff --git a/examples/charts/chartsgallery/pcccustomslice.cpp b/examples/charts/chartsgallery/pcccustomslice.cpp new file mode 100644 index 00000000..6b4857b6 --- /dev/null +++ b/examples/charts/chartsgallery/pcccustomslice.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "pcccustomslice.h" + +PccCustomSlice::PccCustomSlice(const QString &label, qreal value) + : QPieSlice(label, value) +{ + connect(this, &PccCustomSlice::hovered, this, &PccCustomSlice::showHighlight); +} + +QBrush PccCustomSlice::originalBrush() +{ + return m_originalBrush; +} + +void PccCustomSlice::showHighlight(bool show) +{ + if (show) { + QBrush brush = this->brush(); + m_originalBrush = brush; + brush.setColor(brush.color().lighter()); + setBrush(brush); + } else { + setBrush(m_originalBrush); + } +} diff --git a/examples/charts/chartsgallery/pcccustomslice.h b/examples/charts/chartsgallery/pcccustomslice.h new file mode 100644 index 00000000..b7ab879e --- /dev/null +++ b/examples/charts/chartsgallery/pcccustomslice.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PCCCUSTOMSLICE_H +#define PCCCUSTOMSLICE_H + +#include <QPieSlice> + +class PccCustomSlice : public QPieSlice +{ + Q_OBJECT +public: + PccCustomSlice(const QString &label, qreal value); + +public: + QBrush originalBrush(); + +public slots: + void showHighlight(bool show); + +private: + QBrush m_originalBrush; +}; + +#endif diff --git a/examples/charts/chartsgallery/pccpentool.cpp b/examples/charts/chartsgallery/pccpentool.cpp new file mode 100644 index 00000000..11bb1d76 --- /dev/null +++ b/examples/charts/chartsgallery/pccpentool.cpp @@ -0,0 +1,124 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "pccpentool.h" + +#include <QColorDialog> +#include <QComboBox> +#include <QDoubleSpinBox> +#include <QFormLayout> +#include <QPushButton> + +PccPenTool::PccPenTool(const QString &title, QWidget *parent) + : QWidget(parent) +{ + setWindowTitle(title); + setWindowFlags(Qt::Tool); + + m_colorButton = new QPushButton(this); + + m_widthSpinBox = new QDoubleSpinBox(this); + + m_styleCombo = new QComboBox(this); + m_styleCombo->addItem("NoPen"); + m_styleCombo->addItem("SolidLine"); + m_styleCombo->addItem("DashLine"); + m_styleCombo->addItem("DotLine"); + m_styleCombo->addItem("DashDotLine"); + m_styleCombo->addItem("DashDotDotLine"); + + m_capStyleCombo = new QComboBox(this); + m_capStyleCombo->addItem("FlatCap", Qt::FlatCap); + m_capStyleCombo->addItem("SquareCap", Qt::SquareCap); + m_capStyleCombo->addItem("RoundCap", Qt::RoundCap); + + m_joinStyleCombo = new QComboBox(this); + m_joinStyleCombo->addItem("MiterJoin", Qt::MiterJoin); + m_joinStyleCombo->addItem("BevelJoin", Qt::BevelJoin); + m_joinStyleCombo->addItem("RoundJoin", Qt::RoundJoin); + m_joinStyleCombo->addItem("SvgMiterJoin", Qt::SvgMiterJoin); + + auto layout = new QFormLayout; + layout->addRow("Color", m_colorButton); + layout->addRow("Width", m_widthSpinBox); + layout->addRow("Style", m_styleCombo); + layout->addRow("Cap style", m_capStyleCombo); + layout->addRow("Join style", m_joinStyleCombo); + setLayout(layout); + + // Use old style connect on some signals because the signal is overloaded + connect(m_colorButton, &QPushButton::clicked, this, &PccPenTool::showColorDialog); + connect(m_widthSpinBox, &QDoubleSpinBox::valueChanged, this, &PccPenTool::updateWidth); + connect(m_styleCombo, &QComboBox::currentIndexChanged, this, &PccPenTool::updateStyle); + connect(m_capStyleCombo, &QComboBox::currentIndexChanged, this, &PccPenTool::updateCapStyle); + connect(m_joinStyleCombo, &QComboBox::currentIndexChanged, this, &PccPenTool::updateJoinStyle); +} + +void PccPenTool::setPen(const QPen &pen) +{ + m_pen = pen; + m_colorButton->setText(m_pen.color().name()); + m_widthSpinBox->setValue(m_pen.widthF()); + m_styleCombo->setCurrentIndex(m_pen.style()); // index matches the enum + m_capStyleCombo->setCurrentIndex(m_capStyleCombo->findData(m_pen.capStyle())); + m_joinStyleCombo->setCurrentIndex(m_joinStyleCombo->findData(m_pen.joinStyle())); +} + +QPen PccPenTool::pen() const +{ + return m_pen; +} + +QString PccPenTool::name() const +{ + return name(m_pen); +} + +QString PccPenTool::name(const QPen &pen) +{ + return pen.color().name() + ":" + QString::number(pen.widthF()); +} + +void PccPenTool::showColorDialog() +{ + QColorDialog dialog(m_pen.color()); + dialog.show(); + dialog.exec(); + m_pen.setColor(dialog.selectedColor()); + m_colorButton->setText(m_pen.color().name()); + emit changed(); +} + +void PccPenTool::updateWidth(double width) +{ + if (!qFuzzyCompare((qreal) width, m_pen.widthF())) { + m_pen.setWidthF(width); + emit changed(); + } +} + +void PccPenTool::updateStyle(int style) +{ + if (m_pen.style() != style) { + m_pen.setStyle((Qt::PenStyle) style); + emit changed(); + } +} + +void PccPenTool::updateCapStyle(int index) +{ + Qt::PenCapStyle capStyle = (Qt::PenCapStyle) m_capStyleCombo->itemData(index).toInt(); + if (m_pen.capStyle() != capStyle) { + m_pen.setCapStyle(capStyle); + emit changed(); + } +} + +void PccPenTool::updateJoinStyle(int index) +{ + Qt::PenJoinStyle joinStyle = (Qt::PenJoinStyle) m_joinStyleCombo->itemData(index).toInt(); + if (m_pen.joinStyle() != joinStyle) { + m_pen.setJoinStyle(joinStyle); + emit changed(); + } +} diff --git a/examples/charts/chartsgallery/pccpentool.h b/examples/charts/chartsgallery/pccpentool.h new file mode 100644 index 00000000..14a48c89 --- /dev/null +++ b/examples/charts/chartsgallery/pccpentool.h @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PCCPENTOOL_H +#define PCCPENTOOL_H + +#include <QPen> +#include <QWidget> + +QT_FORWARD_DECLARE_CLASS(QComboBox); +QT_FORWARD_DECLARE_CLASS(QDoubleSpinBox); +QT_FORWARD_DECLARE_CLASS(QPushButton); + +class PccPenTool : public QWidget +{ + Q_OBJECT +public: + explicit PccPenTool(const QString &title, QWidget *parent = nullptr); + void setPen(const QPen &pen); + QPen pen() const; + QString name() const; + static QString name(const QPen &pen); + +signals: + void changed(); + +public slots: + void showColorDialog(); + void updateWidth(double width); + void updateStyle(int style); + void updateCapStyle(int index); + void updateJoinStyle(int index); + +private: + QPen m_pen; + QPushButton *m_colorButton; + QDoubleSpinBox *m_widthSpinBox; + QComboBox *m_styleCombo; + QComboBox *m_capStyleCombo; + QComboBox *m_joinStyleCombo; +}; + +#endif diff --git a/examples/charts/chartsgallery/pccwidget.cpp b/examples/charts/chartsgallery/pccwidget.cpp new file mode 100644 index 00000000..2f2e0e02 --- /dev/null +++ b/examples/charts/chartsgallery/pccwidget.cpp @@ -0,0 +1,398 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "pccbrushtool.h" +#include "pcccustomslice.h" +#include "pccwidget.h" +#include "pccpentool.h" + +#include <QChartView> +#include <QCheckBox> +#include <QComboBox> +#include <QDoubleSpinBox> +#include <QFontDialog> +#include <QFormLayout> +#include <QGraphicsLayout> +#include <QGroupBox> +#include <QLineEdit> +#include <QPieSeries> +#include <QPushButton> + +PccWidget::PccWidget(QWidget *parent) + : ContentWidget(parent) +{ + // create chart + auto chart = new QChart; + chart->setTitle("Pie Chart Customization"); + chart->setAnimationOptions(QChart::AllAnimations); + chart->layout()->setContentsMargins(0, 0, 0, 0); + + // create series + m_series = new QPieSeries; + *m_series << new PccCustomSlice("Slice 1", 10.0); + *m_series << new PccCustomSlice("Slice 2", 20.0); + *m_series << new PccCustomSlice("Slice 3", 30.0); + *m_series << new PccCustomSlice("Slice 4", 40.0); + *m_series << new PccCustomSlice("Slice 5", 50.0); + m_series->setLabelsVisible(); + chart->addSeries(m_series); + + connect(m_series, &QPieSeries::clicked, this, &PccWidget::handleSliceClicked); + + // chart settings + m_themeComboBox = new QComboBox(this); + m_themeComboBox->addItem("Light", QChart::ChartThemeLight); + m_themeComboBox->addItem("BlueCerulean", QChart::ChartThemeBlueCerulean); + m_themeComboBox->addItem("Dark", QChart::ChartThemeDark); + m_themeComboBox->addItem("BrownSand", QChart::ChartThemeBrownSand); + m_themeComboBox->addItem("BlueNcs", QChart::ChartThemeBlueNcs); + m_themeComboBox->addItem("High Contrast", QChart::ChartThemeHighContrast); + m_themeComboBox->addItem("Blue Icy", QChart::ChartThemeBlueIcy); + m_themeComboBox->addItem("Qt", QChart::ChartThemeQt); + + m_aaCheckBox = new QCheckBox(this); + m_animationsCheckBox = new QCheckBox(this); + m_animationsCheckBox->setCheckState(Qt::Checked); + + m_legendCheckBox = new QCheckBox(this); + + m_settingsScrollBar = new QScrollArea(this); + auto settingsContentWidget = new QWidget(this); + + auto chartSettingsLayout = new QFormLayout(settingsContentWidget); + chartSettingsLayout->addRow("Theme", m_themeComboBox); + chartSettingsLayout->addRow("Antialiasing", m_aaCheckBox); + chartSettingsLayout->addRow("Animations", m_animationsCheckBox); + chartSettingsLayout->addRow("Legend", m_legendCheckBox); + auto chartSettings = new QGroupBox("Chart", this); + chartSettings->setLayout(chartSettingsLayout); + + connect(m_themeComboBox, &QComboBox::currentIndexChanged, this, &PccWidget::updateChartSettings); + connect(m_aaCheckBox, &QCheckBox::toggled, this, &PccWidget::updateChartSettings); + connect(m_animationsCheckBox, &QCheckBox::toggled, this, &PccWidget::updateChartSettings); + connect(m_legendCheckBox, &QCheckBox::toggled, this, &PccWidget::updateChartSettings); + + // series settings + m_hPosition = new QDoubleSpinBox(this); + m_hPosition->setMinimum(0.0); + m_hPosition->setMaximum(1.0); + m_hPosition->setSingleStep(0.1); + m_hPosition->setValue(m_series->horizontalPosition()); + + m_vPosition = new QDoubleSpinBox(this); + m_vPosition->setMinimum(0.0); + m_vPosition->setMaximum(1.0); + m_vPosition->setSingleStep(0.1); + m_vPosition->setValue(m_series->verticalPosition()); + + m_sizeFactor = new QDoubleSpinBox(this); + m_sizeFactor->setMinimum(0.0); + m_sizeFactor->setMaximum(1.0); + m_sizeFactor->setSingleStep(0.1); + m_sizeFactor->setValue(m_series->pieSize()); + + m_startAngle = new QDoubleSpinBox(this); + m_startAngle->setMinimum(-720); + m_startAngle->setMaximum(720); + m_startAngle->setValue(m_series->pieStartAngle()); + m_startAngle->setSingleStep(1); + + m_endAngle = new QDoubleSpinBox(this); + m_endAngle->setMinimum(-720); + m_endAngle->setMaximum(720); + m_endAngle->setValue(m_series->pieEndAngle()); + m_endAngle->setSingleStep(1); + + m_holeSize = new QDoubleSpinBox(this); + m_holeSize->setMinimum(0.0); + m_holeSize->setMaximum(1.0); + m_holeSize->setSingleStep(0.1); + m_holeSize->setValue(m_series->holeSize()); + + auto appendSlice = new QPushButton("Append slice", this); + auto insertSlice = new QPushButton("Insert slice", this); + auto removeSlice = new QPushButton("Remove selected slice", this); + + auto seriesSettingsLayout = new QFormLayout(settingsContentWidget); + seriesSettingsLayout->addRow("Horizontal position", m_hPosition); + seriesSettingsLayout->addRow("Vertical position", m_vPosition); + seriesSettingsLayout->addRow("Size factor", m_sizeFactor); + seriesSettingsLayout->addRow("Start angle", m_startAngle); + seriesSettingsLayout->addRow("End angle", m_endAngle); + seriesSettingsLayout->addRow("Hole size", m_holeSize); + seriesSettingsLayout->addRow(appendSlice); + seriesSettingsLayout->addRow(insertSlice); + seriesSettingsLayout->addRow(removeSlice); + auto seriesSettings = new QGroupBox("Series", this); + seriesSettings->setLayout(seriesSettingsLayout); + + connect(m_vPosition, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &PccWidget::updateSerieSettings); + connect(m_hPosition, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &PccWidget::updateSerieSettings); + connect(m_sizeFactor, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &PccWidget::updateSerieSettings); + connect(m_startAngle, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &PccWidget::updateSerieSettings); + connect(m_endAngle, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &PccWidget::updateSerieSettings); + connect(m_holeSize, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &PccWidget::updateSerieSettings); + connect(appendSlice, &QPushButton::clicked, this, &PccWidget::appendSlice); + connect(insertSlice, &QPushButton::clicked, this, &PccWidget::insertSlice); + connect(removeSlice, &QPushButton::clicked, this, &PccWidget::removeSlice); + + // slice settings + m_sliceName = new QLineEdit("<click a slice>", this); + m_sliceName->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + m_sliceValue = new QDoubleSpinBox(this); + m_sliceValue->setMaximum(1000); + m_sliceLabelVisible = new QCheckBox(this); + m_sliceLabelArmFactor = new QDoubleSpinBox(this); + m_sliceLabelArmFactor->setSingleStep(0.01); + m_sliceExploded = new QCheckBox(this); + m_sliceExplodedFactor = new QDoubleSpinBox(this); + m_sliceExplodedFactor->setSingleStep(0.01); + m_pen = new QPushButton(this); + m_penTool = new PccPenTool("Slice pen", this); + m_brush = new QPushButton(this); + m_brushTool = new PccBrushTool("Slice brush", this); + m_font = new QPushButton(this); + m_labelBrush = new QPushButton(this); + m_labelBrushTool = new PccBrushTool("Label brush", this); + m_labelPosition = new QComboBox(this); + m_labelPosition->addItem("Outside", QPieSlice::LabelOutside); + m_labelPosition->addItem("Inside horizontal", QPieSlice::LabelInsideHorizontal); + m_labelPosition->addItem("Inside tangential", QPieSlice::LabelInsideTangential); + m_labelPosition->addItem("Inside normal", QPieSlice::LabelInsideNormal); + + auto sliceSettingsLayout = new QFormLayout(settingsContentWidget); + sliceSettingsLayout->addRow("Label", m_sliceName); + sliceSettingsLayout->addRow("Value", m_sliceValue); + sliceSettingsLayout->addRow("Pen", m_pen); + sliceSettingsLayout->addRow("Brush", m_brush); + sliceSettingsLayout->addRow("Label visible", m_sliceLabelVisible); + sliceSettingsLayout->addRow("Label font", m_font); + sliceSettingsLayout->addRow("Label brush", m_labelBrush); + sliceSettingsLayout->addRow("Label position", m_labelPosition); + sliceSettingsLayout->addRow("Label arm length", m_sliceLabelArmFactor); + sliceSettingsLayout->addRow("Exploded", m_sliceExploded); + sliceSettingsLayout->addRow("Explode distance", m_sliceExplodedFactor); + auto sliceSettings = new QGroupBox("Selected slice", this); + sliceSettings->setLayout(sliceSettingsLayout); + sliceSettings->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); + + connect(m_sliceName, &QLineEdit::textChanged, this, &PccWidget::updateSliceSettings); + connect(m_sliceValue, + static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), + this, &PccWidget::updateSliceSettings); + connect(m_pen, &QPushButton::clicked, m_penTool, &PccPenTool::show); + connect(m_penTool, &PccPenTool::changed, this, &PccWidget::updateSliceSettings); + connect(m_brush, &QPushButton::clicked, m_brushTool, &PccBrushTool::show); + connect(m_brushTool, &PccBrushTool::changed, this, &PccWidget::updateSliceSettings); + connect(m_font, &QPushButton::clicked, this, &PccWidget::showFontDialog); + connect(m_labelBrush, &QPushButton::clicked, m_labelBrushTool, &PccBrushTool::show); + connect(m_labelBrushTool, &PccBrushTool::changed, this, &PccWidget::updateSliceSettings); + connect(m_sliceLabelVisible, &QCheckBox::toggled, this, &PccWidget::updateSliceSettings); + connect(m_sliceLabelVisible, &QCheckBox::toggled, this, &PccWidget::updateSliceSettings); + connect(m_sliceLabelArmFactor, &QDoubleSpinBox::valueChanged, + this, &PccWidget::updateSliceSettings); + connect(m_sliceExploded, &QCheckBox::toggled, this, &PccWidget::updateSliceSettings); + connect(m_sliceExplodedFactor, &QDoubleSpinBox::valueChanged, + this, &PccWidget::updateSliceSettings); + connect(m_labelPosition, &QComboBox::currentIndexChanged, + this, &PccWidget::updateSliceSettings); + + // create chart view + m_chartView = new QChartView(chart); + + // create main layout + auto settingsLayout = new QVBoxLayout; + settingsLayout->addWidget(chartSettings); + settingsLayout->addWidget(seriesSettings); + settingsLayout->addWidget(sliceSettings); + + settingsContentWidget->setLayout(settingsLayout); + + m_settingsScrollBar->setWidget(settingsContentWidget); + m_settingsScrollBar->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_settingsScrollBar->setWidgetResizable(true); + m_settingsScrollBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + m_chartView->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + + m_baseLayout = new QGridLayout; + m_baseLayout->addWidget(m_settingsScrollBar, 0, 0); + m_baseLayout->addWidget(m_chartView, 1, 0); + setLayout(m_baseLayout); + + updateSerieSettings(); + updateChartSettings(); +} + +void PccWidget::updateChartSettings() +{ + auto theme = static_cast<QChart::ChartTheme>(m_themeComboBox->itemData( + m_themeComboBox->currentIndex()).toInt()); + m_chartView->chart()->setTheme(theme); + m_chartView->setRenderHint(QPainter::Antialiasing, m_aaCheckBox->isChecked()); + + if (m_animationsCheckBox->checkState() == Qt::Checked) + m_chartView->chart()->setAnimationOptions(QChart::AllAnimations); + else + m_chartView->chart()->setAnimationOptions(QChart::NoAnimation); + + if (m_legendCheckBox->checkState() == Qt::Checked) + m_chartView->chart()->legend()->show(); + else + m_chartView->chart()->legend()->hide(); +} + +void PccWidget::updateSerieSettings() +{ + m_series->setHorizontalPosition(m_hPosition->value()); + m_series->setVerticalPosition(m_vPosition->value()); + m_series->setPieSize(m_sizeFactor->value()); + m_holeSize->setMaximum(m_sizeFactor->value()); + m_series->setPieStartAngle(m_startAngle->value()); + m_series->setPieEndAngle(m_endAngle->value()); + m_series->setHoleSize(m_holeSize->value()); +} + +void PccWidget::updateSliceSettings() +{ + if (!m_slice) + return; + + m_slice->setLabel(m_sliceName->text()); + + m_slice->setValue(m_sliceValue->value()); + + m_slice->setPen(m_penTool->pen()); + m_slice->setBrush(m_brushTool->brush()); + + m_slice->setLabelBrush(m_labelBrushTool->brush()); + m_slice->setLabelVisible(m_sliceLabelVisible->isChecked()); + m_slice->setLabelArmLengthFactor(m_sliceLabelArmFactor->value()); + // We assume that label position index is in sync with the enum + m_slice->setLabelPosition((QPieSlice::LabelPosition)m_labelPosition->currentIndex()); + + m_slice->setExploded(m_sliceExploded->isChecked()); + m_slice->setExplodeDistanceFactor(m_sliceExplodedFactor->value()); +} + +void PccWidget::handleSliceClicked(QPieSlice *slice) +{ + m_slice = static_cast<PccCustomSlice *>(slice); + + // name + m_sliceName->blockSignals(true); + m_sliceName->setText(slice->label()); + m_sliceName->blockSignals(false); + + // value + m_sliceValue->blockSignals(true); + m_sliceValue->setValue(slice->value()); + m_sliceValue->blockSignals(false); + + // pen + m_pen->setText(PccPenTool::name(m_slice->pen())); + m_penTool->setPen(m_slice->pen()); + + // brush + m_brush->setText(m_slice->originalBrush().color().name()); + m_brushTool->setBrush(m_slice->originalBrush()); + + // label + m_labelBrush->setText(PccBrushTool::name(m_slice->labelBrush())); + m_labelBrushTool->setBrush(m_slice->labelBrush()); + m_font->setText(slice->labelFont().toString()); + m_sliceLabelVisible->blockSignals(true); + m_sliceLabelVisible->setChecked(slice->isLabelVisible()); + m_sliceLabelVisible->blockSignals(false); + m_sliceLabelArmFactor->blockSignals(true); + m_sliceLabelArmFactor->setValue(slice->labelArmLengthFactor()); + m_sliceLabelArmFactor->blockSignals(false); + m_labelPosition->blockSignals(true); + // We assume that label position index is in sync with the enum + m_labelPosition->setCurrentIndex(slice->labelPosition()); + m_labelPosition->blockSignals(false); + + // exploded + m_sliceExploded->blockSignals(true); + m_sliceExploded->setChecked(slice->isExploded()); + m_sliceExploded->blockSignals(false); + m_sliceExplodedFactor->blockSignals(true); + m_sliceExplodedFactor->setValue(slice->explodeDistanceFactor()); + m_sliceExplodedFactor->blockSignals(false); +} + +void PccWidget::showFontDialog() +{ + if (!m_slice) + return; + + QFontDialog dialog(m_slice->labelFont()); + dialog.show(); + dialog.exec(); + + m_slice->setLabelFont(dialog.currentFont()); + m_font->setText(dialog.currentFont().toString()); +} + +void PccWidget::appendSlice() +{ + *m_series << new PccCustomSlice("Slice " + QString::number(m_series->count() + 1), 10.0); +} + +void PccWidget::insertSlice() +{ + if (!m_slice) + return; + + int i = m_series->slices().indexOf(m_slice); + + m_series->insert(i, new PccCustomSlice("Slice " + QString::number(m_series->count() + 1), 10.0)); +} + +void PccWidget::removeSlice() +{ + if (!m_slice) + return; + + m_sliceName->setText("<click a slice>"); + + m_series->remove(m_slice); + m_slice = 0; +} + +void PccWidget::resizeEvent(QResizeEvent *) +{ + if (width() == 0 || height() == 0) + return; + + const double aspectRatio = double(width()) / double(height()); + + if ((aspectRatio < 1.0) && (m_oldAspectRatio > 1.0)) { + m_baseLayout->removeWidget(m_chartView); + m_baseLayout->removeWidget(m_settingsScrollBar); + + m_baseLayout->addWidget(m_chartView, 0, 0); + m_baseLayout->addWidget(m_settingsScrollBar, 1, 0); + + m_oldAspectRatio = aspectRatio; + } else if ((aspectRatio > 1.0) && (m_oldAspectRatio < 1.0)) { + m_baseLayout->removeWidget(m_chartView); + m_baseLayout->removeWidget(m_settingsScrollBar); + + m_baseLayout->addWidget(m_chartView, 0, 0); + m_baseLayout->addWidget(m_settingsScrollBar, 0, 1); + + m_oldAspectRatio = aspectRatio; + } +} diff --git a/examples/charts/chartsgallery/pccwidget.h b/examples/charts/chartsgallery/pccwidget.h new file mode 100644 index 00000000..3b0e617c --- /dev/null +++ b/examples/charts/chartsgallery/pccwidget.h @@ -0,0 +1,80 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PCCWIDGET_H +#define PCCWIDGET_H + +#include "contentwidget.h" + +#include <QGridLayout> +#include <QScrollArea> + +QT_FORWARD_DECLARE_CLASS(QChartView); +QT_FORWARD_DECLARE_CLASS(QCheckBox); +QT_FORWARD_DECLARE_CLASS(QComboBox); +QT_FORWARD_DECLARE_CLASS(QDoubleSpinBox); +QT_FORWARD_DECLARE_CLASS(QLineEdit); +QT_FORWARD_DECLARE_CLASS(QPushButton); +QT_FORWARD_DECLARE_CLASS(QPieSeries); +QT_FORWARD_DECLARE_CLASS(QPieSlice); + +class PccPenTool; +class PccBrushTool; +class PccCustomSlice; + +class PccWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit PccWidget(QWidget *parent = nullptr); + +public slots: + void updateChartSettings(); + void updateSerieSettings(); + void updateSliceSettings(); + void handleSliceClicked(QPieSlice *slice); + void showFontDialog(); + void appendSlice(); + void insertSlice(); + void removeSlice(); + +private: + void resizeEvent(QResizeEvent *e); + + QComboBox *m_themeComboBox = nullptr; + QCheckBox *m_aaCheckBox = nullptr; + QCheckBox *m_animationsCheckBox = nullptr; + QCheckBox *m_legendCheckBox = nullptr; + + QChartView *m_chartView = nullptr; + QPieSeries *m_series = nullptr; + PccCustomSlice *m_slice = nullptr; + + QDoubleSpinBox *m_hPosition = nullptr; + QDoubleSpinBox *m_vPosition = nullptr; + QDoubleSpinBox *m_sizeFactor = nullptr; + QDoubleSpinBox *m_startAngle = nullptr; + QDoubleSpinBox *m_endAngle = nullptr; + QDoubleSpinBox *m_holeSize = nullptr; + + QLineEdit *m_sliceName = nullptr; + QDoubleSpinBox *m_sliceValue = nullptr; + QCheckBox *m_sliceLabelVisible = nullptr; + QDoubleSpinBox *m_sliceLabelArmFactor = nullptr; + QCheckBox *m_sliceExploded = nullptr; + QDoubleSpinBox *m_sliceExplodedFactor = nullptr; + QPushButton *m_brush = nullptr; + PccBrushTool *m_brushTool = nullptr; + QPushButton *m_pen = nullptr; + PccPenTool *m_penTool = nullptr; + QPushButton *m_font = nullptr; + QPushButton *m_labelBrush = nullptr; + QComboBox *m_labelPosition = nullptr; + PccBrushTool *m_labelBrushTool = nullptr; + QGridLayout *m_baseLayout = nullptr; + QScrollArea *m_settingsScrollBar = nullptr; + + double m_oldAspectRatio = 0.; +}; + +#endif diff --git a/examples/charts/chartsgallery/piedrilldownchart.cpp b/examples/charts/chartsgallery/piedrilldownchart.cpp new file mode 100644 index 00000000..7d60f727 --- /dev/null +++ b/examples/charts/chartsgallery/piedrilldownchart.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "piedrilldownchart.h" +#include "piedrilldownslice.h" + +PieDrilldownChart::PieDrilldownChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) + : QChart(QChart::ChartTypeCartesian, parent, wFlags) +{ +} + +void PieDrilldownChart::changeSeries(QAbstractSeries *series) +{ + if (m_currentSeries) + removeSeries(m_currentSeries); + m_currentSeries = series; + addSeries(series); + setTitle(series->name()); +} + +void PieDrilldownChart::handleSliceClicked(QPieSlice *slice) +{ + auto *drilldownSlice = static_cast<PieDrilldownSlice *>(slice); + changeSeries(drilldownSlice->drilldownSeries()); +} diff --git a/examples/charts/chartsgallery/piedrilldownchart.h b/examples/charts/chartsgallery/piedrilldownchart.h new file mode 100644 index 00000000..66c54f9b --- /dev/null +++ b/examples/charts/chartsgallery/piedrilldownchart.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PIEDRILLDOWNCHART_H +#define PIEDRILLDOWNCHART_H + +#include <QChart> + +QT_FORWARD_DECLARE_CLASS(QAbstractSeries); +QT_FORWARD_DECLARE_CLASS(QPieSlice); + +class PieDrilldownChart : public QChart +{ + Q_OBJECT +public: + explicit PieDrilldownChart(QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = {}); + void changeSeries(QAbstractSeries *series); + +public slots: + void handleSliceClicked(QPieSlice *slice); + +private: + QAbstractSeries *m_currentSeries = nullptr; +}; + +#endif diff --git a/examples/charts/chartsgallery/piedrilldownslice.cpp b/examples/charts/chartsgallery/piedrilldownslice.cpp new file mode 100644 index 00000000..a8612cde --- /dev/null +++ b/examples/charts/chartsgallery/piedrilldownslice.cpp @@ -0,0 +1,33 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "piedrilldownslice.h" + +PieDrilldownSlice::PieDrilldownSlice(qreal value, const QString &prefix, QAbstractSeries *drilldownSeries) + : m_drilldownSeries(drilldownSeries), + m_prefix(prefix) +{ + setValue(value); + updateLabel(); + setLabelFont(QFont("Arial", 8)); + connect(this, &PieDrilldownSlice::percentageChanged, this, &PieDrilldownSlice::updateLabel); + connect(this, &PieDrilldownSlice::hovered, this, &PieDrilldownSlice::showHighlight); +} + +QAbstractSeries *PieDrilldownSlice::drilldownSeries() const +{ + return m_drilldownSeries; +} + +void PieDrilldownSlice::updateLabel() +{ + setLabel(QStringLiteral("%1 $%2, %3%").arg(m_prefix, + QString::number(value()), + QString::number(percentage() * 100, 'f', 1))); +} + +void PieDrilldownSlice::showHighlight(bool show) +{ + setLabelVisible(show); + setExploded(show); +} diff --git a/examples/charts/chartsgallery/piedrilldownslice.h b/examples/charts/chartsgallery/piedrilldownslice.h new file mode 100644 index 00000000..9579c42e --- /dev/null +++ b/examples/charts/chartsgallery/piedrilldownslice.h @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PIEDRILLDOWNSLICE_H +#define PIEDRILLDOWNSLICE_H + +#include <QPieSlice> + +QT_FORWARD_DECLARE_CLASS(QAbstractSeries); + +class PieDrilldownSlice : public QPieSlice +{ + Q_OBJECT +public: + PieDrilldownSlice(qreal value, const QString &prefix, QAbstractSeries *drilldownSeries); + QAbstractSeries *drilldownSeries() const; + +public slots: + void updateLabel(); + void showHighlight(bool show); + +private: + QAbstractSeries *m_drilldownSeries = nullptr; + QString m_prefix; +}; + +#endif diff --git a/examples/charts/chartsgallery/piedrilldownwidget.cpp b/examples/charts/chartsgallery/piedrilldownwidget.cpp new file mode 100644 index 00000000..59555f72 --- /dev/null +++ b/examples/charts/chartsgallery/piedrilldownwidget.cpp @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "piedrilldownchart.h" +#include "piedrilldownslice.h" +#include "piedrilldownwidget.h" + +#include <QChart> +#include <QLegend> +#include <QPieSeries> +#include <QRandomGenerator> + +PieDrilldownWidget::PieDrilldownWidget(QWidget *parent) + : ContentWidget(parent) +{ + auto *chart = new PieDrilldownChart; + chart->setTheme(QChart::ChartThemeLight); + chart->setAnimationOptions(QChart::AllAnimations); + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignRight); + + auto yearSeries = new QPieSeries(this); + yearSeries->setName("Sales by year - All (Click on slice to drill down)"); + + const QStringList months = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + const QStringList names = { + "Jane", "John", "Axel", "Mary", "Susan", "Bob" + }; + + for (const QString &name : names) { + auto series = new QPieSeries(this); + series->setName("Sales by month - " + name); + + for (const QString &month : months) + *series << new PieDrilldownSlice(QRandomGenerator::global()->bounded(1000), month, yearSeries); + + QObject::connect(series, &QPieSeries::clicked, chart, &PieDrilldownChart::handleSliceClicked); + + *yearSeries << new PieDrilldownSlice(series->sum(), name, series); + } + + QObject::connect(yearSeries, &QPieSeries::clicked, chart, &PieDrilldownChart::handleSliceClicked); + + chart->changeSeries(yearSeries); + + createDefaultChartView(chart); +} diff --git a/examples/charts/chartsgallery/piedrilldownwidget.h b/examples/charts/chartsgallery/piedrilldownwidget.h new file mode 100644 index 00000000..cc2b16f1 --- /dev/null +++ b/examples/charts/chartsgallery/piedrilldownwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PIEDRILLDOWNWIDGET_H +#define PIEDRILLDOWNWIDGET_H + +#include "contentwidget.h" + +class PieDrilldownWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit PieDrilldownWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/piewidget.cpp b/examples/charts/chartsgallery/piewidget.cpp new file mode 100644 index 00000000..feb67afe --- /dev/null +++ b/examples/charts/chartsgallery/piewidget.cpp @@ -0,0 +1,40 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "piewidget.h" + +#include <QChart> +#include <QPieSeries> +#include <QPieSlice> + +PieWidget::PieWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto series = new QPieSeries; + series->append("Jane", 1); + series->append("Joe", 2); + series->append("Andy", 3); + series->append("Barbara", 4); + series->append("Axel", 5); + //![1] + + //![2] + QPieSlice *slice = series->slices().at(1); + slice->setExploded(); + slice->setLabelVisible(); + slice->setPen(QPen(Qt::darkGreen, 2)); + slice->setBrush(Qt::green); + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Simple Pie Chart"); + chart->legend()->hide(); + //![3] + + //![4] + createDefaultChartView(chart); + //![4] +} diff --git a/examples/charts/chartsgallery/piewidget.h b/examples/charts/chartsgallery/piewidget.h new file mode 100644 index 00000000..c57200e9 --- /dev/null +++ b/examples/charts/chartsgallery/piewidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PIEWIDGET_H +#define PIEWIDGET_H + +#include "contentwidget.h" + +class PieWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit PieWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/pointconfigurationwidget.cpp b/examples/charts/chartsgallery/pointconfigurationwidget.cpp new file mode 100644 index 00000000..5c2970c3 --- /dev/null +++ b/examples/charts/chartsgallery/pointconfigurationwidget.cpp @@ -0,0 +1,160 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "pointconfigurationwidget.h" + +#include <QChart> +#include <QChartView> +#include <QCheckBox> +#include <QColor> +#include <QComboBox> +#include <QGraphicsLayout> +#include <QGridLayout> +#include <QHBoxLayout> +#include <QIcon> +#include <QLabel> +#include <QLineEdit> +#include <QLineSeries> + +//![1] +PointConfigurationWidget::PointConfigurationWidget(QWidget *parent) + : ContentWidget(parent) +{ +//![1] + //![2] + m_series = new QLineSeries; + m_series->setPointsVisible(true); + m_series->append({QPointF(0, 7), QPointF(2, 4), + QPointF(3, 5), QPointF(7, 4), + QPointF(10, 5), QPointF(11, 1), + QPointF(13, 3), QPointF(17, 6), + QPointF(18, 3), QPointF(20, 2)}); + //![2] + + //![3] + auto selectedPointIndexLabel = new QLabel(tr("Selected Point: "), this); + m_selectedPointIndexLineEdit = new QLineEdit(this); + m_selectedPointIndexLineEdit->setReadOnly(true); + + auto colorLabel = new QLabel(tr("Color: "), this); + m_colorCombobox = new QComboBox(this); + QStringList colorStrings = {"red", "orange", "yellow", "green", "blue", + "indigo", "violet", "black"}; + QStringList trColorStrings = {tr("red"), tr("orange"), tr("yellow"), + tr("green"), tr("blue"), tr("indigo"), + tr("violet"), tr("black")}; + for (int i = 0; i < colorStrings.size(); i++) + m_colorCombobox->addItem(QIcon(), trColorStrings[i], QColor(colorStrings[i])); + + auto sizeLabel = new QLabel(tr("Size: "), this); + m_sizeCombobox = new QComboBox(this); + for (auto size : { 2, 3, 4, 6, 8, 10, 12, 15 }) + m_sizeCombobox->addItem(QIcon(), QString::number(size), size); + + auto labelVisibilityLabel = new QLabel(tr("Label Visibility: "), this); + m_labelVisibilityCheckbox = new QCheckBox(this); + + auto customLabelLabel = new QLabel(tr("Custom Label: "), this); + m_customLabelLineEdit = new QLineEdit(this); + //![3] + + //![4] + QObject::connect(m_series, &QXYSeries::clicked, m_series, [&](const QPointF &point) { + int index = m_series->points().indexOf(point.toPoint()); + if (index != -1) { + m_series->deselectAllPoints(); + m_series->selectPoint(index); + m_selectedPointIndex = index; + m_selectedPointConfig = m_series->pointConfiguration(index); + const QPointF selectedPoint(m_series->at(index)); + m_selectedPointIndexLineEdit->setText("(" + QString::number(selectedPoint.x()) + ", " + + QString::number(selectedPoint.y()) + ")"); + PointConfigurations config = m_series->pointConfiguration(index); + + QVariant colorVar = config[QXYSeries::PointConfiguration::Color]; + QColor color = colorVar.isValid() ? colorVar.value<QColor>() : m_series->color(); + if (m_colorCombobox->findData(color) < 0) + m_colorCombobox->addItem(color.name(), color); + m_colorCombobox->setCurrentIndex(m_colorCombobox->findData(color)); + + QVariant sizeVar = config[QXYSeries::PointConfiguration::Size]; + qreal size = sizeVar.isValid() ? sizeVar.toReal() : m_series->markerSize(); + if (m_sizeCombobox->findData(size) < 0) + m_sizeCombobox->addItem(QString::number(size), size); + m_sizeCombobox->setCurrentIndex(m_sizeCombobox->findData(size)); + + QVariant labelVisibilityVar = config[QXYSeries::PointConfiguration::LabelVisibility]; + bool labelVisibility = labelVisibilityVar.isValid() ? labelVisibilityVar.toBool() : + m_series->pointLabelsVisible(); + m_labelVisibilityCheckbox->setChecked(labelVisibility); + + QVariant customLabelVar = config[QXYSeries::PointConfiguration::LabelFormat]; + QString customLabel = customLabelVar.isValid() ? customLabelVar.toString() : ""; + m_customLabelLineEdit->setText(customLabel); + } + }); + //![4] + + //![5] + QObject::connect(m_colorCombobox, &QComboBox::activated, m_series, [&](const int) { + m_selectedPointConfig[QXYSeries::PointConfiguration::Color] = m_colorCombobox->currentData(); + m_series->setPointConfiguration(m_selectedPointIndex, m_selectedPointConfig); + }); + QObject::connect(m_sizeCombobox, &QComboBox::activated, m_series, [&](const int) { + m_selectedPointConfig[QXYSeries::PointConfiguration::Size] = m_sizeCombobox->currentData(); + m_series->setPointConfiguration(m_selectedPointIndex, m_selectedPointConfig); + }); + QObject::connect(m_labelVisibilityCheckbox, &QAbstractButton::clicked, m_series, [&](const bool checked) { + m_selectedPointConfig[QXYSeries::PointConfiguration::LabelVisibility] = checked; + m_series->setPointConfiguration(m_selectedPointIndex, m_selectedPointConfig); + }); + QObject::connect(m_customLabelLineEdit, &QLineEdit::editingFinished, m_series, [&]() { + m_selectedPointConfig[QXYSeries::PointConfiguration::LabelFormat] = m_customLabelLineEdit->text(); + m_series->setPointConfiguration(m_selectedPointIndex, m_selectedPointConfig); + }); + //![5] + + //![6] + auto chart = new QChart; + chart->addSeries(m_series); + chart->createDefaultAxes(); + chart->setTitle("Select points with mouse click"); + chart->layout()->setContentsMargins(0, 0, 0, 0); + chart->legend()->setVisible(false); + + m_selectInitialPointConnection = QObject::connect(chart, &QChart::geometryChanged, chart, [&]() { + m_series->selectPoint(4); + m_series->clicked(m_series->at(m_series->selectedPoints()[0])); + disconnect(m_selectInitialPointConnection); + }); + + auto chartView = new QChartView(chart, this); + chartView->setRenderHint(QPainter::Antialiasing); + + auto controlWidget = new QWidget(this); + auto controlLayout = new QGridLayout(controlWidget); + controlLayout->setColumnStretch(1, 1); + + controlLayout->addWidget(selectedPointIndexLabel, 0, 0); + controlLayout->addWidget(m_selectedPointIndexLineEdit, 0, 1); + + controlLayout->addWidget(colorLabel, 1, 0); + controlLayout->addWidget(m_colorCombobox, 1, 1); + + controlLayout->addWidget(sizeLabel, 2, 0); + controlLayout->addWidget(m_sizeCombobox, 2, 1); + + controlLayout->addWidget(labelVisibilityLabel, 3, 0); + controlLayout->addWidget(m_labelVisibilityCheckbox, 3, 1, 1, 2); + + controlLayout->addWidget(customLabelLabel, 4, 0); + controlLayout->addWidget(m_customLabelLineEdit, 4, 1); + + auto mainLayout = new QHBoxLayout(this); + mainLayout->addWidget(chartView); + mainLayout->setStretch(0, 1); + mainLayout->addWidget(controlWidget); + //![6] +//![7] +} +//![7] diff --git a/examples/charts/chartsgallery/pointconfigurationwidget.h b/examples/charts/chartsgallery/pointconfigurationwidget.h new file mode 100644 index 00000000..92aba69d --- /dev/null +++ b/examples/charts/chartsgallery/pointconfigurationwidget.h @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef POINTCONFIGURATIONWIDGET_H +#define POINTCONFIGURATIONWIDGET_H + +#include "contentwidget.h" + +#include <QXYSeries> + +QT_FORWARD_DECLARE_CLASS(QLineEdit) +QT_FORWARD_DECLARE_CLASS(QComboBox) +QT_FORWARD_DECLARE_CLASS(QCheckBox) + +typedef QHash<QXYSeries::PointConfiguration, QVariant> PointConfigurations; + +class PointConfigurationWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit PointConfigurationWidget(QWidget *parent = nullptr); + +private: + QXYSeries *m_series = nullptr; + + QMetaObject::Connection m_selectInitialPointConnection; + int m_selectedPointIndex = -1; + PointConfigurations m_selectedPointConfig; + + QLineEdit *m_selectedPointIndexLineEdit = nullptr; + QComboBox *m_colorCombobox = nullptr; + QComboBox *m_sizeCombobox = nullptr; + QCheckBox *m_labelVisibilityCheckbox = nullptr; + QLineEdit *m_customLabelLineEdit = nullptr; +}; + +#endif diff --git a/examples/charts/chartsgallery/polarchartview.cpp b/examples/charts/chartsgallery/polarchartview.cpp new file mode 100644 index 00000000..751ae45f --- /dev/null +++ b/examples/charts/chartsgallery/polarchartview.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "polarchartview.h" + +#include <QAbstractAxis> +#include <QMouseEvent> +#include <QPolarChart> +#include <QValueAxis> + +PolarChartView::PolarChartView(QWidget *parent) + : QChartView(parent) +{ +} + +//![1] +void PolarChartView::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Plus: + chart()->zoomIn(); + break; + case Qt::Key_Minus: + chart()->zoomOut(); + break; + case Qt::Key_Left: + chart()->scroll(-1.0, 0); + break; + case Qt::Key_Right: + chart()->scroll(1.0, 0); + break; + case Qt::Key_Up: + chart()->scroll(0, 1.0); + break; + case Qt::Key_Down: + chart()->scroll(0, -1.0); + break; + case Qt::Key_Space: + switchChartType(); + break; + default: + QGraphicsView::keyPressEvent(event); + break; + } +} +//![1] + +//![2] +void PolarChartView::switchChartType() +{ + QChart *newChart; + QChart *oldChart = chart(); + + if (oldChart->chartType() == QChart::ChartTypeCartesian) + newChart = new QPolarChart; + else + newChart = new QChart; + + // Move series and axes from old chart to new one + const QList<QAbstractSeries *> seriesList = oldChart->series(); + const QList<QAbstractAxis *> axisList = oldChart->axes(); + QList<QPair<qreal, qreal> > axisRanges; + + for (QAbstractAxis *axis : axisList) { + auto valueAxis = static_cast<QValueAxis *>(axis); + axisRanges.append(QPair<qreal, qreal>(valueAxis->min(), valueAxis->max())); + } + + for (QAbstractSeries *series : seriesList) + oldChart->removeSeries(series); + + for (QAbstractAxis *axis : axisList) { + oldChart->removeAxis(axis); + newChart->addAxis(axis, axis->alignment()); + } + + for (QAbstractSeries *series : seriesList) { + newChart->addSeries(series); + for (QAbstractAxis *axis : axisList) + series->attachAxis(axis); + } + + int count = 0; + for (QAbstractAxis *axis : axisList) { + axis->setRange(axisRanges[count].first, axisRanges[count].second); + count++; + } + + newChart->setTitle(oldChart->title()); + setChart(newChart); + delete oldChart; +} +//![2] diff --git a/examples/charts/chartsgallery/polarchartview.h b/examples/charts/chartsgallery/polarchartview.h new file mode 100644 index 00000000..ee4e8fb9 --- /dev/null +++ b/examples/charts/chartsgallery/polarchartview.h @@ -0,0 +1,21 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef POLARCHARTVIEW_H +#define POLARCHARTVIEW_H + +#include <QChartView> + +class PolarChartView : public QChartView +{ +public: + PolarChartView(QWidget *parent = nullptr); + +protected: + void keyPressEvent(QKeyEvent *event); + +private: + void switchChartType(); +}; + +#endif diff --git a/examples/charts/chartsgallery/polarchartwidget.cpp b/examples/charts/chartsgallery/polarchartwidget.cpp new file mode 100644 index 00000000..d0550c7c --- /dev/null +++ b/examples/charts/chartsgallery/polarchartwidget.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "polarchartview.h" +#include "polarchartwidget.h" + +#include <QAreaSeries> +#include <QChart> +#include <QLineSeries> +#include <QPolarChart> +#include <QScatterSeries> +#include <QSplineSeries> +#include <QValueAxis> + +PolarChartWidget::PolarChartWidget(QWidget *parent) + : ContentWidget(parent) +{ + const qreal angularMin = -100; + const qreal angularMax = 100; + + const qreal radialMin = -100; + const qreal radialMax = 100; + + auto series1 = new QScatterSeries; + series1->setName("scatter"); + for (int i = angularMin; i <= angularMax; i += 10) + series1->append(i, (i / radialMax) * radialMax + 8.0); + + auto series2 = new QSplineSeries; + series2->setName("spline"); + for (int i = angularMin; i <= angularMax; i += 10) + series2->append(i, (i / radialMax) * radialMax); + + auto series3 = new QLineSeries; + series3->setName("star outer"); + qreal ad = (angularMax - angularMin) / 8; + qreal rd = (radialMax - radialMin) / 3 * 1.3; + series3->append(angularMin, radialMax); + series3->append(angularMin + ad * 1, radialMin + rd); + series3->append(angularMin + ad * 2, radialMax); + series3->append(angularMin + ad * 3, radialMin + rd); + series3->append(angularMin + ad * 4, radialMax); + series3->append(angularMin + ad * 5, radialMin + rd); + series3->append(angularMin + ad * 6, radialMax); + series3->append(angularMin + ad * 7, radialMin + rd); + series3->append(angularMin + ad * 8, radialMax); + + auto series4 = new QLineSeries; + series4->setName("star inner"); + ad = (angularMax - angularMin) / 8; + rd = (radialMax - radialMin) / 3; + series4->append(angularMin, radialMax); + series4->append(angularMin + ad * 1, radialMin + rd); + series4->append(angularMin + ad * 2, radialMax); + series4->append(angularMin + ad * 3, radialMin + rd); + series4->append(angularMin + ad * 4, radialMax); + series4->append(angularMin + ad * 5, radialMin + rd); + series4->append(angularMin + ad * 6, radialMax); + series4->append(angularMin + ad * 7, radialMin + rd); + series4->append(angularMin + ad * 8, radialMax); + + auto series5 = new QAreaSeries; + series5->setName("star area"); + series5->setUpperSeries(series3); + series5->setLowerSeries(series4); + series5->setOpacity(0.5); + + //![1] + auto chart = new QPolarChart; + //![1] + chart->addSeries(series1); + chart->addSeries(series2); + chart->addSeries(series3); + chart->addSeries(series4); + chart->addSeries(series5); + + chart->setTitle("Use arrow keys to scroll, +/- to zoom, and space to switch chart type."); + + //![2] + auto angularAxis = new QValueAxis; + angularAxis->setTickCount(9); // First and last ticks are co-located on 0/360 angle. + angularAxis->setLabelFormat("%.1f"); + angularAxis->setShadesVisible(true); + angularAxis->setShadesBrush(QBrush(QColor(249, 249, 255))); + chart->addAxis(angularAxis, QPolarChart::PolarOrientationAngular); + + auto radialAxis = new QValueAxis; + radialAxis->setTickCount(9); + radialAxis->setLabelFormat("%d"); + chart->addAxis(radialAxis, QPolarChart::PolarOrientationRadial); + //![2] + + series1->attachAxis(radialAxis); + series1->attachAxis(angularAxis); + series2->attachAxis(radialAxis); + series2->attachAxis(angularAxis); + series3->attachAxis(radialAxis); + series3->attachAxis(angularAxis); + series4->attachAxis(radialAxis); + series4->attachAxis(angularAxis); + series5->attachAxis(radialAxis); + series5->attachAxis(angularAxis); + + radialAxis->setRange(radialMin, radialMax); + angularAxis->setRange(angularMin, angularMax); + + auto chartView = new PolarChartView(this); + chartView->setChart(chart); + setDefaultChartView(chartView); +} diff --git a/examples/charts/chartsgallery/polarchartwidget.h b/examples/charts/chartsgallery/polarchartwidget.h new file mode 100644 index 00000000..8d151059 --- /dev/null +++ b/examples/charts/chartsgallery/polarchartwidget.h @@ -0,0 +1,18 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef POLARCHARTWIDGET_H +#define POLARCHARTWIDGET_H + +#include "contentwidget.h" + +class PolarChartView; + +class PolarChartWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit PolarChartWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/scatterinteractionswidget.cpp b/examples/charts/chartsgallery/scatterinteractionswidget.cpp new file mode 100644 index 00000000..b518c6da --- /dev/null +++ b/examples/charts/chartsgallery/scatterinteractionswidget.cpp @@ -0,0 +1,58 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "scatterinteractionswidget.h" + +#include <QChart> +#include <QtMath> +#include <QScatterSeries> + +ScatterInteractionsWidget::ScatterInteractionsWidget(QWidget *parent) + : ContentWidget(parent) +{ + m_scatter = new QScatterSeries; + m_scatter->setName("scatter1"); + for (qreal x(0.5); x <= 4.0; x += 0.5) { + for (qreal y(0.5); y <= 4.0; y += 0.5) + *m_scatter << QPointF(x, y); + } + m_scatter2 = new QScatterSeries; + m_scatter2->setName("scatter2"); + + auto *chart = new QChart; + chart->setTitle("Click to interact with scatter points"); + chart->addSeries(m_scatter2); + chart->addSeries(m_scatter); + chart->createDefaultAxes(); + chart->axes(Qt::Horizontal).first()->setRange(0, 4.5); + chart->axes(Qt::Vertical).first()->setRange(0, 4.5); + + connect(m_scatter, &QScatterSeries::clicked, + this, &ScatterInteractionsWidget::handleClickedPoint); + + createDefaultChartView(chart); +} + +void ScatterInteractionsWidget::handleClickedPoint(const QPointF &point) +{ + QPointF clickedPoint = point; + + // Find the closest point from series 1 + QPointF closest(INT_MAX, INT_MAX); + qreal distance(INT_MAX); + const auto points = m_scatter->points(); + for (const QPointF ¤tPoint : points) { + qreal currentDistance = qSqrt((currentPoint.x() - clickedPoint.x()) + * (currentPoint.x() - clickedPoint.x()) + + (currentPoint.y() - clickedPoint.y()) + * (currentPoint.y() - clickedPoint.y())); + if (currentDistance < distance) { + distance = currentDistance; + closest = currentPoint; + } + } + + // Remove the closest point from series 1 and append it to series 2 + m_scatter->remove(closest); + m_scatter2->append(closest); +} diff --git a/examples/charts/chartsgallery/scatterinteractionswidget.h b/examples/charts/chartsgallery/scatterinteractionswidget.h new file mode 100644 index 00000000..ba757c53 --- /dev/null +++ b/examples/charts/chartsgallery/scatterinteractionswidget.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef SCATTERINTERACTIONSWIDGET_H +#define SCATTERINTERACTIONSWIDGET_H + +#include "contentwidget.h" + +QT_FORWARD_DECLARE_CLASS(QScatterSeries) + +class ScatterInteractionsWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit ScatterInteractionsWidget(QWidget *parent = nullptr); + +private slots: + void handleClickedPoint(const QPointF &point); + +private: + QScatterSeries *m_scatter = nullptr; + QScatterSeries *m_scatter2 = nullptr; +}; + +#endif diff --git a/examples/charts/chartsgallery/scatterwidget.cpp b/examples/charts/chartsgallery/scatterwidget.cpp new file mode 100644 index 00000000..413a090b --- /dev/null +++ b/examples/charts/chartsgallery/scatterwidget.cpp @@ -0,0 +1,84 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "scatterwidget.h" + +#include <QChart> +#include <QLegendMarker> +#include <QImage> +#include <QPainter> +#include <QScatterSeries> +#include <QtMath> + +ScatterWidget::ScatterWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto series0 = new QScatterSeries; + series0->setName("scatter1"); + series0->setMarkerShape(QScatterSeries::MarkerShapeCircle); + series0->setMarkerSize(15.0); + + auto series1 = new QScatterSeries; + series1->setName("scatter2"); + series1->setMarkerShape(QScatterSeries::MarkerShapeRectangle); + series1->setMarkerSize(20.0); + + auto series2 = new QScatterSeries; + series2->setName("scatter3"); + series2->setMarkerShape(QScatterSeries::MarkerShapeRectangle); + series2->setMarkerSize(30.0); + //![1] + + //![2] + series0->append(0, 6); + series0->append(2, 4); + series0->append(3, 8); + series0->append(7, 4); + series0->append(10, 5); + + *series1 << QPointF(1, 1) << QPointF(3, 3) << QPointF(7, 6) << QPointF(8, 3) << QPointF(10, 2); + *series2 << QPointF(1, 5) << QPointF(4, 6) << QPointF(6, 3) << QPointF(9, 5); + //![2] + + //![3] + QPainterPath starPath; + starPath.moveTo(28, 15); + for (int i = 1; i < 5; ++i) { + starPath.lineTo(14 + 14 * qCos(0.8 * i * M_PI), + 15 + 14 * qSin(0.8 * i * M_PI)); + } + starPath.closeSubpath(); + + QImage star(30, 30, QImage::Format_ARGB32); + star.fill(Qt::transparent); + + QPainter painter(&star); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(QRgb(0xf6a625)); + painter.setBrush(painter.pen().color()); + painter.drawPath(starPath); + + series2->setBrush(star); + series2->setPen(QColor(Qt::transparent)); + //![3] + + //![4] + auto chart = new QChart; + chart->addSeries(series0); + chart->addSeries(series1); + chart->addSeries(series2); + + chart->setTitle("Simple Scatter Chart"); + chart->createDefaultAxes(); + chart->setDropShadowEnabled(false); + //![4] + + //![5] + chart->legend()->setMarkerShape(QLegend::MarkerShapeFromSeries); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/scatterwidget.h b/examples/charts/chartsgallery/scatterwidget.h new file mode 100644 index 00000000..6b836718 --- /dev/null +++ b/examples/charts/chartsgallery/scatterwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef SCATTERWIDGET_H +#define SCATTERWIDGET_H + +#include "contentwidget.h" + +class ScatterWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit ScatterWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/selectedbarwidget.cpp b/examples/charts/chartsgallery/selectedbarwidget.cpp new file mode 100644 index 00000000..d54d2201 --- /dev/null +++ b/examples/charts/chartsgallery/selectedbarwidget.cpp @@ -0,0 +1,147 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "selectedbarwidget.h" + +#include <QBarCategoryAxis> +#include <QBarSeries> +#include <QBarSet> +#include <QChart> +#include <QChartView> +#include <QGraphicsLayout> +#include <QHBoxLayout> +#include <QLabel> +#include <QValueAxis> + +SelectedBarWidget::SelectedBarWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + QBarSet *setChicken = createChickenSet(); + QBarSet *setPork = createPorkSet(); + QBarSet *setTurkey = createTurkeySet(); + QBarSet *setHam = createHamSet(); + qreal totalSum = setChicken->sum() + setPork->sum() + setTurkey->sum() + setHam->sum(); + QList<QBarSet *> setList = QList<QBarSet *>{setChicken, setPork, setTurkey, setHam}; + + auto series = new QBarSeries; + series->append(setList); + //![1] + + //![2] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle(tr("Meat Consumption (Click on bars to select them)")); + chart->setAnimationOptions(QChart::SeriesAnimations); + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + chart->layout()->setContentsMargins(0, 0, 0, 0); + //![2] + + //![3] + const auto barSets = series->barSets(); + for (QBarSet *barSet : barSets) + barSet->setSelectedColor(barSet->brush().color().darker()); + //![3] + + //![4] + QStringList categories = createYearCategories(); + auto axisX = new QBarCategoryAxis; + axisX->setCategories(categories); + chart->addAxis(axisX, Qt::AlignBottom); + series->attachAxis(axisX); + + auto axisY = new QValueAxis; + axisY->setRange(0, 20); + axisY->setTitleText(tr("Tons")); + axisY->setLabelsAngle(-90); + axisY->setTitleVisible(true); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisY); + //![4] + + //![5] + auto chartView = new QChartView(chart, this); + chartView->setRenderHint(QPainter::Antialiasing); + //![5] + + //![6] + auto labelWidget = new QWidget(this); + auto labelLayout = new QHBoxLayout(labelWidget); + labelLayout->setAlignment(Qt::AlignCenter); + + auto totalSumLabel = new QLabel(tr("Total sum: %1 T").arg(totalSum), this); + labelLayout->addWidget(totalSumLabel); + totalSumLabel->setContentsMargins(0, 0, 54, 0); + + auto selectedSumLabel = new QLabel(tr("Selected sum: 0 T"), this); + labelLayout->addWidget(selectedSumLabel); + + auto unselectedSumLabel = new QLabel(tr("Unselected sum: %1 T").arg(totalSum), this); + labelLayout->addWidget(unselectedSumLabel); + unselectedSumLabel->setContentsMargins(54, 0, 0, 0); + //![6] + + //![7] + QObject::connect(series, &QAbstractBarSeries::clicked, series, [=](int index, QBarSet *set) { + set->toggleSelection({index}); + qreal selectedSum = 0.; + for (int i = 0; i < setList.size(); ++i) { + auto selectedIndices = setList.at(i)->selectedBars(); + for (int k = 0; k < selectedIndices.size(); ++k) + selectedSum += setList.at(i)->at(selectedIndices.at(k)); + } + selectedSumLabel->setText(tr("Selected sum: %1 T").arg(selectedSum)); + // Because of rounding errors, selectedSum can result in being bigger than total sum + qreal unselectedSum = totalSum - selectedSum < 0 ? 0. : totalSum - selectedSum; + unselectedSumLabel->setText( + tr("Unselected sum: %1 T") + .arg(unselectedSum) + ); + }); + //![7] + + //![8] + auto mainLayout = new QVBoxLayout(this); + + mainLayout->addWidget(chartView); + mainLayout->addWidget(labelWidget); + //![8] +} + +QBarSet *SelectedBarWidget::createChickenSet() +{ + auto set = new QBarSet(tr("Chicken")); + set->append({15.0, 12.0, 8.0, 11.5, 13.7}); + return set; +} + +QBarSet *SelectedBarWidget::createPorkSet() +{ + auto set = new QBarSet(tr("Pork")); + set->append({9.0, 11.0, 9.0, 7, 12.2}); + return set; +} + +QBarSet *SelectedBarWidget::createTurkeySet() +{ + auto set = new QBarSet(tr("Turkey")); + set->append({5.0, 7.6, 9.3, 8, 8.1}); + return set; +} + +QBarSet *SelectedBarWidget::createHamSet() +{ + auto set = new QBarSet(tr("Ham")); + set->append({5.4, 7.1, 9.3, 12.3, 11.3}); + return set; +} + +QStringList SelectedBarWidget::createYearCategories() +{ + return QStringList{tr("2017"), + tr("2018"), + tr("2019"), + tr("2020"), + tr("2021")}; +} diff --git a/examples/charts/chartsgallery/selectedbarwidget.h b/examples/charts/chartsgallery/selectedbarwidget.h new file mode 100644 index 00000000..2e401dcd --- /dev/null +++ b/examples/charts/chartsgallery/selectedbarwidget.h @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef SELECTEDBARWIDGET_H +#define SELECTEDBARWIDGET_H + +#include "contentwidget.h" + +#include <QStringList> + +QT_FORWARD_DECLARE_CLASS(QBarSet) + +class SelectedBarWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit SelectedBarWidget(QWidget *parent = nullptr); + +private: + QBarSet *createChickenSet(); + QBarSet *createPorkSet(); + QBarSet *createTurkeySet(); + QBarSet *createHamSet(); + QStringList createYearCategories(); +}; + +#endif diff --git a/examples/charts/chartsgallery/splinewidget.cpp b/examples/charts/chartsgallery/splinewidget.cpp new file mode 100644 index 00000000..1bda42f7 --- /dev/null +++ b/examples/charts/chartsgallery/splinewidget.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "splinewidget.h" + +#include <QChart> +#include <QSplineSeries> + +SplineWidget::SplineWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto series = new QSplineSeries; + series->setName("Spline"); + //![1] + + //![2] + series->append(0, 6); + series->append(2, 4); + series->append(3, 8); + series->append(7, 4); + series->append(10, 5); + *series << QPointF(11, 1) << QPointF(13, 3) << QPointF(17, 6) << QPointF(18, 3) << QPointF(20, 2); + //![2] + + //![3] + auto chart = new QChart; + chart->legend()->hide(); + chart->addSeries(series); + chart->setTitle("Simple Spline Chart"); + chart->createDefaultAxes(); + chart->axes(Qt::Vertical).first()->setRange(0, 10); + //![3] + + //![4] + createDefaultChartView(chart); + //![4] +} diff --git a/examples/charts/chartsgallery/splinewidget.h b/examples/charts/chartsgallery/splinewidget.h new file mode 100644 index 00000000..5d9d20cc --- /dev/null +++ b/examples/charts/chartsgallery/splinewidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef SPLINEWIDGET_H +#define SPLINEWIDGET_H + +#include "contentwidget.h" + +class SplineWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit SplineWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/stackeddrilldownchart.cpp b/examples/charts/chartsgallery/stackeddrilldownchart.cpp new file mode 100644 index 00000000..89df4ec0 --- /dev/null +++ b/examples/charts/chartsgallery/stackeddrilldownchart.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "stackeddrilldownchart.h" + +#include <QBarCategoryAxis> +#include <QValueAxis> + +StackedDrilldownChart::StackedDrilldownChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) + : QChart(QChart::ChartTypeCartesian, parent, wFlags), + m_axisX(new QBarCategoryAxis), + m_axisY(new QValueAxis) +{ + addAxis(m_axisY, Qt::AlignLeft); + addAxis(m_axisX, Qt::AlignBottom); +} + +void StackedDrilldownChart::changeSeries(StackedDrilldownSeries *series) +{ + if (m_currentSeries) + removeSeries(m_currentSeries); + + m_currentSeries = series; + + // Reset axis + m_axisX->setCategories(m_currentSeries->categories()); + addSeries(series); + series->attachAxis(m_axisX); + series->attachAxis(m_axisY); + m_axisY->setRange(0,m_currentSeries->maxValue()); + setTitle(series->name()); +} + +void StackedDrilldownChart::handleClicked(int index, QBarSet *) +{ + auto series = static_cast<StackedDrilldownSeries *>(sender()); + changeSeries(series->drilldownSeries(index)); +} diff --git a/examples/charts/chartsgallery/stackeddrilldownchart.h b/examples/charts/chartsgallery/stackeddrilldownchart.h new file mode 100644 index 00000000..85f3a1a0 --- /dev/null +++ b/examples/charts/chartsgallery/stackeddrilldownchart.h @@ -0,0 +1,33 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef STACKEDDRILLDOWNCHART_H +#define STACKEDDRILLDOWNCHART_H + +#include "stackeddrilldownseries.h" + +#include <QChart> + +QT_FORWARD_DECLARE_CLASS(QBarCategoryAxis) +QT_FORWARD_DECLARE_CLASS(QValueAxis) + +//! [1] +class StackedDrilldownChart : public QChart +{ + Q_OBJECT +public: + explicit StackedDrilldownChart(QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = {}); + + void changeSeries(StackedDrilldownSeries *series); + +public slots: + void handleClicked(int index, QBarSet *); + +private: + StackedDrilldownSeries *m_currentSeries = nullptr; + QBarCategoryAxis *m_axisX = nullptr; + QValueAxis *m_axisY = nullptr; +}; +//! [1] + +#endif diff --git a/examples/charts/chartsgallery/stackeddrilldownseries.cpp b/examples/charts/chartsgallery/stackeddrilldownseries.cpp new file mode 100644 index 00000000..39b8ca4d --- /dev/null +++ b/examples/charts/chartsgallery/stackeddrilldownseries.cpp @@ -0,0 +1,33 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "stackeddrilldownseries.h" + +StackedDrilldownSeries::StackedDrilldownSeries(const QStringList &categories, + int maxValue, + QObject *parent) + : QStackedBarSeries(parent) + , m_maxValue(maxValue) +{ + m_categories = categories; +} + +void StackedDrilldownSeries::mapDrilldownSeries(int index, StackedDrilldownSeries *drilldownSeries) +{ + m_drilldownSeries[index] = drilldownSeries; +} + +StackedDrilldownSeries *StackedDrilldownSeries::drilldownSeries(int index) const +{ + return m_drilldownSeries[index]; +} + +QStringList StackedDrilldownSeries::categories() const +{ + return m_categories; +} + +int StackedDrilldownSeries::maxValue() const +{ + return m_maxValue; +} diff --git a/examples/charts/chartsgallery/stackeddrilldownseries.h b/examples/charts/chartsgallery/stackeddrilldownseries.h new file mode 100644 index 00000000..3161811e --- /dev/null +++ b/examples/charts/chartsgallery/stackeddrilldownseries.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef STACKEDDRILLDOWNSERIES_H +#define STACKEDDRILLDOWNSERIES_H + +#include <QMap> +#include <QStackedBarSeries> + +//! [1] +class StackedDrilldownSeries : public QStackedBarSeries +{ + Q_OBJECT +public: + StackedDrilldownSeries(const QStringList &categories, int maxValue, QObject *parent = nullptr); + + void mapDrilldownSeries(int index, StackedDrilldownSeries *drilldownSeries); + StackedDrilldownSeries *drilldownSeries(int index) const; + QStringList categories() const; + int maxValue() const; + +private: + QMap<int, StackedDrilldownSeries *> m_drilldownSeries; + QStringList m_categories; + int m_maxValue = 0; +}; +//! [1] + +#endif diff --git a/examples/charts/chartsgallery/stackeddrilldownwidget.cpp b/examples/charts/chartsgallery/stackeddrilldownwidget.cpp new file mode 100644 index 00000000..5882c0c7 --- /dev/null +++ b/examples/charts/chartsgallery/stackeddrilldownwidget.cpp @@ -0,0 +1,91 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "stackeddrilldownchart.h" +#include "stackeddrilldownseries.h" +#include "stackeddrilldownwidget.h" + +#include <QBarSet> +#include <QChart> +#include <QRandomGenerator> + +StackedDrilldownWidget::StackedDrilldownWidget(QWidget *parent) + : ContentWidget(parent) +{ + //! [1] + auto drilldownChart = new StackedDrilldownChart; + drilldownChart->setAnimationOptions(QChart::SeriesAnimations); + //! [1] + + //! [2] + // Define categories + const QStringList months = { + "May", "Jun", "Jul", "Aug", "Sep" + }; + const QStringList weeks = { + "week 1", "week 2", "week 3", "week 4" + }; + const QStringList plants = { + "Habanero", "Lemon Drop", "Starfish", "Aji Amarillo" + }; + //! [2] + + //! [3] + // Create drilldown structure + auto seasonSeries = new StackedDrilldownSeries(months, 320, drilldownChart); + seasonSeries->setName("Crop by month - Season (Click on bar to drill down)"); + + // Each month in season series has drilldown series for weekly data + for (int month = 0; month < months.count(); month++) { + // Create drilldown series for every week + auto weeklySeries = new StackedDrilldownSeries(weeks, 80, drilldownChart); + seasonSeries->mapDrilldownSeries(month, weeklySeries); + + // Drilling down from weekly data brings us back to season data. + for (int week = 0; week < weeks.count(); week++) { + weeklySeries->mapDrilldownSeries(week, seasonSeries); + weeklySeries->setName(QString("Crop by week - " + months.at(month))); + } + + // Use clicked signal to implement drilldown + QObject::connect(weeklySeries, &StackedDrilldownSeries::clicked, + drilldownChart, &StackedDrilldownChart::handleClicked); + } + + // Enable drilldown from season series using clicked signal + QObject::connect(seasonSeries, &StackedDrilldownSeries::clicked, + drilldownChart, &StackedDrilldownChart::handleClicked); + //! [3] + + //! [4] + // Fill monthly and weekly series with data + for (const QString &plant : plants) { + auto monthlyCrop = new QBarSet(plant); + for (int month = 0; month < months.count(); month++) { + auto weeklyCrop = new QBarSet(plant); + for (int week = 0; week < weeks.count(); week++) + *weeklyCrop << QRandomGenerator::global()->bounded(20); + // Get the drilldown series from season series and add crop to it. + seasonSeries->drilldownSeries(month)->append(weeklyCrop); + *monthlyCrop << weeklyCrop->sum(); + } + seasonSeries->append(monthlyCrop); + } + //! [4] + + //! [5] + // Show season series in initial view + drilldownChart->changeSeries(seasonSeries); + drilldownChart->setTitle(seasonSeries->name()); + //! [5] + + //! [6] + drilldownChart->axes(Qt::Horizontal).first()->setGridLineVisible(false); + drilldownChart->legend()->setVisible(true); + drilldownChart->legend()->setAlignment(Qt::AlignBottom); + //! [6] + + //! [7] + createDefaultChartView(drilldownChart); + //! [7] +} diff --git a/examples/charts/chartsgallery/stackeddrilldownwidget.h b/examples/charts/chartsgallery/stackeddrilldownwidget.h new file mode 100644 index 00000000..a8c8acff --- /dev/null +++ b/examples/charts/chartsgallery/stackeddrilldownwidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef STACKEDDRILLDOWNWIDGET_H +#define STACKEDDRILLDOWNWIDGET_H + +#include "contentwidget.h" + +class StackedDrilldownWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit StackedDrilldownWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/temperaturerecordswidget.cpp b/examples/charts/chartsgallery/temperaturerecordswidget.cpp new file mode 100644 index 00000000..36b748be --- /dev/null +++ b/examples/charts/chartsgallery/temperaturerecordswidget.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "temperaturerecordswidget.h" + +#include <QBarCategoryAxis> +#include <QBarSeries> +#include <QBarSet> +#include <QChart> +#include <QLegend> +#include <QStackedBarSeries> +#include <QValueAxis> + +TemperatureRecordsWidget::TemperatureRecordsWidget(QWidget *parent) + : ContentWidget(parent) +{ + //![1] + auto low = new QBarSet("Min"); + auto high = new QBarSet("Max"); + + *low << -52 << -50 << -45.3 << -37.0 << -25.6 << -8.0 + << -6.0 << -11.8 << -19.7 << -32.8 << -43.0 << -48.0; + *high << 11.9 << 12.8 << 18.5 << 26.5 << 32.0 << 34.8 + << 38.2 << 34.8 << 29.8 << 20.4 << 15.1 << 11.8; + //![1] + + //![2] + auto series = new QStackedBarSeries; + series->append(low); + series->append(high); + //![2] + + //![3] + auto chart = new QChart; + chart->addSeries(series); + chart->setTitle("Temperature records in Celcius"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //![3] + + //![4] + QStringList categories = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + auto axisX = new QBarCategoryAxis; + axisX->append(categories); + axisX->setTitleText("Month"); + chart->addAxis(axisX, Qt::AlignBottom); + auto axisY = new QValueAxis; + axisY->setRange(-52, 52); + axisY->setTitleText("Temperature [°C]"); + chart->addAxis(axisY, Qt::AlignLeft); + series->attachAxis(axisX); + series->attachAxis(axisY); + //![4] + + //![5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //![5] + + //![6] + createDefaultChartView(chart); + //![6] +} diff --git a/examples/charts/chartsgallery/temperaturerecordswidget.h b/examples/charts/chartsgallery/temperaturerecordswidget.h new file mode 100644 index 00000000..eb43717c --- /dev/null +++ b/examples/charts/chartsgallery/temperaturerecordswidget.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef TEMPERATURERECORDSWIDGET_H +#define TEMPERATURERECORDSWIDGET_H + +#include "contentwidget.h" + +class TemperatureRecordsWidget : public ContentWidget +{ + Q_OBJECT +public: + explicit TemperatureRecordsWidget(QWidget *parent = nullptr); +}; + +#endif diff --git a/examples/charts/chartsgallery/themewidget.cpp b/examples/charts/chartsgallery/themewidget.cpp new file mode 100644 index 00000000..31afc403 --- /dev/null +++ b/examples/charts/chartsgallery/themewidget.cpp @@ -0,0 +1,353 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "themewidget.h" +#include "ui_themewidget.h" + +#include <QAbstractBarSeries> +#include <QAreaSeries> +#include <QBarSet> +#include <QChartView> +#include <QCheckBox> +#include <QComboBox> +#include <QGridLayout> +#include <QLegend> +#include <QLineSeries> +#include <QPercentBarSeries> +#include <QPieSeries> +#include <QPieSlice> +#include <QRandomGenerator> +#include <QScatterSeries> +#include <QSplineSeries> +#include <QStackedBarSeries> +#include <QValueAxis> + +ThemeWidget::ThemeWidget(QWidget *parent) : + ContentWidget(parent), + m_listCount(3), + m_valueMax(10), + m_valueCount(7), + m_dataTable(generateRandomData(m_listCount, m_valueMax, m_valueCount)), + m_ui(new Ui_ThemeWidgetForm) +{ + m_ui->setupUi(this); + populateThemeBox(); + populateAnimationBox(); + populateLegendBox(); + + //create charts + + QChartView *chartView; + + chartView = new QChartView(createAreaChart(), this); + m_ui->gridLayout->addWidget(chartView, 1, 0); + m_charts << chartView; + + chartView = new QChartView(createPieChart(), this); + // Funny things happen if the pie slice labels do not fit the screen, so we ignore size policy + chartView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + m_ui->gridLayout->addWidget(chartView, 1, 1); + m_charts << chartView; + + //![5] + chartView = new QChartView(createLineChart(), this); + m_ui->gridLayout->addWidget(chartView, 1, 2); + //![5] + m_charts << chartView; + + chartView = new QChartView(createBarChart(m_valueCount), this); + m_ui->gridLayout->addWidget(chartView, 2, 0); + m_charts << chartView; + + chartView = new QChartView(createSplineChart(), this); + m_ui->gridLayout->addWidget(chartView, 2, 1); + m_charts << chartView; + + chartView = new QChartView(createScatterChart(), this); + m_ui->gridLayout->addWidget(chartView, 2, 2); + m_charts << chartView; + + // Set defaults + m_ui->antialiasCheckBox->setChecked(true); + + updateUI(); +} + +ThemeWidget::~ThemeWidget() +{ + delete m_ui; +} + +DataTable ThemeWidget::generateRandomData(int listCount, int valueMax, int valueCount) const +{ + DataTable dataTable; + + // generate random data + for (int i(0); i < listCount; i++) { + DataList dataList; + qreal yValue(0); + for (int j(0); j < valueCount; j++) { + yValue = yValue + QRandomGenerator::global()->bounded(valueMax / (qreal) valueCount); + QPointF value((j + QRandomGenerator::global()->generateDouble()) * ((qreal) m_valueMax / (qreal) valueCount), + yValue); + QString label = "Slice " + QString::number(i) + ":" + QString::number(j); + dataList << Data(value, label); + } + dataTable << dataList; + } + + return dataTable; +} + +void ThemeWidget::populateThemeBox() +{ + // add items to theme combobox + m_ui->themeComboBox->addItem("Light", QChart::ChartThemeLight); + m_ui->themeComboBox->addItem("Blue Cerulean", QChart::ChartThemeBlueCerulean); + m_ui->themeComboBox->addItem("Dark", QChart::ChartThemeDark); + m_ui->themeComboBox->addItem("Brown Sand", QChart::ChartThemeBrownSand); + m_ui->themeComboBox->addItem("Blue NCS", QChart::ChartThemeBlueNcs); + m_ui->themeComboBox->addItem("High Contrast", QChart::ChartThemeHighContrast); + m_ui->themeComboBox->addItem("Blue Icy", QChart::ChartThemeBlueIcy); + m_ui->themeComboBox->addItem("Qt", QChart::ChartThemeQt); +} + +void ThemeWidget::populateAnimationBox() +{ + // add items to animation combobox + m_ui->animatedComboBox->addItem("No Animations", QChart::NoAnimation); + m_ui->animatedComboBox->addItem("GridAxis Animations", QChart::GridAxisAnimations); + m_ui->animatedComboBox->addItem("Series Animations", QChart::SeriesAnimations); + m_ui->animatedComboBox->addItem("All Animations", QChart::AllAnimations); +} + +void ThemeWidget::populateLegendBox() +{ + // add items to legend combobox + m_ui->legendComboBox->addItem("No Legend ", 0); + m_ui->legendComboBox->addItem("Legend Top", Qt::AlignTop); + m_ui->legendComboBox->addItem("Legend Bottom", Qt::AlignBottom); + m_ui->legendComboBox->addItem("Legend Left", Qt::AlignLeft); + m_ui->legendComboBox->addItem("Legend Right", Qt::AlignRight); +} + +QChart *ThemeWidget::createAreaChart() const +{ + auto chart = new QChart; + chart->setTitle("Area Chart"); + + // The lower series initialized to zero values + QLineSeries *lowerSeries = nullptr; + QString name("Series "); + int nameIndex = 0; + for (int i(0); i < m_dataTable.count(); i++) { + auto upperSeries = new QLineSeries(chart); + for (int j(0); j < m_dataTable[i].count(); j++) { + Data data = m_dataTable[i].at(j); + if (lowerSeries) { + const auto &points = lowerSeries->points(); + upperSeries->append(QPointF(j, points[i].y() + data.first.y())); + } else { + upperSeries->append(QPointF(j, data.first.y())); + } + } + auto area = new QAreaSeries(upperSeries, lowerSeries); + area->setName(name + QString::number(nameIndex)); + nameIndex++; + chart->addSeries(area); + lowerSeries = upperSeries; + } + + chart->createDefaultAxes(); + chart->axes(Qt::Horizontal).first()->setRange(0, m_valueCount - 1); + chart->axes(Qt::Vertical).first()->setRange(0, m_valueMax); + // Add space to label to add space between labels and axis + auto axisY = qobject_cast<QValueAxis *>(chart->axes(Qt::Vertical).first()); + Q_ASSERT(axisY); + axisY->setLabelFormat("%.1f "); + + return chart; +} + +QChart *ThemeWidget::createBarChart(int valueCount) const +{ + Q_UNUSED(valueCount); + auto chart = new QChart; + chart->setTitle("Bar Chart"); + + auto series = new QStackedBarSeries(chart); + for (int i(0); i < m_dataTable.count(); i++) { + auto set = new QBarSet("Bar set " + QString::number(i)); + for (const Data &data : m_dataTable[i]) + *set << data.first.y(); + series->append(set); + } + chart->addSeries(series); + + chart->createDefaultAxes(); + chart->axes(Qt::Vertical).first()->setRange(0, m_valueMax * 2); + // Add space to label to add space between labels and axis + auto axisY = qobject_cast<QValueAxis *>(chart->axes(Qt::Vertical).first()); + Q_ASSERT(axisY); + axisY->setLabelFormat("%.1f "); + + return chart; +} + +QChart *ThemeWidget::createLineChart() const +{ + //![1] + auto chart = new QChart; + chart->setTitle("Line Chart"); + //![1] + + //![2] + QString name("Series "); + int nameIndex = 0; + for (const DataList &list : m_dataTable) { + auto series = new QLineSeries(chart); + for (const Data &data : list) + series->append(data.first); + series->setName(name + QString::number(nameIndex)); + nameIndex++; + chart->addSeries(series); + } + //![2] + + //![3] + chart->createDefaultAxes(); + chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax); + chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount); + //![3] + //![4] + // Add space to label to add space between labels and axis + auto axisY = qobject_cast<QValueAxis *>(chart->axes(Qt::Vertical).first()); + Q_ASSERT(axisY); + axisY->setLabelFormat("%.1f "); + //![4] + + return chart; +} + +QChart *ThemeWidget::createPieChart() const +{ + auto chart = new QChart; + chart->setTitle("Pie Chart"); + + auto series = new QPieSeries(chart); + for (const Data &data : m_dataTable[0]) { + QPieSlice *slice = series->append(data.second, data.first.y()); + if (data == m_dataTable[0].first()) { + // Show the first slice exploded with label + slice->setLabelVisible(); + slice->setExploded(); + slice->setExplodeDistanceFactor(0.5); + } + } + series->setPieSize(0.4); + chart->addSeries(series); + + return chart; +} + +QChart *ThemeWidget::createSplineChart() const +{ + auto chart = new QChart; + chart->setTitle("Spline Chart"); + QString name("Series "); + int nameIndex = 0; + for (const DataList &list : m_dataTable) { + auto series = new QSplineSeries(chart); + for (const Data &data : list) + series->append(data.first); + series->setName(name + QString::number(nameIndex)); + nameIndex++; + chart->addSeries(series); + } + + chart->createDefaultAxes(); + chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax); + chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount); + + // Add space to label to add space between labels and axis + auto axisY = qobject_cast<QValueAxis *>(chart->axes(Qt::Vertical).first()); + Q_ASSERT(axisY); + axisY->setLabelFormat("%.1f "); + return chart; +} + +QChart *ThemeWidget::createScatterChart() const +{ + // scatter chart + auto chart = new QChart; + chart->setTitle("Scatter Chart"); + QString name("Series "); + int nameIndex = 0; + for (const DataList &list : m_dataTable) { + auto series = new QScatterSeries(chart); + for (const Data &data : list) + series->append(data.first); + series->setName(name + QString::number(nameIndex)); + nameIndex++; + chart->addSeries(series); + } + + chart->createDefaultAxes(); + chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax); + chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount); + // Add space to label to add space between labels and axis + auto axisY = qobject_cast<QValueAxis *>(chart->axes(Qt::Vertical).first()); + Q_ASSERT(axisY); + axisY->setLabelFormat("%.1f "); + return chart; +} + +void ThemeWidget::updateUI() +{ + //![6] + auto theme = static_cast<QChart::ChartTheme>( + m_ui->themeComboBox->itemData(m_ui->themeComboBox->currentIndex()).toInt()); + //![6] + const auto charts = m_charts; + if (!m_charts.isEmpty() && m_charts.at(0)->chart()->theme() != theme) { + for (QChartView *chartView : charts) { + //![7] + chartView->chart()->setTheme(theme); + //![7] + } + } + + // Update antialiasing + //![11] + bool checked = m_ui->antialiasCheckBox->isChecked(); + for (QChartView *chart : charts) + chart->setRenderHint(QPainter::Antialiasing, checked); + //![11] + + // Update animation options + //![9] + QChart::AnimationOptions options( + m_ui->animatedComboBox->itemData(m_ui->animatedComboBox->currentIndex()).toInt()); + if (!m_charts.isEmpty() && m_charts.at(0)->chart()->animationOptions() != options) { + for (QChartView *chartView : charts) + chartView->chart()->setAnimationOptions(options); + } + //![9] + + // Update legend alignment + //![10] + Qt::Alignment alignment( + m_ui->legendComboBox->itemData(m_ui->legendComboBox->currentIndex()).toInt()); + + if (!alignment) { + for (QChartView *chartView : charts) + chartView->chart()->legend()->hide(); + } else { + for (QChartView *chartView : charts) { + chartView->chart()->legend()->setAlignment(alignment); + chartView->chart()->legend()->show(); + } + } + //![10] +} + diff --git a/examples/charts/chartsgallery/themewidget.h b/examples/charts/chartsgallery/themewidget.h new file mode 100644 index 00000000..e630040c --- /dev/null +++ b/examples/charts/chartsgallery/themewidget.h @@ -0,0 +1,50 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef THEMEWIDGET_H +#define THEMEWIDGET_H + +#include "contentwidget.h" + +QT_FORWARD_DECLARE_CLASS(QChart) +QT_FORWARD_DECLARE_CLASS(QChartView) +QT_FORWARD_DECLARE_CLASS(Ui_ThemeWidgetForm); + +typedef QPair<QPointF, QString> Data; +typedef QList<Data> DataList; +typedef QList<DataList> DataTable; + +class ThemeWidget: public ContentWidget +{ + Q_OBJECT +public: + explicit ThemeWidget(QWidget *parent = nullptr); + ~ThemeWidget(); + +private slots: + void updateUI(); + +private: + DataTable generateRandomData(int listCount, int valueMax, int valueCount) const; + void populateThemeBox(); + void populateAnimationBox(); + void populateLegendBox(); + void connectSignals(); + QChart *createAreaChart() const; + QChart *createBarChart(int valueCount) const; + QChart *createPieChart() const; + QChart *createLineChart() const; + QChart *createSplineChart() const; + QChart *createScatterChart() const; + +private: + int m_listCount; + int m_valueMax; + int m_valueCount; + QList<QChartView *> m_charts; + DataTable m_dataTable; + + Ui_ThemeWidgetForm *m_ui; +}; + +#endif diff --git a/examples/charts/chartsgallery/themewidget.ui b/examples/charts/chartsgallery/themewidget.ui new file mode 100644 index 00000000..9ea2bb7c --- /dev/null +++ b/examples/charts/chartsgallery/themewidget.ui @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ThemeWidgetForm</class> + <widget class="QWidget" name="ThemeWidgetForm"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>900</width> + <height>600</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="themeLabel"> + <property name="text"> + <string>Theme:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="themeComboBox"/> + </item> + <item> + <widget class="QLabel" name="animatedLabel"> + <property name="text"> + <string>Animation:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="animatedComboBox"/> + </item> + <item> + <widget class="QLabel" name="legendLabel"> + <property name="text"> + <string>Legend:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="legendComboBox"/> + </item> + <item> + <widget class="QCheckBox" name="antialiasCheckBox"> + <property name="text"> + <string>Anti-aliasing</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>themeComboBox</sender> + <signal>currentIndexChanged(int)</signal> + <receiver>ThemeWidgetForm</receiver> + <slot>updateUI()</slot> + </connection> + <connection> + <sender>antialiasCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>ThemeWidgetForm</receiver> + <slot>updateUI()</slot> + </connection> + <connection> + <sender>legendComboBox</sender> + <signal>currentIndexChanged(int)</signal> + <receiver>ThemeWidgetForm</receiver> + <slot>updateUI()</slot> + </connection> + <connection> + <sender>animatedComboBox</sender> + <signal>currentIndexChanged(int)</signal> + <receiver>ThemeWidgetForm</receiver> + <slot>updateUI()</slot> + </connection> + </connections> + <slots> + <slot>updateUI()</slot> + </slots> +</ui> |