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

タグ: 10.14savvy

ISOコードから国名を取得

Posted on 12月 30, 2019 by Takaaki Naganoya

ISOコード(ISO 3166-1 Alpha-2 code)で示した国の名称を取得するAppleScriptです。

現在のロケールでローカライズして取得するもの(retCountryNameInCurrentLocale)と、指定ロケールでローカライズするもの(retCountryNameInSpecifiedLocale)を用意しておきました。

Mac App Storeに出したDouble PDF v2.0(30言語以上ローカライズした)の審査は年越し確実で、審査に1か月以上かかるという、自分の知っている範囲での最長記録を更新しつつあります(AppleがOSに作ったバグを回避する以外の追加機能はわずかなのに)。

そんな中、各国語で国名を表記する方法を調べておきました。ヒンディー語、タイ語、ギリシア語あたりは文字が見慣れないながらもギリギリ文字単位での識別が可能で、非日常感があって(日本語の表記範囲で出てこない文字なので)クールな感じがします(個人の見解です)。アラビア語やヘブライ語になってしまうと、コンピュータの挙動が変わってしまうので(表記が右→左)、ちょっとシャレにならない感じであります。

AppleScript名:ISOコードから国名を取得.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/30
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aCountryName to retCountryNameInCurrentLocale("GE") of me
–> "ジョージア" —In Japanese (Current Locale, diffenret in each user)

set bCountryName to retCountryNameInSpecifiedLocale("GE", "en_US") of me
–> "Georgia" –In English
set cCountryName to retCountryNameInSpecifiedLocale("GE", "fr") of me
–> "Géorgie"–In French
set dCountryName to retCountryNameInSpecifiedLocale("GE", "ru") of me
–> "Грузия"–In Russian
set eCountryName to retCountryNameInSpecifiedLocale("GE", "zh") of me
–>"格鲁吉亚"–In Chinese
set fCountryName to retCountryNameInSpecifiedLocale("GE", "ko_KR") of me
–> "조지아"–In Korean
set gCountryName to retCountryNameInSpecifiedLocale("GE", "hi") of me
–> "जॉर्जिया" –In Hindi
set hCountryName to retCountryNameInSpecifiedLocale("GE", "th") of me
–> "จอร์เจีย"-In Thai
set iCountryName to retCountryNameInSpecifiedLocale("GE", "el_GR") of me
–> "Γεωργία"–In Greek

–指定の国名コードから国名を現在のロケールでローカライズして返す
on retCountryNameInCurrentLocale(isoCode as string)
  set curLocale to current application’s NSLocale’s currentLocale()
  
set aLocLangCode to (curLocale’s objectForKey:(current application’s NSLocaleLanguageCode)) as string
  
set aCountry to curLocale’s displayNameForKey:(current application’s NSLocaleCountryCode) value:isoCode
  
return aCountry as string
end retCountryNameInCurrentLocale

–指定の国名コードから国名を指定のロケールでローカライズして返す
on retCountryNameInSpecifiedLocale(isoCode as string, targLocale as string)
  set curLocale to current application’s NSLocale’s localeWithLocaleIdentifier:targLocale
  
set aLocLangCode to (curLocale’s objectForKey:(current application’s NSLocaleLanguageCode)) as string
  
set aCountry to curLocale’s displayNameForKey:(current application’s NSLocaleCountryCode) value:isoCode
  
return aCountry as string
end retCountryNameInSpecifiedLocale

★Click Here to Open This Script 

Posted in Language Locale | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSLocale NSLocaleCountryCode NSLocaleLanguageCode | Leave a comment

アラートダイアログをタイトル付き表示

Posted on 12月 27, 2019 by Takaaki Naganoya

アラートダイアログで、ウィンドウタイトルを表示させるAppleScriptです。

AppleScriptには登場当初からダイアログの表示命令「display dialog」が備わっていました。久しぶりにClassic MacOSのdisplay dialogコマンドの表示を見ると、あまりの素朴さに驚かされます。

display dialogは手軽であるもののモーダルなダイアログなので、その後のMac OS Xへの移行時にはいまひとつ合わない感じがしていました。完全にMac OS Xの流儀で書き直されたのが、「display alert」ダイアログです。こちらもモーダル表示ですが、見た目は少しMac OS Xらしく変更されました。

display alertは、さまざまなオプションを指定できる一方で手軽さがいまひとつで、やはりdisplay dialog命令のほうが圧倒的に利用されてきたという経緯があります(自分もほとんどdisplay alertは使いません)。

# display alertダイアログに表示されるボタンはTouchBarに表示されますが、display dialogダイアログ上のボタンはTouchBarには表示されません

非モーダル表示なUIについては、display notificationで通知表示が使えるようになり、これで一応の解決を見たというところでしょうか。こういう動作をする通知ダイアログは、Newt On Projectの中で「どうしても欲しい」という要望が出て、2000年代初頭にすでに仲間内で作ってもらったものがあり、AppleScriptから半透明のダイアログ(画面右側からスライドアニメーション表示)を表示させていました。

その一方で、display youtubeとかdisplay tableといった、ダイアログに各種GUI部品を詰め込んで簡易表示させるインタフェース「箱庭インタフェース」が今年局所的なブームを起こし、地図は表示させるわテーブルは表示させるわ、世界地図を表示して国選択を行わせるわで「計算結果を手軽に表示できたり、ファイルパスをわかりやすく選択できて便利」という評価をいただいています。

で、この「箱庭インタフェース」はもれなくalert dialogのインタフェースを用いています。display alertで表示されるものと同じGUI部品ですね。


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

ちょっとした結果表示や項目選択のためにアラートダイアログ上にGUI部品を並べるやりかたは、「アリ」だと思っています。

そんな中、知り合い筋から「alertダイアログにタイトルは出せないのか?」と問い合わせがありました。アラートダイアログでタイトル表示が可能なことは知っていましたが、

Human Interface Guidelineに合わない仕様なので、タイトル表示は使ってきませんでした。

ただ、「箱庭インタフェース」シリーズでここまで無茶苦茶をやっておいて、いまさら他人に「HIGに反する」とかいっても説得力がまったくありません。

HIGのガイドラインに反するといっても、実際に使い勝手がよくなったり、顧客や上司からの無茶振りをかわすためであればアラートダイアログにタイトルを追加するというのも、アリなのかもしれません。自分はやりませんけれども。

AppleScript名:アラートダイアログをタイトル付き表示
— Created 2019-12-26 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property NSRunningApplication : a reference to current application’s NSRunningApplication

property returnCode : 0

set paramObj to {myMessage:"アラート表示", mySubMessage:"アラートダイアログを表示します", myTitle:"たいとる?"}
set dRes to displayAlert(paramObj) of me

on displayAlert(paramObj)
  my performSelectorOnMainThread:"displayAlertDialog:" withObject:(paramObj) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
return true
end displayAlert

on displayAlertDialog:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTitle to myTitle of paramObj
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
–its addButtonWithTitle:"Cancel"
    
set myWin to its |window|
  end tell
  
  
myWin’s setTitle:aTitle
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
return (aScrollWithTable’s documentView’s selectedRow()) + 1
end displayAlertDialog:

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

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSAlert NSRunningApplication | 2 Comments

アラートダイアログ上にTable Viewを表示 v8_ソート、並び替え、複数選択

Posted on 12月 25, 2019 by Takaaki Naganoya

アラートダイアログ上にTable Viewを表示するGUI部品を提供するAppleScriptです。Edama2さんから投稿していただいたものです。

箱庭テーブルビューシリーズは、macOS標準装備の「choose from list」コマンドでは選択肢が増えたときに使いにくいので、その代替品としてTable Viewを用いたポップアップしない単項目の選択UIを実装したものでした。


▲最初のバージョン。みなぎる使い捨て感

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

コードサイズの投げやりなまでの小ささと、いつもどおりのプレーンな読み味から改造のためのベースキットとして活用されまくり、派生品や亜種が内外でいろいろ発生しています(きっと知らないところで改変版が作られているはず)。

sdefをつけてライブラリ化したり、色付きダイアログなどいろいろキワモノが派生しましたが、実用性を考慮して半透明ウィンドウではなく通常ウィンドウとして表示する方向に変わりつつあります。

Version Author Date Description
v1 Piyomaru 2019/2/14 For choose from list without popup menu. One field selection UI.
v2 Piyomaru 2019/2/14
v3 Piyomaru 2019/2/14
v4 Piyomaru 2019/7/11 Table user interface for item selection
v5_e KniazidisR 2019/11/2 Separate display dialog stage into making and displaying
v5_m Piyomaru 2019/12/23 Filtering by multiple field
v6 Piyomaru 2019/12/24 Search Field Added
v7 Edama2 2019/12/240 Vertical & horizontal resizing
v8 Edama2 2019/12/25 Record sorting & Field re-ordering

以下、v8についてのedama2さんからの説明文です。

コラムのヘッダのクリックでテーブルのソート
ドラッグ&ドロップによる行の並び替え
行の複数選択

ができるようになりましたが、ヘッダのソート状態のときにD&Dの並び替えはできません。
飛び飛びに複数行選択しても並び替えできますが、たまにズレている気がします...(汗)

ArrayControllerを使用したのが機能過剰と思われたかもしれませんが、まずXCode上で一度組んだものを逆移植しているのでそんなに深い意味はないです。
でもソート方法を試行錯誤して調べていたら、Cocoa bindingをコーディングから設定できることがわかった(よく考えれば当たり前ですが)のが、何か壁を一枚破った気がしました。

Cocoa bindingがプログラムから動的に実行できることは、調査して知っていましたが、実際にAppleScriptのコードとして記述されたものは見たことがありませんでした(Mac Scripterのフォーラムを漁ると出てくるのかも?)。

### 表示内容をNSArrayControllerにバインディング
bind_toObject_withKeyPath_options_(current application’s NSValueBinding, my _data_source, "arrangedObjects." & aTitle, missing value)

ここの部分の記述は、クリスマスプレゼントとして楽しませていただきます。Cocoa Bindingなんて、Xcode上でGUI部品とイベント受信部分のハンドラとの接続をただ漫然とヒモでつなぐ作業をしていただけだったので、Cocoa Binding Referenceなんてはじめて細部にわたって眺めましたわー。


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


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


▲Priceフィールドを先頭に移動させてみた


▲Priceでソートさせてみた


▲Specsでソートさせてみた


▲選択行をドラッグ&ドロップで移動。複数行も移動可能。ただし、フィールド指定のソート状態を有効にすると行移動が効かないという


▲Commandキーを押しながら複数行選択


▲ドラッグ&ドロップで移動


▲移動後

AppleScript名:アラートダイアログ上にTable Viewを表示 v8_ソート、並び替え、複数選択.scpt
on run
  
  
set aTableList to {}
  
set aTableList’s end to {|Name|:"MacBook Air", Price:"119,800円", Specs:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}
  
set aTableList’s end to {|Name|:"MacBook Pro 13", Price:"139,800円", Specs:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}
  
set aTableList’s end to {|Name|:"MacBook Pro 15", Price:"258,800円", Specs:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"Mac mini", Price:"122,800円", Specs:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"iMac 21.5", Price:"120,800円", Specs:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}
  
set aTableList’s end to {|Name|:"iMac 4K 21.5", Price:"142,800円", Specs:"3.6GHzクアッドコアIntel Core i3"}
  
set aTableList’s end to {|Name|:"iMac 5K 27", Price:"198,800円", Specs:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}
  
set optionRec to {aTableList:aTableList}
  
set optionRec to optionRec & {aSortOrder:{column1:"Name", column2:"Price", column3:"Specs"}}
  
  
set aMainMes to "項目の選択"
  
set aSubMes to "適切なものを以下からえらんでください"
  
set dateObj to my chooseData(aMainMes, aSubMes, optionRec)
end run

# アラートダイアログでtableviewを表示
on chooseData(aMainMes, aSubMes, optionRec)
  script MyDialog
    property parent : AppleScript
    
use AppleScript
    
use scripting additions
    
use framework "Foundation"
    
property _retrieve_data : missing value
    
on make
      set aClass to me
      
script
        property parent : aClass
      end script
    end make
    
## ダイアログの呼び出し
    
on call(aMainMes, aSubMes, optionRec)
      set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myOption:optionRec}
      
parent’s performSelectorOnMainThread:"raize:" withObject:paramObj waitUntilDone:true
      
if (my _retrieve_data) is missing value then error number -128
      
return (my _retrieve_data)
    end call
    
## ダイアログの生成
    
on raize:paramObj
      ###
      
set mesText to paramObj’s myMessage
      
set infoText to paramObj’s mySubMessage
      
set paramObj to paramObj’s myOption
      
### set up view
      
set {theView, makeObj} to my makeContentView(paramObj)
      
### set up alert
      
tell current application’s NSAlert’s new()
        setMessageText_(mesText)
        
setInformativeText_(infoText)
        
addButtonWithTitle_("OK")
        
addButtonWithTitle_("Cancel")
        
setAccessoryView_(theView)
        
tell |window|()
          setInitialFirstResponder_(theView)
        end tell
        
#### show alert in modal loop
        
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
      end tell
      
### retrieve date
      
my retrieveData(makeObj)
    end raize:
    
    
## ContentView を作成
    
property _data_source : missing value
    
property _data_type : (current date) as text –do shell script "uuidgen" –>ユニークな文字列ならなんでもいい?
    
on makeContentView(paramObj)
      ## 準備
      
set aList to (paramObj’s aTableList) as list
      
set keyRec to (paramObj’s aSortOrder) as record
      
set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyRec)
      
      
set my _data_source to current application’s NSArrayController’s new()
      
repeat with anItem in aList
        set aDict to (current application’s NSDictionary’s dictionaryWithDictionary:anItem)
        ((
my _data_source)’s addObject:aDict)
      end repeat
      (
my _data_source)’s setSelectionIndex:0
      
      
## NSTableView
      
tell current application’s NSTableView’s alloc()
        tell initWithFrame_(current application’s CGRectZero)
          setAllowsEmptySelection_(false)
          
setAllowsMultipleSelection_(true)
          
setDataSource_(me)
          
setDelegate_(me)
          
setDoubleAction_("doubleAction:")
          
setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask)
          
setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular)
          
setTarget_(me)
          
setUsesAlternatingRowBackgroundColors_(true)
          
          
registerForDraggedTypes_({my _data_type})
          
setDraggingSourceOperationMask_forLocal_(current application’s NSDragOperationCopy, false)
          
          
set thisRowHeight to rowHeight() as integer
          
set aTableObj to it
        end tell
      end tell
      
      
## NSTableColumn
      
### Columnの並び順を指定する
      
set viewWidth to 0
      
set columnsCount to keyDict’s |count|()
      
repeat with colNum from 1 to columnsCount
        
        
set keyName to "column" & colNum as text
        
set aTitle to (keyDict’s objectForKey:keyName)
        
        
tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text))
          tell headerCell()
            setStringValue_(aTitle)
            
set thisHeaderHeight to cellSize()’s height
          end tell
          
          
### ソートの設定
          
set sortDescriptor to (current application’s NSSortDescriptor’s sortDescriptorWithKey:aTitle ascending:true selector:"compare:")
          
setSortDescriptorPrototype_(sortDescriptor)
          
          
### 表示内容をNSArrayControllerにバインディング
          
bind_toObject_withKeyPath_options_(current application’s NSValueBinding, my _data_source, "arrangedObjects." & aTitle, missing value)
          
          
set aTableColumn to it
        end tell
        
        
### Columnの横幅を調整
        
tell aTableObj
          addTableColumn_(aTableColumn)
          
–reloadData()
          
set colWidthMax to 0
          
set rowCount to numberOfRows()
          
          
repeat with rowNum from 1 to rowCount
            tell preparedCellAtColumn_row_(colNum – 1, rowNum – 1)
              set thisWidthSize to cellSize()’s width
              
–log result
              
if thisWidthSize is greater than or equal to colWidthMax then set colWidthMax to thisWidthSize
            end tell
          end repeat
        end tell
        
–log colWidthMax
        
        
set colWidthMax to colWidthMax + 5 –> 5は余白
        
tell aTableColumn
          setWidth_(colWidthMax)
        end tell
        
        
set viewWidth to viewWidth + colWidthMax
      end repeat
      
      
## NSScrollView
      
### Viewの高さを計算
      
set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない
      
set vSize to current application’s NSMakeRect(0, 0, viewWidth + 10, viewHeight) –> 10を足すといい感じの横幅になる
      
      
### Viewを作成
      
tell current application’s NSScrollView’s alloc()
        tell initWithFrame_(vSize)
          setBorderType_(current application’s NSBezelBorder)
          
setDocumentView_(aTableObj)
          
–setHasHorizontalScroller_(true)
          
–setHasVerticalScroller_(true)
          
set aScroll to it
        end tell
      end tell
      
      
return {aScroll, aTableObj}
    end makeContentView
    
    
## retrieve date
    
on retrieveData(aTableObj)
      set aIndexSet to aTableObj’s selectedRowIndexes()
      
set my _retrieve_data to ((my _data_source)’s arrangedObjects()’s objectsAtIndexes:aIndexSet) as list –setSelectionIndexes:aIndexSet
      
log result
    end retrieveData
    
    
    
# NSTableViewDatasource
    
on numberOfRowsInTableView:aTableView
      –log "numberOfRowsInTableView:"
      
return (my _data_source)’s content()’s |count|()
    end numberOfRowsInTableView:
    
# NSTableViewDelegate
    
on tableView:aTableView objectValueForTableColumn:aColumn row:aRow
    
end tableView:objectValueForTableColumn:row:
    
# テーブル内のセルが編集できるか
    
on tableView:aTableView shouldEditTableColumn:aColumn row:aRow
      return false
    end tableView:shouldEditTableColumn:row:
    
# テーブル内をダブルクリックしたらOKボタンを押す
    
on doubleAction:sender
      –log "doubleAction"
      
–log sender’s |className|() as text
      
## ヘッダをクリックした時は何もしない
      
if (sender’s clickedRow()) is -1 then return
      
      
set theEvent to current application’s NSEvent’s ¬
        keyEventWithType:(current application’s NSEventTypeKeyDown) ¬
          location:(current application’s NSZeroPoint) ¬
          
modifierFlags:0 ¬
          
timestamp:0.0 ¬
          
windowNumber:(sender’s |window|()’s windowNumber()) ¬
          
context:(current application’s NSGraphicsContext’s currentContext()) ¬
          
|characters|:return ¬
          
charactersIgnoringModifiers:(missing value) ¬
          
isARepeat:false ¬
          
keyCode:0
      current application’s NSApp’s postEvent:theEvent atStart:(not false)
    end doubleAction:
    
    
# Drag Operation Method
    
## ドラッグを開始(ペーストボードに書き込む)
    
on tableView:aTableView writeRowsWithIndexes:rowIndexes toPasteboard:pboard
      –log "writeRowsWithIndexes"
      
set aData to current application’s NSKeyedArchiver’s archivedDataWithRootObject:rowIndexes
      
pboard’s declareTypes:{my _data_type} owner:(missing value)
      
pboard’s setData:aData forType:(my _data_type)
      
return true
    end tableView:writeRowsWithIndexes:toPasteboard:
    
## ドラッグ途中
    
on tableView:aTableView validateDrop:info proposedRow:row proposedDropOperation:operation
      –log "validateDrop"
      
### 列の間にドラッグ
      
if (operation is current application’s NSTableViewDropAbove) then
        return current application’s NSDragOperationMove
      end if
      
### 項目の上にドラッグ
      
set aTypes to info’s draggingPasteboard’s types()
      
if (aTypes’s containsObject:(my _data_type)) as boolean then
        return current application’s NSDragOperationNone
      end if
      
return current application’s NSDragOperationNone
    end tableView:validateDrop:proposedRow:proposedDropOperation:
    
## ドラッグ終了
    
on tableView:aTableView acceptDrop:info row:row dropOperation:operation
      –log "acceptDrop"
      
set pboard to info’s draggingPasteboard()
      
      
set rowData to pboard’s dataForType:(my _data_type)
      
set rowIndexes to current application’s NSKeyedUnarchiver’s unarchiveObjectWithData:rowData
      
      
if (rowIndexes’s firstIndex()) < row then
        set row to row – (rowIndexes’s |count|())
      end if
      
      
set aRange to current application’s NSMakeRange(row, rowIndexes’s |count|())
      
set aIndexSet to current application’s NSIndexSet’s indexSetWithIndexesInRange:aRange
      
      
tell my _data_source
        set anObj to content()’s objectsAtIndexes:rowIndexes
        
removeObjects_(anObj)
        
insertObjects_atArrangedObjectIndexes_(anObj, aIndexSet)
        
rearrangeObjects()
      end tell
      
return true
    end tableView:acceptDrop:row:dropOperation:
  end script
  
  
##
  
tell (make MyDialog)
    return call(aMainMes, aSubMes, optionRec)
  end tell
end chooseData

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり

Posted on 12月 24, 2019 by Takaaki Naganoya

先日「macOS Native」のイベント後に秋葉原で顔見知りのScripter3人でカレー食べて帰ってきました。その際に、アラートダイアログ上に作成した箱庭User Interface類の話で盛り上がっていました。「意外と使える」「自分でもいろいろ作ってる」「公開してよ」といったような。

本Scriptはその際の話を受けてEdama2さんが作っている試作品の、さらに試作品になるわけですが、プログラム内容が自分とは別の価値体系で清書し直されており、あいかわらずロジックとコードの美しさにうならされます。また、「本来倒すべき敵」に向けての実装が行われているので、単にテーブルビューを表示するだけには少々機能過剰になっています。

以下、Edama2さんによる投稿内容です。

タイトルは「アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり」

主な変更点は
表示する内容に合わせてviewのサイズを調整
レコードのラベルをヘッダのタイトルにする
Table ViewをダブルクリックするとOKボタンを押す
setAllowsEmptySelection: をfalseにして1行目を選択するようにした

データ作成部分が詰め込み効率優先ではなく可読性を高めた作りになっているのが、Edama2さんらしいところです。また、tellブロックをCocoa Scriptingにも多用しているのも特徴です。

ロジックが読みやすい一方で、Cocoa Framework相手にここまで見やすく書くのは、相当の努力が必要なはずで、Cocoa Scripting時に省いてしまいがちな「清書」という1手間が入っていることを感じさせます。

本Scriptはハンドラ(サブルーチン)が前の方に配置される記法です。AppleScriptはもともとハンドラ宣言部を前に書く言語なので、世界的に見るとこちらが主流です(Shane Stanleyですらハンドラ宣言部は前に書きます)。逆に、自分が他の言語に合わせてハンドラ宣言部を後ろに配置するのは、全体から見ると異端といえます(他の言語と書き方が違いすぎるのはどうかと思って、ハンドラ宣言部を後ろに書いています)。

たしかに、自分の箱庭テーブルビューダイアログではカラム幅とテーブルの縦サイズは一切手をつけていなかったので、縦横幅高さの自動調整が入ると、格段に見やすくなります。

Cocoaの機能を呼び出すAppleScriptにおいても、書きこなれてきたことによりハンドラ内にScript文を書いてさらに内部にハンドラを記述するといったような、論理分割やら階層構造化が本Scriptにおいて多用されています。そのあたり、applescript-stdlib(2015)で見かけましたが、本Scriptのように控えめな利用だと安心できます。

本Script内には継続記号(「¬」)が多用されていますが、macOS日本語ユーザー環境上でScript Debuggerを使っていると、構文確認時にこれがひっかかってエラーになることがあります(というか、実際にウチでこれを編集してなっています)。継続記号がひっかかって構文確認(コンパイル)できないようであれば、いっそ継続記号をすべて削除すべきでしょう。継続記号に「可読性を上げる」以外の機能はないため、削除しても無害です。

AppleScript名:アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり.scpt
on run
  
  
set aTableList to {}
  
set aTableList’s end to {|Name|:"MacBook Air", Price:"119,800円", Specs:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}
  
set aTableList’s end to {|Name|:"MacBook Pro 13", Price:"139,800円", Specs:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}
  
set aTableList’s end to {|Name|:"MacBook Pro 15", Price:"258,800円", Specs:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"Mac mini", Price:"122,800円", Specs:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"iMac 21.5", Price:"120,800円", Specs:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}
  
set aTableList’s end to {|Name|:"iMac 4K 21.5", Price:"142,800円", Specs:"3.6GHzクアッドコアIntel Core i3"}
  
set aTableList’s end to {|Name|:"iMac 5K 27", Price:"198,800円", Specs:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}
  
set optionRec to {aTableList:aTableList}
  
set optionRec to optionRec & {aSortOrder:{column1:"Name", column2:"Price", column3:"Specs"}}
  
  
set aMainMes to "項目の選択"
  
set aSubMes to "適切なものを以下からえらんでください"
  
set dateObj to my chooseData(aMainMes, aSubMes, optionRec)
end run

# アラートダイアログでtableviewを表示
on chooseData(aMainMes, aSubMes, optionRec)
  script MyDialog
    property parent : AppleScript
    
use AppleScript
    
use scripting additions
    
use framework "Foundation"
    
property _retrieve_data : missing value
    
on make
      set aClass to me
      
script
        property parent : aClass
      end script
    end make
    
## ダイアログの呼び出し
    
on call(aMainMes, aSubMes, optionRec)
      set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myOption:optionRec}
      
parent’s performSelectorOnMainThread:"raize:" withObject:paramObj waitUntilDone:true
      
if (my _retrieve_data) is missing value then error number -128
      
return (my _retrieve_data)
    end call
    
## ダイアログの生成
    
on raize:paramObj
      ###
      
set mesText to paramObj’s myMessage
      
set infoText to paramObj’s mySubMessage
      
set paramObj to paramObj’s myOption
      
### set up view
      
set {theView, makeObj} to my makeContentView(paramObj)
      
### set up alert
      
tell current application’s NSAlert’s new()
        setMessageText_(mesText)
        
setInformativeText_(infoText)
        
addButtonWithTitle_("OK")
        
addButtonWithTitle_("Cancel")
        
setAccessoryView_(theView)
        
tell |window|()
          setInitialFirstResponder_(theView)
        end tell
        
#### show alert in modal loop
        
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
      end tell
      
### retrieve date
      
my retrieveData(makeObj)
    end raize:
    
    
## ContentView を作成
    
property _data_source : missing value
    
on makeContentView(paramObj)
      ## 準備
      
set aList to (paramObj’s aTableList) as list
      
set keyRec to (paramObj’s aSortOrder) as record
      
set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyRec)
      
      
set my _data_source to current application’s NSArrayController’s new()
      
repeat with anItem in aList
        set aDict to (current application’s NSDictionary’s dictionaryWithDictionary:anItem)
        ((
my _data_source)’s addObject:aDict)
      end repeat
      
      
## NSTableView
      
tell current application’s NSTableView’s alloc()
        tell initWithFrame_(current application’s CGRectZero)
          setAllowsEmptySelection_(false)
          
setDataSource_(me)
          
setDelegate_(me)
          
setDoubleAction_("doubleAction:")
          
setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask)
          
setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular)
          
setTarget_(me)
          
setUsesAlternatingRowBackgroundColors_(true)
          
          
set thisRowHeight to rowHeight() as integer
          
set aTableObj to it
        end tell
      end tell
      
      
## NSTableColumn
      
### Columnの並び順を指定する
      
set viewWidth to 0
      
set columnsCount to keyDict’s |count|()
      
repeat with colNum from 1 to columnsCount
        
        
set keyName to "column" & colNum as text
        
set aTitle to (keyDict’s objectForKey:keyName)
        
        
tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text))
          tell headerCell()
            setStringValue_(aTitle)
            
set thisHeaderHeight to cellSize()’s height
          end tell
          
          
set aTableColumn to it
        end tell
        
        
### Columnの横幅を調整
        
tell aTableObj
          addTableColumn_(aTableColumn)
          
–reloadData()
          
set colWidthMax to 0
          
set rowCount to numberOfRows()
          
          
repeat with rowNum from 1 to rowCount
            tell preparedCellAtColumn_row_(colNum – 1, rowNum – 1)
              set thisWidthSize to cellSize()’s width
              
–log result
              
if thisWidthSize is greater than or equal to colWidthMax then set colWidthMax to thisWidthSize
            end tell
          end repeat
        end tell
        
–log colWidthMax
        
        
set colWidthMax to colWidthMax + 5 –> 5は余白
        
tell aTableColumn
          setWidth_(colWidthMax)
        end tell
        
        
set viewWidth to viewWidth + colWidthMax
      end repeat
      
      
## NSScrollView
      
### Viewの高さを計算
      
set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない
      
set vSize to current application’s NSMakeRect(0, 0, viewWidth + 10, viewHeight) –> 10を足すといい感じの横幅になる
      
      
### Viewを作成
      
tell current application’s NSScrollView’s alloc()
        tell initWithFrame_(vSize)
          setBorderType_(current application’s NSBezelBorder)
          
setDocumentView_(aTableObj)
          
–setHasHorizontalScroller_(true)
          
–setHasVerticalScroller_(true)
          
set aScroll to it
        end tell
      end tell
      
      
return {aScroll, aTableObj}
    end makeContentView
    
    
## retrieve data
    
on retrieveData(aTableObj)
      set aRow to aTableObj’s selectedRow()
      
log result
      
if aRow is -1 then
        set my _retrieve_data to {}
      else
        set my _retrieve_data to ((my _data_source)’s arrangedObjects()’s objectAtIndex:aRow) as record
      end if
    end retrieveData
    
    
    
# NSTableViewDatasource
    
on numberOfRowsInTableView:aTableView
      –log "numberOfRowsInTableView:"
      
return (my _data_source)’s content()’s |count|()
    end numberOfRowsInTableView:
    
# NSTableViewDelegate
    
on tableView:aTableView objectValueForTableColumn:aColumn row:aRow
      –log "objectValueForTableColumn"
      
set keyName to aColumn’s headerCell()’s title()
      
set aDict to (my _data_source)’s arrangedObjects()’s objectAtIndex:aRow
      
set aStr to aDict’s objectForKey:keyName
      
–log result as text
      
return aStr
    end tableView:objectValueForTableColumn:row:
    
# テーブル内のセルが編集できるか
    
on tableView:aTableView shouldEditTableColumn:aColumn row:aRow
      return false
    end tableView:shouldEditTableColumn:row:
    
# テーブル内をダブルクリックしたらOKボタンを押す
    
on doubleAction:sender
      –log "doubleAction"
      
–log sender’s |className|() as text
      
set theEvent to current application’s NSEvent’s ¬
        keyEventWithType:(current application’s NSEventTypeKeyDown) ¬
          location:(current application’s NSZeroPoint) ¬
          
modifierFlags:0 ¬
          
timestamp:0.0 ¬
          
windowNumber:(sender’s |window|()’s windowNumber()) ¬
          
context:(current application’s NSGraphicsContext’s currentContext()) ¬
          
|characters|:return ¬
          
charactersIgnoringModifiers:(missing value) ¬
          
isARepeat:false ¬
          
keyCode:0
      current application’s NSApp’s postEvent:theEvent atStart:(not false)
    end doubleAction:
  end script
  
  
##
  
tell (make MyDialog)
    return call(aMainMes, aSubMes, optionRec)
  end tell
end chooseData

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy | 1 Comment

アラートダイアログ上にTable Viewを表示 v6_サーチフィールドつき

Posted on 12月 24, 2019 by Takaaki Naganoya

検索フィールドつきのTable View表示ダイアログを表示するAppleScriptです。項目選択を行う簡易ユーザーインタフェース(ダイアログ)で、選択肢の数が多い場合に備えて項目のキーワード抽出機能を付加したものです。

サーチフィールドの実装は無理かと思っていたのですが、単体でアラートダイアログ上に表示してみたら、予想外にキーボード入力イベントと検索語のクリアのイベントを簡単に拾えたので、Table Viewと組み合わせて試してみました。

AppleScript名:アラートダイアログ上にTable Viewを表示 v6_サーチフィールドつき
— Created 2019-12-23 by Takaaki Naganoya
— 2019 Piyomaru Software
set aRes to displayCondTable() of filterTableDialogView
–> {field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)", field2:198800, field1:"iMac 5K 27"}

script filterTableDialogView
  use scripting additions
  
use framework "Foundation"
  
use framework "AppKit"
  
property parent : AppleScript
  
  
property NSView : a reference to current application’s NSView
  
property NSAlert : a reference to current application’s NSAlert
  
property NSIndexSet : a reference to current application’s NSIndexSet
  
property NSPredicate : a reference to current application’s NSPredicate
  
property NSScrollView : a reference to current application’s NSScrollView
  
property NSTableView : a reference to current application’s NSTableView
  
property NSSearchField : a reference to current application’s NSSearchField
  
property NSTableColumn : a reference to current application’s NSTableColumn
  
property NSMutableArray : a reference to current application’s NSMutableArray
  
property NSRunningApplication : a reference to current application’s NSRunningApplication
  
property NSModalPanelWindowLevel : a reference to current application’s NSModalPanelWindowLevel
  
  
property theResult : 0
  
property returnCode : 0
  
property theDataSource : {}
  
property tView : missing value
  
property aDataList : {}
  
  
  
on displayCondTable()
    set (my theResult) to 0 –initialize
    
    
set aDataList to {{field1:"MacBook Air", field2:119800, field3:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}, {field1:"MacBook Pro 13", field2:139800, field3:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}, {field1:"MacBook Pro 15", field2:258800, field3:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}, {field1:"Mac mini", field2:122800, field3:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}, {field1:"iMac 21.5", field2:120800, field3:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}, {field1:"iMac 4K 21.5", field2:142800, field3:"3.6GHzクアッドコアIntel Core i3"}, {field1:"iMac 5K 27", field2:198800, field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}, {field1:"iMac Pro", field2:558800, field3:"3.2GHz Intel Xeon W Turbo Boost使用時最大4.2GHz 19MBキャッシュ"}, {field1:"Mac Pro 2019", field2:599800, field3:"3.5GHz 8コアIntel Xeon Wプロセッサ(Turbo Boost使用時最大4.0GHz)"}}
    
    
set paramObj to {myMessage:"項目の選択", mySubMessage:"適切なものを以下からえらんでください", aTableList:aDataList, aSortOrder:{"field1", "field2", "field3"}}
    
    
–my chooseItemByTableView:paramObj –for debug
    
my performSelectorOnMainThread:"chooseItemByTableView:" withObject:paramObj waitUntilDone:true
    
    
return (my theResult)
  end displayCondTable
  
  
  
  
on chooseItemByTableView:paramObj
    set aMainMes to myMessage of paramObj
    
set aSubMes to mySubMessage of paramObj
    
set aTList to (aTableList of paramObj) as list
    
set labelSortList to (aSortOrder of paramObj) as list
    
    
set aWidth to 800
    
set aHeight to 300
    
    
–Viewをつくる
    
set parentView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
    
set aTextInput to makeNSSearchField(0, aHeight – 20, 300, 20) of me
    
    
    
set aScroll to makeTableView(aTList, aWidth, aHeight – 80, labelSortList) of me
    
    
parentView’s setSubviews:{aTextInput, aScroll}
    
    
— set up alert  
    
set theAlert to NSAlert’s alloc()’s init()
    
tell theAlert
      its setMessageText:aMainMes
      
its setInformativeText:aSubMes
      
its addButtonWithTitle:"OK"
      
its addButtonWithTitle:"Cancel"
      
its setAccessoryView:(parentView)
      
      
set myWindow to its |window|
    end tell
    
    
myWindow’s setLevel:(NSModalPanelWindowLevel)
    
    
— show alert in modal loop
    
NSRunningApplication’s currentApplication()’s activateWithOptions:0
    
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
    
if (my returnCode) = 1001 then error number -128
    
    
set tmpResult to (aScroll’s documentView’s selectedRow()) + 1
    
set (my theResult) to contents of item tmpResult of ((my theDataSource) as list)
  end chooseItemByTableView:
  
  
  
on doModal:aParam
    set (my returnCode) to (aParam’s runModal()) as number
  end doModal:
  
  
  
  
  
–TableView Event Handlers
  
on numberOfRowsInTableView:aView
    return (my theDataSource)’s |count|()
  end numberOfRowsInTableView:
  
  
  
on tableView:aView objectValueForTableColumn:aColumn row:aRow
    set aRec to (my theDataSource)’s objectAtIndex:(aRow as number)
    
set aTitle to (aColumn’s headerCell()’s title()) as string
    
set aRes to (aRec’s valueForKey:aTitle)
    
return aRes
  end tableView:objectValueForTableColumn:row:
  
  
  
  
on makeTableView(aDicList, aWidth, aHeight, labelSortList)
    set aOffset to 40
    
set theDataSource to NSMutableArray’s alloc()’s init()
    
theDataSource’s addObjectsFromArray:aDicList
    
    
–TextField
    
set aTextInput to makeNSSearchField(0, 0, 300, 20) of me
    
    
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
    
set tView to NSTableView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
    
    
set aLen to length of labelSortList
    
    
repeat with i in labelSortList
      set j to contents of i
      
set aColumn to (NSTableColumn’s alloc()’s initWithIdentifier:j)
      (
aColumn’s setWidth:(aWidth div aLen))
      (
aColumn’s headerCell()’s setStringValue:j)
      (
tView’s addTableColumn:aColumn)
    end repeat
    
    
tView’s setDelegate:me
    
tView’s setDataSource:me
    
tView’s reloadData()
    
    
aScroll’s setDocumentView:tView
    
tView’s enclosingScrollView()’s setHasVerticalScroller:true
    
aScroll’s setVerticalLineScroll:(30.0 as real)
    
    
–1行目を選択
    
set aIndexSet to NSIndexSet’s indexSetWithIndex:0
    
tView’s selectRowIndexes:aIndexSet byExtendingSelection:false
    
    
–強制的にトップにスクロール
    
set aDBounds to aScroll’s documentView()’s |bounds|()
    
if class of aDBounds = list then
      –macOS 10.13 or later
      
set maxHeight to item 2 of item 1 of aDBounds
    else
      –macOS 10.10….10.12
      
set maxHeight to height of |size| of aDBounds
    end if
    
    
set aPT to current application’s NSMakePoint(0.0, -1 * (maxHeight as real))
    
aScroll’s documentView()’s scrollPoint:aPT
    
    
return aScroll
  end makeTableView
  
  
  
on alertShowHelp:aNotification
    display dialog "Help Me!" buttons {"OK"} default button 1 with icon 1
    
return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする
  end alertShowHelp:
  
  
  
on makeNSSearchField(xPos as integer, yPos as integer, myWidth as integer, myHeight as integer)
    set aSearchF to NSSearchField’s alloc()’s initWithFrame:(current application’s NSMakeRect(xPos, yPos, myWidth, myHeight))
    
aSearchF’s setDelegate:(me)
    
return aSearchF
  end makeNSSearchField
  
  
  
–検索クエリ入力時
  
on searchFieldDidStartSearching:(aField)
    set aStr to (aField’s stringValue()) as string
    
set predicStr to "field1 contains[cd] ’" & aStr & "’ OR field3 contains[cd] ’" & aStr & "’"
    
set filteredArray to filterRecListByLabel1((my aDataList), predicStr) of me
    
set (my theDataSource) to filteredArray
    
tView’s reloadData()
  end searchFieldDidStartSearching:
  
  
  
–Search Fieldの「x」ボタンを押したイベント
  
on searchFieldDidEndSearching:(aField)
    set (my theDataSource) to NSMutableArray’s arrayWithArray:(my aDataList)
    
tView’s reloadData()
  end searchFieldDidEndSearching:
  
  
  
–リストに入れたレコードを、指定の属性ラベルの値で抽出
  
on filterRecListByLabel1(aRecList as list, aPredicate as string)
    set aArray to NSMutableArray’s arrayWithArray:aRecList
    
set aPredicate to NSPredicate’s predicateWithFormat:aPredicate
    
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
    
return filteredArray
  end filterRecListByLabel1
  
end script

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSIndexSet NSModalPanelWindowLevel NSMutableArray NSPredicate NSRunningApplication NSScrollView NSSearchField NSTableColumn NSTableView NSView | 8 Comments

アラートダイアログ上にTable Viewを表示 v5_複数キー抽出つき

Posted on 12月 23, 2019 by Takaaki Naganoya

アラートダイアログ上にTableViewを表示し、複数キーの組み合わせによって項目抽出。選択項目のデータを返すAppleScriptです。

あくまで試作品なので、あちらこちらに「お可愛らしい」実装が転がっています。NSBoxがまだまともに使いこなせていないあたりとか(Xcode上で部品として配置するのは楽勝ですが、プログラムから動的に生成して使用するのは別なので)。

抽出キー数は可変で外部から指定できるとよいのですが、本Scriptでは固定です。また、各抽出条件も外部から供給するのが本来あるべき姿ですが、ハードコーディングしています。

あと、TableViewの下側に謎の余白があるのは、めんどくさくなってそのまま放置しています。これで外部から各種パラメータを指定できるようにして、sdefつけて1命令で呼び出せるようにしておけば、レコード数の多い選択項目から条件抽出しつつ項目選択できてよいでしょう。

プログラム内容自体は、高度でもなければ高尚でもありません。実にダラダラとGUI部品のパラメータを設定しているだけです。

AppleScript名:アラートダイアログ上にTable Viewを表示 v5_複数キー抽出つき
— Created 2019-12-22 by Takaaki Naganoya
— 2019 Piyomaru Software
set aRes to displayCondTable() of filterTableDialogView
–> {field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)", field2:198800, field1:"iMac 5K 27"}

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

★Click Here to Open This Script 

Posted in dialog GUI list Record search | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSAlertSecondButtonReturn NSBox NSColor NSCompoundPredicate NSIndexSet NSModalPanelWindowLevel NSMutableArray NSPopUpButton NSPredicate NSRunningApplication NSScrollView NSTableColumn NSTableView NSView | 1 Comment

CotEditor PowerPackのPiyomaru Software内での位置付け

Posted on 12月 22, 2019 by Takaaki Naganoya

CotEditor向けの機能強化Script群「CotEditor PowerPack」は、自分たちが便利そうだと思った機能をびっしりと敷き詰めて実装してみました(基礎的なScriptingについては、同梱の基礎的なサンプルスクリプト集「BasicPack」にまとめています)。PowerPackを実際に作ってみたら、メニューで項目が増えたときに絵文字によるラベリングが割と有効だという発見ももたらしました。

ただ、PowerPackがPowerPack単体で存在するはずもなく、Piyomaru Softwareが作成している各種プログラムとも密接な関係にあります。歴史的な経緯の中で先行ソフトウェアの影響も受けていますし、さまざまなプロジェクトの成果物や技術の実証の場として利用しているからです。

Tanzakuの部品を利用

具体例を言うと、Tanzaku用に開発した コマンド解析用 簡易日本語形態素解析エンジン「easyJParse」。これはAppleScriptだけで記述した80行ほどの規模の形態素解析プログラムです。これをコマンド解析用以外の、伏せ字処理のプログラムに使っています。

「航空機の部品を炊飯器に転用する」ような違和感はありますが、想定外の用途に部品を使いまわすと意外な発見があります。easyJParseはコマンド解釈用の日本語Parseプログラムであり、長大な文章を解釈するようには実装していません。そこに実際に数十〜数百KBのサイズの日本語テキストを与えてParseするとどうなるかという実験が行えました。実際、ループで文章単位のテキストを与えているため、クラッシュしたり迷走したりすることなく処理できました(それなりに時間はかかりましたが)。データの持ち方をより大規模なデータを与えても大丈夫なように備えていれば、ある程度の大規模データにも備えることができるでしょう。

コマンド構造は日本語音声認識コマンダーの影響

多くのコマンドから全体を構成するノウハウは、2003〜2004年ごろに作っていた日本語音声認識コマンダー(いまのAIスピーカーみたいなもの)「符令韻投句」(ぷれいんとーく)で培ったものです。各コマンドの粒度については小さいほうがいいのか大きいものがあるべきかなどまだ実験中ですが、PowerPackの場合には「細かいコマンドを寄せ集めるとどうなるか」という実験を行なってみています。

Piyomaru Script Assistantに影響

スクリプトエディタの編集エリア上でコンテクストメニューを表示させて呼び出すScript Assistantは、Apple純正のものがOSにデフォルトで入っていますが、網羅性がいまひとつで実用性もさっぱりです。

そこで、Mac OS X 10.3から4の時代にこれを自分に合わせてすべて書き直し、さらに書籍のオマケに出すために網羅性を高めたものが現在配付中のPiyomaru Script Assistantです。

Piyomaru Script AssistantのScriptのうちmacOS 10.13で動かなくなってしまったものがいくつかあり、また、内容についても更新の必要を(さすがに2006年ごろに作ったものなので)感じたため、こちらも強化を行なっています。

さらに、PowerPackで積み重ねた実験結果をPiyomaru Script Assistantにフィードバック。CotEditorのPowerPackで培ったメニューにおける絵文字の活用ノウハウが生かされており、また、初心者に違和感なくCocoa Scriptingの部品を使ってもらえるような配慮を行っています(Cocoaの機能をまったく使わないVanilla Scriptの中にCocoa ScriptingのScriptingモジュールを埋め込む格好)。

そもそも、本Blogがスタートした経緯そのものが、すべてAppleScriptで記述した人工知能インタフェース「Newt On」(文字版のSiri)の内部ルーチン(部品)のメンテナンスとか共有が目的だったので、Piyomaru SoftwareのプログラムはおしなべてNewt Onの影響を受けていたり、そのものの部品を使っていたりもします。

これらの技術や経験もすべてTanzakuに向けて蓄積されているところです。

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

アラビア語ローカライズしたアプリケーションの挙動の変化

Posted on 12月 22, 2019 by Takaaki Naganoya

Xcode上でAppleScriptのアプリケーションを作って(Double PDF)、各国語にローカライズしていたときに、最難関言語として名高いアラビア語(Arabic)を試してみたことがありました。

ローカライズ作業自体にもAppleScriptを投入し、テキストエディタ上でオープンしているstringsファイルを読み取ってGoogle Translate APIを呼び出して指定言語に数秒で翻訳。記号類だけから構成されるフィールドは翻訳しないなど、その場でScriptを調整しながら大幅な翻訳作業の省力化を実現(ほかの方法もあるようなので、これがベストとは言いませんが、、、)。このScriptなしに他の言語への翻訳などというめんどくさい作業はやろうとは思いませんでした。

この翻訳用AppleScriptの威力で30言語ぐらい翻訳。主要言語のうち、最後に残ったのが、文字が右から左に流れるアラビア語とヘブライ語でした。

まずは、挙動がどのぐらい違うのか確認。Xcodeでローカライズ言語にアラビア語を追加。stringファイルの内容自体はオリジナルの英語のままの状態で、実行時のアプリケーションの言語環境に「アラビア語」を指定して起動してみました。

起動してびっくり!


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


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


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


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


▲アラビア語環境で表示したAbout Box@macOS 10.14.6。スクロールバーが右側ではなく左側に

ツールバーアイコンはデフォルトの英語環境や日本語環境では、左→右の順で並びますが、アラビア語環境ではウィンドウ上部右→左と並びました。メニューの内容も右側にタイトルが来て、左側にキーボードショートカットが並ぶなど、予想外の配置です。

これは、バグとかいった種類の話ではなく、はじめてアラビア語環境で自作のアプリケーションを起動して、その挙動の違いに驚かされたという話であります。

2つのPDFViewを並べてあり、それぞれ右とか左とか言わなくてもわかるよう、左側には青色のラベル、右側には赤色のラベルを貼っておいたのですが、ツールバーアイコンの左右も入れ替わってしまい、自分の意図したとおりのレイアウトからはかけ離れたものとなってしまいました。

アプリケーション内部で、アプリケーション起動環境から言語がアラビア語やヘブライ語のように右→左の表示を行う言語だった場合には何か処理を行なってツールバーボタンの入れ替えなどを行う必要があることでしょうか。ちょっとノーアイデアで分からないところ。ひたすら驚かされました。他の言語は軒並みGoogle Translation APIをAppleScriptから呼び出して翻訳できたのですが、アラビア語とヘブライ語は難敵でした。攻略できていないので、そこんとこなかなか辛いです。

Double PDF v2.0自体はMac App Storeで絶賛レビュー中です。In Reviewの状態に入ってからはや10日が経過し、さすがに不安になってDeveloper Connectionに電話して聞いてしまいましたが、わかったのは本当にまだレビュー作業中で、レビューにさらなる時間がかかるということだけです。

そろそろ、実際にアップデート作業にかけた時間よりもレビューにかかっている時間のほうが長くなりつつあり、そして確実に数倍の時間がかかりそうでもあり……各国語のPDFでも作って(表示をローカライズした30言語分)、それらのPDFで差分検出チェックでもしてるんじゃないかと、、、

Posted in AppleScript Application on Xcode | Tagged 10.14savvy | Leave a comment

PermissionsKitでユーザー権限のチェックとリクエスト(フルディスクアクセス)

Posted on 12月 19, 2019 by Takaaki Naganoya

PermissionsKit.frameworkの機能を呼び出して、実行中のアプリケーションがフルディスクアクセスの権限を持っているかどうか確認を行い、持っていなかった場合には権限を許可してもらえることを願ってシステム環境設定の「セキュリティとプライバシー」>「セキュリティ」を表示します。

–> Download PermissionsKit_tester(Code-Signed Executable Applet with Framework in its bundle)

だいたいはサンプルのObjective-CのプログラムをAppleScriptに翻訳して実行しているだけですが、1箇所だけ発見がありました。

ご存知のとおりBlocks構文はAppleScriptでは書けないので、Blocks構文の記述が要求されるAPIはAppleScriptから呼び出せません。

本FrameworkのAPIも権限を要求するときにBlocks構文の記述が求められるのですが、ためしにBlocks(^)の記述を要求される箇所にヌルを指定してみたところ、、、、動きました(^ー^;;;;;;

# なんでも効くわけではないようです

あー、Blocks構文のところにヌルを指定すると動くんだー。処理終了時に実行する内容をBlocksの中に書くのを、何も書かないことで同期実行とほぼ同じ扱いができるんじゃないかと。いろいろ試してみると発見があるかもしれません。

# 今日、Edama2さんと世間話していたら「パラメータとしてBlocks構文を必要とされる箇所にmissing valueを入れて動いたケースもあったよ」とのこと。そういうの重要なので教えてほしかったー(T_T)

PermissionsKit.frameworkでは各種権限設定の確認と要求が行えるようにはなっているのですが、フルディスクアクセス以外はすべてクラッシュして動かなかったので、フルディスクアクセスの権限確認&要求専用とみなしています。

AppleScript名:PermissionsKitでユーザー権限のチェックとリクエスト(フルディスクアクセス).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/19
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "PermissionsKit" –https://github.com/MacPaw/PermissionsKit
use scripting additions

property MPPermissionTypeCalendar : 0
property MPPermissionTypeReminders : 1
property MPPermissionTypeContacts : 2
property MPPermissionTypePhotos : 3
property MPPermissionTypeFullDiskAccess : 4

–MPPermissionTypeFullDiskAccess 以外はクラッシュして処理継続できなかった

set aPerm to current application’s MPPermissionsKit’s alloc()’s init()
set pRes to current application’s MPPermissionsKit’s authorizationStatusForPermissionType:(MPPermissionTypeFullDiskAccess)

set sRes to getStatus(pRes) of me
log sRes

if pRes is not equal to 0 then
  set pRes to current application’s MPPermissionsKit’s requestAuthorizationForPermissionType:(MPPermissionTypeFullDiskAccess) withCompletion:""
end if

on getStatus(aFlag)
  if aFlag = 0 then
    return "Denied" –拒否された
  else if aFlag = 1 then
    return "Authorized" –承認ずみ
  else if aFlag = 2 then
    return "Not determined" –未定
  end if
end getStatus

★Click Here to Open This Script 

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

画像の空白判定 v4

Posted on 12月 19, 2019 by Takaaki Naganoya

画像のすべてのピクセルが白であることをチェックするAppleScriptです。

以前にmacOS 10.12上で作成した「空白画像検出v3」は一般的に処理時間のかかる空白画像の検出(ヒストグラム計算してチェックする?)が高速に処理できます(GPUImageでヒストグラム計算を行うより2倍ぐらい高速)。空白検出のためだけにAdobe Photoshopが必要になるといった事態は避けられます。

–> test Data(動作検証用各種テストデータ)

macOS 10.14.6上でこのルーチンを再度試してみたところ、思ったとおりに空白検出(数百万ピクセルの画像でも1つだけ黒であればfalseを返す)してくれません。ちょっと書き方を変えていますが、単体で通常のAppleScript(Cocoa不使用)にコンテクストメニューから突っ込むモジュールとして運用するためです。

copyコマンドがdeep copyしてくれない?

参照ではなくすべてのデータをコピーするコマンドとして、copyコマンドはとても重宝しています。単に「パラメータの代入の方向が違う偏屈なコマンド」ではなく、明示的にデータ内容をコピーするためのコマンドと認識しています。

AppleScriptのオブジェクト同様、Cocoaのオブジェクトについてもcopyコマンドでdeep copyしてくれるものと理解していましたが、macOS 10.14.6上で本ルーチンを試してみたところ、どうも参照をコピーしている気配がします。

AppleScriptのオブジェクト(配列とか)をcopyコマンドでコピーすると、参照ではなくデータが複製されていることを確認しました。Cocoaオブジェクトのcopyも同様かどうかは、ちょっとそうじゃないんじゃないかというところです。

set bNSImage to aNSImage's |copy|()

Cocoaの世界のオブジェクトのコピーはこんな感じ(↑)でできたので、今後はこちらを採用することにします。

1×1のNSImageに色を塗るとデータサイズが増える

ビットマップ画像の状態であるNSBitmapImageRepになっていれば、1×1画像のサイズは同じことが期待されます。実際、macOS 10.12上ではそのように扱えていました。

ところが、macOS 10.14上で1×1ドットのNSImageに対して色を塗って、NSBitmapImageRepを取得すると、塗らない状態のNSImageよりもデータ(NSData)サイズが大きくなることがわかりました。


▲1×1のPNG画像をビットマップ化したデータ。赤が処理前、青が塗りつぶし処理後のデータ(macOS 10.14.6)。ピクセル数が増えてるのでは? と疑ってサイズを何回も計算して確認しましたが、間違っていません

しかも、データ量がmacOS 10.14と10.15では大幅に異なります(macOS 10.15では大幅にデータ量が減る)。

 1×1 PNG画像 ビットマップ化したデータサイズ:3,354 Bytes(macOS 10.14.6)
 1×1 PNG画像 塗りつぶし ビットマップ化したデータサイズ:3,574 Bytes(macOS 10.14.6)
 1×1 PNG画像 塗りつぶし ビットマップ化したデータサイズ:750 Bytes(macOS 10.15.2)

さすがに匙を投げかけましたが、比較元のデータに透明の色(clearColor())を塗る=画像的に意味はないが、同じ処理を通すことでデータサイズを同程度にするという処理を行うことで、いい感じに空白検出できるようになりました。

なにをどういじくったら、1×1ビットマップ画像のサイズがOSアップデートごとに大幅に変わるのかさっぱり理解できないのですが、そこに何か意味があるのでしょうか?

Mac App Storeに出したアプリケーションで、PDFの空白ページ検出に本ルーチンを利用していますが、Appleのレビュー段階で「単にPDFから文字を抽出して空白検出しているのだろう」と思われてリジェクトされました。実際には、全ページを画像レンダリングして1ドットの画像でも存在していたら空白としては認識しない仕様になっているのですが、Appleのレビューワーには処理内容を説明して、各種ベンチマーク結果などを出さないと信じてもらえませんでした。

Appleのエンジニアが考えるところの空白検出が「文字チェックだけ」の使い物にならない処理だということはよくわかりました。また、頭からそのように思い込んで攻撃してきた間抜けにも遭遇してウンザリしました。

AppleScript名:画像の空白判定 v4
set aFile to (choose file of type {"public.image"})
set iRes to checkImageIsWhite(aFile) of whiteImageKit
–> true –white (blank) image

script whiteImageKit
  use AppleScript version "2.7" — High Sierra (10.13) or later
  
use framework "Foundation"
  
use framework "AppKit"
  
use scripting additions
  
property parent : AppleScript
  
  
property NSData : a reference to current application’s NSData
  
property NSDate : a reference to current application’s NSDate
  
property |NSURL| : a reference to current application’s |NSURL|
  
property NSColor : a reference to current application’s NSColor
  
property NSImage : a reference to current application’s NSImage
  
property NSBezierPath : a reference to current application’s NSBezierPath
  
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
  
  
–Compare Original Data and
  
on checkImageIsWhite(aFile)
    set aPOSIXpath to POSIX path of aFile
    
set aURL to |NSURL|’s fileURLWithPath:(aPOSIXpath)
    
    
set aNSImage to NSImage’s alloc()’s initWithContentsOfURL:(aURL)
    
set bNSImage to NSImage’s alloc()’s initWithContentsOfURL:(aURL)
    
    
–copy aNSImage to bNSImage–Not deep copy ????
    
    
set fillColor1 to NSColor’s clearColor()
    
set blankNSImage1 to drawImageWithFilledColor(aNSImage, fillColor1) of me
    
    
set fillColor2 to makeNSColorFromRGBAval(65535, 65535, 65535, 65535, 65535) of me
    
set blankNSImage2 to drawImageWithFilledColor(bNSImage, fillColor2) of me
    
    
set aTiff to blankNSImage1’s TIFFRepresentation()
    
set bTiff to blankNSImage2’s TIFFRepresentation()
    
    
set chkWhite to (aTiff’s isEqualToData:bTiff) as boolean
    
    
return chkWhite
  end checkImageIsWhite
  
  
  
on getSizeOfImage(anNSImage)
    set aSize to anNSImage’s |size|()
    
set aClass to class of aSize
    
if aClass = record then
      copy aSize to theSize –To macOS 10.12.x
    else –macOS 10.13 or later
      set sizeX to (item 1 of item 2 of aSize)
      
set sizeY to (item 2 of item 2 of aSize)
      
set theSize to {width:sizeX, height:sizeY}
    end if
    
return theSize
  end getSizeOfImage
  
  
  
–指定サイズの画像を作成し、背景を指定色で塗る
  
on drawImageWithFilledColor(anImage, fillColor)
    set aSize to getSizeOfImage(anImage) of me
    
    
anImage’s lockFocus()
    
    
set theRect to {{x:0, y:0}, {width:(width of aSize), height:(height of aSize)}}
    
set theNSBezierPath to NSBezierPath’s bezierPath
    
theNSBezierPath’s appendBezierPathWithRect:theRect
    
fillColor’s |set|()
    
theNSBezierPath’s fill()
    
    
anImage’s unlockFocus()
    
    
return anImage
  end drawImageWithFilledColor
  
  
  
–aMaxValを最大値とする数値でNSColorを作成して返す
  
on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
    set aRedCocoa to (redValue / aMaxVal) as real
    
set aGreenCocoa to (greenValue / aMaxVal) as real
    
set aBlueCocoa to (blueValue / aMaxVal) as real
    
set aAlphaCocoa to (alphaValue / aMaxVal) as real
    
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
    
return aColor
  end makeNSColorFromRGBAval
  
end script

★Click Here to Open This Script 

Posted in Image | Tagged 10.13savvy 10.14savvy 10.15savvy NSBezierPath NSBitmapImageRep NSColor NSData NSImage NSURL | 3 Comments

元号変換v41

Posted on 12月 19, 2019 by Takaaki Naganoya

西暦→元号の変換を行うAppleScriptです。誰もがこんな感じのプログラムを作って(書き捨てて)いると思うのですが、不思議と誰も公開しないという、、、

Japanese Gengo is only for Japanese calendar. It is old-style age names in Japanese. I don’t care of it for daily use.

AppleScript名:元号変換v41
set a to "2019/7/21"
set {aGengoStr, aGengoNum} to retJapaneseGengo(a) of JGengoKit
–> {"令和", 1}

script JGengoKit
  on retJapaneseGengo(aDate as string)
    set aDateObj to date aDate
    
    
tell current application
      set aYear to year of aDateObj
      
set aMonth to month of aDateObj as number
      
set aDay to day of aDateObj
      
      
set aStr to retZeroPaddingText(aYear, 4) of me & retZeroPaddingText(aMonth, 2) of me & retZeroPaddingText(aDay, 2) of me
      
      
set aGengo to ""
      
if aStr ≥ "20190501" then
        set aGengo to "令和"
        
set aGengoNum to aYear – 2019 + 1
      else if aStr ≥ "19890108" then
        set aGengo to "平成"
        
set aGengoNum to aYear – 1989 + 1
      else if aStr ≥ "19261225" then
        set aGengo to "昭和"
        
set aGengoNum to aYear – 1926 + 1
      else if aStr ≥ "19120730" then
        set aGengo to "大正"
        
set aGengoNum to aYear – 1912 + 1
      else if aStr ≥ "18680125" then
        set aGengo to "明治"
        
set aGengoNum to aYear – 1868 + 1
      end if
      
      
return {aGengo, aGengoNum}
    end tell
  end retJapaneseGengo
  
  
–数値にゼロパディングしたテキストを返す
  
on retZeroPaddingText(aNum, aLen)
    set tText to ("0000000000" & aNum as text)
    
set tCount to length of tText
    
set resText to text (tCount – aLen + 1) thru tCount of tText
    
return resText
  end retZeroPaddingText
end script

★Click Here to Open This Script 

Posted in Calendar | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy | 2 Comments

macOS 10.13以降、スクリプトエディタのヘルプが更新され続けて驚く

Posted on 12月 18, 2019 by Takaaki Naganoya

力が入っているのか抜けているのかわかりませんが、スクリプトエディタのヘルプがOSのメジャーバージョンアップごとに更新されているのを見つけて驚かされました。

 「スクリプトエディタヘルプ」(macOS High Sierra用)
 「スクリプトエディタユーザーガイド macOS Mojave用」
 「スクリプトエディタユーザーガイド macOS Catalina用」

などと、わざわざOSごとに更新され、日本語環境用に翻訳されています。ちょっと驚きました。

ただ、相変わらず内容は「は?」とか「えーー?」とかいう感想しか出てこないものなので、存在している以上の実質的な価値はありません。存在しないよりは数万倍よいのですが、「これを読んで使えるようにはならないだろー」という程度のものです。


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


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


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

冒頭から内容が、

「OS X 10.10 以降では、AppleScript に加え、スクリプト言語である JavaScript for Automation を使用してスクリプトを記述することができます。また、シェルスクリプトや、UserTalk などの他社製のスクリプト言語を使用してスクリプトを記述することもできます。」

とあり、「あたかもシェルスクリプトをOSA言語的に扱えるかのような間違った印象を与えてしまう」「UserTalk(Frontier)なんてもう誰も使っていないし、消滅して久しいぞ」といったツッコミを行なってしまうところですが、Apple社内の現場が内容のチェックをしないで秘伝のタレのように注ぎ足し注ぎ足しで更新しているだけだと痛感させられます。

内容は機械的に翻訳しているだけのようで、コンテクストメニューから呼び出せるApple純正のScriptはみんなローカライズされていない(事実)のに、ヘルプ内では名称などが日本語訳されて紹介されており、実際にコンテクストメニューを表示させると困惑させられること請け合いです。外注の翻訳会社のせいではなく、Apple側が何も指定しなかったので機械的に翻訳しただけでしょう。


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


▲実際にmacOS 10.15上のスクリプトエディタでコンテクストメニューを表示すると英語のまま

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

CotEditor用のPowerPackの影響を受けて、そういう方向のScriptも突っ込んでいます。スクリプトエディタのコンテクストメニューから呼び出したAppleScriptから、スクリプトエディタのウィンドウをぐるぐる回してみようとしたら、ほとんど再描画されずに結果だけ表示されるという一幕もありました。

Posted in Bug | Tagged 10.13savvy 10.14savvy 10.15savvy Script Editor | Leave a comment

macOS 10.14でScript Editorのsdefから「execute」コマンドが除去されていた

Posted on 12月 16, 2019 by Takaaki Naganoya

macOS 10.14のスクリプトエディタのAppleScript用語辞書(sdef)から「execute」コマンドが除去されていることに気づきました。

このことは、別に「GUI側からスクリプトエディタ上のAppleScriptを実行できなくなった」ということではありません。

スクリプトエディタ自体もAppleScriptからコントロールできる「スクリプタブルな」アプリケーションであり、macOS 10.14以降ではAppleScriptからスクリプトエディタをコントロールしてAppleScriptを実行させることができなくなった、ということです。

セキュリティ向上のためにexecuteコマンドを削除したように見えますが、そういうことをやったならRelease Notesに明記しておいてほしいものです。

Piyomaru Context menu AssistantのmacOS 10.13版を10.14上で動作確認していたら、動かなくなっているものがあって(クラッシュするものも出てきております)、原因を確認していたら一部の予約語が、

with timeout of 36000 seconds --1時間のタイムアウト
	«event sedsexec»
end timeout

のように文字化け。これによって「execute」(Script実行)の予約語が抹消されたことがわかりました。スクリプトエディタ自体をコントロールしてScriptを実行させるケースは少ないので、世界中でも私ぐらいしか影響はないことでしょう。ちなみに、サードパーティのAppleScript統合開発環境であるScript Debuggerにはexecuteコマンドがあります。

別にスクリプトエディタでAppleScript書類をオープンして実行するような迂遠かつマニアックかつ「もっと簡単な手段があるのにわざわざそんなことしなくても」というアクションを好き好んで行う人はほとんどいないでしょう。全人類のうち、私以外にダメージはないことでしょう。

実際にスクリプトエディタにexecuteコマンドを送って実行していた例というのは、実行時間の計測を行うAppleScriptです。地味にそういうのを使っていました(最近は、Shane StanleyのScript Geekで処理時間を計測するパターンが多いので使っていませんでしたが)。


▲macOS 10.13.6


▲macOS 10.13.6


▲macOS 10.14.6


▲macOS 10.14.6


▲macOS 10.15.2


▲macOS 10.15.2

Posted in sdef | Tagged 10.14savvy 10.15savvy Script Editor | Leave a comment

choose multiple path Script Libraryをv2.1にアップデート

Posted on 12月 16, 2019 by Takaaki Naganoya

NSPathControlでドラッグ&ドロップにより複数のパス情報を設定するダイアログUIを提供するAppleScriptライブラリ「choose multiple path Script Library」をv2.1にアップデートしました。

–> Download choosePathLib.scptd(~/Library/Script Libraries)

変更点:
・dialog widthでダイアログの表示幅を指定できるようにした

・NSPathControlの配色がDarkModeに合わなかったので、色は無指定にした

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

さまざまなGUI部品を使ってアラートダイアログ上に箱庭ユーザーインタフェースを作り込んだ場合に、通常のAppleScript書類のままだと、ランタイム環境(とくにScript Debugger)の差異の影響を受けてドラッグ&ドロップを受信できないとか実行時に明示的にメインスレッド実行が必要になるケースが見られます。

AppleScript用語辞書を通じてアクセスすることで、そうした問題を「うやむやにできる」ことがわかってきたので(技術的には説明できないものの、だいたいそんなもんだろうと予想はしていました)、こうした再利用の可能性が高いGUI利用部品は積極的にライブラリにまとめています。

Posted in Script Libraries | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy | Leave a comment

指定緯度における1km相当の経度の大きさを求める

Posted on 12月 15, 2019 by Takaaki Naganoya

指定緯度における1kmあたりの経度の角度などを計算するAppleScriptです。Rubyのコードで書かれたものがあったので、AppleScriptに翻訳してみました。

三角関数を用いるため、関数計算ライブラリ「calcLibAS」を併用しています。

なんでこんな計算をすることになったかといえば、Apple Mapsの仕様で、地図表示エリアの指定時に表示距離ではなく表示角度(spn)が要求されるようなので、計算方法を求めておいたという次第です。

AppleScript名:1kmあたりの経度の大きさを求める.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/15
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use calcLib : script "calcLibAS"

–参照:1kmあたりの経度の大きさ
–https://github.com/ryym/google-maps-demo/blob/master/note.md
set aLat to 31.5719562 –鹿児島の緯度
set aDeg to calc1kmDegreeinLatitude(aLat) of me –経度方向(横)に1kmに相当する角度
–> 0.010543813284

set bDeg to calc100mDegreeinLatitude(aLat) of me –経度方向(横)に100mに相当する角度
–> 0.001054381328

set aLat to 35.73993521 –練馬の緯度
set aDeg to calc1kmDegreeinLatitude(aLat) of me –経度方向(横)に1kmに相当する角度
–> 0.011067403977

set bDeg to calc100mDegreeinLatitude(aLat) of me –経度方向(横)に100mに相当する角度
–> 0.001106740398

–1kmあたりの緯度の大きさ
set eRes to calc1kmDegreeinLong() of me –緯度方向(縦)に1kmに相当する角度
–> 0.008983152841

set eRes to calc100mDegreeinLong() of me –緯度方向(縦)に100mに相当する角度
–> 8.98315284119522E-4

–指定緯度における1km相当の経度の角度
on calc1kmDegreeinLatitude(aLat)
  set eRadius to 6378.137 –in km
  
set r to eRadius * (cos (aLat * pi / 180))
  
set cm to 2 * pi * r
  
set kd to 360 / cm
  
return kd
end calc1kmDegreeinLatitude

–指定緯度における1km相当の経度の角度
on calc100mDegreeinLatitude(aLat)
  set eRadius to 6378.137 –in km
  
set r to eRadius * (cos (aLat * pi / 180))
  
set cm to 2 * pi * r
  
set kd to 360 / cm
  
return kd / 10
end calc100mDegreeinLatitude

–1km相当の緯度の角度
on calc1kmDegreeinLong()
  set eRadius to 6378.137 –in km
  
set pc to 2 * pi * eRadius
  
set dLat to 360 / pc
  
return dLat
end calc1kmDegreeinLong

–100m相当の緯度の角度
on calc100mDegreeinLong()
  return calc1kmDegreeinLong() / 10
end calc100mDegreeinLong

★Click Here to Open This Script 

Posted in geolocation Number | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy Maps | Leave a comment

map scripter script Library

Posted on 12月 14, 2019 by Takaaki Naganoya

macOS添付の地図.app(Maps.app)をAppleScript的な用語で操作するAppleScriptライブラリ「map scripter」の配布を開始しました。macOS 10.10以降(作成+動作確認は10.14)で、動作するはずです。

–> Download mapScripter(To ~/Library/Script Libraries)

Maps.appのコントロールは非同期のURL Eventsで行われるため、本ライブラリを通じてMaps.appを操作しても、macOSの「セキュリティ」ダイアログは表示されません。そのかわり、100%操作できるという保証もありません(時間帯によって処理要求が返ってこなかったりします。とくに、グルメ系検索)。また、Maps.appの実行のためにインターネット接続が必須です。

Maps.appは外部からURL Eventのみでコントロール可能なアプリケーションです。操作の方法がエキセントリックすぎるので、一般的なAppleScript対応アプリケーションと同様の英語的な用語でアクセスできるようにしてみました。例によって、実行結果イメージやサンプルScriptをsdefの中に入れてあります。

macOS 10.15.2上で動作させたときに、「display around here」コマンドが、

のようなエラーを表示することがあります。これは、どうも位置情報サービスに要求を出したのに拒否されたという種類のOS側のエラーのようで、システム環境設定の「セキュリティとプライバシー」>「プライバシー」>「位置情報サービス」のあたりで認証が行われなくてはならないはずのもの(認証済み)が、エラーを起こしているようで、、、、macOS側のバグと言っていい挙動だと思います。

Maps.app以外でも、AppleScript系の機能実装がおかしいApple純正のアプリケーションに対し、Framework経由でデータアクセスするようなライブラリがあると便利かもしれませんが、そこまでやったらフリー配布はちょっと勘弁してほしい感じがします。

AppleScript名:display map by address.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/14
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use mapLib : script "map scripter"

display map by address "東京都港区六本木6丁目10番1号" map type normal zoom level 25

★Click Here to Open This Script 

AppleScript名:display map route.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/14
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use mapLib : script "map scripter"

display map route from "豊島園" to "目黒" using public

★Click Here to Open This Script 

AppleScript名:display point.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/14
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use mapLib : script "map scripter"

display point query "レストラン" latitude 31.5719562 longitude 130.56257084

★Click Here to Open This Script 

AppleScript name:display around here.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/14
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use mapLib : script "map scripter"

display around here query "ラーメン"

★Click Here to Open This Script 

com.apple.Maps

Posted in geolocation | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy Maps | Leave a comment

ダークモードの検出 v4

Posted on 12月 13, 2019 by Takaaki Naganoya

現在のアピアランスがDark ModeかLight Modeかを検出するAppleScriptです。macOS 10.14以降用です。

System Eventsに問い合わせれば簡単に確認できますが、アプレット内/Cocoa Application内でSystem Eventsを呼び出すとセキュリティダイアログが表示されてしまうので、このぐらいで表示されてしまうのは癪なので、Cocoaの機能を利用して検出してみました。

AppleScript名:ダークモードの検出 v4
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/13
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — Mojave (10.14) or later
use framework "Foundation"
use scripting additions

set apRes to retLightOrDark() of me

on retLightOrDark()
  set curAppearance to ((current application’s NSAppearance)’s currentAppearance()’s |name|()) as string
  
set aDark to (current application’s NSAppearanceNameDarkAqua) as string
  
  
if curAppearance = aDark then
    return true
  else
    return false
  end if
end retLightOrDark

★Click Here to Open This Script 

System Eventsを使うとこんな感じになりますが、、、

AppleScript名:ダークモードの検出 v3
set apRes to retLightOrDark() of me

on retLightOrDark()
  tell application "System Events"
    tell appearance preferences
      return dark mode –returns true in dark mode
    end tell
  end tell
end retLightOrDark

★Click Here to Open This Script 

Posted in System | Tagged 10.14savvy 10.15savvy | 2 Comments

macOS 10.14にchoose fileのバグ?

Posted on 12月 13, 2019 by Takaaki Naganoya

macOS史上最低、どうしてあの状態でリリースしたのか理解に苦しむmacOS 10.13の後なので、どうしてもかなり贔屓目に見てしまうmacOS 10.14ですが、やはりバグはありました。

2019/12/13追記 デフォルト設定値の問題で、階層表示は行えることを確認しました。

choose file with showing package contents

このコマンドを実行した場合には、パッケージ内容を表示しつつパッケージ内のファイルを選択できるはずですが、macOS 10.14ではこれがパッケージ内のファイルは選択できるものの、階層表示できていません。こんなchoose file with showing package contentsの挙動なんて見たことがありません。


▲macOS 10.13.6上の動作


▲macOS 10.14.6上の動作。一応、バンドルを選択するとその内部のフォルダに移動することは可能だが、階層表示されない


▲macOS 10.15.2上の動作

追記 edama2さんから「こちらではそういうことはないよ?」というツッコミを送っていただきました。再度確認したところ、choose fileダイアログの項目分けの初期値がmacOS 10.14では「なし」ではなく「名前」になっているために、自分のデフォルト設定では階層表示されていなかったようです。


▲自分のmacOS 10.14.6環境の初期状態。項目分けが「名前」になっていた


▲自分のmacOS 10.14.6環境でも、項目分けを「なし」に変更すると階層表示された

Posted in Bug dialog file | Tagged 10.14savvy | Leave a comment

Continuity Camera AS

Posted on 12月 12, 2019 by Takaaki Naganoya

Xcode上でCocoa AppleScriptアプリケーションを作成し、macOS 10.14から搭載された「Continuity Camera」の機能を利用して、iOSデバイス上で撮影した写真をその場でMacに転送・保存してみました。

–> ContinityCameraAS(Xcode 11 Project)

Continuity Cameraをサポートする部分のObjective-CのコードはThomas Zoechling氏のBlog上のものを利用させていただいております。

本当は通常のスクリプトでdisplay dialog的なダイアログを表示して、その上に作成したNSImageViewでContinuity Cameraを呼び出せるとよかったのですが、なかなかそこまで噛み砕いて解釈できなかったので、Xcode上のアプリそのままです。

Continuity CameraのプロジェクトをXcode上でビルド&実行すると、なにもないっぽいWindowが表示されますが、下地にNSImageViewを敷いてあるような気がします。このウィンドウの真ん中の方でControl-クリックあるいはマウスの右ボタンをクリックすると、コンテクストメニューが表示され、そこでカメラから画像取り込みを行うデバイスを選択し、それらの周辺デバイスで撮影した写真をそのまま取り込めます。

Continuity Camera機能は、同じiCloud IDで関連づけたiOSデバイスのカメラを無線LANのネットワークごしにMacから利用するものと理解しています(Bluetoothもオンにする必要があるかもしれない)。

ウィンドウ上の「Save Image」ボタンをクリックすると、その内容をデスクトップにPNG画像で保存します。

AppleScriptアプリケーションでもContinuity Cameraを利用できることがわかったので、このプログラムにAppleScript用語辞書(sdef)をつけてScriptableなアプリケーションに仕立て上げれば、通常のAppleScriptからcapture cameraといったコマンドで取り込めてよいのではないでしょうか。

コンテクストメニューの先にある機能に直接アクセスするためには、もう少し調べる必要がありそうではあります。

AppleScript名:AppDelegate.applescript
—
— AppDelegate.applescript
— continityCameraAS
—
— Created by Takaaki Naganoya on 2019/10/21.
— Copyright © 2019 Takaaki Naganoya. All rights reserved.
—

script AppDelegate
  property parent : class "NSObject"
  
  
— IBOutlets
  
property theWindow : missing value
  
property theImageV : missing value
  
  
on applicationWillFinishLaunching:aNotification
    — Insert code here to initialize your application before any files are opened
  end applicationWillFinishLaunching:
  
  
  
on applicationShouldTerminate:sender
    — Insert code here to do any housekeeping before your application quits
    
return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
  
on clicked:aSender
    set imgRes to theImageV’s image()
    
set dtPath to POSIX path of (path to desktop)
    
log {"dtPath", dtPath}
    
set fRes to retUUIDfilePath(dtPath, "png") of me
    
log {"fRes", fRes}
    
set sRes to saveNSImageAtPathAsPNG(imgRes, fRes) of me
  end clicked:
  
  
  
–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 –true/false
  end saveNSImageAtPathAsPNG
  
  
  
on retUUIDfilePath(aPath, aEXT)
    set aUUIDstr to (current application’s NSUUID’s UUID()’s UUIDString()) as string
    
set aPath to ((current application’s NSString’s stringWithString:aPath)’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:aEXT
    
return aPath
  end retUUIDfilePath
  
end script

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode Image | Tagged 10.14savvy 10.15savvy | Leave a comment

マウスカーソルの現在座標を取得する

Posted on 12月 12, 2019 by Takaaki Naganoya

マウスカーソルの現在座標を取得するAppleScriptです。

各種アプリケーションで座標系が微妙に(左上が原点だったり、左下が原点だったり)違いますが、Cocoaの座標系は左下が原点です。

# macOS 12からは左上が原点になるのだそうで

ただ、取得したところで何に使うかというあたりに疑問があります。そんなに使った記憶がありません。スクリプトエディタ上で本Scriptを実行した場合には、実行ボタンを押したときのマウスカーソルの座標が返ってきますし、Script Menuから実行した場合には、実行時のメニュー選択のマウスカーソルの座標が返ってくるだけです。

ちょっとひとひねりして、タイマー割り込みで定期的に取得したところで、何か意味のあるデータが取れるわけでもありません(ジェスチャー検出まで昇華できる気がまったくしない)。

逆に、マウスカーソルの座標の取得でどれだけ「いいこと」があるか教えてほしいもんです。

2021/5/22追記:
ついに、本処理が役立つ用途が見つかりました。「マウスカーソルが存在するディスプレイの内容だけスクリーンキャプチャを撮る」というものです。

ふつう、マルチディスプレイ環境でShift-Command-3のキーボードショートカットによるスクリーンキャプチャ撮影を行うと、それぞれのディスプレイごとに独立した画像ファイルが生成されます。

これが望ましい場合もあれば、そうでない場合もあるので……現在マウスカーソルが存在するディスプレイIDをマウスカーソルの座標値から計算し、マウスカーソルが存在するディスプレイのスクリーンショットのみ撮影するAppleScriptを書いて実行できました。Stream Deckのアクションから呼び出して、ワンボタンで実行できています。

このアクションはなかなか使い勝手がいいですし、本Scriptを書いておかなかったら気楽に実現することはできませんでした。

AppleScript名:マウスカーソルの現在座標を取得する
— Created 2018-02-03 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set mLoc to current application’s NSEvent’s mouseLocation()
–> {x:114.1171875, y:1142.87109375}

★Click Here to Open This Script 

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

Post navigation

  • Older posts
  • Newer posts

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

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • macOS 15, Sequoia
  • 指定のWordファイルをPDFに書き出す
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Numbersで選択範囲のセルの前後の空白を削除
  • メキシカンハットの描画
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • AppleScriptによる並列処理
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • Keynote/Pagesで選択中の表カラムの幅を均等割
  • デフォルトインストールされたフォント名を取得するAppleScript
  • macOS 15 リモートApple Eventsにバグ?
  • Keynote、Pages、Numbers Ver.14.0が登場
  • macOS 15でも変化したText to Speech環境
  • AppleScript入門① AppleScriptってなんだろう?
  • macOS 14で変更になったOSバージョン取得APIの返り値

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (194) 14.0savvy (147) 15.0savvy (132) CotEditor (66) Finder (51) iTunes (19) Keynote (117) 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 (55) 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
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • 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
  • 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年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