Archive for the 'Application Control' Category

2017/05/27 Wikipediaで検索を行ってキーワードのカテゴリを取得する v1

与えられたキーワードをWikipediaで検索して、キーワードのカテゴリ名を取得するAppleScriptです。

何かのキーワードのカテゴリを取得するのに、いろいろプログラムやシソーラス辞書を検索することも試してみましたが、もっと乱暴かつ粗暴で即物的で低レベルな解決方法を見つけたので試してみました。

wiki0_resized.png

WikipediaをREST API経由で検索して、本文を取得し、文末にあるカテゴリ情報を取得すればいい感じのカテゴリ情報になるんでは? と思いつき、試してみたところ、、、うまく行ってしまいました(汗)

wiki1_resized.png

まだ、キーワードのフォワーディング(Mac OS X→macOS)、同音異義語や類義語への分岐(カール→複数の同音異義語への分岐)には対応していませんが、対応するのも(パターンがわかれば)それほど難しくなさそうです。

→ 実際にやってみたら割と簡単に処理できてしまいました(汗) 

AppleScript名:Wikipediaで検索を行ってキーワードのカテゴリを取得する v1
– Created 2017-05-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–https://www.mediawiki.org/wiki/API:Main_page/ja
–http://piyocast.com/as/archives/4666

set aRes1 to getCategoryFromKeyword(“カール (スナック菓子)”) of me
–>  {”スナック菓子の商品名”, “明治 (企業)”, “登録商標”}

on getCategoryFromKeyword(aText)
  set wikiStr to getBodyFromWikipedia(aText) of me
  
set aList to categoryTagListFromWikipedia(wikiStr, “[[Category:”) of me
  
set bList to cleanupArray(aList, “[[Category:”, “]]”) of me
  
return bList
end getCategoryFromKeyword

on categoryTagListFromWikipedia(sText, pickUpKey)
  set aString to current application’s NSString’s stringWithString:sText
  
set anArray to aString’s componentsSeparatedByString:(string id 10)
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“SELF BEGINSWITH %@”, pickUpKey)
  
return (anArray’s filteredArrayUsingPredicate:aPredicate) as list
end categoryTagListFromWikipedia

–Wikipediaで指定キーワードの検索を行う(同義語、類義語などへの分岐に未対応)
on getBodyFromWikipedia(aText)
  set reqURLStr to “https://jp.wikipedia.org/w/api.php” –Japanese Version
  
  
set aRec to {action:“query”, titles:aText, |prop|:“revisions”, rvprop:“content”, |format|:“json”}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESTres to (pages of query of json of aRes)
  
  
set allKeys to (aRESTres’s allKeys()) as list
  
repeat with i in allKeys
    set j to contents of i
    
set aKeyPath to j & “.” & “revisions.*”
    
set aContent to (aRESTres’s valueForKeyPath:aKeyPath)
    
return aContent as string
  end repeat
end getBodyFromWikipedia

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

on cleanupArray(aList, fromStr, toStr)
  set newList to {}
  
  
repeat with i in aList
    set b1 to repChar(i, fromStr, “”) of me
    
set b2 to repChar(b1, toStr, “”) of me
    
    
set b3 to offset of “|” in b2
    
if b3 is not equal to 0 then
      set b2 to text 1 thru (b3 - 1) of b2
    end if
    
set the end of newList to b2
  end repeat
  
  
return newList
end cleanupArray

–Pure AppleScript Version
on repChar(origText, targStr, repStr)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

★Click Here to Open This Script 

2017/05/27 書式つきテキストを組み立てて、クリップボードに設定(影をつける)

書式つきテキスト(NSAttributedString)を組み立てて、クリップボードに設定するAppleScriptの改良版です。影(NSShadow)の追加指定を行ってみました。

stylet2.png

AppleScript名:書式つきテキストを組み立てて、クリップボードに設定(影をつける)
– Created 2017-05-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
–http://piyocast.com/as/archives/4664

set anAttrStr to makeRTFfromParameters("ぴよまるソフトウェア", 2.0, 36) of me

set attrList to {anAttrStr}
restoreClipboard(attrList) of me –set the clipboard to rtf_data

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, outlineNum as real, aFontSize as real)
  –フォント
  
set aVal1 to current application’s NSFont’s fontWithName:"HiraKakuStdN-W8" |size|:aFontSize
  
set aKey1 to (current application’s NSFontAttributeName)
  
  
–色
  
set aVal2 to current application’s NSColor’s blackColor()
  
set aKey2 to (current application’s NSForegroundColorAttributeName)
  
  
–カーニング
  
set aVal3 to -2.0
  
set akey3 to (current application’s NSKernAttributeName)
  
  
–アンダーライン
  
set aVal4 to 0
  
set akey4 to (current application’s NSUnderlineStyleAttributeName)
  
  
–リガチャ
  
set aVal5 to 2 –全てのリガチャを有効にする
  
set akey5 to (current application’s NSLigatureAttributeName)
  
  
–枠線(アウトライン)
  
set aVal6 to outlineNum
  
set akey6 to (current application’s NSStrokeWidthAttributeName)
  
  
–Shadow
  
set aVal7 to current application’s NSShadow’s alloc()’s init()
  
aVal7’s setShadowOffset:(current application’s CGSizeMake(1.0, 1.0)) –影のサイズ
  
aVal7’s setShadowColor:(current application’s NSColor’s blackColor()) –影の色
  
aVal7’s setShadowBlurRadius:2.0 –ぼかしの半径
  
set akey7 to (current application’s NSShadowAttributeName)
  
  
set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey6, akey7}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aVal6, aVal7}
  
set attrsDictionary to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to current application’s NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
  
end makeRTFfromParameters

–From Shane Stanley’s Book "Everyday AppleScriptObjC"
on restoreClipboard(theArray as list)
  set thePasteboard to current application’s NSPasteboard’s generalPasteboard()
  
thePasteboard’s clearContents()
  
thePasteboard’s writeObjects:theArray
end restoreClipboard

★Click Here to Open This Script 

2017/05/26 書式つきテキストを組み立てて、クリップボードに設定

書式つきテキスト(NSAttributedString)を組み立てて、クリップボードに設定するAppleScriptです。

作成した書式つきテキストは、そのままKeynoteなどのアプリケーションにペーストして利用することが可能です。グラフィックではないため、アプリケーション上での再編集や影の指定、色の変更などが可能です。

styles.png

Keynoteでアウトライン文字を使いたいことはままあるので、このように手軽にクリップボードに転送して利用できると実用性が高いことでしょう。

AppleScript名:書式つきテキストを組み立てて、クリップボードに設定
– Created 2017-05-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4662

set anAssrStr to makeRTFfromParameters(“ぴよまるソフトウェア”, 3.0, 72) of me

set attrList to {anAssrStr}
restoreClipboard(attrList) of me –set the clipboard to rtf_data

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, outlineNum as real, aFontSize as real)
  –フォント
  
set aVal1 to current application’s NSFont’s fontWithName:“HiraKakuStdN-W8″ |size|:aFontSize
  
set aKey1 to (current application’s NSFontAttributeName)
  
  
–色
  
set aVal2 to current application’s NSColor’s blueColor()
  
set aKey2 to (current application’s NSForegroundColorAttributeName)
  
  
–カーニング
  
set aVal3 to -2.0
  
set akey3 to (current application’s NSKernAttributeName)
  
  
–アンダーライン
  
set aVal4 to 0
  
set akey4 to (current application’s NSUnderlineStyleAttributeName)
  
  
–リガチャ
  
set aVal5 to 2 –全てのリガチャを有効にする
  
set akey5 to (current application’s NSLigatureAttributeName)
  
  
–枠線(アウトライン)
  
set aVal6 to outlineNum
  
set akey6 to (current application’s NSStrokeWidthAttributeName)
  
  
set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey6}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aVal6}
  
set attrsDictionary to current application’s NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to current application’s NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
  
end makeRTFfromParameters

–From Shane Stanley’s Book “Everyday AppleScriptObjC”
on restoreClipboard(theArray as list)
  set thePasteboard to current application’s NSPasteboard’s generalPasteboard()
  
thePasteboard’s clearContents()
  
thePasteboard’s writeObjects:theArray
end restoreClipboard

★Click Here to Open This Script 

2017/05/24 restcountriesですべての国の情報を取得する

REST API経由で世界の国情報を配信しているrestcountries.euのサービスを呼び出して、すべての国情報を取得するAppleScriptです。

restcountriesのREST APIサービスについては、ひととおりすべての機能をAppleScriptから呼び出せることを確認しています。

国情報についてはそれほど変更があるわけでもないので(人口などの統計は年単位でしかアップデートされないでしょうし、首都などもコロコロ変わるわけではなく)、いっそOS側にビルトインで最初から用意されていてもいい気がします。

アプリケーションの各国語ローカライズを行う場合などに国情報は参考にするので、意外と国情報にREST API経由でアクセスできるのは助かります。

AppleScript名:ALL(すべての国の情報を取得する)
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4660

set reqURLStr to “https://restcountries.eu/rest/v2/all”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESCode to (responseCode of aRes) as integer
if aRESCode is not equal to 200 then return false

set aRESTres to (json of aRes)

return aRESTres
–> {{alpha2Code:”AF”, alpha3Code:”AFG”, nativeName:”افغانستان“, region:”Asia”, borders:{”IRN”, “PAK”, “TKM”, “UZB”, “TJK”, “CHN”}, numericCode:”004″, currencies:{{name:”Afghan afghani”, symbol:”؋“, code:”AFN”}}, translations:{fr:”Afghanistan”, pt:”Afeganistão”, de:”Afghanistan”, it:”Afghanistan”, es:”Afganistán”, br:”Afeganistão”, ja:”アフガニスタン”}, flag:”https://restcountries.eu/data/afg.svg”, name:”Afghanistan”, callingCodes:{”93″}, timezones:{”UTC+04:30″}, subregion:”Southern Asia”, altSpellings:{”AF”, “Afġānistān”}, topLevelDomain:{”.af”}, population:27657145, area:652230.0, regionalBlocs:{{otherNames:{}, otherAcronyms:{}, acronym:”SAARC”, name:”South Asian Association for Regional Cooperation”}}, latlng:{33.0, 65.0}, capital:”Kabul”, gini:27.8, demonym:”Afghan”, languages:{{nativeName:”پښتو“, iso639_2:”pus”, name:”Pashto”, iso639_1:”ps”}…

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

2017/05/20 最小公倍数を求める

2つの数の最小公倍数を求めるAppleScriptです。

最大公約数や素数チェックや素因数分解を作ったので、ついでにこの手の計算を行ってみました。

AppleScript名:最小公倍数を求める
– Created 2017-05-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4654

set a1Num to 75
set b1Num to 315
set cRes to getLCMbetweenTwoNums(a1Num, b1Num) of me
–> 1575

–2つの数の最小公倍数を求める
on getLCMbetweenTwoNums(a1Num, b1Num)
  set aGCD to getGCDbetweenTwoNums(a1Num, b1Num) of me
  
set a2Num to a1Num div aGCD
  
set b2Num to b1Num div aGCD
  
return (aGCD * a2Num * b2Num)
end getLCMbetweenTwoNums

–2つの数の最大公約数を求める
on getGCDbetweenTwoNums(aNum, bNum)
  set aRes to getPrimeFactor(aNum) of me
  
set bRes to getPrimeFactor(bNum) of me
  
  
–2つのリストを集合として扱い、積集合を求める
  
set aSet to current application’s NSMutableSet’s setWithArray:aRes
  
set bSet to current application’s NSMutableSet’s setWithArray:bRes
  
aSet’s intersectSet:bSet
  
set cRes to aSet’s allObjects() as list
  
  
set dRes to multipleListElements(cRes) of me
  
return dRes
end getGCDbetweenTwoNums

–与えられた数を素因数分解する
on getPrimeFactor(aTargNum)
  copy aTargNum to a
  
set pFactList to {}
  
  
repeat
    set pRes to checkPrimeNumber(a) of me
    
if pRes = true then
      set the end of pFactList to a
      
return pFactList
    end if
    
    
set aFactor to checkFactor(a) of me
    
set the end of pFactList to aFactor
    
set a to a div aFactor
  end repeat
end getPrimeFactor

–素数チェック
on checkPrimeNumber(aNum)
  repeat with i from 2 to aNum - 1
    if aNum mod i is equal to 0 then
      return false
    end if
  end repeat
  
return true
end checkPrimeNumber

–素因数チェック
on checkFactor(aNum)
  repeat with i from 2 to aNum - 1
    if aNum mod i is equal to 0 then
      return i
    end if
  end repeat
end checkFactor

–リスト内の要素をすべて乗算する
on multipleListElements(aList)
  set aNum to 1
  
set aRes to 1
  
repeat with i in aList
    set aRes to aRes * (aNum * i)
  end repeat
  
return aRes
end multipleListElements

★Click Here to Open This Script 

2017/05/20 最大公約数を求めるv2

2つの数の最大公約数を求めるAppleScriptです。

素数チェックや素因数分解を作ったので、この手の計算を行ってみました。

AppleScript名:最大公約数を求めるv2
– Created 2017-05-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4653

set aRes to getGCDbetweenTwoNums(12, 18) of me
–> 6

–2つの数の最大公約数を求める
on getGCDbetweenTwoNums(aNum, bNum)
  set aRes to getPrimeFactor(aNum) of me
  
set bRes to getPrimeFactor(bNum) of me
  
  
–2つのリストを集合として扱い、積集合を求める
  
set aSet to current application’s NSMutableSet’s setWithArray:aRes
  
set bSet to current application’s NSMutableSet’s setWithArray:bRes
  
aSet’s intersectSet:bSet
  
set cRes to aSet’s allObjects() as list
  
  
set dRes to multipleListElements(cRes) of me
  
return dRes
end getGCDbetweenTwoNums

–与えられた数を素因数分解する
on getPrimeFactor(aTargNum)
  copy aTargNum to a
  
set pFactList to {}
  
  
repeat
    set pRes to checkPrimeNumber(a) of me
    
if pRes = true then
      set the end of pFactList to a
      
return pFactList
    end if
    
    
set aFactor to checkFactor(a) of me
    
set the end of pFactList to aFactor
    
set a to a div aFactor
  end repeat
end getPrimeFactor

–素数チェック
on checkPrimeNumber(aNum)
  repeat with i from 2 to aNum - 1
    if aNum mod i is equal to 0 then
      return false
    end if
  end repeat
  
return true
end checkPrimeNumber

–素因数チェック
on checkFactor(aNum)
  repeat with i from 2 to aNum - 1
    if aNum mod i is equal to 0 then
      return i
    end if
  end repeat
end checkFactor

–リスト内の要素をすべて乗算する
on multipleListElements(aList)
  set aNum to 1
  
set aRes to 1
  
repeat with i in aList
    set aRes to aRes * (aNum * i)
  end repeat
  
return aRes
end multipleListElements

★Click Here to Open This Script 

2017/05/20 1箇所から別の箇所の方位を求める

1箇所から別の箇所の方位を求めるAppleScriptです。

主な三角関数を呼び出すためにBridgePlus AppleScript Librariesを、atan2関数を呼ぶためにSatimage OSAXを利用しています。

まだ正確かどうか検証しながら使っている状態なので、正しく方位が計算できているかわかりません。間違っている箇所がある可能性も否定できません。

テスト用の緯度経度情報は、住所情報からYaooの住所ジオコーダーAPIを使って求めました(日本測地系で)。

atan2はこういう用途で必要なので(こういう用途でもないと使わなさそうですが)、BridgePlusの機能に組み込んでもらえるようにShane Stanleyにお願いしているところです。

いっそ、Numbersで組み込み関数を一気に増やしたCephes Mathematical Libraryでも呼び出せるようにするといいのに、とは思っていますが・・・Cの関数ライブラリなのでObjective-Cでいったんラッピングする必要があるので大変そうです。

geo1_resized.png

geo2_resized.png

geo3_resized.png

geo4_resized.png

geo5_resized.png

AppleScript名:1箇所から別の箇所の方位を求める
– Created 2017-04-27 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use bPlus : script “BridgePlus”
–http://zurb.com/forrst/posts/Direction_between_2_CLLocations-uAo
–http://piyocast.com/as/archives/4647

set coord1 to {latitude:35.73677496, longitude:139.63754457} –中村橋駅

set coord2 to {latitude:35.78839012, longitude:139.61241447} –和光市駅
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  -25.925429877542

set coord2 to {latitude:35.7227821, longitude:139.63860897} –鷺宮駅
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  175.649833487804

set coord2 to {latitude:35.73590542, longitude:139.62986745} –富士見台駅
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  -96.385293928667

set coord2 to {latitude:35.73785024, longitude:139.65339321} –練馬駅
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  85.959474671834

set coord2 to {latitude:35.71026838, longitude:139.81215754} –東京スカイツリー
set dirRes to calcDirectionBetweenTwoPlaces(coord1, coord2) of me
–>  96.826542737106

–位置情報1から位置情報2の方角を計算。北が0度
on calcDirectionBetweenTwoPlaces(coord1, coord2)
  load framework
  
set deltaLong to (longitude of coord2) - (longitude of coord1)
  
set yComponent to bPlus’s sinValueOf:deltaLong
  
set xComponent to (bPlus’s cosValueOf:(latitude of coord1)) * (bPlus’s sinValueOf:(latitude of coord2)) - (bPlus’s sinValueOf:(latitude of coord1)) * (bPlus’s cosValueOf:(latitude of coord2)) * (bPlus’s cosValueOf:deltaLong)
  
  
set vList to {yComponent, xComponent}
  
set radians to atan2 vList —Requires SatImage OSAX
  
set degreeRes to (radToDeg(radians) of me)
  
  
return degreeRes
end calcDirectionBetweenTwoPlaces

on radToDeg(aRadian)
  return aRadian * (180 / pi)
end radToDeg

★Click Here to Open This Script 

2017/05/16 MatRによる行列データの取り扱い

MatR(By David Cox)をFramework化した「DCMatrix.framework」を呼び出して行列データのハンドリングを行うAppleScriptです。

行列データの作成、入力、演算、出力などをひととおり試してみたものです(野球でいえば、素振りのレベル。でも、これをやっておかないと高度なScriptが組めない、、、)。

OS X 10.10以降用にビルドしたフレームワークのバイナリを用意しておきました。興味のある方は自己責任で~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

AppleScriptで行列を扱うのに、仏Satimage Softwareが提供しているOSAX「Numerics OSAX」を用いるのが一般的です。

ただ、数値変数の表現レンジ(指数表示を使わずに表現できる幅)が1E10ぐらいのAppleScriptの数値演算で、行列が扱えても実用上どうなんだろうと疑問に思っておりました。Numerics OSAX内で高精度の数値型のデータが使えるようではあるものの、外部とやりとりする場合には当然のごとく精度が落ちてしまいます。

# Satimage SoftwareのNumerics OSAXは、高速フーリエ変換とか画像処理とか、3次元配列の計算など、私には使い切れないほど高度な演算を提供してくれるOSAXなので、ぜひ知っておいていただきたいものです

ときに、そんなAppleScriptの数値ハンドリング機能でも、機械学習などの分野においては数値の表現レンジが大きくなくてよいので、割とよさそうです。

そうした利用分野では、CSVやタブ区切りテキストファイルから行列データを取得するケースが多いので、ファイルや配列から行列データを作成する手段を探していました。機械学習、とまで気合を入れなくても線形回帰分析(Linear Regression)を行うための準備として、このMatRを試してみたものです。

線形回帰分析の原理は、KeynoteやNumbersのグラフ「分散図」で実感することができます。「月ごとの平均気温とビール消費量の関係」「広告宣伝費と来店者数」など、グラフ化してみるととてもよく相関が見てとれます。

sample_graph.png

AppleScript名:MatRのじっけん v1
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DCMatrix”
–https://github.com/davidcox95/MatR
–http://piyocast.com/as/archives/4640

set aMatrix to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:5) andRows:3 byColumns:4
set aList to (aMatrix’s |print|())
(*
5 5 5 5
5 5 5 5
5 5 5 5
*)

set bMatrix to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:3) andRows:4 byColumns:3
set bScalar to current application’s NSNumber’s numberWithDouble:2
set bRes to bMatrix’s addScalar:bScalar
bRes’s |print|()
(*
5 5 5
5 5 5
5 5 5
5 5 5
*)

set cScalar to current application’s NSNumber’s numberWithDouble:2
set cRes to bMatrix’s subtractScalar:cScalar
cRes’s |print|()
(*
1 1 1
1 1 1
1 1 1
1 1 1
*)

set dScalar to current application’s NSNumber’s numberWithDouble:2
set eRes to bMatrix’s multiplyScalar:dScalar
eRes’s |print|()
(*
6 6 6
6 6 6
6 6 6
6 6 6
*)

set fScalar to current application’s NSNumber’s numberWithDouble:2
set fRes to bMatrix’s divideScalar:fScalar
fRes’s |print|()
(*
1.5 1.5 1.5
1.5 1.5 1.5
1.5 1.5 1.5
1.5 1.5 1.5
*)

set m4 to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:4) andRows:3 byColumns:3
set m2 to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:2) andRows:3 byColumns:3
set gRes to m4’s addMatrix:m2
gRes’s |print|()
(*
6 6 6
6 6 6
6 6 6
*)

set hRes to m4’s subtractMatrix:m2
hRes’s |print|()
(*
2 2 2
2 2 2
2 2 2
*)

set iRes to m4’s multiplyMatrix:m2
iRes’s |print|()
(*
24 24 24
24 24 24
24 24 24
*)

★Click Here to Open This Script 

AppleScript名:MatRのじっけん v2
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DCMatrix”
–https://github.com/davidcox95/MatR
–http://piyocast.com/as/archives/4640

set aMatrix to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:5) andRows:3 byColumns:4
set aList to doubleListFromMatrix(aMatrix) of me
–>  {{5.0, 5.0, 5.0, 5.0}, {5.0, 5.0, 5.0, 5.0}, {5.0, 5.0, 5.0, 5.0}}

set bList to intListFromMatrix(aMatrix) of me
–>  {{5, 5, 5, 5}, {5, 5, 5, 5}, {5, 5, 5, 5}}

set cList to strListFromMatrix(aMatrix) of me
–>  {{”5″, “5″, “5″, “5″}, {”5″, “5″, “5″, “5″}, {”5″, “5″, “5″, “5″}}

set bMatrix to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:3) andRows:4 byColumns:3
set bScalar to current application’s NSNumber’s numberWithDouble:2
set bRes to bMatrix’s addScalar:bScalar
set bbRes to intListFromMatrix(bRes) of me
–>  {{5, 5, 5}, {5, 5, 5}, {5, 5, 5}, {5, 5, 5}}

set cScalar to current application’s NSNumber’s numberWithDouble:2
set cRes to bMatrix’s subtractScalar:cScalar
set ccRes to intListFromMatrix(cRes) of me
–>  {{1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}}

set dScalar to current application’s NSNumber’s numberWithDouble:2
set eRes to bMatrix’s multiplyScalar:dScalar
set eeRes to intListFromMatrix(eRes) of me
–>  {{6, 6, 6}, {6, 6, 6}, {6, 6, 6}, {6, 6, 6}}

set fScalar to current application’s NSNumber’s numberWithDouble:2
set fRes to bMatrix’s divideScalar:fScalar
set ffRes to doubleListFromMatrix(fRes) of me
–>  {{1.5, 1.5, 1.5}, {1.5, 1.5, 1.5}, {1.5, 1.5, 1.5}, {1.5, 1.5, 1.5}}

set m4 to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:4) andRows:3 byColumns:3
set m2 to current application’s |Matrix|’s alloc()’s initWithValue:(current application’s NSNumber’s numberWithDouble:2) andRows:3 byColumns:3
set gRes to m4’s addMatrix:m2
set ggRes to intListFromMatrix(gRes) of me
–>  {{6, 6, 6}, {6, 6, 6}, {6, 6, 6}}

set hRes to m4’s subtractMatrix:m2
set hhRes to intListFromMatrix(hRes) of me
–>  {{2, 2, 2}, {2, 2, 2}, {2, 2, 2}}

set iRes to m4’s multiplyMatrix:m2
set iiRes to intListFromMatrix(iRes) of me
–>  {{24, 24, 24}, {24, 24, 24}, {24, 24, 24

on doubleListFromMatrix(aMatrix)
  set aList to {}
  
set aRows to aMatrix’s rows()
  
set aColumns to aMatrix’s columns()
  
  
repeat with rowC from 0 to (aRows - 1)
    set colList to {}
    
repeat with colC from 0 to (aColumns - 1)
      set the end of colList to (aMatrix’s getElementAtRow:rowC andColumn:colC) as real
    end repeat
    
set the end of aList to colList
  end repeat
  
return aList
end doubleListFromMatrix

on intListFromMatrix(aMatrix)
  set aList to {}
  
set aRows to aMatrix’s rows()
  
set aColumns to aMatrix’s columns()
  
  
repeat with rowC from 0 to (aRows - 1)
    set colList to {}
    
repeat with colC from 0 to (aColumns - 1)
      set the end of colList to (aMatrix’s getElementAtRow:rowC andColumn:colC) as integer
    end repeat
    
set the end of aList to colList
  end repeat
  
return aList
end intListFromMatrix

on strListFromMatrix(aMatrix)
  set aList to {}
  
set aRows to aMatrix’s rows()
  
set aColumns to aMatrix’s columns()
  
  
repeat with rowC from 0 to (aRows - 1)
    set colList to {}
    
repeat with colC from 0 to (aColumns - 1)
      set the end of colList to (aMatrix’s getElementAtRow:rowC andColumn:colC) as string
    end repeat
    
set the end of aList to colList
  end repeat
  
return aList
end strListFromMatrix

★Click Here to Open This Script 

AppleScript名:MatRのじっけん v3
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DCMatrix”
–https://github.com/davidcox95/MatR
–http://piyocast.com/as/archives/4640

–listからMatrixを作る
set mList to {{1, 2, 3, 4}, {11, 12, 13, 14}, {21, 22, 23, 24}}
set aMatrix to current application’s |Matrix|’s alloc()’s initWithArray:mList andRows:3 byColumns:4
aMatrix’s |print|()
(*
1 2 3 4
11 12 13 14
21 22 23 24
*)

set a1Res to aMatrix’s |transpose|() –左回転
(
a1Res’s |print|())
(*
1 11 21
2 12 22
3 13 23
4 14 24
*)

★Click Here to Open This Script 

tab_sep_text.png

AppleScript名:MatRのじっけん v4
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “DCMatrix”
–https://github.com/davidcox95/MatR
–http://piyocast.com/as/archives/4640

–タブ区切りテキストファイルからMatrixを作る
set aPosixFile to POSIX path of (choose file)
set aMatrix to current application’s |Matrix|’s alloc()’s initWithContentsFromFile:aPosixFile
aMatrix’s |print|()
(*
5 6 7 8
3 4 5 6
1 2 3 4
*)

★Click Here to Open This Script 

2017/05/14 Twitter ScripterでAppleScriptからTwitter投稿、検索

AppleScriptから利用する専用のGUIなしTwitterクライアント「Twitter Scripter」(By Mousedown)を呼び出してTwitter投稿や検索を行うAppleScriptです。

Twitter Scripterはフリーでダウンロードして利用できます。Twitter Scripterは本当にAppleScriptから呼び出すためだけに作られているので、ユーザーインタフェースは一切ありません。

tw1.png

システム環境設定から確認できる(macOS 10.12, Sierra)、Twitterアカウントの一覧を取得します。Twitterアカウントは複数登録されている可能性があるため、リスト(配列)で返ってきます。

AppleScript名:Twitterアカウントを取得
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
–> {”piyomaru”}
end tell

★Click Here to Open This Script 

さらに、得られたアカウント一覧から適当なものを選んでTwitter投稿してみましょう。

AppleScript名:Twitterにアカウントを指定して投稿(テキストのみ)
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
–> {”piyomaru”}
  
  
if length of availableAccounts = 0 then return
  
  
set twitterAccount to first item of availableAccounts
  
–> “piyomaru”
  
  
tweet “てすと” using account twitterAccount
end tell

★Click Here to Open This Script 

twitter2.png

システムからTwitterアカウントを取得しなくても、直接文字列で指定することも(当然)できます。ただし、自分で所有してOSに登録していないTwitterアカウントを指定するとエラー応答が返ってきます。

–> {response:{{}}, action:false}

AppleScript名:Twitterにアカウントを指定して投稿(テキストのみ2)
–http://piyocast.com/as/archives/4636
tell application “Twitter Scripter”
  tweet “てすと” using account “piyomaru”
end tell

★Click Here to Open This Script 

Twitter Scripterからはコマンド実行時に大量のデータが返ってくるため、Cocoaの機能を利用してデータの絞り込みを行うのにぴったりです。むしろ、Cocoaの機能を使わないと大量のデータからのしぼりこみを行うのは大変です。

画像を指定してテキストと一緒に画像を投稿することもできるのですが、画像添付についてはうまく動くときと画像が投稿されないときがあるようです(なんで? どうして???)。

検索も可能で、出力対象にrecent(最近の投稿から)、popular(話題の投稿から)、mixed(デフォルト)の3タイプから選択可能です。出力件数には数値で自由に指定できるようになっていますが、実際にためしてみたところ最大件数は100件のようです。

AppleScript名:Twitterを検索する
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “mixed”
end tell

★Click Here to Open This Script 

AppleScript名:Twitterを検索する(最近の投稿から)
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “recent” –最近の投稿
end tell

★Click Here to Open This Script 

AppleScript名:Twitterを検索する(話題の投稿から)
tell application “Twitter Scripter”
  set availableAccounts to available accounts
  
if length of availableAccounts = 0 then return
  
  
set anAccount to first item of availableAccounts
  
search for “戦場の絆” using account anAccount returning entries 10 result type “popular” –話題の投稿
end tell

★Click Here to Open This Script 

その他のサンプルScriptもMousedownのWebサイトに掲載されています。

2017/04/28 えほんシリーズ、続刊作成中

AppleScriptえほんシリーズ(1)として作成した「iTunes Control」の続刊が「Keynote Control」(近日刊行予定)です。

ただし、分量が割と増えたために2分冊になります。難易度を上げずに丁寧に説明すると、分量が増えてしまいます(汗)。

keynote1.png
▲Keynote Control (1)

keynote1a.png
▲Keynote Control (1) もくじ ※作成中のため、変更になる可能性もあります

keynote2.png
▲Keynote Control (2) 表紙

Keynote Control 1では、Keynoteの基礎的なオブジェクトへのアクセスを体験。
続刊のKeynote Control 2では、より実践的なオブジェクトのアクセスを体験できるようになります。

そして、この2冊は同時に出る予定です(1冊はほぼ書きあがったので2冊目に着手)。本シリーズは、ある程度バリエーションが増えないと価値が出てこないので、アイテム数を増やす方向で作業をすすめています。

「その次」についての候補があればぜひご意見をお寄せください。ダミーで表紙を作ってみた感じではありますが、こんな感じ(↓)のものもありえそうで。

airpods_control.png

2017/04/26 指定画像の余白の自動トリミング

オープンソースのKGPixelBoundsClip(By David Keegan)をフレームワーク化した「KGPixelBoundsClipKit」を呼び出して指定画像を自動でトリミングするAppleScriptです。

OS X 10.10以降用にビルドしたフレームワークのバイナリを用意しておきました。興味のある方は自己責任で~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

指定画像(背景を透明に指定したPNGで実行)を自動トリミングして、デスクトップに結果を書き出します。

Photoshopにも同様の機能があったような記憶がありますが、フレームワーク経由で実行することで並列処理を行いやすい部品を作っておくことが目的です。

original_and_trimed.png

AppleScript名:指定画像の余白の自動トリミング
– Created 2017-04-26 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “KGPixelBoundsClipKit” –https://github.com/kgn/KGPixelBoundsClip
–http://piyocast.com/as/archives/4611

set aFile to POSIX path of (choose file of type {“public.image”})
set anImage to (current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile)

set bImage to anImage’s imageClippedToPixelBounds()

set aDesktopPath to (current application’s NSProcessInfo’s processInfo()’s environment()’s objectForKey:(“HOME”))’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
set fRes to saveNSImageAtPathAsPNG(bImage, savePath) of me

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/04/21 Safariの最前面のウィンドウで表示している内容をMacJournalの選択中のjournalに新規エントリを作成して入れ

Safariの最前面のウィンドウで表示しているページのテキストとタイトルで、MacJournalの選択中のJournalに新規エントリを作成するAppleScriptです。

mjournal1.png

さまざまなデータ処理を行うさいのコーパス(例文)を集めるために作成したものです。本来は指定URLをリストで保持しておいて、そこから順次Webページを表示して複数のページを一気に処理するAppleScriptを作ったときに「ついで」に作成。

SafariからWebサイトのテキストを取得するのは、なかなか大変です。

何が大変かといえば、「取得そのもの」ではなく、「取得した内容」が本当に文字化けしていないかどうか、チェックする必要があるということです。とくに、古いWebサイトだと文字コードの指定が不十分で、自動判別できないケースもあります

とりあえず、その処理は省略して(すでに書いてあるので)、MacJournalの中にWebサイトから取得した本文を入れてみることにしました。

すべて自動で連続して作成したときには問題はなかったのですが、「現在表示中のWebサイトの内容を現在選択中のMacJournalのジャーナルに新規エントリに作成」とかいう、1回実行するだけのScriptを走らせてみたところ、予想外の挙動がありました。

選択中のジャーナル(フォルダみたいなもの)の中に新規エントリ(記事)を作成すると、そのエントリへのオブジェクト参照が返ってくることが期待できます。ところが、MacJournalで実行したところ、なぜか以前に作成したエントリに内容が上書きされてしまいました。

おそらく、内部処理のバグだと思うのですが、本来であればID(固有値)が返ってくるべきところでindex(順番で数えた数)が返ってきてしまっている様子。エントリの新規作成時の返り値をそのまま使うとだまされるわけです。

そこで、新規作成エントリが選択中される(selected entry)ことを利用し、その選択中のエントリに対してタイトルと本文を設定することで、新規作成エントリを指定しないで対処しました。

何も考えずに単にObjective-Cの解釈と書き換えを行うだけのAppleScriptObjCのプログラムよりも、出来の悪いGUIアプリケーションを相手に対策を考えることのほうがプログラミングっぽい感じがします。

AppleScript名:Safariの最前面のウィンドウで表示している内容をMacJournalの選択中のjournalに新規エントリを作成して入れる
– Created 2017-04-21 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4598

–現在選択中のJournalを取得
tell application "MacJournal"
  tell document 1
    set curJournal to selected journal
  end tell
end tell

set titleRes to getPageTitleOfFrontmostWindow() of me
if titleRes = false then return

set textRes to getPageTextOfFrontmostWindow() of me
if textRes = false then return

tell application "MacJournal"
  tell curJournal
    set jRes to make new journal entry –ここで新規作成したentryへの参照を取得するが、どうも固定のIDではなくindexが返ってくるもよう
  end tell
  
  
tell selected entry
    set name to titleRes
    
set plain text content to textRes
  end tell
end tell

on getPageTextOfFrontmostWindow()
  tell application "Safari"
    if (count every window) = 0 then return false
    
tell window 1
      set aProp to properties
    end tell
    
set aDoc to document of aProp
    
set aText to text of aDoc
  end tell
  
return aText
end getPageTextOfFrontmostWindow

on getPageTitleOfFrontmostWindow()
  tell application "Safari"
    if (count every window) = 0 then return false
    
tell window 1
      set aProp to properties
    end tell
    
set aDoc to document of aProp
    
set aText to name of aDoc
  end tell
  
return aText
end getPageTitleOfFrontmostWindow

★Click Here to Open This Script 

2017/04/20 日本語音声認識テスト

日本語音声認識コマンドを待機、認識するAppleScriptです。

とくに日本語であることは指定していませんが、実行ユーザー環境が日本語環境で、Macに日本語音声認識エンジンがインストールされていて、サウンド入力デバイスが存在している場合には音声認識を行います(対象言語を指定して音声認識エンジンを起動できるといいのに。あるいは、与えるコマンド文字列から対応するエンジンが起動されるのか、、、、)。

OS側で用意している日本語音声認識によるAppleScript呼び出し機能よりも、素朴な機能ではありますが、音声認識文字列をリストで動的にAppleScript側から指定できる点や、他の音声認識機能の抑止指定が便利なこともあることでしょう。

recog1.png

普通に認識してくれます。認識フローティングウィンドウの「表示」をクリックすると、

recog2.png

指定したコマンド文字列一覧を表示します。

問題は音声認識の停止です。stopListening()を実行するとコマンド認識は停止するようですが、この認識フローティングウィンドウが消えないので、ちょっと困ります。

AppleScript名:日本語音声認識テスト
– Created 2017-04-20 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4595

set aCmdList to {“こんにちは”, “こんばんは”, “おはよう”}
–set aCmdList to {”Hello”, “goodnight”, “goodevening”}

set aRecognizer to current application’s NSSpeechRecognizer’s alloc()’s init()
aRecognizer’s setCommands:aCmdList
aRecognizer’s setDelegate:me
aRecognizer’s setListensInForegroundOnly:true
aRecognizer’s startListening()
aRecognizer’s setBlocksOtherRecognizers:true

on speechRecognizer:aSender didRecognizeCommand:aCmd
  set recogCmd to aCmd as string
  
say recogCmd using “Otoya” –Japanese Male Voice
  
–aSender’s stopListening()
end speechRecognizer:didRecognizeCommand:

★Click Here to Open This Script 

2017/04/19 NSColorからCIColorを作成

NSColorからCIColorを作成するAppleScriptです。

CoreImageの各種フィルタを利用して画像加工するさいに、色オブジェクトを指定するフィルタがいくつかあります。その際に指定するのがCIColor。そのため、NSColorからCIColorを求めるサンプルを書いてみました。

ただ、パラメータを指定してCIColorを作ってみても、たいして難しくもなんともないんですね(汗)

AppleScript名:NSColorからCIColorを作成
– Created 2017-04-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”
use framework “AppKit”
–http://piyocast.com/as/archives/4591

set redValue to 0
set greenValue to 0
set blueValue to 1
set alphaVlaue to 1.0

set aNSColor to current application’s NSColor’s colorWithCalibratedRed:redValue green:greenValue blue:blueValue alpha:alphaVlaue
set aCIColor to current application’s CIColor’s alloc()’s initWithColor:aNSColor
–>  (CIColor) (0 0 1 1)

★Click Here to Open This Script 

AppleScript名:値を指定してCIColorを作成
– Created 2017-04-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”
–http://piyocast.com/as/archives/4591

set redValue to 0
set greenValue to 0
set blueValue to 1
set alphaVlaue to 1.0

set aCIColor to current application’s CIColor’s alloc()’s initWithRed:redValue green:greenValue blue:blueValue alpha:alphaVlaue
–>  (CIColor) (0 0 1 1)

★Click Here to Open This Script 

2017/04/18 HelpBookの項目表示、キーワード検索

macOS上でヘルプを表示するHelpViewerはAppleScriptに対応しており、任意の項目の表示、キーワード検索、指定URLのオープンなどを行えるようになっています。

ただし、HelpViewerをAppleScriptから普通に操作したときの動作が怪しいので、Cocoa経由でも操作する方法を調べておきました。

HelpViewerで指定HelpBook中の指定のアンカーを表示させるだけであれば、

AppleScript名:help_sample
tell application “HelpViewer”
  activate
  
try
    lookup anchor “scpedt1126″ in book “com.apple.ScriptEditor.help”
  end try
end tell

★Click Here to Open This Script 

このぐらいの簡素な記述でOKです。これをCocoa経由で操作すると、

AppleScript名:指定アプリケーションのヘルプブック内の指定アンカーを表示する
– Created 2017-04-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4590

set anAppName to “Script Editor”
set aTargAnchor to “scpedt1126″ —-https://help.apple.com/scripteditor/mac/10.12/index.html?localePath=ja.lproj#/scpedt1126
set hRes to openHelpBook(anAppName, aTargAnchor) of me

–指定アプリケーションのヘルプブックで、指定アンカーを表示する
on openHelpBook(anAppName, aTargAnchor)
  set locBookName to getHelpBook(anAppName) of me
  
if locBookName = false then return false
  
current application’s NSHelpManager’s sharedHelpManager()’s openHelpAnchor:aTargAnchor inBook:locBookName
end openHelpBook

–指定アプリケーションのヘルプブック名称を取得する
on getHelpBook(anAppName)
  set aWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set appPath to aWorkspace’s fullPathForApplication:anAppName
  
if appPath is equal to missing value then return false
  
  
set locBookName to (current application’s NSBundle’s bundleWithPath:appPath)’s objectForInfoDictionaryKey:“CFBundleHelpBookName”
  
if locBookName is equal to missing value then return false
  
–> “com.apple.ScriptEditor.help”
  
return locBookName
end getHelpBook

★Click Here to Open This Script 

このぐらいになります。ただし、前者の記述ではHelpBook名称があらかじめわかっていることが前提、後者ではアプリケーション名からHelpBook名を取得して項目表示するという違いがあります。

問題はキーワード検索で、HelpViewerを直接コントロールすると、

AppleScript名:HelpViewerで検索する?(未遂)
tell application “HelpViewer”
  activate
  
search looking for “用語説明”
end tell

★Click Here to Open This Script 

このぐらいの簡素な記述で済むはずですが、結果はHelpViewerに表示されません(単に記述が違っているという可能性もあります)。

これをCocoaの機能を用いて操作すると、きちんと検索結果が表示されます。Webサーバー上にあるヘルプのコンテンツを連続して同一ページを表示させると、5回に1回はエラー(未表示)になるなど、挙動がよくわかりません。

HelpViewerの中身はほぼ(遅い)Webブラウザで、有用性についても疑問が残ります。

AppleScript名:指定アプリケーションのヘルプブック内で指定キーワードを検索する
– Created 2017-04-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4590

set anAppName to “Script Editor”
set aTargString to “用語説明” –”Script Dictionary” in Japanese
set hRes to searchHelpBook(anAppName, aTargString) of me

–指定アプリケーションのヘルプブックで、指定アンカーを表示する
on searchHelpBook(anAppName, aTargString)
  set locBookName to getHelpBook(anAppName) of me
  
if locBookName = false then return false
  
current application’s NSHelpManager’s sharedHelpManager()’s findString:aTargString inBook:locBookName
end searchHelpBook

–指定アプリケーションのヘルプブック名称を取得する
on getHelpBook(anAppName)
  set aWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set appPath to aWorkspace’s fullPathForApplication:anAppName
  
if appPath is equal to missing value then return false
  
  
set locBookName to (current application’s NSBundle’s bundleWithPath:appPath)’s objectForInfoDictionaryKey:“CFBundleHelpBookName”
  
if locBookName is equal to missing value then return false
  
–> “com.apple.ScriptEditor.help”
  
return locBookName
end getHelpBook

★Click Here to Open This Script 

2017/04/17 新刊書籍「iTunes Control」のオンライン販売を開始しました

新刊書籍「iTunes Control」の販売を開始しました。全48ページ、全ページカラーです。

AppleScriptがわからなくても、簡単な記述でアプリケーションの情報をとってきたり、アプリケーションに情報を設定したり、曲を検索して再生させるなどの高度な操作がかんたんに行える「体験」をご提供する本です。

AppleScriptをわかっている人には、よりGUIアプリケーションの操作をわかっていただけるものと思います。

「ホップ」「ステップ」「ジャンプ」の3段階難易度設定で、急に難しくならない「痛くない入門書」。一番最後に掲載されているプログラムリストでも10行に満たないコンパクトさ。それでいて、iTunesをはじめとしたGUIアプリケーション操作の真髄ともいえる、フィルタ参照を実際に体感できます。

なお、初心者向けの「Control」シリーズと、中級者向けの「Scripting」シリーズを計画しており、「iTunes Control」の続刊にはご要望にお応えして「Keynote Control」を予定しています。

2017/04/12 Xcode上のAppleScriptのプログラム内でpragma markを表示する

「pragma mark」については最近まで存在自体を知らなかったのですが、Xcodeのプロパティやメソッドの一覧メニューに任意の文字列を表示させる定型コメント文の一種です。

xcode3_resized.png

プログラム的には何も意味はありませんが、Xcodeのメニューに項目を表示させるためだけの定型コメント文です。メニューを整理するための項目を表示させます。

xcode5_resized.png

このメニューには、プロパティやハンドラの一覧が上から順番に表示されるため、プログラムが複雑になるとメニュー項目数が多くなりがちです。

そのため、メニュー表示内容を特殊なコメント文でカスタマイズできるようになっているというわけです(自分は、最近「Cocoa勉強会池袋」で知りました)。それが、pragma markです(マクロの一種かと思ってました、、、)。

Objective-Cでは「//pragma mark:」、Swiftでは「// MARK:」。
AppleScriptでは「# MARK:」「– MARK:」「(* MARK: *)」のように書けます。
メニューに区切り線を入れる場合には、「# MARK -」と書きます。

xcode4.png

通常項目の「# MARK:」、ToDo項目を表示させる「# TODO:」、修正項目を表示させる「# FIXME:」などもあります。この3種類はとくにプログラム的な扱いの違いはありませんが、メニューに表示されるアイコンが異なります。

Edama2さんから「# MARK:」のパターンについては聞いていたのですが、他のパターンも調べてみたら実際にXcode 8.3.1上で表示されました。ただし、これを使うことの副作用としてときどき日本語が入ったAppleScriptのソース表示が文字化けするという問題があるようです。いったん文字化け状態になったら、外部のスクリプトエディタ(スクリプトエディタ、ASObjC Explorer 4、Script Debugger)で当該のAppleScriptを編集することでXcodeのエディタ上でもリロードが行われ、文字化けが解消していました(知らずに文字化け現象に直面すると驚かされます)。

スクリプトエディタ上でも実験してみましたが、そういうpragma markの表示機能はないようです。

xcode2.png

スクリプト名:pragma_mark_test
  #  MARK: 見出し1
  
#  MARK: -
  
#  MARK: 見出し2
  
#  MARK: -
  
#  TODO: 見出し3
  
#  FIXME: 見出し4
  
#  !!!: 見出し5
  
#  ???: 見出し6

  –   MARK: 見出し11
  
– MARK: -
  
– MARK: 見出し12
  
– MARK: -
  
– TODO: 見出し13
  
– FIXME: 見出し14
  
– !!!: 見出し15
  
– ???: 見出し16
  
  
(* MARK: 見出し 21 *)
  
(* MARK: - *)
  
(* MARK: 見出し22 *)
  
(* MARK: - *)
  
(* TODO: 見出し23 *)
  
(* FIXME: 見出し24 *)
  
(* !!!: 見出し25 *)
  
(* ???: 見出し26 *)

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

2017/04/10 技術書典2で販売した電子書籍のダウンロードリクエストを手動で処理

ダウンロードリクエストのメールに対して、取り急ぎすべて手動で応答しておきました。

tbf2_salesys.png
▲技術書典2の書籍販売用に作成したシステム

プログラム的な問題の解決よりも、まずは購入者の方々に書籍をお届けすることを優先しました。

Mail.appからメールを取得する部分と、メールアドレスからレジストコードを分離するシーケンスでの問題、およびQRコード読み取り時に拡張Gmailアドレス中の「+」の文字が抜け落ちる現象が確認されています。

Gmailアドレスから「+」が抜け落ちて伝わる現象については、よくメールが届くものだと感心します(^ー^;

想定していなかったさまざまな形式のメールアドレスのデータは、こういう機会がなければ集められないものでした。とくに、QRコードでGmailの拡張メールアドレスを配布して、仕様がバラバラなQRコード読み取りソフトウェアで読み取った場合の挙動については、想定外の連続で(汗)

当日にカッコよく決めることはできませんでしたが、破損画像データのように事故発生事例データというのは、なかなか得がたいものがあります。成功から学ぶことはできませんが、失敗からは山のように学ぶべきことがあります。

2017/04/09 技術書典2にお越しいただき、ありがとうございました!

雨天の中、技術書典2にお越しいただき誠にありがとうございました! 今回の技術書典では、電子書籍をオンライン販売するシステムを直前に開発し、朝から稼動させることも1つのテーマにしておりました。

稼動状況はSlack経由でiPhoneでモニターしていたのですが、事前にテストしていた範囲では発生していなかった問題点が多数発生!!!

何通か、正常にダウンロード先URLをお送りできていることは確認しているのですが、大部分でトラブルが発生。以下、その状況をお知らせいたします。

(1)QRコードのメール受信を行うMacでメーラーで「迷惑メール」フィルタがオンになっていた!!!

これは盲点でした(汗) 発見し、すぐさまオフにした運用に移行しました。

(2)メールアドレスの入り方のバリエーションが多く、そのすべてのパターンに対処できていなかった

事前のテストプラットフォームが主にiOSだったので、それ以外のプラットフォームで問題が発生しているようです。実に、バリエーション豊かな入り方をしています。

目下、順次問題を解消して対処すべく作業を行っております。また、maro@piyocast.comからご連絡のメールをお送りしている分もあるのですが、このメールアドレスからのメールが迷惑メールに仕分けられていないか、祈るばかりです。

エラー分に仕分けられていたメール3通については、手動でDropboxへのPDFのアップロードとメッセージ通知を行いました。処理キューに入っていたメールについては、本日はいったん対処を打ち切ってシステムを休止。明日、ステップごとの処理を順次行ってデータを検証しつつ、問題箇所の切り分けを実施。メール受け付け分については最悪でも手動でPDFの返送を行います。

イレギュラーを発生させていた形式のメールについてはそのフィードバックを行い、1週間程度のシステム稼動を継続します。

まずは、状況確認とご報告までに。

2017/04/01 Wikipediaの任意の項目の本文に入っているURLがリンク切れになっていないかどうかチェック v2

Wikipedia(日本語版)の任意の項目の本文に入っているURLがリンク切れかどうかを確認するAppleScriptです。

Wikipedia(日本語版)にREST API経由でアクセスして指定項目の本文を取得し、本文に含まれているURLを抽出。抽出されたURLをすべて総当たりで存在確認して、リンク切れになっているものがあるかどうかを確認します。

一応、「Mac OS X」→「MacOS」と項目転送されているキーワード項目については自動転送を検出し、転送先のキーワードで再アクセスしています。ただし、「Mac OS X」ぐらいの項目でしかチェックしていないので、エラーチェックもほとんどなきに等しく大甘です。

自分が投稿した項目のリンク切れ自動チェックのために書いた「書き捨てレベル」のAppleScriptなので、だいたいこんなものだと思っています。

リンク切れチェックについては順次アクセスしてWebサーバーからのResponse Codeを取得しています。チェックするURLの数が増えると処理時間が長くなるので、このあたりは(使い捨てレベルではなく本気で組む場合には)並列処理で行っておきたいところです。

AppleScript名:Wikipediaの任意の項目の本文に入っているURLがリンク切れになっていないかどうかチェック v2
– Created 2017-03-29 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–https://www.mediawiki.org/wiki/API:Main_page/ja
–http://piyocast.com/as/archives/4573

set aKeyword to “AppleScript”

set aStr to getBody(aKeyword) of me

–ものすごく大甘な転送検出
if aStr begins with “#転送” then
  set bStr to detectForwarding(aStr) of me
  
–転送先のキーワードで再度処理
  
set aStr to getBody(bStr) of me
end if

set aRes to extractURLsAndValidateThem(aStr) of me
–>  {safeURL:27, forwardedURL:19, brokenURL:0, brokenURLs:{}}

on detectForwarding(aStr)
  if aStr begins with “#転送” then
    set aRes to parseByDelim(aStr, {“[[”, “]]”}) of me
    
–>  {”#転送 “, “MacOS”, “”}
    
return item 2 of aRes –エラーチェックはやっていない。大甘
  else
    return aStr
  end if
end detectForwarding

on extractURLsAndValidateThem(aStr)
  set urlList to extractLinksFromNaturalText(aStr) of me as list
  
  
set okList to {}
  
set fwList to {}
  
set ngList to {}
  
  
repeat with i in urlList
    set j to contents of i
    
set aTarg to j’s absoluteString() as string
    
set {exRes, headerRes, aData, resURL} to checkURLResourceExistence(j, 30) of me
    
    
if exRes = false then
      –URL取得時に連続するスペースをURLの一部として誤解して取得するケースがあるので、クリーニングしてリトライ  
      
set bStr to cleaningURLStr(aTarg) of me
      
      
if bStr = false then
        –クリーニング対象文字列がなかった。本当にダメだった
        
set the end of ngList to aTarg
      else
        –リトライ(タイムアウト条件も緩和)
        
set bURL to (current application’s |NSURL|’s URLWithString:bStr)
        
set {exRes, headerRes, aData, resURL} to checkURLResourceExistence(bURL, 60) of me
        
        
if exRes = false then
          –やっぱりダメでした。ごめんなさい(T_T)
          
set the end of ngList to bStr
        else if resURL is not equal to bStr then
          –URLがForwardされていた
          
set the end of fwList to bStr
        else
          –OKだった(リクエストしたURLとリプライURLが同じ、そこに何かの同名のファイルが存在した)
          
set the end of okList to bStr
        end if
        
      end if
      
    else if resURL is not equal to aTarg then
      –URLがForwardされていた
      
set the end of fwList to aTarg
    else
      –OKだった(リクエストしたURLとリプライURLが同じ、そこに何かの同名のファイルが存在した)
      
set the end of okList to aTarg
    end if
    
  end repeat
  
  
set resList to {safeURL:length of okList, forwardedURL:length of fwList, brokenURL:length of ngList, brokenURLs:ngList}
  
return resList
end extractURLsAndValidateThem

on cleaningURLStr(aStr)
  set anOffset to offset of “%20″ in aStr
  
if anOffset = 0 then return false
  
set bStr to text 1 thru (anOffset - 1) of aStr
  
return bStr
end cleaningURLStr

on getBody(aKeyword)
  –set reqURLStr to “https://en.wikipedia.org/w/api.php”–English Version
  
set reqURLStr to “https://jp.wikipedia.org/w/api.php” –Japanese Version
  
  
set aRec to {action:“query”, titles:aKeyword, |prop|:“revisions”, rvprop:“content”, |format|:“json”}
  
–set aRec to {action:”query”, titles:”AppleScript|Mac OS X|Objective-C”, |prop|:”revisions”, rvprop:”content”, |format|:”json”}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESTres to (json of aRes)
  
–> {query:{pages:{2954:{pageid:2954, title:”AppleScript”, revisions:{{contentformat:”text/x-wiki”, *:”{{Infobox プログラミング言語|名前 = AppleScript ……., contentmodel:”wikitext”}}, ns:0}}}, batchcomplete:”"}
  
  
–最初にヒットしたものだけを返す(同じ名前の項目が複数存在した場合でも)
  
set aRes to (aRESTres’s valueForKeyPath:“query.pages”)
  
set aKeyStr to (aRes’s allKeys()’s firstObject()) as string
  
set aKeyPath to aKeyStr & “.revisions.*”
  
  
set aBody to (aRes’s valueForKeyPath:aKeyPath)’s firstObject() as string
  
return aBody
end getBody

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData), 最終的なURLの文字列}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (current application’s NSURLRequest’s requestWithURL:aURL cachePolicy:(current application’s NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
  
set resURL to ((|URL| of bRes)’s |absoluteURL|()’s absoluteString()) as string
  
  
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
  
return {(aResCode = 200), hRes, dRes, resURL}
end checkURLResourceExistence

on extractLinksFromNaturalText(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
  
set {theDetector, theError} to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeLink) |error|:(reference)
  
  
set theMatches to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
set theResults to theMatches’s valueForKey:“URL”
  
  
return theResults as list
end extractLinksFromNaturalText

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

★Click Here to Open This Script 

2017/03/28 Keynote/Pages/Numbersがアップデート

Keynote/Pages/Numbersがアップデートし、それぞれKeynote 7.1、Pages 6.1、Numbers 4.1になりました(要macOS 10.12)。

■Keynote 7.1.0
–AppleScript用語辞書の変更点
・current slideがread only属性ではなくなった
・image formatで指定できる値が変更された
small / medium / large –> 60p / 540p / 720p / 1080p / 2160p / native size
・movie formatで指定できる値が変更された
small / medium / large –> 360p / 540p / 720p / 1080p / 2160p / native size
–機能上の変更点
・PDF書き出しをmacOS 10.12.4上で行えるようになった(権限エラーが出なくなった)

■Pages 6.1.0
–AppleScript用語辞書の変更点
・exportコマンドで指定できるフォーマットとして「formatted text」が追加された(RTF?)
–機能上の変更点
・PDF書き出しをmacOS 10.12.4上で行えるようになった(権限エラーが出なくなった)

2017/03/24 はじめてresultが役に立った

これまで、「result」が役に立ったことはなく、AppleのサンプルScript中で多用されているのを苦々しく思っておりました。

ところが、クラウド経由で遠隔地のMacにAppleScriptを実行させる仕組みを作っていたとき、たまたま処理結果を返してこないScriptを「run script」コマンドで実行したところ・・・・結果を(なんでもテキスト化して)返す処理部分でエラー。

つまり、run scriptコマンドの結果を代入している変数には何も入っていないので(beepコマンドだし)その変数自体が定義されていないというエラーに。

そこで、直接run scriptコマンドの結果を変数に代入せずに、実行結果をresultから取得。resultから取得する部分にエラートラップを仕掛けて「コマンドの実行結果が、何も値を返さない」パターンに対処するようにしてみました。

これで、無事対応でき、遠隔地の複数のMacにクラウド経由でコマンドを送って結果を取得できるようになりました。実に、はじめてresultが役に立ちました。

Siriみたいな自然言語処理コマンドインタフェースや、音声認識インタフェース、果ては全国規模の分散プリントを実現するGUIアプリケーションまでAppleScriptで作りましたが、resultが役に立ったのはこれがはじめてです。

AppleScript名:はじめてresultが役に立った
run script “beep 1″
try
  set aTmp to result
  
set a to aTmp
on error
  set a to “”
end try

return a

★Click Here to Open This Script 

2017/03/24 Text Utilitiesで文字操作

「Text Utilities」AppleScript Librariesを用いて文字操作を行うAppleScriptです。

同Script Libraryは文字操作を行う機能を提供するもので、AppleScript用語辞書も含んでいるため、英語っぽい記法で記述できます。

同ライブラリをダウンロードして~/Library/Script Librariesフォルダに入れると同ライブラリの命令を使えるようになります。

同ライブラリには基礎的なテキスト操作(大文字/小文字変換、文字置換、文字列の前後の空白削除)の機能を提供するものです。日本語環境でも問題なく利用できるものかどうか、実際に使って試してみました。

どうでもいい話ですが、日本語には複数形の名詞の語尾変化はないので「AppleScript Libraries」という単語はなかなか言いづらいものがあります。「ライブラリ」だけでいいのかも。

本ライブラリは、あまりScriptに親しみのない人が、文字置換とか文字列の前後のスペースを削除したいとかいう「かわいらしい用途」に使うには向いているのではないでしょうか。

ソースを読んでみると、割とCocoaの基礎的な機能を使っているため「Unicodeの特殊文字が置換できない!」とかいう話に対しては応えられないレベルです。

AppleScript名:Text Utilitiesで文字の置換を行う
– Created 2017-03-24 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use textLib : script “Text Utilities”
–http://piyocast.com/as/archives/4561

set aStr to “日本語のテキストだよ”
set bStr to replace text “日本語” with “にほんご” in aStr
–>  ”にほんごのテキストだよ”

★Click Here to Open This Script 

AppleScript名:Text Utilitiesで文字の置換を行う(複数一括置換)
– Created 2017-03-24 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use textLib : script “Text Utilities”
–http://piyocast.com/as/archives/4561

set aStr to “日本語のテキストだよ日本語かも日本語だったら日本語なのに”
set bStr to replace text “日本語” with “にほんご” in aStr
–>  ”にほんごのテキストだよにほんごかもにほんごだったらにほんごなのに”

★Click Here to Open This Script 

AppleScript名:Text Utilitiesで文字列の大文字,小文字の変換を行う
– Created 2017-03-24 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use textLib : script “Text Utilities”
–http://piyocast.com/as/archives/4561

set aStr to “piyomaru software”
set bStr to transform text aStr to upper case
–>  ”PIYOMARU SOFTWARE”

set cStr to transform text bStr to lower case
–>  ”piyomaru software”

set dStr to transform text aStr to word case
–>  ”Piyomaru Software”

★Click Here to Open This Script 

AppleScript名:Text Utilitiesで前後の空白を削除する(1行のテキスト)
– Created 2017-03-24 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use textLib : script “Text Utilities”
–http://piyocast.com/as/archives/4561

set aStr to ” This string starts with some space characters.この文字列はいくつかの空白文字ではじまっています。”
set bStr to remove white space around aStr
–>  ”This string starts with some space characters.この文字列はいくつかの空白文字ではじまっています。”

set cStr to ” This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。 “
set dStr to remove white space around cStr
–>  ”This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。”

★Click Here to Open This Script 

AppleScript名:Text Utilitiesで前後の空白を削除する(複数行のテキスト)
– Created 2017-03-24 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use textLib : script "Text Utilities"
–http://piyocast.com/as/archives/4561

set aStr to " This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。
This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。
This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。
This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。
"
set bStr to remove white space around aStr
(*
"This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。
This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。
This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。
This string starts with some space characters.この文字列は前後にいくつかの空白文字があります。
"
*)

★Click Here to Open This Script 

2017/03/23 Sharing UtilitiesでTwitterにメッセージ投稿

「Sharing Utilities Library」を使ってTwitterで指定のメッセージを投稿するAppleScriptです。

Blindでそのまま送信するわけではなく、いったんダイアログが表示されます。

tweet1_resized.png

Twitter投稿ダイアログが表示されるので、

tweet2.png

「Post」をクリックすると、

tweet3.png

Twitterに指定メッセージが投稿されます。以前に掲載した「ASOCでTwitter投稿」と処理内容は一緒です。

blindで投稿したい場合には、コマンドラインから使うTwitterクライアント「tw」を呼び出す方法がおすすめです。

AppleScript名:Sharing UtilitiesでTwitterにメッセージ投稿
– Created 2017-03-23 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use shareLib : script “Sharing Utilities”
–http://piyocast.com/as/archives/4556

tweet text “ぴよー”

★Click Here to Open This Script 

2017/03/23 Sharing UtilitiesでAirDrop経由のファイル送信を行う

AirDrop経由でファイルを他のMacに送信するAppleScriptです。

ファイル共有やTwitterでメッセージ共有を行う「Sharing Utilities library」の存在は今日の今日まで知りませんでした(なにそれ?!)。

この「Sharing Utilities library」を用いて、AirDropで他のMacにファイルを送信します。

実行する前提条件は、2台のMacが無線LANで通信可能なこと、あらかじめ双方でAirDropによるファイル送信を行なった実績があること。受信側のMacではFinderの「AirDrop」を選択して受信状態にしてあること(↓ 受信側)。

airdrop0_resized.png

その状態で、送信側のMacで本Scriptを実行すると、

airdrop1.png

相手のMac(MBA11)が見つからないので、「Don’t see who you’re looking for?」をクリック。

airdrop2.png

「Search for an Older Mac」 をクリック。

airdrop3.png

送信先(MBA11)をクリックすると、ファイル送信が開始されます。

airdrop4.png

送信完了。

連続で複数のファイルを送信する場合には、指定フォルダ内のファイルへの参照を取得しておくなどしてください。

airdrop5.png

iPhoneやiPadにも送信できます。ただ、送信したファイルをオープンできるアプリがインストールされていないとどうにもならない感じですけれども(汗)

「Sharing Utilities library」はソースコードも見られるようになっているので、見ておくととても参考になります。

AppleScript名:AirDropでファイルを送信する
– Created 2017-03-23 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use shareLib : script “Sharing Utilities”
–http://piyocast.com/as/archives/4554

set anItem to choose file with prompt “Select File to send via AirDrop”
set aaList to {anItem}
begin AirDrop with aaList

★Click Here to Open This Script 

2017/03/22 NSImageのリサイズ(アンチエイリアス解除)

アンチエイリアスをかけずにNSImageを拡大するAppleScriptです。

何も考えずにNSImageをリサイズするとアンチエイリアスがかかります。これはこれで、写真やスクリーンショットを拡大する場合には助かるわけですが、そういう場合ばかりとはかぎりません。

QRコードやバーコードなど、AppleScriptで作成する機会の増えたこれらの認識用画像データは、そのままではサイズが小さいため、適宜リサイズする必要があります。その際にアンチエイリアスがかかるのは避けたいところ。

ML上で質問してShane Stanleyといろいろやりとりして、「こうしたらいいんじゃないの?」というコードがなかなか動かない。エラーで止まる。自分で調べてみても、どうやらこの方法で間違っていないはず。これで動かないということは、OSのバグ? なんだこりゃ、どうしたら動くんだろう??

bab79125-9bdb-46aa-9849-5793d41a0453_resized.png

Shaneの出した結論が「”Antlialias”と書くべきところを”AntiAlias”と大文字小文字を間違えて書いていた」というもの。これは、エディタ側でもチェック対象外だし、なかなか気づきません(T_T)

c75cc957-5c4c-4f4b-b79c-e0f02a8de207.png
▲普通にQRコード画像を拡大したもの。アンチエイリアスが効いている。iPhoneのカメラで読み取って認識できないわけではないが、気になる

a9cf82f4-7f1c-4aa0-93d0-d034e164d7e0.png
▲本ScriptでQRコード画像を拡大したもの。アンチエイリアスを無効化できている

AppleScript名:NSImageをリサイズ(アンチエイリアス解除)pattern 4
– Created 2017-02-03 by Takaaki Naganoya
– Modified 2017-03-22 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4544

set aPath to POSIX path of (choose file of type {“public.image”} with prompt “Select Image file to scale up (x10)”)
set aNSImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aPath

set resizedImg to my resizeNSImageWithoutAntlialias:aNSImage toScale:10

set aDesktopPath to (current application’s NSProcessInfo’s processInfo()’s environment()’s objectForKey:(“HOME”))’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
set fRes to saveNSImageAtPathAsPNG(resizedImg, savePath) of me

–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 current application’s NSBitmapImageRep’s alloc()’s initWithBitmapDataPlanes:(missing value) pixelsWide:aWidth pixelsHigh:aHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:true isPlanar:false colorSpaceName:(current application’s NSCalibratedRGBColorSpace) bytesPerRow:0 bitsPerPixel:0
  
  
set newSize to {width:aWidth, height:aHeight}
  
aRep’s setSize:newSize
  
  
current application’s NSGraphicsContext’s saveGraphicsState()
  
  
set theContext to current application’s NSGraphicsContext’s graphicsContextWithBitmapImageRep:aRep
  
current application’s NSGraphicsContext’s setCurrentContext:theContext
  
theContext’s setShouldAntialias:false
  
theContext’s setImageInterpolation:(current application’s NSImageInterpolationNone)
  
  
aSourceImg’s drawInRect:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) fromRect:(current application’s NSZeroRect) operation:(current application’s NSCompositeCopy) fraction:(1.0)
  
  
current application’s NSGraphicsContext’s restoreGraphicsState()
  
  
set newImg to current application’s NSImage’s alloc()’s initWithSize:newSize
  
newImg’s addRepresentation:aRep
  
  
return newImg
end resizeNSImageWithoutAntlialias:toScale:

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
  
return aRes
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2017/03/19 QuickTime Player+macOS Sierraでのファイル保存

macOS 10.12上でQuickTime PlayerをAppleScriptからコントロールして、デスクトップ上にムービーを保存しようとすると、エラーが出て実行できません。

mov1.png

mov2.png

AppleScriptを実行すると、まずQuickTime Player側で「権限がない」云々というエラーが表示され、QuickTime Player側のエラーダイアログをクリックすると、スクリプトエディタ側でエラーが表示されます。

Appleにバグレポートしておきましたが、Apple的にはバグではなく「仕様」だと見なされている可能性があります。

デスクトップにムービーを保存することはできませんでしたが、ためしてみたら「ムービー」フォルダに保存することはできました

こういう「仕様」を説明もなしに決定されるのは迷惑ですし、きちんとリリースノートなどで説明してほしいものです。

AppleScript名:QTPlayerのバグ再現
set a to (choose file name) as string

tell application “QuickTime Player”
  tell document 1
    save in file a
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:QTPlayerのバグ再現2
set a to (choose file name)

tell application “QuickTime Player”
  tell document 1
    save in a
  end tell
end tell

★Click Here to Open This Script 

2017/03/18 文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v3

複数の文字列同士の近似度を擬似的に計算できないかと考えて、文字列をCountedSetに変換して、CountedSet同士で演算(intersectSet, minusSet)を実行。その計算結果を加味して「文字列中に含まれている文字列の傾向が似ている」と判断するテストのAppleScriptです。

結局、いちばん差分が小さくて(minusSet)、重複部分が大きい(intersectSet)ものを「一番似ている」ものとして扱うようにソートして求めてみました。

実際には、こんな小さい文字列同士のデータの傾向ではなく、もっと大きな文字列同士の傾向を比較することを念頭に置いています。

今回は文字を分解して「文字」同士で比較していますが、「単語」で比較したほうがいいんだろうか、文字のままでいいんだろうかなど、結論は出ていないので実際のデータでいろいろ試してみたいところです。

AppleScript名:文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v3
– Created 2017-03-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4534

set aStr to "This is a pinnapple."
set bStr to "This is an apple."
set cStr to "Piyomaru San Dayo."

set aRes to stringDifferenceWithAppearance(aStr, bStr, "A & B") of me
–>  {intersect:11, minus:3}

set bRes to stringDifferenceWithAppearance(bStr, cStr, "B & C") of me
–>  {intersect:5, minus:8}

set cRes to stringDifferenceWithAppearance(cStr, aStr, "C & A") of me
–>  {intersect:5, minus:9}

–結果をソートする
set allRes to current application’s NSArray’s arrayWithArray:{aRes, bRes, cRes}
set aSortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:"intersect" ascending:false
set bSortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:"minus" ascending:true
set aSortedArray to allRes’s sortedArrayUsingDescriptors:{aSortDesc, bSortDesc}

–もっとも似ている傾向を持つデータの組み合わせ
set aMostApproximative to aSortedArray’s firstObject() as record
–>  {minus:3, dataLabel:"A & B", intersect:11}

–文字列同士の近似傾向を、相違点と重複箇所の2つの方向から計算する
on stringDifferenceWithAppearance(aStr, bStr, aLabel)
  set aMinus to getApproximationBetweenStrings(aStr, bStr) of me
  
set aIntersect to getApproximationBetweenStringsIntersect(aStr, bStr) of me
  
return {intersect:aIntersect, minus:aMinus, dataLabel:aLabel}
end stringDifferenceWithAppearance

–与えられた文字列同士の相違点を計算する(結果がより小さい値のデータ同士が似ている)
on getApproximationBetweenStrings(aStr, bStr)
  set aList to current application’s NSMutableArray’s arrayWithArray:(characters of aStr)
  
set bList to current application’s NSMutableArray’s arrayWithArray:(characters of bStr)
  
  
set aIndex to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bIndex to current application’s NSCountedSet’s alloc()’s initWithArray:bList
  
  
aIndex’s minusSet:bIndex
  
set aRes to aIndex’s allObjects()’s |count|()
  
  
bIndex’s minusSet:aIndex
  
set bRes to bIndex’s allObjects()’s |count|()
  
  
if aRes bRes then
    return bRes
  else
    return aRes
  end if
end getApproximationBetweenStrings

–与えられた文字列同士の重複箇所を計算する(結果がより大きい値のデータ同士が似ている)
on getApproximationBetweenStringsIntersect(aStr, bStr)
  set aList to current application’s NSMutableArray’s arrayWithArray:(characters of aStr)
  
set bList to current application’s NSMutableArray’s arrayWithArray:(characters of bStr)
  
  
set aIndex to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bIndex to current application’s NSCountedSet’s alloc()’s initWithArray:bList
  
  
aIndex’s intersectSet:bIndex
  
set aRes to aIndex’s allObjects()’s |count|()
  
return aRes
end getApproximationBetweenStringsIntersect

★Click Here to Open This Script 

2017/03/18 文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v2

複数の文字列同士の近似度を擬似的に計算できないかと考えて、文字列をCountedSetに変換して、CountedSet同士でand演算(intersectSet)を実行。その計算結果が大きいほど「文字列中に含まれている文字列の傾向が似ている」と判断するテストのAppleScriptです。

countedset_intersect_resized.png

とりあえずはintersectSetで積集合を計算しています。重複している部分を求めているわけです。

 「This is an apple.」と「This is a pinapple.」–> 11
 「This is a pinapple.」と「Piyomaru San Dayo.」–> 5
 「Piyomaru San Dayo.」と「This is an apple.」–> 5

v1とv2の計算結果を合わせて、両方の傾向を反映させるようにするとよいのかもしれません。

AppleScript名:文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v2
– Created 2017-03-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4532

set aStr to "This is a pinnapple."
set bStr to "This is an apple."
set cStr to "Piyomaru San Dayo."

set a1Res to getApproximationBetweenStringsIntersect(aStr, bStr) of me
–>  11

set bRes to getApproximationBetweenStringsIntersect(bStr, cStr) of me
–>  5

set cRes to getApproximationBetweenStringsIntersect(cStr, aStr) of me
–>  5

on getApproximationBetweenStringsIntersect(aStr, bStr)
  set aList to current application’s NSMutableArray’s arrayWithArray:(characters of aStr)
  
set bList to current application’s NSMutableArray’s arrayWithArray:(characters of bStr)
  
  
set aIndex to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bIndex to current application’s NSCountedSet’s alloc()’s initWithArray:bList
  
  
aIndex’s intersectSet:bIndex
  
set aRes to aIndex’s allObjects()’s |count|()
  
return aRes
end getApproximationBetweenStringsIntersect

★Click Here to Open This Script 

2017/03/18 文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v1

複数の文字列同士の近似度を擬似的に計算できないかと考えて、文字列をCountedSetに変換して、CountedSet同士で減算を実行。その計算結果が少ないほど「文字列中に含まれている文字列の傾向が似ている」と判断するテストのAppleScriptです。

countedset.png

とりあえずはminusSetで減算を行なっていますが、ほかの方法も試してみたいところです。

本Scriptでは、得られた結果の数値が小さければ重複している文字が多いということで、計算結果そのものにはあまり意味はありませんが、複数の結果を大小比較して、数値が小さいもののペアが「似たような傾向を持つもの」として期待できます。

 「This is an apple.」と「This is a pinapple.」–> 3
 「This is a pinapple.」と「Piyomaru San Dayo.」–> 8
 「Piyomaru San Dayo.」と「This is an apple.」–> 9

ということで、これらの間では「This is an apple.」と「This is a pinapple.」の近似度が一番高いといえることになります。

AppleScript名:文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v1
– Created 2017-03-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4530

set aStr to “This is a pinnapple.”
set bStr to “This is an apple.”
set cStr to “Piyomaru San Dayo.”

set a1Res to getApproximationBetweenStrings(aStr, bStr) of me
–>  3

set bRes to getApproximationBetweenStrings(bStr, cStr) of me
–>  8

set cRes to getApproximationBetweenStrings(cStr, aStr) of me
–>  9

on getApproximationBetweenStrings(aStr, bStr)
  set aList to current application’s NSMutableArray’s arrayWithArray:(characters of aStr)
  
set bList to current application’s NSMutableArray’s arrayWithArray:(characters of bStr)
  
  
set aIndex to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bIndex to current application’s NSCountedSet’s alloc()’s initWithArray:bList
  
  
aIndex’s minusSet:bIndex
  
set aRes to aIndex’s allObjects()’s |count|()
  
  
bIndex’s minusSet:aIndex
  
set bRes to bIndex’s allObjects()’s |count|()
  
  
if aRes bRes then
    return bRes
  else
    return aRes
  end if
end getApproximationBetweenStrings

★Click Here to Open This Script