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

这篇具有很好参考价值的文章主要介绍了python 自动化学习(三) 句柄获取、模拟按键、opencv安装。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、什么是句柄

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

    为什么要说句柄,我们如果想做自动化操作时,肯定也不想程序占用了我们整个电脑,稍微操作一下程序步骤就乱掉了,更加希望自动化程序在运行的时候能够只针对某个窗口或者某个程序进行操作,即使我们把自动化的程序放入都后台时也不影响两边的操作,这里就需要用到句柄了

所需的包

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

基本使用

#部分参考文档
https://huaweicloud.csdn.net/63803058dacf622b8df86819.html?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~BlogCommendFromBaidu~activity-1-122498299-blog-111083068.pc_relevant_vip_default&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~BlogCommendFromBaidu~activity-1-122498299-blog-111083068.pc_relevant_vip_default&utm_relevant_index=1

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

import time
import win32api
import win32gui


time.sleep(2)

point = win32api.GetCursorPos()  #win32api.GetCursorPos 获取鼠标当前的坐标(x,y)

hwnd = win32gui.WindowFromPoint(point)  #查看坐标位置窗口的句柄

print(hwnd)  #输出句柄

如下图,我执行了3遍分别在执行后将鼠标放在文本、桌面、idea上面,返回了句柄ID

 python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

2、通过句柄获取类名

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

每一个程序在开发之初就存在着一个叫"类名"的概念,类名和句柄每次变更不同,它在定义后几乎是不会发生变化的,所以我们最好是先找到一个程序的类名,后续直接通过类名找到句柄,然后在通过句柄进行真正所需要的操作

 vi main.py

import time
import win32api
import win32gui

# 通过句柄获取窗口类名
def get_clasname(hwnd):
    clasname = win32gui.GetClassName(hwnd)
    print('窗口类名:%s' % (clasname))
    return clasname



time.sleep(2)
point = win32api.GetCursorPos()
hwnd = win32gui.WindowFromPoint(point)

#查看窗口类名
get_clasname(hwnd)

python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

 可以看到上面我们获取到了文档窗口的类名,现在开始我们直接通过类名去获取句柄

3、通过类名获取句柄

没有找到特定的方法,我们下面大概的思路就是先把主机上所有的句柄id都拿到,通过循环把所有句柄id的类名拿出来然后做对比,对的上的id都留在列表中,所以说如果开启了多个相同程序的窗口,我们也会获取到多个句柄

import time
import win32api
import win32gui

#获取当前主机上的所有句柄id
def 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

#遍历窗口句柄的所有子窗口
def 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


# 根据标题查找窗口句柄
def 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

if __name__ == '__main__':
    hwnd = find_hwnd_by_title("Edit")
    print(hwnd)

python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

 可以看到我们能够直接取到相同类名下所有已经打开的窗口句柄,这样我们甚至可以做个循环加多线程,来实现一个窗口并发的效果

二、模拟按键

上面我们已经拿到了文本文档的一个句柄信息,通过句柄我们可以做很多事情,最常见的就是模拟鼠标和键盘的按键操作,每个操作可能都较为细小琐碎,我们定义一个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下有个函数PostMessage,是用来与windows api交互的,参数如下
1、要发送消息的目标窗口的句柄
2、发送的消息类型
3、以及消息的参数 


win32api.PostMessage(句柄id, 消息类型, 消息标识, 具体的坐标(x,y))

获取目标坐标

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

返回

(328, 250)

 python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

鼠标按键案例

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

   在左右键按下的时候才需要定义标识,比如模拟左键时会使用WM_LBUTTONDOWN和MK_LBUTTON  ,而松开时使用WM_LBUTTONUP和None

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


#声明鼠标操作的类
class 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)    

可以看到在下图中,我们运行程序后,不论文本文档是否在前台还是后台,哪怕被遮挡住后也会照常进行鼠标点击(数字太多看不清,大致就是我把鼠标放到末尾,程序在我上面取坐标的地方点一下左键)

python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

其他按键补全

    #按下鼠标左键并移动
    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

#计算鼠标从起始点到目标点的偏移过程
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))


class WinMouse(object):

    def __init__(self, handle_num: int, num_of_steps=80): #添加num_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 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

全量代码


import time
import win32api
import win32con
import win32gui




#---------------------------------------------------句柄配置的分割线
#获取当前主机上的所有句柄id
def 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

#遍历窗口句柄的所有子窗口
def 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

# 根据标题查找窗口句柄
def 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))

#声明鼠标操作的类
class 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-python
pip install pillow
pip install opencv-contrib-python
pip install numpy
pip install PIL

我这边在安装上opencv-python 后调用cv2下任意方法都提示报黄,没有代码提示,下面列出解决方法

(venv) PS C:\Users\Administrator\IdeaProjects\test> pip install opencv-python
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Requirement 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 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

测试语句

import cv2
# 加载一张图片
img = cv2.imread('11.png', 1)   #脚本文件旁边自行准备一个图片

# 显示图片
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

1、添加截图函数

import ctypes
import time

import cv2
import numpy as np
import win32api
import win32con
import win32gui
import win32ui
from PIL import Image


def get_bitmap(hwnd):
    """
    获取窗口的位图并返回图像对象。

    参数:
        hwnd (int): 窗口的句柄。

    返回:
        Image: 窗口的位图图像。
    """
    # 获取窗口的位置和大小信息
    left, top, right, bot = win32gui.GetWindowRect(hwnd)  # 获取窗口的左上角和右下角坐标
    w = right - left  # 计算窗口的宽度
    h = bot - top  # 计算窗口的高度

    # 获取窗口的设备上下文(DC)
    hwnd_dc = win32gui.GetWindowDC(hwnd)  # 获取窗口的设备上下文
    mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)  # 创建一个与窗口设备上下文兼容的设备上下文
    save_dc = mfc_dc.CreateCompatibleDC()  # 创建一个与窗口设备上下文兼容的设备上下文

    # 创建与窗口大小相同的兼容位图
    save_bitmap = win32ui.CreateBitmap()  # 创建位图对象
    save_bitmap.CreateCompatibleBitmap(mfc_dc, w, h)  # 创建与窗口大小相同的兼容位图

    # 将位图选择到设备上下文中
    save_dc.SelectObject(save_bitmap)  # 将位图选择到设备上下文中

    # 将窗口内容绘制到位图中
    windll.user32.PrintWindow(hwnd, save_dc.GetSafeHdc(), 0)  # 将窗口内容绘制到位图中

    # 获取位图的信息和位图数据
    bmpinfo = save_bitmap.GetInfo()  # 获取位图的信息
    bmpstr = save_bitmap.GetBitmapBits(True)  # 获取位图的数据

    # 使用位图数据创建图像对象
    bmp = Image.frombuffer('RGB',
                           (bmpinfo['bmWidth'], bmpinfo['bmHeight']),
                           bmpstr, 'raw', 'BGRX', 0, 1)  # 使用位图数据创建图像对象

    # 清理资源
    win32gui.DeleteObject(save_bitmap.GetHandle())  # 删除位图对象
    save_dc.DeleteDC()  # 删除设备上下文
    mfc_dc.DeleteDC()  # 删除设备上下文
    win32gui.ReleaseDC(hwnd, hwnd_dc)  # 释放设备上下文

    # 将位图保存到文件(用于调试目的)
    bmp.save("asdf.png")  # 将位图保存到文件

    return bmp  # 返回位图图像对象



#调用代码
if __name__ == '__main__':
    hwnd = find_hwnd_by_title("AfxFrameOrView140u")  # 通过类名获取句柄
    # bd = WinMouse(hwnd[0])              #实例化WinMouse 类,传入句柄值
    # bd.left_click_move(109,180,232,341)


    #调用截图函数并返回位图
    print(get_bitmap(hwnd[0]))

这里我截图换成了xshell的句柄类名

python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

突然感觉还是用桌面演示比较好,下面我代码改下类名,不记得怎么获取类名的去看上面

import ctypes
import time

import cv2
import numpy as np
import win32api
import win32con
import win32gui
import win32ui
from PIL import Image


# ---------------------------------------------------句柄配置的分割线
# 获取当前主机上的所有句柄id
def 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


# 遍历窗口句柄的所有子窗口
def 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


# 根据标题查找窗口句柄
def 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))


# 声明鼠标操作的类
class 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)



import win32gui  # 导入 win32gui 模块,用于窗口相关操作
import win32ui  # 导入 win32ui 模块,用于设备上下文相关操作
from ctypes import windll  # 导入 windll 模块,用于调用动态链接库函数
from PIL import Image  # 导入 Image 模块,用于图像处理

def get_bitmap(hwnd):
    """
    获取窗口的位图并返回图像对象。

    参数:
        hwnd (int): 窗口的句柄。

    返回:
        Image: 窗口的位图图像。
    """
    # 获取窗口的位置和大小信息
    left, top, right, bot = win32gui.GetWindowRect(hwnd)  # 获取窗口的左上角和右下角坐标
    w = right - left  # 计算窗口的宽度
    h = bot - top  # 计算窗口的高度

    # 获取窗口的设备上下文(DC)
    hwnd_dc = win32gui.GetWindowDC(hwnd)  # 获取窗口的设备上下文
    mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)  # 创建一个与窗口设备上下文兼容的设备上下文
    save_dc = mfc_dc.CreateCompatibleDC()  # 创建一个与窗口设备上下文兼容的设备上下文

    # 创建与窗口大小相同的兼容位图
    save_bitmap = win32ui.CreateBitmap()  # 创建位图对象
    save_bitmap.CreateCompatibleBitmap(mfc_dc, w, h)  # 创建与窗口大小相同的兼容位图

    # 将位图选择到设备上下文中
    save_dc.SelectObject(save_bitmap)  # 将位图选择到设备上下文中

    # 将窗口内容绘制到位图中
    windll.user32.PrintWindow(hwnd, save_dc.GetSafeHdc(), 0)  # 将窗口内容绘制到位图中

    # 获取位图的信息和位图数据
    bmpinfo = save_bitmap.GetInfo()  # 获取位图的信息
    bmpstr = save_bitmap.GetBitmapBits(True)  # 获取位图的数据

    # 使用位图数据创建图像对象
    bmp = Image.frombuffer('RGB',
                           (bmpinfo['bmWidth'], bmpinfo['bmHeight']),
                           bmpstr, 'raw', 'BGRX', 0, 1)  # 使用位图数据创建图像对象

    # 清理资源
    win32gui.DeleteObject(save_bitmap.GetHandle())  # 删除位图对象
    save_dc.DeleteDC()  # 删除设备上下文
    mfc_dc.DeleteDC()  # 删除设备上下文
    win32gui.ReleaseDC(hwnd, hwnd_dc)  # 释放设备上下文

    # 将位图保存到文件(用于调试目的)
    bmp.save("asdf.png")  # 将位图保存到文件

    return bmp  # 返回位图图像对象


def get_clasname(hwnd):
    clasname = win32gui.GetClassName(hwnd)
    print('窗口类名:%s' % (clasname))
    return clasname

if __name__ == '__main__':
    hwnd = find_hwnd_by_title("SysListView32")  # 通过类名获取句柄
    # bd = WinMouse(hwnd[0])              #实例化WinMouse 类,传入句柄值
    # bd.left_click_move(109,180,232,341)


    #调用截图函数并返回位图
    print(get_bitmap(hwnd[0]))

程序截图效果

python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

2、通过局部图片分析全局坐标位置

现在我们已经能拿到指定程序的截图,现在我们需要知道鼠标应该点这个窗口的具体什么位置,比方说我想要点桌面的谷歌浏览器,就需要提前对谷歌截个图,然后分析图片在全局图片中的具体坐标

import cv2
import numpy as np

# 读取大图和小图
large_image = cv2.imread('asdf.png')
small_image = cv2.imread('111.png')

# 将小图匹配到大图中
result = cv2.matchTemplate(large_image, small_image, cv2.TM_CCOEFF)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

# 获取小图在大图中的坐标
top_left = max_loc
bottom_right = (top_left[0] + small_image.shape[1], top_left[1] + small_image.shape[0])

# 在大图中标记出小图的位置
cv2.rectangle(large_image, top_left, bottom_right, (0, 255, 0), 2)

# 显示标记后的大图
cv2.imshow('Result', large_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

 python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

可以看到成功定位到了图片所在的位置,下面我们通过定位获取到坐标

3、获取匹配坐标


from ctypes import wintypes
from PIL import Image


# 获取指定窗口的大小和位置
def get_window_rect(hwnd):
    try:
        # 调用win api获取窗口属性(位置、大小、状态)
        f = ctypes.windll.dwmapi.DwmGetWindowAttribute
    except WindowsError:
        f = None
    if f:
        # 创建结构体存储窗口的状态(这个结构体通常包含四个整数成员:left、top、right、bottom,分别表示矩形区域的左边界、上边界、右边界和下边界的坐标值)
        # https://blog.csdn.net/jxlhljh/article/details/129815925
        rect = wintypes.RECT()
        DWMWA_EXTENDED_FRAME_BOUNDS = 9

        # ctypes.wintypes.HWND(hwnd)  将我们传入的句柄转换成windows api定义的句柄类型
        # ctypes.wintypes.DWORD(DWMWA_EXTENDED_FRAME_BOUNDS)  指定获取窗口扩展边界信息
        # ctypes.byref(rect)    将获取到的窗口属性信息写入到这个结构体中
        # ctypes.sizeof(rect)  获取 RECT 结构体的大小
        f(wintypes.HWND(hwnd),
          wintypes.DWORD(DWMWA_EXTENDED_FRAME_BOUNDS),
          ctypes.byref(rect),
          ctypes.sizeof(rect)
          )
        return [rect.left, rect.top, rect.right, rect.bottom]


# 定义一个窗口操作的类
class windowControl():

    def __init__(self, hwnd):
        self.hwnd = hwnd  # 传入句柄

    # Target   图片位置
    # A   窗口位置
    def window_capture(self, Target, windowPosition, zqd=0.99):

        # 获取句柄窗口的大小
        # 获取窗口的宽高
        w_A = windowPosition[2] - windowPosition[0]
        h_A = windowPosition[3] - windowPosition[1]

        # 使用Windows API进行窗口图像的捕获和处理,创建了位图对象,并使用BitBlt函数将窗口图像复制到位图对象中
        hwndDC = win32gui.GetWindowDC(self.hwnd)
        mfcDC = win32ui.CreateDCFromHandle(hwndDC)
        saveDC = mfcDC.CreateCompatibleDC()
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, w_A, h_A)
        saveDC.SelectObject(saveBitMap)

        saveDC.BitBlt((0, 0), (w_A, h_A), mfcDC, (windowPosition[0], windowPosition[1]), win32con.SRCCOPY)

        ###获取位图信息
        bmpinfo = saveBitMap.GetInfo()
        bmpstr = saveBitMap.GetBitmapBits(True)

        ###生成图像
        im_PIL_TEMP = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
        # 使用OpenCV将图像转换为可处理的格式
        img = cv2.cvtColor(np.asarray(im_PIL_TEMP), cv2.COLOR_RGB2BGR)
        target = img

        template = cv2.imread(Target)
        theight, twidth = template.shape[:2]  # 获得模板图片的高宽尺寸
        # 执行模板匹配,采用的匹配方式cv2.TM_SQDIFF_NORMED
        result = cv2.matchTemplate(target, template, cv2.TM_SQDIFF_NORMED)

        # 归一化处理
        cv2.normalize(result, result, 0, 1, cv2.NORM_MINMAX, -1)
        # 寻找矩阵(一维数组当做向量,用Mat定义)中的最大值和最小值的匹配结果及其位置
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
        strmin_val = str(min_val)

        # 释放句柄、设备上下文、hwndDC
        win32gui.DeleteObject(saveBitMap.GetHandle())
        mfcDC.DeleteDC()
        saveDC.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, hwndDC)

        if abs(float(strmin_val)) <= (1 - zqd) and min_loc[0] != 0 and min_loc[1] != 0:
            return min_loc[0] + windowPosition[0], min_loc[1] + windowPosition[1]
        else:
            return 0, 0





if __name__ == '__main__':
    hwnd = find_hwnd_by_title("SysListView32")
    bd = WinMouse(hwnd[0])
    # bd.left_double_click(395,619) #400 450



    # 初始化窗口类
    w = windowControl(hwnd[0])
    #指定窗口检查坐标
    #(0, 0, 1920, 1080) 是窗口地址  0 0 表示最左边和最上面,1920  1080是最右边和最下面
    #我因为是在桌面操作,就按照最大值跑,后面用小窗口就直接引用窗口的大小
    # 0.95  是匹配度,模糊匹配95%的都符合
    result_x, result_y = w.window_capture("111.png", (0, 0, 1920, 1080), 0.95)
    print("目标图像在窗口中的位置坐标:", result_x, result_y)

    # 不知为何,匹配出来的坐标始终差一点,先手动调整了
    if result_x - 50 < 0:
        x = result_x
    else:
        x = result_x - 50
    if result_y - 50 < 0:
        y = result_y
    else:
        y = result_y - 50

    # 鼠标点击
    bd.left_double_click(x, y)

python 获取窗口句柄 模拟 点击按钮,python自动化,自动化,学习,运维

实测中存在一定的坐标偏移,目前没找到啥好方法,在研究研究

 

全量如下文章来源地址https://www.toymoban.com/news/detail-777805.html

import ctypes
import time

import cv2
import numpy as np
import win32api
import win32con
import win32gui
import win32ui
from PIL import Image


# ---------------------------------------------------句柄配置的分割线
# 获取当前主机上的所有句柄id
def 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


# 遍历窗口句柄的所有子窗口
def 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


# 根据标题查找窗口句柄
def 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))


# 声明鼠标操作的类
class 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)


def get_clasname(hwnd):
    clasname = win32gui.GetClassName(hwnd)
    print('窗口类名:%s' % (clasname))
    return clasname


from ctypes import wintypes
from PIL import Image


# 获取指定窗口的大小和位置
def get_window_rect(hwnd):
    try:
        # 调用win api获取窗口属性(位置、大小、状态)
        f = ctypes.windll.dwmapi.DwmGetWindowAttribute
    except WindowsError:
        f = None
    if f:
        # 创建结构体存储窗口的状态(这个结构体通常包含四个整数成员:left、top、right、bottom,分别表示矩形区域的左边界、上边界、右边界和下边界的坐标值)
        # https://blog.csdn.net/jxlhljh/article/details/129815925
        rect = wintypes.RECT()
        DWMWA_EXTENDED_FRAME_BOUNDS = 9

        # ctypes.wintypes.HWND(hwnd)  将我们传入的句柄转换成windows api定义的句柄类型
        # ctypes.wintypes.DWORD(DWMWA_EXTENDED_FRAME_BOUNDS)  指定获取窗口扩展边界信息
        # ctypes.byref(rect)    将获取到的窗口属性信息写入到这个结构体中
        # ctypes.sizeof(rect)  获取 RECT 结构体的大小
        f(wintypes.HWND(hwnd),
          wintypes.DWORD(DWMWA_EXTENDED_FRAME_BOUNDS),
          ctypes.byref(rect),
          ctypes.sizeof(rect)
          )
        return [rect.left, rect.top, rect.right, rect.bottom]


# 定义一个窗口操作的类
class windowControl():

    def __init__(self, hwnd):
        self.hwnd = hwnd  # 传入句柄

    # Target   图片位置
    # A   窗口位置
    def window_capture(self, Target, windowPosition, zqd=0.99):

        # 获取句柄窗口的大小
        # 获取窗口的宽高
        w_A = windowPosition[2] - windowPosition[0]
        h_A = windowPosition[3] - windowPosition[1]

        # 使用Windows API进行窗口图像的捕获和处理,创建了位图对象,并使用BitBlt函数将窗口图像复制到位图对象中
        hwndDC = win32gui.GetWindowDC(self.hwnd)
        mfcDC = win32ui.CreateDCFromHandle(hwndDC)
        saveDC = mfcDC.CreateCompatibleDC()
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, w_A, h_A)
        saveDC.SelectObject(saveBitMap)

        saveDC.BitBlt((0, 0), (w_A, h_A), mfcDC, (windowPosition[0], windowPosition[1]), win32con.SRCCOPY)

        ###获取位图信息
        bmpinfo = saveBitMap.GetInfo()
        bmpstr = saveBitMap.GetBitmapBits(True)

        ###生成图像
        im_PIL_TEMP = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
        # 使用OpenCV将图像转换为可处理的格式
        img = cv2.cvtColor(np.asarray(im_PIL_TEMP), cv2.COLOR_RGB2BGR)
        target = img

        template = cv2.imread(Target)
        theight, twidth = template.shape[:2]  # 获得模板图片的高宽尺寸
        # 执行模板匹配,采用的匹配方式cv2.TM_SQDIFF_NORMED
        result = cv2.matchTemplate(target, template, cv2.TM_SQDIFF_NORMED)

        # 归一化处理
        cv2.normalize(result, result, 0, 1, cv2.NORM_MINMAX, -1)
        # 寻找矩阵(一维数组当做向量,用Mat定义)中的最大值和最小值的匹配结果及其位置
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
        strmin_val = str(min_val)

        # 释放句柄、设备上下文、hwndDC
        win32gui.DeleteObject(saveBitMap.GetHandle())
        mfcDC.DeleteDC()
        saveDC.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, hwndDC)

        if abs(float(strmin_val)) <= (1 - zqd) and min_loc[0] != 0 and min_loc[1] != 0:
            return min_loc[0] + windowPosition[0], min_loc[1] + windowPosition[1]
        else:
            return 0, 0


if __name__ == '__main__':
    hwnd = find_hwnd_by_title("SysListView32")
    bd = WinMouse(hwnd[0])
    # bd.left_double_click(395,619) #400 450



    # 初始化窗口类
    w = windowControl(hwnd[0])
    #指定窗口检查坐标
    #(0, 0, 1920, 1080) 是窗口地址  0 0 表示最左边和最上面,1920  1080是最右边和最下面
    #我因为是在桌面操作,就按照最大值跑,后面用小窗口就直接引用窗口的大小
    # 0.95  是匹配度,模糊匹配95%的都符合
    result_x, result_y = w.window_capture("111.png", (0, 0, 1920, 1080), 0.95)
    print("目标图像在窗口中的位置坐标:", result_x, result_y)

    # 不知为何,匹配出来的坐标始终差一点,先手动调整了
    if result_x - 50 < 0:
        x = result_x
    else:
        x = result_x - 50
    if result_y - 50 < 0:
        y = result_y
    else:
        y = result_y - 50

    # 鼠标点击
    bd.left_double_click(x, y)

到了这里,关于python 自动化学习(三) 句柄获取、模拟按键、opencv安装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 爬虫学习(16):selenium自动化测试:人为模拟滑块移动

    爬虫学习(16):selenium自动化测试:人为模拟滑块移动

    场景描述:本次项目是给一个学校的填体温系统,做了一个自动填报工具,遇到了一个滑块,需要模拟移动一下。 这里只对滑块模拟部分代码进行展示,以帮助大家面临这种移动问题的解决。 思想其实很简单,就是模拟人慢慢的拖动。其实有个技术难点是缺口的识别,计算移

    2024年02月16日
    浏览(14)
  • 基于appnium+python+夜神模拟器的自动化

    基于appnium+python+夜神模拟器的自动化

    目录 1、安装夜神模拟器 2、定位元素 3、开始编码 首先搭好appnium环境!参考https://www.cnblogs.com/testlearn/p/11419797.html   下载安装夜神模拟器后,在cmd命令输入adb connect 127.0.0.1:62001,显示出设备则表示连接成功 打开安卓目录下,...sdktools目录下面的uiautomatorviewer.bat文件; 打开

    2024年02月12日
    浏览(8)
  • python自动化运维——模拟键盘鼠标重复性操作Pyautoui

    python自动化运维——模拟键盘鼠标重复性操作Pyautoui

    将程序与cmd.xls文件放在同一文件夹,每一步的截图也放在当前文件夹 通过图片在屏幕上面进行比对,找到点击处进行自动化操作 自动化rpa测试 1. Pyautoui 模块:主要针对图片进行定位 pyautogui.locateCenterOnScreen() ,在屏幕上面找到该图片位置后进行 pyautogui.click 单击,双击,右键

    2024年02月06日
    浏览(16)
  • python+appium+夜神模拟器搭建app自动化环境

    python+appium+夜神模拟器搭建app自动化环境

    目录 1.Appium用法 2.准备工具 1、安装JDK并配置JDK的环境变量 2、安装安卓SDK并配置SDK的环境变量。 3、安装夜神模拟器并在模拟器上安装被测app 4、安装并配置python 5、安装Node js 6、安装和配置Appium appium是做app的一个自动化框架,它的主要优势是支持android和ios,另外脚本语言也

    2024年02月07日
    浏览(12)
  • python+Appium实现手机移动端自动化 - 模拟器  或  真机【有线】

    python+Appium实现手机移动端自动化 - 模拟器 或 真机【有线】

    有线连接真机,实现真机自动化?实现模拟器自动化?在这里,都能满足你的需求! 什么都不说了,直接上干货吧,亲测可用哦👇 首先,先介绍下所需的环境,如下: JDK1.8 Appium Python3.x Android-sdk 夜深模拟器/mumu模拟器(PS: 自动化真机时可以选择不安装) 1)下载 方式1:官

    2024年02月16日
    浏览(15)
  • python微信PC端自动化-获取聊天记录

    目前已有许多微信客户端的自动化工具,可供我们自动获取聊天记录、发送消息等等。不过微信网页版现在已无法登录,因此一些python库如itchat、wxpy等已经无法使用了(基于网页)。 现在有大佬开发出了一个好玩的微信自动化python库——wxauto。wxauto基于uiautomation、win32gui等自

    2024年02月03日
    浏览(45)
  • python 邮箱自动化操作(1) 邮件的自动获取 imapclient 非常详细!有范例!!

    python 邮箱自动化操作(1) 邮件的自动获取 imapclient 非常详细!有范例!!

    python 3.8 imapclient:3.0.0  注意: 这里是以腾讯企业邮箱为例,不同的邮箱可能会有不同的编码格式,就可能会产生错误。 SMTP (Simple Mail Transfer Protocol) : SMTP 是用于发送电子邮件的标准协议。 客户端使用 SMTP 将邮件发送到邮件服务器。 SMTP 客户端连接到 SMTP 服务器的 25 号端口

    2024年04月22日
    浏览(12)
  • Python爬虫技术之Selenium自动化测试及模拟点击页面爬虫最全知识

    Python爬虫技术之Selenium自动化测试及模拟点击页面爬虫最全知识

    Selenium是一套Web网站的程序自动化操作解决方案(比如点击界面按钮,在文本框中输入文字等操作) Selenium的自动化原理如下 3.1 下载selenium库的命令 3.2 下载谷歌浏览器Chrome对应的驱动 驱动库网址:http://chromedriver.storage.googleapis.com/index.html 注意:根据自己的Chrome版本进行选择

    2024年01月16日
    浏览(49)
  • Appium+python自动化(二十二)- 控件坐标获取(超详解)

    Appium+python自动化(二十二)- 控件坐标获取(超详解)

    有些小伙伴或者是童鞋可能会好奇会问上一篇中的那个monkey脚本里的坐标点是如何获取的,不是自己随便蒙的猜的,或者是自己用目光或者是尺子量出来的吧,答案当然是:NO。获取控件坐标点的方式这里宏哥给小伙伴们分享和讲解三种方法,个人喜欢第二种,也推荐小伙伴

    2024年02月15日
    浏览(13)
  • Appium+python自动化(二十五)-获取控件ID(超详解)

    Appium+python自动化(二十五)-获取控件ID(超详解)

    在前边的第二十二篇文章里,已经分享了通过获取控件的坐标点来获取点击事件的所需要的点击位置,那么还有没有其他方法来获取控件点击事件所需要的点击位置呢?答案是:Yes!因为在不同的大小屏幕的手机上获取控件的坐标点,不是一样的,而是有变化的,因此在不同

    2024年02月15日
    浏览(13)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包