はじめに
今回は質問いただいた「リアルタイムに右から左にグラフ描画する方法」について解説したいと思います。
ScottPlotでは「左から右」のグラフが基準で、簡単に逆向きにする設定はありません。ですが、描画方法を工夫することで逆向きのグラフを実現することができたので解説したいと思います。バージョンは現時点での最新「Ver.4.1.58」を使います。
※ 『れん』さん質問いただきありがとうございます。
環境
今回の記事は以下の環境で作成しています。
環境 | バージョン | 備考 |
VisualStudio | 2019 | |
.NET Framework | 4.7.2 | |
ScottPlot | 4.1.58 | |
Windows | 11 | 10でもOKです。 |
ScottPlotのインストール方法や基本的な使い方についてはこちらの記事をご覧ください。
プログラム概要
今回のプログラムの概要は以下の通りです。
- グラフはリアルタイム(動的)に描画する
- X軸の値は日時(1秒ごと)
- 古いデータは左、新しいデータは右にしたい
- 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の値に対応させる」方法で対策します。
// 現在時刻を取得してラベルに追加します
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軸位置を取得します
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は「もっとTypeScriptの力を引き出そう」
JavaScriptの拡張言語でしょ?と思っているかたへ、Union型などTypeScriptの持つ秘めたる力を解説する特集となっています。
個人的には第2特集の「Ubuntuの現代的な使い方」がの方が気になりました。より詳しい内容は以下のリンクからご覧ください。
質問・要望 大歓迎です
「こんな解説記事作って」「こんなことがしたいけど、〇〇で困ってる」など、コメント欄で教えてください。 質問・要望に、中の人ができる限り対応します。
使えたよ・設定できたよの一言コメントも大歓迎。気軽に足跡を残してみてください。記事を紹介したい方はブログ、SNSにバシバシ貼ってもらってOKです。
えす様
こちらの質問をさせていただいた者です。
記事にまとめていただきありがとうございます。
X軸の値は数値にして、ラベルに数値に対応した日付文字列を表示する方法は思いつきませんでしたので非常に参考になりました。同様の方法をSignalPlotXYで実装した場合にも応用できないか念のため確認してみましたが、
SignalPlotXYだと負の方向に値が増えていく配列(X[0]=0,X[1]=-1,X[2]=-2)をデータプロット時に追加して
描画するとやはりエラーが出てしまいうまくいきませんでした。
なので左から右方向に描画されるグラフを実装する際は今回ご紹介頂いた場合はScatterPlottでの方法を使わさせて頂きます。ご協力ありがとうございました。
れんさん
コメントありがとうございます。
SignalPlotでの確認ありがとうございます。
マイナス方向に追加するとエラーになってしまうんですね。
ScatterPlot + 少々トリッキーなコードになってしまいましたが、
お役に立てたようで嬉しいです。
また何かあればコメント等でご連絡ください。