【测试】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)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。 装饰器模式通过将对象包装在装饰器类中,以便动态…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
