Czy istnieje skrypt (lub oprogramowanie) do otwierania okna aplikacji w określonej rzutni i pozycji?

8

Tak więc mam 8 wirtualnych komputerów stacjonarnych w Unity (z Compiz), ponieważ mam wiele projektów, nad którymi pracuję jednocześnie.

Problem w tym, że za każdym razem, gdy muszę ponownie uruchomić lub przypadkowo zamknąć Chrome (który stanowi dużą część okien potrzebnych mi do pracy), muszę ręcznie otworzyć te okna ponownie, a następnie je skonfigurować (otworzyć pliki, przejść do właściwego adresy URL itp.).

Jak poszedłbyś pisać scenariusz, który zrobi to wszystko dla mnie? To znaczy: 1) Otwórz okna 2) Umieść je we właściwych współrzędnych na właściwych ekranach wirtualnych

(1) jest oczywiste, w Google Chrome wystarczy uruchomić „google-chrome”. Ale jak to umieścić we właściwym miejscu? (2)

A może istnieje już skrypt / oprogramowanie, które zrobiłoby to dla mnie?

snitko
źródło
Mogę spróbować wymyślić skrypt do umieszczania dowolnych okien, które mogą być potrzebne na odpowiednich komputerach podczas uruchamiania, może to jednak potrwać kilka dni, ponieważ w przyszłym tygodniu mam finał. Będzie to wymagało wmctrl, podobnie jak oprogramowanie do sterowania oknami za pomocą skryptu lub terminala. Ale jeśli chodzi o ponowne uruchomienie okna, może to być nieco większe wyzwanie
Sergiy Kolodyazhnyy
Chociaż konkretnie pytałeś o Unity, warto zauważyć, że KDE ma (głównie nieudokumentowany) program o nazwie kstart, który robi to za Ciebie. Działa najlepiej z programami KDE, ale odnosi również sukcesy z innymi programami.
Joe

Odpowiedzi:

14

Można to zrobić bardzo dobrze, ale potrzebujesz trochę zrozumienia w Unity / rzutniach. Mam nadzieję, że poniższa historia jest zrozumiała, jeśli nie, proszę zostawić komentarz.

Poniższego skryptu można użyć do otwarcia okna dowolnej aplikacji w dowolnej z twoich rzutni, w dowolnej pozycji, jeśli uruchomisz ją z właściwymi argumentami. Skrypt jest edytowanej wersji tego jednego , ale teraz przygotowany do wstawiania okien na trwającej wirtualnym pulpicie .

1. Zrozumienie rzutni i współrzędnych okna

Obszary robocze w jedności

W Unity, w przeciwieństwie do innych menedżerów okien, w rzeczywistości masz tylko jeden obejmujący obszar roboczy, który jest podzielony na rzutnie. W twoim przypadku twój obszar roboczy jest podzielony na osiem rzutni.

Jak definiowane jest położenie okien

Pozycja okna, jako wynik polecenia:

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

jest opisany jako pozycja względem lewego górnego rogu bieżącej rzutni :


Więc jeśli jesteś w rzutni 1:
okno w rzutni 2, które można ustawić na np. 1700 (x-wise) x 500 (y-wise)
(mój ekran to 1680x1050)

wprowadź opis zdjęcia tutaj


Jeśli jednak masz rzutnię 6:
to samo okno będzie ustawione na 20 (x), -550 (y) wprowadź opis zdjęcia tutaj


Prawidłowe użycie tych współrzędnych jest ważne, aby uruchomić skrypt z właściwymi argumentami, jak opisano poniżej:

2. Jak korzystać ze skryptu

Poniższego skryptu można użyć do umieszczenia nowego okna aplikacji w wirtualnym (obejmującym) obszarze roboczym.

  1. Upewnij się, że wmctrljest zainstalowany:

    sudo apt-get install wmctrl
    
  2. Skopiuj poniższy skrypt do pustego pliku, zapisz go jako setwindow(bez rozszerzenia) w ~/bin. Utwórz katalog, jeśli jeszcze nie istnieje. Spraw, aby skrypt był wykonywalny .

  3. Jeśli właśnie utworzyłeś ~/bin, uruchom polecenie source ~/.profilelub wyloguj się / zaloguj, aby udostępnić katalog w $PATH.
  4. Uruchom testowo polecenie:

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

    na przykład

    setwindow gedit 100 100 200 200
    

    Okno gedit powinno pojawić się w bieżącej rzutni.

Uwagi:

  • Należy pamiętać, że nie wszystkie aplikacje dopuszczają rozmiary okien poniżej określonej szerokości lub wysokości. Minimalna szerokość geditokna w moim systemie to np. Ok. 470 pikseli.
  • Skrypt działa dobrze tylko wtedy, gdy całe okno pasuje do docelowej rzutni, odpowiednio wybierz współrzędne / rozmiary. Pamiętaj również, że Unity Launcher i panel zajmują trochę miejsca (!), Co może wpływać na położenie okna.
  • Użyj znaku ujemnego, <x_position>aby umieścić okna po lewej stronie bieżących rzutni
  • Użyj znaku ujemnego, <y_position>aby umieścić okna nad bieżącymi rzutniami
  • Aby otworzyć nowe okna jednocześnie w różnych rzutniach, możesz po prostu połączyć polecenia. Patrząc na ustawienia rzutni w przykładzie „Długa historia”: Jeśli korzystam z rzutni 1, mogę otworzyć okna gedit w rzutniach 1, 2, 3 i 4 za pomocą polecenia:

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

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



EDYCJA: leniwa wersja

Jeśli wolisz po prostu wprowadzić współrzędne i rozmiar, po prostu jakbyś otworzył okno w bieżącej rzutni i podał docelową rzutnię jako argument (bez konieczności obliczania), skorzystaj z poniższej wersji ...

Jeśli skonfigurujesz go jak pierwszą wersję skryptu, możesz uruchomić go za pomocą polecenia:

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

Przykład: aby otworzyć Google-Chromeokno ustawione na 20, 20, rozmiar 300x300, w rzutni 5:

setwindow google-chrome 20 20 300 300 5

Konfiguracja jest prawie taka sama jak pierwsza wersja skryptu.
Zauważ, że również ten skrypt działa poprawnie tylko wtedy, gdy zdefiniowane okno (pozycja / rozmiar) całkowicie pasuje do docelowej rzutni.

Scenariusz:

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


Otwieranie okien aplikacji za pomocą argumentów

Aby zakończyć zadanie, całkowicie odpowiedz na pytanie:

Jeśli uruchomisz skrypt jako np .:

setwindow google-chrome 20 20 300 300 5

otworzy domyślne okno na docelowych komputerach.
Jednak w najnowszej wersji skryptu możesz dodać dodatkowy argument, aby otworzyć okno aplikacji, na przykład url:

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

na przykład:

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

Jeśli (dodatkowy) argument zawiera spacje, użyj cudzysłowów. Powyższy przykład otworzy google-chromeokno w rzutni 3, otwierając url http://askubuntu.com.

Możesz połączyć polecenia, aby otworzyć wiele okien / adresów URL w różnych obszarach roboczych w jednym poleceniu, np .:

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
źródło
@ snitko Dzięki za miłe pytanie, było to fajne wyzwanie, aby to zrobić :)
Jacob Vlijm
Zauważyłem, że podczas korzystania ze skryptu istnieje niewielkie przesunięcie okna. Na przykład, jeśli otworzę przy współrzędnych 0 0, faktycznie otwiera to nieco bardziej w prawo i na dół (przesunięcie o ~ 10px). Zgadza się, ale problem tkwi w drugim skrypcie: przesunięcie w drugim skrypcie jest dziwnie większe na osi poziomej. Myślę, że po lewej stronie jest około 50 pikseli. Czy rozumiesz, dlaczego tak jest? W takim przypadku ustawienie ujemnych współrzędnych nie pomaga.
snitko
Kolejne pytanie: jak otworzyć okno na pełnym ekranie?
snitko
Aktualizacja: przesunięcie w przypadku drugiego skryptu wydaje się być takie samo jak szerokość doku Unity po lewej stronie (chociaż jest ukryte).
snitko
Dla zainteresowanych wdrożyłem Desktopen: github.com/snitko/desktopen
snitko
1

Rozszerza to świetną odpowiedź @Jacob Vlijim powyżej z nieco zmodyfikowanym setwindowskryptem:

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

Opis zmian:

  1. python3do python(tylko osobiste preferencje)
  2. sys.argvaby argparseza pomocą interfejsu linii komend lepiej
  3. ścisłe parsowanie okna id (a nie identyfikator procesu)
    • niektóre programy używają jednego identyfikatora procesu dla wielu okien
  4. while pętla 0,5 s do 1 pełnego sekundy czasu snu
  5. więcej pełnych / czytelnych nazw zmiennych i przestrzeganie pep8
  6. globalne zmienne stałe dla wielkości ekranu zamiast xrandrzależności

UWAGA: Jest to tylko nieco ulepszona wersja, którą napisałem do użytku osobistego na Debian Jessie LXDE. Twoje wyniki mogą się różnić.

lscstu22
źródło
0

Dla zainteresowanych wdrożyłem Desktopen: github.com/snitko/desktopen

Pozwala napisać skrypt do otwierania okien w różnych rzutniach i ekranach w bardzo przyjazny sposób.

snitko
źródło