逆引き Ruby/Tk

Widget 全般

幅や高さを設定する

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Hello!!"
  width 10
  height 10
  pack
}

Tk.mainloop

ボタンで大きさが変わるサンプル。

#!/usr/bin/env ruby

require "tk"

l = TkLabel.new {
  text "Hello!!"
  pack
}

TkButton.new {
  text "Wide"
  command { l.width(l.width + 10); print "width: #{l.width}\n" }
  pack('fill' => 'x')
}

TkButton.new {
  text "Narrow"
  command { l.width(l.width - 10); print "width: #{l.width}\n" }
  pack('fill' => 'x')
}

Tk.mainloop

幅や高さを取得する

#!/usr/bin/env ruby

require "tk"

b = TkButton.new {
  text "width"
  pack('fill' => 'x')
}

b.command { print TkWinfo.width(b), "\n" }

Tk.mainloop

得られるのは、ピクセル値(だと思う)

Widget の見ためを変える

#!/usr/bin/env ruby

require "tk"

['flat',  'groove', 'raised',
 'ridge', 'solid',  'sunken'].each do |r|
  TkLabel.new {
    text r
    width 10
    borderwidth 4
    relief r
    pack('fill' => 'x')
  }
end

Tk.mainloop

Widget 間の間隔(余白)を設定する

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "Label"
  bd 2
  pack('padx' => 10, 'pady' => 40)
}

Tk.mainloop

前景、背景色を変える

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Green"
  fg 'green'
  bg 'red'
  pack
}

Tk.mainloop

アクティブ時(マウスが上に来たとき)の前景、背景色を変える

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Red"
  #bg 'green'
  default_bg = bg
  bind 'Enter', proc { bg 'red' }
  bind 'Leave', proc { bg default_bg }
  pack('fill' => 'x')
}

TkButton.new {
  text "Red"
  activebackground 'red'
  pack('fill' => 'x')
}

Tk.mainloop

フォントの情報を取得する

TkFont.new の引数の書式は [ruby-dev:22585] のように幾つかあるようです。

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Font 1"
  font TkFont.new('times 20 bold')
  p font           # => #<TkFont:0x2d675ac ... >
  p font['family'] # => "Times New Roman"
  p font['size']   # => 20
  p font['weight'] # => "bold"
  p font['slant']  # => "roman"
  p font['underline']   # => false
  p font['overstrike']  # => false
}.pack

Tk.mainloop

フォントを変える

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Font 1"
  font TkFont.new(['times', 20, ['bold']])
}.pack

TkLabel.new {
  text "Font 2"
  font TkFont.new({'family' => 'times',
                    'weight' => 'bold',  # or 'normal'
                    'slant' => 'italic', # or 'roman'
                    'underline' => true,
                    'overstrike' => true})
}.pack

Tk.mainloop

フォント(文字)の色を変える

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Green"
  fg 'green'
  pack
}

Tk.mainloop

前景色を変えるのと同義(多分)

フォントの大きさを変える

#!/usr/bin/env ruby

require "tk"

[8, 10, 12, 16, 20, 24, 30].each do |size|
  TkLabel.new {
    text "Font #{size}"
    font({'size' => size})
    #font {'size' => size} # => parse error
  }.pack
end

Tk.mainloop

または、

#!/usr/bin/env ruby

require "tk"

[8, 10, 12, 16, 20, 24, 30].each do |size|
  TkLabel.new(nil, 'font' => {'size' => size}) {
    text "Font #{size}"
  }.pack
end

Tk.mainloop

フォントの一覧を取得する

TkFont.families でフォント名の配列が得られます。

#!/usr/bin/env ruby

require "tk"

$label = TkLabel.new(:text=>'Hello, world!')
$frame = TkFrame.new

$box = TkListbox.new($frame) {
  for name in TkFont.families
    insert(:end, name)
  end
  bind '<ListboxSelect>', proc {
    if i = curselection.first
      $label.font = TkFont.new(:family=>get(i))
    end
  }
  yscrollbar $bar = TkScrollbar.new($frame)
}

$box.pack(:side=>:left, :fill=>:both, :expand=>true)
$bar.pack(:side=>:left, :fill=>:y)

$label.pack(:side=>:top, :fill=>:x)
$frame.pack(:side=>:top, :fill=>:both, :expand=>true)

Tk.mainloop

フォーカスを移動する

#!/usr/bin/env ruby

require 'tk'

e1 = TkEntry.new {
  pack
}

b1 = TkButton.new {
  text "focus"
  command { e1.focus }
  bind 'Return', proc { invoke }
  pack
}

e1.bind 'Return', proc { b1.focus }

Tk.mainloop

イメージつきにする

マウスカーソルの形状を変更する

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "hand"
  cursor 'hand2'
  pack('fill' => 'x')
}

TkLabel.new {
  text "watch"
  cursor 'watch'
  pack('fill' => 'x')
}

#Tk.root.cursor 'hand2'

Tk.mainloop

時間のかかる処理時にマウスカーソルの形状を時計にする

「プログラミングRuby」には以下のような方法が紹介されています。 (趣旨と busy メソッドを引用) 時間のかかる処理をメソッド busy のブロックとして渡します。

#!/usr/bin/env ruby

require "tk"

$root = Tk.root

def busy
  begin
    $root.cursor "watch"
    $root.update
    yield
  ensure
    $root.cursor ""
    $root.update
  end
end

TkButton.new {
  text "watch"
  command { busy { sleep 2 } }
  pack('fill' => 'x')
}

Tk.mainloop

独自カーソルを使用する

Widget の種類を取得する

#!/usr/bin/env ruby

require "tk"

b = TkButton.new {
  text "Type"
  pack
}

b.command {
  if b.type == TkButton
    print "Button!!\n"
  end
}

Tk.mainloop

これだと、継承して別クラスになっているとダメそう。 こんな感じか?

#!/usr/bin/env ruby

require "tk"

b = TkButton.new {
  text "Type"
  pack
}

b.command {
  if TkWinfo.classname(b) == 'Button'
    print "Button!!\n"
  end
}

Tk.mainloop

Widget 全てに対して同じ属性を適用する

配置

Widget 間の間隔(余白)を設定する

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "red"
  bg 'red'
  pack('pady' => 40)
}

TkLabel.new {
  text "green"
  bg 'green'
  pack('pady' => 40)
}

Tk.mainloop

以下のように書くと、Widget内の間隔があく

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "red"
  bg 'red'
  pady 40
  pack
}

TkLabel.new {
  text "green"
  bg 'green'
  pady 40
  pack
}

Tk.mainloop

Widget を指定する

コンストラクタに指定すれば親 Widget になる。動的に親を変更する方法って あるんだろうか?

#!/usr/bin/env ruby

require "tk"

f = TkFrame.new {
  pack
}

TkButton.new(f) {
  text "button"
  pack
}

Tk.mainloop

Widget を非表示にする

#!/usr/bin/env ruby

require "tk"

l = TkLabel.new {
  text "Label"
  pack('side' => 'top')
}

TkButton.new {
  text "unpack"
  #command { l.pack_forget }
  command { l.unpack }
  pack('side' => 'top')
}

TkButton.new {
  text "pack"
  command { l.pack }
  pack('side' => 'top')
}

Tk.mainloop

上から下に順にpackする

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Top"
  pack('side' => 'top')
}

TkLabel.new {
  text "Middle"
  pack('side' => 'top')
}

TkLabel.new {
  text "Bottom"
  pack('side' => 'top')
}

Tk.mainloop

下から上に順にpackする

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Bottom"
  pack('side' => 'bottom')
}

TkLabel.new {
  text "Middle"
  pack('side' => 'bottom')
}

TkLabel.new {
  text "Top"
  pack('side' => 'bottom')
}

Tk.mainloop

左から右に順にpackする

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Left"
  pack('side' => 'left')
}

TkLabel.new {
  text "Center"
  pack('side' => 'left')
}

TkLabel.new {
  text "Right"
  pack('side' => 'left')
}

Tk.mainloop

右から左に順にpackする

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Right"
  pack('side' => 'right')
}

TkLabel.new {
  text "Center"
  pack('side' => 'right')
}

TkLabel.new {
  text "Left"
  pack('side' => 'right')
}

Tk.mainloop

Widget 間の幅を揃える

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "Wide Wide Wide Wide"
  pack('fill' => 'x')
}

TkButton.new {
  text "narrow"
  pack('fill' => 'x')
}

Tk.mainloop

Widget が大きくなったときそれに合わせて Widget を大きくする

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "Button 1"
  pack
}

TkButton.new {
  text "Button 2"
  pack('fill' => 'x', 'expand' => true)
}

TkButton.new {
  text "Button 3"
  pack('fill' => 'both', 'expand' => true)
}

Tk.mainloop

絶対座標で配置する

#!/usr/bin/env ruby

require "tk"

#Tk.root.width(200)
#Tk.root.height(200)

TkLabel.new {
  text "Button"
}.place('x' => 100, 'y' => 50)

Tk.mainloop

相対座標で配置する

#!/usr/bin/env ruby

require "tk"

#Tk.root.width(200)
#Tk.root.height(200)

TkLabel.new {
  text "Button"
}.place('relx' => 0.25, 'rely' => 0.5)

Tk.mainloop

格子状の配置をする

#!/usr/bin/env ruby

require "tk"

1.upto(9) {|i|
  TkButton.new {
    text i
    i -= 1
    grid("row" => 2 - i / 3, "column" => i % 3)
  }
}

Tk.mainloop

"row" と "column" 以外特に指定しない状態では、

となるため、場合によっては非常に不恰好になります。

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "ABC"
  grid("row" => 0, "column" => 0)
}

TkButton.new {
  text "ABC\nABC"
  grid("row" => 0, "column" => 1)
}

TkButton.new {
  text "ABCABC\nABCABC"
  grid("row" => 1, "column" => 0)
}

TkButton.new {
  text "ABCABC"
  grid("row" => 1, "column" => 1)
}

Tk.mainloop

グリッドに対する配置方法は、grid() に "sticky" オプションを指定すること で制御できます。

例えば Widget をグリッド一杯に広げるには、

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "ABC"
  grid("row" => 0, "column" => 0, "sticky" => "news")
}

TkButton.new {
  text "ABC\nABC"
  grid("row" => 0, "column" => 1, "sticky" => "news")
}

TkButton.new {
  text "ABCABC\nABCABC"
  grid("row" => 1, "column" => 0, "sticky" => "news")
}

TkButton.new {
  text "ABCABC"
  grid("row" => 1, "column" => 1, "sticky" => "news")
}

Tk.mainloop

親 Widget をリサイズしたときにグリッドの大きさが変わるようにするには、 TkGrid.columnconfigure や TkGrid.rowconfigure を使います。第一引数に 親 Widget、第二引数に行・列番号、第三引数にオプションを指定します。

とりあえず全ての行・列が同じ比率で大きくなるようにするには

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "ABC"
  grid("row" => 0, "column" => 0, "sticky" => "news")
}

TkButton.new {
  text "ABC\nABC"
  grid("row" => 0, "column" => 1, "sticky" => "news")
}

TkButton.new {
  text "ABCABC\nABCABC"
  grid("row" => 1, "column" => 0, "sticky" => "news")
}

TkButton.new {
  text "ABCABC"
  grid("row" => 1, "column" => 1, "sticky" => "news")
}

0.upto(1) {|i| TkGrid.columnconfigure(Tk.root, i, "weight" => 1)}
0.upto(1) {|i| TkGrid.rowconfigure(Tk.root, i, "weight" => 1)}

Tk.mainloop

"weight" の値を変えると、大きくなる度合いを行・列によって変えることも できます。

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "ABC"
  grid("row" => 0, "column" => 0, "sticky" => "news")
}

TkButton.new {
  text "ABC\nABC"
  grid("row" => 0, "column" => 1, "sticky" => "news")
}

TkButton.new {
  text "ABCABC\nABCABC"
  grid("row" => 1, "column" => 0, "sticky" => "news")
}

TkButton.new {
  text "ABCABC"
  grid("row" => 1, "column" => 1, "sticky" => "news")
}

TkGrid.columnconfigure(Tk.root, 0, "weight" => 1)
TkGrid.columnconfigure(Tk.root, 1, "weight" => 2) # 0列目の二倍早く大きくなる

TkGrid.rowconfigure(Tk.root, 0, "weight" => 0) # 高さを変えない
TkGrid.rowconfigure(Tk.root, 1, "weight" => 1)

Tk.mainloop

格子状の配置でいくつかのセルをまたがらせる

#!/usr/bin/env ruby

require "tk"

1.upto(9) {|i|
  TkButton.new {
    text i
    i -= 1
    grid("row" => 2 - i / 3, "column" => i % 3, "sticky" => "news")
  }
}

TkButton.new {
  text "0"
  grid("row" => 3, "column" => 0, "columnspan" => 2, "sticky" => "news")
}

TkButton.new {
  text "."
  grid("row" => 3, "column" => 2, "sticky" => "news")
}

TkButton.new {
  text "en\nter"
  bg "gray"
  grid("row" => 2, "column" => 3, "rowspan" => 2, "sticky" => "news")
}

TkButton.new {
  text "+"
  bg "gray"
  grid("row" => 0, "column" => 3, "rowspan" => 2, "sticky" => "news")
}

Tk.mainloop

イベント

マウスの第一ボタンをクリックしたときの動作を設定する

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  width 10
  height 2
  text "Hello!!"
  bind 'Button', proc { print "Click!\n" }
  #bind 'Button-1', proc { print "Click!\n" }
  pack
}

Tk.mainloop

マウスをダブルクリックしたときの動作を設定する

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  width 10
  height 2
  text "Hello!!"
  #bind 'Double-Button', proc { print "Click!\n" }
  bind 'Double-1', proc { print "Click!\n" }
  pack
}

Tk.mainloop

マウスの第二ボタンをクリックしたときの動作を設定する

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  width 10
  height 2
  text "Hello!!"
  bind 'Button-2', proc { print "Click!\n" }
  pack
}

Tk.mainloop

マウスを移動したときの動作を設定する

#!/usr/bin/env ruby

require "tk"

TkFrame.new {
  width 100
  height 100
  bind 'Motion', proc { print "Move!\n" }
  pack
}

Tk.mainloop

マウスの座標を取得する

#!/usr/bin/env ruby

require "tk"

TkFrame.new {
  width 100
  height 100
  #bind 'Motion', proc { |x,y| print "Move! (#{x}, #{y})\n" }, "%x %y"
  bind 'Button', proc { |x,y| print "Click! (#{x}, #{y})\n" }, "%x %y"
  pack
}

Tk.mainloop

Enter キーを押したときの動作を設定する

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text 'Hello'
  command { print "Hello\n" }
  #bind 'Return', proc { print "Return\n" }
  pack
}

Tk.root.bind 'Return', proc { print "Return\n" }

Tk.mainloop

どのキーが押されたかを取得する

#!/usr/bin/env ruby

require "tk"

TkFrame.new {
  width 100
  height 100
  pack
}

Tk.root.bind 'KeyPress',
  proc { |a,k,kk,n| print "%A: #{a}  %k: #{k}  %K: #{kk}  %N: #{n}\n" },
  "%A %k %K %N"

Tk.mainloop

Widget にフォーカスが移って来たときの動作を設定する

#!/usr/bin/env ruby

require "tk"

3.times do |i|
  TkEntry.new {
    self.value = "entry#{i}"
    bind "FocusIn", proc { self.fg = "red" }
    bind "FocusOut", proc { self.fg = "black" }
    pack(:side=>:top, :fill=>:x)
  }
end

Tk.mainloop

ファンクションキー F1 でヘルプを表示する

(以下のコードでは窓を開くだけでヘルプは表示しない。)

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Push F1!!"
  pack
}

Tk.root.bind 'F1', proc { TkToplevel.new }

Tk.mainloop

特殊キー(Shift, Crtl, ALT)などが押されたかどうか知る

C-x C-c で終了する

#!/usr/bin/env ruby

require "tk"

TkFrame.new {
  width 100
  height 100
  pack
}

Tk.root.bind ['Control-x', 'Control-c'], proc { exit }

Tk.mainloop

テンキー入力であるかどうか知る

特定 Widget のイベント

Label Widget

表示後にテキストを変更する

#!/usr/bin/env ruby

require "tk"

l = TkLabel.new {
  text "Perl"
  pack
}

TkButton.new {
  text "Ruby"
  command { l.text("Ruby") }
  pack
}

Tk.mainloop

表示したテキストを取得する

#!/usr/bin/env ruby

require "tk"

l = TkLabel.new {
  text "hoge"
  pack
}

TkButton.new {
  text "print Label text"
  command { print l.text, "\n" }
  pack
}

Tk.mainloop

文字の位置揃えをする

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Ruby\nPython\nPerl"
  #justify 'left'
  #justify 'center'
  justify 'right'
  pack
}

Tk.mainloop

複数行表示する

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Ruby\nPython\nPerl"
  underline 1
  pack
}

Tk.mainloop

これでもOK。

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Ruby
Python
Perl"
  underline 1
  pack
}

Tk.mainloop

折り返し幅を指定する

label にあるんだったけ?

下線を引く

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Hello!!"
  underline 1
  pack
}

Tk.mainloop

選択可能なラベルを作る

Entry Widget で代用する案を考えてみた

#!/usr/bin/env ruby

require "tk"

TkLabel.new {
  text "Label"
  pack('fill' => 'x')
}

e = TkEntry.new {
  relief 'flat'
  #pack('fill' => 'x', 'anchor' => 'center')
  pack('fill' => 'x')
}

e.value = "Entry"
e.state 'disabled'
e.width e.value.length

Tk.mainloop

Message Widget

折り返し幅を指定する

Button Widget

Enter で選択できるようにする

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "Hello!!"
  command { print "Hello!!\n" }
  bind 'Return', proc { invoke }
  pack
}

Tk.mainloop

ショートカットできるようにする

Windows ではアクセスキーと呼ぶ?やつ。 ALT + キーを押すとボタンと押したときと同じ動作をするようになる。

#!/usr/bin/env ruby

require "tk"

b1 = TkButton.new {
  text "OK (A)"
  command { print "OK (A)\n" }
  bind 'Return', proc { invoke }
  pack
}

Tk.root.bind('Alt-a', proc { b1.focus; b1.invoke })

Tk.mainloop

ボタンを使用不可にする

#!/usr/bin/env ruby

require "tk"

b = TkButton.new {
  text "Hello!!"
  command { print "Hello!!\n" }
  pack
}

TkCheckButton.new {
  text "disabled"
  command {
    if b.state == 'normal'
      b.state('disabled')
    else
      b.state('normal')
    end
  }
  pack
}

Tk.mainloop

イメージつきボタンを使用する

標準のビットマップを使う。色を変えることもできる。

#!/usr/bin/env ruby

require "tk"

["error", "gray12", "gray25", "gray50", "gray75", "hourglass",
 "info", "questhead", "question", "warning"].each {|s|
  TkButton.new {
    bitmap s
    fg "darkgreen"
    pack("side" => "left")
  }
}

Tk.mainloop

自分の画像を使う。XBM,GIF,PPM,PGM が使えるらしい。 TkImage は抽象クラスなので直接使うとエラーになる。派生クラスの TkBitmapImage, TkPhotoImage を使う。

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  image TkPhotoImage.new("file" => "your.gif")
  pack
}

Tk.mainloop

拡張 tk の機能を使うと、JPEG や PNG なども読み込める(ruby-1.8.2以降)

#!/usr/bin/env ruby

require "tk"
require "tkextlib/tkimg/png"

TkButton.new {
  image TkPhotoImage.new("file" => "your.png")
  pack
}

Tk.mainloop

CheckButton Widget

ボタンの選択、非選択を切り換える

#!/usr/bin/env ruby

require "tk"

cb = TkCheckButton.new {
  text "CheckButton"
  pack('fill' => 'x')
}

TkButton.new {
  text "select"
  command { cb.select }
  pack('fill' => 'x')
}

TkButton.new {
  text "deselect"
  command { cb.deselect }
  pack('fill' => 'x')
}

TkButton.new {
  text "toggle"
  command { cb.toggle }
  pack('fill' => 'x')
}

Tk.mainloop

チェックされているかどうか調べる

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new

cb = TkCheckButton.new {
  text "CheckButton"
  variable v
  deselect
  #onvalue 'checkon'
  #offvalue 'checkoff'
  pack('fill' => 'x')
}

TkButton.new {
  text "variable"
  command { print "#{v}\n" }
  pack('fill' => 'x')
}

Tk.mainloop

ボタンをグループ化する

チェック領域を表示しないようにする

トグルボタンになる。

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new

cb = TkCheckButton.new {
  text "CheckButton"
  variable v
  deselect
  indicatoron 'off'
  pack('fill' => 'x')
}

TkButton.new {
  text "variable"
  command { print "#{v}\n" }
  pack('fill' => 'x')
}

Tk.mainloop

ビットマップを使用する

ボタンを選択(非選択)したときの動作を設定する

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new

cb = TkCheckButton.new {
  text "CheckButton"
  variable v
  deselect
  command { print "#{v}\n" }
  pack('fill' => 'x')
}

TkButton.new {
  text "variable"
  command { print "#{v}\n" }
  pack('fill' => 'x')
}

Tk.mainloop

ボタンを選択したときの背景色を設定する

#!/usr/bin/env ruby

require "tk"

cb = TkCheckButton.new {
  text "CheckButton"
  selectcolor 'blue'
  #indicatoron 'off'
  pack('fill' => 'x')
}

Tk.mainloop

インジケータが表示されているときはインジケータの色が変わる。 インジケータが表示されていないときはテキストの背景色が変わる。

チェック内容を別 Widget にリアルタイムで反映する

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new

cb = TkCheckButton.new {
  text "CheckButton"
  variable v
  onvalue 'checkon'
  offvalue 'checkoff'
  deselect # これが必要みたい
  pack('fill' => 'x')
}

TkLabel.new {
  textvariable v
  pack('fill' => 'x')
}

Tk.mainloop

TkVariable の値が変更されたときの動作を設定する

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new
v.trace("w", proc { puts v.value })

cb = TkCheckButton.new {
  text "CheckButton"
  variable v
  onvalue 'checkon'
  offvalue 'checkoff'
  deselect # これが必要みたい
  pack('fill' => 'x')
}

TkLabel.new {
  textvariable v
  pack('fill' => 'x')
}

Tk.mainloop

RadioButton Widget

どのボタンが選択されているかどうかを調べる

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new

rb1 = TkRadioButton.new {
  text "RadioButton 1"
  variable v
  value 1
  pack('fill' => 'x')
}

rb2 = TkRadioButton.new {
  text "RadioButton 2"
  variable v
  value 2
  pack('fill' => 'x')
}

TkButton.new {
  text "RadioButton value"
  command { puts v }
  #command { puts v.value.to_i + 1  }
  pack('fill' => 'x')
}

Tk.mainloop

ボタンの選択、非選択を切り換える

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new

rb1 = TkRadioButton.new {
  text "RadioButton 1"
  variable v
  value 1
  pack('fill' => 'x')
}

rb2 = TkRadioButton.new {
  text "RadioButton 2"
  variable v
  value 2
  pack('fill' => 'x')
}

TkButton.new {
  text "RadioButton 1 select"
  command { rb1.select }
  pack('fill' => 'x')
}

TkButton.new {
  text "RadioButton 1 deselect"
  command { rb1.deselect }
  pack('fill' => 'x')
}

Tk.mainloop

ボタンをグループ化する

チェック領域を表示しないようにする

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new

rb1 = TkRadioButton.new {
  text "RadioButton 1"
  variable v
  value 1
  indicatoron false
  pack('fill' => 'x')
}

rb2 = TkRadioButton.new {
  text "RadioButton 2"
  variable v
  value 2
  indicatoron false
  pack('fill' => 'x')
}

Tk.mainloop

ビットマップを使用する

ボタンを選択(非選択)したときの動作を設定する

Entry Widget

入力内容を取得する

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  pack
}

TkButton.new {
  text "Entry"
  command { print "#{e.value}\n" }
  pack('fill' => 'x')
}

Tk.mainloop

あらかじめ文字列を入力しておく

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  pack
}
e.value= "hello"

Tk.mainloop

または(self. がないとローカル変数への代入になってしまうので注意)

#!/usr/bin/env ruby

require "tk"

TkEntry.new {
  self.value = "hello"
  pack
}

Tk.mainloop

または(ruby-1.8.2以降)

#!/usr/bin/env ruby

require "tk"

TkEntry.new {
  set "hello"
  pack
}

Tk.mainloop

入力内容をクリアする

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  pack
}

TkButton.new {
  text "Entry"
  command { e.value = "" }
  #command { e.delete(0, 'end') }
  pack('fill' => 'x')
}

Tk.mainloop

複数行の入力を行う

Text Widget を使いましょう

パスワードの入力を行う

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  show '*'
  pack
}

Tk.mainloop

数値のみ入力できるようにする

#!/usr/bin/env ruby

require 'tk'

entry = TkEntry.new {
  bind 'KeyPress', proc { |e|
    if e.to_s !~ /[0-9\b]/
      Tk.bell
      Tk.callback_break
    end
  }, "%A"
  pack
}

Tk.mainloop

/[0-9\b]//[0-9\b-]/などに変更すると、 「-」が入力できるようになるので、電話番号などの入力用に使えるでしょう。 本当は valid* というのが使えるのかも。

使ってみました。validate を "key" にすると、文字列が挿入・削除されそうな時に vcmd が呼ばれます。vcmd が false を返すと、挿入・削除がキャンセルされ、 invcmd が呼ばれます。

#!/usr/bin/env ruby

require 'tk'

entry = TkEntry.new {
  validate "key"
  vcmd proc {|s| s =~ /[0-9]/}, "%S" # validatecommand
  invcmd proc { Tk.bell } # invalidcommand
  pack
}

Tk.mainloop

入力最大文字数を設定する

Gtk とかは入力最大文字数を簡単に設定できるようだが、Tk にはないようだ。 以下のようなコードでどうだろうか? (最大数を超えるとバックスペースしか入力できないのが、ちょっとかっこ悪い…)

#!/usr/bin/env ruby

require 'tk'

max_length = 5

entry = TkEntry.new {
  bind 'KeyPress', proc { |e|
    if e.to_s !~ /[\b]/ && value.size >= max_length
      Tk.bell
      Tk.callback_break
    end
  }, "%A"
  pack
}

Tk.mainloop

最大数を超えたときに移動、削除系の入力もできるようにしたもの

#!/usr/bin/env ruby

require 'tk'

max_length = 5

entry = TkEntry.new {
  bind 'KeyPress', proc { |e,k|
    # \b    C-h
    # \001  C-a
    # \004  C-d
    # \005  C-e
    # \006  C-f
    # \013  C-k
    # \028  C-b
    if e.to_s !~ /[\b\001\004\005\006\028\013]/ &&
       !((k == 'Control_L') || (k == 'Control_R') ||
         (k == 'Left') || (k == 'Right') ||
         (k == 'Home') || (k == 'End') || (k == 'Delete')) &&
       value.size >= max_length &&
      Tk.bell
      Tk.callback_break
    end
  }, "%A %K"
  pack
}

Tk.mainloop

valid* を使うと、6文字以上の長さの文字列をコピペされても大丈夫

#!/usr/bin/env ruby

max_length = 5

require 'tk'

entry = TkEntry.new {
  validate "key"
  vcmd proc {|s| s.size <= max_length }, "%P" # validatecommand
  invcmd proc { Tk.bell } # invalidcommand
  pack
}

Tk.mainloop

Read Only にする

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  state 'disabled'
  pack
}

Tk.mainloop

'disabled'にすると、スクリプト内での値の書き換えも出来なくなってしまう。

選択部分を取得する

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  pack
}

TkButton.new {
  text 'print selection'
  command {
    if e.selection_present
      puts e.value[e.index('sel.first'),e.index('sel.last')]
    end
  }
  pack
}

Tk.mainloop

日本語が入っていた場合うまく動くかは未確認。

選択部分を消去する

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  pack
}

TkButton.new {
  text 'delete selection'
  command { e.delete('sel.first', 'sel.last') if e.selection_present }
  pack
}

Tk.mainloop

特定の領域を選択状態にする

フォーカスされたら全体を選択状態にする

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  bind 'FocusIn', proc { selection_range(0, 'end') }
  pack
}

TkButton.new {
  text 'Quit'
  command { exit }
  pack
}

Tk.mainloop

別の Widget に移ったら選択を解除する仕様の方が良ければ、

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  bind 'FocusIn', proc { selection_range(0, 'end') }
  bind 'FocusOut', proc { selection_clear }
  pack
}

TkButton.new {
  text 'Quit'
  command { exit }
  pack
}

Tk.mainloop

日本語入力を開始する

フォーカスが移ってきたときに日本語入力を自動的にONする

tk に日本語パッチが当っている場合のみ使用可(多分)

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new {
  bind 'FocusIn', proc { TkKinput.start(self, 'over') }
  bind 'FocusOut', proc { TkKinput.input_end(self) }
  pack
}

TkButton.new {
  text 'Quit'
  command { exit }
  pack
}

Tk.mainloop

カーソル位置を取得する

#!/usr/bin/env ruby

require "tk"

e = TkEntry.new.pack

TkButton.new {
  text 'pos'
  command { print "#{e.cursor}\n" }
  pack
}

Tk.mainloop

Enter で入力を完了できるようにする

Enter で次の入力欄に移動できるようにする

#!/usr/bin/env ruby

require "tk"

e1 = TkEntry.new {
  pack
}
e2 = TkEntry.new {
  pack
}

e1.bind 'Return', proc { e2.focus }
e2.bind 'Return', proc { e1.focus }

Tk.mainloop

入力内容を別 Widget にリアルタイムで反映する

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new

e = TkEntry.new {
  textvariable v
  pack('fill' => 'x')
}

TkLabel.new {
  textvariable v
  pack('fill' => 'x')
}

Tk.mainloop

スクロールバーを付ける

#!/usr/bin/env ruby

require "tk"

scr = TkScrollbar.new.pack('fill'=>'x', 'side'=>'bottom')
TkEntry.new.pack('side'=>'bottom').xscrollbar(scr)

# 界道編 p.215 リスト 7-6

Tk.mainloop

Text Widget

スクロールバーを付ける

Y方向のみ

#!/usr/bin/env ruby

require "tk"

scr = TkScrollbar.new.pack('fill'=>'y', 'side'=>'right')
TkText.new {
  yscrollbar(scr)
  pack('fill' => 'both', 'expand' => true, 'side'=>'right')
}

Tk.mainloop

X, Y方向両方

#!/usr/bin/env ruby

require "tk"

scr_x = TkScrollbar.new.pack('fill'=>'x', 'side'=>'bottom')
scr_y = TkScrollbar.new.pack('fill'=>'y', 'side'=>'right')
TkText.new {
  xscrollbar(scr_x)
  yscrollbar(scr_y)
  pack('fill' => 'both', 'expand' => true, 'side'=>'right')
}

Tk.mainloop

テキストの全内容を取得する

#!/usr/bin/env ruby

require "tk"

t = TkText.new.pack

TkButton.new {
  text "print"
  command { print t.value; STDOUT.flush }
  pack('fill' => 'x')
}

Tk.mainloop

テキストの指定範囲の内容を取得する

テキストの全内容を消去する

#!/usr/bin/env ruby

require "tk"

t = TkText.new {
  pack
}

TkButton.new {
  text "Clear"
  command { t.value = "" }
  #command { t.delete('1.0', 'end') }
  pack('fill' => 'x')
}

Tk.mainloop

Read Only にする

#!/usr/bin/env ruby

require "tk"

t = TkText.new {
  #insert "1.0", "hogehoge"
  state 'disabled'
  pack
}

Tk.mainloop

'disabled'にすると、スクリプト内での値の書き換えも出来なくなってしまう。

選択部分を取得する

#!/usr/bin/env ruby

require "tk"

t = TkText.new {
  pack
}

TkButton.new {
  text 'print selection'
  command {
    sel = TkTextTagSel.new(t)
    begin
      puts t.get(sel.first, sel.last)
    rescue # 選択していないとき用(一度選択して選択を解除したとき)
    end
  }
  pack
}

Tk.mainloop

クリップボードを使う

カーソル位置を取得する

行.桁 で文字列が返って来る(一番上端左端は1.0)

#!/usr/bin/env ruby

require "tk"

t = TkText.new {
  pack
}

i = TkTextMarkInsert.new(t)

TkButton.new {
  text 'print cursor position'
  command {
    puts t.index(i)
  }
  pack
}

Tk.mainloop

カーソル位置に文字を挿入する

Entry Widget で Enter を押すと Text に挿入されます。

#!/usr/bin/env ruby

require "tk"

t = TkText.new {
  pack
}

i = TkTextMarkInsert.new(t)

TkEntry.new {
  bind 'Return', proc { t.insert(t.index(i), value); delete(0, 'end') }
  pack
}

Tk.mainloop

Undo する

画像を表示する

文字の属性を変更する

文字に下線を引く

文字の上に横線を引く

タブキーで別 Widget に移動する

ListBox Widget

項目を追加する

#!/usr/bin/env ruby

require "tk"

i = 1

l = TkListbox.new {
  pack
}

TkButton.new {
  text 'insert'
  command { l.insert('end', "item #{i}"); i += 1 }
  pack
}

Tk.mainloop

先頭に追加するには、commandの部分を

command { l.insert(0, "item #{i}"); i += 1 }

のようにすれば良い。

項目を削除する

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

TkButton.new {
  text 'delete(1)'
  command { l.delete(1) }
  pack
}

TkButton.new {
  text 'delete(2,3)'
  command { l.delete(2,3) }
  pack
}

Tk.mainloop

全項目を削除する

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

TkButton.new {
  text 'delete'
  command { l.delete(0, 'end') }
  pack
}

Tk.mainloop

項目数を取得する

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

TkButton.new {
  text 'size'
  command { print "#{l.size}\n" }
  pack
}

Tk.mainloop

選択された項目を取得する

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  #selectmode 'multiple'
  selectmode 'extended'
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

TkButton.new {
  text 'print selection'
  command { l.curselection.each { |i| print "#{l.get(i)}\n" } }
  pack
}

Tk.mainloop

選択された項目数を取得する

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  #selectmode 'multiple'
  selectmode 'extended'
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

TkButton.new {
  text 'print selection'
  command { puts l.curselection.size }
  pack
}

Tk.mainloop

複数選択できるリストボックスを作る

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

TkButton.new {
  text 'single'
  command { l.selectmode 'single' }
  pack
}

TkButton.new {
  text 'browse'
  command { l.selectmode 'browse' }
  pack
}

TkButton.new {
  text 'multiple'
  command { l.selectmode 'multiple' }
  pack
}

TkButton.new {
  text 'extended'
  command { l.selectmode 'extended' }
  pack
}

Tk.mainloop

リストボックスの項目が選択されたときの動作を設定する

マウスのドラッグ中でも、選択範囲が変わるたびに呼び出される。 (最終的に確定したときによばれるわけではないようだ)

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  #selectmode 'multiple'
  selectmode 'extended'
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

l.bind '<ListboxSelect>', proc { p l.curselection }

Tk.mainloop

リストボックスを使用不可にする

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

TkButton.new {
  text "toggle"
  command {
    if l.state == "normal"
      l.state "disabled"
    else
      l.state "normal"
    end
  }
  pack
}

Tk.mainloop

もしくは(上は下の構文糖)

#!/usr/bin/env ruby

require "tk"

l = TkListbox.new {
  pack
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

TkButton.new {
  text "toggle"
  command {
    if l.cget("state") == "normal"
      l.configure("state" => "disabled")
    else
      l.configure("state" => "normal")
    end
  }
  pack
}

Tk.mainloop

スクロールバーを付ける

#!/usr/bin/env ruby

require "tk"

scr = TkScrollbar.new.pack('fill'=>'y', 'side'=>'right')
l = TkListbox.new {
  yscrollbar(scr)
  pack('fill' => 'both', 'expand' => true, 'side'=>'right')
}

0.upto(10) { |i| l.insert('end', "item #{i}") }

Tk.mainloop

連動するスクロールバーを付ける

先頭に表示される項目を設定する

multi column なリストボックスを作る

標準ではできません。

Scale Widget

スケールの範囲を設定する

#!/usr/bin/env ruby

require 'tk'

TkScale.new {
  from 0
  to   200
  pack
}

Tk.mainloop

水平のスケールを使用する

#!/usr/bin/env ruby

require 'tk'

TkScale.new {
  from 0
  to   200
  orient 'horizontal'
  pack
}

Tk.mainloop

現在の値を取得する

#!/usr/bin/env ruby

require 'tk'

s = TkScale.new {
  from 0
  to   200
  pack
}

TkButton.new {
  text 'value'
  command { print "#{s.value}\n" }
  pack
}

Tk.mainloop

値を変更する

#!/usr/bin/env ruby

require 'tk'

s = TkScale.new {
  from 0
  to   200
  orient 'horizontal'
  pack('fill' => 'x')
}

TkEntry.new {
  bind 'Return', proc { s.value = value }
  pack('fill' => 'x')
}

Tk.mainloop

現在の値を表示するかどうかを変える

デフォルトでは値が表示される。

#!/usr/bin/env ruby

require 'tk'

TkScale.new {
  from 0
  to   200
  #showvalue 0
  #showvalue 'no'
  showvalue 'off'
  pack
}

Tk.mainloop

ラベル(文字列)を傍に表示する。

#!/usr/bin/env ruby

require "tk"

TkScale.new {
  from 0
  to   255
  label "scale"
  pack
}

Tk.mainloop

値の増減単位を設定する。

#!/usr/bin/env ruby

require 'tk'

TkScale.new {
  from 0
  to   200
  resolution 10
  pack
}

Tk.mainloop

スケールの移動単位を設定する

値の表示精度を設定する。

スケールの値が変更されたときの動作を設定する

#!/usr/bin/env ruby

require 'tk'

TkScale.new {
  from 0
  to   200
  command { puts value }
  pack
}

Tk.mainloop

Canvas Widget

線を描画する

#!/usr/bin/env ruby

require 'tk'

c = TkCanvas.new {
  #width 200
  #height 200
  pack
}

#              x1  y1  x2  y2  x3   y3 ...
TkcLine.new(c, 10, 10, 50, 50, 100, 50)

Tk.mainloop

もしくは、

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new {
  #width 200
  #height 200
  #                 x1  y1  x2  y2  x3   y3 ...
  TkcLine.new(self, 10, 10, 50, 50, 100, 50)
  pack
}

Tk.mainloop

もしくは、

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #              x1  y1  x2  y2  x3   y3 ...
  TkcLine.new(c, 10, 10, 50, 50, 100, 50)
  pack
}

Tk.mainloop

色を変えたり、矢印にしたり、線の太さを変えることもできる。

#!/usr/bin/env ruby

require "tk"

c = TkCanvas.new {
  width 100
  height 100
  bg 'blue'
  pack
}

TkcLine.new(c, 20, 15, 60, 55) {
  fill 'red'
  arrow 'first'
}

TkcLine.new(c, 50, 15, 90, 55) {
  fill 'green'
  arrow 'last'
}

TkcLine.new(c, 35, 50, 75, 90) {
  fill 'yellow'
  arrow 'both'
  width 2
}

Tk.mainloop

多角形を描画する

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #                 x1  y1  x2   y2  x3   y3 ...
  TkcPolygon.new(c, 50, 50, 100, 50, 150, 150)
  pack
}

Tk.mainloop

四角を描画する

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #                   x1  y1  x2   y2
  TkcRectangle.new(c, 50, 75, 150, 125)
  pack
}

Tk.mainloop

円を描画する

外接長方形を(x1,y1,x2,y2)で指定する。 正方形なら真円になるし、そうでなければ楕円になる。

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #              x1  y1  x2   y2
  TkcOval.new(c, 50, 50, 150, 150)
  pack
}

Tk.mainloop

扇形を描画する

外接長方形を(x1,y1,x2,y2)で指定する。 弧の開始角を、時計の3時方向を0度として"start"に指定する。 そしてその位置から、反時計回りにどれだけ進んだ角度で弧が終了するかを "extent"に指定する。(単位は度)

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #             x1  y1  x2   y2
  TkcArc.new(c, 50, 50, 150, 150, "start" => 0, "extent" => 270)
  pack
}

Tk.mainloop

テキストを描画する

デフォルトでは、座標はテキストの中心の座標(左上ではない)

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  TkcText.new(c, 50, 50) {|t|
    text "Hello"
    #font TkFont.new("Times 24")
  }
  pack
}

Tk.mainloop

anchorを使ってテキストのどの位置の座標か指定することができる。 (例えば "nw" だと文字の左上隅の座標という意味になる)

#!/usr/bin/env ruby

require "tk"

v = TkVariable.new('center')

%w(nw n ne w center e sw s se).each_with_index do |anchor, i|
  TkRadioButton.new {
    text anchor
    variable v
    value anchor
    command { $text.anchor = value }
    indicatoron false
    grid(:row=>i/3, :column=>i%3, :sticky=>:news)
  }
end

TkCanvas.new {|c|
  width 200
  height 200
  TkcLine.new(c, 0, 100, 200, 100, :dash=>[1, 1])
  TkcLine.new(c, 100, 0, 100, 200, :dash=>[1, 1])
  $text = TkcText.new(c, 100, 100) {|t|
    text "Hello"
    font TkFont.new(:size=>30)
  }
  grid(:row=>4, :column=>0, :columnspan=>3, :sticky=>:news)
}

Tk.mainloop

イメージを描画する

同じく、座標はイメージの中心の座標

#!/usr/bin/env ruby

require "tk"
#require "tkextlib/tkimg/png"

image = TkPhotoImage.new("file" => "your.gif")
#image = TkPhotoImage.new("file" => "your.png")

TkCanvas.new {|c|
  #               x          y
  TkcImage.new(c, width / 2, height / 2, "image" => image)
  pack
}

Tk.mainloop

図形の中身を塗りつぶす

四角形の場合

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #                   x1  y1  x2   y2
  TkcRectangle.new(c, 50, 75, 150, 125, "fill" => "white", "outline" => "black")
  pack
}

Tk.mainloop

スクロールバーを付ける

#!/usr/bin/env ruby

require "tk"

scr_x = TkScrollbar.new.pack('fill'=>'x', 'side'=>'bottom')
scr_y = TkScrollbar.new.pack('fill'=>'y', 'side'=>'right')
c = TkCanvas.new {
  width 200
  height 200
  scrollregion [0, 0, 400, 400]
  xscrollbar(scr_x)
  yscrollbar(scr_y)
  pack('fill' => 'both', 'expand' => true, 'side'=>'right')
}
TkcOval.new(c, 50, 50, 150, 150)

Tk.mainloop

表示範囲を設定する

PostScript を出力する

GIF を出力する

Img 拡張を使うか Tk8.3 以降ならできる?

図形の色を変更する

#!/usr/bin/env ruby

require "tk"

TkCanvas.new { |c|
  #width 200
  #height 200
  #                        x1  y1  x2   y2
  $r = TkcRectangle.new(c, 50, 75, 150, 125)
  pack
}

TkButton.new {
  text "Warm"
  command { $r.configure("fill" => "yellow", "outline" => "red") }
  pack
}

TkButton.new {
  text "Cool"
  command { $r.configure("fill" => "LightBlue", "outline" => "blue") }
  pack
}

Tk.mainloop

もしくは、

#!/usr/bin/env ruby

require "tk"

TkCanvas.new { |c|
  #width 200
  #height 200
  #                        x1  y1  x2   y2
  $r = TkcRectangle.new(c, 50, 75, 150, 125)
  pack
}

TkButton.new {
  text "Warm"
  command { $r.fill "yellow"; $r.outline "red" }
  pack
}

TkButton.new {
  text "Cool"
  command { $r.fill "LightBlue"; $r.outline "blue" }
  pack
}

Tk.mainloop

タグを使えば、複数の図形の色を同時に変更できる

#!/usr/bin/env ruby

require 'tk'

$c = TkCanvas.new { |c|
  #width 200
  #height 200
  #                   x1   y1  x2   y2
  TkcRectangle.new(c, 50,  75, 150, 125, "tags" => "hoge")
  TkcRectangle.new(c, 70,  95, 170, 145, "fill" => "LightBlue")
  TkcRectangle.new(c, 90, 115, 190, 165, "tags" => "hoge")
  pack
}

TkButton.new {
  text "ivory"
  command { $c.itemconfigure("hoge", "fill" => "ivory") }
  pack
}

TkButton.new {
  text "white"
  command { $c.itemconfigure("hoge", "fill" => "white") }
  pack
}

Tk.mainloop

TkcTag を使えば、もっと直感的に記述できる

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  $tag = TkcTag.new(c)
  TkcRectangle.new(c, 50,  75, 150, 125, "tags" => $tag)
  TkcRectangle.new(c, 70,  95, 170, 145, "fill" => "LightBlue")
  TkcRectangle.new(c, 90, 115, 190, 165, "tags" => $tag)
  pack
}

TkButton.new {
  text "ivory"
  command { $tag.fill("ivory") }
  pack
}

TkButton.new {
  text "white"
  command { $tag.fill("white") }
  pack
}

Tk.mainloop

図形の大きさを変更する & 図形を移動する

この二つは同時に処理できる(Tkc??? のコンストラクタで指定された座標を変更)

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #                        x1  y1  x2   y2
  $r = TkcRectangle.new(c, 50, 75, 150, 125)
  pack
}

TkButton.new {
  text "coords 1"
  command { $r.coords(60, 85, 160, 135) }
  pack
}

TkButton.new {
  text "coords 2"
  command { $r.coords(40, 85, 120, 235) }
  pack
}

Tk.mainloop

移動を相対量で行うには

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #                        x1  y1  x2   y2
  $r = TkcRectangle.new(c, 50, 75, 150, 125)
  pack
}

TkButton.new {
  text "move 1"
  #                 dx  dy
  command { $r.move(10, 10) }
  pack
}

TkButton.new {
  text "move 2"
  command { $r.move(-10, -10) }
  pack
}

Tk.mainloop

拡大縮小を比率で行うには、scale というメソッドを使う。x, y を中心に ax, ay 倍拡大される。

#!/usr/bin/env ruby

require "tk"

TkCanvas.new {
  width 300
  height 300
  arc = TkcArc.new(self, 50, 50, 100, 100, "start" => 0, "extent" => 270)
  #                                 x   y   ax       ay
  bind 'Button-1', proc { arc.scale(20, 20, 4.0 / 3, 4.0 / 3) }
  bind 'Button-3', proc { arc.scale(20, 20, 3.0 / 4, 3.0 / 4) }
  pack
}

Tk.mainloop

図形を消去する

#!/usr/bin/env ruby

require 'tk'

$c = TkCanvas.new { |c|
  #width 200
  #height 200
  #                         x1   y1  x2   y2
  $r1 = TkcRectangle.new(c, 50,  75, 150, 125)
  $r2 = TkcRectangle.new(c, 70,  95, 170, 145)
  $r3 = TkcRectangle.new(c, 90, 115, 190, 165)
  pack
}

TkButton.new {
  text "delete one"
  command { $c.delete $r2 }
# command { $r2.destroy }
  pack
}

TkButton.new {
  text "delete all"
  command { $c.delete "all" }
  pack
}

Tk.mainloop

一時的に隠すだけなら、

#!/usr/bin/env ruby

require 'tk'

TkCanvas.new { |c|
  #width 200
  #height 200
  #                        x1  y1  x2   y2
  $r = TkcRectangle.new(c, 50, 75, 150, 125)
  pack
}

TkButton.new {
  text "hide"
  command { $r.state "hidden" }
  pack
}

TkButton.new {
  text "show"
  command { $r.state "normal" }
  pack
}

Tk.mainloop

図形の上下関係を設定する

lower(raise) の引数に指定した図形のすぐ下(上)に来る

#!/usr/bin/env ruby

require "tk"

TkCanvas.new { |c|
  #width 200
  #height 200
  #                         x1  y1  x2   y2
  $r1 = TkcRectangle.new(c, 50, 75, 150, 125, "fill" => "white")
  $r2 = TkcRectangle.new(c, 70, 95, 170, 145, "fill" => "LightBlue")
  pack
}

TkButton.new {
  text "Raise"
  command { $r1.raise $r2 }
  pack
}

TkButton.new {
  text "Lower"
  command { $r1.lower $r2 }
  pack
}

Tk.mainloop

引数を省略すると、全図形の一番下(一番上)に来る

#!/usr/bin/env ruby

require "tk"

TkCanvas.new { |c|
  #width 200
  #height 200
  #
  #                         x1  y1  x2   y2
  $r1 = TkcRectangle.new(c, 50,  75, 150, 125, "fill" => "white")
  $r2 = TkcRectangle.new(c, 70,  95, 170, 145, "fill" => "LightBlue")
  $r3 = TkcRectangle.new(c, 90, 115, 190, 165, "fill" => "ivory")
  pack
}

TkButton.new {
  text "order 1"
  command { $r1.lower; $r2.lower; $r3.lower }
  pack
}

TkButton.new {
  text "order 2"
  command { $r1.raise; $r2.raise; $r3.raise }
  pack
}

Tk.mainloop

図形をドラッグして移動する

"fill"がない時は、輪郭線をドラッグしたときだけ移動して、 "fill"がある時は、内側をドラッグしたときも移動した。

"ButtonPress-1" の -1 は、左ボタンという意味。

#!/usr/bin/env ruby

require "tk"

TkCanvas.new { |c|
  #width 200
  #height 200
  #                   x1  y1  x2   y2
  TkcRectangle.new(c, 50, 75, 150, 125) {
    fill "white"
    a = nil
    bind "ButtonPress-1", proc {|x,y| a = [x,y] }, "%x %y"
    bind "Motion", proc {|x,y| next unless a
      move(x - a[0], y - a[1]); a = [x,y] }, "%x %y"
    bind "ButtonRelease-1", proc { a = nil }
  }
  pack
}

Tk.mainloop

下のようにすると、全部の図形をまとめて動かしたり、特定のタグを振った 図形だけ動かしたりできる

#!/usr/bin/env ruby

require "tk"

$tag = nil

TkCanvas.new { |c|
  #width 200
  #height 200
  $tag_two = TkcTag.new(c)
  $tag_all = TkcTagAll.new(c)
  #                   x1  y1   x2   y2
  TkcRectangle.new(c, 50, 75,  150, 125, "fill" => "white", "tags" => $tag_two)
  TkcRectangle.new(c, 70, 95,  170, 145, "fill" => "LightBlue")
  TkcRectangle.new(c, 90, 115, 190, 165, "fill" => "ivory", "tags" => $tag_two)
  pack
}

def setup(tag)
  if $tag
    $tag.bind_remove("ButtonPress-1")
    $tag.bind_remove("Motion")
    $tag.bind_remove("ButtonRelease-1")
  end
  $tag = tag
  if $tag
    a = nil
    $tag.bind "ButtonPress-1", proc {|x,y| a = [x,y] }, "%x %y"
    $tag.bind "Motion", proc {|x,y| next unless a;
    $tag.move(x - a[0], y - a[1]); a = [x,y] }, "%x %y"
    $tag.bind "ButtonRelease-1", proc { a = nil }
  end
end

TkButton.new {
  text "two figures"
  command { setup($tag_two) }
  pack
}

TkButton.new {
  text "all figures"
  command { setup($tag_all) }
  pack
}

TkButton.new {
  text "no figures"
  command { setup(nil) }
  pack
}

Tk.mainloop

図形を一定時間ごとに移動する

TkAfter.new の第一引数は実行される間隔(ミリ秒)で、 第二引数は実行される回数(負の数ならば無限回数)

#!/usr/bin/env ruby

require "tk"

TkCanvas.new { |c|
  #width 300
  #height 300
  #                    x1  y1  x2   y2
  $arc = TkcArc.new(c, 10, 10, 110, 110, "start" => 0, "extent" => 270)
  pack
}

def timer
  n = 0
  TkAfter.new(100, -1, proc { # or TkTimer
    $arc.move(30 * Math.sin(Math::PI * n / 10), 0) # move
    $arc.configure("start" => n * 20) # rotate
    n += 1
  }).start
end

timer # start timer

Tk.mainloop

もしくは、(ruby-1.8.2 以降)

#!/usr/bin/env ruby

require "tk"

TkCanvas.new { |c|
  #width 300
  #height 300
  #                    x1  y1  x2   y2
  $arc = TkcArc.new(c, 10, 10, 110, 110, "start" => 0, "extent" => 270)
  pack
}

def timer(n)
  Tk.after(100, proc {
    $arc.move(30 * Math.sin(Math::PI * n / 10), 0) # move
    $arc.configure("start" => n * 20) # rotate
    timer(n + 1) # repeat timer
  })
end

timer(0) # start timer

Tk.mainloop

メニュー

項目に下線を引く

チェックボタンメニューを作る

ラジオボタンメニューを作る

カスケードメニューを作る

セパレータを使う

イメージを使用する

切り離されないメニューを作る

ポップアップメニューを作る

たとえば、以下のようにすると、テキスト領域の右ボタンで ポップアップメニューが出る。

#!/usr/bin/env ruby

require "tk"

scr_x = TkScrollbar.new.pack('fill'=>'x', 'side'=>'bottom')
scr_y = TkScrollbar.new.pack('fill'=>'y', 'side'=>'right')
t = TkText.new {
  xscrollbar(scr_x)
  yscrollbar(scr_y)
  pack('fill' => 'both', 'expand' => true, 'side'=>'right')
}

# menu
m = TkMenu.new {
  tearoff 'off'
}
['Cut', 'Copy', 'Paste'].each do |x|
  m.add('command', 'label' => x,
        'command' => proc { puts x })
end

t.bind 'ButtonPress-3', proc { |x, y| m.popup(x, y) }, "%X %Y"

Tk.mainloop

オプションメニューを作る

ショートカットキーを設定する

メニューの項目を使用不可にする

ヘルプメニューのみ右端に付けるようにする

ダイアログ

ダイアログボックスを表示する

メッセージボックスを表示する

独自のダイアログを作成する

ファイル選択のダイアログを表示する

Tk.getOpenFile を使用する。

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "File Open"
  command { file = Tk.getOpenFile; puts file }
  pack
}

Tk.mainloop

以下のようにオプションをハッシュで指定できる。

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "File Open"
  command { file = Tk.getOpenFile({
    'title' => 'File Open',
    'filetypes' => [['text', '.txt'], ['All Files', '*']],
  }); puts file }
  pack
}

Tk.mainloop

ディレクトリ選択のダイアログを表示する

カラー選択のダイアログを表示する

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "chooseColor"
  #command { color = Tk.chooseColor ; p color }
  command { color = Tk.chooseColor('initialcolor' => 'red',
                                   'title' => 'Color Selection') ; p color }
  pack
}

Tk.mainloop

イメージ

イメージを表示する

PNG, JPEG, TIFF などを使用する

対応する tk extension がインストールされていれば、それぞれ

と書くだけで GIF と同じように TkPhotoImage で読み込めるようになる。 (余談ですが、JPEGの読み込みがActiveTcl8.4.9.1から大幅に高速化してます)

XBM, PPM, PGM をスクリプトに埋め込む

GIF をスクリプトに埋め込む

画像をBase64エンコーディングしたものを埋め込む。画像からは、 Base64.encode64(要 require "base64")を使って生成できる。

require "tk"

data1 = <<EOS
R0lGODlhEAAQACIAACwAAAAAEAAQAIIAAAB/fwD//wC/v7////8AAAAAAAAA
AAADOUi60bEQhgFAAFFS1XAml+RlITNGl9M9qOqe7FdyhGDf9gyC7qQ/ONyP
5xoMg7dfT3WqOJ+VjxSSAAA7
EOS

data2 = <<EOS
R0lGODlhEAAQACIAACwAAAAAEAAQAIIAAAB/AAB/f38A////AAD/AP+/v7//
//8DP3gqDPpwnTFdfGJOM2zMVMh5WLgNBimJaHcJLSerS4wCwVXPRJHrAoAQ
1/vpHkTfEZI0LptLZIAQcB5xtKsiAQA7
EOS

TkLabel.new {
  image TkPhotoImage.new(:data=>data1)
  pack
}

TkLabel.new {
  image TkPhotoImage.new(:data=>data2)
  pack
}

Tk.mainloop

GIF を Web サイトからダウンロードして表示する

TkPhotoImage.new の :data には、Base64エンコーディングした画像や、 Tk::BinaryStringを通した画像を渡すことができる。

require "tk"
require "open-uri"

binary = open("http://www.ruby-lang.org/image/title.gif", "rb"){|io| io.read}

TkLabel.new {
  image TkPhotoImage.new(:data=>Tk::BinaryString(binary))
  pack
}

Tk.mainloop

または

require "tk"
require "open-uri"
require "base64"

binary = open("http://www.ruby-lang.org/image/title.gif", "rb"){|io| io.read}

TkLabel.new {
  image TkPhotoImage.new(:data=>Base64.encode64(binary))
  pack
}

Tk.mainloop

Combobox

標準では使えない。 立石さんなどが作られています。 <URL:http://kt-www.jaist.ac.jp:8000/~ttate/lang/ruby/scripts.html>

標準ではできないこと

その他

プログラムを終了する

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "Quit"
  command { exit }
  #command { Tk.root.destroy }
  pack
}

Tk.mainloop

ウィンドウマネージャーのクローズボタンを押したときにすぐに終了しないようにする

#!/usr/bin/env ruby

require 'tk'

def quit
  answer = Tk.messageBox('icon' => 'warning',
                         'type' => 'okcancel',
                         'title' => 'Quit?',
                         'message' => 'Quit?')
  if answer == 'ok'
    exit
  end
end

button = TkButton.new {
  text "Quit"
  command { quit }
  pack
}

Tk.root.protocol("WM_DELETE_WINDOW", proc { button.invoke })

Tk.mainloop

タイトルを設定する

#!/usr/bin/env ruby

require 'tk'

Tk.root.title("Test")

Tk.mainloop

アイコンを設定する

アイコン化されたときに表示される文字列を設定する

#!/usr/bin/env ruby

require 'tk'

Tk.root.iconname("Test")

Tk.mainloop

新しいウィンドウを生成する

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "New Window"
  command { TkToplevel.new }
  pack
}

Tk.mainloop

ウィンドウの中に何か置く場合は、

#!/usr/bin/env ruby

require "tk"

def new_window
  t = TkToplevel.new
  TkButton.new(t) {
    text "destroy"
    command { t.destroy }
    pack
  }
end

TkButton.new {
  text "New Window"
  command { new_window }
  pack
}

Tk.mainloop

ウィンドウを消去する

ウィンドウを非表示にする

画面中央にウィンドウを表示する

ウィンドウを移動する

#!/usr/bin/env ruby

require 'tk'

TkButton.new {
  text "Move Window"
  command { Tk.root.geometry "+100+100" }
  pack
}

Tk.mainloop

画面の大きさを取得する

ウィンドウの大きさを固定する

#!/usr/bin/env ruby

require 'tk'

TkLabel.new {
  text "大きさを変えてみ"
  pack
}

Tk.root.resizable(0, 0)

Tk.mainloop

モーダルウィンドウを作成する

#!/usr/bin/env ruby

require "tk"

TkButton.new {
  text "Modal Window"
  command {
    w = TkToplevel.new.grab("set") # modal
    TkButton.new(w) {
      text "Be modeless"
      command { w.grab("release") } # be modeless
      pack
    }
  }
  pack
}

Tk.mainloop

ベルを鳴らす

#!/usr/bin/env ruby

require 'tk'

button = TkButton.new {
  text "Bell"
  command { bell }
#  command { Tk.bell }
  pack
}

Tk.mainloop

Windows で動くようにする

一例として、

1. http://www.activestate.com/ から ActiveTcl Windows 版をダウンロードする

2. インストールする(以下、"C:\Tcl" にインストールしたと仮定)

3. C:\Tcl\bin にパスが通っていることを確認する

4a. VisualC++ の場合、環境変数 INCLUDE に C:\TCL\INCLUDE を、LIB に C:\TCL\LIB を設定

4b. bcc32 の場合、コンパイラの bin ディレクトリにある bcc32.cfg の -I の行に C:\TCL\INCLUDE を追加し、ruby をビルドするディレクトリで

coff2omf c:\tcl\lib\tcl84.lib tcl84.lib
coff2omf c:\tcl\lib\tk84.lib tk84.lib

をそれぞれ実行し、borland 用 lib ファイルを作成する。("84"はインストールした ActiveTcl のバージョンによって変える)

あとは普通に ruby をビルドする。

漢字コードを設定する

ウィンドウがアクティブのとき特定の Widget にフォーカスを移動する

使っている tk のバージョンを調べる

ruby -rtk -e 'p Tk::TCL_VERSION'
ruby -rtk -e 'p Tk::TK_VERSION'

使っている tk に日本語パッチが当たっているかどうか調べる

ruby -rtk -e 'p Tk::JAPANIZED_TK'

tk8.1以降なら日本語パッチが当っていなくても一応日本語が使える(はず)。

使っている tk のライブラリディレクトリを調べる

ruby -rtk -e 'p Tk::TCL_LIBRARY'
ruby -rtk -e 'p Tk::TK_LIBRARY'

環境を調べる

ruby -rtk -e 'p Tk::PLATFORM'
ruby -rtk -e 'p Tk::PLATFORM["platform"]'


The RWiki