当前位置:   article > 正文

使用pywinauto进行UI自动化_pywinauto之windows ui自动化

pywinauto之windows ui自动化

1.pywinauto

操作控件需要以下几个步骤:

第一步 实例化要操作的进程:得到的app是Application对象。

第二步 选择窗口 :app.window('一个或多个筛选条件') 得到的窗口是WindowSpecification对象

第三步:基于WindowSpecification对象使用其方法再往下查找,定位到具体的控件

第四步:使用控件的方法属性执行我们需要的操作。

WindowSpecification源码中有一些自带的方法可以直接使用,也有注释说到:

WindowSpecification 说明

该对象中__getattribute__和__getitem__两个魔术方法,隐式地记录一些私有方法

我们可以继续往下一层一层的查找,下面一层一层的控件其实是各种各样的wrapper对象,wrapper有很多种是一系列对象,对象源码都在pywinauto源码的controls目录中

1.1链接/打开应用

在我们安装好Pywinauto之后,首先要确定哪种可访问性技术(pywinauto的backend)可以用于我们的应用程序,在windows上受支持的辅助功能技术有两种:

  • Win32 API (backend="win32") 默认backend

  • MS UI Automation (backend="uia")

可以借助于GUI对象检查工具来确定程序到底适用哪种backend,常用的检查工具有Inspect.exe,Spy++ 等,如果Spy++可定位到的元素信息更多,则使用win32;如果inspect.exe定位到的元素更多,则使用uia

 
  1. from pywinauto import application # 方式一:创建应用程序时可以,指定应用程序的合适的backend,start方法中指定启动的应用程序
  2. app = application.Application(backend='uia').start('notepad.exe')
  1. from pywinauto import application
  2. # 方式二:查看要打开的程序进程号,通过process指定进程号连接
  3. app = application.Application().connect(process=19035)

1.2窗口选择

  1. # 方式一 :不适用于窗口名为中文的
  2. wind_1 = app.窗口名
  3. # 方式二 :窗口名可以为中文
  4. wind_2 = app["窗口名"]
  5. # 方式三 :窗口名可以为中文
  6. app.window(class_name = ‘Notepad’) # 关键字 title, title_re,class_name_re等
  7. # 案例使用:选择上面打开的计算器程序窗口
  8. wind_calc = app['无标题 - 记事本']

以下为只支持窗口模式的控件
  1. dlg.close() # 关闭界面
  2. dlg.minimize() # 最小化界面
  3. dlg.maximize() # 最大化界面
  4. dlg.restore() # 将窗口恢复为正常大小,比如最小化的让他正常显示在桌面
  5. dlg.get_show_state() # 正常0,最大化1,最小化2
  6. dlg.exists(timeout=None, retry_interval=None) # 判断是否存在
  7. #timeout:等待时间,一般默认5s
  8. #retry_interval:timeout内重试时间
  9. dlg.wait(wait_for, timeout=None, retry_interval=None) # 等待窗口处于特定状态
  10. dlg.wait_not(wait_for_not, timeout=None, retry_interval=None) # 等待窗口不处于特定状态,即等待消失
  11. # wait_for/wait_for_not:
  12. # * 'exists' means that the window is a valid handle
  13. # * 'visible' means that the window is not hidden
  14. # * 'enabled' means that the window is not disabled
  15. # * 'ready' means that the window is visible and enabled
  16. # * 'active' means that the window is active
  17. # timeout:等待多久
  18. # retry_interval:timeout内重试时间
  19. # eg: dlg.wait('ready')

1.3控件定位

元素定位工具
  • Inspect

  • ViewWizard

  • Spy++

层级查找控件的方法
  1. window(**kwargs) # 用于窗口的查找
  2. child_window(**kwargs) # 可以不管层级的找后代中某个符合条件的元素,最常用
  3. parent() # 返回此元素的父元素,没有参数
  4. children(**kwargs) # 返回符合条件的子元素列表,支持索引,是BaseWrapper对象(或子类)
  5. iter_children(**kwargs) # 返回子元素的迭代器,是BaseWrapper对象(或子类)
  6. descendants(**kwargs) # 返回符合条件的所有后代元素列表,是BaseWrapper对象(或子类)
  7. iter_children(**kwargs) # 符合条件后代元素迭代器,是BaseWrapper对象(或子类)
  1. 可以通过print_control_identifiers()这个方法,来获取这个窗口下的直接子控件
  2. app["窗口名"]["控件名"] # 基于title定位
  3. app.window(class_name = ’Notepad’).window(class_name = ‘#32770’) # 层级定位
  4. app.window(class_name = ‘Notepad’).child_window(class_name = ‘#32770’)
  5. app_window.children()[1].children()[0].children()[0] .children()[2] #定位用户名输入框控件(序号从0开始查)
  6. app.window("窗口名").window(class_name='ClassName', found_index=0)# 通过className定位,选择符合条件的第1个控件
 
可用查找条件关键字
  1. Possible values are:
  2. * **class_name** Elements with this window class
  3. * **class_name_re** Elements whose class matches this regular expression
  4. * **parent** Elements that are children of this
  5. * **process** Elements running in this process
  6. * **title** Elements with this text
  7. * **title_re** Elements whose text matches this regular expression
  8. * **top_level_only** Top level elements only (default=True)
  9. * **visible_only** Visible elements only (default=True)
  10. * **enabled_only** Enabled elements only (default=False)
  11. * **best_match** Elements with a title similar to this
  12. * **handle** The handle of the element to return
  13. * **ctrl_index** The index of the child element to return
  14. * **found_index** The index of the filtered out child element to return
  15. * **predicate_func** A user provided hook for a custom element validation
  16. * **active_only** Active elements only (default=False)
  17. * **control_id** Elements with this control id
  18. * **control_type** Elements with this control type (string; for UIAutomation elements)
  19. * **auto_id** Elements with this automation id (for UIAutomation elements)
  20. * **framework_id** Elements with this framework id (for UIAutomation elements)
  21. * **backend** Back-end name to use while searching (default=None means current active backend)

例如

  1. wind_1 = app["窗口名"]
  2. # 按name值定位
  3. wind_1.window(title="name").click()
  4. # 通过calssName定位,单击符合此className的第一个控件
  5. wind_1.window(class_name='className', found_index=0).click()
  6. # 定位符合条件的控件的父元素,并左键双击
  7. wind_1.window(title="name").parent().double_click_input()
  8. #多个关键字组合定位
  9. wind_1.child_window(title_re='name', class_name="Edit").click_input()

控件的常用属性
  1. ctrl.children_texts() # 所有子控件的文字列表,对应inspect中Name字段
  2. ctrl.window_text() # 控件的标题文字,对应inspect中Name字段
  3. ctrl.class_name() # 控件的类名,对应inspect中ClassName字段,有些控件没有类名
  4. ctrl.element_info.control_type # 控件类型,inspect界面LocalizedControlType字段的英文名
  5. ctrl.is_child(parent) # ctrl是否是parent的子控件
  6. ctrl.legacy_properties().get('Value') # 可以获取inspect界面LegacyIAccessible开头的一系列字段,在源码uiawraper.py中找到了这个方法,非常有用

按坐标定位控件

绝对坐标/相对坐标

1.4常用鼠标操作

  1. # ctrl即定位到的控件
  2. ctrl.click()# 左键单击
  3. ctrl.click_input() # 左键单击
  4. ctrl.right_click_input() # 鼠标单击
  5. # 键盘输入,底层还是调用keyboard.send_keys
  6. ctrl.type_keys(keys, pause = None, with_spaces = False,)
  7. # keys:要输入的文字内容
  8. # pause:每输入一个字符后等待时间,默认0.01就行
  9. # with_spaces:是否保留keys中的所有空格,默认去除0
  10. ctrl.double_click_input(button ="left", coords = (None, None)) # 左键双击
  11. ctrl.press_mouse_input(coords = (None, None)) # 指定坐标按下左键,不传坐标默认左上角
  12. ctrl.release_mouse_input(coords = (None, None)) # 指定坐标释放左键,不传坐标默认左上角
  13. ctrl.move_mouse_input(coords=(0, 0)) # 将鼠标移动到指定坐标,不传坐标默认左上角
  14. ctrl.drag_mouse_input(dst=(0, 0)) # 将ctrl拖动到dst,是press-move-release操作集合

pywinauto自带的鼠标操作有些时候并不能完全满足要求,可以调用mouse的方法

导入

from pywinauto import mouse

常见操作:

  1. # 移动鼠标
  2. mouse.move(coords=(x, y))
  3. # 指定位置,鼠标左击
  4. mouse.click(button='left', coords=(40, 40))
  5. # 鼠标双击
  6. mouse.double_click(button='left', coords=(140, 40))
  7. # 将属性移动到(140,40)坐标处按下
  8. mouse.press(button='left', coords=(140, 40))
  9. # 将鼠标移动到(300,40)坐标处释放,
  10. mouse.release(button='left', coords=(300, 40))
  11. # 右键单击指定坐标
  12. mouse.right_click(coords=(400, 400))
  13. # 鼠标中键单击指定坐标(很少用的到)
  14. mouse.wheel_click(coords=(400, 400))
  15. # 滚动鼠标 wheel_dist指定鼠标滚轮滑动,正数往上,负数往下。
  16. mouse.scroll(coords=(1200,300),wheel_dist=-3)

示例:

  1. # 以控件中心为起点,滚动
  2. def mouse_scroll(control, distance):
  3. rect = control.rectangle()
  4. cx = int((rect.left+rect.right)/2)
  5. cy = int((rect.top + rect.bottom)/2)
  6. mouse.scroll(coords=(cx, cy), wheel_dist=distance)
  7. mouse_scroll(control=wind_1.window(title="name", distance=-5)

1.5常用键盘操作

和控件自己的type_keys方法效果一样,但是更快,那个是从前到后啪啪啪的输入,这个是一下就出来了那种

在发送文件和图片的时候可以使用键盘模块,复制粘贴,比啪啪啪输入路径再发送速度快多了

并且该模块可以适配很多表情等特殊符号

  1. import keyboard
  2. import io
  3. for line in io.StringIO(msg):
  4. keyboard.write(line.strip()) #
  5. keyboard.send('ctrl+enter')
  6. keyboard.write(chat_name)
  7. keyboard.send('enter')
  8. keyboard.send('ctrl+v')

想要通过pywinauto模拟操作键盘,需要重新导入库

from pywinauto.keyboard import send_keys 

源码

  1. def send_keys(keys,
  2. pause=0.05,
  3. with_spaces=False,
  4. with_tabs=False,
  5. with_newlines=False,
  6. turn_off_numlock=True,
  7. vk_packet=True):
  8. """Parse the keys and type them"""
  9. keys = parse_keys(
  10. keys, with_spaces, with_tabs, with_newlines,
  11. vk_packet=vk_packet)
  12. for k in keys:
  13. k.run()
  14. time.sleep(pause)
  15. SendKeys = deprecated(send_keys)

示例

  1. from pywinauto.keyboard import send_keys
  2. from pywinauto import Application
  3. import time
  4. app = Application().start('notepad.exe')
  5. # 通过支持的控件输入内容
  6. app['无标题 - 记事本'].Edit.type_keys('测试')
  7. time.sleep(2)
  8. # 回车
  9. send_keys('{ENTER}')
  10. # F5
  11. send_keys('{VK_F5}')
  12. # ctrl+a
  13. send_keys('^a')
  14. # 也可以把多个键盘输入写在一起
  15. send_keys('{ENTER}'
  16. '{VK_F5}'
  17. '^a'

以下是一些特殊键盘
按键名称对应符号
SHIFT+
CTRL^
ALT%
SPACE{SPACE}
BACKSPACE{BACKSPACE} {BS} or{BKSP}
BREAK{BREAK}
CAPS LOCK{CAPSLOCK}
DEL or DELETE{DELETE} or {DEL}
DOWN ARROW{DOWN}
END{END}
ENTER{ENTER} or ~
ESC{ESC}
HELP{HELP}
HOME{HOME}
INS or INSERT{INSERT} or {INS}
LEFT ARROW{LEFT}
NUM LOCK{NUMLOCK}
PAGE DOWN{PGDN}
PAGE UP{PGUP}
PRINT SCREEN{PRTSC}
RIGHT ARROW{RIGHT}
SCROLL LOCK{SCROLLLOCK}
TAB{TAB}
UP ARROW{UP}
+{ADD}
-{SUBTRACT}
*{MULTIPLY}
/{DIVIDE}

1.6等待

隐式等待
  1. wait(wait_for, timeout = None, retry_interval = None) # visible,ready: visible + enable
  2. wait_not(wait_for_not,timeout = None,retry_interval = None)

1、等待法。

先预估一个转换所需的最长时间,保证此时操作已经完成,然后让程序等待这么长时间后再进行下一步。

  1. import time
  2. ...
  3. time.sleep(100)
  4. ctrl.click()
2、 查询法。

写个循环,一直查询是否存在目标控件,若存在,则退出循环。

  1. ...
  2. while(True):
  3. if app.window(title=r'name',class_name='ClassName').exists():
  4. break
  5. wind_1.click()

注意,在查询的时候,最好不要用app[‘name’].exists()。这个匹配不精准,如下图中的最后一个句柄。这个句柄在开启程序后就一直存在,且由于我们要找的对话框title一样,所以我们在查找的时候需要加上class_name。

3、查询等待法。

查询有个缺点就是如果一直没出现,就会一直等待。所以我们最好设置一个等待时间限。

使用模块中自带的wait函数就可以实现该功能了,了解更多Waiting for Long Operations。

  1. wind_1.Wait('enabled',timeout=300)
  2. wind_1.click()

1.7菜单栏

app['窗口名或类名'].menu_select(Edit -> Replace)

示例

  1. from pywinauto import application
  2. app = application.Application(backend="win32") # 默认为win32,设置成‘uia’出错
  3. app.start(r"notepad.exe")
  4. app['Notepad'].wait('ready') # 'Notepad'为类名,用标题名“无标题 - 记事本”也可以,app.UntitledNotepad 也可以
  5. app['Notepad'].menu_select("文件->页面设置...") # 不用加“.click()”,已经点击,“...”不能少
  6. app['页面设置']['ComboBox1'].select(4) # ComboBox1是第一个,ComboBox2是第二个,select从0开始
  7. app['页面设置']['ComboBox1'].select("A5") #直接选择里面的项
  8. app['页面设置']['取消'].click() # 按钮点击
  9. app['页面设置']['Edit3'].set_edit_text("68") # Edit 置文本
  10. app['页面设置']['Edit2'].type_keys("86") # Edit 输入按键(输入的插入到前面)

1.8勾选

  1. check() # 勾选checkbox
  2. uncheck() # 不勾选checkbox
  3. is_checked() # 勾选返回true,未勾选返回false,不定返回None
  4. get_check_state() # 返回checkbox的勾选状态(0没勾选,1勾选,2不定)
  5. get_toggle_state() # 返回checkbox的勾选状态(0没勾选,1勾选),可用

1.9对控件截图保存

需要先安装PIL模块
  1. # 对窗口/控件进行截图并保存
  2. # PIL is required for capture_as_image
  3. wind_1.capture_as_image().save(file_path)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/706509
推荐阅读
相关标签
  

闽ICP备14008679号