目录
『python自动化』分享win32gui、cv2、easyocr等库相关的新手入门教程,目标是编写python程序进行自动化办公,解放双手。
欢迎关注 『python自动化』 系列,持续更新中
欢迎关注 『python自动化』 系列,持续更新中
如果安装win32gui库遇到了困难,可以参考下文安装。
源码和工具下载
大漠综合工具->坐标和窗口信息抓取
- 大漠综合工具获取句柄和窗口信息
1.用十字标记拖动到需要的窗口(注意拖到大窗口而不是小窗口,是整个记事本窗口而不是记事本的输入区域的小窗口)
2.绑定(方便后续的操作)
3.点击属性即可查看窗口信息,包括窗口的大小、坐标、16进制句柄
我的gitee仓库pywin32库自动操作键鼠
我们一般用的是16进制句柄,在这里查看
- 坐标和色点采集
注意这里的相对坐标和绝对坐标,我们绑定了窗口后默认把窗口左上角(0,0)视为参考标准。
在你的桌面上新建一个记事本用于后面的代码测试
完整项目源码
下载拿来即用
我的gitee仓库pywin32库自动操作键鼠
实现思路
介绍win32的基础思路
万物皆是窗口,首先获取窗口的句柄,然后再以此为基础对于窗口输送键鼠的命令。
基本步骤
获取窗口句柄->激活窗口->设置窗口大小位置->激活聊天输入框->输入内容
API介绍
下面的代码实战会详细介绍每个方法的参数,也可以访问pywin32的官方文档查看。(建议这么做,因为以后我们自学其他库都要经历查看文档这个过程)
官网网址
http://timgolden.me.uk/pywin32-docs/contents.html
代码实战
1. 获取窗口句柄的三种方法
注意窗口类名, 窗口标题名两者必须至少有一个才能找到你想要的窗口,另一个可以缺省为None
法1:win32gui.FindWindow(窗口类名, 窗口标题名)#直接查找窗口
实例:win32gui.FindWindow(“Notepad”, None)
返回值:窗口的16进制句柄
import win32gui
#如果失败,要做好异常处理
#1.根据窗口标题获取句柄,通过标题查找,仅返回一个顶层窗口的句柄,不支持模糊查询
try:
# 获取窗口句柄
handle = win32gui.FindWindow("Notepad", None)#通过窗口标题获取窗口句柄
print("窗口句柄是:{}".format(handle))
except Exception as e:
print("窗口句柄获取失败:{}".format(e))
法2:win32gui.FindWindowEx(父窗口句柄, 查找窗口顺序, 窗口类名, 窗口标题名)#根据父窗口查找子窗口
实例:win32gui.FindWindowEx(0, 0, “Notepad”, None)
父窗口句柄:如果为 0,则假定为桌面窗口
查找窗口顺序:子窗口后按Z顺序搜索,可以为0搜索全部
返回值:查找得到的子窗口窗口的16进制句柄
#2.通过父窗口找子窗口,根据窗口类名和标题检索句柄,查找
# 参数
# 家长:PyHANDLE
# 将搜索其子窗口的窗口。如果为 0,则假定为桌面窗口。
# ChildAfter : PyHANDLE
# 子窗口后按Z顺序搜索,可以为0搜索全部
# 类名:PyResourceId
# 要查找的窗口类的名称或原子,可以是 None
# 窗口名称:字符串
# 要查找的窗口标题,可以是 None
import win32gui
try:
# 获取窗口句柄
# handle = win32gui.FindWindowEx(0, 0, "Notepad", '新建文本文档.txt - 记事本')
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)
#注意,当你修改了记事本后标题名字前加上了一个*变成了“*新建文本文档.txt - 记事本”,所以这里可以填写None缺省,只用类名查找
print("窗口句柄是:{}".format(handle))
except Exception as e:
print("窗口句柄获取失败:{}".format(e))
法3:win32gui.WindowFromPoint((窗口x坐标, 窗口y坐标))#根据窗口坐标查找顶层符合坐标要求的一个窗口
实例:win32gui.WindowFromPoint((0, 0))
窗口坐标是每个窗口都会有的属性,但是这个方法只会查找顶层的那一个窗口,所以说改变置顶窗口就会改变通过这个方法获取的窗口句柄,所以不建议用。
返回值:查找得到的子窗口窗口的16进制句柄
#3.根据窗口左上角坐标获取句柄,(0, 0)只获取最顶层的左上角窗口,这种方法我个人不建议使用,因为顶层的窗口变化了,句柄就变化了
import win32gui
try:
# 获取窗口句柄
handle = win32gui.WindowFromPoint((0, 0))
print("窗口句柄是:{}".format(handle))
except Exception as e:
print("窗口句柄获取失败:{}".format(e))
2. 根据窗口句柄获取窗口信息
#获取窗口信息 (x,y坐标,还有宽度,高度)
原型:win32gui.GetWindowRect(窗口句柄)
实例:handleMessage = win32gui.GetWindowRect(handle)
返回值:handleMessage是一个元组,包含四个元素,结构是(x坐标,y坐标,宽度,高度)
#获取窗口标题
原型:win32gui.GetWindowText(窗口句柄)
实例:win32gui.GetWindowText(handle)
返回值:字符串,窗口标题
#获取窗口类名
原型:win32gui.GetClassName(窗口句柄)
实例:win32gui.GetClassName(handle)
返回值:字符串,窗口类名
import win32gui
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#
print("窗口句柄是:{}".format(handle))
except Exception as e:
print("窗口句柄获取失败:{}".format(e))
# 注意窗口信息获取如果失败,要做好异常处理
try:
# 获取窗口信息 (x,y坐标,还有宽度,高度)
handleMessage = win32gui.GetWindowRect(handle)#通过窗口句柄获取窗口信息
print("窗口 x,y坐标,还有宽度,高度是:{}".format(handleMessage))
# 获取窗口标题
title = win32gui.GetWindowText(handle)
print("窗口标题是:{}".format(title))
# 获取窗口类名
class_name = win32gui.GetClassName(handle)
print("窗口类名是:{}".format(class_name))
except Exception as e:
print("窗口信息获取失败:{}".format(e))
3. 通过句柄设置窗口
#设置窗口信息
原型:win32gui.MoveWindow(窗口句柄, 窗口左边界,窗口上边界,窗口宽度,窗口高度,确定窗口是否被刷新)
实例:win32gui.MoveWindow(handle, 0, 0, 1280, 768, True)
返回值:None
import win32gui
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#
print("窗口句柄是:{}".format(handle))
except Exception as e:
print("窗口句柄获取失败:{}".format(e))
try:
# 获取窗口信息 (x,y坐标,还有宽度,高度)
handleMessage = win32gui.GetWindowRect(handle)#通过窗口句柄获取窗口信息
print("窗口 x,y坐标,还有宽度,高度是:{}".format(handleMessage))
# 获取窗口标题
title = win32gui.GetWindowText(handle)
print("窗口标题是:{}".format(title))
# 获取窗口类名
class_name = win32gui.GetClassName(handle)
print("窗口类名是:{}".format(class_name))
# 设置窗口
# 参数:句柄,窗口左边界,窗口上边界,窗口宽度,窗口高度,确定窗口是否被刷新
win32gui.MoveWindow(handle, 0, 0, 1280, 768, True)
# 设置窗口后获取窗口信息,查看是否设置成功
handleMessage = win32gui.GetWindowRect(handle)#通过窗口句柄获取窗口信息
print("修改窗口后的窗口 x,y坐标,还有宽度,高度是:{}".format(handleMessage))
except Exception as e:
print("窗口信息获取失败:{}".format(e))
4. 激活窗口
#激活窗口
原型:win32gui.SetForegroundWindow(窗口句柄)
实例:win32gui.SetForegroundWindow(handle)
返回值:None
import win32gui
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#
print("窗口句柄是:{}".format(handle))
# 将创建指定窗口的线程设置到前台,并且激活该窗口
win32gui.SetForegroundWindow(handle)
except Exception as e:
print("窗口句柄获取失败或是前台设置失败:{}".format(e))
5. 鼠标信息的获取
#获取鼠标位置
原型:win32api.GetCursorPos()
实例:point_position = win32api.GetCursorPos()
返回值:point_position 是一个元组,结构是(x坐标,y坐标)
#设置鼠标位置
原型:win32api.SetCursorPos((x坐标,y坐标))
实例:win32api.SetCursorPos((0, 0))
返回值:None
import win32gui
import win32api
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#
print("窗口句柄是:{}".format(handle))
# 获取鼠标位置
point_position = win32api.GetCursorPos()
print("鼠标位置是:{}".format(point_position))
# 设置鼠标位置
win32api.SetCursorPos((0, 0))
# 获取鼠标位置
point_position = win32api.GetCursorPos()
print("设置后鼠标位置是:{}".format(point_position))
except Exception as e:
print("窗口句柄获取失败或是前台设置失败:{}".format(e))
6. 鼠标点击事件
鼠标点击分为点击和弹起2个过程。
#按下鼠标左键
原型:win32api.mouse_event(键位操作, 0, 0)
实例:win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0)
返回值:None
#松开鼠标左键
原型:win32api.mouse_event(键位操作, 0, 0)
实例:win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0)
参数解读(dwFlags ,dx ,dy ,dwData ,dwExtraInfo)
dwFlags=0:双字,指定各种功能选项的标志
dx:double类型,鼠标的水平位置
dy : double类型,鼠标的垂直位置
dwData:double类型,标志特定参数
dwExtraInfo=0:double类型,与鼠标事件相关的附加数据
返回值:None
#键位表
MOUSEEVENTF_LEFTDOWN:表明接按下鼠标左键
MOUSEEVENTF_LEFTUP:表明松开鼠标左键
MOUSEEVENTF_RIGHTDOWN:表明按下鼠标右键
MOUSEEVENTF_RIGHTUP:表明松开鼠标右键
MOUSEEVENTF_MIDDLEDOWN:表明按下鼠标中键
MOUSEEVENTF_MIDDLEUP:表明松开鼠标中键
MOUSEEVENTF_WHEEL:鼠标轮移动,数量由data给出
import win32api
import win32con
import win32gui
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#
print("窗口句柄是:{}".format(handle))
# 获取鼠标位置
point_position = win32api.GetCursorPos()
print("鼠标位置是:{}".format(point_position))
# 模拟鼠标在(1000, 500)位置进行点击操作
point = (1000, 500)
win32api.SetCursorPos(point)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0)#按下鼠标左键
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0)#松开鼠标左键
# 获取鼠标位置
point_position = win32api.GetCursorPos()
print("鼠标位置是:{}".format(point_position))
except Exception as e:
print("窗口句柄获取失败或是前台设置失败:{}".format(e))
7. 键盘键位操作
键盘操作类似鼠标也分为按下和弹起2个过程。
有所区别的是,如果要输入内容,要先让鼠标激活输入框的焦点,所以要先点击一下。
同时也要注意一点,窗口操作最好加入一些延时,避免时序错误,导致出现先输入再激活焦点的错误,从而导致无法输入。
#按下按键
原型:win32api.keybd_event(键码, 硬件扫描码, 按下还是弹起的标志位, 与击键相关的附加的32位值)
实例:win32api.keybd_event(65, 0, KEYEVENTF_EXTENDEDKEY, 0)#按下A
实例:win32api.keybd_event(65, 0, 0, 0)#按下A
返回值:None
#弹起按键
原型:win32api.keybd_event(键码, 硬件扫描码, 按下还是弹起的标志位, 与击键相关的附加的32位值)
实例:win32api.keybd_event(65, 0, win32con.KEYEVENTF_KEYUP, 0)#松开A
实例:win32api.keybd_event(65, 0, 1, 0)#松开A
返回值:None
bVk:虚拟键码
bScan:硬件扫描码,一般设置为0即可
dwFlags:函数操作的一个标志位,如果值为KEYEVENTF_EXTENDEDKEY则该键被按下,也可设置为0即可,如果值为KEYEVENTF_KEYUP(也就是1)则该按键被释放
dwExtraInfo:定义与击键相关的附加的32位值,一般设置为0即可
import win32gui
import win32api
import win32con
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#桌面窗口的所有子窗口检索类名"Edit",标题为None的窗口
print("窗口句柄是:{}".format(handle))
# 设置窗口
# 参数:句柄,窗口左边界,窗口上边界,窗口宽度,窗口高度,确定窗口是否被刷新
win32gui.MoveWindow(handle, 0, 0, 500, 500, True)
# 设置窗口后获取窗口信息,查看是否设置成功
handleMessage = win32gui.GetWindowRect(handle)#通过窗口句柄获取窗口信息
print("修改窗口后的窗口 x,y坐标,还有宽度,高度是:{}".format(handleMessage))
# 将创建指定窗口的线程设置到前台,并且激活该窗口
win32gui.SetForegroundWindow(handle)
#加入延时,否则可能会操作失败
print("如果没有下面以这一行代码,可能无法正常输入A B 字母,这就是延时等待的作用")
win32api.Sleep(1000)
#激活窗口输入栏
# 模拟鼠标在(246,217)位置进行点击操作,这个坐标主要是点击一下txt记事本的输入窗口,激活焦点
point = (246,217)
win32api.SetCursorPos(point)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0)#按下鼠标左键
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0)#松开鼠标左键
#加入延时,否则可能会操作失败
win32api.Sleep(1000)
#在记事本中输入A B 字母
win32api.keybd_event(65, 0, 0, 0)#按下A
win32api.keybd_event(66, 0, 0, 0)#按下B
win32api.keybd_event(65, 0, win32con.KEYEVENTF_KEYUP, 0)#松开A
win32api.keybd_event(66, 0, win32con.KEYEVENTF_KEYUP, 0)#松开B
except Exception as e:
print("窗口句柄获取失败或是前台设置失败:{}".format(e))
8. 延时等待
这一节的内容和第7节类似,就是让大家试试看,如果不加入延时会发生什么,强调一下延时的作用。
#延时等待
原型:win32api.Sleep(等待毫秒)
实例:win32api.Sleep(1000)
返回值:None
import win32gui
import win32api
import win32con
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#桌面窗口的所有子窗口检索类名"Edit",标题为None的窗口
print("窗口句柄是:{}".format(handle))
# 设置窗口
# 参数:句柄,窗口左边界,窗口上边界,窗口宽度,窗口高度,确定窗口是否被刷新
win32gui.MoveWindow(handle, 0, 0, 500, 500, True)
# 设置窗口后获取窗口信息,查看是否设置成功
handleMessage = win32gui.GetWindowRect(handle)#通过窗口句柄获取窗口信息
print("修改窗口后的窗口 x,y坐标,还有宽度,高度是:{}".format(handleMessage))
# 将创建指定窗口的线程设置到前台,并且激活该窗口
win32gui.SetForegroundWindow(handle)
#加入延时,否则可能会操作失败
print("如果没有下面以这一行代码,可能无法正常输入A B 字母,这就是延时等待的作用")
win32api.Sleep(1000)
#激活窗口输入栏
# 模拟鼠标在(246,217)位置进行点击操作
point = (246,217)
win32api.SetCursorPos(point)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0)#按下鼠标左键
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0)#松开鼠标左键
#加入延时,否则可能会操作失败
print("如果没有下面以这一行代码,可能无法正常输入A B 字母,这就是延时等待的作用")
win32api.Sleep(1000)
#在记事本中输入A B 字母
win32api.keybd_event(65, 0, 0, 0)#按下A
win32api.keybd_event(66, 0, 0, 0)#按下B
win32api.keybd_event(65, 0, win32con.KEYEVENTF_KEYUP, 0)#松开A
win32api.keybd_event(66, 0, win32con.KEYEVENTF_KEYUP, 0)#松开B
except Exception as e:
print("错误:{}".format(e))
9. 剪切板操作
#打开剪切板
原型:win32clipboard.OpenClipboard()
实例:win32clipboard.OpenClipboard()
返回值:None
#剪切板置空
原型:win32clipboard.EmptyClipboard()
实例:win32clipboard.EmptyClipboard()
置剪切板为空,赋初始值是程序员的好习惯,注意任何数据一开始的样子
返回值:None
#设置剪切板内容
原型:win32clipboard.SetClipboardText(消息内容, 要使用的剪贴板格式)
实例:win32clipboard.SetClipboardText(send_message, win32clipboard.CF_TEXT)
返回值:一串很长的数字,我猜测要么数据内容要么是内存地址(实际使用没有必要这么深究,大家有知道的评论区留言告知我)
SetClipboardText的2个参数解读:
- send_message ->文本:str / unicode 要放置在剪贴板上的文本。
win32clipboard.CF_TEXT ->格式 = CF_TEXT:- 要使用的剪贴板格式 - 必须是CF_TEXT或CF_UNICODETEXT
#获取剪切板数据并转码为中文
原型:win32clipboard.GetClipboardData(剪贴板格式 ).decode(编码类型)
实例:getResult= win32clipboard.GetClipboardData(win32clipboard.CF_TEXT).decode(‘GBK’)
返回值:getResult是字符串文本
#关闭剪切板,有始有终,关闭以此减少资源占用
原型:win32clipboard.CloseClipboard()
实例:win32clipboard.CloseClipboard()
返回值:None
import win32clipboard
import win32gui
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#桌面窗口的所有子窗口检索类名"Edit",标题为None的窗口
print("窗口句柄是:{}".format(handle))
send_message = '发送的消息内容'
win32clipboard.OpenClipboard()#打开剪切板
win32clipboard.EmptyClipboard()#设置剪切板为空,赋初始值是程序员的好习惯,注意一开始的样子
win32clipboard.SetClipboardText(send_message, win32clipboard.CF_TEXT)
print("设置剪切板的返回值是:{}".format(win32clipboard.SetClipboardText(send_message, win32clipboard.CF_TEXT)))
#SetClipboardText的2个参数解读
#send_message ->文本:str / unicode 要放置在剪贴板上的文本。
#win32clipboard.CF_TEXT ->格式 = CF_TEXT: 要使用的剪贴板格式 - 必须是CF_TEXT或CF_UNICODETEXT
getResult= win32clipboard.GetClipboardData(win32clipboard.CF_TEXT).decode('GBK')#获取剪切板数据并转码
win32clipboard.CloseClipboard()#关闭剪切板,有始有终,关闭以此减少资源占用
print("获取到剪切板内容是:{}".format(getResult))
except Exception as e:
print("错误:{}".format(e))
10. 消息框与选择问答
#弹出一个消息框,并要求反馈
原型:win32api.MessageBox(窗口句柄, “消息内容”, “消息标题”, 选项个数标记)
选项个数标记对应关系: 标记0表示一个选项,标记1表示2个选项,标记2表示3个选项
实例:result = win32api.MessageBox(handle, “消息内容”, “消息标题”, 1)
返回值:选择了第几个选项,从左到右,从1开始计数
import win32api
import win32gui
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#桌面窗口的所有子窗口检索类名"Edit",标题为None的窗口
print("窗口句柄是:{}".format(handle))
result = win32api.MessageBox(handle, "消息内容", "消息标题", 1)#win32con.MB_OK和0是一样的 表示一个选项,1表示有两个选项,2表示有三个选项
print(result)#返回1表示选择第一个选项,返回2表示选择第二个选项
if result==1:
win32api.MessageBox(handle, "我猜对了吗?", "我猜你你选了第1个选项", 0)
elif result==2:
win32api.MessageBox(handle, "我猜对了吗?", "我猜你你选了第2个选项", 0)
except Exception as e:
print("窗口句柄获取失败或是前台设置失败:{}".format(e))
11. 关闭窗口
#关闭窗口
原型:win32gui.PostMessage(handle, 对窗口发送的消息命令, 0, 0)
实例:win32gui.PostMessage(handle, win32con.WM_CLOSE, 0, 0)
返回值:None
import win32api
import win32con
import win32gui
# 注意窗口句柄获取如果失败,要做好异常处理
try:
# 获取窗口句柄
handle = win32gui.FindWindowEx(0, 0, "Notepad", None)#桌面窗口的所有子窗口检索类名"Edit",标题为None的窗口
print("窗口句柄是:{}".format(handle))
result=win32api.MessageBox(handle, "我要关闭句柄为{}的窗口了".format(handle), "关闭窗口警告", 1)
if result == 1:
win32api.MessageBox(handle, "关闭窗口了", "关闭窗口", 0)
win32gui.PostMessage(handle, win32con.WM_CLOSE, 0, 0)#关闭窗口
elif result == 2:
win32api.MessageBox(handle, "看来你不想关闭窗口啊", "不关闭窗口", 0)
except Exception as e:
print("窗口句柄获取失败或是前台设置失败:{}".format(e))
N. 小结
-
为了更好的模拟人类正常的操作,其实还需要对于官方原生函数进行这样那样的封装改良。(有些情景有反机器人机制),比如鼠标按下弹起之间有一个随机的延时,比如鼠标点击有一个随机的抖动,按下后随机偏移几个像素点坐标然后再弹起,后面会讲讲这些内容的封装。
-
其实键鼠的操作可以用到python的另一个库
pynput
会更加方便。 -
专栏后面的文章也会讲解
pynput
。 -
未完待续,持续更新(评论区留言)
总结
欢迎关注 『python自动化』 系列,持续更新中
欢迎关注 『python自动化』 系列,持续更新中
【python自动化】01.安装配置库和环境之win32gui安装失败(保姆级图文)
【python自动化】02. pywin32库自动操作键鼠(保姆级代码注释)(167条消息) 【python自动化】02. pywin32库自动操作键鼠(保姆级代码注释)_发现你走远了的博客-CSDN博客_pywin32 键盘
【更多内容敬请期待】
标签:02,窗口,python,句柄,pywin32,win32gui,print,handle,win32api From: https://www.cnblogs.com/lidabo/p/16922006.html