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


21

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

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

单击应用程序图标时,将获得所有窗口的全屏概览。我必须手动选择每个窗口,然后在屏幕的一半上移动鼠标几次。

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

有更好的解决方案吗?


@Joschua将应用程序的所有窗口置于最前面并不是很困难,但是您想如何定义应用程序?组合键+单击应用程序的窗口会做什么?
Jacob Vlijm 2015年

@Joschua或mayby更优雅,组合键+应用程序名称的第一个字符?
Jacob Vlijm 2015年

我认为所有应用程序的行为都相同。我通常会在终端窗口中错过此功能,在该窗口中,我经常并排打开两个或多个窗口。然后我切换到全屏窗口(例如Firefox),当我想切换回两个终端窗口时,这有点困难。到目前为止,我发现最好的方法是在Firefox应用程序栏上单击鼠标中键,这会将Firefox置于后台,以便使两个终端再次位于前端。但是,只有在顶部没有太多应用程序时,此方法才有效:D
peq

还有@Joschua可能有一个组合键可以将应用程序窗口带到前面; 按下一次->将显示所有firefox窗口,再按一次->将显示所有终端窗口,等等。有趣。正在努力。虽然需要一些工作。
Jacob Vlijm

@JacobVlijm听起来像是正确的方向。::)对我来说最重要的是,组合键和单击图标会将该应用程序的所有窗口(例如,提到的许多peq终端)都置于最前面,最好是散布(也许这样的东西可能会成为Unity的一部分?!)
Joschua 2015年

Answers:


21

编辑-新答案-

下面的答案仍然是完全有效的,因此建议的选项。然而,持续不断的洞察力使我添加了此选项以使用下面的指标,这可能是最优雅的解决方案。

因此,它可能应该替换选项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中,因此您可以按其名称运行可执行文件)

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

在单独的选项中,将说明可能的其他步骤。

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

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

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

升高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窗口埋在其他窗口下。

<图像>

引发所有鹦鹉螺窗口(示例快捷键: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:组合键调用一个选项列表,显示当前视口上每个应用程序的窗口数

原来,这比我想象的要方便得多:

按下(再次示例-)组合键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秒来适应)。

如前所述,尽管功能齐全,但此选项只是一个概念。它具有一些小的化妆品缺点。最重要的:

  • 动作后,光标“滚轮”保持旋转几秒钟。尽管它不影响功能,但它在外观上是不利的。
  • 正在运行的应用程序列表更改后,启动器图标中的应用程序列表需要1-2秒才能更新。

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

如何使用

在下面您会发现:

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

  1. 准备安装在“如何使用”,保存第一(main-)脚本raise_app~/bin
  2. 将下面的图标另存为(右键单击,另存为)为 raise.png

    <图标>

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

        Icon=/path/to/raise.png
    

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

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

  5. 复制第二个脚本,将其粘贴到一个空文件,将其保存为update_apps~/bin,使其可执行。
  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

这些行包括:

  • 第一列:窗口的ID(我们可以用来引发它)
  • 第三列:拥有窗口的pid。
  • 第4列/第5列:窗口的几何xy(用于查看窗口是否在当前视口icw上xrandr

在的输出中查找pid,ps -u <username>以获取应用程序的“用户可读”标识(名称)。
因此,我们可以将窗口分配给应用程序。随后,我们可以for使用命令循环提高给定应用程序的窗口wmctrl -ia

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


我同意,再次感谢您的所有努力!:) || 我之前没有注意到过一件奇怪的事。有时在使用Option 2脚本之后,当应用程序窗口处于焦点状态(未最大化)并且我单击“下面”可见的另一个窗口时,下面的应用程序无法获得焦点。
约书亚

@Joschua这个问题的OP:askubuntu.com/questions/575830/…引起了我的一个错误,该错误是在最新的“功能”更新中引入的。正确/错误混合在一起,导致脚本崩溃,当没有应用程序的窗口超过一个时。如果您使用option2,请更新至最新版本。
Jacob Vlijm 2015年

选项1在ubuntu xenial上不适合我。something @ something:〜/ bin $ ./raise_app Gtk消息:GtkDialog映射时没有临时父代。不鼓励这样做。我试图打开终端窗口。什么都没发生。
xtrinch

@Nirri您使用了什么应用程序名称?如果在没有Gtk父级的情况下运行的zenity窗口,则该消息非常正常。“精打细算”不是一个错误。
雅各布·弗利姆

终端的第一个字符。它可以工作-有点-它会引发任何应用程序的窗口-但只有其中一个,而不是所有的都像预期的那样@ user72216
xtrinch

1

Super+ W快捷方式将显示所有当前打开的窗口的博览会,尽管它将包括其他应用程序。这是默认设置,不需要任何更改,因此也许这是最简单的选择。

除其他外,您可以使用Ctrl+ Super+ Left/ Right按钮将窗口定位在屏幕的左右两半,然后使用Alt +〜(波浪号,数字键旁边的一个)在它们之间切换。


但是,这并不能将应用程序的所有窗口带到顶部。您可以看到它们,但必须先单击很多才能使用它们。
Joschua 2015年

1

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

那可能不是您要找的东西,但是它对我有用,并且简单得多,所以我想我会选择的!


1
您也可以按向下箭头键以立即显示应用程序窗口。
克里斯(Kris)

1

我采用了@JacobVlijm的raise_apps.py脚本,并对它进行了一些增强,包括使其更强大。

具体来说,我发现一两天后,@ JacobVlijm的脚本将停止运行,并且我必须手动重新启动该脚本才能使其再次运行。回想起来,我最好的猜测是,对xrandr的多次调用最终会导致问题。

无论如何,我修改了他的代码,将轮询频率从5秒增加到了每1秒,因为它反正并没有占用太多CPU,并且使其更强大。我通常可以让它运行几天/几周而不会出现问题。

一个警告是,我在启动过程中只调用xrandr一次,以获得屏幕分辨率尺寸。因此,如果您更改屏幕分辨率(例如,从1920x1080更改为其他分辨率),则可能需要手动重新启动raise-apps.py,以便它将采用新的分辨率。我个人从不更改屏幕分辨率,所以这对我来说不是问题。此外,我有充分的理由相信对xrandr的太多调用是导致@JacobVlijm的脚本版本在一两天后停止工作的原因,因此我强烈建议不要简单地将对xrandr的大量调用放回原处。

顺便说一句,您需要将raise.png图像放置在/ usr / local / icons /目录中。或者,如果您想将raise.png放在其他目录中,请对该脚本进行适当的更改,以便脚本可以找到图像文件。

希望Ubuntu早日将这种“提升所有窗口”功能集成到其系统中,因为它非常有用:

#!/usr/bin/python2
#
# Note to self:
# You need to add raise.png to /usr/local/icons/ directory.
#
# This script was taken from: /ubuntu/446521/how-to-show-raise-all-windows-of-an-application, 
# (@JacobVlijm's answer), and then improved to fix some
# issues, that were causing it to stop working after a day or two.
#
#
from __future__ import print_function

from sys import stderr, exit
import signal
import gi

gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GObject, GLib

import logging
import logging.handlers

import time
import os
import subprocess
import getpass

logger = logging.getLogger('MyLogger')
logger.setLevel(logging.DEBUG)

log_handler = logging.handlers.SysLogHandler(address='/dev/log')

logger.addHandler(log_handler)


currpath = os.path.dirname(os.path.realpath(__file__))

class Indicator():
    def __init__(self):
        self.app = 'raise-apps'
        iconpath = '/usr/local/icons/raise.png'
        self.indicator = AppIndicator3.Indicator.new(
            self.app, iconpath,
            AppIndicator3.IndicatorCategory.OTHER)
        self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)

        self.prev_menu_item_names = []
        self.menu_items = []

        res_output = get("xrandr").split()
        if (len(res_output) == 0):
            logger.error("raise-apps.py: invocation of xrandr failed! Unable to continue..")
            exit(-1)

        idf = res_output.index("current")
        res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
        (self.screen_width, self.screen_height) = res
        logger.info("raise-apps.py: screen resolution is %s x %s" % (self.screen_width, self.screen_height))

        self.indicator.set_menu(self.create_menu())

        GLib.timeout_add_seconds(1.0, self.check_recent)

    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)

        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_items[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_items:

            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):
        # 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]) < self.screen_width and 0 < int(w[4]) < self.screen_height]
        # 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):
        # print("in check_recent()", file=stderr)
        self.menu_items = self.get_apps()
        for app in self.menu_items:
            app[0] = "gnome-terminal" if "gnome-terminal" in app[0] else app[0]
        # check if menu items have changed:
        has_changed = len(self.menu_items) != len(self.prev_menu_item_names)
        if (not has_changed):
            for i in range(len(self.menu_items)):
                if self.prev_menu_item_names[i] != self.menu_items[i][0]:
                    has_changed = True
                    break

        if has_changed:
            GObject.idle_add(
                self.set_new,
                priority=GObject.PRIORITY_DEFAULT)

            self.prev_menu_item_names = []
            for item in self.menu_items:
                self.prev_menu_item_names.append(item[0])

        GLib.timeout_add_seconds(1.0, self.check_recent)


    def stop(self, source):
        Gtk.main_quit()


    def recreate_menu(self, *args):
        logger.info("in recreate_menu()")
        self.prev_menu_item_names = []
        self.menu_items = []

        self.menu_items = self.get_apps()
        for app in self.menu_items:
            app[0] = "gnome-terminal" if "gnome-terminal" in app[0] else app[0]

        GObject.idle_add(
            self.set_new,
            priority=GObject.PRIORITY_DEFAULT)

        self.prev_menu_item_names = []
        for item in self.menu_items:
            self.prev_menu_item_names.append(item[0])


def get(command):
    # enable to get a feel for what this app is doing..
    # print("get", command, file=stderr)
    try:
        return subprocess.check_output(command).decode("utf-8")

    except subprocess.CalledProcessError as e:
        logger.error("raise-apps.py error: cmd=%s, error=%s" % (command, e))
        return ""

    except OSError as e:
        logger.error("raise-apps.py error: cmd=%s, error=%s" % (command, e))
        return ""

def execute(command):
    # enable to get a feel for what this app is doing..
    # print("exec", command, file=stderr)
    try:
        subprocess.call(command)

    except subprocess.CalledProcessError as e:
        logger.error("raise-apps.py error: cmd=%s, error=%s" % (command, e))
        return ""
    except OSError as e:
        logger.error("raise-apps.py error: cmd=%s, error=%s" % (command, e))
    return ""


logger.info("(raise-apps.py is starting up..)")
Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.