You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Resources/Web/pywinauto/uia_defines.py

236 lines
9.6 KiB

6 years ago
# GUI Application automation and testing library
# Copyright (C) 2006-2018 Mark Mc Mahon and Contributors
# https://github.com/pywinauto/pywinauto/graphs/contributors
# http://pywinauto.readthedocs.io/en/latest/credits.html
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of pywinauto nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Common UIA definitions and helper functions"""
import comtypes
import comtypes.client
import six
class _Singleton(type):
"""
Singleton metaclass implementation from StackOverflow
http://stackoverflow.com/q/6760685/3648361
"""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
@six.add_metaclass(_Singleton)
class IUIA(object):
"""Singleton class to store global COM objects from UIAutomationCore.dll"""
def __init__(self):
self.UIA_dll = comtypes.client.GetModule('UIAutomationCore.dll')
self.ui_automation_client = comtypes.gen.UIAutomationClient
self.iuia = comtypes.CoCreateInstance(
self.ui_automation_client.CUIAutomation().IPersist_GetClassID(),
interface=self.ui_automation_client.IUIAutomation,
clsctx=comtypes.CLSCTX_INPROC_SERVER
)
self.true_condition = self.iuia.CreateTrueCondition()
self.tree_scope = {
'ancestors': self.UIA_dll.TreeScope_Ancestors,
'children': self.UIA_dll.TreeScope_Children,
'descendants': self.UIA_dll.TreeScope_Descendants,
'element': self.UIA_dll.TreeScope_Element,
'parent': self.UIA_dll.TreeScope_Parent,
'subtree': self.UIA_dll.TreeScope_Subtree,
}
self.root = self.iuia.GetRootElement()
self.get_focused_element = self.iuia.GetFocusedElement
# collect all known control types
start_len = len('UIA_')
end_len = len('ControlTypeId')
self._control_types = [attr[start_len:-end_len] for attr in dir(self.UIA_dll) if attr.endswith('ControlTypeId')]
self.known_control_types = {} # string id: numeric id
self.known_control_type_ids = {} # numeric id: string id
for ctrl_type in self._control_types:
type_id_name = 'UIA_' + ctrl_type + 'ControlTypeId'
type_id = getattr(self.UIA_dll, type_id_name)
self.known_control_types[ctrl_type] = type_id
self.known_control_type_ids[type_id] = ctrl_type
def build_condition(self, process=None, class_name=None, title=None, control_type=None,
content_only=None):
"""Build UIA filtering conditions"""
conditions = []
if process:
conditions.append(self.iuia.CreatePropertyCondition(self.UIA_dll.UIA_ProcessIdPropertyId, process))
if class_name:
conditions.append(self.iuia.CreatePropertyCondition(self.UIA_dll.UIA_ClassNamePropertyId, class_name))
if control_type:
if isinstance(control_type, six.string_types):
control_type = self.known_control_types[control_type]
elif not isinstance(control_type, int):
raise TypeError('control_type must be string or integer')
conditions.append(self.iuia.CreatePropertyCondition(self.UIA_dll.UIA_ControlTypePropertyId, control_type))
if title:
# TODO: CreatePropertyConditionEx with PropertyConditionFlags_IgnoreCase
conditions.append(self.iuia.CreatePropertyCondition(self.UIA_dll.UIA_NamePropertyId, title))
if isinstance(content_only, bool):
conditions.append(self.iuia.CreatePropertyCondition(self.UIA_dll.UIA_IsContentElementPropertyId,
content_only))
if len(conditions) > 1:
return self.iuia.CreateAndConditionFromArray(conditions)
if len(conditions) == 1:
return conditions[0]
return self.true_condition
# Build a list of named constants that identify Microsoft UI Automation
# control patterns and their appropriate comtypes classes
# We'll try to add all patterns available for the given version of Windows OS
# Reference:
# https://msdn.microsoft.com/en-us/library/windows/desktop/ee671195(v=vs.85).aspx
# header: UIAutomationClient.h
def _build_pattern_ids_dic():
"""
A helper procedure to build a registry of control patterns
supported on the current system
"""
base_names = [
'Dock', 'ExpandCollapse', 'GridItem', 'Grid', 'Invoke', 'ItemContainer',
'LegacyIAccessible', 'MulipleView', 'RangeValue', 'ScrollItem', 'Scroll',
'SelectionItem', 'Selection', 'SynchronizedInput', 'TableItem', 'Table',
'Text', 'Toggle', 'VirtualizedItem', 'Value', 'Window',
'Transform',
# Windows 8 and later
'Annotation', 'Drag', 'Drop', 'ObjectModel', 'Spreadsheet',
'SpreadsheetItem', 'Styles', 'TextChild', 'TextV2', 'TransformV2',
# Windows 8.1 and later
'TextEdit',
# Windows 10 and later
'CustomNavigation'
]
ptrn_ids_dic = {}
# Loop over the all base names and try to retrieve control patterns
for ptrn_name in base_names:
# Construct a class name and check if it is supported by comtypes
v2 = ""
name = ptrn_name
if ptrn_name.endswith("V2"):
name = ptrn_name[:-2]
v2 = "2"
cls_name = ''.join(['IUIAutomation', name, 'Pattern', v2])
if hasattr(IUIA().ui_automation_client, cls_name):
klass = getattr(IUIA().ui_automation_client, cls_name)
# Contruct a pattern ID name and get the ID value
ptrn_id_name = 'UIA_' + name + 'Pattern' + v2 + 'Id'
ptrn_id = getattr(IUIA().UIA_dll, ptrn_id_name)
# Update the registry of known patterns
ptrn_ids_dic[ptrn_name] = (ptrn_id, klass)
return ptrn_ids_dic
pattern_ids = _build_pattern_ids_dic()
# Return values for the toggle_state propery
# enum ToggleState {
# ToggleState_Off,
# ToggleState_On,
# ToggleState_Indeterminate
# };
# 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 = IUIA().ui_automation_client.ToggleState_Off
toggle_state_on = IUIA().ui_automation_client.ToggleState_On
toggle_state_inderteminate = IUIA().ui_automation_client.ToggleState_Indeterminate
class NoPatternInterfaceError(Exception):
"""There is no such interface for the specified pattern"""
pass
# values for enumeration 'ExpandCollapseState'
expand_state_collapsed = IUIA().ui_automation_client.ExpandCollapseState_Collapsed
expand_state_expanded = IUIA().ui_automation_client.ExpandCollapseState_Expanded
expand_state_partially = IUIA().ui_automation_client.ExpandCollapseState_PartiallyExpanded
expand_state_leaf_node = IUIA().ui_automation_client.ExpandCollapseState_LeafNode
# values for enumeration 'WindowVisualState'
window_visual_state_normal = IUIA().ui_automation_client.WindowVisualState_Normal
window_visual_state_maximized = IUIA().ui_automation_client.WindowVisualState_Maximized
window_visual_state_minimized = IUIA().ui_automation_client.WindowVisualState_Minimized
# values for enumeration 'ScrollAmount'
scroll_large_decrement = IUIA().ui_automation_client.ScrollAmount_LargeDecrement
scroll_small_decrement = IUIA().ui_automation_client.ScrollAmount_SmallDecrement
scroll_no_amount = IUIA().ui_automation_client.ScrollAmount_NoAmount
scroll_large_increment = IUIA().ui_automation_client.ScrollAmount_LargeIncrement
scroll_small_increment = IUIA().ui_automation_client.ScrollAmount_SmallIncrement
def get_elem_interface(element_info, pattern_name):
"""A helper to retrieve an element interface by the specified pattern name
TODO: handle a wrong pattern name
"""
# Resolve the pattern id and the class to query
ptrn_id, cls_name = pattern_ids[pattern_name]
# Get the interface
try:
cur_ptrn = element_info.GetCurrentPattern(ptrn_id)
iface = cur_ptrn.QueryInterface(cls_name)
except(ValueError):
raise NoPatternInterfaceError()
return iface