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.
272 lines
8.8 KiB
272 lines
8.8 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.
|
||
|
|
||
|
"""Wrap"""
|
||
|
from .win32structures import RECT, LOGFONTW
|
||
|
from . import deprecated
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
class FuncWrapper(object):
|
||
|
|
||
|
"""Little class to allow attribute access to return a callable object"""
|
||
|
|
||
|
def __init__(self, value):
|
||
|
self.value = value
|
||
|
|
||
|
def __call__(self, *args, **kwargs):
|
||
|
"""Return the saved value"""
|
||
|
return self.value
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
class ControlProps(dict):
|
||
|
|
||
|
"""Wrap controls read from a file to resemble hwnd controls"""
|
||
|
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
dict.__init__(self, *args, **kwargs)
|
||
|
|
||
|
self.ref = None
|
||
|
#self.menu_items = []
|
||
|
|
||
|
def __getattr__(self, attr):
|
||
|
# if the key is not in the dictionary but the plural is
|
||
|
if attr not in self and attr + "s" in self:
|
||
|
# return the first element of the possible list item
|
||
|
return FuncWrapper(self[attr+'s'][0])
|
||
|
|
||
|
return FuncWrapper(self[attr])
|
||
|
|
||
|
#def friendly_class_name(self):
|
||
|
# print "sdafafasdfafasdfasdf",
|
||
|
# try:
|
||
|
# print "---", self['friendly_class_name']
|
||
|
# except Exception as e:
|
||
|
# print "fffffffffffffffffffff"
|
||
|
# print `e`
|
||
|
# return self['friendly_class_name']
|
||
|
|
||
|
def window_text(self):
|
||
|
return self['texts'][0]
|
||
|
# Non PEP-8 alias
|
||
|
WindowText = deprecated(window_text)
|
||
|
|
||
|
def has_style(self, style):
|
||
|
return self['style'] & style == style
|
||
|
# Non PEP-8 alias
|
||
|
HasStyle = deprecated(has_style)
|
||
|
|
||
|
def has_exstyle(self, exstyle):
|
||
|
return self['exstyle'] & exstyle == exstyle
|
||
|
# Non PEP-8 alias
|
||
|
HasExStyle = deprecated(has_exstyle, deprecated_name="HasExStyle")
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
def GetMenuBlocks(ctrls):
|
||
|
allMenuBlocks = []
|
||
|
for ctrl in ctrls:
|
||
|
if 'menu_items' in ctrl.keys():
|
||
|
# we need to get all the separate menu blocks!
|
||
|
menuBlocks = MenuBlockAsControls(ctrl.menu_items())
|
||
|
allMenuBlocks.extend(menuBlocks)
|
||
|
|
||
|
return allMenuBlocks
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
def MenuBlockAsControls(menuItems, parentage = None):
|
||
|
|
||
|
if parentage is None:
|
||
|
parentage = []
|
||
|
blocks = []
|
||
|
|
||
|
curBlock = []
|
||
|
for item in menuItems:
|
||
|
|
||
|
# do a bit of conversion first :-)
|
||
|
itemAsCtrl = MenuItemAsControl(item)
|
||
|
|
||
|
# update the friendly_class_name to contain the 'path' to
|
||
|
# this particular item
|
||
|
# TODO: CHECK - as itemPath is currently unused!
|
||
|
if parentage:
|
||
|
itemPath = "%s->%s" % ("->".join(parentage), item['text'])
|
||
|
else:
|
||
|
itemPath = item['text']
|
||
|
|
||
|
#append the item to the current menu block
|
||
|
curBlock.append(itemAsCtrl)
|
||
|
|
||
|
# If the item has a sub menu
|
||
|
if 'menu_items' in item.keys():
|
||
|
|
||
|
# add the current item the path
|
||
|
parentage.append(item['text'])
|
||
|
|
||
|
# Get the block for the SubMenu, and add it to the list of
|
||
|
# blocks we have found
|
||
|
blocks.extend(
|
||
|
MenuBlockAsControls(
|
||
|
item['menu_items']['menu_items'], parentage))
|
||
|
|
||
|
# and seeing as we are dong with that sub menu remove the current
|
||
|
# item from the path
|
||
|
del(parentage[-1])
|
||
|
|
||
|
# add the current block to the list of blocks
|
||
|
blocks.append(curBlock)
|
||
|
|
||
|
return blocks
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
def MenuItemAsControl(menuItem):
|
||
|
"""Make a menu item look like a control for tests"""
|
||
|
itemAsCtrl = ControlProps()
|
||
|
|
||
|
itemAsCtrl["texts"] = [menuItem['text'], ]
|
||
|
itemAsCtrl["control_id"] = menuItem['id']
|
||
|
itemAsCtrl["type"] = menuItem['type']
|
||
|
itemAsCtrl["state"] = menuItem['state']
|
||
|
|
||
|
itemAsCtrl["class_name"] = "MenuItem"
|
||
|
itemAsCtrl["friendly_class_name"] = "MenuItem"
|
||
|
|
||
|
# as most of these don't matter - just set them up with default stuff
|
||
|
itemAsCtrl["rectangle"] = RECT(0, 0, 999, 999)
|
||
|
itemAsCtrl["fonts"] = [LOGFONTW(), ]
|
||
|
itemAsCtrl["client_rects"] = [RECT(0, 0, 999, 999), ]
|
||
|
itemAsCtrl["context_help_id"] = 0
|
||
|
itemAsCtrl["user_data"] = 0
|
||
|
itemAsCtrl["style"] = 0
|
||
|
itemAsCtrl["exstyle"] = 0
|
||
|
itemAsCtrl["is_visible"] = 1
|
||
|
|
||
|
return itemAsCtrl
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
def SetReferenceControls(controls, refControls):
|
||
|
"""Set the reference controls for the controls passed in
|
||
|
|
||
|
This does some minor checking as following:
|
||
|
* test that there are the same number of reference controls as
|
||
|
controls - fails with an exception if there are not
|
||
|
* test if all the ID's are the same or not
|
||
|
"""
|
||
|
|
||
|
# numbers of controls must be the same (though in future I could imagine
|
||
|
# relaxing this constraint)
|
||
|
|
||
|
if len(controls) != len(refControls):
|
||
|
raise RuntimeError(
|
||
|
"Numbers of controls on ref. dialog does not match Loc. dialog")
|
||
|
|
||
|
# set the controls
|
||
|
for i, ctrl in enumerate(controls):
|
||
|
ctrl.ref = refControls[i]
|
||
|
|
||
|
toRet = 1
|
||
|
allIDsSameFlag = 2
|
||
|
allClassesSameFlag = 4
|
||
|
|
||
|
# find if all the control id's match
|
||
|
if [ctrl.control_id() for ctrl in controls] == \
|
||
|
[ctrl.control_id() for ctrl in refControls]:
|
||
|
|
||
|
toRet += allIDsSameFlag
|
||
|
|
||
|
# check if the control classes match
|
||
|
if [ctrl.class_name() for ctrl in controls] == \
|
||
|
[ctrl.class_name() for ctrl in refControls]:
|
||
|
|
||
|
toRet += allClassesSameFlag
|
||
|
|
||
|
return toRet
|
||
|
|
||
|
|
||
|
|
||
|
##====================================================================
|
||
|
#class ControlProps(dict):
|
||
|
# #----------------------------------------------------------------
|
||
|
# def __init__(self, props = {}):
|
||
|
# # default to having menuItems for all things
|
||
|
# self.menu_items = []
|
||
|
#
|
||
|
# self.update(props)
|
||
|
# #for x in props:
|
||
|
# #self[x] = props[x]
|
||
|
#
|
||
|
# if hasattr(props, "handle"):
|
||
|
# self.__dict__['handle'] = props.handle
|
||
|
# else:
|
||
|
# self.__dict__['handle'] = None
|
||
|
#
|
||
|
# self.__dict__['ref'] = None
|
||
|
#
|
||
|
# #----------------------------------------------------------------
|
||
|
# # handles attribute access for dictionary items and
|
||
|
# # for plurals (e.g. if self.fonts = [4, 2] then self.font = 4)
|
||
|
# def __getattr__(self, key):
|
||
|
#
|
||
|
# # if the key is not in the dictionary but the plural is
|
||
|
# if key not in self and key + "s" in self:
|
||
|
#
|
||
|
# # try to get the first element of the possible list item
|
||
|
# try:
|
||
|
# return self[key + "s"][0]
|
||
|
# except TypeError as e:
|
||
|
# pass
|
||
|
#
|
||
|
# if key in self:
|
||
|
# return self[key]
|
||
|
#
|
||
|
# return self.__dict__[key]
|
||
|
#
|
||
|
# #----------------------------------------------------------------
|
||
|
# def __setattr__(self, key, value):
|
||
|
# if key in self.__dict__:
|
||
|
# self.__dict__[key] = value
|
||
|
# else:
|
||
|
# self[key] = value
|
||
|
#
|
||
|
# #----------------------------------------------------------------
|
||
|
# def has_style(self, flag):
|
||
|
# return self.style & flag == flag
|
||
|
#
|
||
|
# #----------------------------------------------------------------
|
||
|
# def has_exstyle(self, flag):
|
||
|
# return self.exstyle & flag == flag
|
||
|
#
|
||
|
#
|