
Esto es similar aColoque rápidamente una ventana en otra pantalla usando solo el teclado, pero quiero poder usar la línea de comando (de modo que todo lo que necesito hacer es recuperar la línea de comando del historial de bash).
Por ejemplo, enviar
- todas las ventanas de terminal de gnome a
eDP1
, - todas las ventanas de Emacs a
VGA1
y - todas las ventanas de Chrome para
HDMI1
(y maximizarlos después de moverse, pero no de la F11manera loca, la maximización normal al estilo del administrador de ventanas).
Me gustaría especificar Windows por el nombre del ejecutable.
Respuesta1
Mover todas las ventanas de una clase de ventana específica a una pantalla específica por nombre (de pantalla)
El siguiente script enviará ventanas, pertenecientes a una WM_CLASS
(aplicación) específica, a una pantalla específica, según la dirección de la pantalla.nombre. Cómo se hace esto se explica en el guión y también más adelante.
El guión supone que las pantallas están dispuestas horizontalmente y más o menos alineadas hacia arriba (con una diferencia < 100 PX).
La secuencia de comandos
#!/usr/bin/env python3
import subprocess
import sys
# just a helper function, to reduce the amount of code
get = lambda cmd: subprocess.check_output(cmd).decode("utf-8")
# get the data on all currently connected screens, their x-resolution
screendata = [l.split() for l in get(["xrandr"]).splitlines() if " connected" in l]
screendata = sum([[(w[0], s.split("+")[-2]) for s in w if s.count("+") == 2] for w in screendata], [])
def get_class(classname):
# function to get all windows that belong to a specific window class (application)
w_list = [l.split()[0] for l in get(["wmctrl", "-l"]).splitlines()]
return [w for w in w_list if classname in get(["xprop", "-id", w])]
scr = sys.argv[2]
try:
# determine the left position of the targeted screen (x)
pos = [sc for sc in screendata if sc[0] == scr][0]
except IndexError:
# warning if the screen's name is incorrect (does not exist)
print(scr, "does not exist. Check the screen name")
else:
for w in get_class(sys.argv[1]):
# first move and resize the window, to make sure it fits completely inside the targeted screen
# else the next command will fail...
subprocess.Popen(["wmctrl", "-ir", w, "-e", "0,"+str(int(pos[1])+100)+",100,300,300"])
# maximize the window on its new screen
subprocess.Popen(["xdotool", "windowsize", "-sync", w, "100%", "100%"])
Cómo utilizar
El guión necesita ambos
wmctrl
yxdotool
:sudo apt-get install xdotool wmctrl
Copie el siguiente script en un archivo vacío y guárdelo como
move_wclass.py
Ejecútelo con el comando:
python3 /path/to/move_wclass.py <WM_CLASS> <targeted_screen>
Por ejemplo:
python3 /path/to/move_wclass.py gnome-terminal VGA-1
Para el WM_CLASS
, puedes usarpartedel WM_CLASS
, como en el ejemplo. El nombre de la pantalla debe ser elexactoy nombre completo.
Cómo se hace (el concepto)
La explicación se centra principalmente en el concepto, no tanto en la codificación.
En la salida de xrandr, para cada pantalla conectada, hay una cadena/línea que se ve así:
VGA-1 connected 1280x1024+1680+0
Esta línea nos da información sobre la pantalla.posicióny esnombre, como se explicaaquí.
El script enumera la información de todas las pantallas. Cuando el script se ejecuta con la pantalla y la clase de ventana como argumentos, busca la posición (x-) de la pantalla, busca todas las ventanas (-id) de una determinada clase (con la ayuda wmctrl -l
y la salida de xprop -id <window_id>
.
Posteriormente, el script mueve todas las ventanas, una por una, a una posición en la pantalla de destino (usando wmctrl -ir <window_id> -e 0,<x>,<y>,<width>,<height>
) y la maximiza (con xdotool windowsize 100% 100%
).
Nota
El script funcionó bien en las pruebas con las que lo ejecuté. Sin embargo, el uso de wmctrl
, e incluso xdotool
, en Unity puede tener algunas peculiaridades persistentes que a veces deben resolverse mediante experimentos en lugar de razonamiento. Si puede encontrar excepciones, menciónelas.
Respuesta2
Reescribí el código Python de @jacobs en bash simple y lo hice funcionar (probé esto en Ubuntu 16 Cinnamon).
Tuve que agregar remove,maximized_vert, remove,maximized_horz
sin que las ventanas no se movieran.
#!/bin/bash
if [ ! -z "$1" ] || [ -z "$2" ]; then
command=$(wmctrl -l | grep $1 | cut -d" " -f1)
if [ ! -z "$command" ]; then
position=$(xrandr | grep "^$2" | cut -d"+" -f2)
if [ ! -z "$position" ]; then
for window in $command; do
wmctrl -ir $window -b remove,maximized_vert
wmctrl -ir $window -b remove,maximized_horz
wmctrl -ir $window -e 0,$position,0,1920,1080
wmctrl -ir $window -b add,maximized_vert
wmctrl -ir $window -b add,maximized_horz
done
else
echo -e "not found monitor with given name"
fi
else
echo -e "not found windows with given name"
fi
else
echo -e "specify window and monitor name;\nmove.sh window-name monitor-name"
fi
sudo apt-get install xdotool wmctrl
/path/to/script.sh "window-name" "monitor-name"
Respuesta3
Para que conste, esto es lo que uso para la combinación de esta pregunta yRestaurar la configuración de varios monitores:
# configure multiple displays and
# move the windows to their appropriate displays
import subprocess
import os
import wmctrl
import re
mydisplays = [("VGA1",0,"left"),
("eDP1",1080,"normal"),
("HDMI1",3000,"left")]
# https://askubuntu.com/questions/702002/restore-multiple-monitor-settings
def set_displays ():
subprocess.check_call(" && ".join([
"xrandr --output %s --pos %dx0 --rotate %s" % d for d in mydisplays]),
shell=True)
# https://askubuntu.com/questions/702071/move-windows-to-specific-screens-using-the-command-line
mywindows = [("/emacs$","VGA1"),
("/chrome$","HDMI1"),
("gnome-terminal","eDP1")]
def max_windows ():
didi = dict([(d,x) for d,x,_ in mydisplays])
for w in wmctrl.Window.list():
try:
exe = os.readlink("/proc/%d/exe" % (w.pid))
for (r,d) in mywindows:
if re.search(r,exe):
x = didi[d]
print "%s(%s) --> %s (%d)" % (r,exe,d,x)
w.set_properties(("remove","maximized_vert","maximized_horz"))
w.resize_and_move(x,0,w.w,w.h)
w.set_properties(("add","maximized_vert","maximized_horz"))
break
except OSError:
continue
def cmdlines (cmd):
return subprocess.check_output(cmd).splitlines()
def show_displays ():
for l in cmdlines(["xrandr"]):
if " connected " in l:
print l
if __name__ == '__main__':
show_displays()
set_displays()
show_displays()
max_windows()
necesitarías usarwmctrlversión 0.3 o posterior (debido a misolicitud de extracción).
Respuesta4
Según la respuesta de @AndrzejPiszczek, aquí hay una manera de mover todas las ventanas a una pantalla específica:
function move_win {
if [ -z "$1" ]; then
echo -e "Specify a screen, possible options: "
echo -e $(xrandr | grep " connected " | cut -d'-' -f1)
return
fi
MONITOR=$1
# get all relevant windows on all screens
windows=$(wmctrl -l | egrep -v " -1 " | cut -d" " -f1)
if [ ! -z "$windows" ]; then
# get the necessary metrics from the screen the windows should be moved to
# will contain: width, height, offsetX, offsetY
screen_values=($(xrandr | grep "^$MONITOR-.* connected" | grep -Eo '[0-9]+x[0-9]+\+[0-9]+\+[0-9]+' | sed 's/x/ /g; s/+/ /g'))
if (( ${#screen_values[@]} )); then
# get the start/end position of the screen so we can later determine
# if the window is already on the screen or not
screen_start_pos=$(( ${screen_values[2]} ))
screen_end_pos=$(( ${screen_values[2]} + ${screen_values[0]} ))
for window in $windows; do
# get the window name
window_name=$(wmctrl -lG | grep "$window" | awk -F "$HOSTNAME " '{print $2}')
# extract relevant window geometry values such as x, y, width, height
window_values=($(wmctrl -lG | grep "$window" | awk -F " " '{print $3, $5, $6}'))
# if the window's X origin position is already inside the screen's
# total width then don't move it (this won't work exactly for windows only partially on the screen)
if (( ${window_values[0]} >= $screen_end_pos || ${window_values[0]} < $screen_start_pos )); then
echo -e "Moving to screen $MONITOR: $window_name"
wmctrl -ir $window -b remove,maximized_vert
wmctrl -ir $window -b remove,maximized_horz
# the -e parameters are gradient,x,y,width,height
# move window to (X,Y) -> (0,0) of new screen and the same window dimensions
wmctrl -ir $window -e 0,$screen_start_pos,0,${window_values[1]},${window_values[2]}
else
echo -e "Already on screen $MONITOR: $window_name"
fi
done
else
echo -e "No screen found"
fi
else
echo -e "No windows found"
fi
}