2011年09月23日(金)
Ruby/Tk
備忘録 | |
![]()
Tcl/Tk
Ruby/Tk は Tcl/Tk の Tk の移植(?)です。
Ruby/Tk を利用するには環境に Tcl/Tk がインストールされている必要があります。
Tcl はスクリプト言語です。Tk はその GUI ツールキットです。
Tcl/Tk がインストールされた環境では、それぞれのインタプリタとして tclsh、wish がインストールされていると思われます。
wish (tk) スクリプトは以下のような感じになります。(hello.wish)
#!/bin/bash
# \
exec wish "$0" "$*"
#「hello」とラベルされたボタン1つから成る GUI です
# ボタンを押下するとプログラムを終了させます
button .b -text "hello" -command {exit}
pack .b
これは、実際には bash スクリプトです。
2行目のコメント行の末尾が「\」で終端しています。
bash (sh) ではコメント行は継続しませんが、wish では継続します(3行目もコメントになる)
3 行目で wish に exec しています。以降の行は bash としては実行されません。
exec した wish では、「button .b 〜」の行の前までコメントになります。
それ以降が wish スクリプトとして実行されます。
これは、以前から(少くとも15年以上前から)使われる wish の常套テクです。
スクリプトは以下のようにして実行できます。
$ bash hello.wish または $ sh hello.wish または $ wish hello.wish または $ ./hello.wish # 実行権が付与(chmod +x 〜)されている必要がある
以下は Ubuntu Linux で実行したところです。「hello」ボタンを押すと終了します。
Tcl/Tk のバージョンは wish を起動して以下のように確認できます。
$ wish % puts $tcl_version 8.4 % puts $tk_version 8.4 % exit # 終了は exit または CTRL-D
Ruby/Tk
同じ GUI を Ruby/Tk で書いてみます。(hello.rb)
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'tk' TkButton.new { text "hello"; command {exit} }.pack Tk.mainloop # vim:set ts=2 sw=2 et ai fenc=utf-8:
実行すると wish で作成したのと同じ GUI が表示されます。
$ ruby hello.rb
スクリプトでは、
しています。
エンコード(utf-8など)の指定は (多分、Ruby1.9以降では) 日本語のメッセージを扱うときに(多分)重要になります。
pack
Tk ではジオメトリマネージャ*1が以下の 3 種類あります。
- Packer : ウィジェットを順に詰める。よく使われる
- Placer : ウィジェットを指定位置に配置する
- Grid : ウィジェットを格子状に配置する
pack メソッドは Packer によるレイアウトを指定します。
オプションの指定
ウィジェットのオプション指定は上記のように new にブロックを与える方法の他に以下のようにもできます。
# インスタンスメソッドで設定する button = TkButton.new button.text "hello" # TkButton#text による設定 button.command {exit} # TkButton#command による設定 button.pack または button = TkButton.new button.text = "hello" # TkButton#text= による設定 button.command = proc {exit} # TkButton#command= による設定 button.pack または button = TkButton.new button.text "hello" button.command = "exit" # proc の代わりに文字列を与える button.pack
# new の引数に Hash として渡す TkButton.new(:text => "hello", :command => proc {exit}).pack または TkButton.new('text' => "hello", 'command' => proc {exit}).pack または TkButton.new(:text => "hello", :command => "exit").pack
# メソッドチェーン (引数ありのオプション設定メソッドは self を返す) TkButton.new.text("hello").command{exit}.pack または TkButton.new.text("hello").command("exit").pack
- text で取れる方法は他の値を与えるオプションでも使えます(多分)。
- command で取れる方法は他の proc を与えるオプション*2でも使えます(多分)。
- これらは、ボタン(TkButton)以外のウィジェットでも同様です(多分)。
new に与えたブロックでは self が TkButton インスタンスになります。若干注意が必要です。
以下の場合、ボタン押下時に表示される self が異なります。
TkButton.new.text("button1").command{ puts self }.pack # => self は実行環境(このスクリプトの場合 main) TkButton.new{text("button2").command{ puts self }}.pack # => self は TkButton インスタンス
以下の1行目の場合、ボタンに文字列は設定されません。
TkButton.new{ text = "hello" } # この場合 text はダイナミックローカル変数なので TkButton#text= を呼び出せていない TkButton.new{ text "hello" } # この場合 text は TkButton#text なので意図した通りになる
引数なしでオプション設定メソッドを呼び出すとその値を返します。
(引数ありの場合(設定する場合)は self を返します)
button = TkButton.new button.text "hello" p button.text # => "hello"
プログラムランチャー
だいぶ適当ですが、簡単なプログラムランチャーです。(launcher.rb)
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'tk' TkButton.new { text "firefox" ; command {`firefox`} }.pack TkButton.new { text "eclipse" ; command {`eclipse`} }.pack TkButton.new { text "squeak" ; command {`squeak`} }.pack TkButton.new { text "terminal"; command {`gnome-terminal`}}.pack TkButton.new { text "exit" ; command {exit} }.pack Tk.mainloop # vim:set ts=2 sw=2 et ai fenc=utf-8:
実行。
$ ruby launcher.rb
pack は :side オプションで詰め方を指定できます。
以下のようにするとウィジェットを左から詰めていきます。(デフォルトは :side=> :top)
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'tk' TkButton.new { text "firefox" ; command {`firefox`} }.pack(:side=>:left) TkButton.new { text "eclipse" ; command {`eclipse`} }.pack(:side=>:left) TkButton.new { text "squeak" ; command {`squeak`} }.pack(:side=>:left) TkButton.new { text "terminal"; command {`gnome-terminal`}}.pack(:side=>:left) TkButton.new { text "exit" ; command {exit} }.pack(:side=>:left) Tk.mainloop # vim:set ts=2 sw=2 et ai fenc=utf-8:
ルートウィジェットとウィジェット階層
全てのウィジェットの親となるルートウィジェットを明示的に作ることもできます。
require 'tk' root = TkRoot.new { # X クライアントとしての設定ができる geometry '320x320+0+0' # ジオメトリの指定 (X11 書式) title "hello,world" # タイトルの指定 } # 第一引数に明示的に親ウィジェットを指定できる TkButton.new(root, :text => "exit", :command => proc {exit}).pack Tk.mainloop
- ルートウィジェットは 1つしか作成されない*3 *4
- ルートウィジェットは明示的に作成しなければ暗黙のうちに作成される
- ウィジェットの new の時に明示的に指定しなければ*5親はルートウィジェットになる
- mainloop を抜ける(けどプログラム自体は終了しない)場合は、ルートウィジェットの destroy メソッドを呼びだす
ルートウィジェット以外を親ウィジェットに指定されるものとしては、
- TkToplevel
- TkFrame
- TkCanvas
などがあります。
ちなみに、Tcl/Tk ではルートウィジェットは「.」と表現されます。
「.b」はルートウィジェットを親とする b ウィジェットです。
「.a.b.c」はルートウィジェットの子の a ウィジェットの、子の b ウィジェットの、子の c ウィジェット、という意味になります。(ファイルシステムのパスに似ています)
Ruby/Tk のウィジェットのパスは path メソッドで確認できます。
p root.path # => "." p button.path # => ".w0000" (パス名は実装依存)
イベントのハンドリング
イベントのハンドリングには bind メソッドを使います。
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'tk' TkLabel.new{ text "Click me!" width 20 # in characters height 10 # in rows bind("1", proc {|x,y| puts "Button<1> [#{x},#{y}]" }, "%x %y") bind("2", proc {|x,y| puts "Button<2> [#{x},#{y}]" }, "%x %y") bind("3", proc {|x,y| puts "Button<3> [#{x},#{y}]" }, "%x %y") bind("Control-Button-1") { exit } }.pack Tk.mainloop # vim:set ts=2 sw=2 et ai fenc=utf-8:
bind 引数は以下のように扱われます。
- 第一引数 : イベント名。"1" はマウス第 1 ボタン押下イベント
- 第二引数 : コールバックする proc
- 第三引数 : イベントで取得するパラメタの書式。パラメタはコールバック proc に引数として渡される。
取得するイベントパラメタがなければ、第二引数以降省略できます(コールバック proc は ブロックとして渡す)。
実行してみます。
$ ruby bind-test.rb
これをマウスでクリックすると以下のように出力されます。
Control キーを押しながら マウス第1ボタンでクリックすると、このプログラムは終了します。
: Button<1> [78,54] Button<1> [78,54] Button<1> [78,54] Button<1> [78,54] Button<3> [103,123] Button<2> [96,108] :
これも bind の例です。
動的にウィジェットの属性を変更します。
マウス第1〜第5 ボタンの「クリック」で背景色を変更します。
Ubuntu Linux の X 環境では以下のように割り当てられています。
- マウスの第4ボタンの「クリック」= マウスホイールの回転(上方向)
- マウスの第5ボタンの「クリック」= マウスホイールの回転(下方向)
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'tk' label = TkLabel.new(:text => "Click me!", :width => 20, # in characters :height => 10, # in rows :bg => :White). # background color pack # 色の指定は「'#ff0000'」のようにRGB値でも指定できます。 label.bind("1") { label.bg :Red } # label.bg '#ff0000' でも可 label.bind("2") { label.bg :Blue } label.bind("3") { label.bg :Yellow } label.bind("4") { label.bg :Magenta } label.bind("5") { label.bg :Green } label.bind("Control-Button-1") { exit } Tk.mainloop # vim:set ts=2 sw=2 et ai fenc=utf-8:
参考URL








