如何将窗口从不可见的视口移动到当前视口,而无需切换视口

如何将窗口从不可见的视口移动到当前视口,而无需切换视口

我使用带有 Unity 和 4 个虚拟桌面的 15.04。

当我在桌面 1 上打开一个窗口并正在查看桌面 2(例如)时,有没有办法轻松地让桌面 1 上的窗口显示在桌面 2 上,而无需将视图切换到桌面 1?

因此,我想从当前不可见的桌面获取一个窗口到我的活动桌面,而不需要看到不可见的桌面(以及最终在该桌面上打开的其他窗口)。

有没有简单的方法可以实现这一点?

答案1

列出窗口,选择一个移动到当前工作区

调用以下脚本时,它将列出所有工作区上的所有窗口。选择一个并按下OK以将窗口移动到当前工作区并将其提升。默认情况下,它会将窗口移动到位置100(x)、100(y)

由于使用了wmctrl和 ,因此脚本相对简单xdotool。虽然wmctrl用于列出所有窗口,但xdotool只需将它们移动到当前工作区上的预定义位置,“而无需询问”窗口的大小(与 不同wmctrl)以及两个工作区之间的相对位置。
根据窗口在原始工作区上的位置,可以更精确地定位窗口,但这也会增加所需的代码(例如这里)我认为在大多数情况下,这样做就可以了。

一个例子

我在工作区 8 上,而在工作区 1 上有一个gedit窗口。调用脚本会列出窗口:

在此处输入图片描述

选择 gedit 窗口将会把它移动到当前工作区:

在此处输入图片描述

剧本

#!/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

如何使用

  1. 脚本wmctrl需要xdotool

    sudo apt-get install wmctrl xdotool
    
  2. 将脚本复制到一个空文件中,保存为move_windows.py

  3. 通过命令测试运行:

    python3 /path/to/move_windows.py
    
  4. 应该出现一个窗口,列出当前打开的窗口:

    在此处输入图片描述

    选择一个,看看它是否被移动到当前工作区并正确提升。

  5. 如果一切正常,将其添加到快捷键:选择:系统设置>“键盘”>“快捷键”>“自定义快捷键”。单击“+”并添加命令:

    python3 /path/to/move_windows.py
    

笔记

列出当前窗口的窗口大小zenity是自动设置的。脚本会查找最长的窗口名称和行数(窗口),并相应地设置大小。


编辑

根据评论中的要求,以下版本中的zenity列表窗口包含更多信息:de当前的目标窗口及其所属应用程序的工作区。

在此处输入图片描述

如上所述,有关相对/绝对工作区位置的信息会导致更多“大量”的代码,但幸运的是我可以使用这个先前的答案作为基础。

如何使用

使用方法与脚本的第一个版本(上面)基本相同,但命令需要包含首选排序选项。使用以下命令之一运行它:

python3 /path/to/move_windows.py -app

按应用程序对列表进行排序,

python3 /path/to/move_windows.py -ws

按工作空间对列表进行排序,以及

python3 /path/to/move_windows.py -win

按窗口名称对列表进行排序。

剧本:

#!/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

编辑 2:特定于 15.04

在 15.04 中,所用命令的输出ps似乎已更改。因此,在 15.04 中,上面的脚本中无法正确显示的应用程序名称。以下版本从中获取应用程序名称 ,如命令的输出所示:gnome-terminalgnome-terminalWM_CLASSxprop

在此处输入图片描述

用法与上面(第二个)脚本完全相同:

#!/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

相关内容