pywinauto/Branches/OriginalPydlgChecks/XMLHelpers.py
2006-01-04 19:40:23 +00:00

408 lines
11 KiB
Python

# how should we read in the XML file
# NOT USING MS Components (requirement on machine)
# maybe using built in XML
# maybe using elementtree
# others?
import elementtree
from elementtree.ElementTree import Element, SubElement, ElementTree
import xml.parsers.expat
import ctypes
import re
import PIL.Image
from win32structures import RECT, LOGFONTW
class XMLParsingError(RuntimeError):
pass
charReplacements = (
("\\\\" , "\\"),
("\\t" , "\t"),
("\\r" , "\r"),
("\\n" , "\n"),
("\\x12" , "\x12"),
)
#todo - make the dialog reading function not actually know about the
# types of each element (so that we can read the control properties
# without having to know each and every element type)
# probably need to store info on what type things are.
#
# This has mostly been done:
# - if it is a ctypes struct then there is a __type__ field
# which says what kind of stuct it is
# - If it is an image then a "_IMG" is appeded to the the element tag
# - if it is a long then _LONG is appended to attribute name
# everything else is considered a string!
#-----------------------------------------------------------------------------
def SetNodeProps(element, name, value):
# if it is a ctypes structure
if isinstance(value, ctypes.Structure):
# create an element for the structure
structElem = SubElement(element, name)
clsModule = value.__class__.__module__
clsName = value.__class__.__name__
structElem.set("__type__", "%s" % clsName)
# iterate over the fields in the structure
for propName in value._fields_:
propName = propName[0]
itemVal = getattr(value, propName)
if isinstance(itemVal, (int, long)):
propName += "_LONG"
itemVal = unicode(itemVal)
structElem.set(propName, EscapeSpecials(itemVal))
elif isinstance(value, PIL.Image.Image):
try:
imageData = value.tostring().encode("bz2").encode("base64")
SetNodeProps(
element,
name + "_IMG",
{"mode": value.mode, "size":value.size, "data":imageData})
# a system error is raised from time to time when we try to grab
# the image of a control that has 0 height or width
except SystemError, e:
pass
#print ";;;" * 20
#print value.mode, value.size
#imageData = ""
elif isinstance(value, (list, tuple)):
# add the element to hold the values
#listElem = SubElement(element, name)
# remove the s at the end (if there)
#name = name.rstrip('s')
for i, attrVal in enumerate(value):
SetNodeProps(element, "%s_%05d"%(name, i), attrVal)
elif isinstance(value, dict):
dictElem = SubElement(element, name)
for n, val in value.items():
SetNodeProps(dictElem, n, val)
else:
if isinstance(value, (int, long)):
name += "_LONG"
element.set(name, EscapeSpecials(value))
#-----------------------------------------------------------------------------
def WriteDialogToFile(fileName, props):
# build a tree structure
root = Element("DIALOG")
root.set("_version_", "2.0")
for ctrl in props:
ctrlElem = SubElement(root, "CONTROL")
for name, value in sorted(ctrl.items()):
SetNodeProps(ctrlElem, name, value)
# wrap it in an ElementTree instance, and save as XML
tree = ElementTree(root)
tree.write(fileName, encoding="utf-8")
#-----------------------------------------------------------------------------
def EscapeSpecials(inStr):
"Ensure that some characters are escaped before writing to XML"
# make sure input is a unicode string or convert
inStr = unicode(inStr)
# unicode_escape encoding seems to be broken
#escaped = escaped.encode("unicode_escape")
for (replacement, char) in charReplacements:
inStr = inStr.replace(char, replacement)
return inStr
#-----------------------------------------------------------------------------
def UnEscapeSpecials(inStr):
"Replace escaped characters with real character"
#bits = inStr.split("\\\\")
#for i, bit in enumerate(bits):
# for (replacement, char) in charReplacements[1:]:
# inStr = inStr.replace(replacement, char)
inStr = inStr.decode("unicode_escape")
return unicode(inStr)
#-----------------------------------------------------------------------------
def XMLToStruct(element, structType = None):
"""Convert an ElementTree to a ctypes Struct
If structType is not specified then element['__type__']
will be used for the ctypes struct type"""
# handle if we are passed in an element or a dictionary
if isinstance(element, elementtree.ElementTree._ElementInterface):
attribs = element.attrib
else:
attribs = element
# if the type has not been passed in
if not structType:
# get the type and create an instance of the type
struct = globals()[attribs["__type__"]]()
else:
# create an instance of the type
struct = globals()[structType]()
# get the attribute and set them upper case
structAttribs = dict([(at.upper(), at) for at in dir(struct)])
# for each of the attributes in the element
for propName in attribs:
# get teh value
val = attribs[propName]
# if the value ends with "_long"
if propName.endswith("_LONG"):
# get an long attribute out of the value
val = long(val)
propName = propName[:-5]
# if the value is a string
elif isinstance(val, basestring):
# make sure it if Unicode
val = unicode(val)
# now we can have all upper case attribute name
# but structure name will not be upper case
if propName.upper() in structAttribs:
propName = structAttribs[propName.upper()]
# set the appropriate attribute of the Struct
setattr(struct, propName, val)
# reutrn the struct
return struct
#====================================================================
def OLD_XMLToTitles(element):
# get all the attribute names
titleNames = element.keys()
# sort them to make sure we get them in the right order
titleNames.sort()
# build up the array
titles = []
for name in titleNames:
val = element[name]
val = val.replace('\\n', '\n')
val = val.replace('\\x12', '\x12')
val = val.replace('\\\\', '\\')
titles.append(unicode(val))
return titles
#====================================================================
def ExtractProperties(properties, propName, propValue):
# get the base property name and number if it in the form
# "PROPNAME_00001" = ('PROPNAME', 1)
propName, reqdIndex = SplitNumber(propName)
# if there is no required index, and the property
# was not already set - then just set it
# if this is an indexed member of a list
if reqdIndex == None:
# Have we hit a property with this name already
if propName in properties:
# try to append current value to the property
try:
properties[propName].append(propValue)
# if that fails then we need to make sure that
# the curruen property is a list and then
# append it
except:
newVal = [properties[propName], propValue]
properties[propName] = newVal
# No index, no previous property with that name
# - just set the property
else:
properties[propName] = propValue
# OK - so it HAS an index
else:
# make sure that the property is a list
properties.setdefault(propName, [])
# make sure that the list has enough elements
while 1:
if len(properties[propName]) <= reqdIndex:
properties[propName].append('')
else:
break
# put our value in at the right index
properties[propName][reqdIndex] = propValue
#====================================================================
def GetAttributes(element):
properties = {}
# get all the attributes
for attribName, val in element.attrib.items():
# if it is 'Long' element convert it to an long
if attribName.endswith("_LONG"):
val = long(val)
attribName = attribName[:-5]
else:
# otherwise it is a string - make sure we get it as a unicode string
val = UnEscapeSpecials(val)
ExtractProperties(properties, attribName, val)
return properties
#====================================================================
number = re.compile(r"^(.*)_(\d{5})$")
def SplitNumber(propName):
found = number.search(propName)
if not found:
return propName, None
return found.group(1), int(found.group(2))
#====================================================================
def ReadXMLStructure(controlElement):
# get the attributes for the current element
properties = GetAttributes(controlElement)
for elem in controlElement:
# if it is a ctypes structure
if "__type__" in elem.attrib:
# create a new instance of the correct type
# grab the data
propVal = XMLToStruct(elem)
elif elem.tag.endswith("_IMG"):
elem.tag = elem.tag[:-4]
# get image Attribs
img = GetAttributes(elem)
data = img['data'].decode('base64').decode('bz2')
propVal = PIL.Image.fromstring(img['mode'], img['size'], data)
else:
propVal = ReadXMLStructure(elem)
ExtractProperties(properties, elem.tag, propVal)
return properties
#====================================================================
def ReadPropertiesFromFile(filename):
"""Return an list of controls from XML file filename"""
# parse the file
parsed = ElementTree().parse(filename)
# Return the list that has been stored under 'CONTROL'
props = ReadXMLStructure(parsed)['CONTROL']
# it is an old XML so let's fix it up a little
if not parsed.attrib.has_key("_version_"):
# find each of the control elements
for ctrlProp in props:
ctrlProp['Fonts'] = [XMLToStruct(ctrlProp['FONT'], "LOGFONTW"), ]
ctrlProp['Rectangle'] = XMLToStruct(ctrlProp["RECTANGLE"], "RECT")
ctrlProp['ClientRects'] = [XMLToStruct(ctrlProp["CLIENTRECT"], "RECT"),]
ctrlProp['Texts'] = OLD_XMLToTitles(ctrlProp["TITLES"])
ctrlProp['Class'] = ctrlProp['CLASS']
ctrlProp['ContextHelpID'] = ctrlProp['HELPID']
ctrlProp['ControlID'] = ctrlProp['CTRLID']
ctrlProp['ExStyle'] = ctrlProp['EXSTYLE']
ctrlProp['FriendlyClassName'] = ctrlProp['FRIENDLYCLASS']
ctrlProp['IsUnicode'] = ctrlProp['ISUNICODE']
ctrlProp['IsVisible'] = ctrlProp['ISVISIBLE']
ctrlProp['Style'] = ctrlProp['STYLE']
ctrlProp['UserData'] = ctrlProp['USERDATA']
for propName in [
'CLASS',
'CLIENTRECT',
'CTRLID',
'EXSTYLE',
'FONT',
'FRIENDLYCLASS',
'HELPID',
'ISUNICODE',
'ISVISIBLE',
'RECTANGLE',
'STYLE',
'TITLES',
'USERDATA',
]:
del(ctrlProp[propName])
return props
#====================================================================
if __name__ == "__main__":
import sys
props = ReadPropertiesFromFile(sys.argv[1])
WriteDialogToFile(sys.argv[1] + "__", props)
import pprint
pprint.pprint(props)