百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

【python 工具】pywinauto自动化、pyautogui-操作windows

myzbx 2025-04-08 16:43 15 浏览

pywinauto 简单使用

pywinauto是一组实现windows GUI窗口自动化的python模块。使用pywinauto可以方便的给窗口发送鼠标、键盘事件,使用python程序控制程序窗口。主要是针对CS程序自动化应用。必须是GUI程序。

参考: https://github.com/pywinauto/pywinauto https://www.cnblogs.com/steveni/p/15362833.html

1. 官网解释

大概意思就是程序有两种backend,win32 (默认是这个)和 uia, 针对不同的backend 有不同的窗体检测工具。

Spy++ (定位元素工具(win32))
Inspect(定位元素工具(uia))
UI Spy (定位元素工具)
Swapy(可简单生成pywinauto代码)

下面例子用 spy++ ,一个小型的压缩包,解压后就可以使用。

2. 简单使用

可以简单理解,如果要想操作,肯定是需要启动一个程序、或者连接到一个已经存在的APP,然后获取其相关窗口、对话框等控件元素进行操作

0. spy++ 简单使用

spy++ 可以用于定位一些app的相关的class、title 等信息,也可以定位内部的窗口:

(1).找到如下按钮

(2)拖到对应app的窗口会提示相关的句柄、title、class 等信息, 比如拖到记事本显示信息如下:

1. 启动和定位APP以及查看包含哪些组件

import pywinauto
from pywinauto.application import Application

# 获取一个APP的方法
# 第一个是启动, start(默认超时是5s)
app = Application().start('notepad.exe')
dlg = app.window(title='无标题 - 记事本')
print(dlg.__class__)
dlg.print_control_identifiers()

print('======')
# 第二个是获取, 根据title 获取。 从所有应用程序直接获取
app1 = pywinauto.Desktop()
dlg1 = app1['无标题 - 记事本']
print(dlg1.__class__)
dlg1.print_control_identifiers()

结果:


Control Identifiers:

Notepad - '无标题 - 记事本'    (L48, T259, R1345, B807)
['Notepad', '无标题 - 记事本Notepad', '无标题 - 记事本']
child_window(title="无标题 - 记事本", class_name="Notepad")
   | 
   | Edit - ''    (L56, T310, R1337, B777)
   | ['Edit', '无标题 - 记事本Edit']
   | child_window(class_name="Edit")
   | 
   | StatusBar - ''    (L56, T777, R1337, B799)
   | ['StatusBar', '无标题 - 记事本StatusBar', 'StatusBar   第 1 行,第 1 列', 'StatusBar   Windows (CRLF)', 'StatusBar100%']
   | child_window(class_name="msctls_statusbar32")
======

Control Identifiers:

Notepad - '无标题 - 记事本'    (L48, T259, R1345, B807)
['Notepad', '无标题 - 记事本Notepad', '无标题 - 记事本']
child_window(title="无标题 - 记事本", class_name="Notepad")
   | 
   | Edit - ''    (L56, T310, R1337, B777)
   | ['Edit', '无标题 - 记事本Edit']
   | child_window(class_name="Edit")
   | 
   | StatusBar - ''    (L56, T777, R1337, B799)
   | ['StatusBar', '无标题 - 记事本StatusBar', 'StatusBar   第 1 行,第 1 列', 'StatusBar   Windows (CRLF)', 'StatusBar100%']
   | child_window(class_name="msctls_statusbar32")

也可以根据processId 、句柄、class_name 、title等获取,当然可以组合

# 获取一个APP的方法
# 根据进程ID获取
# app = Application().connect(process=22972)
# 根据句柄获取,句柄可以用spy++ 点击后查看到
# app = Application().connect(handle=0x0051082A)
# 根据class_name 获取,属性值可以用spy++ 获取
from pywinauto import Application

app = Application().connect(class_name='Notepad')
dlg = app.window().print_control_identifiers()

结果:

Control Identifiers:

Notepad - '无标题 - 记事本'    (L-32000, T-32000, R-31840, B-31972)
['Notepad', '无标题 - 记事本Notepad', '无标题 - 记事本']
child_window(title="无标题 - 记事本", class_name="Notepad")
   | 
   | Edit - ''    (L-32000, T-32000, R-30719, B-31533)
   | ['Edit', '无标题 - 记事本Edit']
   | child_window(class_name="Edit")
   | 
   | StatusBar - ''    (L-32000, T-31533, R-30719, B-31511)
   | ['StatusBar   Windows (CRLF)', 'StatusBar100%', 'StatusBar   第 1 行,第 1 列', '无标题 - 记事本StatusBar', 'StatusBar']
   | child_window(class_name="msctls_statusbar32")

2. 遍历打印该窗口所有的菜单

from pywinauto import Application

'''
通过 menu().items() 可以获取当前级次下的菜单项,再通过 sub_menu() 判断是有下级菜单项,不断的递归,把所有的菜单项打印出来
'''


def list_menu(menu_item, space):
    '''
     作用:递归法遍历菜单
    '''
    for i in menu_item.items():
        if (len(i.text()) > 0):
            print(space + i.text())
        if (i.sub_menu() != None):
            list_menu(i.sub_menu(), "    " + space)


app = Application().connect(class_name='Notepad')
dlg = app.window()

list_menu(dlg.menu(), "")

结果:

Control Identifiers:

Notepad - '无标题 - 记事本'    (L-8, T-8, R1928, B1048)
['Notepad', '无标题 - 记事本', '无标题 - 记事本Notepad']
child_window(title="无标题 - 记事本", class_name="Notepad")
   | 
   | Edit - ''    (L0, T43, R1920, B1018)
   | ['Edit']
   | child_window(class_name="Edit")
   | 
   | StatusBar - ''    (L0, T1018, R1920, B1040)
   | ['StatusBar   Windows (CRLF)', '无标题 - 记事本StatusBar', 'StatusBar100%', 'StatusBar   第 1 行,第 1 列', 'StatusBar']
   | child_window(class_name="msctls_statusbar32")
文件(&F)
    新建(&N)	Ctrl+N
    打开(&O)...	Ctrl+O
    保存(&S)	Ctrl+S
    另存为(&A)...
    页面设置(&U)...
    打印(&P)...	Ctrl+P
    退出(&X)
编辑(&E)
    撤消(&U)	Ctrl+Z
    剪切(&T)	Ctrl+X
    复制(&C)	Ctrl+C
    粘贴(&P)	Ctrl+V
    删除(&L)	Del
    &使用 Bing 搜索...	Ctrl+E
    查找(&F)...	Ctrl+F
    查找下一个(&N)	F3
    替换(&R)...	Ctrl+H
    转到(&G)...	Ctrl+G
    全选(&A)	Ctrl+A
    时间/日期(&D)	F5
格式(&O)
    自动换行(&W)
    字体(&F)...
查看(&V)
    缩放(&Z)
        &放大	Ctrl+Plus
        &缩小	Ctrl+Minus
        &恢复默认缩放	Ctrl+0
    状态栏(&S)
帮助(&H)
    查看帮助(&H)
    关于记事本(&A)

4. 找子窗口

from pywinauto import Application

app = Application().connect(class_name='Notepad')
dlg = app.window()
print(dlg)

# 找子窗口
dlg2 = dlg.child_window(class_name='Edit')
print(dlg2)
print(dlg.Edit)
print(dlg['Edit'])

如果找不到元素或者找到多个元素,直接调用方法会报错,解决办法就是自己通过下标指定index(可以结合下面draw_outline 划线 来定位元素)

saveasAppWindow.child_window(class_name='ToolbarWindow32', found_index=3).click()

5. 对找到的元素打标记

from pywinauto import Application

app = Application().connect(class_name='Notepad')
dlg = app.window()
print(dlg)

# 找子窗口
dlg2 = dlg.child_window(class_name='Edit')
dlg2.draw_outline(colour = 'red')

结果: 会用红线标记找到的元素

3. 键盘和点击事件

如下例子,打开记事本,输入一些字符串然后另存为E:/1.txt

'''
1. 打开记事本
2. 在编辑区域输入pywinauto Works!
3. 点击菜单栏的 文件->另存为

4. 在另存为窗口输入路径: E:\\
5. 名称输入 1.txt
6. 点击确定按钮
'''
from pywinauto import Application
from pywinauto.keyboard import send_keys

'''
在记事本窗口操作
'''
# 打开记事本
app = Application().start('notepad.exe')
dlg = app.window(title='无标题 - 记事本')

# 找到编辑区域输入内容
dlg.child_window(class_name='Edit').type_keys("pywinauto Works!", with_spaces=True)

# 选择菜单, 文件->另存为
dlg.menu_select("文件(&F)->另存为(&A)")

'''
在另存为窗口操作
'''
# 输入的对话框输入路径和文件名称然后点击确定
# 选择文件(pywinauto自动选择)
saveasApp = Application().connect(title='另存为')
saveasAppWindow = saveasApp.window(found_index=0)
# 选择文件上传的窗口
# 1. 上面路径选择窗口: 点击 -》 输入路径 -》 回车
# 选择文件地址输入框,点击
saveasAppWindow.child_window(class_name='ToolbarWindow32', found_index=3).click()
# 键盘输入上传文件的路径
send_keys("E:\\")
# 键盘输入回车,打开该路径
send_keys("{VK_RETURN}")
# 2. 名称按钮输入名称
saveasAppWindow['Edit'].type_keys("1.txt")
# 3. 保存按钮点击
saveasAppWindow.child_window(title='保存(&S)').click()

结果和期望的一样会自动进行一系列操作。

pyautogui 简单使用

感觉这个更加方便,这个不依赖于平台,就是类似于手动去操作鼠标键盘。进行一系列的事件。

1. 安装

pip install pyautogui

2. 简单使用

import time

import pyautogui

time.sleep(1)

# 获取窗口大小
# x1, y1 = pyautogui.size()
# print(x1, y1)
# 结果是鼠标当前位置,可以想象成以屏幕左上角为原点的第一象限
# x, y = pyautogui.position()
# print(x, y)
# 判断像素是否在屏幕上
# result = pyautogui.onScreen(100000, 1)
# print(result)

# 鼠标函数
# 鼠标移动到(x,y),整个过程持续1s
# pyautogui.moveTo(500, 500, 1)

#  相当于按下左键拖动到x, y
# pyautogui.dragTo(500, 500, 1)
# 鼠标左键点击(x,y)
# pyautogui.click(24, 38)
# pyautogui.rightClick(770, 380)
# 中击
# pyautogui.middleClick(770, 380)
# 双击
# pyautogui.doubleClick(770, 380)
# 三击
# pyautogui.tripleClick(770, 380)
# 鼠标在(x,y)滚动
# pyautogui.scroll(770, 380)
# 鼠标按下, 可以指定鼠标按键。同理mouseUp为鼠标松开
# pyautogui.mouseDown(24, 38, button='left')
# pyautogui.mouseDown(770, 380, button='right')

# 键盘函数
# 输入"hello"
# pyautogui.typewrite("hello")
# 依次输入"a","b","c"
# pyautogui.typewrite(['a','b','c'])
# 按键ctrl+c
# pyautogui.hotkey('ctrl', 'a')
# pyautogui.hotkey('ctrl', 'c')
# pyautogui.hotkey('ctrl', 'v')
# pyautogui.hotkey('ctrl', 'v')
# 按下键盘
# pyautogui.keyDown('a')
# pyautogui.keyUp('a')
# pyautogui.keyDown('b')
# pyautogui.keyUp('b')

# 提示框相关,类似JS的三种框
# alert, confirm, prompt, password
alert = pyautogui.alert('stop')
print(alert)
va1 = pyautogui.confirm('stop')
print(va1)
prompt = pyautogui.prompt('stop')
print(prompt)
password = pyautogui.password('stop')
print(password)

# 屏幕截图
# region参数,截图区域,由左上角坐标、宽度、高度4个值确定,如果指定区域超出了屏幕范围,超出部分会被黑色填充,默认`None`,截全屏
# pyautogui.screenshot('E:/shot{}.png'.format(1), region=(0, 0, x1, y1))

这里需要注意。selenium 获取到的元素是相对于浏览器左上角为坐标,高度等信息不包含菜单栏。结合使用需要注意菜单栏的高度。

相关推荐

下一代EUV光刻机,万事俱备?(下一代光刻技术)

光刻机在半导体领域一向是个热门话题,这个能一次又一次突破工艺极限的设备仿佛一个时光机器,连接着芯片的现在和未来。从ASML宣布将推出下一代光刻机开始,人们的目光就从当前最新一代的0.33NA光刻系...

鸿蒙NEXT-状态管理V1和状态管理V2的差别

1.在V2中没有了@Link,来进行父组件和子组件的双向绑定。所以我们需要在子组件中通过@Event,调用父组件的事件,来实现装饰回调(白话来讲就是:子组件调用@Event装饰的函数,传入参数,修改父...

15个Excel工作表技巧,效率必备,办公必备

在数据的统计分析中,最常用的办公软件就是Office中的Excel,如果你对Excel的应用技巧掌握较少,可以从学习本文开始。一、Excel工作表技巧:锁定标题行方法:将光标定位到标题行下面的任意单...

苹果这个新的稳定平台适合用户/开发者测试

苹果好低调地就发布了这个平台,还好我们没有错过。从今天起你将有一种新的、更简便的方式去了解,Safari和其他使用Webkit的应用中将有什么特性和完善。SafariTechnologyPr...

教程|PPT绘制箭头最全攻略,收藏一下

微信公众号:有宝物的柜子编辑:落水无波2020-05-02原创由于没有较好的方向,公众号的更新一直暂停,根据昨天的留言,今天更新一篇关于绘制各类箭头的PPT教程,希望帮到需要的同学。那么,我们从最简...

老板让我制作动态图表,我不会,同事说用vlookup函数3步就搞定了

大家好,动态图表你会制作吗?是不是觉得动态图表制作起来应该非常的麻烦?其实并不是,大家熟悉的vlookup函数就可以用来制作动态图表,操作也非常的简单,下面就让我们来一起操作下吧COLUMN函数在这里...

ES6(ECMAScript 2015)主要特性一览

下面按“语法糖”“新数据类型/API”“异步&迭代”三大类,总结ES6的核心要点。---##一、语法糖-块级作用域:`let`、`const`-`let`:声明块级变量,不可...

Excel工作表中F1—F12应用技巧解读,再不会就Out了

键盘中,有一组非常显眼的功能键,就是F1—F12,其功能非常的强大,在Excel工作表中也有特别重要的作用,通过本文的学习,相信你一定有所了解。一、Excel工作表功能键:F1。功能:打开帮助对话框...

es6的模块和核心语法(es6模块化的语法)

目标:通过本教案,将掌握ES6的核心语法和模块化概念,理解解构表达式的用法,并初步了解Node.js的使用,为学习Vue3打下坚实的基础。学习内容:ES6核心语法模块化解构表达式Nod...

ECMAScript标准制定过程展示及ES7新特性披露

2015年6正式发布的ES6是ECMAScript的最新版本,它的发布具有里程碑意义,不仅带来了众多的新特性,而且自此将改变ECMAScript的发布策略。本文将会介绍ECMAScript标准的最新...

冲激函数的理解(冲激函数的性质有哪些问题)

一、冲激函数是什么?1.通俗理解想象一根理想化的针:长度无限小(宽度为0)高度无限大(强度无限)但总面积(能量)=1这就是冲激函数δ(t)!类比:用针尖瞬间触碰水面→产生一个无限高但宽度为...

前端常见面试 - 请求篇(前端面试经典问题)

对于前端来说,请求是前端日常工作必备的,通过请求才能与后端进行数据交互,尤其在现在前后端分离的开发模式下,请求显得就更加重要。因此,对于前端开发者来说,掌握请求就很重要。下面将从http请求和常见...

不会js中原型、原型链与constructor到底是什么?

关注我:知码前端,获取更多前端知识~~~前言哇呀呀~我说寒山说哭我带你出我敬滴酒带你出我欲成冰再也无退路怎舍寒冰冰冻我心哭~~~Hello,广大的前端小伙伴们,又到了写文章的时候,我们说一下在...

你真的懂js的执行上下文吗?(详细说明js的执行过程)

JavaScript执行上下文目录JavaScript执行上下文前言概念执行上下文的特点JS如何管理多个执行上下文执行栈执行上下文的生命周期创建阶段ThisBinding词法环境变量环境执行阶段销...

停止滥用箭头函数:这5个场景请务必使用 function

自ES6问世以来,箭头函数(ArrowFunctions)以其简洁的语法和对this的词法绑定,迅速成为了JavaScript开发者的“新宠”。我们似乎倾向于在任何可以使用函数的地方都换...