Графики

Qt Charts: встроенный модуль для визуализации данных без сторонних библиотек

Введение

В отличие от QCustomPlot, Qt Charts — официальный модуль Qt (доступный как коммерческое расширение в более старых версиях, и часть открытой экосистемы Qt в более новых), интегрированный с системой сборки Qt и поддерживающий как Qt Widgets, так и QML из одной базы кода. Это делает его естественным выбором, когда не хотят добавлять внешнюю зависимость и важна нативная интеграция с обеими UI-технологиями Qt. В статье построим график на основе Qt Charts в обоих вариантах — Widgets и QML.

Концепция

В Qt Widgets центральный класс — QChart, который содержит один или несколько объектов-серий (QLineSeries, QBarSeries, QPieSeries и другие), отображаемый через QChartView (наследник QGraphicsView). В QML аналогичная функциональность доступна через элемент ChartView с дочерними элементами-сериями (LineSeries, BarSeries и так далее), что даёт декларативный, привычный для QML стиль описания графика прямо в дереве компонентов интерфейса.

Пример кода

// --- Вариант на Qt Widgets ---
#include <QApplication>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    auto *series = new QLineSeries();
    series->setName("Температура");
    for (int hour = 0; hour < 24; ++hour) {
        const double temperature = 10 + 8 * std::sin((hour - 6) * M_PI / 12.0);
        series->append(hour, temperature);
    }

    auto *chart = new QChart();
    chart->addSeries(series);
    chart->setTitle("Температура за сутки");
    chart->createDefaultAxes();
    chart->axes(Qt::Horizontal).first()->setTitleText("Час");
    chart->axes(Qt::Vertical).first()->setTitleText("°C");

    QChartView chartView(chart);
    chartView.setRenderHint(QPainter::Antialiasing);
    chartView.resize(640, 400);
    chartView.show();

    return app.exec();
}
// --- Вариант на QML ---
import QtQuick
import QtCharts

ApplicationWindow {
    visible: true
    width: 640
    height: 400

    ChartView {
        anchors.fill: parent
        title: "Температура за сутки"
        antialiasing: true

        LineSeries {
            name: "Температура"
            XYPoint { x: 0; y: 8 }
            XYPoint { x: 6; y: 2 }
            XYPoint { x: 12; y: 18 }
            XYPoint { x: 18; y: 14 }
            XYPoint { x: 23; y: 9 }
        }
    }
}

Пояснения к коду

В варианте на Qt Widgets QLineSeries заполняется парами значений через append(x, y), затем добавляется в QChart через addSeries(). Вызов createDefaultAxes() автоматически создаёт оси X и Y с диапазонами, подобранными по данным серии — без него график был бы построен без видимых осей и подписей. QChartView оборачивает QChart для отображения в обычной иерархии виджетов, аналогично любому другому виджету.

В QML-варианте та же концептуальная структура выражена декларативно: ChartView — корневой контейнер графика, а LineSeries с дочерними XYPoint описывают данные прямо в дереве QML-компонентов, без необходимости в отдельном императивном коде для заполнения серии — это особенно удобно, когда данные графика статичны или меняются через привязки к моделям, а не императивно в цикле.

Подводные камни

  • Производительность Qt Charts на очень больших объёмах данных (десятки тысяч точек, особенно при частом обновлении в реальном времени) заметно ниже, чем у специализированных библиотек типа QCustomPlot — для задач с высокочастотным обновлением больших датасетов (осциллографы, потоковая телеметрия) Qt Charts может не справляться с нужной частотой кадров.
  • Различия API между вариантами для Widgets и QML не являются полностью симметричными — некоторые возможности доступны только в одном из двух вариантов API, что может вызывать путаницу при попытке найти аналогичную функциональность в документации, написанной для другого варианта использования.
  • Модуль Qt Charts требует отдельного подключения компонента в CMake (find_package(Qt6 COMPONENTS Charts) и соответствующий импорт в QML) — в более старых версиях Qt этот модуль был частью коммерческой подписки, и стоит проверить актуальный статус лицензирования для конкретной используемой версии Qt в проекте.
  • Ограниченная гибкость кастомизации внешнего вида по сравнению с полностью самостоятельной отрисовкой через QPainter. Если требуется визуально нестандартный график, сильно отличающийся от предустановленных стилей Qt Charts, может оказаться проще и предсказуемее реализовать кастомную отрисовку напрямую (как в статье про кастомные графики на QPainter), чем пытаться «подогнать» внешний вид Qt Charts под нетипичные требования дизайна.