Existe um script (ou software) para abrir uma janela do aplicativo em uma viewport e posição específica?

8

Portanto, tenho 8 desktops virtuais no Unity (com o Compiz) porque tenho muitos projetos nos quais estou trabalhando simultaneamente.

O problema é que toda vez que preciso reiniciar ou fechar acidentalmente o Chrome (que constitui uma grande parte das janelas necessárias para o trabalho), preciso abrir manualmente essas janelas novamente e configurá-las (abrir arquivos, vá para a correta URLs etc.).

Como você escreveria um roteiro que fará tudo isso por mim? Ou seja: 1) Janelas abertas 2) Coloque-as nas coordenadas corretas nas telas virtuais corretas

(1) é óbvio, para o Google Chrome você acabou de executar o 'google-chrome'. Mas então, como você o coloca no lugar certo? 2)

Ou já existe um script / software que faria isso por mim?

snitko
fonte
Eu posso tentar inventar um script para colocar as janelas necessárias em áreas de trabalho apropriadas na inicialização, mas pode demorar alguns dias, já que tenho finais chegando na próxima semana. Envolverá wmctrl, que é como um software para controlar janelas através de script ou terminal. Mas, como para o reinício de uma janela, que pode ser algo de um pouco mais de desafio
Sergiy Kolodyazhnyy
Embora você tenha perguntado especificamente sobre o Unity, é importante notar que o KDE possui um programa (na maioria não documentado) chamado kstart que faz isso por você. Funciona melhor com os programas do KDE, mas também tem algum sucesso com outros programas.
Joe

Respostas:

14

Isso pode ser feito muito bem, mas você precisa de algum entendimento sobre Unity / viewports. Espero que a história abaixo seja compreensível, se não, por favor, deixe um comentário.

O script abaixo pode ser usado para abrir uma janela de qualquer aplicativo em qualquer uma das suas viewports, em qualquer posição, se você a executar com os argumentos corretos. O script é uma versão editada deste , mas agora preparado para colocar janelas na área de trabalho virtual de abrangência .

1. Compreendendo viewports e coordenadas de janela

Áreas de trabalho no Unity

No Unity, diferentemente de outros gerenciadores de janelas, na verdade você só possui um espaço de trabalho de abrangência, que é dividido em viewports. No seu caso, seu espaço de trabalho é dividido em oito viewports.

Como a posição das janelas é definida

A posição da janela, como a saída do comando:

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

é descrita como a posição, relativa ao canto superior esquerdo da viewport atual :


Portanto, se você estiver na viewport 1:
uma janela na viewport 2 poderá ser posicionada em, por exemplo, 1700 (x) X 500 (Y)
(minha tela é 1680x1050)

insira a descrição da imagem aqui


No entanto, se você estiver na viewport 6:
a mesma janela seria posicionada em 20 (x), -550 (y) insira a descrição da imagem aqui


Usar essas coordenadas corretamente é importante para executar o script com os argumentos corretos, conforme descrito abaixo:

2. Como usar o script

O script abaixo pode ser usado para colocar uma nova janela de um aplicativo no seu espaço de trabalho virtual (estendido).

  1. Verifique se wmctrlestá instalado:

    sudo apt-get install wmctrl
    
  2. Copie o script abaixo em um arquivo vazio, salve-o como setwindow(sem extensão) em ~/bin. Crie o diretório se ele ainda não existir. Torne o script executável .

  3. Se você acabou de criar ~/bin, execute o comando source ~/.profileou efetue logout / logon para disponibilizar o diretório $PATH.
  4. Teste, execute o comando:

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

    por exemplo

    setwindow gedit 100 100 200 200
    

    Uma janela do gedit deve aparecer na viewport atual.

Notas:

  • Lembre-se de que nem todos os aplicativos permitem tamanhos de janela abaixo de uma certa largura ou altura. A largura mínima de uma geditjanela no meu sistema é, por exemplo, appr. 470 px.
  • O script só funciona bem se a janela inteira se encaixar na janela de exibição alvo, escolha suas coordenadas / tamanhos de acordo. Lembre-se também de que o Unity Launcher e o painel usam algum espaço (!) Que pode influenciar a posição da janela.
  • Use negativo <x_position>para colocar janelas à esquerda das janelas de visualização atuais
  • Use negativo <y_position>para colocar janelas acima da (s) janela (s) atual (is)
  • Para abrir novas janelas em diferentes viewports ao mesmo tempo, você pode simplesmente encadear comandos. Observando a configuração da viewport no exemplo "Long story", se eu estiver na viewport 1, posso abrir janelas do gedit nas viewport 1, 2, 3 e 4 com o 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
    

O script

#!/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: a versão preguiçosa

Caso prefira inserir coordenadas e tamanho, basta abrir uma janela na viewport atual e exibir a viewport direcionada como argumento (sem precisar calcular nada), e use a versão abaixo ...

Se você configurá-lo como a primeira versão do script, poderá executá-lo com o comando:

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

Um exemplo: para abrir uma Google-Chromejanela posicionada em 20, 20, tamanho 300x300, na janela de exibição 5:

setwindow google-chrome 20 20 300 300 5

A configuração é praticamente a mesma que a primeira versão do script.
Observe que também esse script funciona corretamente se a janela definida (posição / tamanho) se encaixar completamente na viewport de destino.

O script:

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


Abrindo janelas de aplicativos com argumentos

Para finalizar o trabalho, responda sua pergunta completamente:

Se você executar o script como por exemplo:

setwindow google-chrome 20 20 300 300 5

ele abrirá uma janela padrão nos desktops direcionados.
No entanto, com a versão mais recente do script, você pode adicionar um argumento adicional para abrir a janela do aplicativo, por exemplo url:

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

por exemplo:

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

Se o argumento (extra) contiver espaços, use aspas. O exemplo acima abrirá uma google-chromejanela na janela de visualização 3, abrindo o url http://askubuntu.com.

Você pode encadear comandos para abrir várias janelas / URLs em diferentes áreas de trabalho em um comando, por exemplo:

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
fonte
@snitko Obrigado pela pergunta agradável, foi um bom desafio para obtê-lo feito :)
Jacob Vlijm
Percebi que há uma pequena diferença na janela quando estou usando o script. Assim, por exemplo, se eu abrir nas coordenadas 0 0, ele realmente abrirá um pouco mais para a direita e para a parte inferior (um deslocamento de ~ 10px). Tudo bem, mas o problema é realmente o segundo script: o deslocamento no segundo script é estranhamente maior no eixo horizontal. Eu acho que é sobre ~ 50px para a esquerda. Você pode ver por que isso? Definir coordenadas negativas não ajuda nesse caso.
Snitko
Outra pergunta: como faço para abrir uma janela em tela cheia?
Snitko
Uma atualização: o deslocamento no caso do segundo script parece ser o mesmo que a largura do dock da unidade à esquerda (embora esteja oculto).
Snitko
Para os interessados, eu tenho implementado Desktopen: github.com/snitko/desktopen
Snitko
1

Isso se expande na grande resposta de @Jacob Vlijim acima, com um setwindowscript ligeiramente 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)

Uma descrição das mudanças:

  1. python3para python(apenas uma preferência pessoal)
  2. sys.argva argparsepara uma interface de linha de comando melhor
  3. análise de janela de identificação de janela estrita (e não de identificação de processo)
    • alguns programas usam uma única identificação de processo para várias janelas
  4. while loop de 0,5 segundo a 1 segundo de sono completo
  5. nomes de variáveis ​​mais detalhados / legíveis e adesão ao pep8
  6. variáveis ​​constantes globais para tamanho de tela em vez de xrandrdependência

NOTA: Esta é uma versão apenas um pouco aprimorada que escrevi para uso pessoal no Debian Jessie LXDE. Seus resultados podem variar.

lscstu22
fonte
0

Para os interessados, implementei o Desktopen: github.com/snitko/desktopen

Ele permite que você escreva um script para abrir janelas em diferentes viewports e displays de uma maneira muito amigável.

snitko
fonte