前言
上一篇文章地址:
pywinauto:Windows桌面应用自动化测试(四)_pywinauto以管理员身身份start-CSDN博客
下一篇文章地址:
pywinauto:Windows桌面应用自动化测试(六)-CSDN博客
一、实战常用方法
1、元素的文本获取
window_text()
方法可以获取窗口或控件的文本内容。
texts()
方法返回控件的所有文本内容,以列表形式表示。适用于多文本控件,如组合框或列表框。
.wrapper_object().window_text()方法,需要将控件包装成特定类型的对象,以便获取更多属性和方法。
.legacy_properties()['Value']方法,对于某些特定的 UIA 控件,可以通过 legacy_properties
获取其属性值。
.element_info.name
或 .element_info.rich_text
方法,通过 element_info
可以获取更多控件的信息。
.get_value()
方法,对于一些特定类型的控件,可以使用 get_value()
方法获取其值。
from pywinauto import Application
# 连接到应用程序
app = Application(backend='uia').start('notepad.exe')
dlg = app['Untitled - Notepad']
# 获取编辑控件
edit = dlg.child_window(class_name='Edit')
# 使用不同的方法获取文本内容
# 方法1: window_text()
text = edit.window_text()
print("window_text():", text)
# 方法2: texts()
texts = edit.texts()
print("texts():", texts)
# 方法3: wrapper_object().window_text()
edit_wrapped = edit.wrapper_object()
text_wrapped = edit_wrapped.window_text()
print("wrapper_object().window_text():", text_wrapped)
# 方法4: legacy_properties()['Value']
value = edit.legacy_properties().get('Value', '')
print("legacy_properties()['Value']:", value)
# 方法5: element_info.name
name = edit.element_info.name
print("element_info.name:", name)
# 方法6: get_value()
value_get = edit.get_value()
print("get_value():", value_get)
2、元素点击不到
(1)点击区域不在中心点
如下图,这是inspect给出的元素范围,这种情况是因为点击区域不在中心点,所以点击之后没反应。因此,通过以下方法是点击不到的。
def enter_website(self):
about_app = Application(backend='uia').connect(title="Form") # Form
about_win = about_app.window(title='Form')
about_win.child_window(title='进入官网').click_input()
我们需要使用上一节说到的点击方式进行点击:
def enter_website(self):
def _element_num_of_copies(element, fraction):
print(element.rectangle(), type(element.rectangle()))
rectangle = element.rectangle()
L, T, R, B = rectangle.left, rectangle.top, rectangle.right, rectangle.bottom
width = R - L
height = B - T
x = int(L + width * fraction)
y_center = int(T + height / 2)
return (x, y_center)
about_app = Application(backend='uia').connect(title="Form") # Form
about_win = about_app.window(title='Form')
# about_win.child_window(title='进入官网').click_input()
element = about_win.child_window(title='进入官网')
rectangle = _element_num_of_copies(element, 1/5)
click(coords=rectangle)
(2)元素没来得及显示
有些元素需要点击按钮才会显示出来,这种情况下没那么快找到元素,此时需要在显示前加适当延时。
def open_about(self):
time.sleep(3)
help_app = Application(backend='uia').connect(title='Form')
help_win = help_app.window(title='Form')
help_win.child_window(title="关于").click_input()
或者比较优雅的方式,如下操作:
def open_about(self):
help_app = Application(backend='uia').connect(title='Form')
help_win = help_app.window(title='Form')
_about = help_win.child_window(title="关于")
_about.wait('visible', timeout=10) # 等待显示
_about.click_input()
一般来讲,如果窗口存在,则元素一般能显示,因此还可以这样改:
使用 lambda
表达式返回 help_win
对象本身来解决等待窗口出现的问题,但关键在于需要一个检查函数,能够在 wait_until_passes
方法中动态地检查条件,并在满足条件时返回所需的对象。其中 .exists()是用于判断元素是否存在的方法。
def open_about(self):
help_app = Application(backend='uia').connect(title='Form')
# help_win = help_app.window(title='Form')
# 使用 wait_until_passes 等待关于窗口出现
from pywinauto import timings
timings.wait_until_passes(10, 0.5, lambda :help_app.window(title='Form').exists(timeout=1))
_about = help_win.child_window(title="关于")
_about.click_input()
3、窗口定位不到
如上节所诉,得通过inspect来查看定位窗口的层级,如果是二级窗口,得采用window的方法,如果是三级及其往后的层级窗口,可以使用child_window来获取。
这个需要特别注意,因为如下窗口都可能是二级窗口。
4、当inspect不好用时
以下2种方式获取的元素信息是不一致的,而且pywinauto
目前并不支持 msaa
作为后台,但可以使用win32或uia后台技术尝试访问。一般使用uia也可以访问msaa后台,元素获取比win32多一些。但是,当 msaa
作为后台时,通过inspect的uia获取元素信息是不完整的,此时需要使用 .print_control_identifiers() 方法打印, 对于pywinauto来说,print_control_identifiers()是最准的。
app = Application(backend='uia').connect(title_re=".*666.*")
window = app.window(title_re=".*666.*")
window.print_control_identifiers()
5、元素模糊匹配
元素其实支持模糊匹配,可以通过title_re实现,示例如下:
app = Application(backend='uia').connect(title_re=".*666.*")
window = app.window(title_re=".*666.*")
6、为什么wait_until_passes方法需要这个函数 exists
def exists(self, timeout=None, retry_interval=None):
"""
Check if the window exists, return True if the control exists
:param timeout: the maximum amount of time to wait for the
control to exists. Defaults to ``Timings.exists_timeout``
:param retry_interval: The control is checked for existance this number
of seconds. ``Defaults to Timings.exists_retry``
"""
# set the current timings -couldn't set as defaults as they are
# evaluated at import time - and timings may be changed at any time
if timeout is None:
timeout = Timings.exists_timeout
if retry_interval is None:
retry_interval = Timings.exists_retry
# modify the criteria as exists should look for all
# windows - including not visible and disabled
exists_criteria = self.criteria[:]
for criterion in exists_criteria:
criterion['enabled_only'] = False
criterion['visible_only'] = False
try:
self.__resolve_control(exists_criteria, timeout, retry_interval)
return True
except (findwindows.ElementNotFoundError,
findbestmatch.MatchError,
controls.InvalidWindowHandle,
controls.InvalidElement):
return False
exists
方法用于检查控件是否存在,它的实现细节涉及到如何在一定时间内不断尝试查找控件,并返回是否找到控件的结果。而wait_until_passes
方法实际上是用于重复尝试某个操作,直到操作成功或超时。在 pywinauto
中,wait_until_passes
方法用于确保操作可以在给定时间内成功完成,而 exists
方法则是在这个过程中判断控件是否存在。
(1)exists
方法的源码分析
参数设置:timeout
:指定等待控件存在的最大时间。retry_interval
:指定每次检查控件存在与否的时间间隔。
Criteria 修改:将控件的 enabled_only
和 visible_only
设置为 False
,这样可以找到所有控件,不管它们是否可见或是否启用。
查找控件:使用 self.__resolve_control
方法尝试找到控件。如果在超时时间内找不到控件,将抛出异常。处理了 findwindows.ElementNotFoundError
、findbestmatch.MatchError
、controls.InvalidWindowHandle
和 controls.InvalidElement
这几种可能的异常,捕获异常后返回 False
。
(2)wait_until_passes
方法的作用
wait_until_passes
是用于重复尝试某个操作,直到成功或超时。这个方法通常用来等待某个控件的存在或某个条件的满足。在调用 exists
方法时,通常需要通过 wait_until_passes
来确保控件在某个时间段内是可用的。例如,确保一个窗口或控件在特定时间内被成功加载。
(3)为什么 wait_until_passes
需要 exists
方法
wait_until_passes
方法通常会用 exists
方法来判断控件是否存在。如果你使用 wait_until_passes
来等待一个控件的存在,它会重复调用 exists
方法直到控件存在或超时。这样可以处理控件加载时间不确定的情况。