永井@知能.九工大です.

From: 里 佳史 <sato_yos / nifty.com>
Subject: [ruby-list:39668] Re: Ruby/TkでのIWidget::dateentryの利用
Date: Wed, 19 May 2004 00:06:54 +0900
Message-ID: <20040519000106.F2CE.SATO_YOS / nifty.com>
> Tk.tk_call()で生成するIwidgetのウィジェットの親ウィジェットとして、
> TkObejctのウィジェット(例えばTkFrame)を指定することはできるでしょうか?
> またその逆は可能でしょうか?

はい.もちろん可能です.

一つにはウィジェットパスを自分で管理する方法です.
ウィジェットは TkObject というよりは TkWindow のサブクラス
と考えた方が良いのですが,path メソッド (pack などのジオメ
トリマネージャに渡す時なら epath メソッド) を使うことでウィ
ジェットパス文字列を得ることができます.
ウィジェットパスはウィジェットの親子関係も示していますから,
例えば
---------------------------------------------------
f1 = TkFrame.new.pack
Tk.tk_call('iwidgets::dateentry', f1.path + '.de')
---------------------------------------------------
とすればいいということになります.
さらに,通常はウィジェット生成時にはそのウィジェットパスを
返すはずですから,
---------------------------------------------------
f1 = TkFrame.new.pack
dateent = TkComm.window(Tk.tk_call('iwidgets::dateentry', f1.path + '.de'))
---------------------------------------------------
などとすれば,(tk.rb にバグがなければ (^_^;) tk_call の結果
として帰ってくるウィジェットパス文字列に基づいて適当なクラス
(必要なら TkWindow のサブクラスを生成して) のインスタンスを
生成することができます (もちろん既知のパスならそれに対応する
オブジェクトが返されます).

なお,TkWindow オブジェクトを tk_call の引数に使う場合,オブ
ジェクトをそのまま引数として指定して構いません.ですので
---------------------------------------------------
dateent = TkComm.window(Tk.tk_call('iwidgets::dateentry', f1.path + '.de'))
Tk.tk_call('pack', deteent)
---------------------------------------------------
は可です (この例では本当は dateent.pack でいいのですが).

逆にウィジェットパスを明示して TkFrame のオブジェクトなどを
生成したい場合には,ウィジェット生成時に与えるオプションで
widgetname を使ってください.
また,オブジェクトとして生成したいウィジェットが Tcl/Tk 上で
既に生成されているものであるなら without_creating というオプ
ションを使ってください.
これらはオブジェクトの生成時に与える必要がありますから,必ず
new メソッドの引数として Hash で与えねばなりません.

例えば,親 x が TkWindow であるなら
---------------------------------------------------
f = TkFrame.new(x, :widgetname=>'.f').pack
---------------------------------------------------
とすることで (x のパス).f というパスを持った TkFrame オブジェ
クトを生成できます.
親 x が TkWindow でない場合には x を new メソッドの第1引数に
与えることができません.
その場合には,親を nil として widgetname にフルパスを与えてく
ださい.
---------------------------------------------------
f = TkFrame.new(nil, :widgetname=> <x のパス> + '.f').pack
---------------------------------------------------

以上でも十分に扱えるかと思いますが,同種のウィジェットをいくつ
も生成する必要があったり,ウィジェットを生成した後にそれをいろ
いろと操作する必要があったりするのなら,きちんとクラスを作った
方がいいかもしれません.

まじめにやるならクラス階層をどうするかなどのことをきちんと考え
ねばなりませんが,ちょっと使うだけなら簡単です.
ウィジェットの生成時にしか与えることができないオプションが存在
しないなら,例えば
---------------------------------------------------
class IwDateEntry < TkWindow
  def create_self
    tk_call('iwidgets::dateentry', @path)
  end
  private :create_self
end
---------------------------------------------------
で終わりです.
これで問題なく
---------------------------------------------------
f = TkFrame.new
dateent = IwDateEntry.new(f, <Hash によるオプション指定>).pack
---------------------------------------------------
などとできます.
ただしこの場合,オプション設定はウィジェット生成後に configure 
で行うため,生成の際に同時に指定しなければならないオプションに
は対応できません.その場合は少し面倒になりますが,
---------------------------------------------------
class IwDateEntry < TkWindow
  def create_self(keys)
  if keys and keys != None
    tk_call('iwidgets::dateentry', @path, *hash_kv(keys))
  else
    tk_call('iwidgets::dateentry', @path)
  end
  private :create_self
end
---------------------------------------------------
とでもしておけばいいでしょう.
さらに丁寧に winfo class で返されるウィジェットクラスにまで
対応するなら,
---------------------------------------------------
class IwDateEntry < TkWindow
  TkCommandNames = ['iwidget::dateentry'.freeze].freeze
  WidgetClassName = '<winfo class で返される文字列>'.freeze
  WidgetClassNames[WidgetClassName] = self

    ...

---------------------------------------------------
という記述を追加しておくと better です.

というわけで,元のスクリプトは
---------------------------------------------------
require 'tk'

TkPackage.require('Iwidgets', '4.0')

class IwDateEntry < TkWindow
  def create_self
    tk_call('iwidgets::dateentry', @path)
  end
  private :create_self
end

f1 = TkFrame.new.pack

IwDateEntry.new(f1).pack

Tk.mainloop
---------------------------------------------------
という感じではいかがでしょうか?
-- 
                                         永井 秀利 (九工大 知能情報)
                                             nagai / ai.kyutech.ac.jp