[ SuperCard ] [ Delphi ] [ SmalltalkAgents ]

Building Simple Application in SmalltalkAgents

[ Updated: Apr 7, 1997 ]


SmalltalkAgents でデジタル時計を作る

新しいプロジェクトを作る
ウィンドウを作る
とりあえず動かしてみる
何を作ったのか
アプリケーションモデルを実装する
リアルタイムに動かしてみる
idleTask version
Thread version
スタンドアロンアプリケーションを作る

さて、今度は SmalltalkAgents です。対象ヴァージョンは 2.1(.1) です。

新しいプロジェクトを作る

SmalltalkAgents でも、新しいアプリケーションの開発は、新しいプロジェクトを起こすことから始まります。

  1. AppleScript メニューから Create A New Project... を選択する
いずれかの方法で、新規プロジェクトを起こします。

Project Name: を "ShowTime" とし、Project Description に簡単な説明を入れておきます。 Options では、"Open UI Editor" をチェックしておくと、新規プロジェクト作成と同時に GUI エディタが開きます。


New Project

OK で "ShowTime" Project を作成したら、Project Browser で、"ShowTime" Project をデフォルト・ライブラリに設定しておきましょう。Set ボタンを押すだけです。


set ShowTime to Default Library

[ go to top ]

ウィンドウを作る

"UI Editor" (ウィンドウのキャプションは "I.C.Editor" になっています) だけが開かれても、手も足も出ません。Box メニューから、Parts Catalog, GUI Tools 1, GUI Tools 2 を出しておきましょう。これで準備ができました。


UI Editor


GUI Tools


Parts Catalog

GUI の作成は、"UI Editor" にコンポーネントを配置していくことで進めます。SuperCard (Delphi とも) と同じ感覚ですね。30 個ほどのコンポーネント群から、テキスト・フィールドとボタンを一つ配置します。適宜、Save Changes で save しておいて下さい。

2つのコンポーネントには、GUI Tools パレットの、Edit Identifier から、ウィンドウ内でユニークな識別子 (名前) を付けておきます。

ボタンには Edit Caption で Quit というキャプションを付けておきます。コンポーネントの移動は、選択してからドラッグで行います。サイズ変更はコントロール・キーを押しながらカーソルをコンポーネント領域上に移動すると、カーソルが変わるので判ります。Caps Lock が掛かっているとコントロール・キーが利かなくなるので注意してください。

その他、必要に応じて、GUI Tools 1 の Set Positioner で、位置関係を設定します。これがなされない場合、フィールドの表示領域は固定化され、ウィンドウをリサイズしても追随しません。ここでは、フィールドは、ウィンドウの上下左右端によって領域を決め、ボタンは右と下端から固定位置に表示するようにしました。

field

button

調子に乗って、フィールドは GUI Tools 1 の Set Framer で、ちょっと沈んだような外観を持たせ、ウィンドウ内部にはグレイのパターンを敷いてみました。えーと、 System 7.5.5 + KT 7.5.2 で GUI をいじる際、ボタン等を移動させると、下のパターンで塗りつぶされてしまって、移動中のオブジェクトが見えなくなります。こんなバグ (?) は、さっさと潰してもらいたいです。以前のシステムでは顕在したかどうか覚えていません。

[ go to top ]

とりあえず動かしてみる

ここまでできたら、モジュールとして動かすことができます。アップルメニューの Launch Module から起動してみましょう。例題としての動作は、まだできませんが、外観はうまくできていますか。

[ go to top ]

何を作ったのか

さて、ここまででいったい何ができたのでしょうか。

ここまでの段階で、2 つのクラスと 6 つのメソッドが自動的に生成されています。Project Browser のリストから、ウィンドウ・クラスとモジュール・クラスをダブル・クリックして、コードブラウザを拡げて見てみましょう。

これで予想がつくかと思いますが、SmalltalkAgents のアプリケーション・フレームワークは、コンポーネントを張り付けたウィンドウとモジュールというスタイルで構成されています。配布 CD-ROM に入っている "SmalltalkAgentsご紹介プレゼン" によると、

モジュール
  • アプリケーションの基本コントロール
  • About..., File, Edit メニューの定義およびその実行
ウィンドウ
  • ウィンドウ制御とユーザアクション処理
  • ウィンドウ固有のメニューの定義およびその実行
コンポーネント
  • ウィンドウに張り付ける各種部品
となっています。

[ go to top ]

アプリケーションモデルを実装する

外観ができました。しかし、時間を表示する機能がありません。見栄えがよければ、アプリケーションモデルの実装へ進みましょう。ここから先は Code Browser を主な作業場所にします。Project Browser のクラス・リストから、ShowTimeModule, ShowTimeWindow をダブル・クリックして、そのクラスの Code Browser を開きます。むろん、Open Code Browser で開いて、クラス名を指定しても同じです。また、Listener 等で

CodeBrowser new open currentClass: ShowTimeModule.
CodeBrowser new open currentClass: ShowTimeWindow.
を評価しても Code Browser を開くことができます。


Code Browser

アプリケーションの実装の最初は、終了ボタンから始めましょう。ボタンのリリースイベント ( mouseUp ) を受け取るために、ボタンオブジェクトに対して、

on: #buttonRelease: send: #doQuit
と設定しておけば、ボタンがリリースされた時点で、#doQuit というメッセージが送られます。このような、「なになに」されたときに「あれこれ」を「どこそこ」のオブジェクトに送るという機能は Smalltalk/V 2.* のアプリケーション・フレームワークでも観ることができましたが、SmalltalkAgents では、とくに semantic message と呼称しています。

このような設定は、どのメソッドで記述すればよいのでしょうか。レシーバはボタンです。#vsAutoConfiguration でボタンが生成されているので、それより後でないといけません。こんなとき、 Smalltalk ではいちいちマニュアルを開いたりしません。Smalltalk は Smalltalk で記述されています。その上、そのソースを見ることができます。それどころか、そのソースを変更することすらできます。

コードブラウザで #(ShowTimeWindow-I-#vsAutoConfiguration) メソッドを開きます。Operations メニューの、Message Senders で、#vsAutoConfiguration メソッドがどこから送られてくるのか見てみましょう。 11 個のメソッドから呼ばれているのが判ります。スーパークラスの UIWindow 関連のものが 2 つあるので、それを見てみます。

  1. #(UIWindow-I-initializeComponent)
  2. #(UIWindow-I-vsEditorInitializeComponent)
このうち #vsEditorInitializeComponent を手繰っていくと、 #vsEditorInitializeComponent というメソッドで
Initialize the component as required. This method is issued privately by the VS Editor.
という Directives に出くわします。 VS Editor とはなんじゃろかい? と、こいつを手繰っていくと、 ViewSystemModel というクラスの #doEditComponent: というメソッドが呼んでいることが判ります。この時点で、これを参照することは止めました。いずれにせよ、これは issued privately by the VS Editor. なので、サンプルとして参考にするのならともかく、めったやたらにつつき回して佳いものではないしょう。

もう一方の #initializeComponent を観てみると、 #vsAutoConfiguration メソッドは、#createComponent したあとで、呼ばれています。その後の #privateGUIBuilderCreateMethod は private という接頭辞が付いているのでやばそうです。以後は

#configureContainer; (Configure the default characteristics for this container.)
#createWindow; (* 1)
#createMenus; (Create the default menus needed for this component.)
#initializationCompletion. (* 2)
とカスケード・メッセージが送られています。このうち、#configureContainer, #createMenus は、 Directives に <overridable> とある以外は、空です。 #createWindow, #initializationCompletion も Directives に <overridable>, <specializable> とある以外は、空です。#createWindow は 3 行ほどコードを持っているように見えますが、佳く見ればコメントになっています。どこに書いても同じように思えますが、とりあえず、各々の Directives を観てみましょう。

#configureContainer
Configure the default characteristics for this container.

#createWindow
“Description: Configure the custom components contained within this window and any other basic characteristics. This method can be used to override the definitions provided by the CCS [GUI] tools.

Comments: The default action is to do nothing. See the #(UIWindow-I-createComponent) method for the default characteristics of all windows.”

#createMenus
Create the default menus needed for this component.

#initializationCompletion
“Description: Perform any final customizations to the component that can only be done via hand-written code. This is the last method invoked as part of the component initialization/configuration.

Detail: I.e., Set any properties or elements which are not appropriate for the store-on mechanism to handle via properties or semantic messages or which the standard properties cloning code cannot handle.”

単純に予想すると、#createWindow か #initializationCompletion をオーヴァーライドすれば佳いように思えますが、 #createWindow の Description の後半には気になることが書いてあります。 can がなければ『 (以前は) [GUI] ツールによる定義をオーヴァーライドするために常用された』となるはずなんですが、 can が入ると判らなくなりました (笑)。だいたい can が表す、能力、可能、傾向、推量、許可、命令、自発的行為って、主語のベクトルは未来を向いているではないですか。「できる」ということは「まだやってない」んですから。過去にはやったのかも知れないけど、ここで「できる」が対象とするものは、「まだなされていない何か」で、それを「できる」とか「してもよい」とか判断してる訳です。

なんでこんなことをグダグダ書いているかというと、さる方より

セミナーテキストによると #createWindow は 「新規プロジェクト作成時に自動生成されるが、 GUI Builder で更新を実行すると削除されてしまう」 とありました。(要約: 西原)
という情報を読んだからです。セミナーテキストがどのヴァージョンを対象にしているのか不明なんですが、 2.1 では #createWindow は自動生成されないから、これは以前のヴァージョンを対象にしているのだろうと考えて、後半の「GUI Builder で更新を実行すると削除されてしまう」を観ていなかった!

で、ちょっとテストして、 GUI Tools 1 から Save Changes してみたら、これが #createWindow は、ものの見事にクリアされるので、ひっくり返ってしまいました (「削除される」って言ってるんだもの、当たり前だって)。じゃ、どこに書けばいいんだ!?

この件に関して TGI-NET の Y さんは 『#createWindow にコンポーネントの動作を記述するのは適当ではないぞよ。 #initializationCompletion に書くがよろしかろう』 とフォローされています。ただし「version 1.2b2 以前では #createWindow でよろしい」。余談ですが、前任のサポート担当、仕切り役、進行役も Y さんという方でした。と、こんな訳で、答えはすでに出ていたのですが、丸暗記じゃ面白くないから、その仕組みを見てみようと思ったわけです。結果としては「判らない」ですけど。

#initializationCompletion メソッドは、デフォルトでは作成されません。新規に作成する必用があります。新規メソッドを作成するには、コードブラウザの Operations メニューの `New Instance Method' によって、新しいインスタンスメソッドを定義します。 `New Instance Method' を選択すると、

messageDescriptor

    $ Begin Directives $

       Description: Return a  based on 

        Method Author:      ''.
        Method Created:     'Fri 08/30/1996 02:11:56 AM'.        
        Method Category:    'operation'.
        
        Allow Undefined Identifiers: no.
        
    $ End Directives $
    
   ^self
のようなスケルトンが現れます。Directives は hidden になっているかも知れません。その場合は、$ Directive Hidden $ の辺りを、コマンド・キーを押しながらクリックすると開くことができます。ここは、Script Mode と Caps Lock キーを見ているようなので、開かない場合は確認してください。このスケルトンを元に、
initializationCompletion

    $ Begin Directives $
        ( 中略 )	
    $ End Directives $

    (self @ 'quitButton')
        on: #buttonRelease: send: #doQuit.
    ^self
と、修正してみます。ボタンへの semantic message を登録しています。修正をセーブするには、File メニューから Save Changes を選択します。

#doQuit メソッドを作り、その中で、モジュールに「終わってね」とお願いします。具体的には、モジュールのインスタンスに「終了して」とお願いします。

doQuit

    $ Begin Directives $
        ( 中略 )	
    $ End Directives $
    
    self module doQuit.
    ^self
のようになります。

フィールドに時刻を表示してみます。ついでに、#vsAutoConfiguration メソッドを参考にフォントも変えてみます。行揃えメソッドは、StaticTextField クラスを覗いてみると判ります。このように、判らないところは、とりあえず該当するクラスとその周辺を探していると見つかるものです。

    (self @ 'showTimeField')
        penState:(
            (PenState new)
                textFont: 'Osaka';
                textSize: 18);
        justification: #center;
        textContents: (Time now asLongDate).

実行してみましょう。このままでは、終了時にいきなり終わってしまいます。ユーザに確認を取るため、ShowTimeModule クラスの #doQuit メソッドに手を加えましょう。

終了ダイアログは、固定文字列と、2 個のボタンを付けたウィンドウです。どのクラスを使えばいいのでしょうか。というわけで、コードブラウザで UIWindow クラスを開いて、Tree View を見てみましょう。いくつかそれらしいクラスが見つかります。Notifier か Prompter のどちらかでしょう。それぞれのクラスを見てみます。Prompter クラスでは user-interaction というカテゴリに prompt: というクラス・メソッドがあるので、これを試してみます。

    Prompter prompt: 'Are you sure?'
うーむ、返答用のフィールドも付いてきちゃいました。おまけにウィンドウはリサイズ可能になっています。今度は Notifier クラスを見てみます。同じ user-interaction というカテゴリに prompt: というクラス・メソッドがありました。
    Notifier prompt: 'Are you sure?'
こちらですね、探しているものは。というわけで、

doQuit

    $ Begin Directives $
        ( 中略 )	
    $ End Directives $

   Custom behavior here
    Speakers beep.
    (Notifier prompt: 'Are you sure?')
        ifFalse: [^nil].
	
   Now carry out the standard exit procedure
    super doQuit.
このように変更します。ビープ音を出してユーザの注意を引き、質問します。OK でなければ、なにもせずに抜けます。OK なら、スーパークラスに「終了してね」とお願いします。

[ go to top ]

リアルタイムに動かしてみる

さて、かんじんの時刻更新です。どうしたらいいでしょう。注意深い方なら、Smalltalk メニューのいちばん下に、Enable Idle Tasks というアイテムがあるのに気がついていると思います。SuperCard ユーザなら、なるほど、on idle が使えるのだなと考えるでしょう。まさにそのとおりです。

アイドルタスクに関しては、聖典に曰く、

しかし、ストップウオッチとしては、リアルタイムに時間を表示しなければ なりません。リアルタイムに動作させるには、タスクがアイドリングの時に 制御をもらえるように、 addIdleTask: メソッドを使って自分自身を アイドルタスクとして登録しておく必要があります。この処理は、`Start' ボタンが押されたときにおこなって、`Stop' ボタンが押されたときには removeIdleTask: メソッドを使って登録しておいたタスクを削除しておく のが良いでしょう。

最初にウィンドウを作る際にアイドルタスク登録を行い、終了するときにアイドルタスクを削除することにしましょう。まず ShowTimeWindow クラスのインスタンス・メソッドとしてアイドルタスクを作ります。

idleTask

    $ Begin Directives $
        ( 中略 )	
    $ End Directives $
    
    (self isOpen and: [self isVisible])
        ifTrue: 
            [(self @ 'showTimeField')
                textContents: (Time now asLongDate)].
   ^self

自分 (ShowTimeWindow) がオープンされており、なおかつ可視状態であれば (ということは hide 状態もあるでしょうね) 、テキスト・フィールドの中味を変えます。どのように変えるかというと、現在時刻をロング表示したものです。これが idle が発生する度に実行されます。

#initializationCompletion で登録処理
    idleTasks := nil.
    self addIdleTask: self.
#doQuit で削除処理
    (self module doQuit) isNil
        ifFalse: [self removeIdleTask: self]. 
    ^self

idle 版はこれで終わりです。SmalltalkAgents の idle は、mouseStillDown 中でも動きます。なかなか使い勝手が佳いですね。さて、でき上がったものは、Project メニューから export して、PIPO ファイルにしておきましょう。

[ go to top ]

さて、中嶋睦月師の SmalltalkAgents の紹介, コンポーネントの作り方 をお読みの方なら、Thread を使った実装方法もあるということをご存知でしょう。次に、こっちの方を考えてみましょう。

最初にウィンドウを作る際にスレッドを生成し、終了するときにスレッドを停止することになります。では、やってみましょう。まず、聖典に則り、update というインスタンス変数を定義します。

UserLib@#ShowTime 
                 name: #ShowTimeWindow
           superclass: Environment@#UIWindow
           properties: 0x0
                 tags: #()
   classVariableNames: #()
instanceVariableNames: #(update)
  sharedVariableNames: #()
           namespaces: {Environment@#NewLook}
   structureTemplates: #()
   defaultStorageSize: 0

ついで、#initializationCompletion メソッド中のアイドルタスク登録の 2 行を削除し、スレッドを生成します。下記のように、idleTask メソッドを 1 秒おきに走らせてみます。そして、このスレッドの名前をインスタンス変数に放り込んでいます。

    update := Thread run:
        [thread priority: thread highPriority.
            [self isOpen] whileTrue: [
                self idleTask.
                Time delayForTicks: 60]].
このコードは、Threrad クラスの "run:" メソッドの Senders を参照してひねくり出しました。何度でも繰り返しますが、 Smalltalk では、こういう芸当ができます。Smalltalker がマニュアルを読まないというのは、おそらく、こういうことです。システム全域に渡るソースを、hypertext like に見ることができるのに、なんで紙のマニュアルが必要なの? ということなのでしょう。idleTask メソッドは、別の名前にした方が佳いかも知れませんが、今回は、こちらでも兼用しますので削除しないで下さい。停止は、#closeWindow の中でスレッドを強制終了させます。
closeWindow

    $ Begin Directives $
        ( 中略 )	
    $ End Directives $
    
    (update respondsTo: #terminate) ifTrue: [
        update terminate].
    super closeWindow.

ShowTimeWindow クラスの #doQuit メソッドも、アイドルタスクの削除処理を取っておきます。

doQuit

    $ Begin Directives $
        ( 中略 )	
    $ End Directives $
    
    self module doQuit.
    ^self

Thread 版はこれで終わりです。

[ go to top ]

スタンドアロンアプリケーションを作る


Application Delivery Toolkit

スタンドアロンアプリケーションの作正手順は、PIPO ファイルを Application Delivery Toolkit (ADT) で変換する、という手順を踏みます。一度、SmalltalkAgents を終了し、ADT をダブル・クリックすると ADT モジュールが立ち上がります。File メニューから Import PIPO... で選択し、Creator を入れ、メモリ割り当てを決め、オプションを設定して、Build を実行すれば、スタンドアロンアプリケーションが作成されます。SuperCard に比べると処理時間はけっこう掛かります。最大 8 つのパスを通じて、使われていないクラスを探索して、要るモンだけを抜いているからでしょう。でき上がった実行ファイルは、およそ 2MB 以上の容量を持ちます。けっこう大きいですね。最後に、聖典に則り、Build Options の設定を変えてみて、idleTask 版、Thread 版それぞれのアプリケーションを作成してみた結果を掲げておきます。システムは System 7.5.5 に漢字トーク 7.5.2 をかぶせたものです。

システム構成

なお、現状では PowerMacintosh 上で Application Delivery Toolkit によって作成されたアプリケーションは、

が、アプリケーションと同一ディレクトリ, または Extensions (機能拡張フォルダ) 内に存在しないと動きません。ない場合は立ち上げ時にエラー・ダイアログを出してそのまま終わってしまいます。

Build Options 生成に要する時間 アプリケーションサイズ (bytes)
thread version
Fast Build (2 pass) 2'10" 2,404,739
Standard Build (8 pass) 4'33" 2,328,527
Minimum Space Build (8 pass) 4'33" 2,328,387
idleTask version
Fast Build (2 pass) 2'11" 2,404,263
Standard Build (8 pass) 4'34" 2,327,735
Minimum Space Build (8 pass) 4'34" 2,327,735

こちらのアプリケーション作成も、ひじょうに簡単であることがお判りいただけたかと思います。実際に作成したのは、モジュール・クラスの #doAbout をのぞき、Thread 版で 33 ステップ、idleTask 版で、わずか 23 ステップです。他の面倒な作業は、ぜんぶクラス・ライブラリがやってくれます。

[ go to top ]


Created: Sep 1, 1996
[ SuperCard ] [ Delphi ] [ SmalltalkAgents ]