如何在不切换视口的情况下将窗口从不可见视口移动到当前视口

我使用15.04与Unity和4个虚拟桌面。

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

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

有没有一种简单的方法来实现这一目标?

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

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

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

一个例子

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

在此处输入图像描述

选择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. 该脚本需要wmctrlxdotool

     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列表窗口包含更多信息的版本下方:目标窗口的当前工作空间及其所属的应用程序。

在此处输入图像描述

如上所述,关于相对/绝对工作空间位置的信息导致更“实质”的代码量,但幸运的是,我可以使用此早期答案作为基础。

如何使用

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

 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中的gnome-terminal ,使用的ps命令的输出似乎已经改变。 因此,在15.04中, gnome-terminal的应用程序名称未在上面的脚本中正确显示。 下面的版本从WM_CLASS中导出应用程序名称,如xprop命令的输出:

在此处输入图像描述

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

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