Archive for the 'AppleScriptObjC' Category

05/19 指定文字列から、指定ペア文字で囲まれる部分を削除。前方からスキャンして削除実行

指定文字列に対して、指定ペア文字列で囲まれる部分を削除するAppleScriptです。

この手の処理では、文字列の前と後ろからサーチするような処理が多いですが、本プログラムはどちらも前方からサーチします。

プログラムのタイトルを見ただけでは何に使ったものかさっぱり分かりませんが、プログラムリストを見ると一目瞭然。CocoaのAPIの名前をAppleScriptObjCに自動置換するための試作品です(ものすごく強引で、やっつけ仕事のオンパレードなうえに、なんでもかんでも処理できるようにはなっていません)。

もうちょっとこねくり回すとなんとかなってくるものでしょうか。

スクリプト名:指定文字列から、指定ペア文字で囲まれる部分を削除。前方からスキャンして削除実行
–Cocoaのメソッド名をAppleScriptObjCフォーマットに変換するテスト

set aStr to "drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withFont:(UIFont *)font minFontSize:(CGFloat)minFontSize actualFontSize:(CGFloat *)actualFontSize lineBreakMode:(UILineBreakMode)lineBreakMode baselineAdjustment:(UIBaselineAdjustment)baselineAdjustment"

set sStr to "("
set eStr to ")"

–変数の型で2語に別れるものを徹底的に置換
set aStr to repChar(aStr, "unsigned short", "unsignedshort") of me
set aStr to repChar(aStr, "signed short", "signedshort") of me
set aStr to repChar(aStr, "signed int", "signedint") of me
set aStr to repChar(aStr, "unsigned int", "unsignedint") of me

if aStr does not end with ";" then
  set aStr to aStr & ";"
end if

set aStr to trimStrByCharPair(aStr, sStr, eStr, 0, 0) of me

set aStr to repChar(aStr, tab, "") of me
set aStr to repChar(aStr, character id 10, "") of me

set aStr to repChar(aStr, ";", " ") of me
set aStr to repChar(aStr, ":", "_") of me

set bList to parseByDelim(aStr, {"_", " "}) of me
set aRes to ""
repeat with i from 1 to ((length of bList) - 1) by 2
  set j to contents of item i of bList
  
set aRes to aRes & (j & "_")
end repeat

set bRes to "("
repeat with i from 2 to (length of bList) by 2
  set j to contents of item i of bList
  
set bRes to bRes & (j & ", ")
end repeat
set bRes to bRes & ")"

set bRes to repChar(bRes, ", )", ")") of me –ゴミ掃除

set cRes to aRes & bRes
–>"drawAtPoint_forWidth_withFont_minFontSize_actualFontSize_lineBreakMode_baselineAdjustment_(point, width, font, minFontSize, actualFontSize, lineBreakMode, baselineAdjustment)"

–指定文字列から、指定ペア文字で囲まれる部分を削除。前方からスキャンして削除実行
on trimStrByCharPair(aStr, sStr, eStr, sTrimOffset, eTrimOffset)
  repeat
    set sOffst to offset of sStr in aStr
    
set eOffst to offset of eStr in aStr
    
    
set aLen to length of aStr
    
if aLen < 2 then return ""
    
    
if sOffst = 0 or eOffst = 0 then
      exit repeat
    end if
    
    
if sOffst > 1 then
      set tmp1Str to text 1 thru (sOffst - 1 + sTrimOffset) of aStr
      
set tmp2Str to text (eOffst + 1 + eTrimOffset) thru -1 of aStr
      
set aStr to tmp1Str & tmp2Str
      
    else if sOffst = 1 then
      set aStr to text (eOffst + 1) thru -1 of aStr
      
    else if sOffst = aLen - 1 then
      set aStr to ""
      
    end if
    
  end repeat
  
  
return aStr
end trimStrByCharPair

–文字置換ルーチン
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

–与えられた文字列を、指定デリミタ文字でparseしてリストにして返す
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

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

05/06 ASObjC Runner〜ASOCの機能にAppleScript的な記法を

ASObjC Runnerは、ASOCのスクリプト(テキストベース)の実行機能をMac OS X 10.6.xと10.7.xに提供するフリーのアプリケーションです。作者はShane Stanley。ほかにも、Late Nighet SoftwareのMark Alldrittなども協力しているとのこと。

asobjc1.png

ASOCは基本的には、ランタイムアプリケーション上で動作するものですが、ASObjC Runnerではテキストファイル形式のASOCのプログラムをそのまま実行します。

tell application “ASObjC Runner”

のtellブロック内にscriptを記述します。サンプルはmacosxautomation.comに掲載されているので、雰囲気を伺い知ることができます。

asoctable.png

……以前までは、ASobjC Runnerはあまり個人的な興味を喚起されない存在だったのですが、途中のバージョンアップで面白い要素が追加されました。

Cocoaのオブジェクトに対して標準的なAppleScriptの用語でアクセスできるようにする機能です。

正確にいえば……ASObjC Runner側で想定している範囲の機能について、ASObjC RunnerがAppleScript用語辞書を用意しており、その用語を使って一般的なAppleScript的なアプローチでScriptingが行えるというわけです。

AS用語辞書が提供されており、通常のAppleScript的な用語が利用できるのは、

リスト操作(ソートや検索など)、文字列操作(検索、置換、日付文字列書式指定など。正規表現による検索/置換文字列指定の機能を含む)、ファイル操作(情報取得、コピー、削除、移動、ファイル作成、plist読み書き)などのほか、プログレスバー付きダイアログの表示

などです(ASOC的記法で書けば、より広い範囲のCocoaの機能をダイレクトに使用できます)。

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分もかかりませんでした。

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

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

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

03/11 NSTextViewからのテキストデータ取得、テキスト設定サンプル

NSTextViewへのデータ設定、データ取得を行うAppleScriptObjCのサンプルです。

AppleScript Studioで作ってあったOpenSSLのユーティリティをAppleScriptObjCで作り直そうとしたときに、どうも一定文字以上のデータのエンコード時に文字化けが発生。その問題検証のために作成したサンプルです。

実際に試してみて、NSTextViewからの値の取得/設定時にはとくに問題がないことがはっきりしました。やはり、実際に組んで試してみないと。

tv1.jpg

→ プロジェクトのダウンロード(Xcode 4.1 on Mac OS X 10.6.8で検証)

AppleScriptObjCファイル名:textview1AppDelegate.applescript

– textview1AppDelegate.applescript
– textview1

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

script textview1AppDelegate
  
property parent : class “NSObject”
  
  
property aTV : missing value
  
property bTV : missing 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 clicked_(sender)
    
    
set textStorage to (my aTV’s textStorage())
    
set theText to textStorage’s |string|()
    
display dialog (theText as string)
    
  
end clicked_
  
  
on clicked2_(sender)
    
    
    
set textStorage to (my aTV’s textStorage())
    
set theText to textStorage’s |string|()
    
    
set theText to theText as Unicode text
    
    
my bTV’s setString_(theText)
    
  
end clicked2_
  
  
on clicked3_(sender)
    
    
set textStorage to (my aTV’s textStorage())
    
set theTextA to textStorage’s |string|()
    
set theTextA to theTextA as Unicode text
    
    
set textStorage to (my bTV’s textStorage())
    
set theTextB to textStorage’s |string|()
    
set theTextB to theTextB as Unicode text
    
    
if theTextA = theTextB then
      
display dialog “左右のテキスト内容はイコールです”
    
else
      
display dialog “左右のテキスト内容は合っていません”
    
end if
    
  
end clicked3_
  
end script

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

03/07 テキストへの色指定サンプル

テキストに色指定を行うサンプルのAppleScriptObjCプログラムです。

textcol.jpg

AppleScriptObjCファイル名:textcolorTestAppDelegate.applescript

– textcolorTestAppDelegate.applescript
– textcolorTest

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

script textcolorTestAppDelegate
  
property parent : class "NSObject"
  
  
property nsc : class "NSColor"
  
property tf : missing value
  
property aTextField : missing value
  
  
on applicationWillFinishLaunching_(aNotification)
    
    
–ひとつめのNSTextField(入力フィールド)
    
set aString to current application’s NSMutableAttributedString’s alloc()’s initWithString_("これはテスト用の文字列ですよー")
    
    
set colorArray to current application’s NSArray’s arrayWithObjects_(nsc’s redColor(), nsc’s orangeColor(), nsc’s yellowColor(), nsc’s greenColor(), nsc’s blueColor, nsc’s purpleColor(), missing value)
    
    
repeat with i from 0 to (aString’s |length|()) - 1
      
aString’s addAttribute_value_range_(current application’s NSForegroundColorAttributeName, colorArray’s objectAtIndex_(i mod 6), {i, 1})
    
end repeat
    
    
tf’s setAttributedStringValue_(aString)
    
    
    
–ふたつめのNSTextField(Label)
    
set bString to current application’s NSMutableAttributedString’s alloc()’s initWithString_("●私のユーザー名")
    
    
set colorArray to current application’s NSArray’s arrayWithObjects_(nsc’s redColor(), nsc’s blackColor())
    
    
set bLen to bString’s |length|()
    
    
bString’s addAttribute_value_range_(current application’s NSForegroundColorAttributeName, colorArray’s objectAtIndex_(0), {0, 1})
    
bString’s addAttribute_value_range_(current application’s NSForegroundColorAttributeName, colorArray’s objectAtIndex_(1), {1, (bLen - 1)})
    
    
aTextField’s setAttributedStringValue_(bString)
    
  
end applicationWillFinishLaunching_
  
  
  
  
on applicationShouldTerminate_(sender)
    
return current application’s NSTerminateNow
  
end applicationShouldTerminate_
  
  
end script

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

03/07 指定のRGBカラーをGeneric CMYKプロファイルでCMYKに変換して……RGB値を取得

指定のRGBカラー値をGeneric CMYKプロファイルを用いてCMYK変換し、CMYK値を取り出そうとして……できなかったので仕方なくRGB値を取り出したAppleScriptObjCのプログラムです。

上のColorWellをクリックして任意の色(ここではライトグリーン)を選択。

asoc1.jpg

「Convert RGB to CMYK」ボタンをクリックすると、Mac OS XのGeneric CMYK Profileに基づいてCMYK変換を行い、下のColorWellに色を設定します。

asoc2.jpg

いい感じのCMYKっぽい色に変換されるのですが、ここからCMYK値を取り出すのに失敗(なんでだ〜)。仕方なくRGB値を取り出しています。

→ プロジェクトのダウンロード(Xcode 4.1 on Mac OS X 10.6.8)

AppleScriptObjCファイル名:rgb2cmykAppDelegate.applescript

– rgb2cmykAppDelegate.applescript
– rgb2cmyk

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

– generic CMYK color Spaceを用いて、RGB→CMYK変換を行い、ColorWellにCMYKベースで色をセットしたあとRGB値でその色を取り出す
– 

script rgb2cmykAppDelegate
  
  
property parent : class “NSObject”
  
  
property rgbWell : missing value
  
property rgbR : missing value
  
property rgbG : missing value
  
property rgbB : missing value
  
  
property cmykWell : missing value
  
property cmykC : missing value
  
property cmykM : missing value
  
property cmykY : missing value
  
property cmykK : missing 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 clicked_(sender)
    
    
–RGB Color Wellのカラー値を読み取って、TextFieldに文字を入れる
    
set {rNum, gNum, bNum, aNum} to retColData(rgbWell)
    
    
set rNumStr to (rNum as integer) as string
    
set gNumStr to (gNum as integer) as string
    
set bNumStr to (bNum as integer) as string
    
    
rgbR’s setStringValue_(rNumStr)
    
rgbG’s setStringValue_(gNumStr)
    
rgbB’s setStringValue_(bNumStr)
    
    
    
–RGB Colorwellからカラーを読み取る
    
set tmpColor to rgbWell’s |color|
    
    
–Generic ProfileでRGB to CMYK変換してColorWellに設定
    
set cmykColSpace to current application’s NSColorSpace’s genericCMYKColorSpace
    
set color1 to tmpColor’s colorUsingColorSpace_(cmykColSpace)
    
    
    
–cmykWell’s setColor_(color1)
    
tell cmykWell
      
setColor_(color1)
    
end tell
    
    
    
–とりあえずRGBAでデータを取り出してみる
    
set {temp21, temp22, temp23, temp24} to retColData(cmykWell)
    
    
set temp21 to temp21 as string
    
set temp22 to temp22 as string
    
set temp23 to temp23 as string
    
set temp24 to temp24 as string
    
    
–TextFieldにCMYKデータを入れる
    
cmykC’s setStringValue_(temp21)
    
cmykM’s setStringValue_(temp22)
    
cmykY’s setStringValue_(temp23)
    
cmykK’s setStringValue_(temp24)
    
  
end clicked_
  
  
  
  
–指定オブジェクトのカラー値を取得する
  
on retColData(aObject)
    
    
–色空間のコンバートを行ってカラー値を取得する(強制的にRGB)
    
set tmpColor to aObject’s |color|
    
set color1 to tmpColor’s colorUsingColorSpaceName_(current application’s NSCalibratedRGBColorSpace)
    
    
set temp11 to color1’s redComponent()
    
set temp12 to color1’s greenComponent()
    
set temp13 to color1’s blueComponent()
    
set temp14 to color1’s alphaComponent()
    
    
–RGBAの並びでデータを返す
    
set TheColor to {(temp11 * 255) as integer, (temp12 * 255) as integer, (temp13 * 255) as integer, (temp14 * 255) as integer}
    
    
return TheColor
  
end retColData
  
  
  
end script

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

03/05 AppleScriptObjCのプログラムリストも、色分け+リンクつきテキスト掲載へ

すでに、掲載記事で実用化テストを行ってありますが……AppleScriptObjCのプログラムの構文色分けテキストでの掲載を行うことにしました。

これまでは、Xcodeが正しいAppleScript構文色分けを行ってくれないために、仕方なく画面のスクリーンショット+プロジェクトのアーカイブをダウンロード、という形式でAppleScriptObjCのプログラムリストを掲載していましたが、掲載するのもめんどうですし、検索性もよくありませんでした。

そこで、Xcodeのスクリプトメニューから呼び出すと、正しい構文色分けを行ったテキストを出力するAppleScriptを作成。これで、AppleScriptObjCのリスト掲載の作業を大幅に減らしてみやすい記事を掲載することができるようになりました。

通常のAppleScriptのリストと見分けられるように、リストの背景色を変えています。通常のAppleScriptは青系統の色に指定していますが、AppleScriptObjCのリストは緑系統の色を指定しています。

▼AppleScriptObjCのプログラムリスト
asoclist.jpg

▼通常のAppleScriptのプログラムリスト
aslist.jpg

03/03 100倍速いシェルソートでリストをソート

edama2さんよりの投稿です

基本的には掲載されている「シェルソートでリストをソート」をスプリクトオブジェクトで処理にしているだけです。サンプルでソートする数が100倍なだけで「100倍速い」という数字には特に根拠はありません。

スクリプト名:100倍速いシェルソートでリストをソート
set theList to {}
set i to 1
repeat 30000 times
  copy (random number from 100 to 99999) to the end of theList
  
set i to i + 1
end repeat

set s0Time to current date
set aRes to shellSort_order_(theList, false) of me
set s1Time to current date

display dialog (s1Time - s0Time) as string

–シェルソートでリストを昇順ソート(入れ子ではないリスト)
on shellSort_order_(a, isAsc)
  
  
script a_ref
    property contents : a
  end script
  
  
set n to length of a
  
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 contents of a_ref
        
set j to i
        
repeat while (j h) and ((item (j - h + 1) of contents of a_ref) > v)
          set (item (j + 1) of contents of a_ref) to (item (j - h + 1) of contents of a_ref)
          
set j to j - h
        end repeat
        
set item (j + 1) of contents of a_ref to v
      end repeat
    end if
  end repeat
  
  
if not isAsc then set a to a’s reverse
  
  
return a
end shellSort_order_

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

Piyomaru Softwareによる解説:

以前に掲載したシェルソートのルーチンの改良版です。ソートというか、巨大なリスト型変数(配列)の操作をAppleScriptで行うとスピードが低下するので、3,000〜4,000要素(Item)を超えるサイズのリスト型変数を扱う場合には、何らかの高速化を行うべきです。

AppleScriptにおけるソートの高速化手法にはおよそ3つあって、1つが「a reference to」による間接アクセスによる高速化。

著しく高速化するものの、データを格納する変数と間接アクセスする変数の2つをグローバル宣言する必要があり、効果は大きいものの、副作用も大きい(グローバル宣言した変数が他のルーチンの変数をぶつかっていないか確認が必要、など)ため、そのための作業コストが大きく「手軽」とはいえませんでした。

2つ目が、ここで活用されている「スクリプトオブジェクト内にプロパティを持つ」というアクセス方法で、a reference toと同程度ぐらいには(厳密に計測してはいないのですが)高速化することが知られています。

スクリプトオブジェクトの利用による高速化処理の前後で、サンプルの3万項目の乱数リストをソート所要時間を計測比較してみたところ(MacBook Pro Core i7 2.66GHz, Mac OS X 10.6.8, 8GB RAM)、

 高速化前:2967秒
 高速化後:8秒

と、370倍ぐらい高速になっていました。リストの項目が大きいほど高速化の効果が出やすいものと思われます。

3つめが、Mac OS X 10.6から使えるようになったAppleScript ObjCを用いること。この環境内でCocoaのソートルーチンを呼び出すと速いので、一度すべてのやりかたでどのソート方法が速いのか、比較をしておくべきでしょう。

追記(2012/3/4):

MacBook Air 2011 (Core i5 1.6GHz、Mac OS X 10.7.3、RAM 4GB)でこの乱数3万件のソートを試してみたところ……

 本ScriptをAppleScript Editor上で実行:7秒
 本ScriptをAppleScriptObjC on Xcodeのプロジェクトで実行:8秒
 AppleScriptObjC on XcodeのCocoaベースのソートでリスト(正確にはレコード)をScriptのオブジェクトに持たせた場合:129秒
 AppleScriptObjC on XcodeのCocoaベースのソートで何も高速化処理を行っていない場合:(計測不能)

Cocoaベースのソートがたいして速くない(純粋にリスト=NSArrayではないので、同じ処理ではないですが)ことに驚きました。AppleScriptObjCのプログラムも、ソート処理をAppleScriptネイティブのものに書き換えておいたほうがよさそうな……。

あるいは、3万件というデータ件数になると、AppleScriptObjCでもCoreDataを併用することを検討すべきなのかもしれません。データベースに入れてしまえば、3万件ごときのソートなど一瞬でしょうし。

さらに追記(2012/3/4):

AppleScriptObjCで以下のようなプログラム(単なるArrayをソート)にしてみたところ、3万件でもソートに要する時間は1秒以下でした(MacBook Pro Core i7 2.66GHz, Mac OS X 10.6.8, RAM 8GB)。

AppleScriptObjCファイル名:sortArrayAppDelegate.applescript

– sortArrayAppDelegate.applescript
– sortArray

– Created by Takaaki Naganoya on 12/03/04.
– Copyright 2012 Piyomaru Software. All rights reserved.

script aRef
  
property contents : {}
end script

script sortArrayAppDelegate
  
property parent : class “NSObject”
  
  
on applicationWillFinishLaunching_(aNotification)
    
– Nothing
  
end applicationWillFinishLaunching_
  
  
on applicationShouldTerminate_(sender)
    
return current application’s NSTerminateNow
  
end applicationShouldTerminate_
  
  
on clicked_(sender)
    
    
set aList to {}
    
repeat 30000 times
      
tell current application
        
set the end of (contents of aRef) to (random number from 1000 to 9999) as string
      
end tell
    
end repeat
    
    
tell current application
      
set sTime to current date
    
end tell
    
    
set theArray to current application’s class “NSMutableArray”’s arrayWithArray_(contents of aRef)
    
set bList to theArray’s sortedArrayUsingSelector_(“localizedCaseInsensitiveCompare:”)
    
    
tell current application
      
set eTime to current date
      
set totalTime to eTime - sTime
    
end tell
    
    
display dialog (totalTime as string)
    
  
end clicked_
  
end script

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

02/23 Mac App Store公開アプリのSandbox対応義務化 期限が6/1に延期

Mac App Store公開アプリは必ずSandbox対応を義務化します…………期限は3月1日ですよ! という無茶な話が、6/1延期になりました(そこかしこのBlogで話題になっています)。

まさか、この極端なBlogしか見ていないという人もいないとは思いますが、補足までに。

補足:タイトルが「Sadbox」になっていたのを「Sandbox」に修正しました。

02/19 AppleriptObjCベースのCocoaアプリケーションのSandbox化は可能か?

Sadbox化(Sandboxing)とは、アプリケーションを実行する際に、「保護された環境下でプログラムを実行し不正処理を防ぐセキュリティモデルの一種」

2011年末に「2012年3月以降、Mac AppStoreではSandbox対応したアプリケーションでないとリジェクト(却下)する」らしいよ、という噂が流れました。

Sandbox対応すると、ファイルの読み書きについて指定フォルダ以下のみに限定されたり、AppleEventによる通信も(開発時に)指定されたアプリケーションに限られるなど、アプリケーションが「できること」が制限されます。また、実行コードの同一性を保証するための「コード署名」の機構が用意され、コードの書き換えを禁止しています。

AppleScript業界的にいえば、AppleScriptのアプレットについては……プロパティを書き換えただけで実行ファイルが変更されてしまうわけで、Sandbox対応になじみません。AppleScript Studioアプリもモデルが古いのでMac AppStoreに提出しても通らないだろうというところ。

AppleScriptObjCアプリになって、はじめてMac AppStore上で流通できるアプリケーションを作成し得るだろう、という見方があったわけですが……ちょっと待て、そもそもAppleScriptObjCアプリってSandbox化できるのか? といった議論がありました。

実際、2011年末のTwitter界隈では一種パニック状態に陥っているんだか、声高に騒いでいる人が大勢(海外に)いました。この騒ぎについてはリアルタイムにキャッチしていたのですが、事実関係が把握できないと、怒っていいのか安心していいのか分りません。まずは、情報の収集と分析を行っていました。

そんな中、実際にBEN WALDIEによって検証記事が掲載され、これを1か月ほど後になって見つけました。

「Sandboxing a Cocoa-AppleScript (AppleScriptObjC) Application」 2011/10/13

結論をいえば、AppleScriptObjCアプリのSandbox化はできるということで、ひとまずは安心してよいということになります。ただ、いろいろ試行錯誤は必要なんだろうなと思われるところです。BEN WALDIEの記事にはSandbox化したAppleScriptObjCのXcodeプロジェクトのアーカイブがリンクされており、参考になると思います。

02/18 AppleScriptObjC Explorer 2 v2.2

AppleScriptObjCの世界をほぼ1人で引っ張っている感のある、Shane StanleyによるAppleScriptObjCに特化したスクリプトエディタが「AppleScriptObjC Explorer 2」です。

exp1.jpg

▲構文色分けがまったくできていないXcode 4(左)と構文色分けに対応しているAppleScriptObjC Explorer(右)

exp2.jpg
▲インデントも正確なAppleScript Explorer 2

まだ使い始めて日が浅いのですが、その特徴を紹介すると……

・Xcode 4ではまったく効かない「AppleScriptの構文に応じた色分け」ができる(これはでかい! というか、Xcode 4がダメすぎ)
・Xcode 4ではまったくダメな「インデント」が正確(Xcode 4、本当にダメすぎ)
・Xcode 4では絶望的な、ログ表示機能を備える
・単体で動作可能なAppleScriptObjCベースのアプリケーションを作成可能
・Xcode 4の外部エディタとして動作可能(XcodeでScriptのファイルを選択して、コンテクストメニューから「Open With External Editor」を実行。あらかじめFinder上で拡張子とアプリケーションの関連を変更しておく必要がある)

exp3.jpg

・定番の処理(Arrayを作るとか、ソートするとか)はメニューからScript本文中にInsert可能

exp4.jpg

exp5.jpg

・Script文中で入力補完機能を利用可能(ここだけ、まだ試していない)

といったところでしょうか。AppleScriptObjCが使えるMac OS X 10.6/10.7で動作します。

構文色分けができないとプログラミング効率が悪すぎるのと、インデントが正確でないXcode 4にシビレを切らしてAppleScriptObjC Explorerを購入(5,000円……為替レートの変動で変わる??)。

ただ、デバッグ機能についてはアプリケーション起動時に呼ばれる「applicationWillFinishLaunching_」イベントハンドラの中で実行されるコードしかデバッグできなさそうなので、デバッグ機能に期待して購入すると、期待外れになってしまうかもしれません。

総合的には……残念ながら、値段の割には……と、感じる点が多々あります。

AppleのCocoaについてのヘルプからAppleScriptObjC用のヘルプとして変換して表示するとか、既存のObjective-Cのコードをプロジェクトにインポートすると、その呼び出し用のAppleScriptObjCコードがScript本文中に展開されるとか……そういう機能を期待したいところです。

いまの段階では、ちょっと気の利いた構文色分けエディタ、というレベルです。それ以上でもそれ以下でもありません(Shane Stanleyへのお布施、という意味ではとても意義があるかもしれない)。

とりあえず30日間は試用ができるため、Xcode 4にブチ切れまくっている方は試してみるとよいでしょう。

01/30 AppleScriptObjCのメモリー浪費現象を解決?!

AppleScriptObjCで作成したプログラムが、実際に作業で使ってみたらメモリを馬鹿喰いして困る、という話を海外でよく見かけます。

自分も、1,000色ほどの色セットと任意の色の近似色を求めるAppleScriptObjCのプログラムを作成し、任意の1色の近似色検索を行う分には問題なかったものの……まとめて数十〜数百色を処理しようとしたところ、MacBook Pro搭載の8GBのメモリーをアレヨアレヨという間に喰いつぶされてしまいました。

その時には急いでいたので、根本的な解決をあきらめ……近似色検索処理だけを通常のAppleScriptのアプレットに追い出し、AppleScriptObjCのプログラムからAppleScriptアプレットをtell文で呼び出すように処理変更。

余計なプロセス間通信を行うので、余計に時間がかかるようになりましたが、メモリを喰いつぶしてスワップしまくるよりはマシという判断でした。事実、プロセス分離を行う前よりもずいぶん処理速度が落ちたのですが……背に腹はかえられません。

後日、時間に余裕があるときに調査を行ったところ……以前に掲載したCocoaベースのソートルーチンが問題の発生源であることが判明(わかってたけど)。

その場でプログラムから作成したオブジェクトについては、ループ処理などで後続の処理がすぐに発生した場合……自動的にはメモリー上から解放されないということが分りました。

ソートルーチンはサブルーチン化しておいたので、AppleScript的にはローカル変数などで処理される安全な「離れ小島」ですが、Cocoaアプリケーション的には「離れ小島ではない」という状態だと理解しました。

解決策は、作成したオブジェクトを明示的にリリース(release)すること。ほんの短い追加ではあるものの、数百件のデータを処理してもまったくメモリーを馬鹿喰いすることなく、処理を継続できました。

このあたり、Info.plistの記述を書き換えると回避できる問題なのかどうか、もうちょっと調べる必要がありますが……

スクリプト名:Cocoaで入れ子のリストを昇順ソート v2(AppleScriptObjC)
set aRecList to {{aName:“ひよこさん”, favoriteFood:“やきそば”, moneyInWallet:30}, {aName:“ぴよこ”, favoriteFood:“やきうどん”, moneyInWallet:20000}}
set sortedList to cocoaSortListAscending(aRecList, “moneyInWallet”)

–Cocoaで入れ子のリストを昇順ソート v2(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})
  
  
—————————————————————————————
  
theSet’s release –** Important!! ** 重要!!
  
—————————————————————————————
  
  
return sortedList
  
end cocoaSortListAscending

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

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

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

06/07 OS X 10.7でAppleScriptの機能が追加されるもよう

本日WWDCのキーノートスピーチで発表され、AppleのWebサイト上で「250を超える新機能」が紹介されました。

AppleScript関連の新機能も掲載されており

aas.jpg

感心するやら、「なんでこれが紹介されていない?」と不満を覚えるやら…………(もごもご)…………あんなバグやこんなバグが直っているとかいないとか、さまざまな感想はありますが、それはさておき。

発表された内容について検証してみることとしましょう。

■Cocoa AppleScriptアプレット

Mac OS X 10.6で装備されたAppleScriptObjCのことと思われます。OS X 10.7上で作成した「Cocoa AppleScriptアプレット」がMac OS X 10.6.x上でも動作するのかどうかは不明です。

Mac OS X 10.6上ではAppleScriptObjCはXcode上でしか記述できませんでした。それが、AppleScriptエディタの上でも書けるようになる、ということでしょう。OSA言語ポップアップメニューで「AppleScript」と「AppleScriptObjC」を選択して記述することになるのか、ほかの方法で通常のAppleScriptとAppleScriptObjCを切り替えるのかは不明です。

AppleScriptObjCをAppleScriptエディタ上で記述できるといっても、GUIを作れない(はず)とか、さまざまな制約があるはずなので、どの程度の用途を見込んでいるのかはいまひとつ不明です。

この件に関しては、AppleScriptObjCに関するドキュメントがAppleからほとんど出てきていないため、ドキュメントが整備されるのかどうか、というあたりが最大のみどころでしょう(いまのままの状態だとサイテーです)。

■スクリプトのテンプレート

これは、各Scripterが自前で用意していたものが、AppleScriptエディタ標準で提供するようになったということで、とくに目新しいことではありません。

ユーザーが新たにテンプレートを追加できるようになったりすると便利でしょう。

■スクリプトのターゲットアプリケーションをグローバルに指定

いまひとつ使い道がはっきりしない、「対象アプリケーション」ポップアップの機能を強化した…………もののように見えますが、すでに発表段階で「あまり役に立たなさそうな気配」が漂っています。

■アプリケーション実行のメニューオプション

AppleScriptエディタ上とアプレット書き出しして実行した時でAppleScriptの挙動が部分的に変わってしまうことが、たまにあります。アプレットとして実行する機能が追加されたということのようですが、その時にログを出しながら実行するとか……デバッグ系の機能がどの程度使えるのか、あるいはまったく使えないのか……といったあたりが見どころです。

04/10 サウンド入力デバイス名を取得する

AppleScriptObjCで、サウンド入力デバイス名のリストを取得するプログラムです。

sound1.jpg

実行すると、ログ表示ウィンドウに……

sound2.jpg

と、デバイス名の一覧がログ表示されます。

プロジェクトにQTKit.frameworkをリンクしておく必要があるところが要チェックポイントです。

sound3.jpg

→ getinputdevice.zip

02/16 TextViewの内容を取得する

AppleScriptObjCでTextViewからテキストを取得するサンプルです。

→ ダウンロード

01/16 タイマー割り込みでマウスの座標を取得する

NSTimerで宣言したタイマー割り込みサービスで定期的に(0.1秒ごとに)マウスカーソルの座標を取得するサンプルです。

昔、このために専用のアプリが配布されていましたが……AppleScriptでこんなに簡単に実現できるようになった、というのはすごいことです。以前から不満に思っていた機能が割とあるので、このサンプルをもとにいろいろ追加してしまいました。

→ mousewatcher.zip

01/16 複数のNSButtonで別々のクリックイベントを受信

複数のNSButtonで別々のクリックイベントを受信するサンプルです。

AppleScript Studioのときには、このように1本のScriptの別々のイベントハンドラでボタンごとのクリックイベントを受信することはできませんでした。

→ buttons_separate_event.zip

01/16 TextFieldの表示/非表示設定

NSTextFieldの表示/非表示を行うサンプルです。

→ hidetf.zip

01/16 NSTextFieldからの文字列取得/文字列設定

ASOCでNSTextFieldへのアクセスを行うサンプルです。

 set a to textField1’s stringValue() –textFieldからの文字列の取得
 textField2’s setStringValue_(a) –textFieldへの文字列の設定

→ twotextfield.zip

11/24 AppleScriptObjCでプログラムを実際に組んでみると、通常のAppleScriptのコードが大部分

ここのところ、AppleScriptObjCでいろいろプログラムを組んだり、プログラムを構成するための小さなテストプログラムを試したりしています。

AppleScriptでGUIプログラムを組むAppleScript Studioの環境とAppleScriptObjCの環境は全然違うものですが、どちらでも言えるのは、「通常のAppleScriptのコード部分のほうが、GUIや環境設定をいじくるコード部分よりもはるかに多い」ということです。

実際に、AppleScriptObjCの942行のプログラムを組んでみました。これには、TableViewやDrawer、複数のWindowなどが含まれ、環境設定内容を保存したり、CSVファイルの読み書き、ネイティブ形式ファイルの読み書き、LAN上の他のマシンのアプリケーションをコントロールするといった機能などを実装してみました。時間の制約はあったものの、ひととおりの機能を備えているものです。

そのソースコードを評価して、AppleScriptObjCのコードと、通常のAppleScriptのコードの分量を比較してみました。

asoc_graph.jpg

だいたい、AppleScriptObjCのコードは1割程度。内容やアプローチにもよると思うのですが、AppleScript Studioライクな書き方をするかぎりでは、こんな感じです。

ASOCのサンプルプロジェクトとして公開されているものの中には、まだ書き慣れていないためか冗長すぎるコードが多かったり、必要以上にASOCのコードばかり(Objective-Cから書き換えた?)入っているものも見られます。

ただ、stringを扱うのに毎回NSStringを使ったり、list型変数でなくわざわざNSArrayを使う必要というのは少ないわけで……私が組んでみたプロジェクトでもASOCのコードは1割程度。このぐらいなら、ありもののサンプルからコピー&ペーストで持ってこられる程度の分量です。

11/23 AppleScriptObjCでNSTextFieldの表示/非表示きりかえ

AppleScript Studioで、各GUI部品を表示したり隠したりするのは、実に「よくある処理」でした。ただ、それをAppleScriptObjCで再現しようとすると、簡単そうな割に再現しづらい状況。

先日も、AppleScriptObjCでプログラムを組んでいたところ、Text Fieldの表示/非表示をいじくりたくなったのですが……いくつか並べられたText Fieldをまとめて隠そうとする方法が見つからず、面倒くさかったのでtab viewにまとめて突っ込んで、tabレスのtabにして(tabが表示されない)、表示tab view itemを切り替えることでまとめて表示/非表示を制御してしまいました。

時間のあるときに、「実際のところはどうやるんだろう?」と思い出し、検索エンジンで調べてみました。Scripter連中がObjective-C系のMLやフォーラムで質問を投稿している様子を見つけましたが、対する回答は……

 「Window上から削除しちゃえばいいじゃん」

のような「それだと明らかに再表示は無理ですよね〜」という返事だったりして、納得しづらいものがありました。

しばらくMLの過去ログを中心に調べていたものの、どうもやり方が見当たらなかったので、根本的にやり方を変更してみたらあっさり実現。

hidetf.jpg

TextFieldのvisibilityをshared user defaultsにbindして、その環境設定値を書き換える(falseで表示、trueで非表示)ようにしてみました。

hidetf2.jpg

ASOCはサンプルや文献が少ないので、本当にこれでいいのか分かりませんが、動いているのでいいんじゃないでしょうか。

ちなみに、shared user defaultsの環境設定値の書き換えはedama2さんからコメントで教えていただいたあと、実際にプロジェクトを送っていただいたものからピックアップしました(直裁的な表現でいえばコピペですね)。

Xcodeのエディタの背景色を白から薄緑に変更してみたら、AppleScriptObjCの「変数ばっかり」なプログラムに対する抵抗感が薄れました(Allan CraigのBlogで発見)。本当は背景色を黒にするともっとよいのですが、背景色が白から変更できないAppleScriptエディタ上でも見えるためにはこのぐらいの色がよいではないかと思っています。

→ プロジェクトのダウンロード

11/18 AppleScriptObjCでリモート制御

AppleScriptObjCで、LAN上の他のマシン上でコマンドを実行するサンプルです。Xcode 3.2.4+Mac OS X 10.6.5で作成しました。

AppleScriptObjCのプログラム内にeppc://プロトコルで他のマシンを指定しても、「マシンが見つからない」というエラーになってしまいます。

そこで考えられる対処は2つ。1つは、eppcで他のマシンと通信するAppleScriptアプレットをAppleScriptObjCプログラムのバンドル内に格納しておいて起動し、起動して迂回通信する方法。

もう1つが、リモート制御を行うAppleScriptをテキストに書き出し、osascriptコマンドでそのテキストを実行するという方法(もちろん、実行後にファイルは削除)。

前者は、処理の自由度がいまひとつであるため、後者を選択してみました。

remoteasoc.jpg

→ remoteasoc.zip

まずは、準備を。

リモートマシン(動かされるマシン)側では、システム環境設定の「共有」で「リモートアップルイベント」にチェックを入れておきます。次に、このプログラムをコントロールする側のMacで起動。「Select」ボタンを押してリモートマシンを選択し、そのマシンに登録してあるユーザー名とパスワードを入力し、「Remote Execution」ボタンを押します。

ボタンを押すと、リモートマシンでbeep音が鳴ります。本サンプルはそれだけの内容ですが、より高度な処理を行うための基礎になるものです。

ちなみに、すべてのNSTextFieldのcontentsをShared User Defaultsにバインドしてあり、一度入力すると次回起動時にもそのまま維持されるはずですが、eppc://と書いたURLは保存されません。どういう仕様なんだか、、、、セキュリティ確保のためなんでしょうか。

11/18 AppleScriptObjCでハマったこと

Appleからはほとんどドキュメントもサンプルも出てきていないので、実際にどう書いたらよいのかさっぱり分らなかったAppleScriptObjCですが、海外で電子媒体(PDF)の参考書が出てきたため、ようやく書き方や使い方が分ってきた今日このごろ。

book2.jpg
・Hamish Sanderson「Learn AppleScript」/Chapter 30「Creating Cocoa Applications with AppleScriptObjC」(50ページ、25ユーロ(だったか))
サンプルを多数掲載するのではなく、動作原理についてやさしく詳しく解説している入門書的な内容。図版がグレースケールで掲載されていることぐらいしか本書の入門書としての欠点が見つからない。かなり分りやすい。

book1.jpg
・Shane Stanley「AppleScriptObjC Explored」(132ページ、$US29.95)
サンプルプロジェクトが豊富なので、さわって動かして学習できる。ただし、細かい動作原理についてはあまり詳しく解説していないので、プログラムを組めるようにはなるものの疑問は残る。

……これらを参考に、AppleScriptObjCのプログラムを組めるようになってきたものの、やはり実戦で使ってみると分らないこともたくさんあります(MacScripterのフォーラムが情報源)。

これまでにハマったポイントをいくつかご紹介しましょう。AppleScript Studioのつもりで扱うと、いろいろとハマるポイントが……

■ウィンドウをクローズして再オープンしようとしたら固まる

アプリケーションのメインウィンドウ上のボタンをクリックして環境設定用のサブウインドウを表示。
環境設定ウィンドウを一度クローズして、再度オープンすると…………クローズボタンが押されたままの状態で固まっており、それ以降の操作を受け付けない。終了させるしかない。
いろいろ検索しまくってみたら、Interface Builder上で「Release When Closed」のチェックを「外す」とうまく動くことが分った。これは、一体どういうことなんだろう?

■eppcでLAN上のマシンをコントロールしようとしたらエラー

LAN上の他のMacをeppc://で指定してメッセージを送ってコントロールしようとしたら、アドレスが分らないと言われた。
AppleScriptエディタ上で実行する分には問題ない。

エラー回避のため、リモートマシンに命令する部分のAppleScriptだけテキストファイルに書き出して、osascriptコマンドでシェル経由で間接実行するようにしてみた。

この回避策はうまく行ったが、これがAppleScriptObjCの処理系のバグなのか、それともプロジェクト中のInfo.plistでeppcのURLプロトコルを宣言してあげないと通信がブロックされるのか、実際に試してみないとなんともいえない。ためしに、Info.plistにeppcのURLプロトコルを書いてみたが、状況に変化はなかった。

■「eppc://」のURLをshared user defautsに保存しようとして拒否された

LAN上のMacをコントロールするアプリケーションを作っていて、LAN上からマシンを選択するようにしてみた。

 choose URL showing {Remote applications}

でLAN上のプログラムリンクがオンになっているマシンを検索して、それをNSTextFieldにstringに変換しつつストア。
NSTextFieldの内容をShared User Defaultsにバインドして、そのまま初期設定ファイルに保存させようとしたら……ダメ。

  eppc://192.168.0.80:3031

のような(ただの)URLの文字列なのに、どうやっても保存できない。

さんざん試してみたところ、プロトコルを示す「eppc://」と、ポート番号を示す「:3031」を削除しないと保存できなかった。
AppleScript Studioではやりたい放題だったが、AppleScriptObjCではMac OS Xの機能の新たな一面を知ることになった。
っていうか、なんか微妙にめんどくさい。

ただ、出来上がったアプリケーションは「これ、本当にAppleScriptで作ってあるの?」という凝った動きができるし、GUIを操作する部分がやけに高速。そのうち、Mac OS X上でAppleScript Studioのアプリケーションが動かなくなるであろうことを考えると(Snow Leopard上では動く)、AppleScriptObjCへの乗り換えを次期OS「Lion」までに行っておくのがよいのではないでしょうか。

12/23 JN SoftwareがMenuApp ASOCのプロジェクトを公開

JN Softwareは、昔からAppleScript Studioの各種サンプルソースコードを公開しており、個人的に大変参考にしていたサイトです。同サイトで、AppleScript Objective-Cのサンプルコード「MenuApp ASOC」を公開しているのを見つけました。

メニューバーに対してメニューを付加し、メニューのサブメニューを動的に生成して、生成したメニューの各項目からハンドラを呼び出しています。

asoc1.jpg

メニューのアイコンもプログレスアイコン、アニメーションアイコンなどに変更できるようになっており、いろいろ利用価値は高そうです。これらの機能のメイン部分はObjective-Cで書かれたプログラムを呼び出すようになっており、ASOC側はコントロールを行うだけになっています。

コード内容を読んでみて、さすがのJN SoftwareもまだASOCに慣れていないためなのか、記述が冗長になっている部分が散見されます。機能を考えるとこれだけの内容で390行(コメント、空行含む)というのは、いくらなんでも長過ぎです(半分ぐらいで納めたい)。分かりやすさを優先したためか、普通ならサブルーチン化するような部分がダラダラ展開されています。

asoc2.jpg

とくにこの(↑)あたり。ここは、サブルーチン化してコンパクトにするだろー、ふつーー。……と思うのですが、ダラダラと代わり映えのしないコードが続きます。ああ、こういうのを見ると無性に効率化したくなる、、、

全体的に、使い回しを重視したようなコードが少なく、流儀の違いを感じます(とくに、User defaultの読み書きを行うあたり。ここを汎用化しないでどーする?)。この調子で大きなプログラムを作っていくのは、自分には大変危険に思えます。また、これだけ見ていると、とうてい高級言語のプログラムとは思えないほど記述効率がよくありません。

AppleScript Studioの頃は、プログラムコードを分割してload scriptで変数に読み込んで実行することで、プログラム分割+共通モジュールの使い回しを実現していました。ASOCでは、どうもこれとは違うアプローチで分割+共通モジュールの使い回しを行うことになりそうです。

ASOCに関しては上級者から初級者まで、まだ手探り状態で「汚いコード、非効率なコードのオンパレード」なのが気になります。……Apple自身がサンプルコードをそれほど出せていないところを見ると、AppleのAppleScriptチーム自身も手探り状態なのでしょうか。

11/28 AppleScriptObjc 3分間クッキング

AppleScriptObjCで、TextFieldに入力した内容をダイアログに表示するという簡単なサンプルを作って、その模様をMac OS X 10.6の機能を使って録画、YouTubeにアップしてみました。

■プロジェクトのダウンロード
asoctest_1.zip
(more…)

11/18 AppleScriptObjC-Dev MLが新設された

US Appleがホスティングしているメーリングリストに新たなML、「applescriptobjc-dev」が追加されていました。アナウンスはとくになかったので……今日まで気付きませんでした。

AppleScript系のMLとしては

AppleScript Users ML:ふつうのAppleScriptに関する話題。AppleScript StudioとかAppleScriptObjCの話題をポストしてはいけない

AppleScript Studio ML:Mac OS X 10.5までAppleScriptのGUIつきプログラム開発環境「AppleScript Studio」(Xcode+Interface Builderを使って書く)に関するML。Mac OS X 10.6のリリース以降、AppleScriptObjCの話題はこちらにポストされていた。

Applescript-implementors ML:アプリケーションへのAppleScript対応機能を実装する開発者のためのML。

Automator Dev ML:Automatorのactionを開発する開発者のためのML。Objective-C系の開発者とAppleScript系の開発者とUNIX系の開発者の間で話題が合うわけもなく、閑古鳥が鳴いている状態。

Automator Users ML:Automatorを使ってワークフロー開発を行うユーザーのためのML。Automator Devよりは流量があるが、それほど話題があるわけではない。

などがあります。AppleScript Studioで作った自作アプリケーション「ML Ranking」で流量に応じたランキングを計算すると、こんな感じ(↓)です。昨日の00:00から1日半ほどの間の流用で集計してみたのですが、AS系ではAppleScript Users MLの流量が多いことが一目瞭然。AppleScript Studio MLから分離したApplescriptObjC Dev MLの今後を見守りたいと思います。

mlranking.jpg

11/17 AppleScriptObjCベースのAutomator Actionのサンプルが公開される

AppleのAppleScriptプロダクトマネージャであるSal Soghoianが、AppleScriptObjCベースのAutomatorアクションのサンプルプロジェクトを公開したことをAppleScript Users MLに投稿していました。

scrn2.jpg

リンク先のページの右側にある「BEFORE YOU BEGIN」にダウンロードファイルへのリンクがあります(分かりづらいなあ)。

scrn1.jpg

FileMaker Proのデータベースに入れた内容をPages上に割り付けるActionの、Xcodeプロジェクトが配布されており……インストーラーでインストール。…………どこへ???

scrn3.jpg

インストールした内容は、/Developper/Examples/Automatorに入ります(説明ないよ、、、)。

scrn4.jpg

掲載しているサイト「Macosxautomation.com」は「Appleがホスティングしているサイトではない」と書かれており……ドメインもSal Soghoian個人の持ち物のようです。そして、今回公開されたサンプルはApple社員によるものではないことが見てとれます。割と画期的な動きというか……Apple.comのサイトでは更新が窮屈ということなのでしょうか?

今回のAutomatorアクションのサンプルは、かなり気合いが入ったものですが……ただ、サンプルという意味ではもっとシンプルなものが数多くあったほうがいいように思います。