Tcl/Tkでは、ウィンドウにウィジェット(GUI 部品)をレイアウトするために、place、pack、gridという3つの方法が用意されています。
placeでは、ウィジェットに対して、配置するウィンドウのx座標とy座標を指定して、配置します。
サンプルで使用する画像はここからダウンロードできます。解凍した img フォルダをコードファイルと同じディレクトリに置いてください。
Linuxをお使いの場合は、次のようにして、Img パッケージをインストールしてください。(macOSやWindowsでActiveTcl 8.6をインストールした場合はこの作業は必要ありません)
sudo apt-get install libtk-img
package require Img
oo::class create Example {
constructor {} {
my initUI
}
method initUI {} {
wm title . 絶対配置
frame .fr -background #333
pack .fr -fill both -expand 1
image create photo img1 -file img/valley.jpg
label .fr.lbl1 -image img1
place .fr.lbl1 -x 20 -y 20
image create photo img2 -file img/waterfall.jpg
label .fr.lbl2 -image img2
place .fr.lbl2 -x 40 -y 160
image create photo img3 -file img/precipice.jpg
label .fr.lbl3 -image img3
place .fr.lbl3 -x 170 -y 50
}
}
wm geometry . 300x280+300+300
set e [Example new]
package require Img
JPGファイルを使用するために Img パッケージを読み込みます。
frame .fr -background #333
frame コマンドでフレームを作成します。フレームはウィジェットを配置するための台座になるウィジェットです。.fr はルートウィンドウに fr という名前のフレームを作ることを意味します。そして、-background オプションでフレームの色を ♯333 (濃いグレー) に設定しています。
pack .fr -fill both -expand 1
pack コマンドもレイアウトコマンドの一つです。ここでは .fr に対して、x軸とy軸の両方向 (-fill both) に、広がる (-expand 1)ことを指定しています。
image create photo img1 -file img/valley.jpg
JPEファイルから写真イメージオブジェクトを作成しています。
label .fr.lbl1 -image img1
ラベルを作成して、イメージとして、先ほど作成した写真イメージオブジェクトを設定しています。ラベルはテキストやイメージを含めることができます。ラベルの名前が .fr.lbl1 になっていることに注意してください。これは、ルートウィンドウのfrフレームにlbl1というラベルを作ることを意味しています。
place .fr.lbl1 -x 20 -y20
place コマンドで .fr.lbl1 をルートウィンドウのx座標20、y座標20に配置しています。
ウィンドウを最大化してレイアウトがどうなるか見てください。
pack コマンドは、レイアウトを、fillオプション、expandオプション、sideオプションで指定します。
次の例では、2つのボタンをpackコマンドを使って、ウィンドウの右下に配置しています。
oo::class create Example {
constructor {} {
my initUI
}
method initUI {} {
wm title . Buttons
frame .fr
pack .fr -fill both -expand 1
frame .fr.pnl
pack .fr.pnl -fill both -expand 1
ttk::button .fr.cb -text Close -command exit
pack .fr.cb -padx 5 -pady 5 -side right
ttk::button .fr.ok -text OK
pack .fr.ok -side right
}
}
wm geometry . 300x200+300+300
set e [Example new]
frame .fr.pnl
pack .fr.pnl -fill both -expand 1
最初の .fr フレームの上にさらに .pnl というフレームを作っています。
ttk::button .fr.cb -text Close -command exit
pack .fr.cb -padx 5 -pady 5 -side right
最初の .fr フレームの上にボタン .cb を作成します。そしてそのボタン .cb を pack コマンドの -sideオプションを使って right (右)に配置しています。上下の配置は、最初の .fr フレームが最大限に広がるように設定していますので、.cb は下に押し下げられます。
-padx オプションで .cb ボタンの左右に5ピクセルの空間を作っています。同様に、-pady オプションで .cb ボタンの上下に5pクセルの空間を作っています。
pack .fr.ok -side right
.ok ボタンも -side オプションで右側に配置しています。-side left と指定するとウィンドウの左側に表示されます。-padx は指定しません。-pady は、.cb ボタンのレイアウトに影響されることになります。
ウィンドウを最大化してレイアウトがどうなるか見てください。
次の例では pack コマンドを使って、もう少し実用的なレイアウトを作成します。
review.tcl
oo::class create Example {
constructor {} {
my initUI
}
method initUI {} {
wm title . 書評
frame .fr1
pack .fr1 -fill x
label .fr1.lbl1 -text 題名 -width 6
pack .fr1.lbl1 -side left -padx 5 -pady 5
entry .fr1.ent1
pack .fr1.ent1 -fill x -padx 5 -expand 1
frame .fr2
pack .fr2 -fill x
label .fr2.lbl2 -text 著者 -width 6
pack .fr2.lbl2 -side left -padx 5 -pady 5
entry .fr2.ent2
pack .fr2.ent2 -fill x -padx 5 -expand 1
frame .fr3
pack .fr3 -fill both -expand 1
label .fr3.lbl3 -text 書評 -width 6
pack .fr3.lbl3 -side left -anchor n -padx 5 -pady 5
text .fr3.txt
pack .fr3.txt -fill both -padx 5 -pady 5 -expand 1
}
}
wm geometry . 300x300+300+300
set e [Example new]
pack .fr -fill x
-fill x でx (横)方向のみに広がることを指定しています。
label .fr1.lbl1 -text 題名 -width 6
-width 6 で、ラベルの横幅を 6 に設定しています。この設定値は一応文字数ということになっています。
entry .fr1.ent1
entry コマンドは、文字列を1行だけ入力できる、エントリーウィジェットを作成します。
pack .fr3.lbl3 -side left -anchor n -padx 5 -pady 5
pack コマンドの -anchor オプションはウィジェットの位置を固定します。ここでは n (north、北)を指定して、上に固定するようにしています。
text .fr3.txt
text コマンドは、文字列を複数行入力できるテキストウィジェットを作成します。
grid レイアウトはウィジェットを格子状に配置します。ここではサンプルとして、実際の機能はありませんが、電卓のレイアウトを作ってみようと思います。
oo::class create Example {
constructor {} {
my initUI
}
method initUI {} {
wm title . 計算機
frame .fr -padx 5 -pady 5
pack .fr -fill both -expand 1
ttk::style configure TButton -widht 8 -height 8 \
-font "serif 10"
grid columnconfigure .fr 0 -pad 3
grid columnconfigure .fr 1 -pad 3
grid columnconfigure .fr 2 -pad 3
grid columnconfigure .fr 3 -pad 3
grid rowconfigure .fr 0 -pad 3
grid rowconfigure .fr 1 -pad 3
grid rowconfigure .fr 2 -pad 3
grid rowconfigure .fr 3 -pad 3
grid rowconfigure .fr 4 -pad 3
entry .fr.ent
grid .fr.ent -columnspan 4 -sticky we
ttk::button .fr.cls -text Cls
grid .fr.cls -row 1 -column 0
ttk::button .fr.bck -text Back
grid .fr.bck -row 1 -column 1
ttk::button .fr.lbl
grid .fr.lbl -row 1 -column 2
ttk::button .fr.clo -text Close
grid .fr.clo -row 1 -column 3
ttk::button .fr.sev -text 7
grid .fr.sev -row 2 -column 0
ttk::button .fr.eig -text 8
grid .fr.eig -row 2 -column 1
ttk::button .fr.nin -text 9
grid .fr.nin -row 2 -column 2
ttk::button .fr.div -text /
grid .fr.div -row 2 -column 3
ttk::button .fr.fou -text 4
grid .fr.fou -row 3 -column 0
ttk::button .fr.fiv -text 5
grid .fr.fiv -row 3 -column 1
ttk::button .fr.six -text 6
grid .fr.six -row 3 -column 2
ttk::button .fr.mul -text *
grid .fr.mul -row 3 -column 3
ttk::button .fr.one -text 1
grid .fr.one -row 4 -column 0
ttk::button .fr.two -text 2
grid .fr.two -row 4 -column 1
ttk::button .fr.thr -text 3
grid .fr.thr -row 4 -column 2
ttk::button .fr.mns -text -
grid .fr.mns -row 4 -column 3
ttk::button .fr.zer -text 0
grid .fr.zer -row 5 -column 0
ttk::button .fr.dot -text .
grid .fr.dot -row 5 -column 1
ttk::button .fr.equ -text =
grid .fr.equ -row 5 -column 2
ttk::button .fr.pls -text +
grid .fr.pls -row 5 -column 3
}
}
wm geometry . +300+300
set e [Example new]
ttk::style configure TButton -widht 8 -height 8 \
-font "serif 10"
ttk::button のスタイルを設定しています。1行目末尾の \ (バックスラッシュ)は、行継続文字です。コマンドは1行で書かなければなりませんが、複数行にまたがる場合は、行継続文字を使用して、行が続いていることを Tcl に知らせます。
grid columnconfigure .fr 0 -pad 3
これから使うgridレイアウトの0番目の列の左右に3ピクセルの空白を設定しています。
gridレイアウトの列番号は0から始まります。
このコードを記述しなくても、セル間の空白がなくなるだけで、アプリケーションは正常に動作します。また、gridを使用する前にこのコードを書いておくという決まりでもありません。実際にこのコードをソースファイルの最後に記述しても、アプリケーションは正常に動作します。
なお、このコードによってgridの列数が決まるわけではありません。このコードが実際の列数より少ない場合は、空白が設定されないだけです。実際の列数より多い場合は無視されるだけです。
grid rowconfigure .fr 0 -pad 3
これから使うgridレイアウトの0番目の行の上下に3ピクセルの空白を設定しています。
gridレイアウトの行番号は0から始まります。
このコードを記述しなくても、セル間の空白がなくなるだけで、アプリケーションは正常に動作します。また、gridを使用する前にこのコードを書いておくという決まりでもありません。実際にこのコードをソースファイルの最後に記述しても、アプリケーションは正常に動作します。
なお、このコードによってgridの行数が決まるわけではありません。このコードが実際の行数より少ない場合は、空白が設定されないだけです。実際の行数より多い場合は無視されるだけです。
grid .fr.ent -columnspan 4 -sticky we
TkのGUIフレームワークではgridの行数と列数を前もって設定しておくことはしません。gridの行数と列数は、その後の文脈によって決定されます。
また、最初に配置されるウィジェットは -row 0 -columen 0に決まっていますので、これらのオプションを省略することができます。
「-columnspan 4」で、このウィジェットが4列にまたがることを設定しています。
「-sticky we」で、このウィジェットがwest east、つまり左右に広がることを指定しています。Tkではgridに配置されたウィジェットがそのセルを埋めるように自動的に広がることはありません。
ttk::button .fr.cls -text Cls
grid .fr.cls -row 1 -column 0
ボタン .fr.cls は、1行目 (表示上は2列目) の 0列目 (表示上は1列目) に表示されます。
この章の最後として、gridを使ってもう少し柔軟なレイアウトを作ってみようと思います。
oo::class create Example {
constructor {} {
my initUI
}
method initUI {} {
wm title . Windows
frame .fr -padx 5 -pady 5
pack .fr -fill both -expand 1
grid columnconfigure .fr 1 -weight 1
grid columnconfigure .fr 3 -pad 7
grid rowconfigure .fr 3 -weight 1
grid rowconfigure .fr 5 -pad 7
label .fr.lbl -text Windows
grid .fr.lbl -sticky w -pady 4 -padx 5
text .fr.txt
grid .fr.txt -row 1 -column 0 -columnspan 2 \
-rowspan 4 -padx 5 -sticky ewsn
ttk::button .fr.act -text Activate
grid .fr.act -row 1 -column 3
ttk::button .fr.cls -text Close
grid .fr.cls -row 2 -column 3 -pady 4
ttk::button .fr.hlp -text Help
grid .fr.hlp -row 5 -column 0 -padx 5
ttk::button .fr.ok -text OK
grid .fr.ok -row 5 -column 3
}
}
wm geometry . 350x300+300+300
set e [Example new]
grid columnconfigure .fr 1 -weight 1
...
grid rowconfigure .fr 3 -weight 1
weight オプションで2番目の列と4番目の行を拡張可能にしています。この行と列のセルは、ウィンドウが広がると、このセルも広がることになります。
grid .fr.lbl -sticky w -pady 4 -padx 5
ラベル .fr.lbl は,「sticky w」により west (左)に貼りつくことになります。
text .fr.txt
grid .fr.txt -row 1 -column 0 -columnspan 2 \
-rowspan 4 -padx 5 -sticky ewsn
テキストウィジェット .fr.txt を作成しています。このテキストウィジェットは2行目の1列目から5行目の3列目にまでまたがって占有しています。そしてewsnの4つの側面すべてに張り付いています。先に、4行目と2列目のセルは拡大可能に設定していますので、ウィンドウが拡大すれば、このテキストウィジェットも拡大することになります。
ウィンドウを最大化してレイアウトがどう変更されるか見てください。