![窗口‘分组’?](https://linux22.com/image/984376/%E7%AA%97%E5%8F%A3%E2%80%98%E5%88%86%E7%BB%84%E2%80%99%EF%BC%9F.png)
我只是想知道,有没有办法“分组”窗口?我的意思是,有没有办法将两个或多个窗口的边缘连接在一起,这样当一个窗口移动时,另一个窗口也会随之移动,就像一个大窗口一样?或者至少有类似的方法,移动一个窗口会以相同的方式移动另一个窗口?我正在运行带有 GNOME 3.18 的 Ubuntu GNOME 15.10。
答案1
答案未完成;寻求意见
尽管乍一看这似乎完全可行,但使用起来wmctrl
,现实总是比理论复杂得多。
我犹豫着是否把这个作为答案发布出来,因为这只是一个实验性的,概念性的答案,由于一些错误,目前还不是一个现成的解决方案。尽管如此,我还是发布了它,希望得到一些关于解决当前版本问题的意见。这个问题很有趣,值得进一步开发(在我看来),看看是否可以创建一个顺利的解决方案。
语言:无
虽然我用 编写了脚本Python
,但该语言与我遇到的问题无关;主要与 的使用特殊性有关wmctrl
。
我可以用于xdotool
定位窗口,但由于 OP 提到将一组窗口移动到另一个工作区,因此wmctrl
有一些优势,特别是在使用侏儒,其中工作空间的排列与 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()
如何使用
脚本
wmctrl
需要xdotool
sudo apt-get install xdotool wmctrl
将脚本复制到一个空文件中,另存为
group_windows.py
如果你使用的是 Gnome:
在脚本的头部部分,更改以下行:deviation = 28
进入
deviation = 0
将两个命令添加到不同的快捷方式:
python3 /path/to/group_windows.py a
将窗口添加到组,以及
python3 /path/to/group_windows.py r
重新排列窗口,如屏幕截图所示
通过将一些窗口添加到一个组、移动它们并恢复它们的相对位置来测试脚本,如屏幕截图所示。
进一步的发展
这个问题可以通过代码来解决,只要拒绝移动窗口,以防任何窗口需要离开当前工作区即可。在这种情况下,即使刚刚移动的窗口也应该返回到其初始位置,以保持相对位置有效。
然而,这需要大量计算(对计算机来说不算什么,但编码起来很复杂),如果能将部分定位在当前工作区之外,那就更好了;当窗口手动定位在“边缘上或边缘之外”时,这没有问题。
欢迎提出任何解决此问题的建议。