Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

カテゴリー: list

Safariで現在見えている表を抽出してCSV書き出し

Posted on 9月 24, 2019 by Takaaki Naganoya

Safariの最前面のウィンドウで表示中のページのうち、現在ウィンドウ内に表示中の表要素をCSV書き出ししてNumbersでオープンするAppleScriptです。

このところ下調べを行なっていた「Webブラウザで表示中の要素を処理する」「表示中ではない要素は処理をしない」というScriptです。

これで、「表の一部を選択しておく」とかいった操作は不要になりました。ウィンドウ内に表示されている表をWebコンテンツ内部の表示座標をもとに自動抽出します。表示エリア外に位置しているものは書き出し処理しません。

各DOM ElementsのWebコンテンツ中の表示座標を取得して、絞り込みを行なっています。ただし、各DOM座標はWebブラウザのスクロールにしたがって数値が変わる(相対座標)ため、少々手こずりました。また、本Scriptでは上下スクロールのみ考慮してDOM要素の抽出を行なっており、横に長いページの横方向スクロールは考慮しておりません。

本Scriptは大量一括処理を志向するプログラムではなく、「見えているもの」をそのまま処理してほしいという考えで作ったものでもあり、Webブラウザ(Safari)で表示中のページのソースを取得してそのまま処理しています。つまり、ユーザーが閲覧中のページのデータそのものを処理しています。

これは、ページのソースを取得するコマンドを持っていないGoogle Chromeにはできない処理です(同じURLの内容を別途curlコマンドなどで取得すればOK。Cookie値などの再現が大変でしょうけれども)。

その他、実際に作って使ってみた感想は、装飾用に使われている表データまで取り込んでしまう点に不満があるぐらいでしょうか。これら「ゴミデータ」(再利用する価値のない装飾用の表データ)を区別するために、行数が足りない場合には書き出さないといった「足切り」を行う必要性を感じます。

–> Download VisibleTableExporter(Code-signed executable applet with Framework in its bundle)

AppleScript名:Safariで現在見えている表を抽出してCSV書き出し.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/22
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader

property NSUUID : a reference to current application’s NSUUID
property NSString : a reference to current application’s NSString
property HTMLDocument : a reference to current application’s HTMLDocument
property NSMutableArray : a reference to current application’s NSMutableArray
property NSJSONSerialization : a reference to current application’s NSJSONSerialization

set aTag to "table"

set indRes to getVisibleElementIndexList(aTag) of me
if indRes = false or indRes = {} then
  display notification "No Visible Table in Web browser"
  
return
end if

tell application "Safari"
  tell front document
    set aSource to source
  end tell
end tell

repeat with i in indRes
  set inList to filterATableAndPaseCells(aSource, i, aTag) of me
  
if inList = false or inList = {} then return
  
set aUUID to current application’s NSUUID’s UUID()’s UUIDString() as text
  
set aNewFile to ((path to desktop) as string) & aUUID & ".csv"
  
saveAsCSV(inList, aNewFile) of me
  
  
tell application "Numbers"
    open (aNewFile as alias)
  end tell
end repeat

tell application "Numbers" to activate

on filterATableAndPaseCells(aSource as string, targInd as integer, aTag as string)
  set aHTML to current application’s HTMLDocument’s documentWithString:(aSource as string)
  
  
–Table要素をリストアップ
  
set eList to (aHTML’s nodesMatchingSelector:aTag) as list
  
set aObj to contents of item (targInd + 1) of eList
  
  
  
–Count columns of Table Header
  
set aTableHeader to (aObj’s nodesMatchingSelector:"tr")’s firstObject()
  
set hList to aTableHeader’s nodesMatchingSelector:"th"
  
set hStrList to {}
  
repeat with i1 in hList
    set the end of hStrList to i1’s textContent() as string
  end repeat
  
set hLen to length of hStrList –count columns
  
  
–Acquire whole table body contents
  
set aTableBody to (aObj’s nodesMatchingSelector:"tbody")’s firstObject()
  
set bList to aTableBody’s nodesMatchingSelector:"td"
  
set bbList to {}
  
repeat with i2 in bList
    set the end of bbList to i2’s textContent() as string
  end repeat
  
  
set tbList to makeList1DTo2D(bbList, hLen) of me
  
  
return {hStrList} & tbList
end filterATableAndPaseCells

–1D Listを2D化
on makeList1DTo2D(orig1DList as list, aMax)
  set tbList to {}
  
set tmpList to {}
  
set aCount to 1
  
  
repeat with i3 in orig1DList
    set j to contents of i3
    
set the end of tmpList to j
    
    
if aCount ≥ aMax then
      set aCount to 1
      
set the end of tbList to tmpList
      
set tmpList to {}
    else
      set aCount to aCount + 1
    end if
  end repeat
  
  
return tbList
end makeList1DTo2D

–Safariのウィンドウ上で表示中のDOM Elementsを座標計算して返す
on getVisibleElementIndexList(aTag as string)
  tell application "Safari"
    set dCount to count every document
    
if dCount = 0 then return false
    
    
set jRes to do JavaScript "var winWidth = window.innerWidth,
winHeight = window.innerHeight,
winLeft = window.scrollX
winTop = window.scrollY,
winBottom = winTop + winHeight,
winRight = winLeft + winWidth,
    elementsArray = document.body.getElementsByTagName(’" & aTag & "’),
    elemLen = elementsArray.length,
inView = [];
      
    var step;
    for (step = 0 ; step < elemLen ; step++) {
      var tmpElem = document.body.getElementsByTagName(’" & aTag & "’)[step];
      var bVar = tmpElem.getBoundingClientRect();
      if (bVar.top > 0 && bVar.top < winHeight) {
        inView.push(step);
      }
    }
    JSON.stringify(inView);"
in front document
    
    
set jList to parseJSONAsList(jRes) of me
    
return jList
    
  end tell
end getVisibleElementIndexList

on parseJSONAsList(jsRes as string)
  set jsonString to NSString’s stringWithString:jsRes
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
return aJsonDict as list
end parseJSONAsList

–Save 2D List to CSV file
on saveAsCSV(aList as list, aPath)
  set crlfChar to (string id 13) & (string id 10)
  
set LF to (string id 10)
  
set wholeText to ""
  
  
repeat with i in aList
    set newLine to {}
    
    
–Sanitize (Double Quote)
    
repeat with ii in i
      set jj to ii as text
      
set kk to repChar(jj, string id 34, (string id 34) & (string id 34)) of me –Escape Double Quote
      
set the end of newLine to kk
    end repeat
    
    
–Change Delimiter
    
set aLineText to ""
    
set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to "\",\""
    
set aLineList to newLine as text
    
set AppleScript’s text item delimiters to curDelim
    
    
set aLineText to repChar(aLineList, return, "") of me –delete return
    
set aLineText to repChar(aLineText, LF, "") of me –delete lf
    
    
set wholeText to wholeText & "\"" & aLineText & "\"" & crlfChar –line terminator: CR+LF
  end repeat
  
  
if (aPath as string) does not end with ".csv" then
    set bPath to aPath & ".csv" as Unicode text
  else
    set bPath to aPath as Unicode text
  end if
  
  
writeToFileAsUTF8(wholeText, bPath, false) of me
  
end saveAsCSV

on writeToFileAsUTF8(this_data, target_file, append_data)
  tell current application
    try
      set the target_file to the target_file as text
      
set the open_target_file to open for access file target_file with write permission
      
if append_data is false then set eof of the open_target_file to 0
      
write this_data as «class utf8» to the open_target_file starting at eof
      
close access the open_target_file
      
return true
    on error error_message
      try
        close access file target_file
      end try
      
return error_message
    end try
  end tell
end writeToFileAsUTF8

on repChar(origText as text, targChar as text, repChar as text)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

★Click Here to Open This Script 

Posted in file JavaScript JSON list Text | Tagged 10.12savvy 10.13savvy 10.14savvy HTMLDocument NSJSONSerialization NSMutableArray NSString NSUUID Numbers Safari | 1 Comment

tableExtractor

Posted on 9月 15, 2019 by Takaaki Naganoya

Safariで表示中のページのうち、テキストを選択中のキーワードを含む表をCSVファイルに書き出してNumbersでオープンするAppleScriptです。

–> Download tableExtractor Run-Only (Code-Signed Executable including Framework in its bundle)

–> Watch Demo movie

実行前にSafariの「開発」メニューから、「スマート検索フィールドからのJavaScriptの実行を許可」「AppleEventからのJavaScriptの実行を許可」を実行しておく必要があります(実行済みの場合には必要ありません)。


▲Safariで表示中のページのうち、CSV書き出ししたい表のテキストを選択


▲本Scriptで表をCSVに書き出してNumbersでオープン

以前に作成した「Safariで表示中のPageの選択中の文字を含む表データを取得」Scriptがいい線を行っていた(あらかじめ表中のテキストを選択しておく、という前提条件がかったるいかと思っていたのに、そうでもなかった)ので、ありもののサブルーチンを追加して、表部分のHTMLからのタグ削除やCSV書き出しなどを行えるようにしました。

本Scriptは表データをCSV書き出しする必要はどこにもないのですが、Numbers v6.1に「表を新規作成して表のセル数を指定すると多くの場合にエラーになる」というバグがあるので、Numbersを直接操作してデータ出力させることはやっていません。

処理時間もさほどかからないので、表示中のページのすべての表オブジェクトをCSV化したり、表を選択するUIを実装して、「どの表を出力するか?」という選択処理をしてもいいかもしれません。


▲漫然とMacOS日本語で書き出ししたため文字化けしたもの(左)、UTF8を指定して書き出ししたために文字化けしなくなったもの(右)

途中でCSV書き出しした表データに文字化けが発生していたのですが、これはUTF8でファイル書き出ししていなかったためでした。

本Scriptは前バージョンよりもキーワードの検出処理をていねいに行なっています。各TableのHTMLに対してタグの除去を行なったうえでWebブラウザ上で選択中の文字列を含んでいるかどうかをチェックしています。

AppleScript名:tableExtractor.scptd
— Created 2019-09-15 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader

property NSString : a reference to current application’s NSString
property HTMLDocument : a reference to current application’s HTMLDocument
property NSMutableArray : a reference to current application’s NSMutableArray

tell application "Safari"
  set dList to every document –Sometimes "count every document"causes error
  
if length of dList = 0 then return
  
  
–Get URL
  
tell front document
    set aURL to URL
  end tell
  
  
–Get Selected Text
  
set aRes to do JavaScript "var selObj = window.getSelection();
  var selStr = (selObj).getRangeAt(0);
  unescape(selStr);"
in front document
  
  
if aRes = "" then return
end tell

set aRes to filterATableAndPaseCells(aURL, aRes) of me
if aRes = false then
  display notification "I could not filter table data…"
  
return
end if

–Save 2D List to temp CSV file on desktop folder
set savePath to ((path to desktop) as string) & (do shell script "uuidgen") & ".csv"
saveAsCSV(aRes, savePath) of me

tell application "Numbers"
  activate
  
open file savePath
end tell

on filterATableAndPaseCells(aURL, aKeyword)
  set aData to (do shell script "curl " & aURL)
  
set aHTML to current application’s HTMLDocument’s documentWithString:(aData as string)
  
  
–Table要素をリストアップ
  
set eList to (aHTML’s nodesMatchingSelector:"table")
  
  
–Table要素のうちSafari上で選択中の文字列を含むものをサーチ(指定データを含むものを抽出)
  
set hitF to false
  
repeat with i in eList
    set cellList to i’s children()’s array()
    
set htmlSource to i’s serializedFragment() as string –HTML source
    
set html2 to trimStrFromTo(htmlSource, "<", ">") of me
    
set html3 to repChar(html2, return, "") of me
    
    
if html3 contains aKeyword then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
if hitF = false then return false
  
  
–Count columns of Table Header
  
set aTableHeader to (i’s nodesMatchingSelector:"tr")’s firstObject()
  
set hList to aTableHeader’s nodesMatchingSelector:"th"
  
set hStrList to {}
  
repeat with i1 in hList
    set the end of hStrList to i1’s textContent() as string
  end repeat
  
set hLen to length of hStrList –count columns
  
  
–Acquire whole table body contents
  
set aTableBody to (i’s nodesMatchingSelector:"tbody")’s firstObject()
  
set bList to aTableBody’s nodesMatchingSelector:"td"
  
set bbList to {}
  
repeat with i2 in bList
    set the end of bbList to i2’s textContent() as string
  end repeat
  
  
set tbList to makeList1DTo2D(bbList, hLen) of me
  
  
return {hStrList} & tbList
end filterATableAndPaseCells

–1D Listを2D化
on makeList1DTo2D(orig1DList, aMax)
  set tbList to {}
  
set tmpList to {}
  
set aCount to 1
  
  
repeat with i3 in orig1DList
    set j to contents of i3
    
set the end of tmpList to j
    
    
if aCount ≥ aMax then
      set aCount to 1
      
set the end of tbList to tmpList
      
set tmpList to {}
    else
      set aCount to aCount + 1
    end if
  end repeat
  
  
return tbList
end makeList1DTo2D

on trimStrFromTo(aParamStr, fromStr, toStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set anArray to current application’s NSMutableArray’s array()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {theResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
    
theScanner’s scanString:fromStr intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
if theValue is missing value then set theValue to ""
    
    
theScanner’s scanString:toStr intoString:(missing value)
    
    
anArray’s addObject:theValue
  end repeat
  
  
if anArray’s |count|() = 0 then return aParamStr
  
  
copy aParamStr to curStr
  
repeat with i in (anArray as list)
    set curStr to repChar(curStr, fromStr & i & toStr, "") of me
  end repeat
  
  
return curStr
end trimStrFromTo

on repChar(aStr, targStr, repStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr
  
set cString to bString as string
  
return cString
end repChar

–2D List to CSV file
on saveAsCSV(aList, aPath)
  –set crlfChar to (ASCII character 13) & (ASCII character 10)
  
set crlfChar to (string id 13) & (string id 10)
  
set LF to (string id 10)
  
set wholeText to ""
  
  
repeat with i in aList
    set newLine to {}
    
    
–Sanitize (Double Quote)
    
repeat with ii in i
      set jj to ii as text
      
set kk to repChar(jj, string id 34, (string id 34) & (string id 34)) of me –Escape Double Quote
      
set the end of newLine to kk
    end repeat
    
    
–Change Delimiter
    
set aLineText to ""
    
set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to "\",\""
    
set aLineList to newLine as text
    
set AppleScript’s text item delimiters to curDelim
    
    
set aLineText to repChar(aLineList, return, "") of me –delete return
    
set aLineText to repChar(aLineText, LF, "") of me –delete lf
    
    
set wholeText to wholeText & "\"" & aLineText & "\"" & crlfChar –line terminator: CR+LF
  end repeat
  
  
if (aPath as string) does not end with ".csv" then
    set bPath to aPath & ".csv" as Unicode text
  else
    set bPath to aPath as Unicode text
  end if
  
  
writeToFileUTF8(wholeText, bPath, false) of me
  
end saveAsCSV

on writeToFileUTF8(this_data, target_file, append_data)
  tell current application
    try
      set the target_file to the target_file as text
      
set the open_target_file to open for access file target_file with write permission
      
if append_data is false then set eof of the open_target_file to 0
      
write this_data as «class utf8» to the open_target_file starting at eof
      
close access the open_target_file
      
return true
    on error error_message
      try
        close access file target_file
      end try
      
return error_message
    end try
  end tell
end writeToFileUTF8

★Click Here to Open This Script 

Posted in file Internet JavaScript list Text | Tagged 10.12savvy 10.13savvy 10.14savvy HTMLDocument NSMutableArray NSString Numbers Safari | 1 Comment

htmlの並列ダウンロード処理

Posted on 9月 13, 2019 by Takaaki Naganoya

Webコンテンツ(HTML)の並列ダウンロードを行うAppleScriptです。

Webコンテンツからデータを抽出する際に、あらかじめ一括でHTMLを並列ダウンロードしておいて、ダウンロードずみのデータを一括処理すると処理時間を大幅に短縮できます。「戦場の絆」Wikiから機体データの表をすべて取得するのに、順次HTMLを取得して抽出していた場合には3〜4分程度かかっていたものを、並列ダウンロードしたのちにデータ処理するように変更すれば、これが十数秒程度にまで高速化できます。

既存のプログラムを修正してHTMLのダウンロード用に仕立ててみました。並列キュー(未処理タスク数)へのアクセスはAppleScriptからはできなかった(実行すると結果が返ってこなかった)のですが、そこに手をつけずに並列処理の完了状態を検出しています。

ダウンロードが完了したデータはlist(配列)に入るので、リクエスト数と完了リクエスト数をループ内でチェックし、完了アイテム数がリクエスト数と同じになれば終了、条件を満たさない場合でも指定のタイムアウト時間を超えたらエラー終了という処理を行なっています。

問題点は、スクリプトエディタ上では実行できるもののScript Debugger上では実行できないことです(結果が返ってきません)。AppleScriptアプレットに書き出して実行してみたところ、結果が返ってきません。ただし、Script Menuからの実行は行えます(macOS 10.12.6、10.13.6、10.14.6で同様)。XcodeのAppleScript Appのプロジェクト内で実行することはできました。

このように、ランタイム環境に実行状況が左右される点にご注意ください。ただし、そうしたマイナス面を補ってあまりあるほどダウンロードは高速です。90ファイル強のダウンロードを数秒で完了(マシン、ネットワーク速度、ダウンロード先サーバーの負荷状況、キャッシュ状態などに左右される、参考値です)するため、ダウンロード後に各HTMLからのデータ抽出も高速に行えます。

AppleScript名:htmlの並列ダウンロード処理
— Created 2019-09-13 by Takaaki Naganoya
— 2019 Piyomaru Software
—
– オリジナル: HTTP からファイルをダウンロードして、ローカルに保存する方法(shintarou_horiのブログ)
— http://shintarou-hori.hatenablog.com/entry/2014/03/15/193604

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSData : a reference to current application’s NSData
property NSString : a reference to current application’s NSString
property NSOperationQueue : a reference to current application’s NSOperationQueue
property NSInvocationOperation : a reference to current application’s NSInvocationOperation
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

property aTimer : missing value

script spd
  property mList : {}
  
property resList : {}
end script

set (mList of spd) to {}
set (resList of spd) to {}
set baseURL to "https://w.atwiki.jp/senjounokizuna/pages/"

set numList to {25, 1594, 890, 70, 1669, 82, 1717, 997, 1080, 1614, 1712, 159, 1311, 1694, 1752, 1263}

set urlList to {}
repeat with i in numList
  set the end of urlList to (baseURL & i as string) & ".html"
end repeat

set aLen to length of urlList
set paramObj to {urls:urlList, timeOutSec:60}

–set (resList of spd) to downloadHTMLConcurrently(urlList, 60) of me
my performSelectorOnMainThread:"downloadHTMLConcurrently:" withObject:(paramObj) waitUntilDone:true
return length of (mList of spd)

on downloadHTMLConcurrently:paramObj
  set urlList to (urls of paramObj) as list
  
set timeOutS to (timeOutSec of paramObj) as real
  
set aLen to length of urlList
  
  
repeat with i in urlList
    set j to contents of i
    
set aURL to (|NSURL|’s URLWithString:j)
    
set aQueue to NSOperationQueue’s new()
    
set aOperation to (NSInvocationOperation’s alloc()’s initWithTarget:me selector:"execDL:" object:aURL)
    (
aQueue’s addOperation:aOperation)
  end repeat
  
  
–Check Completion
  
set compF to false
  
set loopTimes to (timeOutS * 10)
  
repeat loopTimes times
    if length of (mList of spd) = aLen then
      set compF to true
      
exit repeat
    end if
    
delay 0.1
  end repeat
  
  
if compF = false then error "Concurrent download timed out"
end downloadHTMLConcurrently:

on execDL:theURL
  set receiveData to NSData’s alloc()’s initWithContentsOfURL:theURL
  
if receiveData = missing value then
    return
  end if
  
  
set aFileName to theURL’s |lastPathComponent|()
  
my performSelectorOnMainThread:"saveData:" withObject:{receiveData, aFileName} waitUntilDone:false
end execDL:

on saveData:theDataArray
  copy theDataArray to {theData, saveFileName}
  
set aCon to NSString’s alloc()’s initWithData:theData encoding:NSUTF8StringEncoding
  
set the end of (mList of spd) to {fileName:saveFileName as string, contentsData:aCon as string}
end saveData:

★Click Here to Open This Script 

Posted in list Network URL | Tagged 10.12savvy 10.13savvy 10.14savvy NSData NSInvocationOperation NSOperationQueue NSString NSURL NSUTF8StringEncoding | Leave a comment

指定フォルダ以下のアプリケーションを取得して、Scriptabilityをチェック

Posted on 9月 11, 2019 by Takaaki Naganoya

指定フォルダ以下のアプリケーションをSpotlightで検索して、それらのうちAppleScript対応の(Scriptableな)ものを集計するAppleScriptです。

Shane StanleyのMetadata Libを呼び出してSpotlight検索を行い、アプリケーションファイルの一覧を取得。その後、各アプリケーションのInfo.plistの情報を取得してNSAppleScriptEnabled=trueのアプリケーションのパスをlist(array)に追加します。

ただし、一部のApple製のアプリケーションに見られるように、NSAppleScriptEnabled=trueであるにもかかわらず、実際のAppleScriptサポート機能が実装されていないものもありますので(例:iBooks Author)これだけではScript対応かどうかの完全な判断は行えません。

一方、AppleScriptなどのOSA言語によるコントロールに対応していないアプリケーションはこれで完全にフィルタできます。NSAppleScriptEnabledのエントリがInfo.plistに存在しないものは非対応アプリケーションです。

非対応のアプリケーションに対して、AppleScriptからは起動、終了、ファイルのオープン、印刷などの基礎的な操作やアプリケーションの情報取得(名称やバージョンなど)、Finder経由で指定書類をアプリケーションでオープン、ぐらいの操作。また、GUI Scriptingによるメニューやボタンの強制操作しかできません。

AppleScript名:指定フォルダ以下のアプリケーションを取得して、Scriptabilityをチェック.scptd
— Created 2019-09-11 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use mdLib : script "Metadata Lib" version "2.0.0" –https://macosxautomation.com/applescript/apps/Script_Libs.html

set origPath to POSIX path of (choose folder default location (path to applications folder))
set aResList to mdLib’s searchFolders:{origPath} searchString:("kMDItemContentType == %@") searchArgs:{"com.apple.application-bundle"}

set allCount to length of aResList
set sList to {}

repeat with i in aResList
  set j to contents of i
  
set sRes to retAppScriptabilityFromBundleIPath(j) of me
  
if sRes = true then
    set the end of sList to j
  end if
end repeat

return (length of sList)
–> 297

–指定パスからアプリケーションのScriptabilityをbooleanで返す
on retAppScriptabilityFromBundleIPath(appPath as string)
  set aDict to (current application’s NSBundle’s bundleWithPath:appPath)’s infoDictionary()
  
set aRes to aDict’s valueForKey:"NSAppleScriptEnabled"
  
if aRes = missing value then return false
  
return aRes as boolean
end retAppScriptabilityFromBundleIPath

★Click Here to Open This Script 

Posted in file list Spotlight | Tagged 10.12savvy 10.13savvy 10.14savvy NSBundle | Leave a comment

2つのレコードのキーの重複を計算

Posted on 9月 7, 2019 by Takaaki Naganoya

2つのレコードのキーの重複を検出するAppleScriptです。

こういう処理がお手軽にできるようになったので、macOS 10.10以降のAppleScriptでないとプログラムが組めなくなっている今日このごろ。死ぬほど努力してOld Style AppleScript(Cocoaの機能を使わない)だけで組めないこともないですが、それだけで半日は費やして数百行のプログラムになってしまいそうです。

というわけで、Cocoaの機能を利用した「ありもの」のルーチンを組み合わせるだけでほぼ完成。2つのレコードのキーの重複計算を行います。

どういう用途で使うかといえば、パラメータつきのURLに対して追加パラメータを付加したい場合です。URLからレコード形式でパラメータを分離し、追加パラメータ(レコード形式)を足し算する「前」に、2つのレコード間で重複キーがないかチェックする、という用途です。

では、2つのレコードの加算はどーするのか? という話になりますが、それは単に&演算子で加算するだけです。

set aRec to {abc:"1", bcd:"2"}
set bRec to {ccc:"0", ddd:"9"}
set cRec to aRec & bRec
–> {abc:"1", bcd:"2", ccc:"0", ddd:"9"}

★Click Here to Open This Script 

AppleScript名:2つのレコードのキーの重複を計算.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/06
—
–  Copyright © 2019 jp.piyomarusoft, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property NSSet : a reference to current application’s NSSet
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableDictionary : a reference to current application’s NSMutableDictionary

set aRec to {autoplay:"1", hd:"9"}
set bRec to {v:"GP_tVXTYdmY", t:"120", hd:"3"}

set dupKeys to detectDuplicateKeysInTwoRecords(aRec, bRec) of me
–> {"hd"}

on detectDuplicateKeysInTwoRecords(aRec, bRec)
  set aDict to NSMutableDictionary’s dictionaryWithDictionary:aRec
  
set bDict to NSMutableDictionary’s dictionaryWithDictionary:bRec
  
  
set aKeyList to (aDict’s allKeys()) as list
  
set bKeyList to (bDict’s allKeys()) as list
  
  
set allKeyList to aKeyList & bKeyList
  
  
set aRes to returnDuplicatesOnly(allKeyList) of me
  
return aRes
end detectDuplicateKeysInTwoRecords

–1D Listから重複項目のみ返す
on returnDuplicatesOnly(aList as list)
  set aSet to NSCountedSet’s alloc()’s initWithArray:aList
  
set bList to (aSet’s allObjects()) as list
  
  
set dupList to {}
  
repeat with i in bList
    set aRes to (aSet’s countForObject:i)
    
if aRes > 1 then
      set the end of dupList to (contents of i)
    end if
  end repeat
  
  
return dupList
end returnDuplicatesOnly

★Click Here to Open This Script 

Posted in list Record | Tagged 10.12savvy 10.13savvy 10.14savvy NSCountedSet NSMutableDictionary NSSet | Leave a comment

Numbersで選択範囲のセルから数字以外の文字を除去する

Posted on 8月 29, 2019 by Takaaki Naganoya

Numbersで選択範囲のセルから数字以外の文字を除去するAppleScriptです。

よく、Webブラウザ上で表示中の表データからNumbersなどの表計算ソフトにデータをコピー&ペーストして再利用することがあります。この際に、Webブラウザ上で選択中のDOM構造などを取得できると便利なのですが、いろいろ調べてみてもなかなか方法が見つかりません(自分が設計していたら、絶対に実装してるんですけれども)。

そこで、表データをWebブラウザから表計算ソフト上にコピー後にデータ加工することを考えることになります。

ExcelやNumbers上に表データをペーストして、その後で加工することをよく行います。手動で行うとかったるいので、選択範囲のセルを順次加工するScriptを日常的に作ってはストックしてあります(セル内の改行文字を削除するとか、いろいろ)。

本ScriptはmacOS 10.14.6+Numbers v6.1で検証してあります。

OS標準装備のScript Menuに入れて呼び出して利用しています。

もともと、Numbersのセル書き換え速度はそれほど速くないので、数百セルとか数千セルを一気に書き換えるような用途は考慮していません。そういう用途には、書き換えたデータをCSV書き出しして、CSVのファイルをNumbersでオープンするといった処理になると思います。

AppleScript名:選択範囲のセルから数字以外の文字を除去する
— Created 2019-08-29 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

tell application "Numbers"
  tell front document
    tell active sheet
      try
        set theTable to first table whose class of selection range is range
      on error
        return false
      end try
      
      
tell theTable
        set cellList to cell of selection range
        
set mySelectedRanges to value of cell of selection range
        
set res2 to returnNumbersCharOnlyList(mySelectedRanges) of me
        
        
repeat with i from 1 to (length of cellList)
          ignoring application responses –Async Mode
            tell item i of cellList
              set value to item i of res2
            end tell
          end ignoring
        end repeat
        
      end tell
      
    end tell
  end tell
end tell

on returnNumbersCharOnlyList(aList)
  set nList to {}
  
repeat with i in aList
    set the end of nList to returnNumberCharsOnly(i) of me
  end repeat
  
return nList
end returnNumbersCharOnlyList

on returnNumberCharsOnly(aStr)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:"[^0-9]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end returnNumberCharsOnly

★Click Here to Open This Script 

Posted in list regexp Text | Tagged 10.12savvy 10.13savvy 10.14savvy NSRegularExpressionSearch NSString | Leave a comment

画面上の指定座標にマウスカーソルを強制移動させてクリック

Posted on 8月 22, 2019 by Takaaki Naganoya

マウスカーソルを指定座標に強制的に移動させて、マウスクリック(プレス)を行う補助アプリケーション+呼び出しAppleScriptです。

# ご注意:マウスカーソルの移動やクリックを行うのは、本来のAppleScriptの処理ではありません
# ご注意:他のマシン上で同じ動作を再現することが(初心者には)難しいため、おすすめしません

# 本ツールはCodeSignしてMac App Storeで近日リリースする予定です

必要悪! 画面上の部品や座標をクリックする機能

マウスカーソルの強制移動とクリックは、AppleScriptではなるべく避けるべき操作ですが、ごく一部の操作を実行するため、ごくごくまれに必要になることがあります。

macOS標準装備のAppleScript専用のツール「System Events」に「click」コマンドがあり、指定のGUI部品か、あるいは画面上の座標をクリックするようになっています。

ただし、これはあくまで「指定のGUI部品や指定座標をクリックしたというメッセージ」を対象(アプリケーション)に送るというものであり、実際にマウスカーソルを移動させてクリックを実行するものではありません。

強制マウス操作ツールの歴史

それでも、アプリケーションがAppleScriptに対して機能を解放していない機能を呼び出す必要があって、かつ、メニューやボタンのクリックなどで実行できないような場合には、止むを得ず指定座標のクリックをごくまれに行うことがあります(1年に1度ぐらいの頻度)。


▲強制的にマウスカーソルを移動させてクリックする支援ツールの変遷

これまで、Framework呼び出しでこれらの動作を行ってきましたが、macOS 10.14で(SIPを解除しないかぎり)スクリプトエディタ上ではサードパーティのFrameworkを呼べなくなりました(AppleScriptドロップレット上ではバンドル内のFrameworkを呼べます)。

呼び出し側から一番簡単に利用できる方法は、指定座標のクリック機能を持つアプリケーションをXcode上でAppleScriptで作成しておき、sdefを定義して、AppleScript対応アプリケーションをAppleScriptで作るものです(豆腐をすりつぶして「ひろうす」を作るようなこの迂遠さ。Google翻訳で絶対に伝わらないニュアンス。パンをすりつぶしてパンを作るような、、、)。

そのため、いままでFrameworkで運用してきたプログラムを、アプリケーション化してsdefを付加し、スクリプタブルなバックグラウンドアプリケーション(Dockに表示されない、メニューやウィンドウが表示されない)にする必要が出てきます(まんまとAppleにタダ働きさせられているような気がするので気分はよくありませんが)。

自分ではほとんど使わないマウス強制移動&クリックツールを作ってみた

そこで、sdef(AppleScript用語辞書)をつけたライブラリやアプリケーションを作る方向で調査を行っていました。実際にパラメータの受け渡しをどのように行えるのか、どのあたりでハマるのか、どのぐらいの作業量になるのか。

その1つの到達点として、この補助アプリケーション「mouseClick」を作ってみました。バックグラウンド実行専用のため、起動してもDockにアイコンは表示されません(これを知らないユーザーがバックグラウンド起動専用のアプリケーションに、Mac App Sroreでいちゃもんをつけているのを見かけて、遠い目になりました)。

–> Download mouseClick.app (To /Applications)

※ このツールは、AppleScriptからコマンドで操作する専用のものであり、画面上には何も表示されません。

簡単なAppleScriptでマウスカーソルの移動とクリックを実行できます。初回実行時はシステム環境設定の「セキュリティとプライバシー」>「プライバシー」>「アクセシビリティ」でmouseClickに「コンピュータの制御を許可」しておく必要があります(管理者権限が必要、2回目以降は操作不要)。

forceClickでは、画面の左上を原点とした座標系を使用しています。

呼び出し側と実行側をすべてAppleScriptで組めるようになったわけで、Classic MacOS時代からこの手の「指定座標の強制クリック系ソリューション」を(止むを得ず)使ってきた身からするとなかなか感慨深いものがあります。

参考文献:
objective c 入門 CocoaアプリケーションにAppleScriptサポートを追加するにはどうすればよいですか?

AppleScript名:force click sample
tell application "mouseClick"
  force click at {4, 4} –click apple menu
end tell

★Click Here to Open This Script 

Posted in GUI Scripting list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy 10.15savvy | 2 Comments

Script Editor, Script Debuggerから選択範囲の情報を取得

Posted on 8月 19, 2019 by Takaaki Naganoya

スクリプトエディタ(Script Editor)およびScript Debuggerの最前面の書類から選択範囲の情報を取得するAppleScriptです。

スクリプトエディタとScript Debuggerで動く共通のツールをAppleScriptで作ってみたら、結果が異なりました。両エディタで同じ範囲を選択してみても、


▲Script Editor上で選択(左)、Script Debugger上で選択(右)

Script Editorでは、character range of selectionを実行すると、{文字開始位置, 文字終了位置}を返してきます。

一方、Script Debuggerでは、character range of selectionを実行すると、{文字開始位置, 選択文字長}を返してきます。

この違いにより、両エディタで異なる結果が得られたのでした。

選択中のテキスト(contents of selection)をそのまま取得して処理するのはよくあるパターンです。選択範囲を数値で取得するケースは(個人的に)あまりありませんでした。

これらのエディタ上で選択中のAppleScriptソースを取得して、そのままコンパイル(構文確認)して変数のみ抽出して変数名の一覧リストが欲しかったのですが、部分的にコンパイル(構文確認)しただけではエラーになる例が多い(ライブラリを呼び出している場合とか)ので、ファイル全体をコンパイル(構文確認)したのちに、エディタ上の選択範囲に該当するデータを抽出するように変更してみました。


▲選択範囲からAppleScript構文色分け情報をもとに変数のみ列挙して返すAppleScriptを実行。このために両エディタ対応を調べていた

AppleScript名:Ecript Editorの選択範囲情報を取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/08/19
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
tell application "Script Editor"
  tell front document
    set {cStart, cEnd} to character range of selection –{start pos, end pos}
    
return {cStart, cEnd}
    
–> {516, 1334}
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:Script Debuggerの選択範囲情報を取得
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/08/19
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
tell application "Script Debugger"
  tell front document
    set {cStart, cNums} to character range of selection –{start pos, length}
    
return {cStart, cStart + cNums – 1}
    
–> {516, 1335}
  end tell
end tell

★Click Here to Open This Script 

Posted in list Text | Tagged 10.12savvy 10.13savvy 10.14savvy Script Debugger Script Editor | Leave a comment

アラートダイアログ上にcheckboxを表示 v2

Posted on 8月 11, 2019 by Takaaki Naganoya

アラートダイアログ上にチェックボックスで選択肢の入力を求めるAppleScriptです。


▲macOS 10.14上のLight Mode(Left)、Dark Mode(Right)

ラジオボタンで項目選択を行うのと同じぐらい、チェックボックスで項目選択を行うのは「よくある処理」です。ただ、それだけのためにXcode上でAppleScriptのプログラムを作るのは大袈裟なのと、できることはなるべく普通のAppleScript(Xcode上ではない)のランタイム環境上でできたほうがよいとの考えから、こうして箱庭UIをちまちま作っているものです。

本AppleScriptは、スクリプトエディタ上、スクリプトメニュー、AppleScriptアプレット、Script Debugger上などのランタイム環境で動作します。他のAppleScriptのバンドル中に入れてAppleScript Librariesとして呼び出して使うことを前提に整備してあります。

チェックした項目の番号がリストで返ってきます。各項目番号は1始まり(1-based index)です。


▲「?」ボタン(Help Button)をクリックすると、すべてのチェックボックスをオン/オフにするかを指定できる(ないと不便そうだったので)

ボタンのタイプはいくつか変更して試していますが、このあたりは最近のOSアップデートでいろいろ変更が加わっており、実際に指定しても有意な結果が得られない(なんで存在するのかよくわからない)ものもあり、実際に指定して試してみることをおすすめします。

本Scriptにはいくつか制約事項があります。選択肢の個数が奇数の場合にうやむやにする処理はつけていません。また、選択肢の文字数が長い場合への対処も一切していません。このあたりは真面目にプログラムを組む際には問題になってくることでしょう。


▲NSButtonTypeOnOff


▲NSButtonTypePushOnPushOff


▲NSButtonTypeRadio


▲NSButtonTypeSwitch


▲NSButtonTypeToggle(クリックすると遅れてステートが変化する。なにこれ?)

実際には、ライブラリ化してこんな風に呼び出して使うことを想定しています。

— Created 2019-08-07 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use checkLib : script "checkboxLib"

set paramObj to {myMessage:"Select target Item", mySubMessage:"適切なものを以下からえらんでください", mySuppression:"", myColNum:2, matrixTitleList:{"にんじん", "ごぼう", "だいこん", "りんご", "キャベツ", "レタス", "じゃがいも", "にんじん", "小松菜", "青梗菜", "しいたけ", "舞茸"}}
set aRes to dispCheckBoxMain(paramObj) of checkLib
–> {1, 2}

★Click Here to Open This Script 

AppleScript名:アラートダイアログ上にcheckboxを表示 v2
— Created 2019-08-07 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSView : a reference to current application’s NSView
property NSAlert : a reference to current application’s NSAlert
property NSButton : a reference to current application’s NSButton
property NSOnState : a reference to current application’s NSOnState
property NSOffState : a reference to current application’s NSOffState
property NSMutableArray : a reference to current application’s NSMutableArray
property NSButtonTypeSwitch : a reference to current application’s NSButtonTypeSwitch
property NSRunningApplication : a reference to current application’s NSRunningApplication

property theResult : 0
property returnCode : 0
property bArray : {} –Checkbox button object array

on run
  set paramObj to {myMessage:"Select target Item", mySubMessage:"適切なものを以下からえらんでください", mySuppression:"", myColNum:2, matrixTitleList:{"にんじん", "ごぼう", "だいこん", "りんご", "キャベツ", "レタス", "じゃがいも", "にんじん", "小松菜", "青梗菜", "しいたけ", "舞茸"}}
  
  
–my chooseItemByCheckBox:paramObj –for Debugging
  
my performSelectorOnMainThread:"chooseItemByCheckBox:" withObject:(paramObj) waitUntilDone:true
  
return my sort1DNumList:theResult ascOrder:true
  
–> {1, 3, 5, 7, 9, 11}
end run

on chooseItemByCheckBox:(paramObj)
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aMatList to (matrixTitleList of paramObj) as list
  
set aLen to length of aMatList
  
set aSupMes to mySuppression of paramObj
  
  
set colNum to (myColNum of paramObj) as integer
  
set rowNum to (aLen div colNum) + (aLen mod colNum)
  
  
set aButtonCellWidth to 150
  
set aButtonCellHeight to 24
  
  
set viewWidth to aButtonCellWidth * colNum
  
set viewHeight to aButtonCellHeight * rowNum
  
  
–define the matrix size where you’ll put the radio buttons
  
set matrixRect to current application’s NSMakeRect(0.0, 0.0, viewWidth, viewHeight)
  
set aView to NSView’s alloc()’s initWithFrame:(matrixRect)
  
  
set aCount to 1
  
set bArray to current application’s NSMutableArray’s new()
  
repeat with y from 1 to rowNum
    repeat with x from 1 to colNum
      if aCount ≤ aLen then
        set j to contents of item aCount of aMatList
        
set tmpB to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum) * aButtonCellHeight, aButtonCellWidth, aButtonCellHeight)))
        
        (
tmpB’s setTitle:j)
        (
tmpB’s setShowsBorderOnlyWhileMouseInside:true)
        (
tmpB’s setTag:(aCount))
        (
tmpB’s setTarget:me)
        (
tmpB’s setAction:("clicked:"))
        (
tmpB’s setButtonType:(NSButtonTypeSwitch))
        
        (
bArray’s addObject:tmpB)
        
      end if
      
      
set aCount to aCount + 1
    end repeat
  end repeat
  
  
–Select the first radio button item
  
–(tmpArray’s objectAtIndex:0)’s setState:(current application’s NSOnState)
  
set theResult to {}
  
  (
aView’s setSubviews:bArray)
  
  
— 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:aView
    
    
–for Help Button
    
its setShowsHelp:(true)
    
its setDelegate:(me)
    
    
–for suppression check box ( No use for this case? )
    
if (aSupMes as string) is not equal to "" then
      its setShowsSuppressionButton:(true) –「今後このメッセージを表示しない」チェックボックスを表示
      
set suppressionB to its suppressionButton
      
suppressionB’s setTitle:(aSupMes)
    else
      its setShowsSuppressionButton:(false)
    end if
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
  
set theResult to (tmpArray’s valueForKeyPath:"status") as list
end chooseItemByCheckBox:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

on clicked:aParam
  set aTag to (tag of aParam) as integer
  
if aTag is not in (theResult as list) then
    set the end of theResult to aTag
  else
    set theResult to my deleteItem:aTag fromList:theResult
  end if
end clicked:

on deleteItem:anItem fromList:theList
  set theArray to NSMutableArray’s arrayWithArray:theList
  
theArray’s removeObject:anItem
  
return theArray as list
end deleteItem:fromList:

–1D List(数値)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DNumList:theList ascOrder:aBool
  tell current application’s NSSet to set theSet to setWithArray_(theList)
  
tell current application’s NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_(missing value, true)
  
set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor}
  
return (sortedList) as list
end sort1DNumList:ascOrder:

–Help Button Clicked Event Handler
on alertShowHelp:aNotification
  set aRes to display dialog "Do you change all checkbox state?" buttons {"All Off", "All On", "Cancel"} default button 3 with icon 1
  
set bRes to (button returned of aRes) as string
  
  
if bRes = "All Off" then
    set bLen to bArray’s |count|()
    
set theResult to {}
    
repeat with i from 0 to bLen
      ((bArray’s objectAtIndex:i)’s setState:(current application’s NSOffState))
    end repeat
    
  else if bRes = "All On" then
    set bLen to bArray’s |count|()
    
set theResult to {}
    
repeat with i from 0 to bLen
      ((bArray’s objectAtIndex:i)’s setState:(current application’s NSOnState))
      
set the end of theResult to i + 1
    end repeat
  end if
  
  
return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする
end alertShowHelp:

★Click Here to Open This Script 

Posted in GUI list | Tagged 10.12savvy 10.13savvy 10.14savvy NSAlert NSButton NSButtonTypeSwitch NSMutableArray NSOffState NSOnState NSRunningApplication NSView | 1 Comment

標準偏差を求める v2

Posted on 8月 7, 2019 by Takaaki Naganoya

1次元配列から標準偏差を求めるAppleScriptです。

Numbersで検算してみたら結果が異なりました。元になっているSwiftのプログラムとは計算結果が合っているので、そういうこと(Cocoaの計算機能+変数型による差異)なんでしょうか。

AppleScript名:標準偏差を求める v2
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/08/07
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property NSExpression : a reference to current application’s NSExpression
property NSMutableArray : a reference to current application’s NSMutableArray

set aList to {14.0, 37.0, 18.5, 59.0, 100}
set d1Res to calcStddev(aList) of me
–> 122.708110069451

set a2List to {1, 2, 3, 4, 4, 5, 9, 11}
set d2Res to calcStddev(a2List) of me
–> 3.218598297396

–標準偏差を計算する
on calcStddev(aList)
  –https://nshipster.com/nsexpression/
  
set anArray to NSMutableArray’s arrayWithArray:aList
  
set anExp to NSExpression’s expressionForFunction:"stddev:" arguments:{(NSExpression’s expressionForConstantValue:anArray)}
  
set valList to anExp’s expressionValueWithObject:(missing value) context:(missing value)
  
return valList as real
end calcStddev

★Click Here to Open This Script 

Posted in list Number | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSExpression NSMutableArray | Leave a comment

アラートダイアログ上にRadio Buttonを表示 v4a

Posted on 8月 7, 2019 by Takaaki Naganoya

アラートダイアログ上にRadio Buttonを表示して項目選択するAppleScriptです。

普通にラジオボタンで項目選択できるようになりました。

表示列数を指定できるようにしておきましたが、選択項目の余りが発生すると処理に矛盾が発生するため、キリのいい(割り切れる)列数を指定してください(短時間で作った試作品なので、そこまで気合は入れていません)。

AppleScript名:アラートダイアログ上にRadio Buttonを表示 v4a
— Created 2019-08-07 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSView : a reference to current application’s NSView
property NSAlert : a reference to current application’s NSAlert
property NSButton : a reference to current application’s NSButton
property NSRadioButton : a reference to current application’s NSRadioButton
property NSButtonTypeRadio : a reference to current application’s NSButtonTypeRadio
property NSRunningApplication : a reference to current application’s NSRunningApplication

property theResult : 0
property returnCode : 0

set paramObj to {myMessage:"Select target Item", mySubMessage:"適切なものを以下からえらんでください", myColNum:2, matrixTitleList:{"にんじん", "ごぼう", "だいこん", "りんご", "キャベツ", "レタス", "じゃがいも", "にんじん", "小松菜", "青梗菜", "しいたけ", "舞茸"}}

–my chooseItemByRadioButton:paramObj –for Debugging
my performSelectorOnMainThread:"chooseItemByRadioButton:" withObject:(paramObj) waitUntilDone:true
return (theResult as integer)

on chooseItemByRadioButton:(paramObj)
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aMatList to (matrixTitleList of paramObj) as list
  
set aLen to length of aMatList
  
  
set colNum to (myColNum of paramObj) as integer
  
set rowNum to (aLen div colNum) + (aLen mod colNum)
  
  
set aButtonCellWidth to 150
  
set aButtonCellHeight to 24
  
  
set viewWidth to aButtonCellWidth * colNum
  
set viewHeight to aButtonCellHeight * rowNum
  
  
–define the matrix size where you’ll put the radio buttons
  
set matrixRect to current application’s NSMakeRect(0.0, 0.0, viewWidth, viewHeight)
  
set aView to NSView’s alloc()’s initWithFrame:(matrixRect)
  
  
set aCount to 1
  
set tmpArray to current application’s NSMutableArray’s new()
  
repeat with y from 1 to rowNum
    repeat with x from 1 to colNum
      if aCount ≤ aLen then
        set j to contents of item aCount of aMatList
        
set tmpB to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum) * aButtonCellHeight, aButtonCellWidth, aButtonCellHeight)))
        
        (
tmpB’s setTitle:j)
        (
tmpB’s setShowsBorderOnlyWhileMouseInside:true)
        (
tmpB’s setTag:(aCount))
        (
tmpB’s setTarget:me)
        (
tmpB’s setAction:("clicked:"))
        (
tmpB’s setButtonType:(current application’s NSButtonTypeRadio))
        
        (
tmpArray’s addObject:tmpB)
        
      end if
      
      
set aCount to aCount + 1
    end repeat
  end repeat
  
  
–Select the first radio button item
  (
tmpArray’s objectAtIndex:0)’s setState:(current application’s NSOnState)
  
set theResult to 1
  
  (
aView’s setSubviews:tmpArray)
  
  
— 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:aView
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
end chooseItemByRadioButton:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

on clicked:aParam
  set aTag to tag of aParam
  
set theResult to aTag
end clicked:

★Click Here to Open This Script 

Posted in dialog GUI list | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSAlert NSButton NSButtonTypeRadio NSRadioButton NSRunningApplication NSView | Leave a comment

Keynoteの表の内容を回転(Transpose)

Posted on 8月 2, 2019 by Takaaki Naganoya

Keynoteでオープン中の書類の現在表示中のページ(スライド)上にある表のデータを回転(transpose)する、行列入れ替えするAppleScriptです。

–> Download tableTransposer (Code Signed Applet)

Numbersでは、指定の表データの縦横転置のためのコマンド「行と列を転置」(transpose)がありますが、Keynoteにはありません。

Keynoteの表データをコピーしてNumbersに持って行って転置してKeynoteに戻していますが、メニュー操作で一発で転置できるようにしてみたものです。

回転(転置)前にテーブルの縦横のセル数をカウントして、高さと幅が同じでない場合には処理を行いません。ただ、これでは明らかに使い勝手がよくありません。いっそ、表のセル数を変更して転置したデータ形状に表の方を合わせるべきです。

Script Menuから実行させてみる場合には、コード署名してアプレット書き出しする必要があります。上記のリンクから、コード署名したアプレットをダウンロードして実行してみてください。

AppleScript名:表の内容を回転(Transpose)
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/05/16
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

set tName to getTargetTableName() of me
if tName = false then return

–Get Table Values as 2D List
set t1List to getValuesOfATableBy2DList(tName) of me

–Check Table width and height is same or not
set tRes to countElementsOf2DList(t1List) of me
if tRes = false then
  display notification "Table width and height is not same….."
  
return
end if

–Transpose 2D List
set t2List to transpose2DList(t1List) of me

–Set values to a table
setValuesToATableBy2DList(tName, t2List) of me

–Keynoteの現在の最前面の書類の現在のスライド上の指定名称の表に2D Listの値を入れる
on setValuesToATableBy2DList(tName, tList)
  tell application "Keynote"
    tell front document
      tell current slide
        tell table tName
          set rCount to count every row
          
set cCount to count every column
          
          
repeat with i from 1 to rCount
            tell row i
              repeat with ii from 1 to cCount
                tell cell ii
                  ignoring application responses –Sync Mode
                    set value of it to getItemByXY(ii, i, tList, "") of me
                  end ignoring
                end tell
              end repeat
            end tell
          end repeat
        end tell
      end tell
    end tell
  end tell
end setValuesToATableBy2DList

–Keynoteの現在の最前面の書類の現在のスライド上の指定名称の表から2D Listで値を取り出す
on getValuesOfATableBy2DList(tName)
  set a2Dlist to {}
  
tell application "Keynote"
    tell front document
      tell current slide
        tell table tName
          set rCount to count every row
          
repeat with i from 1 to rCount
            tell row i
              set tList to value of every cell
            end tell
            
set the end of a2Dlist to tList
          end repeat
        end tell
      end tell
    end tell
  end tell
  
return a2Dlist
end getValuesOfATableBy2DList

–処理対象となる表の名称を取得する
on getTargetTableName()
  tell application "Keynote"
    tell front document
      tell current slide
        set tCount to count every table
        
        
if tCount = 0 then
          display notification "There is no Table"
          
return false
          
        else if tCount > 1 then
          set tList to name of every table
          
          
set tRes to choose from list tList with prompt "Chose target Table"
          
if tRes = false then return false
          
set tName to first item of tRes
        else
          set tName to name of table 1
        end if
        
        
return tName
        
      end tell
    end tell
  end tell
end getTargetTableName

–2D ListをTransposeする
on transpose2DList(aList as list)
  load framework
  
return (ASify from (current application’s SMSForder’s colsToRowsIn:aList |error|:(missing value)))
end transpose2DList

—-リストに対して、一般的な配列添字っぽくアクセスするサブルーチン(ただし、要素は1はじまり)

on getItemByXY(aX, aY, aList, aBlankItem)
  try
    set aContents to contents of (item aX of item aY of aList)
  on error
    set aContents to aBlankItem
  end try
  
return aContents
end getItemByXY

on setItemByXY(aX, aY, aList, aContents)
  set (item aX of item aY of aList) to aContents
  
return aList
end setItemByXY

–空白の2D Array を出力する
on make2DBlankArray(curLen, curMax)
  set outArray to {}
  
repeat curMax times
    set aList to {}
    
repeat curLen times
      set the end of aList to ""
    end repeat
    
set the end of outArray to aList
  end repeat
  
return outArray
end make2DBlankArray

–配列のサイズを数えるサブルーチン

–2D Listの要素数カウント
on countElementsOf2DList(aList)
  set aDim to getDimension given tArray:aList
  
if aDim is not equal to 2 then return false
  
  
set aHeight to count every item of aList
  
set aWidth to count every item of item 1 of aList
  
return {xWidth:aWidth, yHeight:aHeight}
end countElementsOf2DList

–指定Listの次元を再帰で取得する
on getDimension given tArray:aList as list : {}, curDim:aNum as integer : 1
  
  
set anItem to contents of first item of aList
  
set aClass to class of anItem
  
  
if aClass = list then
    set aNum to aNum + 1
    
set aRes to getDimension given tArray:anItem, curDim:aNum
  else
    return aNum
  end if
  
end getDimension

★Click Here to Open This Script 

Posted in list | Tagged 10.12savvy 10.13savvy 10.14savvy Keynote | Leave a comment

選択中のリストのテキスト(多分)をもとにKeynoteの表を作成 v2

Posted on 8月 1, 2019 by Takaaki Naganoya

Script Editor上で選択中の1D List(1次元配列)のテキストを評価してリストとして解釈し、それをもとにKeynoteの新規書類上に表を作成するAppleScriptの改良版です。

こんな感じ(↑)に、Script Editor上で1D List(1次元配列)のテキストが存在しているものを、資料化するためにKeynote上で表にすることがあり、その作業を自動化してみました。

スクリプトエディタ上の選択範囲のテキストをrun scriptコマンドで実際に実行(Evalのような使い方)して本物のAppleScriptのリストを作成して処理します。

ここがたいへんにセキュリティホールになりやすいので、実行前にテキストをAppleScriptとして構文確認を行い、危ない構文要素(コマンド類)が入っていないか、AppleScriptの構文的に中途半端でないかチェックを行います。

技術的にはMac OS X 10.4ないし10.5の時代に確立していた内容ではありますが、当時はスクリプトエディタをコントロールして構文色分け情報を取得していたので、macOS 10.10以降でCocoaの機能がAppleScriptから直接利用できるようになって、よりスマートに処理できるようになりました。

Mac OS X 10.4の時代には、plistの情報はテキストで記録されていたので、構文色分け設定を読むのは簡単でした。スクリプトエディタから書式つきテキスト情報(attribute runs)を取得して付け合わせを行っていました。途中でplistの内容がバイナリ化されてplistを読むことが難しくなったので、新規書類に「すべての構文要素が入った最低限度のAppleScript」を転送して構文確認を行い、attribute runsを取得して規定の場所の書式情報を取得することで、特定の構文要素の色分け情報を取得していました(一瞬で新規書類を作成して構文確認し、すぐに破棄するので目には見えない)。

–> Download selectedStrToKeynoteTable (Source AppleScript Bundle with Library)

–> Download selectedStrToKeynoteTableWithCodeSign(AppleScript Applet executable with Code Sign)

ただし、本ScriptをmacOS 10.14上で実行してみていろいろ問題を感じました。まずは、スクリプトエディタ/Script Debugger上で実行する分には何も問題はありません。ここは大事なので明記しておきます。

これ以外の実行環境で実行させると、とたんに首をひねりたくなるような挙動が見られます。Script Menuから実行することを前提に作ってみたわけですが………

普通にScriptとして保存して実行(署名なし、公証なし):初回実行時にオートメーション認証ダイアログが表示されて実行。数回同じScriptを実行すると実行されなくなる(!)

Appletとして保存して実行(署名なし、公証なし):実行時、毎回オートメーション認証ダイアログが表示されて実行。

Appletとして保存して実行(署名あり、公証なし):初回実行時、オートメーション認証ダイアログが表示されて実行。連続して実行するとダイアログ表示なし。しばらく時間を置いて実行するとダイアログ表示される。

macOS 10.14については2か月前から使い始めたので、公証についてもまだうまく行えていません(SD Nortaryで開発者IDが認識されない、、、)。こちら(公証)も試してみるべきなんでしょう。

公証関連については、実際に行っている方、失敗した方の意見をまとめておきたいので、本Blog併設のフォーラムにて(言語は英語でも何語でもOKなので)情報交換させてください。

AppleScript名:選択中のリストのテキスト(多分)をもとにKeynoteの表を作成 v2
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/31
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use dangerAS : script "checkDangerAS"

set aTargMasterSlide to "空白" –This string is *Localized* in Japanese. This is "Blank"

tell application "Script Editor"
  tell front document
    set bText to contents of selection
  end tell
end tell

if bText = "" then return

–指定のテキストをAppleScriptとして評価し、指定の構文要素に該当するものが含まれているかどうかチェック
set dRes to chkTargLexicalElementsFromCompiledScriptAttribute(bText, {9, 14}) of dangerAS
if dRes = missing value then
  –The selected text contains AppleScript lexical error
  
display dialog "選択中のテキストにAppleScriptの構文エラーが検出されました" default answer bText with title "Error" with icon 2
  
return
end if

if dRes = true then
  –The selected text contain danger AppleScript lexical element (maybe it is any command)
  
display dialog "選択中のテキストは実行に危険な文字列(AppleScriptコマンド)を含んでいるため、処理しませんでした" default answer bText with title "Error" with icon 2
  
return
end if

try
  set aRes to run script bText –Danger!! Take Care of.
  
set aClass to class of aRes
on error
  –The Evaluation error (some AppleScript lexical error)
  
display dialog "The contents of clipboard seems not to be an AppleScript list but " & (aClass as string) & return & bText with title "Error (1)" with icon 2
  
return
end try

if aClass is not equal to list then
  –The Evaluated result is not list
  
display dialog "The contents of clipboard seems not to be an AppleScript list but " & (aClass as string) & return & bText with title "Error (2)" with icon 2
  
return
end if

set aLen to length of aRes
set aDim to getDimension given tArray:aRes
if aDim > 1 then
  –Check List’s dimension (the taeget dimension is 1)
  
display dialog "選択中のテキストを評価したリスト(配列)の次元数が1ではなかったので、処理しませんでした" default answer bText with title "Dimension Error(3)" with icon 2
  
return
end if

tell application "Keynote"
  activate
  
set aDoc to (make new document with properties {document theme:theme "ホワイト", width:1024, height:768}) — –This string is *Localized* in Japanese. This is "White"
  
  
tell aDoc
    set masList to name of every master slide
    
if aTargMasterSlide is not in masList then return –多分ないと思うが、作成予定のマスタースライドが現在有効でない場合に処理打ち切り
    
    
set base slide of current slide to master slide aTargMasterSlide
    
    
tell current slide
      set aTable to make new table with properties {header column count:0, header row count:1, row count:2, column count:aLen, name:"Test Table"}
      
      
tell aTable
        repeat with y from 1 to 1
          tell row y
            repeat with x from 1 to aLen
              tell cell x
                set value to (item x of aRes) as string
              end tell
            end repeat
          end tell
        end repeat
      end tell
      
    end tell
  end tell
end tell

–指定Listの次元を再帰で取得する
on getDimension given tArray:aList as list : {}, curDim:aNum as integer : 1
  set anItem to contents of first item of aList
  
set aClass to class of anItem
  
  
if aClass = list then
    set aNum to aNum + 1
    
set aRes to getDimension given tArray:anItem, curDim:aNum
  else
    return aNum
  end if
end getDimension

★Click Here to Open This Script 

Posted in Code Sign Color list Notarization OSA regexp | Tagged 10.12savvy 10.13savvy 10.14savvy Keynote Script Editor | Leave a comment

アイテム番号リストをもとに、ヒットしなかった項目を返す

Posted on 7月 31, 2019 by Takaaki Naganoya

アイテム番号の入ったリスト(配列変数)をもとに、ヒットしなかった項目を返すというAppleScriptです。

これは、なにがしかのデータとなにがしかの書類上に配置されたデータの照合を行って、存在が確認されなかったデータの一覧を作成するために作成したものです。

存在確認といえば、たいていは「存在していたデータそのもの、あるいはデータのアイテム番号一覧」といったものを出力するようになっています。そこに、「存在が確認できなかったデータの一覧が欲しい」という要望が出てくることも、だいたいは普通の出来事です。

ただ、この手の「言語化すると大したことはないが、実施に精神的な抵抗感をおぼえる処理」といいいますか、「後片付け的な処理」というのは、書くのが面倒で後回しになりがちなものでもあります。

事実、実際に書いてみたら、、、既存のサブルーチンの組み合わせで作りましたが、1日たって見直してみると何が書いてあるのかよく思い出せないレベルです(既存のサブルーチンの組み合わせが過ぎて、意味がわかりにくいのかも)。

AppleScript名:アイテム番号リストをもとに、ヒットしなかった項目を返す.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/29
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

property NSArray : a reference to current application’s NSArray
property NSMutableSet : a reference to current application’s NSMutableSet
property NSMutableArray : a reference to current application’s NSMutableArray

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set strList to {"Apple", "Book", "Cheeze", "Dictionary", "Escalgo", "Find", "Gorila", "hook", "ink", "jet"}
set hitIDList to {1, 2, 3, 4, 6}

set bList to getUnpickedContents(hitIDList, strList) of me
–> {"Escalgo", "Gorila", "hook", "ink", "jet"}

on getUnpickedContents(aList, strList)
  set aMax to length of strList
  
set bList to getSequentialNumList(aMax) of me
  
set cList to excludeTwoList(bList, aList) of me
  
  
set anArray to NSMutableArray’s arrayWithArray:(cList)
  
set dList to (anArray’s sortedArrayUsingSelector:"compare:") as list
  
  
set eList to retConListFromItemNumList(dList, strList) of me
  
return eList
end getUnpickedContents

–アイテム項目番号が入っているリストをもとに、中身が入っているリストから項目を取り出す
on retConListFromItemNumList(dList, strList)
  set outList to {}
  
repeat with i in dList
    set aTmp to item (contents of i) of strList
    
set the end of outList to aTmp
  end repeat
  
return outList
end retConListFromItemNumList

on excludeTwoList(aList, bList)
  set aArray to NSArray’s arrayWithArray:(aList)
  
set bArray to NSArray’s arrayWithArray:(bList)
  
  
set aSet to NSMutableSet’s alloc()’s initWithArray:aArray
  
set bSet to NSMutableSet’s alloc()’s initWithArray:bArray
  
  
aSet’s minusSet:bSet –補集合
  
set resList to aSet’s allObjects() as list
  
  
return resList
end excludeTwoList

on getSequentialNumList(aMax)
  set outList to {}
  
repeat with i from 1 to aMax
    set the end of outList to i
  end repeat
  
return outList
end getSequentialNumList

★Click Here to Open This Script 

Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSArray NSMutableArray NSMutableSet | Leave a comment

選択中のリストのテキスト(多分)をもとにKeynoteの表を作成

Posted on 7月 31, 2019 by Takaaki Naganoya

Script Editor上で選択中の1D List(1次元配列)のテキストを評価してリストとして解釈し、それをもとにKeynoteの新規書類上に表を作成するAppleScriptです。

こんな感じ(↑)に、Script Editor上で1D List(1次元配列)のテキストが存在しているものを、資料化するためにKeynote上で表にすることがありますが、これが個人的に超絶かったるいです。

きれいにデータになっているものを、表のセルに細切れにして突っ込む作業がかったるいので、おそらくそれを手動で行う数倍の時間をかけてAppleScriptで自動化しておきました。

(1)Script Editor上でListの箇所を選択しておく
(2)Script Menuに入れておいた本Scriptを呼び出す
(3)Keynoteで新規書類を作成し、1ページ目に表を新規作成し、(1)の内容を1行目に代入

という動作を行います。上記のとおり、Script Menuに入れて呼び出すことを前提にしています。もし、そうでなければ当該部分をコピーしておいて、クリップボード経由で受け取るようにしてみてください。

当初は本Scriptもそういう構造になっていましたが、選択部分をコピーするのを忘れることが多いため、選択箇所から取得するように変更しました。

本Scriptは自分自身で使うことを前提に作ったため(本Blogまるごとそんなもんですが)、run scriptコマンドで文字列をAppleScriptとして評価して実行して結果を取得するという、ひじょーーーーーにセキュリティホールになりやすい処理を行っています。

本来、取得した文字列をAppleScriptとして評価して、構文要素的に「コマンド類」が入っていないか(とくにdo shell script)を評価する必要があると思います。その上で、もしもコマンド類が入っていた場合にはユーザーに再考を促すようにダイアログを表示するなどの処理を行うのがまっとうなやりかたでしょう。

このあたり、ものすごくラフに作ったので、利用はあくまで自己責任で行ってください(本Blogまるごとそんなもんですが)。

AppleScript名:選択中のリストのテキスト(多分)をもとにKeynoteの表を作成
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/31
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aTargMasterSlide to "空白" –This string is *Localized* in Japanese. This is "Blank"

tell application "Script Editor"
  tell front document
    set bText to contents of selection
  end tell
end tell

if bText = "" then return

try
  set aRes to run script bText –Danger!! Take Care of.
  
set aClass to class of aRes
on error
  display dialog "The contents of clipboard seems not to be an AppleScript list (1)." with title "Error"
  
return
end try

if aClass is not equal to list then
  display dialog "The contents of clipboard seems not to be an AppleScript list (2)." with title "Error"
  
return
end if

set aLen to length of aRes

tell application "Keynote"
  activate
  
set aDoc to (make new document with properties {document theme:theme "ホワイト", width:1024, height:768}) — –This string is *Localized* in Japanese. This is "White"
  
  
tell aDoc
    set masList to name of every master slide
    
if aTargMasterSlide is not in masList then return –多分ないと思うが、作成予定のマスタースライドが現在有効でない場合に処理打ち切り
    
    
set base slide of current slide to master slide aTargMasterSlide
    
    
tell current slide
      set aTable to make new table with properties {header column count:0, header row count:1, row count:2, column count:aLen, name:"Test Table"}
      
      
tell aTable
        repeat with y from 1 to 1
          tell row y
            repeat with x from 1 to aLen
              tell cell x
                set value to (item x of aRes) as string
              end tell
            end repeat
          end tell
        end repeat
      end tell
      
    end tell
  end tell
end tell

★Click Here to Open This Script 

Posted in list | Tagged 10.12savvy 10.13savvy 10.14savvy Keynote Script Editor | Leave a comment

クリップボードに入れたIllustratorのオブジェクトをQRコード認識 v2

Posted on 7月 28, 2019 by Takaaki Naganoya

Adobe Illustrator書類上にある、QRコードのオブジェクトをクリップボード経由でNSImageに変換してQRコード認識するAppleScriptです。QRコード認識には、OS内蔵のCoreImageの機能を使っています。

# 実行前にAI書類上のQRコードオブジェクトを選択してコピー(Command-C)を実行してあることを前提条件としています。QRコードっぽいものであれば、別にAI書類上のオブジェクトでなくてもかまいません

昨日のScriptで、「そういえばなんでZXingObjCを使っているんだっけ?」と疑問を抱きました。理由ははっきりしています。CIDetectorではそのまま認識させてみたら認識しなかったためです。

QRコードやJANコード画像認識ソフトウェアは、割といろいろ条件がそろわないと認識してくれません。いわく、画像の大きさ、画像の解像度、画像の向き、コントラスト比、その他いろいろ。

スマホのカメラで認識する際にはユーザーが認識するまでいろいろ条件を変えて試行錯誤するので、ソフトウェアの力だけで認識させているわけではありません。いわば、人間をスマホの周辺機器として使用している状態です。

一方、静止画をソフトウェアで認識処理するさいには、人間が行っているフィードバック動作をソフトウェア側で行わなくてはなりません。

macOS内蔵のCIDetectorは、どうやら画像サイズに敏感なようで、いろいろリサイズして認識するかどうかチェックしなくてはなりません。逆に、最初からQRコードではないとわかっている画像を、しつこくQRコード認識し直しても時間の無駄です。

とりあえず、認識対象を2倍から4倍まで拡大して認識をリトライしています。実際にやってみたら、1倍では認識しなくても2倍で認識してくれました。実際に処理するデータ次第ですが、4倍まで試さなくても大丈夫なケースも多いことでしょう。

AppleScript名:rocogClipAsQR_v2.scptd
— Created 2019-07-28 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "CoreImage"

property CIImage : a reference to current application’s CIImage
property NSImage : a reference to current application’s NSImage
property CIDetector : a reference to current application’s CIDetector
property NSZeroRect : a reference to current application’s NSZeroRect
property NSDictionary : a reference to current application’s NSDictionary
property NSPasteboard : a reference to current application’s NSPasteboard
property NSCompositeCopy : a reference to current application’s NSCompositeCopy
property NSGraphicsContext : a reference to current application’s NSGraphicsContext
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property CIDetectorAccuracy : a reference to current application’s CIDetectorAccuracy
property CIDetectorTypeQRCode : a reference to current application’s CIDetectorTypeQRCode
property CIDetectorAccuracyHigh : a reference to current application’s CIDetectorAccuracyHigh
property NSImageInterpolationNone : a reference to current application’s NSImageInterpolationNone
property CIDetectorImageOrientation : a reference to current application’s CIDetectorImageOrientation
property NSCalibratedRGBColorSpace : a reference to current application’s NSCalibratedRGBColorSpace

set aRes to recogPasteboardASQR() of me

–クリップボードの内容をNSImageに
on recogPasteboardASQR()
  set aNSIMage to getClipboardASImage() of me
  
if aNSIMage = false then return false
  
  
set aRes to recogNSImageAsQRcodes(aNSIMage) of me –11: kBarcodeFormatQRCode
  
if aRes is not equal to {} then return aRes
  
  
–ループでQRコードとおぼしきイメージを拡大しつつQRコード認識
  
repeat with i from 2 to 4 –ここはチューニングの余地がある
    set bNSIMage to (my resizeNSImageWithoutAntlialias:aNSIMage toScale:(i as real))
    
set aRes to recogNSImageAsQRcodes(bNSIMage) of me
    
if aRes is not equal to {} then return aRes
  end repeat
  
  
return false
end recogPasteboardASQR

–NSImageをバーコードとして認識する
on recogNSImageAsQRcodes(aNSIMage)
  set imageRef to convNSImageToCIimage(aNSIMage) of me
  
  
— 検出器のオプションを NSDictonary で作成
  
set optDic1 to NSDictionary’s dictionaryWithObject:(CIDetectorAccuracyHigh) forKey:(CIDetectorAccuracy)
  
set faceDetector to CIDetector’s detectorOfType:(CIDetectorTypeQRCode) context:(missing value) options:optDic1
  
  
— QRコードの検出を行う際のオプションを NSDictonary で作成
  
set optDic2 to NSDictionary’s dictionaryWithObject:(CIDetectorImageOrientation) forKey:"Orientation"
  
  
— QRコード検出を実行
  
set faceArray to faceDetector’s featuresInImage:imageRef options:optDic2
  
set fList to {}
  
  
— 検出されたQRコードの位置とサイズをログに出力
  
repeat with i from 1 to (count of faceArray)
    set face to item i of faceArray
    
set bRec to (face’s messageString()) as string
    
–set cRec to retURLdecodedStrings(bRec) of me –URLエンコード対策
    
set the end of fList to bRec
  end repeat
  
  
return fList
end recogNSImageAsQRcodes

— クリップボードの内容をNSImageとして取り出して返す
on getClipboardASImage()
  set theNSPasteboard to NSPasteboard’s generalPasteboard()
  
set anArray to theNSPasteboard’s readObjectsForClasses:({NSImage}) options:(missing value)
  
if anArray = missing value or (anArray as list) = {} then return false
  
set aRes to anArray’s objectAtIndex:0
  
return aRes
end getClipboardASImage

—
on convNSImageToCIimage(aNSIMage)
  set tiffDat to aNSIMage’s TIFFRepresentation()
  
set aRep to NSBitmapImageRep’s imageRepWithData:tiffDat
  
set newImg to CIImage’s alloc()’s initWithBitmapImageRep:aRep
  
return newImg
end convNSImageToCIimage

–NSImageを指定倍率で拡大(アンチエイリアス解除状態で)
on resizeNSImageWithoutAntlialias:aSourceImg toScale:imgScale
  set aSize to aSourceImg’s |size|()
  
set aWidth to (aSize’s width) * imgScale
  
set aHeight to (aSize’s height) * imgScale
  
  
set aRep to NSBitmapImageRep’s alloc()’s initWithBitmapDataPlanes:(missing value) pixelsWide:aWidth pixelsHigh:aHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:true isPlanar:false colorSpaceName:(NSCalibratedRGBColorSpace) bytesPerRow:0 bitsPerPixel:0
  
  
set newSize to {width:aWidth, height:aHeight}
  
aRep’s setSize:newSize
  
  
NSGraphicsContext’s saveGraphicsState()
  
  
set theContext to NSGraphicsContext’s graphicsContextWithBitmapImageRep:aRep
  
NSGraphicsContext’s setCurrentContext:theContext
  
theContext’s setShouldAntialias:false
  
theContext’s setImageInterpolation:(NSImageInterpolationNone)
  
  
aSourceImg’s drawInRect:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) fromRect:(NSZeroRect) operation:(NSCompositeCopy) fraction:(1.0)
  
  
NSGraphicsContext’s restoreGraphicsState()
  
  
set newImg to NSImage’s alloc()’s initWithSize:newSize
  
newImg’s addRepresentation:aRep
  
  
return newImg
end resizeNSImageWithoutAntlialias:toScale:

★Click Here to Open This Script 

Posted in Clipboard Image list QR Code | Tagged 10.12savvy 10.13savvy 10.14savvy CIDetector CIDetectorAccuracy CIDetectorAccuracyHigh CIDetectorImageOrientation CIDetectorTypeQRCode CIImage NSBitmapImageRep NSCalibratedRGBColorSpace NSCompositeCopy NSDictionary NSGraphicsContext NSImage NSImageInterpolationNone NSPasteboard NSZeroRect | 4 Comments

NumbersのColumn Adr(26進数)と10進数との相互変換

Posted on 7月 22, 2019 by Takaaki Naganoya

Numbersの表のカラムを表現するアドレス文字列(26進数)と数値の間のエンコーダーおよびデコーダーのAppleScriptです。

もともとは、Excel 2004/2008で採用されたカラムアドレス形式に対処するためのScriptでした。

Excel上のセルのアドレス(場所)を指し示す方法は、「R1C1」(行とカラムを数値指定)形式、あるいはAppleScriptの行オブジェクトとセルオブジェクトで指定する形式でした。

そこへ新たに「A1」形式……画面上で表記されている行、カラムと親和性の高い形式が採用されることになりました。当初は相互変換のための関数なども用意されていなかったと記憶しています(間違っていたらご指摘ください)。そのため、Scripterが自力でこのA1形式に対応する必要が出てきました。

そんな中、Excel書類にAppleScriptを埋め込んで実行するAppleScriptを開発。本ルーチンはそのScript開発のために作成したものです。

Excel 2008でVBAの処理系が外されてMac上のVBA的なマクロ処理はAppleSctiptに一本化されるという話になっていたため(当時の話)、これを好機ととらえ、マイクロソフトのご担当にデモして、US本社で紹介していただくということになりました。

ただ、その会議上でVBAの復活プランが発表され、自分の提案したプランは廃案に(まさか当時のREALbasicのコンパイラの開発者を引き抜いてきてVBAの処理系をスクラッチで書かせるとは思いませんでしたわー)。

その後は、Excel 2011でVBAの処理系が復活。本ルーチンも割とHDDの肥やしとして絶賛在庫状態になっておりました。ごくたまーに、このExcel 2011でVBAの処理系が復活したことを知らない方がいて、「VBAで動いているマクロをAppleScriptに移植してほしい」という問い合わせがあるのですが、「Excelの最新版を購入してください。VBAがありますよ」とお返事しています。最新のExcelが動く環境を購入する費用よりも安く仕事としてお受けすることは困難なので。

そこから年月が流れ、AppleがNumbersをリリース。そのカラム表記がExcel 2004/2008と同じ形式になっているために、しまいこんでいたルーチンをふたたび引っ張り出してきた次第です。

一応、条件つきで使えるものの、作りが古い(やっつけ仕事な)点が気になります。きっと、誰かがもっといいルーチンを作って使っているに違いありません。一応、本ルーチンでは1〜1351の範囲での動作を確認しています。

もう少し改良したいところではあります。実用上は、Numbersでそこまで大きなデータは扱わない(はず)ので、問題はあまりないものと思われます。

AppleScript名:NumbersのColumn Adr(26進数)と10進数との相互変換
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/21
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property NSString : a reference to current application’s NSString
property NSArray : a reference to current application’s NSArray
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch

repeat with i from 1 to 1351
  set a to numAdrToColumnEncode(i) of me
  
set b to colAddrToNumDecode(a) of me
  
set aRes to (i = b) as boolean
  
log {i, b, a, aRes}
  
if aRes = false then display dialog i as string
end repeat

–10進数数値をExcel 2004/2008的カラム表現にエンコードするサブルーチン(エンコード範囲:1〜1351)
on numAdrToColumnEncode(origNum)
  if origNum > 1351 then
    error "エラー:Numbersのカラム表現(A1形式)への変換ルーチンにおいて、想定範囲外(1351以上)のパラメータが指定されました"
  end if
  
  
set upperDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
set lowerDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
  
set oNum to origNum
  
set nTh to 26
  
set stringLength to 4
  
  
–数字が1桁の場合の対応
  
if origNum < 27 then
    set aRes to (item origNum of upperDigitEncTable) as string
    
return aRes
  end if
  
  
  
if origNum > 702 then
    –3桁になる場合
    
set upupNum to oNum div 676 –整数除算–上の上の桁
    
set oNum to oNum – (upupNum * 676)
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–log {origNum, upupNum, upNum, lowNum}
    
    
–超つじつま合わせルーチン【強引】
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upupChar to (item upupNum of upperDigitEncTable) as string
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upupChar & upChar & lowChar
    
  else
    –2桁の場合
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
    
–超つじつま合わせルーチン【強引】
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upChar & lowChar
    
  end if
  
  
return resText
  
end numAdrToColumnEncode

–Numbersの横方向アドレス(A〜Zの26進数)文字列を10進数に変換
on colAddrToNumDecode(origStr)
  return aNthToDecimal(origStr, {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}) of me
end colAddrToNumDecode

–n進数文字列を10進数に変換する
on aNthToDecimal(origStr, nTh)
  set resNumber to 0
  
  
set sList to reverse of (characters of origStr)
  
set aLen to length of nTh
  
set digitCount to 0
  
  
repeat with i in sList
    set j to contents of i
    
set aRes to offsetInList(j, nTh) of me
    
    
set resNumber to resNumber + (aLen ^ digitCount) * aRes
    
    
set digitCount to digitCount + 1
  end repeat
  
  
return resNumber as integer
end aNthToDecimal

on offsetInList(aChar, aList)
  set anArray to NSArray’s arrayWithArray:aList
  
set aInd to (anArray’s indexOfObject:aChar)
  
if aInd = current application’s NSNotFound or (aInd as number) > 9.99999999E+8 then
    error "Invalid Character Error"
  else
    return (aInd as integer) + 1 –0 to 1 based index conversion
  end if
end offsetInList

★Click Here to Open This Script 

Posted in list Number | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSRegularExpressionSearch NSString Numbers | Leave a comment

アラートダイアログ上にTable Viewを表示 v4

Posted on 7月 11, 2019 by Takaaki Naganoya

アラートダイアログ上にTable Viewを表示するAppleScriptです。

データを表UIで表示する部品としては、Shane Stanleyの「Myriad Tables Lib」が定番ですが、高機能なぶんライブラリ中にFrameworkが含まれていたりして、場合によっては困ることもあります。また、見た目をカスタマイズしたい場合にも、あまりいじくれなくて困ることもあります(とくにデータの文字サイズを変更できなくて困ること多し)。

そのため、AppleScriptだけで書いた表UI表示用の部品も、たまに必要になることがあります。


▲スクリプトエディタで実行@macOS 10.14.5(左:Light Mode、右:Dark Mode)


▲Script Debuggerで実行@macOS 10.14.5(左:Light Mode、右:Dark Mode)


▲AppleScriptアプレットで実行@macOS 10.14.5(左:Light Mode、右:Dark Mode)

AppleScript名:アラートダイアログ上にTable Viewを表示 v4
— Created 2019-02-21 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 NSColor : a reference to current application’s NSColor
property NSIndexSet : a reference to current application’s NSIndexSet
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 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 NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn

property theResult : 0
property returnCode : 0
property theDataSource : {}

on run
  set (my theResult) to 0 –initialize
  
  
set paramObj to {myMessage:"項目の選択", mySubMessage:"適切なものを以下からえらんでください", aTableList:{{field1:"MacBook Air", field2:"119,800円", field3:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}, {field1:"MacBook Pro 13", field2:"139,800円", field3:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}, {field1:"MacBook Pro 15", field2:"258,800円", field3:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}, {field1:"Mac mini", field2:"122,800円", field3:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}, {field1:"iMac 21.5", field2:"120,800円", field3:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}, {field1:"iMac 4K 21.5", field2:"142,800円", field3:"3.6GHzクアッドコアIntel Core i3"}, {field1:"iMac 5K 27", field2:"198,800円", field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}, {field1:"iMac Pro", field2:"558,800円", field3:"3.2GHz Intel Xeon W Turbo Boost使用時最大4.2GHz 19MBキャッシュ"}}, aSortOrder:{"field1", "field2", "field3"}}
  
  
–my chooseItemByTableView:paramObj–for debug
  
my performSelectorOnMainThread:"chooseItemByTableView:" withObject:paramObj waitUntilDone:true
  
return (my theResult)
end run

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 600
  
set aHeight to 200
  
  
set aScroll to makeTableView(aTList, aWidth, aHeight, labelSortList) of me
  
  
–Detect Dark Mode
  
set dMode to retLIghtOrDark() of me
  
if dMode = true then
    set tvCol to 1
    
set tvAlpha to 0.8
    
set bCol to 0.1
    
set bAlpha to 0.8
    
set contentCol to (NSColor’s cyanColor())
  else
    set tvCol to 0
    
set tvAlpha to 0.1
    
set bCol to 1
    
set bAlpha to 0.8
    
set contentCol to (NSColor’s blackColor())
  end if
  
  
— 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:aScroll
    
    
set myWindow to its |window|
  end tell
  
  
myWindow’s setOpaque:(false)
  
myWindow’s setBackgroundColor:(NSColor’s colorWithCalibratedWhite:(bCol) alpha:(bAlpha))
  
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 (my theResult) to (aScroll’s documentView’s selectedRow()) + 1
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 current application’s NSMutableArray’s alloc()’s init()
  
theDataSource’s addObjectsFromArray:aDicList
  
  
set aScroll to current application’s NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
  
set aView to current application’s 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 (current application’s NSTableColumn’s alloc()’s initWithIdentifier:j)
    (
aColumn’s setWidth:(aWidth div aLen))
    (
aColumn’s headerCell()’s setStringValue:j)
    (
aView’s addTableColumn:aColumn)
  end repeat
  
  
aView’s setDelegate:me
  
aView’s setDataSource:me
  
aView’s reloadData()
  
  
aScroll’s setDocumentView:aView
  
aView’s enclosingScrollView()’s setHasVerticalScroller:true
  
aScroll’s setVerticalLineScroll:(30.0 as real)
  
  
–1行目を選択
  
set aIndexSet to current application’s NSIndexSet’s indexSetWithIndex:0
  
aView’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:

–ダークモードの判定。ダークモード時:true、ライトモード時:falseが返る。System Eventsを使っていないのは、macOS 10.14以降対策(承認を取得しなくてもいいように)
on retLIghtOrDark()
  set curMode to (current application’s NSUserDefaults’s standardUserDefaults()’s stringForKey:"AppleInterfaceStyle") as string
  
return (curMode = "Dark") as boolean
end retLIghtOrDark

★Click Here to Open This Script 

Posted in Color GUI list Record | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAlert NSAlertSecondButtonReturn NSColor NSIndexSet NSModalPanelWindowLevel NSMutableArray NSRunningApplication NSScrollView NSTableColumn NSTableView | 1 Comment

構文色分け情報を取得(Color Spaceを考慮)

Posted on 7月 4, 2019 by Takaaki Naganoya

macOS標準搭載のスクリプトエディタで定義している、AppleScriptの構文要素ごとの色分け情報。これを設定ファイルから取り出してレコード型データ(を入れた配列)に変換するAppleScriptの、カラースペースを考慮したデータの取り出しを行う改修版です。

AppleScriptの構文色分け情報は、ユーザーごとに異なります。一応、OSデフォルトの趣味の悪い配色なんかもありますが、

 (1)美的センスがなく見た目に気持ち悪い(ほら変更したいでしょ? 自分の好きなように変えてね、というメッセージだと受け止めました)

 (2)重複した色が設定されている構文要素がたくさんある(構文要素ごとの見分けがしにくいので、デフォルトのままだと無意味。ほら変更したくなったでしょ、というメッセージだと受け止めました)

ということから、AppleScript入門初日に設定色を変更。何度か細かい変更を行いつつも、今日までだいたい同じ感じの設定を使い続けています。

他のScripterがどのような配色にしているかはあまり知りません。Scripterごとに違うんだろうな、ぐらいの感じです。

ところが、蓋を開けてみたらこの構文色分け設定に「RGB以外の色」が設定できることが判明。けっこう長い間、この処理系使い続けてきましたけれど、昨日はじめて知りました。

具体的に言えば、RGB以外の「CMYK色」と「グレースケール色」の2つを設定可能なことが判明。CMYK色は、何度かアクセスしているうちに勝手にRGB色に置き換わってしまうようですが、グレースケールで指定した色はそのまま色種別がグレースケールのまま保持される(RGBに変換されたりしない)ようです。そのため、NSColorからredValue()などのメソッドで数値を取り出そうとするとエラーになる事例が発生したのでした。

そこで、RGBの各チャンネルの値を取り出す前にNSColorの色空間(グレースケール色空間というのはないので、種別ぐらい?)をチェックし、RGB以外であれば変換処理を行うようにしてみました。

ただし、この色空間(色モデル)の確認にNSColorのcolorSpaceNameというメソッドを使っている点に注意が必要です。これはmacOS 10.13で非推奨(deprecated)になっています。ヘッダーファイル上の説明を読むかぎりだと、Color Space(色空間)といいながら実際には色空間に該当する(HSBとかL*a*b*とか)ものを指定しているわけではないので、色タイプと言い換えることにした、という説明が行われていました。

このため、macOS 10.15betaでは未確認ではあるものの、このNSColorからRGB値を取り出すという処理自体を書き換える必要が出てくるため、本ルーチンだけ別のライブラリに分離しておくとか、メンテナンス時にわかるように書いておくとよいでしょう(NSColorのtypeを取得してみたものの、いまひとつサンプルなどもなく不明。macOS 10.14上で全部一律で0が返ってくるし)。

AppleにOS内部の機能修正リクエストを出す場合、「実際に機能不全を起こしているんだから直してほしい」と依頼してもなかなか直らない一方で、「学術的に正しくない」という指摘をされるととっとと変更するんだなーという発見をしたような気がします。

AppleScript名:構文色分け情報を取得(Color Spaceを考慮).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/04
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSDictionary : a reference to current application’s NSDictionary
property NSUnarchiver : a reference to current application’s NSUnarchiver

set cList to getAppleScriptSourceColors() of me

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceColors()
  — get the plist info as a dictionary
  
set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist"
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
— extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list
  
  
set colList to {}
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me
    
    
set fontData to NSFont of anEntry
    
set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize}
    
    
set the end of colList to aColRec
  end repeat
  
  
return colList
end getAppleScriptSourceColors

–NSColorからRGBの値を取り出す
on retColListFromNSColor(aCol, aMAX as integer)
  set aSpace to (aCol’s colorSpaceName()) as string
  
  
if aSpace is in {"NSDeviceRGBColorSpace", "NSCalibratedRGBColorSpace"} then
    –do nothing
  else
    –RGB以外の色空間の色は、RGBに変換
    
–CMYKとグレースケールは同一メソッドでRGBに変換できることを確認済み
    
set cCol to aCol’s colorUsingColorSpaceName:"NSCalibratedRGBColorSpace"
    
if cCol = missing value then error "Color Space Conversion Error (" & aSpace & " to NSCalibratedRGBColorSpace)"
    
copy cCol to aCol
  end if
  
  
set aRed to round ((aCol’s redComponent()) * aMAX) rounding as taught in school
  
set aGreen to round ((aCol’s greenComponent()) * aMAX) rounding as taught in school
  
set aBlue to round ((aCol’s blueComponent()) * aMAX) rounding as taught in school
  
  
if aRed > aMAX then set aRed to aMAX
  
if aGreen > aMAX then set aGreen to aMAX
  
if aBlue > aMAX then set aBlue to aMAX
  
  
return {aRed, aGreen, aBlue}
end retColListFromNSColor

★Click Here to Open This Script 

Posted in Color list Record System | Tagged 10.12savvy 10.13savvy 10.14savvy NSArray NSColor NSDictionary NSString NSUnarchiver | Leave a comment

sd checker

Posted on 6月 30, 2019 by Takaaki Naganoya

Script Debuggerを操作して、オープン中のScriptのうちのいずれかから、選択してScriptからリンクしているFrameworkやライブラリが指定のScriptバンドル内に存在しているかどうかチェックするAppleScriptです。

AppleScript appletの納品時にライブラリやFrameworkをすべてバンドル内に入れておいたはずなのに、納品先の環境でFrameworkやライブラリが入っていないエラーに直面したことは、実はけっこうあります。とくにFrameworkです。

つまり、バンドルに収めたFrameworkやライブラリがリンクされていれば問題ないものの、~/Library/Frameworksや~/Library/Script Librariesフォルダ内のファイルが指し示されている場合には、他の環境に持って行ったときに実行できません。

その割に、どのFrameworkがバンドル外ファイルにリンクされているのかといったチェックを(Apple純正のスクリプトエディタでは)明示的に行う手段がなく、一時的に開発環境内の当該Frameworkを削除してアプリケーション書き出しして様子を見るといったチェック作業を行っていました。

AppleScriptのバンドル内にFrameworkを突っ込む機能がScript Debugger上に用意されていないため手作業で行っており、たまーに突っ込むフォルダの階層を間違えてしまうんですね(涙)。

自分のことながら、この作業自体がひたすら間抜けだったので、プログラムでチェックだけでも行えるようにしてみたものがこのAppleScriptです。

本Scriptは1つのScriptをそのままチェックするだけのものです。本来は、当該Scriptに含まれるScript Libraryを再帰でオープンしてチェックすべきなんでしょう。

Script Debuggerにこの機能が用意されているということは、メーカーのLate Night Softwareでもいろいろハマったんだと思いますが、メニューなどのGUI要素がついていない理由がよくわかりません。Shaneにはアプリ書き出し時にFrameworkのバンドル内組み込みを行う方向で要望は出しています。現場的には必須の機能です。

AppleScript名:sd checker.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/06/30
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

tell application "Script Debugger"
  set dList to name of every document
  
set dRes to choose from list dList with prompt "Choose Script to check linked files (framework/script libraries)"
  
if dRes = false then return
  
set dName to contents of item 1 of dRes
  
  
tell document dName
    set myFile to path
    
    
–Framework Check
    
set uList to used framework files
    
set fList to {}
    
repeat with i in uList
      set j to POSIX path of i
      
if (j does not start with myFile) and (j does not start with "/System/Library/Frameworks/") then
        set the end of fList to j
      end if
    end repeat
    
    
–Script Library Check
    
set libList to used script library files
    
set sList to {}
    
repeat with i in sList
      set j to POSIX path of i
      
if j does not start with myFile then
        set the end of sList to POSIX path of i
      end if
    end repeat
    
    
(*
    –Scripting Additions Check (Omitted)
    set sdList to used scripting addition files
    set dList to {}
    repeat with i in dList
      set j to POSIX path of i
      if j does not start with myFile then
        set the end of dList to POSIX path of i
      end if
    end repeat
    *)

  end tell
end tell

set resList to {fList, sList}
set res2List to FlattenList(resList) of me
set res3List to cleanUp1DList(res2List, {""}) of me
if res3List = {} then
  display dialog "Lib/Framework link check OK"
else
  set aLen to length of res3List
  
set tmpStr to retDelimedText(res3List, return) of me
  
display dialog (aLen as string) & " Lib/Framework exists out of the bundle." & return & tmpStr
end if

–次元数上限なしの多次元配列を1次元配列にフラット化 By Paul Berkowitz
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 cleanUp1DList(aList as list, cleanUpItems as list)
  set bList to {}
  
repeat with i in aList
    set j to contents of i
    
if j is not in cleanUpItems then
      set the end of bList to j
    end if
  end repeat
  
return bList
end cleanUp1DList

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

★Click Here to Open This Script 

Posted in File path list | Tagged 10.12savvy 10.13savvy 10.14savvy Script Debugger | 3 Comments

Post navigation

  • Older posts
  • Newer posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • Numbersで選択範囲のセルの前後の空白を削除
  • macOS 26, Tahoe
  • macOS 15でも変化したText to Speech環境
  • KagiのWebブラウザ、Orion
  • Script Debuggerの開発と販売が2025年に終了
  • 【続報】macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ
  • macOS 15 リモートApple Eventsにバグ?
  • NSObjectのクラス名を取得 v2.1
  • 2024年に書いた価値あるAppleScript
  • 有害ではなくなっていたSpaces
  • macOS 15:スクリプトエディタのAppleScript用語辞書を確認できない
  • Xcode上のAppleScriptObjCのプログラムから、Xcodeのログ欄へのメッセージ出力を実行
  • (確認中)AppleScript Dropletのバグっぽい動作が解消?
  • AVSpeechSynthesizerで読み上げテスト
  • AppleScript Dropletのバグっぽい動作が「復活」(macOS 15.5β)
  • 指定フォルダ以下の画像のMD5チェックサムを求めて、重複しているものをピックアップ
  • macOS 26, 15.5でShortcuts.app「AppleScriptを実行」アクションのバグが修正される
  • Numbersで選択中の2列のセルを比較して並べ直して書き戻す v2
  • Apple、macOS標準搭載アプリ「写真」のバージョン表記を間違える
  • Script DebuggerがmacOS 15.x上で起動せず→起動

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (204) 14.0savvy (159) 15.0savvy (156) CotEditor (66) Finder (52) Keynote (119) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (76) Pages (56) Pixelmator Pro (20) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • date
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Newt On Project
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • process
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • Scripting Additions
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年10月
  • 2025年9月
  • 2025年8月
  • 2025年7月
  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC