|
[incr Tk]超簡単ガイド ガイドというか、3か月後の自分のためのガイドかもしれません。ということで! [incr Tk]は、標準Tkで提供されるラベルやボタンなどのウィジェット(GUI部品) を組合せて、新しい複合ウィジェットを作るためのフレームワークです。 [incr Tk]で作ったウィジェットも、
一番簡単な例として、 ここでは「LabeledEntry」という複合ウィジェットを作ってみます。 これは名前からは想像もつかないでしょうが、 なんと、エントリとラベルが一緒になったものです(ベタベタやん)。
package require Itcl
package require Itk
itk::usual LabeledEntry {
keep -background -foreground -font
}
class LabeledEntry {
inherit itk::Widget
constructor {args} {}
destructor {}
public method insert {args}
}
body LabeledEntry::constructor {args} {
itk_component add lab {
label $itk_interior.lab -rel groove -bd 3
} { usual
rename -font -labelfont labelFont Font
rename -text -labeltext labelText Text
}
itk_component add ent {
entry $itk_interior.ent
} {
usual
keep -textvariable
}
eval itk_initialize $args
pack $itk_interior.lab $itk_interior.ent \
-side left -fill y
}
LabeledEntry .le -textvariable Ope -labeltext Input:
pack .le
bind . <Key-Return> {
tk_messageBox -message $Ope
}
# end.
●パッケージの宣言とクラス package require Itcl package require Itkと書けば[incr Tcl] [incr Tk]の機能が使えるようになります。 [incr Tk] では、 作りたい複合ウィジェットを1つのクラスとして定義します。 そのクラスは、必ず itk::Widgetを継承してください。
●コンストラクタ inherit itk::Widgetと書いてitk::Widgetを継承します。 この例では、複合ウィジェットLabeledEntryの上にはラベルとエントリの 2つのTkウィジェットが載ります。
●itk_component add コマンド
itk_component add lab {
label $itk_interior.lab -rel groove -bd 3
} { usual
rename -font -labelfont labelFont Font
rename -text -labeltext labelText Text
}
●keep
itk_component add ent {
entry $itk_interior.ent
} {
usual
keep -textvariable
}
この例で具体的にいうと、「keep -textvariable」
としているので、あとで出てきますがこのLabeledEntry
ウィジェットを作るとき、
LabeledEntry .a -textvariable hogerattaとすると、上の $itk_interior.ent が自動的に entry $itk_interior.ent -textvariable hogerattaと指定されたのと同じ扱いになります。ちなみに
itk_component add lst {
listbox $itk_interior.lst
} {
usual
keep -textvariable
}
このようなことをするとエラーになります。
-textvariableはエントリにつくオプションで、
リストボックスにはつけられないからです。
●usual
itk::usual LabeledEntry {
keep -background -foreground -font
}
そうすれば、あとは毎回ひとことusualと書くだけでこれらのオプションが「keep」されます。
よく「keep」される代表的なオプションには
「-activeforeground」などがあります。
●rename
LabeledEntry .a -labelfont {Helvetica 16 normal} \
-entryfont {Helvetica 12 normal}
こんな感じで指定させたいとします。しかし、標準Tkには -labelfont
などの名前のオプションはありません。そこで使うのがrenameコマンドで、
rename -font -labelfont labelFont Font
こうすると、
LabeledEntry .a -labelfont {Helvetica 16 normal}
というオプションでLabeledEntryを作ったとすると、
label $itk_interior.lab -font {Helvetica 16 normal}
このように指定されたかのように扱われます。どこがどう対応するか、
わかりますか?
ちなみにrenameコマンドの最後の2つの引数はそれぞれオプションのリソース名、
クラスと呼ばれるものですが、
私もあまりよく知らないので細かい説明はできないので、とばします。
…適当にそれっぽいのを指定してください(無責任)
普通は、リソース名はオプションの先頭の「-」を削ったものを使い、
必ず英小文字で始める必要があります。クラスのほうは、
なんかほんとうに適当でもいいのかという程度ですが、
必ず英大文字で始める必要があります。
ちなみに、標準Tkのどのオプションにも相当しない、 まったく新しい種類のオプションを作りたいという場合については、 残念ながらこのサンプルにネタがないので、後に回します。
●itk_initialize
eval itk_initialize $args
毎回このとおり使いまわせばOKです。
●いざ使ってみよう LabeledEntry .le -textvariable Ope -labeltext Input: pack .leLabeledEntryコマンドの使い方が、 標準Tkのウィジェットを作るコマンドとほぼ同じで、 しかも [incr Tcl]でいうクラスのインスタンス化をする処理でもある点に注目してください。 GUI部品のクラス定義という、他のOO言語と同じ考え方ですね。 上の .le が、Tkのウィジェット階層を反映した「ウィジェットの名前」 であるのと同時に、[incr Tcl]クラスの「インスタンス名」 を兼ねているわけです。 で、上のようにして作ったウィジェットも、通常のlabelやentry などと同様に、 set font [.le cget -labelfont] .le configure -labeltext "入力:"このようにcget&configureも使えるほか、 frame .fa LabeledEntry .fa.le -textvariable Ope -labeltext Input:このように他のフレームやトップレベルに載せることもできます。
連動するウィジェットをまとめよう [incr Tk]を使うと特にありがたいのは、複数の互いに「連動する」 ウィジェットを組み合わせた複合ウィジェットをクラス定義しておくことで、 複合ウィジェットを構成するウィジェットの内部操作と「外界」 が入り混じってバグっぽくなるのを防ぐことができやすい点です。 連動するというとリストボックスとスクロールバーの連動が代表的ですが、 もちろんああいうのも[incr Tk]にうってつけの複合ウィジェットの例といえるでしょう。 ユーザーインターフェースの観点からポピュラーな連動の例が、 ラジオボタンとエントリの組合せです。 つまり、ラジオボタンがチェック状態になったときだけ、 エントリに入力可能になるという。 こんな例です。
ユーザーを迷わせない、という基本にそって、 このラジオボタンとエントリを1組としてウィジェットにしたのが次のスクリプトです。
package require Itcl
package require Itk
itk::usual OptEntry {
keep -foreground -background -font
}
class OptEntry {
inherit itk::Widget
constructor {args} {}
destructor {}
public method trace_proc
}
body OptEntry::constructor {args} {
itk_component add rad {
radiobutton $itk_interior.rad
} { usual
keep -variable -value
rename -font -labelfont labelFont Font
rename -text -labeltext labelText Text
}
itk_component add ent {
entry $itk_interior.ent -state disabled
} {
usual
keep -textvariable
rename -width -entrywidth entryWidth Width
}
eval itk_initialize $args
set varname [$this cget -variable]
global $varname
trace variable $varname w "$this trace_proc"
pack $itk_interior.rad $itk_interior.ent \
-side left -fill y
}
body OptEntry::trace_proc {args} {
set varname [$itk_component(rad) cget -variable]
global $varname
set value [$itk_component(rad) cget -value]
if {"[set $varname]" == "$value"} {
$itk_component(ent) configure -state normal -bg white
focus $itk_component(ent)
} else {
$itk_component(ent) configure -state disabled -bg gray80
}
}
. configure -bg #c0c080
radiobutton .ra -variable Operation -value 0 -text {全て選択} -bg #c0c080 \
-font 8x16
OptEntry .oe1 -textvariable OpeFile -labeltext {ファイルで指定:} \
-variable Operation -value 1 -background #c0c080 -entrywidth 20 \
-labelfont 8x16
OptEntry .oe2 -textvariable OpeUrl -labeltext {URLで指定:} \
-variable Operation -value 2 -background #c0c080 -entrywidth 40 \
-labelfont 8x16
set OpeFile {}
button .cmda -text OK -command ok_clicked
pack .ra .oe1 .oe2 -side top -anchor w
pack .cmda -side top -anchor e
set Operation 0
proc ok_clicked {} {
global Operation OpeFile OpeUrl
switch $Operation {
0 { tk_messageBox -message "ALL." }
1 { tk_messageBox -message "FILE: $OpeFile" }
2 { tk_messageBox -message "URL: $OpeUrl" }
}
exit
}
# end.
●itk_component変数 $itk_interior.fa.labのようにTkパスで指定する方法のほかに、itk_component add コマンドで指定した部品ウィジェットの「識別名」を使って、 $itk_component(lab)このようにitk_component変数を使うこともできます。
複合ウィジェットの王道?スクロールバーつきリスト 先述の通り、複合ウィジェットの王道といえばこれ。 スクロールバーとリストボックスが一体になった Scrolledlist というウィジェットを下のように定義すれば、 Scrolledlist .sl -background "#c0c080" -height 6 pack .slこんな感じで簡単にスクロールバーつきリストが出せるようになります。
package require Itcl
package require Itk
itk::usual Scrolledlist {
keep -background -foreground -font
}
class Scrolledlist {
inherit itk::Widget
constructor {args} {}
destructor {}
itk_option define -dccommand dccommand Text {}
public method insert {args}
public method curselection {}
public method clear {}
public method get {index}
public method __doubleclicked {}
}
body Scrolledlist::constructor {args} {
frame $itk_interior.fa -rel groove -bd 3
itk_component add list {
listbox $itk_interior.fa.lsta \
-yscrollcommand "$itk_interior.fa.scrv set"
} {
usual; keep -selectmode -height -width
}
bind $itk_interior.fa.lsta
●あとで操作しないウィジェット
itk_component add frame {
frame $itk_interior.fa -rel groove -bd 3
}
とわざわざ書かなくても、
frame $itk_interior.fa -rel groove -bd 3
で十分です。ただし、こう書いた子ウィジェットはコンストラクタ以外では「名無し」
として扱われるので、[incr Tk]クラスのコンポーネントとしては操作できません。
●独自のオプションを定義するには
itk_option define -dccommand dccommand Text {}
ここでは Scrolledlist に「-dccommand」
という名前の新しいオプションをつけられるようにしています。
このようにして作ったオプションは、「-width」や「-font」などの
Tkでおなじみのオプションと全く同様に扱えます。
つまり、ウィジェットの作成時にオプションとして指定できたり、
cget & configure サブコマンドで使ったりできます。
Scrolledlist .sl -background "#c0c080" \ -dccommand "dc" -height 6せっかく作ったオプションは、自由に使って構いません。 この「-dccommand」オプションは、Tclコマンドを何か指定しておけば、 Scrolledlist内のリストボックスがダブルクリックされたときに、 そのコマンドが自動的に実行されます。 というか、クラスの設計者がそういうふうに書くわけですね。
body Scrolledlist::__doubleclicked {} {
if {"$itk_option(-dccommand)" != ""} {
eval $itk_option(-dccommand)
}
}
このように、現在のオプションの値はインスタンス変数
$itk_option(-オプションの名前)で取得できます。
よくある例:ガイデッドリストボックス こんな複合ウィジェットも結構ポピュラーなインターフェースですね。つまり、 リストやラジオボタンの項目をどれか選択すると、 その選択についての解説文が隣りに現れるという。ここではインストーラによくある、 「標準インストール」「最小インストール」「カスタムインストール」 の選択をさせるダイアログをモデルとして使ってみました。
package require Itcl
package require Itk
itk::usual GuidedListbox {
keep -background -foreground -font
}
class GuidedListbox {
inherit itk::Widget
constructor {args} {}
destructor {}
itk_option define -direction direction Text {vertical}
public method __selection_changed
public method insert {index item message}
public method curselection {}
public method get {index}
private variable __guidedmessage
}
body GuidedListbox::constructor {args} {
itk_component add frame {
frame $itk_interior.fa
}
itk_component add listbox {
listbox $itk_interior.fa.lsta \
-yscrollcommand "$itk_interior.fa.scrv set"
} { usual; keep -selectmode
rename -width -listboxwidth listboxWidth Width
rename -height -listboxheight listboxHeight Height
}
scrollbar $itk_interior.fa.scrv -orient vertical \
-command "$itk_interior.fa.lsta yview"
itk_component add message {
message $itk_interior.msga
} {
rename -width -messagewidth messageWidth Width
}
foreach e {lsta scrv} {
pack $itk_interior.fa.$e -side left -fill y
}
foreach e {fa msga} {
pack $itk_interior.$e -side top
}
bind $itk_component(listbox)
![]() -direction horizontal
今度のもitk_option defineを使っています。 今度は「-direction」という新しいオプションを作っていますが、 つまりメッセージをリストボックスの右に出す(horizontal)か、 下に出す(vertical)か、という指定をさせるわけですが、
itk_option define -direction direction Text {vertical}
この場合は前の Scrolledlist の -dccommand とときと違う種類の工夫が必要です。
つまり、
.gl configure -direction horizontal -messagewidth 100
のようにconfigureされた瞬間に何か処理をすることによって値の内容を実際に反映しないといけないわけで、
このためにはconfigbodyという専用のコマンドが用意されています。
configbody GuidedListbox::direction {
switch $itk_option(-direction) {
vertical { set side top }
horizontal { set side left }
}
foreach e {frame message} {
pack forget $itk_component($e)
}
foreach e {frame message} {
pack $itk_component($e) -side $side
}
}
configbodyコマンドは、上のように
configbody クラス名::オプション名 {
このオプションがconfigureされた直後に実行する処理
}
という書き方をします。
実行する処理の部分は、
通常の[incr Tcl]クラスのメソッドと同じ書き方をすることができ、
[incr Tk]特有のインスタンス変数 itk_component や itk_option
も使うことができます。
|