●エントリーウィジェット
- エントリー (entry) は 1 行の文字列を入力、または編集することができるウィジェット
- エントリーオブジェクトは new_entry で生成する
- よく使うオプションは -textvariable で、入力されたデータは指定した変数に格納される
- オブジェクトの値が変更されると、エントリーの内容も変更される
- オプション -show はパスワードのように画面に見えてはいけない文字列を打ち込むときに使う
- たとえば、-show = '*' とすれば、入力された文字は * として表示される
- メソッドも多数用意されているが、文字列の入力だけならば、それらを使う機会はあまりないと思われる
リスト : 式入力電卓
use strict;
use warnings;
use Safe;
use Tkx;
# 設定
Tkx::option_add('*font', ['', 14]);
my $buffer = "";
my $cpt = new Safe;
# 計算
sub calc {
$buffer = $cpt->reval($buffer);
}
my $top = Tkx::widget->new('.');
my $e0 = $top->new_entry(-textvariable => \$buffer);
$e0->g_pack;
$e0->g_focus;
$e0->g_bind("<Return>", \&calc); # バインディング
Tkx::MainLoop();
- 計算は関数 eval を使えば簡単
- eval は与えられた文字列を Perl のコードとして実行する
- eval を使う場合、system などの危険な関数は、実行できないようにしておいた方が安全
- このために Safe モジュールを使う
- メソッド reval が文字列を Perl のコードとして実行するが、危険な関数は実行できない
- 一般の GUI アプリケーションの場合、キー入力はアクティブになっているウィンドウに渡される
- Tk では、これを「フォーカスウィンドウ (focus window)」という
- フォーカスはメソッド g_focus によってプログラムで設定することができる
$widget->g_focus;
$e0->g_focus; でフォーカスウィンドウをエントリーに設定する
メソッド g_bind はあとで説明する
数式を入力する
リターンキーで計算する
●イベント処理
$widget->g_bind(eventsequence, callback);
すでにバインドされているコールバック関数がある場合、新しい関数に差し替えられる
callback を省略すると、そのイベントにバインドされている関数が返される
引数を省略すると、そのウィジェットにバインドされているすべてのイベントをリストに格納して返す
<modifier-modifier-type-detail>
type は GUI 環境上で発生するイベントタイプを表す
ユーザーが操作するときに発生する主なイベントタイプには次のようなものがある
表:イベントタイプ
Key, KeyPress | キーが押された |
KeyRelease | キーが離された |
Button, ButtonPress | マウスのボタンが押された |
ButtonRelease | マウスのボタンが離された |
Motion | マウスの移動 |
Enter | マウスカーソルがウィンドウの中に入った |
Leave | マウスカーソルがウィンドウから出た |
- このほかにも、ウィンドウが破棄されたときに発生するイベントなど、様々なイベントタイプがある
- マウスとキーのイベントには、ボタンやキーの種類を detail で指定する
- マウスでは左ボタンが 1 となる
- キーの種類は名前で指定する
- 英数字はその文字がそのまま名前となる
- このほかに、改行キーに対する Return、バックスペースキーに対する BackSpace などがある
- detail を指定する場合は type を省略することができる
- <1> という指定は <KeyPress-1> ではなく <Button-1> となるので注意
- 通常の英数字の場合、<> も省略することができる
- つまり、<KeyPress-a> は a と書くことができる
- <KeyPress> のように detail を省略すると、種類によらずキーが押されたときにバインドされたコマンドが実行される
- イベントタイプの前にはモディファイア (modifier) をつけることができる
- たとえば、<Control-d> はコントロールキーと d キーを同時に押したときのイベントを表す
- 主なモディファイアを下表に示す
表 : モデファイア
Control | Ctrl キーを押しながらの入力 |
Shift | Shift キーを押しながらの入力 |
Alt | Alt キーを押しながらの入力 |
Button1, B1 | マウスの左ボタンを押しながらの入力 |
Button3, B3 | マウスの右ボタンを押しながらの入力 |
Double | ダブルクリック |
Triple | トリプルクリック |
- Tk の出身地である X Window は 3 ボタンマウスを使う
- Button2 は右ボタンではなく中ボタンとなる
- たとえば、左ボタンのダブルクリックに対応するイベントは <Double-1> となる
- イベントタイプは複数個指定することができる
- たとえば、<Escape>a はEsc キーが押されたあとで a キーを押したイベントに対応する
- バインドされたコールバック関数にイベントの詳細情報を渡すには、関数 Tkx::Ev(type, ...) を使う
- type には文字列を指定する
- よく使われる type を下表に示す
表 : Ev() で使用する type
%b | マウスボタンの番号 |
%x, %y | マウスカーソルの座標 |
%t | イベントの発生時刻 |
%A | キーに対応する文字 |
%K | キーに対応する名前 |
- 30 種類以上の情報にアクセスすることができる
- たとえば、次のプログラムを実行すると、キーに対応する名前を表示することができる
リスト : キーの名前を表示
use strict;
use warnings;
use Tkx;
Tkx::option_add('*font', ['', 14]);
my $top = Tkx::widget->new('.');
my $buffer = "";
# キーの表示
sub print_key {
my $key = shift;
$buffer = "push key is $key";
}
$top->new_label(-text => '*** push any key ***')->g_pack;
my $l = $top->new_label(-textvariable => \$buffer);
$l->g_pack(-anchor => 'w');
$l->g_bind("<Any-KeyPress>", [\&print_key, Tkx::Ev("%K")] );
$l->g_focus;
Tkx::MainLoop();
- 実際に試してみると、F1 や F2 キーには F1, F2 という名前が割り当てられていることがわかる
F1 キーを押したときの動作
●リストボックス
- リストボックスは複数の文字列を表示し、ユーザーはその中からひとつ以上の文字列を選択することができる
- リストボックスは new_listbox で生成する
- new_listbox で指定する主なオプションを下表に示す
表 : new_listbox の主なオプション
-xscrollcommand | x 方向のスクロールメソッドを指定 |
-yscrollcommand | y 方向のスクロールメソッドを指定 |
-selectmode | セレクションモード |
- -xscrollcommand/-yscrollcommand にはスクロールバーウィジェットのメソッド set を指定する
- リストボックスの表示範囲が変更されたとき、指定したメソッド set が呼び出される
- これはスクロールバーと一緒に説明する
- セレクションには以下のモードが用意されている
- single
ひとつの行をマウスの左クリックで選択する
- browse
single と同じだが、ドラッグによって選択される行が変化し、
ボタンを離したところの行が選択される。
- multiple
左クリックで複数行を選択する (ドラッグは不可)
- extended
ドラッグで複数行を選択するが、左クリックではいままで選択した行はキャンセルされ、クリックした行のみ選択される。
CTRL キーを押しながら左クリックするとトグル動作(結果が反転)となり、
シフトキーを押しながら左クリックすると直前に左クリックした行から現在の行までが選択される。
- デフォルト値は browse
- データの挿入、削除、取得は次のメソッドで行う
- insert(index, *element)
指定した位置の直前に文字列を挿入
- delete(first, last)
指定した範囲の行を削除する
- get(first, last)
指定した範囲の行をリストに格納して返す
- index(index)
指定した位置の行番号を返す
- curselection()
選択された行番号をリストに格納して返す
- see(index)
指定した位置が見えるようにスクロールする
- このほかに、スクロールバーに関係する xview/yview メソッドが重要
- これはスクロールバーのところで説明する
- 位置の指定方法
- n (数値) : n 行目
- active : 左ボタンを離したときの行
- anchor : 左ボタンを押したときの行
- end : 最後の行、insert() で指定すると最終行の次にデータが追加される
- @x,y : 指定した座標に最も近い行
- セレクションモードが extended のときにドラッグで選択した場合、最初の行が anchor で最後の行が active となる
- したがって、delete に anchor と active を指定すると、選択した行をリストボックスから削除することになる
リスト : 色の選択
use strict;
use warnings;
use Tkx;
Tkx::option_add('*font', ['', 12]);
my $top = Tkx::widget->new('.');
my $buff = "";
# ラベルの生成
my $la = $top->new_label(-textvariable => \$buff);
$la->g_pack(-fill => 'x');
# リストボックスの生成
my $lb = $top->new_listbox;
$lb->g_pack;
# 色の選択
sub get_color {
my @xs = Tkx::SplitList($lb->curselection());
if (@xs) {
my $color = $lb->get($xs[0]);
$buff = $color;
$la->configure(-bg => $color);
}
}
# バインディング
$lb->g_bind('<ButtonRelease-1>', \&get_color);
foreach my $x ('red', 'green', 'blue', 'yellow', 'cyan', 'pink', 'white', 'black') {
$lb->insert('end', $x);
}
Tkx::MainLoop();
- リストボックスに表示された色を選択してラベルの背景色を変更する
- 関数 get_color で選択した色を取得する
- curselection の返り値 @xs が空の場合は未選択
- @xs の先頭に選択した行の番号が格納されている
- get_color はリストボックスのイベント ButtonRelease-1 にバインドする
- リストボックスには仮想イベント <ListboxSelect> があるので、それにバインドしてもよい
- 具体的には $lb->g_bind('<<ListboxSelect>>', \&get_color) とする
- <ListboxSelect> はリストボックス上でマウスをドラッグしている間も発生する
- その間ラベルの背景色が変わることになる
- <ListboxSelect> にバインドするときは selectmode を single に設定したほうが使いやすいかもしれない
初期状態
yellow を選択
pink を選択
●スクロールバー
- スクロールバーはほかのウィジェットの表示範囲を制御するウィジェット
- スクロールバーはその両端に矢印がつき、中央付近には四角いスライダが表示される
- 矢印を左クリックするか、スライダをドラッグすることで表示位置を変更する
- また、矢印とスライダの隙間をクリックすると 1 画面分スクロールする
- スクロールバーは new_scrollbar で生成する
- new_scrollbar で主に使用されるオプションを下表に示す
表 : new_scrollbar の主なオプション
-orient | スクロールバーの方向 |
-troughcolor | 矢印とスクロールの隙間の色 |
-command | スクロールバーが動いたときに実行するメソッド |
- -orient で horizontal または h を指定すると水平になり、vertical または v で垂直になる
- -command はスクロールバーを動かしたときに実行する関数を指定する
- リストボックスと連動させる場合、ここにリストボックスの表示位置を制御するメソッド xview や yview を指定する
- スクロールバーのメソッド set はリストボックスのオプション -xscrollcommand / -yscrollcommand で使用する
-xscrollcommand => [$scrollbar_widget, "set"]
リストボックスで表示範囲が変更されると set が実行される
このとき、リストボックスの表示範囲 (first, last) が引数として渡される
first と last は 0 から 1 の間の実数で、表示されている範囲を表す
たとえば、リストボックスの全体の行数が 100 行で 20 行目から 30 行分表示されているとすると、set(0.2, 0.5) となる
つまり、データ全体の 20 % の位置から 50 % の位置まで表示されていることを表す
- スクロールバーを変更したとき、それをリストボックスに反映させるためのオプションが -command
- ここにリストボックスのメソッド xview や yview を指定する
-command => [$listbox_widget, "yview"]
スクロールバーの操作によって、メソッドには次に示す文字列が引数として渡される
- 'moveto', 数値
指定した数値 (0 - 1.0) の位置までスクロール
- 'scroll', n, unit
上または下に n 単位スクロールする
- 'scroll', n, pages
上または下に n ページスクロールする
リスト : フォントの表示
use strict;
use warnings;
use utf8;
use Tkx;
# メインウィンドウ
my $top = Tkx::widget->new('.');
# ラベルの生成
my $la = $top->new_label(-text => "Hello, world!, こんにちは世界!!", -font => ['', 12]);
$la->g_pack(-fill => 'x');
# リストボックスの生成
my $lb = $top->new_listbox(-selectmode => 'single', -height => 20, -width => 40);
$lb->g_pack(-side => 'left');
# スクロールバーの生成
my $sb = $top->new_scrollbar(-command => [$lb, 'yview']);
$sb->g_pack(-side => 'left', -fill => 'y');
$lb->configure(-yscrollcommand => [$sb, 'set']);
# フォントの選択
sub get_font {
my @xs = Tkx::SplitList($lb->curselection());
if (@xs) {
my $font = $lb->get($xs[0]);
$la->configure(-font => [$font, 12]);
}
}
# バインディング
$lb->g_bind('<<ListboxSelect>>', \&get_font);
my @name_list = Tkx::SplitList(Tkx::font_families());
foreach my $name (sort @name_list) {
$lb->insert('end', $name);
}
Tkx::MainLoop();
- フォント名をリストボックスに入れて、右横にスクロールバーを付ける
- $lb と $sb の g_pack には -side => 'left' を指定する
- $sb の g_pack は -fill => 'y' の指定も必要になる
- リストボックスを生成するとき、まだ new_scrollbar は実行されていない
- new_scrollbar を実行してから configure で -yscrollcommand に set をセットする
- 関数 get_font で選択したフォント名を取得し、ラベルの font を変更する
初期状態
Consolas を選択
Ricty Diminished を選択
メイリオを選択
●Gridder
- メソッド g_grid はウィジェットを格子状に配置するジオメトリーマネージャ (Gridder)
- ウィンドウを M 行 N 列のセルに分割し、そこにウィジェットを配置する
- x 方向の位置はオプション -column で指定する
- y 方向の位置は -row で指定する
- オプション -columnspan と -rowspan は、複数のセルにまたがってウィジェットを配置するために使う
- -columnspan は x 方向にまたがるセルの数を指定する
- -rowspan は y 方向にまたがるセルの数を指定する
- g_grid はオプション -sticky でウィジェットを引き伸ばす
表 : -sticky オプション
n | 上寄せ |
s | 下寄せ |
e | 右寄せ |
w | 左寄せ |
ns | 上下方向に引き伸ばす |
ew | 左右方向に引き伸ばす |
- -sticky は g_pack のオプション -anchor と同じ機能もあわせ持っている
リスト : ボタンを格子状に配置
use strict;
use warnings;
use Tkx;
my $top = Tkx::widget->new('.');
foreach my $x (0 .. 4) {
foreach my $y (0 .. 4) {
my $n = $y * 5 + $x + 1;
$top->new_button(-text => " $n ")->g_grid(-column => $x, -row => $y, -sticky => 'ew');
}
}
Tkx::MainLoop();

リスト : 履歴付き式入力電卓
use strict;
use warnings;
use Safe;
use Tkx;
my $buffer = ""; # グローバル変数
my $cpt = new Safe;
my $top = Tkx::widget->new('.');
Tkx::option_add('*font', ['', 14]);
# Entry
my $e0 = $top->new_entry(-textvariable => \$buffer);
# Listbox
my $lb = $top->new_listbox;
# Scrollbar
my $sb1 = $top->new_scrollbar(-orient => 'v', -command => [$lb, 'yview']);
my $sb2 = $top->new_scrollbar(-orient => 'h', -command => [$lb, 'xview']);
# Listbox の設定
$lb->configure(-yscrollcommand => [$sb1, 'set']);
$lb->configure(-xscrollcommand => [$sb2, 'set']);
# grid による配置
$e0->g_grid(-row => 0, -columnspan => 2, -sticky => 'ew');
$lb->g_grid(-row => 1, -column => 0, -sticky => 'nsew');
$sb1->g_grid(-row => 1, -column => 1, -sticky => 'ns');
$sb2->g_grid(-row => 2, -column => 0, -sticky => 'ew');
# 計算
sub calc {
$lb->insert('end', $buffer);
$lb->see('end');
$buffer = $cpt->reval($buffer);
$e0->icursor(0);
}
# 取り出し
sub get_expr {
$buffer = $lb->get('active');
$e0->g_focus;
}
# バインディング
$e0->g_bind("<Return>", \&calc);
$lb->g_bind("<Double-1>", \&get_expr);
# フォーカスの設定
$e0->g_focus;
Tkx::MainLoop();
- エントリーは 2 つのセル (0, 0) と (0, 1) にまたがって配置するので -columnspan を 2 に設定する
- リストボックスはセル (1, 0) に配置する
- リストボックスの右側のセル (1, 1) と下側のセル (2, 0) にスクロールバーをセットする
- エントリーではリターンキーが入力されると関数 calc が実行される
- calc では式をリストボックスに代入し、計算結果を $buffer にセットする
- メソッド see を使ってセットした計算式が見えるようにスクロールする
- エントリーのメソッド icursor(index) はカーソルを index の位置に移動する
- 今回は先頭 (0) に移動する (最後尾の場合は 'end' を指定する)
- リストボックスで行をダブルクリックすると関数 get_expr が実行される
- 選択した式を取り出して $buffer にセットし、フォーカスをエントリーにセットする
式の履歴が残るように改造した電卓
リターンキーを押せば計算結果も履歴に入る
式の選択
●チェックボタンとラジオボタン
- チェックボタンは new_checkbutton で生成し、ON / OFF のような二者択一の情報を設定するために使う
- ラジオボタンは new_radiobutton で生成し、複数の値からひとつを選ぶ場合に使う
- どちらのウイジェットもオプション -text でボタンの名前を指定する
- ラジオボタンは選択する値をオプション -value で指定する
- その値を格納する変数をオプション -variable で指定する
- また、オプション -command でコールバック関数を指定することもできる
- チェックボタンは値が ON / OFF の 2 通りしかない
リスト : チェックボタンとラジオボタン
use strict;
use warnings;
use Tkx;
my $top = Tkx::widget->new('.');
Tkx::option_add('*font', ['', 14]);
my $opts1 = 1;
my $opts2 = 0;
my $opts3 = 1;
my $action = 1;
$top->new_label(-text => 'Check Button')->g_pack;
$top->new_checkbutton(-text => 'option 1', -variable => \$opts1)->g_pack;
$top->new_checkbutton(-text => 'option 2', -variable => \$opts2)->g_pack;
$top->new_checkbutton(-text => 'option 3', -variable => \$opts3)->g_pack;
$top->new_label(-text => 'Radio Button')->g_pack;
$top->new_radiobutton(-text => 'action A', -variable => \$action, -value => 0)->g_pack;
$top->new_radiobutton(-text => 'action B', -variable => \$action, -value => 1)->g_pack;
$top->new_radiobutton(-text => 'action C', -variable => \$action, -value => 2)->g_pack;
Tkx::MainLoop();
チェックボタンとラジオボタン
●イメージ
- Tk は標準で GIF, PPM / PGM 形式の画像ファイルを扱うことがでる
- Tk 8.6 から PNG がサポートされた
- ActivePerl の Tkx の場合、PNG はまだ未対応のようだ
- PPM はカラー、PGM はグレイスケールの画像を扱う、UNIX で標準的に用いられるベタフォーマット
- 画像を取り扱う主な関数を下表に示す
関数名 | 機能 |
image_create_photo(名前, -file => ファイル名); | 画像ファイルからイメージを生成する |
image_delete(名前); | イメージを削除する |
image_names(); | 全イメージの名前をリストにして返す |
image_height(名前); | イメージの高さを返す |
image_weight(名前); | イメージの幅を返す |
- 名前は生成したイメージを表す
- イメージを生成するとき名前を省略することができる
- その場合は適当な名前がつけられ、その名前が返り値となる
- その名前を使ってラベルやボタンなどのウィジェットにイメージを表示させることができる
- 簡単な使用例
リスト : 画像の表示
use strict;
use warnings;
use Tkx;
my $top = Tkx::widget->new('.');
my $image1 = Tkx::image_create_photo(-file => 'earth.gif');
$top->new_label(-image => $image1)->g_pack;
Tkx::MainLoop();
- earth.gif は Tcl/Tk の配布パッケージに含まれている画像ファイル
- このプログラムでは earth.gif がカレントディレクトリにあることを仮定している
- ファイルがカレントディレクトリにないとエラーが表示されて動作しないので注意すること
- ラベルやボタンにイメージを表示するには、オプション -image に画像の名前をセットする
earth.gif の表示
●ファイルの選択
- Tcl/Tk にはファイルを選択するためのコマンド tk_getOpenFile, tk_getSveFile が用意されている
- Tkx では '_' を '___' に変換したものが関数になる
- tk___getOpenFile(), 入力ファイルを選択
- tk___getSaveFile(), 出力ファイルを選択
- これらの関数を実行すると、ファイル選択のウィンドウ (ダイアログ) が開かれる
- ウィンドウ上の操作でディレクトリをたどりファイルを選ぶことができる
- 使用できるオプションを示す
- -initialdir => ディレクトリ
最初に選択されているディレクトリ
- -initialfile => ファイル
最初に選択されているファイル(出力ファイルのみ有効)
- -defaultextension => 拡張子
最初に選択されている拡張子
- -filetypes => パターン
使用可能なファイル種別と拡張子を指定
- -title => 文字列
ダイアログボックスのタイトル
- -filetypes はアプリケーションで扱うことができるファイル種別を拡張子で指定し、そのファイルだけを表示する
- 指定は文字列で行う (表記法は Tcl/Tk とほぼ同じ)
-filetypes: " ファイル種別 ... "
ファイル種別 := {名前 {拡張子 ... }}
名前に空白を入れたい場合は { } で囲む
GIF / PPM ファイルを指定する場合は次のようになる
-filetypes => "{{画像 Files} {.gif .ppm}}"
この場合は GIF と PPM ファイルが一緒に表示される
次のように指定すると、表示するファイルをダイアログの操作で切り替えることができる
-filetypes => "{GIF {.gif}} {PPM {.ppm}} {ALL {*}}"
すべてのファイルを表示する場合は * を使う
また、空文字列 "" を指定すると、拡張子のないファイルを表示する
ファイルを選択すると、ファイル名をフルパス形式で返す
選択しない (キャンセル) 場合は、空文字列が返される
リスト : 画像ローダー
use strict;
use warnings;
use Tkx;
use File::Basename;
my $top = Tkx::widget->new('.');
Tkx::option_add("*tearOff", 0);
my $m = $top->new_menu(-type => 'menubar');
$top->configure(-menu => $m);
my $path_name = "."; # パスを格納する変数
my $image_data = Tkx::image_create_photo(-width => 64, -height => 64);
my $label = $top->new_label(-image => $image_data);
$label->g_pack;
# ファイルを選んで表示する
sub load_file {
my $filename = Tkx::tk___getOpenFile(-filetypes => "{GIF {.gif}} {PPM {.ppm}} {ALL {*}}",
-initialdir => $path_name );
if ($filename) {
$path_name = dirname($filename) . '/';
Tkx::image_delete($image_data);
$image_data = Tkx::image_create_photo(-file => $filename);
$label->configure(-image => $image_data);
}
}
my $m1 = $m->new_menu;
$m->add_cascade(-menu => $m1, -label => 'File', -under => 0);
$m1->add_command(-label => 'Open', -under => 0, -command => \&load_file);
$m1->add_separator;
$m1->add_command(-label => 'Exit', -under => 0, -command => sub { exit; });
Tkx::MainLoop();
- メニュー File の下に、ファイルを選択する Open とアプリケーションを終了する Exit の 2 つのメニューを設定する
- 変数 $path_name は選択されたファイルのパスを格納する
- tk___getOpenFile にこのパスを指定することで、次にファイルを選ぶときは同じディレクトリから始めることができる
- 初期値はカレントディレクトリ
- アプリケーションの開始時にはファイルは指定されていないので、空のイメージを作って表示しておく
- 画像ファイルのロードは関数 load_file で行う
- tk___getOpenFile でファイル名を取得する
- 関数 dirname でパスを取り出して変数 $path_name にセットする
- dirname はファイル名からパス部分を取り出して返す
- ファイル名をゲットしたら、それが空文字列でないことを確認する
- 新しいイメージを image_create_photo で生成し、ラベルの configure で表示するイメージを変更する
GIF ファイルの選択
PPM ファイルの選択
PPM ファイルを表示