J'utilise 15.04 avec Unity et 4 bureaux virtuels.
Lorsque j'ai ouvert une fenêtre sur le bureau 1 et que je consulte le bureau 2 (par exemple), existe-t-il un moyen d'obtenir facilement cette fenêtre du bureau 1 pour qu'elle s'affiche sur le bureau 2 sans basculer la vue sur le bureau 1?
Je veux donc obtenir une fenêtre d'un bureau actuellement invisible sur mon bureau actif sans voir le bureau invisible (et éventuellement d'autres fenêtres ouvertes sur celui-ci).
Existe-t-il un moyen simple d'y parvenir?
Lorsque le script ci-dessous est appelé, il répertorie toutes les fenêtres de tous les espaces de travail. Choisissez-en un et appuyez sur OK pour déplacer la fenêtre vers l'espace de travail actuel et l'augmenter. Par défaut, il déplace la fenêtre en position 100
(X), 100
(y)
Le script est relativement simple grâce à l'utilisation de wmctrl
et xdotool
. Alors que wmctrl
est utilisé pour répertorier toutes les fenêtres, xdotool
les déplace simplement vers une position prédéfinie dans l'espace de travail actuel "sans poser de questions" sur la taille de la fenêtre (contrairement à wmctrl
) et positions relatives des deux espaces de travail.
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 ici ). Je suppose que dans la plupart des situations, cela suffira.
Un exemple :
Je suis sur l'espace de travail 8, alors que j'ai une fenêtre gedit
sur l'espace de travail 1. L'appel du script répertorie les fenêtres:
choisir la fenêtre gedit la déplacera dans l'espace de travail courant:
#!/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
Le script nécessite à la fois wmctrl
et xdotool
Sudo apt-get install wmctrl xdotool
copiez le script dans un fichier vide, enregistrez-le comme move_windows.py
Testez-le par la commande:
python3 /path/to/move_windows.py
une fenêtre devrait apparaître, listant les fenêtres actuellement ouvertes:
choisissez-en un pour voir s'il est déplacé vers l'espace de travail actuel et correctement relevé.
Si tout fonctionne bien, ajoutez-le à 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
La taille de la fenêtre zenity
, répertoriant les fenêtres actuelles, est définie automatiquement. Le script recherche le nom de fenêtre le plus long et le nombre de lignes (fenêtres) et définit la taille en conséquence.
Comme demandé dans un commentaire, sous une version dans laquelle la fenêtre de liste zenity
contient plus d'informations: de espace de travail actuel de la fenêtre ciblée (s) et l'application à laquelle il appartient.
Comme mentionné ci-dessus, les informations sur les positions d'espace de travail relatives/absolues conduisent à une quantité de code plus "substantielle", mais heureusement, j'ai pu utiliser cette réponse précédente comme base.
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-le par l'une des commandes:
python3 /path/to/move_windows.py -app
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.
#!/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
La sortie de la commande ps
utilisée semble avoir changé pour gnome-terminal
au 15.04. Par conséquent, en 15.04, le nom d'application de gnome-terminal
ne s'est pas affiché correctement dans le script ci-dessus. La version ci-dessous dérive le nom de l'application du WM_CLASS
, comme dans la sortie de la commande xprop
:
L'utilisation est exactement la même que dans le (second) 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