web-dev-qa-db-fra.com

«Groupement» de fenêtres?

Je me demandais simplement, est-il possible de "grouper" les fenêtres? Je veux dire, existe-t-il un moyen de joindre les bords de deux ou plusieurs fenêtres de sorte que lorsque l'une est déplacée, l'autre se déplace avec elle, agissant comme une grande fenêtre? Ou au moins quelque chose de similaire où le déplacement d'une fenêtre déplace l'autre de la même manière? J'utilise Ubuntu GNOME 15.10 avec GNOME 3.18.

3
user364819

Réponse inachevée; recherche d'entrée

Bien qu'à première vue cela semble très bien faisable, en utilisant wmctrl, comme toujours, la réalité est (beaucoup) plus compliquée que la théorie.

J'hésite à poster ceci comme une réponse, car ce n'est qu'une réponse expérimentale, conceptuelle, pas (encore) une solution prête à l'emploi à cause de quelques bugs. Je le poste néanmoins dans l'espoir d'obtenir des informations sur la résolution des problèmes dans la version actuelle. La question est suffisamment intéressante pour un développement ultérieur (OMI) pour voir si une solution fluide peut être créée.

Langue: aucune

Bien que j'aie écrit le script dans Python, la langue n'est pas pertinente pour les problèmes que je rencontre; principalement lié aux particularités de l'utilisation de wmctrl.

I pourrait utiliser xdotool pour positionner les fenêtres, mais comme OP mentionne le déplacement d'un ensemble de fenêtres vers un autre espace de travail, wmctrl présente certains avantages, en particulier en utilisant Gnome , où les espaces de travail sont organisés différemment de Unity.

Un exemple de fonctionnement actuel, déplacement d'un groupe de fenêtres

enter image description here

déplacer des fenêtres en groupe

L'exemple de l'écran ci-dessus a été réalisé sur Unity. Il devrait cependant fonctionner de la même manière sur Gnome (à part l'écart, voir plus loin à propos du script).

  • Dans la configuration actuelle, un groupe de fenêtres peut être créé en ajoutant la fenêtre la plus en avant au groupe en appelant le script ci-dessous avec l'argument: a. Dans la distribution d'écran, j'ai ajouté la commande à un lanceur (Unity), sur Gnome, cela pourrait être fait avec une touche de raccourci.
  • Par la suite, si le script est appelé avec l'argument r après le déplacement de l'une des fenêtres groupées, le script déplace toutes les fenêtres du groupe de la même manière, en restaurant les fenêtres les unes par rapport aux autres:

Comment c'est fait

  • Exécutez avec l'option a, le script ci-dessous ajoute la fenêtre actuellement active, sa position et sa taille (comme dans la ligne correspondante dans la sortie de wmctrl -lG), dans un fichier, wgroup_data.txt (dans ~).
  • Exécutez avec l'option r, le script lit le fichier, recherche la fenêtre qui a changé de position, calcule le vecteur entre l'ancienne et la nouvelle position et déplace les autres fenêtres en conséquence.
  • Si une fenêtre est fermée, elle est automatiquement supprimée de la liste des groupes par le script.

Jusqu'à présent aucun problème.

Problèmes

Cependant, si l'une des fenêtres ne correspond pas complètement à l'intérieur des bordures de l'espace de travail actuel, le script échoue. En fait, la commande wmctrl échoue, car elle échoue également "en dehors" du script, exécuté comme une seule commande.

Le script

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

Comment utiliser

  1. Le script nécessite à la fois wmctrl et xdotool

    Sudo apt-get install xdotool wmctrl
    
  2. Copiez le script dans un fichier vide, enregistrez-le sous group_windows.py

  3. Si vous êtes sur Gnome:
    Dans la section d'en-tête du script, changez la ligne:

    deviation = 28
    

    dans

    deviation = 0 
    
  4. Ajoutez deux commandes à différents raccourcis:

    python3 /path/to/group_windows.py a
    

    pour ajouter des fenêtres à un groupe, et

    python3 /path/to/group_windows.py r
    

    pour réorganiser les fenêtres, comme indiqué dans la distribution d'écran

  5. Testez le script en ajoutant des fenêtres à un groupe, déplacez-les et restaurez leur position relative, comme indiqué dans la distribution d'écran.

La poursuite du développement

Le problème pourrait être résolu, au niveau du code, en refusant simplement de déplacer les fenêtres au cas où l'une des fenêtres devrait sortir de l'espace de travail actuel. Dans ce cas, même la fenêtre qui vient d'être déplacée doit être remise à sa position initiale pour conserver les positions relatives.

Cela nécessiterait cependant un calcul approfondi (rien pour l'ordinateur, mais compliqué à coder), et il serait plus élégant de rendre possible un positionnement partiel en dehors de l'espace de travail actuel; ce n'est pas un problème lorsqu'une fenêtre est positionnée "sur ou sur le bord" manuellement.
Toute suggestion sur la résolution du problème est plus que bienvenue.

1
Jacob Vlijm