Liste des fenêtres, choisissez-en une pour la déplacer vers l'espace de travail actuel.
Lorsque le script ci-dessous est appelé, il listera toutes les fenêtres sur tous les espaces de travail. Choisissez-en une et appuyez sur OK pour déplacer la fenêtre dans l'espace de travail actuel et l'élever. Par défaut, il déplace la fenêtre à la position 100
(x), 100
(y)
Le script est relativement simple en raison de l'utilisation des deux éléments suivants wmctrl
y xdotool
. Alors que wmctrl
est utilisé pour répertorier tous les Windows, xdotool
les déplace simplement à une position prédéfinie sur l'espace de travail actuel "sans poser de questions" sur la taille de la fenêtre (contrairement à l'option wmctrl
) et les positions relatives des deux espaces de travail l'un par rapport à l'autre.
Un positionnement plus précis de la fenêtre, en fonction de la position sur son espace de travail d'origine, serait très bien possible, mais multiplierait également le code nécessaire (comme par exemple aquí ). Je suppose que dans la plupart des situations, cela fera l'affaire.
Un exemple :
Je suis sur workspace 8, alors que j'ai une gedit
sur l'espace de travail 1. L'appel du script liste les fenêtres :
en choisissant la fenêtre gedit, vous la déplacerez dans l'espace de travail actuel :
Le script
#!/usr/bin/env python3
import subprocess
import socket
import time
def get(command):
return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8")
def check_window(w_id):
w_type = get("xprop -id "+w_id)
if " _NET_WM_WINDOW_TYPE_NORMAL" in w_type:
return True
else:
return False
# split wmctrl output by machine name
wlist = [l.split(socket.gethostname()) for l in get("wmctrl -l").splitlines()]
# extract window -id from first section
wlist = [[wlist[i][0].split()[0], wlist[i][-1].strip()] for i, l in enumerate(wlist)]
# filter only "real, normal" windows
wlist = [w for w in wlist if check_window(w[0]) == True]
# create columns for zenity list
cols = (" ").join(['"'+w[1]+'" '+'"'+w[0]+'"' for w in wlist])
# calculate height and width for the zenity window, according to window names and list length
h = str(140+(len(wlist)*23))
w = str((max([len(w[-1]) for w in wlist])*8))
# define the zenity window
cmd = "zenity --list --hide-column=2 --print-column=2 "+\
"--title='Window list' --column='Current windowlist' "+\
"--column='wid' --height="+h+" --width="+w+" "+cols
try:
# call the window
w_id = get(cmd).split("|")[-1].strip()
# move the selected window to the current workspace
subprocess.Popen(["xdotool", "windowmove", "--sync", w_id, "100", "100"])
# raise it (the command below alone should do the job, but sometimes fails
# on firefox windows without first moving the window).
subprocess.Popen(["wmctrl", "-iR", w_id])
except subprocess.CalledProcessError:
pass
Mode d'emploi
-
Le script a besoin à la fois de wmctrl
y xdotool
sudo apt-get install wmctrl xdotool
-
copier le script dans un fichier vide, le sauvegarder en tant que move_windows.py
-
Testez-le par la commande :
python3 /path/to/move_windows.py
-
une fenêtre devrait apparaître, énumérant les fenêtres actuellement ouvertes :
Choisissez-en un pour voir s'il est déplacé vers l'espace de travail actuel et s'il est élevé correctement.
-
Si tout fonctionne bien, ajoutez-la à une touche de raccourci : choisissez : Paramètres système > "Clavier" > "Raccourcis" > "Raccourcis personnalisés". Cliquez sur le "+" et ajoutez la commande :
python3 /path/to/move_windows.py
Note
La taille de la zenity
La fenêtre, qui énumère les fenêtres en cours, est définie automatiquement. Le script recherche le nom de fenêtre le plus long et le nombre de rangées (Windows) et définit la taille en conséquence.
EDIT
Comme demandé dans un commentaire, ci-dessous une version dans laquelle le zenity
La fenêtre Liste comprend plus d'informations : de actuel l'espace de travail de la ou des fenêtres ciblées et de l'application à laquelle elle appartient.
Comme mentionné ci-dessus, les informations sur les positions relatives/absolues de l'espace de travail conduisent à une quantité plus "substantielle" de code, mais heureusement, j'ai pu utiliser cette réponse précédente comme base.
Mode d'emploi
L'utilisation est à peu près la même que la première version du script (ci-dessus), mais la commande doit inclure l'option de tri préférée. Exécutez-la par l'une des commandes :
python3 /path/to/move_windows.py -app
pour trier la liste par application,
python3 /path/to/move_windows.py -ws
pour trier la liste par espace de travail, et
python3 /path/to/move_windows.py -win
pour trier la liste par nom de fenêtre.
Le script :
#!/usr/bin/env python3
import subprocess
import socket
import sys
arg = sys.argv[1]
# list (column) header titles and their (data) position in the produced window data list
cols = [["Workspace", -1], ["Application name", -2] , ["Window name", -3]]
# rearrange columns, depending on the chosen option
if arg == "-app":
cols = [cols[1], cols[2], cols[0]]
elif arg == "-ws":
cols = [cols[0], cols[2], cols[1]]
elif arg == "-win":
cols = [cols[2], cols[1], cols[0]]
# extract headers, list positions, to be used in the zenity list
col1 = cols[0][0]; i1 = cols[0][1]
col2 = cols[1][0]; i2 = cols[1][1]
col3 = cols[2][0]; i3 = cols[2][1]
# just a helper function
get = lambda cmd: subprocess.check_output([
"/bin/bash", "-c", cmd
]).decode("utf-8")
# analyse viewport data, to be able to calculate relative/absolute position of windows
# and current viewport
def get_spandata():
xr = get("xrandr").split(); pos = xr.index("current")
res = [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]
spandata = get("wmctrl -d").split()
span = [int(n) for n in spandata[3].split("x")]
cols = int(span[0]/res[0]); rows = int(span[1]/res[1])
curr_vector = [int(n) for n in spandata[5].split(",")]
curr_viewport = int((curr_vector[1]/res[1])*cols + (curr_vector[0]/res[0])+1)
return {"resolution": res, "n_columns": cols, "vector": curr_vector, "current_viewport": curr_viewport}
posdata = get_spandata()
vector = posdata["vector"]; cols = posdata["n_columns"]
res = posdata["resolution"]; currvp = posdata["current_viewport"]
# function to distinguish "normal" windows from other types (like the desktop etc)
def check_window(w_id):
w_type = get("xprop -id "+w_id)
if " _NET_WM_WINDOW_TYPE_NORMAL" in w_type:
return True
else:
return False
# split windowdata by machine name
mach_name = socket.gethostname()
wlist = [[l.strip() for l in w.split(mach_name)] for w in get("wmctrl -lpG").splitlines()]
# split first section of window data
for i, w in enumerate(wlist):
wlist[i][0] = wlist[i][0].split()
# filter only "real" windows
real_wlist = [w for w in wlist if check_window(w[0][0]) == True]
# adding the viewport to the window's data
for w in real_wlist:
w.append(get("ps -p "+w[0][2]+" -o comm=").strip())
loc_rel = [int(n) for n in w[0][3:5]]
loc_abs = [loc_rel[0]+vector[0], loc_rel[1]+vector[1]]
abs_viewport = int((loc_abs[1]/res[1])*cols + (loc_abs[0]/res[0])+1)
abs_viewport = str(abs_viewport)+"*" if abs_viewport == currvp else str(abs_viewport)
w.append(abs_viewport)
# set sorting rules
if arg == "-app":
real_wlist.sort(key=lambda x: x[-2])
elif arg == "-ws":
real_wlist.sort(key=lambda x: x[-1])
elif arg == "-win":
real_wlist.sort(key=lambda x: x[-3])
# calculate width and height of the zenity window:
# height = 140px + 23px per line
h = str(140+(len(real_wlist)*23))
# width = 250px + 8px per character (of the longest window title)
w = str(250+(max([len(w[-3]) for w in real_wlist])*8))
# define the zenity window's content
cmd = "zenity --list --hide-column=4 --print-column=4 --title='Window list' "\
"--width="+w+" --height="+h+" --column='"+col1+"' --column='"+col2+"' --column='"+col3+\
"' --column='w_id' "+(" ").join([(" ").join([
'"'+w[i1]+'"','"'+w[i2]+'"','"'+w[i3]+'"','"'+w[0][0]+'"'
]) for w in real_wlist])
# finally, call the window list
try:
w_id = subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8").split("|")[0]
subprocess.Popen(["xdotool", "windowmove", "--sync", w_id, "100", "100"])
subprocess.Popen(["wmctrl", "-iR", w_id])
except subprocess.CalledProcessError:
pass
EDIT 2 : 15.04 spécifique
La sortie de l'outil utilisé ps
semble avoir changé pour gnome-terminal
en 15.04. Par conséquent, en 15.04, le nom de l'application de gnome-terminal
n'était pas affiché correctement dans le script ci-dessus. La version ci-dessous dérive le nom de l'application à partir de l'élément WM_CLASS
comme dans la sortie du xprop
commandement :
L'utilisation est exactement la même que dans le (deuxième) script ci-dessus :
#!/usr/bin/env python3
import subprocess
import socket
import sys
arg = sys.argv[1]
# list (column) header titles and their (data) position in the produced window data list
cols = [["Workspace", -1], ["Application name", -2] , ["Window name", -3]]
# rearrange columns, depending on the chosen option
if arg == "-app":
cols = [cols[1], cols[2], cols[0]]
elif arg == "-ws":
cols = [cols[0], cols[2], cols[1]]
elif arg == "-win":
cols = [cols[2], cols[1], cols[0]]
# extract headers, list positions, to be used in the zenity list
col1 = cols[0][0]; i1 = cols[0][1]
col2 = cols[1][0]; i2 = cols[1][1]
col3 = cols[2][0]; i3 = cols[2][1]
# just a helper function
get = lambda cmd: subprocess.check_output([
"/bin/bash", "-c", cmd
]).decode("utf-8")
# analyse viewport data, to be able to calculate relative/absolute position of windows
# and current viewport
def get_spandata():
xr = get("xrandr").split(); pos = xr.index("current")
res = [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]
spandata = get("wmctrl -d").split()
span = [int(n) for n in spandata[3].split("x")]
cols = int(span[0]/res[0]); rows = int(span[1]/res[1])
curr_vector = [int(n) for n in spandata[5].split(",")]
curr_viewport = int((curr_vector[1]/res[1])*cols + (curr_vector[0]/res[0])+1)
return {"resolution": res, "n_columns": cols, "vector": curr_vector, "current_viewport": curr_viewport}
posdata = get_spandata()
vector = posdata["vector"]; cols = posdata["n_columns"]
res = posdata["resolution"]; currvp = posdata["current_viewport"]
# function to distinguish "normal" windows from other types (like the desktop etc)
def check_window(w_id):
w_type = get("xprop -id "+w_id)
if " _NET_WM_WINDOW_TYPE_NORMAL" in w_type:
cl = [l.replace('"', '').split(",")[-1].strip()\
for l in w_type.splitlines() if "WM_CLASS(STRING)" in l][0]
return (True, cl)
else:
return (False, "")
# split windowdata by machine name
mach_name = socket.gethostname()
wlist = [[l.strip() for l in w.split(mach_name)] for w in get("wmctrl -lpG").splitlines()]
# split first section of window data
for i, w in enumerate(wlist):
wlist[i][0] = wlist[i][0].split()
# filter only "real" windows
real_wlist = [w+[check_window(w[0][0])[1]] for w in wlist if check_window(w[0][0])[0] == True]
# adding the viewport to the window's data
for w in real_wlist:
loc_rel = [int(n) for n in w[0][3:5]]
loc_abs = [loc_rel[0]+vector[0], loc_rel[1]+vector[1]]
abs_viewport = int((loc_abs[1]/res[1])*cols + (loc_abs[0]/res[0])+1)
abs_viewport = str(abs_viewport)+"*" if abs_viewport == currvp else str(abs_viewport)
w.append(abs_viewport)
# set sorting rules
if arg == "-app":
real_wlist.sort(key=lambda x: x[-2])
elif arg == "-ws":
real_wlist.sort(key=lambda x: x[-1])
elif arg == "-win":
real_wlist.sort(key=lambda x: x[-3])
# calculate width and height of the zenity window:
# height = 140px + 23px per line
h = str(140+(len(real_wlist)*23))
# width = 250px + 8px per character (of the longest window title)
w = str(250+(max([len(w[-3]) for w in real_wlist])*8))
# define the zenity window's content
cmd = "zenity --list --hide-column=4 --print-column=4 --title='Window list' "\
"--width="+w+" --height="+h+" --column='"+col1+"' --column='"+col2+"' --column='"+col3+\
"' --column='w_id' "+(" ").join([(" ").join([
'"'+w[i1]+'"','"'+w[i2]+'"','"'+w[i3]+'"','"'+w[0][0]+'"'
]) for w in real_wlist])
# finally, call the window list
try:
w_id = subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8").split("|")[0]
subprocess.Popen(["xdotool", "windowmove", "--sync", w_id, "100", "100"])
subprocess.Popen(["wmctrl", "-iR", w_id])
except subprocess.CalledProcessError:
pass