WICHTIGE BEARBEITUNG
Unten eine umgeschriebene Version des Skripts aus der ersten Antwort (unten). Die Unterschiede:
Das Skript
#!/usr/bin/env python3
import subprocess
import sys
import time
import math
app_class = sys.argv[1]
ws_lock = [int(n)-1 for n in sys.argv[2].split(",")]
def check_wlist():
# get the current list of windows
try:
raw_list = [
l.split() for l in subprocess.check_output(
["wmctrl", "-lG"]
).decode("utf-8").splitlines()
]
ids = [l[0] for l in raw_list]
return (raw_list, ids)
except subprocess.CalledProcessError:
pass
def get_wssize():
# get workspace size
resdata = subprocess.check_output(["xrandr"]).decode("utf-8").split()
i = resdata.index("current")
return [int(n) for n in [resdata[i+1], resdata[i+3].replace(",", "")]]
def get_current(ws_size):
# vector of the current workspace to origin of the spanning desktop
dt_data = subprocess.check_output(
["wmctrl", "-d"]
).decode("utf-8").split()
curr = [int(n) for n in dt_data[5].split(",")]
return (int(curr[0]/ws_size[0]), int(curr[1]/ws_size[1]))
def get_relativewinpos(ws_size, w_data):
# vector to the application window, relative to the current workspace
xpos = int(w_data[2]); ypos = int(w_data[3])
xw = ws_size[0]; yw = ws_size[1]
return (math.ceil((xpos-xw)/xw), math.ceil((ypos-yw)/yw))
def get_abswindowpos(ws_size, w_data):
# vector from the origin to the current window's workspace (flipped y-axis)
curr_pos = get_current(ws_size)
w_pos = get_relativewinpos(ws_size, w_data)
return (curr_pos[0]+w_pos[0], curr_pos[1]+w_pos[1])
def wm_class(w_id):
# get the WM_CLASS of new windows
return subprocess.check_output(
["xprop", "-id", w_id.strip(), "WM_CLASS"]
).decode("utf-8").split("=")[-1].strip()
ws_size = get_wssize()
wlist1 = []
subprocess.Popen(["notify-send", 'workspace lock is running for '+app_class])
while True:
# check focussed window ('except' for errors during "wild" workspace change)
try:
focus = subprocess.check_output(
["xdotool", "getwindowfocus"]
).decode("utf-8")
except subprocess.CalledProcessError:
pass
time.sleep(1)
wdata = check_wlist()
if wdata != None:
# compare existing window- ids, checking for new ones
wlist2 = wdata[1]
if wlist2 != wlist1:
# if so, check the new window's class
newlist = [[w, wm_class(w)] for w in wlist2 if not w in wlist1]
valids = sum([[l for l in wdata[0] if l[0] == w[0]] \
for w in newlist if app_class in w[1]], [])
# for matching windows, check if they need to be moved (check workspace)
for w in valids:
abspos = list(get_abswindowpos(ws_size, w))
if not abspos == ws_lock:
current = get_current(ws_size)
move = (
(ws_lock[0]-current[0])*ws_size[0],
(ws_lock[1]-current[1])*ws_size[1]-56
)
new_w = "wmctrl -ir "+w[0]+" -e "+(",").join(
["0", str(int(w[2])+move[0]),
str(int(w[2])+move[1]), w[4], w[5]]
)
subprocess.call(["/bin/bash", "-c", new_w])
# re- focus on the window that was focussed
if not app_class in wm_class(focus):
subprocess.Popen(["wmctrl", "-ia", focus])
wlist1 = wlist2
Wie benutzt man
Das Skript benötigt beides wmctrl
und xdotool
:
sudo apt-get install wmctrl xdotool
Kopieren Sie das obige Skript in eine leere Datei und speichern Sie es unter lock_towspace.py
Finden Sie unter Ihrer spezifischen Anwendung Folgendes heraus WM_CLASS
: Öffnen Sie Ihre Anwendung, führen Sie sie in einem Terminal aus:
xprop WM_CLASS and click on the window of the application
Die Ausgabe sieht wie folgt aus (in Ihrem Fall):
WM_CLASS: WM_CLASS(STRING) = "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
Verwenden Sie entweder den ersten oder den zweiten Teil des Befehls, um das Skript auszuführen.
Der Befehl zum Ausführen des Skripts lautet dann:
python3 /path/to/lock_towspace.py "sun-awt-X11-XFramePeer" 2,2
Im Befehl der letzte Abschnitt; 2,2
ist der Arbeitsbereich, in dem Sie die Anwendung (ohne Leerzeichen: (!) Spalte, Zeile ) im "menschlichen" Format sperren möchten ; Die erste Spalte / Zeile ist1,1
- Testen Sie das Skript, indem Sie es ausführen. Öffnen Sie während der Ausführung Ihre Anwendung und lassen Sie sie wie gewohnt Fenster erstellen. Alle Fenster sollten im Zielarbeitsbereich angezeigt werden, wie im Befehl festgelegt.
Veraltete Antwort:
(zweite) TESTVERSION
Das folgende Skript sperrt eine bestimmte Anwendung an ihren ursprünglichen Arbeitsbereich. Wenn das Skript gestartet wird, bestimmt es, auf welchem Arbeitsbereich sich die Anwendung befindet. Alle zusätzlichen Fenster, die die Anwendung erstellt, werden in Sekundenbruchteilen in denselben Arbeitsbereich verschoben.
Das Fokusproblem wird gelöst, indem automatisch auf das Fenster fokussiert wird, das vor der Erstellung des zusätzlichen Fensters fokussiert wurde.
Das Skript
#!/usr/bin/env python3
import subprocess
import time
import math
app_class = '"gedit", "Gedit"'
def get_wssize():
# get workspace size
resdata = subprocess.check_output(["xrandr"]).decode("utf-8").split()
i = resdata.index("current")
return [int(n) for n in [resdata[i+1], resdata[i+3].replace(",", "")]]
def get_current(ws_size):
# get vector of the current workspace to the origin of the spanning desktop (flipped y-axis)
dt_data = subprocess.check_output(["wmctrl", "-d"]).decode("utf-8").split(); curr = [int(n) for n in dt_data[5].split(",")]
return (int(curr[0]/ws_size[0]), int(curr[1]/ws_size[1]))
def get_relativewinpos(ws_size, w_data):
# vector to the application window, relative to the current workspace
xw = ws_size[0]; yw = ws_size[1]
return (math.ceil((w_data[1]-xw)/xw), math.ceil((w_data[2]-yw)/yw))
def get_abswindowpos(ws_size, w_data):
curr_pos = get_current(ws_size)
w_pos = get_relativewinpos(ws_size, w_data)
return (curr_pos[0]+w_pos[0], curr_pos[1]+w_pos[1])
def wm_class(w_id):
return subprocess.check_output(["xprop", "-id", w_id, "WM_CLASS"]).decode("utf-8").split("=")[-1].strip()
def filter_windows(app_class):
# find windows (id, x_pos, y_pos) of app_class
try:
raw_list = [l.split() for l in subprocess.check_output(["wmctrl", "-lG"]).decode("utf-8").splitlines()]
return [(l[0], int(l[2]), int(l[3]), l[4], l[5]) for l in raw_list if wm_class(l[0]) == app_class]
except subprocess.CalledProcessError:
pass
ws_size = get_wssize()
init_window = get_abswindowpos(ws_size, filter_windows(app_class)[0])
valid_windows1 = filter_windows(app_class)
while True:
focus = subprocess.check_output(["xdotool", "getwindowfocus"]).decode("utf-8")
time.sleep(1)
valid_windows2 = filter_windows(app_class)
if all([valid_windows2 != None, valid_windows2 != valid_windows1]):
for t in [t for t in valid_windows2 if not t[0] in [w[0] for w in valid_windows1]]:
absolute = get_abswindowpos(ws_size, t)
if not absolute == init_window:
current = get_current(ws_size)
move = ((init_window[0]-current[0])*ws_size[0], (init_window[1]-current[1])*ws_size[1]-56)
new_w = "wmctrl -ir "+t[0]+" -e "+(",").join(["0", str(t[1]+move[0]), str(t[2]+move[1]), t[3], t[4]])
subprocess.call(["/bin/bash", "-c", new_w])
focus = str(hex(int(focus)))
z = 10-len(focus); focus = focus[:2]+z*"0"+focus[2:]
if not wm_class(focus) == app_class:
subprocess.Popen(["wmctrl", "-ia", focus])
valid_windows1 = valid_windows2
Wie benutzt man
Das Skript benötigt sowohl wmctrl
undxdotool
sudo apt-get install wmctrl xdotool
Kopieren Sie das Skript in eine leere Datei und speichern Sie es unter keep_workspace.py
Bestimmen Sie die WM_CLASS Ihrer Anwendung, indem Sie die Anwendung öffnen, dann ein Terminal öffnen und den folgenden Befehl ausführen:
xprop WM_CLASS
Klicken Sie dann auf das Fenster Ihrer Anwendung. Kopieren Sie die Ausgabe wie "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
in Ihrem Fall und platzieren Sie sie wie angegeben zwischen einfachen Anführungszeichen im Kopfbereich des Skripts.
Führen Sie das Skript mit dem folgenden Befehl aus:
python3 /path/to/keep_workspace.py
Wenn es so funktioniert, wie Sie möchten, füge ich eine Umschaltfunktion hinzu. Obwohl es auf meinem System bereits einige Stunden funktioniert, muss es möglicherweise zuerst angepasst werden.
Anmerkungen
Auch wenn Sie es nicht bemerken sollte, wird das Skript tut etwas Prozessorlast zum System hinzuzufügen. Bei meinem älteren System bemerkte ich einen Anstieg von 3-10%. Wenn Ihnen die Funktionsweise gefällt, werde ich sie wahrscheinlich weiter optimieren, um die Last zu reduzieren.
Das Skript geht davon aus, dass die sekundären Fenster zur selben Klasse gehören wie das Hauptfenster, wie Sie in einem Kommentar angegeben haben. Mit einem (sehr) einfache Änderung, die sekundären Fenster können jedoch einer anderen Klasse sein.
Erläuterung
Obwohl das Skript für einen durchschnittlichen Leser wahrscheinlich nicht sehr interessant ist, berechnet es in Vektoren. Beim Start berechnet das Skript:
- der Vektor vom Ursprung zum aktuellen Arbeitsbereich mit der Ausgabe von
wmctrl -d
- der Vektor zum Fenster der Anwendung, relativ zum aktuellen Arbeitsbereich, durch die Ausgabe von
wmctrl -lG
- Aus diesen beiden berechnet das Skript die absolute Position des Anwendungsfensters auf dem übergreifenden Desktop (alle Arbeitsbereiche in einer Matrix).
Von da an sucht das Skript nach neuen Fenstern derselben Anwendung mit der Ausgabe von xprop WM_CLASS
, sucht ihre Position auf die gleiche Weise wie oben und verschiebt sie in den "ursprünglichen" Arbeitsbereich.
Da das neu erstellte Fenster den Fokus aus dem zuletzt verwendeten Fenster "gestohlen" hat, an dem der Benutzer gearbeitet hat, wird der Fokus anschließend auf das Fenster gesetzt, das zuvor den Fokus hatte.