当某个应用程序处于活动状态时阻止Unity键盘快捷键

伟大的JetBrains IDE(IDEA等)为某些function分配了几乎所有可想到的键盘快捷键。 虽然有时候有点压倒性,但它也有效地使用。

我的问题是Unity也分配了一些这些快捷方式,并且它们优先。 一个特别恼人的例子是CTRL + ALT + L. 这个问题在此之前已经探讨过了 。

但是,这两种方法都不令人满意。

  1. 全局关闭系统快捷方式会影响我使用该系统的整体工作效率。
  2. 当我在不同平台上开发(并且必须选择不同的映射)时,切换到IDEA中的不同键映射会让我感到困惑。

有没有办法只在某个应用程序处于活动状态时关闭系统快捷方式,即运行和聚焦?

每次启动应用程序时,我都愿意运行脚本。

如果(并且只要)特定应用程序的窗口处于活动状态,如何自动禁用多个(特定)快捷方式

当任意应用程序的窗口处于活动状态时,下面的脚本将禁用特定的密钥快捷方式。

虽然你提到“ ”我每次启动应用程序时都愿意运行一个脚本。“ ,之后没有理由杀死脚本,它的function非常低。

剧本

#!/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 

如何使用

  1. 该脚本需要xdotool

     sudo apt-get install xdotool 
  2. 将脚本复制到空文件中,将其另存为disable_shortcuts.py

  3. 在脚本的头部,替换为行:

     app = "gedit" 

    您的应用程序的“gedit”,意思是:拥有该窗口的进程名称。

  4. 通过命令测试脚本:

     python3 /path/to/disable_shortcuts.py 
  5. 如果一切正常,请将其添加到启动应用程序: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"] ] # --- 

添加(禁用)注销快捷方式的示例:

  1. 打开终端窗口,运行命令dconf watch /
  2. 打开系统设置>“键盘”>“快捷方式”>“系统”
  3. 将快捷方式重新设置为自身。 在终端中,您可以看到属于快捷方式的gsettings键:

    在此处输入图像描述

  4. 现在我们必须添加找到的键(外观略有不同):

     ["org.gnome.settings-daemon.plugins.media-keys", "logout"] 

    …到我们函数中的“keys”列表:

     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 + LCTRL + ALT + Delete

说明

如上所述,快捷方式(如您提到的快捷方式)在dconf数据库中设置。 在示例CTRL + ALT + L中 ,设置或编辑schortcut的关键是:

 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 

如何使用

  1. 像第一个脚本一样,需要安装xdotool

     sudo apt-get install xdotool 
  2. 将脚本复制到空文件中,将其另存为disable_shortcuts.py

  3. 在脚本的头部,替换为行:

     appdir = "/opt" 

    “/ opt”在您的应用程序所在的目录中。

  4. 通过命令测试脚本:

     python3 /path/to/disable_shortcuts.py 
  5. 如果一切正常,请将其添加到启动应用程序:Dash>启动应用程序>添加。 添加命令:

     /bin/bash -c "sleep 15 && python3 /path/to/disable_shortcuts.py" 

向列表添加其他快捷方式与脚本的版本1完全相同。

它适用于所有应用程序吗?

在你的回答中,你提到:

xprop不会显示所有窗口的PID。 失败的例子:秒表。

带有pid 0的 Windows(如tkinter窗口,包括Idle)在xprop -id的输出中没有window-id。 根据我的经验, Idle没有任何冲突的捷径。 如果您遇到任何需要禁用特定快捷方式的pid 0应用程序,请提及。

在这种情况下,可能的转义是转换输出

 xdotool getactivewindow 

到hex, wmctrl使用的格式,然后在输出中查找相应的pid

 wmctrl -lp 

虽然这似乎是最明显的事情,但我没有在脚本中使用它来保持脚本尽可能轻量级。

基于(旧版本)Jacob Vlijm的回答我写了这个版本来解决这些额外的问题:

  1. 荣誉更改用户在脚本运行时所做的操作。
  2. 不重置用户设置为默认值的值。
  3. 存储设置的备份,以防脚本在禁用快捷方式时退出。
  4. 处理gsettingsdconf快捷方式。 (这可能不是问题。)

开放问题:

  • 我找不到一些快捷键,如Alt + `Alt + F1 。 这些来自Unity resp。 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但在那里dconf更改没有任何效果。 作为一般规则,使用Jacob方法添加的密钥作为gsettings以及必须在dconf手动跟踪的dconf
  • 将备份文件作为脚本运行,以防快捷方式变得混乱,例如,在禁用快捷方式时脚本终止。