【问题标题】:pywinauto find_elements() returns ElementNotFoundErrorpywinauto find_elements() 返回 ElementNotFoundError
【发布时间】:2017-10-07 20:44:38
【问题描述】:

我正在尝试使用 pywinauto 和 Python 3.6 自动化一个简单的应用程序。该应用程序有一个像这样的 Windows“打开”对话框,我想单击“取消”按钮:

我使用 SWAPY 来获取按钮的 class_name 和 control_id 属性。

现在的问题是,当我使用这些参数调用 find_element() 方法时,它会引发 ElementNotFoundError。这是我的代码:

cancel_button = pywinauto.findwindows.find_element(class_name="button", control_id=2)

我尝试过(class_name="button", control_id="2")(class_name="Button", control_id=2),但它们都给出了相同的错误。我尝试在此对话框中找到的任何其他元素都会出现同样的问题。

那么我该如何使用从 SWAPY 读取的属性呢?我没有发现官方的 pywinauto 文档很有用。很多事情都没有解释清楚。

编辑:我决定不使用 find_elements 方法,而是使用 find_windows() 来获取打开对话框的句柄。

w_open_handle = pywinauto.findwindows.find_windows(title=u'Open', class_name='#32770')[0]

然后我使用这个句柄得到一个 WindowSpecification 对象:

w_open = app.window_(handle=w_open_handle)

然后我打电话:

w_open['Cancel'].click()

这行得通。现在我想在“文件名:”编辑框中输入一个文件名,然后单击打开按钮打开该文件。所以我这样做:

w_open['File name:'].type_keys("abc.txt")

这行得通。我使用 print_control_identifiers() 打印出控件标识符,并获得了“打开”按钮的名称。所以使用 draw_outline() 我在它外面画了一个边界,它显示了正确的按钮。

w_open['SplitButton6'].draw_outline()

但是在 'SplitButton6' 上调用 .click() 方法会引发 WindowSpecification 类没有 'click' 方法 错误。知道是什么原因造成的吗?该错误似乎具有误导性,因为 WindowSpec 类确实具有 .click 方法。

【问题讨论】:

  • Getting Started Guide 应该解释很多。
  • 正如我所说,官方指南并没有解释这些事情。
  • 但它解释了您应该使用更高级别的东西来获得更好的生产力。有关详细信息,请参阅我的答案。
  • 文档中描述了top_level_only 的证明链接:pywinauto.readthedocs.io/en/latest/code/…

标签: python pywinauto


【解决方案1】:

正确答案是您错过了top_level_only=False(默认为True,因为更高级别的API 至少调用了两次)。那么您可能有 2 个符合此标准的控件(可能来自不同的应用程序)。 find_element 是一个低级函数。我不建议直接使用它(代码太长,在更高级别的 API 上考虑了很多陷阱)。

>>> pywinauto.findwindows.find_element(class_name="Button", control_id=2, top_level_only=False)
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "...\pywinauto\findwindows.py", line 98, in find_element
    raise exception
ElementAmbiguousError: There are 2 elements that match the criteria {'class_name': 'Button', 'control_id': 2, 'top_level_only': False}

>>> pywinauto.findwindows.find_element(class_name="Button", title='Cancel', top_level_only=False)
<win32_element_info.HwndElementInfo - 'Cancel', Button, 395554>

使用更高级别的 API(Application 对象和指南中描述的WindowSpecifications),您不必每次都将进程 ID、后端名称和其他内容传递给 find_element

附:在我看来,SWAPY 可能会得到显着改进,但去年没有得到维护。我希望将来用更小的代码库和 MS UI 自动化支持来重写它。但目前全自动脚本生成器优先级更高。


编辑:

这个按钮w_open['SplitButton6'].draw_outline() 可以被检测为一般的HwndWrapper 对象而不是ButtonWrapper。您可以使用以下方法进行检查:

w_open['SplitButton6'].wrapper_object()

这正是《入门指南》中所写的内容(您说您已阅读)。

幸运的是,您可以使用方法.click_input() 进行任何控制:

w_open['SplitButton6'].click_input()

我可以说更多:WindowSpecification 没有click 方法。这是一个动态实例化的ButtonWrapper 方法。例如,这些语句的工作方式相同(但 Python 可以隐藏 .wrapper_object() 调用):

w_open['SplitButton6'].wrapper_object().click_input()
w_open['SplitButton6'].click_input()

这又在Getting Started Guide 中进行了描述。请阅读整个指南。你会发现很多有用的高级东西。如果还有什么不清楚的地方,我可以为一些极端情况提供建议。

【讨论】:

  • 我尝试设置 top_level_only=False 仍然得到 ElementNotFoundError!
  • 你运行哪个语句?
  • pywinauto.findwindows.find_element(class_name="Button", control_id=2, top_level_only=False)
  • 我决定不使用 find_element 方法。相反,我使用 find_windows 来获取打开对话框的句柄。
  • find_windows 只是 find_elements 的包装,我们为向后兼容而制作。为什么不只是app.OpenDialog.CancelButton.click()?还是app.OpenDialog.child_window(class_name='Button', control_id=2).click()?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
  • 2017-03-20
  • 2016-06-15
  • 2023-02-01
  • 1970-01-01
相关资源
最近更新 更多