QtChartsを活用したデータ可視化アプリケーションの構築

はじめに

組み込みシステムのデバッグやセンサーデータの解析では、時間軸に沿った数値変動を直感的に把握できる可視化機能が不可欠です。Qt5以降で標準搭載されたQtChartsモジュールを使用すれば、専用のライブラリを追加することなく、高性能なチャート描画機能を実装できます。本稿では、テキストファイルから数値データを読み込み、リアルタイムに折れ線グラフで表示する実装方法を解説します。

プロジェクト設定

QtChartsを使用するためには、プロジェクトファイル(.pro)に以下の設定を追加します。

QT += core gui charts

また、ソースコードでは名前空間の宣言が必要です。

QT_CHARTS_USE_NAMESPACE

グラフ描画クラスの初期化

メインウィンドウのコンストラクタで、QChartView、QLineSeries、QValueAxisを初期化します。以下の実装では、スムーズな描画のためアンチエイリアスを有効化し、タイマー駆動でデータ更新を行います。

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      ui(new Ui::MainWindow),
      plot_curve(new QLineSeries),
      main_chart(new QChart),
      x_axis(new QValueAxis),
      y_axis(new QValueAxis),
      update_timer(new QTimer(this))
{
    ui->setupUi(this);

    // データ系列の設定
    plot_curve->setName("センサー値");
    plot_curve->setColor(QColor(0, 120, 212));
    
    // グラフ全体の設定
    main_chart->addSeries(plot_curve);
    main_chart->setTitle("リアルタイムデータモニタ");
    main_chart->legend()->setAlignment(Qt::AlignBottom);

    // X軸の設定(時間軸)
    x_axis->setRange(0, 120);
    x_axis->setTitleText("経過時間(秒)");
    x_axis->setTickCount(13);
    x_axis->setMinorTickCount(3);
    x_axis->setGridLineVisible(true);
    
    // Y軸の設定(値域)
    y_axis->setRange(-10.0, 15.0);
    y_axis->setTitleText("測定値");
    y_axis->setTickCount(11);
    y_axis->setMinorTickCount(2);
    y_axis->setGridLineVisible(true);

    // 軸を系列に適用
    main_chart->setAxisX(x_axis, plot_curve);
    main_chart->setAxisY(y_axis, plot_curve);

    // 描画ビューの設定
    ui->chartView->setChart(main_chart);
    ui->chartView->setRenderHint(QPainter::Antialiasing);
    ui->chartView->setRubberBand(QChartView::RectangleRubberBand);

    // タイマー設定(100ms間隔)
    update_timer->setInterval(100);
    connect(update_timer, &QTimer::timeout, this, &MainWindow::onTimerTimeout);
    update_timer->start();
}

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

サンプル波形の生成

テスト用として、ボタンクリックでサイン波を生成する関数を実装します。データポイントは時間間隔を考慮して追加します。

void MainWindow::onGenerateWaveform()
{
    plot_curve->clear();
    
    const double interval = 0.05;
    for (int i = 0; i < 500; ++i) {
        double x_val = i * interval;
        double y_val = qCos(x_val * 2.0 * M_PI) * 5.0 + qSin(x_val * 4.0 * M_PI) * 2.0;
        plot_curve->append(x_val, y_val);
    }
}

テキストファイルからのデータ読み込み

実際の測定データを扱う場合、CSV形式のテキストファイルを想定した実装が便利です。以下は、行単位で読み込み、カンマ区切りの数値を解析する例です。

void MainWindow::loadDataFromFile(const QString &file_path)
{
    QFile data_file(file_path);
    if (!data_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "ファイルオープン失敗:" << file_path;
        return;
    }

    plot_curve->clear();
    QTextStream stream(&data_file);
    int line_index = 0;
    
    while (!stream.atEnd()) {
        QString line = stream.readLine().trimmed();
        if (line.isEmpty() || line.startsWith('#')) {
            continue; // 空行とコメント行をスキップ
        }

        QStringList tokens = line.split(',');
        if (tokens.size() >= 2) {
            bool x_ok, y_ok;
            double x_pos = tokens[0].toDouble(&x_ok);
            double y_pos = tokens[1].toDouble(&y_ok);
            
            if (x_ok && y_ok) {
                plot_curve->append(x_pos, y_pos);
                line_index++;
            }
        }
    }
    
    // 自動的に軸範囲を調整
    main_chart->createDefaultAxes();
    data_file.close();
}

リアルタイムデータ更新

タイマーイベントでデータを逐次追加する処理例です。実際のハードウェアからデータを取得する場合、このスロット関数でデータ取得を行います。

void MainWindow::onTimerTimeout()
{
    static int counter = 0;
    double new_value = (qrand() % 200 - 100) / 10.0; // -10.0 ~ 10.0のランダム値
    
    plot_curve->append(counter * 0.1, new_value);
    counter++;
    
    // 古いデータの削除(表示ポイント数を制限)
    if (plot_curve->count() > 500) {
        plot_curve->remove(0);
    }
    
    // 表示範囲の自動スクロール
    if (counter * 0.1 > x_axis->max()) {
        x_axis->setRange(x_axis->min() + 10, x_axis->max() + 10);
    }
}

まとめ

QtChartsを活用することで、プロフェッショナルなデータ可視化ツールを短時間で構築できます。本稿で紹介した実装パターンは、テキストファイル解析からリアルタイム表示まで、幅広い応用が可能です。軸範囲の動的調整やデータカーソル機能の追加により、更なる使い勝手の向上が図れるでしょう。

タグ: Qt QtCharts QLineSeries QChartView データ可視化

6月19日 17:47 投稿