如何编写自动切换键盘布局脚本

wen 实用脚本 3

本文目录导读:

如何编写自动切换键盘布局脚本

  1. Linux (X11) - 使用 setxkbmap
  2. Linux (Wayland) - 使用 ydotool
  3. Windows - PowerShell 脚本
  4. macOS - AppleScript + Shell
  5. 更高级的方案 - 使用 Python
  6. 使用方法
  7. 注意事项

Linux (X11) - 使用 setxkbmap

根据应用窗口切换

#!/bin/bash
# 监听窗口焦点变化并切换键盘布局
# 定义应用对应的键盘布局
declare -A APP_LAYOUTS
APP_LAYOUTS["firefox"]="us"
APP_LAYOUTS["code"]="us"
APP_LAYOUTS["terminal"]="de"
APP_LAYOUTS["slack"]="fr"
# 获取当前活动窗口的信息
get_active_window_info() {
    xdotool getactivewindow getwindowname
}
# 切换键盘布局
switch_layout() {
    local app_name="$1"
    local layout="${APP_LAYOUTS[$app_name]}"
    if [[ -n "$layout" ]]; then
        setxkbmap "$layout"
        echo "切换到 $layout 布局"
    fi
}
# 主循环
while true; do
    current_app=$(get_active_window_info)
    # 提取应用程序名称(小写)
    app_lower=$(echo "$current_app" | tr '[:upper:]' '[:lower:]')
    # 检查是否有匹配的应用
    for app in "${!APP_LAYOUTS[@]}"; do
        if [[ "$app_lower" == *"$app"* ]]; then
            switch_layout "$app"
            break
        fi
    done
    sleep 0.5
done

Linux (Wayland) - 使用 ydotool

#!/bin/bash
# Wayland 环境下的键盘布局切换
# 安装依赖
# sudo apt install ydotool
switch_layout_wayland() {
    local layout="$1"
    case "$layout" in
        "us")
            gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us')]"
            ;;
        "de")
            gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'de')]"
            ;;
        "fr")
            gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'fr')]"
            ;;
    esac
}
# 根据时间切换(示例)
while true; do
    hour=$(date +%H)
    if [ "$hour" -ge 9 ] && [ "$hour" -lt 18 ]; then
        # 工作时间使用英文布局
        switch_layout_wayland "us"
    else
        # 休息时间使用母语布局
        switch_layout_wayland "de"
    fi
    sleep 3600  # 每小时检查一次
done

Windows - PowerShell 脚本

# 自动切换键盘布局脚本
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class KeyboardLayout {
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
    [DllImport("user32.dll")]
    public static extern IntPtr GetKeyboardLayout(uint idThread);
    [DllImport("user32.dll")]
    public static extern IntPtr ActivateKeyboardLayout(IntPtr hkl, uint flags);
}
"@
function Get-ActiveProcessName {
    $hwnd = [KeyboardLayout]::GetForegroundWindow()
    $null = [KeyboardLayout]::GetWindowThreadProcessId($hwnd, [ref]$pid)
    return (Get-Process -Id $pid).ProcessName
}
function Set-KeyboardLayout {
    param([string]$LayoutName)
    # 布局ID映射
    $layouts = @{
        "US" = 0x04090409
        "DE" = 0x04070407
        "FR" = 0x040C040C
    }
    if ($layouts.ContainsKey($LayoutName)) {
        $hkl = [IntPtr]::new($layouts[$LayoutName])
        [KeyboardLayout]::ActivateKeyboardLayout($hkl, 0)
    }
}
# 主循环
while ($true) {
    $processName = Get-ActiveProcessName
    switch -Wildcard ($processName) {
        "chrome*" { Set-KeyboardLayout "US" }
        "firefox*" { Set-KeyboardLayout "US" }
        "notepad*" { Set-KeyboardLayout "US" }
        "devenv*" { Set-KeyboardLayout "US" }
        "*word*" { Set-KeyboardLayout "FR" }
        "*excel*" { Set-KeyboardLayout "FR" }
        default { Set-KeyboardLayout "DE" }
    }
    Start-Sleep -Milliseconds 500
}

macOS - AppleScript + Shell

#!/bin/bash
# macOS 键盘布局切换脚本
# 获取当前活动应用
get_active_app() {
    osascript -e 'tell application "System Events" to get name of first process whose frontmost is true'
}
# 切换输入源
switch_input_source() {
    local layout="$1"
    case "$layout" in
        "US")
            osascript -e 'tell application "System Events" to tell process "SystemUIServer" to click menu bar item 1 of menu bar 2'
            ;;
        "German")
            osascript -e 'tell application "System Events" to tell process "SystemUIServer" to click menu bar item 2 of menu bar 2'
            ;;
        "French")
            osascript -e 'tell application "System Events" to tell process "SystemUIServer" to click menu bar item 3 of menu bar 2'
            ;;
    esac
}
# 配置应用-布局映射
declare -A APP_LAYOUTS
APP_LAYOUTS["Safari"]="US"
APP_LAYOUTS["Chrome"]="US"
APP_LAYOUTS["Terminal"]="US"
APP_LAYOUTS["Xcode"]="US"
APP_LAYOUTS["TextEdit"]="German"
APP_LAYOUTS["Pages"]="French"
while true; do
    current_app=$(get_active_app)
    if [[ -n "${APP_LAYOUTS[$current_app]}" ]]; then
        switch_input_source "${APP_LAYOUTS[$current_app]}"
    fi
    sleep 0.5
done

更高级的方案 - 使用 Python

#!/usr/bin/env python3
"""
跨平台键盘布局自动切换器
"""
import platform
import time
import subprocess
import re
class KeyboardLayoutSwitcher:
    def __init__(self):
        self.system = platform.system()
        self.setup_layout_mapping()
    def setup_layout_mapping(self):
        """定义应用和布局的映射关系"""
        self.layout_map = {
            # 应用名关键词: 布局名
            'browser': 'us',      # 浏览器
            'terminal': 'us',     # 终端
            'code': 'us',         # 编程IDE
            'office': 'fr',       # 办公软件
            'chat': 'de',         # 聊天软件
            'default': 'us'       # 默认布局
        }
        # 具体应用映射
        self.app_map = {
            'firefox': 'browser',
            'chrome': 'browser',
            'safari': 'browser',
            'terminal': 'terminal',
            'iterm2': 'terminal',
            'vscode': 'code',
            'code': 'code',
            'word': 'office',
            'excel': 'office',
            'slack': 'chat',
            'telegram': 'chat',
            'discord': 'chat',
        }
    def get_active_window_name(self):
        """获取当前活动窗口名称"""
        if self.system == 'Linux':
            try:
                result = subprocess.run(
                    ['xdotool', 'getactivewindow', 'getwindowname'],
                    capture_output=True, text=True
                )
                return result.stdout.strip().lower()
            except:
                return ''
        elif self.system == 'Darwin':  # macOS
            try:
                result = subprocess.run(
                    ['osascript', '-e', 
                     'tell application "System Events" to get name of first process whose frontmost is true'],
                    capture_output=True, text=True
                )
                return result.stdout.strip().lower()
            except:
                return ''
        elif self.system == 'Windows':
            try:
                import win32gui
                window = win32gui.GetForegroundWindow()
                return win32gui.GetWindowText(window).lower()
            except:
                return ''
        return ''
    def switch_layout(self, layout):
        """切换键盘布局"""
        if self.system == 'Linux':
            subprocess.run(['setxkbmap', layout])
        elif self.system == 'Darwin':
            # macOS 切换输入源
            script = f'''
            tell application "System Events"
                tell process "SystemUIServer"
                    click menu bar item 1 of menu bar 2
                end tell
            end tell
            '''
            subprocess.run(['osascript', '-e', script])
        elif self.system == 'Windows':
            # Windows 切换布局
            import win32api
            import win32con
            layouts = {
                'us': 0x04090409,
                'de': 0x04070407,
                'fr': 0x040C040C
            }
            if layout in layouts:
                win32api.LoadKeyboardLayout(f'{layouts[layout]:08X}', win32con.KLF_ACTIVATE)
        print(f"切换到 {layout} 布局")
    def determine_layout(self, window_name):
        """根据窗口名称确定应使用的布局"""
        window_lower = window_name.lower()
        # 检查具体应用
        for app, category in self.app_map.items():
            if app in window_lower:
                return self.layout_map.get(category, self.layout_map['default'])
        # 检查关键词
        for keyword, layout in self.layout_map.items():
            if keyword in window_lower and keyword != 'default':
                return layout
        return self.layout_map['default']
    def run(self):
        """主运行循环"""
        print("键盘布局自动切换器已启动...")
        last_layout = None
        while True:
            try:
                window_name = self.get_active_window_name()
                if window_name:
                    target_layout = self.determine_layout(window_name)
                    if target_layout != last_layout:
                        self.switch_layout(target_layout)
                        last_layout = target_layout
                time.sleep(0.5)  # 每0.5秒检查一次
            except KeyboardInterrupt:
                print("\n程序已停止")
                break
            except Exception as e:
                print(f"错误: {e}")
                time.sleep(2)
if __name__ == "__main__":
    switcher = KeyboardLayoutSwitcher()
    switcher.run()

使用方法

Linux 用户:

# 保存脚本为 switch_layout.sh
chmod +x switch_layout.sh
./switch_layout.sh  # 直接运行
# 或设置为开机自启动
# 添加到 ~/.bashrc 或使用 systemd

Windows 用户:

# 保存为 .ps1 文件
# 以管理员身份运行 PowerShell
Set-ExecutionPolicy RemoteSigned
.\keyboard_switcher.ps1

macOS 用户:

# 保存脚本
chmod +x keyboard_switcher.sh
./keyboard_switcher.sh

注意事项

  1. 权限问题:某些系统可能需要管理员权限
  2. 性能影响:建议将检查间隔设为0.5-1秒
  3. 自定义映射:根据自己的使用习惯修改应用-布局映射
  4. 调试建议:先使用 echo 测试窗口检测功能

选择适合你系统的版本进行定制即可!

抱歉,评论功能暂时关闭!