Como iniciar um aplicativo com tamanho e posição predefinidos da janela?

22

Gostaria de saber se existe alguma maneira de obter o efeito dos atalhos Ctrl-Alt-Keypad no Unity usando comandos de terminal? Eu quero um comando que define uma janela GUI para metade do tamanho da tela, alinhada à esquerda ou à direita.

Como pano de fundo, estou escrevendo um script que é executado após o login. Ele usa o Zenity para perguntar se eu quero abrir meu ambiente de desenvolvimento (GVim e IPython lado a lado). Eu tenho tentado alcançar duas janelas de igual tamanho para esses programas usando set lines= columns=no meu .gvimrce c.IPythonWidget.width =e c.IPythonWidget.height =na minha ipython_qtconsole_config.py. No entanto, existem problemas associados a essa abordagem.

STim
fonte
Eu atualizei a minha resposta porque não explicar o tema em detalhes suficientes, você pode querer verificar a atualização
kos

Respostas:

17

O que você encontrará

Se você deseja primeiro chamar um aplicativo e, posteriormente, colocar sua janela em uma posição e tamanho específicos, o tempo entre a chamada do aplicativo e o momento em que a janela realmente aparece é imprevisível. Se o seu sistema estiver ocupado, ele poderá ser significativamente mais longo do que se estiver ocioso.

Você precisa de uma maneira "inteligente" para garantir que o posicionamento / redimensionamento seja feito (imediatamente) depois que a janela aparecer .

Script para chamar um aplicativo, aguarde que ele apareça e posicione-o na tela

Com o script abaixo, você pode chamar um aplicativo e definir a posição e o tamanho em que ele deve aparecer com o comando:

<script> <application> <x-position> <y-position> <h-size> <v-size>

Alguns exemplos:

  • Para chamar gnome-terminale redimensionar sua janela para 50% e colocá-la na metade direita:

    <script> gnome-terminal 840 0 50 100

    insira a descrição da imagem aqui

  • Para ligar gedit, coloque sua janela à esquerda e ligue gnome-terminal, coloque-a à direita (definindo v-size46% para dar um pouco de espaço entre as janelas):

    <script> gedit 0 0 46 100&&<script> gnome-terminal 860 0 46 100

    insira a descrição da imagem aqui

  • Para chamar o Inkscape, coloque sua janela no quarto esquerdo / superior da tela:

    <script> inkscape 0 0 50 50

    insira a descrição da imagem aqui

O script e como usá-lo

  1. instale ambos xdotoole wmctrl. Eu usei os dois, pois o redimensionamento com wmctrlpode causar algumas peculiaridades em (especificamente) Unity.

    sudo apt-get install wmctrl
    sudo apt-get install xdotool
  2. Copie o script abaixo em um arquivo vazio, salve-o como setwindow(sem extensão) em ~/bin; crie o diretório, se necessário.
  3. Tornar o script executável (!)
  4. Se você acabou de criar ~bin, execute:source ~/.profile
  5. Execute o script com o comando (por exemplo)

    setwindow gnome-terminal 0 0 50 100

    Em outras palavras:

    setwindow <application> <horizontal-position> <vertical-position> <horizontal-size (%)> <vertical-size (%)>

Se tudo funcionar bem, use o comando sempre que precisar.

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])

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 = "xdotool windowsize --sync "+procs[0][0][1]+" "+sys.argv[4]+"% "+sys.argv[5]+"%"
        cmd4 = "xdotool windowmove "+procs[0][0][1]+" "+sys.argv[2]+" "+sys.argv[3]
        for cmd in [cmd1, cmd2, cmd3, cmd4]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1

O que faz

Quando o script é chamado, ele:

  1. inicia o aplicativo
  2. fica de olho na lista de janelas (usando wmctrl -lp)
  3. se uma nova janela aparecer, ele verifica se a nova janela pertence ao aplicativo chamado (usando ps -ef ww, comparando o pid da janela com o pid do aplicativo)
  4. Nesse caso, define o tamanho e a posição, de acordo com seus argumentos. Caso um aplicativo não "apareça" dentro de aprox. 15 segundos, o script assume que o aplicativo não será executado devido a um erro. O script é encerrado para impedir a espera infinita da nova janela.

Problema menor

No Unity, quando você (re) posiciona e (re) dimensiona uma janela com um wmctrlou outro xdotool, a janela sempre mantém uma pequena marca nos limites da tela, a menos que você a defina como 100%. Você pode ver isso na imagem (3) acima; enquanto a inkscapejanela foi colocada na xposição 0, você ainda pode ver uma pequena diferença entre o Unity Launcher e a inkscapejanela.

Jacob Vlijm
fonte
Caro Jacob, este é um roteiro fantástico! Uma pergunta: como posso encontrar a dimensão de uma janela aberta para ser usada posteriormente juntamente com o script?
orschiro
Oi @orschiro eu tenho que correr para uma reunião ... estará de volta em poucas horas :)
Jacob Vlijm
11
Oi @orschiro, supondo que você tenha wmctrlinstalado, o comando: wmctrl -lG | grep <windowname>irá mostrar-lhe uma saída como: 0x03600e4f 0 723 197 1114 563 jacob-System-Product-Name dimkeyboard.sh (~/Bureaublad) - gedit. Nesta saída, a terceira à sexta coluna mostra as dimensões, x, y, largura, largura, altura.
Jacob Vlijm
Muito apreciado Jacob! Acho que fiz a configuração corretamente, mas a janela respectiva é exibida em tela cheia agora. Alguma ideia?
Orschiro #
11
Adorei esse script, mas tive que fazer as seguintes alterações no primeiro bloco para fazê-lo funcionar, porque adicionei parâmetros ao aplicativo: appAndParams = sys.argv[1] app = appAndParams[0].split(' ', 1)[0] get = lambda x: subprocess.check_output(["/bin/bash", "-c",x]).decode("utf-8") ws1 = get("wmctrl -lp"); t = 0 subprocess.Popen(["/bin/bash", "-c", appAndParams])</pre> <edit> ele se recusa a formatar esse código. </edit>
Ron Thompson
3

O comando real que você deseja é algo como

wmctrl -r :ACTIVE: -b add,maximized_vert && 
wmctrl -r :ACTIVE: -e 0,0,0,$HALF,-1

Isso fará com que a janela atual ocupe metade da tela (mude $HALFpara as dimensões da tela) e se encaixe no lado esquerdo. Para encaixar à direita, use

wmctrl -r :ACTIVE: -b add,maximized_vert && 
wmctrl -r :ACTIVE: -e 0,$HALF,0,$HALF,-1 

Você também pode jogar wmctrlpara obter o ID das janelas nas quais está interessado, em vez de usar :ACTIVE:. Não posso ajudar lá, pois isso depende das janelas em questão. Dê uma olhada man wmctrlpara mais.


Eu escrevi um roteiro para isso. Como não uso o Unity, não posso garantir que funcione com ele, mas não vejo motivo para não fazê-lo. Ele precisa wmctrl, xdpyinfoe disperpara ser instalado:

sudo apt-get install wmctrl x11-utils disper

Em seguida, salve o script abaixo como ~/bin/snap_windows.sh, torne-o executável com chmod a+x ~/bin/snap_windows.she você poderá executar

snap_windows.sh r

Para encaixar no lado direito. Usarl para o lado esquerdo e sem argumentos para maximizar a janela. Observe que ele é executado na janela atual, portanto, você precisará atribuir um atalho a ele, se quiser que ele seja executado em qualquer coisa, menos no terminal.

O script é um pouco mais complicado do que o que você pede, porque eu o escrevi para trabalhar nas configurações de monitor único e duplo.

#!/usr/bin/env bash

## If no side has been given, maximize the current window and exit
if [ ! $1 ]
then
    wmctrl -r :ACTIVE: -b toggle,maximized_vert,maximized_horz
    exit
fi

## If a side has been given, continue
side=$1;
## How many screens are there?
screens=`disper -l | grep -c display`
## Get screen dimensions
WIDTH=`xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x'`;
HALF=$(($WIDTH/2));

## If we are running on one screen, snap to edge of screen
if [ $screens == '1' ]
then
    ## Snap to the left hand side
    if [ $side == 'l' ]
    then
        ## wmctrl format: gravity,posx,posy,width,height
    wmctrl -r :ACTIVE: -b add,maximized_vert && wmctrl -r :ACTIVE: -e 0,0,0,$HALF,-1
    ## Snap to the right hand side
    else
    wmctrl -r :ACTIVE: -b add,maximized_vert && wmctrl -r :ACTIVE: -e 0,$HALF,0,$HALF,-1 
    fi
## If we are running on two screens, snap to edge of right hand screen
## I use 1600 because I know it is the size of my laptop display
## and that it is not the same as that of my 2nd monitor.
else
    LAPTOP=1600; ## Change this as approrpiate for your setup.
    let "WIDTH-=LAPTOP";
    SCREEN=$LAPTOP;
    HALF=$(($WIDTH/2));
    if [ $side == 'l' ]
    then
        wmctrl -r :ACTIVE: -b add,maximized_vert && wmctrl -r :ACTIVE: -e 0,$LAPTOP,0,$HALF,-1
    else
    let "SCREEN += HALF+2";
    wmctrl -r :ACTIVE: -b add,maximized_vert && wmctrl -r :ACTIVE: -e 0,$SCREEN,0,$HALF,-1;
    fi
fi
Terdon
fonte
3

Você pode fazer isso usando xdotool.

Para instalar, xdotoolvocê pode executar:

sudo apt-get update && sudo apt-get install xdotool

Em seguida, para enviar uma tecla Ctrl+ Alt+ <keypad_key>para a Xjanela do terminal , você pode executar:

xdotool key Ctrl+Alt+<keypad_key_value>

* <keypad_key_value> = valor da tecla do teclado na lista abaixo

Para executar um programa GUI e enviar o pressionamento de tecla para sua Xjanela (que neste caso é a janela ativa no momento da xdotoolexecução do comando), você pode executar:

<command> && window="$(xdotool getactivewindow)" xdotool key --delay <delay> --window "$window" <keypad_key_value>

* <comando> = comando que abre a janela para a qual você deseja enviar o pressionamento de tecla; <delay> = tempo de espera em milissegundos antes de enviar o pressionamento de tecla; <keypad_key_value> = valor da tecla do teclado na lista abaixo

Observe que, na maioria dos casos, você precisará executar o comando que está emitindo como um processo independente (por exemplo, executando em nohup <command> &vez de <command>no exemplo acima), caso contrário xdotool, não será executado até que <command>a execução seja concluída.

Você também precisará definir algum atraso, caso contrário, o pressionamento de tecla será enviado antes que a janela de destino seja totalmente carregada X(um atraso 500msdeve ser feito).

Os valores possíveis para <keypad_key_value>são:

  • 0: 90
  • 1: 87
  • 2: 88
  • 3: 89
  • 4: 83
  • 5: 84
  • 6: 85
  • 7: 79
  • 8: 80
  • 9: 81

Como regra geral, para descobrir o valor de qualquer tecla do teclado no Xambiente, é possível executar xeve pressionar a tecla para gerar seu valor dentro do terminal.

kos
fonte
Há algo de errado com nossas edições?
Tim
@ Tim Não, pelo menos não a remoção da última linha, o que eu concordo é bastante inútil, mas na minha opinião o que pode variar dentro de um comando é melhor formatado se estiver entre colchetes angulares, que é a notação padrão na teoria da linguagem a ser consultada para uma categoria sintática (referindo-se à sua edição) e acho que um nome de comando é melhor formatado se colocado entre backticks, para ser imediatamente reconhecido como tal (referindo-se à edição da AB); repare que nem tudo isso está fora de cogitação, eu já tinha dúvidas e perguntei isso no meta
kos:
Sim, está tudo bem como está agora :) <> é o padrão, e eu procuro `` around any command :)
Tim
3

Criei um aplicativo chamado Worksets (no github ) para Unity que permite fazer isso facilmente através de uma interface gráfica do usuário - é gratuito e de código aberto.

tMenu da bandeja

É basicamente um invólucro para as soluções wmctrl e xdotool listadas como respostas aqui, e fornece uma maneira fácil de fazer e salvar rapidamente essas configurações.

Tumulto
fonte
1

Não tenho o representante para comentar diretamente sobre a excelente postagem de Jacob Vlijm, mas modifiquei o script para permitir o início de um aplicativo com argumentos (necessário usar setwindowcom gedit --new-window). Mudança:

if app in p and w[2] in p] for w in ws2]

para:

if app.split()[0] in p and w[2] in p] for w in ws2]
D. Hancock
fonte
0

Eu brinquei com o script de Terdon de cima e adicionei algumas coisas novas (capacidade de selecionar o monitor e definir a altura de cada monitor). Seria extensível a adição de mais monitores, eu acho. Espero que outros achem útil.

Sintaxe básica:

prog_name monitor l/r/m window_name (optional)

Onde prog_name é como você salvou esse código; monitor é o número do monitor, por exemplo, 1 ou 2; l / r / m é esquerda ou direita ou max; e window_name é o nome (ou uma fração do nome) da janela de destino.

Por exemplo:

setwindow 1 m chrome

Script Bash

#!/usr/bin/env bash
set -e
#######################-    Early Definitions    -#######################

snap () {
    wmctrl -r ${WIN} -b toggle,add,maximized_vert && wmctrl -r ${WIN} -e 0,${WINX},0,${WINWIDTH},${WINHEIGHT}
    }

DISPLAYWIDTH=`xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x'`;       ## Get screen dimensions
LEFTSCREENWIDTH=1024    ## user set
LEFTSCREENHEIGHT=720    ## user set
RIGHTSCREENHEIGHT=960   ## user set
let "RIGHTSCREENWIDTH=(DISPLAYWIDTH-LEFTSCREENWIDTH)"

#############################-    Logic    -#############################

if [ ! ${3} ]; then
    WIN=":ACTIVE:"
else
    WIN=${3}
fi
case ${1} in
    1)  # monitor one
        LEFT=0
        WINHEIGHT=${LEFTSCREENHEIGHT}
        let "WINWIDTH=LEFTSCREENWIDTH/2"
    ;;
    2)  # monitor two
        let "LEFT=LEFTSCREENWIDTH"
        WINHEIGHT=${RIGHTSCREENHEIGHT}
        let "WINWIDTH=RIGHTSCREENWIDTH/2"
    ;;
    "") # error please select a monitor
        echo "please select a monitor (1 or 2)"
        exit 0
    ;;
esac
case ${2} in
    l|L)
        WINX=${LEFT}
        snap
    ;;
    r|R)
        let "WINX=LEFT+WINWIDTH"
        snap
    ;;
    ""|m|M)
        WINX=${LEFT}
        let "WINWIDTH=WINWIDTH*2"
        snap
    ;;
esac

exit 0
Filósofo Rex
fonte