丁度良いエントリがあったので、練習がてら全部 jrubyfx
で書いてた。
- Try! JavaFX! : JavaFX 2.0によるGUI開発入門 (第一回) - naokirin tech news
- Try! JavaFX! : JavaFX 2.0によるGUI開発入門 (第二回) - naokirin tech news
- Try! JavaFX! : JavaFX 2.0によるGUI開発入門 (第三回) - naokirin tech news
Getting Started · jruby/jrubyfx Wiki ここらへんを読んでどんな形で DSL を提供してるかを把握して jrubyfx/samples/javafx at master · jruby/jrubyfx サンプルから似たような事をやっていそうな所を見つけてくる作業。
第一回のサンプルをJRubyで書く
hello_world.rb
require 'jrubyfx'
class JRubyHelloWorld < JRubyFX::Application
def start(stage)
with(stage, title: 'Hello') do
layout_scene do
hbox do
label 'Hello World!'
end
end
show
end
end
end
JRubyHelloWorld.launch
とても Ruby らしく書けて素晴らしい!:)
JavaFX 固有の API は大体ブロックを渡すと良い感じに処理してくれるっぽい。
具体的には、ブロック内のメソッド呼び出しのレシーバが、ブロックを渡したメソッドのレシーバになるっぽい。
実装見てないけど多分 instance_eval{ yield }
みたいな感じ。
ポイント
- stage オブジェクトがウィンドウの枠担当
- scene オブジェクトが描画されるオブジェクトのルートとなる
- scene はレンダリングの為にシーングラフというものを内部に持っている模様
- scene 内に様々なオブジェクトを配置してシーングラフをつくって、それを stage#show で描画するのが一連の流れなのかな
- hbox は子要素を水平方向に描画する
第二回の最終的なコード
メモ帳っぽい体裁になった。
ウィンドウサイズの変化に合わせて、中身のオブジェクトの大きさも変化させる。
require 'jrubyfx'
class JRubyFXMemo < JRubyFX::Application
def start(stage)
with(stage, title: self.class.name, height: 700, width: 1024) do
edit_area = nil
command_area = nil
command_field = nil
command_button = nil
scene = layout_scene do
vbox do
edit_area = text_area
command_area = hbox do
command_field = text_field
command_button = button('Command')
end
end
end
edit_area.min_height_property.bind(scene.height_property.subtract(command_area.height_property))
command_field.min_width_property.bind(scene.width_property.subtract(command_button.width_property))
show
end
end
end
JRubyFXMemo.launch
ポイント
- 描画オブジェクトのインスタンス変数には
*_property
が必ず存在し、このメソッドでインスタンス変数へのプロパティオブジェクトが取得できる - プロパティオブジェクトの
bind
メソッドは、渡したオブジェクトが変更されたときに、レシーバのプロパティに対応するインスタンス変数を変化させる-
hoge.min_height_property.bind(foo)
としたら、foo
の値が変わるとhoge
のmin_height
の値も一緒に変わる(同じ値になる)
-
- 渡せるオブジェクトは、Property もしくは Binding のオブジェクト(のよう)である
- 今回のように計算後の値が必要な場合は、プロパティに定義されている四則演算のメソッドを使って、Binding オブジェクトを生成して渡してやるとよいらしい
もやもや
- バインドの為だけにインスタンス変数が増えたのが・・・悔しい
- ここら辺は非常に Java っぽいのが更に悔しい。まる。
- もっと良いやり方がある気がするけど、とりあえずこれで良しとしよう
第三回の宿題までをやってみる
イベントの実装。
終了とファイル保存を実装。
# -*- coding: utf-8 -*-
require 'jrubyfx'
class JRubyFXMemo < JRubyFX::Application
def start(stage)
with(stage, title: self.class.name, height: 700, width: 1024) do
edit_area = nil
command_area = nil
command_field = nil
command_button = nil
scene = layout_scene do
vbox do
edit_area = text_area
command_area = hbox do
command_field = text_field
command_button = button('Command') do
set_on_action do
case command_field.text
when 'quit', 'q', 'exit'
Platform.exit
when /^save:(.*)/
File.write($1, edit_area.text)
else
edit_area.text = 'コマンドが間違っています'
end
end
end
end
end
end
edit_area.min_height_property.bind(scene.height_property.subtract(command_area.height_property))
command_field.min_width_property.bind(scene.width_property.subtract(command_button.width_property))
show
end
end
def stop
puts 'アプリケーションを終了します...'
end
end
JRubyFXMemo.launch
終了コマンドを、元記事より増やしてみた。
名前を付けて保存、上書き保存は、実装が面倒そうだから、正規表現でパパっと。スクリプト言語で書けると、この辺がすごく楽で良い。
ポイント
- 描画オブジェクトには
set_on_*
メソッドが存在していて、ブロックを使ってイベントを登録できる - Ruby の力を持ってすれば、文字列のパースがとても楽
- 描画経路
- コンストラクタ
- init()メソッド
- start()メソッド
- ウィンドウが全て閉じられたとき、もしくはPlatform.exit()が呼び出されたとき、アプリケーション終了を待機
- stop()メソッド
感想
- JavaFX さえ分かってしまえば、簡単に GUI アプリが作れる
- jrubyfx はとても Ruby ぽくコードを書ける
- でも API レベルで Java 臭い箇所はあきらめるしかないかも
- スクリプト言語で GUI アプリを書けるのはとても良い
- 何より GUI アプリつくるのは楽しい:)
- 次は簡単な twitter クライアントでもつくりたいなぁ…