「Objective-Cクラスとの連係」
さて、AppleScript Studioを使った基本的な開発スタイルは、だいたいイメージできたことと思います。要するに、用意されている部品を組み合わせ、それをスクリプトで操作して動かすわけですね。従って、用意されていない機能は使えません。例えば、グラフィック。Cocoaには「NSImageView」といったイメージ表示のコントロールがありますが、これはファイルから読み込んで表示したりすることはできるけれど、スクリプトを使って図形などを描画させる機能はありません。
「スクリプトからグラフィックを利用できない」というのは思った以上に大きいものです。このために、例えばグラフィック利用のゲームとかいったものは、かなり限定されたものしか作れなくなってしまうのです。これがネックになって「AppleScriptは使えない、やっぱりObjective-Cを覚えないとダメだ」と思う人は多いようです。
そこで今回は、使えないグラフィック描画機能を使えるようにしてみましょう。——え?「使えないのにどうやるんだ?」って? 実をいえば、現在のAppleScript
Studio 1.2には、「使えない機能を使えるように拡張する」ための機能がちゃんとついているんですよ。それは「Objective-Cの機能を呼び出す『call
method』」というものです。
call methodは、「Objective-Cで書かれたクラス内にあるメソッドをAppleScriptからコールする(呼び出す)もの」です。これにより、例えばAppleScript
Studioにない機能をObjective-Cで作成して呼び出せるようになる、つまり「使えない機能を使えるようにできる」というわけです。ただし、これを使うには、Objective-Cができないとダメなわけですね。が、例えばObjective-Cがわかる人間が、汎用的な機能をまとめたクラスを用意して「自由に使って下さい」なんて配付するようになってくれば、それらを使うことで、AppleScriptの機能どんどん拡張していけるわけです。プラグインファイルのような感覚でそうした「AppleScript
Studioユーザ向けのクラス」がどんどん出てくれば、きっとAppleScript Studioは今以上に楽しくなるはずです。
まぁ、これを読んでいる多くの人は「Objective-Cなんてわからない」という人でしょうから、Objective-Cの詳細については触れません。ここでは「Objective-Cでクラスを作成し、それをAppleScript
Studioから利用する方法」について整理しておくことにしましょう。
Objective-Cを使ってAppleScript Studioを拡張する場合、以下のような手順を追って作業をします。
- Project Builderで、Objective-Cのクラスを新たに作成し、プロジェクトに組み込む。
- 作成したObjective-Cのクラスのソースコードを作成し完成させる。
- インターフェイスで利用する場合は、Interface Builderを起動し、作成したObjective-Cクラスを読み込む。
- Objective-Cクラスをデザイナに配置して利用する。
ざっとこんな流れになります。ごく単純に「何かの機能を呼び出すだけ」といったもの場合は、Project Builderに組み込むだけで済みますから、1〜2の作業だけで利用できるようになります。3〜4は、コントロールとしてObjective-Cを利用する場合に必要な作業と考えて下さい。
では、実際に簡単なサンプルを作ってみましょう。ここでは「マウスで絵を描くサンプル」を作ってみます。Objective-Cで「スクリプトから円を描画させるクラス」というのを作成し、それをInterface
Builderで配置して、AppleScriptでその機能を呼び出して描画をさせてみることにしましょう。
1.まず、新規にAppleScript Studioのプロジェクトを作成して下さい。これは、通常の「AppleScript Application」というタイプのものでよいでしょう。
2.プロジェクトを作成したら、Objective-Cのクラスを作成して組み込みます。Project Builderの「ファイル」メニューから「新規ファイル」を選んで下さい。するとファイルの種類を選択するパネルが現れるので、「Objective-C
Class」を選択します。
3.次へ進むと、クラスファイル名を設定する画面となります。ここでは「ASView.m」としておきましょう。mという拡張子はObjective-Cクラスファイルを示すものですから、他のものにはしないで下さい。
4.これで、プロジェクトに「ASView.m」「ASView.h」という2つのファイルが組み込まれます。.h拡張子のものが「ヘッダーファイル」、.m拡張子が実際のObjective-Cソースコードファイルです。これは両者が揃って機能するものですから、必ず2つが揃っていることを確認して使って下さい。
5.次に、ソースコードの作成です。まずヘッダーファイルの「ASView.h」からです。これを以下のように修正をして下さい。
#import <Cocoa/Cocoa.h>
@interface ASView : NSView {
NSImage *bkImg;
float lr,lg,lb,la;
}
-(void)clearBkImg;
-(void)setCColor_r:(float)r g:(float)g b:(float)b a:(float)a;
-(void)drawCOval_x:(float)x y:(float)y w:(float)w h:(float)h;
@end
6.次に、ソースコードファイル「ASView.m」です。これは少々長くなりますので、注意して作成しましょう。
#import "ASView.h"
@implementation ASView
// 初期化の処理
-(id)initWithFrame:(NSRect)frameRect
{
[super initWithFrame:frameRect];
bkImg = [[NSImage alloc] initWithSize:frameRect.size];
[ASView clearBkImg:self];
return self;
}
// 開放時の処理
-(void)dealloc
{
[bkImg release];
[super dealloc];
}
// 表示をクリアする処理
-(void)clearBkImg
{
[bkImg lockFocus];
[[NSColor whiteColor] set];
NSBezierPath *bp1 = [NSBezierPath
bezierPathWithRect:NSMakeRect(0,0,[bkImg size].width,
[bkImg size].height)];
[bp1 fill];
[bkImg unlockFocus];
[self display];
}
// 色変更の処理
-(void)setCColor_r:(float)r g:(float)g b:(float)b a:(float)a
{
lr = r;
lg = g;
lb = b;
la = a;
}
// 円描画の処理
- (void) drawCOval_x:(float)x y:(float)y w:(float)w h:(float)h
{
[bkImg lockFocus];
NSRect lastR = NSMakeRect(x,y,w,h);
[[NSColor colorWithCalibratedRed:lr green:lg blue:lb alpha:la] set];
NSBezierPath *bp1 = [NSBezierPath bezierPathWithOvalInRect:lastR];
[bp1 fill];
[bkImg unlockFocus];
[self display];
}
// 画面の更新処理
-(void)drawRect:(NSRect)aRect
{
[bkImg compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
}
@end
これで「ASView」というコントロール・クラスは完成です。ファイル類は保存しておきましょう。まだ何が何だかわからないと思いますが、これは「Objective-Cではこういう感じでプログラミングをするんだ」という程度に眺めておけばいいですよ。
作成した「ASView」というのは、「NSView」というコントロールを拡張して作ったもので、ウィンドウに配置して使います。では、その作業を行っていきましょう。
1.まず、Project Builderで、nibファイル(通常はMainMenu.nib)をダブルクリックしてInterface Builderで開きましょう。
2.では、作成したASViewを組み込みます。——Nibファイルウィンドウで「Classes」タブをクリックして選択します。そして「Classes』メニューから「Read
File」を選んで下さい。
3.画面にファイルを選択するパネルが現れるので、「ASView.h」ファイルを選びます。そして「Parse」ボタンをクリックします。これで、ASViewクラスが使えるようになります。
4.では、ASViewを配置しましょう。ウィンドウデザイナに、「NSView」というコントロールを配置します。そして、Infoパレットの「Custom
Class」という部分で「ASView」を選択して下さい。これで、配置したNSViewが「ASView」として認識されるようになります。
5.後は、AppleScriptで利用するための設定を行うだけです。ここでは、以下の項目を設定して下さい。
- 配置したASViewの設定
- 名前を「view1」とする。
- イベントの「Mouse」項目内にある「mouse down」「mouse dragged」の2つをONにする。
- スクリプトファイル「アプリ名.applescript」のチェックをONにする。
- ASViewを配置したウィンドウの設定
- 名前を「win1」とする。
さて、これでInterface Builderの設定は完了です。これで、自作したASViewが、そのまま画面に表示され使えるようになります。のみならず、このASViewの機能をAppleScriptから利用することもできるようになっているのです。
では、スクリプトを作成しましょう。今回はmouse downとmouse draggedという2つのイベントを使います。これらはいずれもマウス関係の操作を行った際に発生するものです。コントロールによっては、このようにマウスの操作に応じてイベントを発生させるものもあるんですね。
on mouse down theObject event theEvent
if name of theObject is "view1" then
set l1 to location of theEvent
set l2 to position of theObject
set x1 to (item 1 of l1) - (item 1 of l2)
set y1 to (item 2 of l1) - (item 2 of l2)
set r to (random number from 1 to 256) / 256
set g to (random number from 1 to 256) / 256
set b to (random number from 1 to 256) / 256
call method "setCColor_r:g:b:a:" of theObject with parameters {r, g, b, 0.5}
drawOval(theObject, x1, y1)
end if
end mouse down
on mouse dragged theObject event theEvent
if name of theObject is "view1" then
set l1 to location of theEvent
set l2 to position of theObject
set x1 to (item 1 of l1) - (item 1 of l2)
set y1 to (item 2 of l1) - (item 2 of l2)
drawOval(theObject, x1, y1)
end if
end mouse dragged
on drawOval(obj, x, y)
call method "drawCOval_x:y:w:h:" of obj with parameters {x - 5, y - 5, 10, 10}
end drawOval
これがスクリプトです。できあがったら、プロジェクトをビルドし、実行してみて下さい。ウィンドウ内に配置したASViewが白い矩形で表示されます。その中をマウスでドラッグすると、ランダムに設定された色でドラッグした通りに図形が描かれます。
どうです、AppleScript Studioで使えないはずの「グラフィック描画」が、AppleScriptで実現できたじゃないですか! 確かにObjective-Cを使うのは大変そうですが、「不可能ではない」ということがわかっただけでも十分ですよね。
まずはmouse downとmouse draggedハンドラについて簡単に説明しておきましょう。今まで、マウスクリック関係のclickedイベントハンドラを中心に説明してきましたが、もちろんCocoaコントロールに用意されているイベントはこれだけではありません。今回使った「mouse」項目内に用意されているのが、マウス関係のイベントです。mouse
downは文字通りマウスボタンを押し下げたときに発生するものですし、mouse draggedは、ドラッグ中、常に発生し続けるイベントです。
これらのイベントは、以下のようなイベントハンドラを呼び出します。
on mouse down theObject event theEvent
・・・実行する処理・・・
end mouse downon mouse dragged theObject event theEvent
・・・実行する処理・・・
end mousedragged
ちょっとclickedとは違っていますね? これらのイベントハンドラでは、2つのパラメータが用意されているのです。1つ目のtheObjectは、clickedなどと同じくイベントが発生したオブジェクトを示すものです。が、その後にあるtheEventは、「発生したイベントに関する情報をまとめたオブジェクト」が渡されているのです。要するに、「イベントのオブジェクト」です。
「イベントのオブジェクト」といわれてもイメージしにくいでしょうが、このオブジェクトを調べることで、発生したイベントに関するさまざまな情報が得られるようになっているのだ、と考えて下さい。例えば、今回のサンプルでは以下のようにしてクリックしたマウスの位置を調べています。
set l1 to location of theEvent
このようにイベント・オブジェクトの「loation」を調べることで、マウスイベント発生時のマウスポインタの位置を得ることができます。ただし、これで得られるのはコントロール内の位置ではなく、「イベントが発生したウィンドウ内の位置」です。そこで、ここではコントロールのpositionプロパティ(位置を示すもの)を調べ、その差を計算してコントロール内の位置を調べています。
マウス関係のイベントは他にもありますが、だいたい基本は同じです。他にどのようなものがあるか調べて、実際に利用してみるとよいでしょう。意外と簡単にマウス操作によるスクリプトが作れるはずですよ。また、これらはコントロールの種類によって使えるものと使えないものがあります。どういうコントロールで使えるのかも調べてみましょう。
さて、肝心のASViewの機能を呼び出している部分を見てみましょう。ここでは2ヶ所で「call method」という文を使っています。1つは色を変更するための「setCColor_r」というメソッドを呼び出しており、もう1つは円を描く「drawCOval_x」メソッドを呼び出しています。
このcall methodと、Objective-Cにあるメソッドの定義を比較してみましょう。どのようになっているでしょうか。いずれも、上がObjective-Cの定義、下がスクリプトのcall
methodとなります。
・SetCColor_rの場合
-(void)setCColor_r:(float)r g:(float)g b:(float)b a:(float)a;
call method "setCColor_r:g:b:a:" of theObject with parameters {r, g, b, 0.5}
・drawCOval_xの場合
-(void)drawCOval_x:(float)x y:(float)y w:(float)w h:(float)h;
call method "drawCOval_x:y:w:h:" of obj with parameters {x - 5, y - 5, 10, 10}
なんだかよくわからないでしょうが、もう少し整理してみると、以下のような形になっていることがわかります。
・Objective-Cの定義
−(返値) メソッド名:引数 キーワード1:引数 キーワード2:引数 ・・・;
・call methodの呼び出し
call method "メソッド名:キーワード1:キーワード2・・・" of "オブジェクト名" with parameters { 引数のリスト}
「キーワード」というのは、Objective-C特有のもので、要するに「メソッドにさまざまな値をつけて送信するためのもの」と考えればよいでしょう。更にこれらをわかりやすく整理すると、以下のようになっていることがわかります。
これで、だいぶObjective-Cのメソッドがcall methodでどう記述するか、見えてきましたね。この「call methodで呼び出す際の書き方」さえわかれば、Objective-Cのメソッド呼び出しはけっこう簡単です。気をつけておきたいのは「受け渡す値のタイプを間違えない」「キーワードと値の数と順番を間違えない」ということです。Objective-Cのメソッドは、たくさんのキーワードが並ぶものが多いので、けっこう間違えやすいんですね。・Objective-Cの場合
「メソッド名の後に、キーワードと引数がセットになったものが必要なだけ並ぶ」
・call methodの場合
「メソッド名とキーワードがまとめたものを指定し、その後にofでオブジェクト名を、with parametersでキーワードで送る値のリストをそれぞれ指定する」
※——なお、製作ソフトウェアのページに、「ASKDrawView Kit」というフリーウェアをアップしてあります。これは、AppleScript Studioから利用できる、Objective-Cクラスです。今回作成したクラスを拡張したようなもので、直線・曲線・円形・四角形といった基本図形の描画、TIFFイメージの読み書き、キャラクタイメージの設定と背景との合成といった機能をスクリプトから利用できます。興味のある方は、こちらからどうぞ。
※表示をクリアするボタンのスクリプト例
引数として渡すASViewは、「view "view1"」と指定するところがポイントです。「ASView "view1"」なんて書くと、うまくいきませんよ! また、引数を持たないメソッドは、単にメソッド名だけで後に:記号は付けません。:記号は「その後に引数がある」ということを意味するのです。
on clicked theObject
if name of theObject is "clBtn" then
call method "clearBkImg" of (view "view1" of window "win1")
end if
end clicked