窗口“分组”?

5 gnome window software-recommendation xdotool wmctrl

我只是想知道,有没有办法对窗口进行“分组”?我的意思是,是否有一种方法可以将两个或多个窗口的边缘连接在一起,以便当一个窗口移动时,另一个窗口也随之移动,充当一个大窗口?或者至少是类似的东西,移动一个窗口会以相同的方式移动另一个窗口?我正在运行 Ubuntu GNOME 15.10 和 GNOME 3.18。

Jac*_*ijm 1

未完成的答案;寻找输入

虽然乍一看似乎非常可行,但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)

如何使用

  1. 该脚本需要wmctrlxdotool

    sudo apt-get install xdotool wmctrl
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将脚本复制到一个空文件中,另存为group_windows.py

  3. 如果您使用的是 Gnome:
    在脚本的 head 部分中,更改以下行:

    deviation = 28
    
    Run Code Online (Sandbox Code Playgroud)

    进入

    deviation = 0 
    
    Run Code Online (Sandbox Code Playgroud)
  4. 将两个命令添加到不同的快捷方式:

    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)

    重新排列窗口,如截屏所示

  5. 通过将一些窗口添加到组中来测试脚本,移动它们并恢复它们的相对位置,如屏幕截图所示。

进一步的发展

这个问题可以从代码角度解决,只需在任何窗口预计离开当前工作区的情况下拒绝移动窗口即可。在这种情况下,即使刚刚移动的窗口也应该返回到其初始位置,以保持相对位置处于活动状态。

然而,这需要大量的计算(对计算机来说没有什么,但编码复杂),并且在当前工作空间之外进行部分定位可能会更优雅;当手动将窗口定位在“边缘上或边缘上”时,这是没有问题的。
任何有关解决该问题的建议都非常受欢迎。