AppleScript Programming Tips...と書いたけど、内容はたいしたことはない。[Cocoa Programming Tips](http://hmdt.jp/cocoaProg/index.html "Cocoa Programming Tips | HMDT Programming Tips") の真似がしたかっただけです。知っているとちょっと役に立つかもしれないことをまとめられるといいかな、と。
run script 命令について。load script 命令はわりと話題にでるけど、run script 命令のことってあまり見ない。これは、おそらく run script 命令を使うと処理が遅くなる、ということに起因しているのではないでしょうか。でも、たいして遅くならないよ。今の Mac なら。頻繁に使うとなんだけど、ちょっと使うぐらいなら気にはならない。AppleScript は Mac OS X(特に 10.4 以降)になってからできることが飛躍的に増えて、run script を使うことが個人的には増えました。
run script 命令は文字列(もしくはスクリプトファイルやスクリプトが記述されたテキストファイル)を、実行時にコンパイルし AppleScript として評価する命令です。実際は scripting component(利用するスクリプト言語のこと)を指定することができるのですが、AppleScript 以外を使ったことがないので他の scripting component については割愛します。
run script 命令に渡す文字列は実行時にコンパイルされるので、文法的に正しいなら省略して書いても構いません。
run script "tell app \"finder\" to activate"
文字列をその場でスクリプトとしてコンパイルするので、実行しているスクリプトとは別物のスクリプトが生成されます。ですから、run script で生成したスクリプトは変数やプロパティは共有されません。
set x to 10
run script "set x to 20\r display dialog x as text"
display dialog x as text
このようにそれぞれ個別の変数 x を持っています。ただ、run script 命令はスクリプトの run ハンドラを実行するスクリプトですので、with parameters オプションを使って実行するスクリプトから値を渡すことはできます。
set x to 10
set x to run script "on run argv\nset x to argv\ndisplay dialog x as text\nreturn 20\nend run" with parameters x
display dialog x as text
実際のところ run ハンドラが引数を受け取れるようにしておく状況というのは、
- run script から利用するためのスクリプト
- シェルスクリプトとして利用するためのスクリプト
のどちらかだと思います。しかし、以下のように常に run ハンドラが値を受け取ることができるようにしておいても何も問題はありません。
on run (argv)
-- argv は、ハンドラらしく on run (argv) と書いても OK
-- argv はリストとして解釈される
if argv is me then
-- なんらかの処理
display dialog "me"
else
-- run script で値を渡された時の処理
set argv_class to class of argv
display dialog argv_class as text
end if
end run
このスクリプトを Script Editor 等で実行すると display dialog "me" が実行されます。run ハンドラ実行時に渡す引数がないとき、argv は me(そのスクリプト自身)になります。だから、上記のように引数があるときとない時で処理を分岐することができます。
on run (argv)
-- argv は、ハンドラらしく on run (argv) と書いても OK
-- argv はリストとして解釈される
if argv is me then
-- 通常の処理
-- このスクリプトを編集、実行しているときはこの if 文が実行される
display dialog "me"
-- 自分を呼び出してみる
-- with parameters に渡すのはリストでもただの文字列でも OK
run script (path to me) with parameters {10, "100", (current date) as text}
run script (path to me) with parameters "Hello, World"
-- with parameters なしで実行すると...予想通り、無限ループ
-- run script (path to me)
else
-- run script で値を渡された時の処理
set argv_class to class of argv
display dialog argv_class as text
repeat with this_item in argv
display dialog this_item
end repeat
end if
end run
このスクリプトを保存してから実行してみてください。run script の動きと渡される引数が何かが分かります。
以前にハンドラを文字列で指定するという話題(文字列で指定したハンドラを実行する、その後の「文字列で指定したハンドラを実行する」)を書きましたが、あの時も run script を使いました。スクリプトとして解釈できる文字列を利用する run script 命令は使い方次第でいろいろなことができます。
例えば、System Events の keystroke 命令。この命令は便利なものだけど、ショートカットキーを押すのに修飾キーを指定しないといけない。この修飾キーがあるために汎用的なハンドラにするのは if 文だらけになりそうだ...というとき。以下のように run script を使うときれいにまとまる。
do_keystroke("Finder", "n", "command down") -- 新規 Finder window
do_keystroke("Finder", "n", "{command down, shift down}") -- 新規フォルダ
on do_keystroke(process_name, key_string, modifier_keys)
-- Send key stroke specify application.
-- modifier_keys argument is string. "command down" or "{command down, shift down}" ...
tell application "System Events"
tell process process_name
if not frontmost then set frontmost to true
keystroke key_string using (run script modifier_keys)
end tell
end tell
end do_keystroke
例えば、Spaces。Mac OS X 10.5 からは System Events で「システム環境設定」の設定を調べたり、変更したりできるようになりました。Spaces もそのひとつ。以下のスクリプトで Spaces でどのアプリケーションがどの操作スペースに割り当てられているかを調べることができます。
tell application "System Events"
tell expose preferences
tell spaces preferences
application bindings
end tell
end tell
end tell
実行してみると分かるのですが、返ってくるのは以下のようなレコード。
{|com.apple.safari|:9, |com.apple.terminal|:65544}
アプリケーションの id がラベルになっていて、操作スペースの数字が値になっている。65544 というのは全ての操作スペースに割り当てた時のものだな。では、上記のようなレコードを作って追加すれば新しいアプリケーションも追加できるのだな。
...と、全くその通りに追加できるのですが、ふと思う。このようなレコードをどうやって作ればいいのだろうか?と。ご存知の通り、AppleScript のレコードは他のプログラム言語にあるようなハッシュや辞書のような使い勝手のいいものではなくて、レコードに新しくラベル(キー)を追加することができない。こういうときに run script。以下のスクリプトでは前面のアプリケーションに操作スペースを割り当てます(スクリプトメニューなどから利用します)。
property upper : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
property lower : "abcdefghijklmnopqrstuvwxyz"
property all_spaces : 65544
tell application "System Events"
set front_app to my front_application()
set identifier to my to_lower(bundle identifier of front_app)
set app_name to name of front_app
tell expose preferences
tell spaces preferences
set props to properties
set num to (spaces columns of props) * (spaces rows of props)
set bindings to application bindings of props
end tell
end tell
set {spaces_list, menu_list} to my make_menu(num)
end tell
tell application (path to frontmost application as text)
set the_result to choose from list menu_list default items (item 1 of menu_list) with prompt (app_name & " をどこに割り当てますか?")
if the_result is false then return
set the_result to the_result as text
repeat with i from 1 to count menu_list
if item i of menu_list is the_result then exit repeat
end repeat
set target_spaces to item i of spaces_list
set the_record to my make_record(identifier, target_spaces)
if bindings contains the_record then
display dialog app_name & " は " & item i of menu_list & " に既に割り当てられています" with icon 1 buttons {"OK"} default button 1
return
else
display dialog app_name & " を " & item i of menu_list & " に割り当てます" with icon 1
end if
end tell
tell application "System Events"
tell expose preferences
tell spaces preferences
set application bindings to the_record & bindings
application bindings
end tell
end tell
end tell
on make_record(identifier, spaces_number)
set r to ("{|" & identifier & "|:" & spaces_number as text) & "}"
return run script r
end make_record
on front_application()
tell application "System Events" to return first item of (application processes whose frontmost is true)
end front_application
on make_menu(num)
set spaces_list to {}
set menu_list to {}
repeat with i from 1 to num
set end of spaces_list to i
set end of menu_list to "Spaces " & (i as text)
end repeat
set end of spaces_list to all_spaces
set end of menu_list to "All Spaces"
return {spaces_list, menu_list}
end make_menu
on to_lower(the_text)
set char_list to characters of the_text
considering case
repeat with char in char_list
set num to offset of char in upper
if num is not 0 then set contents of char to character num of lower
end repeat
end considering
return char_list as text
end to_lower
これは単純なサンプルですが、Spaces のように簡単に追加や削除がやりにくいレコードで結果を返すアプリケーションって、増えているのです。格好のいいものではないのですが、レコードの動的生成はよく使います。あと run script でよく利用するのは、先ほども書いたハンドラを文字列で指定するとき。単純にハンドラを文字列で指定するだけなら以下のような方法で十分でしょう。
on run
set my_path to path to me as text
set the_method to run script ("say_hello of (load script file \"" & my_path & "\")")
the_method("Bob")
end run
on say_hello(your_name)
display dialog ("Hello, " & your_name)
end say_hello
保存されていることが条件ですが、これもよく利用します。
0 件のコメント :
コメントを投稿