鼠标模拟和键盘模拟类似,也是分前台和后台模拟。话不多说直接,上代码:
import time
import win32api
import win32con
import win32gui
from ctypes import *
MOUSEEVENTF_LEFTDOWN = 0x2
MOUSEEVENTF_LEFTUP = 0x4
MOUSEEVENTF_MIDDLEDOWN = 0x20
MOUSEEVENTF_MIDDLEUP = 0x40
MOUSEEVENTF_RIGHTDOWN = 0x8
MOUSEEVENTF_RIGHTUP = 0x10
MOUSEEVENTF_MOVE = 0x1
class FrontstageMouse():
def __init__(self):
print('FrontstageMouse init')
def bind(self, hwnd):
self.hwnd = hwnd
def getRelativePosition(self, x, y):
left, top, right, bottom = win32gui.GetWindowRect(self.hwnd)
return [left + x, top + y]
# 前台模拟方法1——使用win32api.mouse_event(默认) ############################################
# 模拟鼠标的按键按下
def clickDown(self, x, y, button="l"):
win32api.SetCursorPos(self.getRelativePosition(int(x), int(y)))
button = button.lower()
if button == "l":
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0,0,0)
elif button == "m":
win32api.mouse_event(win32con.MOUSEEVENTF_MIDDLEDOWN,0,0,0,0)
elif button == "r":
win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN,0,0,0,0)
print('clickDown x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的按键抬起
def clickUp(self, x, y, button="l"):
win32api.SetCursorPos(self.getRelativePosition(int(x), int(y)))
button = button.lower()
if button == "l":
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0,0,0)
elif button == "m":
win32api.mouse_event(win32con.MOUSEEVENTF_MIDDLEUP,0,0,0,0)
elif button == "r":
win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP,0,0,0,0)
print('clickUp x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的点击
def click(self, x, y, button="l", interval=0.1):
win32api.SetCursorPos(self.getRelativePosition(int(x), int(y)))
button = button.lower()
if button == "l":
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0,0,0)
time.sleep(interval)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0,0,0)
elif button == "m":
win32api.mouse_event(win32con.MOUSEEVENTF_MIDDLEDOWN,0,0,0,0)
time.sleep(interval)
win32api.mouse_event(win32con.MOUSEEVENTF_MIDDLEUP,0,0,0,0)
elif button == "r":
win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN,0,0,0,0)
time.sleep(interval)
win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP,0,0,0,0)
print('clicked x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的左键双击
def doubleClick(self, x, y):
win32api.SetCursorPos(self.getRelativePosition(int(x), int(y)))
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0,0,0)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0,0,0)
time.sleep(0.05)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0,0,0)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0,0,0)
print('doubleClick x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的左键拖动(即在第一个点按下,拖动至第二个点抬起
# 但是,不知道为啥,实际上是不准的,滑动的值会比实际的多2倍左右,因此默认除以2
# 有时候会误触导致滑动改成点击
def slide(self, x1, y1, x2, y2):
# win32api.SetCursorPos(self.getRelativePosition(int(x1), int(y1)))
time.sleep(0.1)
offsetX = int(x2)/2 - int(x1)/2
offsetY = int(y2)/2 - int(y1)/2
print("offsetX: " + str(offsetX))
print("offsetY: " + str(offsetY))
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0,0,0)
time.sleep(0.1)
win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, int(offsetX), int(offsetY))
time.sleep(0.1)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0,0,0)
print('slide x1: ' + str(x1) + ', y1: ' + str(y1) + ', x2: ' + str(x2) + ', y2: ' + str(y2))
# 前台模拟方法2——使用ctypes ############################################
# 模拟鼠标的按键按下
def clickDown_2(self, x, y, button="l"):
win32api.SetCursorPos(self.getRelativePosition(int(x), int(y)))
button = button.lower()
if button == "l":
windll.user32.mouse_event(MOUSEEVENTF_LEFTDOWN)
elif button == "m":
windll.user32.mouse_event(MOUSEEVENTF_MIDDLEDOWN)
elif button == "r":
windll.user32.mouse_event(MOUSEEVENTF_RIGHTDOWN)
print('clickDown_2 x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的按键抬起
def clickUp_2(self, x, y, button="l"):
win32api.SetCursorPos(self.getRelativePosition(int(x), int(y)))
button = button.lower()
if button == "l":
windll.user32.mouse_event(MOUSEEVENTF_LEFTUP)
elif button == "m":
windll.user32.mouse_event(MOUSEEVENTF_MIDDLEUP)
elif button == "r":
windll.user32.mouse_event(MOUSEEVENTF_RIGHTUP)
print('clickUp_2 x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标左键单击
def click_2(self, x, y, button='l', interval=0.05):
win32api.SetCursorPos(self.getRelativePosition(int(x), int(y)))
button = button.lower()
if button == "l":
windll.user32.mouse_event(MOUSEEVENTF_LEFTDOWN)
time.sleep(interval)
windll.user32.mouse_event(MOUSEEVENTF_LEFTUP)
elif button == "m":
windll.user32.mouse_event(MOUSEEVENTF_MIDDLEDOWN)
time.sleep(interval)
windll.user32.mouse_event(MOUSEEVENTF_MIDDLEUP)
elif button == "r":
windll.user32.mouse_event(MOUSEEVENTF_RIGHTDOWN)
time.sleep(interval)
windll.user32.mouse_event(MOUSEEVENTF_RIGHTUP)
print('clicked_2 x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的左键双击
def doubleClick_2(self, x, y):
win32api.SetCursorPos(self.getRelativePosition(int(x), int(y)))
windll.user32.mouse_event(MOUSEEVENTF_LEFTDOWN)
time.sleep(0.05)
windll.user32.mouse_event(MOUSEEVENTF_LEFTUP)
time.sleep(0.05)
windll.user32.mouse_event(MOUSEEVENTF_LEFTDOWN)
time.sleep(0.05)
windll.user32.mouse_event(MOUSEEVENTF_LEFTUP)
print('doubleClick_2 x: ' + str(x) + ', y: ' + str(y))
def slide_2(self, x1, y1, x2, y2):
# win32api.SetCursorPos(self.getRelativePosition(int(x1), int(y1)))
time.sleep(0.1)
offsetX = int(x2)/2 - int(x1)/2
offsetY = int(y2)/2 - int(y1)/2
print("offsetX: " + str(offsetX))
print("offsetY: " + str(offsetY))
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0,0,0)
time.sleep(0.1)
win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, int(offsetX), int(offsetY))
time.sleep(0.1)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0,0,0)
print('slide x1: ' + str(x1) + ', y1: ' + str(y1) + ', x2: ' + str(x2) + ', y2: ' + str(y2))
这里使用了两个不同的库分别实现,分别是win32api和ctypes库。win32库实际上也是实现鼠标后台模拟的关键,也可以实现前台模拟,使用mouse_event方法。
其中,win32api中mouse_event的函数原型:
VOID mouse_event(DWORD dwFlags,DWORD dx,DWORD dwFlags,OWORD dx,DWORD dy, DWORD dwData, DWORD dwExtralnfo);
dwFlags:标志位集,指定点击按钮和鼠标动作的多种情况。此参数里的各位可以是下列值的任何合理组合:
MOOSE_EVENTF_ABSOLOTE:表明参数dX,dy含有规范化的绝对坐标。如果不设置此位,参数含有相对数据:相对于上次位置的改动位置。此标志可被设置,也可不设置,不管鼠标的类型或与系统相连的类似于鼠标的设备的类型如何。要得到关于相对鼠标动作的信息,参见下面备注部分。
MOOSEEVENTFMOVE:表明发生移动。
M00SEEVENTF_LEFTDOWN:表明接按下鼠标左键。
M00SEEVENTF_LEFTUP:表明松开鼠标左键。
MOOSEEVENTF_RIGHTDOWN:表明按下鼠标右键。
MOOSEEVENTF_RIGHTUP:表明松开鼠标右键。
MOOSEEVENTF_MIDDLEDOWN:表明按下鼠标中键。
MOOSEEVENTF_MIDDLEUP:表明松开鼠标中键。
MOOSEEVENTF_WHEEL:在Windows NT中如果鼠标有一个轮,表明鼠标轮被移动。移动的数量由dwData给出。
dx:指定鼠标沿x轴的绝对位置或者从上次鼠标事件产生以来移动的数量,依赖于MOOSEEVENTF_ABSOLOTE的设置。给出的绝对数据作为鼠标的实际X坐标;给出的相对数据作为移动的mickeys数。一个mickey表示鼠标移动的数量,表明鼠标已经移动。
dy:指定鼠标沿y轴的绝对位置或者从上次鼠标事件产生以来移动的数量,依赖于MOOSEEVENTF_ABSOLVTE的设置。给出的绝对数据作为鼠标的实际y坐标,给出的相对数据作为移动的mickeys数。
dwData:如果dwFlags为MOOSEEVENTF_WHEEL,则dwData指定鼠标轮移动的数量。正值表明鼠标轮向前转动,即远离用户的方向;负值表明鼠标轮向后转动,即朝向用户。一个轮击定义为WHEEL_DELTA,即120。
如果dwFlagsS不是MOOSEEVENTF_WHEEL,则dWData应为零。
dwExtralnfo:指定与鼠标事件相关的附加32位值。应用程序调用函数GetMessgeExtrajnfo来获得此附加信息。
返回值:无。
import time
import win32api
import win32con
import win32com
import win32gui
import pythoncom
import math
class BackstageMouse():
def __init__(self):
print('BackstageMouse init')
def bind(self, hwnd):
self.hwnd = hwnd
# 聚焦句柄对应的窗口
def focusHwnd(self):
pythoncom.CoInitialize()
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys('%')
win32gui.SetForegroundWindow(self.hwnd)
# 后台模拟方法1——使用SendMessage(默认) ############################################
# 模拟鼠标的移动
def move(self, x, y):
point = win32api.MAKELONG(int(x), int(y))
win32api.SendMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, point)
# 模拟鼠标的按键按下
def clickDown(self, x, y, button="l"):
point = win32api.MAKELONG(int(x), int(y))
button = button.lower()
if button == "l":
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, point)
elif button == "m":
win32api.SendMessage(self.hwnd, win32con.WM_MBUTTONDOWN, win32con.MK_MBUTTON, point)
elif button == "r":
win32api.SendMessage(self.hwnd, win32con.WM_RBUTTONDOWN, win32con.MK_RBUTTON, point)
print('clickDown x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的按键抬起
def clickUp(self, x, y, button="l"):
point = win32api.MAKELONG(int(x), int(y))
button = button.lower()
if button == "l":
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, point)
elif button == "m":
win32api.SendMessage(self.hwnd, win32con.WM_MBUTTONUP, win32con.MK_MBUTTON, point)
elif button == "r":
win32api.SendMessage(self.hwnd, win32con.WM_RBUTTONUP, win32con.MK_RBUTTON, point)
print('clickUp x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的点击
def click(self, x, y, button="l", interval=0.1):
point = win32api.MAKELONG(int(x), int(y))
button = button.lower()
if button == "l":
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, point)
time.sleep(interval)
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, point)
elif button == "m":
win32api.SendMessage(self.hwnd, win32con.WM_MBUTTONDOWN, win32con.MK_MBUTTON, point)
time.sleep(interval)
win32api.SendMessage(self.hwnd, win32con.WM_MBUTTONUP, win32con.MK_MBUTTON, point)
elif button == "r":
win32api.SendMessage(self.hwnd, win32con.WM_RBUTTONDOWN, win32con.MK_RBUTTON, point)
time.sleep(interval)
win32api.SendMessage(self.hwnd, win32con.WM_RBUTTONUP, win32con.MK_RBUTTON, point)
print('clicked x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的左键双击
def doubleClick(self, x, y):
point = win32api.MAKELONG(int(x), int(y))
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONDBLCLK, win32con.MK_LBUTTON, point)
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, point)
print('doubleClick x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标移动到坐标,并进行左键单击
def moveToPosClick(self, x, y):
point = win32api.MAKELONG(int(x), int(y))
win32api.SendMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, point)
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, point)
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, point)
print('moveToPosClick x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标移动到坐标,并进行左键双击
def moveToPosDoubleClick(self, x, y):
point = win32api.MAKELONG(int(x), int(y))
win32api.SendMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, point)
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONDBLCLK, win32con.MK_LBUTTON, point)
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, point)
print('moveToPosDoubleClick x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标使用滚轮向上滚动
# 需要聚焦到窗口
def wheelUp(self, x, y):
self.focusHwnd()
point = win32api.MAKELONG(int(x), int(y))
win32api.SendMessage(self.hwnd, win32con.WM_MOUSEWHEEL, win32con.WHEEL_DELTA * 5, point)
print('wheelDown x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标使用滚轮向下滚动
# 需要聚焦到窗口
def wheelDown(self, x, y):
self.focusHwnd()
point = win32api.MAKELONG(int(x), int(y))
win32api.SendMessage(self.hwnd, win32con.WM_MOUSEWHEEL, win32con.MK_LBUTTON, point)
print('wheelDown x: ' + str(x) + ', y: ' + str(y))
# 模拟鼠标的左键拖动(即在第一个点按下,拖动至第二个点抬起)
# @step 步长,模拟拖动会将一个操作分开多次来模拟,次数=拖动的长度/步长
# @delay 拖动时间,默认0.5秒
# @isNeedBtnUp 是否需要弹起鼠标,默认是,若不弹起可通过多次调用实现轨迹拖动
def slide(self, x1, y1, x2, y2, isNeedBtnUp=True, step=20, delay=0.5):
offsetX = x2-x1
offsetY = y2-y1
count = int(math.sqrt(offsetX*offsetX + offsetY*offsetY) / step)
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, win32api.MAKELONG(int(x1), int(y1)))
for i in range(count):
point2 = win32api.MAKELONG(int(x1+offsetX/count*(i+1)), int(y1+offsetY/count*(i+1)))
win32api.SendMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, point2)
time.sleep(delay/count)
if isNeedBtnUp:
win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONUP, 0, point2)
print('slide x1: ' + str(x1) + ', y1: ' + str(y1) + ', x2: ' + str(x2) + ', y2: ' + str(y2))
这里同样使用win32api的SendMessage方法来实现鼠标的后台模拟,此方法的原型在键盘模拟中提过了,这里不再复述。
使用后台键盘模拟,需要绑定窗口句柄,关于窗口句柄的获取,可以看看下面这篇文章:Python自动化(1)——获取窗口句柄
完整自动化工程代码:https://gitee.com/chj-self/PythonRobotization
大佬们找到问题欢迎拍砖~