如何获取 Unity Launcher 中正在运行的 GUI 应用程序列表?

如何获取 Unity Launcher 中正在运行的 GUI 应用程序列表?

我需要一个列表,其中仅包含当前在 Dash 中打开/运行的应用程序,即图标左侧带有小白色箭头的应用程序。

有什麼方法可以得到它?

带箭头的截图

答案1

有趣的问题。

与往常一样,有不同的方法可以获得这些应用程序的列表,每种方法都有特定的优点和缺点。

使用窗口列表获取启动器中显示的进程列表

由于只有具有(映射)窗口的应用程序才会出现在启动器中,因此使用:

wmctrl -lp 

wmctrl默认情况下未安装),我们可以获取已打开窗口的列表以及窗口所属的进程 ID。输出的格式为:

0x05204641  0 12618  jacob-System-Product-Name verhaal (~) - gedit

对我们来说最重要的信息是:

  • 第一个字符串(0x05204641);这是窗口 ID
  • 第三个字符串(12618);这是进程 ID(pid)该窗口所属的,以及
  • 最后一部分(verhaal (~) - gedit);这是窗口姓名

一旦我们有了 pid,我们就可以查找相应的进程姓名通过以下命令:

ps -p <pid> -o comm=

我们可以编写上述步骤的脚本,并列出现有窗口的输出,如下所示(使用python):

{'gnome-terminal', 'nautilus', 'gedit', 'GuitarPro.exe', 'firefox', 'thunderbird', 'soffice.bin'}

并发症

这看起来很简单。然而,现实总是有点复杂。我们需要注意一些例外和复杂情况:

  1. 有些窗口将属于进程 0,当尝试获取其属性时将引发错误。IdlepythonIDE)的窗口或tkinter就是这样的窗口。
  2. 有些窗口不是“真实”窗口,例如临时窗口(从其他窗口调用并属于其他窗口的窗口)或例如桌面本身。这些窗口在 的输出中列为窗口wmctrl,但不会在 Dash 中单独出现。
  3. LibreOffice在某些情况下,应用程序的名称与进程名称有很大不同,例如在所有模块的进程名称为 的情况下soffice.bin。同时,运行命令 soffice.bin无法正常工作。如果您需要识别模块CalcWriter),你需要从窗口的姓名例如。
  4. 另一个示例是 的进程名称gnome-terminal,如 的输出所示,出现在 进程列表中ps -e ww。在 14.04 中,gnome-terminal显示为gnome-terminal,然而,在 15.04 / 15.10 中它显示为:/usr/lib/gnome-terminal/gnome-terminal-server

我们需要解决的问题至少

要解决上述最重要的问题,您需要:

  • 添加检查,以确认窗口是“真实”窗口还是“正常”窗口,使用以下方法检查

    xprop -id <window_id>
    

    如果输出包含以下行:

    _NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_NORMAL
    

    该窗口是 Unity Launcher 意义上的有效窗口

  • gnome-terminal要修复中的进程名称15.x(假设您希望显示的进程名称为gnome-terminal),我们需要添加一个例外,gnome-terminal当进程名称显示为时, 将其重命名为/usr/lib/gnome-terminal/gnome-terminal-server

脚本

#!/usr/bin/env python3
import subprocess
import sys

try:
    listed = sys.argv[1]
except IndexError:
    listed = []

get = lambda cmd: subprocess.check_output(cmd).decode("utf-8").strip()

def check_wtype(w_id):
    # check the type of window; only list "NORMAL" windows
    return "_NET_WM_WINDOW_TYPE_NORMAL" in get(["xprop", "-id", w_id])

def get_process(w_id):
    # get the name of the process, owning the window
    proc = get(["ps", "-p", w_id, "-o", "comm="])
    proc = "gnome-terminal" if "gnome-terminal" in proc else proc
    return proc

wlist = [l.split() for l in subprocess.check_output(["wmctrl", "-lp"])\
         .decode("utf-8").splitlines()]

validprocs = set([get_process(w[2]) for w in wlist if check_wtype(w[0]) == True])

if listed == "-list":
    for p in validprocs:
        print(p)
else:
    print(validprocs)

如何使用

  1. 该脚本需要wmctrl

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

  3. 通过命令运行它:

    python3 /path/to/get_running.py
    

    它将输出如下内容:

    {'gnome-terminal', 'nautilus', 'gedit', 'GuitarPro.exe', 'firefox', 'thunderbird', 'soffice.bin'}
    

    或者,使用以下参数运行-list

    thunderbird
    nautilus
    gnome-terminal
    firefox
    gedit
    GuitarPro.exe
    soffice.bin
    

笔记

从您的问题来看,找到的列表的用途并不完全清楚。如果您需要应用程序的名称(如界面中显示的名称(“可读”名称),则可能适合采用完全不同的方法:

  • 所有全局安装的应用程序在 中都有一个.desktop文件/usr/share/applications。在大多数情况下,我们可以得出进程名称应用程序文件中的界面名称.desktop。利用此信息,我们可以相对轻松地创建正在运行的 GUI 应用程序列表,并以“可读”的名称呈现。

然而,在这种情况下,现实也比理论更复杂,正如所解释的那样这里

答案2

qdbus使用和界面来实现的方法org.ayatana.bamf

按文件列出的未结申请清单.desktop

$ qdbus org.ayatana.bamf /org/ayatana/bamf/matcher \                           
> org.ayatana.bamf.matcher.RunningApplicationsDesktopFiles
/usr/share/applications/compiz.desktop
/usr/share/applications/firefox.desktop
/usr/share/applications/x-terminal-emulator.desktop

使用org.ayatana.bamf.matcher.RunningApplicationsorg.ayatana.bamf.view.Name方法

$ qdbus org.ayatana.bamf /org/ayatana/bamf/matcher  \                          
> org.ayatana.bamf.matcher.RunningApplications | \                             
> xargs -I {} qdbus org.ayatana.bamf {} org.ayatana.bamf.view.Name
Firefox Web Browser
MY CUSTOM TERMINAL
Compiz

相关内容