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

8

Donc, j'ai 8 bureaux virtuels dans Unity (avec Compiz) parce que j'ai de nombreux projets sur lesquels je travaille simultanément.

Le problème est que, chaque fois que je dois redémarrer ou fermer accidentellement Chrome (ce 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 les fichiers, aller dans la bonne URL, etc.).

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

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

Ou existe-t-il déjà un script / logiciel qui le ferait pour moi?

snitko
la source
Je peux essayer de concocter un script pour placer toutes les fenêtres dont vous pourriez avoir besoin sur les bureaux appropriés au démarrage, cela pourrait me prendre quelques jours, car j'ai des finales à venir la semaine prochaine. Cela impliquera wmctrl, qui est comme un logiciel pour contrôler les fenêtres via un script ou un terminal. Mais comme pour le redémarrage d'une fenêtre, cela pourrait être un peu plus difficile
Sergiy Kolodyazhnyy
Bien que vous ayez spécifiquement posé des questions sur Unity, il convient de noter que KDE a un programme (principalement non documenté) appelé kstart qui le fait pour vous. Il fonctionne mieux avec les programmes KDE, mais a également un certain succès avec d'autres programmes.
Joe

Réponses:

14

Cela peut être très bien fait, mais vous avez besoin de comprendre Unity / viewports. J'espère que l'histoire ci-dessous est compréhensible, sinon, veuillez laisser 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, à n'importe quelle position, si vous l'exécutez avec les bons arguments. Le script est une version modifiée de celui-ci , mais maintenant prêt à placer des fenêtres sur le bureau virtuel couvrant .

1. Comprendre les fenêtres et les coordonnées des fenêtres

Espaces de travail dans Unity

Dans Unity, contrairement à d'autres gestionnaires de fenêtres, vous n'avez en fait qu'un seul espace de travail s'étendant, qui est 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 sortie de la commande:

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

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


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

entrez la description de l'image ici


Cependant, si vous êtes sur la fenêtre 6:
la même fenêtre serait positionnée sur 20 (x), -550 (y) entrez la description de l'image ici


Il est important d'utiliser correctement ces coordonnées 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 (s'étendant).

  1. Assurez-vous qu'il wmctrlest installé:

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

  3. Si vous venez de créer ~/bin, exécutez la commande source ~/.profileou déconnectez-vous pour rendre le répertoire disponible dans $PATH.
  4. Testez 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.

Remarques:

  • Gardez à l'esprit que toutes les applications ne permettent pas des tailles de fenêtre inférieures à une certaine largeur ou hauteur. La largeur minimale d'une geditfenêtre sur mon système est par exemple env. 470 px.
  • Le script ne fonctionne correctement que si toute la fenêtre tient dans la fenêtre cible, choisissez vos coordonnées / tailles en conséquence. Gardez également à l'esprit que le lanceur Unity et le panneau utilisent un espace (!) Qui peut influencer la position de la fenêtre.
  • Utilisez un négatif <x_position>pour placer les fenêtres à gauche de la ou des fenêtres actuelles
  • Utilisez un négatif <y_position>pour placer les fenêtres au-dessus des fenêtres actuelles
  • Pour ouvrir simultanément de nouvelles fenêtres sur différentes fenêtres, vous pouvez simplement enchaîner les commandes. En regardant la configuration de la fenêtre dans l'exemple "Longue histoire", si je suis sur la fenêtre 1, je peux ouvrir des fenêtres gedit sur 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

Dans le cas où vous préférez simplement entrer les coordonnées et la taille, tout comme si vous ouvriez une fenêtre sur la fenêtre actuelle et donniez la fenêtre cible comme argument (sans avoir à calculer quoi que ce soit), utilisez la version ci-dessous ...

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

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

Un exemple: pour ouvrir une Google-Chromefenêtre positionnée sur 20, 20, taille 300x300, sur fenêtre 5:

setwindow google-chrome 20 20 300 300 5

La configuration est à peu près la même que la première version du script.
Notez également que ce script ne fonctionne correctement que si la fenêtre définie (position / taille) s'intègre complètement dans la fenêtre cible.

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, en répondant 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 (s) bureau (s) ciblé (s).
Cependant, avec la dernière version du script, vous pouvez ajouter un argument supplémentaire pour ouvrir la fenêtre de l'application, par exemple 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 google-chromefenêtre dans la fenêtre 3, ouvrant le url http://askubuntu.com.

Vous pouvez chaîner des commandes pour ouvrir plusieurs fenêtres / URL sur différents espaces de travail en une seule commande, 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"
Jacob Vlijm
la source
@snitko Merci pour la belle question, c'était un beau défi de le faire :)
Jacob Vlijm
J'ai remarqué qu'il y a un léger décalage pour la fenêtre lorsque j'utilise le script. Ainsi, par exemple, si j'ouvre aux coordonnées 0 0, cela l'ouvre en fait un peu plus vers la droite et vers le bas (un décalage de ~ 10px). C'est bien, mais le problème est en fait avec le deuxième script: le décalage dans le deuxième script est étrangement plus grand sur l'axe horizontal. Je pense que c'est environ 50 pixels à gauche. Pouvez-vous voir pourquoi est-ce? La définition de coordonnées négatives n'aide pas dans ce cas.
snitko
Une autre question: comment ouvrir une fenêtre en plein écran?
snitko
Une mise à jour: le décalage dans le cas du deuxième script semble être le même que la largeur du dock d'unité à gauche (bien qu'il soit masqué).
snitko
Pour ceux qui sont intéressés, j'ai implémenté Desktopen: github.com/snitko/desktopen
snitko
1

Cela se développe sur la grande réponse de @Jacob Vlijim ci-dessus avec un setwindowscript légèrement modifié :

#!/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 __name__ == '__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)

Une description des changements:

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

NOTE: Ceci est juste 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.

lscstu22
la source
0

Pour ceux qui sont intéressés, 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 affichages d'une manière très conviviale.

snitko
la source