Esiste uno script (o un software) per aprire una finestra dell'applicazione su una finestra e una posizione specifiche?


8

Quindi, ho 8 desktop virtuali in Unity (con Compiz) perché ho molti progetti a cui sto lavorando contemporaneamente.

Il problema è che ogni volta che devo riavviare o chiudere accidentalmente Chrome (che costituisce gran parte delle finestre di cui ho bisogno per lavoro) devo aprire di nuovo manualmente quelle finestre e quindi impostarle (apri i file, vai al corretto URL ecc.).

Come faresti a scrivere una sceneggiatura che farà tutto questo per me? Cioè: 1) Apri Windows 2) Inseriscili nelle coordinate corrette su schermi virtuali corretti

(1) è ovvio, per Google Chrome esegui semplicemente "google-chrome". Ma allora come lo metti nel posto giusto? (2)

O esiste già uno script / software che lo farebbe per me?


Posso provare a creare uno script per posizionare qualsiasi finestra di cui potresti aver bisogno sui desktop appropriati all'avvio, ma potrebbero volerci alcuni giorni, dato che ho le finali in arrivo la prossima settimana. Coinvolgerà wmctrl, che è come un software per il controllo di Windows tramite script o terminale. Ma per quanto riguarda il riavvio di una finestra, questa potrebbe essere una sorta di sfida in più
Sergiy Kolodyazhnyy,

Sebbene tu abbia chiesto specificamente di Unity, vale la pena notare che KDE ha un programma (per lo più privo di documenti) chiamato kstart che fa questo per te. Funziona meglio con i programmi KDE, ma ha anche qualche successo con altri programmi.
Joe

Risposte:


14

Può essere fatto molto bene, ma è necessario un po 'di comprensione su Unity / viewport. Spero che la storia qui sotto sia comprensibile, in caso contrario, si prega di lasciare un commento.

Lo script seguente può essere utilizzato per aprire una finestra di qualsiasi applicazione su uno dei tuoi viewport, in qualsiasi posizione, se lo esegui con gli argomenti giusti. Lo script è una versione modificata di questo , ma ora è pronto per posizionare le finestre sul desktop virtuale di spanning .

1. Comprensione delle finestre e delle coordinate della finestra

Aree di lavoro in Unity

In Unity, a differenza di altri gestori di finestre, in realtà hai solo un'area di lavoro estesa, che è divisa in finestre. Nel tuo caso, l'area di lavoro è divisa in otto finestre.

Come viene definita la posizione delle finestre

La posizione della finestra, come output del comando:

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

è descritta come la posizione, relativa all'angolo in alto a sinistra della finestra corrente :


Quindi, se sei su viewport 1:
una finestra su viewport 2 potrebbe essere posizionata ad esempio su 1700 (x-saggio) x 500 (y-saggio)
(il mio schermo è 1680x1050)

inserisci qui la descrizione dell'immagine


Tuttavia, se sei su viewport 6:
la stessa finestra verrebbe posizionata su 20 (x), -550 (y) inserisci qui la descrizione dell'immagine


L'uso corretto di queste coordinate è importante per eseguire lo script con gli argomenti giusti, come descritto di seguito:

2. Come usare lo script

Lo script seguente può essere utilizzato per posizionare una nuova finestra di un'applicazione nell'area di lavoro virtuale (spanning).

  1. Assicurarsi che wmctrlsia installato:

    sudo apt-get install wmctrl
    
  2. Copia lo script seguente in un file vuoto, salvalo come setwindow(senza estensione) in ~/bin. Crea la directory se non esiste ancora. Rendi eseguibile lo script .

  3. Se hai appena creato ~/bin, esegui il comando source ~/.profileo esci / accedi per rendere disponibile la directory $PATH.
  4. Test eseguire il comando:

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

    per esempio

    setwindow gedit 100 100 200 200
    

    Una finestra gedit dovrebbe apparire sulla finestra corrente.

Appunti:

  • Tieni presente che non tutte le applicazioni consentono dimensioni di finestre inferiori a una determinata larghezza o altezza. La larghezza minima di una geditfinestra sul mio sistema è ad es. Appr. 470 px.
  • Lo script funziona bene solo se l'intera finestra si adatta alla finestra di destinazione, scegli le coordinate / dimensioni di conseguenza. Ricorda inoltre che Unity Launcher e il pannello utilizzano dello spazio (!) Che può influenzare la posizione della finestra.
  • Usa negativo <x_position>per posizionare le finestre a sinistra delle finestre correnti
  • Usa negativo <y_position>per posizionare le finestre sopra le finestre correnti
  • Per aprire contemporaneamente nuove finestre su finestre diverse, puoi semplicemente concatenare i comandi. Osservando la configurazione della finestra nell'esempio "Storia lunga", Se sono su Viewport 1, posso aprire Windows gedit su Viewport 1, 2, 3 e 4 con il 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
    

Il copione

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

Nel caso in cui preferissi inserire solo coordinate e dimensioni, semplicemente come se aprissi una finestra sulla finestra corrente e fornissi la finestra di destinazione come argomento (senza dover calcolare nulla), quindi utilizza la versione seguente ...

Se lo configuri come la prima versione dello script, puoi eseguirlo con il comando:

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

Un esempio: per aprire una Google-Chromefinestra posizionata su 20, 20, dimensioni 300x300, su viewport 5:

setwindow google-chrome 20 20 300 300 5

L'installazione è praticamente la stessa della prima versione dello script.
Si noti che anche questo script funziona correttamente solo se la finestra (posizione / dimensione) definita si adatta perfettamente alla vista di destinazione.

Il copione:

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


Apertura di finestre dell'applicazione con argomenti

Per finire il lavoro, rispondendo completamente alla tua domanda:

Se esegui lo script come ad esempio:

setwindow google-chrome 20 20 300 300 5

si aprirà una finestra predefinita sui desktop target.
Con l'ultima versione dello script, tuttavia, è possibile aggiungere un argomento aggiuntivo per aprire la finestra dell'applicazione, ad esempio un url:

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

per esempio:

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

Se l'argomento (extra) contiene spazi, utilizzare le virgolette. L'esempio sopra aprirà una google-chromefinestra su viewport 3, aprendo il url http://askubuntu.com.

È possibile concatenare i comandi per aprire più finestre / URL su diverse aree di lavoro in un solo comando, ad esempio:

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"

@snitko Grazie per la bella domanda, è stata una bella sfida farlo :)
Jacob Vlijm,

Ho notato che c'è un leggero offset per la finestra quando sto usando lo script. Quindi, ad esempio, se apro alle coordinate 0 0, in realtà lo apre leggermente più a destra e in basso (un offset di ~ 10px). Va bene, ma il problema è in realtà con il secondo script: l'offset nel secondo script è stranamente più grande sull'asse orizzontale. Penso che sia circa ~ 50px a sinistra. Riesci a vedere perché? L'impostazione di coordinate negative non aiuta in questo caso.
Snitko,

Un'altra domanda: come faccio ad aprire una finestra a schermo intero?
Snitko,

Un aggiornamento: l'offset nel caso del secondo script sembra essere lo stesso della larghezza del dock unitario a sinistra (sebbene sia nascosto).
Snitko,

Per gli interessati, ho implementato Desktopen: github.com/snitko/desktopen
snitko

1

Questo si espande sulla grande risposta di @Jacob Vlijim sopra con uno setwindowscript leggermente modificato :

#!/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 descrizione delle modifiche:

  1. python3a python(solo una preferenza personale)
  2. sys.argvad argparseun CLI meglio
  3. analisi della finestra id finestra rigorosa (e non ID processo)
    • alcuni programmi utilizzano un singolo ID processo per più finestre
  4. while ciclo da 0,5 secondi a 1 secondo di tempo di sospensione completo
  5. nomi di variabili più dettagliati / leggibili e aderenza pep8
  6. variabili globali costanti per le dimensioni dello schermo anziché per l' xrandraffidabilità

NOTA: questa è solo una versione leggermente migliorata che ho scritto per uso personale su Debian Jessie LXDE. I risultati possono variare.


0

Per gli interessati, ho implementato Desktopen: github.com/snitko/desktopen

Ti permette di scrivere uno script per aprire finestre su diversi viewport e display in un modo molto amichevole.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.