[更新情報]

▽Go to footer

gnuplot の C,C#,VB からの使い方のメモを整理しておくことにしました.

gnuplot + C:基礎 目次

MinGW Cから使う(popen と gnuplot.exe の利用)

概要

Windows で動く MinGW gcc の stdio.h にある popen 関数を使い,gnuplot を開いてコマンドを送り込みます. コマンドの送り方は,fprintf でテキスト・数値を送ります. なおpopenで開くのは「wgnuplot.exe」ではなく「gnuplot.exe」です.古いバージョンでは「pgnuplot.exe」を使う必要があったようですが,pgnuplot.exe は wgnuplot.exe を呼び出していてエラーがでた時に画面に残ったままとなりトラブルの原因になるので,「gnuplot.exe」のほうがよいようです. また最後に,「fflush(fp);」および「pclose(fp);」を忘れずにつけます.

FILE *fp;
fp=popen("gnuplot.exe","w");
    fprintf(fp,"set terminal png\n");
    ・・・・・
    fflush(fp);
pclose(fp);

コンパイル後,入力ファイル・出力ファイルがあれば,それらを含むコマンドを実行します. 次の例は,下のサンプルプログラムのコンパイルと実行の指令です.

>gcc -o gccGPtest.exe gccGPtest.c (gccGPtest.cをコンパイルして実行ファイルgccGPtest.exeを作る)
>gccGPtest                        (gccGPtest.exeを実行)


サンプルプログラム

以下のサンプルプログラムは,「sin(x)」のグラフを書かせて,プログラム中指定した出力ファイル「fig_GPtest.png」に画像を出力するものです.


/*---------------------------*/
/* プログラム:gccGPtest.c */
/*---------------------------*/
#include <stdio.h>
/*---------------------------------------------------------------------------*/

int main()
{
    char fnameW[]="fig_GPtest.png"; /*出力ファイル名*/
    FILE *fp;

    fp=popen("gnuplot.exe","w");
        fprintf(fp,"set terminal png\n");
        fprintf(fp,"set output \"%s\"\n",fnameW);
        fprintf(fp,"plot sin(x) with lines\n");
        fflush(fp);
    pclose(fp);

    printf("リターンキーを押してください");
    getchar();
    return 0;
}
/*---------------------------------------------------------------------------*/

出力図(ファイル名:fig_GPtest.png)

png



「plot "-" 」の読み込み配列行数の限界は小さい?

「plot "-"」は,その後に数値データをつなげてプロットできる機能ですが,簡単なプログラムで試したところ,読み込む配列の行数が多くなると,途中で wgnuplot が立ち上がり「e」の入力を催促してきました. ここで wgnuplot の画面上で「e」を入力すると読み込み可能であった範囲までのグラフを作成してくれます. 読み込み可能行数は,PC のメモリなど環境により多少異なりますが,WANtaro のテストでは460〜470行程度でした.

いきなり30000行とかを読み込ませようとすると wgnuplot は暴走する場合があるので注意.

なお,これはwgnuplot.exeを使うpgnuplot.exeの場合であり,gnuplot.exeを使えば問題ないようです.


/*---------------------------*/
/* プログラム:gccGParray1.c */
/*---------------------------*/
#include <stdio.h>
#define Nmax 32768
/*---------------------------------------------------------------------------*/

int main()
{
    char fnameW[]="fig_GParray1.png"; /*出力ファイル名*/
    FILE *fp;
    int ndata,i;
    double datax[Nmax];
    double datay[Nmax];

    ndata=1000;
    for(i=0;i<=ndata;i++){
        datax[i]=(double)i;
        datay[i]=datax[i];
    }

    fp=popen("pgnuplot.exe","w");
        fprintf(fp,"set terminal png\n");
        fprintf(fp,"set output \"%s\"\n",fnameW);
        fprintf(fp,"plot \"-\" with points notitle\n");
        for(i=0;i<=ndata;i++){
            fprintf(fp,"%f %f\n",datax[i],datay[i]);
            fflush(fp); /*有っても無くても同じ結果です*/
        }
        fprintf(fp,"e\n");
        fflush(fp);
    pclose(fp);

    printf("リターンキーを押してください");
    getchar();
    return 0;
}
/*---------------------------------------------------------------------------*/

出力図(ファイル名:fig_GParray1.png)

png



一時ファイルにデータを落として読み込ませると・・・

下記のように,一時ファイルにデータを落とし,これを「plot "temp.prn"」で読み込みプロットさせると100000行までは問題なく動作しました.これなら WANtaro 的には実用上問題ないかと思います.

なお,プログラム的には「remove("temp.prn");」で一時ファイルを消去したいところですが,実行速度と関連するのか,remove をいれるとうまく作図できない場合がありますので,あえて一時ファイルの削除は行っていません.


/*---------------------------*/
/* プログラム:gccGParray2.c */
/*---------------------------*/
#include <stdio.h>
#define Nmax 100000
/*---------------------------------------------------------------------------*/

int main()
{
    char fnameW[]="fig_GParray2.png"; /*出力ファイル名*/
    FILE *fp;
    int ndata,i;
    double datax[Nmax];
    double datay[Nmax];

    ndata=99999;
    for(i=0;i<=ndata;i++){
        datax[i]=(double)i;
        datay[i]=datax[i];
    }

    fp=fopen("temp.prn","w");
        for(i=0;i<=ndata;i++){
            fprintf(fp,"%f %f\n",datax[i],datay[i]);
        }
    fclose(fp);

    fp=popen("pgnuplot.exe","w");
        fprintf(fp,"set terminal png\n");
        fprintf(fp,"set output \"%s\"\n",fnameW);
        fprintf(fp,"plot \"temp.prn\" with points notitle\n");
        fflush(fp);
    pclose(fp);

    printf("リターンキーを押してください");
    getchar();
    return 0;
}
/*---------------------------------------------------------------------------*/

出力図(ファイル名:fig_GParray2.png)

png



C# から使う(Process,StandardInput と gnuplot.exe の利用)

C#では,コーディングは面倒なものの比較的簡単に独自に画像ファイルを作れると思います.でも「C# から gnuplot を使おう!」ということも考え,サンプルコードを作ってみました.

C# から使う場合は,他のアプリケーションを開く場合などに利用する「System.Diagnostics.Process」クラスと「Process.StandardInput」プロパティを用います(http://msdn.microsoft.com/ja-jp/library/system.diagnostics.process.standardinput(VS.80).aspx にコーディング事例があります).また,「gnuplot.exe」を呼び出します.「wgnuplot.exe」だと wgnuplot が立ち上がるだけで命令を読み込んでくれません.

なお,gnuplot に送り込むファイルパス名には,「\」マークを2個つけています.このため C# での記載は「\」マーク4個です.VB では「\」マークは2個で ok です.ここでは,変数「dir」にカレントディレクトリを取得し,「Replace」で1個の「\」マークを2個に置換しています.

dir = System.IO.Directory.GetCurrentDirectory();
fname = "out_vcs_test.png";
fnameW = dir.Replace("\\", "\\\\") + "\\\\" + fname;

C# の Form (pictureBox) に読み込む時には,「\」マークを 1 個にする必要があるため,再度「Replace」で置換を行っています.

fnameW = fnameW.Replace("\\\\", "\\");

また,gnuplot を終了して C# の Form (pictureBox) に読み込む時,ファイル書き込み時間を確保するため,sleep で時間稼ぎをしています.

System.Threading.Thread.Sleep(1000); //1秒待つ

以下に,C# から使用する場合のサンプルプログラムを示します.png ファイルを作成し,C# の Form (pictureBox) に呼び込んで表示するものです. パスはカレントディレクトリに指定していますので,画像ファイルは,C# の実行ファイルのあるフォルダに落ちています.

使用コントロールは,toolStrip1(Button1)とpictureBox1です.

C#-project

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

namespace vcsGPtest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process pr;
            System.IO.StreamWriter sw;
            string dir="";
            string fnameW="";
            string fname="";

            pr = new System.Diagnostics.Process();
            pr.StartInfo.FileName = "gnuplot.exe";
            pr.StartInfo.UseShellExecute = false;
            pr.StartInfo.RedirectStandardInput = true;
            pr.Start();

            dir = System.IO.Directory.GetCurrentDirectory();
            fname = "out_vcs_test.png";
            fnameW = dir.Replace("\\", "\\\\") + "\\\\" + fname;
            Console.WriteLine(fnameW);

            sw = pr.StandardInput;
            sw.WriteLine("set terminal png");
            sw.WriteLine("set output \"" + fnameW + "\"");
            sw.WriteLine("plot besj0(x) with lines");
            sw.Close();
            pr.WaitForExit();
            pr.Close();

            System.Threading.Thread.Sleep(1000); //1秒待つ
                             
            fnameW = fnameW.Replace("\\\\", "\\");
            Console.WriteLine(fnameW);
            Image img;
            img = Image.FromFile(fnameW);
            pictureBox1.Size=new Size(img.Width,img.Height);
            pictureBox1.Left = 0;
            pictureBox1.Top = toolStrip1.Height;
            this.ClientSize = new Size(pictureBox1.Width, pictureBox1.Height + toolStrip1.Height);
            pictureBox1.Image = img;

        }
    }
}

実行結果画面(ファイル名:scr_shot.png)

png



VB から使う(Process,StandardInput と gnuplot.exe の利用)

C# のついでに,VB から使うサンプルコードも作りました.C# のコードを VB に変えただけです.

VB-project

Public Class Form1

    Private Sub ToolStripButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripButton1.Click

        Dim pr As New System.Diagnostics.Process()
        Dim sw As System.IO.StreamWriter
        Dim dir As String = ""
        Dim fnameW As String = ""
        Dim fname As String = ""

        pr.StartInfo.FileName = "gnuplot.exe"
        pr.StartInfo.UseShellExecute = False
        pr.StartInfo.RedirectStandardInput = True
        pr.Start()

        fname = "out_vcs_test.png"
        dir = System.IO.Directory.GetCurrentDirectory()
        fnameW = dir.Replace("\", "\\") & "\\" & fname
        Console.WriteLine(fnameW)

        sw = pr.StandardInput
        sw.WriteLine("set terminal png")
        sw.WriteLine("set output """ & fnameW & """")
        sw.WriteLine("plot besj0(x) with lines")
        sw.Close()
        pr.WaitForExit()
        pr.Close()

        System.Threading.Thread.Sleep(1000) '1秒待つ

        fnameW = fnameW.Replace("\\", "\")
        Console.WriteLine(fnameW)
        Dim img As Image
        img = Image.FromFile(fnameW)
        PictureBox1.Size = New Size(img.Width, img.Height)
        PictureBox1.Left = 0
        PictureBox1.Top = ToolStrip1.Height
        Me.ClientSize = New Size(PictureBox1.Width, PictureBox1.Height + ToolStrip1.Height)
        PictureBox1.Image = img

    End Sub
End Class


inserted by FC2 system