メニューエクストラを確実に操作するGUIスクリプティング

たぶん、デスクトップ右上のメニューバーには、いくつかのアイコンメニューが並んでいると思う。その中でも、プロセス名"SystemUIServer"が管理するアイコンメニューは、menu extra*1と呼ばれている。(control-F8と矢印キー←→↑↓で操作できるアイコンメニュー)
今まで、メニューエクストラをAppleScriptで操作する時は、アイテム名称ではアクセスできないと思い込んでいて、専らアイテム番号でアクセスしていた。


tell application "System Events"
tell process "SystemUIServer"
click menu bar 1's menu bar item 7
end tell
end tell

上記のスクリプトは、自分のMacBook環境ではスクリプトメニューをクリックする結果となる。しかし、これはこの時の自分のMacBook環境限定の操作結果であり、メニューエクストラが表示されている個数や並び順によっては全く違った操作になってしまう...。自分も含めて、上記のスクリプトをそのまま利用する訳にはいかず、目的とするメニューエクストラのアイテム番号を調べて、現状に合わせて、修正する必要があるのだ。

状況によってその都度、修正しないと動かないようなスクリプトは極力避けたい。理想は、アイテム名称で絶対指定する方法なのだ。*2そのような方法がないものか常日頃、頭の片隅で考えていたら、最近何か別の検索中に偶然、見つけた!(嬉しい)

英語の情報なので、普段なら、英語嫌いな自分が気にも留めるはずないのだが、「Re: Accesing menu bar items.」というタイトルを見た瞬間、興味をそそられてしまった。読めないなりに一気に読んでしまうと、まさしく自分が求めている情報だった。素晴らしい!

作業環境

click_menu_extra(menu_path)の定義

...ということで、早速、自分のAppleScriptライブラリ:_gui.scptに click_menu_extra メソッドを追加してみた。


--アイコンメニュー(ステータスメニュー)を通常のパスで指定できるようになった
--click_menu_extra("AppleScript/Script Editor Scripts/About these scripts...")
--click_menu_extra({"AppleScript", "Script Editor Scripts", "About these scripts..."})
on click_menu_extra(menu_path) if menu_path is "" then
error "menu_path が入力されていません。"
end if
set mp to split(menu_path, "/") tell application "System Events"
tell process "SystemUIServer"
set frontmost to true
click (menu bar 1's first menu bar item whose attribute "AXDescription"'s value is (mp's item 1)) repeat with i from 2 to mp's length
click (result's menu 1's menu item (my number_from(mp's item i))) end repeat
delay 0.1 --連続してメニューを操作する時、ひと呼吸必要
end tell
end tell
end click_menu_extra

on split(sourceText, separator) if sourceText = "" then return {} set oldDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to {separator} set theList to text items of sourceText
set AppleScript's text item delimiters to oldDelimiters
return theList
end split

on number_from(str) try
str as number
on error
str
end try
end number_from

メニューエクストラの名称は、以下のスクリプトで確認できた。


tell application "System Events"
tell process "SystemUIServer"
menu bar 1's menu bar items's attribute "AXDescription"'s value
end tell
end tell

--結果
(* {"iStat menus Temperatures", "iStat menus Network", "iStat menus Memory", "iStat menus CPU", "time machine", "Sync", "AppleScript", "bluetooth", "システムサウンド音量", "AirMac Menu Extra", "text input", "バッテリーメニュー。 充電済み .", "user"} *)

  • whoseフィルタの使い方は知っていた。
  • メニューバーアイテムがattributesを持っていることも知っていた。(UIElementInspector.appから読み取って)
  • だから、過去にwhoseとattributesを利用して試してみた記憶はあるが、うまくできなかった...。
  • たぶん、whoseフィルタの結果が、リストとして返されることに気付かなくて、エラーを出していたのだと思う。
  • しかしその時は、メニューエクストラのattributesではアクセスできないと思い込んでしまったのかもしれない。
      • それにしても、「バッテリーメニュー。 充電済み .」というDescription(記述, 叙述, 説明)には幻滅する。(絶対指定という目的においては)
      • Description(記述, 叙述, 説明)なのだから、バッテリーメニューの方が正しいのかもしれないが...。
      • 試しに、電源アダプタを抜いてみると「バッテリーメニュー。 残り時間を計算中... .」に変化した。

メニューエクストラ以外のアイコンメニューの操作

  • メニューエクストラ以外のアイコンメニューの確実な操作については、お手上げ。
  • 現状はcliclickコマンドに頼っている。
    • シンプルにワンライナーで書けるところは大好きなのだが...
    • デスクトップの状況の変化に対応するのが極めて困難なのだ。

もしかして、自分が知らないだけで、世界のどこかに素晴らしい方法があるのかもしれない...。(知りたいです!)

*1:/System/Library/CoreServices/Menu Extras/ フォルダに入っているので。

*2:アイテム"番号"は相対指定と考えてみた。しかし、アイテム"名称"も言語環境によって変化してしまう可能性があるので、必ずしも絶対指定とは言えないかも。