在Python编程中, 有时需要模拟键盘或鼠标事件, 自动操作计算机, 比如玩游戏等。
本文介绍使用ctypes
模块调用API函数, 模拟键盘鼠标事件的方法。
1.导入ctypes模块
ctypes 是 Python 的外部函数库,提供了与 C 兼容的数据类型,并且支持直接调用 DLL 中的函数 (这对于调用API函数非常有用)。
from ctypes import *
2.通过ctypes.windll调用api函数
可通过操作dll对象的属性来操作这些函数。如:
windll.user32.keybd_event(32,0,0,0) # 按下空格键
有时需要传递数据类型给函数 :
class PointAPI(Structure):
#PointAPI类型,用于获取鼠标坐标
_fields_ = [("x", c_ulong), ("y", c_ulong)]
def getpos():
"获取当前鼠标位置。"
po = PointAPI()
windll.user32.GetCursorPos(byref(po)) # 传递数据类型的引用
return int(po.x), int(po.y)
3.模拟键盘事件
模拟键盘事件主要用到了keybd_event()
API函数, 用于在活动窗口生成一个键盘事件。
完整源代码见: qfcy_ / Python · GitCode
KEYEVENTF_KEYUP=2
VK_CODE = { # 定义按键代码
'backspace':0x08,
'tab':0x09,
'clear':0x0C,
'enter':0x0D,
'shift':0x10,
'ctrl':0x11,
'alt':0x12,
'pause':0x13,
'caps_lock':0x14,
'esc':0x1B,
'space':0x20,
'page_up':0x21,'pgup':0x21,
'page_down':0x22,'pgdn':0x22,
'end':0x23,
'home':0x24,
'left':0x25,
'up':0x26,
'right':0x27,
'down':0x28,
'+':0xBB,
',':0xBC,
'-':0xBD,
'.':0xBE,
'/':0xBF,
'`':0xC0,
';':0xBA,
'[':0xDB,
'\\':0xDC,
']':0xDD,
"'":0xDE,
'`':0xC0}
VK_CODE.update(
dict([("f%d"%i,0x6F+i) for i in range(1,13)])
)
VK_CODE.update(
dict([("numpad_%d"%i,0x60+i) for i in range(10)])
)
keybd_event=windll.user32.keybd_event
def __convert(keycode_or_keyname):
#识别参数格式, 将按键名称转换为键码值
if type(keycode_or_keyname) is str:
keyname=keycode_or_keyname.lower().replace(' ','_')
if keyname in VK_CODE:
return VK_CODE[keyname]
else:
if not len(keycode_or_keyname)==1:
raise ValueError(
"{} is not a correct key name".format(keycode_or_keyname))
return ord(keycode_or_keyname.upper())
else:return keycode_or_keyname
def keydown(keycode_or_keyname):
"""模拟键按下。
keycode_or_keyname:按键名称或该按键的键码值"""
keycode=__convert(keycode_or_keyname)
keybd_event(keycode,0,0,0)
def keyup(keycode_or_keyname):
"""模拟键释放。
keycode_or_keyname:按键名称或该按键的键码值"""
keycode=__convert(keycode_or_keyname)
keybd_event(keycode,0,KEYEVENTF_KEYUP,0)
def keypress(keycode_or_keyname,delay=0.05):
"""模拟按键。
keycode_or_keyname:按键名称或该按键的键码值
delay:键按下与释放之间的的间隔时间,间隔时间越小,按键速度越快。"""
keycode=__convert(keycode_or_keyname)
keydown(keycode)
time.sleep(delay)
keyup(keycode)
time.sleep(delay)
4.模拟鼠标事件
GetCursorPos()
: 获得鼠标坐标。
SetCursorPos()
: 设置鼠标坐标, 即移动鼠标。
mouse_event()
: 在当前光标位置生成一个鼠标事件, 如鼠标按下、释放。
#API常量
MOUSEEVENTF_LEFTDOWN = 0x2
MOUSEEVENTF_LEFTUP = 0x4
MOUSEEVENTF_MIDDLEDOWN = 0x20
MOUSEEVENTF_MIDDLEUP = 0x40
MOUSEEVENTF_RIGHTDOWN = 0x8
MOUSEEVENTF_RIGHTUP = 0x10
MOUSEEVENTF_MOVE = 0x1
user32=windll.user32
class PointAPI(Structure):
#PointAPI类型,用于获取鼠标坐标
_fields_ = [("x", c_ulong), ("y", c_ulong)]
def get_screensize():
"获取当前屏幕分辨率。"
GetSystemMetrics = user32.GetSystemMetrics
return GetSystemMetrics(0), GetSystemMetrics(1)
def getpos():
"""获取当前鼠标位置。
返回值为一个元组,以(x,y)形式表示。"""
po = PointAPI()
user32.GetCursorPos(byref(po))
return int(po.x), int(po.y)
def goto(x,y):
"将鼠标移动至指定位置(x,y)。"
user32.SetCursorPos(x,y)
setpos=goto
def move(x,y):
"""模拟移动鼠标。
与goto不同,move()*产生*一个鼠标事件。"""
goto(x,y)
try:
user32.mouse_event(MOUSEEVENTF_MOVE,x,y)
# 忽略可能的ValueError: Procedure probably called with not enough arguments (16 bytes missing)
except ValueError as err:
warn("ValueError: "+str(err))
def click(delay=0):
"模拟鼠标左键单击"
try:
user32.mouse_event(MOUSEEVENTF_LEFTDOWN)
except ValueError as err: # 同上
warn("ValueError: "+str(err))
time.sleep(delay)
try:
user32.mouse_event(MOUSEEVENTF_LEFTUP)
except ValueError as err:
warn("ValueError: "+str(err))
def right_click(delay=0):
"模拟鼠标右键单击"
try:
user32.mouse_event(MOUSEEVENTF_RIGHTDOWN)
except ValueError as err:
warn("ValueError: "+str(err))
time.sleep(delay)
try:
user32.mouse_event(MOUSEEVENTF_RIGHTUP)
except ValueError as err:
warn("ValueError: "+str(err))
def dblclick(delay=0.25):
"模拟鼠标左键双击"
click()
time.sleep(delay)
click()
def middle_click(delay=0):
"模拟单击鼠标中键"
try:
user32.mouse_event(MOUSEEVENTF_MIDDLEDOWN)
except ValueError as err:
warn("ValueError: "+str(err))
time.sleep(delay)
try:
user32.mouse_event(MOUSEEVENTF_MIDDLEUP)
except ValueError as err:
warn("ValueError: "+str(err))