Использование гистограмм Qt Charts совместно с моделями данных

Добавлено 8 января 2021 в 01:43

В данной статье представлен простой пример использования диаграмм Qt Charts, в частности гистограмм, для отображения данных из модели данных, производной от класса QAbstractTableModel.

Пример приложения для демонстрации использования гистограмм Qt Charts совместно с моделями данных
Использования гистограмм Qt Charts совместно с моделями данных

MVC (модель-представление-контроллер)

MVC (модель-представление-контроллер) - это паттерн проектирования, созданный на основе Smalltalk, который часто используется при создании пользовательских интерфейсов. В «Паттернах проектирования» Э. Гамма и др. написано:

MVC состоит из трех типов объектов. Модель – это объект приложения, Представление – это его экранное представление, а Контроллер определяет способ реакции пользовательского интерфейса на ввод пользователя. До MVC проекты пользовательского интерфейса имели тенденцию объединять эти объекты вместе. MVC разделяет их для повышения гибкости и повторного использования.

Qt так же содержит набор классов, которые для управления связями между данными и способами их представления пользователю используют архитектуру модель/представление. Разделение функций, введенное этой архитектурой, дает разработчикам большую гибкость для настройки представления элементов и предоставляет стандартный интерфейс модели, позволяющий использовать широкий диапазон источников данных с существующими представлениями элементов.

В данном примере мы будем использовать одну модель (источник данных) для одновременного преставления данных из нее двумя способами: в таблице и на диаграмме.

Использование моделей данных с гистограммами

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

Начнем с создания экземпляра класса CustomTableModel. Класс CustomTableModel является производным от QAbstractTableModel и был создан только для этого примера. Конструктор этого класса заполняет внутреннее хранилище данных модели данными, необходимыми для нашего примера работы с диаграммами.

m_model = new CustomTableModel;

Теперь у нас есть модель с данными, которые мы хотели бы отображать как на гистограмме, так и в QTableView. Объект QTableView уже создан в дизайнере, и теперь говорим ему использовать только что созданную модель в качестве источника данных. Для хорошего представления данных режим изменения размера заголовков tableView изменен на растягивающийся. В дизайнере также задана минимальная ширина виджета.

ui->tableView->setModel(m_model);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_model->setParent(ui->tableView);

Теперь нам нужен экземпляр QChart для отображения на гистограмме тех же данных. Также включаем у него анимацию. Так легче увидеть, как изменение данных модели влияет на диаграмму.

QChart *chart = new QChart;
chart->setAnimationOptions(QChart::AllAnimations);

Первая строка приведенного ниже кода создает новый объект последовательности полос. Переменные firstRow и rowCount используются для определения настраиваемого сопоставления модели. Настраиваемое сопоставление позволяет взять из модели только часть данных. В этом случае данные из пяти строк, начиная со строки с индексом 3. Следующие три строки создают преобразователь данных, экземпляр класса QVBarModelMapper, и указывают, что данные для наборов полос должны быть взяты из столбцов модели с индексами от 1 до 4 (включительно). Чтобы создать связь между последовательностью полос и моделью, мы передаем оба этих объекта преобразователю данных, т.е. объекту mapper класса QVBarModelMapper.

Наконец, добавляем к диаграмме последовательность полос series.

QBarSeries *series = new QBarSeries;

int firstRow = 3;
int rowCount = 5;

QVBarModelMapper *mapper = new QVBarModelMapper(this);

mapper->setFirstBarSetColumn(1);
mapper->setLastBarSetColumn(4);
mapper->setFirstRow(firstRow);
mapper->setRowCount(rowCount);
mapper->setSeries(series);
mapper->setModel(m_model);

chart->addSeries(series);

Чтобы показать в QTableView, какие данные соответствуют какому набору полос, в этом примере используется раскраска таблицы. Когда последовательность полос добавляется к диаграмме, ей назначается цвет в зависимости от выбранной в данный момент темы. Код ниже извлекает этот цвет из последовательности полос и использует его для создания фона ячеек QTableView. Раскрашивание другого представления не является частью функционала QChart.

// для хранения hex-кода цвета из последовательности полос
QString seriesColorHex = "#000000";

// получаем цвет последовательности и используем его индикации области таблицы, отображаемой на диаграмме
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();
    m_model->addMapping(seriesColorHex, QRect(1 + i, firstRow, 1, barsets.at(i)->count()));
}

Также неплохо было бы, чтобы на оси диаграммы были подписаны категории, которые описывали бы суть данных. В следующем фрагменте показано, как это сделать. Здесь для примера всё сильно упрощено, в реальном проекте эти категории так же брались бы из модели.

QStringList categories;
categories << "April" << "May" << "June" << "July" << "August";
QBarCategoryAxis *axisX = new QBarCategoryAxis();
axisX->append(categories);
chart->addAxis(axisX, Qt::AlignBottom);
series->attachAxis(axisX);
QValueAxis *axisY = new QValueAxis();
chart->addAxis(axisY, Qt::AlignLeft);
series->attachAxis(axisY);

Чтобы избежать настройки QGraphicsScene, мы используем класс QChartView, который делает это за нас. Указатель на объект QChart используется как параметр конструктора QChartView. Чтобы визуализация выглядела лучше, включаем антиалиасинг и устанавливаем минимальный размер виджета chartView.

QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
chartView->setMinimumSize(640, 480);

И, наконец, помещаем виджет chartView в специально подготовленный в дизайнере виджет QGroupBox.

ui->chartGroupBox->layout()->addWidget(chartView);

В результате получаем приложение следующего вида. Изменение данных в таблице влияет на их представление на диаграмме.

Рисунок 1 Пример приложения для демонстрации использования гистограмм Qt Charts совместно с моделями данных
Рисунок 1 – Пример приложения для демонстрации использования гистограмм Qt Charts совместно с моделями данных

Исходный код

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

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

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QtCharts>

using namespace QtCharts;

//---------------------------------------------------------------------------------------
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_model = new CustomTableModel;

    ui->tableView->setModel(m_model);
    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    m_model->setParent(ui->tableView);

    QChart *chart = new QChart;
    chart->setAnimationOptions(QChart::AllAnimations);

    QBarSeries *series = new QBarSeries;

    int firstRow = 3;
    int rowCount = 5;

    QVBarModelMapper *mapper = new QVBarModelMapper(this);

    mapper->setFirstBarSetColumn(1);
    mapper->setLastBarSetColumn(4);
    mapper->setFirstRow(firstRow);
    mapper->setRowCount(rowCount);
    mapper->setSeries(series);
    mapper->setModel(m_model);

    chart->addSeries(series);

    // 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();
        m_model->addMapping(seriesColorHex, QRect(1 + i, firstRow, 1, barsets.at(i)->count()));
    }

    QStringList categories;
    categories << "April" << "May" << "June" << "July" << "August";
    QBarCategoryAxis *axisX = new QBarCategoryAxis();
    axisX->append(categories);
    chart->addAxis(axisX, Qt::AlignBottom);
    series->attachAxis(axisX);
    QValueAxis *axisY = new QValueAxis();
    chart->addAxis(axisY, Qt::AlignLeft);
    series->attachAxis(axisY);

    QChartView *chartView = new QChartView(chart);
    chartView->setRenderHint(QPainter::Antialiasing);
    chartView->setMinimumSize(640, 480);

    ui->chartGroupBox->layout()->addWidget(chartView);
}

//---------------------------------------------------------------------------------------
MainWindow::~MainWindow()
{
    delete ui;
}

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

 

Теги

C++ / CppGUI / Графический интерфейс пользователяMVC / Model-View-Controller / Модель-Представление-КонтроллерQtQtChartsГистограммаПрограммирование

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

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