●Hello, Perl/Tkx!!
- Tkx は Perl で GUI (Graphical User Interface) を扱うための標準的なライブラリ
- Tcl/Tk の Tk (Tool Kit) の部分を Perl から利用できるようにしたもの
- Perl/Tk の Tk モジュールとは実装方法が異なる
- Tkx は Tcl インタプリタを介して Tk を利用する
- ActivePerl の場合、Tkx は標準で同梱されている
- 他の Perl では、CPAN からモジュール Tkx をインストールする必要がある
- Unix 系 OS であれば、cpan を起動して install Tkx と入力するだけ
- だが、M.Hiroi の環境 (Xubunts 18.04 on VirtualBox, 2019 年 3 月) ではテストに失敗してインストールできなかった
- 本ページでは Windows 版 ActivePerl を使って Tkx の基本を簡単に説明する
- 参考 URL
- Tkx::Tutorial - How to use Tkx, (meta.cpan.org)
- Tkx - Yet another Tk interface, (meta.cpan.org)
- TkDocs - Tk Tutorial, (TkDocs)
- Tcl/Tk お気楽 GUI プログラミング入門, (M.Hiroi)
●Tkx の基礎知識
- イベント駆動方式
- GUI アプリケーションは、ユーザーからの入力やシステムの状態変化など、ある出来事をきっかけにプログラムが実行される
- この出来事を「イベント (event)」という
- イベントをきっかけにしてプログラムが起動されることを「イベントドリブン (eventdriven : イベント駆動)」という
- イベントドリブン型のアプリケーションは、一般に次のようなメインルーチンを持っている
- 初期化
- イベントを取得する
- イベントの種類に応じて処理を振り分ける
- 2 に戻る
- 2 から 4 を「イベントループ」と呼び、アプリケーションはユーザーからの入力などのイベントを待つ
- 3 の処理に対応する機能を「バインディング (binding)」という
- バインディングは、ウィンドウでイベントが発生したときに、それに応じて定義したプログラムを実行する
- このプログラムを「イベントハンドラ」とか「コールバック関数」と呼ぶ
- Perl/Tkx で必要な手順
- トップレベルのウィンドウ (メインウィンドウ) を作る
- ウィジェットを設定してウィンドウに配置する
- Tk では GUI 用の部品のことを「ウィジェット (widget)」と呼ぶ
- イベントループを開始してユーザーからの要求 (イベント) を処理する
- このほかに、必要に応じてコールバック関数を作成する
- Tkx は Tcl/Tk ライクな書き方と、オブジェクト指向ライクな書き方の 2 通りがある
- 最初に前者の書き方を簡単に説明する
リスト : ボタンの表示 (Tcl/Tk ライク)
use strict;
use warnings;
use Tkx;
Tkx::button(".b", -text => "Hello, Perl/Tkx!!");
Tkx::pack(".b");
Tkx::MainLoop();
- 最初に use Tkx; で Tkx モジュールをロードする
- Tcl はシェルスクリプトに似たコマンド言語で、Tk の操作は次の形式に大別される
- コマンド ウィジェット名 引数 ...
- ウィジェット名 アクション 引数 ...
- コマンド サブコマンド 引数 ...
- Tcl/Tk では 2 を「ウィジェットコマンド」という
- Tkx の場合、1 のコマンドは関数になる
1. Tkx::コマンド("ウイジェット名", 引数, ...);
button はボタンウイジェットを生成する関数
ウィジェット名のつけ方は自由だが、ドット ( . ) から始めなければいけない
- 単なるドット ( . ) はメインウィジェットと呼ばれ、メインウィンドウを表す
- この中にボタンを作るので名前が .b のようにピリオドから始まる
- ウィジェットを入れ子にする場合はドットで区切る
- Tkx のドキュメントではウィジェット名を「パス (path)」と呼んでいる
- 本稿も Tkx に合わせてパス (path) と記述する
引数 -text => '...' は、ボタンに表示されるテキストを指定する
ウィジェットの配置は「ジオメトリマネージャ」を使う
関数 pack はそのうちの一つ
最後に関数 Tkx::MainLoop() を呼び出して、イベントループを開始する
2 のウィジェットコマンドの場合、パスにドットが含まれるため、それを関数名にすることはできない
次の形式で実行することはできるが、オブジェクト指向を使ったほうが簡単
2. Tkx::i::call("パス", "アクション", 引数, ...);
3 の形式はコマンドとサブコマンドをアンダーバー ( _ ) でつないだ名前が関数になる
3. Tkx::コマンド_サブコマンド(引数, ...);
次にオブジェクト指向ライクな書き方を説明する
リスト : ボタンの表示 (オブジェクト指向)
use strict;
use warnings;
use Tkx;
my $top = Tkx::widget->new(".");
my $b = $top->new_button(-text => "Hello, Perl/Tkx!!");
$b->g_pack;
Tkx::MainLoop();
- Tkx::widget->new でウィジェットに対応するオブジェクトを生成する
- 引数はパス
- "." を指定するとメインウィンドウに対応するオブジェクトを返す
- ウィジェットの生成はコンストラクタを使うと簡単
$widget->new_widgetname(-option => value, ...);
widgetname はウィジェットを生成するコマンドと同じ名前
その前に new_ を付けたものがコンストラクタになる
たとえば、ボタンのコンストラクタは new_button になる
$top->new_button() でメインウィンドウの中にボタンが作成される
このとき、Tkx が適当なパス (ウィジェット名) を付ける
ユーザーは返り値のオブジェクトを使ってウィジェットを操作する
ウィジェットコマンドの場合、アクションと同じ名前がメソッドになる
$widget->アクション(引数, ...);
第 1 引数にパスを受け取るコマンドは、コマンド名の前に g_ を付けたメソッドになる
たとえば、Tkx::pack(".b") は $b->g_pack; というメソッド呼び出しになる
本稿はオブジェクト指向的な方法でプログラムを記述する
ボタンの表示
●Tcl/Tk のリストと Perl の配列
- Tcl には「リスト」というデータ構造がある
- リストは波カッコで囲み、要素を空白で区切ったもの
- 簡単に言えば Lisp のリストと同じ (カッコが波カッコになる)
- 詳細は拙作のページ Tcl/Tk 入門: リストと制御構造 を参照
- Tcl/Tk にはリストを返すコマンドがあるが、Tkx ではリストを文字列に変換して返す
{a b c d e} => "a b c d e"
{a b {c d} e} => "a b {c d} e"
{{a b} {c d} {e f}} => "{a b} {c d} {e f}"
Perl/Tk や Python/Tkinter のように、Perl の配列 (または Python のリスト) に変換してくれるわけではない
Tkx ではリスト (文字列) を配列に変換する関数 Tkx::SplitList($list) が用意されている
Tkx::SplitList("a b c d e") => ("a", "b", "c", "d", "e")
Tkx::SplitList("a b {c d} e") => ("a", "b", "c d", "e")
Tkx::SplitList("{a b} {c d} {e f}") => ("a b", "c d", "e f")
コマンドによっては SplitList でデータの変換が必要になるので注意すること
●ウィジェットの生成
表 : Tk に用意されている主なウィジェット
種類 | 名前 | 概要 |
フレーム | frame | ウィジェットを格納する枠組みを作る |
ラベル | label | 文字列やイメージを表示する |
メッセージ | message | 複数行の文字列を表示する |
ボタン | button | ボタンを作る |
ラジオボタン | radiobutton | ラジオボタンを作る |
チェックボタン | checkbutton | チェックボタンを作る |
リストボックス | listbox | リストボックスを作る |
スクロールバー | scrollbar | スクロールバーを作る |
スケール | scale | スケールを作る |
エントリー | entry | 1 行の文字列の入力と編集 |
メニュー | menu | メニューを作る |
メニューボタン | menubutton | メニューボタンを作る |
ビットマップ | bitmap | ビットマップを作る |
キャンバス | canvas | キャンバスを作る |
テキスト | text | テキストの入力と編集 |
ラベルフレーム | labelframe | ラベル付きフレーム |
スピンボックス | spinbox | スピンボックスを作る |
ペインウィンドウ | panedwindow | ペインウィンドウを作る |
表 : ほとんどのウィジェットで共通のオプション
-foreground (-fg) | 文字や線を描くのに使用する色を指定 |
-background (-bg) | 背景色の指定 |
-text | ウィジェット内に表示されるテキスト |
-textvariable | テキストを格納する変数を指定 |
-image | ウィジェット内に表示されるイメージ |
-bitmap | ウィジェット内に表示されるビットマップ |
-borderwidth (-bd) | ウィジェットの枠の幅 |
-relief | ウィジェットの枠のスタイル |
-height | ウィジェットの高さ |
-width | ウィジェットの幅 |
-anchor | ウィジェットや表示されるデータの位置を指定 |
- ウィジェットの幅と高さは、テキストを表示するウィジェットでは文字数になる
- それ以外のウィジェットはピクセル単位となる
- オプションは -option => value の形式で指定する
- あとからオプションの値を変更するにはメソッド configure() を使う
- オプションの値を参照するにはメソッド cget() を使う
$widget->configure(-option => value, ...);
$widget->cget(-option_name);
cget の -option_name は文字列 '-option_name' にして渡すか、そのまま渡してもよい
configure() と cget() は全てのウィジェットで共通に使用することができる
●ジオメトリマネージャ
- ウィジェットの配置は「ジオメトリマネージャ (Geometry Manager)」で行う
- Tk には 3 種類のマネージャが用意されている
- Placer
メソッド g_place() はウィジェットを指定した座標に配置する。
- Packer
メソッド g_pack() はウィンドウにウィジェットを詰め込む。
ウィジェットの数や大きさによって、ウィンドウの大きさも変化する。
- Gridder
メソッド g_grid() はウィジェットを格子状に配置する。
ウィジェットの数や大きさによって、ウィンドウの大きさも変化する。
- いちばんよく使われるマネージャが Packer
- Placer はウィジェットの位置を座標で指定するため、並べて表示する場合は設定が少々面倒
- ウィジェットを格子状に配置する場合は Gridder が便利
- ここでは Packer の使い方を簡単に説明する
- g_pack() はウィジェットを上から順に詰め込み、ウィンドウに配置する
- ウィジェットのまわりに余白がある場合、g_pack() はウィジェットを中央に配置する
- これを変更するにはオプション -anchor を使う
nw --- n --- ne
| |
w c e
| |
sw --- s --- se
- 記号はそれぞれ 'e' (East), 'w' (West), 's' (South), 'n' (North), 'c' (Center) を表す
- たとえば、オプション -anchor => 'w' を指定すると左寄せに表示される
詰め込む方向を変えるにはオプション -side を使う
- 指定できる値は 'top', 'bottom', 'left', 'right' の 4 つ
- ウィジェットによって詰め込む方向を変えてもかまわない
ウィジェットを余白いっぱいに広げるにはオプション -fill を使う
- 方向は 'x', 'y' で指定する
- 両方向に広げるには 'both' を指定する
●ボタンとラベル
- ボタン (button) はボタンを、ラベル (label) は文字列を表示するウィジェット
- テキストを表示するウィジェットでよく使用されるオプションを下表に示す
表 : テキスト表示のオプション
-font | 使用するフォント |
-underline | 下線つき表示する文字位置 |
-padx | 水平方向の詰めもの |
-pady | 垂直方向の詰めもの |
-command 押したときに実行する関数 (コールバック関数) を指定
コールバック関数は「関数へのリファレンス」または「無名の配列」で指定する
-command に Perl を終了する関数 exit を指定し、そのボタンを押すとアプリケーションが終了することになる
my $b = $top->new_button(-text => "Hello, Perl/Tkx!!", -command => sub { exit; });
- Perl/Tk ならば -command => \&exit; で動作するが、Tkx ではボタンを押すとエラー
- このため、無名関数で exit を包んでいる
無名の配列を使う場合、最初の要素が関数へのリファレンス、あとの要素はその関数に渡される引数になる
たとえば、関数 foo(10, 20) を呼び出したい場合は -command => [\&foo, 10, 20] とする
コールバック関数にメソッドを登録する場合は、次のように指定する
[$object, 'methodname', args, .....] # $object->methodname(args, .....); と同じ
最初に呼び出すメソッドのオブジェクト、その次にメソッド名 methodname、最後に引数を指定する
$object と 'methodname' の順番が Perl/Tk とは逆になるので注意すること
リスト : ボタンとラベルのサンプル
use strict;
use warnings;
use Tkx;
my $top = Tkx::widget->new('.');
my $buff = '';
# ラベルの生成
my $label = $top->new_label(-textvariable => \$buff);
$label->g_pack;
# コールバック関数
sub push_button {
my $n = shift;
$buff = "button $n pressed";
}
# ボタンの生成
foreach my $x (1, 2, 3, 4) {
my $button = $top->new_button(-text => "button $x", -command => [\&push_button, $x]);
$button->g_pack;
}
Tkx::MainLoop()
- オプション -textvariable で変数 $buff を指定する
- 変数の指定にはリファレンスを使う
- これにより、変数 $buff に格納されている文字列がラベルに表示される
- $buff の値を書き換えると、ラベルの表示を変更することができる
- コールバック関数は push_button とし、押されたボタンの番号を引数として渡す
- ボタンは 4 つ作成して番号で区別する
- -text の値は変数置換によってボタンの番号が設定される
- -command の値も、無名の配列にはボタンの番号がセットされる
- 番号 1 のボタンを押した場合、呼び出される関数は &push_button(1) になる
button 1 を押した動作
button の pack() に -fill = 'both' を指定し、button 2 を押した動作
button の pack() に -side = 'left' を指定し、button 3 を押した動作
label の pack() に -anchor = 'w' を指定し、button 4 を押した動作
●色とフォントの指定
- Tk の場合、色の指定は名前または数値で行う
- 名前は 'red', 'green', 'blue' のように文字列で指定する
- 色の名前は大文字小文字の区別をしない (red と RED は同じ色を表す)
- 数値の場合は、赤、緑、青の三原色を 16 進数でする
- 指定方法には、次の 4 通りの形式がある
- #RGB
- #RRGGBB
- #RRRGGGBBB
- #RRRRGGGGBBBB
- 色の指定は # から始まり、R, G, B はそれぞれ赤、緑、青の強度を表す数値
- それぞれの色を表す桁数は同じでなければならない
- 1. では、R, G, B が 16 段階なので 4096 色の指定ができる
- 2. は 256 段階なので、約 1600 万色の指定ができる
- 実際の表示は使用しているハードウェアの環境に依存する
- フォントの指定にはいくつかの方法があるが、無名の配列や文字列を使う方法が簡単
無名の配列: [family, size, style1, style2]
文字列: 'family size style1 style2'
文字列で指定する場合は、要素を空白で区切る
family はフォント名を表す
- 空文字列を指定すると、設定されているフォントは変更されない
- フォントを変更せずに size や style1, style2 を変更することができる
size はフォントの大きさを数値で指定する
style1 と style2 はフォントのスタイルで、次の文字列の中から選択する
style1 : normal, bold, roman, italic
style2 : underline, overstrike
style1 と style2 は省略することができる
リスト : 色とフォントの変更
use strict;
use warnings;
use utf8;
use Tkx;
my $top = Tkx::widget->new('.');
my $a = 'Hello, world, こんにちは世界';
$top->new_label(-text => $a, -font => ['', 12])->g_pack;
$top->new_label(-text => $a, -fg => 'darkgreen', -font => ['', 12])->g_pack;
$top->new_label(-text => $a, -font => ['游明朝', 14 'italic'])->g_pack;
$top->new_label(-text => $a, -fg => 'blue', -font => ['游明朝', 14, 'italic'])->g_pack;
$top->new_label(-text => $a, -font => ['メイリオ', 16, 'underline'])->g_pack;
$top->new_label(-text => $a, -fg => 'red', -font => ['メイリオ', 16, 'underline'])->g_pack;
Tkx::MainLoop();
- 使用可能なフォントは Tkx::font_families() で取得できる
リスト : フォント名の表示
use strict;
use warnings;
use Encode;
use Tkx;
my @name_list = Tkx::SplitList(Tkx::font_families());
foreach my $name (sort @name_list) {
my $name1 = Encode::encode('shift_jis', $name);
print("$name1\n");
}
最近の Windows であれば、'メイリオ' や '游ゴシック' といった名前を見つけることができる
色とフォントの変更
●オプションのデフォルト値の変更
- Tk は各オプションのデフォルト値を持っている
- デフォルト値は Tkx::option_add() を使って変更することができる
- たとえば、フォントのデフォルト値を変更する場合は、次のように行う
リスト : デフォルト値の設定
use strict;
use warnings;
use Tkx;
Tkx::option_add('*font', ['Consolas', 12]);
option_add() の第 1 引数はデフォルト値を設定するウィジェットを表すパターン
第 2 引数が設定する値
第 3 引数には優先順位 priority (0 - 100) を設定することができる
パターンは、アプリケーション名、ウィジェット名、オプション名をドットで区切って表す
ワイルドカード * やウィジェットを表すクラス名を指定することもできる
- クラス名は Tcl/Tk でウィジェットの型を表す名前のこと
- Perl のオブジェクト指向とは関係ない
- たいていのクラス名は、ウィジェットの名前の先頭文字を大文字にしたもの
*font の場合は、アプリケーションで使用するフォントを指定することになる
ラベルに対してフォントを設定したい場合は *Label.font とする
リスト : ボタンのフォントと背景色を変更
use strict;
use warnings;
use Tkx;
Tkx::option_add('*Button.font', ['Consolas', 12]);
Tkx::option_add('*Button.background', 'cyan');
my $top = Tkx::widget->new('.');
my $buff = '';
# ラベルの生成
my $label = $top->new_label(-textvariable => \$buff);
$label->g_pack(-anchor => 'w');
# コールバック関数
sub push_button {
my $n = shift;
$buff = "button $n pressed";
}
# ボタンの生成
foreach my $x (1, 2, 3, 4) {
my $button = $top->new_button(-text => "button $x", -command => [\&push_button, $x]);
$button->g_pack(-side => 'left');
}
Tkx::MainLoop()
ボタンのフォントと背景色を変更
●スケールウィジェット
- スケール (scale) ウィジェットは整数値を選択するために使用する
- スライダーをドラッグするかスケールをクリックすることで、その値を更新することができる
- スケールで使用する主なオプションを下表に示す
表 : Scale の主なオプション
-label | スケールのラベル |
-from | スケールの最小値 |
-to | スケールの最大値 |
-orient | スケールの方向 |
-showvalue | 値を表示するか |
-variable | スケールの値を格納する変数を指定 |
-command | 値が変化したときに実行する関数 |
-resolution | 解像度 |
- -orient はスケールの方向を指定する
- orizontal または h を指定すると水平になり、vertical または v で垂直になる
- デフォルトでは垂直に設定される
- -showvalue は現在の値を表示/非表示を設定する
- -variable はスケールの値を格納する変数を指定する
- -command はスケールの値が変更されたときに実行する関数を指定する
- このほかにも、ウィジェットの大きさを設定するオプションがある
- スケールの主なメソッド
- coords(value)
指定した値 (value) に対応するグライダの座標をタプル (x, y) で返す。
- get(x, y)
指定した座標に対応するスケールの値を返す。座標を省略した場合は現在値を返す。
- set(value)
スケールの値を value に変更する。
- identify(x, y)
指定した座標にスライダがあれば文字列 slider を返す。
スライダより上(左)にあれば trough1 を返し、下(右)にあれば trough2 を返す。
リスト : ボタンの背景色をスケールで変更する
use strict;
use warnings;
use Tkx;
# スケールの値を格納する変数
my $red = 0;
my $blue = 0;
my $green = 0;
# メインウィンドウ
my $top = Tkx::widget->new('.');
# ボタン
my $button = $top->new_button(-text => "button", -bg => '#000');
$button->g_pack(-fill => 'both');
# ボタンの背景色を変更
sub change_color {
my $color = sprintf("#%02x%02x%02x", $red, $green, $blue);
$button->configure(-bg => $color);
}
# スケール
$top->new_scale(-label => 'red',
-orient => 'h',
-from => 0,
-to => 255,
-variable => \$red,
-command => \&change_color)->g_pack(-fill => 'both');
$top->new_scale(-label => 'blue',
-orient => 'h',
-from => 0,
-to => 255,
-variable => \$blue,
-command => \&change_color)->g_pack(-fill => 'both');
$top->new_scale(-label => 'green',
-orient => 'h',
-from => 0,
-to => 255,
-variable => \$green,
-command => \&change_color)->g_pack(-fill => 'both');
Tkx::MainLoop();
- スケールの値は、それぞれ $red, $blue, $green という変数にセット
- 値は 0 に初期化しておく
- ボタンのオブジェクトは背景色を変更するときに必要になるので、変数 $button に格納しておく
- 値が変化したときに実行する関数が change_color
- 文字列のフォーマットを使って $red, $green, $blue の値をカラーコードに変換する
- 数値を 2 桁にそろえるため書式は %02x としている
- その後、ボタンの背景色を configure メソッドで変更する
スライダで RGB を指定する
●メニューバー
- Tk にはメニューのためのウィジェットがいくつか用意されていて、いろいろなメニューを構成することができる
- ここでは、Windows でも標準になっている「メニューバー」という方法を説明する
- コンストラクタ new_menu でメニューのオブジェクトを生成する (これがメニューバーの実体になる)
- メインウィンドウのオプション -menu に生成したオブジェクトを configure() で設定する
- 設定したメニューバーに具体的なメニューを追加する
- メニューを追加する主なメソッド
- add_cascade(), 複数のメニューを表示する
- add_checkbutton(), チェックボタンを表示
- チェックボタンは yes/no のような二者択一の情報を設定するために使う
- add_command(), command で指定したコールバック関数を実行
- add_radiobutton(), ラジオボタンを表示
- add_separator(), 区切りを表示する
- add_checkbutton() と add_radiobutton() は add_cascade() と組み合わせて使うことが一般的
- 選択する値をオプション -value で指定し、その値を格納する変数をオプション -variable で指定する
- また、-command オプションを設定することもできる
- この場合、変数に値がセットされるとともに、指定した関数が実行される
リスト : ゲームのメニュー
use strict;
use warnings;
use Tkx;
# 設定
Tkx::option_add('*font', ['', 14]);
Tkx::option_add("*tearOff", 0);
# グローバル変数
my $level = 1;
my $action = 0;
# ダミー
sub start { }
# メインウィンドウ
my $top = Tkx::widget->new('.');
my $m = $top->new_menu(-type => 'menubar');
$top->configure(-menu => $m);
# cascade の設定
my $m1 = $m->new_menu;
$m->add_cascade(-menu => $m1, -label => 'Games', -under => 0);
my $m2 = $m->new_menu;
$m->add_cascade(-menu => $m2, -label => 'Level', -under => 0);
# Games の設定
$m1->add_command(-label => 'Start', -under => 0, -command => \&start);
$m1->add_separator;
$m1->add_radiobutton(-label => 'first', -variable => \$action, -value => 0);
$m1->add_radiobutton(-label => 'second', -variable => \$action, -value => 1);
$m1->add_separator;
$m1->add_command(-label => 'exit', -under => 0, -command => sub { exit() });
# Level の設定
$m2->add_radiobutton(-label => 'Level 1', -variable => \$level, -value => 1);
$m2->add_radiobutton(-label => 'Level 2', -variable => \$level, -value => 2);
$m2->add_radiobutton(-label => 'Level 3', -variable => \$level, -value => 3);
# Label ウィジェットの設定
$top->new_label(-text => '***** Menu Test *****')->g_pack;
Tkx::MainLoop();
- 将棋やリバーシのようなゲームのメニューを考えてみる
- 最低限必要となるメニューは、ゲームの開始、先手と後手の選択、コンピュータの強さの設定など
- 最初の 2 つはメニュー Games で設定し、強さはメニュー Level で選択することにする
- 最初に font と tearOff のデフォルト値を変更する
- tearOff はメニューをウィンドウから引きちぎることができるかを設定する
- デフォルトでは真になっている
- その場合、メニューを選択すると一番上に破線が表示される
- そこをクリックするとそのメニューが独立したウィンドウになる
- メニューバーの本体 $m は $top->new_menu で生成する
- 次に、Games と Level の本体 $m1 と $m2 を $m->new_menu で生成する
- これらのメニューはメニューバー $m に配置するので $m から new_menu を呼び出すこと
- add_cascade のオプション -menu で $m1 と $m2 を $m に登録する
- メニューバーの構築方法は Tcl/Tk とほぼ同じ
- Perl/Tk とは少々異なるので注意すること
- オプション -label はメニューに表示するテキストを設定する
- オプション -underline (-under でも可) は、ラベルの文字に下線を付け加える
- Windows の場合、Alt キーを押すとキーボードでメニューを選択できる
- このときラベルの文字に下線が表示される
- 下線のついた文字をキーボードから入力することで、そのメニューを選ぶことができる
- 先手・後手の選択はラジオボタンを使っている
- たとえば、後手をクリックすると、action の値は 1 にセットされ、ラベルの左側にレ点がつく
- Level は 3 つのラジオボタンを用意して、その中から一つを選ぶ
Games を選択したときの動作
Level を選択したときの動作