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?


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"

@ 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ć.


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.