C#

【質問回答】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のインストール方法や基本的な使い方についてはこちらの記事をご覧ください。

bookfan 1号店 楽天市場店
¥1,980 (2022/11/19 22:15時点 | 楽天市場調べ)

プログラミングのトレンドを知るには雑誌が最適です

プログラム概要

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

実現方法
  • 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でリアルタイムに右から左にグラフ描画する方法について解説しました。

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

【おすすめ】UdemyでC#のスキルを上げる

C#のスキルを上げるには、Udemyの動画講座がおすすめです。「C#で読みやすいコードを書く50の方法」「保守性の高いコードの書き方」など、脱初心者/スキルアップのための講座が多数公開されています。

1講座 2440円程度で返金保証あり。視聴期間も無制限なので「自分のペースでコスパ良くスキルアップしたい」「失敗したくない」とお悩み方におすすめです。

⇒ C#の講座一覧はこちら
icon

応援・要望お待ちしてます

ブログを見ていて「この辺を詳しく知りたい」「このライブラリの使い方を知りたい」「こんなことで困ってる」...etc があれば、コメント・問い合わせ・Twitterで教えてください。質問・ご要望に合わせて解説記事を作ります。

ブログを気に入っていただけたり、「応援してもいいよ」という方がいたら、ブログやSNSでの紹介をお願いします。 あたたかい応援は、中の人の更新の大きな励みになります。

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

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