使用Python检测空闲时间

39

如何使用Python检测Windows系统是否空闲(即没有键盘或鼠标活动)。

这已经在之前被问过,但在pywin32模块中似乎没有GetLastInputInfo函数。


5
既然之前已经问过了,为什么又要再问一遍呢?你认为会有什么改变导致得到不同的答案吗? - S.Lott
2
也许现在有人可以回答这个问题,但是这个古老的问题已经被时间和遗忘所掩埋。你如何“顶起”别人的旧问题? - Craig McQueen
2
之前提问时,回答是关于检测鼠标点击的,这根本不是那个问题的答案! - Badri
6个回答

44
from ctypes import Structure, windll, c_uint, sizeof, byref

class LASTINPUTINFO(Structure):
    _fields_ = [
        ('cbSize', c_uint),
        ('dwTime', c_uint),
    ]

def get_idle_duration():
    lastInputInfo = LASTINPUTINFO()
    lastInputInfo.cbSize = sizeof(lastInputInfo)
    windll.user32.GetLastInputInfo(byref(lastInputInfo))
    millis = windll.kernel32.GetTickCount() - lastInputInfo.dwTime
    return millis / 1000.0

调用get_idle_duration()以获取空闲时间(秒)。


另一个库可能以不兼容的方式在windll.user32.GetLastInputInfo上设置argtypesrestypeerrcheck,因此您应该使用自己的WinDLL实例,例如 user32 = WinDLL('user32', use_last_error=True) - Eryk Sun
1
@ErykSun,有时候即使使用了user32 = ...的代码,它也会返回负数。 - Luis Mauricio

16
import win32api
def getIdleTime():
    return (win32api.GetTickCount() - win32api.GetLastInputInfo()) / 1000.0

这会产生一个小于1的数字,似乎随时间不增加。当检测到输入时,它会重置为零,但然后它只变成0.015或0.016并保持不变。 - jewbix.cube

11

看起来GetLastInputInfo现在可在pywin32中使用:

win32api.GetLastInputInfo()

这个技巧可以返回用户上次输入操作的计时器滴答。

附带一个示例程序:

import time
import win32api
for i in range(10):
   print(win32api.GetLastInputInfo())
   time.sleep(1)
如果在脚本休眠时按键/移动鼠标,打印的数字会发生改变。

2

@FogleBird的回答很棒且有效,但在匆忙中我不确定它是如何工作的,所以这里提供一个小测试示例。一个线程正在启动,每10秒钟查找上一次空闲时间。如果在此时间窗口内进行任何移动,它将被打印出来。

from ctypes import Structure, windll, c_uint, sizeof, byref
import threading

//Print out every n seconds the idle time, when moving mouse, this should be < 10
def printit():
  threading.Timer(10.0, printit).start()
  print get_idle_duration()



class LASTINPUTINFO(Structure):
    _fields_ = [
        ('cbSize', c_uint),
        ('dwTime', c_uint),
    ]

def get_idle_duration():
    lastInputInfo = LASTINPUTINFO()
    lastInputInfo.cbSize = sizeof(lastInputInfo)
    windll.user32.GetLastInputInfo(byref(lastInputInfo))
    millis = windll.kernel32.GetTickCount() - lastInputInfo.dwTime
    return millis / 1000.0

printit()

请注意,根据最近的更新(在我的电脑上,Windows 10),time millis 返回的时间不正确。将其替换为 (win32api.GetTickCount() - win32api.GetLastInputInfo()) / 1000.0 可以解决这个问题。 - Sleepy
请注意,根据最近的更新(在我的电脑上,Windows 10),time millis 返回的时间不正确。将其替换为 (win32api.GetTickCount() - win32api.GetLastInputInfo()) / 1000.0 可以解决这个问题。 - undefined

2

实际上,您可以通过cytpes库访问GetLastInputInfo

import ctypes
GetLastInputInfo = ctypes.windll.User32.GetLastInputInfo  # callable function pointer

这可能并不是您想要的,因为它不提供整个系统的空闲信息,而只提供调用该函数的会话的信息。 请参见MSDN文档。 或者,您可以检查系统是否已锁定,或者屏幕保护程序是否已启动。

2

到目前为止,基本上有两种类型的答案。一种使用pywin32(通过win32api),另一种使用ctypes。我能想到的唯一区别它们的执行时间,所以这里是一个timeit测试的结果:

# idlefuncs.py

import ctypes
from ctypes import Structure, c_uint, sizeof, byref

import win32api

user32 = ctypes.WinDLL("user32", use_last_error=True)
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)

class LASTINPUTINFO(Structure):
    _fields_ = [
        ('cbSize', c_uint),
        ('dwTime', c_uint),
    ]


def get_idle_duration():
    lastInputInfo = LASTINPUTINFO()
    lastInputInfo.cbSize = sizeof(lastInputInfo)
    user32.GetLastInputInfo(byref(lastInputInfo))
    millis = kernel32.GetTickCount() - lastInputInfo.dwTime
    return millis / 1000.0


def getIdleTime():
    return (win32api.GetTickCount() - win32api.GetLastInputInfo()) / 1000.0


在IPython中:
In[1]: from idlefuncs import *

In[2]: %timeit get_idle_duration()
1.21 µs ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In[4]: %timeit getIdleTime()
465 ns ± 7.69 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)


与我的预期相反,ctypes 版本需要的时间是原来的 2.6 倍!

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