GUI tutorial for numerical simulation (4)

シミュレーション本体

シミュレーションモデル本体(ここではXXXmodel.javaとし、クラス名も同名とする)を作成する。必須のものは以下のとおり。

/**
 *  シミュレーションモデルの本体
 **/

import java.io.*;
import java.util.*;

public class XXXmodel {

    // クラス内グローバル変数の定義
    protected int alpha;
    protected double beta;
    public int N=5;
    public double x[], y[];

    /******* Constructor ****************/
    XXXmodel(int a, double b) {
	x = new double[N]; y =new double[N];
	setParameters(a, b);
    }
    /** end of Constructor */

    /** パラメータの設定 */
    public void setParameters(int a, double b) {
	alpha = a;
	beta = b;
    }
    /** 初期条件 */
    public void setInitialCondition() {

    }

    /** 時間変化プロセス */
    public boolean updateSystem() {
	for(int i=0; i<N; i++) {    // サンプル
	    x[i] = Math.random();
	    y[i] = Math.random();
	}
	return true;
    }

}

GUIでの処理

/**
 *  ボタン、スライダなどでパラメータ設定するサンプル
 *  step2: ボタンやスライダにリスナーをつける
 *  step3: シミュレーション動画用ウィンドウを開く(別スレッド)
 *  step4: シミュレーション本体 XXXmodelを実装し
 *         スタートボタンのリスナー内でオブジェクト生成
 *         run()において繰り返し実行
 **/

import java.awt.*;
import javax.swing.*;

import java.awt.event.*;       // リスナー用
import javax.swing.event.*;

// Runnableインターフェイスを装着(インプリメント)step3
public class sample_gui extends JPanel implements Runnable {

    /******* step4: thread variables ***************/
    private Thread myTh;
    private boolean halt_;
    /******* implementing simulation model *********/
    private XXXmodel XXX;

    /** グラフィック関係変数 */
    JButton startBtn, stopBtn, exitBtn;       // ボタン
    JLabel label_1, label_2;                  // ラベル
    JSlider slider_1, slider_2;                // スライダ

    /** 各種グローバル */
    static int maxSize = 600; // width of window in pixels
    static double scale_2 = 100;  // スライダ2の値の単位

    /** step3: シミュレーション描画用 中身はdrawFigure.javaで定義 */
    drawFigure simulationFrame;


    /************* コンストラクタ ************************/
    sample_gui() {

        /** step3: シミュレーションフレームの生成 */
        simulationFrame = new drawFigure(500); // サイズを引数に

        /** パラメータ初期値 */
        int  ini_1 = 500, ini_2 = 10;

        /** ボタン生成 */
        startBtn = new JButton("start");
        stopBtn = new JButton("stop");
        exitBtn = new JButton("終了");
        /** スライドバー生成 (値は整数)*/
        slider_1 = new JSlider(JSlider.HORIZONTAL, 0, 1000, ini_1);
        slider_2 = new JSlider(JSlider.HORIZONTAL, 0, 100, ini_2);

        /** ラベル生成 */
        label_1 = new JLabel(String.valueOf(ini_1));
        label_2 = new JLabel(String.valueOf(ini_2/scale_2));


        /** スライダーやボタンにリスナーをつける */
        // step3 + 4
        startBtn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    /**
                     * startボタンが押されたら、ゲームオブジェクトを作成し、
                     * スレッドをスタートさせる
                     */
		    /** step4: get parameters and create simulation object ***/
		    int a = slider_1.getValue();
		    double b = (double) slider_2.getValue()/scale_2;
		    XXX = new XXXmodel(a,b);

                    thread_start();
                }
            });

        exitBtn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    System.exit(0);
                }
            });

        slider_1.addChangeListener(new ChangeListener() {
                public void stateChanged(ChangeEvent e) {
                    label_1.setText(Integer.toString(slider_1.getValue()));
                }
            });
        slider_2.addChangeListener(new ChangeListener() {
                public void stateChanged(ChangeEvent e) {
                    label_2.setText(Double.toString(slider_2.getValue()/scale_2));
                }
            });

        /***********************************************/

        /** パネルにスライドバー、ラベルを貼る */
        JPanel panel_operation = new JPanel(); //ボタン3つを一つのJPanelに
        panel_operation.add(startBtn);
        panel_operation.add(stopBtn);
        panel_operation.add(exitBtn);

        JPanel panel_1 = new JPanel(); // ラベル、スライダ、値表示を一つのJpanelに
        panel_1.add(new JLabel("パラメータ1"));
        panel_1.add(slider_1);
        panel_1.add(label_1);

        JPanel panel_2 = new JPanel();
        panel_2.add(new JLabel("パラメータ2"));
        panel_2.add(slider_2);
        panel_2.add(label_2);

        /** 親パネルにボタンと、スライドバー・ラベル を並べる */
        JPanel p = new JPanel();
        p.setLayout(new GridLayout(2, 2)); // 2行2列に設定
        p.add(panel_operation);            // 上で作ったJPanelを並べる
        p.add(panel_1);
        p.add(panel_2);

        add(p, BorderLayout.NORTH);       // 親フレームに置く

    }

    /** main */
    public static void main(String[] args) {

        // パラメータセットのGUIを作成し、表示
        JFrame setParamFrame = new JFrame("パラメータセット"); // 親フレーム
        sample_gui ctr = new sample_gui();  // このクラスの実体を作る(実体名: ctr)
        setParamFrame.getContentPane().add(ctr, BorderLayout.CENTER); // 親フレームに「実体」を貼る
        setParamFrame.setSize(750, 100);    // フレームの大きさ設定
        setParamFrame.setLocation(600, 20); // フレームを置く位置
        setParamFrame.setVisible(true);     // 見えるようにする
    }

    /*********************step3での追加 *****************************/
    /** GUIで操作できるようにrunnableインターフェイスに基づいてスレッドを操作をする */
    public void thread_start() { /** startボタンにより呼び出す */
        halt_ = false;
        myTh = new Thread(this);
        myTh.start(); /** start()によりrun()が実行される */
    }

    public void stop() { /** stopボタンで呼び出す */
        halt_ = true;
        myTh.interrupt();
    }

    /** 新しいスレッドの実体 */
    public void run() {

	for(int i=0; i< 10; i++) {
	    XXX.updateSystem();
	    simulationFrame.drawCircles(XXX); // arg. is the simulation model object
	}
    }
}

/**
 * step5での課題:
 *   ストップボタンで中断
 *   再スタートボタンで再開
 *   スタートボタンで、パラメータを読み込んで新規にスタート
*/

描画クラスの手直し

描画メソッドは、シミュレーション本体オブジェクトを引数を通じて受け取り、車の位置などの値を参照して描画するように書き換える。

/** シミュレーションのスナップショット描画 */
import java.io.*;
import java.awt.*; // uses the abstract windowing toolkit
import javax.swing.*;
import javax.imageio.ImageIO;         // スナップショットのセーブなどに利用
import java.awt.image.BufferedImage;

public class drawFigure extends JPanel {

    Graphics g;              // 車の配置などを書き込むカンバス
    JFrame gWin;
    double screenWidth;
    int frameTop, frameLeft; // platform-dependent width of frame's top and left
    int marginX=20, marginY=20;

    /** コンストラクタ */
    drawFigure(int fSize) {
    	screenWidth = (double) fSize;

    	// シミュレーション本体画面の初期化
    	gWin = new JFrame("Trafic Flow Model");
    	gWin.setLocation(20, 150); // screen coordinates of top left corner
    	gWin.setResizable(false);
    	gWin.setVisible(true); // show it!

	/** 下30ピクセルは文字出力用inset*/
    	Insets theInsets = gWin.getInsets();
    	gWin.setSize(fSize + theInsets.left + theInsets.right + marginX*2, fSize
		     + theInsets.top + marginY*2+ theInsets.bottom+30);
    	this.frameTop = theInsets.top;
    	this.frameLeft = theInsets.left;
    	this.g = gWin.getGraphics(); // 画像をここに書き込む
    }

    public void drawCircles(XXXmodel XXX) { // step4: arg. is the simulation model

	// 画面を白で塗りつぶす
    	g.setColor(new Color(255,255,255));
    	g.fillRect(0, 0, gWin.getWidth(),gWin.getHeight());

	// 適当に丸を書く
    	for(int n = 0; n< XXX.N ;n++){
	    double centerX = XXX.x[n];
	    double centerY = XXX.y[n];
	    this.g.setColor(new Color(255,0,0)); // 色の指定
	    double nX =  screenWidth * centerX +this.frameLeft+this.marginX;
	    double nY =  screenWidth * centerY +this.frameTop + this.marginY;
	    this.g.fillOval((int) nX, (int) nY, 30, 30);
	}
	// 1秒休む
	try{
	    Thread.sleep(1000);
	}catch (InterruptedException e){
	}
    }
}

1. あるオブジェクト内の変数は、多くの場合、外から書き換えられないようにprivateあるいはprotect宣言しておき、その値を外から参照するためのメソッド(getterメソッドと通称)を用意する。ここでは簡便のためにpublic宣言しておく。