iTerm2 Python API 实战:用 copycolor 脚本在分屏时保持标签页颜色同步
iTerm2 Python API 实战用 copycolor 脚本在分屏时保持标签页颜色同步【免费下载链接】iTerm2iTerm2 is a terminal emulator for Mac OS X that does amazing things.项目地址: https://gitcode.com/gh_mirrors/it/iTerm2导读在 iTerm2 中同一标签页Tab内的多个窗格Pane可以来自不同的 Profile如果这些 Profile 定义了不同的标签页颜色分屏后就会出现同一标签页内两个窗格颜色不一致的情况。iTerm2 官方 Python API 示例 copycolor.rst 提供了一段名为 Preserve Tab Color保持标签页颜色的守护脚本它监听新会话的创建自动把标签页内已有会话的标签颜色复制给新窗格并在不改动底层 Profile 的前提下完成这一修改。读完本文你将掌握LocalWriteOnlyProfile、NewSessionMonitor、async_set_profile_properties等核心 API 的用法并能直接部署这段脚本到自己的 iTerm2 环境中。问题背景分屏后标签页颜色为什么会对不上iTerm2 允许每个 Profile 单独配置标签页颜色Tab Color。当你执行用其他 Profile 在此处分割Split with a different profile操作时新窗格会继承该 Profile 的标签页颜色而旧窗格保留原 Profile 的颜色。如果两个 Profile 的颜色不同同一个标签页就会呈现两种颜色视觉上容易被误认为两个独立的标签页。官方示例开篇即点明了这一场景If you split a session using a different profile, you can end up with two panes that have different tab colors. This script copies the old tab color to the new split pane so they stay in sync.解决思路很直接监听新会话创建事件找到它所在的标签页读取该标签页中已经存在的会话的标签颜色再用会话级而非 Profile 级的写操作把颜色应用到新会话上。这段脚本的核心价值在于第二个技巧——如何修改会话的 Profile 属性而不修改底层 Profile因为直接改底层 Profile 会影响所有使用该 Profile 的会话。完整示例代码以下是 copycolor.rst 中的完整脚本约 44 行你可以从仓库的 copycolor.its 下载该脚本的原始版本#!/usr/bin/env python3.7 import iterm2 async def main(connection): async def async_color_in_tab(tab, exclude): Return the tab color of any session that already existed. colors [] for session in tab.sessions: if session exclude: continue profile await session.async_get_profile() if not profile: continue color profile.tab_color if color: return color return None app await iterm2.async_get_app(connection) async with iterm2.NewSessionMonitor(connection) as mon: while True: # Wait for a new session to be created session_id await mon.async_get() session app.get_session_by_id(session_id) if not session: continue window, tab app.get_tab_and_window_for_session(session) if not tab: continue color await async_color_in_tab(tab, session) if not color: continue # Another session had a color in this tab. Change the tab # color property of the new session. Use LocalWriteOnlyProfile # and session.async_set_profile_properties to avoid changing the # underlying profile. change iterm2.LocalWriteOnlyProfile() change.set_tab_color(color) await session.async_set_profile_properties(change) iterm2.run_forever(main)脚本结构清晰自顶向下可以分为四个部分读取已有颜色、监听新会话、查找所属标签页、写入颜色。下面逐段剖析。逐段剖析四个核心步骤1. 读取标签页中已有会话的颜色async def async_color_in_tab(tab, exclude): Return the tab color of any session that already existed. colors [] for session in tab.sessions: if session exclude: continue profile await session.async_get_profile() if not profile: continue color profile.tab_color if color: return color return Noneasync_color_in_tab遍历标签页内的所有会话tab.sessions跳过新创建的那个会话exclude对每个已有会话调用session.async_get_profile()获取其生效的 Profile读取profile.tab_color。一旦找到非空颜色就立即返回全部没有颜色则返回None。两个细节值得注意async_get_profile()返回的 Profile包含会话本地修改。从 session.py 的实现看它通过iterm2.rpc.async_get_profile拉取数据docstring 明确写着including any session-local changes not in the underlying profile包含不在底层 Profile 中的会话本地修改。这意味着即使旧窗格此前已被脚本改过颜色新窗格也能读到最终生效的颜色而不是回退到 Profile 原始值。函数内部声明了colors []但从未使用属于示例代码遗留的冗余变量不影响逻辑可自行删除。2. 监听新会话的创建app await iterm2.async_get_app(connection) async with iterm2.NewSessionMonitor(connection) as mon: while True: # Wait for a new session to be created session_id await mon.async_get() session app.get_session_by_id(session_id) if not session: continueiterm2.async_get_app(connection)获取当前 App 的对象模型包含窗口、标签页、会话的树状结构。NewSessionMonitor是一个异步上下文管理器进入时订阅新会话创建通知退出时自动取消订阅见 lifecycle.py 的实现__aenter__中调用async_subscribe_to_new_session_notification__aexit__中调用async_unsubscribe。mon.async_get()会阻塞等待下一个新会话事件返回新会话的 ID字符串。随后用app.get_session_by_id(session_id)把 ID 解析为Session对象。如果解析失败例如会话已被关闭直接continue等待下一个事件。整个while True循环让脚本变成一个常驻守护进程这正是run_forever入口的意义。3. 定位新会话所属的标签页window, tab app.get_tab_and_window_for_session(session) if not tab: continueget_tab_and_window_for_session返回(Window, Tab)二元组用于找出会话所属的窗口和标签页。值得注意的是从 app.py 的源码注释看这个方法已标记为 Deprecated理由是名字与返回参数顺序不符——它实际返回的是(Window, Tab)而非名字暗示的(Tab, Window)官方建议使用新方法get_window_and_tab_for_session。内部实现是遍历terminal_windows与window.tabs检查session in tab.all_sessions来定位。新代码中可以改用get_window_and_tab_for_session语义更清晰。如果找不到所属标签页例如会话已被关闭跳过本次事件。4. 通过 LocalWriteOnlyProfile 写入颜色color await async_color_in_tab(tab, session) if not color: continue # Another session had a color in this tab. Change the tab # color property of the new session. Use LocalWriteOnlyProfile # and session.async_set_profile_properties to avoid changing the # underlying profile. change iterm2.LocalWriteOnlyProfile() change.set_tab_color(color) await session.async_set_profile_properties(change)这是全脚本最关键的部分不修改底层 Profile只修改这一个会话的生效配置。LocalWriteOnlyProfile是一个可写不可读的临时 Profile 对象从 profile.py 的实现看它的values属性保存着一组(键, JSON 值)对本身并不与任何真实 Profile 绑定。change.set_tab_color(color)会调用_color_set(Tab Color, value)把颜色对象序列化为 JSON 存进values字典。session.async_set_profile_properties(change)负责把这份变更应用到会话。从 session.py 的实现看当连接支持多属性批量设置supports_multiple_set_profile_properties时一次性把所有(key, json_value)打包为 assignments通过async_set_profile_properties_json发送对不支持的老版本3.3.0beta9 及更早则退化为逐条调用async_set_profile_property_json任一属性写入失败都会抛出RPCException。其 docstring 明确说明When you use this function the underlying profile is not modified. The session will keep a copy of its profile with these modifications.使用此函数时底层 Profile 不会被修改会话会保留一份带这些修改的 Profile 副本。这正是脚本能只影响新窗格、不影响其他会话的根本原因。关键 API 源码级剖析LocalWriteOnlyProfile会话级临时配置LocalWriteOnlyProfile是 iTerm2 Python API 中实现局部覆盖的标准工具官方文档在 profile.py 中直接引用本示例作为用法范例。它配合Session.async_set_profile_properties使用相关方法包括方法作用底层键名set_tab_color(color)设置标签页颜色未启用明暗分离颜色时生效Tab Colorset_tab_color_light(color)设置明色模式变体启用明暗分离颜色时生效Tab Color (Light)set_tab_color_dark(color)设置暗色模式变体启用明暗分离颜色时生效Tab Color (Dark)set_use_tab_color(value)是否启用标签页颜色Use Tab Color颜色对象通过_color_set内部的value.get_dict()序列化为 JSON 存储。值得注意的是set_tab_color的 docstring 特别说明它仅在未启用独立明暗模式颜色时使用used only when separate light/dark mode colors are not enabled。如果目标 Profile 启用了Use separate colors for light and dark mode选项标签页颜色实际由Tab Color (Light)/Tab Color (Dark)决定此时应改用set_tab_color_light/set_tab_color_dark才能生效——这是本示例在特定配置下的一个扩展点。Color颜色值的表示iterm2.Color见 color.py以 RGBA 描述颜色r、g、b、a取值均为 0-255a默认 255color_space目前仅支持 sRGB。它还提供from_hex解析#aabbcc或 13 位十六进制与from_cocoa解析 NSKeyedArchiver 编码等工厂方法。本示例中颜色直接取自已有 Profile 的tab_color属性属于Color实例无需自行构造。NewSessionMonitor事件驱动的守护模式NewSessionMonitorlifecycle.py内部使用asyncio.Queue缓存通知async_get()从队列取出新会话 ID 并返回。它与其他 Monitor如SessionTerminationMonitor、LayoutChangeMonitor一样遵循async with上下文管理器协议退出时自动退订。配合iterm2.run_forever(main)入口见 connection.py脚本可以无限期运行这正是官方守护脚本的典型形态。对照实验settabcolor 示例带来的启发同目录下的另一个官方示例 settabcolor.rst 与本脚本形成了很好的对照app await iterm2.async_get_app(connection) session app.current_terminal_window.current_tab.current_session change iterm2.LocalWriteOnlyProfile() color iterm2.Color(255, 128, 128) change.set_tab_color(color) change.set_use_tab_color(True) await session.async_set_profile_properties(change)settabcolor 演示的是主动设置对当前会话写入硬编码颜色并额外调用change.set_use_tab_color(True)开启标签页颜色开关最后用run_until_complete一次性执行。而 copycolor 是被动同步它只复制颜色值本身。对比两者可以得出一个重要结论——copycolor 假设已有会话的标签页颜色已经处于生效状态即旧 Profile 已开启Use Tab Color新窗格沿用相同的颜色值时自然沿用开关状态若旧窗格颜色来自关闭开关的状态即未启用标签颜色则profile.tab_color与渲染结果不一定一致这是使用本脚本时需要了解的边界条件。若需在新窗格上强制开启标签颜色可参考 settabcolor 追加change.set_use_tab_color(True)。安装与运行脚本以#!/usr/bin/env python3.7开头要求 Python 3.7 及以上版本当前 iTerm2 的 Python API 运行时通常内置 Python 3.10兼容无虞。运行方式有两种从仓库下载脚本文件copycolor.its 是该示例的官方可下载版本可以通过 iTerm2 菜单Scripts Manage Import...导入.its 即 iTerm2 Script 文件的扩展名。手动创建脚本新建一个以.py结尾的文件粘贴上文完整代码放入~/Library/Application Support/iTerm2/Scripts/AutoLaunch/目录即可随 iTerm2 启动自动运行或放入Scripts/目录后从Scripts菜单手动启动。由于脚本主体是while True循环 run_forever(main)它属于常驻守护脚本启动后持续监听直到 iTerm2 退出或脚本被手动终止。分屏操作完成后新窗格的标签颜色会自动与同标签页内已有会话对齐之后无需再手动干预。关于 Python API 脚本的更多运行机制普通脚本与守护脚本的区别、调试方法可以继续阅读仓库中的 tutorial/index.rst、running.rst 与 daemons.rst。扩展思路与注意事项颜色继承方向脚本总是优先取tab.sessions中第一个按会话顺序有颜色的会话作为颜色来源。若同一标签页内多个窗格颜色本就不同新窗格会继承最先遇到的那个方向不可配置。明暗分离模式若旧窗格 Profile 启用了独立明暗颜色脚本读取到的tab_color可能为空此时需要改用tab_color_light/tab_color_dark属性并对应调用set_tab_color_light/set_tab_color_dark两者均已在LocalWriteOnlyProfile中提供见 profile.py。临时会话与 tmux 会话脚本通过app.get_session_by_id解析会话对任何新创建会话一视同仁如果session_id无法解析为 Session 对象如会话已关闭脚本会安全跳过不会崩溃。API 版本兼容性async_set_profile_properties内部会自动检测连接能力对不支持批量写入的老版本 iTerm23.3.0beta9 及更早退化为逐条写入因此脚本具备较好的向后兼容性。与新方法的关系get_tab_and_window_for_session已废弃新代码建议改用get_window_and_tab_for_session返回顺序同为(Window, Tab)。总而言之copycolor 脚本是一份麻雀虽小、五脏俱全的官方范例它完整演示了事件监听NewSessionMonitor 对象模型查询async_get_app/get_session_by_id/get_tab_and_window_for_session 局部写入LocalWriteOnlyProfileasync_set_profile_properties这一 iTerm2 Python API 的典型编程范式尤其适合作为学习在不污染底层 Profile 的前提下做会话级定制的入门教材。【免费下载链接】iTerm2iTerm2 is a terminal emulator for Mac OS X that does amazing things.项目地址: https://gitcode.com/gh_mirrors/it/iTerm2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →