トップページ‎ > ‎GUI‎ > ‎

Tk


目次

  1. 1 Widget全般
    1. 1.1 幅や高さを設定する
    2. 1.2 幅や高さを取得する
    3. 1.3 Widgetの見た目を変える
    4. 1.4 Widget間の間隔(余白)を設定する
    5. 1.5 前景・背景色を変える
    6. 1.6 アクティブ時(マウスが上に来たとき)の前景、背景色を変える
    7. 1.7 フォントの情報を取得する
    8. 1.8 フォントを変える
    9. 1.9 フォントの色を変える
    10. 1.10 フォントの大きさを変える
    11. 1.11 フォントの一覧を取得する
    12. 1.12 フォーカスを移動する
    13. 1.13 マウスカーソルの形状を変更する
    14. 1.14 時間のかかる処理時にマウスカーソルの形状を時計にする
    15. 1.15 Widgetの種類を取得する
  2. 2 配置
    1. 2.1 Widget間の間隔(余白)を設定する
    2. 2.2 親 Widget を指定する
    3. 2.3 Widgetを非表示にする
    4. 2.4 上から下に順にpackする
    5. 2.5 下から上に順にpackする
    6. 2.6 左から右に順にpackする
    7. 2.7 右から左に順にpackする
    8. 2.8 Widget間の幅を揃える
    9. 2.9 親 Widget が大きくなったときそれに合わせて Widget を大きくする
    10. 2.10 絶対座標で配置する
    11. 2.11 相対座標で配置する
    12. 2.12 格子状の配置をする
    13. 2.13 格子状の配置でいくつかのセルをまたがらせる
  3. 3 イベント
    1. 3.1 マウスの第一ボタンをクリックしたときの動作を設定する
    2. 3.2 マウスをダブルクリックしたときの動作を設定する
    3. 3.3 マウスの第二ボタンをクリックしたときの動作を設定する
    4. 3.4 マウスを移動したときの動作を設定する
    5. 3.5 マウスの座標を取得する
    6. 3.6 Enterキーを押したときの動作を設定する
    7. 3.7 どのキーが押されたかを取得する
    8. 3.8 Widget にフォーカスが移って来たときの動作を設定する
    9. 3.9 ファンクションキー F1 でヘルプを表示する
    10. 3.10 C-x C-c で終了する
  4. 4 Label Widget
    1. 4.1 表示後にテキストを変更する
    2. 4.2 表示したテキストを取得する
    3. 4.3 文字の位置揃えをする
    4. 4.4 複数行表示する
    5. 4.5 下線を引く
    6. 4.6 選択可能なラベルを作る
  5. 5 Button Widget
    1. 5.1 Enter で選択できるようにする
    2. 5.2 ショートカットできるようにする
    3. 5.3 ボタンを使用不可にする
    4. 5.4 イメージつきボタンを使用する
  6. 6 CheckButton Widget
    1. 6.1 ボタンの選択、非選択を切り換える
    2. 6.2 チェックされているかどうか調べる
    3. 6.3 チェック領域を表示しないようにする
    4. 6.4 ボタンを選択(非選択)したときの動作を設定する
    5. 6.5 ボタンを選択したときの背景色を設定する
    6. 6.6 チェック内容を別 Widget にリアルタイムで反映する
    7. 6.7 TkVariable の値が変更されたときの動作を設定する

Widget全般

幅や高さを設定する

require "tk"

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

Tk.mainloop
ボタンで大きさが変わるサンプル
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

幅や高さを取得する

require "tk"

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

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

Tk.mainloop

Widgetの見た目を変える

require "tk"

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

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

Tk.mainloop

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

require "tk"

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

Tk.mainloop

前景・背景色を変える

require "tk"

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

Tk.mainloop

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

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

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

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

フォントを変える

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

フォントの色を変える

require "tk"

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

Tk.mainloop

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

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
または、
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

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

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

フォーカスを移動する

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

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

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

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

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の種類を取得する

require "tk"

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

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

Tk.mainloop

配置

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

require "tk"

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

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

Tk.mainloop
以下のように書くと、Widget内の間隔があく
require "tk"

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

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

Tk.mainloop

親 Widget を指定する

require "tk"

f = TkFrame.new {
  pack
}

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

Tk.mainloop

Widgetを非表示にする

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する

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する

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する

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する

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間の幅を揃える

require "tk"

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

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

Tk.mainloop

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

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

絶対座標で配置する

require "tk"

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

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

Tk.mainloop

相対座標で配置する

require "tk"

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

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

Tk.mainloop

格子状の配置をする

require "tk"

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

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

各 Widget はそれぞれのグリッドの中央に配置される
親 Widget が大きさを変えてもグリッドの大きさは変わらない
となるため、場合によっては非常に不恰好になります。
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" オプションを指定すること で制御できます。

上に寄せる : "sticky" => "n" (north)
下に寄せる : "sticky" => "s" (south)
左に寄せる : "sticky" => "w" (west)
右に寄せる : "sticky" => "e" (east)
左右一杯に広げる : "sticky" => "we" (or "ew")
上下一杯に広げる : "sticky" => "ns" (or "sn")
全方位に広げる : "sticky" => "news" (n, e, w, s が含まれていればいい)
例えば Widget をグリッド一杯に広げるには、
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、第二引数に行・列番号、第三引数にオプションを指定します。

とりあえず全ての行・列が同じ比率で大きくなるようにするには
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" の値を変えると、大きくなる度合いを行・列によって変えることも できます。
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

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

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

イベント

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

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

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

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

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

require "tk"

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

Tk.mainloop

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

require "tk"

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

Tk.mainloop

マウスの座標を取得する

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キーを押したときの動作を設定する

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

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

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 にフォーカスが移って来たときの動作を設定する

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 でヘルプを表示する

require "tk"

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

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

Tk.mainloop

C-x C-c で終了する

require "tk"

TkFrame.new {
  width 100
  height 100
  pack
}

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

Tk.mainloop

Label Widget

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

require "tk"

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

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

Tk.mainloop

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

require "tk"

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

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

Tk.mainloop

文字の位置揃えをする

require "tk"

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

Tk.mainloop

複数行表示する

require "tk"

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

Tk.mainloop
これでもOK。
require "tk"

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

Tk.mainloop

下線を引く

require "tk"

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

Tk.mainloop

選択可能なラベルを作る

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

Button Widget

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

require "tk"

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

Tk.mainloop

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

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

ボタンを使用不可にする

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

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

標準のビットマップを使う。色を変えることもできる。
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 を使う。
require "tk"

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

Tk.mainloop
拡張 tk の機能を使うと、JPEG や PNG なども読み込める(ruby-1.8.2以降)
require "tk"
require "tkextlib/tkimg/png"

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

Tk.mainloop

CheckButton Widget

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

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

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

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

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

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

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

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

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

require "tk"

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

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

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

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 の値が変更されたときの動作を設定する

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





Comments