pywinauto find_elements() returns ElementNotFoundError
python, pywinauto
Solution
Correct answer is that you missed `top_level_only=False` (it's `True` by default because higher level API calls it at least twice). Then you may have 2 controls matching this criterion (maybe from different applications). `find_element` is a low level function. I wouldn't recommend its direct usage (the code is too long, there are many pitfalls that were taken into account on a higher level 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>
Using higher level API (`Application` object and `WindowSpecification`s described in the Guide) you shouldn't care about passing process id, backend name and other things to `find_element` every time.
P.S. In my mind SWAPY could be significantly improved, but it's not maintained last year. I hope to re-write it in the future with smaller code base and MS UI Automation support. But currently fully automatic script generator is a higher priority.
EDIT:
This button `w_open['SplitButton6'].draw_outline()` could be detected as general `HwndWrapper` object instead of `ButtonWrapper`. You can check it using this:
w_open['SplitButton6'].wrapper_object()
And this is what exactly written in the Getting Started Guide (which you said you've read).
Fortunately you can use method `.click_input()` for any control:
w_open['SplitButton6'].click_input()
I can say more: WindowSpecification does NOT have `click` method. It's a method of `ButtonWrapper` which is instantiated dynamically. For example these statements work the same way (but Python can hide `.wrapper_object()` call):
w_open['SplitButton6'].wrapper_object().click_input()
w_open['SplitButton6'].click_input()
And again this is all described in the Getting Started Guide. Please read the whole guide. You will find many useful high level things. I can advise for some corner cases if something is still not clear.
Problem
I'm trying to automate a simple app using pywinauto & Python 3.6. The app has a Windows "Open" dialog like this and I want to click on the "Cancel" button: I used SWAPY to get the class_name and control_id attributes for the button. Now the problem is that when I call the find_element() method with these parameters it raises an ElementNotFoundError. Here's my code: ``` cancel_button = pywinauto.findwindows.find_element(class_name="button", control_id=2) ``` I've tried `(class_name="button", control_id="2")`, `(class_name="Button", control_id=2)` but they all give the same error. The same problem occurs for any other element I try to find on this dialog. So how do I use the attributes read from SWAPY? I didn't find the official pywinauto documentation to be very useful. It doesn't explain a lot of things clearly. EDIT: I decided not to use the find_elements method and instead used find_windows() to get a handle to the Open dialog. ``` w_open_handle = pywinauto.findwindows.find_windows(title=u'Open', class_name='#32770')[0] ``` I then get a WindowSpecification object using this handle: ``` w_open = app.window_(handle=w_open_handle) ``` I then call: ``` w_open['Cancel'].click() ``` and this works. Now I want to enter a file name in the "File name:" edit box and click on Open button to open that file. So I do this: ``` w_open['File name:'].type_keys("abc.txt") ``` This works. I printed out the control identifiers using print_control_identifiers() and got the name for Open button. So using draw_outline() I draw a boundary outside it and it shows the correct button. ``` w_open['SplitButton6'].draw_outline() ``` But calling .click() method on 'SplitButton6' throws a WindowSpecification class has no 'click' method error. Any idea what's causing this? The error seems to be misleading since WindowSpec class does have a .click method.