17
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Go言語でJavaFXを動かしてみた

Posted at

GopherJSを使って、Go言語のコードをJSに変換し、Nashornを使うことで、JavaFXを動かしてみた。
同様の内容を天下一altjs武闘会で話した。

GopherJS

GopherJSは、Go言語のコードからJSのコードを生成するツールである。
go/astパッケージが使われている。
go get でインストールする。

$ go get github.com/gopherjs/gopherjs

GopherJSは、Node.jsかブラウザで動くJSを履く前提で作られている。
Hello, worldを作ってみる。

package main

func main() {
    println("hello, GopherJS")
}

ビルドしてみる。

$ go build -o /dev/null && gopherjs build hello.go

go buildしてるのは、GopherJSのビルドのエラーログが分かりにくいため、とりあえずGo言語としてコンパイルして、その段階でコンパイルエラーになるものを発見しやすくするためである。

ビルドするとhello.jshello.js.mapが生成される。hello.jsには自分で書いたコードと、ランタイムが付加される。上記のコードで、48KBほどである。hello.js.mapはsource mapファイルで、これを使えばデバッグがしやすくなる。

Node.jsで実行すると、hello, GopherJSと出力される。

$ node hello.js
hello, GopherJS

Nashorn

JavaのJavaScriptエンジンである。私はあまり詳しくないが、JavaからJSのオブジェクトを使えたり、JSからJavaのオブジェクトを使うことができる。jjsコマンドを使うと、JSファイルをNashornで実行できる。
なお、デフォルトではjjsにはパスが通ってないため、パスを通しておく必要がある。
また、-fxオプションをつければ、JavaFXも使うことができる。

Nashornで、GopherJSで生成したJSファイルを実行しようとすると、グローバルオブジェクトが設定されずに落ちる。そのため、GopherJSに手をいれて、トップレベルのthisをグローバルオブジェクトが入る変数go$globalに入るようにしてやる必要がある。

また、consoleもないので、console.logとかはprintとかに置き換える必要があるが、今回は面倒なのでやってない。そのため、上記のコードは今回は動かせない。

GopherJSでJSのオブジェクトを使う

js.ObjectインタフェースがJSのオブジェクトに対応している。
GetSetCallIndexLengthなどを使って、プロパティに値を設定したり、メソッドを呼んだりする。使い方は、reflectパッケージのValue型によく似ている。

例えば、以下のように使うことができる。

package main

import (
    "github.com/gopherjs/gopherjs/js"
)

func main() {
    Object := js.Global.Get("Object")
    obj := Object.New()
    obj.Set("name", "hoge")
    println(obj.Get("name"))

    JSON := js.Global.Get("JSON")
    println(JSON.Call("stringify", obj))
}
go build -o /dev/null sample1.go && gopherjs build sample1.go && node sample1.js
hoge
{"name":"hoge"}

GopherJSとJavaFX

それでは、JavaFXのWebViewを表示するサンプルを作ってみよう。

package main

import (
    "github.com/gopherjs/gopherjs/js"
)

var java = js.Global.Get("Java")

func main() {
    js.Global.Set("start", start)
}

func start(stage js.Object) {
    stage.Set("title", "Hello, GopherJS")
    WebView := java.Call("type", "javafx.scene.web.WebView")
    webview := WebView.New()
    webview.Call("getEngine").Call("load", "http://gopherjs.github.io/playground")
    Scene := java.Call("type", "javafx.scene.Scene")
    scene := Scene.New(webview, 600, 600)
    stage.Call("setScene", scene)
    stage.Call("show")
}

javaという変数は、Javaのクラスを使うためのオブジェクトである。グローバル変数なので、js.Globalのプロパティから取得している。
JavaFXのプログラムはmain関数ではなく、start関数から始まる。そのため、start関数をトップレベルに作る必要があるため、js.Globalのプロパティとして、start関数を設定している。GopherJSから吐出されるJSは必ずmainパッケージのmain関数が実行されるようなコードになっている。

start関数の中は、WebViewクラスのインスタンスを作って、Sceneに貼り付ける簡単な実装が書かれている。

実行してみる。

$ go build -o /dev/null sample2.go && gopherjs build sample2.go && jjs -fx sample2.js

sample2.png

いい感じにでている。ちなみに、表示しているのは、GopherJS PlaygroundGo Playground的なことができる上に、吐き出すJSを見ることができる。

GopherJSとJavaFX 3D

せっかくなので、3Dのプログラムも書いてみる。今回は以下のGopherの3Dオブジェクトを表示させていみる。

package main

import (
    "github.com/gopherjs/gopherjs/js"
)

var java = js.Global.Get("Java")

var (
    Group             = java.Call("type", "javafx.scene.Group")
    Scene             = java.Call("type", "javafx.scene.Scene")
    ObjModelImporter  = java.Call("type", "com.interactivemesh.jfx.importer.obj.ObjModelImporter")
    DrawMode          = java.Call("type", "javafx.scene.shape.DrawMode")
    PerspectiveCamera = java.Call("type", "javafx.scene.PerspectiveCamera")
    Point3D           = java.Call("type", "javafx.geometry.Point3D")
)

func start(stage js.Object) {
    root := Group.New()

    objImporter := ObjModelImporter.New()
    objImporter.Call("read", "gopher.obj")
    objMesh := objImporter.Call("getImport")
    objImporter.Call("close")

    for i := 0; i < objMesh.Length(); i++ {
        root.Call("getChildren").Call("addAll", objMesh.Index(i))
        objMesh.Index(i).Call("drawModeProperty").Call("set", DrawMode.Get("FILL"))
    }
    root.Call("setRotationAxis", Point3D.New(0.0, 1.0, 0.0))
    root.Call("setRotate", 210.0)

    scene := Scene.New(root, 600, 600)

    camera := PerspectiveCamera.New(true)
    scene.Call("setCamera", camera)
    camera.Call("setTranslateZ", -10.0)

    stage.Call("setScene", scene)
    stage.Set("title", "Hello, Gopher3D")
    stage.Call("show")
}

func main() {
    js.Global.Set("start", start)
}

ObjModelImporterというクラスを使うには、以下のサイトからjarファイルを落としてくる必要がある。objファイル以外のImporterクラスもある模様。

実行してみる。なお、-cpオプションで落としてきたjarファイルにクラスパスを通している。

$ go build -o /dev/null gopher3d.go && gopherjs build gopher3d.go && jjs -fx gopher3d.js -cp jimObjModelImporterJFX.jar

gopher3d.png

いろいろ試したけど、こっち向いてくれない。

感想

誰得感があって非常に良かった。
気が乗ったら、GopherJSに手を入れたところをPR投げようと思う。
JavaFXのGo言語のバインディングを書こうと思ったけど、ジェネリクスがつらそうだなと思った。

17
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?