使用 ctypes 查询按键
Query a key press using ctypes
我想在不使用 win32api
的情况下查询键盘的每个键。我使用 win32api.GetAsyncKeyState(key)
可以正常工作,但如果未安装该模块,我也想添加支持。
到目前为止,我已经找到了一段完整的工作代码,尽管它看起来有点重量级,因为它需要自己的线程,并且需要超过 1600 个单独的函数,因为我想捕获每个键,无论修饰符(每个键有 14 种可能的组合)。
这是我找到的代码,有人能提出替代方案或如何解决修饰符问题吗?
import ctypes
import ctypes.wintypes
import win32con
class GlobalHotKeys(object):
"""
Register a key using the register() method, or using the @register decorator
Use listen() to start the message pump
Example:
from globalhotkeys import GlobalHotKeys
@GlobalHotKeys.register(GlobalHotKeys.VK_F1)
def hello_world():
print 'Hello World'
GlobalHotKeys.listen()
"""
key_mapping = []
user32 = ctypes.windll.user32
MOD_ALT = win32con.MOD_ALT
MOD_CTRL = win32con.MOD_CONTROL
MOD_CONTROL = win32con.MOD_CONTROL
MOD_SHIFT = win32con.MOD_SHIFT
MOD_WIN = win32con.MOD_WIN
@classmethod
def register(cls, vk, modifier=0, func=None):
"""
vk is a windows virtual key code
- can use ord('X') for A-Z, and 0-1 (note uppercase letter only)
- or win32con.VK_* constants
- for full list of VKs see: http://msdn.microsoft.com/en-us/library/dd375731.aspx
modifier is a win32con.MOD_* constant
func is the function to run. If False then break out of the message loop
"""
# Called as a decorator?
if func is None:
def register_decorator(f):
cls.register(vk, modifier, f)
return f
return register_decorator
else:
cls.key_mapping.append((vk, modifier, func))
@classmethod
def listen(cls):
"""
Start the message pump
"""
for index, (vk, modifiers, func) in enumerate(cls.key_mapping):
if not cls.user32.RegisterHotKey(None, index, modifiers, vk):
raise Exception('Unable to register hot key: ' + str(vk) + ' error code is: ' + str(ctypes.windll.kernel32.GetLastError()))
try:
msg = ctypes.wintypes.MSG()
i = 0
while cls.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
(vk, modifiers, func) = cls.key_mapping[msg.wParam]
if not func:
break
func()
cls.user32.TranslateMessage(ctypes.byref(msg))
cls.user32.DispatchMessageA(ctypes.byref(msg))
finally:
for index, (vk, modifiers, func) in enumerate(cls.key_mapping):
cls.user32.UnregisterHotKey(None, index)
@classmethod
def _include_defined_vks(cls):
for item in win32con.__dict__:
item = str(item)
if item[:3] == 'VK_':
setattr(cls, item, win32con.__dict__[item])
@classmethod
def _include_alpha_numeric_vks(cls):
for key_code in (list (range(ord('A'), ord('Z') + 1)) + list(range(ord('0'), ord('9') + 1)) ):
setattr(cls, 'VK_' + chr(key_code), key_code)
GlobalHotKeys._include_defined_vks()
GlobalHotKeys._include_alpha_numeric_vks()
这是一个示例,说明如何使用它来阅读 a
:
@GlobalHotKeys.register(ord('A'))
def a():
print 'a'
@GlobalHotKeys.register(ord('A'), GlobalHotKeys.MOD_SHIFT)
def a_shift():
print 'shift + a'
@GlobalHotKeys.register(ord('A'), GlobalHotKeys.MOD_CONTROL | GlobalHotKeys.MOD_SHIFT)
def a_ctrl_shift():
print 'ctrl + shift + a'
...
GlobalHotKeys.listen()
原来是一个非常简单的答案,我在尝试阅读 GetKeyState
函数的 Microsoft 信息时终于偶然发现了它。
ctypes.windll.user32.GetKeyState(key)
未按下时状态将为 0 或 1,按下时会增加到 60000 之类的值,因此要获得 True/False
结果,检查 > 1
似乎可以解决问题。
GetAsyncKeyState
也有点用,但有时会产生负数,有时不会,所以我认为最好使用替代方法。
我想在不使用 win32api
的情况下查询键盘的每个键。我使用 win32api.GetAsyncKeyState(key)
可以正常工作,但如果未安装该模块,我也想添加支持。
到目前为止,我已经找到了一段完整的工作代码,尽管它看起来有点重量级,因为它需要自己的线程,并且需要超过 1600 个单独的函数,因为我想捕获每个键,无论修饰符(每个键有 14 种可能的组合)。
这是我找到的代码,有人能提出替代方案或如何解决修饰符问题吗?
import ctypes
import ctypes.wintypes
import win32con
class GlobalHotKeys(object):
"""
Register a key using the register() method, or using the @register decorator
Use listen() to start the message pump
Example:
from globalhotkeys import GlobalHotKeys
@GlobalHotKeys.register(GlobalHotKeys.VK_F1)
def hello_world():
print 'Hello World'
GlobalHotKeys.listen()
"""
key_mapping = []
user32 = ctypes.windll.user32
MOD_ALT = win32con.MOD_ALT
MOD_CTRL = win32con.MOD_CONTROL
MOD_CONTROL = win32con.MOD_CONTROL
MOD_SHIFT = win32con.MOD_SHIFT
MOD_WIN = win32con.MOD_WIN
@classmethod
def register(cls, vk, modifier=0, func=None):
"""
vk is a windows virtual key code
- can use ord('X') for A-Z, and 0-1 (note uppercase letter only)
- or win32con.VK_* constants
- for full list of VKs see: http://msdn.microsoft.com/en-us/library/dd375731.aspx
modifier is a win32con.MOD_* constant
func is the function to run. If False then break out of the message loop
"""
# Called as a decorator?
if func is None:
def register_decorator(f):
cls.register(vk, modifier, f)
return f
return register_decorator
else:
cls.key_mapping.append((vk, modifier, func))
@classmethod
def listen(cls):
"""
Start the message pump
"""
for index, (vk, modifiers, func) in enumerate(cls.key_mapping):
if not cls.user32.RegisterHotKey(None, index, modifiers, vk):
raise Exception('Unable to register hot key: ' + str(vk) + ' error code is: ' + str(ctypes.windll.kernel32.GetLastError()))
try:
msg = ctypes.wintypes.MSG()
i = 0
while cls.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
(vk, modifiers, func) = cls.key_mapping[msg.wParam]
if not func:
break
func()
cls.user32.TranslateMessage(ctypes.byref(msg))
cls.user32.DispatchMessageA(ctypes.byref(msg))
finally:
for index, (vk, modifiers, func) in enumerate(cls.key_mapping):
cls.user32.UnregisterHotKey(None, index)
@classmethod
def _include_defined_vks(cls):
for item in win32con.__dict__:
item = str(item)
if item[:3] == 'VK_':
setattr(cls, item, win32con.__dict__[item])
@classmethod
def _include_alpha_numeric_vks(cls):
for key_code in (list (range(ord('A'), ord('Z') + 1)) + list(range(ord('0'), ord('9') + 1)) ):
setattr(cls, 'VK_' + chr(key_code), key_code)
GlobalHotKeys._include_defined_vks()
GlobalHotKeys._include_alpha_numeric_vks()
这是一个示例,说明如何使用它来阅读 a
:
@GlobalHotKeys.register(ord('A'))
def a():
print 'a'
@GlobalHotKeys.register(ord('A'), GlobalHotKeys.MOD_SHIFT)
def a_shift():
print 'shift + a'
@GlobalHotKeys.register(ord('A'), GlobalHotKeys.MOD_CONTROL | GlobalHotKeys.MOD_SHIFT)
def a_ctrl_shift():
print 'ctrl + shift + a'
...
GlobalHotKeys.listen()
原来是一个非常简单的答案,我在尝试阅读 GetKeyState
函数的 Microsoft 信息时终于偶然发现了它。
ctypes.windll.user32.GetKeyState(key)
未按下时状态将为 0 或 1,按下时会增加到 60000 之类的值,因此要获得 True/False
结果,检查 > 1
似乎可以解决问题。
GetAsyncKeyState
也有点用,但有时会产生负数,有时不会,所以我认为最好使用替代方法。