以下のプログラムでは、
シミュレーションにおいて、開始ボタンをおして時間発展の描画を始めたとする。逐次実行のプログラムでは時間発展画面だけがアクティブになり、コントロールフレーム内のボタンを押しても反応してくれない。
2つのフレームを同時にアクティブにするには、時間発展計算用に新しいスレッド(thread, 櫛)を作成し(つまり処理を枝分かれさせ)、計算が進んでいる間も、メインのGUIのリスナーも活性状態にしておくという手法が取られる。
以降のプログラムでは、
ここでは描画フレームの練習として、3.を新規にdrawFigure.javaという名で準備する。
/** * ボタン、スライダなどでパラメータ設定するサンプル * step2: ボタンやスライダにリスナーをつける * step3: シミュレーション動画用ウィンドウを開く(別スレッド) **/ import java.awt.*; import javax.swing.*; import java.awt.event.*; // リスナー用 import javax.swing.event.*; // Runnableインターフェイスを装着(インプリメント)step3 public class sample_gui extends JPanel implements Runnable { /******* step3: for thread ***************/ private Thread myTh; private boolean halt_; /** グラフィック関係変数 */ 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 startBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { /** * startボタンが押されたら、ゲームオブジェクトを作成し、 * スレッドをスタートさせる */ 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() { this.simulationFrame.drawCircles(); } } /** step4での課題: シミュレーションの本体部分のクラスを作成し、 それを描画用オブジェクトとつなげて、スナップショットを描く */
// /* シミュレーションのスナップショット描画 */ 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() { // 画面を白で塗りつぶす g.setColor(new Color(255,255,255)); g.fillRect(0, 0, gWin.getWidth(),gWin.getHeight()); // 適当に丸を書く for(int n = 0; n<3;n++){ double centerX = Math.random(); double centerY = Math.random(); 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); } } }
Graphicsクラスの中に、基本的な図形(線、楕円、長方形など)の描画メソッドが用意されている。 詳しくは、マニュアルを参照してほしい。 https://docs.oracle.com/javase/jp/7/api/java/awt/Graphics.html
など。