WebEngineでJRubyとHTML間で連携をしてみる
JRuby(JRubyFX)とWebView上のHTMLの連携を試してみる。
JRuby側のボタンを押したらHTML上のspanにメッセージを表示して、
HTML上のボタンを押したらJRuby側のラベルにメッセージを表示する
プログラムは、RubyコードとHTMLコードの2つを用意します。
require 'jrubyfx' class App < JRubyFX::Application def start(stage) with(stage, width: 800, height: 600, title: "sample") do layout_scene do vbox do hbox do button(id:"fx-btn", text:"click", min_width: 100) label(id:"fx-msg", text:"=== ===") end web_view(id:"browser") do |v| v.engine.load "file:///path/to/sample.html" v.engine.set_java_script_enabled true end end end show end stage['#fx-btn'].set_on_action do doc = stage['#browser'].engine.get_document doc.get_element_by_id("web-msg").set_text_content "clicked javafx button." end stage['#browser'].engine.get_load_worker.state_property.add_change_listener do |ov,os,new_state| if new_state == Worker::State::SUCCEEDED el = Proc.new do stage['#fx-msg'].text = " clicked browser button !!!" end doc = stage['#browser'].engine.get_document doc.get_element_by_id('web-btn').add_event_listener( "click", el, false) end end end end App.launch
- sample.html
<html> <head> <title>sample</title> </head> <body> <p>hello sample.</p> <input type="button" id="web-btn" value="button" /><br/> message: <span id="web-msg">hogehoge</span><br/> </body> </html>
普通にGUIでいいじゃんって話なんですが、どうしてもJavascriptのGUIをRubyから扱いたくて、現在試行錯誤中です。
これであとはRubyからJavascriptにアクセスできればやりたいことができそうな気がします。
重い処理をバックグラウンドスレッド実行させてみる
重い処理をTimeline上に書いてしまうと、処理中はGUIが止まってしまうことになります。
それを回避するためにServiceとTaskを用いる方法があります。
重い処理をTaskに書いて、バックグラウンド実行し、GUIで定期的にモニタリングするようなプログラムを作ってみました。
Taskのcall関数内がバックグラウンドで実行するプログラムです。
sleepを入れているが、入れなくてもよい。バックグラウンド実行中もGUI操作を阻害しません。
なかなかよいです。
require 'jrubyfx' @@count = 0 class XTask < Java::javafx::concurrent::Task def call loop do sleep(0.03) puts "background #{@@count}" @@count += 1 end end end class XService < Java::javafx::concurrent::Service def createTask puts "CreateTask" XTask.new end end class Hello < JRubyFX::Application def initialize puts "intialise" @service = XService.new @pos_x = 100 end def start(stage) @stage = stage with(stage,title: "sample",width: 100,height: 50) do layout_scene do group do label("hello", id: "v") end end show end @service.start play end def play handler = EventHandler.impl do |n, e| @stage['#v'].text = "hello #{@@count}" puts "handler" end time = Timeline.new time.cycle_count = :indefinite time.keyFrames << KeyFrame.new(1000.ms, handler) time.play end end Hello.launch
はじめ、Rubyのスレッドを使ってみたのですが、うまくいかず、今回の方法にしました。
なかなかJRubyFXの情報がなくて苦戦中。
今回はJRubyFXでServiceとTaskがていぎされていなかったので、JavaFXから直接読んでしまっています。
JRubyFXは素晴らしいと思いますが完成度がまだまだでちょっと残念。。
円を左右に動かすアニメーション
JRubyFXでアニメーションをやりたくて、まずは簡単なところから始めてみた。
はじめに、サークルを描画して、大きさを150、色をgreen、位置を縦方向に200の場所においた。
そのあと、play関数を呼んで、
そこで、idが"c"となっているサークルのX方向をtimelineを使って動かす。
0秒(0.sec)の状態で横方向200の位置、1秒(1.sec)の状態で横方向600の位置、自動的にリバースさせる
という設定になっている。
timelineの設定はnodeごとにできるっぽい。
require 'jrubyfx' class Hello < JRubyFX::Application def start(stage) @stage = stage with(stage,title: "sample",width: 800,height: 400) do layout_scene do group do c = circle(150, Color.web("green"), id: "c") c.translate_y = 200 end end show end play end def play translate_x = @stage['#c'].translateXProperty timeline(cycle_count: :indefinite, auto_reverse: true) do animate translate_x, 0.sec => 1.sec, 200 => 600 end.play end end Hello.launch
なるほど、なんとなくアニメーションがわかってきたぞ。
Timelineを用いて1秒ごとに時刻を表示するプログラム
JRubyFXによるアニメーションを調べていて、まずは簡単なところから始めてみようと思う。
テキスト(ラベル)を1秒ごとに更新するプログラムを作成してみた。
Timelineに登録されたハンドラを1000msごとに呼ぶという単純なプログラムになっている。
require 'jrubyfx' class Hello < JRubyFX::Application def start(stage) @stage = stage with(stage,title: "sample",width: 200,height: 100) do tt = nil layout_scene do stack_pane do label("", id: 'view' ) end end show end play end def refresh_time t = Time.now #puts t @stage['#view'].text = t.to_s end def play refresh_time handler = EventHandler.impl {|n,e| refresh_time} time = Timeline.new time.cycle_count = :indefinite time.keyFrames << KeyFrame.new(1000.ms, handler) time.play end end Hello.launch
[JRuby][JRubyFX] Ubuntu16.04でJRubyFXを動かす
手順としては次の通り。
- Java8を入れる
- rbenvを入れる
- jruby-1.7.26を入れる
- jrubyfxを入れる
- サンプルを動かす
まず、Javaを入れる。
$ sudo add-apt-repository ppa:webupd8team/java $ sudo apt-get update $ sudo apt-get install oracle-java8-installer
次に、rbenvを入れる。
$ sudo apt-get install git build-essential libssl-dev $ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
~/.profile の最後行あたりに次を記述。
export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)"
つぎに、rbenvによりJRubyをインストールする。
ここでは、jruby-1.7.26を入れる。jruby-9kでもよいがアプリを動かしたときwarningが出るので、1.7系にしておく。
$ rbenv install --list $ rbenv install jruby-1.7.26 $ rbenv global jruby-1.7.26
次に、jrubyfxを入れる。
$ jruby -S gem install jrubyfx
これで、準備が完了。
サンプルプログラムを動かしてみる。
require 'jrubyfx' class Hello < JRubyFX::Application def start(stage) with(stage,title: "sample",width: 200,height: 100) do layout_scene do vbox do label("hello", id:"view") end end show end end end Hello.launch
$ jruby hello.rb
シグモイド関数をグラフ表示
Ubuntu 16.04 で試した
% sudo apt-get install gnuplot % gem install gnuplot
require 'gnuplot' def sigmod( x ) 1.0 / (1.0 + Math.exp( -x ) ) end Gnuplot.open do |gp| Gnuplot::Plot.new(gp) do |plot| plot.title 'sample' plot.ylabel 'ylabel' plot.xlabel 'xlabel' x = (-100..100).collect{|v| v.to_f*0.1 } y = x.map {|f| sigmod(f) } plot.data << Gnuplot::DataSet.new( [x,y] ) do |ds| ds.with = "lines" ds.notitle end end end
UDPで送受信を行う
単純に2つのポートを開いて
Server->Client ... port:10000
Client->Server ... port:10001
として双方向の通信を行ってみます。
UDPのため送信されたデータが届くことは保証されないですが、TCPより手続きが簡単かと思います。
- サーバ側のプログラム
require 'socket' u1 = UDPSocket.new() u1.bind('localhost', 10001) u2 = UDPSocket.new() u2.connect('localhost', 10000) loop do sleep 1 begin p u1.recvfrom_nonblock(65536) rescue puts "[Server] Error, recvfrom" end begin puts "[Server] >>" u2.send('[Server] Hello world', 0) rescue puts "[Server] Error, send" end end
- クライアント側のプログラム
require 'socket' u1 = UDPSocket.new() u1.bind('localhost', 10000) u2 = UDPSocket.new() u2.connect('localhost', 10001) loop do sleep 1 begin p u1.recvfrom_nonblock(65536) rescue puts "[Client] Error, recvfrom" end begin puts "[Client] >>" u2.send('[Client] Hello world', 0) rescue puts "[Client] Error, send" end end