Jak mogę uruchomić aplikację z wcześniej zdefiniowanym rozmiarem i pozycją okna?

22

Zastanawiam się, czy jest jakiś sposób na uzyskanie efektu skrótu Ctrl-Alt-Keypad w Unity za pomocą poleceń terminalu? Chcę polecenia, które ustawia okno GUI na połowę wielkości ekranu, wyrównane do lewej lub prawej strony.

Jako tło piszę skrypt, który działa po zalogowaniu. Używa Zenity, aby zapytać, czy chcę otworzyć moje środowisko programistyczne (GVim i IPython obok siebie). Mam stara się osiągnąć dwa równej wielkości okna dla tych programów za pomocą set lines= columns=w moim .gvimrci c.IPythonWidget.width =i c.IPythonWidget.height =w moim ipython_qtconsole_config.py. Istnieją jednak problemy związane z tym podejściem.

STIM
źródło
Zaktualizowałem swoją odpowiedź, ponieważ nie wyjaśniła wystarczająco szczegółowo tematu, możesz sprawdzić aktualizację
Kos

Odpowiedzi:

17

Na co wpadniesz

Jeśli chcesz najpierw wywołać aplikację, a następnie umieścić jej okno na określonej pozycji i rozmiarze, czas między wywołaniem aplikacji a momentem, w którym okno faktycznie się pojawi , jest nieprzewidywalny. Jeśli twój system jest zajęty, może być znacznie dłuższy niż w przypadku bezczynności.

Potrzebujesz „inteligentnego” sposobu, aby upewnić się, że pozycjonowanie / zmiana rozmiaru jest wykonywana (natychmiast) po pojawieniu się okna .

Skrypt wywołuje aplikację, czeka na jej pojawienie się i ustawia na ekranie

Za pomocą poniższego skryptu możesz wywołać aplikację i ustawić za pomocą polecenia pozycję i rozmiar:

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

Kilka przykładów:

  • Aby wywołać gnome-terminali zmienić rozmiar okna do 50% i umieścić je w prawej połowie:

    <script> gnome-terminal 840 0 50 100

    wprowadź opis zdjęcia tutaj

  • Aby zadzwonić gedit, umieść okno po lewej stronie i zadzwoń gnome-terminal, ustaw je po prawej stronie (ustawiając v-size46%, aby dać mu trochę przestrzeni między oknami):

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

    wprowadź opis zdjęcia tutaj

  • Aby wywołać Inkscape, umieść jego okno w lewej / górnej ćwiartce ekranu:

    <script> inkscape 0 0 50 50

    wprowadź opis zdjęcia tutaj

Skrypt i jak go używać

  1. zainstaluj zarówno xdotooli wmctrl. Użyłem obu, ponieważ zmiana rozmiaru za pomocą wmctrlmoże powodować pewne osobliwości (szczególnie) Unity.

    sudo apt-get install wmctrl
    sudo apt-get install xdotool
  2. Skopiuj poniższy skrypt do pustego pliku, zapisz go jako setwindow(bez rozszerzenia) w ~/bin; w razie potrzeby utwórz katalog.
  3. Spraw, aby skrypt był wykonywalny (!)
  4. Jeśli właśnie utworzyłeś ~bin, uruchom:source ~/.profile
  5. Uruchom skrypt z poleceniem (np.)

    setwindow gnome-terminal 0 0 50 100

    Innymi słowy:

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

Jeśli wszystko działa poprawnie, użyj polecenia wszędzie tam, gdzie jest to potrzebne.

Scenariusz

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

Co to robi

Po wywołaniu skryptu:

  1. uruchamia aplikację
  2. pilnuje listy okien (za pomocą wmctrl -lp)
  3. jeśli pojawi się nowe okno, sprawdza, czy nowe okno należy do wywoływanej aplikacji (używając ps -ef ww, porównując pid okna z pid aplikacji)
  4. jeśli tak, ustawia rozmiar i pozycję, zgodnie z twoimi argumentami. W przypadku, gdy aplikacja nie pojawi się w ok. 15 sekund, skrypt zakłada, że ​​aplikacja nie uruchomi się z powodu błędu. Skrypt kończy się, aby zapobiec niekończącemu się oczekiwaniu na nowe okno.

Mniejszy problem

W Unity, gdy (re) pozycjonujesz i (re) zmieniasz okno za pomocą jednego wmctrllub drugiego xdotool, okno zawsze zachowuje mały margines do granic ekranu, chyba że ustawisz je na 100%. Możesz to zobaczyć na obrazku (3) powyżej; podczas gdy inkscapeokno zostało umieszczone w xpozycji 0, nadal możesz zobaczyć niewielki marge między Unity Launcher a inkscapeoknem.

Jacob Vlijm
źródło
Drogi Jakubie, to fantastyczny scenariusz! Jedno pytanie: jak znaleźć wymiar otwartego okna, który będzie później używany razem ze skryptem?
orschiro
Cześć @orschiro Muszę pobiec na spotkanie ... wrócę za kilka godzin :)
Jacob Vlijm
1
Cześć @orschiro, zakładając, że masz wmctrlzainstalowany, polecenie: wmctrl -lG | grep <windowname>pokaże wyjście jak: 0x03600e4f 0 723 197 1114 563 jacob-System-Product-Name dimkeyboard.sh (~/Bureaublad) - gedit. Na tym wyjściu kolumna od 3 do 6 pokazuje wymiary, x, y, szerokość, szerokość, wysokość.
Jacob Vlijm
Bardzo ceniony Jacob! Myślę, że poprawnie wykonałem konfigurację, ale odpowiednie okno otwiera się teraz na pełnym ekranie. Jakieś pomysły?
orschiro
1
Uwielbiałem ten skrypt, ale musiałem wprowadzić następujące zmiany w pierwszym bloku, aby działał dla mnie, ponieważ dodałem parametry do aplikacji: 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> odmawia sformatowania tego kodu. </edit>
Ron Thompson
3

Właściwe polecenie, które chcesz, to coś w rodzaju

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

To sprawi, że bieżące okno zajmie połowę ekranu (zmień $HALFwymiary ekranu) i przyciągnie do lewej strony. Aby przyciągnąć w prawo, użyj

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

Możesz także pobawić wmctrlsię identyfikatorem okien, które Cię interesują, zamiast ich używać :ACTIVE:. Nie mogę jednak pomóc, ponieważ zależy to od okien, o których mowa. Zobacz man wmctrlwięcej.


Napisałem do tego skrypt. Nie używam Unity, więc nie mogę zagwarantować, że będzie z nim współpracować, ale nie widzę powodu, dla którego miałbym tego nie robić. Potrzebuje wmctrl, xdpyinfoa disperktóre mają być zainstalowane:

sudo apt-get install wmctrl x11-utils disper

Następnie zapisz poniższy skrypt jako ~/bin/snap_windows.sh, wykonaj go za pomocą chmod a+x ~/bin/snap_windows.shi możesz uruchomić

snap_windows.sh r

Aby przyciągnąć do prawej strony. Użyj ldla lewej strony i bez argumentów, aby zmaksymalizować okno. Zauważ, że działa w bieżącym oknie, więc musisz przypisać do niego skrót, jeśli chcesz, aby działał tylko na terminalu.

Skrypt jest nieco bardziej skomplikowany niż to, o co prosisz, ponieważ napisałem go, aby działał zarówno w konfiguracji z jednym, jak i z dwoma monitorami.

#!/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
źródło
3

Możesz to zrobić za pomocą xdotool.

Aby zainstalować xdotool, możesz uruchomić:

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

Następnie, aby wysłać naciśnięcie klawisza Ctrl+ Alt+ <keypad_key>do Xokna terminala , możesz uruchomić:

xdotool key Ctrl+Alt+<keypad_key_value>

* <wartość_klucza_ klawiatury> = wartość klawisza na liście poniżej

Aby uruchomić program GUI i wysłać naciśnięcie klawisza do jego Xokna (które w tym przypadku jest aktywnym oknem w momencie xdotoolwykonywania polecenia), możesz uruchomić:

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

* <polecenie> = polecenie otwierające okno, do którego chcesz wysłać naciśnięcie klawisza; <opóźnienie> = czas oczekiwania w milisekundach przed wysłaniem klawisza; <wartość_klucza> = wartość klawisza na poniższej liście

Zauważ, że w większości przypadków będziesz musiał uruchomić polecenie, które wydajesz jako proces autonomiczny (np. Uruchamiając nohup <command> &zamiast <command>w powyższym przykładzie), w przeciwnym razie xdotoolnie zostanie uruchomione, dopóki <command>wykonanie nie zostanie zakończone.

Musisz także ustawić pewne opóźnienie, w przeciwnym razie naciśnięcie klawisza zostanie wysłane, zanim okno docelowe zostanie w pełni załadowane X(opóźnienie 500mspowinno wystarczyć ).

Możliwe wartości <keypad_key_value>to:

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

Zgodnie z ogólną zasadą, aby znaleźć wartość dowolnego klawisza na klawiaturze w Xśrodowisku, można uruchomić xevi nacisnąć ten klawisz, aby wypisać jego wartość w terminalu.

kos
źródło
Czy coś jest nie tak z naszymi edycjami?
Tim
@Tim Nie, przynajmniej nie usunięcie ostatniego wiersza, co, jak się zgadzam, jest dość nieużyteczne, ale to, co może się różnić w poleceniu, jest moim zdaniem lepiej sformatowane, jeśli jest zawarte w nawiasach kątowych, co jest standardowym zapisem w teorii języków do kategorii składniowej (odnoszącej się do twojej edycji) i myślę, że nazwa polecenia jest lepiej sformatowana, jeśli jest ujęta w backticks, aby mogła zostać natychmiast rozpoznana jako taka (odnosząca się do edycji AB); zauważ, że nie wszystko to jest poza moim zasięgiem, miałem wcześniej wątpliwości i zapytałem o to na meta
kos
Tak, jest w porządku, jak jest teraz :) <> jest standardem, i wybieram `` wokół każdego polecenia :)
Tim
3

Stworzyłem aplikację o nazwie Worksets (na github ) dla Unity, która pozwala to zrobić z łatwością za pomocą graficznego interfejsu użytkownika - To bezpłatne i otwarte oprogramowanie.

Menu t zasobnika

Jest to po prostu opakowanie dla rozwiązań wmctrl i xdotool wymienionych jako odpowiedzi tutaj i zapewnia łatwy sposób szybkiego tworzenia i zapisywania takich ustawień.

Zgiełk
źródło
1

Nie mam przedstawiciela, który mógłby komentować bezpośrednio doskonałego posta Jacoba Vlijma, ale zmodyfikowałem skrypt, aby umożliwić uruchomienie aplikacji z argumentami (konieczne do użycia setwindowz gedit --new-window). Zmiana:

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

do:

if app.split()[0] in p and w[2] in p] for w in ws2]
D. Hancock
źródło
0

I bawił się skryptu Terdon jest z góry i dodano kilka nowych rzeczy (możliwość wyboru monitora i ustawić wysokość dla każdego monitora). Myślę, że można by go rozszerzyć, dodając więcej monitorów. Mam nadzieję, że inni uznają to za przydatne.

Podstawowa składnia:

prog_name monitor l/r/m window_name (optional)

Gdzie nazwa_programu jest tym, czym zapisałeś ten kod; monitor to numer monitora, np. 1 lub 2; l / r / m jest lewy, prawy lub maksymalny; a nazwa_okna to nazwa (lub ułamek jej nazwy) okna docelowego.

Na przykład:

setwindow 1 m chrome

Skrypt 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
Philosopher Rex
źródło