Como posso mover uma janela de uma viewport invisível para a atual, sem alternar viewports

3

Eu uso o 15.04 com o Unity e quatro desktops virtuais.

Quando abro uma janela na área de trabalho 1 e vejo a área de trabalho 2 (por exemplo), há uma maneira de obter facilmente essa janela da área de trabalho 1 para aparecer na área de trabalho 2 sem alternar a exibição para a área de trabalho 1?

Por isso, quero obter uma janela de uma área de trabalho atualmente invisível para a minha área de trabalho ativa sem ver a área de trabalho invisível (e, eventualmente, outras janelas abertas nela).

Existe uma maneira fácil de conseguir isso?

    
por Byte Commander 18.07.2015 / 14:37

1 resposta

4

Listar janelas, escolha uma para mover para a área de trabalho atual

Quando o script abaixo é chamado, ele listará todas as janelas em todos os espaços de trabalho. Escolha uma e pressione OK para mover a janela para a área de trabalho atual e aumentá-la. Por padrão, ele move a janela para a posição 100 (x), 100 (y)

O script é relativamente simples como resultado do uso de wmctrl e xdotool . Enquanto wmctrl é usado para listar todas as janelas, xdotool simplesmente as move para uma posição predefinida na área de trabalho atual "sem fazer perguntas" no tamanho da janela (diferentemente de wmctrl ) e posições relativas de ambas as áreas de trabalho umas às outras.
Um posicionamento mais preciso da janela, de acordo com a posição em sua área de trabalho original, seria muito bem possível, mas também multiplicaria o código necessário (por exemplo, aqui ). Eu suponho que na maioria das situações isso acontecerá.

Um exemplo :

Estou na área de trabalho 8, enquanto eu tenho uma janela gedit na área de trabalho 1. Chamar o script lista as janelas:

escolhendoajaneladogeditirámovê-loparaaáreadetrabalhoatual:

O 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

Como usar

  1. O script precisa dos dois wmctrl e xdotool

    sudo apt-get install wmctrl xdotool
    
  2. copie o script em um arquivo vazio, exiba-o como move_windows.py

  3. Teste-o pelo comando:

    python3 /path/to/move_windows.py
    
  4. uma janela deve aparecer, listando as janelas abertas no momento:

    escolhaumaparaverseelaémovidaparaaáreadetrabalhoatualeaumentadacorretamente.

  5. Setudofuncionarbem,adicione-oaumatecladeatalho:escolha:Configuraçõesdosistema>"Teclado" > "Atalhos" > "Atalhos personalizados". Clique no botão "+" e adicione o comando:

    python3 /path/to/move_windows.py
    

Nota

O tamanho da janela zenity , listando as janelas atuais, é definido automaticamente. O script procura o nome da janela mais longo e o número de linhas (janelas) e define o tamanho de acordo.

EDITAR

Conforme solicitado em um comentário, abaixo de uma versão na qual a janela zenity list- inclui mais informações: a área de trabalho atual da (s) janela (s) de destino e o aplicativo ao qual ela pertence. / p>

Comomencionadoacima,asinformaçõessobreasposiçõesrelativas/absolutasdoespaçodetrabalholevamaumaquantidadedecódigomais"substancial", mas felizmente eu poderia usar essa resposta anterior como base.

Como usar

O uso é praticamente o mesmo da primeira versão do script (acima), mas o comando precisa incluir a opção de classificação preferida. Execute-o por um dos comandos:

python3 /path/to/move_windows.py -app

para classificar a lista por aplicativo,

python3 /path/to/move_windows.py -ws

para ordenar a lista por espaço de trabalho e

python3 /path/to/move_windows.py -win

para ordenar a lista pelo nome da janela.

O 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 específico

A saída do comando ps usado parece ter mudado para gnome-terminal em 15.04. Portanto, em 15.04, o nome do aplicativo de gnome-terminal não foi exibido corretamente no script acima. A versão abaixo deriva o nome do aplicativo do WM_CLASS , como na saída do comando xprop :

Ousoéexatamenteomesmoquenoscript(segundo)acima:

#!/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     
por Jacob Vlijm 20.07.2015 / 23:12