Gibt es ein Skript (oder eine Software) zum Öffnen eines Anwendungsfensters für ein bestimmtes Ansichtsfenster und eine bestimmte Position?

8

Ich habe also 8 virtuelle Desktops in Unity (mit Compiz), weil ich viele Projekte habe, an denen ich gleichzeitig arbeite.

Das Problem ist, dass ich jedes Mal, wenn ich Chrome neu starten oder versehentlich schließen muss (was einen großen Teil der Fenster ausmacht, die ich für die Arbeit benötige), diese Fenster manuell erneut öffnen und dann einrichten muss (Dateien öffnen, zum richtigen Fenster wechseln) URLs etc.).

Wie würden Sie ein Skript schreiben, das all das für mich erledigt? Das heißt: 1) Öffnen Sie die Fenster. 2) Setzen Sie sie auf den richtigen virtuellen Bildschirmen in die richtigen Koordinaten

(1) ist offensichtlich, für Google Chrome führen Sie einfach "Google-Chrome" aus. Aber wie bringen Sie es dann an die richtige Stelle? (2)

Oder gibt es bereits ein Skript / eine Software, die dies für mich erledigen würde?

Snitko
quelle
Ich kann versuchen, ein Skript zu erstellen, um die Fenster, die Sie beim Start benötigen, auf geeigneten Desktops zu platzieren. Dies kann jedoch einige Tage dauern, da ich nächste Woche das Finale habe. Es handelt sich dabei um wmctrleine Software zur Steuerung von Fenstern über ein Skript oder ein Terminal. Aber was den Neustart eines Fensters
betrifft
Obwohl Sie speziell nach Unity gefragt haben, ist es erwähnenswert, dass KDE ein (größtenteils undokumentiertes) Programm namens kstart hat, das dies für Sie erledigt. Es funktioniert am besten mit KDE-Programmen, hat aber auch einige Erfolge mit anderen Programmen.
Joe

Antworten:

14

Es kann sehr gut gemacht werden, aber Sie benötigen etwas Verständnis für Unity / Ansichtsfenster. Ich hoffe, die folgende Geschichte ist verständlich. Wenn nicht, hinterlassen Sie bitte einen Kommentar.

Das folgende Skript kann verwendet werden, um ein Fenster einer beliebigen Anwendung in einem Ihrer Ansichtsfenster an einer beliebigen Position zu öffnen, wenn Sie es mit den richtigen Argumenten ausführen. Das Skript ist eine bearbeitete Version dieses Skripts, ist jedoch jetzt darauf vorbereitet, Fenster auf dem übergreifenden virtuellen Desktop zu platzieren .

1. Grundlegendes zu Ansichtsfenstern und Fensterkoordinaten

Arbeitsbereiche in Einheit

In Unity haben Sie im Gegensatz zu anderen Fenstermanagern nur einen übergreifenden Arbeitsbereich, der in Ansichtsfenster unterteilt ist. In Ihrem Fall ist Ihr Arbeitsbereich in acht Ansichtsfenster unterteilt.

Wie die Position der Fenster definiert wird

Die Fensterposition als Ausgabe des Befehls:

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

wird als Position relativ zur oberen linken Ecke des aktuellen Ansichtsfensters beschrieben :


Wenn Sie sich also im Ansichtsfenster befinden 1:
Ein Fenster in Ansichtsfenster 2 kann beispielsweise auf 1700 (x-weise) x 500 (y-weise)
positioniert werden (mein Bildschirm ist 1680x1050).

Geben Sie hier die Bildbeschreibung ein


Wenn Sie sich jedoch in Ansichtsfenster 6 befinden, wird
dasselbe Fenster auf 20 (x), -550 (y) positioniert. Geben Sie hier die Bildbeschreibung ein


Die korrekte Verwendung dieser Koordinaten ist wichtig, um das Skript mit den richtigen Argumenten auszuführen, wie unten beschrieben:

2. Verwendung des Skripts

Mit dem folgenden Skript können Sie ein neues Fenster einer Anwendung in Ihrem virtuellen (übergreifenden) Arbeitsbereich platzieren.

  1. Stellen Sie sicher, dass wmctrlinstalliert ist:

    sudo apt-get install wmctrl
    
  2. Kopieren Sie das folgende Skript in eine leere Datei und speichern Sie es als setwindow(keine Erweiterung) in ~/bin. Erstellen Sie das Verzeichnis, falls es noch nicht vorhanden ist. Machen Sie das Skript ausführbar .

  3. Wenn Sie gerade erstellt haben ~/bin, führen Sie entweder den Befehl aus source ~/.profileoder melden Sie sich ab, um das Verzeichnis in verfügbar zu machen $PATH.
  4. Testen Sie den Befehl:

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

    z.B

    setwindow gedit 100 100 200 200
    

    Im aktuellen Ansichtsfenster sollte ein gedit-Fenster angezeigt werden.

Anmerkungen:

  • Beachten Sie, dass nicht alle Anwendungen Fenstergrößen unterhalb einer bestimmten Breite oder Höhe zulassen. Die Mindestbreite eines geditFensters auf meinem System beträgt z. 470 px.
  • Das Skript funktioniert nur dann einwandfrei, wenn das gesamte Fenster auf das gewünschte Ansichtsfenster passt. Wählen Sie Ihre Koordinaten / Größen entsprechend aus. Beachten Sie auch, dass der Unity Launcher und das Bedienfeld etwas Platz (!) Verwenden, der die Position des Fensters beeinflussen kann.
  • Verwenden Sie Negativ <x_position>, um Fenster links von den aktuellen Ansichtsfenstern zu platzieren.
  • Verwenden Sie Negativ <y_position>, um Fenster über den aktuellen Ansichtsfenstern zu platzieren.
  • Um neue Fenster in verschiedenen Ansichtsfenstern gleichzeitig zu öffnen, können Sie einfach Befehle verketten. Wenn ich mich in Ansichtsfenster 1 befinde, kann ich gedit-Fenster in Ansichtsfenster 1, 2, 3 und 4 mit dem folgenden Befehl öffnen:

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

Das Skript

#!/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: die faule Version

Wenn Sie lieber nur Koordinaten und Größe eingeben möchten, als würden Sie ein Fenster im aktuellen Ansichtsfenster öffnen und das Zielansichtsfenster als Argument angeben (ohne etwas berechnen zu müssen), verwenden Sie die folgende Version ...

Wenn Sie es wie die erste Version des Skripts einrichten, können Sie es mit dem folgenden Befehl ausführen:

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

Ein Beispiel: ein zum Öffnen Google-ChromeFenster positioniert 20, 20, Größe 300x300, auf Darstellungsfeld 5:

setwindow google-chrome 20 20 300 300 5

Das Setup entspricht weitgehend der ersten Version des Skripts.
Beachten Sie, dass auch dieses Skript nur dann ordnungsgemäß funktioniert, wenn das definierte Fenster (Position / Größe) vollständig in das Zielansichtsfenster passt.

Das Skript:

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


Anwendungsfenster mit Argumenten öffnen

Um den Job zu beenden, beantworten Sie Ihre Frage vollständig:

Wenn Sie das Skript wie folgt ausführen:

setwindow google-chrome 20 20 300 300 5

Es wird ein Standardfenster auf den Ziel-Desktops geöffnet .
Mit der neuesten Version des Skripts können Sie jedoch ein zusätzliches Argument hinzufügen , um das Anwendungsfenster zu öffnen, z. B. a url:

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

z.B:

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

Wenn das (zusätzliche) Argument Leerzeichen enthält, verwenden Sie Anführungszeichen. Das obige Beispiel öffnet ein google-chromeFenster in Ansichtsfenster 3 und öffnet das url http://askubuntu.com.

Sie können Befehle verketten, um mehrere Fenster / URLs in verschiedenen Arbeitsbereichen in einem Befehl zu öffnen, z.

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
quelle
@ Snitko Danke für die nette Frage, es war eine schöne Herausforderung, es zu erledigen :)
Jacob Vlijm
Ich habe festgestellt, dass das Fenster bei Verwendung des Skripts leicht versetzt ist. Wenn ich zum Beispiel bei den Koordinaten 0 0 öffne, öffnet es sich tatsächlich etwas weiter rechts und unten (ein Versatz von ~ 10 Pixel). Das ist in Ordnung, aber das Problem liegt tatsächlich beim zweiten Skript: Der Versatz im zweiten Skript ist auf der horizontalen Achse merkwürdig größer. Ich denke, es ist ungefähr 50px links. Kannst du sehen warum das so ist? Das Einstellen negativer Koordinaten hilft in diesem Fall nicht.
Snitko
Eine andere Frage: Wie öffne ich ein Fenster im Vollbildmodus?
Snitko
Ein Update: Der Versatz im Fall des zweiten Skripts scheint der Breite des Unity-Docks auf der linken Seite zu entsprechen (obwohl es ausgeblendet ist).
Snitko
Für Interessierte habe ich Desktopen implementiert: github.com/snitko/desktopen
snitko
1

Dies erweitert die großartige Antwort von @Jacob Vlijim oben mit einem leicht modifizierten setwindowSkript:

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

Eine Beschreibung der Änderungen:

  1. python3zu python(nur eine persönliche Präferenz)
  2. sys.argvum argparsefür eine Leitungsschnittstelle besser Befehls
  3. strenge Fenster-ID (und nicht Prozess-ID) Fensteranalyse
    • Einige Programme verwenden eine einzelne Prozess-ID für mehrere Fenster
  4. while Schleife 0,5 Sekunden bis 1 volle Sekunde Schlafzeit
  5. Ausführlichere / lesbarere Variablennamen und pep8-Einhaltung
  6. globale konstante Variablen für die Bildschirmgröße anstelle der xrandrAbhängigkeit

HINWEIS: Dies ist nur eine leicht verbesserte Version, die ich für den persönlichen Gebrauch auf Debian Jessie LXDE geschrieben habe. Ihre Ergebnisse können variieren.

lscstu22
quelle
0

Für Interessenten habe ich Desktopen implementiert: github.com/snitko/desktopen

Sie können ein Skript zum Öffnen von Fenstern in verschiedenen Ansichtsfenstern und Anzeigen auf sehr benutzerfreundliche Weise schreiben.

Snitko
quelle