Введение в работу с гистограммами в Qt
Гистограмма представляет данные в виде горизонтальных или вертикальных полос, сгруппированных по категориям. Рассмотрим, как с помощью гистограммы можно визуализировать данные в приложении, использующем фреймворк 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);
В результате получаем гистограмму следующего вида:
Изменение внешнего вида гистограммы
Можно заметить, что цвета полос, начиная с шестой (Уральский федеральный округ), начинают повторяться, причем с пониженной яркостью.
Чтобы решить эту проблему, можно вручную установить цвет для определенного набора полос (объекта 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);
}
В итоге получаем следующую гистограмму:
Исходный код
Полный исходный код проекта доступен на 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()
{
}
//---------------------------------------------------------------------------------------