JavaFX を Jython から使ってみる
今日本屋さんにいってJythonの本を見かけた。
おおかた2年前に、すこしさわってたらバグがあって、開発者のウズベッキさんにTwitter から返事をいただいたのが懐かしい。
しばらく触ってないなーというか、Jython って何に使えるんだろーなーとか思いつつ、そういえば、前はSwing とかうごかしてたなー
今なら、JavaFXか。Python スクリプトから、JavaFX 動かせたら便利かしら。
JavaFXも、全然触ってないので、Jython から、JavaFXしてみるかな。
JavaFX SDK のダウンロード
ということで、
JDK 7 update 4 では、JavaFX 2.1 SDK (およびランタイム) が同梱されている。
http://www.oracle.com/technetwork/java/javase/downloads/
JDK 6 で JavaFX 2.1 SDK(およびランタイム) のインストーラ単独で必要な場合、
http://www.oracle.com/technetwork/java/javafx/downloads/
らしい。この間、JDK 7 update 3 入れたところだけど、u4を入れ直す。
インストーラを起動すると、JavaFX のSDK がインストールされる旨、表示される。
JDK のインストールが終わると、JavaFX SDK のインストールが開始
まずは、Java でテスト
プロジェクトのプロパティの、Java Build Path の Libraries タブから、Add External JARs を選択して、JavaFX SDK をインストールしたパスの配下から、jfxrt.jar を選択。
自分の場合は、
C:\Program Files (x86)\Oracle\JavaFX 2.1 SDK\rt\lib\jfxrt.jar
チュートリアルに従って、こんちわ JavaFX作成
http://docs.oracle.com/javafx/2/get_started/hello_world.htm
package info.typea.javafx.test; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class HelloJavaFx extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Hello JavaFX"); Button btn = new Button(); btn.setText("Hello"); btn.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { System.out.println("Hello JavaFX!"); } }); StackPane root = new StackPane(); root.getChildren().add(btn); primaryStage.setScene(new Scene(root, 300, 250)); primaryStage.show(); } }
当然ながら、こちらは難なく動く。
Jython から JavaFXを使う
Jython 自体のインストールは、2年前とほとんど変わっていない。
あと、Eclipse PyDev プラグイン を利用する。
アップデート サイトは、http://pydev.org/updates/
手順は、こちら。
上記手順で、インタプリタに、Python を指定しているところを、Jython のパスを指定し、ライブラリのパスを指定するときに New Jar/Zip(s) から、上記Javaの場合同様、JavaFX の、jfxrt.jar を指定する。
後は、何となく、上記Java のコードを移植する。
はまり(注意)点
- 基底クラスのメソッドを呼ぶ場合、Java と違って、super() を利用する必要がありますよと
- 無名クラスは、単なる関数に置き換えられますよと
- Application の launch メソッドに String[] をどうしても渡せなかった。jarray.array(list, java.lang.String) をあれやこれや試行錯誤したけど、TypeError: launch(): 1st arg can’t be coerced to String[] がでてどうしようもない。String(“”) を渡してお茶を濁しました。
- Application の launch メソッドには、String[] のみの引数をとるもの(Java の例はこちら)と、Class と String[] をとるものがある。String[] のみの場合、Class を内部で解決しているのだが、Thread.currentThread().getStackTrace() から取得した、StackTraceElement の2つ目を利用している。Javaの場合は、いいのだろうが、Jythonの場合、想定した、Applicationのサブクラスが取得ではなく、sun.reflect.NativeMethodAccessorImpl が取得されている。数時間はまった。うーんこんなコードアリなのかな?Jython側で確認した内容は以下な感じ。
#確認したコード sts = Thread.currentThread().getStackTrace(); for st in sts: print st.getClassName() #出力 java.lang.Thread sun.reflect.NativeMethodAccessorImpl sun.reflect.NativeMethodAccessorImpl sun.reflect.DelegatingMethodAccessorImpl java.lang.reflect.Method org.python.core.PyReflectedFunction org.python.core.PyReflectedFunction org.python.core.PyObject org.python.core.PyObject org.python.core.PyMethod org.python.pycode._pyx0 :
上記を踏まえ、Jython のコード
# -*- coding: utf-8 -*- from javafx.application import Application from javafx.scene.control import Button from javafx.scene.layout import StackPane from javafx.scene import Scene from java.lang import String def btn_click_handler(event): print "hello!" class HelloFx(Application): def main(self): # パラメータにString配列を渡せない!? super(HelloFx, self).launch(self.__class__, String("")) def start(self, primaryStage): primaryStage.setTitle("Hello JavaFX") btn = Button() btn.setText("Hello") btn.setOnAction(btn_click_handler) root = StackPane() root.getChildren().add(btn) primaryStage.setScene(Scene(root, 300, 250)) primaryStage.show() if __name__ == '__main__': h = HelloFx() h.main()
実行してみた
ちゃんと動きつつ、ボタンのクリックで、標準出力もされている。
コマンドラインから
- クラスパスを設定して、実行でOK
- jython コマンドのパラメータに、クラスパスは指定できなさそう。。。
C:\Users\piroto\workspace372\jython_test>set CLASSPATH="C:\Program Files (x86)\O racle\JavaFX 2.1 SDK\rt\lib\jfxrt.jar" C:\Users\piroto\workspace372\jython_test>jython hello_fx.py Device "Intel(R) HD Graphics" (\\.\DISPLAY1) initialization failed : WARNING: bad driver version detected, device disabled. Please update your driver to at least version 8.15.10.2302 hello!
そんな感じ。