vieweditattachhistorytopchangessearchhelp

第77回


■SqueakではじめるSmalltalk入門   第77回  鷲見 正人


本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今回は、前回の最後にソースを提示しただけで終わってしまったメソッド「GuiBuilder >> #generateScriptOf:」の解説をします。念のため、次のリンク先に当該メソッドのソースコードを示しておきますので、前回のメールがお手元にない場合はこちらを参照してください。

http://squab.no-ip.com:8080/mosaren/70


▼ウィジェット生成用コードのカタログ化
すでに第75回の「扱うことができるウィジェットの種類を動的に得る」という項目で、カテゴリ'widget types'に分類してあるメソッド名の一覧を得る方法には触れました。

GuiBuilder allMethodsInCategory: 'widget types'


こうして得られたウィジェット生成のためのメソッド名(セレクタ)を手がかりにして、まず、それぞれに対応するメソッドの実体(すなわち、オブジェクトとしてのメソッド)を取得します。

Objective-Cも、クラスをはじめ意外なものがオブジェクトなので、慣れない当初はいろいろと驚かされた方も多いと思います。しかし、SmalltalkではObjective-Cの比ではない規模であらゆるものがオブジェクトとして表現されています。メソッドもその一つです。コンパイルされたメソッドはりっぱなオブジェクトで、メッセージを受けることができ、あらかじめ定められた「振る舞い」をします。

オブジェクトとしてのメソッドを得るには、クラスに対して「compiledMethodAt: selectorSymbol」というメッセージを送ります。

GuiBuilder compiledMethodAt: #field


Squeakでは、#compiledMethodAt:のエイリアス(別名)として#>>が定義されているので、次のようにメソッドを指すのに使う慣用的な表記を、そのままSmalltalk式として評価することも可能です。

GuiBuilder >> #field


さて。オブジェクトとしてのメソッド(a CompiledMethod)は、正体こそ単なるバイト列に過ぎないのですが、実にいろいろなことを知っています。どんなことを知っていて教えてくれるのかについては、CompiledMethodをbrowse it(cmd + B)してブラウザを開き、accessingとかliterals、source code managementといったカテゴリに属するメソッドを眺めてみるとよいでしょう。

たとえば、自らのソースコード中にどんなオブジェクトが現われたかという情報については、literalsというメッセージを送ることで引き出すことが可能です。#generateScriptOf:メソッドでは、ウィジェット生成用メソッドのソースに現われたオブジェクトの中から「#Pluggable」で始まるものについて、それが当該メソッドの生成しようとするウィジェットが属するクラスであると判断しピックアップしています。

また、自身がコンパイルして生成される際に用いられたソースコードそれ自体にアクセスする方法も心得ています。getSourceFromFileというメッセージを送ることで、ちょうどブラウザのコードペインに入力した内容そのままを得ることが可能です。ただ今回の目的(ウィジェット生成用のコードを得る)にはメソッドのソースコードの一行目にあるメッセージパターンやコード冒頭のリターン「^」は邪魔なので、それらを省く文字列処理(#indexOf:、#allButFirst:)を行ない、ウィジェットを作るための式のみを抽出しています。

こうして得られたウィジェットのクラスを「キー」に、そのウィジェットを生成するためのコードを「値」にして、あらかじめ用意したsourceCodesという辞書のエントリーとして登録(#at:put:)しておく…という手続きが、#generateScriptOf:の前半に行なわれている作業の内容です。


▼レイアウト情報のソースコード化とその出力
まず、自動生成するソースをはき出すためのテキストファイルを開き、fileという名前でアクセスできるように関連づけしています。

file := FileStream newFileNamed: 'mywindow.st'.


ファイルストリーム「file」に対しては、「nextPutAll: aString」というメッセージを送ることで、パラメータとして添えたテキスト(aString)を追記してゆくことができます。なお、メッセージ「cr」は整形のための改行の追加を意味します。

  1. generateScriptOf:で自動生成しているコードはおおよそ次のような内容のものです。

  1. 一時変数としてmodel、windowを宣言(| model window |)
  2. 適当なモデルをmodelに関連付け(model := ...)
  3. システムウインドウを生成(#labelled:)し、windowに関連付け(window := ...)
  4. windowにウィジェットを追加(#addMorph:frame:)
  5. windowを表示(#openInWorld)

この中で(4)の部分は、GUIビルダウインドウに配置したウィジェットから情報を得て動的に記述する必要があります。そのための処理を行なっているのが次の部分です。

window paneMorphs do: [:morph |
  file nextPutAll: 'window addMorph: ('.
  file nextPutAll: (sourceCodes at: morph class).
  file nextPutAll: ') frame: ('.
  frame := morph layoutFrame.
  file nextPutAll: (
     frame leftFraction @ frame topFraction
        corner: frame rightFraction @ frame bottomFraction) printString.
  file nextPutAll: ').'; cr].


GUIビルダウインドウのpaneMorphs変数から、同ウインドウにレイアウト済みウィジェットについての情報を得て、各ウィジェットをmorphという変数に関連付けしながら#do:で二行目から最終行までの処理を繰り返しています。

ウィジェットを生成するための式は、前半でカタログ化したものを参照。辞書ではウィジェットのクラスがキーになっているので、単純に#at:でアクセスするだけです。得られたコードをそのまま#nextPutAll:しています。配置のための情報は、まず、ウィジェットのlayoutFrame情報を得て、そこからウインドウ内での相対座標情報を#@や#corner:を使って矩形(a Rectangle)情報として再構成。あらためてprintStringメッセージを送ることで、文字列化後、fileへ出力しています。

すべてのコード出力作業を終えると、fileにeditというメッセージが送られ、出力済みのファイル内容が新しいウインドウに表示されます。


以上、#generateScriptOf:は少々長めで、一見、複雑なメソッドに見えますが、メソッドオブジェクトの存在やファイル出力の方法について知ってしまえば、いたって単純な作業内容のメソッドであることがおわかりいただけたと思います。次回は、出力されたスクリプトを改変し、適当なモデルと結びつけることで、実際にGUIとして動作するかどうかを確認してみましょう。

このページを編集 (5561 bytes)


Congratulations! 以下の 1 ページから参照されています。

This page has been visited 248 times.