C# PR

【質問回答】ScottPlot Ver.4.1.58で右から左にリアルタイムにグラフを描画する方法

記事内に商品プロモーションを含む場合があります

はじめに

今回は質問いただいた「リアルタイムに右から左にグラフ描画する方法」について解説したいと思います。

ScottPlotでは「左から右」のグラフが基準で、簡単に逆向きにする設定はありません。ですが、描画方法を工夫することで逆向きのグラフを実現することができたので解説したいと思います。バージョンは現時点での最新「Ver.4.1.58」を使います。

※ 『れん』さん質問いただきありがとうございます。

環境

今回の記事は以下の環境で作成しています。

環境 バージョン 備考
VisualStudio 2019
.NET Framework 4.7.2
ScottPlot 4.1.58
Windows 11 10でもOKです。

ScottPlotのインストール方法や基本的な使い方についてはこちらの記事をご覧ください。

楽天ブックス
¥2,139 (2023/02/03 06:42時点 | 楽天市場調べ)
\楽天ポイント4倍セール!/
楽天市場

プログラム概要

今回のプログラムの概要は以下の通りです。

実現方法
  • X軸の1目盛の表示を日時(文字列)にする
  • X,Yの値を1目盛ずつリアルタイムに変更する。
  • Xの値を「マイナス」側に更新して「右から左へ描画」させる

※質問者さんは「SignalPlot」とのことでしたが、ScatterPlotでした今回の仕組みが使えないようなので、ScatterPlotの方法を解説しています。

実行結果

後述するコードを実行すると、以下のように右から左にリアルタイムに更新されるグラフを描画することができます。

※ 見栄えのため0.5秒間隔のグラフにしています。

全体コード

全体コードは以下の通りです。詳細な内容は後述する「コードのポイント」で解説します。

デザイナで「Timer」を追加後、Tickイベントにtimer1_Tick関数を登録してください。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ScottPlot41_Reverse
{
    public partial class Form1 : Form
    {
        // ScottPlotに動的にデータをセットするためのリストです。
        ScottPlot.Plottable.ScatterPlotList<double> pointList = null;

        // 日時表示のラベルと、ラベルに対応するX軸の位置のリストです。
        List<string> dateLabelList = new List<string>();
        List<double> xPositionList = new List<double>();

        // Y軸のダミー値用の乱数です。
        Random random = new System.Random();

        // X軸に表示する日時に使います
        DateTime dt = new DateTime();

        public Form1()
        {
            InitializeComponent();

            // x軸の位置リストに0をセットします
            xPositionList.Add(0.0);

            // ScottPlotからデータ更新用のリストを取得します。
            pointList = formsPlot1.Plot.AddScatterList<double>();

            formsPlot1.Refresh();

            // タイマーの処理を開始します
            timer1.Start();
        }
        //
        // タイマーの処理です
        //
        private void timer1_Tick(object sender, EventArgs e)
        {
            // 現在時刻を取得してラベルに追加します
            dt = DateTime.Now;
            string timeStr = dt.ToString("mm:ss");
            dateLabelList.Add(timeStr);

            //
            // x軸の位置配列と、対応するラベル配列をScottPlotにセットします。
            //
            formsPlot1.Plot.XAxis.ManualTickPositions(xPositionList.ToArray(), dateLabelList.ToArray());

            // データプロット用にX軸位置を取得します
            double xPosition = xPositionList.Last();

            // Y軸用の乱数を生成します(0~1.0)
            double yPosition = random.NextDouble();

            // データをプロットします
            pointList.Add(xPosition, yPosition);

            // ポイントデータに合わせて表示範囲を自動調整します。
            formsPlot1.Plot.AxisAuto();

            // 4.1.58(?)では、データ等の更新後にRefreshが必要なようです。
            formsPlot1.Refresh();

            // グラフを描画します
            formsPlot1.Render();

            // 次周期用のXの位置をセットします
            // マイナス1することで、右 -> 左にプロットを進めます
            xPositionList.Add(xPosition - 1 );

        }
    }
}

コードのポイント

ラベルを使って日時を設定

ScottPlotでは、Xの値を日時で指定する機能がありますが、質問者さんより「新しい日時をマイナス側に設定するとエラーになってしまう」と教えていただきました。

そのため、Xの値は数値にし「”文字列にした日時”をラベルとしてXの値に対応させる」方法で対策します。

Xの値と、日付ラベルの対応付けイメージ
ラベルの設定イメージ
            // 現在時刻を取得してラベルに追加します
            dt = DateTime.Now;
            string timeStr = dt.ToString("mm:ss");
            dateLabelList.Add(timeStr);

            //
            // x軸の位置配列と、対応するラベル配列をScottPlotにセットします。
            //
            formsPlot1.Plot.XAxis.ManualTickPositions(xPositionList.ToArray(), dateLabelList.ToArray());

ラベルの設定には「すべてのラベル名称」を固定長配列で指定する必要があるので、現在時刻を取得後にListに保持しておきます。

また、時刻とデータのずれを(なるべく)防ぐため、Yの値の取得処理に近い部分で日時を取得しています。

設定したラベルにポイントをプロット

ラベルを設定済みのXの値を使ってポイントを設定します。Yの値には解説用に乱数の値をセットしています。

Xの値と、日付ラベルの対応させた部分にプロットするイメージ
プロットのイメージ
            // データプロット用にX軸位置を取得します
            double xPosition = xPositionList.Last();

            // Y軸用の乱数を生成します(0~1.0)
            double yPosition = random.NextDouble();

            // データをプロットします
            pointList.Add(xPosition, yPosition);

グラフの動的な描画は、コンストラクタで取得したScatterPlotList型のリストに追加するだけで、ScottPlotが随時描画してくれます。

            // ScottPlotからデータ更新用のリストを取得します。
            pointList = formsPlot1.Plot.AddScatterList<double>();

Xの値を更新

グラフが左から右に表示されるように、Xの値をマイナス側に更新します。

前述した「ラベルとXの値の対応」に固定長配列が必要になるため、Xの値はすべてリストで管理します。

            // 次周期用のXの位置をセットします
            // マイナス1することで、右 -> 左にプロットを進めます
            xPositionList.Add(xPosition - 1 );

まとめ

ScottPlot 4.1.58でリアルタイムに右から左にグラフ描画する方法について解説しました。

少し分かりづらいコードになってしまいましたが、質問者さんや同じ問題を解決したい方の参考になればうれしいです。

お知らせ

今月号の『Software Design』はドメイン駆動設計(DDD)。実例を交えたDDDの手法の解説が特集されています。

編集:Software Design 編集部
¥1,562 (2024/03/13 12:59時点 | Amazon調べ)
\楽天ポイント4倍セール!/
楽天市場

質問・要望 大歓迎です

「こんな解説記事作って」「こんなことがしたいけど、〇〇で困ってる」など、コメント欄で教えてください。 質問・要望に、中の人ができる限り対応します。

使えたよ・設定できたよの一言コメントも大歓迎。気軽に足跡を残してみてください。記事を紹介したい方はブログ、SNSにバシバシ貼ってもらってOKです。

ABOUT ME
えす
現役のソフトウェアエンジニアです。 C++ C# Python を使ってます。10年ちょい設計/開発部門にいましたが、今はQAエンジニアっぽいことをしています。

POSTED COMMENT

  1. れん より:

    えす様

    こちらの質問をさせていただいた者です。
    記事にまとめていただきありがとうございます。

    X軸の値は数値にして、ラベルに数値に対応した日付文字列を表示する方法は思いつきませんでしたので非常に参考になりました。同様の方法をSignalPlotXYで実装した場合にも応用できないか念のため確認してみましたが、

    SignalPlotXYだと負の方向に値が増えていく配列(X[0]=0,X[1]=-1,X[2]=-2)をデータプロット時に追加して
    描画するとやはりエラーが出てしまいうまくいきませんでした。

    なので左から右方向に描画されるグラフを実装する際は今回ご紹介頂いた場合はScatterPlottでの方法を使わさせて頂きます。ご協力ありがとうございました。

    • えす より:

      れんさん

      コメントありがとうございます。

      SignalPlotでの確認ありがとうございます。
      マイナス方向に追加するとエラーになってしまうんですね。

      ScatterPlot + 少々トリッキーなコードになってしまいましたが、
      お役に立てたようで嬉しいです。

      また何かあればコメント等でご連絡ください。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です