iTerm2のカラースキームを時間帯によって自動的に変えよう


ずっと同じカラースキームでターミナルの画面を見ていると飽きるし、日中はライトテーマ・夕方はダークテーマにして気分をリフレッシュしたいと思いませんか? ですが、毎日いちいち手動で朝晩にテーマを切り替えるのは面倒なのでPythonにやらせます。

Python API

2019年7月にリリースされたiTerm2バージョン3.3.0からPython APIが提供されるようになりました(それ以前はAppleScriptというAPIがあったらしい?)。pip install iterm2 すればiTerm2外部からもAPIを叩けます。

APIを使用するには Preferences > General > Magic にある Enable Python API にチェックを入れます。

自前のスクリプトを用意したい場合、Scripts > Manage > New Python Script を選ぶとガイドが出てきて雛形を作ってくれます。
作成したPythonのコードは ~/Library/Application Support/iTerm2/Scripts に保存するようになっていますが、アプリ立ち上げ時に自動で読み込ませたい場合は上記のScripts傘下にAutoLaunchフォルダを作り、そこに保管します。

実はカラースキームの自動切り替えスクリプトはフルスクラッチする必要がなく、公式の Example Scripts にほぼ完成形が用意してあります。

元のサンプルコードだとカラースキームの切り替え時にiTerm2が起動している必要があったので、アプリ起動時にも現在の時刻を参考に切り替えてくれるよう変更してあります。以下のコードを~/Library/Application Support/iTerm2/Scripts/AutoLaunch/change_color.pyとして保存してください。

change_color.py
#!/usr/bin/env python3.7

import asyncio
import datetime
import iterm2

# Clock time to change colors.
LIGHT_TIME = (7, 0)
DARK_TIME = (17, 0)

# Color presets to use
LIGHT_PRESET_NAME = "material"
DARK_PRESET_NAME = "onedark"

# Profiles to update
PROFILES = ["Default"]

def get_datetime(t, time):
    return datetime.datetime(t.year, t.month, t.day, time[0], time[1])

def datetime_after(t, time):
    today = get_datetime(t, time)
    if today > t:
        return today
    # Same time tomorrow
    return today + datetime.timedelta(1)


def next_deadline_after(t):
    light_deadline = datetime_after(t, LIGHT_TIME)
    dark_deadline = datetime_after(t, DARK_TIME)
    if light_deadline < dark_deadline:
        return (LIGHT_PRESET_NAME, light_deadline)
    return (DARK_PRESET_NAME, dark_deadline)


def get_duration():
    now = datetime.datetime.now()
    preset_name, deadline = next_deadline_after(now)
    duration = (deadline - now).seconds
    print("Sleep for {} seconds until {}".format(duration, deadline))
    return duration, preset_name


async def set_colors(connection, preset_name):
    print("Change to preset {}".format(preset_name))
    preset = await iterm2.ColorPreset.async_get(connection, preset_name)
    for partial in (await iterm2.PartialProfile.async_query(connection)):
        if partial.name in PROFILES:
            await partial.async_set_color_preset(preset)


async def main(connection):
    now = datetime.datetime.now()
    begin = get_datetime(now, LIGHT_TIME)
    end = get_datetime(now, DARK_TIME)
    if (now > begin and now < end):
        await set_colors(connection, LIGHT_PRESET_NAME)
    else:
        await set_colors(connection, DARK_PRESET_NAME)
    while True:
        duration, preset_name = get_duration()
        await asyncio.sleep(duration)
        await set_colors(connection, preset_name)
        await asyncio.sleep(1)


iterm2.run_forever(main)

最初に定義した5つの定数

  • LIGHT_TIME=(7, 0) : ライトテーマに切り替える時間(HH, MM)
  • DARK_TIME=(16, 0) : ダークテーマに切り替える時間(HH, MM)
  • LIGHT_PRESET_NAME="..." : ライトテーマ名(デフォルトテーマはLight Background
  • DARK_PRESET_NAME="..." : ダークテーマ名(デフォルトテーマはDark Background
  • PROFILES=["..."] : テーマを変更させるプロファイル名(複数選択可)

は好みに合わせて変更してください。プロファイル名は自分の作ってあるプロファイル一覧から、テーマのプリセット名は右下の Color Presets... メニューから選びます。

ちなみに print(...) の標準出力や、存在しないプリセット名を指定してしまったときなどのエラーは Scripts > Manage > Console に吐き出されます。

退屈なことPythonにやらせるのサイコ〜〜〜〜〜〜

参考

iTerm2 にステータスバーが付いた