幻燈片放映模式切換windows terminal背景圖片

LynneHuan發表於2021-04-10

幻燈片放映模式切換windows terminal背景圖片

指令碼功能

幻燈片模式自動切換windows terminal的背景圖片,可自定義包含圖片的目錄、切換頻率等。

使用命令python change_tty_image.py --help檢視使用幫助。

程式碼一共就162行,核心功能程式碼事實上可能只有不到50行,其他都是一些檢查、日誌等語句。感興趣的可以download指令碼,自行定製一些功能。

開發需求

近期在折騰windows terminal,於我而言這款終端軟體也基本完全替代xshell,特別是win 10內建了ssh, scp等命令,用起來非常舒服和流暢。再和wsl結合起來一起玩,簡直爽到飛起。

windows terminal可以自定義主題樣式,自定義背景圖片。作為一個偽二次元愛好者,當然要把背景換成adroable的小姐姐!

然而,每次終端只能設定一張圖片,根本無法滿足敲命令的時候看不一樣的二次元小姐姐的需求。聯想到windows可以設定圖片目錄,並選擇幻燈片模式動態切換桌面背景,於是去google一番,發現windows terminalsettings.json好像沒有這個選項。查閱[官方文件](Windows Terminal Appearance Profile Settings | Microsoft Docs)如下:

要麼給一個路徑,要麼就和桌面桌布設定為一樣。

所以,如果想要自動切換windows terminal的背景圖片,有一個折中方案:把backgroundImage設定為desktopWallpaper,然後桌面背景搞成幻燈片模式,也就是下面這樣子:

這樣就能自動切換。

但是像我這樣桌布比較多的收藏家,正愁桌布多得無處安放,怎麼能把desktopwindows terminal設定成一樣的背景呢?這多不合適!

於是,我花了1個小時,用python寫了一個簡單的指令碼,支援設定桌布目錄更新頻率隨機更新功能,每個固定時間就為windows terminal切換一張背景圖片。

使用技術

要實現這個功能其實很簡單,不需要高大上的技術。整個開發需求主要包含兩點:

  • 定時任務
  • 修改windows terminalsettings.json中的backgroundImage項,切換為指定目錄下的圖片路徑,並進行輪循設定。

針對兩點需求,實現手段分別為:

  • 使用time.sleep()設定定時任務。這應該是簡單的方式了,適合簡單的定時任務觸發。
  • 使用IO操作,先讀取指定目錄的所有image路徑,然後取一個路徑出來,替換掉backgroundImage的值即可。

實現起來很簡單,也順便幫我複習了一下python操作檔案和目錄的一些介面。

  • time模組獲取時間,方便記錄日誌
  • random模組獲取隨機數,得到隨機圖片,顯然,此處無需使用安全隨機數生成器
  • os.walk()遍歷所有目錄下所有的圖片路徑
  • 設定臨時檔案,讀配置的時候,邊讀邊寫,然後可以使用re模組,正則匹配含有backgroundImage的行,替換掉路徑
  • 執行緒休眠實現定時任務

操作說明

  • python change_tty_image.py -h檢視幫助
  • 確保settings.json中已經預定義了一個路徑
  • 每次開始任務之前會備份一份配置檔案,不用擔心原有配置丟失
  • 更新頻率至少為10 min,太快了不就走馬觀花
  • 建議使用pythonw後臺執行指令碼

使用示例

檢視幫助

輸入引數使用

關鍵操作都會記錄日誌,或在螢幕輸出!

指令碼詳情

# -*- encoding: utf-8 -*-
'''
@File    : change_tty_image.py
@Time    : 2021/04/08 21:00:20
@Author  : Roderick Chan
@Email   : ch22166@163.com
@Desc    : Change windows-terminal background image automatically
'''

import os
import sys
import functools
import random
import re
import time

# key word to set image
key_word = "\"backgroundImage\""

# help message
help_msg = """
Usage: 
    python change_tty_image.py [settings_path] [picture_directory] [update_frequency] [random]
Function:
    Change windows-terminal background image automatically.
Note:
    settings_path:          [required]
        The absolute path of windows-terminal setting file.
    picture_directory:      [required]
        A absolute directory path fulled with pictures, only support 'png', 'jpg', 'gif'.
    update_frequency:       [required]
        The frequency to update image, should be more than 10, default value is 30, which represents 30min.
    random:                 [optional]
        Select image randomly or not. Default value: False.
Tips:
    1. Use `python` to run this script and output log-info on the screen.
    2. Use `pythonw` to run this script in the background and output nothing, but your can use 'tasklist' and 'taskkill' to stop. 
    3. recommendation command:
        pythonw change_tty_image.py [settings_path] [picture_directory] [update_frequency] [random] > change_image.log
    4. Use `python change_tty_image.py -h` to get help.
"""

def get_time():
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 

def log(msg):
    print("\033[1;32mINFO\033[0m: {}    \033[1;34mTime\033[0m: {}\n".format(msg, get_time()))

# parse args
# check args
args = sys.argv
arg_len = len(args)

# show help
if len(args) > 1 and (args[1] == "-h" or args[1] == "--help"):
    print(help_msg)
    sys.exit(0)

if arg_len < 4 or arg_len > 5:
    print("\033[1;31m[-] Args Error!\033[0m\n")
    print(help_msg)
    sys.exit(-1)

# validate args
settings_path = args[1]
picture_directory = args[2]
update_frequency = args[3]
random_enabled = False
if arg_len == 5:
    random_enabled = bool(args[4])

assert os.path.exists(settings_path), "settings_path doesn't exist."
assert os.path.isfile(settings_path), "settings_path is not a file path."
assert os.path.exists(picture_directory), "picture_directory doesn't exist."
assert os.path.isdir(picture_directory), "picture_directory is not a dir path."

# process settings_path
settings_dir, settings_full_name = os.path.split(settings_path)
settings_name, setting_suffix = os.path.splitext(settings_full_name)
backup_setting_path = os.path.join(settings_dir, settings_name + "_backup" + setting_suffix)
tmp_setting_path = os.path.join(settings_dir, settings_name + "_tmpfile" + setting_suffix)


# process update_frequency
if update_frequency.isdecimal():
    update_frequency = int(update_frequency)
    if update_frequency < 10:
        update_frequency = 30
else:
    update_frequency = 30
log('settings_path: {}'.format(settings_path))
log('backup_setting_path: {}'.format(backup_setting_path))
log('picture_directory: {}'.format(picture_directory))
log('update_frequency: {}'.format(update_frequency))
log('random_enabled: {}'.format(random_enabled))

# get all picture path
all_picture_path = []
support_suffix = ('.jpg', '.png', '.gif')
for r, dl, fl in os.walk(picture_directory,):
    for f in fl:
        is_ok = functools.reduce(lambda a, b : a or b, map(lambda x: f.endswith(x), support_suffix))
        if not is_ok:
            continue
        # check size
        if len(all_picture_path) > 0x1000:
            continue;
        all_picture_path.append(os.path.join(r, f))

assert len(all_picture_path) > 0, 'no pictures appended, check your picture_directory.'

# validate settings_path
flag = False
with open(file=settings_path, mode='r+', encoding='utf-8') as fd:
    for line in fd:
        if line.strip().startswith(key_word):
            flag = True
            break
assert flag, "please initial your windows-terminal settings file first, add {} value at least.".format(key_word)

log('all_picture_path : {}'.format(all_picture_path))

# back up
if not os.path.exists(backup_setting_path):
    cmd = "copy {} {}".format(settings_path, backup_setting_path)
    os.popen(cmd)
    log("execute \"{}\"".format(cmd))

idx = -1

while True:
    if random_enabled:
        idx = random.randint(0, len(all_picture_path) - 1)
    else:
        idx += 1
        idx %= len(all_picture_path)
    
    # replace '\' with '/'
    cur_picture_path = all_picture_path[idx].replace("\\", "/")
    log('cur_picture_path: {}'.format(cur_picture_path))
    with open(file=settings_path, mode='r', encoding='utf-8') as fd_src:
        with open(file=tmp_setting_path, mode='w+', encoding='utf-8') as fd_bck:
            for line in fd_src:
                if not line.strip().startswith(key_word):
                    fd_bck.write(line)
                    continue
                res = re.sub(r"({}\s?:\s?)\".+\",".format(key_word), r'\1"{}",'.format(cur_picture_path), line)
                fd_bck.write(res)
    
    cmd = "copy {} {}".format(tmp_setting_path, settings_path)
    os.popen(cmd)
    log("execute \"{}\"".format(cmd))
    
    cmd = "del {}".format(tmp_setting_path)
    os.popen(cmd)
    log("execute \"{}\"".format(cmd))
    
    # sleep
    log("sleep start...")
    time.sleep(update_frequency * 60)
    log("sleep end...")
    

引用參考

windows terminal profile setting:<Windows Terminal Appearance Profile Settings | Microsoft Docs>

相關文章