指定文字列のAppleScriptを実行してエラー詳細を取得
指定文字列のAppleScriptをコンパイル(構文確認、中間コードへのコンパイル)、実行してエラーの詳細を取得するAppleScriptです。
| AppleScript名:指定文字列のAppleScriptを実行してエラー詳細を取得.scptd |
| — – Created by: Takaaki Naganoya – Created on: 2020/01/01 — – Copyright © 2020 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use scripting additions set asSourceString to "set a to 123 beepz" –This AppleScript text has an error set asObj to current application’s NSAppleScript’s alloc()’s initWithSource:(asSourceString) –AppleScriptオブジェクトがコンパイル(構文確認、中間コードへの解釈)ずみかどうかをチェック set isCompiledF1 to asObj’s isCompiled() –> false –コンパイル(構文確認)してエラー確認 set isCompiledF2 to asObj’s compileAndReturnError:(reference) –> {true, missing value} set exeRes1 to asObj’s executeAndReturnError:(reference) –> {<NSAppleEventDescriptor: null()>, missing value}–No error –> {missing value, (NSDictionary) {NSAppleScriptErrorMessage:"beepz変数は定義されていません。", NSAppleScriptErrorBriefMessage:"beepz変数は定義されていません。", NSAppleScriptErrorNumber:-2753, NSAppleScriptErrorRange:(NSConcreteValue) NSRange: {13, 5}}} |
display text fields Library v1.3
「display text fields」AppleScriptライブラリをv1.3にアップデートしました。
–> Download display text fields_v13 (To ~/Library/Script Libraries/)
v1.3ではテキストフィールドの最大横幅の計算を修正し、フィールド数が増えたときにダイアログ上から項目がはみ出さないようにScroll Viewをつけるようにしました。
本来目的としていた用途に使ってみたらイマイチだった点を修正した格好です。AppleScriptで取得した各種アプリケーションのオブジェクトのプロパティ情報をダイアログ上で一覧表示して確認するというのが、自分がこのライブラリを作った目的です。
以下は、サンプルスクリプト「文字列で指定したAppleScriptの実行結果をテキストで取得する v2」についての説明です。
割とえげつない処理をしていますが、作りためておいたルーチンを引っ張り出してきただけなので、書くのにさほど時間はかけていません。
こうしたAppleScriptのプロパティ値をparseするには、スクリプトエディタの結果欄を文字列として取得するか(GUI Scripting経由でやったことがあります)、こうしてメモリ上にスクリプトエディタ+結果表示用のビューを生成してメモリ上でAppleScriptを実行して結果をテキストで取得するということになると思います。前者だとGUI Scriptingの実行権限が必要になるため、Cocoaの機能を利用したほうが手軽という状況です。
(途中で入れ替えた)v2では、macOS 10.15対応、ランタイム環境によってはうまく動かない「as anything」の使用をやめるなどの変更を加えました。
| AppleScript名:文字列で指定したAppleScriptの実行結果をテキストで取得する v2.scpt |
| — Created 2016-01-08 by Takaaki Naganoya — 2016 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" use framework "OSAKit" use framework "AppKit" use tfLib : script "display text fields" property OSAScript : a reference to current application’s OSAScript property NSTextView : a reference to current application’s NSTextView property OSAScriptView : a reference to current application’s OSAScriptView property OSAScriptController : a reference to current application’s OSAScriptController property myRes : "" –OSのメジャーバージョンを数値で取得 set osVer to system attribute "sys2" if osVer ≥ 15 then set srcStr to "tell application \"Music\" set aSel to first item of selection set aRes to (properties of aSel) end tell" else set srcStr to "tell application \"iTunes\" set aSel to first item of selection set aRes to (properties of aSel) end tell" end if my performSelectorOnMainThread:"getResultStringFromScript:" withObject:(srcStr) waitUntilDone:true set aRes to getListFromText(myRes) of me set aList to {} set bList to {} set aLen to length of aRes repeat with i from 1 to aLen set {aCon, bCon} to contents of item i of aRes set the end of aList to aCon set the end of bList to bCon end repeat confirm text fields main message "Track Info" sub message "Properties about selected track" key list aList value list bList –Get AppleScript’s Result as string on getResultStringFromScript:paramObj set srcStr to paramObj as string set myRes to "" set targX to 500 –View Width set targY to 200 –View Height set osaCon to OSAScriptController’s alloc()’s init() set osaView to OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY)) set resView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY)) resView’s setRichText:true resView’s useAllLigatures:true osaCon’s setScriptView:osaView osaCon’s setResultView:resView osaView’s setString:srcStr osaCon’s runScript:(missing value) set myRes to resView’s |string|() as string end getResultStringFromScript: –スクリプトエディタのresult欄に返ってきたテキストをリストに変える on getListFromText(aText) script getListFromTextO property aaText : "" property gList : {} property outList : {} property aG : "" property valList : {} end script copy aText to (aaText of getListFromTextO) set (gList of getListFromTextO) to {} set (outList of getListFromTextO) to {} set (aG of getListFromTextO) to "" set (valList of getListFromTextO) to {} if (aaText of getListFromTextO) does not start with "{" and (aaText of getListFromTextO) does not end with "}" then return {} end if set aLen to length of (aaText of getListFromTextO) set (aG of getListFromTextO) to text 2 thru -2 of (aaText of getListFromTextO) set (gList of getListFromTextO) to characters of (aG of getListFromTextO) set sPos to 2 –1文字目は\"{\"なので2文字目からスキャンを開始する set ePos to 2 set imdF to false –Immediate Data Flag(文字列中を示すダブルクォート内の場合にはtrueになる) set listF to 0 –stacking段数が入る set attrF to true –属性ラベルスキャン時にtrue、データ末尾スキャン時にfalse repeat with i in (gList of getListFromTextO) set j to contents of i if attrF = true and imdF = false and listF = 0 then –属性値部分の末尾検出 if j = ":" then if text sPos thru sPos of (aaText of getListFromTextO) = " " then set sPos to sPos + 1 end if set anOut to text sPos thru ePos of (aaText of getListFromTextO) set sPos to ePos + 1 set the end of (valList of getListFromTextO) to anOut set attrF to false –データのスキャンを開始する set imdF to false set listF to 0 end if else if imdF = false and listF = 0 and j = "," then –データ部分の末尾検出 set anOut to text sPos thru (ePos – 1) of (aaText of getListFromTextO) set sPos to ePos + 1 set the end of (valList of getListFromTextO) to anOut set the end of (outList of getListFromTextO) to (valList of getListFromTextO) set (valList of getListFromTextO) to {} set attrF to true –次が属性値ラベルであることを宣言 set imdF to false set listF to 0 else if j = "{" then if imdF = false then set listF to listF + 1 –1段スタックにpush end if else if j = "}" then if imdF = false then set listF to listF – 1 –1段スタックからpop end if else if j = "\"" then if imdF = true then set imdF to false else set imdF to true end if end if set ePos to ePos + 1 end repeat –ラストのデータ部分を出力 try set the end of (valList of getListFromTextO) to text sPos thru (ePos – 1) of (aaText of getListFromTextO) set the end of (outList of getListFromTextO) to (valList of getListFromTextO) on error false end try return contents of (outList of getListFromTextO) end getListFromText |
SVGのPath指定をParseしてx,y座標の最小値、最大値を求める
SVGのPath指定(相対座標指定)をparseして、x,y座標の最小値、最大値を求めるAppleScriptです。
SVGで作られた世界地図を表示し、指定の国を赤く塗るというAppleScriptを試作したことがありました(choose countryといった命令を作る野望が)。しかし、メルカトル図法の世界地図で塗った箇所がわからないぐらいの規模の国が世界中にはたくさんあることを再認識しただけでした。つまり、アメリカ合衆国とかロシアとか中華人民共和国ぐらい国土が広ければ赤く塗っておけば視認できますが、赤く塗っても面積が小さければ視認できません。
そこで、地図上では「点」にしか見えないような国の位置を明示するためには、色を変えて塗る以外の表現方法が必要になってきます。
SVG世界地図上では、それぞれの国データがpathで表現されていました。このpathはすべてを絶対座標で表現するタイプと相対座標で表現するタイプがあり、部品として使いまわしたりデータサイズを抑えることを考えれば、相対座標でデータ表現したくなることでしょう。実際、自分が見つけたSVG世界地図データは相対座標でデータが作られていました。
そこで、指定のPath内の座標値をすべて絶対座標に計算し直しつつ、x,y座標の最小値、最大値を求め、その値を元に塗りつぶすことで、面積の小さな国の所在位置を赤い線の交点として表現できそうだと考えました。
そうした実証実験のために作成したのが本AppleScriptです。
テストデータは日本列島のもので、このサイズのデータからx,yの最小値、最大値を計算するのに0.06秒ぐらいで処理できています(自分の開発環境での計測値です)。プログラムの作りやすさを優先して、あとで全データから最大値/最小値を求めていますが、座標計算しながら最大値/最小値を求めれば、0.05秒ぐらいにはなると思います。
→ 同時に計算したら0.039秒で済みました
座標データ数がそれほど多くないので、Cocoaの機能を利用しても大幅な速度向上は見込めません。
本Scriptで取得したPathの最小値、最大値をもとに塗りつぶしを行なってみたところ、地図上で1ドットにも満たないような面積の国でも所在位置がわかるように表現できました。
面積に応じて、塗りつぶし時のアルファ値を変更するような処理もためしていますが、面積が広いのに点にも見えない国(島で構成される国)だと、対象が見えないのに塗りつぶし色が薄くて見にくいなど、まだいろいろ試行錯誤が必要なようです。
| AppleScript名:parseSVGPath.scptd |
| use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use scripting additions script spd property dStr : "" property dList : {} property d2List : {} property d3List : {} property absX : {} property absY : {} end script –Initialize set (dStr of spd) to retData() of me set (dList of spd) to parseByDelim((dStr of spd), {"m ", " z"}) of me set (d2List of spd) to {} –Parse into each stroke repeat with i in (dList of spd) set j to contents of i if j is not equal to "" then set d3 to parseByDelim(j, " ") of me if d3 is not equal to "" then set the end of (d2List of spd) to d3 end if end if end repeat set (d3List of spd) to FlattenList((d2List of spd)) of me set origPos to first item of (d3List of spd) set (d3List of spd) to rest of (d3List of spd) set {origX, origY} to parseByDelim(origPos, ",") of me set origX to origX as real set origY to origY as real set (absX of spd) to {} set (absY of spd) to {} set the end of (absX of spd) to origX set the end of (absY of spd) to origY –絶対座標のリストに変換する repeat with i in (d3List of spd) set j to contents of i if j is not equal to "" then set {curX, curY} to parseByDelim(j, ",") of me set origX to origX + (curX as real) set origY to origY + (curY as real) set the end of (absX of spd) to origX set the end of (absY of spd) to origY end if end repeat set minX to minimumFromList((absX of spd)) of me set maxX to maximumFromList((absX of spd)) of me set minY to minimumFromList((absY of spd)) of me set maxY to maximumFromList((absY of spd)) of me return {{minX, minY}, {maxX, maxY}} –最大値を取得する on maximumFromList(nList) script o property NL : nList end script set max to item 1 of o’s NL repeat with i from 2 to (count nList) set n to item i of o’s NL if n > max then set max to n end repeat return max end maximumFromList –最小値を取得する on minimumFromList(nList) script o property NL : nList end script set min to item 1 of o’s NL repeat with i from 2 to (count nList) set n to item i of o’s NL if n < min then set min to n end repeat return min end minimumFromList 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 on FlattenList(aList) set oldDelims to AppleScript’s text item delimiters set AppleScript’s text item delimiters to {"????"} set aString to aList as text set aList to text items of aString set AppleScript’s text item delimiters to oldDelims return aList end FlattenList on retData() return "m 878.53675,325.4254 0.352,0.02 0.157,-0.018 0.269,0.059 1.067,0.591 0.322,0.075 0.333,0.009 0.234,-0.048 0.206,-0.162 0.644,-0.717 0.678,-0.655 0.075,0.025 0,0.181 -0.05,0.201 -0.3,0.599 -0.333,0.804 -0.071,0.406 0.108,0.395 0.209,0.326 0.165,0.445 0.19,0.617 0.268,0.078 0.146,0.009 0.268,-0.185 0.253,-0.235 0.218,-0.029 0.229,0.04 -0.301,0.164 -0.284,0.2 -0.244,0.378 -0.091,0.071 -0.281,-0.023 -0.161,0.014 -0.329,0.158 -0.289,0.179 -0.276,0.219 -0.301,0.117 -0.321,0.028 -0.495,0.179 -0.321,0.013 -0.603,-0.156 -0.293,0.042 -0.639,0.352 -0.582,0.508 -0.509,0.569 -0.425,0.682 -0.171,0.354 -0.103,0.396 -0.014,0.262 -0.038,0.251 -0.098,0.175 -0.118,0.142 -0.351,-0.083 -0.576,-0.362 -1.117,-0.527 -1.18,-0.809 -0.664,-0.407 -1.248,0.122 -1.181,0.776 -0.105,-0.066 -0.451,-0.532 -0.219,-0.211 -0.261,-0.06 -0.194,0.007 -0.188,0.039 -0.267,0.274 -0.098,0.197 -0.076,0.224 -0.023,0.158 0.032,0.155 0.253,0.35 0.312,0.26 0.141,0.049 0.299,-0.018 0.139,0.027 0.501,0.521 0.549,0.488 0.121,0.161 -0.203,0.169 -0.222,0.084 -0.259,-0.021 -0.256,-0.064 -0.438,-0.207 -0.188,0.176 -0.292,0.361 -0.161,0.392 -0.131,0.179 -0.322,0.236 -0.341,0.122 -0.179,-0.04 -0.136,-0.148 -0.077,-0.177 -0.039,-0.206 0.072,-0.446 0.177,-0.403 0.068,-0.412 -0.145,-0.581 -0.092,-0.122 -0.362,-0.341 -0.168,-0.333 -0.04,-0.415 0.021,-0.229 0.088,-0.507 0.088,-0.256 0.166,-0.084 0.182,-0.051 0.279,-0.184 0.307,-0.238 0.294,-0.272 0.291,-0.333 0.151,-0.366 -0.249,-0.45 -0.052,-0.268 0.037,-0.253 0.266,-0.135 0.273,0.102 0.551,0.372 0.107,0.037 0.378,0.017 0.518,0.081 0.3,-0.02 0.145,-0.056 0.218,-0.307 0.107,-0.392 -0.039,-0.505 -0.002,-0.502 0.138,-0.41 0.432,-0.661 0.124,-0.39 0.045,-0.952 0.156,-0.42 0.125,-0.437 0.06,-0.918 -0.177,-0.886 -0.178,-0.436 -0.204,-0.415 0.03,-0.397 0.165,-0.369 0.004,-0.111 0.039,-0.099 0.309,-0.07 0.145,-0.079 0.139,-0.178 0.165,-0.104 0.121,0.104 0.1,0.182 0.436,0.447 0.687,0.796 0.809,1.209 0.507,0.587 0.534,0.531 0.599,0.542 0.627,0.465 0.4,0.22 0.293,0.35 0.181,0.057 z m -7.723,-4.787 -0.108,0.252 -0.146,-0.375 -0.029,-0.405 0.084,0.002 0.154,0.061 0.037,0.197 0.008,0.268 z m 0.625,0.848 -0.194,0.028 -0.227,-0.166 -0.028,-0.208 0.164,-0.166 0.163,0.061 0.163,0.214 0.054,0.112 -0.095,0.125 z m -5.091,11.738 -0.064,0.02 -0.066,-0.032 -0.065,-0.285 0.056,-0.15 0.181,-0.105 0.176,-0.029 -0.149,0.524 -0.069,0.057 z m 4.906,2.653 0.11,0.07 0.524,-0.189 -0.1,0.571 -0.056,0.575 0.038,0.952 0.047,0.428 0.091,0.414 0.224,0.296 0.292,0.21 0.423,0.669 0.227,0.82 0.16,0.397 0.12,0.416 0.037,0.19 0.003,0.19 -0.036,0.26 0.04,0.21 -0.046,0.658 -0.189,0.759 -0.024,0.387 -0.165,0.077 -0.1,0.179 -0.085,0.083 -0.095,0.064 -0.137,0.016 -0.099,0.073 -0.039,0.205 -0.063,0.189 -0.12,0.175 -0.094,0.192 -0.077,0.468 -0.028,0.479 -0.115,0.334 -0.279,0.087 -0.319,-0.005 -0.409,0.154 -0.088,0.091 -0.323,0.582 -0.093,0.341 -0.003,0.366 0.09,0.45 0.122,0.439 0.092,0.815 -0.097,1.238 -0.094,0.395 -0.205,0.267 -0.156,0.124 -0.134,0.152 -0.174,0.401 -0.288,0.798 -0.023,0.2 10e-4,0.207 -0.078,0.27 -0.05,0.265 0.047,0.308 0.089,0.288 0.386,0.737 0.151,0.218 0.17,0.198 -0.659,0.219 -0.118,0.101 -0.392,0.418 -0.125,0.396 0.01,0.438 -0.065,0.157 -0.107,0.136 -0.112,0.09 -0.438,0.2 -0.28,0.199 -0.278,0.311 -0.11,0.162 -0.215,-0.052 -0.126,-0.144 0.124,-0.18 -0.038,-0.213 0.061,-0.547 -0.069,-0.22 0.224,-0.167 0.106,-0.266 0.232,-0.213 0.167,-0.189 0.029,-0.154 -0.149,-0.166 -0.158,-0.12 -0.218,0 -0.21,0.035 -0.137,0.158 -0.045,0.215 0.011,0.1 -0.018,0.087 -0.33,0.294 0.043,0.307 0.097,0.156 0.124,0.074 -0.037,0.105 -0.157,0.247 -0.109,0.024 -0.201,-0.346 -0.252,-0.189 -0.312,10e-4 -0.32,0.069 -0.243,0.23 -0.08,0.191 -0.052,0.197 0.018,0.479 -0.101,0.4 -0.198,0.351 -0.092,0.128 -0.241,0.238 -0.167,0.031 -0.118,-0.108 -0.096,-0.164 0.122,-0.6 -0.005,-0.338 0.282,-0.172 -0.231,-0.24 -0.284,-0.097 -0.399,0.128 -0.113,0.145 -0.077,0.194 -0.215,0.246 -0.236,0.229 -0.269,0.392 -0.18,0.463 -0.59,-0.151 -0.321,-0.034 -0.325,0.012 -0.576,-0.057 -0.632,0.095 -0.72,0.182 0.043,-0.131 0.592,-0.279 0.021,-0.082 -0.057,-0.153 -0.147,-0.007 -0.354,0.053 -0.181,-0.023 -0.073,-0.164 -0.12,-0.07 -0.08,0.067 0.026,0.318 -0.087,0.042 -0.117,-0.082 0.037,-0.248 -0.079,-0.363 -0.01,-0.226 0.124,-0.194 -0.127,-0.082 -0.134,0.031 -0.17,0.093 -0.15,0.132 -0.317,0.663 -0.123,0.378 0.232,0.303 0.634,0.423 0.108,0.104 0.002,0.18 -0.077,0.191 -0.172,0.083 -0.695,0.141 -0.603,0.273 -0.174,0.276 -0.548,1.066 -0.439,0.727 -0.62,0.25 -0.68,-0.223 -0.165,-0.253 -0.133,-0.313 -0.253,-0.283 -0.229,-0.309 -0.133,-0.367 0.021,-0.592 -0.099,-0.357 0.089,-0.095 0.375,-0.218 0.123,-0.122 0.212,-0.284 0.076,-0.158 0.011,-0.239 -0.17,-0.125 -0.44,0.004 -0.439,0.075 -0.313,-0.104 -0.407,-0.29 -0.126,-0.062 -0.438,-0.018 -0.313,0.054 -0.307,0.105 -0.325,0.034 -0.108,0.055 -0.375,0.355 -0.299,0.223 -0.258,0.111 -0.557,0.029 -0.279,0.072 -0.292,0.117 -0.082,-0.01 -0.308,0.162 -0.354,0.14 -0.188,0.14 -0.347,-0.092 -0.686,0.251 -0.332,0.031 -0.342,-0.139 -0.318,-0.225 -0.305,0.097 -0.209,0.331 -0.102,0.658 -0.119,0.296 -0.036,0.355 -0.158,-0.056 -0.918,-0.639 -0.063,-0.023 -0.742,0.11 -0.191,0.053 -0.239,0.129 -0.253,0.058 -0.23,-0.093 -0.221,-0.151 -0.212,0.045 -0.217,0.106 -0.084,-0.966 0.043,-0.127 0.133,-0.169 0.147,-0.145 0.359,-0.05 0.363,0.047 0.26,-0.066 0.22,-0.192 0.231,-0.272 0.261,-0.222 0.354,-0.175 0.342,-0.201 0.301,-0.282 0.285,-0.309 0.262,-0.227 0.285,-0.19 0.434,-0.458 0.575,-0.515 0.221,-0.382 0.137,-0.105 0.495,-0.213 0.657,-0.164 0.31,0.008 0.306,0.335 0.165,-0.046 0.168,-0.087 0.338,-0.048 0.348,0.056 0.34,0.001 0.34,-0.042 0.652,-0.11 0.344,-0.133 0.336,-0.172 1.196,-0.121 0.819,-0.289 0.13,0.021 0.126,0.066 0.01,0.214 -0.104,0.233 0.101,0.141 0.166,0.087 0.771,0.027 0.22,0.05 0.322,-0.159 0.304,-0.197 0.317,-0.26 0.222,-0.293 -0.205,-0.367 -0.045,-0.402 0.172,-0.437 0.249,-0.368 0.296,-0.223 0.273,-0.257 0.552,-0.73 0.399,-0.592 0.144,-0.73 -0.084,-0.866 0.349,-0.646 0.334,-0.111 0.663,-0.295 0.348,-0.086 0.052,0.129 -0.01,0.169 -0.521,0.542 -0.299,0.223 -0.178,0.069 -0.164,0.098 -0.067,0.191 0.266,0.318 0.067,0.235 -0.017,0.225 0.012,0.203 0.3,0.22 0.345,0.073 0.144,-0.002 0.126,-0.058 0.394,-0.539 0.088,-0.094 1.12,-0.396 0.551,-0.303 0.305,-0.079 0.286,-0.158 0.642,-0.61 0.236,-0.282 0.215,-0.312 0.172,-0.363 0.136,-0.393 0.186,-0.245 1.017,-0.585 0.327,-0.319 0.104,-0.155 0.126,-0.449 0.087,-0.467 0.123,-0.367 0.167,-0.344 0.223,-0.351 0.251,-0.327 0.148,-0.335 0.216,-0.803 0.094,-0.446 0.074,-0.161 0.108,-0.135 0.094,-0.172 0.072,-0.192 0.033,-0.19 0.046,-0.582 -0.028,-0.454 -0.168,-0.385 -0.139,-0.109 -0.151,-0.006 -0.228,0.033 -0.193,-0.157 0.04,-0.139 0.197,-0.026 0.134,-0.072 0.097,-0.131 0.181,-0.421 0.108,-0.452 0.01,-0.2 -0.142,-0.365 -0.112,-0.439 -0.003,-0.237 0.123,-0.274 0.176,-0.223 0.157,-0.052 0.17,-0.016 0.155,-0.086 0.144,-0.125 0.081,-0.138 0.126,-0.376 0.049,-0.214 -0.08,-0.576 0.082,-0.158 0.116,-0.098 0.155,0.074 0.16,0.015 0.186,-0.023 0.178,0.061 0.033,0.148 0.112,0.972 0.064,0.131 0.13,0.102 0.146,-0.015 0.126,-0.151 0.085,-0.201 0.168,-0.042 0.513,0.216 0.182,-0.154 0.118,-0.239 0.103,-0.423 -0.05,-0.382 -0.123,-0.142 -0.127,0.025 -0.112,0.105 -0.126,0.057 -0.757,0.201 0.004,-0.426 0.162,-0.64 0.089,-0.203 0.128,-0.096 0.318,0.112 0.155,0.074 0.339,0.318 z m -8.098,12.885 -0.267,0.009 -0.066,-0.035 0.161,-0.088 0.015,-0.147 0.097,-0.262 -0.002,-0.075 -0.212,-0.014 0.011,-0.297 0.158,-0.294 0.435,-0.463 0.119,-0.087 0.018,0.203 -0.132,0.479 -0.026,0.173 0.342,0.036 -0.22,0.573 -0.431,0.289 z m -13.958,5.664 -0.128,0.129 -0.24,-0.042 -0.139,-0.187 0.046,-0.211 0.251,-0.162 0.24,0.325 -0.03,0.148 z m 17.08,5.069 -0.03,0.16 -0.149,-0.035 -0.071,-0.104 0.009,-0.187 0.157,-0.001 0.084,0.167 z m -28.264,1.264 -0.057,0.163 -0.19,-0.115 -0.087,-0.104 0.177,-0.513 -0.021,-0.195 0.011,-0.095 0.351,-0.269 0.061,0.051 0.021,0.075 -0.031,0.115 0.018,0.254 -0.264,0.42 0.011,0.213 z m 15.569,0.221 -0.304,0.288 -0.264,-0.02 -0.132,-0.129 -0.044,-0.159 0.251,-0.25 0.216,-0.354 0.196,-0.157 0.158,-0.088 0.124,0.003 -0.278,0.494 0.077,0.372 z m -1.631,-0.661 -0.052,0.067 -0.05,-0.018 -0.179,0.155 -0.038,-0.149 -0.141,-0.099 -0.017,-0.078 0.403,-0.051 0.13,0.041 -0.056,0.132 z m 0.015,0.769 0.389,0.141 0.397,-0.04 -0.006,0.617 0.057,0.207 0.111,0.185 -0.057,0.269 0.18,0.092 -0.534,0.306 -0.481,0.406 -0.198,0.273 -0.179,0.293 -0.104,0.31 -0.067,0.333 -0.161,-0.132 -0.464,-0.541 -0.294,-0.148 -0.473,-0.079 -0.15,0.018 -0.972,0.505 -0.129,0.369 -0.265,0.555 -0.126,0.183 -0.14,0.053 -0.099,0.095 -0.108,0.472 -0.301,0.29 -0.185,0.009 -0.314,-0.08 -0.142,0.045 0.188,-0.465 -0.3,-0.057 -0.301,0.01 -0.007,-0.303 -0.182,-0.172 0.135,-0.224 0.004,-0.183 0.079,-0.101 0.027,-0.147 -0.011,-0.126 -0.185,-0.038 -0.113,-0.089 0.021,-0.332 -0.106,-0.012 -0.264,0.058 -0.548,0.256 -0.149,0 0.229,-0.183 0.487,-0.25 0.219,-0.144 0.478,-0.404 0.301,-0.191 0.156,-0.339 0.049,-0.206 0.102,-0.182 0.09,-0.291 0.154,-0.097 0.269,-0.25 0.154,0.024 0.172,0.307 0.231,0.235 0.166,-0.02 0.297,-0.121 0.144,-0.027 0.343,0.015 0.309,-0.149 0.126,-0.176 0.047,-0.22 -0.115,-0.369 0.148,0.037 0.143,-0.015 0.335,-0.235 0.345,-0.14 0.357,-0.035 0.402,0.133 0.389,0.212 z m -14.25,0.449 -0.183,0.137 -0.078,-0.209 0.08,-0.594 0.343,0.122 -0.006,0.182 -0.156,0.362 z m 9.258,0.028 -0.081,0.135 -0.249,-0.041 0.1,-0.117 0.076,-0.142 0.056,-0.029 0.047,0.154 0.051,0.04 z m -0.877,0.573 0.137,0.124 0.325,-0.05 0.04,0.035 -0.095,0.112 -0.144,0.111 -0.26,-0.083 -0.164,-0.004 -0.023,-0.185 0.021,-0.067 0.163,0.007 z m -3.062,1.151 0.377,0.1 0.16,0.006 0.147,-0.045 0.226,-0.131 0.235,-0.097 0.169,0.049 0.149,0.117 0.078,0.165 -0.038,0.172 -0.267,0.37 -0.22,0.394 0.504,0.074 0.504,-0.008 -0.117,0.243 -0.02,0.212 0.154,0.101 0.131,0.136 -0.034,0.123 -0.075,0.121 0.275,0.182 -0.018,0.122 -0.071,0.128 -0.687,0.834 -0.202,0.421 -0.14,0.463 -0.129,0.338 -0.094,0.351 -0.071,0.378 -0.128,0.39 0.043,0.345 -0.043,0.354 -0.345,0.872 -0.246,-0.016 -0.309,-0.105 -0.193,0.016 -0.1,0.193 0.177,0.397 -0.55,0.471 -0.608,0.316 -0.004,-0.144 0.058,-0.113 0.087,-0.091 0.063,-0.11 0.089,-0.37 -0.044,-0.373 -0.184,-0.468 -0.013,-0.168 0.127,-0.068 0.085,-0.02 0.046,-0.065 0.002,-0.155 -0.058,-0.115 -0.174,-0.037 -0.166,-0.002 -0.117,0.174 -0.161,0.335 -0.078,0.339 0.034,0.185 0.071,0.166 0.221,0.277 -0.064,0.164 -0.092,0.127 -0.78,-0.289 -0.169,-0.021 -0.14,-0.061 -0.15,-0.381 0.318,-0.092 0.094,-0.046 0.035,-0.122 0.043,-0.372 -0.148,-0.312 -0.126,-0.111 -0.102,-0.127 0.064,-0.263 -0.042,-0.332 -0.004,-0.464 0.055,-0.08 0.295,-0.094 0.213,-0.249 0.188,-0.284 0.275,-0.5 0.226,-0.541 -0.217,-0.023 -0.184,-0.102 0.2,-0.256 -0.062,-0.324 -0.3,-0.398 -0.165,-0.47 -0.265,-0.208 -0.14,-0.076 -0.171,0.11 -0.14,0.132 0.13,0.306 -0.016,0.271 0.021,0.268 0.132,0.015 0.163,-0.067 0.13,0.047 0.076,0.141 0.021,0.181 -0.058,0.179 -0.119,0.089 -0.146,-0.007 -0.147,-0.097 -0.115,-0.138 -0.275,-0.076 -0.29,0.163 -0.275,0.332 -0.235,0.168 0.11,-0.246 0.053,-0.266 -0.114,-0.187 -0.27,-0.311 -0.063,-0.18 -0.016,-0.218 0.048,-0.216 0.276,0.248 0.142,0.309 0.204,0.136 0.255,0.001 -0.196,-0.454 -0.07,-0.115 -0.274,-0.204 -0.376,-0.343 -0.238,-0.166 0.084,-0.359 0.141,-0.071 0.118,0.017 0.398,0.127 0.037,-0.179 -0.059,-0.096 -0.03,-0.111 0.263,-0.155 0.429,-0.128 0.088,-0.06 0.076,-0.129 0.105,-0.067 0.3,0.002 0.253,-0.124 0.209,-0.335 0.05,-0.184 0.074,-0.154 0.521,-0.272 0.129,-0.042 0.349,0.034 0.318,0.154 0.157,0.324 0.138,0.346 0.331,0.235 z m -3.87,-0.491 -0.194,0.139 -0.146,-0.108 0.071,-0.3 0.048,-0.1 0.166,0.099 0.055,0.27 z m -0.854,1.762 -0.197,0.157 -0.143,0 0.13,-0.184 0.018,-0.087 0.109,-0.247 0.213,-0.088 0.09,-0.011 -0.174,0.256 -0.046,0.204 z m -1.164,1.274 -0.07,0.036 -0.09,-0.3 -0.063,-0.107 0.105,-0.057 0.211,-0.545 0.038,0.216 0.082,0.214 0.082,0.034 -0.08,0.156 -0.117,0.058 -0.098,0.295 z m 30.21,-0.718 -0.049,0.035 -0.135,-0.109 -0.02,-0.097 0.024,-0.06 0.088,-0.013 0.182,0.119 -0.09,0.125 z m -31.365,0.906 0.109,0.09 0.159,-0.051 0.127,-0.012 0.091,0.043 0.115,0.231 0.042,0.137 -0.205,0.019 -0.087,0.032 -0.112,0.167 -0.161,-0.062 -0.101,-0.079 -0.022,-0.111 0.045,-0.404 z m 3.977,1.835 -0.222,0.118 -0.028,-0.113 -0.094,-0.052 0.161,-0.159 -0.006,-0.072 -0.102,-0.107 0.118,-0.406 -0.032,-0.175 0.443,-0.065 0.081,0.165 0.008,0.5 -0.327,0.366 z m 0.838,-0.641 -0.248,0.015 -0.103,-0.039 -0.041,-0.105 0.348,-0.213 0.27,0.038 -0.12,0.192 -0.106,0.112 z m -1.86,2.526 -0.088,0.057 0.056,-0.258 0.226,-0.226 0.019,0.146 -0.213,0.281 z m 3.484,4.107 -0.246,0.034 -0.005,-0.188 0.195,-0.424 0.021,-0.312 0.182,-0.394 0.077,-0.086 0.058,-0.031 0.063,0.123 -0.07,0.482 -0.183,0.365 -0.092,0.431 z m -0.945,0.433 -0.321,0.07 -0.176,-0.075 -0.162,-0.399 0.307,-0.25 0.41,0.248 0.084,0.071 -0.142,0.335 z m -3.284,6.573 -0.242,0.257 -0.257,-0.232 -0.31,-0.155 0.147,-0.042 0.086,-0.063 0.009,-0.098 0.201,-0.146 0.398,-0.113 0.127,-0.007 0.143,-0.106 0.047,-0.096 0.059,-0.046 0.257,-0.132 0.07,0.152 -0.012,0.119 -0.193,0.066 -0.188,0.159 -0.175,0.198 -0.156,0.084 -0.05,0.056 0.039,0.145 z m -0.361,0.33 0.019,0.074 -0.275,-0.062 -0.112,-0.289 0.183,0.052 0.056,0.099 0.129,0.126 z m -0.915,1.213 -0.118,0.058 -0.157,-0.08 -0.049,-0.362 0.07,-0.176 0.123,-0.039 0.107,0.313 0.074,0.129 -0.05,0.157 z m -2.075,3.352 -0.271,0.144 -0.1,0.171 -0.25,0.058 -0.243,0.24 -0.236,0.043 0.006,0.193 0.101,0.163 -0.157,0.029 -0.165,0.199 -0.013,0.145 0.059,0.116 -0.008,0.059 -0.208,0.172 -0.215,0.008 -0.009,-0.186 0.014,-0.139 0.203,-0.338 0.005,-0.393 0.188,-0.045 0.068,-0.055 0.297,-0.28 0.055,-0.119 -0.154,-0.116 0.012,-0.137 0.035,-0.058 0.244,0.044 0.099,0.102 0.049,0.011 0.143,-0.076 0.067,-0.136 0.266,-0.268 0.108,-0.266 0.215,0.218 -0.058,0.286 -0.147,0.211 z m 39.093,0.114 -0.052,0.002 -0.176,-0.331 0.051,-0.016 0.103,0.052 0.113,0.191 -0.039,0.102 z m -46.992,5.802 -0.237,0.08 -0.254,-0.047 0.041,-0.429 0.088,0.06 0.055,0.146 0.189,0.086 0.118,0.104 z m -3.231,0.698 -0.165,0.484 -0.137,0.07 -0.14,-0.039 -0.144,-0.27 0.101,-0.104 0.14,0.055 0.113,-0.021 0.257,-0.394 0.062,0.064 -0.087,0.155 z m -1.134,0.722 -0.177,0.043 -0.213,-0.053 -0.194,-0.015 -0.002,-0.091 0.203,-0.094 0.004,-0.131 0.05,-0.071 0.459,0.161 -0.02,0.118 -0.11,0.133 z" end retData |
ISOコードから国名を取得
ISOコード(ISO 3166-1 Alpha-2 code)で示した国の名称を取得するAppleScriptです。
現在のロケールでローカライズして取得するもの(retCountryNameInCurrentLocale)と、指定ロケールでローカライズするもの(retCountryNameInSpecifiedLocale)を用意しておきました。
Mac App Storeに出したDouble PDF v2.0(30言語以上ローカライズした)の審査は年越し確実で、審査に1か月以上かかるという、自分の知っている範囲での最長記録を更新しつつあります(AppleがOSに作ったバグを回避する以外の追加機能はわずかなのに)。
そんな中、各国語で国名を表記する方法を調べておきました。ヒンディー語、タイ語、ギリシア語あたりは文字が見慣れないながらもギリギリ文字単位での識別が可能で、非日常感があって(日本語の表記範囲で出てこない文字なので)クールな感じがします(個人の見解です)。アラビア語やヘブライ語になってしまうと、コンピュータの挙動が変わってしまうので(表記が右→左)、ちょっとシャレにならない感じであります。
| AppleScript名:ISOコードから国名を取得.scpt |
| — – Created by: Takaaki Naganoya – Created on: 2019/12/30 — – Copyright © 2019 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use scripting additions set aCountryName to retCountryNameInCurrentLocale("GE") of me –> "ジョージア" —In Japanese (Current Locale, diffenret in each user) set bCountryName to retCountryNameInSpecifiedLocale("GE", "en_US") of me –> "Georgia" –In English set cCountryName to retCountryNameInSpecifiedLocale("GE", "fr") of me –> "Géorgie"–In French set dCountryName to retCountryNameInSpecifiedLocale("GE", "ru") of me –> "Грузия"–In Russian set eCountryName to retCountryNameInSpecifiedLocale("GE", "zh") of me –>"格鲁吉亚"–In Chinese set fCountryName to retCountryNameInSpecifiedLocale("GE", "ko_KR") of me –> "조지아"–In Korean set gCountryName to retCountryNameInSpecifiedLocale("GE", "hi") of me –> "जॉर्जिया" –In Hindi set hCountryName to retCountryNameInSpecifiedLocale("GE", "th") of me –> "จอร์เจีย"-In Thai set iCountryName to retCountryNameInSpecifiedLocale("GE", "el_GR") of me –> "Γεωργία"–In Greek –指定の国名コードから国名を現在のロケールでローカライズして返す on retCountryNameInCurrentLocale(isoCode as string) set curLocale to current application’s NSLocale’s currentLocale() set aLocLangCode to (curLocale’s objectForKey:(current application’s NSLocaleLanguageCode)) as string set aCountry to curLocale’s displayNameForKey:(current application’s NSLocaleCountryCode) value:isoCode return aCountry as string end retCountryNameInCurrentLocale –指定の国名コードから国名を指定のロケールでローカライズして返す on retCountryNameInSpecifiedLocale(isoCode as string, targLocale as string) set curLocale to current application’s NSLocale’s localeWithLocaleIdentifier:targLocale set aLocLangCode to (curLocale’s objectForKey:(current application’s NSLocaleLanguageCode)) as string set aCountry to curLocale’s displayNameForKey:(current application’s NSLocaleCountryCode) value:isoCode return aCountry as string end retCountryNameInSpecifiedLocale |
アラートダイアログをタイトル付き表示
アラートダイアログで、ウィンドウタイトルを表示させるAppleScriptです。
AppleScriptには登場当初からダイアログの表示命令「display dialog」が備わっていました。久しぶりにClassic MacOSのdisplay dialogコマンドの表示を見ると、あまりの素朴さに驚かされます。
display dialogは手軽であるもののモーダルなダイアログなので、その後のMac OS Xへの移行時にはいまひとつ合わない感じがしていました。完全にMac OS Xの流儀で書き直されたのが、「display alert」ダイアログです。こちらもモーダル表示ですが、見た目は少しMac OS Xらしく変更されました。
display alertは、さまざまなオプションを指定できる一方で手軽さがいまひとつで、やはりdisplay dialog命令のほうが圧倒的に利用されてきたという経緯があります(自分もほとんどdisplay alertは使いません)。
# display alertダイアログに表示されるボタンはTouchBarに表示されますが、display dialogダイアログ上のボタンはTouchBarには表示されません
非モーダル表示なUIについては、display notificationで通知表示が使えるようになり、これで一応の解決を見たというところでしょうか。こういう動作をする通知ダイアログは、Newt On Projectの中で「どうしても欲しい」という要望が出て、2000年代初頭にすでに仲間内で作ってもらったものがあり、AppleScriptから半透明のダイアログ(画面右側からスライドアニメーション表示)を表示させていました。
その一方で、display youtubeとかdisplay tableといった、ダイアログに各種GUI部品を詰め込んで簡易表示させるインタフェース「箱庭インタフェース」が今年局所的なブームを起こし、地図は表示させるわテーブルは表示させるわ、世界地図を表示して国選択を行わせるわで「計算結果を手軽に表示できたり、ファイルパスをわかりやすく選択できて便利」という評価をいただいています。
で、この「箱庭インタフェース」はもれなくalert dialogのインタフェースを用いています。display alertで表示されるものと同じGUI部品ですね。

▲HIGにサイコーに反していろいろいじくりまくっているpickup colorダイアログ。アイコン部分を色プレビュー領域に。でも、強烈に使いやすい



ちょっとした結果表示や項目選択のためにアラートダイアログ上にGUI部品を並べるやりかたは、「アリ」だと思っています。
そんな中、知り合い筋から「alertダイアログにタイトルは出せないのか?」と問い合わせがありました。アラートダイアログでタイトル表示が可能なことは知っていましたが、
Human Interface Guidelineに合わない仕様なので、タイトル表示は使ってきませんでした。
ただ、「箱庭インタフェース」シリーズでここまで無茶苦茶をやっておいて、いまさら他人に「HIGに反する」とかいっても説得力がまったくありません。
HIGのガイドラインに反するといっても、実際に使い勝手がよくなったり、顧客や上司からの無茶振りをかわすためであればアラートダイアログにタイトルを追加するというのも、アリなのかもしれません。自分はやりませんけれども。
| AppleScript名:アラートダイアログをタイトル付き表示 |
| — Created 2019-12-26 by Takaaki Naganoya — 2019 Piyomaru Software use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" property NSAlert : a reference to current application’s NSAlert property NSRunningApplication : a reference to current application’s NSRunningApplication property returnCode : 0 set paramObj to {myMessage:"アラート表示", mySubMessage:"アラートダイアログを表示します", myTitle:"たいとる?"} set dRes to displayAlert(paramObj) of me on displayAlert(paramObj) my performSelectorOnMainThread:"displayAlertDialog:" withObject:(paramObj) waitUntilDone:true if (my returnCode as number) = 1001 then error number -128 return true end displayAlert on displayAlertDialog:paramObj set aMainMes to myMessage of paramObj set aSubMes to mySubMessage of paramObj set aTitle to myTitle of paramObj — set up alert set theAlert to NSAlert’s alloc()’s init() tell theAlert its setMessageText:aMainMes its setInformativeText:aSubMes its addButtonWithTitle:"OK" –its addButtonWithTitle:"Cancel" set myWin to its |window| end tell myWin’s setTitle:aTitle — show alert in modal loop NSRunningApplication’s currentApplication()’s activateWithOptions:0 my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true return (aScrollWithTable’s documentView’s selectedRow()) + 1 end displayAlertDialog: on doModal:aParam set (my returnCode) to aParam’s runModal() end doModal: |
アラートダイアログ上にTable Viewを表示 v8_ソート、並び替え、複数選択
アラートダイアログ上にTable Viewを表示するGUI部品を提供するAppleScriptです。Edama2さんから投稿していただいたものです。
箱庭テーブルビューシリーズは、macOS標準装備の「choose from list」コマンドでは選択肢が増えたときに使いにくいので、その代替品としてTable Viewを用いたポップアップしない単項目の選択UIを実装したものでした。

▲最初のバージョン。みなぎる使い捨て感
それが途中から「Myriad Tablesが使えない場面(Framework入りの高機能ライブラリのため、利用できないケースが発生)に使う使い捨て部品」に進化。

コードサイズの投げやりなまでの小ささと、いつもどおりのプレーンな読み味から改造のためのベースキットとして活用されまくり、派生品や亜種が内外でいろいろ発生しています(きっと知らないところで改変版が作られているはず)。
sdefをつけてライブラリ化したり、色付きダイアログなどいろいろキワモノが派生しましたが、実用性を考慮して半透明ウィンドウではなく通常ウィンドウとして表示する方向に変わりつつあります。
| Version | Author | Date | Description |
| v1 | Piyomaru | 2019/2/14 | For choose from list without popup menu. One field selection UI. |
| v2 | Piyomaru | 2019/2/14 | |
| v3 | Piyomaru | 2019/2/14 | |
| v4 | Piyomaru | 2019/7/11 | Table user interface for item selection |
| v5_e | KniazidisR | 2019/11/2 | Separate display dialog stage into making and displaying |
| v5_m | Piyomaru | 2019/12/23 | Filtering by multiple field |
| v6 | Piyomaru | 2019/12/24 | Search Field Added |
| v7 | Edama2 | 2019/12/240 | Vertical & horizontal resizing |
| v8 | Edama2 | 2019/12/25 | Record sorting & Field re-ordering |
以下、v8についてのedama2さんからの説明文です。
コラムのヘッダのクリックでテーブルのソート ドラッグ&ドロップによる行の並び替え 行の複数選択 ができるようになりましたが、ヘッダのソート状態のときにD&Dの並び替えはできません。 飛び飛びに複数行選択しても並び替えできますが、たまにズレている気がします...(汗) ArrayControllerを使用したのが機能過剰と思われたかもしれませんが、まずXCode上で一度組んだものを逆移植しているのでそんなに深い意味はないです。 でもソート方法を試行錯誤して調べていたら、Cocoa bindingをコーディングから設定できることがわかった(よく考えれば当たり前ですが)のが、何か壁を一枚破った気がしました。
Cocoa bindingがプログラムから動的に実行できることは、調査して知っていましたが、実際にAppleScriptのコードとして記述されたものは見たことがありませんでした(Mac Scripterのフォーラムを漁ると出てくるのかも?)。
### 表示内容をNSArrayControllerにバインディング bind_toObject_withKeyPath_options_(current application’s NSValueBinding, my _data_source, "arrangedObjects." & aTitle, missing value)
ここの部分の記述は、クリスマスプレゼントとして楽しませていただきます。Cocoa Bindingなんて、Xcode上でGUI部品とイベント受信部分のハンドラとの接続をただ漫然とヒモでつなぐ作業をしていただけだったので、Cocoa Binding Referenceなんてはじめて細部にわたって眺めましたわー。

▲起動状態。フィールドの横サイズそろえ、レコード数により表UIの縦サイズが調整されている

▲Specsフィールドを中央に持ってきた。ちゃんとUIが細かいアニメーション動作を行う

▲選択行をドラッグ&ドロップで移動。複数行も移動可能。ただし、フィールド指定のソート状態を有効にすると行移動が効かないという
| AppleScript名:アラートダイアログ上にTable Viewを表示 v8_ソート、並び替え、複数選択.scpt |
| on run set aTableList to {} set aTableList’s end to {|Name|:"MacBook Air", Price:"119,800円", Specs:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"} set aTableList’s end to {|Name|:"MacBook Pro 13", Price:"139,800円", Specs:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"} set aTableList’s end to {|Name|:"MacBook Pro 15", Price:"258,800円", Specs:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"} set aTableList’s end to {|Name|:"Mac mini", Price:"122,800円", Specs:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"} set aTableList’s end to {|Name|:"iMac 21.5", Price:"120,800円", Specs:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"} set aTableList’s end to {|Name|:"iMac 4K 21.5", Price:"142,800円", Specs:"3.6GHzクアッドコアIntel Core i3"} set aTableList’s end to {|Name|:"iMac 5K 27", Price:"198,800円", Specs:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"} set optionRec to {aTableList:aTableList} set optionRec to optionRec & {aSortOrder:{column1:"Name", column2:"Price", column3:"Specs"}} set aMainMes to "項目の選択" set aSubMes to "適切なものを以下からえらんでください" set dateObj to my chooseData(aMainMes, aSubMes, optionRec) end run # アラートダイアログでtableviewを表示 on chooseData(aMainMes, aSubMes, optionRec) script MyDialog property parent : AppleScript use AppleScript use scripting additions use framework "Foundation" property _retrieve_data : missing value on make set aClass to me script property parent : aClass end script end make ## ダイアログの呼び出し on call(aMainMes, aSubMes, optionRec) set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myOption:optionRec} parent’s performSelectorOnMainThread:"raize:" withObject:paramObj waitUntilDone:true if (my _retrieve_data) is missing value then error number -128 return (my _retrieve_data) end call ## ダイアログの生成 on raize:paramObj ### set mesText to paramObj’s myMessage set infoText to paramObj’s mySubMessage set paramObj to paramObj’s myOption ### set up view set {theView, makeObj} to my makeContentView(paramObj) ### set up alert tell current application’s NSAlert’s new() setMessageText_(mesText) setInformativeText_(infoText) addButtonWithTitle_("OK") addButtonWithTitle_("Cancel") setAccessoryView_(theView) tell |window|() setInitialFirstResponder_(theView) end tell #### show alert in modal loop if runModal() is (current application’s NSAlertSecondButtonReturn) then return end tell ### retrieve date my retrieveData(makeObj) end raize: ## ContentView を作成 property _data_source : missing value property _data_type : (current date) as text –do shell script "uuidgen" –>ユニークな文字列ならなんでもいい? on makeContentView(paramObj) ## 準備 set aList to (paramObj’s aTableList) as list set keyRec to (paramObj’s aSortOrder) as record set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyRec) set my _data_source to current application’s NSArrayController’s new() repeat with anItem in aList set aDict to (current application’s NSDictionary’s dictionaryWithDictionary:anItem) ((my _data_source)’s addObject:aDict) end repeat (my _data_source)’s setSelectionIndex:0 ## NSTableView tell current application’s NSTableView’s alloc() tell initWithFrame_(current application’s CGRectZero) setAllowsEmptySelection_(false) setAllowsMultipleSelection_(true) setDataSource_(me) setDelegate_(me) setDoubleAction_("doubleAction:") setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask) setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular) setTarget_(me) setUsesAlternatingRowBackgroundColors_(true) registerForDraggedTypes_({my _data_type}) setDraggingSourceOperationMask_forLocal_(current application’s NSDragOperationCopy, false) set thisRowHeight to rowHeight() as integer set aTableObj to it end tell end tell ## NSTableColumn ### Columnの並び順を指定する set viewWidth to 0 set columnsCount to keyDict’s |count|() repeat with colNum from 1 to columnsCount set keyName to "column" & colNum as text set aTitle to (keyDict’s objectForKey:keyName) tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text)) tell headerCell() setStringValue_(aTitle) set thisHeaderHeight to cellSize()’s height end tell ### ソートの設定 set sortDescriptor to (current application’s NSSortDescriptor’s sortDescriptorWithKey:aTitle ascending:true selector:"compare:") setSortDescriptorPrototype_(sortDescriptor) ### 表示内容をNSArrayControllerにバインディング bind_toObject_withKeyPath_options_(current application’s NSValueBinding, my _data_source, "arrangedObjects." & aTitle, missing value) set aTableColumn to it end tell ### Columnの横幅を調整 tell aTableObj addTableColumn_(aTableColumn) –reloadData() set colWidthMax to 0 set rowCount to numberOfRows() repeat with rowNum from 1 to rowCount tell preparedCellAtColumn_row_(colNum – 1, rowNum – 1) set thisWidthSize to cellSize()’s width –log result if thisWidthSize is greater than or equal to colWidthMax then set colWidthMax to thisWidthSize end tell end repeat end tell –log colWidthMax set colWidthMax to colWidthMax + 5 –> 5は余白 tell aTableColumn setWidth_(colWidthMax) end tell set viewWidth to viewWidth + colWidthMax end repeat ## NSScrollView ### Viewの高さを計算 set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない set vSize to current application’s NSMakeRect(0, 0, viewWidth + 10, viewHeight) –> 10を足すといい感じの横幅になる ### Viewを作成 tell current application’s NSScrollView’s alloc() tell initWithFrame_(vSize) setBorderType_(current application’s NSBezelBorder) setDocumentView_(aTableObj) –setHasHorizontalScroller_(true) –setHasVerticalScroller_(true) set aScroll to it end tell end tell return {aScroll, aTableObj} end makeContentView ## retrieve date on retrieveData(aTableObj) set aIndexSet to aTableObj’s selectedRowIndexes() set my _retrieve_data to ((my _data_source)’s arrangedObjects()’s objectsAtIndexes:aIndexSet) as list –setSelectionIndexes:aIndexSet log result end retrieveData # NSTableViewDatasource on numberOfRowsInTableView:aTableView –log "numberOfRowsInTableView:" return (my _data_source)’s content()’s |count|() end numberOfRowsInTableView: # NSTableViewDelegate on tableView:aTableView objectValueForTableColumn:aColumn row:aRow end tableView:objectValueForTableColumn:row: # テーブル内のセルが編集できるか on tableView:aTableView shouldEditTableColumn:aColumn row:aRow return false end tableView:shouldEditTableColumn:row: # テーブル内をダブルクリックしたらOKボタンを押す on doubleAction:sender –log "doubleAction" –log sender’s |className|() as text ## ヘッダをクリックした時は何もしない if (sender’s clickedRow()) is -1 then return set theEvent to current application’s NSEvent’s ¬ keyEventWithType:(current application’s NSEventTypeKeyDown) ¬ location:(current application’s NSZeroPoint) ¬ modifierFlags:0 ¬ timestamp:0.0 ¬ windowNumber:(sender’s |window|()’s windowNumber()) ¬ context:(current application’s NSGraphicsContext’s currentContext()) ¬ |characters|:return ¬ charactersIgnoringModifiers:(missing value) ¬ isARepeat:false ¬ keyCode:0 current application’s NSApp’s postEvent:theEvent atStart:(not false) end doubleAction: # Drag Operation Method ## ドラッグを開始(ペーストボードに書き込む) on tableView:aTableView writeRowsWithIndexes:rowIndexes toPasteboard:pboard –log "writeRowsWithIndexes" set aData to current application’s NSKeyedArchiver’s archivedDataWithRootObject:rowIndexes pboard’s declareTypes:{my _data_type} owner:(missing value) pboard’s setData:aData forType:(my _data_type) return true end tableView:writeRowsWithIndexes:toPasteboard: ## ドラッグ途中 on tableView:aTableView validateDrop:info proposedRow:row proposedDropOperation:operation –log "validateDrop" ### 列の間にドラッグ if (operation is current application’s NSTableViewDropAbove) then return current application’s NSDragOperationMove end if ### 項目の上にドラッグ set aTypes to info’s draggingPasteboard’s types() if (aTypes’s containsObject:(my _data_type)) as boolean then return current application’s NSDragOperationNone end if return current application’s NSDragOperationNone end tableView:validateDrop:proposedRow:proposedDropOperation: ## ドラッグ終了 on tableView:aTableView acceptDrop:info row:row dropOperation:operation –log "acceptDrop" set pboard to info’s draggingPasteboard() set rowData to pboard’s dataForType:(my _data_type) set rowIndexes to current application’s NSKeyedUnarchiver’s unarchiveObjectWithData:rowData if (rowIndexes’s firstIndex()) < row then set row to row – (rowIndexes’s |count|()) end if set aRange to current application’s NSMakeRange(row, rowIndexes’s |count|()) set aIndexSet to current application’s NSIndexSet’s indexSetWithIndexesInRange:aRange tell my _data_source set anObj to content()’s objectsAtIndexes:rowIndexes removeObjects_(anObj) insertObjects_atArrangedObjectIndexes_(anObj, aIndexSet) rearrangeObjects() end tell return true end tableView:acceptDrop:row:dropOperation: end script ## tell (make MyDialog) return call(aMainMes, aSubMes, optionRec) end tell end chooseData |
アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり
先日「macOS Native」のイベント後に秋葉原で顔見知りのScripter3人でカレー食べて帰ってきました。その際に、アラートダイアログ上に作成した箱庭User Interface類の話で盛り上がっていました。「意外と使える」「自分でもいろいろ作ってる」「公開してよ」といったような。
本Scriptはその際の話を受けてEdama2さんが作っている試作品の、さらに試作品になるわけですが、プログラム内容が自分とは別の価値体系で清書し直されており、あいかわらずロジックとコードの美しさにうならされます。また、「本来倒すべき敵」に向けての実装が行われているので、単にテーブルビューを表示するだけには少々機能過剰になっています。
以下、Edama2さんによる投稿内容です。
タイトルは「アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり」 主な変更点は 表示する内容に合わせてviewのサイズを調整 レコードのラベルをヘッダのタイトルにする Table ViewをダブルクリックするとOKボタンを押す setAllowsEmptySelection: をfalseにして1行目を選択するようにした
データ作成部分が詰め込み効率優先ではなく可読性を高めた作りになっているのが、Edama2さんらしいところです。また、tellブロックをCocoa Scriptingにも多用しているのも特徴です。
ロジックが読みやすい一方で、Cocoa Framework相手にここまで見やすく書くのは、相当の努力が必要なはずで、Cocoa Scripting時に省いてしまいがちな「清書」という1手間が入っていることを感じさせます。
本Scriptはハンドラ(サブルーチン)が前の方に配置される記法です。AppleScriptはもともとハンドラ宣言部を前に書く言語なので、世界的に見るとこちらが主流です(Shane Stanleyですらハンドラ宣言部は前に書きます)。逆に、自分が他の言語に合わせてハンドラ宣言部を後ろに配置するのは、全体から見ると異端といえます(他の言語と書き方が違いすぎるのはどうかと思って、ハンドラ宣言部を後ろに書いています)。
たしかに、自分の箱庭テーブルビューダイアログではカラム幅とテーブルの縦サイズは一切手をつけていなかったので、縦横幅高さの自動調整が入ると、格段に見やすくなります。
Cocoaの機能を呼び出すAppleScriptにおいても、書きこなれてきたことによりハンドラ内にScript文を書いてさらに内部にハンドラを記述するといったような、論理分割やら階層構造化が本Scriptにおいて多用されています。そのあたり、applescript-stdlib(2015)で見かけましたが、本Scriptのように控えめな利用だと安心できます。
本Script内には継続記号(「¬」)が多用されていますが、macOS日本語ユーザー環境上でScript Debuggerを使っていると、構文確認時にこれがひっかかってエラーになることがあります(というか、実際にウチでこれを編集してなっています)。継続記号がひっかかって構文確認(コンパイル)できないようであれば、いっそ継続記号をすべて削除すべきでしょう。継続記号に「可読性を上げる」以外の機能はないため、削除しても無害です。
| AppleScript名:アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり.scpt |
| on run set aTableList to {} set aTableList’s end to {|Name|:"MacBook Air", Price:"119,800円", Specs:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"} set aTableList’s end to {|Name|:"MacBook Pro 13", Price:"139,800円", Specs:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"} set aTableList’s end to {|Name|:"MacBook Pro 15", Price:"258,800円", Specs:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"} set aTableList’s end to {|Name|:"Mac mini", Price:"122,800円", Specs:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"} set aTableList’s end to {|Name|:"iMac 21.5", Price:"120,800円", Specs:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"} set aTableList’s end to {|Name|:"iMac 4K 21.5", Price:"142,800円", Specs:"3.6GHzクアッドコアIntel Core i3"} set aTableList’s end to {|Name|:"iMac 5K 27", Price:"198,800円", Specs:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"} set optionRec to {aTableList:aTableList} set optionRec to optionRec & {aSortOrder:{column1:"Name", column2:"Price", column3:"Specs"}} set aMainMes to "項目の選択" set aSubMes to "適切なものを以下からえらんでください" set dateObj to my chooseData(aMainMes, aSubMes, optionRec) end run # アラートダイアログでtableviewを表示 on chooseData(aMainMes, aSubMes, optionRec) script MyDialog property parent : AppleScript use AppleScript use scripting additions use framework "Foundation" property _retrieve_data : missing value on make set aClass to me script property parent : aClass end script end make ## ダイアログの呼び出し on call(aMainMes, aSubMes, optionRec) set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myOption:optionRec} parent’s performSelectorOnMainThread:"raize:" withObject:paramObj waitUntilDone:true if (my _retrieve_data) is missing value then error number -128 return (my _retrieve_data) end call ## ダイアログの生成 on raize:paramObj ### set mesText to paramObj’s myMessage set infoText to paramObj’s mySubMessage set paramObj to paramObj’s myOption ### set up view set {theView, makeObj} to my makeContentView(paramObj) ### set up alert tell current application’s NSAlert’s new() setMessageText_(mesText) setInformativeText_(infoText) addButtonWithTitle_("OK") addButtonWithTitle_("Cancel") setAccessoryView_(theView) tell |window|() setInitialFirstResponder_(theView) end tell #### show alert in modal loop if runModal() is (current application’s NSAlertSecondButtonReturn) then return end tell ### retrieve date my retrieveData(makeObj) end raize: ## ContentView を作成 property _data_source : missing value on makeContentView(paramObj) ## 準備 set aList to (paramObj’s aTableList) as list set keyRec to (paramObj’s aSortOrder) as record set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyRec) set my _data_source to current application’s NSArrayController’s new() repeat with anItem in aList set aDict to (current application’s NSDictionary’s dictionaryWithDictionary:anItem) ((my _data_source)’s addObject:aDict) end repeat ## NSTableView tell current application’s NSTableView’s alloc() tell initWithFrame_(current application’s CGRectZero) setAllowsEmptySelection_(false) setDataSource_(me) setDelegate_(me) setDoubleAction_("doubleAction:") setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask) setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular) setTarget_(me) setUsesAlternatingRowBackgroundColors_(true) set thisRowHeight to rowHeight() as integer set aTableObj to it end tell end tell ## NSTableColumn ### Columnの並び順を指定する set viewWidth to 0 set columnsCount to keyDict’s |count|() repeat with colNum from 1 to columnsCount set keyName to "column" & colNum as text set aTitle to (keyDict’s objectForKey:keyName) tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text)) tell headerCell() setStringValue_(aTitle) set thisHeaderHeight to cellSize()’s height end tell set aTableColumn to it end tell ### Columnの横幅を調整 tell aTableObj addTableColumn_(aTableColumn) –reloadData() set colWidthMax to 0 set rowCount to numberOfRows() repeat with rowNum from 1 to rowCount tell preparedCellAtColumn_row_(colNum – 1, rowNum – 1) set thisWidthSize to cellSize()’s width –log result if thisWidthSize is greater than or equal to colWidthMax then set colWidthMax to thisWidthSize end tell end repeat end tell –log colWidthMax set colWidthMax to colWidthMax + 5 –> 5は余白 tell aTableColumn setWidth_(colWidthMax) end tell set viewWidth to viewWidth + colWidthMax end repeat ## NSScrollView ### Viewの高さを計算 set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない set vSize to current application’s NSMakeRect(0, 0, viewWidth + 10, viewHeight) –> 10を足すといい感じの横幅になる ### Viewを作成 tell current application’s NSScrollView’s alloc() tell initWithFrame_(vSize) setBorderType_(current application’s NSBezelBorder) setDocumentView_(aTableObj) –setHasHorizontalScroller_(true) –setHasVerticalScroller_(true) set aScroll to it end tell end tell return {aScroll, aTableObj} end makeContentView ## retrieve data on retrieveData(aTableObj) set aRow to aTableObj’s selectedRow() log result if aRow is -1 then set my _retrieve_data to {} else set my _retrieve_data to ((my _data_source)’s arrangedObjects()’s objectAtIndex:aRow) as record end if end retrieveData # NSTableViewDatasource on numberOfRowsInTableView:aTableView –log "numberOfRowsInTableView:" return (my _data_source)’s content()’s |count|() end numberOfRowsInTableView: # NSTableViewDelegate on tableView:aTableView objectValueForTableColumn:aColumn row:aRow –log "objectValueForTableColumn" set keyName to aColumn’s headerCell()’s title() set aDict to (my _data_source)’s arrangedObjects()’s objectAtIndex:aRow set aStr to aDict’s objectForKey:keyName –log result as text return aStr end tableView:objectValueForTableColumn:row: # テーブル内のセルが編集できるか on tableView:aTableView shouldEditTableColumn:aColumn row:aRow return false end tableView:shouldEditTableColumn:row: # テーブル内をダブルクリックしたらOKボタンを押す on doubleAction:sender –log "doubleAction" –log sender’s |className|() as text set theEvent to current application’s NSEvent’s ¬ keyEventWithType:(current application’s NSEventTypeKeyDown) ¬ location:(current application’s NSZeroPoint) ¬ modifierFlags:0 ¬ timestamp:0.0 ¬ windowNumber:(sender’s |window|()’s windowNumber()) ¬ context:(current application’s NSGraphicsContext’s currentContext()) ¬ |characters|:return ¬ charactersIgnoringModifiers:(missing value) ¬ isARepeat:false ¬ keyCode:0 current application’s NSApp’s postEvent:theEvent atStart:(not false) end doubleAction: end script ## tell (make MyDialog) return call(aMainMes, aSubMes, optionRec) end tell end chooseData |
アラートダイアログ上にTable Viewを表示 v6_サーチフィールドつき
検索フィールドつきのTable View表示ダイアログを表示するAppleScriptです。項目選択を行う簡易ユーザーインタフェース(ダイアログ)で、選択肢の数が多い場合に備えて項目のキーワード抽出機能を付加したものです。
サーチフィールドの実装は無理かと思っていたのですが、単体でアラートダイアログ上に表示してみたら、予想外にキーボード入力イベントと検索語のクリアのイベントを簡単に拾えたので、Table Viewと組み合わせて試してみました。
| AppleScript名:アラートダイアログ上にTable Viewを表示 v6_サーチフィールドつき |
| — Created 2019-12-23 by Takaaki Naganoya — 2019 Piyomaru Software set aRes to displayCondTable() of filterTableDialogView –> {field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)", field2:198800, field1:"iMac 5K 27"} script filterTableDialogView use scripting additions use framework "Foundation" use framework "AppKit" property parent : AppleScript property NSView : a reference to current application’s NSView property NSAlert : a reference to current application’s NSAlert property NSIndexSet : a reference to current application’s NSIndexSet property NSPredicate : a reference to current application’s NSPredicate property NSScrollView : a reference to current application’s NSScrollView property NSTableView : a reference to current application’s NSTableView property NSSearchField : a reference to current application’s NSSearchField property NSTableColumn : a reference to current application’s NSTableColumn property NSMutableArray : a reference to current application’s NSMutableArray property NSRunningApplication : a reference to current application’s NSRunningApplication property NSModalPanelWindowLevel : a reference to current application’s NSModalPanelWindowLevel property theResult : 0 property returnCode : 0 property theDataSource : {} property tView : missing value property aDataList : {} on displayCondTable() set (my theResult) to 0 –initialize set aDataList to {{field1:"MacBook Air", field2:119800, field3:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}, {field1:"MacBook Pro 13", field2:139800, field3:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}, {field1:"MacBook Pro 15", field2:258800, field3:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}, {field1:"Mac mini", field2:122800, field3:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}, {field1:"iMac 21.5", field2:120800, field3:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}, {field1:"iMac 4K 21.5", field2:142800, field3:"3.6GHzクアッドコアIntel Core i3"}, {field1:"iMac 5K 27", field2:198800, field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}, {field1:"iMac Pro", field2:558800, field3:"3.2GHz Intel Xeon W Turbo Boost使用時最大4.2GHz 19MBキャッシュ"}, {field1:"Mac Pro 2019", field2:599800, field3:"3.5GHz 8コアIntel Xeon Wプロセッサ(Turbo Boost使用時最大4.0GHz)"}} set paramObj to {myMessage:"項目の選択", mySubMessage:"適切なものを以下からえらんでください", aTableList:aDataList, aSortOrder:{"field1", "field2", "field3"}} –my chooseItemByTableView:paramObj –for debug my performSelectorOnMainThread:"chooseItemByTableView:" withObject:paramObj waitUntilDone:true return (my theResult) end displayCondTable on chooseItemByTableView:paramObj set aMainMes to myMessage of paramObj set aSubMes to mySubMessage of paramObj set aTList to (aTableList of paramObj) as list set labelSortList to (aSortOrder of paramObj) as list set aWidth to 800 set aHeight to 300 –Viewをつくる set parentView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) set aTextInput to makeNSSearchField(0, aHeight – 20, 300, 20) of me set aScroll to makeTableView(aTList, aWidth, aHeight – 80, labelSortList) of me parentView’s setSubviews:{aTextInput, aScroll} — set up alert set theAlert to NSAlert’s alloc()’s init() tell theAlert its setMessageText:aMainMes its setInformativeText:aSubMes its addButtonWithTitle:"OK" its addButtonWithTitle:"Cancel" its setAccessoryView:(parentView) set myWindow to its |window| end tell myWindow’s setLevel:(NSModalPanelWindowLevel) — show alert in modal loop NSRunningApplication’s currentApplication()’s activateWithOptions:0 my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true if (my returnCode) = 1001 then error number -128 set tmpResult to (aScroll’s documentView’s selectedRow()) + 1 set (my theResult) to contents of item tmpResult of ((my theDataSource) as list) end chooseItemByTableView: on doModal:aParam set (my returnCode) to (aParam’s runModal()) as number end doModal: –TableView Event Handlers on numberOfRowsInTableView:aView return (my theDataSource)’s |count|() end numberOfRowsInTableView: on tableView:aView objectValueForTableColumn:aColumn row:aRow set aRec to (my theDataSource)’s objectAtIndex:(aRow as number) set aTitle to (aColumn’s headerCell()’s title()) as string set aRes to (aRec’s valueForKey:aTitle) return aRes end tableView:objectValueForTableColumn:row: on makeTableView(aDicList, aWidth, aHeight, labelSortList) set aOffset to 40 set theDataSource to NSMutableArray’s alloc()’s init() theDataSource’s addObjectsFromArray:aDicList –TextField set aTextInput to makeNSSearchField(0, 0, 300, 20) of me set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight)) set tView to NSTableView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight)) set aLen to length of labelSortList repeat with i in labelSortList set j to contents of i set aColumn to (NSTableColumn’s alloc()’s initWithIdentifier:j) (aColumn’s setWidth:(aWidth div aLen)) (aColumn’s headerCell()’s setStringValue:j) (tView’s addTableColumn:aColumn) end repeat tView’s setDelegate:me tView’s setDataSource:me tView’s reloadData() aScroll’s setDocumentView:tView tView’s enclosingScrollView()’s setHasVerticalScroller:true aScroll’s setVerticalLineScroll:(30.0 as real) –1行目を選択 set aIndexSet to NSIndexSet’s indexSetWithIndex:0 tView’s selectRowIndexes:aIndexSet byExtendingSelection:false –強制的にトップにスクロール set aDBounds to aScroll’s documentView()’s |bounds|() if class of aDBounds = list then –macOS 10.13 or later set maxHeight to item 2 of item 1 of aDBounds else –macOS 10.10….10.12 set maxHeight to height of |size| of aDBounds end if set aPT to current application’s NSMakePoint(0.0, -1 * (maxHeight as real)) aScroll’s documentView()’s scrollPoint:aPT return aScroll end makeTableView on alertShowHelp:aNotification display dialog "Help Me!" buttons {"OK"} default button 1 with icon 1 return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする end alertShowHelp: on makeNSSearchField(xPos as integer, yPos as integer, myWidth as integer, myHeight as integer) set aSearchF to NSSearchField’s alloc()’s initWithFrame:(current application’s NSMakeRect(xPos, yPos, myWidth, myHeight)) aSearchF’s setDelegate:(me) return aSearchF end makeNSSearchField –検索クエリ入力時 on searchFieldDidStartSearching:(aField) set aStr to (aField’s stringValue()) as string set predicStr to "field1 contains[cd] ’" & aStr & "’ OR field3 contains[cd] ’" & aStr & "’" set filteredArray to filterRecListByLabel1((my aDataList), predicStr) of me set (my theDataSource) to filteredArray tView’s reloadData() end searchFieldDidStartSearching: –Search Fieldの「x」ボタンを押したイベント on searchFieldDidEndSearching:(aField) set (my theDataSource) to NSMutableArray’s arrayWithArray:(my aDataList) tView’s reloadData() end searchFieldDidEndSearching: –リストに入れたレコードを、指定の属性ラベルの値で抽出 on filterRecListByLabel1(aRecList as list, aPredicate as string) set aArray to NSMutableArray’s arrayWithArray:aRecList set aPredicate to NSPredicate’s predicateWithFormat:aPredicate set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate return filteredArray end filterRecListByLabel1 end script |
アラートダイアログ上にTable Viewを表示 v5_複数キー抽出つき
アラートダイアログ上にTableViewを表示し、複数キーの組み合わせによって項目抽出。選択項目のデータを返すAppleScriptです。
あくまで試作品なので、あちらこちらに「お可愛らしい」実装が転がっています。NSBoxがまだまともに使いこなせていないあたりとか(Xcode上で部品として配置するのは楽勝ですが、プログラムから動的に生成して使用するのは別なので)。
抽出キー数は可変で外部から指定できるとよいのですが、本Scriptでは固定です。また、各抽出条件も外部から供給するのが本来あるべき姿ですが、ハードコーディングしています。
あと、TableViewの下側に謎の余白があるのは、めんどくさくなってそのまま放置しています。これで外部から各種パラメータを指定できるようにして、sdefつけて1命令で呼び出せるようにしておけば、レコード数の多い選択項目から条件抽出しつつ項目選択できてよいでしょう。
プログラム内容自体は、高度でもなければ高尚でもありません。実にダラダラとGUI部品のパラメータを設定しているだけです。


| AppleScript名:アラートダイアログ上にTable Viewを表示 v5_複数キー抽出つき |
| — Created 2019-12-22 by Takaaki Naganoya — 2019 Piyomaru Software set aRes to displayCondTable() of filterTableDialogView –> {field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)", field2:198800, field1:"iMac 5K 27"} script filterTableDialogView use scripting additions use framework "Foundation" use framework "AppKit" property parent : AppleScript property NSBox : a reference to current application’s NSBox property NSView : a reference to current application’s NSView property NSAlert : a reference to current application’s NSAlert property NSColor : a reference to current application’s NSColor property NSIndexSet : a reference to current application’s NSIndexSet property NSPredicate : a reference to current application’s NSPredicate property NSScrollView : a reference to current application’s NSScrollView property NSTableView : a reference to current application’s NSTableView property NSTableColumn : a reference to current application’s NSTableColumn property NSPopUpButton : a reference to current application’s NSPopUpButton property NSMutableArray : a reference to current application’s NSMutableArray property NSRunningApplication : a reference to current application’s NSRunningApplication property NSCompoundPredicate : a reference to current application’s NSCompoundPredicate property NSModalPanelWindowLevel : a reference to current application’s NSModalPanelWindowLevel property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn property theResult : 0 property returnCode : 0 property theDataSource : {} property tView : missing value property aDataList : {} property aSel : 0 property bSel : 0 property cSel : 0 property predList : {} property a1Button : missing value property a2Button : missing value property a3Button : missing value on displayCondTable() set (my theResult) to 0 –initialize set aDataList to {{field1:"MacBook Air", field2:119800, field3:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}, {field1:"MacBook Pro 13", field2:139800, field3:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}, {field1:"MacBook Pro 15", field2:258800, field3:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}, {field1:"Mac mini", field2:122800, field3:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}, {field1:"iMac 21.5", field2:120800, field3:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}, {field1:"iMac 4K 21.5", field2:142800, field3:"3.6GHzクアッドコアIntel Core i3"}, {field1:"iMac 5K 27", field2:198800, field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}, {field1:"iMac Pro", field2:558800, field3:"3.2GHz Intel Xeon W Turbo Boost使用時最大4.2GHz 19MBキャッシュ"}, {field1:"Mac Pro 2019", field2:599800, field3:"3.5GHz 8コアIntel Xeon Wプロセッサ(Turbo Boost使用時最大4.0GHz)"}} set paramObj to {myMessage:"項目の選択", mySubMessage:"適切なものを以下からえらんでください", aTableList:aDataList, aSortOrder:{"field1", "field2", "field3"}} –my chooseItemByTableView:paramObj –for debug my performSelectorOnMainThread:"chooseItemByTableView:" withObject:paramObj waitUntilDone:true return (my theResult) end displayCondTable on chooseItemByTableView:paramObj set aMainMes to myMessage of paramObj set aSubMes to mySubMessage of paramObj set aTList to (aTableList of paramObj) as list set labelSortList to (aSortOrder of paramObj) as list set aWidth to 800 set aHeight to 400 set my aSel to 0 set my bSel to 0 –Viewをつくる set parentView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) –BOX Aをつくる set aBox to (NSBox’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aHeight – 60, aWidth, 60))) (aBox’s setTitle:("Filter Condition")) –このあたり、項目数に合わせてUIを可変で生成するように(本Scriptは試作品なので、決め打ちでUI生成) set a1Button to (NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 200, 30)) pullsDown:false) a1Button’s removeAllItems() (a1Button’s addItemsWithTitles:{"▼Select Item", "MacBook", "iMac", "mini", "Air", "Pro"}) a1Button’s setTarget:(me) a1Button’s setAction:("mySelector:") a1Button’s setEnabled:(true) set a2Button to (NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(220, 0, 200, 30)) pullsDown:false) a2Button’s removeAllItems() (a2Button’s addItemsWithTitles:{"▼Select Item", "150000", "200000", "300000", "400000", "600000"}) a2Button’s setTarget:(me) a2Button’s setAction:("mySelector:") a2Button’s setEnabled:(true) set a3Button to (NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(440, 0, 200, 30)) pullsDown:false) a3Button’s removeAllItems() (a3Button’s addItemsWithTitles:{"▼Select Item", "デュアルコア", "クアッドコア", "6コア", "8コア"}) a3Button’s setTarget:(me) a3Button’s setAction:("mySelector:") a3Button’s setEnabled:(true) (aBox’s addSubview:a1Button) (aBox’s addSubview:a2Button) (aBox’s addSubview:a3Button) –BOX Bをつくる set bBox to (NSBox’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight – 80))) (bBox’s setTitle:("Data")) set aScroll to makeTableView(aTList, aWidth, aHeight – 150, labelSortList) of me (bBox’s addSubview:aScroll) parentView’s setSubviews:{aBox, bBox} — set up alert set theAlert to NSAlert’s alloc()’s init() tell theAlert its setMessageText:aMainMes its setInformativeText:aSubMes its addButtonWithTitle:"OK" its addButtonWithTitle:"Cancel" its setAccessoryView:(parentView) set myWindow to its |window| end tell myWindow’s setLevel:(NSModalPanelWindowLevel) — show alert in modal loop NSRunningApplication’s currentApplication()’s activateWithOptions:0 my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true if (my returnCode) = 1001 then error number -128 set tmpResult to (aScroll’s documentView’s selectedRow()) + 1 set (my theResult) to contents of item tmpResult of ((my theDataSource) as list) end chooseItemByTableView: on doModal:aParam set (my returnCode) to (aParam’s runModal()) as number end doModal: –TableView Event Handlers on numberOfRowsInTableView:aView return (my theDataSource)’s |count|() end numberOfRowsInTableView: on tableView:aView objectValueForTableColumn:aColumn row:aRow set aRec to (my theDataSource)’s objectAtIndex:(aRow as number) set aTitle to (aColumn’s headerCell()’s title()) as string set aRes to (aRec’s valueForKey:aTitle) return aRes end tableView:objectValueForTableColumn:row: on makeTableView(aDicList, aWidth, aHeight, labelSortList) set aOffset to 40 set theDataSource to NSMutableArray’s alloc()’s init() theDataSource’s addObjectsFromArray:aDicList set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight)) set tView to NSTableView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight)) set aLen to length of labelSortList repeat with i in labelSortList set j to contents of i set aColumn to (NSTableColumn’s alloc()’s initWithIdentifier:j) (aColumn’s setWidth:(aWidth div aLen)) (aColumn’s headerCell()’s setStringValue:j) (tView’s addTableColumn:aColumn) end repeat tView’s setDelegate:me tView’s setDataSource:me tView’s reloadData() aScroll’s setDocumentView:tView tView’s enclosingScrollView()’s setHasVerticalScroller:true aScroll’s setVerticalLineScroll:(30.0 as real) –1行目を選択 set aIndexSet to NSIndexSet’s indexSetWithIndex:0 tView’s selectRowIndexes:aIndexSet byExtendingSelection:false –強制的にトップにスクロール set aDBounds to aScroll’s documentView()’s |bounds|() if class of aDBounds = list then –macOS 10.13 or later set maxHeight to item 2 of item 1 of aDBounds else –macOS 10.10….10.12 set maxHeight to height of |size| of aDBounds end if set aPT to current application’s NSMakePoint(0.0, -1 * (maxHeight as real)) aScroll’s documentView()’s scrollPoint:aPT return aScroll end makeTableView on alertShowHelp:aNotification display dialog "Help Me!" buttons {"OK"} default button 1 with icon 1 return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする end alertShowHelp: on mySelector:aObject set aIndex to (aObject’s indexOfSelectedItem()) as number filterByMultipleCondition() of me end mySelector: on filterByMultipleCondition() set prediCatesArray to {} set aInd to (my a1Button’s indexOfSelectedItem()) as number set bInd to (my a2Button’s indexOfSelectedItem()) as number set cInd to (my a3Button’s indexOfSelectedItem()) as number if {aInd, bInd, cInd} = {0, 0, 0} then set (my theDataSource) to NSMutableArray’s arrayWithArray:(my aDataList) tView’s reloadData() return end if –このあたり、複数条件をハードコーディングしているのは超絶頭悪い。項目数の増減に対応できるべき if aInd > 0 then set aTitle to (my a1Button’s title()) as string set the end of prediCatesArray to "field1 contains ’" & aTitle & "’" end if if bInd > 0 then set bTitle to (my a2Button’s title()) as string set the end of prediCatesArray to "field2.integerValue < " & bTitle end if if cInd > 0 then set cTitle to (my a3Button’s title()) as string set the end of prediCatesArray to "field3 contains ’" & cTitle & "’" end if –データ 抽出 set tmpList to filterDictArrayByLabel3((my aDataList), prediCatesArray) of me –データ 再表示 set (my theDataSource) to NSMutableArray’s arrayWithArray:(tmpList) tView’s reloadData() end filterByMultipleCondition –リストに入れたレコードを、指定の属性ラベルの値で抽出(複数PredicatesをANDで合成) on filterDictArrayByLabel3(origArray as list, aPredicateList as list) set aArray to NSMutableArray’s arrayWithArray:origArray set predArray to NSMutableArray’s new() repeat with i in aPredicateList (predArray’s addObject:(NSPredicate’s predicateWithFormat:(contents of i))) end repeat set pred to current application’s NSCompoundPredicate’s andPredicateWithSubpredicates:predArray set filteredArray to aArray’s filteredArrayUsingPredicate:pred return filteredArray end filterDictArrayByLabel3 end script |
CotEditor PowerPackのPiyomaru Software内での位置付け

CotEditor向けの機能強化Script群「CotEditor PowerPack」は、自分たちが便利そうだと思った機能をびっしりと敷き詰めて実装してみました(基礎的なScriptingについては、同梱の基礎的なサンプルスクリプト集「BasicPack」にまとめています)。PowerPackを実際に作ってみたら、メニューで項目が増えたときに絵文字によるラベリングが割と有効だという発見ももたらしました。
ただ、PowerPackがPowerPack単体で存在するはずもなく、Piyomaru Softwareが作成している各種プログラムとも密接な関係にあります。歴史的な経緯の中で先行ソフトウェアの影響も受けていますし、さまざまなプロジェクトの成果物や技術の実証の場として利用しているからです。
Tanzakuの部品を利用
具体例を言うと、Tanzaku用に開発した コマンド解析用 簡易日本語形態素解析エンジン「easyJParse」。これはAppleScriptだけで記述した80行ほどの規模の形態素解析プログラムです。これをコマンド解析用以外の、伏せ字処理のプログラムに使っています。

「航空機の部品を炊飯器に転用する」ような違和感はありますが、想定外の用途に部品を使いまわすと意外な発見があります。easyJParseはコマンド解釈用の日本語Parseプログラムであり、長大な文章を解釈するようには実装していません。そこに実際に数十〜数百KBのサイズの日本語テキストを与えてParseするとどうなるかという実験が行えました。実際、ループで文章単位のテキストを与えているため、クラッシュしたり迷走したりすることなく処理できました(それなりに時間はかかりましたが)。データの持ち方をより大規模なデータを与えても大丈夫なように備えていれば、ある程度の大規模データにも備えることができるでしょう。
コマンド構造は日本語音声認識コマンダーの影響
多くのコマンドから全体を構成するノウハウは、2003〜2004年ごろに作っていた日本語音声認識コマンダー(いまのAIスピーカーみたいなもの)「符令韻投句」(ぷれいんとーく)で培ったものです。各コマンドの粒度については小さいほうがいいのか大きいものがあるべきかなどまだ実験中ですが、PowerPackの場合には「細かいコマンドを寄せ集めるとどうなるか」という実験を行なってみています。
Piyomaru Script Assistantに影響
スクリプトエディタの編集エリア上でコンテクストメニューを表示させて呼び出すScript Assistantは、Apple純正のものがOSにデフォルトで入っていますが、網羅性がいまひとつで実用性もさっぱりです。
そこで、Mac OS X 10.3から4の時代にこれを自分に合わせてすべて書き直し、さらに書籍のオマケに出すために網羅性を高めたものが現在配付中のPiyomaru Script Assistantです。
Piyomaru Script AssistantのScriptのうちmacOS 10.13で動かなくなってしまったものがいくつかあり、また、内容についても更新の必要を(さすがに2006年ごろに作ったものなので)感じたため、こちらも強化を行なっています。
さらに、PowerPackで積み重ねた実験結果をPiyomaru Script Assistantにフィードバック。CotEditorのPowerPackで培ったメニューにおける絵文字の活用ノウハウが生かされており、また、初心者に違和感なくCocoa Scriptingの部品を使ってもらえるような配慮を行っています(Cocoaの機能をまったく使わないVanilla Scriptの中にCocoa ScriptingのScriptingモジュールを埋め込む格好)。
そもそも、本Blogがスタートした経緯そのものが、すべてAppleScriptで記述した人工知能インタフェース「Newt On」(文字版のSiri)の内部ルーチン(部品)のメンテナンスとか共有が目的だったので、Piyomaru SoftwareのプログラムはおしなべてNewt Onの影響を受けていたり、そのものの部品を使っていたりもします。
これらの技術や経験もすべてTanzakuに向けて蓄積されているところです。
アラビア語ローカライズしたアプリケーションの挙動の変化
Xcode上でAppleScriptのアプリケーションを作って(Double PDF)、各国語にローカライズしていたときに、最難関言語として名高いアラビア語(Arabic)を試してみたことがありました。
ローカライズ作業自体にもAppleScriptを投入し、テキストエディタ上でオープンしているstringsファイルを読み取ってGoogle Translate APIを呼び出して指定言語に数秒で翻訳。記号類だけから構成されるフィールドは翻訳しないなど、その場でScriptを調整しながら大幅な翻訳作業の省力化を実現(ほかの方法もあるようなので、これがベストとは言いませんが、、、)。このScriptなしに他の言語への翻訳などというめんどくさい作業はやろうとは思いませんでした。
この翻訳用AppleScriptの威力で30言語ぐらい翻訳。主要言語のうち、最後に残ったのが、文字が右から左に流れるアラビア語とヘブライ語でした。
まずは、挙動がどのぐらい違うのか確認。Xcodeでローカライズ言語にアラビア語を追加。stringファイルの内容自体はオリジナルの英語のままの状態で、実行時のアプリケーションの言語環境に「アラビア語」を指定して起動してみました。


起動してびっくり!

▲左:日本語環境、右:アラビア語環境

▲日本語環境を指定して起動したところ@macOS 10.14.6

▲アラビア語環境を指定して起動したところ@macOS 10.14.6

▲アラビア語環境で表示したメニュー@macOS 10.14.6

▲アラビア語環境で表示したAbout Box@macOS 10.14.6。スクロールバーが右側ではなく左側に
ツールバーアイコンはデフォルトの英語環境や日本語環境では、左→右の順で並びますが、アラビア語環境ではウィンドウ上部右→左と並びました。メニューの内容も右側にタイトルが来て、左側にキーボードショートカットが並ぶなど、予想外の配置です。
これは、バグとかいった種類の話ではなく、はじめてアラビア語環境で自作のアプリケーションを起動して、その挙動の違いに驚かされたという話であります。
2つのPDFViewを並べてあり、それぞれ右とか左とか言わなくてもわかるよう、左側には青色のラベル、右側には赤色のラベルを貼っておいたのですが、ツールバーアイコンの左右も入れ替わってしまい、自分の意図したとおりのレイアウトからはかけ離れたものとなってしまいました。
アプリケーション内部で、アプリケーション起動環境から言語がアラビア語やヘブライ語のように右→左の表示を行う言語だった場合には何か処理を行なってツールバーボタンの入れ替えなどを行う必要があることでしょうか。ちょっとノーアイデアで分からないところ。ひたすら驚かされました。他の言語は軒並みGoogle Translation APIをAppleScriptから呼び出して翻訳できたのですが、アラビア語とヘブライ語は難敵でした。攻略できていないので、そこんとこなかなか辛いです。
Double PDF v2.0自体はMac App Storeで絶賛レビュー中です。In Reviewの状態に入ってからはや10日が経過し、さすがに不安になってDeveloper Connectionに電話して聞いてしまいましたが、わかったのは本当にまだレビュー作業中で、レビューにさらなる時間がかかるということだけです。
そろそろ、実際にアップデート作業にかけた時間よりもレビューにかかっている時間のほうが長くなりつつあり、そして確実に数倍の時間がかかりそうでもあり……各国語のPDFでも作って(表示をローカライズした30言語分)、それらのPDFで差分検出チェックでもしてるんじゃないかと、、、
PermissionsKitでユーザー権限のチェックとリクエスト(フルディスクアクセス)
PermissionsKit.frameworkの機能を呼び出して、実行中のアプリケーションがフルディスクアクセスの権限を持っているかどうか確認を行い、持っていなかった場合には権限を許可してもらえることを願ってシステム環境設定の「セキュリティとプライバシー」>「セキュリティ」を表示します。
–> Download PermissionsKit_tester(Code-Signed Executable Applet with Framework in its bundle)
だいたいはサンプルのObjective-CのプログラムをAppleScriptに翻訳して実行しているだけですが、1箇所だけ発見がありました。
ご存知のとおりBlocks構文はAppleScriptでは書けないので、Blocks構文の記述が要求されるAPIはAppleScriptから呼び出せません。
本FrameworkのAPIも権限を要求するときにBlocks構文の記述が求められるのですが、ためしにBlocks(^)の記述を要求される箇所にヌルを指定してみたところ、、、、動きました(^ー^;;;;;;
# なんでも効くわけではないようです
あー、Blocks構文のところにヌルを指定すると動くんだー。処理終了時に実行する内容をBlocksの中に書くのを、何も書かないことで同期実行とほぼ同じ扱いができるんじゃないかと。いろいろ試してみると発見があるかもしれません。
# 今日、Edama2さんと世間話していたら「パラメータとしてBlocks構文を必要とされる箇所にmissing valueを入れて動いたケースもあったよ」とのこと。そういうの重要なので教えてほしかったー(T_T)
PermissionsKit.frameworkでは各種権限設定の確認と要求が行えるようにはなっているのですが、フルディスクアクセス以外はすべてクラッシュして動かなかったので、フルディスクアクセスの権限確認&要求専用とみなしています。
| AppleScript名:PermissionsKitでユーザー権限のチェックとリクエスト(フルディスクアクセス).scptd |
| — – Created by: Takaaki Naganoya – Created on: 2019/12/19 — – Copyright © 2019 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use framework "PermissionsKit" –https://github.com/MacPaw/PermissionsKit use scripting additions property MPPermissionTypeCalendar : 0 property MPPermissionTypeReminders : 1 property MPPermissionTypeContacts : 2 property MPPermissionTypePhotos : 3 property MPPermissionTypeFullDiskAccess : 4 –MPPermissionTypeFullDiskAccess 以外はクラッシュして処理継続できなかった set aPerm to current application’s MPPermissionsKit’s alloc()’s init() set pRes to current application’s MPPermissionsKit’s authorizationStatusForPermissionType:(MPPermissionTypeFullDiskAccess) set sRes to getStatus(pRes) of me log sRes if pRes is not equal to 0 then set pRes to current application’s MPPermissionsKit’s requestAuthorizationForPermissionType:(MPPermissionTypeFullDiskAccess) withCompletion:"" end if on getStatus(aFlag) if aFlag = 0 then return "Denied" –拒否された else if aFlag = 1 then return "Authorized" –承認ずみ else if aFlag = 2 then return "Not determined" –未定 end if end getStatus |
画像の空白判定 v4
画像のすべてのピクセルが白であることをチェックするAppleScriptです。
以前にmacOS 10.12上で作成した「空白画像検出v3」は一般的に処理時間のかかる空白画像の検出(ヒストグラム計算してチェックする?)が高速に処理できます(GPUImageでヒストグラム計算を行うより2倍ぐらい高速)。空白検出のためだけにAdobe Photoshopが必要になるといった事態は避けられます。
–> test Data(動作検証用各種テストデータ)
macOS 10.14.6上でこのルーチンを再度試してみたところ、思ったとおりに空白検出(数百万ピクセルの画像でも1つだけ黒であればfalseを返す)してくれません。ちょっと書き方を変えていますが、単体で通常のAppleScript(Cocoa不使用)にコンテクストメニューから突っ込むモジュールとして運用するためです。
copyコマンドがdeep copyしてくれない?
参照ではなくすべてのデータをコピーするコマンドとして、copyコマンドはとても重宝しています。単に「パラメータの代入の方向が違う偏屈なコマンド」ではなく、明示的にデータ内容をコピーするためのコマンドと認識しています。
AppleScriptのオブジェクト同様、Cocoaのオブジェクトについてもcopyコマンドでdeep copyしてくれるものと理解していましたが、macOS 10.14.6上で本ルーチンを試してみたところ、どうも参照をコピーしている気配がします。

AppleScriptのオブジェクト(配列とか)をcopyコマンドでコピーすると、参照ではなくデータが複製されていることを確認しました。Cocoaオブジェクトのcopyも同様かどうかは、ちょっとそうじゃないんじゃないかというところです。
set bNSImage to aNSImage's |copy|()
Cocoaの世界のオブジェクトのコピーはこんな感じ(↑)でできたので、今後はこちらを採用することにします。
1×1のNSImageに色を塗るとデータサイズが増える
ビットマップ画像の状態であるNSBitmapImageRepになっていれば、1×1画像のサイズは同じことが期待されます。実際、macOS 10.12上ではそのように扱えていました。
ところが、macOS 10.14上で1×1ドットのNSImageに対して色を塗って、NSBitmapImageRepを取得すると、塗らない状態のNSImageよりもデータ(NSData)サイズが大きくなることがわかりました。

▲1×1のPNG画像をビットマップ化したデータ。赤が処理前、青が塗りつぶし処理後のデータ(macOS 10.14.6)。ピクセル数が増えてるのでは? と疑ってサイズを何回も計算して確認しましたが、間違っていません
しかも、データ量がmacOS 10.14と10.15では大幅に異なります(macOS 10.15では大幅にデータ量が減る)。
1×1 PNG画像 ビットマップ化したデータサイズ:3,354 Bytes(macOS 10.14.6)
1×1 PNG画像 塗りつぶし ビットマップ化したデータサイズ:3,574 Bytes(macOS 10.14.6)
1×1 PNG画像 塗りつぶし ビットマップ化したデータサイズ:750 Bytes(macOS 10.15.2)
さすがに匙を投げかけましたが、比較元のデータに透明の色(clearColor())を塗る=画像的に意味はないが、同じ処理を通すことでデータサイズを同程度にするという処理を行うことで、いい感じに空白検出できるようになりました。
なにをどういじくったら、1×1ビットマップ画像のサイズがOSアップデートごとに大幅に変わるのかさっぱり理解できないのですが、そこに何か意味があるのでしょうか?
Mac App Storeに出したアプリケーションで、PDFの空白ページ検出に本ルーチンを利用していますが、Appleのレビュー段階で「単にPDFから文字を抽出して空白検出しているのだろう」と思われてリジェクトされました。実際には、全ページを画像レンダリングして1ドットの画像でも存在していたら空白としては認識しない仕様になっているのですが、Appleのレビューワーには処理内容を説明して、各種ベンチマーク結果などを出さないと信じてもらえませんでした。
Appleのエンジニアが考えるところの空白検出が「文字チェックだけ」の使い物にならない処理だということはよくわかりました。また、頭からそのように思い込んで攻撃してきた間抜けにも遭遇してウンザリしました。
| AppleScript名:画像の空白判定 v4 |
| set aFile to (choose file of type {"public.image"}) set iRes to checkImageIsWhite(aFile) of whiteImageKit –> true –white (blank) image script whiteImageKit use AppleScript version "2.7" — High Sierra (10.13) or later use framework "Foundation" use framework "AppKit" use scripting additions property parent : AppleScript property NSData : a reference to current application’s NSData property NSDate : a reference to current application’s NSDate property |NSURL| : a reference to current application’s |NSURL| property NSColor : a reference to current application’s NSColor property NSImage : a reference to current application’s NSImage property NSBezierPath : a reference to current application’s NSBezierPath property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep –Compare Original Data and on checkImageIsWhite(aFile) set aPOSIXpath to POSIX path of aFile set aURL to |NSURL|’s fileURLWithPath:(aPOSIXpath) set aNSImage to NSImage’s alloc()’s initWithContentsOfURL:(aURL) set bNSImage to NSImage’s alloc()’s initWithContentsOfURL:(aURL) –copy aNSImage to bNSImage–Not deep copy ???? set fillColor1 to NSColor’s clearColor() set blankNSImage1 to drawImageWithFilledColor(aNSImage, fillColor1) of me set fillColor2 to makeNSColorFromRGBAval(65535, 65535, 65535, 65535, 65535) of me set blankNSImage2 to drawImageWithFilledColor(bNSImage, fillColor2) of me set aTiff to blankNSImage1’s TIFFRepresentation() set bTiff to blankNSImage2’s TIFFRepresentation() set chkWhite to (aTiff’s isEqualToData:bTiff) as boolean return chkWhite end checkImageIsWhite on getSizeOfImage(anNSImage) set aSize to anNSImage’s |size|() set aClass to class of aSize if aClass = record then copy aSize to theSize –To macOS 10.12.x else –macOS 10.13 or later set sizeX to (item 1 of item 2 of aSize) set sizeY to (item 2 of item 2 of aSize) set theSize to {width:sizeX, height:sizeY} end if return theSize end getSizeOfImage –指定サイズの画像を作成し、背景を指定色で塗る on drawImageWithFilledColor(anImage, fillColor) set aSize to getSizeOfImage(anImage) of me anImage’s lockFocus() set theRect to {{x:0, y:0}, {width:(width of aSize), height:(height of aSize)}} set theNSBezierPath to NSBezierPath’s bezierPath theNSBezierPath’s appendBezierPathWithRect:theRect fillColor’s |set|() theNSBezierPath’s fill() anImage’s unlockFocus() return anImage end drawImageWithFilledColor –aMaxValを最大値とする数値でNSColorを作成して返す on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer) set aRedCocoa to (redValue / aMaxVal) as real set aGreenCocoa to (greenValue / aMaxVal) as real set aBlueCocoa to (blueValue / aMaxVal) as real set aAlphaCocoa to (alphaValue / aMaxVal) as real set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa return aColor end makeNSColorFromRGBAval end script |
元号変換v41
西暦→元号の変換を行うAppleScriptです。誰もがこんな感じのプログラムを作って(書き捨てて)いると思うのですが、不思議と誰も公開しないという、、、
Japanese Gengo is only for Japanese calendar. It is old-style age names in Japanese. I don’t care of it for daily use.
| AppleScript名:元号変換v41 |
| set a to "2019/7/21" set {aGengoStr, aGengoNum} to retJapaneseGengo(a) of JGengoKit –> {"令和", 1} script JGengoKit on retJapaneseGengo(aDate as string) set aDateObj to date aDate tell current application set aYear to year of aDateObj set aMonth to month of aDateObj as number set aDay to day of aDateObj set aStr to retZeroPaddingText(aYear, 4) of me & retZeroPaddingText(aMonth, 2) of me & retZeroPaddingText(aDay, 2) of me set aGengo to "" if aStr ≥ "20190501" then set aGengo to "令和" set aGengoNum to aYear – 2019 + 1 else if aStr ≥ "19890108" then set aGengo to "平成" set aGengoNum to aYear – 1989 + 1 else if aStr ≥ "19261225" then set aGengo to "昭和" set aGengoNum to aYear – 1926 + 1 else if aStr ≥ "19120730" then set aGengo to "大正" set aGengoNum to aYear – 1912 + 1 else if aStr ≥ "18680125" then set aGengo to "明治" set aGengoNum to aYear – 1868 + 1 end if return {aGengo, aGengoNum} end tell end retJapaneseGengo –数値にゼロパディングしたテキストを返す on retZeroPaddingText(aNum, aLen) set tText to ("0000000000" & aNum as text) set tCount to length of tText set resText to text (tCount – aLen + 1) thru tCount of tText return resText end retZeroPaddingText end script |
macOS 10.13以降、スクリプトエディタのヘルプが更新され続けて驚く
力が入っているのか抜けているのかわかりませんが、スクリプトエディタのヘルプがOSのメジャーバージョンアップごとに更新されているのを見つけて驚かされました。

「スクリプトエディタヘルプ」(macOS High Sierra用)
「スクリプトエディタユーザーガイド macOS Mojave用」
「スクリプトエディタユーザーガイド macOS Catalina用」
などと、わざわざOSごとに更新され、日本語環境用に翻訳されています。ちょっと驚きました。
ただ、相変わらず内容は「は?」とか「えーー?」とかいう感想しか出てこないものなので、存在している以上の実質的な価値はありません。存在しないよりは数万倍よいのですが、「これを読んで使えるようにはならないだろー」という程度のものです。

▲macOS 10.13用スクリプトエディタユーザガイド。このScreen ShotのScriptは実際に動くので好感が持てるのですが、なぜかこれが次のOSから変更されています。単に「代わり映えしないから変更した」ぐらいの意図であればよいのですが、、、

▲macOS 10.14用スクリプトエディタユーザガイド

▲macOS 10.15用スクリプトエディタユーザガイド
冒頭から内容が、
「OS X 10.10 以降では、AppleScript に加え、スクリプト言語である JavaScript for Automation を使用してスクリプトを記述することができます。また、シェルスクリプトや、UserTalk などの他社製のスクリプト言語を使用してスクリプトを記述することもできます。」
とあり、「あたかもシェルスクリプトをOSA言語的に扱えるかのような間違った印象を与えてしまう」「UserTalk(Frontier)なんてもう誰も使っていないし、消滅して久しいぞ」といったツッコミを行なってしまうところですが、Apple社内の現場が内容のチェックをしないで秘伝のタレのように注ぎ足し注ぎ足しで更新しているだけだと痛感させられます。
内容は機械的に翻訳しているだけのようで、コンテクストメニューから呼び出せるApple純正のScriptはみんなローカライズされていない(事実)のに、ヘルプ内では名称などが日本語訳されて紹介されており、実際にコンテクストメニューを表示させると困惑させられること請け合いです。外注の翻訳会社のせいではなく、Apple側が何も指定しなかったので機械的に翻訳しただけでしょう。

▲macOS 10.15上のスクリプトエディタヘルプでは、コンテクストメニューの項目名が勝手に日本語訳されている。実物は英語なのに、、、、

▲実際にmacOS 10.15上のスクリプトエディタでコンテクストメニューを表示すると英語のまま
macOS標準搭載のコンテクストメニューScriptがあまりにも使えないので、全部捨てて入れ替えて使えるものを突っ込んだものが「Piyomaru Script Assistant」なわけですが、電子書籍のオマケとして付けたこのScriptもmacOS 10.13でいろいろ動かないものが出てきており、ちょうど機会があったので10.14/10.15用にアップデートしました(10.13対応はちょっとダメだと思います。バグの問題というよりも、絵文字の互換性がないので)。

CotEditor用のPowerPackの影響を受けて、そういう方向のScriptも突っ込んでいます。スクリプトエディタのコンテクストメニューから呼び出したAppleScriptから、スクリプトエディタのウィンドウをぐるぐる回してみようとしたら、ほとんど再描画されずに結果だけ表示されるという一幕もありました。
macOS 10.14でScript Editorのsdefから「execute」コマンドが除去されていた
macOS 10.14のスクリプトエディタのAppleScript用語辞書(sdef)から「execute」コマンドが除去されていることに気づきました。
このことは、別に「GUI側からスクリプトエディタ上のAppleScriptを実行できなくなった」ということではありません。
スクリプトエディタ自体もAppleScriptからコントロールできる「スクリプタブルな」アプリケーションであり、macOS 10.14以降ではAppleScriptからスクリプトエディタをコントロールしてAppleScriptを実行させることができなくなった、ということです。
セキュリティ向上のためにexecuteコマンドを削除したように見えますが、そういうことをやったならRelease Notesに明記しておいてほしいものです。
Piyomaru Context menu AssistantのmacOS 10.13版を10.14上で動作確認していたら、動かなくなっているものがあって(クラッシュするものも出てきております)、原因を確認していたら一部の予約語が、
with timeout of 36000 seconds --1時間のタイムアウト «event sedsexec» end timeout
のように文字化け。これによって「execute」(Script実行)の予約語が抹消されたことがわかりました。スクリプトエディタ自体をコントロールしてScriptを実行させるケースは少ないので、世界中でも私ぐらいしか影響はないことでしょう。ちなみに、サードパーティのAppleScript統合開発環境であるScript Debuggerにはexecuteコマンドがあります。
別にスクリプトエディタでAppleScript書類をオープンして実行するような迂遠かつマニアックかつ「もっと簡単な手段があるのにわざわざそんなことしなくても」というアクションを好き好んで行う人はほとんどいないでしょう。全人類のうち、私以外にダメージはないことでしょう。
実際にスクリプトエディタにexecuteコマンドを送って実行していた例というのは、実行時間の計測を行うAppleScriptです。地味にそういうのを使っていました(最近は、Shane StanleyのScript Geekで処理時間を計測するパターンが多いので使っていませんでしたが)。

▲macOS 10.13.6

▲macOS 10.13.6

▲macOS 10.14.6

▲macOS 10.14.6

▲macOS 10.15.2

▲macOS 10.15.2
choose multiple path Script Libraryをv2.1にアップデート
NSPathControlでドラッグ&ドロップにより複数のパス情報を設定するダイアログUIを提供するAppleScriptライブラリ「choose multiple path Script Library」をv2.1にアップデートしました。
–> Download choosePathLib.scptd(~/Library/Script Libraries)
変更点:
・dialog widthでダイアログの表示幅を指定できるようにした
・NSPathControlの配色がDarkModeに合わなかったので、色は無指定にした

・AppleScript用語辞書にサンプルAppleScript(URLリンク入り)と実行結果イメージを入れた(分かりやすさ、使い勝手Up)

さまざまなGUI部品を使ってアラートダイアログ上に箱庭ユーザーインタフェースを作り込んだ場合に、通常のAppleScript書類のままだと、ランタイム環境(とくにScript Debugger)の差異の影響を受けてドラッグ&ドロップを受信できないとか実行時に明示的にメインスレッド実行が必要になるケースが見られます。
AppleScript用語辞書を通じてアクセスすることで、そうした問題を「うやむやにできる」ことがわかってきたので(技術的には説明できないものの、だいたいそんなもんだろうと予想はしていました)、こうした再利用の可能性が高いGUI利用部品は積極的にライブラリにまとめています。
指定緯度における1km相当の経度の大きさを求める
指定緯度における1kmあたりの経度の角度などを計算するAppleScriptです。Rubyのコードで書かれたものがあったので、AppleScriptに翻訳してみました。
三角関数を用いるため、関数計算ライブラリ「calcLibAS」を併用しています。
なんでこんな計算をすることになったかといえば、Apple Mapsの仕様で、地図表示エリアの指定時に表示距離ではなく表示角度(spn)が要求されるようなので、計算方法を求めておいたという次第です。
| AppleScript名:1kmあたりの経度の大きさを求める.scptd |
| — – Created by: Takaaki Naganoya – Created on: 2019/12/15 — – Copyright © 2019 Piyomaru Software, All Rights Reserved — use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use scripting additions use calcLib : script "calcLibAS" –参照:1kmあたりの経度の大きさ –https://github.com/ryym/google-maps-demo/blob/master/note.md set aLat to 31.5719562 –鹿児島の緯度 set aDeg to calc1kmDegreeinLatitude(aLat) of me –経度方向(横)に1kmに相当する角度 –> 0.010543813284 set bDeg to calc100mDegreeinLatitude(aLat) of me –経度方向(横)に100mに相当する角度 –> 0.001054381328 set aLat to 35.73993521 –練馬の緯度 set aDeg to calc1kmDegreeinLatitude(aLat) of me –経度方向(横)に1kmに相当する角度 –> 0.011067403977 set bDeg to calc100mDegreeinLatitude(aLat) of me –経度方向(横)に100mに相当する角度 –> 0.001106740398 –1kmあたりの緯度の大きさ set eRes to calc1kmDegreeinLong() of me –緯度方向(縦)に1kmに相当する角度 –> 0.008983152841 set eRes to calc100mDegreeinLong() of me –緯度方向(縦)に100mに相当する角度 –> 8.98315284119522E-4 –指定緯度における1km相当の経度の角度 on calc1kmDegreeinLatitude(aLat) set eRadius to 6378.137 –in km set r to eRadius * (cos (aLat * pi / 180)) set cm to 2 * pi * r set kd to 360 / cm return kd end calc1kmDegreeinLatitude –指定緯度における1km相当の経度の角度 on calc100mDegreeinLatitude(aLat) set eRadius to 6378.137 –in km set r to eRadius * (cos (aLat * pi / 180)) set cm to 2 * pi * r set kd to 360 / cm return kd / 10 end calc100mDegreeinLatitude –1km相当の緯度の角度 on calc1kmDegreeinLong() set eRadius to 6378.137 –in km set pc to 2 * pi * eRadius set dLat to 360 / pc return dLat end calc1kmDegreeinLong –100m相当の緯度の角度 on calc100mDegreeinLong() return calc1kmDegreeinLong() / 10 end calc100mDegreeinLong |
イベント「macOS Native」でAppleScriptの説明を
今週の土曜日、秋葉原で行われるイベント「macOS Native Symposium #06」にてデモを行います。
日 時:2019年 12月 21日 (Sat) 15:00〜18:00
場 所:TIME SHARING 秋葉原
会場費:1,500円
15:10より35分ほどお話をさせていただきます。主に、先日配布を開始したCotEditorのPowerPackを中心に話を行います。説明20分、質疑応答15分を予定しています。
前回、自由回答形式で質問を受け付けたら、全部で20分の予定が(時間の余裕があったので)発表20分、質問20分みたいな「発表自体は予定どおりだったけれど、予定通りに終わりませんでした。本当にすんませんでした!」みたいな長さになっていたので、発表自体を短くして自由形式の質問の時間を長くとったほうがいいのかもしれません。

▲CotEditorのPowerPack、そして新たに作り直したスクリプトエディタ向けのPiyomaru Script Assistant(macOS 10.14以降用)についてご紹介
前回は「笑い」を重視していろいろネタを仕込んでおきましたが、今回はお笑い要素は控えめでいきたいと思います。


























