前言
上一篇文章地址:
pywinauto:Windows桌面应用自动化测试(八)_pywinauto.findwindows.elementambiguouserror: there-CSDN博客
下一篇文章地址:
pywinauto:Windows桌面应用自动化测试(十)-CSDN博客
一、实战常用方法
1、控件数量
上一节我们知道了descendants的用法,那么如何使用该方法获取控件对象的数量呢?可以这样:
len(self.dialog.descendants(class_name="QLineEdit", control_type="Edit"))
获取到过后就可以动态知道当前元素的数量,接着在不同数量情况下去定位该元素,类似这样:
def set_trigger_interval(self, num):
'''设置曝光时间'''
element_num = len(self.dialog.descendants(class_name="QLineEdit", control_type="Edit"))
if element_num == 2:
self.dialog.child_window(class_name="QLineEdit", control_type="Edit", found_index=0).set_text(num)
elif element_num == 4:
self.dialog.child_window(class_name="QLineEdit", control_type="Edit", found_index=2).set_text(num)
send_keys('{ENTER}')
2、一个奇怪现象
对于浏览系统文件的操作,如下图,会存在一个小问题,那就是输入盘符目录+文件夹会导致pywinauto连接的窗口丢失,必须三级目录以上才能正常工作。排查发现输入盘符目录+文件夹会出现两个一样名称的窗口,于是报错。
pywinauto.findwindows.ElementAmbiguousError: There are 2 elements that match the criteria {'title': 'SMoreVision', 'top_level_only': False, 'parent': <uia_element_info.UIAElementInfo - 'Smore Vision', MainForm, 2435420>, 'backend': 'uia'}

def choise_folder(self, folder_way):
'''浏览:输入保存路径, 不能是盘符目录+文件目录,必须三级目录以上'''
browse_win = self.study_win.child_window(title='选择保存路径')
# 分割路径
directory, folder = os.path.split(folder_way)
print("Directory:", directory)
print("folder:", folder)
way_element = browse_win.child_window(title_re=".*地址.*", found_index=0)
rectangle = self.element_num_of_copies(way_element, 5 / 6)
click(coords=rectangle)
browse_win.child_window(title_re=".*地址.*", class_name="Edit", found_index=0).type_keys("{BACKSPACE}")
browse_win.child_window(title_re=".*地址.*", class_name="Edit", found_index=0).set_text(directory)
send_keys("{ENTER}")
browse_win.child_window(title="文件夹:", class_name="Edit").set_text(folder)
def cancel_choise_folder(self):
'''浏览:取消保存路径'''
self.study_win.child_window(title="取消", class_name="Button").click_input()
def confirm_choise_folder(self):
'''浏览:确认保存路径'''
self.study_win.child_window(title="选择文件夹").click_input()
3、如何处理同名窗口
有这样一种场景,点击一个控件后,会出现一个窗口,再点击一个控件后,又出现一个弹窗。此时弹窗名和窗口名同名,就会出现“2”这种情况,那么该如何操作这两个窗口呢?
前面我们说过found_index这个参数,以及应用每次都能轮训到最新界面的元素,也就是说只要found_index为0,他至少能找到一个窗口,窗口中的元素也会实时更新,设计如下:
def get_study_win(self):
'''获取 study_win窗口,需要在操作study_win前调用一次'''
title_list = ['SMoreVision', 'Form']
for i in range(2):
try:
# 获取窗口
self.study_win = self.dialog.child_window(title=title_list[i], found_index=0) # Form
# self.print_window_info(self.study_win)
break
except:
print(f"获取窗口,第{i+1}次失败!")
def add_trigger_samples(self):
'''添加触发样本'''
element_num = len(self.study_win.descendants(class_name="QPushButton")) # 窗口中的元素数量
print(element_num)
self.study_win.child_window(class_name="QPushButton", found_index=element_num - 6).click_input() # 当弹窗未出现时,窗口的found_index为0,此时self.study_win为窗口
time.sleep(5)
self.study_win.child_window(class_name="QPushButton", found_index=1).click_input() # 当弹窗未出现时,窗口的found_index为1,弹窗为0,,此时self.study_win为弹窗
4、发送组合键的方式
如果我想发送组合键Ctrl + A 可以"^a"。在 pywinauto 的 type_keys 方法中,可以使用特殊符号来发送不同的组合键。以下是 pywinauto 支持的主要组合键符号及其对应按键:
^:Ctrl键%:Alt键+:Shift键
5、窗口大小获取
可以通过get_show_state方法获取状态,示例如下:
def get_win_state(self):
'''获取 窗口状态 1为最大化 0为缩小 '''
return self.dialog.get_show_state()
官方源码:
def get_show_state(self):
"""Get the show state and Maximized/minimzed/restored state
Returns values as following
window_visual_state_normal = 0
window_visual_state_maximized = 1
window_visual_state_minimized = 2
"""
iface = self.iface_window
ret = iface.CurrentWindowVisualState
return ret
6、右键任务栏应用的窗口获取不到
如下图,很明显该窗口是二级窗口,需要连接应用后再连接窗口,问题在于应用都连接不了。这种情况可以采用不连接应用,直接连接窗口进行操作,方法如下:
time.sleep(5) # 需要增加适当延时等待窗口出现
from pywinauto import Desktop
app = Desktop("uia")
win = app.window(title="******* 的跳转列表")

7、AI识别:模板匹配
之前文章我们有讲到有些元素找不到,这种情况没办法定位元素,因此,我们可以采用AI识别的方式,加上pywinauto混合使用,如下识别效果:

代码设计如下:
封装ai识别到AiRecognize类中
def imread_unicode(self, path, flags=cv2.IMREAD_COLOR):
'''# 中文路径图片读取函数'''
# 使用 np.fromfile 读取文件并转换为 numpy 数组,再用 cv2.imdecode 解码
return cv2.imdecode(np.fromfile(path, dtype=np.uint8), flags)
def match_image(self, image, template_image_path):
'''模板匹配图标控件
image: 实际图片,可以为本地图片路径,也可以为Image对象,如通过pywinauto的 self.dialog.capture_as_image() 获取
template_image_path: 为模板图像路径,即期望找到的图案
:return match_coords : 返回模板所在实际图片的坐标系
示例用法:
match_image(r"../data/images/示例实际图片.jpg", r"../data/images/工具列表-编辑图标.jpg")
'''
# 处理实际图像
if isinstance(image, str):
image_path = image # 实际图像
img_rgb = self.imread_unicode(image_path, cv2.IMREAD_UNCHANGED) # 读取 RGB 图像,转成numpy数组
else:
img_rgb = np.array(image)
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) # 转为灰度图
# 处理模板图像
template = self.imread_unicode(template_image_path, cv2.IMREAD_GRAYSCALE) # 读取模板图像
w, h = template.shape[::-1] # 获取模板的宽和高
# 执行模板匹配
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
# 获取匹配到的图标坐标系(左上角和右下角坐标)
match_coords = [] # 用于存储匹配到的坐标
for pt in zip(*loc[::-1]):
top_left = pt # 左上角坐标
bottom_right = (pt[0] + w, pt[1] + h) # 右下角坐标
match_coords.append((top_left, bottom_right)) # 将坐标添加到列表中
# 可视化:在匹配到的位置画矩形
cv2.rectangle(img_rgb, top_left, bottom_right, (0, 0, 255), 2)
# 保存带有匹配结果的图像
output_path = r'../data/images/匹配结果.jpg'
cv2.imencode('.jpg', img_rgb)[1].tofile(output_path)
print('找到匹配图案:', match_coords)
# 检查当前坐标是否与已有的匹配点接近
filtered_coords = []
filtered_threshold = 5
for coord in match_coords:
is_similar = False
for f_coord in filtered_coords:
# 检查当前坐标是否与已有的匹配点接近
if abs(coord[0][0] - f_coord[0][0]) < filtered_threshold and abs(coord[0][1] - f_coord[0][1]) < filtered_threshold:
is_similar = True
break
if not is_similar:
filtered_coords.append(coord)
print('过滤匹配图案:', filtered_coords)
# 返回过滤后匹配到的坐标列表
return filtered_coords
使用ai识别pywinauto元素对象
def element_num_of_copies_from_ai(self, element, template_image_path, res_x=0, fraction=0.5):
'''通用方法:通过AI识别图标的方式获取其坐标
element: pywinauto的元素对象
template_image_path: AiRecognize.match_image方法需要的对象
res_x: 选择匹配结果的第x个结果,默认第一个
fraction: 选择图标元素中间位置
'''
# 处理实际图片
rectangle = element.rectangle()
# 元素的左上角和右下角坐标
L, T, R, B = rectangle.left, rectangle.top, rectangle.right, rectangle.bottom
print('实际图片绝对坐标:', L, T, R, B)
image = element.capture_as_image() # 获取实际图片
# 处理模板图在实际图片的位置
match_list = AiRecognize().match_image(image, template_image_path)
# 图案的相对坐标 (相对于元素)
tL, tT, tR, tB = match_list[res_x][0] + match_list[res_x][1] # 获取图标的左上角和右下角坐标
t_width = tR - tL
t_height = tB - tT
tx = int(tL + t_width * fraction)
ty = int(tT + t_height * fraction)
print('模板图案中心点的相对坐标:', tx, ty)
print('模板图案中心点的绝对坐标:', L + tx, T + ty)
# click(coords=(L + tx, T + ty))
return (L + tx, T + ty)
8、AI识别:OCR
同理,之前我们说到的ocr识别,现在将ocr识别封装到AiRecognize 中:
def ocr_reader(self, image):
'''读取图片中的文字
可以为本地图片路径,也可以为Image对象,如通过pywinauto的 self.dialog.capture_as_image() 获取
'''
# 处理实际图像
if isinstance(image, str):
image_path = image # 实际图像
image_array = self.imread_unicode(image_path, cv2.IMREAD_UNCHANGED) # 读取 RGB 图像,转成numpy数组
else:
image_array = np.array(image)
ocr = PaddleOCR(use_angle_cls=True, lang="ch")
result = ocr.ocr(image_array, cls=True)
res = []
print("图片中的文本内容:")
for block in result:
for line in block:
text = line[1][0] # 提取文本内容
print(text)
res.append(line[1][0])
return res
5280

被折叠的 条评论
为什么被折叠?



