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!
そんな感じ。
