Archive for 12月, 2011

2011/12/29 ファイル作成曜日に応じてラベルを付ける

指定フォルダ内の全ファイルのファイル作成日を調べ、指定曜日に作成されたものにラベルを振るAppleScriptです。

Radikoのタイマー録音を行う自作のAppleScriptObjCアプリケーションで、いろんな番組を録り貯めていたのですが……録り貯めた膨大なファイルの中から特定の番組だけを抽出するために、作成したものです。

label0.jpg

lebel.jpg

▲伊集院光「深夜の馬鹿力」(月曜日25:00〜27:00……つまり、「火曜日」)を抽出したところ。

label2.jpg

スクリプト名:ファイル作成曜日に応じてラベルを付ける
set aFol to choose folder with prompt “処理対象のファイルが入っているフォルダを選択”

set aList to {“日曜日”, “月曜日”, “火曜日”, “水曜日”, “木曜日”, “金曜日”, “土曜日”}
set aMes to “ラベルを付ける対象曜日を選択してください。”
set targDay to retNumberFromChooseFromList(aList, aMes) of me

tell application “Finder”
  tell folder aFol
    set aList to every file as alias list
  end tell
end tell

repeat with i in aList
  set aInfo to info for i
  
set sDate to creation date of aInfo
  
set dayNum to weekday of sDate as number
  
  
if dayNum = targDay then
    tell application “Finder”
      set label index of i to 2 –赤いラベル
    end tell
  end if
end repeat

–リストからの選択ダイアログで、選択したアイテムのアイテムナンバーを返す
on retNumberFromChooseFromList(aList, aMes)
  set aRes to choose from list aList with prompt aMes
  
if aRes = false then return false
  
set aRes to (contents of aRes) as string
  
set itemCount to 1
  
repeat with i in aList
    set j to contents of i
    
if j is equal to aRes then
      return itemCount
    end if
    
set itemCount to itemCount + 1
  end repeat
end retNumberFromChooseFromList

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

2011/12/27 ファイル選択ダイアログを表示して、パスを取得する(Perl中へのAppleScript埋め込み)

いつもは、AppleScriptからshellコマンドを呼び出したり、AppleScript側からの操作を行っていますが、Perlのプログラムの中にAppleScriptを埋め込んでみました。Perlではありますが、AppleScriptの文字列を埋め込んで呼び出しているため、Mac OS Xの上でしか動きません。

たまたま(久しぶりに)Perlをさわる機会があって、渡されたプログラムを見たらファイルの指定方法がめんどうだったので、中にAppleScriptを埋め込んでファイル選択ダイアログを出してファイル選択できるようにしてみました。

AppleScriptのアプレット内でファイル選択なりドラッグ&ドロップを行って、Perlのプログラムに対して引数で渡すというやりかたもあると思います。

他の言語のプログラム(Objective-Cとか)の中にAppleScriptを埋め込んで処理を行わせているものも見られるのですが、「もうちょっとなんとかしたほうが」という内容のものが多いので、自分で納得できる機能を実装してみました。

結局、AppleScriptのchoose file経由で選択したパス情報を、いったんテキストファイルに書き出して、Perlのプログラム側からテキストファイルの内容を取得する、という仕組みになっています。ファイル経由でなくても値の受け渡しができるとベストです。

#ファイル選択ダイアログを表示して、パスを取得する
getFilePath();
$ret = `cat ~/Desktop/.retval.txt`;
print $ret ;

sub getFilePath {
  system(’osascript’, ‘-e’,
  ’tell application “Finder”
  activate
  set aFile to choose file with prompt “ファイルを選択してください”
  set aPOS to POSIX path of aFile
  end tell
  do shell script “echo ” & aPOS & ” > ~/Desktop/.retval.txt”
  ’);
}

2011/12/24 AppleScriptObjCでの高速化手法〜script文によるpropertyアクセス

AppleScriptObjCでさまざまな小物ツールを作っては日常的に使っています。

ただ、困ったことに……大量のリスト型変数を扱うのに必須な、「a reference to」によるリスト型変数の間接アクセスによる高速化が、AppleScriptObjCの環境では使えません(プログラム中に記述できるものの、実行時にエラーになってしまいます)。

そこで、いろいろ高速化の手段はないものかと考えることに。

実際に、カラーピッカーで選んだ任意の色の類似色を1,000色ぐらいの色見本データからピックアップして提示する、というツールを作ってみたときに、a reference toによるアクセスができず、AppleScriptエディタ上で動作させるよりも遅くなってしまいました。これは由々しき事態です。

そこで、同じ問題に直面している人がいるのではないかと考え、海外のサイトを探し回り……MacScripterでそのものズバリの内容を見つけました。

「Objective-Cに書き換えれば高速化が……」という身もふたもない内容の投稿もありましたが、a reference toと同程度の高速化が実現できる手法を見つけ、実際に自分のコードに入れてみて効果を確認。

それが、script文によって(論理的に)別Scriptにpropertyを追い出して、別Script上のpropertyにアクセスするというものです。

script speedUp
  property aList:{}
end script

script mainScriptDelegate

  set the end of speedUp’s aList to 1
  
end script

のように間接アクセス。これで10倍以上の(こまかく計測していないのですが)スピードアップが図れました。たかだか1,000項目ぐらいではテストデータとしては小さすぎるので、数万アイテムぐらいのテストデータを作って、別途試してみたいところです。

このほか、ソートルーチンにいつものshell sortではなくCocoaベースのソートルーチンを投入してみました。ちょっとこちらの効果は定かではないのですが、前述の近似色サーチのAppleScriptObjCプログラムでは、

 スピードアップいっさいなし:8〜10秒ぐらい
 +Cocoaベースのソートを投入:3〜4秒ぐらい
 +Script文による間接アクセス適用:1秒以下

ぐらいの差が出たので、Cocoaベースのソートルーチンもそれなりに効いているのでしょう。このCocoaベースのソートルーチンは、{{age:20, aName:”ひよこさん”}, {age:43, aName:”ぴよぴよ”}}のような構造のリストをソートするものです。入れ子のリスト……というと厳密には正しくはないですね。レコードのリストというべきか。KeyItemには”age”とか”aName”といったラベルを指定します。

スクリプト名:Cocoaで入れ子のリストを昇順ソート
–Cocoaで入れ子のリストを昇順ソート(AppleScriptObjC)
on cocoaSortListAscending(theList, keyItem)
  
  
– make unique set
  
tell current application’s NSSet to set theSet to setWithArray_(theList)
  
  
– define sorting
  
tell current application’s NSSortDescriptor
    set theDescriptor to sortDescriptorWithKey_ascending_(keyItem, true)
  end tell
  
  
–sort
  
set sortedList to theSet’s sortedArrayUsingDescriptors_({theDescriptor})
  
  
return sortedList
  
end cocoaSortListAscending

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

2011/12/23 Google Maps API Web ServicesをAppleScriptから呼び出す「Location Helper」

GoogleのMaps API Web ServicesにAppleScriptから簡単にアクセスできるようにするツール「Location Helper for AppleScript」がMacApp Storeからフリーで配布されています

作者は、David Blishen氏

jhelp.jpg

同氏はほかに「JSON Helper」という同様のツールもMac AppStoreで同様にフリー配布しており、こちらも注目していました(Location HelperはJSON Helperの機能を含んでいます)。

JSON Helper/Location Helperはともにバックグラウンドで稼働するGUIなしアプリケーションであり、起動してもDockには起動表示は出ません(Mac AppStoreの評価コメントに「起動しない」といったものがありますが、完全な勘違いです)。アクティビティモニタで見ると、Location Helperが起動していることが確認できます。

それでは、Location HelperのGoogle Maps API関連の機能を実際にAppleScriptから呼び出してみましょう。

実行時には、インターネットへの接続が必要となります。

スクリプト名:現在地の緯度、経度を求める
tell application “Location Helper”
  get location coordinates
end tell
–> {35.xxxxxxxxxxxxx, 139.xxxxxxxxxxxx}

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

現在位置については、初回起動時に勝手に取得してよいかダイアログが表示されます。

loc1.jpeg

スクリプト名:現在地の海抜を求める
tell application “Location Helper”
  get location altitude
end tell
–> -1

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

-1が返ってくる場合には海抜の情報が取得できていないケース、とAppleScript用語辞書に書かれています。

スクリプト名:現在地の情報をレコード形式で取得
tell application “Location Helper”
  get location record
end tell
–> {course:-1.0, v_accuracy:-1.0, speed:0.0, timestamp:date “2011年12月23日金曜日 16:30:08″, coordinates:{35.xxxxxxxxxxxxx, 139.xxxxxxxxxxxxx}, lat:35.xxxxxx, lng:139.xxxxxxx, h_accuracy:127.0, altitude:-1}

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

スクリプト名:現在地の情報をテキスト形式で取得
tell application “Location Helper”
  get location string
end tell
–> “< +35.xxxx, +139.xxxx> +/- 127.00m (speed 0.00 mps / course -1.00) @ 2011-12-23 16:30:08 +0900″

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

スクリプト名:現在地の緯度・経度から住所情報を取得
tell application “Location Helper”
  reverse geocode location
end tell

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

スクリプト名:指定住所の緯度・経度情報を取得
tell application “Location Helper”
  geocode address “東京都新宿区西新宿三丁目20-2″ –アップルジャパン合同会社の所在地
end tell
–> {results:{{formatted_address:”日本, 東京都新宿区西新宿3丁目20−2”, address_components:{{short_name:”2”, long_name:”2”, types:{”sublocality_level_4″, “sublocality”, “political”}}, {short_name:”20”, long_name:”20”, types:{”sublocality_level_3″, “sublocality”, “political”}}, {short_name:”3丁目”, long_name:”3丁目”, types:{”sublocality_level_2″, “sublocality”, “political”}}, {short_name:”西新宿”, long_name:”西新宿”, types:{”sublocality_level_1″, “sublocality”, “political”}}, {short_name:”新宿区”, long_name:”新宿区”, types:{”locality”, “political”}}, {short_name:”東京都”, long_name:”東京都”, types:{”administrative_area_level_1″, “political”}}, {short_name:”JP”, long_name:”日本”, types:{”country”, “political”}}}, geometry:{viewport:{northeast:{lat:35.684300980292, lng:139.688203280291}, southwest:{lat:35.681603019708, lng:139.685505319709}}, location:{lat:35.682952, lng:139.6868543}, location_type:”APPROXIMATE”}, types:{”sublocality_level_4″, “sublocality”, “political”}}}, status:”OK”}

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

スクリプト名:現在地と指定の緯度・経度との距離をメートルで返す
tell application “Location Helper”
  get distance from coordinates {35.739, 139.63}
end tell
–> 911.86243144354

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

JSON Helper / Location Helperは非常に有用なツールですが、AppleScriptでGUIつきアプリケーションを作る環境AppleScriptObjCで、プログラムと一緒にLocation Helperを配布するわけにもいかないので、同等の機能を自前で実装する必要が出てくる感じでしょうか。Location Helperとまで行かないまでも、JSON Helperの機能については、別途実装したいところです。

2011/12/21 miで選択中の内容をファイルに書き出してperlのプログラムとしてterminalで実行 v2

miの最前面のドキュメント上で選択中のテキストをファイルに書き出して、perlのプログラムとしてTerminalで実行するAppleScriptのバグ修正版です。

最初のバージョンでは、改行コードがCRの状態で書き出されていたため、改行コードをLFに置換してから実行するようにしました。

初版では、選択範囲にコメント行が含まれているとエラーになっていたりしましたが、このバージョンではそういうことはありません。

スクリプト名:miで選択中の内容をファイルに書き出してperlのプログラムとしてterminalで実行 v2
tell application “mi”
  tell front document
    set this_data to selection
    
    
–エラー対策
    
if this_data = “” then
      display dialog “文字列が何も選択されていません” buttons {“OK”} default button 1 with icon 1 with title “エラー”
      
return
    end if
    
    
set this_data to this_data as Unicode text
    
  end tell
end tell

–改行コードがCRになっている部分をLFに置換(v2で追加)
set this_data to repChar(this_data, ASCII character 13, ASCII character 10) of me

set tmpPath to path to temporary items from user domain
set fStr to (do shell script “date +%Y%m%d%H%M%S”) & “.pl”
set fPath to (tmpPath as string) & fStr

write_to_fileUTF8(this_data, fPath, false) of me
do shell script “sync”

set sText to “perl -w “ & quoted form of POSIX path of fPath
doComInTerminalWindow(sText) of me

on doComInTerminalWindow(aCMD)
  tell application “Terminal”
    set wCount to count (every window whose visible is true)
    
    
– By wayne melrose
    
– Re: New window in Terminal.app
    
    
if wCount = 0 then
      –ウィンドウが1枚も表示されていない場合
      
do script aCMD
    else
      –すでにウィンドウが表示されている場合
      
do script aCMD in front window
    end if
  end tell
end doComInTerminalWindow

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_fileUTF8(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file as «class utf8» starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_fileUTF8

–文字置換ルーチン
on repChar(origText, targStr, repStr)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

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

2011/12/20 与えられたテキストをPhotoshopでRGBデータと評価してCMYK値を返す

「XX, XX, XX」の形式のテキストをparseして、RGBデータとして評価し、Photoshop CS3でRGB→CMYKの変換を行うAppleScriptです。

AppleScriptObjCでカラーデータを入力するプログラムを作っていて、CMYK→RGBの変換は割とすぐできたのですが、RGB→CMYKで手こずり……PhotoshopならAppleScript3行でできてしまう処理。そこで、データをメール経由で収集したあとに手元のプログラムで(Photoshopの機能を用いて)変換することに。

非常に簡単で楽勝でした。

スクリプト名:与えられたテキストをPhotoshopでRGBデータと評価してCMYK値を返す
set aData to "64, 128, 0"
set {cNum, mNum, yNum, kNum} to retCMYKfromRGBstr(aData)
–> {87, 36, 100, 2}

–与えられたテキストをRGBデータと評価してCMYK値を返す
on retCMYKfromRGBstr(aData)
  if aData = "" then return {false, false, false, false}
  
  
set {rDat, gDat, bDat} to divideABC(aData, ",") of me
  
  
  
tell application "Adobe Photoshop CS3"
    set myCMYKColor to convert color {class:RGB color, red:rDat, green:gDat, blue:bDat} to CMYK
    
    
set cyanNum to cyan of myCMYKColor
    
set magentaNum to magenta of myCMYKColor
    
set yellowNum to yellow of myCMYKColor
    
set kuroNum to black of myCMYKColor
  end tell
  
  
set cyanNum to round cyanNum rounding as taught in school –四捨五入
  
set magentaNum to round magentaNum rounding as taught in school –四捨五入
  
set yellowNum to round yellowNum rounding as taught in school –四捨五入
  
set kuroNum to round kuroNum rounding as taught in school –四捨五入
  
  
return {cyanNum, magentaNum, yellowNum, kuroNum}
  
end retCMYKfromRGBstr

–文字列をparseする(R,G,B)の文字データを処理するためのもの
on divideABC(aStr, aDelim)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set tList to every text item of aStr
  
set AppleScript’s text item delimiters to curDelim
  
  
copy tList to {item1, item2, item3}
  
try
    set item1 to item1 as integer
    
set item2 to item2 as integer
    
set item3 to item3 as integer
  on error
    return false
  end try
  
  
return {item1, item2, item3}
end divideABC

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

これを、Photoshop CS3からPhotoshop Elements 6に変更してみたら……きちんと動作するのですが、計算結果がPhotoshop CS3とは異なりました。

スクリプト名:与えられたテキストをPhotoshop ElementsでRGBデータと評価してCMYK値を返す
set aData to "64, 128, 0"
set {cNum, mNum, yNum, kNum} to retCMYKfromRGBstr(aData)
–> {77, 27, 100, 14}

–与えられたテキストをRGBデータと評価してCMYK値を返す
on retCMYKfromRGBstr(aData)
  if aData = "" then return {false, false, false, false}
  
  
set {rDat, gDat, bDat} to divideABC(aData, ",") of me
  
  
  
tell application id "com.adobe.PhotoshopElements" –Phothop Elements
    set myCMYKColor to convert color {class:RGB color, red:rDat, green:gDat, blue:bDat} to CMYK
    
    
set cyanNum to cyan of myCMYKColor
    
set magentaNum to magenta of myCMYKColor
    
set yellowNum to yellow of myCMYKColor
    
set kuroNum to black of myCMYKColor
  end tell
  
  
set cyanNum to round cyanNum rounding as taught in school –四捨五入
  
set magentaNum to round magentaNum rounding as taught in school –四捨五入
  
set yellowNum to round yellowNum rounding as taught in school –四捨五入
  
set kuroNum to round kuroNum rounding as taught in school –四捨五入
  
  
return {cyanNum, magentaNum, yellowNum, kuroNum}
  
end retCMYKfromRGBstr

–文字列をparseする(R,G,B)の文字データを処理するためのもの
on divideABC(aStr, aDelim)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set tList to every text item of aStr
  
set AppleScript’s text item delimiters to curDelim
  
  
copy tList to {item1, item2, item3}
  
try
    set item1 to item1 as integer
    
set item2 to item2 as integer
    
set item3 to item3 as integer
  on error
    return false
  end try
  
  
return {item1, item2, item3}
end divideABC

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

2011/12/18 miで選択中の内容をファイルに書き出してperlのプログラムとしてterminalで実行

miでオープン中の最前面のドキュメントで選択中のテキストを書き出して、Terminalでperlのプログラムとして実行するAppleScriptです。

コメント行を含む内容を実行するとエラーになるのはなぜなんでしょう?(Perl詳しくないもんで)

mi21.jpg

mi11.jpg

スクリプト名:miで選択中の内容をファイルに書き出してperlのプログラムとしてterminalで実行
tell application “mi”
  tell front document
    set this_data to selection
    
    
–エラー対策
    
if this_data = “” then
      display dialog “文字列が何も選択されていません” buttons {“OK”} default button 1 with icon 1 with title “エラー”
      
return
    end if
    
    
set this_data to this_data as Unicode text
    
  end tell
end tell

–set this_data to “print ‘test’;”

set tmpPath to path to temporary items from user domain
set fStr to (do shell script “date +%Y%m%d%H%M%S”) & “.pl”
set fPath to (tmpPath as string) & fStr

write_to_fileUTF8(this_data, fPath, false) of me
do shell script “sync”

set sText to “perl -w “ & quoted form of POSIX path of fPath
doComInTerminalWindow(sText) of me

on doComInTerminalWindow(aCMD)
  tell application “Terminal”
    set wCount to count (every window whose visible is true)
    
    
– By wayne melrose
    
– Re: New window in Terminal.app
    
    
if wCount = 0 then
      –ウィンドウが1枚も表示されていない場合
      
do script aCMD
    else
      –すでにウィンドウが表示されている場合
      
do script aCMD in front window
    end if
  end tell
end doComInTerminalWindow

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_fileUTF8(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file as «class utf8» starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_fileUTF8

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

2011/12/12 選択中のボックスを表に再構成する

OmniGraffleで作成した表をいったん分解して、バラバラにした状態のものを再度「表」に再構成するAppleScriptです。

まずは、OmniGraffle上で表を作成するところから……

tbl1.jpg

完成時のサイズの見当をつけるために、ダミーのボックスをOmniGraffle上で作成し、選択状態にしておく。

tbl2.jpg

Numbers上に表に入れるデータを入力して、表に入れるデータの範囲を選択状態にしておく。

tbl3.jpg

NumbersのデータからOmniGraffle書類上に表を作成するAppleScriptを実行し、OmniGraffle上で表を作成。

tbl4.jpg

いったん作成した「表」を「グループ解除」コマンドで……

tbl5.jpg

再度、バラバラに。

この状態で、本AppleScriptを実行すると……

tbl6.jpg

表の縦、横のセル数をダイアログで聞いてくるので、それに対して適切に入力すると……

tbl7.jpg

ふたたび、表に戻すことができます。このサンプルがやっつけで作ったもののためか、ヘッダー行が入れ替わっていますが……実際に作業で再編集した表は、表の下に新規行を追加するパターンがほとんどでした。

スクリプト名:選択中のボックスを表に再構成する
–選択中のボックスを表に再構成する
tell application “OmniGraffle 5″
  tell front window
    set aSel to selection
    
    
–ここ、計算で誤差を無視して縦x横のオブジェクト数をカウントするとベスト
    
set aText to (text returned of (display dialog “縦x横のセル数を入力” default answer “2×2″))
    
set aRes to divideYxX(aText) of me
    
if aRes = false then
      display dialog “入力内容に誤りがありました。” buttons {“OK”} default button 1 with icon 1
      
return
    end if
    
    
try
      assemble aSel table shape aRes
    end try
    
  end tell
end tell

–「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返す
on divideYxX(aStr)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to “x”
  
set tList to every text item of aStr
  
set AppleScript’s text item delimiters to curDelim
  
  
copy tList to {item1, item2}
  
try
    set item1 to item1 as integer
    
set item2 to item2 as integer
  on error
    return false
  end try
  
  
return {item1, item2}
  
end divideYxX

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

2011/12/12 「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返す

「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返すAppleScriptです。

ダイアログから縦横の表のサイズを文字で入力させるインタフェースを作成したときに、そのparse用として作成したものです。たいしたものではありません。

スクリプト名:「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返す
set aStr to “3×4″
set aRes to divideYxX(aStr) of me
–> {3, 4}

–「YxX」の形式の文字列を、{Y,X}のように数字のリストにして返す
on divideYxX(aStr)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to “x”
  
set tList to every text item of aStr
  
set AppleScript’s text item delimiters to curDelim
  
  
copy tList to {item1, item2}
  
try
    set item1 to item1 as integer
    
set item2 to item2 as integer
  on error
    return false
  end try
  
  
return {item1, item2}
  
end divideYxX

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

2011/12/12 「A x B = C」の数の組み合わせを求める

与えられた数を「A x B」で構成する2つの数の組み合わせをすべて求めるAppleScriptです。

数学的な公式とか、特殊な計算などいっさいなしで、単にループで回しつつ除算を行ってあまりが発生しない数をリストアップしているだけです。

OmniGraffleで表を作成し、グループ化を解除して表の中をいろいろ編集して、再度「表」として構成するさいに、selectionから自動で構成セル数を計算できないか……などと調べている最中に作成したものです。

Finder上に並んでいるアイコンの座標および大きさから縦横のセル数を求めるAppleScriptなど、過去にこのようなものは作ったことがないわけではないのですが、(超高性能なAppleScriptのプログラムについては)いつも「勢い」で一気に作ってしまうので、案外使い回しが利きません。

スクリプト名:「A x B = C」の数の組み合わせを求める
set c to 24
set aResList to calcAxB(c) of me
–> {{24, 1}, {12, 2}, {8, 3}, {6, 4}, {4, 6}, {3, 8}, {2, 12}, {1, 24}}

–「A x B = C」の数の組み合わせを求める
on calcAxB(aMountNum)
  set aList to {}
  
  
repeat with i from 1 to aMountNum
    set aMod to aMountNum mod i
    
if aMod = 0 then
      set aDiv to aMountNum div i
      
set the end of aList to {aDiv, i}
    end if
  end repeat
  
  
return aList
  
end calcAxB

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

2011/12/10 Skimで、現在表示中のページを別ファイルにPDF形式で書き出す

オープンソースのPDFビューワー「Skim」で、現在表示中のPDFのページを別のPDFに書き出すAppleScriptです。

最初は、現在表示中のページだけを残して、他のページを「削除する」AppleScriptを書いていたのですが、Skimではpageを削除できないことにあとから気付きました。

そこで、急遽「ページごとに別ファイルに書き出す」Scriptを機能ダウンさせて現在のページのみPDFで別ファイルに書き出すように変更。

スクリプト名:Skimで、現在表示中のページを別ファイルにPDF形式で書き出す
tell application “Skim”
  tell document 1
    set aBounds to selection bounds
    
tell current page
      set grabData to grab for aBounds as PDF
    end tell
  end tell
end tell

set newAlias to choose file name
write_to_file(grabData, newAlias, false) of me

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_file(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_file

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

こちらが、動かないほうのバージョンです。コンパイル(構文確認)も通るのですが、アプリケーション側がサポートしていないオブジェクトとコマンドの組み合わせだと動かないという、いい見本です。

スクリプト名:Skimで、現在表示中のページ以外をすべて削除する(ダメ)
–そもそも、pageをdeleteすることができなかった
–→ 現在のページだけをExportする方向で書き直し

tell application “Skim”
  tell document 1
    set curP to index of current page –現在表示中のページ番号(index)を取得
    
set totalP to count every page –全ページ数を取得
    
    
    
if totalP = 1 then
      display dialog “1ページしか存在しない書類は処理対象外です” buttons {“OK”} default button 1 with icon 1
      
return
    end if
    
    
if curP = 1 then
      –現在表示中のページが1ページ目だった場合の処理
      
delete (pages 2 thru -1)
      
    else if curP = totalP then
      –現在表示中のページが最終ページだった場合の処理
      
delete (pages 1 thru -2)
      
    else
      –その他 通常処理
      
delete (pages 1 thru (curP - 1))
      
delete (pages (curP + 1) thru -1)
      
    end if
    
  end tell
end tell

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

2011/12/10 SafariでNHKのラジオ局を選局する

Safariで指定のNHKラジオ局をオープンするAppleScriptです。

NHKが行っているインターネットストリーミングラジオ放送「らじる★らじる」のURLをオープンし、ページ内のJavaScriptを呼び出してラジオ局の切り換えを行います。この手の処理の「お約束」で、サイト側の構成が変わった場合には修正を行う必要があります。

radiko1.jpg

自分で使うためだけに作ったRadikoの録音用アプリ(AppleScriptObjCで作成)に、NHKラジオの録音用機能を追加するために作成したものです。

radiko3.jpg

居間でテレビにつないだMac miniが、本アプリケーションを毎週実行してラジオを録音しています。QuickTime Playerの問題で、たま〜に録音を失敗するのがご愛嬌ですが…………。

radiko2.jpg

Safariをコントロールしてラジオ局の放送を聴取し、システムの設定を変更してQuickTime Player X経由で録音を行うものです。

スクリプト名:SafariでNHKのラジオ局を選局する

–set rRes to openNHKstations(”r1″) of me –NHK第一
–set rRes to openNHKstations(”r2″) of me –NHK第二
set rRes to openNHKstations(“fm”) of me –NHK-FM

–NHKのラジオ局を選局する
on openNHKstations(aStation)
  try
    tell application “Safari”
      close every document
      
set nDoc to make new document with properties {URL:“http://www3.nhk.or.jp/netradio/”}
      
      
page_loaded(10) of me
      
      
do JavaScript “RRopen(’” & aStation & “‘);” in document 1
    end tell
    
    
return true
    
  on error
    return false
  end try
end openNHKstations

on page_loaded(timeout_value)
  delay 2
  
repeat with i from 1 to the timeout_value
    tell application “Safari”
      if (do JavaScript “document.readyState” in document 1) is “complete” then
        return true
      else if i is the timeout_value then
        return false
      else
        delay 1
      end if
    end tell
  end repeat
  
return false
end page_loaded

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

2011/12/06 Numbersで選択中のデータをもとにOmniGraffleの選択中のグラフィックのサイズで表作成

Numbers上で選択中のデータをもとに、OmniGraffleで選択中のグラフィックのサイズの表を作成するAppleScriptです。

Numbersのテーブル上で表を作成する対象のデータを選択しておきます。OmniGraffleの最前面の書類上で、四角のボックスを作成して選択状態にしておきます。

この状態で本AppleScriptを実行すると、OmniGraffle上で四角のボックスを削除したうえで、起点やサイズなどを参考にしつつ表を作成します。

スクリプト名:Numbersで選択中のデータをもとにOmniGraffleの選択中のグラフィックのサイズで表作成
property cellYohakuPoint : 3 –セル内の余白(上下/左右を同値で指定)
property textPointSize : 12 –セル内の文字サイズ(ポイント数)
property textFontName : "HiraKakuProN-W3" –セル内のフォント
property headerFillCol : {0.901961, 0.901961, 0.901961} –ヘッダー行の塗り色(うすいグレー)

–Numbersの選択範囲からデータを取得する(Excel-Like Nested List Format)
set aList to retExcelLikeStyleDataFromNumbersSelection() of me

set heightOfList to length of aList –データの高さ(行数)を取得
set widthOfList to length of first item of aList –データの幅(列数)を取得

–Omni Graffleの選択オブジェクト情報を取得
tell application "OmniGraffle 5"
  tell front window
    set aSel to selection
    
set aaSel to first item of aSel
    
    
set {xSize, ySize} to size of aaSel
    
set {xOrigPos, yOrigPos} to origin of aaSel
    
    
delete aaSel –位置指定用のオブジェクトを削除
    
  end tell
end tell

–表のセルの大きさを計算する
set xStep to xSize / widthOfList
set yStep to ySize / heightOfList

–テーブル用のオブジェクトリスト
set tObjList to {}

tell application "OmniGraffle 5"
  tell canvas of front window
    
    
repeat with y from 1 to heightOfList
      repeat with x from 1 to widthOfList
        
        
set aText to contents of item x of item y of aList
        
        
–Numbersの表の空きセルからデータを取ったときに数値の0.0が返る現象への対策
        
if aText = 0.0 then
          set aText to ""
        end if
        
        
set tmpXpos to xOrigPos + ((x - 1) * xStep)
        
set tmpYpos to yOrigPos + ((y - 1) * yStep)
        
        
–セルを作成する
        
if y = 1 then
          –ヘッダー行の場合にはセル内を指定色で塗る
          
set aCellObj to make new shape at end of graphics with properties {fill:solid fill, fill color:headerFillCol, draws shadow:false, size:{xStep, yStep}, side padding:cellYohakuPoint, thickness:0.25, autosizing:vertically only, vertical padding:cellYohakuPoint, origin:{tmpXpos, tmpYpos}, text:{text:aText, font:textFontName, size:textPointSize}}
          
        else
          –通常行はセル内を塗りつぶさない
          
set aCellObj to make new shape at end of graphics with properties {fill:no fill, draws shadow:false, size:{xStep, yStep}, side padding:cellYohakuPoint, thickness:0.25, autosizing:vertically only, vertical padding:cellYohakuPoint, origin:{tmpXpos, tmpYpos}, text:{text:aText, font:textFontName, size:textPointSize}}
          
        end if
        
        
set the end of tObjList to aCellObj
        
      end repeat
    end repeat
    
    
–セルを表に合成する
    
assemble tObjList table shape {heightOfList, widthOfList}
    
  end tell
end tell

–Numbersで選択範囲を縦に区切ったリストを返す
on retExcelLikeStyleDataFromNumbersSelection()
  
  
tell application "Numbers"
    tell document 1
      tell sheet 1
        tell table 1
          set selList to value of every cell of selection range –選択範囲のデータを取得
          
          
set selName to name of selection range –選択範囲のrange情報を取得
          
set {s1, s2} to parseByDelim(selName, ":") of me
          
          
–始点の情報を取得する
          
set s1Row to (address of row of range s1) as integer
          
set s1Column to (address of column of range s1) as integer
          
          
–終点の情報を取得する
          
set s2Row to (address of row of range s2) as integer
          
set s2Column to (address of column of range s2) as integer
          
          
–選択範囲の情報を取得する
          
set selHeight to s2Row - s1Row + 1 –高さ(Height of selection range)
          
set selWidth to s2Column - s1Column + 1 –幅(Width of selection range)
          
        end tell
      end tell
    end tell
  end tell
  
  
set aLen to length of selList
  
set aaLen to selHeight
  
  
set bList to {}
  
repeat with i from 1 to aaLen
    set aHoriList to {}
    
    
repeat with ii from 1 to selWidth
      set j1 to ii + (i - 1) * selWidth
      
set tmpCon to contents of item j1 of selList
      
      
set aClass to class of tmpCon
      
if aClass = number or aClass = integer or aClass = real then
        set tmpCon to tmpCon as integer
      end if
      
      
set the end of aHoriList to tmpCon
    end repeat
    
    
set the end of bList to aHoriList
  end repeat
  
  
return bList
  
end retExcelLikeStyleDataFromNumbersSelection

on parseByDelim(aData, aDelim)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set dList to text items of aData
  
set AppleScript’s text item delimiters to curDelim
  
return dList
end parseByDelim

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

2011/12/06 最前面のドキュメントの全Canvas上のグラフィックがそれぞれグループ化されていないかチェック。されていなければグループ化

OmniGraffleで、最前面のドキュメントの全Canvas(ページ)上のグラフィックがそれぞれグループ化されているかを確認するAppleScriptです。

グループ化されていない場合には、強制的にグループ化します。

スクリプト名:最前面のドキュメントの全Canvas上のグラフィックがそれぞれグループ化されていないかチェック。されていなければグループ化
tell application "OmniGraffle 5"
  tell front document
    set cList to every canvas
    
set nList to name of every canvas
  end tell
  
  
–書き出し対象のドキュメントを特定する
  
set curDoc to document of front window
  
  
–ドキュメント内のすべてのCanvas(ページ)
  
repeat with i in nList
    set j to contents of i
    
    
set aRes to checkAGroupInCanvas(j, curDoc) of me
    
    
–グループ化され切っていないCanvasがあったらグループ化
    
if aRes = false then
      –表示を切り替えてグループ化      
      
set canvas of front window to canvas j of curDoc
      
      
tell canvas of front window
        assemble every graphic –これでグループ化
      end tell
    end if
    
  end repeat
  
end tell

–指定ドキュメントの指定Canvas上のグラフィックがグループ化されていないか(1つになっていないか)チェックして返す
on checkAGroupInCanvas(aCanvasName, curDoc)
  tell application "OmniGraffle 5"
    tell canvas aCanvasName of curDoc
      set gList to every graphic
      
      
set gLen to length of gList
      
      
if gLen > 1 then
        return false
      else if gLen = 1 then
        return true
      end if
      
    end tell
  end tell
end checkAGroupInCanvas

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

2011/12/06 通常版のOmniGraffleでProfessional版でしか作れない「表」を作る

OmniGraffleで、Professional版でしか作れない「表」を作成するAppleScriptです。

AppleScript用語辞書を調べてみたら、OmniGraffleとOmniGraffle Professionalでは差がないことが判明。GUIがないだけで、中身は存在するようでした。

そこで、OmniGraffle Professionalの試用版を利用して表の作成AppleScriptを作成し、それをOmniGraffle用にtellブロックだけ書き換えてみたら……問題なく動きました。

作れますね、表。

スクリプト名:通常版のOmniGraffleでProfessional版でしか作れない「表」を作る
–通常版のOmniGraffleでProfessional版でしか作れない「表」を作る
tell application “OmniGraffle 5″
  tell canvas of front window
    make new shape at end of graphics with properties {fill:no fill, draws shadow:false, size:{197.499985, 18.0}, side padding:0, thickness:0.25, autosizing:vertically only, vertical padding:0, origin:{103.000008, 46.0}, text:{text:“ああああああ”, font:“HiraKakuProN-W3″}}
    
make new shape at end of graphics with properties {fill:no fill, draws shadow:false, size:{197.500015, 18.0}, side padding:0, thickness:0.25, autosizing:vertically only, vertical padding:0, origin:{300.5, 46.0}, text:{text:“いいいいい”, font:“HiraKakuProN-W3″}}
    
make new shape at end of graphics with properties {fill:no fill, draws shadow:false, size:{197.499985, 18.0}, side padding:0, thickness:0.25, autosizing:vertically only, vertical padding:0, origin:{103.000008, 64.0}, text:{text:“ うううううう”, font:“HiraKakuProN-W3″}}
    
make new shape at end of graphics with properties {fill:no fill, draws shadow:false, size:{197.500015, 36.0}, side padding:0, thickness:0.25, autosizing:vertically only, vertical padding:0, origin:{300.5, 64.0}, text:{text:“えええええええええええええええええええええええええええええ”, font:“HiraKakuProN-W3″}}
    
    
assemble (graphics -4 through -1) table shape {2, 2}
  end tell
end tell

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

2011/12/06 現在表示中のCanvasに存在しているラインのうち青いものに影を付ける

OmniGraffleで、現在表示中のCanvasに存在しているラインのうち青いものだけに影を付けるAppleScriptです。

矢印だけに影を付けるとか、さまざまなフィルター参照が使えると便利なことでしょう。

スクリプト名:現在表示中のCanvasに存在しているラインのうち青いものに影を付ける
tell application “OmniGraffle 5″
  tell canvas of front window
    tell (every line whose stroke color is equal to {0.0, 0.0, 1.0}) –フィルタ参照で青いものだけ抽出
      set draws shadow to true
    end tell
  end tell
end tell

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

2011/12/06 リンクつき目次を作成する v1

OmniGraffleで、リンクつき目次を作成するAppleScriptです。

目次を作るAppleScriptで、単なるテキストベースの目次を作成できました。

この、目次は単に「ページ数、タブ、タイトル、改行」の組み合わせで構成されているものですが、これを1行ごとに独立したオブジェクトに分解して、テキストを突っ込み、各ページへのリンクを埋め込みます。

OmniGraffle書類の状態では、あまりご利益はありませんが……OmniGraffleからPDFに書き出せば、リンクが有効な目次つきのPDFになるわけで、けっこう有用です。

スクリプト名:リンクつき目次を作成する v1
–選択中のテキストボックスの内容を行単位で分割して、それぞれを単独のテキストボックスに細分化して、
–「ページ番号 各ページのタイトル」と仮定してページ番号を取り出し、各ページにリンクを張る

tell application "OmniGraffle 5"
  
  
–選択中のテキストボックスの情報を取得
  
tell front window
    –選択中のオブジェクトを取得
    
set aSel to selection
    
if aSel = {} then return
    
set aaSel to first item of aSel
    
    
–選択中のオブジェクトの各種情報を取得する
    
set aText to text of aaSel
    
set {xSize, ySize} to size of aaSel –結果はPointで返る
    
set {xPos, yPos} to origin of aaSel –結果はPointで返る
    
    
set aFontName to font of text of aaSel
    
set aFontSize to size of text of aaSel
    
    
set aText to text of aaSel
    
    
delete aaSel –選択しておいたテキストボックスを削除する
    
  end tell
  
  
–Canvasの大きさを取得(目下、とくに意味なし)  
  
tell canvas of front window
    set {xCanvasSize, yCanvasSize} to page size –結果はPointで返る
    
–> {1155.0, 783.0}
  end tell
  
  
  
set aList to paragraphs of aText
  
set paraCount to length of aList
  
  
  
–書類および各Canvasの情報を取得する
  
set targDoc to document of front window
  
set canvasList to every canvas of targDoc
  
  
–行単位の部品のサイズを計算する
  
set aXsize to xSize
  
set aYsize to ySize / paraCount
  
  
–行単位でループ
  
set aCount to 0
  
repeat with i in aList
    set j to contents of i
    
    
if length of j > 1 then –改行のみの行をスキップ(手作業をしていると先頭とか終末に不意に入ってしまうことがある。それを無視する処理)
      
      
–テキスト各行の冒頭に数字でページ数が振ってあるものと想定
      
set pNum to first word of j
      
set linkTargCanvas to contents of item (pNum as number) of canvasList
      
      
–座標計算
      
set tmpX to xPos
      
set tmpY to yPos + (aYsize * aCount)
      
      
–テキストボックス(自称)を作成(OmniGraffleにそんなオブジェクトはない。みんな図形)
      
tell canvas of front window
        set aShape to make new shape at end of graphics with properties {stroke color:{0.0, 0.0, 0.0}, thickness:0, fill:no fill, draws shadow:false, origin:{tmpX, tmpY}, size:{aXsize, aYsize}, text:{text:j, font:aFontName, size:aFontSize, alignment:left}}
        
        
–jumpは、shapeを作成したあとで指定する必要があった(作成時にpropertyで一緒に突っ込むと無視された)
        
set (jump of aShape) to linkTargCanvas
      end tell
      
    end if
    
    
set aCount to aCount + 1
    
  end repeat
  
end tell

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

2011/12/06 Canvasの大きさを取得(Pointで返る)

OmniGraffleで、現在表示中のCanvasの大きさを取得するAppleScriptです。

結果は、Pointで返ります。

スクリプト名:Canvasの大きさを取得(Pointで返る)
tell application "OmniGraffle 5"
  tell canvas of front window
    set {xCanvasSize, yCanvasSize} to page size –結果はPointで返る
    
–> {1155.0, 783.0}
    
  end tell
end tell

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

2011/12/06 選択中のテキストから情報を取得

OmniGraffleで、選択中のテキストから情報を取得するAppleScriptです。

om30.jpg

スクリプト名:選択中のテキストから情報を取得
tell application "OmniGraffle 5"
  tell front window
    set aSel to selection
    
set aaSel to first item of aSel
    
    
set aText to text of aaSel
    
–> "ぴよ〜"
    
    
set {xSize, ySize} to size of aaSel
    
–> {427.0, 216.0}
    
set {xPos, yPos} to origin of aaSel
    
–> {173.785186767578, 144.184143066406}
    
    
set aFontName to font of text of aaSel
    
–> "HiraKakuProN-W3"
    
set aFontSize to size of text of aaSel
    
–> 144.0
  end tell
end tell

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

2011/12/03 リスト項目のシャッフル

リスト項目のシャッフルを行うAppleScriptです。

「リスト項目のシャッフル」という処理自体は、さほど出現頻度の高いものではありません。だいたい、私がいままで作ったことがなかったぐらいなので、この先も作る必要に迫られる可能性は低そうです。

この手の処理は、1項目呼び出すたびにランダム取り出しをするようなケースが多いので、リスト内容をすべて一括でシャッフルするような処理を書くケースは少ないでしょう。

ただ、それでも「ない」とも言い切れないので試しに組んでみました。予想行数は10行ぐらいだったのですが、予想を大幅に超え、書いているうちにトンでもない行数になってしまいました。

当初は投機的にランダムに項目を抽出して、抽出ずみリストに存在する場合には選び直し……という処理を行っていたのですが(→ 投機的処理)、3,000項目ぐらいのリストなら我慢できたものの、5,000項目を超えると遅く感じられました(MacBook Pro 2010 Core i7 2.66GHz+Mac OS X 10.6.8+日常的なアプリケーション立ち上げまくり環境にて検証)。

投機的処理がどのぐらい「無駄な選び直し」を行っているかを調べてみたところ、だいたい与えた項目数の5〜10倍ぐらい無駄な処理を行っていました。この「無駄」がなければ5〜10倍ぐらいは高速に処理できる可能性があったわけです。

そこで、いいかげんに書かないで地道に処理を行ってみたところ、投機的な処理の3倍ぐらいは高速になりました(→ 地道処理)。頻繁にリストの分割/連結を行うので、このあたりのオーバーヘッドが大きそうですが、それでもアイテム数の回数分しかループを回さないので無駄が少なくてすんでいます。

g1.jpg

t1.jpg

ちなみに、投機的処理を無理矢理「a reference to」による(なげやりな)高速化を施してみたところ、処理時間はずいぶんと改善はしたものの、それでも地道処理に及ばなかったので、地道処理を採用するのがよいのでしょう。

g2.jpg

t2.jpg

めんどくさかったので、地道処理を高速化させるパターンは試しませんでした。

一般的に、AppleScriptでリスト型変数を使って処理を行う場合には、だいたい5,000項目のデータ数がひとつの目安であり、それを超える場合には高速化処理を真剣に検討する必要があります。つまり、ズボラに組むときには、あまり大量のデータ処理を行わないことが重要、ということです。

スクリプト名:リスト項目のシャッフル(投機的処理)
–テスト用に1〜1000までの数字のリストを作成
set aList to {}
repeat with i from 1 to 10000
  set the end of aList to i
end repeat

set t1 to current date
set aRes to length of (retShuffledList(aList) of me)
set t2 to current date

return (t2 - t1)

on retShuffledList(aList)
  
  
–出力用リスト
  
set bList to {}
  
  
set aMax to length of aList
  
set curCount to 0
  
  
repeat
    –ランダム抽出および出力用リスト内の存在確認
    
set anItem to contents of some item of aList –項目のランダム抽出
    
if anItem is not in bList then
      set the end of bList to anItem
      
set curCount to curCount + 1
    end if
    
    
–終了?
    
if curCount = aMax then
      exit repeat
    end if
    
  end repeat
  
  
return bList
  
end retShuffledList

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

スクリプト名:リスト項目のシャッフル(地道処理)
–テスト用に1〜1000までの数字のリストを作成
set aList to {}
repeat with i from 1 to 10000
  set the end of aList to i
end repeat

set t1 to current date
set aRes to length of (retShuffledList(aList) of me)
set t2 to current date

return (t2 - t1)

on retShuffledList(origList)
  
  
copy origList to aList
  
  
  
–出力用リスト
  
set bList to {}
  
  
set aMax to length of aList
  
  
repeat aMax times
    –ランダム抽出および出力用リスト内の存在確認
    
set curLen to (length of aList)
    
if curLen > 1 then
      –通常処理
      
set aRandom to random number from 1 to curLen
      
set anItem to contents of item aRandom of aList
      
      
set the end of bList to anItem
      
      
set aList to delListItem(aList, aRandom) of me
    else
      –最後の項目の場合の例外処理
      
set the end of bList to contents of first item of aList
    end if
  end repeat
  
  
return bList
  
end retShuffledList

–リストから指定のアイテムを削除する
on delListItem(aList, anItemNo)
  set newList to {}
  
  
set aLen to length of aList
  
if anItemNo = 1 then
    set newList to contents of items 2 thru -1 of aList
  else if anItemNo = aLen then
    set newList to contents of items 1 thru -2 of aList
  else
    set p1List to contents of items 1 thru (anItemNo - 1) of aList
    
set p2List to contents of items (anItemNo + 1) thru -1 of aList
    
set newList to p1List & p2List
  end if
  
  
return newList
  
end delListItem

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

スクリプト名:リスト項目のシャッフル(投機的処理) +高速化
global aList, aList_r
global aaaList, aaaList_r
global bList, bList_r

–テスト用に1〜1000までの数字のリストを作成
set aList to {}
set aList_r to a reference to aList

repeat with i from 1 to 10000
  set the end of aList_r to i
end repeat

set t1 to current date
set aRes to length of (retShuffledList(aList_r) of me)
set t2 to current date

return (t2 - t1)

on retShuffledList(aaList)
  
  
copy aaList to aaaList
  
set aaaList_r to a reference to aaaList
  
  
–出力用リスト
  
set bList to {}
  
set bList_r to a reference to bList
  
  
set aMax to length of aaaList_r
  
set curCount to 0
  
  
repeat
    –ランダム抽出および出力用リスト内の存在確認
    
set anItem to contents of some item of aaaList_r –項目のランダム抽出
    
if anItem is not in bList_r then
      set the end of bList_r to anItem
      
set curCount to curCount + 1
    end if
    
    
–終了?
    
if curCount = aMax then
      exit repeat
    end if
    
  end repeat
  
  
return bList
  
end retShuffledList

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

2011/12/03 リストから指定のアイテムを削除する(1項目)

指定リストから指定番号のアイテムを削除するAppleScriptです。

ここで言う「指定番号」とは、アイテム番号のことです。

書き捨てレベルのものなので、よく書いてしまうのですが……何らかの巨大なAppleScriptの必須部品として利用するものの、それ自体をsaveするほどでもないので本Blogに掲載していなかったという……。

スクリプト名:リストから指定のアイテムを削除する(1項目)

set aList to {1, 2, 3, 4, 5}
set bList to delListItem(aList, 4) of me
–> {1, 2, 3, 5}

–リストから指定のアイテムを削除する
on delListItem(aList, anItemNo)
  set newList to {}
  
  
set aLen to length of aList
  
if anItemNo = 1 then
    set newList to contents of items 2 thru -1 of aList
  else if anItemNo = aLen then
    set newList to contents of items 1 thru -2 of aList
  else
    set p1List to contents of items 1 thru (anItemNo - 1) of aList
    
set p2List to contents of items (anItemNo + 1) thru -1 of aList
    
set newList to p1List & p2List
  end if
  
  
return newList
  
end delListItem

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

2011/12/03 OmniGraffleで各ページに用意したtitleから、目次を作成する

OmniGraffleで、各ページ(Canvas)上に用意したtitleから、目次を作成するAppleScriptです。

OmniGraffleで仕様書を作成したときに、目次の自動生成も行えないと不便です。しかし、OmniGraffleにはタイトル用の専用オブジェクトなどという便利なものはありません。

そこで、なければ自分で勝手に作ってしまおうというわけです。

各ページ(Canvas)上に「URL」に「title」という文字を指定したテキストのオブジェクトを配置し、そこに各ページの題名を入れておくことにしました。これを(勝手に)タイトル用のオブジェクトとみなすことにしました。

本AppleScriptはこの各ページのtitleの文字列を読み取ってページ数と組み合わせて目次のテキストデータをクリップボードに転送します。以前にKeynote用に作成しておいたものをほぼそのままOmniGraffle用に転用しています。

スクリプト名:各ページに用意したtitleから、目次を作成する
set separatorC to “(” –「(1/3)」の形式で連番を振っている場合の最初のカッコを書いておく。ここでは、全角カッコを想定

–OmniGraffleでオープン中のドキュメントが存在するかを確認
tell application “OmniGraffle 5″
  set wCount to count every window –本来ならdocumentオブジェクト経由で確認したいところだが、OmniGraffleのdocumentの仕様がおかしい
  
if wCount = 0 then
    display dialog “ドキュメントがオープンされていません” buttons {“OK”} default button 1 with icon 1
    
return
  end if
end tell

–OmniGraffleの各最前面のドキュメントの各Stencilのタイトル(URLが”title”になっているもののtext)を取得する
set aList to retIndexDataFromFrontDocument() of me

–各タイトルにすでに「(1/2)」のようなナンバリングが行われていたらナンバリング部分を削除する
set newList to {}

repeat with i in aList
  set j to contents of i
  
  
set aPos to offset of separatorC in j
  
if aPos is not equal to 0 then
    set j to text 1 thru (aPos - 1) of j
  end if
  
  
set the end of newList to j
end repeat

–目次を作成する
set sPage to 3 –ページ数カウンタ
set curItem to second item of newList
set newList2 to items 3 thru -1 of newList

set outList to {{2, curItem}} –出力用データ

repeat with i in newList2
  set j to contents of i
  
  
if j = curItem then
    –何もしない
  else
    set curItem to j
    
set the end of outList to {sPage, j}
  end if
  
  
set sPage to sPage + 1
  
end repeat

set aText to retItemDelimedAndParagraphDelimedText(outList, tab, return) of me
set the clipboard to aText

–最前面のドキュメントからインデックス用データを取得する
on retIndexDataFromFrontDocument()
  
  
tell application “OmniGraffle 5″
    tell front document
      set cList to every canvas
      
      
set pCount to 1
      
set pList to {}
      
      
repeat with i in cList
        set j to contents of i
        
        
tell j
          set gList to (every graphic whose url is “title”)
          
          
if gList is not equal to {} then
            set aGraphic to contents of first item of gList
            
set aText to text of aGraphic
          end if
        end tell
        
        
–set the end of pList to {pCount, aText}
        
set the end of pList to aText
        
        
set pCount to pCount + 1
        
      end repeat
    end tell
  end tell
  
  
return pList
  
end retIndexDataFromFrontDocument

–入れ子のリストを、アイテム間のデリミタとパラグラフ間のデリミタを指定してテキスト化
–というか、入れ子のリストをタブ区切りテキストにするのが目的
on retItemDelimedAndParagraphDelimedText(aList, itemDelim, paragraphDelim)
  set aText to “”
  
  
repeat with i in aList
    set aStr to retDelimedText(i, itemDelim) of me
    
set aText to aText & aStr & paragraphDelim
  end repeat
  
  
return aText
end retItemDelimedAndParagraphDelimedText

on retDelimedText(aList, aDelim)
  set aText to “”
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retDelimedText

–OmniGraffleですべてのWindowを表示状態に
on exposeEveryWindow()
  tell application “OmniGraffle 5″
    tell (every window whose miniaturized is true)
      set miniaturized to false
    end tell
  end tell
end exposeEveryWindow

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

2011/12/03 Safari 5.1.2、AppleScript用語辞書に変更なし

2011.12.1にSafari 5.1.2がソフトウェアアップデートから入手可能になっていましたが、AppleScript用語辞書に変更はありませんでした。

2011/12/03 Keynote 5.1.1でAppleScript用語辞書に変更アリ

ソフトウェアアップデートから、Keynote 5.1.1のアップデートが入手可能になっています。さっそく、AppleScript用語辞書を前バージョンであるKeynote 5.1と比較したところ、exportコマンドの仕様に変更が加わっていることが判明しました。

Keynote 5.1では、「export html」という命令だったのが、同5.1.1では「export as HTML」という命令に変更になっています。

記述サンプル(動作確認ずみ)を以下に示します。何かKeynote書類をオープンした状態で実行してください。

スクリプト名:Keynote 5.1.1で変更されたexportコマンドのパラメータ実験
set outFile to choose file name

–Keynote 5.1.1で変更されたexportコマンドのパラメータ実験
tell application “Keynote”
  tell slideshow 1
    export to outFile as HTML
  end tell
end tell

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

ここで指定するパスは、普通にalias形式で大丈夫です(同じKeynote内の命令でも「add file」が要求するのはPOSIX pathですが)。ちなみに、「as HTML」を指定しないと……何も起こらないので、現バージョンでは「as HTML」オプションの記述が(省略可能なようにAppleScript用語辞書には書かれていますが)必須です。

ちなみに、上記のAppleScriptを実行すると指定名称のフォルダが作成され、その中にインタラクティブな操作が可能なHTMLコンテンツが生成されます(出来がよくてビックリ)。

keynote511_1.jpg

keynote511_2.jpg

Keynoteからの各種形式での書き出しは、自動処理できたほうが有用なので、ぜひともほかの形式の書き出しについても「as」で指定できるようにしていただきたいところです。とくに、PDFとepub。

Keynoteはもっと根本的にAppleScriptの自動処理に対応できるように改良すべきです。

2011/12/02 OmniGraffleで仕様書中の画面キャプチャを自動更新する v3

2011/12/01 OmniGraffleで選択中のグラフィックのサイズを50%にする

OmniGraffleで、選択中のグラフィック(ひとつ)のサイズを50%に縮小するAppleScriptです。

グラフィックのサイズ変更はできても、文字を同様に処理できないところにOmniGraffleの深い闇を感じます。

スクリプト名:OmniGraffleで選択中のグラフィックのサイズを50%にする
tell application "OmniGraffle 5"
  tell front window
    set aSel to selection
    
set aaSel to contents of (first item of aSel)
    
    
set {aWidth, aHeight} to size of aaSel
    
set size of aaSel to {aWidth / 2, aHeight / 2}
    
  end tell
end tell

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

2011/12/01 OmniGraffleで選択中のテキストとグラフィックのペアから、グラフィックのURLにテキストを入れる

OmniGraffleでテキストとグラフィックのペアを選択しておくと、テキストの内容をグラフィックのURLに代入するAppleScriptです。

資料の上で画面図(グラフィック)と識別子(テキスト)を並べて配置することが多いわけですが、だったらその文字情報をそのままグラフィックのURLに突っ込めると便利だろう、ということで作成したものです。

og40.jpg

このように、テキストとグラフィックの組み合わせを選択状態にしておいて、本Scriptを実行。

og41.jpg

グラフィックのURL欄にテキスト情報が代入されます。

スクリプト名:OmniGraffleで選択中のテキストとグラフィックのペアから、グラフィックのURLにテキストを入れる

tell application "OmniGraffle 5"
  tell front window
    set aSel to selection
    
    
if length of aSel is not equal to 2 then
      display dialog "選択中のアイテムが2つ(テキストとグラフィックのペア)ではありません" buttons {"OK"} default button 1
      
return
    end if
    
    
    
set bList to {}
    
set tLabel to ""
    
set tObj to ""
    
    
repeat with i in aSel
      set aText to (text of i)
      
      
if aText is not equal to "" then
        set tLabel to aText
      else
        set tObj to (contents of i)
      end if
      
      
set the end of bList to (aText is not equal to "")
    end repeat
    
    
    
if bList = {true, false} or bList = {false, true} then
      set url of tObj to tLabel
    end if
    
  end tell
end tell

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

2011/12/01 OmniGraffleで指定名称の書類の現在表示中のCanvas上から、URLつきの画像(Solid)の情報を集める

OmniGraffleで、指定名称(名称の一部を指定してマッチング)の書類の、現在表示中のCanvas上から、URLつきの画像(Solid)の情報を集めるAppleScriptです。

書類上の全Canvasを処理するプログラムも有益ですが、現在表示中とか現在選択中のものだけ処理するという処理もまた有益なことが多いです。

オブジェクトのURL欄に識別用の文字列(URLではないもの)を入れておいて、各オブジェクトの個体識別を行うという手段はよく使います。そのために、URLを抜き出してみたり、特定のURLが仕込まれているオブジェクトを検索してみるという処理は、オブジェクトに仕込んだURLがGUI側から検索や置換などを行えないだけに、AppleScriptで書いておく必要性の高いものです。

スクリプト名:OmniGraffleで指定名称の書類の現在表示中のCanvas上から、URLつきの画像(Solid)の情報を集める
set aDocNameStrOrig to “機能定義書”
set aDocNameStr to retADocWhichContainsStr(aDocNameStrOrig) of me

set urlList to getCanvasNoAndURLFromEachSolidOnlyFromCurrentCanvas(aDocNameStr) of me
–> {{graphic id 66 of canvas id 50 of document “機能定義書v4.graffle” of application “OmniGraffle 5″, “D210″}, {graphic id 4 of canvas id 50 of document “機能定義書v4.graffle” of application “OmniGraffle 5″, “title”}, {graphic id 86557 of canvas id 50 of document “機能定義書v4.graffle” of application “OmniGraffle 5″, “D210″}}

–指定名称の書類の現在表示中のCanvas上から、URLつきの画像(Solid)の情報を集める
on getCanvasNoAndURLFromEachSolidOnlyFromCurrentCanvas(aDocNameStr)
  
  
–指定名称の書類のWindowを取得
  
set aWin to retAWindowByDocumentName(aDocNameStr) of me
  
  
tell application “OmniGraffle 5″
    set aSel to name of canvas of aWin
  end tell
  
  
  
  
tell application “OmniGraffle 5″
    tell document aDocNameStr
      
      
set urlList to {}
      
      
tell canvas aSel
        set s1List to {}
        
set sList to every solid
        
        
repeat with ii in sList
          
          
set jj to contents of ii
          
set uDat to url of jj
          
          
try
            
            
if uDat is not equal to missing value then
              set s1List to {jj, uDat}
              
set the end of urlList to s1List
            end if
            
          end try
          
        end repeat
        
      end tell
      
      
    end tell
    
    
return urlList
    
  end tell
  
end getCanvasNoAndURLFromEachSolidOnlyFromCurrentCanvas

–指定ドキュメント名称を持つWindowを返す
on retAWindowByDocumentName(aDoc)
  set wResList to {}
  
  
tell application “OmniGraffle 5″
    –可視状態で指定名称のドキュメントのウィンドウを返す(AppleScript特有のフィルタ参照処理)
    
set wList to every window whose visible is true and name of document of it is equal to aDoc
    
set wLen to length of wList
    
    
if wLen = 1 then
      –取得されたWindow数が1の場合 (通常処理)
      
return contents of first item of wList
      
    else if wLen > 1 then
      –取得されたWindow数が1以上の場合 (リカバリできなくもないエラー処理)  
      
return contents of first item of wList
      
    else
      –完全にエラー
      
return false
      
    end if
    
  end tell
end retAWindowByDocumentName

–オープン中の書類のうち、指定の文字を含むものの書類名を返す
on retADocWhichContainsStr(aStr)
  tell application “OmniGraffle 5″
    set dList to every document whose name contains aStr
    
if dList = {} then return “”
    
    
set tmpDocName to name of first item of dList
    
    
return tmpDocName
  end tell
end retADocWhichContainsStr

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