408 lines
11 KiB
Python
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)
|
|
|
|
|