Ruby/Tk - FrontPage  Index  Search  Changes  PageRank  RSS  Login

RubyTk - TkVariable Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

!*** このページはまだ記述途中であり,未完成です ***

! 概要

Ruby/Tk (Tcl/Tk) におけるいく種かのウィジェットは,
動作時の何らかの状態を反映/参照するために
Tcl上の変数を割り当てることができます.
ウィジェット以外でも,
Tcl上で何らかの特別な役割をもって用いられる変数なども存在します.

Ruby/TkはTcl/Tkインタープリタをそのまま(無改造で)用いていますから,
Ruby上の変数とTcl上の変数とは異なる存在です.
そのため,Tcl上の変数にアクセスするためには仲立ちとなるものが
必要となります.
それが TkVariable オブジェクトです.

TkVariableオブジェクトは,
Tcl上の変数にアクセスするために用いられるオブジェクトです.
基本として,一つのTkVariableオブジェクトが,
一つのTcl変数に対応するようになっています.

TkVariableオブジェクトが扱うTcl上の変数名は自動的に決められます.
ですが,Tcl上の特定の名前の変数にアクセスしたい場合もあるでしょう.
その場合にはTkVariableクラスのサブクラスであるTkV'''''arAccessクラスを
使ってください.
TkV'''''arAccessオブジェクトを生成する際に,
操作対象となるTcl上の変数名を明示することができます.


! 用法

!! 最も基本となる使い方


Tclの変数には一般の変数(スカラー変数)と
連想配列変数(Rubyで言うならHashのようなもの)とがあります.
Ruby/Tkでの利用においては,多くの場合,スカラー変数で事足ります.

TkVariableオブジェクトでスカラー変数を扱う場合,
TkVariable.newメソッドでオブジェクトを生成するだけでお十分です.
本来はTcl上での変数名が必要なはずですが,
TkVariableクラスで適当な名前を組み立てますので気にしなくても構いません.

TkVariableクラスの最も基本となる使い方で用いられるメソッドは
次の四つです.
多くのRuby/Tkの解説では,これらの利用について説明していると思います.

:TkVariable.new(初期値 = "")
::初期値が与えられない場合,新たなTclのスカラー変数を扱うTkVariableオブジェクトを生成します.
::初期値(Hash以外)が与えられた場合は,その文字列表現がTclのスカラー変数の値となります.

:TkVariable#value
::TkVariableオブジェクトが対応するTclのスカラー変数の値を返します.
::変数の値は文字列です.

:TkVariable#value=(値)
:TkVariable#set_value(値)
::TkVariableオブジェクトが対応するTclのスカラー変数の値を設定します.設定される値は,引数で与えられた値の文字列表現です.
::TkVariable#value=メソッドの戻り値は引数の値,TkVariable#set_valueメソッドの戻り値はself(TkVariableオブジェクト)ですから,スクリプトの文脈に応じて使い分けるとよいでしょう.

最も基本の使い方において注意すべきは,
Tcl上ではすべてが文字列で管理されるという点です.
そのため,どのような値を変数に設定しても一旦は何らかの形で文字列化されてしまいます.
結果として,変数の値として得られるものは(変換支援機能を用いない場合)文字列オブジェクトとなります.

与えられた値 (Hashを除く) の文字列表現は,
Ruby/Tk における Ruby → Tcl の標準的変換ルールに従って作られます.
スカラー変数に設定する値にはHashオブジェクトを用いることはできません.
この変換ルールは別項で説明する予定ですが,
ここでは「ArrayオブジェクトはTclのリストに変換される」ということだけを
特例として記憶しておけば良いでしょう.
Tclのリストは要素の並びを扱うもので,
荒っぽいイメージとしてはRubyのArrayオブジェクトに近いと
考えてもらって構いません.

TkVariable.newでオブジェクトが作成された場合には
Tclインタープリタ上でも同時に定義がおこなわれるため,
通常は変数が存在しないという状態は生じません.
しかし,Tclインタープリタ上でunsetされた場合や,
TkV'''''arAccess.newで未定義変数に対応したオブジェクトを生成した場合などにより,
Tclインタープリタの上に変数が存在しないという状況が生じることもあります.
Tcl上に対応する変数が存在しているかどうかを確認したい場合には,
TkVariable#exist?メソッドを使ってください.
変数が存在していれば真を返します.


!! 変換参照メソッド群

基本的な使い方でのTkVariable#valueメソッドは
文字列を返すものでした.
しかしながら,例えば
数値を入力してもらうentryウィジェットの値は数値で欲しいでしょうし,
checkbuttonウィジェットの状態値は真偽値で欲しかったりするかもしれません.
もちろん,TkVariable#valueメソッドの戻り値を
明示的に変換してあげればいいわけですが,
Tclでは'0'も'no'も'off'も'false'も偽として扱われるなど,
変換が面倒な場合もあります (Tk'''''Commモジュールには
そうした変換を支援するメソッド群も用意されています).

そこでRuby/Tkでは指定したタイプのオブジェクトで
値を参照するようなメソッド群が用意してあります.
各タイプに対して
「タイプ名」による参照メソッドと「タイプ名=」による設定メソッドとを
定義しておくことにより,
例えば "var.numeric += 3" などというような代入演算子的な使い方もできるようにしてあります.
以下にそれらのメソッドを列挙しましょう.

:TkVariable#numeric/numeric=(値)/set_numeric(値)
::変数の値は数値(整数 or 浮動小数点数)であるものとして変換します.
::ピリオドを含まないなど,整数表現とみなせる限りは整数への変換を優先します.数値として不適切な文字列の場合は例外を発生します.

:TkVariable#bool/bool=(値)/set_bool(値)
::変数の値は真偽値であるものとして変換します.

:TkVariable#window/window=(値)/set_window(値)
::変数の値はウィジェットパスであるとして,ウィジェットオブジェクトに変換します.
::ウィジェットパスとして不適切だったり,そのパスで表されるウィジェットが存在しなかった場合は nil を返します.

:TkVariable#variable/variable=(値)/set_variable(値)
::変数の値はTclの変数の名前であるとして,TkVariableオブジェクトを返します.

:TkVariable#procedure/procedure=(値)/set_procedure(値)
::変数の値はTcl → Rubyのコールバック手続きであるとして,対応する手続きオブジェクトを返します.
::変数の値がコールバック手続きではない場合には,変数値そのままの文字列を返すか,Index'''''Errorの例外を生じます.

:TkVariable#string/string=(値)/set_string(値)
::変数の値は文字列であるとして返します.通常であればTkVariable#value/value=と同じです.
::TkVariableオブジェクトに特別な設定(「デフォルトタイプ」の項を参照)をした場合にはTkVariable#valueメソッドの値が文字列を返すとは限らなくなりますが,その場合でもこのメソッドを使えば強制的に文字列で値を得ることができます.

:TkVariable#symbol/symbol=(値)/set_symbol(値)
::変数の値の文字列をシンボルにして返します.

:TkVariable#list/list=(値)/set_list(値)
::変数の値はTclのリストであるとして,リストの各要素を要素とするような配列オブジェクトで返します.
::各リスト要素は文字列です.このメソッドでは1次元のリストだけをサポートします.多次元リストの場合,埋め込みリストは単なる文字列となりますから,必要であればTk[[|]]Comm.listやTk[[|]]Comm.simplelistなどのメソッドを活用するなどして変換してください.
:: (例) "1 2 {3 4} 5" → ["1", "2", "3 4", "5"]

:TkVariable#numlist/numlist=(値)/set_numlist(値)
::変数の値は数値の1次元リストであるとして,リストの各要素を数値(整数 or 浮動小数点数)化した配列オブジェクトで返します.
::リストの要素中に数値の文字列表現とみなせないものが出現した場合は例外を生じます.


!! デフォルトタイプ

タイプを指定しての参照メソッド群は,扱う形式(タイプ)を明示しているという意味で
良いものであると言えます.
多くの場合は,参照の際にRuby側が期待している値のクラスというのがあるでしょうから,
形式明示は難しいことではないはずです.
ですが,連想配列変数に対応したTkVariableオブジェクトにおいて
要素参照をTkVariable#[]メソッドで行いたい場合など,
タイプを明示しなくても,あるいは明示しなかったときは
自動的に特定のタイプで値を返してもらいたいという状況もあるでしょう.
そうした場合に備えて,
TkVariableオブジェクトは,
値を返す場合のデフォルトのタイプを保持できるようになっています.

:TkVariable#default_value_type=(タイプ)/set_default_value_type(タイプ)
::引数で指定したタイプをデフォルトで返す値のタイプとします.
::タイプには次のいずれかをシンボルまたは文字列で与えます.
::  numeric, bool, string, symbol, list, numlist, variable, window, procedure
::前述のタイプ名メソッドと同じですので,それぞれがどのようなタイプで値を返すかはわかると思います.逆に設定を無効化する場合は引数としてnilを与えてください.

:TkVariable#default_value_type
::現在設定されているタイプを返します.未設定時の値はnilです.

TkVariableの値の設定と同時に,デフォルトタイプを設定することも可能です.

:TkVariable#value_type=(値)
::引数で与えた値を設定すると同時に,その値のクラスをデフォルトタイプとする

:TkVariable#numeric_type=(値)/set_numeric_type(値)
:TkVariable#bool_type=(値)/set_bool_type(値)
:TkVariable#window_type=(値)/set_window_type(値)
:TkVarialbe#variable_type=(値)/set_variable_type(値)
:TkVariable#procedure_type=(値)/set_procedure_type(値)
:TkVariable#string_type=(値)/set_string_type(値)
:TkVariable#symbol_type=(値)/set_symbol_type(値)
:TkVariable#list_type=(値)/set_list_type(値)
:TkVariable#numlist_type=(値)/set_numlist_type(値)
::それぞれ"_type"なしのメソッドと同様に値を設定すると同時に,指定したタイプをデフォルトタイプに設定する.

なお,デフォルトタイプの設定が機能するのは,TkVariableオブジェクトの値を参照する場合のみです.
デフォルトタイプの設定とは異なる値を設定しようとしたとしても特にエラーを出すことはありません.


!! 演算子メソッド群とcoerce機構への対応

ここまでで見てきたように,
TkVariableオブジェクトの値を参照するには,通常,何らかの参照メソッドを使う必要があります.
それは,例えば「v = TkVariable.new」などとしている場合に
変数vに格納されているのは特別なオブジェクトであることを
意識しつづけねばならないということでもあります.
TkVariableオブジェクトはTcl上の「変数」でもあるわけですから,
このようなRubyの「変数」との間にあるギャップは小さい方が望ましいと言えるでしょう.

そこでTkVariableクラスでは,演算子メソッド群のサポートと
Rubyのcoerce(強制型変換)機構への対応が組みこまれています.

:TkVariable#+@/-@
::TkVariable#numericの値に対して単項演算子の"+"または"-"を適用したものになる.

:TkVariable#&(other)/|(other)
::otherが配列の場合は「self.list & other」または「self.list | other」を求める.
::他の場合は「self.numeric.to_i & other」または「self.numeric.to_i | other」を計算する.

:TkVariable#+(other)
::otherが配列の場合は「self.list + other」を求める.
::otherが文字列の場合は「self.string + other」を求める.
::otherが数値の場合は「self.numeric + other」を計算するが,その際,TkVariableの値が数値の文字列ではなくself.numericで例外を生じる場合には「self.string + other.to_s」を求める.

:TkVariable#-(other)
::otherが配列の場合は「self.list - other」を求める.
::他の場合は「self.numeric - other」を計算する.

:TkVariable#*(other)/%(other)
::「self.num_or_str * other」または「sefl.num_or_str % other」を計算する.

:TkVariable#/(other)/**(other)
::「self.numeric / other」または「self.numeric ** other」を計算する.

:TkVariable#=~(other)
::「self.string =~ other」を求める.

:TkVariable#==(other)
::otherのクラスに応じてタイプ変換した値で比較する.

:TkVariable#<=>(other)
::otherがNumericかArrayかそれ以外かに応じて,self.numericとの比較, self.listとの比較, self.stringとotherを文字列化したものとの比較を行う.


!! リスト操作

Tcl/TkのリストはRubyで言うならArrayに相当する位置づけのものと考えればよいでしょう.
リストは要素となる文字列を空白で繋げたもので,それ自体が一つの文字列でもあります.つまり,ある文字列を単なる文字列として扱うかリストとして扱うかはプログラマに任されていると言えます.

空白を含むリスト要素は,波括弧({})で囲むことで一つの文字列として含めることが可能です.前述のように,波括弧の中身を文字列として扱うこともリストとして扱うこともできますから,波括弧部分を単なる文字列として捉えれば1次元のリストになりますし,サブリストとして捉えれば多重リストということになります.


(以下,続く)



!! traceとwait

Rubyのグローバル変数には,traceメソッドを使って,変数の値を書き換えた際のフック処理を登録することができます.
Ruby/Tkにおいても,TkVariableオブジェクトにtraceを設定することが可能です.
Ruby自体の場合と異なる点は,
*グローバル変数である必要はない.
*書き込みだけではなく,値が読まれた場合にもフック処理を登録できる.読み書きで異なる処理を登録することも可能.
*通常の変数自体に設定するだけでなく,後述の連想配列の要素に対して個別に設定することも可能.
というようなところです.さらに,これと関連して,
*「変数に値が書き込まれるまで待つ」というwaitメソッドが使える.
ということも重要な点でしょう.


(以下,続く)


!! 連想配列変数

入門レベルのRuby/Tk利用ではスカラー変数のみの利用でも事足りるかもしれません.
しかし,Tk拡張ライブラリを含めてRuby/Tkを使い込んでいこうとすると,
連想配列変数の利用が不可欠となる場合があります.

ある名前のTcl変数がスカラー変数なのか連想配列変数なのかは最初に値を設定した時に決定されます.
一旦決定されてしまうと,後から変更することはできません.
変数がどちらのタイプなのかはプログラム作成者が管理しなければなりません.
Ruby/Tkでは,TkVariableオブジェクトとして生成される時点で
どちらのタイプの変数かが決定されます.

以前のTkVariable.newの説明ではスカラー変数を扱うものとして生成する場合だけに触れていましたが,
より厳密には次のようになります.

:TkVariable.new(初期値 = "")
::初期値が与えられない場合,新たなTclのスカラー変数を扱うTkVariableオブジェクトを生成します.
::初期値としてHashオブジェクト以外のものが与えられた場合,その文字列表現(Ruby → Tclの通常の変換ルールにより文字列化したもの)がTclのスカラー変数の初期値となります.
::初期値としてHashオブジェクトが与えられた場合,新たなTclの連想配列変数を扱うTkVariableオブジェクトを生成します.Tclの連想配列変数の初期値としてのキーと値とは,引数として与えられたHashオブジェクトのキーと値とをそれぞれ文字列表現したものとなります.与えられたのが空のHashオブジェクトであれば,生成される連想配列変数も空の連想配列となります.

また,連想配列変数を生成しようとしていることを明示したければ,次のメソッドも使えます.

:TkVariable.new_hash(初期値 = {})
::新たなTclの連想配列変数を扱うTkVariableオブジェクトを生成します.
::引数として与える初期値がHashオブジェクトではない場合,Argument'''''Errorの例外を生じます.

対照的にはTkVariable.new_scalarというメソッドがあっても良さそうではありますが,
ほとんど利用されそうにないので用意してはいません.

あるTkVariableオブジェクトがスカラー変数なのか連想配列変数なのかを判定する必要が生じた場合は,
次のメソッドを使ってください.

:TkVariable#is_hash?
::対応するTcl変数が連想配列変数である場合に真を返します.

:TkVariable#is_scalar?
::対応するTcl変数がスカラー変数である場合に真を返します.
::このメソッドは !is_hash? として定義されており,対応するTcl変数が未定義(「TkV'''''arAccess.new(変数名)」で生成され値が代入されていないなど)の場合にも真となります.

(以下,続く)