Archive for 4月, 2012

2012/04/30 Microsoft Office v2011 14.2.1アップデートで辞書に変更なし

先日公開された「Microsoft Office 2011 14.2.1 更新プログラム」でアップデートされた、Excel、Outlook、PowerPoint、WordのAppleScript用語辞書にとくに変更がないことを確認しました。

2012/04/29 QuickTime Xで録画して任意のファイル名で保存

QuickTime Xで録画して任意のファイル名で保存するAppleScriptです。

QuickTime Player 7+Mac OS X 10.7で動作するScriptを見て海外の友人が「QuickTime Player Xは機能が少ないよな〜」などとぼやいていたので、この程度ならできるだろうと考えてQuickTime Player X用に書き換えてみました。

相変わらず、時間計測にお手軽にdelayコマンドを使っていますが、録画開始時のGUIアニメーションで2秒近くロスがあります。このため、指定録画時間に2秒追加しています。

スクリプト名:QuickTime Xで録画
property recTime : 10 –録画時間(秒で指定する)

set aFilepath to choose file name with prompt "ムービーを保存するファイル名を入力"
set outFilepathPOSIX to POSIX path of aFilepath

tell application id "com.apple.QuickTimePlayerX"
  –確実に処理を行うためにムービーをすべてクローズ
  
close every document without saving
  
  
–録画開始
  
set recMov to (new movie recording)
  
tell recMov to start
end tell

–録画時間経過待ち
delay (recTime + 2) –秒単位でウェイト(UI系の動作により2秒程度ロスが発生するもよう)

tell application id "com.apple.QuickTimePlayerX"
  –録画停止
  
tell recMov to stop –この操作で、QT7の設定に従ってムービーが保存される
  
  
delay 1 –時間待ち
  
  
tell document 1
    –properties
    
set aProp to properties
    
close
  end tell
end tell

set originPath to file of aProp

–QT Player 7が自動保存したファイルを移動&リネーム
–ただし、自動保存したファイルのパスと移動先の衝突判定は省略
set movieOriginalFile to POSIX path of originPath
set sText to "mv " & quoted form of movieOriginalFile & " " & quoted form of outFilepathPOSIX
do shell script sText

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

2012/04/29 Terminalで全Window中の全Tabのうち、指定タイトルのものをクローズする

Terminal.appの全Window内のTabのうち、指定タイトルのものをクローズするAppleScriptです。

Mac OS X 10.7上のTerminalの各ウィンドウには、任意のタイトルを指定できるようになっています。Mac OS X 10.6上では、カスタマイズしたタイトルについてはAppleScriptからは参照するだけでした。

term00.jpg

スクリプト名:teminal.appでtabを操作する
tell application “Terminal”
  tell window 1
    tell tab 1
      set custom title to “ぴよまる”
    end tell
    
    
tell tab 2
      set custom title to “ぴよぴよ”
    end tell
    
  end tell
end tell

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

term01.jpg

term3.jpg

Tabのクローズについては、Window内のTabを指定してclose命令を実行してみても、クローズはできない状況です。GUI Scriptingで行うのもアレなので(最前面に持ってこないと実行できないというのは、ちょっとめんどう)、別の方法でクローズを実現することに。

Terminal.appの環境設定で、シェル終了後にWindowをどうするか指定することができます。

term1.jpg

term4.jpg

「ウィンドウを閉じる」「シェルが正常終了した場合には閉じる」「ウィンドウを閉じない」の3つから選択可能。

term2.jpg

これを「ウィンドウを閉じる」に設定しておき、指定Tabに「exit」コマンド(シェルコマンド)を送ることで、指定Tabをクローズできるようになります。

まあそんなわけで、Tabのカスタム名称を文字列で指定して該当するTabをすべてクローズできるようになったわけですが、これがどの程度有用かは…………ちょっと分かりません。

スクリプト名:Terminalで全Window中の全Tabのうち、指定タイトルのものをクローズする
set targTabTitle to “ぴよまる”

tell application “Terminal”
  set tabRef to a reference to (every tab of every window whose custom title is equal to targTabTitle)
  
  
repeat with i in tabRef
    do script “exit” in i
  end repeat
  
end tell

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

2012/04/29 QuickTime 7で録画して任意のファイル名で保存

QuickTime Player 7+Mac OS X 10.7で、所定の時間だけムービー録画(おそらく、内蔵のiSight/Face Time Cameraから取り込み)を行い、指定のパスにファイル保存を行うAppleScriptです。

コメント欄の質問から、試しに組んでみましたが……意外と試行錯誤が必要でした。

所定時間だけ録画する、というScriptは簡単です。しかし、録画内容を別のパスに保存するというのがなかなかやっかいです。本来のQuickTime Playerの機能にのっとって処理することを考えるなら、指定の(あらかじめ保存してある)ムービーファイルから保存形式などを読み取って、その内容に基づいてムービーを「export」することになります。

しかし、そこまでまじめに処理をやりだすと相当の長さになってしまうため、サンプルとしてはいささかおおげさです。

そこで、とりあえずQuickTime Playerの設定に基づいて「ムービー 1.mov」などといったファイル名で保存を行わせておき、そのフルパスを取得。ムービーをクローズしたのちに、一時保存したファイルを指定パスに移動させればよいだろう、と考えてこのようなものに。

録画時間の指定にdelay命令を用いていますが、あくまで説明を簡単に行うために用いているものであって、本来はidleハンドラを使ってタイマー割り込みで実装するべきです。

スクリプト名:QuickTime 7で録画
property recTime : 3 –録画時間(秒で指定する)

set aFilepath to choose file name with prompt "ムービーを保存するファイル名を入力"
set outFilepathPOSIX to POSIX path of aFilepath

tell application id "com.apple.quicktimeplayer"
  –確実に処理を行うためにムービーをすべてクローズ
  
close every document without saving
  
  
–録画開始
  
new movie recording
  
start recording 1
end tell

–録画時間経過待ち
delay recTime –秒単位でウェイト(ちょっとバカっぽい処理。本来はタイマー割り込みで時間待ちすべき)

tell application id "com.apple.quicktimeplayer"
  –録画停止
  
stop recording 1 –この操作で、QT7の設定に従ってムービーが保存される
  
  
delay 1 –時間待ち
  
  
tell document 1
    set originPath to original file –保存先のファイルのパスを取得しておく
    
close
  end tell
end tell

–QT Player 7が自動保存したファイルを移動&リネーム
–ただし、自動保存したファイルのパスと移動先の衝突判定は省略
set movieOriginalFile to POSIX path of originPath
set sText to "mv " & quoted form of movieOriginalFile & " " & quoted form of outFilepathPOSIX
do shell script sText

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

2012/04/22 入れ子のリストから、指定フィールドでソートしてデータを返す

{{”Field1″, “Field2″, “Field3″}, {1,2,3}, {4,5,6}}のような入れ子のリストで、指定フィールド名のデータをキーとしてソートして返すAppleScriptです。

CSVファイルをparseしてリストに変換し、リストに対して指定フィールドの値でソートする場合に使用します。

返り値にはフィールドラベルを含んでいません。ソートルーチンは、中途半端な高速化を行ってあります。最速版のソートルーチンを入れ子のリストに対応させられていなかったので、shell sortの入れ子ルーチンをちょっとだけ高速化した状態です。

スクリプト名:入れ子のリストから、指定フィールドでソートしてデータを返す
set aList to {{“ID”, “名称”, “アドレス”, “URL”, “データ”}, {1, 2, 3, 4, 5}, {9, 3, 4, 5, 6}, {3, 4, 5, 6, 7}}

set sortDirecionF to false –true: ascending, false: descending

set resList to getSpecifiedFieldData(aList, “ID”, sortDirecionF) of me
–> {{9, 3, 4, 5, 6}, {3, 4, 5, 6, 7}, {1, 2, 3, 4, 5}}

–入れ子のリストから、指定フィールドのデータを取得する
–1アイテム目はフィールドラベル。2アイテム目以降をデータと見なして指定フィールドのデータをリストで返す
on getSpecifiedFieldData(aData, fieldName, sortAscendF)
  –与えられたデータaDataの1アイテム目はフィールド名と見なす
  
set fieldList to contents of first item of aData
  
set dataList to contents of items 2 thru -1 of aData
  
  
set fNum to getNumberOfField(fieldList, fieldName) of me
  
if fNum = 0 then return false
  
  
if sortAscendF = true then
    –Ascending
    
set resList to shellSortListAscending(dataList, fNum) of me
  else
    –Descending
    
set resList to shellSortListDecending(dataList, fNum) of me
  end if
  
  
–フィールドラベルを先頭に付けておく???
  
–set beginning of dataList to fieldList
  
  
return resList
  
end getSpecifiedFieldData

–与えられたリスト中における任意テキスト要素の出現アイテム番号
on getNumberOfField(aList, targFieldName)
  set aCount to 1
  
repeat with i in aList
    set j to contents of i
    
if j is equal to targFieldName then
      return aCount
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
return 0 – no hit
end getNumberOfField

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(aSortList, keyItem)
  script oBj
    property list : aSortList
  end script
  
  
set n to count oBj’s list’s items
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of list of oBj
        
set j to i
        
repeat while (j h) and ((contents of item keyItem of item (j - h + 1) of list of oBj) > (item keyItem of v))
          set (item (j + 1) of list of oBj) to (item (j - h + 1) of list of oBj)
          
set j to j - h
        end repeat
        
set item (j + 1) of list of oBj to v
      end repeat
    end if
  end repeat
  
  
return aSortList
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(aSortList, keyItem)
  script oBj
    property list : aSortList
  end script
  
  
set n to count oBj’s list’s items
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of list of oBj
        
set j to i
        
repeat while (j h) and ((contents of item keyItem of item (j - h + 1) of list of oBj) < (item keyItem of v))
          set (item (j + 1) of list of oBj) to (item (j - h + 1) of list of oBj)
          
set j to j - h
        end repeat
        
set item (j + 1) of list of oBj to v
      end repeat
    end if
  end repeat
  
  
return aSortList
end shellSortListDecending

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

2012/04/22 入れ子のリストから、指定フィールドのデータを取得する

{{”Field1″, “Field2″, “Field3″}, {1,2,3}, {4,5,6}}のような入れ子のリストで、指定フィールド名のデータを取得して返すAppleScriptです。

CSVファイルをparseしてリストに変換し、リストに対して指定フィールドの値を抽出する場合に使用します。

exceltable1.png

オリジナルのデータは、Excel(あるいはNumbersやFileMaker Pro)上で、上図のような状態になっていることを想定しています。1行目がフィールド名ラベルになっているというパターンです(緑色の箇所)。

入れ子のリストの各指定項目を取り出すような場合には、「何アイテム目を取り出す」と記述するケースが多いところですが、アイテム番号を決め打ちでは処理の柔軟性がないので、1アイテム目をフィールドラベルと見なし、そのフィールドラベルで該当する項目番号をサーチして、項目名で指定できるようにしてみました。

AppleScript中心の処理を追求すると、なるべく「テキストエディタやFileMaker Proに依存した処理を行わない」ことが重要になってきます。置換や検索、ソートなどをこれらのアプリケーションに依存していると、いつまでもそのアプリケーションが存在しない環境では自分の処理ができないことになります。

スクリプト名:入れ子のリストから、指定フィールドのデータを取得する
set aList to {{"ID", "名称", "アドレス", "URL", "データ"}, {1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7}}

set resList to getSpecifiedFieldData(aList, "名称") of me
–> {2, 3, 4}

–入れ子のリストから、指定フィールドのデータを取得する
–1アイテム目はフィールドラベル。2アイテム目以降をデータと見なして指定フィールドのデータをリストで返す
on getSpecifiedFieldData(aData, fieldName)
  –与えられたデータaDataの1アイテム目はフィールド名と見なす
  
set fieldList to contents of first item of aData
  
set dataList to contents of items 2 thru -1 of aData
  
  
set fNum to getNumberOfField(fieldList, fieldName) of me
  
if fNum = 0 then return false
  
  
set resList to {}
  
repeat with i in dataList
    set the end of resList to contents of item fNum of i
  end repeat
  
  
return resList
  
end getSpecifiedFieldData

–与えられたリスト中における任意テキスト要素の出現アイテム番号
on getNumberOfField(aList, targFieldName)
  set aCount to 1
  
repeat with i in aList
    set j to contents of i
    
if j is equal to targFieldName then
      return aCount
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
return 0 – no hit
end getNumberOfField

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

2012/04/22 入れ子のリストで、1アイテム目のアイテム数が他と同じかどうかチェック

{{1,2,3}, {4,5,6}}のような入れ子のリストで、全アイテムのアイテム数が同じかどうかチェックするAppleScriptです。基準となるアイテム数は1アイテム目({1,2,3})を用います。

CSVファイルをparseしてリストに変換し、リストに対して処理を行ってふたたびCSVファイルに書き出すような場合に使用します。合っているとtureを、そうでないとfalseを返します。たいして大きなデータを処理していなかったので、高速化処理などは一切行っていませんが……データの規模によっては高速化処理を行うことがふさわしいところです。

parseした結果としてすべてのアイテムでアイテム数が同じかどうかをチェックすることが目的です。

exceltable.png

オリジナルのデータは、Excel(あるいはNumbersやFileMaker Pro)上で、上図のような状態になっていることを想定しています。1行目がフィールド名ラベルになっているというパターンです(緑色の箇所)。

CSVからparseしたリストの項目数が合っていないと、そのあとでさまざまな処理を行おうにも……処理の内容を保証できません。そこで、まずは項目数が合っているかを検証すると安全でしょう。もちろん、フィールド付加などを行ったあとに項目数をカウントしてチェックを行うことも重要です。

ExcelなどからCSVファイルに書き出して、AppleScript側でCSVファイルをもとにデータ処理を行うケースは非常に多いです。AppleScriptの初心者が陥りがちなポイントですが……そのままExcelに対して必要なデータに(行や列を指定して)アクセスすると、当然のことながらデータ数が増えるとものすごく時間がかかります。selectionからデータを取得する例もありますが、selectionから取得できるデータの大きさに上限が存在していたりで、万能とはいえません(とくに、Mac OS X初期に開発されたExcel v.Xあたりはselectionで取得できるデータサイズの上限が小さめです)。

大量のデータ通信をアプリケーションに対して行って、それをもって「AppleScriptの処理が遅い」と判断するのは間違いです。AppleScriptの内部データ表現形式であるリスト型変数やレコードに取り込んで、その上で処理を行うのがセオリーです。

同様の例はテキストエディタのデータを処理する場合などに多々見られ……テキストエディタの編集中のファイルに対して、テキストエディタ経由でアクセスすれば当然のように遅くなります。しかし、「どのファイルを編集中なのか」という情報をテキストエディタから取得して、AppleScriptで自前でテキストファイルにアクセスして処理すれば圧倒的に高速になります。

スクリプト名:入れ子のリストで、1アイテム目のアイテム数が他と同じかどうかチェック
set aList to {{“ID”, “名称”, “アドレス”, “URL”, “データ”}, {1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7}}

–リスト中のすべての項目(データ行)のフィールド数が同じかどうかチェック
set chRes to chkItemNumInEveryLine(aList) of me
–> true

–入れ子のリストで、1アイテム目のアイテム数が他と同じかどうかチェック
on chkItemNumInEveryLine(aList)
  set aList1 to contents of first item of aList
  
set aList2 to contents of items 2 thru -1 of aList
  
  
set aList1Len to length of aList1
  
repeat with i in aList2
    set tmpLen to length of i
    
if tmpLen is not equal to aList1Len then
      return false
    end if
  end repeat
  
  
return true
end chkItemNumInEveryLine

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

2012/04/21 Safariの最前面のWindowの内容を1枚もののPDFにレンダリングしてデスクトップへ

Safariでオープン中の最前面のWindowの内容をデスクトップにPDF出力するAppleScriptです。

Web関係の仕事をすると、地味に必要になってくる「指定ページを1枚ものの画像にまとめた」PDF。Keynoteの書類などに貼り付けてページ遷移の説明を行ったりするのは、よくある話です。

Safariの最前面のWindowでオープン中のURLを取得し、CLI Webレンダラー「Wkpdf」でPDFにレンダリング出力します。

safario.png

その際に、ファイル名は日付をもとに生成。1枚ものの「長いPDF」として出力するためにWkpdfのレンダリングオプションを指定してページネーションを抑止したり、背景画像の表示をイネーブルにしたりと、見所はそのぐらいで、あとはたいしたことのないあっさりとした処理ばかりです。

Script Menuに入れて使うと便利です。ただ、使用頻度は人によって個人差がありそうなので、実用性がきわめて高い便利なScriptの割には忘れ去られそうな可能性も(自分でも、作っていたことを忘れていました)。

Wkpdf自体のインストールについては、Terminalからコマンドを叩いて行っておく必要がありますが、たいして難しくないので大丈夫でしょう。

スクリプト名:最前面のWindowの内容を1枚もののPDFにレンダリングしてデスクトップへ
tell application “Safari”
  set wCount to count every window
  
if wCount < 1 then
    display dialog “Windowが存在しません” buttons {“OK”} default button 1
    
return
  end if
  
  
tell window 1
    set aInfo to properties
  end tell
  
  
set aDoc to document of aInfo
  
  
tell aDoc
    set aURL to URL
  end tell
  
end tell

set aFileName to “webOut” & (do shell script “date +%Y%m%d%H%M%S”)
renderURLtoPDF(aURL, aFileName) of me

on renderURLtoPDF(aURL, aFileName)
  set s1Text to “cd ~/Desktop && “
  
set outPath to POSIX path of (path to desktop) & aFileName & “.pdf”
  
  
set s2Text to “wkpdf –source “ & aURL & ” –paginate false –print-background –output “ & outPath
  
  
set sAll to s1Text & s2Text
  
do shell script sAll
end renderURLtoPDF

(*
–指定URLの内容をレンダリングしてPDFに書き出す
on renderURLtoPDF(aURL, aFileName)
  set dDir to POSIX path of (path to desktop from user domain)
  set outFile to dDir & aFileName & “.pdf”
  try
    do shell script “cd /usr/local/bin && /usr/local/bin/coral -f PDF -o ” & quoted form of outFile & ” ” & aURL & ” &”
  on error
    return false
  end try
  –return outFile
end renderURLtoPDF
*)

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

2012/04/21 iPhoto, iTunes, Safariアップデートについて

iPhoto 9.2.3、iTunes 10.6.1、Safari 5.1.5の各アップデートが提供されていますが、これらのアプリケーションのAppleScript用語辞書に変更はありません。

2012/04/21 Office v.2011 Service Pack 2による変更点

2012/4/13にOffice v.2011 サービスパック2(バージョン14.2.0)がオンライン配布開始となりました。

例によって、アップデート後にAppleScript用語辞書をAS Dictionaryで書き出して,前バージョンの用語辞書の内容と比較を行ってみました。

 Excel 14.1.2→14.2 変更なし
 Outlook 14.1.2→14.2 変更あり(大規模な変更)
 PowerPoint 14.1.4→14.2 変更あり(「player」関連のオブジェクト、命令が追加に)
 Word 14.1.4→14.2 変更なし

……Outlookについては、日常的に使用していないので用語辞書変更の影響度合いはなんとも言えないのですが、SP2で加えるレベルではないほどの追加(あくまで個人的な意見)が行われています。

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″}

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

2012/04/03 AppleScriptObjC Explorer2で編集中のScriptをすべて保存してXcodeでビルド実行 v2

AppleScriptObjC Exlorer 2で、編集中のScriptをすべて保存してXcodeの最前面の(アクティブな)プロジェクトをビルドして実行するAppleScriptです。

……なるべく1行で内容が分かるように心がけていますが、さすがになんのことやら分かりにくいので、説明します。

本ScriptはMac OS X 10.6.8+Xcode 4.1+AppleScriptObjC Explorer 2 2.2.1で作成。Mac OS X 10.7上でも動くはずです。

Xcode 4.0以降でAppleScriptObjCのサポートは「悲惨」のひとことに尽きます。インデントは狂うわ、構文確認時の各種ブロックの補完は行われないわ、AppleScript構文色分けがまったくきかないわで、生産性がダダ下り。AppleScript Studioの頃には、Mac OS X 10.5の頃まではなんとか構文色分けは維持されてきたものですが……。

asoc1.png

さすがに、Xcode 4.x内蔵のテキストエディタに愛想が尽きて、AppleScriptObjC専用外部エディタである「AppleScriptObjC Explorer 2」をお買い上げ。Xcode上のファイル一覧でAppleScriptを選択した状態でコンテクストメニューを表示。「Open With External Editor」を実行してAppleScriptObjC Explorer 2でAppleScriptのテキストを編集させるようにしています。

asoc2.png

これで、構文確認時の各種ブロック(ifブロックとかrepeatブロックとかtellブロックとか)の入力補完や、AppleScript構文色分けなどが利用できます。

ただ……Mac OS X 10.6/10.7ともに、Xcodeの外部エディタとして使用した場合に、AppleScriptObjC Explorer 2からはXcodeプロジェクトのビルドやら実行やらはできません(Mac OS X 10.7上でAppleScriptエディタ上と同様にAppleScriptObjCプログラムを単体で作成・実行できます)。

そこで、AppleScriptObjC Explorer 2側からXcodeのプロジェクトをビルド&実行するようにしてみたのが本AppleScriptです。

ただし、Xcode 4.x系のAppleScript対応機能は、いまだ実装の発展途上というか、工事中で機能不全な状態なので……まともにXcodeのオブジェクト階層をたどっていく気になりません。なげやりに、GUI Scripting経由でメニュー項目を呼び出したりしています。また、ビルド&実行対象は最前面のXcodeプロジェクトであって……複数のプロジェクトをXcode上でオープンしている場合に、外部エディタで編集しているScriptの所属するプロジェクトが、Xcode上でかならずしも最前面になっているわけではありませんので……そのへんは割り切って使っています。

Xcode 4でビルドを行わせる前に、AppleScriptObjC Explorer 2側でオープン中のScriptをすべてファイル保存させています。未保存のAppleScriptが存在した場合には保存をスキップします。

AppleScriptObjC Explorer 2用のScript Menuに入れて呼び出して使っています。

スクリプト名:AppleScriptObjC Explorer2で編集中のScriptをすべて保存してXcodeでビルド実行 v2
tell application “AppleScriptObjC Explorer 2″
  set dList to every document
  
  
set sList to {}
  
repeat with i in dList
    –documentのfile属性を取得(未保存だとmissing valueになっている)  
    
set tmpFile to file of i
    
    
if tmpFile is not equal to missing value then
      tell i –保存済みのdocumentのみ保存を実行
        save
        
set the end of sList to tmpFile
      end tell
    end if
  end repeat
end tell

–複数のプロジェクトをオープンしている場合にはアクティブ(最前面)のプロジェクトを対象に
–すごい投げやりな処理……
tell application “Xcode”
  set curPrjDoc to active workspace document
  
  
–最前面のプロジェクトが、必ずしもAppleScriptObjC Explorer 2で編集中のScriptが所属するプロジェクトとは限らない
  
–編集中のScriptのファイルパスから上位フォルダに存在するXcode書類を探索して、プロジェクトを特定してもいいかも
  
–ただ、Xcodeのタコさ加減にめまいがして、真剣にXcodeをAppleScriptからつっつく気になれない……
  
  
tell curPrjDoc
    build –ビルド
  end tell
  
end tell

–GUI Scriptingから実行指定
activate application “Xcode”
tell application “System Events”
  tell process “Xcode”
    click menu item “Run” of menu 1 of menu bar item “Product” of menu bar 1
  end tell
end tell

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