◆PROCESSING 逆引きリファレンス
カテゴリー:制御系
マルチウィンドウを表示するには
【解説】
PROCESSINGは手軽にビジュアルなプログラムが作成できる事が利点ですが、シングルウィンドウが基本となります。
PROCESSINGで複数のウィンドウ(マルチウィンドウ)を実現する方法については、これまで多くの方が実験をし、ブログなどに掲載されてきました。その経緯や内容については、下記サイト様などにまとめられており大変参考となります。
しかしちょっと内容が古いですので、本記事では上記サイト様とは別の方法を紹介したいと思います。
なお、PROCESSING 2.0 系と 3.0 系ではマルチウィンドウの実現方法が大きく異なります。この記事ではPROCESSING 3.0 系での実現方法を紹介しています。以下の記述は 2.0 系には当てはまらないので注意してください。
【詳細】
準備
まずはサブウィンドウとして動作する部分を、別クラスとして作成します。クラスの名前は任意ですが、必ず PApplet クラスを継承させておいてください(じゃないと、サブウィンドウ側がPROCESSINGのプログラムとして動作しませんからね:笑)。
またメインウィンドウとサブウィンドウを実行するクラスには、必ず settings() メソッドを実装し、画面サイズはこの中で変更するようにしてください。
標準エディタだけでコーディングをしている方は、普段 settings() メソッドの存在は意識することが少ないと思いますので、注意が必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
//------------------------ // ここはメインウィンドウ側 //------------------------ // settings を必ず実装する void settings() { // 画面サイズはこの中で変更する事 size(300, 300); } void setup() { } void draw() { } //------------------------ // ここはサブウィンドウ側 //------------------------ // PAppletを継承(extends)したクラスにする事 public class SecondApplet extends PApplet { // settings を必ず実装する void settings() { // 画面サイズはこの中で変更する事 size(200, 200); } void setup(){ } void draw() { } } |
たとえば上記のような感じです。
以降はこの雛形を元に説明をさせていただきますが、サブウィンドウ側が終了した時にどうしたいのかにより、プログラムの作りが若干異なってきます。
本記事では、サブウィンドウをSUB、メインウィンドウをMAINとして、以下の4パターンを考えます。
- SUB終了時、MAINも(つまりAPを)終了する
- SUBだけ終了。MAINではSUBが終了した事は気にしない
- SUBだけ終了。MAINではSUBが終了した事だけ分かればOK
- SUBだけ終了。終了時にSUBからMAINに処理結果を通知する
上から下にいくほど、難易度が上がります(汗)。
あとマルチウィンドウを行わせる前提として、PROCESSINGの内部構造に関する知識が必要となりますが、詳しくない方でも心配はいりません。簡単な機能しか利用しませんので(笑)。
簡単にいうと、PROCESSING(PApplet)は内部にPSurface と呼ばれるクラスを持っており、このPSurface から AnimationThread と呼ばれる別スレッドが起動されて、setupやdrawが呼び出されているという事です。
スレッド?と思われた方は、「マルチスレッドを実現するには」記事を参照してください。
上記に簡単な図にしてみましたが、ざっくり書くとこんな感じになっています。
PROCESSINGでコーディングしたプログラムを実行すると、私達の知らない所で main() => runSketch()=>settings()=>PSurfaceクラスの生成という順番で処理が流れているんですね。
覚えておいて欲しい事は、メインスレッドにあるPSurfaceクラスから AnimationThread と呼ばれる別スレッドが起動されていて、その中から setup() や draw() が呼び出されているという事です。
つまりPROCESSINGで作成したアプリケーションは、1つのウィンドウしか作らない場合でも、必ずメインスレッドとサブスレッドの2つが協力しながら動作しているのです。
そして私達が良くお世話になる setup() や draw() 関数は、サブスレッド側で動作しています。
今回はこのサブスレッド(下図のA)から、さらに別のウィンドウ(下図のBサブスレッド)を開きます。
(画像URL:illust-AC 様 wksさん)
サブウィンドウを作成するためには、サブウィンドウ用のクラスの実態(インスタンス)を作成した後に、サブスレッドの初期化を行って、ウィンドウを開かせる必要があります。
そしてその初期化処理は、PApplet内部にある runSketch() と呼ばれるメソッドにまとめられています。
では順番に説明しましょう。
ケース1:SUB終了時、MAINも終了する
一番簡単なケースですね。単純にマルチウィンドウが実現できれば良いという場合です。
まずメインウィンドウ側からサブウィンドウクラス(SecondApplet)を生成します。続いて、生成したサブウィンドウクラスを初期化します。
サブウィンドウが持つPAppletの初期化には、PAppletの runSketch メソッドを利用します。これが一番簡単で確実な方法です。
実はrunSketch メソッドは、メインウィンドウにおいては、アプリケーションが開始すると自動的に呼び出されているメソッドなのです。
ただしサブウィンドウは私達が独自に生成するものですので、自分達で runSketch() により初期化する必要があります。
このメソッドを呼び出せば、メソッド内部でPAppletに必要な初期化処理を全てやってくれます。おまけに static メソッドなので、PApplet . runSketch( … ); で簡単に呼び出せます。便利です。
.
PAppletを初期化するvoid PApplet . runSketch(final String[] args, PApplet constructedSketch);
args : 動作パラメータ(下記表を参照)
constructedSketch : サブウィンドウのPAppletインスタンス
argsにはPROCESSINGの動作に必要なパラメータを与えます。いくつかある中から使えそうなものを紹介します。
№ | 形式 | 説明 |
---|---|---|
1 | –location=x,y | Windowの初期位置を指定します |
2 | –present | 引数はありません。指定すると全画面表示になります |
3 | –window-color=#rrggbb | Windowの背景色がrgbで指定した色になります |
注意としては上記パラメータの最後に、必ずサブウィンドウにつける名前を指定する事です。
名前は何でも構いませんが、なるべく該当サブウィンドウを一意に特定するものが望ましいです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//------------------------ // ここはメインウィンドウ側 //------------------------ void setup() { // サブウィンドウの位置を指定する args = {"--location=100,200","SecondApplet"}; // サブウィンドウの位置を指定しないなら、以下でもOK //String[] args = {"SecondApplet"}; // サブウィンドウを開く SecondApplet sa = new SecondApplet(); PApplet.runSketch(args, sa); } |
こんな感じになります。コーディングの全体像は、下記サンプルを参照してください。
メインウィンドウ側の setup() メソッドの中から サブウィンドウクラスを生成し、runSketch() するだけです。とても簡単ですね。
下記サンプルを実行すると、上記のようにウィンドウが2つ表示されます。
注意点は、サブウィンドウを✕ボタンで閉じると、メインウィンドウも閉じられてしまう事です。つまりアプリケーションが終了してしまいます。
これはPROCESSINGに割り当てられているウィンドウの閉じるボタンの動作が、デフォルトではアプリケーションの終了になっている為です。
ケース2:SUBだけ終了。MAINではSUBが終了した事は気にしない
サブウィンドウの✕ボタンが押下された時に、アプリケーションが終了するのではなく、サブウィンドウだけを閉じるようにしたくはありませんか?
ケース1ではサブウィンドウ側の✕ボタンを押しただけなのに、メインウィンドウも閉じてしまいます。
これはPROCESSINGが、ウィンドウが閉じる際に自動的に実行されるメソッド(これをイベント関数と言います)に、アプリケーションの終了を割り当てているからです。
サブウィンドウの✕ボタンが押下された時にサブウィンドウだけを閉じるためには、ウィンドウが閉じる際に走るイベント関数を破棄して、私達が期待する動作を行う関数に書き換えてやる必要があります。
具体的には、以下の手順でプログラミングします。
- PSurface がもつ Windowフレームを取得
- 該当フレームから、getWindowListeners()でWindow操作用のイベントを取得
- 取得したイベントをremoveWindowListener()で削除
- 該当フレームに、addWindowListener()で新しいイベントをセット
PSurfaceは内部でウィンドウ(Windowフレーム)も管理しています。PSurfaceがもつWindowフレームは、以下のようにして取得します。
1 2 3 4 5 6 7 8 9 10 11 12 |
//------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { … // PSurface がもつ Windowフレームを取得 private JFrame getJFrame(){ return (JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)getSurface() .getNative()).getFrame(); } } |
新しいWindow クローズ動作は DISPOSE_ON_CLOSE に設定します。DISPOSE_ON_CLOSE は、登録されたClose用のイベントを実行した後で、Windowを非表示にして破棄するものです。
呼び出される新しいイベントは、WindowAdapterを継承した独自クラスとして作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
//------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { … private JFrame getJFrame(){ … } // Window操作用のイベントを再定義する private void redefineExitEvent() { // Windowフレームを取得する JFrame frame = getJFrame(); // 該当Windowから、全てのWindow操作用イベントを削除し // 新しいイベントに書き換える for (final java.awt.event.WindowListener evt : frame.getWindowListeners()){ // イベントを削除する frame.removeWindowListener(evt); // Window Close 動作を再定義する // → 登録されている任意の WindowListener を呼び出したあとで // 自動的にフレームを隠して破棄する frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE ); // 新しいWindow 操作イベントをセットする frame.addWindowListener(new WindowManage()); } } } //----------------------- // Window 操作イベントクラス //----------------------- class WindowManage extends WindowAdapter{ // Closeされた直後に動作する部分 public void windowClosed(WindowEvent e) { // 何某かの処理 } } |
一連の流れは、上記のようになります。
ウィンドウ操作イベントについては、JavaのAWTやSwingの知識が必要になりますが、この流れをむりやり図にすると以下のような感じになります。
(画像URL:illust-AC 様 ヤマモリポテコさん、acworksさん)
ちょっと強引な図ですが、JavaのWindows操作がよくわからない人でも、なんとなく何がやりたいのかイメージしていただけるのではないでしょうか?(笑)
上記サンプルではWindowAdapterを継承したクラス(WindowManage)にwindowClosed()をあてがっていますので、サブウィンドウが閉じられた直後のイベントを処理しています。
閉じられる直前のイベントを捕まえたい(例えば、本当に閉じるかどうか確認するダイアログボックスを開きたいなど)の場合は、windowClosed() ではなく windowClosing() を利用してください。
続いてwindowClosed()の中に何を記述するかですが、本ケースでは自分自身のスレッドを終了させる処理を記述する事になります。
メインウィンドウにサブウィンドウが終了した事を教えなくても良いわけですから、素直に自分自身を終了すれば良いのですね。
PROCESSINGは、PSurface から AnimationThread と呼ばれる別スレッドが起動される事で実行されています。つまりサブウィンドウはメインウィンドウとは別のスレッドになっているのです。
DISPOSE_ON_CLOSEはWindowを破棄してくれますが、Windowを表示しているスレッドを終了するわけではありません。今回はサブウィンドウを閉じるだけでなく、サブウィンドウを実行しているスレッドも停止したいので、自分自身のスレッドを終了させる必要があります。
AnimationThread を停止するには、サブウィンドウが持つ PSurfaceの stopThread() メソッドを利用します。
.
AnimationThread を停止するpublic boolean ret = sur . stopThread();
sur : PSurfaceインスタンス
ret : 戻り値。成功なら True
使い方は簡単です。終了させたい PApplet クラスから PSurface を取り出して、stopThread() メソッドを発行するだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { … // Window操作用のイベントを再定義する private void redefineExitEvent() { JFrame frame = getJFrame(); … //自分自身の終了のため、自分のPAppletを渡す frame.addWindowListener(new WindowManage(this)); } } //----------------------- // Window 操作イベントクラス //----------------------- class WindowManage extends WindowAdapter{ PApplet subApp; // コンストラクタ public WindowManage( PApplet _subApp ){ // サブウィンドウのPAppletを受け取る subApp = _subApp; } // Closeされた直後に動作する部分 public void windowClosed(WindowEvent e) { // 自分を終了する subApp.noLoop(); subApp.getSurface().stopThread(); } } |
本例では、Window操作イベントクラスのコンストラクタに終了対象となるPAppletクラスを受け取るようにし、WindowがCloseされたら自分自身を終了しています。
コーディングの全体像は、下記サンプルを参照してください。
ケース3:SUBだけ終了。MAINではSUBが終了した事だけ検知する
それではメインウィンドウ側でサブウィンドウ側の死活を知りたい場合は、どうすれば良いでしょうか?。
PROCESSINGは、PSurface から AnimationThread と呼ばれる別スレッドが起動されていると説明しましたが、PSurface の isStopped()メソッドを利用すると、この AnimationThread の死活状態を取得する事が可能です。
.
AnimationThread の死活状態を取得するpublic boolean ret = sur . isStopped();
sur : PSurfaceインスタンス
ret : 戻り値。生きているなら True
この isStopped() メソッドを、メインウィンドウから定期的に呼び出せば、サブウィンドウ側の死活が検査可能です。
コーディングの全体像は、下記サンプルを参照してください。
ケース4:SUBだけ終了。終了時にSUBからMAINに処理結果を通知する
最も難易度の高いケースです。とはいえ、これが最も良くあるケースなのではないでしょうか? (汗)。
メインウィンドウからサブウィンドウを開き、サブウィンドウ側で何かの処理や計算を行わせて、その結果をメインウィンドウ側で受け取るケースです。
サブウィンドウからメインウィンドウに処理結果を通知する方法は幾つか考えられますが、もっとも理想的なのはサブウィンドウ側からメインウィンドウにあるメソッドを呼び出して、結果を通知する事です。
Javaにはリフレクション (reflection) という仕組みがありますので、今回はこれを利用します。
Javaのクラスが実装している getMethod() というメソッドがあります。このgetMethod() を実行すると、指定したメソッドを呼び出すために必要な Methodクラスの情報が得られます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//------------------------ // ここはメインウィンドウ側 //------------------------ // サブウィンドウから終了ステータスを受け取るメソッド public void GetSubStatus(int status){ // 何かの処理 } //------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { … void setup(){ // メインウィンドウ側の終了ステータス受信メソッドを取得 try{ method = mainApplet.getClass().getMethod("GetSubStatus", int.class); } catch( NoSuchMethodException ex){ ex.printStackTrace(); } … } |
関連する部分を抜き出すと、こんな感じです。mainAppletはメインウィンドウのPAppletです。
サブウィンドウ側からメインウィンドウ側にある GetSubStatus という名前のメソッドを取得しています。
このMethodクラスの情報を元に、サブウィンドウ側からメインウィンドウ側のメソッドを呼び出します。
呼び出しには Method クラスが持っている invoke() メソッドを利用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { … // マウスクリックで終了する public void mouseClicked() { … // メインウィンドウのメソッドを呼び出す try{ // 終了コード(ここではフレームカウント)を渡す if( method != null ){ method.invoke(mainApplet, 100 ); } } catch(IllegalAccessException ex ){ ex.printStackTrace(); } catch(InvocationTargetException ex){ ex.printStackTrace(); } } } |
method は先程取得したメインウィンドウの GetSubStatus メソッド情報です。これを invoke() メソッドを利用して呼び出しています。
上記例では、GetSubStatusに100という値を渡します。
(画像URL:iilust-AC 様 うどんさん)
この流れを絵にすると、こんなイメージになります。
【関連記事】
サンプルプログラム
ケース1:SUB終了時、MAINも終了する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
/** * PROCESSING 3 マルチウィンドウSample * @auther MSLABO * @version 2018/09 1.0 */ //------------------------ // ここはメインウィンドウ側 //------------------------ void settings(){ size(300, 300); } void setup() { // 文字指定 textSize(24); textAlign(LEFT,TOP); //サブウィンドウを開く String[] args = {"SecondApplet"}; SecondApplet sa = new SecondApplet(); PApplet.runSketch(args, sa); } float mainTextX = 0; void draw() { // 適当なものを表示してみる background(0); fill(255); text("Main", mainTextX++, height/2 ); if(mainTextX > width){ mainTextX = 0; } } //------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { void settings() { size(200, 200); } void setup(){ // 文字指定 textSize(24); textAlign(LEFT,TOP); } float subTextX = 0; void draw() { // 適当なものを表示してみる background(255); fill(0); text("Sub", subTextX++, height/2 ); if(subTextX > width){ subTextX = 0; } } } |
実行すると2つのウィンドウが開きます。メインウィンドウを閉じても、サブウィンドウを閉じても、必ずアプリケーションが終了します。
ケース2:SUBだけ終了。MAINではSUBが終了した事は気にしない例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
/** * PROCESSING 3 マルチウィンドウSample * @auther MSLABO * @version 2018/09 2.0 */ import javax.swing.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; //------------------------ // ここはメインウィンドウ側 //------------------------ void settings(){ size(300, 300); } void setup() { // 文字指定 textSize(24); textAlign(LEFT,TOP); //サブウィンドウを開く String[] args = {"SecondApplet"}; SecondApplet sa = new SecondApplet(); PApplet.runSketch(args, sa); } float mainTextX = 0; void draw() { // 適当なものを表示してみる background(0); fill(255); text("Main", mainTextX++, height/2 ); if(mainTextX > width){ mainTextX = 0; } } //------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { void settings() { size(200, 200); } void setup(){ // Window操作イベントを書き換える redefineExitEvent(); // 文字指定 textSize(24); textAlign(LEFT,TOP); } float subTextX = 0; void draw() { // 適当なものを表示してみる background(255); fill(0); text("Sub", subTextX++, height/2 ); if(subTextX > width){ subTextX = 0; } } // PSurface がもつ Windowフレームを取得 private JFrame getJFrame(){ return (JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)getSurface() .getNative()).getFrame(); } // Window操作用のイベントを再定義する private void redefineExitEvent() { // Windowフレームを取得する JFrame frame = getJFrame(); // 該当Windowから、全てのWindow操作用イベントを削除し // 新しいイベントに書き換える for (final java.awt.event.WindowListener evt : frame.getWindowListeners()){ // イベントを削除する frame.removeWindowListener(evt); // Window Close 動作を再定義する // → 登録されている任意の WindowListener を呼び出したあとで // 自動的にフレームを隠して破棄する frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE ); // 新しいWindow 操作イベントをセットする frame.addWindowListener(new WindowManage(this)); } } } //----------------------- // Window 操作イベントクラス //----------------------- class WindowManage extends WindowAdapter{ PApplet subApp; // コンストラクタ public WindowManage( PApplet _subApp ){ subApp = _subApp; } // Closeされた直後に動作する部分 public void windowClosed(WindowEvent e) { // 自分を終了する subApp.noLoop(); subApp.getSurface().stopThread(); } } |
サブウィンドウの✕ボタン押下時、サブウィンドウ側だけ終了するようにしています。
<出力サンプル>
jconsole でスレッドの数を監視しました。サブウィンドウ終了時、ちゃんとスレッド数が減少しているのがわかります。
ケース3:SUBだけ終了。MAINではSUBが終了した事だけ検知する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
/** * PROCESSING 3 マルチウィンドウSample * @auther MSLABO * @version 2018/09 3.0 */ import javax.swing.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; //------------------------ // ここはメインウィンドウ側 //------------------------ SecondApplet sa = null; void settings(){ size(300, 300); } void setup() { // 文字指定 textSize(24); textAlign(LEFT,TOP); } float mainTextX = 0; void draw() { // 適当なものを表示してみる background(0); fill(255); // サブウィンドウの死活により表示を変化させる if(sa != null && !sa.getSurface().isStopped()){ // サブウィンドウが生きている text("Main", mainTextX++, height/2 ); if(mainTextX > width){ mainTextX = 0; } } else { // サブウィンドウは停止している text("SubApplet is Stop", 0, height/2 ); if( sa != null ){ sa .dispose(); sa = null; } } } // マウスクリックでサブウィンドウ生成 void mouseClicked(){ if( sa == null ){ //サブウィンドウを開く String[] args = {"SecondApplet"}; sa = new SecondApplet(); PApplet.runSketch(args, sa); } } //------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { void settings() { size(200, 200); } void setup(){ // Window操作イベントを書き換える redefineExitEvent(); // 文字指定 textSize(24); textAlign(LEFT,TOP); } float subTextX = 0; void draw() { // 適当なものを表示してみる background(255); fill(0); text("Sub", subTextX++, height/2 ); if(subTextX > width){ subTextX = 0; } } // PSurface がもつ Windowフレームを取得 private JFrame getJFrame(){ return (JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)getSurface() .getNative()).getFrame(); } // Window操作用のイベントを再定義する private void redefineExitEvent() { // Windowフレームを取得する JFrame frame = getJFrame(); // 該当Windowから、全てのWindow操作用イベントを削除し // 新しいイベントに書き換える for (final java.awt.event.WindowListener evt : frame.getWindowListeners()){ // イベントを削除する frame.removeWindowListener(evt); // Window Close 動作を再定義する // → 登録されている任意の WindowListener を呼び出したあとで // 自動的にフレームを隠して破棄する frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE ); // 新しいWindow 操作イベントをセットする frame.addWindowListener(new WindowManage(this)); } } } //----------------------- // Window 操作イベントクラス //----------------------- class WindowManage extends WindowAdapter{ PApplet subApp; // コンストラクタ public WindowManage( PApplet _subApp ){ subApp = _subApp; } // Closeされた直後に動作する部分 public void windowClosed(WindowEvent e) { // 自分を終了する subApp.noLoop(); subApp.getSurface().stopThread(); } } |
ケース2に比べて、マウスクリックでサブウィンドウを生成するように変更しています。
メインウィンドウの draw() で定期的にサブウィンドウの死活を監視し、状態を画面に表示します。
ケース4:SUBだけ終了。終了時にSUBからMAINに処理結果を通知する例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
/** * PROCESSING 3 マルチウィンドウSample * @auther MSLABO * @version 2018/09 4.0 */ import javax.swing.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; //------------------------ // ここはメインウィンドウ側 //------------------------ SecondApplet sa = null; int exitCode = -1; //サブ側終了ステータス float mainTextX = 0; void settings(){ size(300, 300); } void setup() { // 文字指定 textSize(24); textAlign(LEFT,TOP); //サブウィンドウを開く String[] args = {"SecondApplet"}; sa = new SecondApplet(this); PApplet.runSketch(args, sa); } void draw() { background(0); fill(255); // サブウィンドウが終了したか監視する if( sa != null && !sa.getSurface().isStopped()){ // サブウィンドウが生きている text("Main", mainTextX++, height/2 ); if(mainTextX > width){ mainTextX = 0; } } else { // サブウィンドウは停止した text("Sub is stoped. \nCode=" + exitCode, 0, height/2 ); if( sa != null ){ sa.dispose(); sa = null; } } } // サブウィンドウから終了ステータスを受け取るメソッド public void GetSubStatus(int status){ exitCode = status; } //------------------------ // ここはサブウィンドウ側 //------------------------ public class SecondApplet extends PApplet { PApplet mainApplet; int exitStatus = 0; Method method = null; float subTextX = 0; // コンストラクタ public SecondApplet(PApplet _mainApplet){ mainApplet = _mainApplet; } void settings() { size(200, 200); } void setup(){ // メインウィンドウ側の終了ステータス受信メソッドを取得 try{ method = mainApplet.getClass().getMethod("GetSubStatus", int.class); } catch( NoSuchMethodException ex){ ex.printStackTrace(); } // Window操作イベントを書き換える redefineExitEvent(); // 文字指定 textSize(24); textAlign(LEFT,TOP); } void draw() { // 適当なものを表示してみる background(255); fill(0); text("Sub", subTextX++, height/2 ); if(subTextX > width){ subTextX = 0; } } // マウスクリックで終了する public void mouseClicked() { // メインウィンドウのメソッドを呼び出す try{ // 終了コード(ここではフレームカウント)を渡す if( method != null ){ method.invoke(mainApplet, frameCount ); } } catch(IllegalAccessException ex ){ ex.printStackTrace(); } catch(InvocationTargetException ex){ ex.printStackTrace(); } // 自分自身のWindowを閉じる JFrame frame = getJFrame(); frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); } // PSurface がもつ Windowフレームを取得 private JFrame getJFrame(){ return (JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)getSurface() .getNative()).getFrame(); } // Window操作用のイベントを再定義する private void redefineExitEvent() { // Windowフレームを取得する JFrame frame = getJFrame(); // 該当Windowから、全てのWindow操作用イベントを削除し // 新しいイベントに書き換える for (final java.awt.event.WindowListener evt : frame.getWindowListeners()){ // イベントを削除する frame.removeWindowListener(evt); // Window Close 動作を再定義する // → 登録されている任意の WindowListener を呼び出したあとで // 自動的にフレームを隠して破棄する frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE ); // 新しいWindow 操作イベントをセットする frame.addWindowListener(new WindowManage(this)); } } } //----------------------- // Window 操作イベントクラス //----------------------- class WindowManage extends WindowAdapter{ PApplet subApp; // コンストラクタ public WindowManage( PApplet _subApp ){ subApp = _subApp; } // Closeされた直後に動作する部分 public void windowClosed(WindowEvent e) { // 自分を終了する subApp.noLoop(); subApp.getSurface().stopThread(); } } |
サブウィンドウをマウスクリックすると、サブウィンドウが終了します。この時、サブウィンドウのフレームカウントをメインウィンドウに渡します。
サブウィンドウを✕ボタンで終了するとフレームカウントが渡されないため、メインウィンドウでは終了コードが -1 のままとなります。
<出力サンプル>
(画像URL:iilust-AC 様 うどんさん)
本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。