Archive for the 'Scriptable, sdef' Category

2015/02/01 badcharanさんがXcode 6.1.1のASOC用ファイルテンプレートを公開

Xcodeの新しいバージョンが公開されるごとに、ASOCのプロジェクトテンプレート「Cocoa-AppleScript」がどこに配置されているかを確認してしまうところ。Cocoa-AppleScriptが「Application」ではなく「Other」に分類されているのはいやな感じです(カスタマイズしたいな〜)。

sc1.png

さらに、XcodeプロジェクトにFileを追加しようとしても、ファイルテンプレートとして「AppleScirpt Class」が存在しておらず、「Other」の「Empty」を選択させられるのが微妙に「大きなScriptアプリケーションを作らせない」ようにクパティーノから嫌がらせを受けているような気がしていました(QuartzComposerみたいに、A社のエンジニアから露骨に「もうバグも直さないし機能追加もしません」宣言をされるよりはマシですけれども)。

sc2.png

そう思っていたのが自分だけではなかったのか、badcharanさんがXcodeプロジェクトでAppleScript(ASOC)ファイルの追加を楽にできるよう、ファイルテンプレートを公開されました

ダウンロードしてきて、内容を確認して・・・内容が理解できたのでさっそく自分でも追加してみました。

sc4.png

sc6.png

sc5.png

自分的にマイブームがきている「ASOCアプリのScriptable化」でよく作る、NSScriptCommandをparent classに設定してある.applescriptファイル。ついでに、最初から「on performDefaultImplementation()」ハンドラを宣言しておきます。my directParameter()にアクセスするコードも書いておきましょう。

sc3.png

Xcode上のASOCについては、いまひとつ生産性の向上が図りづらかったことがありますが、このあたりから生産性をガンガン向上させていきたいところです。

ASOCでCustom Viewを手軽に作れるようになるとよいのですが、、、

2015/02/01 ScriptableなASOCアプリからrecord型の値を返す

Xcode上でGUIベースのASOCアプリを開発できるわけですが、これをAppleScriptからコントロール可能な「Scriptableな」アプリケーションにすることは可能です。具体的な作業については、Shane Stanleyの電子ブック「AppleScriptObjC Explored fifth edition」に詳しく書かれています。

同書ではAppleScript側に返す「返り値」について、stringとfile型を扱っていました。

ASOCからrecord型のデータを返す

ASOCアプリからrecord型でAppleScriptに値を返すのは「大変」と聞いていたのですが、追加調査を行ったところ3つ方法があることがわかりました。

タイプ1:resultのtypeをsdef上で自前で定義

1つは、sdefファイル中でコマンドの返り値を「record」ではなく「別のもの」として宣言すること。「別のもの」を「record type」として宣言。それだけ。AppleScriptへの返り値としてrecordっぽく値が返りますが、ラベルについているのは一般的なrecordのラベルではなく「アプリケーション予約語」。該当するアプリケーション(ASOCアプリ)へのtellブロック外でrecord内の要素にアクセスするとエラーになります。
scriptable1.png

タイプ2:Objective-Cの併用による強制型変換。sdef上でresult typeをrecordと記述

もう1つは、Ron ReuterがDirect Mailで教えてくれたもので、Objective-CでNSDictionaryを拡張しておいて、sdefファイルにはコマンドの返り値として「record」を記述するというもの。普通にASOCアプリ側からrecordを返せました。返り値(record)内の要素にASOCアプリへのtellブロック外でアクセスしてもエラーになりませんでした。

タイプ3:ASOCらしくASOCで強制型変換。sdef上でresult typeをanyと記述

最後のものも、Ron ReuterがAppleScriptObjC Dev ML上で公開したもので、ASOC側から値を返すさいにCocoaの機能を呼び出して加工するもの。普通にrecordがAppleScript側に返ってくるので、ASOCアプリへのtellブロック外で返り値の要素にアクセスしてもエラーになりません。sdef上ではresultのtypeを「any」と書いておくのがポイントです。
scriptable2.png

ただ、返り値に「any」って書いてあったら、AppleScriptを書く側からするとかなり困惑します。ここが、このタイプ3を選ぶかどうかのポイントになるでしょう。

scriptable100.png

これらのやりかたで、

–> {aLabel:”test1″}
–> {aLabel:{”test1″, “test2″, “test3″}}

といった値を返せるようになりました。ここ2・3日で「できない」ことが「手軽にできる」ようになったわけで、実にけっこうなことです。

これら3つの方法について、すべてサンプルProjectを作ってひととおり試してみました。

難易度でいえば、タイプ1が一番簡単です。返り値の取り扱いの簡単さ(普通のレコードとして扱える)でいえばタイプ2とタイプ3が簡単。タイプ1かタイプ3を選択して使ってみるといいかも、というところかと。タイプ3も、badcharanさんのXcode Projectテンプレートの中に変換用ハンドラごとテンプレート化しておけば実装コストもほぼゼロに

まだ返せないデータ形式

ひととおりデータを返せるようになってきた気がしますが、

–> {{”test”, “test2″}, {”test3″, “test4″}}

といった入れ子のlist(2D list)は返せていませんし、

–> {aLabel:{bLabel:”test1″, cLabel:”test2″, dLabel:”test3″}}

といった入れ子のrecordも返せていません。

ただ、一応recordは返せるようになっているので、複雑な構造のデータは返さないようにすれば、問題はないでしょう。

2015/01/30 ASOCアプリをScriptableにして値を返す

基本的に、「AppleScriptObjC Explored fifth edition」のP-157「25. Making it Scriptable」を読めば、ASOCアプリをAppleScript対応にして、AppleScriptからASOCアプリを操作できるようにすることは可能です(この本よりいい本はないので、ASOCを使おうとしたら買うべき)。

基本的には、

(1)Info.plistに「Bundle creator OS Type Code」を英数字4文字で設定

(2)Info.plistに「Scripting definition file name」を設定(”.sdef”ファイルのファイル名。拡張子を含む)

(3)Info.plistに、「Scriptable」のエントリを作成し、値をYES(Boolean)に設定

(4)Xcodeプロジェクトにsdefファイルを追加

(5)sdefファイルの内容を記述

(6)AppDelegate.applescriptにプロパティへのアクセス許可/不許可イベントハンドラ「application_delegateHandlesKey_」を追加

(7)sdef内で宣言したコマンド(class)に対応するサブscriptをASOCプロジェクト内に作成し、コマンド内で行う処理を記述

といった手順でScriptableにできるわけですが、ASOCアプリからAppleScript側に返す値の種類によって難易度が変わるということを、身を持って体験しました。

  string:文字列。かんたん。実際に確認ずみ
  file:ファイルパス。かんたん。実際に確認ずみ
  number:数値。かんたん。実際に確認ずみ
  record:レコード。超難しい。挫折
  record:レコード。情報が少ない。難しくはなかった

アプリケーションからAppleScriptコマンドの返り値をレコードで返すというのは、Finderをはじめとする一般的なアプリケーションでは常識的な動作であり、ASOCアプリでこれを行おうとすると大変、ということがわかりました。

これなら、アプリケーション操作と同等のAppleScript Librariesを(AppleScript用語辞書なしで)別途提供したほうがずっと簡単です。

Objective-Cのプログラムを併用することで、ASOCアプリからNSDictionaryの値をAppleScript側に返してあげられるとよいのですが、、、

あとになって、よくよく考えてみるとAppleScript対応のアプリケーションが、コマンドの実行結果として直接record型のデータを返してくるケースを見たことがないような気がしてきました。true/falseとか、結果が入ったオブジェクトをいくつも返してきて、結果を確認するためにはそれらのオブジェクトの属性にアクセスするような処理が多いような・・・

・・・とか書いていたら、海外から「え、やったことあるけどサンプル送ろうか?」という声が(^ー^;;; ありがたく見せていただくことに。このあたりの書き方でイケるんでしょうか。

・・・あ、自分で試してたらできちゃった。これは、AppleScript用語辞書ファイル(sdef)の書き方の問題なのかー。本当にそれだけなんだー。難易度が高くない(情報が見つかりにくい)ことがわかりました。Objective-Cのコードを1行も書かずに、ASOCだけで実装可能でした。

2012/04/13 AppleScriptObjCアプリをAppleScript対応に(3)

AppleScriptObjCのアプリをスクリプタブルにする話の続きです。

とりあえず、30分もかからずにASOCアプリをスクリプタブルにできました。超簡単です。

そこで、以前から疑問に思っていたことをテストしてみました。

Xcode上では、プロジェクトを構成するさまざまなファイルをローカライズすることが可能です。つまり、各国語環境用に個別にファイルを用意しておいて、対応する言語環境で別々の内容を表示させることができるようになっています。

そこで、sdefファイルをローカライズして、日本語環境下では日本語の解説文が入ったAppleScript用語辞書を表示させられるかを試してみました。

asocs8.png

▲ローカライズされたAppleScript用語辞書。日本のユーザーしか使わないようなツールに英語だけの用語辞書を付けておくことはナンセンス。このようにして分かりやすくできる

結果はばっちり大成功。日本語環境では、日本語で説明の入ったAppleScript用語辞書がオープンされることが確認できました。こうして英語の用語辞書のほかに日本語の用語辞書を用意しておけばよいのではないか? と思われました。

→ プロジェクトのダウンロード(90Kバイト)

※記事掲載当初はアーカイブのダウンロードリンクが切れていました。2012/4/15現在は修正してあります

■総評

正直、AppleScriptで書かれたプログラムをAppleScriptから呼び出すのだから、処理内容自体を呼び出し側に書けばよいようにも思えますし、速度の面でもあまりメリットが感じられません。

リスト要素のソートなど、Cocoaの機能を用いると高速化できるものもありますが、Mac OS X 10.7以降であればAppleScriptエディタ上で直接AppleScriptObjCのプログラムが記述でき、Cocoaの機能も呼び出すことができます。わざわざ、操作が繁雑なXcode上でそれを行うメリットが大きいとも思えません。

AppleScriptでOSAX(のようなもの……つまり、Invisible Processでウィンドウとかメニューなどを持たないアプリ)に近いものが作れるわけで、それについてはなかなか便利でしょう(ライブラリを整備するのと自前OSAX作成とどちらが労力が少なくて済むかは、判断つきかねます)。

ですが……単純にやってみて「おもしろい」と感じられました。もっと高度な命令も実装できることが確認できれば、応用範囲がいろいろと広がるのではないかと思われました。

AppleScript用語辞書の(言語環境に応じた)ローカライズや、一部のAppleのアプリケーションで試行されているサンプルスクリプトの用語辞書への同梱など、「こうできた方が便利では?」というアイデアを試す場として利用できる、とは思っています。

スクリプタブルなアプリケーションを作るのがここまで簡単だとは思わなかったので、そのことが分かったことが最大の成果だと感じました。他人のプログラムを見ながら試して、動くようになるのに30分もかかりませんでした。

2012/04/13 AppleScriptObjCアプリをAppleScript対応に(2)

AppleScriptObjCアプリをスクリプタブルにした話の続きです。

r/oの属性ばかりではなく、書き換えできる属性値を用意し、これをGUIにつないで書き換えが目で見て分かるようにしてみました。

さきほどの用語辞書の属性値「message」はsdefファイルの定義によりAppleScriptObjCプログラム中の「theMessage」プロパティに対応。さらに、Xcode上でtheMessageプロパティをNSTextFieldのvalueにバインドしてみました。

AppleScriptObjCファイル名:asoc1AppDelegate.applescript

– asoc1AppDelegate.applescript
– asoc1

– Created by 長野谷 隆昌 on 12/04/12.
– Copyright 2012 Piyomaru Software. All rights reserved.


– http://macscripter.net/viewtopic.php?id=36000

– MacScripters Meetingの投稿をもとに、いらない部分をそぎ落として分かりやすいように整理したもの
– Original post by akader

script asoc1AppDelegate
  
  
property parent : class “NSObject”
  
  
property tF : missing value –bind to NSTextField
  
  
property theMessage : missing value –bind to tF’s value
  
  
  
  
  
on applicationWillFinishLaunching_(aNotification)
    
– Insert code here to initialize your application before any files are opened
  
end applicationWillFinishLaunching_
  
  
  
on applicationShouldTerminate_(sender)
    
– Insert code here to do any housekeeping before your application quits
    
return current application’s NSTerminateNow
  
end applicationShouldTerminate_
  
  
  
  
on application_delegateHandlesKey_(sender, theKey)
    
    
return theKey is in {“theMessage”}
    
  
end application_delegateHandlesKey_
  
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

アプリケーション「asoc1」を実行すると、こんな感じです。

asocs5.png

スクリプト名:asoc1のメッセージを書き換える
tell application “asoc1″
  set message to “ぴよまるソフトウェア”
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

上記のAppleScriptを実行すると、

asocs6.png

のようにテキストフィールドの内容が変化します。逆に、テキストフィールドの内容を「message」属性を介して取得することもできます。

ただし、テキストフィールドに文字入力中の内容を取得しようとした場合、すぐにテキストフィールドの内容に対する変更が値に反映されないなどの現象が見られました。そこで、Xcode上でテキストフィールドのvalueをバインドしているところで、

asocs7.png

「Continuously Updates Value」のチェック項目があるので、チェックを入れると……入力した内容がすぐに属性「message」に反映されるようにはなるのですが、今度はアプリケーションの動作が若干もたつく感じがしました。外すとそのようなことはなくなったので、少しひかえめな連動を行うべきなのかもしれません。

「ひかえめな連動」というのは、GUI上で入力中のフィールド内容を即座に求めるのではなく、内容が確定して環境設定に書き込んだ内容に対してアクセスするような連動、ということです。キーボード入力された内容をすぐ取得するのは避けたほうがよいでしょう。

2012/04/12 AppleScriptObjCアプリをAppleScript対応に(1)

MacScripter.netで探してAppleScriptObjCアプリをAppleScript対応(スクリプタブル)にする方法を確認してみました。貴重な情報を提供してくれている投稿者の方々には深く感謝しています。

実際の投稿記事はこちら。この一連の記事は同投稿を精査して、より単純化して資料を加え解説するものです。

■Info.plistを編集

まずはXcode上でAppleScriptObjCのプロジェクトを1つ作成してみましょう。サンプルでは、「asoc1」という名前のプロジェクトを作成しました。

asocs2.png

最初に、Info.plist(各Xcodeプロジェクト内でのファイル名は異なります。上の画面では「asoc1-info.plist」が該当)を編集し、キーが「Scriptable」値が「Yes」(Boolean)、キーが「Scripting Definition file name」値が「myApp.sdef」のエントリ(合計2つ)を作成します。

asocs1.png

■sdefファイルをプロジェクトに追加する

sdef(Scripting DEFinition)ファイルをプロジェクトに追加します。Xcodeで「New File」を実行し、「empty file」をプロジェクトに追加。追加ファイルのファイル名を「myApp.sdef」とします。

内容はこんな感じ。画像では内容が見えない場合には、あとでアーカイブ中の実物を見てください。

asocs3.png

なお、sdefファイルの記述がもっと簡単にできる、Shadow Labの「Sdef Editor」というフリー・ソフトウェアが存在します。もっと込み入ったAppleScript用語辞書を作成する場合には、同ソフトウェアを併用するとよいでしょう。

asocs9.png
▲Shadow LabのSdef Editor

■AppleScriptObjCプログラム側にハンドラ追加

on application_delegateHandlesKey_(sender, theKey) ハンドラを追加。予約語messageに対応する「theMessage」を受信したときにtrueを返します。ただ、それだけ。

■AppleScriptObjCプログラム側にプロパティ追加

property theMessage : missing value

これだけ追加しておきましょう。

■ためしに、ビルド

これでXcode上でCommand-Rでビルド&実行するだけで、スクリプタブルなアプリケーション(AppleScript用語辞書つき)になります。アプリケーションに対してプロパティを取得すると、これだけでアプリ名やバージョン番号などの最低限の情報を取得できます。

AppleScript用語辞書をAppleScriptエディタでオープンして内容を確認することも可能です。

スクリプト名:asoc1でアプリのプロパティを取得する
tell application “asoc1″
  properties
end tell

–> {message:missing value, frontmost:false, class:application, name:”asoc1″, version:”1.0″}

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に