如何在Windows、Linux和MacOS中从Python获取显示空闲时间?
我想知道用户上次按键或移动鼠标已经过去多久了——不仅仅是在我的应用程序里,而是整个“电脑”(也就是显示器)上。这是为了判断他们是否还在电脑前,能看到屏幕上弹出的通知。
我希望能纯粹通过(PY)GTK+来实现这个功能,但如果需要调用特定平台的函数,我也可以接受。理想情况下,我希望能调用那些已经用Python封装好的函数,但如果找不到,我也不介意写一点C语言或ctypes
的代码,只要我知道自己在找什么就行。
在Windows上,我觉得我想要的函数是GetLastInputInfo
,但好像pywin32并没有封装这个函数;我希望我只是遗漏了什么。
3 个回答
我在关于鼠标点击的回答中看到建议使用 pyHook:
这里还有一些我用ctypes检测鼠标位置的代码:http://monkut.webfactional.com/blog/archive/2008/10/2/python-win-mouse-position
还有一种更复杂的方法可以实现这个功能,就是通过屏幕截图,然后使用PIL比较图像的变化。
http://www.wellho.net/forum/Programming-in-Python-and-Ruby/Python-Imaging-Library-PIL.html
如果你在Linux上使用PyGTK和X11,你可以像下面这样做,这个方法是基于Pidgin的做法:
import ctypes
import ctypes.util
import platform
class XScreenSaverInfo(ctypes.Structure):
_fields_ = [('window', ctypes.c_long),
('state', ctypes.c_int),
('kind', ctypes.c_int),
('til_or_since', ctypes.c_ulong),
('idle', ctypes.c_ulong),
('eventMask', ctypes.c_ulong)]
class IdleXScreenSaver(object):
def __init__(self):
self.xss = self._get_library('Xss')
self.gdk = self._get_library('gdk-x11-2.0')
self.gdk.gdk_display_get_default.restype = ctypes.c_void_p
# GDK_DISPLAY_XDISPLAY expands to gdk_x11_display_get_xdisplay
self.gdk.gdk_x11_display_get_xdisplay.restype = ctypes.c_void_p
self.gdk.gdk_x11_display_get_xdisplay.argtypes = [ctypes.c_void_p]
# GDK_ROOT_WINDOW expands to gdk_x11_get_default_root_xwindow
self.gdk.gdk_x11_get_default_root_xwindow.restype = ctypes.c_void_p
self.xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo)
self.xss.XScreenSaverQueryExtension.restype = ctypes.c_int
self.xss.XScreenSaverQueryExtension.argtypes = [ctypes.c_void_p,
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.c_int)]
self.xss.XScreenSaverQueryInfo.restype = ctypes.c_int
self.xss.XScreenSaverQueryInfo.argtypes = [ctypes.c_void_p,
ctypes.c_void_p,
ctypes.POINTER(XScreenSaverInfo)]
# gtk_init() must have been called for this to work
import gtk
gtk # pyflakes
# has_extension = XScreenSaverQueryExtension(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
# &event_base, &error_base);
event_base = ctypes.c_int()
error_base = ctypes.c_int()
gtk_display = self.gdk.gdk_display_get_default()
self.dpy = self.gdk.gdk_x11_display_get_xdisplay(gtk_display)
available = self.xss.XScreenSaverQueryExtension(self.dpy,
ctypes.byref(event_base),
ctypes.byref(error_base))
if available == 1:
self.xss_info = self.xss.XScreenSaverAllocInfo()
else:
self.xss_info = None
def _get_library(self, libname):
path = ctypes.util.find_library(libname)
if not path:
raise ImportError('Could not find library "%s"' % (libname, ))
lib = ctypes.cdll.LoadLibrary(path)
assert lib
return lib
def get_idle(self):
if not self.xss_info:
return 0
# XScreenSaverQueryInfo(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
# GDK_ROOT_WINDOW(), mit_info);
drawable = self.gdk.gdk_x11_get_default_root_xwindow()
self.xss.XScreenSaverQueryInfo(self.dpy, drawable, self.xss_info)
# return (mit_info->idle) / 1000;
return self.xss_info.contents.idle / 1000
上面的例子通过ctypes使用gdk,这样就能访问X11特有的功能。Xscreensaver的API也需要通过ctypes来访问。
把它转换成使用PyGI和自省(introspection)应该是比较简单的。
Gajim 在 Windows、OS X 和 GNU/Linux(以及其他类 Unix 系统)上是这样做的:
- 一个 Python 包装模块(里面还有 Windows 空闲时间检测的代码,使用了
GetTickCount
和ctypes
); - 一个基于 Ctypes 的模块,用来获取 X11 的空闲时间(使用了
XScreenSaverQueryInfo
,在旧版 Gajim 中是 C 模块); - 一个 C 模块,用来获取 OS X 的空闲时间(使用了
HIDIdleTime
系统属性)。
这些链接指向的是比较旧的 0.12 版本,所以你可能想查看一下当前的源代码,以了解可能的进一步改进和变化。