Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

encapsulate calling FindItemByProperty #1328

Open
wants to merge 3 commits into
base: atspi
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 9 additions & 9 deletions pywinauto/controls/uia_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -933,14 +933,14 @@ def get_item(self, row):
# Try to find item using FindItemByProperty
# That way we can get access to virtualized (unloaded) items
try:
com_elem = self.iface_item_container.FindItemByProperty(0, IUIA().UIA_dll.UIA_NamePropertyId, row)
elem = self.element_info.item_container.find(propid=IUIA().UIA_dll.UIA_NamePropertyId, value=row)
# Try to load element using VirtualizedItem pattern
try:
get_elem_interface(com_elem, "VirtualizedItem").Realize()
itm = uiawrapper.UIAWrapper(UIAElementInfo(com_elem))
get_elem_interface(elem.element, "VirtualizedItem").Realize()
itm = uiawrapper.UIAWrapper(elem)
except NoPatternInterfaceError:
# Item doesn't support VirtualizedItem pattern - item is already on screen or com_elem is NULL
itm = uiawrapper.UIAWrapper(UIAElementInfo(com_elem))
itm = uiawrapper.UIAWrapper(elem)
except (NoPatternInterfaceError, ValueError):
# com_elem is NULL pointer or item doesn't support ItemContainer pattern
# Get DataGrid row
Expand All @@ -956,15 +956,15 @@ def get_item(self, row):
elif isinstance(row, six.integer_types):
# Get the item by a row index
try:
com_elem = 0
for _ in range(0, self.__resolve_row_index(row) + 1):
com_elem = self.iface_item_container.FindItemByProperty(com_elem, 0, uia_defs.vt_empty)
elem = self.element_info.item_container.find()
for _ in range(0, self.__resolve_row_index(row)):
elem = self.element_info.item_container.find(elem)
# Try to load element using VirtualizedItem pattern
try:
get_elem_interface(com_elem, "VirtualizedItem").Realize()
get_elem_interface(elem.element, "VirtualizedItem").Realize()
except NoPatternInterfaceError:
pass
itm = uiawrapper.UIAWrapper(UIAElementInfo(com_elem))
itm = uiawrapper.UIAWrapper(elem)
except (NoPatternInterfaceError, ValueError, AttributeError):
list_items = self.children(content_only=True)
itm = list_items[self.__resolve_row_index(row)]
Expand Down
9 changes: 3 additions & 6 deletions pywinauto/controls/uiawrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -858,12 +858,9 @@
"""Get texts through the ItemContainer interface"""
texts = []
try:
com_elem = self.iface_item_container.FindItemByProperty(0, 0, uia_defs.vt_empty)
while com_elem:
itm = UIAWrapper(UIAElementInfo(com_elem))
texts.append(itm.texts())
com_elem = self.iface_item_container.FindItemByProperty(com_elem, 0, uia_defs.vt_empty)
except (uia_defs.NoPatternInterfaceError):
for elem in self.element_info.item_container:
texts.append(UIAWrapper(elem).texts())
except uia_defs.NoPatternInterfaceError:

Check notice

Code scanning / CodeQL

Empty except Note

'except' clause does nothing but pass and there is no explanatory comment.
pass
return texts

Expand Down
22 changes: 13 additions & 9 deletions pywinauto/unittests/test_uiawrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,18 +786,22 @@ def test_combobox_texts(self):
for t in combo_box.texts():
self.assertEqual((t in ref_texts), True)

# Mock a 0 pointer to COM element
combo_box.iface_item_container.FindItemByProperty = mock.Mock(return_value=0)
self.assertEqual(combo_box.texts(), ref_texts)
# Mock a empty container
patch_empty = mock.patch.object(UIAElementInfo, "item_container", [])
with patch_empty:
self.assertEqual(combo_box.texts(), ref_texts)

# Mock a combobox without "ItemContainer" pattern
combo_box.iface_item_container.FindItemByProperty = mock.Mock(side_effect=uia_defs.NoPatternInterfaceError())
self.assertEqual(combo_box.texts(), ref_texts)
error_prop = mock.PropertyMock(side_effect=uia_defs.NoPatternInterfaceError())

# Mock a combobox without "ItemContainer" pattern
patch_container_error = mock.patch.object(UIAElementInfo, "item_container", error_prop)
# Mock a combobox without "ExpandCollapse" pattern
# Expect empty texts
combo_box.iface_expand_collapse.Expand = mock.Mock(side_effect=uia_defs.NoPatternInterfaceError())
self.assertEqual(combo_box.texts(), [])
patch_expand_collapse_error = mock.patch.object(UIAWrapper, "iface_expand_collapse", error_prop)

with patch_container_error:
self.assertEqual(combo_box.texts(), ref_texts)
with patch_expand_collapse_error:
self.assertEqual(combo_box.texts(), [])

def test_combobox_select(self):
"""Test select related methods for the combo box control"""
Expand Down
44 changes: 43 additions & 1 deletion pywinauto/windows/uia_element_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@

"""Implementation of the class to deal with an UI element (based on UI Automation API)"""

from comtypes import COMError
from comtypes import COMError, POINTER
from six import integer_types, text_type, string_types
from ctypes.wintypes import tagPOINT
import warnings

from . import uia_defines as uia_defs
from .uia_defines import IUIA
from .uia_defines import get_elem_interface, NoPatternInterfaceError

Expand Down Expand Up @@ -488,6 +489,10 @@ def descendants(self, **kwargs):
else:
return self.get_descendants_with_depth(depth=depth, **kwargs)

@property
def item_container(self):
return _ItemContainer(get_elem_interface(self._element, "ItemContainer"))

@property
def visible(self):
"""Check if the element is visible"""
Expand Down Expand Up @@ -559,3 +564,40 @@ def get_active(cls):
"""Return current active element"""
ae = IUIA().get_focused_element()
return cls(ae)


class _ItemContainer(object):
"""UI item container wrapper"""
def __init__(self, iface):
self._iface = iface # IUIAutomationItemContainerPattern

def __iter__(self):
elem = self.find()
while elem.element:
yield elem
elem = self.find(elem)

def find(self, start_after=None, propid=None, value=None):
"""Return the matching element in the container

* **start_after** Specifies the element after which the search begins.
Defaults to search all elements.
* **propid** Specifies the property identifier.
Defaults to return the next item after **start_after** element.
* **value** Specifies the property value.
Defaults to return the next item after **start_after** element.

if all parameters are not specified, returns the first item.
if the **propid** is specified, the **value** must be specified.
"""
if start_after is None:
# create a null com pointer
com_elem = POINTER(IUIA().ui_automation_client.IUIAutomationElement)()
elif isinstance(start_after, UIAElementInfo):
com_elem = start_after.element
else:
raise TypeError("UIAElementInfo type or None is expected")
propid = propid if propid is not None else 0
value = value if value is not None else uia_defs.vt_empty
itm = self._iface.FindItemByProperty(com_elem, propid, value)
return UIAElementInfo(itm)