在X中获取当前活动窗口的标题

18
我正试图获取当前窗口的标题。该应用程序是一个后台任务,因此如果用户已打开Eclipse,则该函数返回“Eclipse - blabla”,因此它无法获取我的自己窗口的窗口标题。我正在使用Python 2.6和PyQt4进行开发。
我的当前解决方案是从SO的旧答案中借鉴并稍作修改,如下所示:
def get_active_window_title():
    title = ''
    root_check = ''

    root = Popen(['xprop', '-root'],  stdout=PIPE)

    if root.stdout != root_check:
        root_check = root.stdout

        for i in root.stdout:
            if '_NET_ACTIVE_WINDOW(WINDOW):' in i:
                id_ = i.split()[4]
                id_w = Popen(['xprop', '-id', id_], stdout=PIPE)

        for j in id_w.stdout:
            if 'WM_ICON_NAME(STRING)' in j:
                if title != j.split()[2]:
                    return j.split("= ")[1].strip(' \n\"')

它适用于大多数Windows系统,但不适用于全部。例如,它无法找到我的Kopete聊天窗口或我当前正在开发的应用程序的名称。

接下来我将尝试以下方法:

def get_active_window_title(self):
    screen = wnck.screen_get_default()
    if screen == None:
        return "Could not get screen"
    window = screen.get_active_window()
    if window == None:
        return "Could not get window"
    title = window.get_name()
    return title;

但是因为某些原因,window 总是为 None。

有人有更好的方法来获取当前窗口标题吗?或者怎样修改我的方法,让它适用于所有窗口?

编辑:

如果有人想知道,这是我发现的似乎适用于所有窗口的方法。

def get_active_window_title(self):
    root_check = ''
    root = Popen(['xprop', '-root'],  stdout=PIPE)

    if root.stdout != root_check:
        root_check = root.stdout

        for i in root.stdout:
            if '_NET_ACTIVE_WINDOW(WINDOW):' in i:
                id_ = i.split()[4]
                id_w = Popen(['xprop', '-id', id_], stdout=PIPE)
        id_w.wait()
        buff = []
        for j in id_w.stdout:
            buff.append(j)

        for line in buff:
            match = re.match("WM_NAME\((?P<type>.+)\) = (?P<name>.+)", line)
            if match != None:
                type = match.group("type")
                if type == "STRING" or type == "COMPOUND_TEXT":
                    return match.group("name")
        return "Active window not found"

如果stdout非空,将stdout复制到root_check是否有目的? - Christian Chapman
1
请查看下面Alex Spurling的解决方案,他已经去掉了那一部分。 - dutt
5个回答

9

我稍微修改了您的解决方案,以使其更有效地运行(它向xprop传递参数,因此只返回所需的数据)。此外,我不确定缓冲xprop的输出是否有必要,因此我将其删除了。如果由于某种原因无法找到活动窗口,它也应该正确地返回“未找到活动窗口”。

def get_active_window_title(self):
    root = Popen(['xprop', '-root', '_NET_ACTIVE_WINDOW'], stdout=PIPE)

    for line in root.stdout:
        m = re.search('^_NET_ACTIVE_WINDOW.* ([\w]+)$', line)
        if m != None:
            id_ = m.group(1)
            id_w = Popen(['xprop', '-id', id_, 'WM_NAME'], stdout=PIPE)
            break

    if id_w != None:
        for line in id_w.stdout:
            match = re.match("WM_NAME\(\w+\) = (?P<name>.+)$", line)
            if match != None:
                return match.group("name")

    return "Active window not found"

好的,我会使用这个。但是我认为我已经正确地返回了“未找到活动窗口”。 - dutt
我认为我们可以使用 if m: 而不是 if m != None: - azzamsa

9

xdotool 可以完成此操作。

xdotool getactivewindow


我查看了xdotool的源代码,它似乎也使用NET_ACTIVE_WINDOW,他们写了一个注释“xprop的稍微修改版本”。但我会尝试一下,看看他们的轻微修改是否起作用。 - dutt

3

您可以使用xdotool获取活动窗口的标题:

$ xdotool getactivewindow getwindowname

0

我看到这个问题现在有点陈旧,而且对 Python 2 的支持即将接近其预定寿命的尽头,所以我想提一下更适用于 Python 3 的版本。

实际上,它可能比仅仅是更符合第三种风格——在 Python 3 中,“通过 with 语句支持 Popen 对象作为上下文管理器:退出时,标准文件描述符被关闭,并等待进程”。

因此,以下代码可能更适合新版本的 Python,且消耗较少资源:

(此外,如果没有使用 with,你可能会遇到“打开文件过多”的问题——我当然是以困难的方式发现这个问题的,如果你在 Ubuntu ~16 上频繁查询窗口标题,也会出现这个问题。)

    with Popen(['xprop', '-root', '_NET_ACTIVE_WINDOW'], stdout=PIPE) as root:
        for line in root.stdout:
            line = str(line, encoding="UTF-8")

            m = re.search('^_NET_ACTIVE_WINDOW.* ([\w]+)$', line)
            if m is not None:
                id_ = m.group(1)
                with Popen(['xprop', '-id', id_, 'WM_NAME'],
                           stdout=PIPE) as id_w:
                    for line in id_w.stdout:
                        line = str(line, encoding="UTF-8")
                        match = re.match("WM_NAME\(\w+\) = \"(?P<name>.+)\"$",
                                         line)
                    if match is not None:
                        return match.group("name")
                break
    return "Active window not found"

1
你可以将代码压平并取消嵌套的 with 语句,因为每个 for 循环最多只能匹配一行。 - Dan D.
我知道,这绝对是一个选项 - 实际上我做了相反的事情。但它说明了逻辑依赖关系和“with”概念(可能会让那些曾经使用嵌入式try finally块的人有所感触),并且不是非常深奥(在我看来,我同意有更严格的学派),而且有点短小。个人偏爱提取一个函数来将局部变量聚集在一起或添加一些逻辑分组注释。我不确定自己是否漏掉了break,我不完全理解原始代码,但必须做个记号。今天很忙 :) - brezniczky
我会在方便的时候再仔细看一下。感谢跟进!我的意思是我知道有“展平选项”,而不是“最多一次”属性。 - brezniczky
我添加了 break - 只是为了让人们在此期间不会感到困惑。谢谢! - brezniczky

0

这个已经太晚了,不过它确实有效,我有一些使用 wnck 的程序。

wnck 示例需要在调用 wnck 之前调用 screen.force_update()。如果没有这个操作,wnck 就无法获取屏幕上窗口的任何信息。也就是说:

def get_active_window_title(self):
    screen = wnck.screen_get_default()
    if screen is None:
        return "Could not get screen"
    screen.force_update()
    window = screen.get_active_window()
    if window is None:
        return "Could not get window"
    title = window.get_name()
    return title

这可能并不完全无用,因为我正在努力改进从原始答案中提取的内容。我的目标是防止其他人错过这个问题。至于这个问题,我对wnck几乎一无所知。抱歉,这可能是一个愚蠢的问题,但它是否会引起部署问题?https://github.com/ssokolow/quicktile/issues/95 - brezniczky
这是绑定问题。也许我不经常部署。 - Dan D.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接