Ruby/Tk - FrontPage  Index  Search  Changes  PageRank  RSS  Login

Ruby/Tkセミナー(2004/12/11)

2004/12/11に行われたRuby/Tk講習会の資料です.

古いものではありますが, Ruby/Tkは可能な限り互換性を保つようにしていますから, この資料の記述のほとんどは今でも有効であるはずです. ただし,現在のRuby/Tkでは,資料の記述よりももっと便利な機能が実装済みかもしれません.(^_^)

以下の資料(テキスト形式のファイル)や資料中のサンプルスクリプトは このリンク からダウンロードできます.

 
             <<<<  Ruby/Tk 講習会 '04/12/11 --  参考資料  >>>

                      永井 秀利 (九工大 知能情報)
                         nagai@ai.kyutech.ac.jp

                              2004/12/08

=============================================================================

Widget demo を試す


Ruby/Tk とは
  Tcl/Tk とは
    Tcl : アプリケーション組み込み用言語として開発
    Tk  : Tcl の GUI ツールキット

    汎用性の高い少数のウィジェット集合
      各ウィジェットがあらゆるイベントを受理可能
      基本思想:「足りなければ作る」
         --> 差分プログラミングが適するが,
             標準の Tcl/Tk はオブジェクト指向ではない

  Ruby/Tk の構造
    Tcl/Tk との連携方法
      ・Tcl/Tk プロセスとのプロセス間通信 <-- 太古の Ruby/Tk
      ・Tcl/Tk の行解析部を経由して制御   <-- 現在の Ruby/Tk の中の極一部
      ・Tcl/Tk の行解析部を経由せずに制御 <-- 現在の Ruby/Tk
      ・Tk ライブラリだけを切り取って利用 <-- Perl/Tk

      Tcl/Tk の拡張ライブラリなどの資源をそのまま使えるという利点と
      処理速度とのバランスから,現在の方針を選択

    tcltklib : Tcl/Tk インタープリタとのやり取りを司る低レベルライブラリ

    tk.rb tk/* tkextlib/* : tcltklib を wrap する高レベルライブラリ


GUI プログラミングのポイント
  ウィジェットの選定と生成
    ウィジェット (widget) とは
      Window Gadget の略
      GUI の部品
      なんらかの「機能」を持つもの
        画像などは「機能」を持たないのでウィジェットではない

  ウィジェットの属性 (オプション) 設定
    ウィジェットの「見掛け」や「動作」を左右

  ウィジェットの配置
    ジオメトリマネージャによる配置管理
    「操作が連続するものは近くに」

  バインディング
    イベントに対する機能割り当て
    イベント : 「キーを押した」であるとか「マウスを動かした」であるとかの
               ウィンドウの上で発生するあらゆる出来事のこと
    「必要な操作を簡単に」
    「操作が途切れにくいように」

  機能的なまとまり --> 新しいウィジェットクラス == ウィジェットとしての選定対象


Hello, World!! の前に
  root_win.rb

  ルートウィジェット ( TkRoot クラス )
    すべての親
      ウィジェットの親子関係
      ひとつのルートウィジェットを根とする親子関係の木
      別ウィンドウを生成するウィジェットもルートウィジェットに支えられている

    TkRoot#title メソッドでウィンドウのタイトル文字列を指定可能
      未指定時はスクリプトのファイル名
      ( 1.8.2 リリース版では '-e one-liner' の場合やスクリプトを標準入
        力から与えた場合のタイトル文字列は "ruby" となる予定 )

  イベントループ (再描画)
    ルートウィジェットが存在する限りは終了しない
       --> ルートウィジェットの破壊が GUI の終了とほぼ同義


Hello, World!!
   hello1.rb
   ラベルウィジェット ( TkLabel クラス )
     文字列 (または画像) を表示するためのウィジェット


ウィジェット生成
   基本形
     widget_class.new(parent, arg, ..., options){ ... }
       parent  :: 親となるウィジェットオブジェクト
       arg, ...:: ウィジェット固有の引数
       options :: <ウィジェットの属性> => <属性の値> の Hash
                      <ウィジェットの属性> はシンボルまたは文字列で与える

   options での指定とブロック内での操作
     options での指定を適用してウィジェットを生成した後にブロックを評価.

     ウィジェット生成時の options でしか指定できない属性もあることに注意.
          ==> options は一般にウィジェット生成時に同時に指定されるのに対し,
              ブロック内での操作はウィジェットの属性変更になるため,
              生成時に確定させてしまわなければならない属性は options でしか
              設定することができない


Hello, World!! バリエーション
   hello2.rb, hello2b.rb, hello2c.rb, hello3.rb


ウィジェットのオプション (属性) 指定方法
   configure              : 戻り値はウィジェット
   オプション名メソッド   : 戻り値はウィジェット
   オプション名メソッド=  : 戻り値はオプション値
   [オプション名]=        : configure(オプション名, val) と同じ

   (例) widget の text オプションの値を 'hoge' にしたい場合
      widget.configire(:text=>'hoge') または widget.configire('text'=>'hoge')
      widget.text('hoge')
      widget.text = 'hoge'


オプション (属性) 参照方法
   cget                 : オプションの現在値を返す
   configinfo           : ['オプション名', オプションデータベース名,
                           オプションデータベースクラス, デフォルト値,
                           現在値] という 5 項目を持った配列の配列を返す.
                          ただし,引数としてオプション名を与えた場合は
                          対応するオプションについての 5 項目だけを返す.
   current_configinfo   : {'オプション名'=>現在値, ... } という Hash を返す
                          ただし,引数としてオプション名を与えた場合は
                          対応するオプションの情報だけを返す.
   オプション名メソッド : cget と同じ
                          ただし,同名メソッドが定義されていない場合
   [オプション名]       : cget と同じ
                          ただし,[] メソッドが別機能で定義されていない場合

   (例) widget の text オプションの情報を得たい場合
      widget.cget(:text) または widget.cget('text')
      widget.configinfo が返す配列から検索
      widget.configinfo(:text) または widget.configinfo('text')
      widget.current_configinfo が返す Hash から検索
      widget.current_configinfo(:text) または widget.current_configinfo('text')
      widget.text
      widget[:text] または widget['text']


Tcl/Tk のマニュアルの活用方法
   Ruby/Tk 自体のマニュアルは不足状態だが,Tcl/Tk のマニュアルから
   かなりの情報が得られる.

   ウィジェット情報の獲得
     (例) ラベルウィジェットのマニュアル
     ------------------------------------------------------------------
     NAME
            label - Create and manipulate label widgets

     SYNOPSIS
            label pathName ?options?

     STANDARD OPTIONS
            -anchor       -font                  -image     -takefocus
            -background   -foreground            -justify   -text
            -bitmap       -highlightbackground   -padx      -textvariable
            -borderwidth  -highlightcolor        -pady      -underline
            -cursor       -highlightthickness    -relief    -wraplength

            See  the  options manual entry for details on the standard
            options.

     WIDGET-SPECIFIC OPTIONS
            Command-Line Name:-height
            Database Name:  height
            Database Class: Height

                   Specifies a desired height for the  label.   If  an
                   image  or  bitmap  is  being displayed in the label
                   then the value is in screen units (i.e. any of  the
                   forms  acceptable  to Tk_GetPixels); for text it is
                   in lines of text.  If this option isn't  specified,
                   the  label's  desired  height  is computed from the
                   size of the image or bitmap or text being displayed
                   in it.
     ------------------------------------------------------------------

     (例) ラベルウィジェットのクラス定義
     ------------------------------------------------------------------
     class TkLabel<TkWindow
       TkCommandNames = ['label'.freeze].freeze
       WidgetClassName = 'Label'.freeze
       WidgetClassNames[WidgetClassName] = self
       #def create_self(keys)
       #  if keys and keys != None
       #    tk_call_without_enc('label', @path, *hash_kv(keys, true))
       #  else
       #    tk_call_without_enc('label', @path)
       #  end
       #end
       #private :create_self

       def textvariable(v)
         configure 'textvariable', tk_trace_variable(v)
       end
     end
     ------------------------------------------------------------------

     SYNOPSIS はウィジェット生成命令の書式
      ↑  ラベルウィジェットでは 'label' がコマンド名
      ↓
     Ruby/Tk の一般的なウィジェットクラス定義では
          ・TkCommandNames で定義
          ・create_self メソッド内で Tcl/Tk インタープリタ呼び出し

     ウィジェットオプションの定義
      ↑  コマンドライン名,データベース名,データベースクラスの三つ
      |
      |  '-height' オプションの場合は
      |      Command-Line Name:-height
      |      Database Name:  height
      |      Database Class: Height
      |
      |  コマンドライン上での '-height' オプションと '-text' オプションの指定
      |      label parent.myName -height 値 -text 値
      |         ==> コマンドライン名を使う.
      |             parent は親ウィジェットのパス名
      |             myName はラベルウィジェット自身の名前
      |               --> parent.myName がラベルウィジェットのフルパス名
      ↓
     Ruby/Tk 上での指定方法
           オプション指定は Hash で与える
              TkLabel.new(parent, :height=>値, :text=>値)
                または
              TkLabel.new(parent, 'height'=>値, 'text'=>値)

                 ==> parent は親となるウィジェットオブジェクト
                     ラベルウィジェット自身の名前を与える必要なし
                     ( Hash で与えるオプションの中で指定することも可能 )


   Ruby --> Tk のデフォルトでの値の変換ルール
     文字列   --> そのまま (通常は encoding 変換も行われる)
     シンボル --> 文字列化
     数値     --> 文字列化
     Array    --> 各要素を変換したものを Tcl/Tk のリストへ
     Hash     --> TkComm.hash_kv() で [-<キーの文字列化>, 値の文字列化, ... ]
                  の配列に変換 ( ウィジェットオプション指定で頻繁に利用 )

     TkUtil::None --> 特殊なオブジェクトで,後述の Tk.tk_call() メソッドの
                      引数とされたときには,Tcl/Tk インタープリタに渡す情報
                      からは取り除かれる ( 省略パラメータなどの取り扱い用 ).

     TkComm._get_eval_string() や TkComm.hash_kv() で変換結果を確認可能


   TclTkIp#_invoke と TclTkIp#_eval
     _invoke(*args) : 各引数を Tcl/Tk のコマンドラインを構成する token として
                      Tcl/Tk インタープリタを呼び出す
                        ==  Tcl/Tk の行解析を経由しない

     _eval(string)  : 引数文字列を Tcl/Tk のコマンドラインとして
                      Tcl/Tk インタープリタを呼び出す
                        ==  Tcl/Tk の行解析が行われる


   Tk.tk_call,tk_send
     TclTkIp#_invoke の wrapper
     通常のウィジェットコマンドの実装で利用

     tk_call(*args) : 引数から Tcl/Tk のコマンドラインを構成する token を
                      生成して,TclTkIp._invoke を呼ぶ

     tk_send(*args) : 最初の token に,レシーバであるウィジェット自身を
                      追加する点を除いては tk_call と同じ.

     通常は token 生成と戻り値引き渡しに際して encoding 変換が行われる.
     encoding 変換を禁止して呼びたければ tk_call_without_encoding() を使う.


   TkComm のユーティリティメソッド (一部の定義は TkUtil 上)
     tk_tcl2ruby(val) : 戻り値の文字列パターンから推定して,数値や
                        ウィジェットなどのオブジェクトに変換

     _at(x,y=nil) : いくつかのウィジェットのインデックス指定で使われる
                    "@x,y" (または "@x") を返す

     tk_split_list(val) : val を Tcl/Tk のリストとして要素に切り分けた後,
                          各要素をオブジェクトに変換する.
                          要素が Tcl/Tk のリストと見なせる場合は再帰的に変換.

     tk_split_simplelist(val) : val を Tcl/Tk のリストとして要素に切り分ける
                                切り分けたものは文字列の配列として返し,
                                再帰的に切り分けたり変換したりはしない.

     array2tk_list(ary) : Array を Tcl/Tk のリストに変換する.

     _symbolkey2str(hash) : ハッシュのキーを文字列化した新しいハッシュを返す
                            属性指定のハッシュを操作する際に用いる

     hash_kv(hash) : {key=>val, ...} を ['-key', val, ... ] に変換

     bool(str) : str が Tcl/Tk の真偽値であるとして true/false に変換

     number(str) : str が Tcl/Tk の数値であるとして数値オブジェクトに変換

     string(str) : str が Tcl/Tk の文字列であるとしてオブジェクトに変換
                   ただし,str の先頭と末尾が { } である場合は { } を取り除く

     num_or_str(str) : str が数値と見なせる場合は number(str) を返し,
                       そうでなければ string(str) を返す

     list(str) : tk_split_list(str) と同じ

     simplelist(str) : tk_split_simplelist(str) と同じ

     window(str) : str をウィジェット名として,ウィジェットオブジェクトを返す

     procedure(str) : str が登録されたコールバック手続きのコマンド文字列で
                      あるとして,対応する手続きオブジェクトを返す.

     _toUTF8(str, enc=nil) : str を enc から utf-8 に変換する
                             enc 省略時はデフォルトの encoding とする

     _fromUTF8(str, enc=nil) : str を utf-8 から enc に変換する
                               enc 省略時はデフォルトの encoding とする

     _get_eval_string(obj) : obj をTcl/Tk に引き渡すための文字列に変換

     install_cmd(cmd) : cmd をコールバック手続きとして登録し,
                        Tcl/Tk に渡すべき手続き文字列を返す


Hello, World!! 属性変更版
   hello_foreground.rb, hello_background.rb
     前景色および背景色の指定
       文字列 : 色のシンボル名.Tcl/Tk の colors マニュアル (man n colors) 参照
       #RRRRGGGGBBBB, #RRGGBB, #RGB (ただし R, G, B はそれぞれ 16 進文字列)
          : RGB 値を直接指定.R, G, B の桁数は同じでなければならない.

   hello_pad.rb
     空白制御
       数値のみ : pixel サイズ
       数値 + p : ポイント       ( screen dpi に依存 )
       数値 + i : インチ         ( screen dpi に依存 )
       数値 + c : センチメートル ( screen dpi に依存 )
       数値 + m : ミリメートル   ( screen dpi に依存 )


   hello_relief.rb, hello_relief2.rb
     表示枠の指定
       borderwidth : 枠の幅を決定
       relief      : 枠の形状を決定

   hello_anchor.rb
     領域に余裕がある場合に表示を寄せる方向の指定

   hello_compound.rb, hello_compound2.rb
     テキストとイメージの同時表示

   hello_font.rb
     フォントの指定

     指定するフォントを定める属性
       family : フォントファミリー名
       size   : 正の値ならポイントサイズ指定,負の値ならピクセルサイズ指定
       weight : 'normal' or 'bold'
       slant  : 'roman' or 'italic'
       underline, overstrike : それぞれの有無

     指定形式 (style は normal/bold/roman/italic/underline/overstrike)
       "family size" または "family size style ..."
          一つの文字列で指定.family が空白を含むときは { } で囲むこと.

       [family, size] または [family, size, style, ...]
          文字列指定と同内容を配列で指定.family が空白を含んでも { } は不要.

       { key=>val, ... }
          Hash 指定.key には属性名 (シンボルまたは文字列) を用いる.
          underline, overstrike の値には true or false を与える


イメージオブジェクト
   ( 上記 hello_compound.rb, hello_compound2.rb の例を参照 )
   画像データをウィジェット上に表示するためのもの
   TkBitmap : ビットマップデータ
   TkPhotoImage : フォトイメージ
                     標準では PPM/PGM, GIF をサポート.
                     Tcl/Tk の Img 拡張の導入で対応フォーマット増加.
                      ( Img 拡張は ActiveTcl に含まれる )


フォントオブジェクト
   ウィジェットでの文字列表示に用いるフォントを管理するためのオブジェクト

   共通のフォントオブジェクトを割り当てられたウィジェットの表示は,
   そのフォントオブジェクトの操作で同時に変化

   fontobj.rb  : フォントオブジェクトによる複数ウィジェットのフォントの同時変更

   fontobj2.rb : font 属性の参照により現在のフォント属性に基づくオブジェクトを
                 生成し,それを操作するようにした例

   ( fontobj3.rb : 1.8.2 リリース版では,この例のような使い方も可能となる予定 )


Hello ボタン
   ボタンウィジェット ( TkButton クラス )

   ボタンをクリックすると command 属性に指定した手続きを呼ぶ
   手続きを要求するオプション ( command 属性 ) には手続きオブジェクトを与える
      --> invoke メソッドによる外部からの手続き呼び出しも可能

   hello_button.rb

   リピート属性 : 左ボタンを押し続けている間は手続き呼び出しを繰り返す
      hello_repeat.rb


pack ジオメトリマネージャ
   hello_quit1.rb : pack による複数ウィジェットの配置

   空き領域モデル
     空き領域,配置領域,表示領域の三つを考える

     ウィジェットの表示に使われるのが表示領域

     配置領域の中に表示領域を置く ::  配置領域サイズ >= 表示領域サイズ

     矩形の空き領域を直線で切り分けて矩形の配置領域を確保
       -->  残りの空き領域も矩形

   side オプション :: hello_quit1b.rb, hello_quit1c.rb
     空き領域のどの側から配置領域を切り取るかの指定
         top, bottom, left, right
     デフォルトは top

      +-----------------+              +-----------------+
      |                 |              |                 |
      |                 |              |     確保領域    |
      |                 |              |                 |
      |                 |            -----------------------
      |     空き領域    |  => top =>   |                 |    ===>
      |                 |              |     空き領域    |
      |                 |              |                 |
      +-----------------+              +-----------------+

                 +-----------------+               +-----------------+
                 |                 |               |                 |
                 |     確保領域    |               |     確保領域    |
                 |                 |               |                 |
         ===>    +-----------------+  => left =>   +-----------------+
                 +-----------------+               +-------|---------+
                 |                 |               | 確保  |   空き  |
                 |     空き領域    |               |  領域 |    領域 |
                 |                 |               |       |         |
                 +-----------------+               +-------|---------+

   fill オプション :: hello_quit2.rb, hello_quit2b.rb
     配置領域 > 表示領域の時,配置領域に合わせて表示領域を拡大するかの指定
         none, x, y, both
     デフォルトは none

   expand オプション :: hello_quit3.rb, hello_quit3b.rb
     土台となるウィジェットの拡大時,配置領域を拡大するかの指定
         true, false
     デフォルトは false == 拡大しない
     すべての配置領域が expand=> false の場合,空き領域が拡大する

   anchor オプション :: hello_quit4.rb
     配置領域 > 表示領域の時,配置領域内のどの位置に表示領域を置くかの指定
         方角指示 ==  center, n, e, w, s, ne, nw, se, sw
     デフォルトは center == 中心に配置

   余白の制御 :: pack_pad_sample.rb
     padx, pady   : ウィジェットの表示領域の外側に余白を確保する
     ipadx, ipady : ウィジェットの表示領域の内側に余白を確保する

     ウィジェット内容物 (例ではラベルテキスト) の anchor は内側余白の範囲で機能

   土台となるウィジェットの縮小などで表示に必要な領域が確保できない時は?
     pack の順序に依存 ==> 先のものから優先表示
       均等に縮小したりはしない.
       優先順位が高いものはフルサイズで表示.
       残り領域が不十分な時はそのすべてを使ってできるだけ大きく表示.
       残り領域が 0 になれば表示せずに無視.

   propagate 指定 :: propagate-true.rb, propagate-false.rb
     サイズ変更を伝播するかの指定
     通常は内容物に合わせて土台の大きさも変化 --> それでは困る場合も!
      (例) ラベルの表示内容を変える度に全体の大きさが変化してはうっとおしい!
     propagete を false にすれば伝播しない

   縦横配置
     一つの土台の上で縦方向と横方向の配置を混ぜると失敗するケースが多い

     フレームウィジェット ( TkFrame クラス ) を使う
       配置の際の土台にすることが主目的のウィジェット
       ( 他にはスペーサや他のアプリケーションのコンテナなどとして働く )

     ウィジェットの積み木による配置
       複数のフレームウィジェットを横配置
         各フレームウィジェット上でウィジェットを縦配置
       frame_stack.rb
           +----------------------------------------------+
           |    root                                      |
           |  +------------------+  +------------------+  |
           |  |   frame          |  |   frame          |  |
           |  |  +------------+  |  |  +------------+  |  |
           |  |  |   button   |  |  |  |   button   |  |  |
           |  |  +------------+  |  |  +------------+  |  |
           |  |                  |  |                  |  |
           |  |  +------------+  |  |  +------------+  |  |
           |  |  |   button   |  |  |  |   button   |  |  |
           |  |  +------------+  |  |  +------------+  |  |
           |  |                  |  |                  |  |
           |  +------------------+  +------------------+  |
           |                                              |
           +----------------------------------------------+


エントリウィジェット ( TkEntry クラス )
   1行入力を行うためのウィジェット

   内容の読出 : TkEntry#get あるいは TkEntry#value
   内容の設定 : TkEntry#set あるいは TkEntry#value=

   entry_test.rb


課題:名前を入力してからボタンを押すと挨拶を返す

    作例: hello_entry.rb


TkVariable
   Tcl/Tk インタープリタ上の変数へのアクセスのためのオブジェクト
   いく種かのウィジェットは変数を値とするようなオプションを持つ
     (例) ラベルウィジェットの textvariable オプション
            -->  変数の値を表示文字列とする

          エントリウィジェットの textvariable オプション
            -->  エントリボックスの中身と変数の値とを同一にする

   hello_variable.rb

   Tcl/Tk 上の値はすべて文字列となる点に注意!!

   変換メソッド群
      value, value=     : 文字列としての読み書き
      numeric, numeric= : 数値としての読み書き
      bool, bool=       : 真偽値としての読み書き
      list, list=       : Tcl/Tk のリスト (Ruby では Array) としての読み書き

      演算子 : &, |, +, -, *, /, %, **, =~, ==, <=>
        (例) v = TkVariable.new(3)
             v + 4           #==> 7
             v.numeric *= 2  #==> 6
             v.numeric       #==> 6
             v.value         #==> "6"
             v.value += 'aa' #==> "6aa"
             v.value         #==> "6aa"
             v.numeric       #==> raise exception!!

   変数の読み書きに trace をかけることも可能


ラジオボタン ( TkRadiobutton クラス )
   複数の中から一つだけを選択

   共通の TkVariable によって関連付け
     --> 選択されたボタンに対応する値 (value 属性値) を TkVariable に設定
         TkVariable の値が value 属性値に一致するなら選択状態になる

   anchor 属性に注意

   radiobutton1.rb, radiobutton1b.rb


チェックボタン ( TkCheckbutton クラス )
   チェックの ON/OFF

   割り当てられた TkVariable に状態保存 : ON/OFF それぞれの時の値を設定可能
      onvalue オプションと offvalue オプションとで設定

   checkbutton1.rb, checkbutton1b.rb, checkbutton1c.rb
   ( 1.8.2 リリース版では使える予定 : checkbutton2.rb )


スピンボックス ( TkSpinbox クラス )
   スピンボタンが付いたエントリボックス

   エントリボックスの内容を UP/DOWN できる
      --> spinup や spindown メソッドにより外部から UP/DOWN 可能

   UP/DOWN できる値は数値やリスト
     spinbox_test1.rb, spinbox_test1b.rb, spinbox_test1c.rb
     spinbox_test2.rb


バリデーション (検証処理)
   エントリボックスなどでは不正な入力かどうかの検査がしばしば重要

   エントリウィジェットやスピンボックスウィジェットにはバリデーション機能あり
      ==>  入力が正当かどうかを検査する機能

   validate オプション : 検査手続きを呼ぶタイミングの設定
                           none, focus, focusin, focusout, key, all
                         デフォルトは none ( == 検査手続きを呼ばない )

   validatecommand オプション : 検査手続き
   invalidcommand  オプション : 「妥当でない」と判定された時に呼ばれる手続き
      手続きは状態情報を受け取ることが可能
      (例)  proc{|arg| ... }
              arg には情報オブジェクトが渡される
                 arg.widget  : 対象となっているウィジェット
                 arg.current : 対象の現在値
                 arg.string  : 挿入/削除しようとしている文字列
                 arg.value   : 変更を適用した後の対象の値       など

            [proc{|w,str,cur| ... }, '%W', '%S', '%s']
              「 % 置換」によって渡される情報を指定
                     %W : arg.widget 相当
                     %S : arg.string 相当
                     %s : arg.current 相当   など

      ( ※ バリデーションに限らず,% 置換による情報引き渡しが可能な
           手続きを値とするオプションの設定には同様の書式が用いられる.
           TkUtil::CallbackSubst のサブクラスとなっているものがあれば,
           その KEY_TBL と PROC_TBL の定義に注意するとよい.)

   validate_test.rb


トップレベルウィジェット ( TkToplevel クラス )
   ルートウィジェットとは別のウィンドウを開く --> ウィジェット配置の土台
     toplevel_test1.rb

   TkToplevel#title でウィンドウタイトル文字列を指定可能
     未指定時はトップレベルウィジェットのパス名がタイトルとなる

   raise と lower
     toplevel_test2.rb


課題:プロフィール入力ウィンドウ
      次のものを含むようにする
       ・名前を入力するエントリボックス
       ・性別指定のラジオボタン
       ・年齢入力のスピンボックス
       ・「既婚」,「子供あり」,「持ち家」のチェックボタン
       ・設定内容をコンソールに出力するボタン

      作例: profile.rb


ウィジェットの種類 (クラス階層)
  ( ※ 1.8.2 のリリース版での予定分を含む )
  =============================================================================
   <クラス>
   TclTkIp   [tcltklib] Tcl/Tk インタープリタ

   MultiTkIp   複数の Tcl/Tk インタープリタを扱うためのクラス
     RemoteTkIp   同じディスプレイ上にウィンドウを持つ Tcl/Tk インター
                  プリタ (同一マシン上のプロセスでなくても良い) を
                  Tcl/Tk の send コマンドを用いて制御するためのクラス

   TkKernel
     TkObject
       TkWindow
         TkRoot   ルートウィジェット

         TkToplevel   トップレベルウィジェット

         TkFrame  フレームウィジェット
           TkLabelFrame  ラベル付きフレームウィジェット
           TkMenubar     メニューバー

         TkLabel  ラベルウィジェット
           TkMessage   メッセージウィジェット

           TkEntry   エントリウィジェット
             TkSpinbox   スピンボックスウィジェット

           TkButton   ボタンウィジェット
             TkRadioButton (TkRadiobutton)   ラジオボタンウィジェット
               TkCheckButton (TkCheckbutton)   チェックボタンウィジェット

           TkMenubutton   メニューボタン
             TkOptionMenubutton   オプションメニューボタン
                                  (Tcl/Tk の tk_optionMenu コマンドを利用)

         TkTextWin
           TkListbox   リストボックスウィジェット
             TkScrollbox   スクロールバー付きリストボックスウィジェット
           TkText   テキストウィジェット

         TkCnavas  キャンバスウィジェット

         TkMenu   メニューウィジェット
           TkMenuClone       クローンメニュー
           TkSysMenu_Help    システム固有の Help メニュー   ( Unix 系 )
           TkSysMenu_System  システム固有の System メニュー ( Windows )
           TkSysMenu_Apple   システム固有の Apple メニュー  ( Mac )
           TkOptionMenubutton::OptionMenu   オプションメニューボタンに
                                            割り付けられたメニュー

         TkPanedWindow (TkPanedwindow)   ぺインドウィンドウウィジェット

         TkScale   スケールウィジェット

         TkScrollbar  スクロールバーウィジェット
           TkXScrollbar   横方向スクロールバー
           TkYScrollbar   縦方向スクロールバー

         TkDialog2   定型ダイアログオブジェクト (再利用可能型)
           TkDialog     定型ダイアログオブジェクト (使い捨て型)
           TkWarning2   警告表示向け定型ダイアログオブジェクト (再利用可能型)
             TkWarning   警告表示向け定型ダイアログオブジェクト (使い捨て型)


       TkcItem   キャンバスウィジェット上のアイテム (abstract class)
         TkcArc         円弧
         TkcBitmap      ビットマップ
         TkcImage       イメージ
         TkcLine        線
         TkcOval        楕円
         TkcPolygon     多角形
         TkcRectangle   矩形
         TkcText        テキスト
         TkcWindow      埋め込みウィンドウ

       TkcTag  キャンバスタグ
         TkcTagString   タグ名文字列を指定
         TkcTagAll      'all' タグ
         TkcTagCurrent  'current' タグ
         TkcGroup       タググループ (複数タグをまとめて扱う)

       TkImage   イメージオブジェクト (abstract class)
         TkBitmapImage   ビットマップ
         TkPhotoImage    フォトイメージ (標準では PPM/PGM, GIF をサポート.
                         Img 拡張の導入で対応フォーマット増加)

       TkTextTag   テキストタグ
         TkTextNamedTag   タグ名文字列を指定
           TkTextTagSel   'sel' タグ

       TkTextMark   テキストマーク
         TkTextNamedMark    マーク名文字列を指定
           TkTextMarkInsert   'insert' マーク
           TkTextMarkCurrent  'current' マーク
           TkTextMarkAnchor   'anchor' マーク

       TkTextImage   テキストウィジェット上のイメージオブジェクト

       TkTextWindow   テキストウィジェット上の埋め込みウィンドウオブジェクト

       TkNamespace   Tcl/Tk の名前空間
       TkNamespace::NsCode   名前空間束縛

       TkMsgCatalog  メッセージカタログ


     TkUtil::CallbackSubst   コールバック時の % 置換の管理
       Tk::Event   イベントコールバック用
       TkValidateCommand::ValidateArgs   エントリウィジェットのバリデーション用
       TkSpinbox::ValidateArgs   スピンボックスウィジェットのバリデーション用

     TkUtil::CallbackSubst::Info  コールバック置換の内部管理用
                                  ( TkUtil::CallbackSubst で利用 )

     TkCallbackEntry   Tcl/Tk インタープリタ依存コールバック管理用のクラス


   TkFont   フォントオブジェクト
     DescendantFont   Tk8.0jp の compound font 管理用

   TkTimer (TkAfter)   タイマーオブジェクト

   TkBindTag   バインドタグ
     TkBindTagAll   'all' タグ
     TkDatabaseClass   データベースクラス (あるいはタグ名文字列指定) 用タグ
                       (フレームウィジェットの class オプションを参照)

   TkValidateCommand   バリデーションコマンド ( % 置換引数付きコールバック )
     TkSpinbox::SpinCommand スピンボックスウィジェットの command オプション用

   String
     Tk::EncodedString   エンコード情報付き文字列
       Tk::BinaryString   バイナリ文字列
       Tk::UTF8_String    utf-8 文字列

   Array
     TkNamespace::ScopeArgs   名前空間束縛で利用

   Hash
     Tk::OptionObj   ウィジェット属性管理オブジェクト

   Exception
     StandardError
       TkCallbackReturn    Tcl/Tk インタープリタに return コードを返す
       TkCallbackBreak     Tcl/Tk インタープリタに break コードを返す
       TkCallbackContinue  Tcl/Tk インタープリタに continue コードを返す
     MultiTkIp_OK   MultiTkIp での正常戻り値受け取り用


   <モジュール>
   TclTkLib   [tcltklib] Tcl/Tk ライブラリ依存のユーティリティメソッド群
   TclTkLib::EventFlag   イベントループで扱うイベントフラグ定数の定義
   TclTkLib::VarAccessFlag  Tcl/Tk 変数のアクセスフラグ定数の定義
   TkUtil   Tcl/Tk ライブラリ非依存のユーティリティメソッド群 ( C で定義 )
   TkComm   ユーティリティメソッド群 ( Ruby で定義 )
   TkCore   Tcl/Tk インタープリタとのやり取りを扱うモジュール
   Tk  Ruby/Tk の中心となるモジュール.Tcl/Tk の一般コマンドなどを扱う
   Tk::Encoding   エンコーディング設定
   TkBindCore     bind 操作のためのメソッドの定義
   TkTreatFont    フォント操作のためのメソッドの定義
   TkConfigMethod   ウィジェット属性操作のためのメソッドの定義
   TkBgError   Tcl/Tk の bgerror コマンドの wrapper
   TkCanvasItemConfig   キャンバスアイテムの属性操作のための定義モジュール
   TkcTagAccess   キャンバスタグ操作のためのメソッドの定義
   TkClipboard   Tcl/Tk の clipboard コマンドの wrapper
   Tk::Clock   Tcl/Tk の clock コマンドの wrapper
   TkComposite   複合ウィジェット定義のために使われるモジュール
   TkConsole   Tcl/Tk の console コマンドの wrapper
   TkEvent   バインディング設定のためのモジュール
   TkEvent::Event::TypeNum   イベント情報におけるイベントタイプ定数の定義
   TkGrid   grid ジオメトリマネージャの操作メソッド
   TkItemConfigOptkeys   アイテムの属性タイプの一般定義モジュール
   TkItemConfigMethod   アイテムの属性操作メソッドの一般定義モジュール
   TkItemFontOptkeys   アイテムのフォント属性名の一般定義モジュール
   TkTreatItemFont   アイテムのフォント属性操作メソッドの一般定義モジュール
   TkKinput   kinput2 操作メソッド
   TkListItemConfig   リストボックスアイテムの属性操作のための定義モジュール
   TkMacResource   Macintosh resources の操作
   TkMenuEntryConfig   メニューエントリの属性操作のための定義モジュール
   TkSystemMenu   システム固有メニューの定義用
   TkMenuSpec   メニュー構造定義用モジュール
   TkManageFocus   フォーカス管理用 Tcl/Tk ユーティリティコマンドの wrapper
   TkOptionDB   オプションデータベース操作用モジュール
   TkOptionDB::Priority   オプションデータベースの優先度定数定義
   TkPack   pack ジオメトリマネージャ
   TkPackage   Tcl/Tk パッケージ管理用モジュール
   TkPalette  Tcl/Tk の color palette 操作用モジュール
   TkPlace   place ジオメトリマネージャ
   Tk::Scrollable   スクロール可能なウィジェット用のメソッド定義
   Tk::X_Scrollable   横方向にスクロール可能なウィジェット用のメソッド定義
   Tk::Y_Scrollable   縦方向にスクロール可能なウィジェット用のメソッド定義
   TkSelection   Tcl/Tk のセレクション操作コマンドの wrapper
   TkTreatTagFont   テキストタグなどのフォント属性操作のためのメソッド定義
   Tk::ValidateConfigure   % 置換引数を持つ属性を扱うためのモジュール
   Tk::ItemValidateConfigure   アイテムで % 置換引数を持つ属性を扱うモジュール
   TkValidation   バリデーション関連属性を扱うためのモジュール
   TkValidation::ValidateCmd::Action   バリデーション時の操作タイプ定数定義
   TkWinfo   ウィンドウ情報獲得 (Tcl/Tk の winfo コマンド) 用モジュール
   TkWinDDE   Dynamic Data Exchange コマンド用モジュール (Windows)
   TkWinRegistry  Windows の registry 操作用モジュール
   Tk::Wm   ウィンドウマネージャとの連携のためのモジュール
   TkXIM   input method 操作用モジュール
  =============================================================================


TkTimer オブジェクト
   一定時間後の実行や反復処理を行うためのもの
   TkTimer.new(インターバル, 繰り返し回数, 手続き, ... )
     インターバルは
       (1) ミリ秒単位の数値,
       (2) ミリ秒単位の数値を返す手続きオブジェクト,
       (3) idletask 時を表す 'idle'
     のいずれか.

     繰り返し回数が -1 なら無限に反復.

     手続き,... は並び順に反復 (一巡したら 1 回とカウント)

     手続きには self ( TkTimer オブジェクト自身 ) が引数として渡される.
     TkTimer#return_value で,前回実施した手続きの戻り値が参照可能.

   手続きが一つだけなら TkTimer.new(インターバル, 繰り返し回数){ ... } でも可

   TkTimer#start でタイマースタート
     start(ウェイト,初期手続き, 引数,... ) などと引数を与えた場合は,
     ・初期手続きの戻り値が,次の手続きで return_value で参照される値となる
     ・引数,... は,TkTimer#current_args で参照できる

   いきなりスタートするなら TkTimer.start() も可 (引数は TkTimer.new と同じ)

   hello_blink.rb, hello_flash.rb


バインディング
   マウス操作などに対するウィジェットの反応
     ==  デフォルトでのイベントへのバインディング

   イベントシーケンス :: イベントパターン or イベントパターンの配列
                                                      ↓
                                           指定したイベントが連続して発生

   イベントパターン :: 部分省略可能な MOD-...-TYPE-DETAIL という形式の文字列
     一つのイベントを指定するもの
       詳しくは Tcl/Tk の bind のマニュアル (man n bind) を参照
          ----------------------------------------------------
                  Tcl/Tk         →        Ruby/Tk
          ----------------------------------------------------
                <イベント>                'イベント'
           <イベント><イベント>      ['イベント', 'イベント']
              <<仮想イベント>>          '<仮想イベント>'
          ----------------------------------------------------
           ( ※ 仮想イベント : いくつかのイベントシーケンスの
                               集合に対してつける別名 )

     TYPE : イベントの種別
       -----------------------------------------------------------
         Activate             Enter              Map
         ButtonPress (Button) Expose             Motion
         ButtonRelease        FocusIn            MouseWheel
         Circulate            FocusOut           Property
         Colormap             Gravity            Reparent
         Configure            KeyPress, Key      Unmap
         Deactivate           KeyRelease         Visibility
         Destroy              Leave
       -----------------------------------------------------------

     DETAIL : イベントの詳細
       例えば,キー入力イベントなら押されたキーの名称であり,
               マウスクリックであれば押されたボタンの番号.

     MOD : イベントの修飾子
       -----------------------------------------------------------
         Control              Mod2 (M2)
         Shift                Mod3 (M3)
         Lock                 Mod4 (M4)
         Button1 (B1)         Mod5 (M5)
         Button2 (B2)         Meta (M)
         Button3 (B3)         Alt
         Button4 (B4)         Double
         Button5 (B5)         Triple
         Mod1 (M1)
       -----------------------------------------------------------

     (例) 'a' == 'KeyPress-a'
           '1' == 'ButtonPress-1' (数値はキーではなくマウスボタンと見なされる)
           'Control-Shift-Alt-Button3-Double-KeyPress-Return'
               == Control キー, Shift キー, Alt キー, マウスの右ボタンを
                  同時に押しながら,Return キーを 2 回連続で叩く

   キーボードイベントを得るのは,フォーカスが設定されたウィジェット
      --> マウス操作や focus メソッドなどで設定される.

   バインド対象 :: ウィジェットクラス or ウィジェット or バインドタグ(後述)

   バインドの定義 :: bind メソッドを使用
      target.bind(イベントシーケンス, 手続き)

      target.bind(イベントシーケンス){ 手続き }

      target.bind(イベントシーケンス, 手続き, 引数, ... )

      target.bind(イベントシーケンス, 引数, ... ){ 手続き }
          ( → 1.8.2 リリース版では使える予定 )

     hello_entry2.rb
     hello_entry2b.rb
     hello_button2.rb

   イベントに関する情報を得る :: 手続きに引き渡される情報
      (1) 引数の指定がない場合 --> イベント情報オブジェクト
             (例)  proc{|ev| ... }
                     ev.widget : イベントが発生したウィジェット
                     ev.x : イベントが発生した X 座標 (ウィジェット左上が原点)
                     ev.y : イベントが発生した Y 座標 (ウィジェット左上が原点)
                     ev.x_root : ウィンドウでの X 座標 (ウィンドウ左上が原点)
                          ( 1.8.2 リリース版では ev.rootx, ev.root_xも可 )
                     ev.y_root : ウィンドウでの Y 座標 (ウィンドウ左上が原点)
                          ( 1.8.2 リリース版では ev.rooty, ev.root_yも可 )
                     ev.char   : キーボードで押されたキーの文字
                     ev.keysym : キーボードで押されたキーのキーシンボル
                        など

      (2) 引数が指定された場合 --> 指定した引数が渡される.
             ( ただし,空白を含む場合は split したそれぞれが引数となる )

           ・引数中の % + 1 文字 : 可能なら % 置換を実施

           ・引数 == % 置換が可能な % + 1 文字 :
                 置換の上,イベント情報オブジェクトと同様の型で返す

                     %W == ev.widget
                     %x == ev.x
                     %y == ev.y
                     %X == ev.x_root
                     %Y == ev.y_root
                     %K == ev.keysym   など

           ・その他 : 文字列として渡される

             (例)  proc{|a,b,c,d,e| ... }, '%W', 'x%W', '%x %y', 123
                     a == イベントが発生したウィジェット
                     b == 'x' + イベントが発生したウィジェットのパス文字列
                     c == イベントが発生した X 座標 (ウィジェット左上が原点)
                     d == イベントが発生した Y 座標 (ウィジェット左上が原点)
                     e == 文字列 "123"

      hello_button2b.rb
      hello_button2c.rb
      hello_button2d.rb
      event_test.rb

   バインディングにおける手続き選択
     例えば 'ButtonPress-1' は「 Shift ボタンが押されてない」のではなく,
     「 Shift ボタンの状態は問わない」ことを意味する.
         --> Shift を押しながらボタン 1 をクリックした場合,
                ButtonPress-1 と Shift-ButtonPress-1 とに適合
                   --> どちらが選ばれる???

     選択のルール
        Rule1: 各バインドタグごとに,呼ばれるバインディングは一つだけ
        Rule2: より詳細なシーケンス指定ほど優先度が高い
        Rule3: 後から定義した方が優先度が高い

       bind_detail.rb

     Double, Triple に関する注意
     Double の際には Single, Double で発生する
     連続と判断するのは 500ms 以下の時間
       ==>  Double でないことを知るには 500ms を待つ必要あり

      event_test2.rb

   イベントの強制発生
     必要であれば,命令としてイベントを発生させることができる
       (例) target.event_generate('ButtonPress-1', :x=>100, :y=>100 )


バインドタグ
   hello_button3.rb --> なぜ期待に反する?

   イベント処理の順序 == bindtags
     通常は  (1) ウィジェット
             (2) ウィジェットクラス
             (3) ルートウィジェットまたはトップレベルウィジェット
             (4) TkBindTag::ALL

     追加/削除が可能 : bindtags= メソッド
        (例) ウィジェットクラスを外す
               --> そのウィジェットクラスとしての機能を失う

             新しいバインドタグオブジェクトを追加
               --> そのバインドタグに設定したバインディングが追加される

   Tk.callback_continue と Tk.callback_break
     バインドタグリストの実行を制御
       Tk.callback_continue : 現在のタグの処理を中止し,次のタグの処理へ進む
       Tk.callback_break    : 現イベントに対する処理を中止 (後続するタグは無視)

     hello_button3b.rb
     hello_button4.rb
     hello_button5.rb


課題:ラベルウィジェットを使ってボタンウィジェットもどきを作る
   できるだけ似せることを目指すが,ボタンを押し込んだ時にラベル文字列位置が
   ずれる点については難しいので真似なくてよい.

     例: button_label1.rb 〜 button_label4.rb
         リピート機能を追加 : button_label5.rb


補足:
   button_label6.rb
     フレームとラベルを別々に扱うことで,ボタンと同様に押し込み時の
     ラベル位置がずれるようにしたもの.
     ただし,ラベル上でボタンを押したままマウスを動かすと,ラベル上
     から出た途端,まだフレーム上にあったとしても Leave したことに
     なってしまうという問題がある.

   button_label7.rb
     button_label6.rb の問題を修正したもの.
     ボタンを押したままの Motion に対しては,マウス下のウィジェット
     をチェックして,ボタン外かどうかを判断するようにしている.


grid ジオメトリマネージャ
   pack ジオメトリマネージャの限界 : limit_of_pack.rb

   行 (row) と列 (column) での管理
     行ごとにウィジェットを指定していく方法 : grid1.rb
     ウィジェットごとに行と列との位置 (セル) を指定していく方法 : grid1b.rb

   セル ( == 配置領域 ) 内での配置設定
     sticky オプション : セル内での表示領域位置と拡大とに関する指定
                         pack の fill, expand, anchor を合わせたようなもの
         grid2.rb, grid3.rb
         grid_sticky_test.rb

     padx, pady, ipadx, ipady : 余白制御
         grid2b.rb, grid2c.rb

     columnspan, rowspan : 複数のセルに跨る配置
         grid4.rb

     weight オプション : 拡大/縮小の影響を適用する比率
         grid5.rb


スクロールバーウィジェット ( TkScrollbar クラス )
   通常はスクロール可能な他のウィジェットと組み合わせて用いる.

   ウィジェットとスクロールバーとで相互に操作を指示することで連携させる.
     --> かなり面倒!!   (例) scroll_entry_raw.rb

   Ruby/Tk では Scrollable ( X_Scrollable, Y_Scrollable ) モジュールを
   ウィジェットクラスに include することで簡単に連携指定が可能
       (例) scroll_entry.rb
     xscrollbar メソッド : 横方向スクロールバーの割り当て
     yscrollbar メソッド : 縦方向スクロールバーの割り当て

   複数のウィジェットを同時に制御することも可能
     --> TkScrollbar#assin メソッド等を利用


リストボックスウィジェット ( TkListbox クラス )
   項目リストの表示や操作を行うためのウィジェット

   リストは一番上が 0 となるようなインデックスを持つ
   末尾の項目の次の位置は :end または 'end' で指定
   その他,anchor : セレクション位置 (マウスクリックなどで設定される)
           @x,y   : ウィジェット上でのピクセル座標 (x,y) の位置の項目

   TkListbox#inset(index, val, ...) : 項目挿入
   TkListbox#delete(index)          : 1 項目消去
   TkListbox#delete(first, last)    : 指定範囲の項目消去
   TkListbox#clear                  : 全項目の消去
   TkListbox#get(index)             : 1 項目の内容 (文字列) を得る
   TkListbox#get(first, last)       : 指定範囲の内容を配列で得る
   TkListbox#value                  : 全項目の内容を配列で得る
   TkListbox#value=(array)          : 全項目の内容を一度に設定 (置き換え)

   項目ごとの属性設定も可能
     TkListbox#itemcget
     TkListbox#itemconfigure
     TkListbox#itemconfiginfo
     TkListbox#current_itemconfiginfo
       第1引数に項目を指定する index を与える以外は,それぞれの使い方は
       名前から 'item' を除いた cget, configure 等と同じ

   指定項目が表示範囲内になるようにする
     TkListbox#see(index)

   scroll_listbox.rb


課題:リストボックスから選択してエントリウィジェットへ
   方法は複数
   ・リストボックスの '@x,y' インデックスを用いる方法
       例: lbox_entry1.rb

   ・リストボックスの nearest メソッド (インデックスを返す) を用いる方法
       例: lbox_entry2.rb

   ・リストボックスの 'anchor' インデックスを用いる方法:イベント処理順に注意
       例: lbox_entry3.rb  <-- 失敗例
           lbox_entry3b.rb <-- 成功例


panedwindow ウィジェット ( TkPanedwindow クラス )
   大きさを変更できる複数のペインで構成されるウィジェット

   orient 属性の値でペインが並ぶ方向を指定する
     horizontal で横向き (デフォルト), vertical で縦向き

   TkPanedwindow#add メソッドでウィジェットを追加する度に新しいペインを生成
   TkPanedwindow#forget (or delete or remove) で削除

   追加の際には before オプションや after オプションで挿入位置を指定可能

   ペイン属性の参照や操作
     TkPanedwindow#panecget
     TkPanedwindow#paneconfigure
     TkPanedwindow#paneconfiginfo
     TkPanedwindow#current_paneconfiginfo

   TkPanedwindow#panes : 管理下のウィジェットのリスト (配置順) を返す

   paned_lbox.rb
   ( paned_lbox2.rb : TkScrollbar#assign で同時にスクロールするようにした例 )


テキストウィジェット ( TkText クラス )
   テキスト表示を主目的とした多機能ウィジェット
     ==>  行と文字位置とで管理するジオメトリマネージャ

           # 単純な ( ラベルウィジェットレベルの ) 複数行表示なら
           # メッセージウィジェット ( TkMessage クラス ) というものもある

   文字列,イメージ,ウィジェットのすべてが表示可能

   テキストの一部に属性 (フォントやバインディングなど) を設定可能
     ==> 範囲にテキストタグ (後述) を割り当てて,そのテキストタグに属性を設定

   標準でエディタとして十分な機能を持つ
     挿入,削除,検索, cut & paste,undo (デフォルトでは off) など

   wrap オプション : 行の回り込みの設定
                     none なら回り込みなし,char なら文字単位で可,
                     word なら単語単位で可 (デフォルト)

   state オプション : テキストウィジェットの内容の変更の可否
                      normal なら可 (デフォルト),disabled なら不可

   undo 機能を有効にするためのオプション
     :undo=>true, :maxundo=>記録量(0以下は無限), :autoseparators=>true

   TkText#edit_reset : undo/redo スタックのクリア

   TkText#modified? : テキストウィジェットの内容が変更されているかを返す
   TkText#modified(mode) : 内容が変更されている否かを設定する

   インデックス指定
       [ 基本 ]
      行.文字位置  : 行は 1 から.文字位置は 0 から.
                     ( 間違えて実数値として扱わないように注意!! )

      @x,y         : ウィジェット上でのピクセル座標 (x,y) の位置

      end          : 最後の文字の次の位置

      TkTextWindow    : テキスト埋め込みウィジェット (後述) の位置
                        TkTextWindow.new(text_widget, index, options) で生成
                        widget 指定は options で :window=>widget を指定

      TkTextImage     : テキスト埋め込みイメージの (後述) 位置
                        TkTextImage.new(text_widget, index, options) で生成
                        image 指定は options で :image=>image を指定

      TkTextMark      : テキストマーク (後述) の位置

      TkTextTag#first : テキストタグ (後述) の先頭位置
      TkTextTag#last  : テキストタグ (後述) の末尾位置

       [ 修飾 ] (複数指定が可能)
      + 数 chars : 修飾対象から <数> 文字分だけ後方 (スペースなしも可)
      - 数 chars : 修飾対象から <数> 文字分だけ前方 (スペースなしも可)
      + 数 lines : 修飾対象から <数> 行分だけ後方 (スペースなしも可)
      - 数 lines : 修飾対象から <数> 行分だけ前方 (スペースなしも可)
      linestart  : 修飾対象が位置する行の先頭
      lineend    : 修飾対象が位置する行の末尾
      wordstart  : 修飾対象が位置する単語の先頭
      wordend    : 修飾対象が位置する単語の末尾

      (修飾指定例)
         例えば w を TkTextWindow オブジェクトとするとき,
         ・ Array --> リストの変換を利用する方法
              [ w, '+2lines', '+10chars', 'wordstart' ] など.

              この場合は,'+ 2 lines' などとスペースを入れると,
              "<wのパス> {+ 2 lines} +10chars wordstart" のように
              変換されてしまうため,エラーとなる

         ・ TkObject#path メソッドを使って自分で展開する方法
              "#{w.path} + 2 lines + 10 chars wordstart" など.

   TkText#insert(index, text, tag, ...) : テキスト挿入
   TkText#delete(index)                 : 1 文字消去
   TkText#delete(first, last)           : 指定範囲の文字列消去
   TkText#clear                         : 全テキストの消去
   TkText#get(index)                    : 指定位置の文字を得る
   TkText#get(first, last)              : 指定範囲の文字列を得る
   TkText#value                         : 全テキストを得る
   TkText#value=(value)                 : 全テキストを一度に設定 (置き換え)

     ( 埋め込みウィンドウやイメージは1文字として扱われる )

   指定項目が表示範囲内になるようにする
     TkText#see(index)


複合ウィジェットのクラス化
   scroll_listbox.rb :: スクロールバー付きのリストボックス
                                 ↓
                        クラス化したいとしたら?

     他の一般のウィジェットクラスのように引数を解釈して... --> 面倒!!

   TkComposite モジュール == 複合ウィジェット定義を助けるモジュール
     include することで initialize の再定義やいくつかのメソッド定義が行われる

   initialize_composite メソッドの中で複合ウィジェットを構築
     ・new メソッドに渡された引数の内,親ウィジェット指定を
       取り除いたのもがそのまま引数として渡される.

     ・引数のオプション指定 Hash を操作する際は,_symbolkey2str で変換の後に
       操作するのが安全.

     ・土台となるフレームウィジェットが @frame に生成,設定されているので,
       この上に複合ウィジェットを構成するすべてのウィジェットを構築する

     ・@epath には pack などでの配置の対象となるウィジェットのパスを設定する.
       @frame.path に初期設定されているので通常は変更の必要はない.

     ・@path には複合ウィジェットが参照される際の中心となるウィジェットの
       パスを設定する (初期設定は @frame.path).
       複合ウィジェットを親とする場合や複合ウィジェットを pack などでの配置
       の土台とするような場合を考えて設定すること.

   複合ウィジェットクラスの親クラスは,通常は中心となるウィジェットのクラス.
   適当なクラスがない場合は TkWindow を親クラスにする.

   属性操作の設定を助けるメソッド
     delegate(属性, ウィジェット, ... )
       <属性> についての configure が呼ばれた時には,同じ属性設定が
       引数として与えたウィジェットのすべてに適用されるように設定する.

       <属性> を 'DEFAULT' としたものは,他の delegate 設定などで適用
       できるものがなかった場合に採用される.
          --> 通常は複合ウィジェットの中心となるウィジェットを指定

     delegate_alias(エイリアス名, 属性, ウィジェット, ... )
       <エイリアス名> についての configure が呼ばれた時には,
       <属性> の設定が呼ばれたものとして,引数として与えたウィジェットの
       すべてに適用されるように設定する.
       同じエイリアス名で widget1 には <属性1> で widget2 には <属性2> で
       適用したいというようなケースでは,
         delegate_alias(エイリアス名, 属性1, widget1)
         delegate_alias(エイリアス名, 属性2, widget21)
       というように分けて設定する.

     option_methods(メソッド設定, ... )
       単純な属性設定ではなく,メソッド呼び出しによる処理が必要なオプションを
       設定したい場合に用いる.
       メソッド設定が
         method のとき
            属性名は method に等しい.
            configure 時には method(value),cget 時には method() が呼ばれる.
            configinfo 時には [属性名, '', '', '', method()] が返される

         [m_set, m_cget] のとき
            属性名は m_get に等しい.
            configure 時には m_set(value),cget 時には m_cget() が呼ばれる.
            configinfo 時には [属性名, '', '', '', m_cget()] が返される

         [m_set, m_cget, m_info] のとき
            属性名は m_get に等しい.
            configure 時には m_set(value),cget 時には m_cget() が呼ばれる.
            configinfo 時には m_info() の値が返される

   属性操作の設定がすべて完了した後 (initialize_composite の最後など) に,
   configure(keys) ( ただし keys は new メソッドに与えられたオプション指定
   の Hash ) を呼ぶようにする

   scr_lbox_cls.rb


課題:スクロールバー付きテキストフレームクラス
   wrap 属性を char に設定して,縦方向だけのスクロールで良いものとする.
     クラス化していないスクロールバー付きテキストの例: textframe0.rb
     作例: textframe1.rb


Tk.update の必要性
   スクロールバー付きテキストフレームクラスの作例 (textframe1.rb) を
   panedwindow に配置してみる

     textframe1b.rb  --->  失敗??? ペインの大きさが 0 に近くなる!!

   原因の調査
     ウィンドウやウィジェットに関する情報を得るための TkWinfo モジュールの
     メソッドを使って,ウィジェットが要求している大きさを調べてみる
       -->  textframe1c.rb  -->  @frame の要求高さが 2 になっている!!

   panedwindow への配置時点で @frame が内容物の大きさを反映していないのが原因

   ウィジェットの表示変化は idletask 時
     --> Tk.update_idletasks で強制的に idletask を動してみる (textframe1d.rb)
           -->  @frame の要求高さが idletask 後に適切に変化

   ウィンドウサイズを調べるなど,ウィジェットの表示状態が直前までに確定
   している必要がある場合は update を忘れないように注意!

   完成形 : textframe2.rb


テキストタグ ( TkTextTag クラス )
   テキストに属性を与えるためのもの.それゆえ,オブジェクトを生成する際の
   親ウィジェットはタグを設定するテキストウィジェットでなければならない.

   表示テキストの一部を範囲指定して割り当てる.
   タグの属性を操作したりタグにバインディングを設定することで,
   色やフォントなどの表示を変えたりイベントに反応したりさせることが可能.

   テキストを挿入 (TkText#insert) の際の同時指定か,TkTextTag#add で付与.
   TkTextTag#remove([範囲指定]) で除去.

   タグが重複した場合は両方の特性が適用される
   後から設定したタグの方が優先順位が高い ( TkTextTag#raise や lower で変更可 )

   タグの属性設定/操作
     TkTextTag#cget                or  TkText#tag_cget(tag, ...)
     TkTextTag#configure           or  TkText#tag_configure(tag, ...)
     TkTextTag#configinfo          or  TkText#tag_configinfo(tag, ...)
     TkTextTag#current_configinfo  or  TkText#current_tag_configinfo(tag, ...)
     TkTextTag#[]
     TkTextTag#[]=


   タグのバインディング設定/操作
     TkTextTag#bind         or TkText#tag_bind(tag, ...)
     TkTextTag#bind_append  or  TkText#tag_bind_append(tag, ...)
     TkTextTag#bind_remove  or  TkText#tag_bind_remove(tag, ...)
     TkTextTag#bindinfo     or  TkText#tag_bindinfo(tag, ...)

   TkTextTag#nextrange や prevrange を用いることで割り当て範囲を獲得可能
     nextrange(index1 [, index2])
       タグ範囲の先頭が index1 よりも後 (index2 より前) にあるものの内で,
       最初のもののインデックス範囲 (先頭と末尾のインデックスの配列) を返す.

     prevrange(index1 [, index2])
       タグ範囲の先頭が index1 よりも前 (index2 より後) にあるものの内で,
       最後尾のもののインデックス範囲 (先頭と末尾のインデックスの配列) を返す.

     ranges
       タグが設定されているインデックス範囲のすべてを配列で返す.

   texttag_sample.rb
     挿入等でテキストが変化しても,タグ設定がテキストの移動に追随することに注意


テキスト埋め込みのウィンドウとイメージ ( TkTextWindow / TkTextImage クラス )
   親ウィジェットはタグを設定するテキストウィジェットでなければならない

   テキストウィジェットのインデックス上は1文字として扱われる

   埋め込みオブジェクトの生成
     TkTextWindow.new(テキスト,位置,オプション)
       埋め込むウィジェットは :window=>ウィジェット というオプションで指定

     TkTextImage.new(テキスト,位置,オプション)
       埋め込むイメージは :image=>イメージオブジェクト というオプションで指定

   埋め込みオブジェクトの属性設定/操作
     (TkTextWindow|TkTextImage)#cget
     (TkTextWindow|TkTextImage)#configure
     (TkTextWindow|TkTextImage)#configinfo
     (TkTextWindow|TkTextImage)#current_configinfo
     (TkTextWindow|TkTextImage)#[]
     (TkTextWindow|TkTextImage)#[]=

     TkText#window_cget(obj, ...)
     TkText#window_configure(obj, ...)
     TkText#window_configinfo(obj, ...)
     TkText#current_window_configinfo(obj, ...)

     TkText#image_cget(obj, ...)
     TkText#image_configure(obj, ...)
     TkText#image_configinfo(obj, ...)
     TkText#current_image_configinfo(obj, ...)


テキストマーク ( TkTextMark クラス )
   テキスト上の位置を管理するためのもの.それゆえ,オブジェクトを生成する際の
   親ウィジェットはマークを設定するテキストウィジェットでなければならない.

   テキストの変更に追随する (例えば 3 行目の先頭にマークを付けた後に 1 行目を
   消去した場合,その変更に合わせてマーク位置は 2 行目の先頭となる) ため,
   位置管理が容易になる.

   TkTextMark#set(index) で設定.TkTextMark#unset で除去.

   TkTextMark#gravity=(dir) ( dir は right or left ) で,マークが設定位置の
   左右どちら寄りに結び付いているかを指定する.
   この違いは,マーク設定位置に文字列を挿入した場合に,マーク位置が新しい文
   字列の前後どちらに位置するかを決定付ける.

   カーソル位置の管理には TkTextMarkInsert クラスのオブジェクトを用いる.
   あるいは TkText#mark_set('insert', index) としてもよい.
   このマークを設定することでカーソル位置を指定の場所に移動することができる.


課題:コマンド実行ウィンドウ
       ・エントリボックスにコマンド入力.Return かボタンクリックで実行.
             ヒント: `#{エントリの中身}`

       ・結果は縦スクロール付きのテキストウィジェットで表示.

       ・テキストウィジェットは表示用であり,ユーザに変更して欲しくはない.
             ヒント: bindtags
                     takefocus 属性
                       フォーカスが得られない == キー入力できない

       ・実行したコマンドライン部分は青色で,ダブルクリックで再実行.
             ヒント: TkTextTag
                     シングルクリックで入力用エントリボックスにコピー
                     ダブルクリックでエントリボックス内容を実行

        作例: cmdex_win.rb


メニューとメニュースペック
   メニューバーの作成は,メニュースペックで定義したものをルートウィジェットや
   トップレベルウィジェットの add_menubar メソッドで与えるのが簡単

   メニュースペック : メニュー構造を配列で指定したもの
      menuspec ::= [menuinfo, menuinfo, ...]
      menuinfo ::= [btninfo, iteminfo, iteminfo, ...]
      btninfo  ::= [text, underline, configs]
      iteminfo ::= cmd_ent | check, | radio | cascade | separator
      cmd_ent  ::= [label, command, underline, accelerator, configs]
      check    ::= [label, TkVar_obj, underline, accelerator, configs]
      radio    ::= [label, [TkVar_obj,value], underline, accelerator, configs]
      cascade  ::= [label, [menuinfo, ...], underline, accelerator, configs]
      separator::= '---'

   underline : キーボードトラバーサルを定義するための文字の位置

   accelerator : メニュー項目の右端に表示する文字列
                 一般的にはキーボードショートカットを表示したりするために利用
                 ( ショートカットの binding が自動的になされるわけではない )

   configs : メニュー項目の属性を定義する Hash

   メニュー項目の属性設定/操作のためのメソッド
      TkMenu#entrycget
      TkMenu#entryconfigure
      TkMenu#entryconfiginfo
      TkMenu#current_entryconfiginfo

   menubar1.rb, menubar2.rb ( Ruby ソースアーカイブに含まれるもののコピー )
   ( 1.8.2 リリース版ではメニューバー以外の一般のメニューもメニュースペック
     から作成可能となる予定.)


組み込みダイアログ
   Tk.messageBox  : tk_messageBox コマンドの wrapper
     メッセージを表示して押されたボタンの情報を得るような組み込みダイアログ
       アイコン : error, info, question, warning のいずれか
       ボタン構成 : abortretryignore, ok, okcancel,
                    retrycancel, yesno, yesnocancel のいずれか

   Tk.getOpenFile : tk_getOpenFile コマンドの wrapper
     開くためのファイル選択する場合に合わせた組み込みダイアログ
     存在していないファイルを指定した場合はエラーメッセージを表示

   Tk.getSaveFile : tk_getSaveFile コマンドの wrapper
     保存するファイル選択する場合に合わせた組み込みダイアログ
     既に存在するファイルを指定した場合は上書き確認ダイアログを表示

   Tk.chooseDirectory : tk_chooseDirectory コマンドの wrapper
     ディレクトリを選択する場合に合わせた組み込みダイアログ

   Tk.chooseColor : tk_chooseColor コマンドの wrapper
     色に指定に合わせた組み込みダイアログ

   msgbox_test.rb, getfile_test.rb, choosedir_test.rb, choosecol_test.rb


TkDialog : 定型ダイアログの作成
   Tk.messageBox よりももう少し高い自由度でダイアログを作りたい場合に使用

   属性は以下のメソッドを再定義して必要な情報を返すようにするか,
   new の際に同名オプション (default_button のみ :default) で指定する.
     title           : ダイアログのタイトル文字列
     message         : ダイアログに表示するメッセージ文字列
     message_config  : メッセージ文字列のオプションを指定する Hash
     msgframe_config : メッセージ文字列のフレームのオプションを指定する Hash
     bitmap          : 表示するビットマップ
     bitmap_config   : ビットマップのオプションを指定する Hash
     default_button  : デフォルトのボタンの番号か名前 (nil なら設定なし)
     buttons         : 表示するボタンのラベルの配列
     btnframe_config : ボタン群のフレームのオプションを指定する Hash
     button_configs  : ボタンごとのオプションを設定するための情報を得るための
                       Proc/Array/Hash のいずれか (nil なら設定なし)
     prev_command    : ダイアログ表示直前に実行する手続き

   (例) dialog_sample.rb


課題: Ruby/Tk スクリプトの実行テストウィンドウ
         テキストウィジェットにソースを読み込み,必要に応じてエディットして
         system('ruby', '-e', <テキストウィジェットの中身>) で実行.

         メニュー項目は Open, Save, Save as, Exec, Quit

         modified? で保存の必要性をチェックする.

         起動したプロセスを強制停止するボタンも付けたいところだが,
         それには process ID を知る必要がある.
         fork で起動するようにすれば process ID を知ることができるが,
         Windows では fork が使えないため,ここでは強制停止ボタンは諦める.

         ちなみに fork を使う場合は pid = fork{exec('ruby','-e',...)} とする.

         Ruby 1.9 feature の spawn なら,起動したコマンドの process ID
         を得られるので,Process.kill(:KILL, pid) が多分可能.

    作例: tktestwindow.rb


ウィンドウマネージャとの連携
   Tk::Wm モジュール
     ウィンドウマネージャに指示を出したり情報を得たりするためのモジュール
     TkRoot, TkToplevel に include 済み

   メソッド例
     title
       ( title_test.rb )
       ウィンドウのタイトル文字列の指定
       ウィジェット生成時の title 属性でも与えることができる

     withdraw, iconify, deiconify, overrideredirect
       ( iconify_test.rb )
       ウィンドウの表示/非表示/アイコン化といった見掛けの制御
         withdraw  : 非表示を指示
         iconify   : アイコン化を指示
         deiconify : 表示を指示

         overrideredirect
           ウィンドウに枠を付ける (ウィンドウマネージャに無視させる) かどうか.
           true なら枠なし,false なら枠付き.引数なしなら現在の真偽値を返す.
           変更が適用されるのは,表示状態が切り替わった時.
             ==> withdraw → overrideredirect変更 → deiconify が通常の流れ


     geometry, maxsize, minsize, resizable
       ( geometry_test1.rb, geometry_test2.rb )
       ウィンドウの配置/大きさの制御
         geometry
           ウィンドウのサイズと位置を文字列で参照または指定
             引数なし
                geometry 文字列を返す

             "<X>x<Y>"
                大きさだけの指定 (横 <X> pixel, 縦 <Y> pixel)
                (例) "300x200"

             "(+|-)<dX>(+|-)<dY>"
                位置だけの指定
                  +<X> : 画面左から <dX> pixel
                  -<X> : 画面右から <dX> pixel
                  +<Y> : 画面上から <dY> pixel
                  -<Y> : 画面下から <dY> pixel
                (例) "+200-25"

             "<X>x<Y>(+|-)<dX>(+|-)<dY>"
                大きさと位置との同時指定
                (例) "300x200+200-25"

         maxsize(x,y), minsize(x,y)
           ウィンドウが変更を許す最大サイズと最小サイズを指定する.
           引数なしなら現在の値を [x, y] の配列で返す.

         resizable(x_mode, y_mode)
           ウィンドウの縦横それぞれの大きさをユーザが変更可能かを指定する.
           引数なしなら現在の真偽値を [x_mode, y_mode] の配列で返す.

     protocol(プロトコル, 手続き)
       ( wm_protocol_test.rb )
       ウィンドウマネージャ送られてくるプロトコルに対する動作を定義する

       プロトコルの種類
         :WM_DELETE_WINDOW :: ウィンドウの消去を要求
         :WM_TAKE_FOCUS    :: 入力フォーカスの割り当て
         :WM_SAVE_YOURSELF :: 状態保存を要求


課題:終了時に確認ダイアログを表示する
         元にするのは quit1.rb 程度のもので構わない.
         終了させるような操作に対して確認を取ってから終了するようにすること.

   作例: quit2.rb : 終了ボタンが押されたときにダイアログ表示
         quit3.rb : さらに,ウィンドウを強制的に閉じようとしたときも表示
         quit4.rb : さらに,キーボード割り込み (SIGINT) の際にも表示


フォーカスとグラブ
   フォーカス設定
     どのウィジェットがキーボードイベントを受け取るかの設定.
     適切に設定することで GUI の操作性が向上する.
     target.focus で設定する.

       focus.rb
         フォーカス設定を行っていない例.
         「その他」に入力する前にエントリボックスの選択が必要.

       focus1.rb
         フォーカス設定を行った例
         「その他」のラジオボタンを押したときもエントリにフォーカスを設定する

       ---------------------------------------------------------------------
         [ 付録 ]
        focus2.rb
          「その他」項目付きラジオボタンボックスとしてクラス化した例

        focus2b.rb
          ラジオボタン選択時に値を返すべきオブジェクトを変更するようにした例

        fucus3.rb
          親ウィジェット指定に他のウィジェットと同様の自由度を与えるため,
          create_self メソッドの定義で構築するようにした例

        focus4.rb
          TkComposite モジュールを使ってクラス化した例
       ---------------------------------------------------------------------

   グラブ設定
     グラブを設定したウィジェットとその子孫のウィジェット以外の操作を禁止する.
        -->  期待しない操作が行われてしまうことを避ける

             例: パラメータ指定のダイアログを開いている状態 (値が未確定) で
                 パラメータを必要とするような操作が行われてしまうのを禁止する

     グラブ設定に関するメソッド
       target.grab          : ローカルグラブの設定
                              同一アプリケーションの上だけでの操作禁止

       target.grab(:global) : グローバルグラブの設定
                              ウィンドウ全体での操作禁止
                                  ==> かなり危険!! 利用は慎重に!!

       target.grab_release  : グラブの解放

       target.grab_current  : target と同じウィンドウ上で,
                              現時点でグラブが設定されているウィジェットを返す

       target.grab_status   : target のグラブ設定状況を 'none', 'local',
                              'global' で返す

     グラブの操作 :: 重要なのはグラブ解放のタイミング
       ・ウィンドウの破壊を待って解放
           ダイアログのようなモーダルウィンドウの場合に有効な手段

           win.wait_destroy を実行 == win が destroy されるまで待つ

           グラブが不要になった時点で wint を破壊
              ==>  win.grab → win.wait_destroy → win.grab_release という流れ

           例: grab1.rb
             ( grab1b.rb : ウィンドウを構成する一部のウィジェットに設定した例 )

       ・TkVariable への値書き込みを待って解放
           ウィジェットを破壊したくない (再利用したいなど) 場合に有効な手段

           TkVariable#wait を実行 == 変数への書き込みが行われるまで停止

           グラブが不要になった時点で var への値設定 (書き込み)
              ==>  win.grab → var.wait → win.grab_release という流れ

           例: grab2.rb

       ・その他 :: 確実に解放タイミングを捉えることができる方法を考えること!!


キャンバスウィジェット ( TkCanvas クラス )
   図形表示を主目的とした高機能ウィジェット
     ==>  座標で管理するジオメトリマネージャ

   図形 (文字列を含む),イメージ,ウィジェットのすべて (アイテム) が表示可能

   図形も含めて表示されるものはオブジェクト
     ==> 属性を操作したり,バインディングを行ったりすることも可能
         (例) 木構造を描いて,その枝にバインディングを設定する

   キャンバスタグ (後述) を設定し,グループとして操作することも可能

   キャンバスウィジェットの表示内容を Postscript 形式で出力することが可能

   スクロール範囲の決定
     キャンバスのサイズには基本的には制限なし.
     スクロール範囲は :scrollregion => [min_x, min_y, max_x, max_y] で決定.

   操作のためのメソッド例
     bbox(アイテム, ...)
       指定したアイテムすべてが収まるような bounding box の座標を返す

     canvasx(screen_x), canvasy(screen_y)
       スクリーンでの座標値をキャンバス上での座標値に変換する

     find_above(arg), find_all(), find_below(arg), find_closest(*args),
     find_enclosed(*args), find_overlapping(*args), find_withtag(arg)
       指定条件を満たすアイテムの集合を検索する

     postscript(keys)
       Postscript 形式で出力する


   ( 付録 : scrolled_canvas.rb )


キャンバスアイテムとキャンバスタグ
   キャンバスアイテム : 図形などの表示要素
   キャンバスタグ     : TkCanvas#addtag メソッドなどでアイテムに割り当てるタグ

     --> 操作する上ではどちらもほぼ同じに扱うことができる

   キャンバスアイテム
      <キャンバスアイテムクラス>.new(キャンバス, 座標, ... , オプション)

      座標指定は
       ・ x0, y0, x1, y1, ....
       ・ [ x0, y0, x1, y1, .... ]
       ・ [x0, y0], [x1, y1], ....
       ・ [ [x0, y0], [x1, y1], .... ]
      のいずれでも可能

      キャンバスアイテム ( TkcItem のサブクラス ) の種類
        TkcArc       : 円弧
        TkcBitmap    : ビットマップ
        TkcImage     : イメージ
        TkcLine      : 線
        TkcOval      : 楕円
        TkcPolygon   : 多角形
        TkcRectangle : 矩形
        TkcText      : テキスト
        TkcWindow    : 埋め込みウィンドウ

   キャンバスタグ ( TkcTag クラス )
     TkcTag.new(キャンバス) で生成

     ( キャンバスウィジェットのメソッドでタグを指定する場合は文字列も可 )

     特殊なキャンバスタグクラス
       TkcTagAll     : すべてのアイテムを指すタグ (文字列指定の場合は 'all')
       TkcTagCurrent : マウスイベントやキーボードイベントが発生したアイテムを
                       指すタグ (文字列指定の場合は 'current')

       TkcGroup : タグの集合として扱うために include / exclude メソッドが
                  追加されたキャンバスタグクラス

     キャンバスタグの割り当て
       TkcTag#set_to_above(arg)
       TkcTag#set_to_all
       TkcTag#set_to_below(arg)
       TkcTag#set_to_closest(*args)
       TkcTag#set_to_enclosed(*args)
       TkcTag#set_to_overlapping(*args)
       TkcTag#set_to_withtag(arg)

     タグが割り当てられたアイテムの検索
       TkcTag#find

     タグの論理演算 --> 新しいキャンバスタグが返される
       tag & tag, tag | tag, tag ^ tag, -tag

   キャンバスアイテム/キャンバスタグの属性操作
     TkCanvas#itemcget(item, ...)
     TkCanvas#itemconfigure(item, ...)
     TkCanvas#itemconfiginfo(item, ...)
     TkCanvas#current_itemconfiginfo(item, ...)

     (TkcItem|TkcTag)#cget
     (TkcItem|TkcTag)#configure
     (TkcItem|TkcTag)#configinfo
     (TkcItem|TkcTag)#current_configinfo
     (TkcItem|TkcTag)#[]
     (TkcItem|TkcTag)#[]=

   キャンバスアイテム/キャンバスタグのバインディング
     TkCanvas#itembind(item, ...)
     TkCanvas#itembind_append(item, ...)
     TkCanvas#itembind_remove(item, ...)
     TkCanvas#itembindinfo(item, ...)

     (TkcItem|TkcTag)#bind
     (TkcItem|TkcTag)#bind_append
     (TkcItem|TkcTag)#bind_remove
     (TkcItem|TkcTag)#bindinfo

     マウスイベントやキーボードイベントが発生した際には,
     対象アイテムに 'current' タグ (TkcTagCurrent) が加えられるので,
     TkcTagCurrent#find や TkCanvas#find_withtag('current') で
     対象アイテムを獲得できる.
     ( 検索結果は配列で返されるため,その唯一の要素が対象アイテム )

   キャンバスアイテム/キャンバスタグの操作
     TkCanvas#itemfocus(item)
     (TkcItem|TkcTag)#focus
        フォーカス割り当て

     TkCanvas#coords(item, ...)
     (TkcItem|TkcTag)#coords(...)
       アイテムの座標定義を変更する

     TkCanvas#move(item, ...)
     (TkcItem|TkcTag)#coords(...)
       アイテムを移動する

     TkCanvas#raise(item, ...),   TkCanvas#lower(item, ...)
     (TkcItem|TkcTag)#raise(...), (TkcItem|TkcTag)#lower(...)
       アイテムの重なり順序を変更する

     TkCanvas#scale(item, ...)
     (TkcItem|TkcTag)#scale(...)
       図形の拡大/縮小を行う


   (例) canvas_sample.rb


課題:スクロール可能なツールバー (TkCanvas)
         アイコンイメージデータの用意が面倒なので通常のボタンで代用する.
          ( アイコンイメージが用意できたときは,:image 属性に設定すればよい.)

         ボタンを置いたフレームをウィンドウアイテムとして埋め込み
          ( TkcWindow.new(canvas, 座標,  :window=>widget, :anchor=>pos) ),
         そのフレーム範囲でキャンバスをスクロールさせる.

         スクロール範囲は bbox メソッドで得る.
         スクロール範囲を得る前に Tk.update を忘れないようにすること.

         スクロールには,TkCanvas#xview_scroll(num, 'unit') を使うものとする.
          ( num ユニット分だけ横方向にスクロールする.)
         1ユニットがどれだけの量かは xscrollincrement 属性で与える.

         両端にはスクロールタブを用意してスクロールさせる.
          (1) ボタンのリピート属性を利用して押している間スクロール
          (2) TkTimer を使って,マウスカーソルがタブ上にある間スクロール


        作例: (1) scr_toolbar1.rb
              (2) scr_toolbar2.rb

              scr_toolbar1b.rb, scr_toobar2b.rb
                scr_toolbar1.rb, scr_toobar2.rb のそれぞれで,スクロールタブの
                状態が変化するようにしたもの


課題:画像データへのメモ記入ツール
       ( 他に比べると難しい課題であるので,時間をかけて取り組む必要がある.)

       ・画像データを表示する.
       ・丸囲み,矢印引き,メモテキスト入力を可能とする.
       ・左ボタンクリックごとに
           <click>丸中心<click>丸サイズ,矢印起点<click>矢印終点,テキスト入力
         というように状態を進める.
       ・テキストアイテムの文字列をテキストファイル出力を可能にする.
       ・書き込みをしたキャンバスの Postscript 出力を可能にする.

      作例: figmemo_sample.rb

    # 更なる応用として,矢印など記入した図形の属性情報を取り出してファイルに
    # 保存しておき,それを読み込んで元の状態を再現することもできるので,挑戦
    # してみるのもよいだろう.


place ジオメトリマネージャ
   部分的に重なるようにする場合にはほぼ必須
   ( grid で同じセルに配置するという手も使えるが,やや制約がある )

   絶対位置/サイズや土台となるウィンドウに基づく絶対位置/サイズで指定

   (例) place_test.rb


課題:ラベル付きフレーム (TkComposite + place)
        新しい Tcl/Tk ではラベル付きフレームは実装済 ( TkLabelFrame ) だが,
        place の勉強のために類似したものを作成してみる.

        作例: labelframe.rb


Tcl/Tk 拡張について
   基本の Tcl/Tk は機能を絞り込んでいるため,それを補う意味で世の中には
   多数の Tcl/Tk 拡張が存在.

   Ruby/Tk の機構上,リンクした Tcl/Tk で使える拡張は基本的にはすべて
   Ruby/Tk からも利用可能.

   Ruby 1.8.2 では一部の代表的な Tcl/Tk 拡張の wrapper ライブラリを同梱
     --> {Rubyのライブラリディレクトリ]/tkextlib の下にインストールされる

   {Rubyのライブラリディレクトリ]/tkextlib/pkg_checker.rb の実行で,
   現環境ですでに使える状態にあるものの確認が可能
     --> 「インストールされているがパスが設定されていない」などの場合は
         {Rubyのライブラリディレクトリ]/tkextlib の下にある setup.rb
         ( 特定の拡張に関してのみなら tkextlib/<対象とする拡張>/setup.rb )
         に必要な処理を記述する

   wrapper ライブラリがないものは使えない? --> NO!!!
     --> Tk.tk_call などで簡単にコントロール可能.
         頻繁に使うならクラス化/モジュール化をすればよい.

         Ruby のソース配布に含まれる ext/tk/sample/tktree.rb の例も参照.
         同ディレクトリに置かれている Tcl/Tk スクリプトの tktree.tcl を
         wrap して Ruby/Tk のクラスとして使えるようにしている.


課題:Tcl/Tk 拡張の利用
         以前の課題で作成した「 Ruby/Tk スクリプトの実行テストウィンドウ」と,
         BWidget 拡張に含まれる NoteBoook ウィジェットとを使って,
         タブ形式で複数ファイルを扱えるような実行テストウィンドウを作る.

         BWidget 拡張が利用可能になっていることが必要.
           --> ruby -r tk -e "p TkPackage.require('BWidget')" が
               バージョン情報を出力するなら OK

         BWidget の NoteBoook ウィジェット == Tk::BWidget::NoteBook

         インスタンスメソッド例
           insert(index, page, opts) : page の名で index 位置に新規ページを作成
           compute_size()  : 最大ページを表示できるように大きさを再計算
           get_page(page)  : 指定ページを得る.存在しなければ空文字列が返る
           get_frame(page) : 指定ページへの配置用の土台となるウィジェットを返す
           index(page)     : 指定ページのインデックスを得る
           delete(page)    : 指定ページを消去する
           raise(page)     : 指定ページを一番上に持ってくる
           see(page)       : 指定ページが表示されるようにラベルをスクロールする

   作例: tktestmultiwin.rb
           NoteBook#raise メソッドにバグが存在したため,
           作例では tk_send('raise') や tk_send('raise', page) を用いている.
           1.8.2 リリース版では修正されているはずであるので,その場合は
           それぞれ NoteBook#raise() と NoteBook#raise(page) を用いればよい.


=============================================================================

 [ 参考書籍 ]

拙書ではあるが,以下の書籍でも Ruby/Tk について解説している.
Ruby 1.6 のころのものであるため少々古くはあるが,まだ十分に有用と考える.

 ・「 Ruby アプリケーションプログラミング 」
       前田修吾,まつもとゆきひろ,やまだあきら,永井秀利
       ¥3,500(税別),2002/04,508 ページ,オーム社,ISBN: 4-274-06461-1

 ・「 Ruby を 256 倍使うための本 〜 界道編 」
       永井秀利
       ¥1,200(税別),2001/12,295 ページ,アスキー,ISBN: 4-7561-3993-0


=============================================================================

 [ 補足情報 ]

# Web 雑誌 Rubyist Magazine (http://jp.rubyist.net/magazine/) 0003 号の記事
# 「Ruby/Tk の動向」(http://jp.rubyist.net/magazine/?0003-RubyTkMovement) も
# 参照すること.


日本語の表示
   Tcl/Tk8.0 まで : 日本語対応パッチが必要.

   Tcl/Tk8.1 以降 : Tcl/Tk の内部コードは unicode に.
                       -->  unicode に変換することで表示可能.
                                       ↓
                      Ruby/Tk では環境に基づく推定結果に基づき自動で変換.
                      推定が誤った場合は Tk.encoding= で設定を.


多国語の表示
   Tcl/Tk8.0 まででは困難.

   Tcl/Tk8.1 以降で unicode に変換できるなら表示可能.
               ↓
     Ruby/Tk では Tk::EncodedString クラスを用意.
                           ↓
             @encoding にエンコード情報を持つ文字列
                           ↓
          エンコード情報に基づいて unicode に自動変換

   (例) Tk::EncodedString.new(src_str, 'iso2022-kr')
            あるいは  Tk::EncodedString(src_str, 'iso2022-kr')
        Tk::EncodedString.new(src_str, 'euc-jp')
            あるいは  Tk::EncodedString(src_str, 'euc-jp')    など

        Tk::UTF8_String.new(utf8_str) or Tk::UTF8_String(utf8_str)
            UTF-8 に設定された Tk::EncodedString のサブクラス

        Tk::BinaryString.new(bin_str) or Tk::BinaryString(bin_str)
            コード変換をしないように設定された Tk::EncodedString のサブクラス
              --> バイナリデータなど,Tcl/Tk インタープリタへの引き渡しの際に
                  文字コード変換が行われては困る場合に用いる.

        Ruby のソース配布に含まれる ext/tk/sample/encstr_usage.rb や
        ext/tk/sample/binstr_usage.rb も参照すること.


メッセージカタログ :: TkMsgCatalog クラス
   ロケールに対応した文字列出力を行う場合に用いる.

   Ruby のソース配布に含まれる

     ext/tk/sample/tkmsgcat-load_tk.rb
        -->  Tcl/Tk のメッセージカタログ定義 ext/tk/sample/msgs_tk を
             そのまま利用

     ext/tk/sample/tkmsgcat-load_rb.rb  :
        -->  unicode 文字列でのメッセージカタログ定義 ext/tk/sample/msgs_rb を
             利用 ( unicode 文字は \uhhhh 形式での置換を利用 )

     ext/tk/sample/tkmsgcat-load_rb2.rb :
        -->  エンコーディングを指定した定義 ext/tk/sample/msgs_rb2 を利用

   を参照.


リソースデータベース
   ウィジェットの属性などの情報をアプリケーション外部から指定する手段の一つ.

   Ruby/Tk では TkOptionDB モジュールによって扱う.

   Ruby のソース配布に含まれる ext/tk/sample/tkoptdb.rb を参照.


Tk::OptionObj
   フォントオブジェクトと同様に,複数ウィジェットの属性の同時操作を
   支援するためのオブジェクト

   Ruby のソース配布に含まれる ext/tk/sample/optobj_sample.rb を参照.
Last modified:2009/09/01 06:37:01
Keyword(s):[Ruby/Tk]
References:[Ruby/Tk講習会 at 都城(2007/09/27)] [Ruby/Tk講習会資料]