虽然乍一看似乎非常可行,但wmctrl一如既往,现实比理论复杂得多。
我很犹豫是否将此作为答案发布,因为它只是一个实验性的、概念性的答案,由于存在一些错误,还不是一个现成的解决方案。尽管如此,我仍然发布它,希望获得一些关于解决当前版本中的问题的意见。这个问题对于进一步开发(IMO)来说很有趣,看看是否可以创建一个顺利的解决方案。
虽然我用 编写了脚本Python,但该语言与我遇到的问题无关;主要与使用的特殊性有关wmctrl。
我可以用来xdotool定位窗口,但由于 OP 提到将一组窗口移动到另一个工作区,因此wmctrl具有一些优点,特别是在使用Gnome时,其中工作区的排列方式与 Unity 不同。
整体移动窗户
上面的屏幕截图中的示例是在 Unity 上制作的。然而,它应该以类似的方式工作Gnome(除了偏差之外,请参阅下面有关脚本的进一步内容)。
a。在截屏中,我将命令添加到(Unity)启动器中,Gnome可以使用快捷键来完成。r,则该脚本同样会移动该组中的所有窗口,从而相对于彼此恢复窗口:a,下面的脚本将当前活动窗口、其位置和大小(如 输出中的相应行所示wmctrl -lG)添加到文件wgroup_data.txt(在 中~)。r,脚本读取文件,查找更改位置的窗口,计算旧位置和新位置之间的向量,并相应地移动其他窗口。到目前为止没有问题。
但是,如果其中一个窗口未完全适合当前工作区的边框,则脚本将失败。实际上,该wmctrl命令失败了,因为它在脚本“外部”也失败了,作为单个命令运行。
#!/usr/bin/env python3
import subprocess
import os
import sys
arg = sys.argv[1]
# vertical deviation for Unity (use 0 for Gnome)
deviation = 28
fdata = os.environ["HOME"]+"/wgroup_data.txt"
def get_wmctrl():
# try because of buggy wmctrl...
try:
return subprocess.check_output(["wmctrl", "-lG"]).decode("utf-8")
except subprocess.CalledProcessError:
pass
def remove_window(window):
data = open(fdata).readlines()
[data.remove(l) for l in data if l.startswith(window)]
open(fdata, "wt").write(("").join(data))
def addwindow():
relevant = get_wmctrl()
frontmost = hex(int((subprocess.check_output(["xdotool", "getactivewindow"]).decode("utf-8").strip())))
frontmost = frontmost[:2]+str((10-len(frontmost))*"0")+frontmost[2:]
open(fdata, "+a").write([l+("\n") for l in get_wmctrl().splitlines() if frontmost in l][0])
print(frontmost)
def rearrange():
wlist = get_wmctrl()
if wlist != None:
group = [(l.strip(), l.split()) for l in open(fdata).read().splitlines() if not l in ("", "\n")]
try:
changed = [w for w in group if (w[0] in wlist, w[1][0] in wlist) == (False, True)][0] #
# only proceed if one of the grouped windows moved (give priority to a light loop if not):
follow = []
for w in group:
if not w == changed:
test = (w[0] in wlist, w[1][0] in wlist)
if test == (True, True):
follow.append(w)
elif test == (False, False):
# remove closed window from list
remove_window(w[1][0])
# only proceed if there are windows to move:
if follow:
# find match of the moved window (new coords)
wlines = wlist.splitlines()
match = [l.split() for l in wlines if changed[1][0] in l][0]
# calculate the move vector
x_move = int(match[2])-(int(changed[1][2])); y_move = int(match[3])-(int(changed[1][3]))
for w in follow:
# should be changed to try?
w[1][2] = str(int(w[1][2]) + x_move); w[1][3] = str(int(w[1][3]) + y_move - deviation)
subprocess.Popen([
"wmctrl", "-ir", w[1][0], "-e",
(",").join([w[1][1], w[1][2], w[1][3], w[1][4], w[1][5]])
])
# update grouplist
while True:
try:
newlines = sum([[l for l in get_wmctrl().splitlines() if w in l] for w in [match[0]]+[item[1][0] for item in follow]], [])
open(fdata, "wt").write(("\n").join(newlines))
break
except AttributeError:
pass
except IndexError:
print("nothing changed")
if arg == "a":
addwindow()
elif arg == "r":
rearrange()
Run Code Online (Sandbox Code Playgroud)
该脚本需要wmctrl和xdotool
sudo apt-get install xdotool wmctrl
Run Code Online (Sandbox Code Playgroud)将脚本复制到一个空文件中,另存为group_windows.py
如果您使用的是 Gnome:
在脚本的 head 部分中,更改以下行:
deviation = 28
Run Code Online (Sandbox Code Playgroud)
进入
deviation = 0
Run Code Online (Sandbox Code Playgroud)将两个命令添加到不同的快捷方式:
python3 /path/to/group_windows.py a
Run Code Online (Sandbox Code Playgroud)
将窗口添加到组中,以及
python3 /path/to/group_windows.py r
Run Code Online (Sandbox Code Playgroud)
重新排列窗口,如截屏所示
通过将一些窗口添加到组中来测试脚本,移动它们并恢复它们的相对位置,如屏幕截图所示。
这个问题可以从代码角度解决,只需在任何窗口预计离开当前工作区的情况下拒绝移动窗口即可。在这种情况下,即使刚刚移动的窗口也应该返回到其初始位置,以保持相对位置处于活动状态。
然而,这需要大量的计算(对计算机来说没有什么,但编码复杂),并且在当前工作空间之外进行部分定位可能会更优雅;当手动将窗口定位在“边缘上或边缘上”时,这是没有问题的。
任何有关解决该问题的建议都非常受欢迎。