Hammerspoon で童貞を殺すセーターのお姉さんを表示
先日、Hammerspoon で今いる場所の緯度と経度を求め、現在地の天気と気温をデスクトップに表示する、というようなことを試した。
🔗 Mac でロケーションごとの気温を表示する | 林檎コンピュータ
その際、デスクトップの表示は GeekTool を用いたが、よくよく考えると Hammerspoon でも同じようなことが出来るはずである。
さっそく試してみた。
デスクトップに文字列を表示
デスクトップに文字列を表示する場合、かつては hs.drawing というモジュールが使われていたようだ。現在は廃止が視野に入っているらしく、非推奨になっている。
代わりに hs.canvas というのを使えばいいみたいだ。
local c = require("hs.canvas")
local a = c.new{ x = 100, y = 100, h = 200, w = 400 }:show()
a[1] = { type = "text", text = '文字列' }
a:sendToBack()
書き方はいろいろあるようだ。筆者はこれが一番わかりやすかった。
筆者の印象であり、正確な表現ではないと思う。思うのだが印象として、hs.canvas というのは lua のテーブルで、そのテーブルの中に、エレメントと呼ばれるテーブルを入れる。エレメントで表示したい文字列だとか、画像だとかを指定する感じ。ひとつのエレメントがレイヤーみたいになるっぽい。
エレメントのテーブル内の定義は、アトリビュートとか呼ばれており、最低でも「type」を定義する必要がある、とのこと。
新規 hs.canvas オブジェクト「 a 」で、x,y の座標と h,w の縦横の大きさを指定している。最後の:show()
は表示を命ずるメソッドだ。
a[1] = { type = "text", text = '文字列' }
でもって、表示したい項目を定義したことになる。表示したいのはテキスト、テキストの内容は「文字列」でお願い、という意味だ。
a:sendToBack()
で、canvas をデスクトップピクチャより上、アイコンよりは下、という深い階層まで送れる。これは GeekTool と同じ深さだと思われる。
デフォルトだと、ウィンドウや Dock より上の高い階層に設定されており、隠せなくなる。
ちょっと見づらいので、文字色を黒にしてみる。文字色はtextColor = { black = 1.0 }
というように指定する。
a[1] = { type = "text", text = '文字列', textColor = { black = 1.0 } }
あまり見易くなってないので、いろいろ設定してみる。
a[1] = { type = "text", text = '文字列', textColor = { black = 1.0 }, textFont = "Keifont", textSize = 36.0, textAlignment = "center" }
フォントはtextFont = "フォント名"
、サイズはtextSize = 数値
、文字列の揃え方は、textAlignment = "left,right,centerなど"
となっている。
これらの設定項目は、Hammerspoon docs: hs.canvas に掲載されている。
細かい字で英語がずらっと出てくるが、なんとなくの理解で気長に試していくのがいいと思う。
GeekTool みたいに時計を表示したいなら、以下のようなスクリプトが考えられる。
local c = require("hs.canvas")
local a = c.new{ x = 100, y = 100, h = 200, w = 400 }:show()
a[1] = { type = "text", text = '文字列', textColor = { black = 1.0 }, textFont = "Keifont", textSize = 36.0, textAlignment = "center" }
a:sendToBack()
function tokei()
local now = hs.execute("date +%H:%M:%S")
a[1].text = now
end
dtTokei = hs.timer.new(1, tokei)
dtTokei:start()
hs.canvas オブジェクト「 a 」はテーブルなので、下から四行目のa[1].text
みたいに書くと、text キーの値を変更できるのだ。
今回はhs.execute
でシェルのコマンドdate
を実行して時間を取得し、a[1]
の text に代入している。
画像の表示
画像も表示してみたい。
以下のような画像を作った。
画像はパス等で指定して、hs.image でオブジェクト化する。
local img = hs.image.imageFromPath(os.getenv("HOME").."/Desktop/tokei.png")
hs.canvas オブジェクト「 a 」に新たなテーブルを追加する。
a[2] = { type = "image", image = img }
img
というのは先ほど作った hs.image である。
local c = require("hs.canvas")
local a = c.new{ x = 100, y = 100, h = 210, w = 410 }:show()
local img = hs.image.imageFromPath(os.getenv("HOME").."/Desktop/tokei.png")
a[1] = { type = "text", text = '文字列', textColor = { white = 1.0 }, textFont = "Keifont", textSize = 36.0, textAlignment = "center" }
a[2] = { type = "image", image = img }
a:sendToBack()
function tokei()
local now = hs.execute("date +%H:%M:%S")
a[1].text = now
end
dtTokei = hs.timer.new(1, tokei)
dtTokei:start()
reload config して実行すると、時計の数字が画像の下に隠れてしまっている。
エレメントはテーブルの番号順に生成されるみたいだ。
これを修正し、さらに時計の位置や大きさを微調整した。
local c = require("hs.canvas")
local a = c.new{ x = 100, y = 100, h = 105, w = 205 }:show()
local img = hs.image.imageFromPath(os.getenv("HOME").."/Desktop/tokei.png")
a[1] = { type = "image", image = img }
a[2] = { type = "text", text = '文字列', textColor = { white = 1.0 }, textFont = "Keifont", textSize = 24.0, textAlignment = "center", frame = { x = "0", y = "0.3", h = "1", w = "1" } }
a:sendToBack()
function tokei()
local now = hs.execute("date +%H:%M:%S")
a[2].text = now
end
dtTokei = hs.timer.new(1, tokei)
dtTokei:start()
いまいち微調整しきれてない感もあるが、まぁこんな感じだろう。
時計の数字の位置を決めるframe
という定義は、x,y,h,w すべてパーセントで指定する。1 なら 100 パーセント。0.5 なら 50 パーセントということになる。
また、この程度の枠なら、画像を用意しなくても、Hammerspoon で描画できると思う。
矩形や円を描く定義がどっさりあるのだ。
下記のリンクに例文がある。
🔗 hs.canvas.examples · asmagill/hammerspoon Wiki · GitHub
ここまでやって思うのは「GeekToolのほうが楽なんじゃねぇかな」ということだ。
しかし Hammerspoon だからこそ出来ることもあるはずだ。
童貞を殺すセーターのお姉さんを表示する
Hammerspoon なら、キー操作と hs.canvas の表示を組み合わせることも可能なはずだ。んで、いろいろ考えてみた。
こう、ちょっとえっちな感じなお姉さんが、ちょっとした情報を教えてくれる、そんなスクリプトを構想してみた。
「いま何時」だとか、「きょう何曜日」だとか。
そういうのを教えてくれるわけだ。
使うのは hs.hotkey.modal というモジュールだ。
とあるキーの組み合わせで「お姉さんモード」に入り、「お姉さんモード」の時にいろんなキーを押すことで、ちょっとした情報を返す。
ここでは著作権の問題もあって自分でお姉さんの絵を描くが、ちゃんとした絵師の絵を個人利用の範囲で使用させてもらえば、そこそこ良くなるのではなかろうか。
教えてよ、お姉さん、と。ぼく、知りたいよ、もっともっと教えてよ、と。
そんな思いで作ったのが以下のスクリプトだ。
-- oneesanMode
oneesan = nil
oneesanMode = hs.hotkey.modal.new('cmd-alt-ctrl', 'up')
function oneesanMode:entered()
if oneesan then
oneesan:show(0.5)
oneesan:bringToFront()
else
local img = hs.image.imageFromPath(os.getenv("HOME").."/.hammerspoon/oneesan_1024.png")
local c = require("hs.canvas")
oneesan = c.new{ x = 1040, y = 525, h = 400, w = 400 }:show()
oneesan[1] = { type = "image", image = img }
oneesan[2] = { type = "text", text = '', frame = { x = "0.58", y = "0.34", h = "0.2", w = "0.4" }, textColor = { black = 1.0, alpha = 0.0 }, textFont = "07YasashisaGothic", textSize = 27.0, textAlignment = "center" }
oneesan[3] = { type = "text", text = '時刻は', frame = { x = "0.58", y = "0.26", h = "0.2", w = "0.4" }, textColor = { black = 1.0 }, textFont = "07YasashisaGothic", textSize = 20.0, textAlignment = "center" }
oneesan[4] = { type = "text", text = '', frame = { x = "0.58", y = "0.34", h = "0.2", w = "0.4" }, textColor = { black = 1.0, alpha = 1.0 }, textFont = "Keifont", textSize = 24.0, textAlignment = "center" }
function tokei()
local now = hs.execute("date +%H:%M:%S")
oneesan[4].text = now
end
dtTokei = hs.timer.new(1, tokei)
dtTokei:start()
end
end
oneesanFlag = nil
local function oneesanClock()
oneesan[2].textColor.alpha = 0.0
oneesan[4].textColor.alpha = 1.0
oneesan[3].text = '時刻は'
end
function oneesanMode:exited()
if not oneesanFlag then
oneesan:hide(0.5)
end
if oneesanFlag == 'back' then
oneesan:sendToBack()
oneesanFlag = nil
oneesanClock()
elseif oneesanFlag == 'front' then
oneesanClock()
oneesanFlag = nil
end
end
function oneesanMoji(size)
oneesan[2].textSize = size
oneesan[2].textColor.alpha = 1.0
oneesan[4].textColor.alpha = 0.0
end
oneesanMode:bind('', 'escape', function()
oneesanMode:exit()
end)
oneesanMode:bind('', 'q', function()
oneesanMode:exit()
end)
oneesanMode:bind('', 'space', function()
oneesanMode:exit()
end)
oneesanMode:bind('cmd-alt-ctrl', 'up', function()
oneesanMode:exit()
end)
oneesanMode:bind('', 'f', function()
oneesanFlag = 'front'
oneesanMode:exit()
end)
oneesanMode:bind('', 'b', function()
oneesanFlag = 'back'
oneesanMode:exit()
end)
oneesanMode:bind('', 'k', function()
oneesanMoji(30.0)
local kion = hs.execute("cat ~/Documents/weather/kion.txt")
oneesan[2].text = kion
oneesan[3].text = '気温は'
end)
oneesanMode:bind('', 't', function()
oneesanMoji(27.0)
local tenki = hs.execute("cat ~/Documents/weather/tenki.txt")
oneesan[2].text = tenki
oneesan[3].text = '天気は'
end)
oneesanMode:bind('', 'w', function()
local ssid = hs.wifi.currentNetwork()
oneesanMoji(18.0)
oneesan[2].text = ssid
oneesan[3].text = 'wi-fiは'
end)
oneesanMode:bind('', 'm', function()
oneesanMoji(27.0)
local method = hs.keycodes.currentMethod()
if not method then
method = hs.keycodes.currentLayout()
end
oneesan[2].text = method
oneesan[3].text = '入力は'
end)
oneesanMode:bind('', 'j', function()
oneesanClock()
end)
oneesanMode:bind('', 'y', function()
oneesanMoji(27.0)
local week = hs.execute("date +%w")
week = tonumber(week) + 1
local yobi = {'日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'}
oneesan[2].text = yobi[week]
oneesan[3].text = 'きょうは'
end)
oneesanMode:bind('', 'i', function()
oneesanMoji(18.0)
local ip = hs.execute("networksetup -getinfo Wi-Fi | grep '^IP address:' | awk '{print $3}'")
oneesan[2].text = ip
oneesan[3].text = 'ipは'
end)
oneesanMode:bind('', 'g', function()
oneesanMoji(18.0)
local global = hs.execute("curl inet-ip.info")
oneesan[2].text = global
oneesan[3].text = 'ipは'
end)
oneesanMode:bind('', 'n', function()
oneesanMoji(30.0)
local day = hs.execute("date +%m月%d日")
oneesan[2].text = day
oneesan[3].text = '今日は'
end)
command + option + control + ↑ でお姉さんモードに入る。
同じホットキーでお姉さんモードから抜ける。
escape、Qキーを押してもお姉さんモードから抜けることにした。
お姉さんモードでJキーを押すと時間を表示する。
Yキーで曜日を表示する。
Tキーで天気、Kキーで気温を表示する。これは、「Mac でロケーションごとの気温を表示する | 林檎コンピュータ」で使用したシェルスクリプトを利用している。
上記リンク先のシェルスクリプトの最後に、
echo $tenki > $HOME/Documents/weather/tenki.txt
echo $kion > $HOME/Documents/weather/kion.txt
などと書きこんでテキストを吐き出し、それを表示している。
Wキーで wi-fi の ssid を表示し、Mキーで入力ソースを表示することにした。
追記 21.02.05
さらにIキーでローカルな IP アドレスを、Gキーでグローバルな IP アドレスを表示させてみた。
また、Bキーでお姉さんをウィンドウの背後に送ってお姉さんモードを終了、Fキーでお姉さんを前面に残しながらモードを終了することにした。これらの場合は、表示は時計になる。
このように長くなったスクリプトは、別ファイルにコピペして適当な名前をつけて保存し、init.lua に以下のように書くと外部化できる。
-- ~/.hammerspoon/douteiSlayer.lua の場合
require('douteiSlayer')
追記はここまで。
出来上がったのがこれ。
いやー、微妙っすなぁ。
これだったら素直にテキストだけ表示したほうがマシか。
というか、ssid とか入力ソースって、いうほど表示したいか、という話もあるな。
筆者が、この男子中学生が作るようなスクリプトでなにをいいたかったかというと、
oneesan[2].text = '文字列'
などとやれば、表示を容易に変更することができる、ということなのである。
これは GeekTool にはない強みであり、インタラクティブなスクリプトが Hammerspoon なら実現できる、ということなのだ。
このお姉さんにファイルをドラッグ&ドロップして、なにかの処理をさせる、みたいなことが出来るかもしれない。document にそれっぽいことが書いてあって、ちょっと試したけどよくわからなかった。
仮にそれが出来るなら、別の展開もあるかもしれませんね。
最近のコメント