18

I'm trying to get the title of the active window. The application is a background task so if the user has Eclipse open the function returns "Eclipse - blabla", so it's not getting the window title of my own window. I'm developing this in Python 2.6 using PyQt4.

My current solution, borrowed and slightly modified from an old answer here at SO, looks like this:

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\"')

It works for most windows, but not all. For example it can't find my kopete chat windows, or the name of the application i'm currently developing.

My next try looks like this:

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;

But for some reason window is always None.

Does somebody have a better way of getting the current window title, or how to modify one of my ways, that works for all windows?

Edit:

In case anybody is wondering this is the way I found that seems to work for all windows.

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"
dutt
  • 7,909
  • 11
  • 52
  • 85

5 Answers5

9

xdotool can do that.

xdotool getactivewindow

Falmarri
  • 47,727
  • 41
  • 151
  • 191
  • I looked through the source code of xdotool and it seems to use NET_ACTIVE_WINDOW as well, they've written a comment "a slightly modified version of xprop". But i'll give it a try and see if their slight modification made it work. – dutt Oct 21 '10 at 04:13
9

I modified your solution slightly so it should run more efficiently (it passes parameters to xprop so only the data it needs is returned). Also, I'm not sure it's necessary to buffer the output of xprop so I took that out. It should also correct return "Active window not found" if for some reason it can't find the active window.

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"
Alex Spurling
  • 54,094
  • 23
  • 70
  • 76
3

You can get the active window title with xdotool:

$ xdotool getactivewindow getwindowname
Eyal Levin
  • 16,271
  • 6
  • 66
  • 56
0

I see that the question is a bit dusty by now, also support for Python 2 is nearing the end of its scheduled lifetime, so I thought I'd mention a more Python 3'ic version.

In fact, it's probably better than just more 3-style - in Python 3 "Popen objects are supported as context managers via the with statement: on exit, standard file descriptors are closed, and the process is waited for".

Thus the below is probably more adequate and less resource hungry for newer Pythons:

(also, without withs, you might bump into 'too many open files' problems - which I of course found out about in the hard way :) - should you query for the window title frequently enough on 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"
brezniczky
  • 492
  • 3
  • 10
  • 1
    You could have flattened the code and unnested the `with` statements as each of the `for` loops can match a line at most once. – Dan D. Sep 14 '19 at 17:49
  • I am aware, definitely an option - I actually did the opposite. But it illustrates the logical dependencies and the with concept (might ring a bell for those formerly operating with embedded try finally blocks) and it's not that hugely deep (IMHO, I do agree there are stricter schools), plus it's somewhat short. My personal taste would be for extracting a function to cluster the local variables away from each other or to add a couple of logically grouping comments. I am uncertain whether I am missing a `break`, I don't fully understand the original code, but had to take a note. Busy day :) – brezniczky Sep 14 '19 at 18:23
  • (I will take a second elaborate look as it becomes convenient. Thanks for the follow up! I meant I am aware of the flattening option, not the 'at most once' property.) – brezniczky Sep 14 '19 at 18:25
  • I added the `break` - just so people hopefully don't get confused in the meantime. Thanks! – brezniczky Sep 14 '19 at 18:38
0

This is too late to be useful but it does work and I have programs that use wnck.

The wnck example needs a call to screen.force_update() before wnck will work. Without that wnck does not have any information about the windows on the screen. That is:

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
Dan D.
  • 73,243
  • 15
  • 104
  • 123
  • It may not be at all useless, as I am looking to improve what I extracted from the original answer. My aim was to prevent others from missing out on the with. As for this one, I hardly know anything about wnck. Sorry maybe a stupid question, but does it pose a deployment problem https://github.com/ssokolow/quicktile/issues/95 ? – brezniczky Sep 14 '19 at 18:33
  • That is an issue of the bindings. Perhaps, I don't do much deployment. – Dan D. Sep 14 '19 at 20:31