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

投稿者: Takaaki Naganoya

プログラムで作ったアラートダイアログ上のTextViewをフォーカスする

Posted on 4月 14, 2020 by Takaaki Naganoya

AppleScriptでダイナミックに(動的に)生成したアラートダイアログ上にテキストビューを生成し、そのテキストビュー上にテキスト入力用のキャレットを移動させる(フォーカスする)AppleScriptです。

Xcode上でAppleScriptでGUIアプリケーションを作っていて、文字サイズの大きなテキスト入力フィールドを動的に生成。さらに、テキスト入力フィールドにテキスト入力用のキャレットを移動させようとして、割と手こずっていました。

テキスト入力を受け付けるGUI部品に対して、入力フォーカスを与え、テキスト入力のキャレットが置かれた状態を再現するには、GUIの画面上からはマウスカーソルでクリック操作するとできます。同じ状態をプログラム側から作る場合にはFirstResponderにセットするという処理になります。

この、FirstResponderに指定することは理解でき、Xcode(Interface Builder)上で配置した部品に対して実行する処理は昔からやっています。NSTextFieldに強制的に入力フォーカスを置いた上でバーコードリーダーからのテキスト入力を受け付けるといった処理です。

一方、プログラムから動的に生成したGUI部品に対して、Cocoaが用意しているAPIのどれを用いて、どのように指定すべきなのかで困りました。

親WindowのFirstResponderにしてみたり、アラートウィンドウのFirstResponderにしてみたり、いろいろ試した末に、

parentWin's setInitialFirstResponder:aView

で、Alert DialogのWindowをparentWinに、alert dialog上のテキストビューをaViewに代入した状態で実行して、入力キャレットをテキストビュー上に移動できました。本Xcode Projectは上記の1行の記述の検証を行うためのテストプロジェクトです。

–> Download Xcode Project

AppleScript名:AppDelegate.applescript
—
— AppDelegate.applescript
— dialogCarret
—
— Created by Takaaki Naganoya on 2020/04/14.
— Copyright © 2020 Takaaki Naganoya. All rights reserved.
—

script AppDelegate
  property parent : class "NSObject"
  
  
property |NSURL| : a reference to current application’s |NSURL|
  
property NSFont : a reference to current application’s NSFont
  
property NSView : a reference to current application’s NSView
  
property NSAlert : a reference to current application’s NSAlert
  
property NSColor : a reference to current application’s NSColor
  
property NSTextView : a reference to current application’s NSTextView
  
property NSScrollView : a reference to current application’s NSScrollView
  
property NSRunningApplication : a reference to current application’s NSRunningApplication
  
  
— IBOutlets
  
property theWindow : missing value
  
  
property returnCode : 0
  
property resStr : ""
  
  
  
on applicationWillFinishLaunching:aNotification
    —
  end applicationWillFinishLaunching:
  
  
on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
on clicked:sender
    set tmpStr to "TEST"
    
set paramObj to {myMessage:"Input Text", mySubMessage:"", mes1:(tmpStr), mesWidth:400, mesHeight:200}
    
my performSelectorOnMainThread:"dispTextViewWithAlertdialog:" withObject:paramObj waitUntilDone:true
    
set aResText to (my resStr) as string
    
    
display dialog aResText
  end clicked:
  
  
on dispTextViewWithAlertdialog:paramObj
    –Receive Parameters
    
set aMainMes to (myMessage of paramObj) as string –Main Message
    
set aSubMes to (mySubMessage of paramObj) as string –Sub Message
    
set mesStr to (mes1 of paramObj) as string –Text Input field 1 Label
    
set aWidth to (mesWidth of paramObj) as integer –TextView width
    
set aHeight to (mesHeight of paramObj) as integer –TextView height
    
    
set vNum to system attribute "sys2"
    
if vNum > 13 then
      set apRes to retLightOrDark() of me
      
if apRes = true then
        set textColor to (current application’s NSColor’s cyanColor())
      else
        set textColor to (current application’s NSColor’s blackColor())
      end if
    else
      set textColor to (current application’s NSColor’s blackColor())
    end if
    
    
    
— Create a TextView with Scroll View
    
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
    
set aView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
    
aView’s setDelegate:me
    
aView’s setRichText:true
    
aView’s useAllLigatures:true
    
aView’s setTextColor:textColor
    
aView’s setFont:(current application’s NSFont’s systemFontOfSize:90.0)
    
set aColor to current application’s NSColor’s colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.1
    
    
aView’s setBackgroundColor:aColor
    
aView’s setString:mesStr
    
aScroll’s setDocumentView:aView
    
aView’s enclosingScrollView()’s setHasVerticalScroller:true
    
    
— 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 parentWin to its |window|()
    end tell
    
    
parentWin’s setAlphaValue:0.8
    
–parentWin’s setOpaque:(false)
    
parentWin’s setLevel:(current application’s NSScreenSaverWindowLevel)
    
    
parentWin’s setInitialFirstResponder:aView —Place Input Carret to Alert Dialog
    
    
— 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
      —
    else
      set my resStr to aView’s |string|()
    end if
  end dispTextViewWithAlertdialog:
  
  
  
on doModal:aParam
    set (my returnCode) to aParam’s runModal()
  end doModal:
  
  
  
on retLightOrDark()
    return true
  end retLightOrDark
  
end script

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode dialog How To | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSColor NSFont NSMakeRect NSRunningApplication NSScrollView NSTextView | Leave a comment

システムフォントの名称を取得

Posted on 4月 13, 2020 by Takaaki Naganoya

システムフォントの名称を取得するAppleScriptです。

GUIベースのアプリケーションを作っているときに、「もう、無難なフォントなんでもいいからとりあえず指定できるものがあれば指定しておこうよ」という局面はあります。ないフォントを指定するとクラッシュすることもあるので、とりあえずコレ指定しておけば大丈夫だから!

という「安全パイ」のフォントとしてSystem Fontを取得&指定したいという時に書いたScriptでもあります。プログラム内容がつまらない割には、切実なニーズを満たすためのものです。

AppleScript名:システムフォントの名称を取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/04/06
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

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

set aFont to current application’s NSFont’s systemFontOfSize:24.0
set aInfo to aFont’s fontDescriptor()
set aSize to (aInfo’s pointSize()) as real
set aPSName to (aInfo’s postscriptName()) as string
–> ".SFNSDisplay"

set bFont to current application’s NSFont’s boldSystemFontOfSize:24.0
set bInfo to bFont’s fontDescriptor()
set bSize to (bInfo’s pointSize()) as real
set bPSName to (bInfo’s postscriptName()) as string
–> ".SFNSDisplay-Bold"

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode Font | Tagged 10.13savvy 10.14savvy 10.15savvy NSFont | Leave a comment

NSFontPanelでフォントを選択

Posted on 4月 7, 2020 by Takaaki Naganoya

自前でメニューを作ってフォントおよびサイズの選択を行おうとするとめんどくさい(時間がかかるし管理も大変)ので、NSFontPanelを用いてフォントを選択する試作品を作ってみました。

ただし、まだ完全ではなく「1つ前の状態が取得できる」という状態なので、修正が必要です。作成はmacOS 10.14.6+Xcode 11.3.1上で行っています。

–> Download Xcode Project

AppleScript名:AppDelegate.applescript
—
— AppDelegate.applescript
— fontPanel
—
— Created by Takaaki Naganoya on 2020/03/12.
— Copyright © 2020 Takaaki Naganoya. All rights reserved.
—

script AppDelegate
  property parent : class "NSObject"
  
  
— IBOutlets
  
property theWindow : missing value
  
property theField : missing value
  
  
property aFontNameField : missing value
  
property aFontSizeField : missing value
  
  
property aFontManager : missing value
  
  
property font : missing value
  
property aFP : missing value
  
  
on applicationWillFinishLaunching:aNotification
    set aFontManager to current application’s NSFontManager’s sharedFontManager()
    
aFontManager’s setAction:"appSpecificChangeFont:"
    
set aFP to current application’s NSFontPanel’s sharedFontPanel()
    
set aFont to current application’s NSFont’s fontWithName:"Helvetica" |size|:16
    
aFontManager’s setSelectedFont:aFont isMultiple:false
    
    
theField’s setStringValue:"ぴよまるソフトウエア, Piyomaru Software"
  end applicationWillFinishLaunching:
  
  
on applicationShouldTerminate:sender
    — Insert code here to do any housekeeping before your application quits
    
return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
on clicked:aSender
    theWindow’s makeFirstResponder:theField
    
aFP’s makeKeyAndOrderFront:me
  end clicked:
  
  
on appSpecificChangeFont:sender
    set aSelFont to sender’s selectedFont()
    
set aDesc to aSelFont’s fontDescriptor()
    
set aPSID to (aDesc’s postscriptName()) as string
    
set aSize to (aDesc’s pointSize()) as real
    
theField’s setFont:aSelFont
    
aFontNameField’s setStringValue:aPSID
    
aFontSizeField’s setStringValue:(aSize as string)
  end appSpecificChangeFont:
end script

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode Font | Tagged 10.13savvy 10.14savvy 10.15savvy NSFont NSFontManager NSFontPanel | 1 Comment

パネル構成書類「入出力音量調整」

Posted on 4月 3, 2020 by Takaaki Naganoya

新型コロナウィルスの蔓延により、リモートワークでビデオ/オーディオチャットを行う機会が増え、音量調整のためのいい方法がないと嘆いている方が多いようなので、便利な方法をご紹介します。

macOSにはアクセシビリティ系(障害者支援)の機能が多々用意されており、その中核をなす「スイッチコントロール」という機能があります。この機能を利用して、画面上に操作可能な機能ボタンを貼り付けておくことができるため、オーディオ系の音量調整機能などをここに割り振って利用すると便利です。

# 業務系のScriptをフローティングパレット表示しておいて、ワンクリックで実行できるようにしておくと、とても便利だと思います

このスイッチコントロールの機能はユーザーごとに自由にカスタマイズすることができ、さらにその内容を書類に書き出して他のユーザーと共有できるようになっています。

それがパネル構成書類です。これはmacOS標準装備の機能「スイッチコントロール」の書類で、ボタン定義データやボタンから呼び出すAppleScriptが内包されています。

–> Download switchControlUserPanels1.ascconfig(12KB)

[Update] English Version included in same panel

このパネル構成書類はmacOS 10.14上で作成し、macOS10.14.6およびmacOS 10.15.4上での動作を確認しています。

Zipアーカイブから展開して、ユーザーの~/Library/Application Support/com.apple.AssistiveControlフォルダに入れてください。macOS 10.15上では展開後.ascconfig書類がフォルダに見えるかもしれませんが、気にせずそのまま上記フォルダに移動してください。

# 「com.apple.AssistiveControl」フォルダがApplication Support内に存在しない場合には作成してください。このフォルダ名を間違えるとOSに認識されないので、ご注意ください

「システム環境設定」を起動。

「アクセシビリティ」を選択。

左の一覧リストの下の方にある「スイッチコントロール」を選択。

「スイッチコントロールを有効にする」にチェックを入れます。この際に、管理者パスワードを求められる場合があります。

フローティング操作パレット「スイッチコントロール」のホーム画面が表示されるので、「カスタム」をクリック。

カスタムの中にさきほど追加した「入出力音量調整」があるので、それをクリック(”Sound Volume Control” is English version)。

これで、フローティングパレットの入出力音量調整パレットが表示されます。


▲SwitchControlの操作方法。macOS標準のGUI体系とは若干異なる

パレット上の各ボタンをクリックするか、あるいはMacのサブディスプレイとしてiPadを接続している場合には、iPadの画面上を指でタップすると各ボタンの機能が実行されます。

スイッチコントロールのフローティングパレットはパレット左上の「x」をクリックすると消去できます。いったん消去しても、ふたたび「システム環境設定」>「アクセシビリティ」>「スイッチコントロール」に戻って、「スイッチコントロールを有効にする」のチェックを入れると表示されます。

iPadをMacのサブディスプレイとして利用するアプリケーションは、Apple純正のSide Car(macOS 10.15標準装備)のほか、サードパーティ製の「duet display」や「AirDisplay」などが有名です。

とくに、サードパーティ製のアプリケーションは、古めのiOSや古めのiPadをサポートしている(Apple純正機能は最新のiPadしかサポートしていない)ため、身の回りに転がっている古いiPadや中古のiPadをMacのタッチ操作コントローラーにお安く利用できるため、とてもおすすめです。

Posted in news Tools | Tagged 10.14savvy 10.15savvy Switch Control | Leave a comment

NSURLSessionでREST API呼び出しv4.4.2a

Posted on 4月 3, 2020 by Takaaki Naganoya

REST API呼び出しに欠かせない部品をアップデートしました。URLキャッシュが効くようになったような気がします。

–> GET method REST API v4.4.1
–> GET method REST API v4.4
–> GET method REST API v4.3
–> GET method REST API v4.1
–> GET method REST API v4

前バージョンではmacOS 10.15上でクラッシュしないかわりにURLキャッシュが効かないという特徴がありました。ただ、edama2さんと協議した結果、macOS 10.15上でクラッシュする原因と考えられていた内容にあまり根拠がないことがわかってきました(人ごとではなく自分のことなのですが)。

可能な範囲でトライアル&エラーで調査を行なったところ、本バージョンのような処理に落ち着きました。AppleScriptからのCocoa利用については明確にドキュメントがAppleから出ているわけではないので、すでに存在するObjective-Cのプログラムの処理を参考にしつつ、Objective-CからAppleScriptへの置き換えが可能かを検討しています。

URLキャッシュについては、(当然のことながら)処理1回目には効きません。なぜか2回目も効きません。3回目とか4回目あたりから効いていることが実感できる感じです(回数ではなく、前回処理時からの経過時間を見ているのかも? GUIアプリケーションに入れて使うと2回目から効いたりします)。URLキャッシュが効いていない場合には1.5秒ぐらい、URLキャッシュが効き出すと0.02秒ぐらいで結果が返ってきています。

macOS 10.13/14/15で検証を行い、繰り返し処理を行ってもクラッシュしないことを確認しています。ただ、動作保証するというレベルではないので(本Blog掲載のScriptすべてそうですが)、問題があったら知らせてください。

ためしに、Xcode上で作成したGUIベースのAppleScriptアプリケーションに本処理を導入したところ、REST APIへの問い合わせがキャッシュされて著しい高速化を実現できました。

AppleScript名:GET method REST API v4.4.2a_wikipedia.scptd
— Created 2019-05-02 by Takaaki Naganoya
— Modified 2020-04-03 by Takaaki Naganoya
— 2020 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSURLCache : a reference to current application’s NSURLCache
property NSURLSession : a reference to current application’s NSURLSession
property NSMutableData : a reference to current application’s NSMutableData
property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSOperationQueue : a reference to current application’s NSOperationQueue
property NSURLComponents : a reference to current application’s NSURLComponents
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLSessionConfiguration : a reference to current application’s NSURLSessionConfiguration
property NSURLRequestReturnCacheDataElseLoad : a reference to current application’s NSURLRequestReturnCacheDataElseLoad

property retData : missing value
property retCode : 0
property retHeaders : 0
property drecF : false
property aSession : missing value
property aCache : missing value

on run
  set retData to missing value
  
set drecF to false
  
set aSession to missing value
  
  
set aQueryKeyTitle to "AppleScript"
  
set aRes to getWikiText(aQueryKeyTitle) of me
  
return aRes
end run

on getWikiText(aQueryKeyTitle)
  set reqURLStr to "https://en.wikipedia.org/w/api.php"
  
set aRec to {action:"parse", page:aQueryKeyTitle, |prop|:"wikitext", format:"json"}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestGETAPIAndParseResults(aURL, 5) of me
  
if aRes = missing value then return false
  
  
set bRes to (aRes’s valueForKeyPath:"parse.wikitext.*") as string
  
return bRes
end getWikiText

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(reqURLStr as string, timeoutSec as integer)
  set (my retData) to NSMutableData’s alloc()’s init()
  
set (my retCode) to 0
  
set (my retHeaders) to {}
  
set (my drecF) to false
  
  
–if my aCache = missing value then
  
set cachePath to (POSIX path of (path to library folder from user domain)) & "/Caches/AppleScriptURLCache"
  
set my aCache to NSURLCache’s alloc()’s initWithMemoryCapacity:512000 diskCapacity:1024 * 1024 * 5 diskPath:cachePath
  
–end if
  
NSURLCache’s setSharedURLCache:(my aCache)
  
  
  
set aURL to |NSURL|’s URLWithString:reqURLStr
  
set aRequest to NSMutableURLRequest’s requestWithURL:aURL
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setTimeoutInterval:timeoutSec
  
aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding"
  
aRequest’s setValue:"AppleScript/Cocoa" forHTTPHeaderField:"User-Agent"
  
aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type"
  
  
  
set aConfig to NSURLSessionConfiguration’s defaultSessionConfiguration()
  
aConfig’s setRequestCachePolicy:(NSURLRequestReturnCacheDataElseLoad)
  
  
aConfig’s setURLCache:(my aCache)
  
  
–どちらでも速度差がない
  
set my aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(NSOperationQueue’s mainQueue())
  
–set my aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(missing value)
  
  
set aTask to aSession’s dataTaskWithRequest:aRequest
  
  
aTask’s resume() –Start URL Session
  
  
repeat (1000 * timeoutSec) times
    if (my drecF) is not equal to false then
      exit repeat
    end if
    
delay "0.001" as real
  end repeat
  
  
–delegateの無効化
  
my aSession’s finishTasksAndInvalidate()
  
set my aSession to missing value
  
  
return my parseSessionResults()
end callRestGETAPIAndParseResults

on URLSession:tmpSession dataTask:tmpTask didReceiveData:tmpData
  (my retData)’s appendData:tmpData
end URLSession:dataTask:didReceiveData:

on URLSession:tmpSession task:tmpTask didCompleteWithError:tmpError
  if tmpError = missing value then
    set (my drecF) to true
  else
    error "Donwload Failed"
  end if
end URLSession:task:didCompleteWithError:

on parseSessionResults()
  set resStr to NSString’s alloc()’s initWithData:(my retData) encoding:(NSUTF8StringEncoding)
  
set jsonString to NSString’s stringWithString:(resStr)
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
return aJsonDict
end parseSessionResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to 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 (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

Posted in Internet JSON REST API URL | Tagged 10.13savvy 10.14savvy 10.15savvy NSJSONSerialization NSMutableData NSMutableDictionary NSMutableURLRequest NSOperationQueue NSString NSURL NSURLCache NSURLComponents NSURLQueryItem NSURLRequestReturnCacheDataElseLoad NSURLSession NSURLSessionConfiguration NSUTF8StringEncoding | 1 Comment

NSURLSessionでREST API呼び出しv4.4.1

Posted on 4月 2, 2020 by Takaaki Naganoya

REST API呼び出しに欠かせない部品をアップデートしました。macOS 10.15でクラッシュしないように書き換えています。

–> GET method REST API v4.4
–> GET method REST API v4.3
–> GET method REST API v4.1
–> GET method REST API v4

前バージョンではmacOS 10.15上でクラッシュするかわりにmacOS 10.14上で高速という特徴がありました。ただ、macOS 10.15上でクラッシュしないように調整を行っていくと、処理速度については別段速くもなく(キャッシュがヒットしていない雰囲気)、よりいっそうキャッシュについて調べる必要が出てきています。

一応、キャッシュの作り方については調べてみたものの、

キャッシュが効いている雰囲気がまったくありません。

キャッシュの内容を確認してみると、からっぽですね(^ー^; もう少し(時間のあるときに)キャッシュの連携などを調べておくといいのかもしれません。最低限、macOS 10.15でクラッシュしなくなったので、NSURLConnectionを呼び出してリジェクトを喰らわないように準備しておくという感じでしょうか。

AppleScript名:GET method REST API v4.4.1_wikipedia.scptd
— Created 2019-05-02 by Takaaki Naganoya
— Modified 2020-04-02 by Takaaki Naganoya
— 2020 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSURLSession : a reference to current application’s NSURLSession
property NSMutableData : a reference to current application’s NSMutableData
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLSessionConfiguration : a reference to current application’s NSURLSessionConfiguration

property retData : missing value
property retCode : 0
property retHeaders : 0
property drecF : false
property aSession : missing value
property aCache : missing value

on run
  set retData to missing value
  
set drecF to false
  
set aSession to missing value
  
  
set aQueryKeyTitle to "AppleScript"
  
set aRes to getWikiText(aQueryKeyTitle) of me
  
return aRes
end run

on getWikiText(aQueryKeyTitle)
  set reqURLStr to "https://en.wikipedia.org/w/api.php"
  
set aRec to {action:"parse", page:aQueryKeyTitle, |prop|:"wikitext", format:"json"}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestGETAPIAndParseResults(aURL, 5) of me
  
if aRes = missing value then return "Error"
  
  
set bRes to (aRes’s valueForKeyPath:"parse.wikitext.*") as string
  
return bRes
end getWikiText

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(reqURLStr as string, timeoutSec as integer)
  set (my retData) to NSMutableData’s alloc()’s init()
  
set (my retCode) to 0
  
set (my retHeaders) to {}
  
set (my drecF) to false
  
  
–if my aCache = missing value then
  
set cachePath to (POSIX path of (path to library folder from user domain)) & "/Caches/AppleScriptURLCache"
  
set my aCache to current application’s NSURLCache’s alloc()’s initWithMemoryCapacity:512000 diskCapacity:1024 * 1024 * 5 diskPath:cachePath
  
–end if
  
current application’s NSURLCache’s setSharedURLCache:(my aCache)
  
  
  
set aURL to |NSURL|’s URLWithString:reqURLStr
  
set aRequest to NSMutableURLRequest’s requestWithURL:aURL
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setTimeoutInterval:timeoutSec
  
aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding"
  
aRequest’s setValue:"AppleScript/Cocoa" forHTTPHeaderField:"User-Agent"
  
aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type"
  
  
set v2 to system attribute "sys2" –> 14
  
if v2 < 15 then
    –macOS 10.14まで
    
set aConfig to NSURLSessionConfiguration’s defaultSessionConfiguration()
  else
    –macOS 10.15以降。10.15でdefaultSessionConfigurationを使うとクラッシュしやすい???
    
set identifier to "BackgroundSessionConfiguration"
    
set aConfig to NSURLSessionConfiguration’s backgroundSessionConfiguration:(identifier)
  end if
  
  
aConfig’s setRequestCachePolicy:(current application’s NSURLRequestReturnCacheDataElseLoad)
  
  
aConfig’s setURLCache:(my aCache)
  
–aConfig’s setTimeoutIntervalForRequest:0.5
  
–aConfig’s setTimeoutIntervalForResource:timeoutSec
  
–aConfig’s setWaitsForConnectivity:false
  
  
–どちらでも速度差がない
  
set my aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(current application’s NSOperationQueue’s mainQueue())
  
–set my aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(missing value)
  
  
set aTask to aSession’s dataTaskWithRequest:aRequest
  
  
aTask’s resume() –Start URL Session
  
  
repeat (1000 * timeoutSec) times
    if (my drecF) is not equal to false then
      exit repeat
    end if
    
delay "0.001" as real
  end repeat
  
  
–aSession’s invalidateAndCancel() –delegateの無効化
  
my aSession’s finishTasksAndInvalidate()
  
set my aSession to missing value
  
  
return my parseSessionResults()
end callRestGETAPIAndParseResults

on URLSession:tmpSession dataTask:tmpTask didReceiveData:tmpData
  (my retData)’s appendData:tmpData
end URLSession:dataTask:didReceiveData:

on URLSession:tmpSession dataTask:tmpTask willCacheResponse:cacheRes completionHandler:aHandler
  set (my drecF) to true
end URLSession:dataTask:willCacheResponse:completionHandler:

on URLSession:tmpSession task:tmpTask didCompleteWithError:tmpError
  if tmpError = missing value then
    set (my drecF) to true
  else
    error "Donwload Failed"
  end if
end URLSession:task:didCompleteWithError:

on parseSessionResults()
  set resStr to NSString’s alloc()’s initWithData:(my retData) encoding:(NSUTF8StringEncoding)
  
set jsonString to NSString’s stringWithString:(resStr)
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
return aJsonDict
end parseSessionResults

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

★Click Here to Open This Script 

Posted in Internet REST API URL | Tagged 10.13savvy 10.14savvy 10.15savvy NSJSONSerialization NSMutableData NSMutableDictionary NSMutableURLRequest NSString NSURL NSURLCache NSURLComponents NSURLQueryItem NSURLSession NSURLSessionConfiguration NSUTF8StringEncoding | 1 Comment

iWork Apps Update v10.0

Posted on 4月 1, 2020 by Takaaki Naganoya

Keynote、Pages、Numbersのv10.0が配信されました。Keynoteの前バージョンはv9.2、Pagesはv8.2、Numbersはv6.2.1でしたが、バージョン番号を別々に採番することがめんどくさくなったのか、内部のエンジンが一新されたのか、バージョン番号をそろえてきました。

おそらく、マーケティング的な要請やユーザーサポートの手間を省くために番号をそろえてきたのでしょう。

Pages/NumbersについてはmacOS 10.14以上が対象。KeynoteはmacOS 10.15.4が必要とのことですが、いろいろ致命的なバグの修正を含んでいるため、後日macOS 10.14.6向けのKeynoteアップデートが配信されることを期待したいところです。

# アップデートに出てこないだけで、Mac App Storeから個別にKeynote v10.0のダウンロードが(macOS 10.14.6上でも)できました。なんででしょう?

v10.0 UpdateのAppleScript的な変更の有無

■Keynote v10.0:2か所

■Pages v10.0:1か所

■Numbers v10.0:変更点なし
Numbers v6.2で「特定の行数の表を作るとエラーになる」というバグがありましたが、これはv6.2.1で修正されました。これが修正されています(記憶違いだったので、記事を修正しておきます)。

Pages v10.0の変更点

documentにfacing pagesというプロパティが新設されました。見開き表示時にページの左右(奇数ページ、偶数ページ)を考慮して2ページ表示のペアを変更するという機能のようです。

AppleScript名:Pages v10.0で追加されたfacing pagesの操作を行う
tell application "Pages"
  tell front document
    set curStat to facing pages
    
    
set facing pages to not curStat –反転
    
delay 2
    
set facing pages to curStat –元に戻す
  end tell
end tell

★Click Here to Open This Script 

全言語的に普遍的な内容ではあるのですが、日本語ユーザー的には縦書き/横書きの設定属性値を持ってくれたほうがありがたいところです。たぶん、自分は使うことはないと思います。

Keynote v10.0の変更点

Keynoteは前バージョンでかなりおかしなバグが発覚していたので、その修正が行われているかどうか、というのが見所です。

結論からいえば、バグは修正されています。macOSに関して久しぶりに前向きなニュースといえます。彼らにバグを修正する意思と能力が残されていたことについては喜びたいところです。同時に残念なニュースとして、この修正に関連して新たなバグが生まれています(このあたりがいかにもAppleらしい)。

Keynoteバグ1:表(table)作成時に行数によってはエラーになる

5行の表を作るとエラーになり、他の行数でもいろいろエラーになる組み合わせが確認されていました。テスト用のAppleScriptを書いて、行数を2〜100行で変更しつつ作成、列数を2〜30列で変更しつつ作成、行数および列数を順次ループで2〜20まで変更しつつ新規作成するなどのテストを実施。無事、修正を確認できました。

ただし、本テストは表の新規作成についてのみ確認したものであり、既存の表の行数/列数の変更を確認したものではありません。Appleの仕事に関しては、修正点の周囲に新たなバグを生む可能性が高く、修正時と修正後が一番危険な状態です。彼らに「自分の作った機能を動作確認する」という能力を期待してはいけません。信じてもいけません。つねに、疑いの目で見ることが重要です。

AppleScript名:Keynote書類上に表を作成、行数を2から100まで可変
tell application "Keynote"
  tell front document
    tell current slide
      repeat with i from 2 to 100
        set aTable to make new table with properties {header column count:0, header row count:0, row count:i, column count:3}
        
delay 1
        
delete aTable
      end repeat
    end tell
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:Keynote書類上に表を作成、列数を2から30まで可変
tell application "Keynote"
  tell front document
    tell current slide
      repeat with i from 2 to 30
        set aTable to make new table with properties {header column count:0, header row count:0, row count:5, column count:i}
        
delay 1
        
delete aTable
      end repeat
    end tell
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:Keynote書類上に表を作成、行数および列数を2から20まで可変
tell application "Keynote"
  tell front document
    tell current slide
      repeat with x from 2 to 20
        repeat with y from 2 to 20
          set aTable to make new table with properties {header column count:0, header row count:0, row count:y, column count:x}
          
delay 0.01
          
delete aTable
        end repeat
      end repeat
    end tell
  end tell
end tell

★Click Here to Open This Script 

Keynoteバグ2:書類(document)のムービーexport optionsにバグ

数値ではじまる予約語や記号を含む予約語はAppleScriptの言語処理系では宣言できません。エラーになります。それをAppleの(おそらくKeynoteの)担当者がKeynote v7.1のアップデート時に、従来の「small」「midium」「large」といったEnumによる指定から、「360p」「540p」「720p」「1080p」「2160p」という指定を行えるように変更を加えました。

より大きな解像度の書き出しに対処したことは評価できると思いますが、そもそもAppleScriptの処理系で認識できない「数字で始まる予約語」に変えたのはダメダメです(スクリプトエディタ上で構文確認を行うとエラーになる=使えない)。つまり、この担当者はsdefの改変を行なっただけで、実際にコードを書いて動作確認を行っていないことがわかります。

Appleにバグレポートを書きつつ、このような処理が必要な場合にはnative sizeで書き出して、そのあとでムービーをリサイズするような処理で回避していました(GUI Scriptingで乗り切ったScripterもいるようですが)。

Keynote v10.0では「format360p」「format540p」「format720p」「format1080p」「format2160p」と変更され、AppleScriptの構文確認時にエラーでハネられることはなくなりました。この点についてはバグ修正が行われたものと判断してよいと思われます。

ただし、従来動作していたEnum「native size」を指定するとエラーになるようになってしまいました。互換性のために残したが動作していない、といったコメントが書かれているわけでもないため、これはバグだと判断します。

■Keynote書類フォーマットとムービー書き出し時の解像度の対応表(Piyomaru Software独自調査による)

movie export formats 標準(4:3) ワイド(16:9)
format360p 480 × 360 640 × 360
format540p 720 × 540 960 × 540
format720p 960 × 720 1280 × 720
format1080p 1440 × 1080 1920 × 1080
format2160p 2880 × 2160 3840 × 2160
AppleScript名:Keynote 360p movie export test
set targetFileHFSPath to (choose file name) as string –かならずファイル拡張子に「.m4v」を指定する必要がある
if targetFileHFSPath does not end with ".m4v" then
  set targetFileHFSPath to targetFileHFSPath & ".m4v"
end if

with timeout of 3600 seconds
  tell application "Keynote"
    export front document to file targetFileHFSPath as QuickTime movie with properties {movie format:format360p}
  end tell
end timeout

★Click Here to Open This Script 

AppleScript名:Keynote 540p movie export test
set targetFileHFSPath to (choose file name) as string –かならずファイル拡張子に「.m4v」を指定する必要がある
if targetFileHFSPath does not end with ".m4v" then
  set targetFileHFSPath to targetFileHFSPath & ".m4v"
end if

with timeout of 3600 seconds
  tell application "Keynote"
    export front document to file targetFileHFSPath as QuickTime movie with properties {movie format:format540p}
  end tell
end timeout

★Click Here to Open This Script 

AppleScript名:Keynote 720p movie export test
set targetFileHFSPath to (choose file name) as string –かならずファイル拡張子に「.m4v」を指定する必要がある
if targetFileHFSPath does not end with ".m4v" then
  set targetFileHFSPath to targetFileHFSPath & ".m4v"
end if

with timeout of 3600 seconds
  tell application "Keynote"
    export front document to file targetFileHFSPath as QuickTime movie with properties {movie format:format720p}
  end tell
end timeout

★Click Here to Open This Script 

AppleScript名:Keynote 1080p movie export test
set targetFileHFSPath to (choose file name) as string –かならずファイル拡張子に「.m4v」を指定する必要がある
if targetFileHFSPath does not end with ".m4v" then
  set targetFileHFSPath to targetFileHFSPath & ".m4v"
end if

with timeout of 3600 seconds
  tell application "Keynote"
    export front document to file targetFileHFSPath as QuickTime movie with properties {movie format:format1080p}
  end tell
end timeout

★Click Here to Open This Script 

AppleScript名:Keynote 2160p movie export test
set targetFileHFSPath to (choose file name) as string –かならずファイル拡張子に「.m4v」を指定する必要がある
if targetFileHFSPath does not end with ".m4v" then
  set targetFileHFSPath to targetFileHFSPath & ".m4v"
end if

with timeout of 3600 seconds
  tell application "Keynote"
    export front document to file targetFileHFSPath as QuickTime movie with properties {movie format:format2160p}
  end tell
end timeout

★Click Here to Open This Script 

AppleScript名:Keynote ‌‌native size movie export test (Bug)
set targetFileHFSPath to (choose file name) as string –かならずファイル拡張子に「.m4v」を指定する必要がある
if targetFileHFSPath does not end with ".m4v" then
  set targetFileHFSPath to targetFileHFSPath & ".m4v"
end if

with timeout of 3600 seconds
  tell application "Keynote"
    –export front document to file targetFileHFSPath as QuickTime movie with properties {movie format:‌‌native size}
  end tell
end timeout

★Click Here to Open This Script 

Posted in Bug news | Tagged 10.14savvy 10.15savvy Keynote Numbers Pages | 1 Comment

NSURLSessionでREST API呼び出しv4.4

Posted on 3月 29, 2020 by Takaaki Naganoya

REST API呼び出しに欠かせない部品をアップデートしました。

–> GET method REST API v4.3
–> GET method REST API v4.1
–> GET method REST API v4

従来、NSURLConnectionを使って同期処理を行っていましたが、これがDepreceted扱いになりました。すぐになくなることはありませんが、いつかなくなる可能性があります。

このため、NSURLConnectionから動作原理の異なるNSURLSessionを使うよう移行する必要があります(curlコマンドを利用するというルートもあります)。

とはいえ、blocks構文の使えないAppleScriptでこのNSURLSessionを使えるようにするのは大変です。非同期通信を行うNSURLSessionを非同期処理のしにくいAppleScriptで、同期処理っぽく書かなくてはなりません。

NSURLSessionによる通信では、サーバーとの間で何回かのやりとりがあって、データを小分けに複数回受信する必要がありました。NSMutableDataというオブジェクトの存在を知ったのでいい感じに処理できるようになってきました。

NSURLSessionのメリットとして喧伝されている非同期処理は、AppleScriptから呼び出しているかぎりはそれほどメリットになりません。もう少し使い込めば活用できるかもしれませんが、まだそういう段階にはありません。

NSURLSessionの導入によって得られるメリットで最大のものは、キャッシュ機構です。NSURLConnectionでもキャッシュ機構は利用できましたが、NSURLSessionではよりAPIに深く統合されているものと理解しました。

キャッシュの効き方については、設定で何段階かに設定できるとAppleのオンラインドキュメントに書かれています。本Scriptでは、キャッシュ優先でキャッシュが存在している場合にはキャッシュ内のデータを返し、キャッシュが存在していない場合にはWebサーバーに問い合わせを行うように設定しています。

キャッシュが効いている状況なら、NSURLConnectionで1.8秒ぐらいの処理がNSURLSessionで0.1秒程度と大幅に速く処理できます。キャッシュが効いていない状況(初回実行時)だと、NSURLSessionのほうがやや処理に時間がかかるぐらいです。

処理結果について、NSURLConnection版とNSURLSession版で結果の比較を行ってみましたが、とくに差異は見られませんでした。

本サンプルScriptでは、Wikipedia(英語版)へのキーワードの問い合わせを行います。NSURLConnection版では複数回実行しても同じぐらいの速度で実行されますが、NSURLSession版では2度目から早く終わります(Script Debugger上で動かすと秒以下の精度の時間がわかります)。

macOS 10.14上で作ってmacOS 10.14.6/10.13.6上で動作確認しています(スクリプトエディタ、Script Debuggerで動作確認)。ただし、macOS 10.15上だとクラッシュします。Xcodeのプロジェクトに入れてみましたが、同様にmacOS 10.15上ではクラッシュします。

ただし、自分のmacOS 10.14.6環境はSIP解除しているのと、macOS 10.15環境のクラッシュログにSIP関連のメッセージが残っているので、OSバージョンではなくSIPのためかもしれません(macOS 10.14.6でもSIPを有効にするとクラッシュするかもしれない & macOS 10.15.xでもSIPを無効にするとクラッシュしないかもしれない)。

AppleScript名:GET method REST API v4.4_wikipedia
— Created 2019-05-02 by Takaaki Naganoya
— Modified 2020-03-29 by Takaaki Naganoya
— 2020 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSURLSession : a reference to current application’s NSURLSession
property NSMutableData : a reference to current application’s NSMutableData
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLSessionConfiguration : a reference to current application’s NSURLSessionConfiguration

property retData : missing value
property retCode : 0
property retHeaders : 0
property drecF : false

set aQueryKeyTitle to "Steve Jobs" –キーワードの正規化が必要("戦場の絆"→"機動戦士ガンダム 戦場の絆" )
set reqURLStr to "https://en.wikipedia.org/w/api.php"
set aRec to {action:"parse", page:aQueryKeyTitle, |prop|:"wikitext", format:"json"}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL, 10) of me
set bRes to (aRes’s valueForKeyPath:"parse.wikitext.*") as string
return bRes

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(reqURLStr as string, timeoutSec as integer)
  set (my retData) to NSMutableData’s alloc()’s init()
  
set (my retCode) to 0
  
set (my retHeaders) to {}
  
set (my drecF) to false
  
  
set aURL to |NSURL|’s URLWithString:reqURLStr
  
set aRequest to NSMutableURLRequest’s requestWithURL:aURL
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setTimeoutInterval:timeoutSec
  
aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding"
  
aRequest’s setValue:"AppleScript/Cocoa" forHTTPHeaderField:"User-Agent"
  
aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type"
  
  
set aConfig to NSURLSessionConfiguration’s defaultSessionConfiguration()
  
aConfig’s setRequestCachePolicy:(current application’s NSURLRequestReturnCacheDataElseLoad)
  
set aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(missing value)
  
set aTask to aSession’s dataTaskWithRequest:aRequest
  
  
aTask’s resume() –Start URL Session
  
  
repeat (10 * timeoutSec) times
    if (my drecF) = true then
      exit repeat
    end if
    
delay 0.1
  end repeat
  
  
return my parseSessionResults()
end callRestGETAPIAndParseResults

on URLSession:tmpSession dataTask:tmpTask didReceiveData:tmpData
  (my retData)’s appendData:tmpData
end URLSession:dataTask:didReceiveData:

on URLSession:tmpSession dataTask:tmpTask didCompleteWithError:tmpError
  set (my drecF) to true
end URLSession:dataTask:didCompleteWithError:

on URLSession:tmpSession dataTask:tmpTask willCacheResponse:cacheRes completionHandler:aHandler
  set (my drecF) to true
end URLSession:dataTask:willCacheResponse:completionHandler:

on parseSessionResults()
  set resStr to NSString’s alloc()’s initWithData:(my retData) encoding:(NSUTF8StringEncoding)
  
set jsonString to NSString’s stringWithString:(resStr)
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
return aJsonDict
end parseSessionResults

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 filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to filteredArray as {list, missing value}
  
return bList
end filterRecListByLabel1

★Click Here to Open This Script 

AppleScript名:GET method REST API_wikipedia
— Created 2016-03-03 by Takaaki Naganoya
— 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aQueryKeyTitle to "Steve Jobs" –キーワードの正規化が必要("戦場の絆"→"機動戦士ガンダム 戦場の絆" )
set reqURLStr to "https://en.wikipedia.org/w/api.php"
set aRec to {action:"parse", page:aQueryKeyTitle, |prop|:"wikitext", format:"json"}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL, 10) of me
set aRESTres to json of aRes
set bRes to (aRESTres’s valueForKeyPath:"parse.wikitext.*") as string

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL, timeoutSec)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setTimeoutInterval:timeoutSec
  
aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding"
  
aRequest’s setValue:"AppleScript/Cocoa" forHTTPHeaderField:"User-Agent"
  
aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type"
  
  
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
  
set dRes to contents of second item of resList
  
set resCode to (dRes’s statusCode()) as integer
  
  
–Get Response Header
  
set resHeaders to (dRes’s allHeaderFields()) as record
  
  
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 filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to filteredArray as list
  
return bList
end filterRecListByLabel1

★Click Here to Open This Script 

Posted in Internet REST API URL | Tagged 10.13savvy 10.14savvy NSArray NSJSONSerialization NSMutableData NSMutableDictionary NSMutableURLRequest NSPredicate NSString NSURL NSURLComponents NSURLQueryItem NSURLSession NSURLSessionConfiguration NSUTF8StringEncoding | 2 Comments

マウスカーソルを変更

Posted on 3月 24, 2020 by Takaaki Naganoya

Xcode上で記述するAppleScriptアプリケーションにおいて、マウスカーソルを変更するAppleScriptです。

NSCursorにアクセスして、macOS側で用意しているカーソルにマウスカーソルのイメージを変更します。

–> Watch Demo Movie

あらかじめmacOSが用意しているカーソルはいくつかあるわけですが、これで満足するわけがなくて……任意のNSImageをカーソルに指定する方法について調べていたものの、なかなかうまくいかなかったので、このOS側で用意しているカーソルへの切り替えのみまとめておきました。

–> Download Xcode Project

他のアプリケーションに切り替えると通常のカーソルに戻ってしまうので、本プロジェクト側でアプリケーション切り替えのイベントハンドラでカーソルの再変更などを行うべきなのかも。

  set aImage to current application's NSImage's  imageNamed:(current application's NSImageNameComputer)
  set tmpCursor to current application's NSCursor's alloc()'s initWithImage:aImage hotSpot:{0,15}
tmpCursor's |set|()

結局、コンピュータのアイコンをマウスカーソルに指定するというところまでは持って行けた(カーソル画像の差し替えができた)わけなんですが、NSCursorの挙動を評価してみたら、カーソルが標準のものに戻るタイミングがバラバラ(他のディスプレイにカーソルが移動したとき、というわけでもない)で、挙動が謎すぎであります。

その後、対象のビュー(NSViewなど、その派生クラス)にマウスカーソルが入った/出たことを検出するイベントハンドラでマウスカーソルの形状変更を明示的に指定するような実装で落ち着きました(Edama2さんから教えていただきました。ありがとうございます)。

AppleScript名:AppDelegate.applescript
—
— AppDelegate.applescript
— CursorTest
—
— Created by Takaaki Naganoya on 2020/03/24.
— Copyright © 2020 Takaaki Naganoya. All rights reserved.
—

script AppDelegate
  property parent : class "NSObject"
  
  
— IBOutlets
  
property theWindow : missing value
  
  
on applicationWillFinishLaunching:aNotification
    — Insert code here to initialize your application before any files are opened
  end applicationWillFinishLaunching:
  
  
on applicationShouldTerminate:sender
    — Insert code here to do any housekeeping before your application quits
    
return current application’s NSTerminateNoww
  end applicationShouldTerminate:
  
  
on clicked:aSender
    set aTag to (aSender’s tag) as integer
    
if aTag = 100 then
      current application’s NSCursor’s arrowCursor()’s |set|()
    else if aTag = 101 then
      current application’s NSCursor’s disappearingItemCursor()’s |set|()
    else if aTag = 102 then
      current application’s NSCursor’s contextualMenuCursor()’s |set|()
    else if aTag = 103 then
      current application’s NSCursor’s openHandCursor()’s |set|()
    end if
  end clicked:
end script

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode System | Tagged 10.13savvy 10.14savvy 10.15savvy NSCursor NSImage | Leave a comment

指定画像をICNSに変換

Posted on 3月 19, 2020 by Takaaki Naganoya

指定画像をICNS書類に変換するAppleScriptです。

ICNS書類自体は、Classic Mac OS時代にあったアイコンの画像リソースである「icn#」リソースをファイル化したもの、と理解しています(位置付け的に、データ形式とか厳密なレベルで比較したわけではなくて)。

現在では、ICNS書類を作ってアプリケーションアイコンに指定したり、

Xcode上で解像度の異なる画像ファイルをドラッグ&ドロップすると、アプリケーションのICNSファイルを作ってくれます。

Finder上で解像度の異なる画像をフォルダ構造に入れてフォルダの拡張子をつけかえるだけでICNSと認識してくれたような記憶もあります。

そのため、ICNS書類をわざわざScriptから作る需要もないだろうと考えて掲載してこなかったのですが、意外なところでICNS書類の作成を求められました。

自作アプリケーションのカスタム書類を定義して、書類にカスタムアイコンをつけようとしたところ、PNGなどの画像では認識されず、結局ICNS書類を指定する必要に迫られました(意外)。

そんなわけで、いろいろICNS作成Scriptをまとめておきました。

こちらのScriptは実際には実行していませんが、/usr/bin/tiff2icnsコマンドがmacOS 10.15にも含まれていることを確認しています。

AppleScript名:TIFF画像をicnsに変換 v2scpt
tell application "Finder"
  set a to choose file
end tell
set theTiffPath to POSIX path of a
–128 x 128のtiff画像が必要
do shell script "/usr/bin/tiff2icns -noLarge " & (quoted form of theTiffPath)

★Click Here to Open This Script 

こちらは、オープンソースの「IcnsFactory」をフレームワーク化したIconFactory.frameworkを呼び出すバージョンです。自分が実際に使ったのはこちらです。

macOS 10.14以降ではScript Debugger上で実行するか、Script Debuggerから書き出したアプレット(Application(Enhanced))で実行するか、あるいはMacのSIPを解除して実行するなどの方法で実行できます。

Xcode上のAppleScript Applicationプロジェクトに突っ込んで呼び出せば、SIPの解除やScript Debuggerのインストールは必要ありません。

–> Download iconFactory.framework

AppleScript名:画像をICNS書類に
— Created 2017-04-14 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "IconFactory" –https://github.com/kgn/IcnsFactory

set aFile to POSIX path of (choose file)
set outFile to POSIX path of (choose file name)
set anImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile
set iconFam to current application’s IcnsFactory’s writeICNSToFile:outFile withImages:anImage

★Click Here to Open This Script 

Posted in file Image | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

WebKit Utilities v2

Posted on 3月 16, 2020 by Takaaki Naganoya

AppleScriptで指定URLのページを表示する「WebKit Utilities」がmacOS 10.13以降の環境で動かなくなっていたので、書き換えて動くようにしておきました。

–> Download WebKit Utilities_archive v2

これ自体が役に立ったということはなく、単にAppleScript Librariesの書き方のサンプルと理解しています。Webサイトの表示を行うよりも1枚ものの画像やPDFにレンダリングする処理のほうがバッチ処理の中では相性がいいと思います。

また、このScriptはWkWebViewではなく古いWebViewを使っているので、じきに動かなくなります。

AppleScript名:WebKit Utilitiesで指定URLをウィンドウ表示
— Created 2017-03-24 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use webLib : script "WebKit Utilities"

set targetURL to "http://www.piyocast.com/"
display URL targetURL window size {1024, 1024}

★Click Here to Open This Script 

AppleScript名:WebKit Utilities
— An example AppleScript/Objective-C library that uses the WebKit framework.

use AppleScript
use framework "AppKit"
use framework "WebKit"

property NSURL : a reference to current application’s NSURL
property WebView : a reference to current application’s WebView
property NSScreen : a reference to current application’s NSScreen
property NSThread : a reference to current application’s NSThread
property NSWindow : a reference to current application’s NSWindow
property NSURLRequest : a reference to current application’s NSURLRequest
property NSMutableDictionary : a reference to current application’s NSMutableDictionary

on _DisplayWebWindowWithURL:theURLString windowSize:theWindowSize
  — Create and display a horizontally centered window with a WebView
  
  
set {thisWindowWidth, thisWindowHeight} to theWindowSize
  
set screenBounds to the NSScreen’s mainScreen’s visibleFrame as any
  
  
if class of screenBounds = record then
    set screenWidth to screenBounds’s |size|’s width
    
set screenHeight to screenBounds’s |size|’s height
    
set windowLeft to ((screenWidth – thisWindowWidth) / 2) + (screenBounds’s origin’s x)
    
set windowBottom to screenHeight – thisWindowHeight + (screenBounds’s origin’s y) – 40
  else
    copy screenBounds to {{originX, originY}, {screenWidth, screenHeight}}
    
set windowLeft to ((screenWidth – thisWindowWidth) / 2) + originX
    
set windowBottom to screenHeight – thisWindowHeight + originY – 40
  end if
  
  
set theStyleMask to (get current application’s NSTitledWindowMask) + (get current application’s NSClosableWindowMask) + (get current application’s NSMiniaturizableWindowMask) + (get current application’s NSResizableWindowMask)
  
set webWindow to NSWindow’s alloc()’s initWithContentRect:(current application’s NSMakeRect(windowLeft, windowBottom, thisWindowWidth, thisWindowHeight)) ¬
    styleMask:theStyleMask backing:(current application’s NSBackingStoreBuffered) defer:false
  — set webWindow’s title to "WebView Created with AppleScript – " & theURLString
  
  
set theWebView to WebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, thisWindowWidth, thisWindowHeight)) ¬
    frameName:"WebKit Frame" groupName:"WebKit Group"
  set webWindow’s contentView to theWebView
  
  
tell webWindow to makeKeyAndOrderFront:(missing value)
  
  
— Start loading the URL
  
  
set theURL to NSURL’s URLWithString:theURLString
  
set theURLRequest to NSURLRequest’s requestWithURL:theURL
  
tell theWebView’s mainFrame to loadRequest:theURLRequest
  
  
webWindow
end _DisplayWebWindowWithURL:windowSize:

— The following two handlers exist to deal with running on a background thread.
— If we’re on a background thread, we have to run the UI code on the main thread.

on _DisplayWebWindowMainThread:parameterDictionary
  try
    set theURLString to parameterDictionary’s objectForKey:"url string"
    
set theWindowSize to parameterDictionary’s objectForKey:"window size"
    
    
set webWindow to my _DisplayWebWindowWithURL:theURLString windowSize:theWindowSize
    
    
tell parameterDictionary to setObject:webWindow forKey:"result"
  on error errMsg number errNum
    using terms from scripting additions
      display alert "Error!" message errMsg & " (" & errNum & ")"
    end using terms from
    
tell parameterDictionary to setObject:{errMsg, errNum} forKey:"error"
  end try
end _DisplayWebWindowMainThread:

on display URL theURLString window size theWindowSize
  — All UI methods must be invoked on the main thread.
  
if (NSThread’s isMainThread) then
    my _DisplayWebWindowWithURL:theURLString windowSize:theWindowSize
  else
    — Parameters, results and error information are communicated between threads via a dictionary.
    
set parameterDictionary to NSMutableDictionary’s dictionary()
    
tell parameterDictionary to setObject:theURLString forKey:"url string"
    
tell parameterDictionary to setObject:theWindowSize forKey:"window size"
    
    
its performSelectorOnMainThread:"_DisplayWebWindowMainThread:" withObject:parameterDictionary waitUntilDone:true
    
    
— Propagate errors from the other thread
    
    
set errInfo to parameterDictionary’s objectForKey:"error"
    
if errInfo is not missing value then error errInfo’s first item number errInfo’s second item
    
    
parameterDictionary’s objectForKey:"result"
  end if
end display URL

★Click Here to Open This Script 

Posted in GUI Internet URL | Tagged 10.13savvy 10.14savvy 10.15savvy NSMutableDictionary NSScreen NSThread NSURL NSURLRequest NSWindow WebView | Leave a comment

正方形セルの表データで指定セルに隣接するセルブロックを検出

Posted on 3月 11, 2020 by Takaaki Naganoya

正方形セルの表を形成する1次元配列上で、指定セルに隣接する指定データの入っているセルブロックを検出するAppleScriptです。

テストデータは11×11のサイズで作ってみました。本Scriptを作ることが最終目的ではないので、実戦投入はしていませんが、それなりに動いているようです。11セルの対象データを検出するのに、0.002秒ぐらい(10回実行時の平均値)。

本プログラムではセルアドレス=58のセルと隣接する「9」のデータが入っているセルのアドレスをすべて求めています。基準セルの8方向のセルアドレスを計算したうえでデータにアクセスして、「9」が入っているセルを検出。それらをまとめてすべての検出セルの8方向のアドレスを検査し、それ以上検出セル数が増加しない状態までループ処理を続けています。

本プログラムは本番プログラムを作るための習作だったので、ものすごくオーソドックスな組み方をしています。本番のプログラムでは、この「無駄に長い」処理を大幅に簡略化して3分の1ぐらいの長さになりました。

AppleScript名:findBlock2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/03/10
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property dataWidth : 11
property dataHeight : 11
property dataMax : 121

property testData : {"1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", ¬
  "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", ¬
  
"1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", ¬
  
"1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", ¬
  
"1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", ¬
  
"1", "1", "9", "1", "1", "1", "1", "1", "1", "1", "1", ¬
  
"1", "9", "9", "9", "1", "1", "1", "1", "1", "1", "1", ¬
  
"1", "9", "9", "9", "9", "1", "1", "1", "1", "1", "1", ¬
  
"1", "9", "9", "9", "1", "1", "1", "1", "1", "1", "1", ¬
  
"1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", ¬
  
"1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"}

set foundList to {58}

set prevFound to 1
repeat
  set foundList to findAllDirection(foundList) of me
  
set aLen to length of foundList
  
if aLen = prevFound then
    return foundList
  else
    copy aLen to prevFound
  end if
end repeat

return foundList
–> {58, 69, 68, 70, 80, 79, 81, 82, 91, 90, 92}

on findAllDirection(foundList)
  repeat with sCanCell in foundList
    
    
set nRes to findN(sCanCell) of me
    
set sRes to findS(sCanCell) of me
    
set eRes to findE(sCanCell) of me
    
set wRes to findW(sCanCell) of me
    
set nwRes to findNW(sCanCell) of me
    
set neRes to findNE(sCanCell) of me
    
set swRes to findSW(sCanCell) of me
    
set seRes to findSE(sCanCell) of me
    
    
if nRes is not equal to false then
      set newAddr to contents of (item 2 of nRes)
      
if newAddr is not in foundList then
        set the end of foundList to newAddr
      else
        set nRes to false
      end if
    end if
    
    
if sRes is not equal to false then
      set newAddr to contents of (item 2 of sRes)
      
if newAddr is not in foundList then
        set the end of foundList to newAddr
      else
        set sRes to false
      end if
    end if
    
    
if eRes is not equal to false then
      set newAddr to contents of (item 2 of eRes)
      
if newAddr is not in foundList then
        set the end of foundList to newAddr
      else
        set eRes to false
      end if
    end if
    
    
if wRes is not equal to false then
      set newAddr to contents of (item 2 of wRes)
      
if newAddr is not in foundList then
        set the end of foundList to newAddr
      else
        set wRes to false
      end if
    end if
    
    
if nwRes is not equal to false then
      set newAddr to contents of (item 2 of nwRes)
      
if newAddr is not in foundList then
        set the end of foundList to newAddr
      else
        set nwRes to false
      end if
    end if
    
    
if neRes is not equal to false then
      set newAddr to contents of (item 2 of neRes)
      
if newAddr is not in foundList then
        set the end of foundList to newAddr
      else
        set neRes to false
      end if
    end if
    
    
if swRes is not equal to false then
      set newAddr to contents of (item 2 of swRes)
      
if newAddr is not in foundList then
        set the end of foundList to newAddr
      else
        set swRes to false
      end if
    end if
    
    
if seRes is not equal to false then
      set newAddr to contents of (item 2 of seRes)
      
if newAddr is not in foundList then
        set the end of foundList to newAddr
      else
        set seRes to false
      end if
    end if
    
  end repeat
  
return foundList
end findAllDirection

—————————————

on findS(anAddress)
  copy anAddress to tmpAddr
  
set tmpAddr to incLine(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpCon to contents of item tmpAddr of testData
  
if tmpCon = "1" then return false
  
return {true, tmpAddr}
end findS

on findN(anAddress)
  copy anAddress to tmpAddr
  
set tmpAddr to decLine(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpCon to contents of item tmpAddr of testData
  
if tmpCon = "1" then return false
  
return {true, tmpAddr}
end findN

on findE(anAddress)
  copy anAddress to tmpAddr
  
set tmpAddr to incCell(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpCon to contents of item tmpAddr of testData
  
if tmpCon = "1" then return false
  
return {true, tmpAddr}
end findE

on findW(anAddress)
  copy anAddress to tmpAddr
  
set tmpAddr to decCell(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpCon to contents of item tmpAddr of testData
  
if tmpCon = "1" then return false
  
return {true, tmpAddr}
end findW

on findNW(anAddress)
  copy anAddress to tmpAddr
  
set tmpAddr to decLine(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpAddr to decCell(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpCon to contents of item tmpAddr of testData
  
if tmpCon = "1" then return false
  
return {true, tmpAddr}
end findNW

on findNE(anAddress)
  copy anAddress to tmpAddr
  
set tmpAddr to decLine(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpAddr to incCell(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpCon to contents of item tmpAddr of testData
  
if tmpCon = "1" then return false
  
return {true, tmpAddr}
end findNE

on findSW(anAddress)
  copy anAddress to tmpAddr
  
set tmpAddr to incLine(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpAddr to decCell(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpCon to contents of item tmpAddr of testData
  
if tmpCon = "1" then return false
  
return {true, tmpAddr}
end findSW

on findSE(anAddress)
  copy anAddress to tmpAddr
  
set tmpAddr to incLine(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpAddr to incCell(tmpAddr) of me
  
if tmpAddr = false then return false
  
set tmpCon to contents of item tmpAddr of testData
  
if tmpCon = "1" then return false
  
return {true, tmpAddr}
end findSE

—————————————

on incLine(anAddress)
  if anAddress + dataWidth > dataMax then
    set anAddress to false
  else
    set anAddress to anAddress + dataWidth
  end if
  
return anAddress
end incLine

on decLine(anAddress)
  if anAddress – dataWidth < 1 then
    set anAddress to false
  else
    set anAddress to anAddress – dataWidth
  end if
  
return anAddress
end decLine

on incCell(anAddress)
  set tmpMax to anAddress div dataWidth
  
if ((anAddress + 1) div dataWidth) is not equal to tmpMax then
    set anAddress to false
  else
    if anAddress + 1 < dataMax then
      set anAddress to anAddress + 1
    else
      return false
    end if
  end if
  
return anAddress
end incCell

on decCell(anAddress)
  set tmpMax to anAddress div dataWidth
  
set tmp2Max to (anAddress – 1) div dataWidth
  
if anAddress – 1 > 0 then
    if (tmp2Max is equal to tmpMax) then
      set anAddress to anAddress – 1
    end if
  else
    set anAddress to false
  end if
  
return anAddress
end decCell

★Click Here to Open This Script 

Posted in list | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

Numbersの表の選択範囲をシーケンシャル番号で埋める

Posted on 3月 11, 2020 by Takaaki Naganoya

Numbersの最前面の書類で選択中の表の選択範囲を1からはじまるシーケンシャル番号で埋めるAppleScriptです。

Numbers書類のセルを埋めるのは、それなりにコストの高い(処理時間が長い)処理なので、数十とか百ぐらいの要素だと、このように悠長にループで埋めても大丈夫ですが、数千とか数万セルを相手にする場合には途方もない時間がかかります。

要素数が多い場合には、CSVのファイルを組み立ててオープンする処理方法をおすすめします(一瞬で終わるので)。

この手の処理は、ほぼ「書き捨て」で保存すらしないことが多いのですが、たまたま気が向いたので保存しておきました。

本ScriptはmacOS 10.14.6/10.15.3+Numbers v6.2.1、macOS 10.13.6+Numbers v6.2で動作確認しています。

AppleScript名:Numbersの表の選択範囲をシーケンシャル番号で埋める
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/03/10
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

tell application "Numbers"
  tell front document
    tell active sheet
      try
        set theTable to first table whose class of selection range is range
      on error
        display notification "Numbers: There is no selection"
        
return
      end try
      
      
tell theTable
        set aRes to value of cells of selection range
      end tell
      
      
set aLen to length of aRes
      
set newList to makeSequential1DList(aLen) of me
      
      
tell theTable
        set cList to cells of selection range
      end tell
      
      
repeat with i from 1 to aLen
        set anObj to contents of item i of cList
        
set aVal to contents of item i of newList
        
        
ignoring application responses
          set value of anObj to aVal
        end ignoring
        
      end repeat
    end tell
  end tell
end tell

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

★Click Here to Open This Script 

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

mirroringの設定と解除

Posted on 3月 11, 2020 by Takaaki Naganoya

自分はディスプレイのミラーリング機能は日常的にあまり使っていません。実際にやってみたらどうだったのかをまとめてみました。

この手の処理をAppleScriptで書こうとしても、そのための命令が標準で内蔵されていないため、アプローチの方法はかぎられています。

 (1)思いっきりハードウェア寄りのプログラム(Cとかで書いた)を呼び出す
 (2)アクセシビリティ系の機能(GUI Scripting)を使ってシステム環境設定を操作する

の2つです。(2)は、画面上の要素の些細な変更によりプログラムを書き換える必要が出てくるうえに、信頼性が高くないので、あまりやりたくありません。もちろん、画面上の要素を検索しながら処理する方法もあるわけですが、それなりに(画面要素の検索に)時間がかかります。

そうなると、(1)を採用することになります。探すと………すぐにみつかりました。「mirror-displays」というコマンドラインアプリケーションです。ソースコードを読んでみると、CoreGraphics系の各種フレームワークを呼び出している、Objective-Cで書かれた(ほとんどCのコード)プログラムです。

これをスクリプトバンドルの中に入れて呼び出してみました。1回実行するとメインモニタの内容が他のモニタにミラーリングされます。もう1回実行すると、ミラーリングが解除されます。

–> Download mirroring_toggle (Script Bundle with executable command in its bundle)

mirrorコマンドのオプションには、「どのモニタをミラーリングさせるか」といった指定ができるようですが、試していないのでとくに凝った指定も何もしていません。

mirrorコマンドはmacOS 10.14でビルドして10.10以降をターゲットにしてみましたが、そこまで古い環境は手元に残っていないのでテストしていません。また、macOS 10.15のマシンには複数台のモニタをつないでいないので、macOS 10.15上でのテストも行っていません。

AppleScript名:mirroringの設定と解除
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/03/11
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

–https://github.com/hydra/mirror-displays
–バンドルの中に入れたmirrorコマンドをただ呼んでるだけ
set myPath to POSIX path of (path to me)
set comPath to myPath & "/Contents/Resources/mirror"
do shell script quoted form of comPath

★Click Here to Open This Script 

Posted in shell script System | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

ServerInformationで製品情報を取得

Posted on 3月 9, 2020 by Takaaki Naganoya

AppleのPrivate Frameworkである「ServerInformation.framework」を呼び出すAppleScriptです。

AppStoreを通じて配布するアプリケーションではPrivate Frameworkの使用は問題になる可能性がありますが、ユーザーの手元で動かしているAppleScriptでPrivate Frameworkを煮ようが焼こうが関係ないと思います。

「ServerInformation.framework」は割と利用価値が高そうなものであります。実際にGithub上で探したサンプルをAppleScriptに翻訳して試してみました。

本サンプルは、/System/Library/PrivateFrameworks/という、ふだんFrameworkを呼び出す先「ではない」ディレクトリに入っているFrameworkを呼び出すサンプルでもあり、NSBundleを通じてFrameworkを呼び出す実例でもあります。

この方法を利用すれば、最新のmacOSで(SIPを解除しないで)そのままでは呼び出せなくなっていたホームディレクトリ下のFrameworkとかアプレット内に同梱したFrameworkをローディングして呼び出すことができそうな雰囲気があります。

AppleScript名:ServerInformationで製品情報を取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/03/09
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
— https://gist.github.com/erikng/d90d21f502351d64a541600226626a28

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

set ServerInformation to current application’s NSBundle’s bundleWithPath:"/System/Library/PrivateFrameworks/ServerInformation.framework"
set ServerCompatibility to current application’s NSBundle’s bundleWithPath:"/System/Library/PrivateFrameworks/ServerCompatibility.framework"

set ServerInformationComputerModelInfo to ServerInformation’s classNamed:"ServerInformationComputerModelInfo"
set SVCSystemInfo to ServerCompatibility’s classNamed:"SVCSystemInfo"

set myInfo to SVCSystemInfo’s currentSystemInfo()

set exInfo to (ServerInformationComputerModelInfo’s attributesForModelIdentifier:(myInfo’s computerModelIdentifier())) as record

set anArchitecture to exInfo’s architecture
–> "x86_64"

set aMonoImageSel to exInfo’s monochromeSelectedHardwareImage
–> NSImage (Gray)

set aMonoImage to exInfo’s monochromeHardwareImage
–> NSImage (Gray)

set aDesc to exInfo’s |description|
–> "15インチMacBook Pro Retinaディスプレイを装備、デュアルコアIntel Core i7プロセッサ搭載(アルミニウムユニボディ)、2012年の中期に投入。"
–> "Mac mini Intelデュアルコアプロセッサおよび統合型グラフィックス搭載、2014年後期に投入。"
–> "MacBook Air 11インチディスプレイを装備、2011年の中期に投入。"

set aModel to exInfo’s model
–> "MacBook Pro"
–> "Mac mini"
–> "MacBook Air"

set aProcessor to exInfo’s processor
–> "Intel Core i7"
–> "デュアルコアIntel Core i5、デュアルコアIntel Core i7"
–> "Dual-Core Intel Core i5、Intel Core i7"

set aMarketModel to exInfo’s marketingModel
–> "15インチMacBook Pro, Retinaディスプレイ, Intel Core i7 (Mid 2012)"
–> "Mac mini(Late 2014)"
–> "11インチMacBook Air(Mid 2011)"

set aColorImage to exInfo’s hardwareImage
–> NSImage (Color)

★Click Here to Open This Script 

Posted in System | Tagged 10.14savvy 10.15savvy | Leave a comment

使用中のMacの製品呼称を取得する v4

Posted on 3月 8, 2020 by Takaaki Naganoya

使用中のMacの製品呼称を取得するAppleScriptです。

ながらく、この手のルーチンを使い続けてきましたが、macOS 10.15でエラーが出るようになりました。

理由を確認してみたところ、パス名の一部がmacOS 10.15で変更になっていることがわかりました。

目下、Xcode上でアプリケーションを作成すると、ローカライズしたリソースのフォルダについては、「English.lproj」ではなく「en.lproj」と、言語コードが用いられるようになってきました。この、「English」と「en」の変更がOS内部のコンポーネントについても行われた「だけ」というのが理由のようです。

ちなみに、パス名を無意味に途中で切ってつなげているのは、Blog(HTML)やMarkDownのドキュメントに入れたときに、折り返しされずにレンダリング品質を下げる原因になる(行がそこだけ伸びるとか、ページ全体の文字サイズが強制的に小さくなるとか)ためです。

AppleScript名:使用中のMacの製品呼称を取得する v4.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/03/08
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set myInfo to retModelInfo() of me
–> "Mac mini (Late 2014)"

on retModelInfo()
  set v2 to system attribute "sys2"
  
— macOS 10.15.3 –> 15
  
  
if v2 < 15 then
    –macOS 10.14まで
    
set pListPath to "/System/Library/PrivateFrameworks/ServerInformation.framework/" & "Versions/A/Resources/English.lproj/SIMachineAttributes.plist"
  else
    –macOS 10.15以降
    
set pListPath to "/System/Library/PrivateFrameworks/ServerInformation.framework/" & "Versions/A/Resources/en.lproj/SIMachineAttributes.plist"
  end if
  
  
set aRec to retDictFromPlist(pListPath) of me
  
set hwName to (do shell script "sysctl -n hw.model")
  
–>  "Macmini7,1"
  
  
set aMachineRec to retRecordByLabel(aRec, hwName) of me
  
  
set aMachineRec2 to contents of first item of aMachineRec
  
return (marketingModel of _LOCALIZABLE_ of aMachineRec2)
end retModelInfo

on retDictFromPlist(aPath)
  set thePath to current application’s NSString’s stringWithString:aPath
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theDict to current application’s NSDictionary’s dictionaryWithContentsOfFile:thePath
  
return theDict as record
end retDictFromPlist

on retRecordByLabel(aRec as record, aKey as string)
  set aDic to current application’s NSDictionary’s dictionaryWithDictionary:aRec
  
set aVal to aDic’s valueForKey:aKey
  
return aVal as list
end retRecordByLabel

on retRecordByKeyPath(aRec as record, aKey as string)
  set aDic to current application’s NSDictionary’s dictionaryWithDictionary:aRec
  
set aVal to aDic’s valueForKeyPath:aKey
  
return aVal
end retRecordByKeyPath

★Click Here to Open This Script 

Posted in shell script System | Tagged 10.15savvy NSDictionary NSString | Leave a comment

マウス充電用AppleScript

Posted on 3月 6, 2020 by Takaaki Naganoya

本Scriptはいつも書き捨てにするAppleScriptですが、使用頻度はそれなりに高いものです。

AppleのMagic Mouse 2を充電するには、裏返してLightningケーブルを挿す必要があります。

STEP1:コンピュータをスリープさせる
STEP2:マウスを裏返す
STEP3:ケーブルを挿して充電する

という手順になるわけですが、、、マウスを裏返した時点でボタンが押されたりして、充電できないケースが多々あります。

そこで、本Scriptをスクリプトエディタ上で実行して、10秒以内にマウスに充電ケーブルを挿すと、あとからコンピュータがスリープ。問題なくマウスを充電できます。

AppleScript名:マウス充電用Script
delay 10

tell application "System Events" to sleep

★Click Here to Open This Script 

Posted in How To System | Tagged 10.13savvy 10.14savvy 10.15savvy System Events | Leave a comment

FileMaker v18でAppleScriptを使えるように設定

Posted on 3月 6, 2020 by Takaaki Naganoya

Classic MacOS時代はJeditとFileMaker Proがないと機能がなさすぎて即死でしたが、現在はAppleScriptのソリューションを成立させるのにFileMakerが必須ということもなくなりました。本Blog掲載のサンプルを見ていただければ分かるように、データのソーティングも抽出も、AppleScriptだけで相当なことがこなせます。

AppleScript単体でもCocoaの機能を利用することで、数万〜数十万件レベルのデータ件数であれば、別にFileMakerデータベースを併用する必要もなくなってきました。

ただ、かといってFileMakerの魅力が減ったわけではありません。とくに、小規模なチームで短期間で業務支援システムを組み立てる必要がある場合など、FileMakerの手軽さは圧倒的です(個人事業者ならなおのことです)。さらに、各種データソースからデータを自動で取り込んで蓄積する部品として、FileMakerが使えることは大きな力になります。

FileMakerは昔とはやや位置付けが変わりましたが、それでも大きな価値を持っています。

FileMakerのデータベースがAppleScriptから使えるようになるまで

実際にFileMaker v18のお試し版(FileMaker Advanced)をダウンロードして、AppleScriptを利用できるように設定を行ってみました。

デフォルトで、FileMakerのアプリケーションにnameやversionなどを問い合わせすることは可能ですが、データベースへのアクセスは行えません。FileMakerのアプリケーション本体にAppleScript制御受付の設定があるわけではなく、データベース書類に対してユーザー権限とDB書類オープン時のユーザー設定などを行うことで、AppleScriptからアクセスできるようになります。

データベースに対して権限設定を行うので、まずは対象となるデータベースをオープンしておく必要があります。そのうえで、「ファイル」>「管理」>「セキュリティ」を実行。

新規アカウント「AS User」(なんでもいい)を作成し、

この新規作成したアカウント「AS User」の権限を新規に定義します(新規アクセス権セット)。

拡張アクセス件で、必要な権限を設定した上で「Apple EventおよびActiveXによるFileMaker操作の実行を許可(fmextscriptacess)」にチェックを入れておきます。

ユーザーおよびユーザー権限を設定した上で、データベース書類に対して、この新規作成したアカウント「AS User」でオープンするように設定を行います。「ファイル」>「ファイルオプション…」を実行して、

「ファイルオプション」ダイアログ中の「開く」タブで、「次のアカウントを使用してログイン」の欄にユーザー名「AS User」と設定したパスワードを設定して「OK」ボタンをクリック。

これで、いったんデータベース書類をクローズして再度オープンし直すと、ユーザー名「AS User」でオープンした状態になり、AppleScriptからの操作を受け付けるようになります。

昔のFileMakerではpropertyを求めることでファイルメーカーDBのスキーマ定義やレイアウト一覧などが、パスワードを設定してある状態ですらすべて取得できていましたが(いろいろリバースエンジニアリングしました)、現在のFileMakerではproperty属性によるアクセスが行えないので、そこまでガバガバではないようです。

Posted in How To | Tagged 10.14savvy 10.15savvy FileMaker | Leave a comment

common elements Libの評価アプリケーションを配布

Posted on 3月 5, 2020 by Takaaki Naganoya

ライブラリのインストール方法やらスクリプトエディタの使い方やらがわかっていないとcommon elements Libを実際に使って試すことができないので、実際に機能を評価できるよう最低限のGUIをつけてみました。

–> Download GUI App Executable(Zip archive, 57KB)

macOS 10.13,10.14, 10.15上で動作します。実行にはインターネット接続を必要とします。

Posted in AppleScript Application on Xcode Script Libraries | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

common elements Libをロシア語などのクエリーで呼び出す

Posted on 3月 3, 2020 by Takaaki Naganoya

WikipediaのREST APIを呼び出して、2つの単語の共通項を計算する「common elements Lib」を作って実際にいろいろ評価していますが、ロシア語を指定したときに結果が得られないという現象に直面していました。

ロシア語を記述するキリル文字のエンコーディング指定がよくなかったのか、Wikipediaのロシア語サーバーの問題なのか、どこに問題点があるのかよくわかっていませんでした(そういう問題のあぶり出しのためにリリースしてみた事情があります)。

とりあえず人名をGoogle翻訳でロシア語+キリル文字に翻訳してロシア語Wikipediaに突っ込んでみても結果が得られず首をひねっていましたが、ロシア語の人名表記が、

First name Family name

ではなく、

Family name, First name

のフォーマットであることに気づきました。この語順で人名を突っ込んでみたところ、無事結果が得られることを確認しました。

AppleScript名:sample_russian
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/03/03
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use comLib : script "common elements Lib"

–"Family Name, First Name" in Russian Language

–"George Lucas" and "Steven Spielberg"
set cRes to list up common elements with {"Лукас, Джордж", "Спилберг, Стивен"} with language "ru"
–> {"Награда имени Ирвинга Тальберга", "Индиана Джонс", "Кинофантастика", "Золотой глобус", "Монтажёр", "Industrial Light & Magic", "Форд, Харрисон", "DreamWorks", "Премия «Сатурн» за лучший сценарий", "Сиквел", "Продюсер", "Кинорежиссёр", "Калифорния", "Сценарист", "Премия «Сатурн» за лучшую режиссуру", "Оскар (кинопремия)"}

–"Larry Tesler" and "Steve Jobs"
set dRes to list up common elements with {"Теслер, Ларри ", "Джобс, Стив"} with language "ru"
–> {"Apple Computer", "Xerox PARC", "Smalltalk", "Стэнфордский университет"}

★Click Here to Open This Script 

ウクライナ語の人名は、

First name Family name

となっているので、そのように書けば結果が得られます。登録記事数がそれほど多くないので、かなり検索語句を選ぶ印象ではあります。

AppleScript名:sample_Ukrainian
–Українська (Ukrainian)
use comLib : script "common elements Lib"

–"Bill Gates" and "Steve Jobs"
set dRes to list up common elements with {"Білл Гейтс", "Стів Джобс"} with language "uk"
–> {"США", "IBM", "Долар США", "Майкрософт", "Головний виконавчий директор", "Стенфордський університет", "Персональний комп’ютер"}

★Click Here to Open This Script 

Posted in Natural Language Processing REST API Script Libraries | Tagged 10.10savvy 10.12savvy 10.13savvy 10.14savvy 10.15savvy | 1 Comment

Post navigation

  • Older posts
  • Newer posts

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

Google Search

Popular posts

  • macOS 26, Tahoe
  • KagiのWebブラウザ、Orion
  • Script Debuggerの開発と販売が2025年に終了
  • 【続報】macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ
  • NSObjectのクラス名を取得 v2.1
  • 2024年に書いた価値あるAppleScript
  • Xcode上のAppleScriptObjCのプログラムから、Xcodeのログ欄へのメッセージ出力を実行
  • (確認中)AppleScript Dropletのバグっぽい動作が解消?
  • AppleScript Dropletのバグっぽい動作が「復活」(macOS 15.5β)
  • macOS 26, 15.5でShortcuts.app「AppleScriptを実行」アクションのバグが修正される
  • Script Debuggerがフリーダウンロードで提供されることに
  • 指定フォルダ以下の画像のMD5チェックサムを求めて、重複しているものをピックアップ
  • Dock Menu
  • 執筆中:AppleScript最新リファレンスver2.8対応(macOS 15対応アップデート)
  • macOS 15.5beta5(24F74)でaliasのキャスティングバグが修正された???
  • 余白トリミング実験 v3
  • Claris FileMaker Pro 2025(v22)がリリースされた
  • 複数の重複検出ルーチンを順次速度計測
  • Cocoa-AppleScript AppletがRosettaオンで動いた!
  • シンプルな文字置換

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 (160) 26.0savvy (27) CotEditor (67) Finder (53) Keynote (120) 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) 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年12月
  • 2025年11月
  • 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