当前位置: 首页 > 图灵资讯 > 技术篇> python 自动化学习(三) 句柄获取、模拟按键、opencv安装

python 自动化学习(三) 句柄获取、模拟按键、opencv安装

来源:图灵教育
时间:2023-05-29 14:00:28

一、什么是句柄?

句柄是操作系统中的标识符,相当于我们每个人的身份证。句柄在电脑中也是独一无二的。我们启动的每个程序都有自己的句柄号来表示我们的身份

为什么要说句柄?如果我们想做自动化操作,我们当然不希望程序占用我们的整个计算机。如果我们稍微操作一下程序步骤,我们就会失败。我们希望自动化程序只能在运行时操作某个窗口或程序。即使我们把自动化程序放在后台,也不会影响双方的操作,这里需要使用句柄

所需的包

#清华镜像源pip配置 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simplepip config set global.trusted-host pypi.tuna.tsinghua.edu.cn #依赖库pip的安装 install pywin32

基本使用

#部分参考文件

1、获取鼠标所在位置程序的句柄

import timeimport win32apimport win32guitime.sleep(2)point = win32api.GetCursorPos()  #win32api.GetCursorPos 获取鼠标当前坐标(x,y)hwnd = win32gui.WindowFromPoint(point)  #查看坐标位置窗口的句柄print(hwnd)  #输出句柄

如下图所示,执行三次后,我将鼠标放在文本、桌面和idea上,并返回句柄ID

python 自动化学习(三) 句柄获取、模拟按键、opencv安装_类名

2、通过句柄获取类名

每次我们关闭并重新打开一个程序,我们都会发现句柄值发生了变化。每次从头找句柄都太麻烦了。

在开发之初,每个程序都有一个叫做“类名”的概念。每次更改类名和句柄时,定义后几乎不会更改。因此,我们最好先找到程序的类名,然后通过类名直接找到句柄,然后通过句柄进行真正需要的操作

vi main.py

import timeimport win32apimport win32gui# 通过句柄获得窗口类名deff get_clasname(hwnd):    clasname = win32gui.GetClassName(hwnd)    print(窗名:%s' % (clasname))    return clasnametime.sleep(2)point = win32api.GetCursorPos()hwnd = win32gui.WindowFromPoint(point)#查看窗口类名get__clasname(hwnd)

python 自动化学习(三) 句柄获取、模拟按键、opencv安装_自动化_02

从上面可以看出,我们获得了文档窗口的类名。现在我们直接通过类名获得句柄

3、句柄通过类名获取

我们没有找到具体的方法。我们的一般想法是先在主机上获得所有句柄id,通过循环取出所有句柄id的类名并进行比较。正确的上部id留在列表中。因此,如果我们打开多个相同程序的窗口,我们也将获得多个句柄

import timeimport win32apimport win32gui#获取当前主机上的所有句柄idef get_all_windows():    all_window_handles = []    # 枚举所有窗口句柄,添加到列表中    def enum_windows_proc(hwnd, param):        param.append(hwnd)        return True    # 调用枚举窗口API    win32gui.EnumWindows(enum_windows_proc, all_window_handles)    return all_window_handles  #返回是句柄id列表#查询输入的句柄id、类名def get_title(window_handle, class_name):    #查询句柄的类名    window_class = win32gui.GetClassName(window_handle)    #判断窗口类名是否与指定类名相同,如果相同,则返回窗口句柄,否则,返回空值    if window_class == class_name:        return window_handle#deff遍历了窗口句柄的所有子窗口 get_child_windows(parent_window_handle):    child_window_handles = []    def enum_windows_proc(hwnd, param):        param.append(hwnd)        return True    #win32gui.EnumChildWindows    遍历窗口句柄的所有子窗口    win32gui.EnumChildWindows(parent_window_handle, enum_windows_proc, child_window_handles)    return child_window_handles# 根据标题找到窗口句柄deff find_hwnd_by_title(title):    all_windows = get_all_windows()  #查询所有句柄    matched_windows = []      #存储所有匹配类名的句柄id    # 在所有窗口中找到标题匹配的窗口句柄    for window_handle in all_windows:        #get_title法  检查输入句柄对应的类名和我们的实际类名是否对应        window_title = get_title(window_handle, title)        if window_title:            matched_windows.append(window_title) #若对应,则写入列表    # 假如没有匹配,在所有子窗口中找到标题匹配的窗口句柄    if matched_windows:        return matched_windows    else:        child_window_handles = []        for parent_window_handle in all_windows:            #无论窗口是否有数据,都会添加到列表中            child_window_handles.extend(get_child_windows(parent_window_handle))        for child_window_handle in child_window_handles:            if get_title(child_window_handle, title):                matched_windows.append(get_title(child_window_handle, title))    return matched_windowsif __name__ == '__main__':    hwnd = find_hwnd_by_title("Edit")    print(hwnd)

python 自动化学习(3) 获取句柄,模拟按钮,opencv安装_类名_03  style="width: 1200px; visibility: visible;"></p><blockquote style=

我们可以看到,我们可以直接在同一类别下获得所有打开的窗口句柄,这样我们甚至可以做一个循环和多线程来实现窗口的并发效果

二、模拟按钮

我们已经从文本文档中获得了句柄信息。我们可以通过句柄做很多事情。最常见的是模拟鼠标和键盘的按键操作。每个操作可能都很琐碎。我们定义一个class类来存储它

常见的新闻类型和标识

#官方参考https://learn.microsoft.com/zh-cn/windows/win32/inputdev/wm-lbuttondown

消息类型

作用

消息标识

作用

WM_MOUSEMOVE

鼠标 移动

移动通用左键右键标识

WM_RBUTTONDOWN

鼠标 右键按下

MK_RBUTTON

左键按下

WM_RBUTTONUP

鼠标 右键释放

None

释放时不需要标记

WM_LBUTTONDOWN

鼠标 左键按下

MK_LBUTTON

右键按下

WM_LBUTTONUP

鼠标 左键释放

None

释放时不需要标记

当按钮需要按下时,需要先声明消息类型,然后标明按钮的状态 如果需要释放鼠标按钮,可以通过释放按钮直接释放 如果指定的消息类型是移动的,可以作为声明的消息类型,可以直接使用按钮标识

使用语法

#在win32api下有一个函数Postmesage,用于windows api交互的参数如下1、目标窗口发送消息的句柄2、发送的消息类型3、以及新闻的参数 win32api.PostMessage(句柄id, 消息类型, 消息标识, 具体的坐标(x,y))

获取目标坐标

#获取坐标time.sleep(3)print(win32api.GetCursorPos())

返回

(328, 250)

python 自动化学习(三) 句柄获取、模拟按键、opencv安装_句柄_04

鼠标按键案例

下面定义了一个类,首先接受我们上面获得的句柄id,使用鼠标按钮时调用win32api.Postmesage函数 将按键信息发送到句柄所在的窗口

按下左右键时,需要定义标识。例如,在模拟左键时,将使用WM_LBUTONDOWN和MK_LBUTTON ,松开时使用WM_LBUTONUP和None

变量pos 它是鼠标按钮的坐标,需要win32api.MAKELONG 转换数据类型后才能调用

#声明鼠标操作类classs WinMouse(object):    #初始化函数,接受输入的句柄id    def __init__(self, handle_num: int):        self.handle = handle_num      #按下鼠标左键    def left_button_down(self, pos):            win32api.PostMessage(self.handle, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, pos)    #左键释放鼠标    def left_button_up(self, pos):        win32api.PostMessage(self.handle, win32con.WM_LBUTTONUP, None, pos)if __name__ == '__main__':    hwnd = find_hwnd_by_title("Edit")   #句柄通过类名获取    bd = WinMouse(hwnd[0])              #WinMouse实例化 类,传入句柄值    pos = win32api.MAKELONG(328, 250)   #将正常的x、y坐标值转换为特定的数据结构,                                       #给win32api.Postmessage调用    #按下,等待1s、松开    bd.left_button_down(pos)      time.sleep(1)                 bd.left_button_up(pos)

可以看出,在下图中,在我们操作程序后,无论文本文档是在前台还是后台,即使被屏蔽,我们也会像往常一样点击鼠标(太多的数字看不清楚,大致是我把鼠标放在最后,程序在我上面的坐标点击左键)

补充其他按钮

#按下鼠标左键并移动    def mouse_move(self, pos):        win32api.PostMessage(self.handle, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, pos)    #按下鼠标右键并移动    def right_button_move(self, pos):        win32api.PostMessage(self.handle, win32con.WM_MOUSEMOVE, win32con.MK_RBUTTON, pos)    #按右键指定坐标    def right_button_down(self, pos):        win32api.PostMessage(self.handle, win32con.WM_RBUTTONDOWN, win32con.MK_RBUTTON, pos)    #右键释放    def right_button_up(self, pos):        win32api.PostMessage(self.handle, win32con.WM_RBUTTONUP, None, pos)    #模拟左键双击    def left_double_click(self, x_pos:int, y_pos:int, click=2, wait=0.4):        wait = wait / click  #click 表示点击次数,wait是等待时间,意思是双击间隔        point = win32api.MAKELONG(x_pos, y_pos)        for i in range(click):            self.left_button_down(point)            time.sleep(wait)            self.left_button_up(point)    #右键双击    def right_doubleClick(self, x, y, click=2, wait=0.4):        wait = wait / click        pos = win32api.MAKELONG(x, y)        for i in range(click):            self.right_button_down(pos)            time.sleep(wait)            self.right_button_up(pos)

按键组合函数

上面用一个按左键需要几行,我们在这里包装

#让他直接接收x,y坐标,wait是松开按钮的间隔,一般默认    #左键单击    def left_click(self, x_pos:int, y_pos:int, wait=0.2):        point = win32api.MAKELONG(x_pos, y_pos)        self.left_button_down(point)        time.sleep(wait)        self.left_button_up(point)    #右键单击    def right_click(self, x_pos:int, y_pos:int, wait=0.2):        point = win32api.MAKELONG(x_pos, y_pos)        self.right_button_down(point)        time.sleep(wait)        self.right_button_up(point)    #模拟左键双击    def left_double_click(self, x_pos:int, y_pos:int, click=2, wait=0.4):        wait = wait / click  #click 表示点击次数,wait是等待时间,意思是双击间隔        point = win32api.MAKELONG(x_pos, y_pos)        for i in range(click):            self.left_button_down(point)            time.sleep(wait)            self.left_button_up(point)    #右键双击    def right_doubleClick(self, x, y, click=2, wait=0.4):        wait = wait / click        pos = win32api.MAKELONG(x, y)        for i in range(click):            self.right_button_down(pos)            time.sleep(wait)            self.right_button_up(pos)

滑动和拖动鼠标

添加偏移值

vi main.py

#计算鼠标从起点到目标点的偏移过程deff getPointOnLine(start_x, start_y, end_x, end_y, ratio):    x = ((end_x - start_x) * ratio) + start_x    y = ((end_y - start_y) * ratio) + start_y    return int(round(x)), int(round(y))class WinMouse(object):    def __init__(self, handle_num: int, num_of_steps=80): #添加num_um_添加num_um_of_steps=80        self.handle = handle_num        self.num_of_steps = num_of_steps  #添加偏移值

添加左右键拖动方法

#模拟点击和拖动目标,接受两对坐标值    def left_click_move(self, x1:int, y1:int, x2:int, y2:int, wait=2):        point1 = win32api.MAKELONG(x1, y1)        self.left_button_down(point1)  #按下鼠标左键        #在init初始化时获得我们定义的偏移值        steps = self.num_of_steps        #调用上述方法返回具体,循环0-80的值        #你看这里的循环值是80,也就是说会做80个循环操作        #我们介绍了起始坐标和目标坐标,i / steps相当于从头到尾的偏移位置        #可以理解为从左上角到右下角的点        points = [getPointOnLine(x1, y1, x2, y2,  i / steps) for i in range(steps)]        points.append((x2, y2))        wait_time = wait / steps        unique_points = list(set(points))        unique_points.sort(key=points.index)        for point in unique_points:            x, y = point            point = win32api.MAKELONG(x, y)            self.mouse_move(point)            time.sleep(wait_time)        self.left_button_up(point)    #单击右键并滑动批量检查(与上述函数相同)    def right_click_move(self, start_x, start_y, end_x, end_y, wait=2):        pos = win32api.MAKELONG(start_x, start_y)        self.right_button_down(pos)        steps = self.num_of_steps        points = [getPointOnLine(start_x, start_y, end_x, end_y, i / steps) for i in range(steps)]        points.append((end_x, end_y))        time_per_step = wait / steps        distinct_points = list(set(points))        distinct_points.sort(key=points.index)        for point in distinct_points:            x, y = point            pos = win32api.MAKELONG(x, y)            self.right_button_move(pos)            time.sleep(time_per_step)        self.right_button_up(pos)

演示

bd.left_click_move(109,180,232,341)

python 自动化学习(三) 句柄获取、模拟按键、opencv安装_运维_05

全量代码

import ctypesimport timeimport cv2import numpy as npimport win32apimport win32conimport win32guiimport win32uifrom PIL.Image import Imageimport main2#-句柄配置的分割线#获取当前主机上的所有句柄idef get_all_windows():    all_window_handles = []    # 枚举所有窗口句柄,添加到列表中    def enum_windows_proc(hwnd, param):        param.append(hwnd)        return True    # 调用枚举窗口API    win32gui.EnumWindows(enum_windows_proc, all_window_handles)    return all_window_handles  #返回是句柄id列表#查询输入的句柄id、类名def get_title(window_handle, class_name):    #查询句柄的类名    window_class = win32gui.GetClassName(window_handle)    #判断窗口类名是否与指定类名相同,如果相同,则返回窗口句柄,否则,返回空值    if window_class == class_name:        return window_handle#deff遍历了窗口句柄的所有子窗口 get_child_windows(parent_window_handle):    child_window_handles = []    def enum_windows_proc(hwnd, param):        param.append(hwnd)        return True    #win32gui.EnumChildWindows    遍历窗口句柄的所有子窗口    win32gui.EnumChildWindows(parent_window_handle, enum_windows_proc, child_window_handles)    return child_window_handles# 根据标题找到窗口句柄deff find_hwnd_by_title(title):    all_windows = get_all_windows()  #查询所有句柄    matched_windows = []      #存储所有匹配类名的句柄id    # 在所有窗口中找到标题匹配的窗口句柄    for window_handle in all_windows:        #get_title法  检查输入句柄对应的类名和我们的实际类名是否对应        window_title = get_title(window_handle, title)        if window_title:            matched_windows.append(window_title) #若对应,则写入列表    # 假如没有匹配,在所有子窗口中找到标题匹配的窗口句柄    if matched_windows:        return matched_windows    else:        child_window_handles = []        for parent_window_handle in all_windows:            #无论窗口是否有数据,都会添加到列表中            child_window_handles.extend(get_child_windows(parent_window_handle))        for child_window_handle in child_window_handles:            if get_title(child_window_handle, title):                matched_windows.append(get_title(child_window_handle, title))    return matched_windows#-----------------------------------------------------句柄配置的分割线def getPointOnLine(start_x, start_y, end_x, end_y, ratio):    x = ((end_x - start_x) * ratio) + start_x    y = ((end_y - start_y) * ratio) + start_y    return int(round(x)), int(round(y))#声明鼠标操作类classs WinMouse(object):    #初始化函数,接受输入的句柄id    def __init__(self, handle_num: int, num_of_steps=80):        self.handle = handle_num        self.num_of_steps = num_of_steps        #按下鼠标左键    def left_button_down(self, pos):        win32api.PostMessage(self.handle, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, pos)    #左键释放鼠标    def left_button_up(self, pos):        win32api.PostMessage(self.handle, win32con.WM_LBUTTONUP, None, pos)    #按下鼠标左键并移动    def mouse_move(self, pos):        win32api.PostMessage(self.handle, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, pos)    #按下鼠标右键并移动    def right_button_move(self, pos):        win32api.PostMessage(self.handle, win32con.WM_MOUSEMOVE, win32con.MK_RBUTTON, pos)    #按右键指定坐标    def right_button_down(self, pos):        win32api.PostMessage(self.handle, win32con.WM_RBUTTONDOWN, win32con.MK_RBUTTON, pos)    #右键释放    def right_button_up(self, pos):        win32api.PostMessage(self.handle, win32con.WM_RBUTTONUP, None, pos)#--------------------------------------------------------封装按键法的分割线    #让他直接接收x,y坐标,wait是松开按钮的间隔,一般默认    #左键单击    def left_click(self, x_pos:int, y_pos:int, wait=0.2):        point = win32api.MAKELONG(x_pos, y_pos)        self.left_button_down(point)        time.sleep(wait)        self.left_button_up(point)    #右键单击    def right_click(self, x_pos:int, y_pos:int, wait=0.2):        point = win32api.MAKELONG(x_pos, y_pos)        self.right_button_down(point)        time.sleep(wait)        self.right_button_up(point)    #模拟左键双击    def left_double_click(self, x_pos:int, y_pos:int, click=2, wait=0.4):        wait = wait / click  #click 表示点击次数,wait是等待时间,意思是双击间隔        point = win32api.MAKELONG(x_pos, y_pos)        for i in range(click):            self.left_button_down(point)            time.sleep(wait)            self.left_button_up(point)    #右键双击    def right_doubleClick(self, x, y, click=2, wait=0.4):        wait = wait / click        pos = win32api.MAKELONG(x, y)        for i in range(click):            self.right_button_down(pos)            time.sleep(wait)            self.right_button_up(pos)    #模拟点击和拖动目标,接受两对坐标值    #模拟点击和拖动目标,接受两对坐标值    def left_click_move(self, x1:int, y1:int, x2:int, y2:int, wait=2):        point1 = win32api.MAKELONG(x1, y1)        self.left_button_down(point1)  #按下鼠标左键        #在init初始化时获得我们定义的偏移值        steps = self.num_of_steps        #调用上述方法返回具体,循环0-80的值        #你看这里的循环值是80,也就是说会做80个循环操作        #我们介绍了起始坐标和目标坐标,i / steps相当于从头到尾的偏移位置        #可以理解为从左上角到右下角的点        points = [getPointOnLine(x1, y1, x2, y2,  i / steps) for i in range(steps)]        points.append((x2, y2))        wait_time = wait / steps        unique_points = list(set(points))        unique_points.sort(key=points.index)        for point in unique_points:            x, y = point            point = win32api.MAKELONG(x, y)            self.mouse_move(point)            time.sleep(wait_time)        self.left_button_up(point)    #单击右键并滑动批量检查(与上述函数相同)    def right_click_move(self, start_x, start_y, end_x, end_y, wait=2):        pos = win32api.MAKELONG(start_x, start_y)        self.right_button_down(pos)        steps = self.num_of_steps        points = [getPointOnLine(start_x, start_y, end_x, end_y, i / steps) for i in range(steps)]        points.append((end_x, end_y))        time_per_step = wait / steps        distinct_points = list(set(points))        distinct_points.sort(key=points.index)        for point in distinct_points:            x, y = point            pos = win32api.MAKELONG(x, y)            self.right_button_move(pos)            time.sleep(time_per_step)        self.right_button_up(pos)if __name__ == '__main__':    hwnd = find_hwnd_by_title("Edit")   #句柄通过类名获取    bd = WinMouse(hwnd[0])              #WinMouse实例化 类,传入句柄值    bd.left_click_move(109,180,232,341)

三、准备opencv环境

安装模块

pip install opencv-pythonpip install pyautoguipip install pillowpip install opencv-contrib-python

我这边安装opencv-python 调用cv2后,任何方法都会提示黄色报告,没有代码提示。以下是解决方案

(venv) PS C:\Users\Administrator\IdeaProjects\test> pip install opencv-pythonLooking in indexes: https://pypi.tuna.tsinghua.edu.cn/simpleRequirement already satisfied: opencv-python in c:\users\administrator\ideaprojects\test\venv\lib\site-packages (4.7.0.72)Requirement already satisfied: numpy>=1.21.2 in c:\users\administrator\ideaprojects\test\venv\lib\site-packages (from opencv-python) (1.24.3)

成功安装opencv-python模块后,我们将返回安装路径,登录此路径,进入cv2目录,并将cv2.pyd 将文件放在下面的路径下,重启编辑器

c:\users\administrator\ideaprojects\test\venv\lib\site-packages

python 自动化学习(三) 句柄获取、模拟按键、opencv安装_学习_06

测试语句

import cv2# 加载图片img = cv2.imread('11.png', 1)   #在脚本文件旁边准备一张图片# cv2显示图片.imshow('image', img)cv2.waitKey(0)cv2.destroyAllWindows()