【测试】pywinauto的简单使用(安装、常用对象、元素控件、鼠标操作、键盘操作)
1.说明
pywinauto是一个用于自动化Python 模块,适合Windows系统的软件(GUI),可以通过Pywinauto遍历窗口(对话框)和窗口里的控件,也可以控制鼠标和键盘输入,所以它能做的事情比之前介绍的pysimplegui更多
2.安装
一般使用pip安装就行了
pip install pywinauto
官网文档:https://pywinauto.readthedocs.io/en/latest/
3.Application
我们要控制软件的第一件事就是启动一个Windows软件,每一个软件(进程)都是一个Application对象
实例化Application对象的时候可以传入一个backend参数,可选值为win32(默认)和 uia ,
win32对应的框架:MFC、VB6、VCL、简单的 WinForms 控件和大多数旧的遗留应用程序
uia对应的框架:WinForms、WPF、商店应用程序、Qt5、浏览器
如果无法知道要测试的软件是属于哪种框架,可以使用 Inspect(对应uia) 和 Spy++(对应win32) 看看,你看哪个显示得更全就选哪个。Inspect和Spy++需要自己安装一下
下面是Application对象的主要方法
| 方法 | 常用参数 | 说明 |
|---|---|---|
| start() | cmd_line、timeout、retry_interval | 通过cmd命令启动一个软件(进程) |
| connect() | process、handle、path、timeout | 连接一个进程,一般是使用进程号(任务管理器可以看到) |
| top_window() | / | 获取应用的顶层窗口 |
| window() | title、title_re、class_name、best_match | 获取单个窗口(WindowSpecification) |
| windows() | title、title_re、class_name | 获取多个窗口(UIAWrapper) |
| is64bit() | / | 是否64位应用 |
| cpu_usage | interval | CPU占用率 |
| wait_cpu_usage_lower() | threshold、timeout | 等待CPU占率小于某个阈值 |
| active()() | / | 搜索返回一个激活的窗口 |
| kill() | soft | 结束进程 |
| wait_for_process_exit() | timeout、retry_interval | 等待进程结束 |
举例,启动一个微信应用,通过进程号连接,进程号就是在任务管理器里详细信息看到的PID

from pywinauto import Applicationapp = Application(backend="uia")
# app.start(r"D:\Program Files (x86)\Tencent\WeChat\WeChat.exe")
app.connect(process=6556)
print("is64bit:", app.is64bit())
print("cpu_usage:", app.cpu_usage())
app.wait_cpu_usage_lower()
# app.active() # 如果指定时间内不激活则报错
print("kill:", app.kill())
print("wait_for_process_exit:", app.wait_for_process_exit())
4.WindowSpecification
我们要获取窗口,一个窗口都是一个WindowSpecification对象,可以通过Application对象的window()方法获取,参数可以是title、classname或者best_match等,这都可以在inspect.exe上看到,不过需要注意的是inspect看到的Name其实对应的是window()的title参数

WindowSpecification对象常用的方法如下
| 方法 | 常用参数 | 说明 |
|---|---|---|
| maximize() | / | 最大化窗口 |
| minimize() | / | 最小化窗口 |
| restore() | / | 恢复窗口 |
| close() | / | 关闭窗口 |
| get_show_state() | / | 获取窗口状态,0正常1最大化2最小化 |
| was_maximized() | / | 当前是否最大化 |
| draw_outline() | colour、thickness | 给窗口画个框以便定位 |
| print_control_identifiers() | / | 打印所有子窗口和子元素(会打印出对应的control_type) |
| child_window() | title、control_type | 获取子窗口 |
| exists() | timeout | 窗口是否存在 |
| wait() | wait_for, timeout | 等待窗口变成某个状态(exists、visible、enabled、ready、active) |
| wait_not() | wait_for_not, timeout | 等待窗口不处于某个状态(exists、visible、enabled、ready、active) |
举个栗子
dlg = app.window(class_name="WeChatMainWndForPC")
# dlg = app.window(title="微信")
print("get_show_state:", dlg.get_show_state())
print("was_maximized:", dlg.was_maximized())
dlg.print_control_identifiers()
dlg.draw_outline()
dlg.maximize()
dlg.restore()
dlg.minimize()
dlg.close()
5.元素控件
一个窗口里一般都会有各种各样的元素,比如说按钮 (Button)、编辑栏(Edit)、树状视图(Tree View)、复选框(CheckBox)、对话框(Dialog)、工具栏(Toolbar)、状态栏(StatusBar)、列表框(ListBox)、窗格(Pane)、菜单(Menu)、菜单栏(MenuItem)、静态内容(Static)、工具提示(ToolTips)、列表控件(ListView)、单选框(RadioButton)、组合框(ComboBox)、选项卡控件(TabControl)、组框 (GroupBox)、弹出菜单(PopupMenu)、头部(Header)等
因为控件类型太多了不能一个一个学习,但是它们都有一个 element_info 的属性,访问之后会返回一个继承于ElementInfo的对象(UIAElementInfo或HwndElementInfo),比较重要的属性或方法如下
| 方法或属性 | 常用参数 | 说明 |
|---|---|---|
| name | / | 元素的真实名(一般是title) |
| visible | / | 元素是否可见 |
| rich_text | / | 元素的全名 |
| rectangle | / | 返回元素的位置以及宽高 |
| class_name | / | 类名 |
| enabled | / | 元素是否处于可用状态 |
| parent | / | 返回父元素 |
| children() | title、title_re、class_name、best_match | 返回符合要求的子元素(列表) |
| iter_children() | title、title_re、class_name、best_match | 迭代符合要求的子元素(生成器) |
这些元素除了有element_info可以获取一些元素的主要信息,它们还都被包装成一个Wrapper,所以也可以学一下BaseWrapper的常用方法和属性。其实BaseWrapper的方法基本上都是对ElementInfo进一步包装,我只列出部分方法,如下表
| 方法或属性 | 常用参数 | 说明 |
|---|---|---|
| element_info | / | 返回当前元素的ElementInfo对象 |
| from_point() | x、y | 通过坐标查找ElementInfo |
| class_name() | / | 类名,实际是调用element_info.class_name |
| friendly_class_name() | / | 友好的类名,同上 |
| window_text() | / | 元素的文本,实际是调用element_info.rich_text |
| is_visible() | / | 元素是否可见,实际是调用element_info.visible |
| is_enabled() | / | 元素是否可用,实际是调用element_info.enabled |
| rectangle() | / | 元素的位置和宽高,实际是调用element_info.rectangle |
| process_id() | / | 进程号,实际是调用element_info.process_id |
| draw_outline() | colour、thickness | 给当前元素画个框 |
| click_input() | button、coords、double | 鼠标操作,实际是调用mouse模块的_perform_click_input() |
| type_keys() | / | 键盘操作,实际是调用keyboard模块的send_keys() |
dlg = app.window(class_name="WeChatMainWndForPC")
list_data = dlg.child_window(title="会话", control_type="List")
for item in list_data:print(type(item))element_info = item.element_infoprint(type(element_info))print("window_text:", )print("rich_text:", element_info.rich_text)print("name:", element_info.name)print("visible:", element_info.visible)print("rectangle:", element_info.rectangle)print("class_name:", element_info.class_name)print("enabled:", element_info.enabled)print("parent:", element_info.parent)print("children:", element_info.children())print("iter_children:", element_info.iter_children())if item.window_text() == "文件传输助手":item.click_input()item.type_keys("冰冷的希望")item.type_keys("{VK_RETURN}")print()
说明一下,每个控件元素都有对应的Wrapper,所以上面的方法也不一定都用,需要根据实际情况进行测试区分。另外,比较有用的click_input()和type_keys()这两个方法分别用于操作鼠标和键盘(输入),下面我会单独拿出来说一下
6.鼠标操作
鼠标点击肯定离不开点击的位置,桌面就是一个坐标,左上角为坐标原点,往右是X轴正向,往下是Y轴正向。pywinauto提供了一个mouse模块用于鼠标操作,最核心的方法是_perform_click_input(),不过它是一个私有方法,我们调用的是基于它的封装方法,如下表
| 方法 | 参数 | |
|---|---|---|
| click() | button、coords | 单击鼠标某个键 |
| double_click() | button、coords | 双击鼠标某个键 |
| right_click() | coords | 单击鼠标右键 |
| move() | coords | 移动鼠标 |
| press() | button、coords | 按下鼠标 |
| release() | button、coords | 放开鼠标 |
| scroll() | coords、wheel_dist | 滚动鼠标滚轮 |
| wheel_click() | coords | 单击鼠标滚轮 |
参数说明:
参数button的默认值都是“left”,即鼠标左键,可选值有left、right、middle、move、wheel、x
参数coords 的默认值都是元组(0, 0),元组里的两个整数分别是X、Y轴的值
参数wheel_dist表示滚动的距离,大于0是向上滚动,小于0是向下滑动
举个栗子
from pywinauto import Application, mouseapp = Application(backend="uia")
app.connect(process=4352)
dlg = app.window(class_name="WeChatMainWndForPC")
list_data = dlg.child_window(title="会话", control_type="List")
for item in list_data:if item.window_text() == "文件传输助手":# item.click_input()rectangle = item.element_info.rectanglex = int((rectangle.left + rectangle.right) / 2)y = int((rectangle.top + rectangle.bottom) / 2)mouse.click(button='left', coords=(x, y))time.sleep(1)mouse.click("right", (x, y))time.sleep(1)mouse.move((x - 50, y)) # 往左边移动50个像素time.sleep(1)mouse.click(coords=(x, y))break
7.键盘操作
键盘操作主要是按下键盘上的按键,相关方法在keyboard模块,最最主要的是send_keys()方法,第一个参数keys就是我们需要按下的按键,其他参数比如说with_spaces、with_tabs、with_newlines、turn_off_numlock、set_foreground、vk_packet,一看就知道作用,而且都是布尔值,此处不进行举例
pywinauto支持的完整的按键可以在官方文档查看,https://pywinauto.readthedocs.io/en/latest/code/pywinauto.keyboard.html
下面我列举出的是一些比较常用的按键
| 按键 | 符号 | 说明 |
|---|---|---|
| Shift | VK_SHIFT | 上档键 |
| Ctrl | VK_CONTROL、VK_LCONTROL、VK_RCONTROL | Ctrl键、左右Ctrl键 |
| Alt | VK_MENU | Alt键 |
| Windows | VK_LWIN、VK_RWIN | 左右win键 |
| Space | VK_SPACE | 空格键 |
| backspace | BACKSPACE | 退格键 |
| enter | ENTER | 回车键 |
| esc | ESC | 退出键 |
| table | VK_TAB | 制表键 |
| left、right、up、down | VK_LEFT、VK_RIGHT、VK_UP、VK_DOWN | 上下左右方向键 |
| f1~f24 | VK_F1、VK_F2…VK_F24 | f1到f24 |
| capslock | CAPSLOCK | 大写键 |
说明:
1.使用按键时需要搭配大括号,比如说按下回车键是 '{ENTER}' (是字符串)
2.在Windows平台默认是发送虚拟按键的,以VK_开头的按键,都是指虚拟按钮,如果不想使用虚拟按钮可以把VK_前缀去掉,把send_keys()的vk_packet参数改为False即可
单个按键按下抬起还不够,往往需要组合键,这时候就需要修饰符了,在大括号里可以使用down、up控制按键什么时候按下和抬起,如果后面加上数字,表示按下多少次
list_data = dlg.child_window(title="会话", control_type="List")
for item in list_data:if item.window_text() == "文件传输助手":item.click_input()# item.type_keys("冰冷的希望")send_keys(" ") # 随便输入字符串send_keys("{VK_CONTROL down} a {VK_CONTROL up}") # 快捷键Ctrl+a(先按下Ctrl,再按下a,最后放开Ctrl)send_keys("{BACKSPACE}") # 按下退格键删除文本send_keys("{. 6}") # 按6次小数点send_keys("冰冷的希望{ENTER}") # 输入文本,按下回车键
当然,很多时候使用down、up修饰感觉不够简洁,所以pywinauto还提供了简化写法,使用+代替{VK_SHIFT},使用^代替{VK_CONTROL},使用%代替{VK_MENU}
send_keys('^a^c') # 按下Ctrl+a之后再按下Ctrl+c,即全选复制
send_keys('+{INS}') # 按下Shift+Ins键
send_keys('%{F4}') # 按下Alt+F4键
如果不想按下按钮,纯属想要输入纯字符串,那就需要取消转义了,注意修饰符和按钮的写法是不一样的
send_keys('{^}a{^}c{%}') # 输入字符串"^a^c%"而不是当成快捷键
send_keys('{{}ENTER{}}') # 输入字符串"{ENTER}"而不是按下回车键
相关文章:
【测试】pywinauto的简单使用(安装、常用对象、元素控件、鼠标操作、键盘操作)
1.说明 pywinauto是一个用于自动化Python 模块,适合Windows系统的软件(GUI),可以通过Pywinauto遍历窗口(对话框)和窗口里的控件,也可以控制鼠标和键盘输入,所以它能做的事情比之前介…...
Java基础十八(正则表达式 + 日期时间)
1. 正则表达式 1.1 普通字符 字符描述示例[abc]匹配 […] 中所有字符[hlo] 匹配字符串 "hello world" 中所有的 h l o 字母[^ABC]匹配除了 […] 中所有字符[hlo] 匹配字符串 "hello world" 中除了 h l o 的所有字母[^a-z]匹配除了 […] 中所有字符[hlo] 匹…...
Linux C 多进程编程(面试考点)
嵌入式开发为什么要移植操作系统? 1.减小软硬件的耦合度,提高软件的移植性 2. 操作系统提供很多库和工具(QT Open CV),提高开发效率 3.操作系统提供多任务机制,______________________? (提高C…...
c++一级
与7无关的数 #include<iostream> #include<iomanip> using namespace std; int main() { int n,a,sum0,c0; cin>>n; for(int i1;i<n;i){ if(i%7!0){ ai; c0; …...
Code Lab - 34
GAT里面有一些地方看的不是太懂(GAT里Multi Attention的具体做法),暂时找了参考代码,留一个疑问 1. 一个通用的GNN Stack import torch_geometric import torch import torch_scatter import torch.nn as nn import torch.nn.fun…...
后端返回文件流,前端怎么导出、下载(8种方法可实现)
在前端导出和下载后端返回的文件流时,可以使用以下几种方法: 使用window.open()方法: 在前端使用window.open()方法打开一个新的窗口或标签页,并将后端返回的文件流作为URL传递给该方法。浏览器会自动下载该文件。例如:…...
什么是 ThreadLocal?
ThreadLocal 是 Java 中的一个类,用于在多线程环境下,为每个线程提供独立的变量副本。每个线程可以通过 ThreadLocal 存储和获取数据,而不会影响其他线程的数据。这在某些情况下非常有用,特别是当多个线程需要访问共享数据,但又希望保持数据的隔离性时。 ThreadLocal 主要…...
CANOCO5.0实现冗余分析(RDA)最详细步骤
在地理及生态领域会常使用RDA分析,RDA的实现路径也有很多,今天介绍一下CANOCO软件的实现方法。 1.软件安装 时间调整到2010年 2.数据处理 得有不同的物种或者样点数值,再加上环境因子数据。 3.软件运行 4.结果解读 结果解读主要把握这几点…...
【tkinter 专栏】掷骰子游戏
文章目录 前言本章内容导图1. 需求分析2. 系统功能结构3. 设计流程4. 系统开发环境5. 系统预览6. 窗口布局7. 功能实现用户和电脑选择骰子的点数大小摇骰子过程实现判断游戏结果单击开始按钮进行游戏源代码汇总前言 本专栏将参考《Python GUI 设计 tkinter 从入门到实践》书籍…...
19 NAT穿透|python高级
文章目录 网络通信过程NAT穿透 python高级GIL锁深拷贝与浅拷贝私有化import导入模块工厂模式多继承以及 MRO 顺序烧脑题property属性property装饰器property类属性 魔法属性\_\_doc\_\_\_\_module\_\_ 和 \_\_class\_\_\_\_init\_\_\_\_del\_\_\_\_call\_\_\_\_dict\_\_\_\_str…...
2023常见前端面试题
以下是一些2023年秋招常见的前端面试题及其答案: 1. 请解释一下什么是前端开发? 前端开发是指使用HTML、CSS和JavaScript等技术来构建网页和用户界面的过程。前端开发人员负责将设计师提供的视觉设计转化为可交互的网页,并确保网页在不同设备…...
登录校验-JWT令牌-生成和校验
目录 JWT-生成 具体代码 运行结果如下 JWT-校验 具体代码 运行结果如下 小结 JWT-生成 具体代码 /*** 测试JWT令牌的生成*/Testpublic void TestJWT() {// 设置自定义内容Map<String, Object> claims new HashMap<>();claims.put("id", 1);claims…...
GIT 常用指令
基础指令 $ git init #初始化仓库,在该文件夹创建的为workspace$ git add . #已暂存 [.通配符,全部添加]$ git commit -m "log add file" #提交到仓库,并写了日志 ”log add file“$ git status #查看状态,可查看被修改的文件…...
多目标优化
https://zhuanlan.zhihu.com/p/158705342 概念 单目标优化只有一个优化目标,所以可以比较其好坏。 但是多目标优化,在需要优化多个目标时,容易存在目标之间的冲突,一个目标的优化是以其他目标劣化为代价的,所以我们要…...
odoo的优势
plus,主要是为了能尽早通过开发者审核,加入到chatgpt4 api的开发中去,接入到我们odoo aiCenter中。4的回答,明显比3.5的更聪明了。 可能是由于国内的特殊情况吧,我们的chatgpt模块很受欢迎,我也被问了不少…...
Spring Boot(Vue3+ElementPlus+Axios+MyBatisPlus+Spring Boot 前后端分离)【三】
😀前言 本篇博文是关于Spring Boot(Vue3ElementPlusAxiosMyBatisPlusSpring Boot 前后端分离)【三】的分享,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我…...
Kali 软件管理
kali 更新 1. 查看发行版本 ┌──(root㉿kali)-[~] └─# lsb_release -a No LSB modules are available. Distributor ID: Kali Description: Kali GNU/Linux Rolling Release: 2023.2 Codename: kali-rolling2. 查看内核版本 ┌──(root㉿kali)-[~] └─…...
加油站【贪心算法】
加油站 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和…...
java八股文面试[多线程]——死锁、活锁、饥饿
DCL双重锁:TODO 如何预防死锁: 如何查看线程死锁: 知识来源: 【2023年面试】描述一下线程安全活跃态问题,以及竞态条件_哔哩哔哩_bilibili 【2023年面试】如何预防死锁_哔哩哔哩_bilibili 【并发与线程】阿里一面&…...
设计模式——装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。 装饰器模式通过将对象包装在装饰器类中,以便动态…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 ,你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据!该数据集源自2025年4月发表于《地理学报》的论文成果…...
Mysql故障排插与环境优化
前置知识点 最上层是一些客户端和连接服务,包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念,为通过安全认证接入的客户端提供线程。同样在该层上可…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...
高保真组件库:开关
一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...
