¿Existe un script (o software) para abrir una ventana de aplicación en una ventana y posición específica?

8

Entonces, tengo 8 escritorios virtuales en Unity (con Compiz) porque tengo muchos proyectos en los que estoy trabajando simultáneamente.

El problema es que cada vez que necesito reiniciar o cerrar accidentalmente Chrome (que constituye una gran parte de las ventanas que necesito para trabajar) tengo que abrir manualmente esas ventanas nuevamente y luego configurarlas (abrir archivos, ir a la correcta urls, etc.).

¿Cómo harías para escribir un guión que haga todo eso por mí? Es decir: 1) Abra ventanas 2) Colóquelas en las coordenadas correctas en las pantallas virtuales correctas

(1) es obvio, para Google Chrome simplemente ejecutas 'google-chrome'. Pero entonces, ¿cómo lo pones en el lugar correcto? (2)

¿O hay un script / software que ya existe que lo haría por mí?

snitko
fuente
Puedo intentar crear una secuencia de comandos para colocar cualquier ventana que pueda necesitar en los escritorios apropiados al inicio, aunque podría llevarme algunos días, ya que tengo finales la próxima semana. Implicará wmctrl, que es como un software para controlar ventanas a través de script o terminal. Pero en cuanto al reinicio de una ventana, eso podría ser un poco más desafiante
Sergiy Kolodyazhnyy,
Aunque usted preguntó específicamente sobre Unity, vale la pena señalar que KDE tiene un programa (principalmente indocumentado) llamado kstart que hace esto por usted. Funciona mejor con los programas de KDE, pero también tiene cierto éxito con otros programas.
Joe

Respuestas:

14

Se puede hacer muy bien, pero necesita un poco de comprensión sobre Unity / viewports. Espero que la historia a continuación sea comprensible, si no, deje un comentario.

El siguiente script se puede usar para abrir una ventana de cualquier aplicación en cualquiera de sus ventanas gráficas, en cualquier posición, si la ejecuta con los argumentos correctos. El script es una versión editada de este , pero ahora está preparado para colocar ventanas en el escritorio virtual .

1. Comprender las ventanas gráficas y las coordenadas de la ventana

Espacios de trabajo en la unidad

En Unity, a diferencia de otros administradores de ventanas, en realidad solo tiene un área de trabajo que se divide, que se divide en ventanas gráficas. En su caso, su espacio de trabajo se divide en ocho ventanas gráficas.

Cómo se define la posición de las ventanas

La posición de la ventana, como la salida del comando:

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

se describe como la posición, en relación con la esquina superior izquierda de la ventana gráfica actual :


Entonces, si está en la ventana gráfica 1:
una ventana en la ventana gráfica 2 podría colocarse, por ejemplo, en 1700 (x-wise) x 500 (y-wise)
(mi pantalla es 1680x1050)

ingrese la descripción de la imagen aquí


Sin embargo, si está en la ventana gráfica 6:
la misma ventana se colocaría en 20 (x), -550 (y) ingrese la descripción de la imagen aquí


Usar estas coordenadas correctamente es importante para ejecutar el script con los argumentos correctos, como se describe a continuación:

2. Cómo usar el script

La secuencia de comandos a continuación se puede utilizar para colocar una nueva ventana de una aplicación en su espacio de trabajo virtual (de expansión).

  1. Asegúrese de que wmctrlesté instalado:

    sudo apt-get install wmctrl
    
  2. Copie el script a continuación en un archivo vacío, guárdelo como setwindow(sin extensión) en ~/bin. Cree el directorio si aún no existe. Haga que el script sea ejecutable .

  3. Si acaba de crear ~/bin, ejecute el comando source ~/.profileo cierre la sesión / inicie sesión para que el directorio esté disponible $PATH.
  4. Prueba ejecutar el comando:

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

    p.ej

    setwindow gedit 100 100 200 200
    

    Debería aparecer una ventana gedit en la ventana gráfica actual.

Notas:

  • Tenga en cuenta que no todas las aplicaciones permiten tamaños de ventana por debajo de cierto ancho o alto. El ancho mínimo de una geditventana en mi sistema es, por ejemplo, appr. 470 px.
  • La secuencia de comandos solo funciona bien si toda la ventana se ajusta a la vista objetivo, elija sus coordenadas / tamaños en consecuencia. También tenga en cuenta que Unity Launcher y el panel usan algo de espacio (!) Que puede influir en la posición de la ventana.
  • Use negativo <x_position>para colocar ventanas a la izquierda de las ventanas gráficas actuales
  • Use negativo <y_position>para colocar ventanas sobre las ventanas de visualización actuales
  • Para abrir nuevas ventanas en diferentes ventanas gráficas a la vez, simplemente puede encadenar comandos. Mirando la configuración de la ventana gráfica en el ejemplo "Larga historia", si estoy en la ventana gráfica 1, puedo abrir las ventanas gedit en la ventana gráfica 1, 2, 3 y 4 con el comando:

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

La secuencia de comandos

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



EDITAR: la versión perezosa

En caso de que prefiera ingresar solo las coordenadas y el tamaño, simplemente como si abriera una ventana en la vista actual, y diera la vista objetivo como argumento (sin tener que calcular nada), luego use la versión a continuación ...

Si lo configura como la primera versión del script, puede ejecutarlo con el comando:

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

Un ejemplo: para abrir una Google-Chromeventana posicionada en 20, 20, tamaño 300x300, en la ventana gráfica 5:

setwindow google-chrome 20 20 300 300 5

La configuración es más o menos la misma que la primera versión del script.
Tenga en cuenta que también este script solo funciona correctamente si la ventana definida (posición / tamaño) se ajusta completamente dentro de la ventana de visualización de destino.

La secuencia de comandos:

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


Abrir ventanas de aplicaciones con argumentos

Para finalizar el trabajo, responde tu pregunta por completo:

Si ejecuta el script como, por ejemplo:

setwindow google-chrome 20 20 300 300 5

abrirá una ventana predeterminada en los escritorios de destino.
Sin embargo, con la última versión del script, puede agregar un argumento adicional para abrir la ventana de la aplicación, por ejemplo, un url:

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

p.ej:

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

Si el argumento (extra) contiene espacios, use comillas. El ejemplo anterior abrirá una google-chromeventana en la ventana gráfica 3, abriendo el url http://askubuntu.com.

Puede encadenar comandos para abrir múltiples ventanas / URL en diferentes espacios de trabajo en un solo comando, por ejemplo:

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
fuente
@snitko Gracias por la buena pregunta, fue un gran desafío hacerlo :)
Jacob Vlijm
Noté que hay un ligero desplazamiento de la ventana cuando estoy usando el script. Entonces, por ejemplo, si abro en las coordenadas 0 0, en realidad lo abre un poco más hacia la derecha y hacia abajo (un desplazamiento de ~ 10px). Eso está bien, pero el problema es en realidad con el segundo script: el desplazamiento en el segundo script es extrañamente mayor en el eje horizontal. Creo que se trata de ~ 50 px a la izquierda. ¿Puedes ver por qué es eso? Establecer coordenadas negativas no ayuda en ese caso.
snitko
Otra pregunta: ¿cómo abro una ventana a pantalla completa?
snitko
Una actualización: el desplazamiento en el caso del segundo script parece ser el mismo que el ancho del dock de la unidad a la izquierda (aunque está oculto).
snitko
Para aquellos interesados, he implementado Desktopen: github.com/snitko/desktopen
snitko
1

Esto se expande en la gran respuesta de @Jacob Vlijim anterior con un setwindowscript ligeramente modificado :

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

Una descripción de los cambios:

  1. python3a python(solo una preferencia personal)
  2. sys.argva argparseuna interfaz de línea de comandos mejor
  3. análisis de ventana de ID de ventana estricta (y no ID de proceso)
    • algunos programas usan una única identificación de proceso para múltiples ventanas
  4. while bucle 0.5 segundos a 1 segundo de tiempo de sueño completo
  5. nombres de variables más detallados / legibles y adherencia pep8
  6. variables constantes globales para el tamaño de la pantalla en lugar de la xrandrdependencia

NOTA: Esta es solo una versión ligeramente mejorada que escribí para uso personal en Debian Jessie LXDE. Sus resultados pueden variar.

lscstu22
fuente
0

Para aquellos interesados, he implementado Desktopen: github.com/snitko/desktopen

Le permite escribir una secuencia de comandos para abrir ventanas en diferentes ventanas gráficas y pantallas de una manera muy amigable.

snitko
fuente