Skip to content

Commit

Permalink
An initial implementation of ComboBox UIA wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
airelil committed Mar 13, 2016
1 parent 9ad46f2 commit 1f1294a
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 5 deletions.
Binary file modified apps/WPF_samples/WpfApplication1.exe
Binary file not shown.
Binary file modified apps/WPF_samples/x64/WpfApplication1.exe
Binary file not shown.
126 changes: 125 additions & 1 deletion pywinauto/controls/UIAWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
from ..base_wrapper import BaseWrapper
from ..base_wrapper import BaseMeta

from ..UIAElementInfo import UIAElementInfo
from ..UIAElementInfo import UIAElementInfo, elements_from_uia_array
from ..UIAElementInfo import _UIA_dll

#region PATTERNS
Expand Down Expand Up @@ -249,6 +249,130 @@ def invoke(self):
elem = self.element_info.element
iface = uia_defs.get_elem_interface(elem, "Invoke")
iface.Invoke()

# Return itself to allow action chaining
return self

#-----------------------------------------------------------
def expand(self):
"""
An interface to Expand method of the ExpandCollapse control pattern.
Displays all child nodes, controls, or content of the control
"""

elem = self.element_info.element
iface = uia_defs.get_elem_interface(elem, "ExpandCollapse")
iface.Expand()

# Return itself to allow action chaining
return self

#-----------------------------------------------------------
def collapse(self):
"""
An interface to Collapse method of the ExpandCollapse control pattern.
Displays all child nodes, controls, or content of the control
"""

elem = self.element_info.element
iface = uia_defs.get_elem_interface(elem, "ExpandCollapse")
iface.Collapse()

# Return itself to allow action chaining
return self

#-----------------------------------------------------------
def get_expand_state(self):
"""
An interface to CurrentExpandCollapseState property of
the ExpandCollapse control pattern.
Indicates the state, expanded or collapsed, of the control.
Values for enumeration as defined in comptypes.gen.Client module:
ExpandCollapseState_Collapsed = 0
ExpandCollapseState_Expanded = 1
ExpandCollapseState_PartiallyExpanded = 2
ExpandCollapseState_LeafNode = 3
"""

elem = self.element_info.element
iface = uia_defs.get_elem_interface(elem, "ExpandCollapse")
return iface.CurrentExpandCollapseState

#-----------------------------------------------------------
def is_expanded(self):
"""
Test if the control is expanded
"""
state = self.get_expand_state()
return state == uia_defs.expand_state_expanded

#-----------------------------------------------------------
def is_collapsed(self):
"""
Test if the control is collapsed
"""
state = self.get_expand_state()
return state == uia_defs.expand_state_collapsed

def get_selection(self):
"""
An interface to GetSelection of the SelectionProvider pattern
Retrieves a UI Automation provider for each child element
that is selected. Builds a list of UIAElementInfo elements
from all retrieved providers.
"""
elem = self.element_info.element
iface = uia_defs.get_elem_interface(elem, "Selection")
ptrs_array = iface.GetCurrentSelection()
return elements_from_uia_array(ptrs_array)

def can_select_multiple(self):
"""
An interface to CanSelectMultiple of the SelectionProvider pattern
Indicates whether the UI Automation provider allows more than one
child element to be selected concurrently.
"""
elem = self.element_info.element
iface = uia_defs.get_elem_interface(elem, "Selection")
return iface.CurrentCanSelectMultiple

def is_selection_required(self):
"""
An interface to IsSelectionRequired property of the
SelectionProvider pattern.
This property can be dynamic. For example, the initial state of
a control might not have any items selected by default,
meaning that IsSelectionRequired is FALSE. However,
after an item is selected the control must always have
at least one item selected.
"""
elem = self.element_info.element
iface = uia_defs.get_elem_interface(elem, "Selection")
return iface.CurrentIsSelectionRequired

def select_by_name_or_by_idx(self, item_name = None, item_index = 0):
"""Find a child item by the name or index and select
The action can be applied for dirrent controls with items:
ComboBox, TreeView, ListView
"""

list_ = self.element_info.children(title = item_name)
if item_index < len(list_):
elem = list_[item_index].element
iface = uia_defs.get_elem_interface(elem, "SelectionItem")
iface.Select()
else:
raise ValueError



backend.register('uia', UIAElementInfo, UIAWrapper)
40 changes: 39 additions & 1 deletion pywinauto/controls/uia_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

"""Wrap various UIA windows controls
"""

import pywinauto.six as six
import pywinauto.uia_defines as uia_defs
from . import UIAWrapper
from ..uia_defines import _UIA_dll
Expand Down Expand Up @@ -131,3 +131,41 @@ def is_selected(self):
elem = self.element_info.element
iface = uia_defs.get_elem_interface(elem, "SelectionItem")
return iface.CurrentIsSelected

#====================================================================
class ComboBoxWrapper(UIAWrapper.UIAWrapper):
"Wrap a UIA CoboBox control"

control_types = [
_UIA_dll.UIA_ComboBoxControlTypeId
]

#-----------------------------------------------------------
def __init__(self, hwnd):
"Initialize the control"
super(ComboBoxWrapper, self).__init__(hwnd)

def select(self, item):
"""Select the ComboBox item
item can be either a 0 based index of the item to select
or it can be the string that you want to select
"""

item_index = 0
item_name = None
if isinstance(item, six.integer_types):
item_index = item
elif isinstance(item, six.string_types):
item_name = item
else:
self.actions.log(
"UIA ComboBox.select error: wrong item type - {0}".format(item))
raise ValueError

self.expand()
try:
self.select_by_name_or_by_idx(item_name, item_index)
finally:
self.collapse()
return self
13 changes: 10 additions & 3 deletions pywinauto/uia_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import comtypes

_UIA_dll = comtypes.client.GetModule('UIAutomationCore.dll')
_uia_clnt = comtypes.gen.UIAutomationClient

# Build a list of named constants that identify Microsoft UI Automation
# control patterns and their appropriate comtypes classes
Expand Down Expand Up @@ -93,14 +94,20 @@ def _build_pattern_ids_dic():
# The definition can also be found in the comtypes package
# In a file automatically generated according to UIAutomation GUID:
# comtypes\gen\_944DE083_8FB8_45CF_BCB7_C477ACB2F897_*.py
toggle_state_off = 0
toggle_state_on = 1
toggle_state_inderteminate = 2
toggle_state_off = _uia_clnt.ToggleState_Off
toggle_state_on = _uia_clnt.ToggleState_On
toggle_state_inderteminate = _uia_clnt.ToggleState_Indeterminate

class NoPatternInterfaceError(Exception):
"There is no such interface for the specified pattern"
pass

# values for enumeration 'ExpandCollapseState'
expand_state_collapsed = _uia_clnt.ExpandCollapseState_Collapsed
expand_state_expanded = _uia_clnt.ExpandCollapseState_Expanded
expand_state_partially = _uia_clnt.ExpandCollapseState_PartiallyExpanded
expand_state_leaf_node = _uia_clnt.ExpandCollapseState_LeafNode

def get_elem_interface(element_info, pattern_name):
"""A helper to retrieve an element interface by the specified pattern name
Expand Down
41 changes: 41 additions & 0 deletions pywinauto/unittests/test_UIAWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,47 @@ def testRadioButton(self):
cur_state = self.dlg.Yes.select().is_selected()
self.assertEqual(cur_state, True)

def testComboBox(self):
"Test 'Expand' and 'Collapse' for the combo box control"

combo_box = self.dlg.ComboBox

self.assertEqual(combo_box.can_select_multiple(), 0)
self.assertEqual(combo_box.is_selection_required(), False)
selection = combo_box.get_selection()
num_selected_items = len(selection)
self.assertEqual(num_selected_items, 0)

# The ComboBox on the sample app has following items:
# 0. Combo Item 1
# 1. Combo Item 2
selection = combo_box.select(0).get_selection()
self.assertEqual(len(selection), 1)
self.assertEqual(selection[0].name, 'Combo Item 1')

collapsed = combo_box.is_collapsed()
self.assertEqual(collapsed, True)

selection = combo_box.select(1).get_selection()
self.assertEqual(len(selection), 1)
self.assertEqual(selection[0].name, 'Combo Item 2')

combo_box.select('Combo Item 1')
selection = combo_box.get_selection()
self.assertEqual(len(selection), 1)
self.assertEqual(selection[0].name, 'Combo Item 1')

collapsed = combo_box.is_collapsed()
self.assertEqual(collapsed, True)

expanded = combo_box.expand().is_expanded()
self.assertEqual(expanded, True)

collapsed = combo_box.collapse().is_collapsed()
self.assertEqual(collapsed, True)



if __name__ == "__main__":
if UIA_support:
unittest.main()

0 comments on commit 1f1294a

Please sign in to comment.