web-dev-qa-db-fra.com

Existe-t-il un script (ou un logiciel) pour ouvrir une fenêtre d'application sur une fenêtre et une position spécifiques?

J'ai donc 8 bureaux virtuels dans Unity (avec Compiz) car j'ai plusieurs projets sur lesquels je travaille simultanément.

Le problème, c’est que chaque fois que je dois redémarrer ou fermer accidentellement Chrome (qui constitue une grande partie des fenêtres dont j’ai besoin pour travailler), je dois manuellement ouvrir à nouveau ces fenêtres, puis les configurer (ouvrir fichiers, allez aux bonnes URL, etc.).

Comment feriez-vous pour écrire un script qui fera tout cela pour moi? C'est-à-dire que: 1) ouvrez les fenêtres 2) placez-les dans les bonnes coordonnées sur les écrans virtuels appropriés

(1) est évident, pour Google Chrome vous exécutez simplement "google-chrome". Mais alors comment le mettez-vous au bon endroit? (2)

Ou existe-t-il un script/logiciel existant qui le ferait pour moi?

8
snitko

Cela peut être très bien fait, mais vous avez besoin d’un peu de compréhension sur Unity/viewports. J'espère que l'histoire ci-dessous est compréhensible, sinon laissez un commentaire.

Le script ci-dessous peut être utilisé pour ouvrir une fenêtre de n'importe quelle application sur n'importe laquelle de vos fenêtres, sur n'importe quelle position, si vous l'exécutez avec les bons arguments. Le script est une version modifiée de celui-ci , mais il est maintenant prêt à placer des fenêtres sur le bureau virtuel .

1. Comprendre les fenêtres et les coordonnées de la fenêtre

Espaces de travail en unité

Dans Unity, contrairement aux autres gestionnaires de fenêtres, vous n’avez en réalité qu’un seul espace de travail couvrant, divisé en fenêtres. Dans votre cas, votre espace de travail est divisé en huit fenêtres.

Comment la position des fenêtres est définie

La position de la fenêtre, en tant que sortie de la commande:

wmctrl -lG
(you need to have wmctrl installed to run the command)

est décrit comme la position, relative par rapport au coin supérieur gauche de la fenêtre actuelle :


Donc, si vous êtes dans la fenêtre 1:
une fenêtre sur la fenêtre 2 pourrait être positionnée par ex. 1700 (x-sage) x 500 (y-sage)
(mon écran est 1680x1050)

enter image description here


Cependant, si vous êtes dans la fenêtre 6:
la même fenêtre serait positionnée sur 20 (x), -550 (y) enter image description here


Utiliser ces coordonnées correctement est important pour exécuter le script avec les bons arguments, comme décrit ci-dessous:

2. Comment utiliser le script

Le script ci-dessous peut être utilisé pour placer une nouvelle fenêtre d’une application sur votre espace de travail virtuel (étendu).

  1. Assurez-vous que wmctrl est installé:

    Sudo apt-get install wmctrl
    
  2. Copiez le script ci-dessous dans un fichier vide, enregistrez-le sous le nom setwindow (sans extension) dans ~/bin. Créez le répertoire s'il n'existe pas encore. Rend le script exécutable .

  3. Si vous venez de créer ~/bin, exécutez la commande source ~/.profile ou déconnectez-vous/ouvrez pour rendre le répertoire disponible dans $PATH.
  4. Test lance la commande:

    setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size>
    

    par exemple.

    setwindow gedit 100 100 200 200
    

    Une fenêtre gedit devrait apparaître dans la fenêtre courante.

Notes:

  • N'oubliez pas que toutes les applications n'autorisent pas les tailles de fenêtre inférieures à une certaine largeur ou hauteur. La largeur minimale d'une fenêtre gedit sur mon système est par exemple. env. 470 px.
  • Le script ne fonctionne correctement que si l'intégralité de la fenêtre s'adapte à la fenêtre cible, choisissez vos coordonnées/tailles en conséquence. Notez également que Leity Launcher et le panneau utilisent un espace (!) Qui peut influer sur la position de la fenêtre.
  • Utilisez la valeur négative <x_position> pour placer les fenêtres à gauche de la ou des fenêtres actuelles.
  • Utilisez le paramètre négatif <y_position> pour placer les fenêtres au-dessus de la ou des fenêtres actuelles.
  • Pour ouvrir simultanément de nouvelles fenêtres sur différentes fenêtres, vous pouvez simplement chaîner des commandes. En regardant la configuration de la fenêtre dans l'exemple "Longue histoire", si je suis dans la fenêtre 1, je peux ouvrir les fenêtres gedit dans les fenêtres 1, 2, 3 et 4 avec la commande:

    setwindow gedit 100 100 200 200&&setwindow gedit 1780 100 200 200&&setwindow gedit 3460 100 200 200&&setwindow gedit 5140 100 200 200
    

Le scénario

#!/usr/bin/env python3
import subprocess
import time
import sys

app = sys.argv[1]

get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
subprocess.Popen(["/bin/bash", "-c", app])
# fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
app = "chrome" if "chrome" in app else app
while t < 30:      
    ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
    procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
              if app in p and w[2] in p] for w in ws2]
    if len(procs) > 0:
        w_id = procs[0][0][1]
        cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
        cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
        cmd3 = "wmctrl -ir "+procs[0][0][1]+" -e 0,"+sys.argv[2]+","+sys.argv[3]+","+sys.argv[4]+","+sys.argv[5]
        for cmd in [cmd1, cmd2, cmd3]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1



EDIT: la version paresseuse

Si vous préférez simplement entrer les coordonnées et la taille, comme si vous ouvriez une fenêtre sur la fenêtre d'affichage actuelle et donniez la fenêtre ciblée sous forme d'argument (sans rien calculer), utilisez la version ci-dessous ...

Si vous le configurez comme la première version du script, vous pouvez l'exécuter avec la commande suivante:

setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport>

Exemple: pour ouvrir une fenêtre Google-Chrome positionnée sur 20, 20, taille 300x300, dans la fenêtre 5:

setwindow google-chrome 20 20 300 300 5

La configuration est à peu près la même que celle de la première version du script.
Notez également que ce script ne fonctionne correctement que si la fenêtre définie (position/taille) s’adapte parfaitement à la fenêtre de visualisation ciblée.

Le scénario:

#!/usr/bin/env python3
import subprocess
import time
import sys

app = sys.argv[1]
target_vp = int(sys.argv[6])

def get_res():
    # get resolution
    xr = subprocess.check_output(["xrandr"]).decode("utf-8").split()
    pos = xr.index("current")
    return [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]

res = get_res()

def current(set_vp):
    # get the current viewport
    vp_data = subprocess.check_output(
        ["wmctrl", "-d"]
        ).decode("utf-8").split()
    dt = [int(n) for n in vp_data[3].split("x")]
    cols = int(dt[0]/res[0])
    rows = int(dt[1]/res[1])    
    curr_vpdata = [int(n) for n in vp_data[5].split(",")]
    curr_col = int(curr_vpdata[0]/res[0])
    curr_row = int(curr_vpdata[1]/res[1])
    curr_vp = curr_col+curr_row*cols+1
    # calculate the vector to the Origin from the current viewport (in resolution units)
    vec_curr = vector(curr_vp, cols)
    # calculate the vector to the Origin from the targeted viewport
    vec_set = vector(set_vp, cols)
    # calculate the vector between current and targeted viewport
    vec_relative = [vec_set[0] - vec_curr[0],
                    vec_set[1] - vec_curr[1]]
    # calculate needed correction (absolute)
    relative = [vec_relative[0]*res[0],
                vec_relative[1]*res[1]]
    return relative

def vector(vp, cols):
    rem = vp%cols
    vec_x = rem-1 if rem != 0 else cols-1
    vec_y = int((vp-1)/cols)
    return [vec_x, vec_y]

res = get_res() # nieuw
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
# check for additional arguments to run the application
try:
    subprocess.Popen(["/bin/bash", "-c", app+" "+sys.argv[7]])  
except IndexError:
    subprocess.Popen(["/bin/bash", "-c", app])

# fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
app = "chrome" if "chrome" in app else app
while t < 30:      
    ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
    procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
              if app in p and w[2] in p] for w in ws2]
    if len(procs) > 0:
        w_id = procs[0][0][1]
        cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
        cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
        # calculate the correction, related to the current workspace, marge for launcher and panel
        pos_x = int(sys.argv[2]); pos_y = int(sys.argv[3]); x_marge = 65; y_marge = 35
        pos_x = pos_x if pos_x > x_marge else x_marge; pos_y = pos_y if pos_y > y_marge else y_marge
        x_relative = pos_x+current(target_vp)[0]
        y_relative = pos_y+current(target_vp)[1]
        # correct possible inaccurately set width / height
        x_size = res[0]; y_size = res[1]
        set_width = int(sys.argv[4]); set_height = int(sys.argv[5])
        width = set_width if set_width+x_marge+pos_x < x_size else x_size - pos_x - x_marge
        height = set_height if set_height+y_marge+pos_y < y_size else y_size - pos_y - y_marge
        cmd3 = "wmctrl -ir "+w_id+" -e 0,"+str(x_relative)+","+str(y_relative)+","+str(width)+","+str(height)
        for cmd in [cmd1, cmd2, cmd3]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1


Ouverture de fenêtres d'application avec des arguments

Pour terminer le travail, répondez complètement à votre question:

Si vous exécutez le script comme par exemple:

setwindow google-chrome 20 20 300 300 5

il ouvrira une fenêtre par défaut sur le ou les bureaux ciblés.
Cependant, avec la dernière version du script, vous pouvez ajouter un additionnel argument pour ouvrir la fenêtre de l'application, par exemple un url:

setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport> <(optional)_argument>

par exemple.:

setwindow google-chrome 0 0 600 600 3 "--new-window http://askubuntu.com"

Si l'argument (extra) contient des espaces, utilisez des guillemets. L'exemple ci-dessus ouvrira une fenêtre google-chrome sur la fenêtre d'affichage 3, ouvrant la urlhttp://askubuntu.com.

Vous pouvez chaîner des commandes pour ouvrir plusieurs fenêtres/urls sur différents espaces de travail en une seule, par exemple:

setwindow google-chrome 0 0 600 600 8 "--new-window http://askubuntu.com"&&setwindow google-chrome 0 0 600 600 7 "--new-window www.google.com"
14
Jacob Vlijm

Ceci s’étend sur la réponse de @ @ @ @ Jacob Vlijim ci-dessus avec un script légèrement modifié setwindow:

#!/usr/bin/env python

import time
import argparse
import subprocess

DEFAULT_WIDTH = '1920'
DEFAULT_HEIGHT = '1080'


def get_window_list():
    window_list = subprocess.check_output(['/bin/bash', '-c', 'wmctrl -l'])
    parsed_list = []
    for line in window_list.splitlines():
        window_info = line.split()
        if window_info[1] != '-1':
            parsed_list.append(window_info[0])
    return parsed_list


def main(params):
    old_list = get_window_list()
    subprocess.Popen(['/bin/bash', '-c', params.command])

    def get_diff(old):
        new_list = get_window_list()
        return list(set(new_list) - set(old))

    diff = get_diff(old_list)
    x = 0
    while not diff:
        if x == 10:
            print 'window not found'
            return
        x += 1
        diff = get_diff(old_list)
        time.sleep(1)
    if len(diff) > 1:
        raise Exception(diff)
    window_id = diff[0]
    command_list = []
    command_list.append('wmctrl -ir %s -t %s' % (window_id, params.desktop))
    command_list.append('wmctrl -ir %s -b remove,maximized_horz,maximized_vert'
        % window_id)
    command_list.append('wmctrl -ir %s -e 0,%s,%s,%s,%s' %
        (window_id, params.x_pos, params.y_pos, params.width, params.height))
    for command in command_list:
        subprocess.call(['/bin/bash', '-c', command])

if __== '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('command', type=str)
    parser.add_argument('-d', '--desktop', default='0', type=str)
    parser.add_argument('-x', '--x-pos', default='0', type=str)
    parser.add_argument('-y', '--y-pos', default='0', type=str)
    parser.add_argument('-w', '--width', default=DEFAULT_WIDTH, type=str)
    parser.add_argument('-t', '--height', default=DEFAULT_HEIGHT, type=str)
    args = parser.parse_args()
    main(args)

Description des modifications:

  1. python3 à python (juste une préférence personnelle)
  2. sys.argv à argparse pour une meilleure interface en ligne de commande
  3. analyse de fenêtre stricte (et non de processus) fenêtre en cours d'analyse
    • certains programmes utilisent un identifiant de processus unique pour plusieurs fenêtres
  4. while boucle de 0,5 seconde à 1 seconde de sommeil complet
  5. noms de variable plus verbeux/lisibles et adhérence pep8
  6. variables constantes globales pour la taille de l'écran au lieu de xrandr confiance

NOTE: Ceci est une version légèrement améliorée que j'ai écrite pour un usage personnel sur Debian Jessie LXDE. Vos résultats peuvent varier.

1
lscstu22

Pour ceux que ça intéresse, j'ai implémenté Desktopen: github.com/snitko/desktopen

Il vous permet d'écrire un script pour ouvrir des fenêtres sur différentes fenêtres et de manière très conviviale.

0
snitko