如何显示(提升)应用程序的所有窗口?

我有一个使用多个窗口的应用程序。 如何快速将该应用程序的所有窗口都置于前台?

当我使用滚轮滚动应用程序时,它只显示一个窗口。 进入下一个窗口时,最后一个窗口再次进入后台。

当我点击应用程序图标时,我会看到所有窗口的全屏概览。 我必须手动选择每个窗口并将鼠标移动到屏幕的一半上几次。

到目前为止,我最好的解决方案是最小化所有窗口( Ctrl + Super + D ),然后使用滚轮显示我的应用程序的窗口。

有更好的解决方案吗?

编辑 – 新答案 –

下面的答案仍然是完全有效的,所以建议的选项。 然而,正在进行的洞察使我添加此选项以使用下面的指标,这可能是最优雅的解决方案。

因此,它应该替换选项5(使用.desktop文件)。

只需从列表中选择应用程序,相应应用程序的所有窗口(存在于当前视口中)将引发:

在此处输入图像描述

如何使用

来自ppa:

sudo add-apt-repository ppa:vlijm/upfront sudo apt-get update sudo apt-get install upfront 

……或手动:

 #!/usr/bin/env python3 import signal import gi gi.require_version('Gtk', '3.0') gi.require_version('AppIndicator3', '0.1') from gi.repository import Gtk, AppIndicator3, GObject import time from threading import Thread import os import subprocess import getpass currpath = os.path.dirname(os.path.realpath(__file__)) class Indicator(): def __init__(self): self.app = 'raise_apps' iconpath = os.path.join(currpath, "raise.png") self.indicator = AppIndicator3.Indicator.new( self.app, iconpath, AppIndicator3.IndicatorCategory.OTHER) self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) self.indicator.set_menu(self.create_menu()) # the thread: self.update = Thread(target=self.check_recent) # daemonize the thread to make the indicator stopable self.update.setDaemon(True) self.update.start() def create_menu(self): # creates the (initial) menu self.menu = Gtk.Menu() # separator initial = Gtk.MenuItem("Fetching list...") menu_sep = Gtk.SeparatorMenuItem() self.menu.append(initial) self.menu.append(menu_sep) # item_quit.show() self.menu.show_all() return self.menu def raise_wins(self, *args): index = self.menu.get_children().index(self.menu.get_active()) selection = self.menu_items2[index][1] for w in selection: execute(["wmctrl", "-ia", w]) def set_new(self): # update the list, appearing in the menu for i in self.menu.get_children(): self.menu.remove(i) for app in self.menu_items2: sub = Gtk.MenuItem(app[0]) self.menu.append(sub) sub.connect('activate', self.raise_wins) # separator menu_sep = Gtk.SeparatorMenuItem() self.menu.append(menu_sep) # quit item_quit = Gtk.MenuItem('Quit') item_quit.connect('activate', self.stop) self.menu.append(item_quit) self.menu.show_all() def get_apps(self): # calculate screen resolution res_output = get("xrandr").split(); idf = res_output.index("current") res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", ""))) # creating window list on current viewport / id's / application names w_data = [l.split() for l in get(["wmctrl", "-lpG"]).splitlines()] # windows on current viewport relevant = [w for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]] # pids pids = [l.split() for l in get(["ps", "-u", getpass.getuser()]).splitlines()] matches = [[p[-1], [w[0] for w in relevant if w[2] == p[0]]] for p in pids] return [m for m in matches if m[1]] def check_recent(self): self.menu_items1 = [] while True: time.sleep(4) self.menu_items2 = self.get_apps() for app in self.menu_items2: app[0] = "gnome-terminal" if "gnome-terminal" in app[0] else app[0] if self.menu_items2 != self.menu_items1: GObject.idle_add( self.set_new, priority=GObject.PRIORITY_DEFAULT ) self.menu_items1 = self.menu_items2 def stop(self, source): Gtk.main_quit() def get(command): return subprocess.check_output(command).decode("utf-8") def execute(command): subprocess.Popen(command) Indicator() GObject.threads_init() signal.signal(signal.SIGINT, signal.SIG_DFL) Gtk.main() 
  • 指标需要wmctrl

     sudo apt-get wmctrl 
  • 将指标复制到空文件中,将其另存为raise_apps.py

  • 复制下面的图像,将其名称与 raise.png 完全相同 ,并保存在指标相同的目录中。

    在此处输入图像描述

  • 然后只需通过命令运行它:

    python3 /path/to/raise_apps.py

  • 如果要启动应用程序,请添加:

     /bin/bash -c "sleep 10 && python3 /path/to/raise_apps.py" 

老答案:

关于这个问题

使用正确的工具,“仅”提升应用程序的所有窗口并不是非常复杂。 确保引发当前视口的窗口有点复杂。 然而,真正的挑战是找到一种方便的方法来使用户可以使用该操作。

以下五个选项可以解决这个问题,以展示如何做到这一点。 所有选项都可以使用了。 然而,最后一种选择是实验性的; 它工作正常,但有一些小的外观缺点,如选项说明中所述。 我添加它作为一个概念

正如评论中所建议的那样,以非重叠的方式自动传播窗口对我来说似乎不是一个实用的想法; 如果您在(应用程序方面)分组窗口设置中工作,脚本可能会不必要地重新排列窗口。

如何使用

对于您需要的所有选项:

  • 如果你的系统上还没有安装wmctrl

     sudo apt-get install wmctrl 
  • 创建,如果它还不存在,目录:

     ~/bin 

    (解释:目录~/bin在$ PATH中,因此您可以按名称运行可执行文件)

  • 复制与该选项对应的脚本,将其粘贴到空文件中,将其保存为~/bin raise_app (无扩展名)并使其可执行

在单独的选项中,将解释可能的附加步骤。

选项1:通过输入一个或多个字符来选择应用程序

  • 按键组合,将出现zenity窗口
  • 在输入框中输入应用程序名称的一个或多个字符
  • 按enter键

这将使匹配应用程序的所有窗口(在当前视口上)出现在前面。

在当前视口上引发所有gnome-terminal窗口:

在此处输入图像描述

在此处输入图像描述

如何使用:

  • 按照“如何使用”中的描述进行设置
  • 通过命令测试运行它:

     raise_app 
  • 如果一切正常,请将其添加到您选择的快捷键组合中:选择:系统设置>“键盘”>“快捷方式”>“自定义快捷方式”。 单击“+”并添加命令

剧本:

 #!/usr/bin/env python3 import subprocess import getpass def get(command): return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8") def execute(command): subprocess.Popen(["/bin/bash", "-c", command]) # calculate screen resolution res_output = get("xrandr").split(); idf = res_output.index("current") res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", ""))) # creating window list on current viewport / id's / application names w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()] windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]] for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]] # ask user for first characters try: arg = get('zenity --entry --text "first characters" --title "application"').strip() except subprocess.CalledProcessError: pass # raise matching windows try: [execute("wmctrl -ia "+item[1]) for item in windows if item[0].startswith(arg)] except (subprocess.CalledProcessError, NameError): pass 


选项2:循环应用程序并使用组合键提升窗口:

假设我在下面的组合Alt + 1下面有脚本。 我有几个窗户打开:

  • 火狐
  • 侏儒末端
  • 鹦鹉螺

目前的状态:

在此处输入图像描述

按一次Alt + 1 ,所有nautilus窗口都会被抬起:

<图像/>

我再次按Alt + 1 ,所有firefox窗口都被提升:

<图像/>

我再次按下Alt + 1 ,再次抬起所有gnome-terminal窗口,循环开始:

<图像/>

如何使用

  • 按照“如何使用”中的描述进行设置
  • 将其添加到您选择的快捷键组合中:选择:系统设置>“键盘”>“快捷方式”>“自定义快捷方式”。 单击“+”并添加命令

     raise_app 

然后使用组合键在应用程序窗口中循环显示应用程序。

剧本:

 #!/usr/bin/env python3 import subprocess import getpass include_single = True # set to False if you only want to cycle through apps with multiple windows def get(command): return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8") def execute(command): subprocess.Popen(["/bin/bash", "-c", command]) def get_frontmost(): cmd = "xprop -root" frontmost = [l for l in get(cmd).splitlines() if\ "ACTIVE_WINDOW(WINDOW)" in l][0].split()[-1] return frontmost[:2]+"0"+frontmost[2:] # calculate screen resolution res_output = get("xrandr").split(); idf = res_output.index("current") res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", ""))) # creating window list on current viewport / id's / application names w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()] windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]] for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]] # create application list to cycle through if include_single == False: pre = [it[0] for it in windows] apps = sorted(list(set([it for it in pre if pre.count(it) > 1]))) else: apps = sorted(list(set([it[0] for it in windows]))) if len(apps) == 0: pass else: # get the frontmost window as a last itm in the cycle front = get_frontmost() front_pid = [l.split()[2] for l in get("wmctrl -lp").splitlines() if front in l][0] last_infront = get("ps -u "+getpass.getuser()+" | grep "+front_pid).split()[-1] # determine next apllication to raise if not last_infront in apps or last_infront == apps[-1]: arg = apps[0] print(arg) else: arg = apps[apps.index(last_infront)+1] # raise matching windows try: [execute("wmctrl -ia "+item[1]) for item in windows if item[0] == arg] except (subprocess.CalledProcessError, NameError): pass 


选项3:按键组合+单击启动器图标- 或 -应用程序窗口以提升当前视口上的所有窗口

这可能是最接近问题/评论中描述的选项。

假设我有一个凌乱的桌面,其他窗户下面埋着三个nautilus窗户。

<图像/>

要提升所有nautilus窗口(示例快捷方式: Alt + 1 ):

  • Alt + 1 ,释放(!)
  • 在3秒内,要么:

    单击启动器中的应用程序图标

    <图像/>

    要么:

    单击应用程序的某个窗口

    <图像/>

    结果:

    <图像/>

如何使用:

  • 按照“如何使用”中的描述进行设置
  • 通过命令测试运行它:

     raise_app 
  • 如果一切正常,请将其添加到您选择的快捷键组合中:选择:系统设置>“键盘”>“快捷方式”>“自定义快捷方式”。 单击“+”并添加命令

然后:

  • 按下您的组合键,并在3秒内,:

    • 单击启动器中的应用程序图标
    • 单击应用程序的某个窗口

剧本

 #!/usr/bin/env python3 import subprocess import getpass import time def get(command): return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8") def execute(command): subprocess.Popen(["/bin/bash", "-c", command]) def get_frontmost(): cmd = "xprop -root" frontmost = [l for l in get(cmd).splitlines() if\ "ACTIVE_WINDOW(WINDOW)" in l][0].split()[-1] return frontmost[:2]+"0"+frontmost[2:] # calculate screen resolution res_output = get("xrandr").split(); idf = res_output.index("current") res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", ""))) # get window data for various purposes w_data = get("wmctrl -lpG").splitlines() non_windows = sum([[l.split()[0] for l in w_data if it in l]\ for it in ("unity-launcher", "unity-panel", "unity-dash", "Hud")], []) # get id of current window curr_window = get_frontmost() # user gets 3 seconds to pick an application window (or launcher icon) t = 0 while t < 4: w_id1 = get_frontmost() time.sleep(1) w_id2 = get_frontmost() if w_id1 == w_id2 or w_id2 in non_windows+[curr_window]: t = t+1 else: new_frontmost = w_id2 break # raise try: pid = [l.split()[2] for l in w_data if new_frontmost in l] wl_data = [l.split() for l in w_data] raise_windows = [l[0] for l in wl_data if pid[0] == l[2] and\ 0 < int(l[3]) < res[0] and 0 < int(l[4]) < res[1]] [execute("wmctrl -ia "+item) for item in raise_windows] except NameError: pass 

选项4:组合键调用选项列表,显示当前视口上每个应用程序的窗口数

事实certificate这比我假设的更方便:

按(再次示例 - )组合键Alt + 1调用zenity窗口,列出所有应用程序及其当前视口上的窗口数:

在此处输入图像描述

只需按箭头即可转到正确的选项。 按Enter键 ,将引发所选应用程序的所有窗口。

如何使用:

  • 按照“如何使用”中的描述进行设置
  • 通过命令测试运行它:

     raise_app 
  • 如果一切正常,请将其添加到您选择的快捷键组合中:选择:系统设置>“键盘”>“快捷方式”>“自定义快捷方式”。 单击“+”并添加命令

剧本

 #!/usr/bin/env python3 import subprocess import getpass def get(command): return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8") def execute(command): subprocess.Popen(["/bin/bash", "-c", command]) # calculate screen resolution res_output = get("xrandr").split(); idf = res_output.index("current") res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", ""))) # creating window list on current viewport / id's / application names w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()] windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]] for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]] # preparing zenity optionlist apps = [item[0] for item in windows] # prevent multiple zenity windows if apps.count("zenity") > 1: pass elif apps.count("zenity") > 0: execute('zenity --info --text "Another Zenity window is open already"') # preventing empty windowlist elif len(apps) > 0: applist = [[app, str(apps.count(app))] for app in set(apps)] applist.sort(key=lambda x: x[1]) # calling zenity window try: arg = get('zenity --list --text "Choose an application" '+\ '--title "Current windows" '+\ '--column "application" '+\ '--column "windows" '+\ '--height 250 '+\ '--width 250 '+\ (" ").join(sum(applist, []))) except subprocess.CalledProcessError: pass # raise matching windows try: [execute("wmctrl -ia "+item[1]) \ for item in windows if arg.startswith(item[0])] except (subprocess.CalledProcessError, NameError): pass else: execute('zenity --info --text "No windows to list"') 


选项5:从启动器图标中提升正在运行的应用程序的窗口

此选项存在启动器图标,当前正在运行的应用程序位于快速列表中。 选择一个,将引发应用程序的所有窗口。

在此处输入图像描述

当正在运行的应用程序列表(在当前视口上)发生更改时,启动程序会自动更新。 快速列表在其他视口上显示不同的列表,其中打开其他应用程序的窗口(需要1-2秒才能适应)。

如上所述,虽然function齐全,但这个选项意味着一个概念 。 它有一些小的化妆品缺点。 最重要的:

  • 操作后,光标“滚轮”会持续旋转几秒钟。 虽然它不会影响function,但它是一个整容的缺点。
  • 在运行的应用程序列表更改后,启动器图标中的应用程序列表需要1-2秒才能更新。

此外,设置稍微复杂一些(尽管在下面详细说明):

如何使用

您将在下面找到:

两个脚本/一个图标/ .desktop文件

  1. 按照“如何使用”中的说明准备设置,将第一个(main-)脚本保存为~/bin raise_app
  2. 将下面的图标(右键单击,另存为)保存为raise.png

    <图标/>

  3. .desktop文件复制到空文件中,编辑该行

      Icon=/path/to/raise.png 

    到图标的真实路径(引号之间有空格的路径)
    将其保存为~/.local/share/applications raise.desktop

  4. .desktop文件.desktop启动器以添加它

  5. 复制第二个脚本,将其粘贴到一个空文件中,将其保存为~/bin update_apps ,使其可执行。
  6. 将以下命令添加到启动应用程序(Dash>启动应用程序>添加):

     update_apps 
  7. 注销并重新登录以使其正常工作。

第一个脚本

 #!/usr/bin/env python3 import subprocess import getpass import sys arg = sys.argv[1] def get(command): return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8") def execute(command): subprocess.Popen(["/bin/bash", "-c", command]) # calculate screen resolution res_output = get("xrandr").split(); idf = res_output.index("current") res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", ""))) # creating window list on current viewport / id's / application names w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()] windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]] for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]] try: [execute("wmctrl -ia "+item[1]) for item in windows if item[0].startswith(arg)] except (subprocess.CalledProcessError, NameError): pass 

第二个脚本

 #!/usr/bin/env python3 import subprocess import getpass import time import os dtfile = os.environ["HOME"]+"/.local/share/applications/raise.desktop" def get(command): return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8") def execute(command): subprocess.Popen(["/bin/bash", "-c", command]) # calculate screen resolution res_output = get("xrandr").split(); idf = res_output.index("current") res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", ""))) # creating window list on current viewport / id's / application names def applist(): try: w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()] windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]] for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]] except subprocess.CalledProcessError: return [] else: return set([app[0] for app in windows]) def update_dtfile(applications, text): actionline = "Actions="+(";").join(applications)+";\n" with open(dtfile) as src: lines = src.readlines() lines = lines[:[i for i in range(len(lines)) \ if lines[i].startswith("Actions=")][0]]+[actionline] for item in text: for it in item: lines.append(it) with open(dtfile, "wt") as out: for line in lines: out.write(line) while True: apps1 = applist() time.sleep(1) apps2 = applist() if apps1 != apps2: text = [["[Desktop Action "+it+"]\n", "Name="+it+"\n", "Exec=raise_app "+it+"\n", "OnlyShowIn=Unity;\n\n", ]for it in apps2] update_dtfile(apps2, text) 

.desktop文件

 [Desktop Entry] Name=Raise application windows Comment=Raise groups of windows Icon=/path/to/raise.png Terminal=false Type=Application Version=1.0 Actions= 


简要说明

上面的所有解决方案都使用wmctrl来创建一个窗口列表,使用wmctrl -lpG命令。 此命令生成行,如下所示:

 0x044000b3 0 3429 65 24 1615 1026 jacob-System-Product-Name unity - How to show all windows of an application? - Ask Ubuntu - Mozilla Firefox 

这些行包括:

  • 第1列:窗口的id(我们可以使用它来提升它)
  • 第3列:拥有窗口的pid。
  • 第4/5列:窗口的几何xy(我们用来查看窗口是否在当前视口,icw xrandr

ps -u 的输出中查找pid以获得应用程序的“用户可读”标识(名称)。
因此我们可以为应用程序分配窗口。 随后我们可以使用命令wmctrl -iafor循环中提升给定应用程序的窗口。

在选项3中
该脚本启动一个3秒的“等待”循环,重复使用xprop -root命令查看最前面的窗口是否有任何变化; 如果用户单击启动器图标以引发应用程序窗口或直接单击窗口,则会发生这种情况。 如果是这样,while循环中断并查找“新的”最前面的应用程序,然后提升该应用程序的所有其他窗口。

超级 + W快捷方式,它将显示所有当前打开的窗口的展示,但这将包括其他应用程序。 这是默认情况下提供的,不需要任何更改,因此它可能是最简单的选项。

除此之外,您可以使用Ctrl + Super + Left / Right按钮在屏幕的左右两侧放置窗口,并使用Alt +〜(代字号,第一个键旁边的那个)在它们之间切换。

如果按Alt + Tab键循环浏览应用程序,然后进入具有多个窗口的应用程序,只需按住alt键,大约1整秒后,图标将被替换为该应用程序的所有窗口视图。

这可能是也可能不是你想要的,但它对我有用而且更简单,所以我想我会分享选项!