Введение в работу с гистограммами в Qt

Добавлено 7 января 2021 в 06:35

Гистограмма представляет данные в виде горизонтальных или вертикальных полос, сгруппированных по категориям. Рассмотрим, как с помощью гистограммы можно визуализировать данные в приложении, использующем фреймворк C++/Qt и его модуль Qt Charts.

Добавление модуля Qt Charts в проект

Во-первых, включаем использование модуля Qt Charts в qmake-файле проекта.

QT += charts

А в исходном файле используем следующие директивы include и using:

#include <QtCharts>

using namespace QtCharts;

Создание гистограммы

Для простоты всё создание диаграммы выполним в конструкторе объекта класса, который наследуется от QMainWindow.

Данные, которые визуализирует гистограмма, определяются экземплярами класса QBarSet. Объект QBarSet представляет один набор полос на гистограмме.

Сначала мы создаем наборы и добавляем к ним данные. Данные добавляются с помощью оператора <<. В качестве альтернативы можно использовать метод append().

QBarSet *centralFedDistr = new QBarSet("Центральный");
QBarSet *northwesternDistr = new QBarSet("Северо-Западный");
QBarSet *southerhFedDistr = new QBarSet("Южный");
QBarSet *northCaucasianFedDistr = new QBarSet("Северо-Кавказский");
QBarSet *volgaFedDistr = new QBarSet("Приволжский");
QBarSet *uralFedDistr = new QBarSet("Уральский");
QBarSet *siberianFedDistr = new QBarSet("Сибирский");
QBarSet *farEasternFedDistr = new QBarSet("Дальневосточный");

*centralFedDistr        << 3378.83 << 3304.0 << 3261.0 << 2927.4;
*northwesternDistr      << 1261.83 << 1268.6 << 1253.4 << 1096.3;
*southerhFedDistr       << 823.45  << 821.8  << 781.5  << 821.6;
*northCaucasianFedDistr << 295.39  << 313.7  << 312.8  << 197.1;
*volgaFedDistr          << 2291.97 << 2312.6 << 2222.4 << 1992.1;
*uralFedDistr           << 921.92  << 891.5  << 862.1  << 834.8;
*siberianFedDistr       << 1349.41 << 1362.9 << 1313.8 << 1139.8;
*farEasternFedDistr     << 455.14  << 469.7  << 458.0  << 382.7;

Затем создаем последовательность полос (объект класса QBarSeries) и добавляем в нее наборы данных. Последовательность становится владельцем наборов полос. В последовательности данные из наборов группируются в категории. Первые значения каждого набора сгруппированы вместе в первой категории, вторые значения – во второй категории, и так далее.

Класс последовательности определяет способ представления данных:

  • класс QBarSeries представляет данные в виде вертикальных полос;
  • класс QHorizontalBarSeries представляет данные в виде горизонтальных полос.

В данном примере мы используем вертикальное представление.

QBarSeries *series = new QBarSeries();
series->append(centralFedDistr);
series->append(northwesternDistr);
series->append(southerhFedDistr);
series->append(northCaucasianFedDistr);
series->append(volgaFedDistr);
series->append(uralFedDistr);
series->append(siberianFedDistr);
series->append(farEasternFedDistr);

Далее создаем объект диаграммы класса QChart и добавляем к нему последовательность полос. Устанавливаем заголовок для диаграммы с помощью метода setTitle(), а затем включаем анимацию последовательности полос, вызывая метод setAnimationOptions(QChart::SeriesAnimations).

QChart *chart = new QChart();
chart->addSeries(series);
chart->setTitle("Среднесписочная численность работников предприятий малого и среднего бизнеса по федеральным округам");
chart->setAnimationOptions(QChart::SeriesAnimations);

Чтобы на оси отображались категории, нам нужно создать объект QBarCategoryAxis. Здесь мы создаем ось категорий со списком категорий и устанавливаем ее выравнивание по нижнему краю, чтобы она действовала как ось x, и присоединяем ее к последовательности полос series. Диаграмма, объект chart, становится владельцем оси. Для оси y мы используем ось значений, выровненную по левому краю.

QStringList categories;
categories << "01.01.2014" << "01.01.2015" << "01.01.2016" << "01.01.2017";
QBarCategoryAxis *axisX = new QBarCategoryAxis();
axisX->append(categories);
chart->addAxis(axisX, Qt::AlignBottom);
series->attachAxis(axisX);

QValueAxis *axisY = new QValueAxis();
axisY->setRange(0,4000);
chart->addAxis(axisY, Qt::AlignLeft);
series->attachAxis(axisY);

Мы также хотим показать легенду диаграммы. Для этого берем от диаграммы (объекта chart) указатель на легенду (объект класса QLegend) и делаем ее видимой. А затем помещаем легенду в правую часть диаграммы, задав для ее выравнивания значение Qt::AlignRight.

chart->legend()->setVisible(true);
chart->legend()->setAlignment(Qt::AlignRight);

И, наконец, мы добавляем диаграмму в представление. И включаем сглаживание для chartView.

QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);

Теперь диаграмма готова к показу. Мы устанавливаем диаграмму как центральный виджет окна. И изменяем размеры окна.

setCentralWidget(chartView);
resize(800, 400);

В результате получаем гистограмму следующего вида:

Рисунок 1 Приложение с примером гистограммы Qt Charts
Рисунок 1 – Приложение с примером гистограммы Qt Charts

Изменение внешнего вида гистограммы

Можно заметить, что цвета полос, начиная с шестой (Уральский федеральный округ), начинают повторяться, причем с пониженной яркостью.

Чтобы решить эту проблему, можно вручную установить цвет для определенного набора полос (объекта QBarSet) с помощью метода QBarSet::setLabelBrush(const QBrush &brush). Для примера немного упростим задачу и извлечем список наборов полос из объекта последовательности полос и каждому набору установим «случайный» цвет.

QList<QBarSet *> sets = series->barSets();
float currentHue = 0.0;
for(int i = 0; i < sets.size(); ++i)
{
  QColor col = QColor::fromHslF(currentHue, 0.7, 0.5);
  currentHue += 0.618033988749895f;
  currentHue = std::fmod(currentHue, 1.0f);
  sets[i]->setColor(col);
}

В итоге получаем следующую гистограмму:

Рисунок 2 Изменение цвета полос гистограммы Qt Charts
Рисунок 2 – Изменение цвета полос гистограммы Qt Charts

Исходный код

Полный исходный код проекта доступен на GitHub.

Также ниже приведен код исходного файла mainwindow.cpp и, в частности, конструктора, котором создается гистограмма.

#include "mainwindow.h"

#include <cmath>

#include <QtCharts>

using namespace QtCharts;

//---------------------------------------------------------------------------------------
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QBarSet *centralFedDistr = new QBarSet("Центральный");
    QBarSet *northwesternDistr = new QBarSet("Северо-Западный");
    QBarSet *southerhFedDistr = new QBarSet("Южный");
    QBarSet *northCaucasianFedDistr = new QBarSet("Северо-Кавказский");
    QBarSet *volgaFedDistr = new QBarSet("Приволжский");
    QBarSet *uralFedDistr = new QBarSet("Уральский");
    QBarSet *siberianFedDistr = new QBarSet("Сибирский");
    QBarSet *farEasternFedDistr = new QBarSet("Дальневосточный");

    *centralFedDistr        << 3378.83 << 3304.0 << 3261.0 << 2927.4;
    *northwesternDistr      << 1261.83 << 1268.6 << 1253.4 << 1096.3;
    *southerhFedDistr       << 823.45  << 821.8  << 781.5  << 821.6;
    *northCaucasianFedDistr << 295.39  << 313.7  << 312.8  << 197.1;
    *volgaFedDistr          << 2291.97 << 2312.6 << 2222.4 << 1992.1;
    *uralFedDistr           << 921.92  << 891.5  << 862.1  << 834.8;
    *siberianFedDistr       << 1349.41 << 1362.9 << 1313.8 << 1139.8;
    *farEasternFedDistr     << 455.14  << 469.7  << 458.0  << 382.7;


    QBarSeries *series = new QBarSeries();
    series->append(centralFedDistr);
    series->append(northwesternDistr);
    series->append(southerhFedDistr);
    series->append(northCaucasianFedDistr);
    series->append(volgaFedDistr);
    series->append(uralFedDistr);
    series->append(siberianFedDistr);
    series->append(farEasternFedDistr);

    QList<QBarSet *> sets = series->barSets();
    float currentHue = 0.0;
    for(int i = 0; i < sets.size(); ++i)
    {
        QColor col = QColor::fromHslF(currentHue, 0.7, 0.5);
        currentHue += 0.618033988749895f;
        currentHue = std::fmod(currentHue, 1.0f);
        sets[i]->setColor(col);
    }

    QChart *chart = new QChart();
    chart->addSeries(series);
    chart->setTitle("Среднесписочная численность работников предприятий малого и среднего бизнеса по федеральным округам");
    chart->setAnimationOptions(QChart::SeriesAnimations);

    QStringList categories;
    categories << "01.01.2014" << "01.01.2015" << "01.01.2016" << "01.01.2017";
    QBarCategoryAxis *axisX = new QBarCategoryAxis();
    axisX->append(categories);
    chart->addAxis(axisX, Qt::AlignBottom);
    series->attachAxis(axisX);

    QValueAxis *axisY = new QValueAxis();
    axisY->setRange(0,4000);
    chart->addAxis(axisY, Qt::AlignLeft);
    series->attachAxis(axisY);

    chart->legend()->setVisible(true);
    chart->legend()->setAlignment(Qt::AlignRight);

    QChartView *chartView = new QChartView(chart);
    chartView->setRenderHint(QPainter::Antialiasing);

    setCentralWidget(chartView);
    resize(800, 400);
}

//---------------------------------------------------------------------------------------
MainWindow::~MainWindow()
{
}

//---------------------------------------------------------------------------------------
 

Теги

C++ / CppGUI / Графический интерфейс пользователяQtQtChartsГистограммаПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.