San*_*nto 5 shortcut-keys multiple-monitors unity
我的开发机器上有 3 个显示器,它运行 Ubuntu + Unity。我通常将一台显示器专用于网页浏览和 IM,另外两台用于运行 vim 或输入命令的终端。我正在寻找一个单键序列来随意切换它们。
我想知道如何将焦点切换到每个显示器上的(顶部)窗口。我正在寻找或有点像 ALT+TAB 的行为,除了不是在应用程序之间旋转(这只会使最近使用的应用程序实例在旋转中可用),我可以在监视器之间旋转焦点。
作为妥协,如果我可以在列表中包含每个窗口,我可以坚持使用 ALT+TAB 机制。不过,我仍然认为这会很烦人。
在下面的设置中,涉及两个脚本:一个后台脚本用于跟踪聚焦窗口的历史记录(请参阅底部的说明以了解为什么需要它),以及一个放置在快捷键下的“操作”脚本,将焦点设置在下一个屏幕上。如果下一个屏幕当前没有可设置焦点的窗口,则会显示一条消息:
脚本1;后台脚本,将其(准确)保存为focus_track.py
#!/usr/bin/env python3
import subprocess
import time
import os
rootdata = os.environ["HOME"]+"/.focus_history"
def get_screendata():
return sorted([int(s.split("+")[-2]) for s in subprocess.check_output(["xrandr"]).decode("utf-8").split() if s.count("+") == 2])
def current_windows():
try:
return subprocess.check_output(["wmctrl", "-lG"]).decode("utf-8")
except subprocess.CalledProcessError:
pass
def convert_format(w_id):
return w_id[:2]+(10-len(w_id))*"0"+w_id[2:]
def read_data():
return open(rootdata).read().splitlines()
def get_top(wlist):
top = convert_format([l.split("#")[-1].strip() for l in \
subprocess.check_output(["xprop", "-root"]).decode("utf-8").splitlines() \
if "_NET_ACTIVE_WINDOW(WINDOW)" in l][0])
return [l for l in wlist if top in l][0]
if __name__ == "__main__":
open(rootdata, "wt").write("This is an empty line")
while True:
time.sleep(1)
wdata = current_windows()
if wdata != None:
wlist = wdata.splitlines()
# get frontmost window (as in wmctrl -lG)
top = get_top(wlist)
oldlist = read_data()
if not top == oldlist[0]:
# clean up closed windows
[oldlist.remove(l) for l in oldlist if not l.split()[0] in wdata]
# remove possible other mentions of the active window
[oldlist.remove(l) for l in oldlist if l.startswith(top.split()[0])]
open(rootdata, "wt").write(("\n").join([top]+oldlist))
Run Code Online (Sandbox Code Playgroud)
脚本2;操作脚本,将其保存为与next_focus.py 脚本 1位于同一目录中 。
#!/usr/bin/env python3
import subprocess
import focus_track
# read existing windows and their order (x-wise) from file
windows = [w.split() for w in focus_track.read_data()]
w_data = [[w[0], int(w[2])] for w in windows]
# get position of currently focussed window
currfocus = focus_track.get_top(focus_track.current_windows().splitlines()).split()[2]
# get screendata
screens = focus_track.get_screendata()
def screen_pos(x):
return [(int(x) > n) for n in screens].count(True)
scr_position = screen_pos(currfocus)
next_screen = 1 if scr_position == len(screens) else scr_position+1
try:
next_focus = [w for w in w_data if screen_pos(w[1]) == next_screen][0]
subprocess.Popen(["wmctrl", "-ia", next_focus[0]])
except IndexError:
subprocess.Popen(["notify-send", "No window to focus on next screen"])
Run Code Online (Sandbox Code Playgroud)
wmctrl需要安装脚本
sudo apt-get install wmctrl
Run Code Online (Sandbox Code Playgroud)focus_track.py. 名称很重要,因为两个脚本共享功能;脚本 1 被导入到脚本 2 中。next_focus.py 在与脚本 1 相同的目录中。测试运行设置:注意,在打开(从而聚焦)窗口之前启动后台脚本。在后台脚本启动之前打开的窗口在聚焦之前不会被“记录”
使用以下命令启动后台脚本(例如从终端):
python3 /path/to/focus_track.py
Run Code Online (Sandbox Code Playgroud)在不同的屏幕上,打开窗口。
使用以下命令运行脚本 2:
python3 /path/to/next_focus.py
Run Code Online (Sandbox Code Playgroud)焦点应切换到下一个屏幕。如果当前屏幕是行中的最后一个屏幕,则焦点切换到第一个屏幕。
如果一切正常,请将脚本 1 添加到启动应用程序:Dash > 启动应用程序 > 添加命令:
python3 /path/to/focus_track.py
Run Code Online (Sandbox Code Playgroud)
并将脚本2添加到键盘快捷键:选择:系统设置>“键盘”>“快捷键”>“自定义快捷键”。单击“+”并添加命令:
python3 /path/to/next_focus.py
Run Code Online (Sandbox Code Playgroud)
到您喜欢的快捷键。
要将焦点从一个屏幕切换到另一个屏幕,必须确定哪个是每个屏幕最前面的窗口。然而,主要问题是分布在多个屏幕上的窗口实际上都在同一个堆栈中,因此按一个相同的顺序(z 方向)排序。我们拥有的工具(wmctrl、xdotool等xprop)在最好的情况下只能确定当前活动的窗口。它们没有向我们提供有关其他屏幕上的窗口顺序的任何信息,因为窗口位于活动窗口下方。
因此,乍一看,将焦点从一个屏幕切换到另一个屏幕似乎几乎是不可能的。
然而:
然而,通过一种解决方法,有一个逃避:如果我们让后台脚本跟踪当前聚焦的窗口,并维护更改的历史记录(只要窗口存在),我们实际上可以得出什么是 z-当前打开的窗口的顺序。如果我们还跟踪它们的几何形状和位置,我们就可以获得所需的所有信息。
举个例子:
我们当前有五个窗口:A、B、C、D、E。如果它们的焦点通过 D、E、A、C、B 变化,我们就知道窗口的 z 顺序是:B、C、A 、E、D(从前到后)
连同它们的位置(x 方向)和屏幕数据(屏幕的 x 分辨率),我们获得了所需的所有信息。要将焦点切换到下一个屏幕,我们只需查找位于下一个屏幕上的第一个窗口即可。