Archive for 6月, 2016

2016/06/30 recordのlistでKeynoteに表を作成する v1.5

recordのlistでKeynote v6.6.2のドキュメント上に表を作成するAppleScriptの高速化改良版です。

Keynote v6.6.xでtable(表)を作成するのに、セルに値が入った状態で作成できないかと試してみましたが、ダメでした(T_T)

そこで、アプリケーションに対しての操作を高速化させるため、非同期実行モードでセルへの値の設定とフォントサイズの変更を行ってみました。だいたいこれで2倍速ぐらいです。

keynote0.png

keynotes2.png

AppleScript名:recordのlistでKeynoteに表を作成する v1.5
– Created 2016-06-29 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property aFontSize : 12

set aList to {{pathStr:“book1_0.1.pdf”, creationDate:“2016年6月17日金曜日”, pageCount:222, countChars:127978, fileSize:“12027660″}, {pathStr:“book1_0.2.pdf”, creationDate:“2016年6月17日金曜日”, pageCount:230, countChars:129506, fileSize:“11109818″}, {pathStr:“book1_0.210.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:254, countChars:147119, fileSize:“22832000″}, {pathStr:“book1_0.211.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:254, countChars:147123, fileSize:“22831931″}, {pathStr:“book1_0.212.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134856, fileSize:“22273252″}, {pathStr:“book1_0.213.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134845, fileSize:“22271667″}, {pathStr:“book1_0.214.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134850, fileSize:“22270980″}, {pathStr:“book1_0.220.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:242, countChars:134870, fileSize:“21098301″}, {pathStr:“book1_0.222.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:243, countChars:135694, fileSize:“21146421″}, {pathStr:“book1_0.300.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:251, countChars:142787, fileSize:“21427502″}, {pathStr:“book1_0.301.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:251, countChars:142784, fileSize:“21421107″}, {pathStr:“book1_0.302.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:256, countChars:142827, fileSize:“22593201″}, {pathStr:“book1_0.303.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:257, countChars:142845, fileSize:“22616595″}, {pathStr:“book1_0.400.pdf”, creationDate:“2016年6月22日水曜日”, pageCount:281, countChars:162419, fileSize:“22430779″}, {pathStr:“book1_0.500.pdf”, creationDate:“2016年6月23日木曜日”, pageCount:309, countChars:178210, fileSize:“27611566″}, {pathStr:“book1_0.600.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:326, countChars:194751, fileSize:“26820825″}, {pathStr:“book1_0.700.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195943, fileSize:“26408415″}, {pathStr:“book1_0.701.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195926, fileSize:“26406738″}, {pathStr:“book1_0.702.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195924, fileSize:“26406703″}, {pathStr:“book1_0.703.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:311, countChars:196594, fileSize:“26416223″}, {pathStr:“book1_1.0.pdf”, creationDate:“2016年6月25日土曜日”, pageCount:311, countChars:196594, fileSize:“26075419″}}

set anItem to contents of first item of aList
set aDict to (current application’s NSMutableArray’s arrayWithObject:anItem)’s firstObject()
set aKeyList to aDict’s allKeys() as list
set aKeyCount to length of aKeyList
set aRowCount to length of aList

tell application “Keynote”
  tell document 1
    set aNewSlide to make new slide
    
    
tell aNewSlide
      set aTable to make new table with properties {column count:aKeyCount + 1, row count:aRowCount + 1}
      
tell aTable
        –ヘッダー行を作成(ラベルで埋める)
        
tell row 1
          repeat with i from 2 to aKeyCount + 1
            set aKey to item (i - 1) of aKeyList
            
–非同期実行ここから
            
ignoring application responses
              set value of cell i to aKey
              
set font size to aFontSize
            end ignoring
            
–非同期実行ここまで
          end repeat
        end tell
        
        
–各行のデータを埋める
        
repeat with ii from 2 to aRowCount + 1
          set aRecRow to item (ii - 1) of aList
          
tell row ii
            repeat with iii from 2 to (aKeyCount + 1)
              tell cell iii
                set aKey to item (iii - 1) of aKeyList
                
–非同期実行ここから
                
ignoring application responses
                  set value to retValueForKey(aRecRow, aKey) of me
                  
set font size to aFontSize
                end ignoring
                
–非同期実行ここまで
              end tell
            end repeat
          end tell
        end repeat
        
      end tell
    end tell
    
  end tell
end tell

on retValueForKey(aRec, aLabel)
  set tmpDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
return (tmpDic’s valueForKey:aLabel) as string
end retValueForKey

★Click Here to Open This Script 

2016/06/28 applescript-stdlibの失敗に学ぶ教訓

AppleScript Librariesの標準的なライブラリを目指して作られたというapplescript-stdlibがGithubで公開されました

ただし、この一般公開は「もう自分では作らないから、勝手にやって」というタイプの公開でした。同じことを何回繰り返したら気が済むんだ、has?!

そこで、実際にダウンロードして、ソースを一通り読んでみました。

ライブラリのScriptをオープンした瞬間に違和感が・・・このライブラリには、SDEF(AppleScript用語辞書)がついており、ライブラリ自体も用語辞書の用語を用いて記述されています。

この仕組みを使って、自分自身のSDEF用語を使いつつ大きめのライブラリを記述すると、スクリプトエディタ上での編集が遅くなり、編集しにくくなることがわかりました(いやな予感はしていましたが、、、^ー^;)。SDEFつきのライブラリは、実行するのに速度低下はあまり見られないものの、それを編集するのは(ある一定以上の規模になると)大変ということです。

また、ライブラリ内で基礎的なサブルーチンは提供されているものの、プロが普通に欲しくなるようなレベルのものがない、ということもありました。あくまで基礎的かつ常識的なルーチンが整備されたという段階のものだということです。

# Webライブラリはいいですね、これ。書こうと思っていた処理が書いてありました

自分で客先に納品するようなScriptを書く際には、まずSDEFは書かないのですが、この一定の大きさのライブラリで自身のSDEF内の用語を使ってライブラリ自体を記述すると地獄に陥るという「失敗」に学ぶべきものは多いと思われます。

ただ、「基礎的かつ常識的」なものでも役立つ場所はあると思われるので、興味のある方は中身を見ておくとよいでしょう。個人的には、SDEFを除去しないとメンテナンスは無理だと思います、、、

2016/06/28 デスクトップを隠すv6

「技術書典」からのリハビリがなかなか進まず、たまった用事を片付けるかたわらで細かいプログラムを組んだりしています。

ご紹介するのはedama2さんからの投稿プログラム。「技術書典」でお会いした際に、

 edama2さん:メールとどいていませんか?
 自分:いえ、とどいていません、、、(^ー^;;; ほんとにほんと。

後日明らかになったところによれば、メールアドレスの書き間違いでUser Unknownで戻っていたのだとか。後日再送されてきたプログラム、またまた楽しませていただきました。芸が細かい! そして細部までこだわっている! 実行して大笑いしてしまいました。ああ、こういう人を幸せな気分にするプログラムっていいですねぇ。

本Blogにリストをいつものように掲載しようとしたら、サイズが大きいためか、途中になんらかの表示できない文字が入ってしまったためか、リストのHTMLが掲載できませんでした。アーカイブをダウンロードできるようにしておきますので、ご覧ください(実行、編集が可能です)。

hidedesk1.png

hidedesk2.png

(edama2さんからの投稿)少し前に「デスクトップを隠す」のバージョンアップ版を送ったのですが、準備に忙しくて後回しにされていると思ってました。よく見たらメールの送信に失敗してましたのでもう一度送ります。

前回送った「デスクトップを隠す」のバージョンアップです。基本的に自分で色が選べアイコンの表示の有無が選べます。いきなりv6だと面を食らうかもしれませんが、

v2 UIの追加
v3 NSUserDefaultsで設定の保存
v4 試行錯誤の末、廃番
v5 プロパティ値に設定を保存するように変更
v6 NSColor→NSData→NSStringに変換して保存するように変更

v3で設定値が保存されているか色々試している時にplistファイルを削除したのに、前の設定値が残っている時がたまにありました。それでプロパティ値に保存できるかな?と思って試行錯誤したけど無理でした。おそらくcfprefsdのせいだったのかもしれません。

結局、cocoaオブジェクトはプロパティに直接保存できないことがわかったので、
v5はNSColorをRGBの数値のリストで保存して
v6はNSColor→NSData→NSStringからテキストに変換して保存しています。

でもApplescript最新リファレンスを読むとsandbox的にプロパティに保存するのはNGなんですね。初めて知りました(汗) あとサウンドファイルは他のアプリケーションのリソースファイルを参照します。サンプルなんで勘弁して下さい。

→ 実行ファイルのダウンロード

2016/06/28 recordのlistでKeynoteに表を作成する

recordのlistでKeynote v6.6.2のドキュメント上に表を作成するAppleScriptです。

今回作成した本の各バージョンのPDFの情報を収集するAppleScriptを作成し、さあこのデータをどうしようと思ったときに、「じゃあ、まとめたデータをKeynoteの書類上に表で作成すればいいじゃない」ということになり、AppleScriptで自動生成するようにしてみました。

これまでに、意外とAppleScriptで収集したデータをKeynoteにまとめるのは手作業で行っていたりで、なかなか大変でした。発表資料でも、仕様書でも、これが省けるのは(自分的に)省力化になります。

Keynoteでオープン中のドキュメントにページ(slide)を新規追加し、指定データから表を作成します。表の作成は、作成時に一気にすべてのセルのデータを指定することも可能なはずなので、まだまだスピードアップは可能なはずです。

table1.png

table2_resized.png

tabletograph_resized.png

AppleScript名:recordのlistでKeynoteに表を作成する
– Created 2016-06-28 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property aFontSize : 12

set aList to {{pathStr:“book1_0.1.pdf”, creationDate:“2016年6月17日金曜日”, pageCount:222, countChars:127978, fileSize:“12027660″}, {pathStr:“book1_0.2.pdf”, creationDate:“2016年6月17日金曜日”, pageCount:230, countChars:129506, fileSize:“11109818″}, {pathStr:“book1_0.210.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:254, countChars:147119, fileSize:“22832000″}, {pathStr:“book1_0.211.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:254, countChars:147123, fileSize:“22831931″}, {pathStr:“book1_0.212.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134856, fileSize:“22273252″}, {pathStr:“book1_0.213.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134845, fileSize:“22271667″}, {pathStr:“book1_0.214.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134850, fileSize:“22270980″}, {pathStr:“book1_0.220.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:242, countChars:134870, fileSize:“21098301″}, {pathStr:“book1_0.222.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:243, countChars:135694, fileSize:“21146421″}, {pathStr:“book1_0.300.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:251, countChars:142787, fileSize:“21427502″}, {pathStr:“book1_0.301.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:251, countChars:142784, fileSize:“21421107″}, {pathStr:“book1_0.302.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:256, countChars:142827, fileSize:“22593201″}, {pathStr:“book1_0.303.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:257, countChars:142845, fileSize:“22616595″}, {pathStr:“book1_0.400.pdf”, creationDate:“2016年6月22日水曜日”, pageCount:281, countChars:162419, fileSize:“22430779″}, {pathStr:“book1_0.500.pdf”, creationDate:“2016年6月23日木曜日”, pageCount:309, countChars:178210, fileSize:“27611566″}, {pathStr:“book1_0.600.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:326, countChars:194751, fileSize:“26820825″}, {pathStr:“book1_0.700.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195943, fileSize:“26408415″}, {pathStr:“book1_0.701.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195926, fileSize:“26406738″}, {pathStr:“book1_0.702.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195924, fileSize:“26406703″}, {pathStr:“book1_0.703.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:311, countChars:196594, fileSize:“26416223″}, {pathStr:“book1_1.0.pdf”, creationDate:“2016年6月25日土曜日”, pageCount:311, countChars:196594, fileSize:“26075419″}}

set anItem to contents of first item of aList
set aDict to (current application’s NSMutableArray’s arrayWithObject:anItem)’s firstObject()
set aKeyList to aDict’s allKeys() as list
set aKeyCount to length of aKeyList
set aRowCount to length of aList

tell application “Keynote”
  tell document 1
    set aNewSlide to make new slide
    
    
tell aNewSlide
      set aTable to make new table with properties {column count:aKeyCount + 1, row count:aRowCount + 1}
      
tell aTable
        –ヘッダー行を作成(ラベルで埋める)
        
tell row 1
          repeat with i from 2 to aKeyCount + 1
            set aKey to item (i - 1) of aKeyList
            
set value of cell i to aKey
            
set font size to aFontSize
          end repeat
        end tell
        
        
–各行のデータを埋める
        
repeat with ii from 2 to aRowCount + 1
          set aRecRow to item (ii - 1) of aList
          
tell row ii
            repeat with iii from 2 to (aKeyCount + 1)
              tell cell iii
                set aKey to item (iii - 1) of aKeyList
                
set value to retValueForKey(aRecRow, aKey) of me
                
set font size to aFontSize
              end tell
            end repeat
          end tell
        end repeat
        
      end tell
    end tell
    
  end tell
end tell

on retValueForKey(aRec, aLabel)
  set tmpDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
return (tmpDic’s valueForKey:aLabel) as string
end retValueForKey

★Click Here to Open This Script 

2016/06/28 「技術書典」でお買い上げいただいたみなさま、ありがとうございました!

技術書オンリーの同人誌即売会「技術書典」にサークル出展。どのぐらいの来場者数になるのかまったく分からない手探りのなか、1,200人を超える来場者があり「ぴよまるソフトウェア」のブースもフル回転!

開場当初は指名買いのお客様が相次ぎ、生産が間に合っていませんでした。私がご説明しつつ、横で相棒がCD-Rを量産するという体制が昼過ぎぐらいから安定稼働。ちなみに、自分は元ソフトバンクのUNIX USER誌の編集者で、相方は同じく元Cマガジンの編集者です。

3時を過ぎたあたりから(余裕が出たので)持ち込んだプロジェクターで壁面にMacの画面を投射しつつ、本の内容やAppleScriptについてご説明していました。

中には、AppleScriptについてその時点ではじめて知ったという人が「そこまで説明されたら買わなくては」と責任を感じて買われたとか、何かギリギリのことをやっていたような気もいたしますが、、、

実際に、311ページの「AppleScript最新リファレンス OS X 10.11対応」と101ページの「最新事情がわかる AppleScript 10大最新技術 OS X 10.11対応」を並べて説明していましたが、「まさか本当に300ページの本を作っていたとは思わなかった!」という声には少々ヘコみました。866ページの「AppleScriptリファレンス」という前例があるので、これでも全然かわいいサイズです。

直接面識のある方数名+本Blogをご存知の方も数名いらっしゃいました(偶然来場されていたとか)。初めて私を目にする方々は、

  「こんなにおしゃべりな人間だったのか!」

と目を丸くするやら呆れるやら。ええもう、親や兄弟や奥方様が呆れるほどにおしゃべりなので、もうこれは仕方のないことです(^ー^;。

販売上のトラブルについてですが、現時点で、

「CD-R読み取り不良によりダウンロード版をすぐにご提供」が1件、「品物の受け渡しミスでダウンロード版のご提供をした」のが1件。

となっております。何か問題があれば、お早めにお知らせください。全力で対応いたします。

だいたい今週中は技術書典での販売時の初期不良対応などを行う予定です。その後、アップデート(3か所ぐらい些細な間違いがあったのですでに修正)作業に入ります。アップデート後、一般向けオンライン販売に移行します。

技術書典で「AppleScript最新リファレンス OS X 10.11対応」ならびに「最新事情がわかる AppleScript 10大最新技術 OS X 10.11対応」を購入された方々は、ぜひ私の方までメールなどでフィードバックをいただけると助かります(説明が意味不明だとか、間違っているとか)。

また、技術書典でごにょごにょご案内していた「ちょっといい追加ダウンロードコンテンツ」の内容は、コレ(スクリプトアシスタント)を予定しております。どちらの本を買われた方にもご案内します。

スクリプトのハンドラの構造をMindMapに書き出す機能などは、さすがにMindjet MindManagerなどという高額なソフトがないと実行できないため、そのあたりは除いたものになりそうなのと、Excelに出力する機能を残すか削るかといったところで、少々悩みそうです。

他の同人誌即売会への参加については、まだ情報収集中でさっぱりわかりません。関西のオープンソースイベントへの参加なども以前に「Cocoa勉強会関西」にうかがったときにお話しをいただきましたが、正式な申し込みなどをどーするのかなど、まったくぼんやりした状態です。

2016/06/23 オープン中のMarkdownドキュメントに、レンダリングに時間のかかるタグ要素がないかチェック v2

Mac用のオープンソースのMarkdownエディタ「MacDown」でオープン中の最前面のドキュメントをしらべ、Markdownテキスト中にレンダリングに時間のかかる画像および表の構成タグが入っていないかどうかを調べるAppleScriptの改良版です。

実運用してみたら、Tableのレンダリングは画像(Drobpboxからダウンロードして表示するという)処理にかかる時間よりは手短に終わるので、

 画像:20秒待ち
 Table:3秒待ち
 通常:1秒待ち

と、待機秒数を段階的に可変で返すルーチンに仕様を変更しました。また、Tableの検出がleft alighn cellのみの検出だったので、right align cellのタグについても調査するようにしました。

目下、週末土曜日の技術書典に向けて絶賛ラストスパート中ですが、

 AppleScript最新リファレンス:300ページ超え(増加中)
 最新事情がわかる AppleScript 10大最新技術:100ページ(作業終了間近)

と、合計400ページをすでに超えており、1冊分ビルドするためのオーバーヘッド縮小のためにこのようなチューニングが必要になってくるわけです。

AppleScript名:オープン中のMarkdownドキュメントに、レンダリングに時間のかかるタグ要素がないかチェック v2
– Created 2016-06-22 by Takaaki Naganoya
– 2016 Piyomaru Software

set mdRes to retImgOrTableTagsInMarkdownDoc() of me

–オープン中のMarkdownドキュメントに、レンダリングに時間のかかるタグ要素がないかチェック
on retImgOrTableTagsInMarkdownDoc()
  
  
tell application “MacDown”
    tell document 1
      set aProp to properties –個別に要素にアクセスできなかったので、まとめてプロパティを取得
    end tell
  end tell
  
  
set stList to text of aProp –なぜか、結果がファイル名、HTML、生Markdownのリストで返ってくる
  
set aBody to last item of stList –生Markdownテキストを取り出し
  
  
set aIMGFlag to aBody contains “![” –Image
  
set aTableFlag1 to aBody contains “:—” –Table (left align or center align cell)
  
set aTableFlag2 to aBody contains “—:” –Table (right align or center align cell)
  
set aTableFlag to (aTableFlag1 or aTableFlag2)
  
  
if aIMGFlag = true then
    return 20 –画像入りは20秒
  else if aTableFlag = true then
    return 3 –テーブル入りは3秒
  else
    return 1 –通常は1秒
  end if
  
end retImgOrTableTagsInMarkdownDoc

★Click Here to Open This Script 

2016/06/22 オープン中のMarkdownドキュメントに、レンダリングに時間のかかるタグ要素がないかチェック

Mac用のオープンソースのMarkdownエディタ「MacDown」でオープン中の最前面のドキュメントをしらべ、Markdownテキスト中にレンダリングに時間のかかる画像および表の構成タグが入っていないかどうかを調べるAppleScriptです。

MacDownの書類やPagesの書類をまとめてデスクトップフォルダにPDFで出力するAppleScriptを運用していたら、画像を含むページで画像抜けが発生する現象が確認されました。画像をすべてDropboxに置いて、それをリンクしているので余計に時間がかかるんですね。

また、表を含むページのレンダリングには時間がかかるので(表を入れすぎという噂も)、そちらへの対策も念のために行うべきと思われました。

そこで、MacDownでオープン中のドキュメントのソースにアクセスして、画像や表のタグが入っているかどうかを調査。入っていればtrue、入っていなければfalseを返すように書いてみました。

このルーチンの結果を参照して、trueが返ってきた場合にはドキュメントのオープン後、長めに時間待ちして(15秒とか)そのあとでPDF出力を行えば安全です。

最初は、一律に1書類あたり15秒時間待ちをしていたのですが、280ページ本1冊、100ページ本1冊をすべて出力するのに時間がかかりすぎるため、必要な書類についてのみ時間待ちを行うように処理を最適化。

280ページの本については300ページ越えが確実なので、合計400ページも書いていることに。6月25日土曜日の技術書系同人誌即売会「技術書典」まであと少しです。

AppleScript名:オープン中のMarkdownドキュメントに、レンダリングに時間のかかるタグ要素がないかチェック
– Created 2016-06-22 by Takaaki Naganoya
– 2016 Piyomaru Software

set mdRes to retImgOrTableTagsInMarkdownDoc() of me

–オープン中のMarkdownドキュメントに、レンダリングに時間のかかるタグ要素がないかチェック
on retImgOrTableTagsInMarkdownDoc()
  
  
tell application “MacDown”
    tell document 1
      set aProp to properties –個別に要素にアクセスできなかったので、まとめてプロパティを取得
    end tell
  end tell
  
  
set stList to text of aProp –なぜか、結果がファイル名、HTML、生Markdownのリストで返ってくる
  
set aBody to last item of stList –生Markdownテキストを取り出し
  
  
set aIMGFlag to aBody contains “![” –Image
  
set aTableFlag to aBody contains “:—” –Table
  
  
–どちらか含んでいればレンダリングに時間がかかるのでフラグをtrueで返す
  
return (aIMGFlag or aTableFlag)
  
end retImgOrTableTagsInMarkdownDoc

★Click Here to Open This Script 

2016/06/18 指定フォルダ以下にあるMacDownとPages書類をソートしてPDFに書き出す

指定フォルダ以下にあるMacDownで記述したMarkdown書類と、Pages書類をすべての階層からピックアップしてファイル名でソートして、すべてデスクトップにPDFで書き出すAppleScriptです。

コンパイル(構文確認)および実行に際しては、Shane StanleyのScript Library「Bridge Plus」をインストールしておく必要があります。また、GUI Scriptingを利用しているため「システム環境設定」>「セキュリティとプライバシー」>「プライバシー」>「アクセシビリティ」でスクリプトエディタ(アプレットとして実行する場合にはアプレットそのもの)を登録して許可しておく必要があります。

「技術書典」に出す電子ブックのフォーマットがギリギリまで決まらず、しかも縦長のスクロールさせるタイプのものにできないかとあがいていたのですが、結局iPadあたりで読むことを考慮するとiPadのリーダーの仕様にしたがう必要があります。

……あれ?(^ー^; 結局、ページめくりは発生するし、一般的な本と同じような体裁になってしまいますよ → フォーマットがPDFになりました。

Markdown書類とPages書類が混在しているフォルダ構造のトップ階層のフォルダを指定すると、Markdown書類とPages書類をピックアップし、ファイル名でそれらをソートし、順次PDFに書き出すAppleScriptを書いてみました(必要は発明のマザー!)。

book1.png

ただ、MacDownには「書類をPDFに書き出す」という機能がAppleScript側に公開されていません(T_T)。

macdown_dict.png

ないものを「ないない」と嘆いても仕方がないので、さっさとGUI Scriptingで強制的にメニュー操作することにしました。

macdown_gui.png

で、どこに? どこに保存させるのでしょう??

大丈夫! そんなときには、保存ダイアログで幾つかのフォルダに強制的に移動させるキーボードショートカットが存在しており、Command-Dは「カレントディレクトリをデスクトップに移動させる」=「保存先をデスクトップにする」働きをします。

このため、保存先を操作しづらい(不可能とはいいませんけれども)GUI Scriptingにおいて保存先を指定することが、デスクトップフォルダについては可能になっています。

Pagesの方はひじょうに素直に(GUI Scriptingなんて使わずに)PDF書き出しが可能です。

そんなわけで、時間に追い詰められながらもなんとか大量のデータ処理を行っているのでありました。書き出した大量のPDFもAppleScriptでさくっと連結できるので、非常にいい感じです。あとは、本が完成すれば、、、、

AppleScript名:指定フォルダ以下にあるMDとPagesをソートしてPDFに書き出す
– Created 2016-06-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”
–use spotLight : script “spotlightLib”

property searchRes : {}

load framework

set origPath to POSIX path of (choose folder with prompt “Markdown/Pages ファイルの入っているフォルダを選択”)

set aResList to (spotlightSearch(origPath, “kMDItemKind == ’Markdown’ || kMDItemKind == ’Pages 一般書類’”) of me) as list –Caution! this parameter is *localized*

–フルパスとファイル名のペアの2D Listを作成
set newList to {}
repeat with i in aResList
  set j to contents of i
  
set aStr to (current application’s NSString’s stringWithString:j)
  
set aFileName to aStr’s lastPathComponent()
  
set the end of newList to {aStr, aFileName}
end repeat

–番号順にソート
set sortIndexes to {1} –Key Item id: begin from 0, Sort by filename
set sortOrders to {true}
set sortTypes to {“compare:”}
set resList to (current application’s SMSForder’s subarraysIn:(newList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
if resList = {} then return –No Result

–ソートした順番にMarkdownファイル/Pages書類ファイルをオープンしてデスクトップにPDF生成してクローズ
repeat with i in newList
  copy i to {fullPath, aFileName}
  
  
set apFile to (POSIX file (fullPath as string))
  
set anAlias to apFile as alias
  
set aFileName to aFileName as string
  
  
if aFileName ends with “.md” then
    exportFromMacDown(anAlias) of me –Markdown
  else if aFileName ends with “.pages” then
    exportFromPages(anAlias) of me –Pages
  end if
end repeat

–指定のPagesファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromPages(anAlias)
  tell application “Finder”
    set aName to name of anAlias
  end tell
  
  
set dtPath to (path to desktop) as string
  
set outPath to dtPath & aName & “.pdf”
  
  
tell application “Pages”
    close every document without saving
    
open anAlias
    
export document 1 to file outPath as PDF with properties {image quality:Best}
    
close every document without saving
  end tell
end exportFromPages

–指定のMacDownファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromMacDown(anAlias)
  tell application “MacDown”
    open {anAlias}
  end tell
  
  
tell current application
    delay 1 –ここの時間待ちが少ないと画像抜けが発生?
  end tell
  
macDownForceSave() of me
  
  
tell application “MacDown”
    close every document without saving
  end tell
end exportFromMacDown

–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceSave()
  activate application “MacDown”
  
tell application “System Events”
    tell process “MacDown”
      – File > Export > PDF
      
click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
      
–Go to Desktop Folder
      
keystroke “d” using {command down}
      
      
–Save Button on Sheet
      
click button 1 of sheet 1 of window 1
    end tell
  end tell
end macDownForceSave

–Spotlight Libの内容を引っ張り出してきた
on spotlightSearch(origPOSIXpath, aCondition)
  
  
set searchRes to {} –initialize
  
  
initiateSearchForFullPath(aCondition, origPOSIXpath) –Predicate & Scope Directory
  
  
–Waiting for the result
  
repeat while searchRes = {}
    current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
  
set anObj to searchRes’s firstObject() –Pick up the first one for test
  
if anObj = missing value then return {} –No Result
  
  
–set anAttrList to anObj’s attributes() –”mdls” attributes
  
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}
  
  
set resArray to {}
  
repeat with anItem in my searchRes
    set j to contents of anItem
    
set aPath to (j’s valueForAttribute:“kMDItemPath”) as string
    
set the end of resArray to aPath
  end repeat
  
  
return resArray
end spotlightSearch

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  –  
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2016/06/17 ASOCでmdfindするじっけん v4(フルパスを返す)

Spotlightの機能をCocoa経由で呼び出してmdfindコマンドのクエリーと検索対象フォルダをPOSIX pathで与えると、条件に合致するファイルのPOSIX pathをリストに入れて返してくるAppleScriptです。

以前に掲載したバージョンを実際に使おうとしたら、激しく間違っていることを発見。なんで気がつかなかったんでしょう。いえ、なんでこんな忙しいときに気づいてしまうんでしょう。

AppleScript名:ASOCでmdfindするじっけん v4(フルパスを返す)
– Created 2016-06-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property searchRes : {}

set origPath to POSIX path of (choose folder)
set aList to spotlightSearch(origPath, “kMDItemContentType == ’net.daringfireball.markdown’”) of me

on spotlightSearch(origPath, aMDfindParam)
  
  
set my searchRes to {} –initialize
  
initiateSearchForFullPath(aMDfindParam, origPath) –Predicate & Scope Directory
  
  
–Waiting for the result
  
repeat while my searchRes = {}
    delay 0.01
  end repeat
  
  
set anObj to my searchRes’s firstObject() –Pick up the first one for test
  
if anObj = missing value then return {} –No Result
  
  
set resArray to {}
  
repeat with anItem in my searchRes
    set j to contents of anItem
    
set aPath to (j’s valueForAttribute:“kMDItemPath”) as string
    
set the end of resArray to aPath
  end repeat
  
  
return resArray
  
end spotlightSearch

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  log sender
  
–> (NSConcreteNotification) NSConcreteNotification 0×7fa618450820 {name = NSMetadataQueryDidFinishGatheringNotification; object = }
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2016/06/17 “Beyond Dictation”〜WWDCでSal SoghoianのAutomation Showを堪能

WWDC2016のSession 717、”Beyond Dictation ― Enhanced Voice-Control for macOS apps”のライブ中継を見て、AppleScript Product ManagerであるSal Soghoianの独演会を堪能いたしました。相当の高齢のはずですが、あいかわらず元気です。ほどよく日焼けもしています。

あれだけ途切れずにデモを、しかも音声で行うためには、相当の練習が必要だったのではないかと思います。なかなかできないことです。

macOS 10.12における音声コマンドの方向性も見えてきました。10.11では保存しておいたアプレットをオープンさせることで擬似的にAppleScriptアプレットを起動させていましたが、10.12ではWorkflowを実行するという明確な選択肢が用意され、Automator、AppleScript、JXAのワークフローを実行できることに。

Siriとの連携は……10.12ではありません。ただし、Siriと同じ強力な音声認識エンジンを使って、日本語で名付けたワークフローを音声で呼び出すことができるようになったわけで、本Blog開設時(2008年)に想定していたレベルに到達したことを感じます。

ただし、依然としていくつかの問題は存在しています。

1つ目は、音声入力用のデバイスです。WWDCのセッションでは、歌手が使うようなボーカルマイクを用いてデモを行っていました(ほら、ここツッコミどころだからね。笑ってね、という意図を感じました)。WWDCの壇上なので、雑踏の騒音もなく、音声認識を行う場としては非常に有利な条件です(それでもいくたびか認識ミスを、、、)。

ただこれを、家庭内であるとか、カフェとか、職場などで使おうとするとちょっと問題が出てきます。どうしても、ワイヤレスで接続できる高音質なヘッドセット(強力なノイズキャンセリング機能つき)が必要になってきてしまいます。

macOS Sierra自体も「まだヘッドセットを使っての音声認識はできない」と明確に断りを入れているため、macOS Sierraがリリースされるころには高音質なワイヤレスヘッドセット(AirPods?)が登場するか、他のiOS/watchOSデバイス経由での音声入力ができるようになっているのかもしれません。

このAirPods(仮称)の存在を前提にしている機能が多いため、すでに社内では存在しているのでしょう。BluetoothとWiFiの間でハンズオーバーして短距離ではBluetooth、圏外に出るとWiFiで接続を行ったりするのかも。あるいは、Bluetoothだと高音質ヘッドセット(とくに音声入力)のプロファイルがないので、WiFiで通信を行うことが前提の音声デバイスなのかも。

# ちょうど本日、Bluetooth 5.0関連のニュースが入ってきました。ヘッドセットのプロファイル次第ですが、、、

2つ目は音声認識コマンドの可変パラメータです。やはり、予想どおり可変パラメータはサポートされていませんでした。

音声認識コマンドの弱点は、誤認識や言葉のゆらぎに対応しにくい点にあり、その意味でもSiriスタイルの音声認識コマンド体系(シナリオ型)との融合が音声コマンドの課題でしょう。

デモ自体は非常に楽しく見ることができましたし、ところどころ「これは現行のOS X 10.11+Keynote v6.6.2では実現できないな〜」という内容も見られました。各種機能のブラッシュアップが進んでいるということで、地味ながらもmacOS 10.12に期待の持てるデモだったと思います(驚きは皆無でしたが)。

Appleの目標が「キーボードやマウスと同じぐらい信頼性のある入力源として音声を位置付ける」、という点にあることが感じられた有意義なWWDCでありました。

2016/06/16 技術書典で販売する本、目下大詰め(のはず)

2016年6月25日に秋葉原・通運会館にておこなわれる技術書オンリーイベント「技術書典」にB-01ブース(地下1F)にて参加いたしますが、そこで販売する書籍の表紙が(だいたい)出来上がりました。

ぜひ、会場にてお買い求めください(値段は検討中)。あらかじめメールで申し込んでいただければ、そのぶんDVDをご用意しておきます。

▼クリックで画像拡大
books1.png

books2.png

あと、これに加えて、AppleScriptObjC(on Xcode)のサンプルを大量に入れて、かつRadikoの録音を行うAppleScriptObjCアプリケーション「Radirec」のプロジェクトソース全部入りのDVDも発売します。

radirec1_resized.png

なお、DVDが売り切れた場合には、QRコードによるダウンロード販売を行います。

追記:

リクエストがあるようなので、会場での販売以外にもオンライン販売を行います。ただし、即売会後のフィードバックを受けたアップデート版(v1.1)をその対象とします。送金手段については、まだ検討中です。

2016/06/16 Script Debugger 6が登場

sd6header_resized.png

ついにLateNight SoftwareのAppleScript統合開発環境「Script Debugger」のバージョン6が登場しました。既報のとおりOS X 10.10以降が対象で、AppleScriptObjCへの対応が最大のポイントです。

また、従来よりもリーズナブルな価格設定に変更されました。従来は199ドルしましたが、ver6は99ドル。Ver.5からのアップデートは49ドル。

とくに入門者〜中級者に使ってもらいたい(圧倒的に楽なので)アプリケーションなので、そのミスマッチが解消されるのはよいことだと思います。

2016/06/13 WWDC2016では何の発表が?

WWDC2016で新しいOS X(macOS?)である10.12が発表される予定です。WWDCでは、毎年AppleScript Engineering Teamが何らかのセッションを行うことになっています。

過去5年ほどのWWDCでのセッションは以下のとおり。デベロッパー登録していなくてもビデオは見られるようになっていますが、資料(PDF)のダウンロードにはデベロッパー登録が必要とされます。

WWDC 2011(OS X 10.7, Lion) Session 133 - Lion-Sized Automation
WWDC 2012(OS X 10.8, Mountain Lion) Session 206 - Secure Automation Techniques in OS X
WWDC 2013(OS X 10.9, Marvericks)Session 416 - Introducing AppleScript Libraries
WWDC 2014(OS X 10.10, Yosemite)Session 306 - JavaScript for Automation
WWDC 2015(OS X 10.11, El Capitan) Session 306 - Supporting the Enterprise with OS X Automation

何を行うのか直前まで明らかにされないため、当たりの内容なのか、そうでもない内容なのかがわかりません。

ここ数年でいえば、AppleScript関連は2013年がめちゃめちゃ当たり年で、エキサイティングな内容でした。

日本語音声コマンドの実行については、すでにOS X 10.11で実行可能な状態になっているため、OS XにSiriが搭載されてもおかしくはないですが、(Mac ProやMac miniなどのデスクトップ、あるいはMacBook Proなどを閉じて使うLid Closed Modeだとマイクが使えないため)ワイヤレスでMac本体に接続できるマイクというものが必要になりそうですが、音声認識に利用できるほど高音質なものが存在していません。このため、いまひとつピンとこない感じもします。

iOSデバイス、Apple Watch、AppleTVなどとの組み合わせ、「同時に使うとこんな便利で新しい使い方ができますよ」というあたりが、今回のWWDC(で発表される新OS群)のキモのようにも思えます。さて、どうなるやら。

追記:
Session 717:Beyond Dictation — Enhanced Voice-Control for macOS apps

これがどうもAppleScriptエンジニアリングチームの担当のセッションに見えます。XcodeのAppleScriptサポートが改善されたので、そのセッション内に出てくるかとも考えたものの、内容からするとこれなんでしょう。

2016/06/13 異なるバージョンのアプリケーションを区別して指定

IllustratorやPhotoshop、InDesignなどでよくある話ですが、これらのアプリケーションは1つの環境に異なるバージョンのアプリケーションが同時に存在するケースが多いものです。

普通にアプリケーション名やBundle IDを指定しただけでは、最新のアプリケーションが起動します。これは、ずーーっと昔からのMac OS/Mac OS X/OS X(もしかして10.12からmacOS?)の伝統的な挙動です。

しかし、古いバージョンのアプリケーションを指定してコントロールしたい場合もあります(そういうケース多し)。こうした場合にいは、アプリケーションのバージョンを区別して呼び出すことが必要になります。

aicss.png

一番簡単な対処方法は、各バージョンのアプリケーションをフルパスで、POSIX pathで指定するというものです。

2016-06-13-15_14_50.gif

AppleScript名:異なるバージョンのアプリケーションを区別して指定
tell application "/Applications/Adobe Illustrator CS3/Adobe Illustrator.app"
  activate
end tell

delay 3

tell application "/Applications/Adobe Illustrator CS5/Adobe Illustrator.app"
  activate
end tell

delay 3

tell application "/Applications/Adobe Illustrator CC 2015/Adobe Illustrator.app"
  activate
end tell

★Click Here to Open This Script 

ただし、上記のリストは私個人の環境の状態を反映させたものであり、こう書けばどこでも同じように動くわけではありません。各環境のアプリケーションのインストール状態に依存します。

そのため、バンドルIDを指定して実行環境にインストールされている指定アプリケーションのバージョンをすべて検出し、それらへのパスを個別に取得してtellするといった方法が(はるかかなた昔から)編み出され、利用されています。

そして、各バージョン用の処理Scriptをバンドル内に入れておいて、実行ターゲット・バージョンのアプリケーション用Scriptを動的にload scriptして読み込み、実行するというのも非常にオーソドックスなやり方です。

1つのScript内に、異なるバージョンの同名のアプリケーションへの操作を混在させることはできないため、対応バージョンごとにファイルを分割しておき、必要に応じて読み込んで実行。実行が終わったらメモリー上から消去することになります。

2016/06/07 テキストをMarkdown形式と解釈してHTMLを出力

Matt Diephouse氏によるオープンソースのフレームワーク「MMMarkdown」を用いて、Markdownテキストを解釈してHTMLを出力するAppleScriptです。

Markdownエディタをいろいろ試してみましたが、ひとくちに「Markdown」といっても細かいところで文法が違っており、Aというエディタで書いたMarkdownテキストをBというエディタでオープンすると体裁が維持されなかったりと、割とよくありがちな展開になっております。

Markdownタグの置換とか、そういう地道なツールは必要そうに見えます。そんな中、Markdownタグの解釈を行うあたりのフレームワークについて基礎的な調査を行っておいたというのが本Scriptの位置付けです(とくに深い意味なし)。

OS X 10.10以降用にビルドしたFrameworkのバイナリを用意したため、各自自己責任でFrameworkを~/Library/Frameworksフォルダに入れておためしください。

–> Download Framework Binary

AppleScript名:テキストをMarkdown形式と解釈してHTMLを出力
– Created 2016-05-12 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “MMMarkdown” –https://github.com/mdiep/MMMarkdown

set markdown to “# Example
What a library!”

set htmlString to (current application’s MMMarkdown’s HTMLStringWithMarkdown:markdown |error|:(missing value)) as string
–>
(*
“<h1>Example</h1>
<p>What a library!</p>

*)

★Click Here to Open This Script 

2016/06/06 Mail.appで複数のメッセージを移動させる

Apple純正のメーラーアプリケーション「Mail.app」にはmove命令があり、message(メール)を指定の(Mail.app上の)フォルダに移動させることが可能です(ファイルシステム上のフォルダではありません)。

1つのmessageを移動させる場合には問題ないのですが、複数のmessageを同時に移動させる場合にはエラーになります。

この挙動がおかしくなったと感じたのは、OS X 10.8だったと記憶しています。複数のmessageをリストに入れ、move命令で一気に移動させる処理でいきなりエラーが出るようになりました

複数のmessageを一度にmoveできると、アプリケーション側との通信頻度を下げられ、処理速度の面で大幅に有利になるため、対応してほしいところです。

Mail.appのAppleScript用語辞書にもmove命令の説明で、object(s) to move を指定できるという表記があり、

mailappdic.png

複数のメールを一度に移動できることを期待したいところです。

しかし、今日にいたってもリストに入れた複数のmessageは移動できず、にもかかわらずAppleScript用語辞書にはobject(s)と複数形で記載され続けるという状況が続いています。

そこで、自分が期待するところの「複数のmessage」と、Appleが定義するところの「複数のmessage」が実は異なるのではないか、と疑うようになりました。

 自分の定義:リスト型変数に入れた複数のmessage
 Appleの定義:上記以外の何か???

まず、現時点で動作する安全な表記を確認するとこのリスト(↓)のようになります。複数のmessageをリストに抽出し、ループで1つずつmoveします。

AppleScript名:受信フォルダ内の特定のメールアドレスから来たメールを別のフォルダに順次移動
tell application “Mail”
  tell inbox
    set mList to every message whose sender contains “notify@twitter.com”
  end tell
  
  repeat with i in mList
    set j to contents of i
    
ignoring application responses
      move j to mailbox “メルマガ/_個人的なメール/twitter”
    end ignoring
  end repeat
end tell

★Click Here to Open This Script 

ここで、フィルタ参照で取得したmessageのリストに対して直接move命令を実行するとエラーになります。

AppleScript名:受信フォルダ内の特定のメールアドレスから来たメールを別のフォルダに一括移動(動かないパターン)
tell application “Mail”
  set aList to (every message in inbox whose sender contains “notify@twitter.com”)
  
move aList to mailbox “メルマガ/_個人的なメール/twitter” –Error
end tell

★Click Here to Open This Script 

そこで、「リストに入れない状態で、しかも複数のmessageを与えてみたらどうなるだろう?」と考え、実際に書いて試してみました。

AppleScript名:受信フォルダ内の特定のメールアドレスから来たメールを別のフォルダに一括移動
tell application “Mail”
  move (every message in inbox whose sender contains “notify@twitter.com”) to mailbox “メルマガ/_個人的なメール/twitter”
end tell

★Click Here to Open This Script 

あれ? 一括移動できた(^ー^;;; しかし、リストに入れたりソートしたりユニーク化したものを渡せないと、ちょっと使い勝手がよくありません。

しばし悩んだものの、冗談半分で、

AppleScript名:受信フォルダ内の特定のメールアドレスから来たメールを別のフォルダに一括移動(動かないパターン) の解決
tell application “Mail”
  set aList to a reference to (every message in inbox whose sender contains “notify@twitter.com”)
  
move aList to mailbox “メルマガ/_個人的なメール/twitter” –OK
end tell

★Click Here to Open This Script 

と、a reference toで参照したものをリストに入れてみたところ、希望の動作を行いました(!!!!!)。

ただ、このリストを加工したりできないので、問題はあまり解決していないような、、、、

2016/06/06 レコードの値をクリア

与えられたレコード(record)型の変数について、ラベルはそのままで値についてクリアを行うAppleScriptです。

たしかに、ありそうで書いていなかったような気がする内容です。単純にヌル(ヌル文字列)でクリアするバージョンと、与えたrecordの各値の型を見てクリア時の初期値を変更するバージョンを作ってみました。

実際のAppleScriptのワークフローの中で使う場合には、AppleScriptアプレット起動後にplistファイルから設定値を読み取り、初期化動作が必要なものについてだけ初期化を行うというような内容になるでしょうか。

そのため、初期化してほしい属性ラベルのリストを渡すか、初期化してはいけない属性ラベルのリストを与えるかをしつつ、このような動作を行うものが「実戦レベルで使える」ルーチンになるはずです。

AppleScript名:レコードの値をクリア
– Created 2016-06-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”

set initVal to “”
set aRec to {aaa:“111″, bbb:“222″, ccc:“333″}
set aRec to clearRecordValues(aRec, initVal) of me
–>  {aaa:”", bbb:”", ccc:”"}

on clearRecordValues(aRec, initVal)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
repeat with i in (aDic’s allKeys() as list)
    set j to contents of i
    (
aDic’s setValue:initVal forKey:j)
  end repeat
  
return aDic as record
end clearRecordValues

★Click Here to Open This Script 

AppleScript名:レコードの値をクリア v2
– Created 2016-06-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”

set initVal to “”
set aaaaaaaRec to {aaa:“111″, bbb:2.1234, ccc:-1}
set bRec to clearRecordValues(aaaaaaaRec) of me
–>  {aaa:”", bbb:0, ccc:0}

on clearRecordValues(paramRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:paramRec
  
set keyList to (aDic’s allKeys() as list)
  
set valList to (aDic’s allValues() as list)
  
  
repeat with i from 1 to (length of keyList)
    set aKey to contents of item i of keyList
    
set tmpVal to contents of item i of valList
    
set aClass to class of tmpVal
    
    
if aClass is in {number, integer, real} then
      set initVal to 0
    else if aClass is in {string, text, Unicode text} then
      set initVal to “”
    else
      set initVal to missing value
    end if
    
    (
aDic’s setValue:initVal forKey:aKey)
  end repeat
  
return aDic as record
end clearRecordValues

★Click Here to Open This Script 

2016/06/02 文法警察24時

こういうのはリリースノートに書いておいてほしいところ(汗)、OS X 10.11で追加された新機能なんだか珍機能なんだか。AppleScript Users MLで「Grammar Policeだ!」と大騒ぎしていたのは横目で見ていましたが(Glamor Policeに見えた、、、)、当時忙しかったので関心をもっていませんでした。

現在のところ確認されている現象は、「it’s」という表記があったらコンパイル(構文確認)時に「its –Grammar Police」に自動で置き換えるよ、ということのようです。

police1.png
▲コンパイル(構文確認)前

police2.png
▲コンパイル(構文確認)後

ただ、これにはじまった話ではなく、AppleScriptの構文確認時にはけっこうソースが書き換えられます。場数を踏んでいるScripterにとっては面白くもなんともない現象ですが、初心者にとっては驚愕の挙動でしょう。

AppleScriptの構文解釈プログラムが、「saving true」を「with saving」に置き換えたりするのはよく見かける動作です(しかも、最初からwith savingと入力すると構文確認でエラーになるケースも見かけます)。

コマンドのオプションを複数指定したときに「a and b and c」を「a, b and c」のように書き換える、というのもよく見かける挙動です。considering節やignoring節で考慮する/無視する項目も複数書いてあると「a and b and c」が「a, b and c」と書き換えられます。

このあたりも、自動でAppleScriptの解釈プログラムが書き換えたなら、コメントで断りを入れておくべきだと思います>AppleScriptエンジニアリングチーム。

これから先、AppleScriptエンジニアリングチームは、「○’s」 のようなアポストロフィ+sの表記をどうにかしたいのでしょうか? ”use AppleScript”コマンドの実装には、近い将来に大幅な文法変更をしたいという意図を感じるものです(個人的な見解です)。

2016/06/02 Script Debugger 6.0が間もなくやってくる?!

AppleScriptの統合開発環境、プロが普通にあってほしいと思う、ブレークポイント設定、ステップ実行、変数のリアルタイムモニタリングなどを提供する開発用アプリケーション、それがLate Night SoftwareのScript Debuggerです。この世界では信頼と実績の不動のブランドであります。

自分はAppleScriptを覚えたての頃にVersion 2.xの頃のScript Debuggerに出会い、これにずいぶんと助けられました。初心者にこそScript Debuggerをおすすめしたいところです(199ドルするけど)。

そんなScript Debuggerの新バージョンv6.0が間もなく登場するとのニュースが入ってきました。WWDC 2016に合わせるのか合わせないのか、そのあたりはわかりませんが、とても気になるところです。

v6.0をひとことでいえば、AppleScriptObjCへの全面対応が行われたScript Debuggerです(Wikipedia書き換えないと、、)。Objective-Cの(Cocoaの)オブジェクトのデータ表示、データロギングなどが行えます。OS X 10.10以降をサポートしています。

1. コードサイン

Apple純正のスクリプトエディタにしか実装されていないコードサイン機能(shell commandを呼んでる気がする)。サードパーティのFrameworkをアプリケーションバンドルに一緒に入れてコードサインできるのかが気になります。

2. バンドルスクリプトの取り扱いの向上

バンドルID、Copyright、バージョン番号、などの編集機能が追加されました。sdefファイルの指定機能がある、と書かれていますがsdef自体の記述機能はないようです(惜しい!)。

3. プログレス表示

OS X 10.10で追加されたプログレス表示のプロパティを反映して、プログレス表示が行えるとのこと。

4. 素早く開く

Spotlightの機能を使って、インクリメンタルにScriptを見つけ出してオープンする機能のようです。

5. エディタの機能向上

ifブロックやrepeatブロックなどを折りたたんでしまうCode Folding、入力中にAppleScript/ASOCの用語を自動補完するAutocompletionの機能を備えています。Enumについてもサポートしているのか、していないのか・・・Enumについては触れられていないのでEnumのAutocompletionはしてくれないもよう。

また、いったん解釈されてしまうと大文字小文字が固定されてしまうという問題のあった、AppleScriptの変数名、プロパティ名、ハンドラ名称なども自在に大文字小文字を変更できるとのこと。

OS X 10.11で「it’s」を「its–Grammar Police」に自動置換されてしまう機能(知らなかった!)で、「–Grammar Police」表記が自動的に行われてしまう問題に対処。

その他、とくにCocoaの機能を呼び出している際に、AppleScriptの予約語とコンフリクトを起こす予約語については前後に「|」(pipe)を入れる機能、APIブラウザ「DASH」との連携機能などなど。

個人的に気になっているのは、Script Debugger 6の画面スナップショットで「フォアグラウンドで実行」に該当するチェックボックスが見えないこと。フォアグラウンド実行をメニューから通常実行とは別に指示するタイプなのか、そのあたりを隠蔽しているのか・・・実際にさわってみないとわかりません。

2016/06/02 デスクトップを隠す

久しぶりに投稿Scriptです。以前にも「100倍速いシェルソートでリストをソート」「おかえり(シンプル版)」などの腰を抜かすようなScriptを送ってくださったedama2さんからです。

デスクトップを単色設定する方法については、インターネット上で(英語で)検索して幾つか方法を見つけていました。

 plistを書き換える方法(OS X自体はこの方法)
 単色の画像を動的に生成して背景画像に設定する方法(自分が考えた方法)

このScriptはこれら以外の、

 I充┘譽戰襪kCGDesktopWindowLevel/kCGBackstopMenuLevelKeyの透明なNSWindowを動的に生成して、その上にNSViewを生成し、指定色で塗る

というものです。「おかえり(シンプル版)」と似た構成ですが(ウィンドウを消すところなども)、実に洗練されています。

特筆すべきは、AppleScriptObjCでありながら美しいプログラムリスト。美しさよりも「安全に解釈されること」とか「安全に動くこと」を優先しがちなASOCの記述において、ここまで美しいリストは見たことがありません。

だいたいの書き手がShane Stanleyの模倣かちょっと気の利いたリストにとどまっていることを考えると、異次元の美しさです。掛け値なしに、ASOCのプログラムでこれより美しいものは見た事がありません。以下、edama2さんからの説明です。

デスクトップピクチャの差し替えの話を読んでいて思いついたのでメールしました。途中ファイルを作るのが自分的に嫌だったので作ってみました。昨日バージョンアップしたのでこっちもそれに合わせてみました。

色を白ではなく色を付けたのは、最初画像の作成に失敗していることに気づかずただNSWindowだけの状態で表示していることに気づかなかったからです。制作環境はOSX10.11.5です。以下説明です。

●デスクトップを隠す

スクリプトエディタからアプリケーションとして保存する時に「実行後に終了しない」にチェックを入れてください。起動するとデスクトップピクチャを隠します。ダイアログでアイコンを表示するか隠すか選べます。

元に戻す時は終了してください。

AppleScript名:デスクトップを隠す
use AppleScript
use scripting additions
use framework “Foundation”
use framework “AppKit”

property _cover_win : missing value

on run
  set my _cover_win to missing value
  
  
#確認
  
set str to “デスクトップピクチャを隠しますか?”
  
set bOK to “ピクチャのみ”
  
set bOther to “アイコンも隠す”
  
set bCancel to “キャンセル”
  
activate
  
try
    set res to display dialog str buttons {bCancel, bOther, bOK} default button bOK with icon 1
  on error the error_message number the error_number
    quit
  end try
  
  
if res’s button returned = bOK then
    set isShowIcon to true
  else if res’s button returned = bOther then
    set isShowIcon to false
  end if
  
  
set my _cover_win to my makeCoverWindow(50, 75, 127, 255, isShowIcon)
end run

on quit
  if my _cover_winmissing value then
    my closeWin:(my _cover_win)
    
set my _cover_win to missing value
  end if
  
continue quit
end quit

# ウィンドウを作成
on makeCoverWindow(rDat as integer, gDat as integer, bDat as integer, aDat as integer, isShowIcon)
  
  
set rCol to rDat / 255
  
set gCol to gDat / 255
  
set bCol to bDat / 255
  
set aCol to aDat / 255
  
  
set aScreen to current application’s NSScreen’s mainScreen()
  
set screenFrame to aScreen’s frame()
  
set aBacking to current application’s NSBorderlessWindowMask
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
set aHeight to screenFrame’s |size|’s height
  
set aWidth to screenFrame’s |size|’s width
  
set aFrame to current application’s NSMakeRect(0.0, aHeight, aWidth, aHeight)
  
  
# Image
  
tell current application’s NSImage’s alloc()
    tell initWithSize_(screenFrame’s |size|)
      lockFocus()
      
set myColor to current application’s NSColor’s colorWithCalibratedRed:rCol green:gCol blue:bCol alpha:aCol
      
–set myColor to current application’s NSColor’s darkGrayColor()
      
tell current application’s NSBezierPath’s bezierPath()
        appendBezierPathWithRect_(screenFrame)
        
myColor’s |set|()
        
fill()
      end tell
      
unlockFocus()
      
set anImage to it
    end tell
  end tell
  
  
# Custom View
  
tell current application’s NSImageView’s alloc()
    tell initWithFrame_(aFrame)
      setNeedsDisplay_(true)
      
setImage_(anImage)
      
set aCustView to it
    end tell
  end tell
  
  
# Window
  
tell current application’s NSWindow’s alloc()
    tell initWithContentRect_styleMask_backing_defer_screen_(aFrame, aBacking, aDefer, false, aScreen)
      
      
#
      
if isShowIcon then
        setLevel_(current application’s kCGDesktopWindowLevel)
      else
        setLevel_(((current application’s kCGBackstopMenuLevelKey) - 100)) –>メニューバーの影のため100ひく
      end if
      
      
setBackgroundColor_(current application’s NSColor’s clearColor())
      
setContentView_(aCustView)
      
setDelegate_(me)
      
setDisplaysWhenScreenProfileChanges_(true)
      
setHasShadow_(false)
      
setIgnoresMouseEvents_(false)
      
setOpaque_(false)
      
setReleasedWhenClosed_(true)
      
makeKeyAndOrderFront_((me))
      
      
# Sound
      (
current application’s NSSound’s soundNamed:“Purr”)’s play()
      
      
setFrame_display_animate_(screenFrame, true, true)
      
      
set aWin to it
    end tell
  end tell
  
  
return aWin
end makeCoverWindow

# ウィンドウを閉じる
on closeWin:aWindow
  repeat with n from 10 to 1 by -1
    (aWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
aWindow’s |close|()
end closeWin:

★Click Here to Open This Script 

2016/06/01 デスクトップピクチャを白いピクチャとトグルで差し替え v2

ソフトウェアの説明書などの作成時に、画面キャプチャを撮るためにデスクトップピクチャを白いものに一時的に変更するAppleScriptの強化版です。

デスクトップの表示/非表示を連動させるようにしました。スクリプトエディタ上ではpropertyが保存されてトグル表示できることを確認しましたが、ASObjC Explorer 4上では保存されなかったので、実行にはスクリプトエディタを用いてください。

origdesk.png
▲実行前の状態(デスクトップにアイコンが表示されている)

afterdesk.png
▲実行後の状態(白い背景に一時切り替え & デスクトップ非表示)

AppleScript名:デスクトップピクチャを白いピクチャとトグルで差し替え v2
– Created 2016-05-31 by Takaaki Naganoya
– Modified 2016-06-01 by Takaaki Naganoya–Desktop Iconの表示/非表示を追加
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

property aSwitch : false
property desktopPictures : {}
property aColpath : “”

if aSwitch = false then
  –デスクトップを白くする
  
set desktopPictures to getDesktopPicturePathList() of me
  
–白い画像を作成してデスクトップピクチャに設定
  
set aColpath to makeColordImageToTmp(255, 255, 255, 255) of me –R,G,B,A(それぞれ 0〜255)
  
setDesktopPicture(aColpath) of me
  
showHideDesktop(false) of me
  
set aSwitch to true
else
  –保存しておいたDesktop Pictureのリストを戻す
  
setDesktopPicturePathList(desktopPictures) of me
  
do shell script “rm -f “ & quoted form of aColpath
  
showHideDesktop(true) of me
  
set aSwitch to false
end if

–デスクトップの表示/非表示切り替え
on showHideDesktop(aBool as boolean)
  set aBoolStr to aBool as string
  
do shell script “defaults write com.apple.finder CreateDesktop -bool “ & aBoolStr
  
do shell script “killall Finder”
end showHideDesktop

–デスクトップピクチャの状態を復帰する
on setDesktopPicturePathList(aliasList)
  if aliasList = {} then
    display notification “保存しておいたデスクトップピクチャのリストが空になっています”
    
return
  end if
  
  
tell application “System Events”
    set dCount to count every desktop
    
repeat with i from 1 to dCount
      set j to contents of item i of aliasList
      
tell desktop i
        set picture to (POSIX path of j)
      end tell
    end repeat
  end tell
end setDesktopPicturePathList

–デスクトップピクチャの強制指定
on setDesktopPicture(aPathStr)
  tell application “System Events”
    set picture of every desktop to aPathStr
  end tell
end setDesktopPicture

–デスクトップピクチャのパスをaliasリストで取得
on getDesktopPicturePathList()
  set pList to {}
  
tell application “System Events”
    set dCount to count every desktop
    
repeat with i from 1 to dCount
      tell desktop i
        set aPic to (picture as POSIX file) as alias
        
set end of pList to aPic
      end tell
    end repeat
  end tell
  
return pList
end getDesktopPicturePathList

–テンポラリフォルダに指定色の画像を作成
on makeColordImageToTmp(rDat as integer, gDat as integer, bDat as integer, aDat as integer)
  set rCol to 255 / rDat
  
set gCol to 255 / gDat
  
set bCol to 255 / bDat
  
set aCol to 255 / aDat
  

  
set aColor to current application’s NSColor’s colorWithDeviceRed:rCol green:gCol blue:bCol alpha:aCol
  
set aDesktopPath to current application’s NSString’s stringWithString:(POSIX path of (path to temporary items))
  
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
  
set aRes to makeImageWithFilledWithColor(1, 1, savePath, aColor) of me
  
return (savePath as string)
end makeColordImageToTmp

–指定サイズの画像を作成し、指定色で塗ってファイル書き出し
on makeImageWithFilledWithColor(aWidth, aHeight, outPath, fillColor)
  –Imageの作成  
  
set anImage to current application’s NSImage’s alloc()’s initWithSize:(current application’s NSMakeSize(aWidth, aHeight))
  
  
anImage’s lockFocus() –描画実行
  
set theRect to {{x:0, y:0}, {height:aHeight, width:aWidth}}
  
set theNSBezierPath to current application’s NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  
fillColor’s |set|()
  
theNSBezierPath’s fill()
  
anImage’s unlockFocus() –描画ここまで
  
  
–生成した画像のRaw画像を作成
  
set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
  
–書き出しファイルパス情報を作成
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –成功ならtrue、失敗ならfalseが返る
  
end makeImageWithFilledWithColor

★Click Here to Open This Script