出色的 JetBrains IDE(IDEA 等)几乎将所有可以想到的键盘快捷键都分配给了某些功能。虽然有时有点让人不知所措,但使用起来也非常高效。
我的问题是 Unity 也分配了一些快捷键,并且它们优先。一个特别烦人的例子是CTRL+ ALT+ L。这个问题之前已经探讨过了这里。
但这两种方法都不令人满意。
- 关闭系统快捷方式全球妨碍了我使用系统的整体效率。
- 当我在不同的平台上进行开发时(并且必须选择不同的映射),在 IDEA 中切换到不同的键盘映射会让我感到非常困惑。
有没有办法仅在某个应用程序处于活动状态(即正在运行并处于焦点状态)时关闭系统快捷方式?
我愿意在每次启动应用程序时运行一个脚本。
答案1
如果(并且只要)特定应用程序的窗口处于活动状态,如何自动禁用多个(特定)快捷方式
当任意应用程序的窗口处于活动状态时,以下脚本将禁用特定的快捷键。
尽管你提到““我愿意在每次启动应用程序时运行一个脚本。”, 没有理由事后终止该脚本,因为它的效率极低。
剧本
#!/usr/bin/env python3
import subprocess
import time
import os
app = "gedit"
f = os.path.join(os.environ["HOME"], "keylist")
def run(cmd):
subprocess.Popen(cmd)
def get(cmd):
try:
return subprocess.check_output(cmd).decode("utf-8").strip()
except:
pass
def getactive():
return get(["xdotool", "getactivewindow"])
def setkeys(val):
# --- add the keys to be disabled below
keys = [
["org.gnome.settings-daemon.plugins.media-keys", "logout"],
["org.gnome.settings-daemon.plugins.media-keys", "screensaver"],
]
# ---
writelist = []
if not val:
try:
values = open(f).read().splitlines()
except FileNotFoundError:
values = []
for i, key in enumerate(keys):
try:
cmd = ["gsettings", "set"]+key+[values[i]]
except IndexError:
cmd = ["gsettings", "reset"]+key
run(cmd)
else:
for key in keys:
cmd = ["gsettings", "set"]+key+["['']"]
read = get(["gsettings", "get"]+key)
writelist.append(read)
run(cmd)
if writelist:
open(f, "wt").write("\n".join(writelist))
front1 = None
while True:
time.sleep(1)
pid = get(["pgrep", app])
if pid:
try:
active = get(["xdotool", "getactivewindow"])
relevant = get(["xdotool", "search", "--all", "--pid", pid]).splitlines()
front2 = active in relevant
except AttributeError:
front2 = front1
else:
front2 = False
if front2 != front1:
if front2:
setkeys(True)
else:
setkeys(False)
front1 = front2
如何使用
该脚本需要
xdotool
:sudo apt-get install xdotool
将脚本复制到一个空文件中,另存为
disable_shortcuts.py
在脚本的头部,替换以下行:
app = "gedit"
您的应用程序的“gedit”,含义是:拥有该窗口的进程名称。
通过以下命令测试运行脚本:
python3 /path/to/disable_shortcuts.py
如果一切正常,请将其添加到启动应用程序:Dash > 启动应用程序 > 添加。添加命令:
/bin/bash -c "sleep 15 && python3 /path/to/disable_shortcuts.py"
添加更多可禁用的快捷方式
作为示例,我添加了您提到的快捷方式:CTRL++ ALT。L快捷方式在数据库中设置dconf
,可以使用来设置或禁用gsettings
。
在脚本中,这些gsettings
条目在函数中设置:setkeys()
def setkeys(val):
# --- add the keys to be disabled below
keys = [
["org.gnome.settings-daemon.plugins.media-keys", "screensaver"]
]
# ---
添加(禁用)注销快捷方式的示例:
- 打开终端窗口,运行命令
dconf watch /
- 打开系统设置>“键盘”>“快捷键”>“系统”
将快捷方式重新设置为其自身。在终端中,你可以看到
gsettings
属于该快捷方式的键:现在我们必须添加找到的密钥(外观略有不同):
["org.gnome.settings-daemon.plugins.media-keys", "logout"]
...到我们函数中的“键”列表:
def setkeys(val): # --- add the keys to be disabled below keys = [ ["org.gnome.settings-daemon.plugins.media-keys", "screensaver"], ["org.gnome.settings-daemon.plugins.media-keys", "logout"], ]
现在,如果您的应用程序位于前面,则CTRL+ ALT+L和CTRL+ ALT+都将被禁用。Delete
解释
如上所述,快捷方式(如您提到的快捷方式)是在dconf
数据库中设置的。在示例CTRL++中,设置或编辑快捷方式的键是ALT:L
org.gnome.settings-daemon.plugins.media-keys screensaver
到禁用关键的命令是:
gsettings set org.gnome.settings-daemon.plugins.media-keys screensaver ""
要将密钥重置为其默认值:
gsettings reset org.gnome.settings-daemon.plugins.media-keys screensaver
如果发生以下情况,脚本每秒查找一次:
- 你的应用程序完全运行
- 如果是,它会查看是否有任何窗口处于活动状态
再次(仅)如果是,它会禁用快捷方式,列在
# --- add the keys to be disabled below keys = [ ["org.gnome.settings-daemon.plugins.media-keys", "screensaver"], ["org.gnome.settings-daemon.plugins.media-keys", "logout"], ]
...等待下一次状态改变。
如果活动窗口不再是您的应用程序之一,则列表中提到的键将重置为默认值。
笔记
如前所述,该脚本对处理器的额外负担为零。您可以在启动时运行它,如“如何使用”中所述。
影响多个应用程序
正如评论中所讨论的,在 OP 的具体情况下,在团体所有应用程序都位于一个目录中。
下面是将其应用于所有应用程序的版本,其输出为
pgrep -f
将包含特定目录。在我的示例中,我设置了/opt
目录,因此如果活动窗口是 中的任何应用程序之一/opt
,则设置的快捷方式将被禁用。
将 /opt 中某个应用程序的窗口移到最前面将禁用注销快捷方式
如果另一个窗口获得焦点,则重新启用快捷方式
剧本
#!/usr/bin/env python3
import subprocess
import time
import os
appdir = "/opt"
f = os.path.join(os.environ["HOME"], "keylist")
def run(cmd):
subprocess.call(cmd)
def get(cmd):
try:
return subprocess.check_output(cmd).decode("utf-8").strip()
except:
pass
def getactive():
return get(["xdotool", "getactivewindow"])
def setkeys(val):
# --- add the keys to be disabled below
keys = [
["org.gnome.settings-daemon.plugins.media-keys", "logout"],
["org.gnome.settings-daemon.plugins.media-keys", "screensaver"],
["org.gnome.desktop.wm.keybindings", "begin-move"],
]
# ---
writelist = []
if not val:
try:
values = open(f).read().splitlines()
except FileNotFoundError:
values = []
# for key in keys:
for i, key in enumerate(keys):
try:
cmd = ["gsettings", "set"]+key+[values[i]]
except IndexError:
cmd = ["gsettings", "reset"]+key
run(cmd)
else:
for key in keys:
cmd = ["gsettings", "set"]+key+["['']"]
read = get(["gsettings", "get"]+key)
writelist.append(read)
run(cmd)
if writelist:
open(f, "wt").write("\n".join(writelist))
front1 = None
while True:
time.sleep(1)
# check if any of the apps runs at all
checkpids = get(["pgrep", "-f", appdir])
# if so:
if checkpids:
checkpids = checkpids.splitlines()
active = getactive()
# get pid frontmost (doesn't work on pid 0)
match = [l for l in get(["xprop", "-id", active]).splitlines()\
if "_NET_WM_PID(CARDINAL)" in l]
if match:
# check if pid is of any of the relevant apps
pid = match[0].split("=")[1].strip()
front2 = True if pid in checkpids else False
else:
front2 = False
else:
front2 = False
if front2 != front1:
if front2:
setkeys(True)
else:
setkeys(False)
front1 = front2
如何使用
与第一个脚本一样,
xdotool
需要安装:sudo apt-get install xdotool
将脚本复制到一个空文件中,另存为
disable_shortcuts.py
在脚本的头部,替换以下行:
appdir = "/opt"
“/opt” 是您的应用程序所在的目录。
通过以下命令测试运行脚本:
python3 /path/to/disable_shortcuts.py
如果一切正常,请将其添加到启动应用程序:Dash > 启动应用程序 > 添加。添加命令:
/bin/bash -c "sleep 15 && python3 /path/to/disable_shortcuts.py"
向列表中添加其他快捷方式的操作与脚本的第 1 版完全类似。
它适用于所有应用程序吗?
在您的回答中,您提到:
xprop 不会显示所有窗口的 PID。失败示例:秒表。
带有进程 0(如 tkinter windows,包括 Idle),在 的输出中没有 window-id xprop -id
。Idle
根据我的经验,没有任何冲突的快捷方式。如果您遇到任何 pid 为 0 的应用程序,需要禁用特定的快捷方式,请提及。
在这种情况下,一个可能的解决方法是将输出转换为
xdotool getactivewindow
转换为十六进制,格式使用,然后在输出中wmctrl
查找相应的pid
wmctrl -lp
尽管这似乎是最明显的开始做法,但我并没有在脚本中使用它,以使脚本尽可能轻量。
答案2
基于Jacob Vlijm 的答案(旧版本) 我编写了这个版本来解决这些额外的问题:
- 尊重用户在脚本运行时所做的更改。
- 不会将用户设置的值重置为默认值。
- 存储设置的备份,以防快捷方式被禁用时脚本退出。
句柄(这可能不是一个问题。)gsettings
和dconf
快捷方式。
未解决的问题:
- 我找不到某些快捷键(例如Alt+`和Alt+)F1的设置位置。这些来自 Unity 和 Compiz;我们如何才能通过编程更改按住Super?
xprop
不显示所有窗口的 PID。失败示例:stopwatch
。(Jaco Vlijm 有一些想法。
#!/usr/bin/env python3
import subprocess
import time
import os
# Path pattern to block
apppattern = "myprocess"
# Write a backup that can restore the settings at the
# start of the script.
# Leave empty to not write a backup.
backupfile = "~/.keymap_backup"
# Add the keys to be disabled below.
shortcuts = {
"org.gnome.settings-daemon.plugins.media-keys/key" : "gsettings",
"/org/gnome/desktop/wm/keybindings/key" : "dconf",
}
#
# Helper functions
#
# Run a command on the shell
def run(cmd):
subprocess.Popen(cmd)
# Run a command on the shell and return the
# stripped result
def get(cmd):
try:
return subprocess.check_output(cmd).decode("utf-8").strip()
except:
pass
# Get the PID of the currently active window
def getactive():
xdoid = get(["xdotool", "getactivewindow"])
pidline = [l for l in get(["xprop", "-id", xdoid]).splitlines()\
if "_NET_WM_PID(CARDINAL)" in l]
if pidline:
pid = pidline[0].split("=")[1].strip()
else:
# Something went wrong
print("Warning: Could not obtain PID of current window")
pid = ""
return pid
def readkey(key):
if shortcuts[key] == "gsettings":
return get(["gsettings", "get"] + key.split("/"))
elif shortcuts[key] == "dconf":
return get(["dconf", "read", key])
def writekey(key, val):
if val == "":
val = "['']"
if shortcuts[key] == "gsettings":
run(["gsettings", "set"] + key.split("/") + [val])
elif shortcuts[key] == "dconf":
run(["dconf", "write", key, val])
def resetkey(key):
if shortcuts[key] == "gsettings":
run(["gsettings", "reset"] + key.split("/"))
elif shortcuts[key] == "dconf":
run(["dconf", "reset", key])
# If val == True, disables all shortcuts.
# If val == False, resets all shortcuts.
def setkeys(flag):
for key, val in shortcutmap.items():
if flag == True:
# Read current value again; user may change
# settings, after all!
shortcutmap[key] = readkey(key)
writekey(key, "")
elif flag == False:
if val:
writekey(key, val)
else:
resetkey(key)
#
# Main script
#
# Store current shortcuts in case they are non-default
# Note: if the default is set, dconf returns an empty string!
# Optionally, create a backup script to restore the value in case
# this script crashes at an inopportune time.
shortcutmap = {}
if backupfile:
f = open(os.path.expanduser(backupfile),'w+')
f.write('#!/bin/sh\n')
for key, val in shortcuts.items():
if shortcuts[key] == "gsettings":
shortcutmap[key] = get(["gsettings", "get"] + key.split("/"))
if backupfile:
if shortcutmap[key]:
f.write("gsettings set " + " ".join(key.split("/")) + " " +
shortcutmap[key] + "\n")
else:
f.write("gsettings reset " + " ".join(key.split("/")) + "\n")
elif shortcuts[key] == "dconf":
shortcutmap[key] = get(["dconf", "read", key])
if backupfile:
if shortcutmap[key]:
f.write("dconf write " + key + " " + shortcutmap[key] + "\n")
else:
f.write("dconf reset " + key + "\n")
if backupfile: f.close()
# Check every half second if the window changed form or to a
# matching application.
front1 = None
while True:
time.sleep(0.5)
checkpids = get(["pgrep", "-f", apppattern])
if checkpids:
checkpids = checkpids.splitlines()
activepid = getactive()
#print(activepid)
if activepid:
front2 = True if activepid in checkpids else False
else:
front2 = False
else:
front2 = False
if front2 != front1:
#print("Matches: " + str(flag))
if front2:
setkeys(True)
else:
setkeys(False)
front1 = front2
笔记:
- 请注意不同的密钥格式
gsettings
。dconf
gsettings
键做出现在dconf
但在那里所做的更改没有效果。一般来说,添加使用雅各布的方法以及gsettings
那些您必须手动追踪的dconf
。- 将备份文件作为脚本运行,以防快捷方式混乱,例如当快捷方式被禁用时脚本终止。