我希望在使用python xlib更改焦点事件时检测应用程序窗口名称,因此在第一步中我使用此代码:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import Xlib.display
import time
display = Xlib.display.Display()
while True:
window = display.get_input_focus().focus
wmname = window.get_wm_name()
wmclass = window.get_wm_class()
if wmclass is None and wmname is None:
window = window.query_tree().parent
wmname = window.get_wm_name()
print "WM Name: %s" % ( wmname, )
time.sleep(3)
但我想要一个正确的方法,然后我研究xlib事件并找到Input Focus Events并编写这段代码:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import Xlib.display
from Xlib import X
def main():
display = Xlib.display.Display(':0')
root = display.screen().root
root.change_attributes(event_mask=Xlib.X.FocusChangeMask)
while True:
event = root.display.next_event()
#if event.type == X.FocusIn or event.type == X.FocusOut:
if event.type == X.FocusOut :
window = display.get_input_focus().focus
wmname = window.get_wm_name()
wmclass = window.get_wm_class()
if wmclass is None and wmname is None:
window = window.query_tree().parent
wmname = window.get_wm_name()
print "WM Name: %s" % ( wmname, )
if __name__ == "__main__":
main()
可悲的是,它无法正常工作,特别是在谷歌浏览器和Firefox上的标签浏览中,所以这种情况有正确的方法吗?
答案 0 :(得分:4)
您的代码几乎是正确的,但它错过了两件事:
WM_NAME
属性更改的窗口属性事件,这也会在您在浏览器中循环选项卡时发生。话虽如此,这是一个工作样本:
#!/usr/bin/python3
import Xlib
import Xlib.display
disp = Xlib.display.Display()
root = disp.screen().root
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
root.change_attributes(event_mask=Xlib.X.FocusChangeMask)
while True:
try:
window_id = root.get_full_property(NET_ACTIVE_WINDOW, Xlib.X.AnyPropertyType).value[0]
window = disp.create_resource_object('window', window_id)
window.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
window_name = window.get_full_property(NET_WM_NAME, 0).value
except Xlib.error.XError: #simplify dealing with BadWindow
window_name = None
print(window_name)
event = disp.next_event()
答案 1 :(得分:1)
@ rr-正如我刚刚在其他地方更正的那样,您要查询当前的_NET_WM_NAME
(UTF-8)和旧版WM_NAME
(非UTF8)属性或默认属性xterm配置将不返回标题。
我刚刚在your Unix & Linux StackExchange question上发布了一个完整的工作示例。
为了避免让人们进行交叉引用,我在这里发布了代码的副本:
#!/usr/bin/python
from contextlib import contextmanager
import Xlib
import Xlib.display
disp = Xlib.display.Display()
root = disp.screen().root
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME') # UTF-8
WM_NAME = disp.intern_atom('WM_NAME') # Legacy encoding
last_seen = { 'xid': None, 'title': None }
@contextmanager
def window_obj(win_id):
"""Simplify dealing with BadWindow (make it either valid or None)"""
window_obj = None
if win_id:
try:
window_obj = disp.create_resource_object('window', win_id)
except Xlib.error.XError:
pass
yield window_obj
def get_active_window():
win_id = root.get_full_property(NET_ACTIVE_WINDOW,
Xlib.X.AnyPropertyType).value[0]
focus_changed = (win_id != last_seen['xid'])
if focus_changed:
with window_obj(last_seen['xid']) as old_win:
if old_win:
old_win.change_attributes(event_mask=Xlib.X.NoEventMask)
last_seen['xid'] = win_id
with window_obj(win_id) as new_win:
if new_win:
new_win.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
return win_id, focus_changed
def _get_window_name_inner(win_obj):
"""Simplify dealing with _NET_WM_NAME (UTF-8) vs. WM_NAME (legacy)"""
for atom in (NET_WM_NAME, WM_NAME):
try:
window_name = win_obj.get_full_property(atom, 0)
except UnicodeDecodeError: # Apparently a Debian distro package bug
title = "<could not decode characters>"
else:
if window_name:
win_name = window_name.value
if isinstance(win_name, bytes):
# Apparently COMPOUND_TEXT is so arcane that this is how
# tools like xprop deal with receiving it these days
win_name = win_name.decode('latin1', 'replace')
return win_name
else:
title = "<unnamed window>"
return "{} (XID: {})".format(title, win_obj.id)
def get_window_name(win_id):
if not win_id:
last_seen['title'] = "<no window id>"
return last_seen['title']
title_changed = False
with window_obj(win_id) as wobj:
if wobj:
win_title = _get_window_name_inner(wobj)
title_changed = (win_title != last_seen['title'])
last_seen['title'] = win_title
return last_seen['title'], title_changed
def handle_xevent(event):
if event.type != Xlib.X.PropertyNotify:
return
changed = False
if event.atom == NET_ACTIVE_WINDOW:
if get_active_window()[1]:
changed = changed or get_window_name(last_seen['xid'])[1]
elif event.atom in (NET_WM_NAME, WM_NAME):
changed = changed or get_window_name(last_seen['xid'])[1]
if changed:
handle_change(last_seen)
def handle_change(new_state):
"""Replace this with whatever you want to actually do"""
print(new_state)
if __name__ == '__main__':
root.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
get_window_name(get_active_window()[0])
handle_change(last_seen)
while True: # next_event() sleeps until we get an event
handle_xevent(disp.next_event())
在this GitHub Gist中还有一个评论较多的版本。