まつもと ゆきひろです

In message "[ruby-list:5609] Re: megawidgets"
    on 97/12/09, Kikutani Makoto <kikutani / jdc.ericsson.se> writes:

|きくたにです。

|> # 乗る人がいれば,TkCompositeの使い方を説明しますけど.
|
|お願いします。ぼちぼちでよろしければやってみたいと思います。

了解です.このメールは保存版ですね(長いです).

= Composite Widgetの作り方

== Composite Widgetとは

複数のウィンドウ部品がまとまってできているウィンドウ部品のこ
とです.Ruby/TkではTkCompositeモジュールを使うことによって比
較的簡単にComposite Widgetを作ることができます.

# Ruby/TkのComposite Widgetの作り方はSTkのSTklosが元になって
# います.

== 作り方

実例を見ながら作ってみましょう.Composite Widgetの実例として
TkScrollBoxを見てみます.これはScrollbarが付随したListboxに
なります.プログラムは以下の通りです.

     1	#
     2	#		tkscrollbox.rb - Tk Listbox with Scrollbar
     3	#                                 as an example of Composite Widget
     4	#			$Date: 1995/12/12 18:21:01 $
     5	#			by Yukihiro Matsumoto <matz / caelum.co.jp>
     6	
     7	require 'tk.rb'
     8	
     9	class TkScrollbox<TkListbox
    10	  include TkComposite
    11	  def initialize_composite
    12	    list = TkListbox.new(@frame)
    13	    scroll = TkScrollbar.new(@frame)
    14	    @path = list.path
    15	
    16	    list.configure 'yscroll', scroll.path+" set"
    17	    list.pack 'side'=>'left','fill'=>'both','expand'=>'yes'
    18	    scroll.configure 'command', list.path+" yview"
    19	    scroll.pack 'side'=>'right','fill'=>'y'
    20	
    21	    delegate('DEFAULT', list)
    22	    delegate('foreground', list)
    23	    delegate('background', list, scroll)
    24	    delegate('borderwidth', @frame)
    25	    delegate('relief', @frame)
    26	  end
    27	end

まず,tkモジュールをrequireして,それからクラスTkScrollbarを
定義しています(7,9行目).

TkScrollbarはTkListboxのサブクラスとして定義していますが,こ
れはTkScrollbarがTkListboxの振舞い(特にメソッド)を引き継ぎた
いからです.特にそのようなクラスが存在しない場合,TkWindowの
サブクラスにします.Tkクラスの構造の違いについて考える必要は
ありません(実は全てのTkクラスは同じ構造を持っています).

それから,TkCompositeモジュールをincludeします.これは
Compsite Widgetを定義する際に必須です.

次にinitialize_compositeメソッドを定義します.これは通常のク
ラスにおけるinitializeに相当する初期化メソッドです.なお,
Composite Widgetのクラスはinitializeを再定義してはいけません.

このinitialize_compositeメソッドではCompositeの内部部品を生
成し,配置します.このメソッドの中では,内部部品を囲むフレー
ムとしてインスタンス変数 @frame にTkFrameオブジェクトが与え
られていますから,それを使って生成と配置を行います(12〜19行
目まで).

14行目ではインスタンス変数 @path にlistのパスを設定していま
す.これは継承したTkListboxのメソッドを動作させるために必要
です.TkWindowクラス以外のクラスから継承したComposite Widget
を定義する場合にはこのようにして @path にそのクラスのオブジェ
クトのpathを代入しておく必要があります.

さて,次に「属性の委譲」を行います(21〜25行目).これは
configureなどで設定される属性をどのオブジェクトに投げるかを
設定します.例ではforegroundなどはlistに,borderwidthや
reliefは@frameに,backgroundはlistとscrollに,残りの属性
(DEFAULTで指定)はlistに委譲しています.

これでinitialize_compositeの定義は終りです.newへの引数は(親
を指定した第1引数以外は)initialize_compositeにも引き渡されま
すから,ここでconfigureを呼んでおいた方が良いかも知れません.

その場合は

  def initialize_composite(keys=nil)
    ...
    tk_send 'configure' *hash_kv(keys) if keys
  end

となるでしょう.

さて,例はここで終りですが,必要によってはそのクラスに特有の
メソッドを定義する必要があるでしょう.後で参照する必要のある
内部部品はインスタンス変数に代入してとっておきましょう.メソッ
ドの書き方はtk.rbの他のメソッドを参照して下さい.@path(基本
的なメソッドを引き受けるTkの実体)と@epath(外側を囲むTkの実体)
に気をつければそんなに難しくないはずです.

                                まつもと ゆきひろ /:|)