XMLHelpers.py :  » GUI » Python-Win32-GUI-Automation » pywinauto-0.4.0 » pywinauto » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » GUI » Python Win32 GUI Automation 
Python Win32 GUI Automation » pywinauto 0.4.0 » pywinauto » XMLHelpers.py
# GUI Application automation and testing library
# Copyright (C) 2006 Mark Mc Mahon
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
#    Free Software Foundation, Inc.,
#    59 Temple Place,
#    Suite 330,
#    Boston, MA 02111-1307 USA

"""Module containing operations for reading and writing dialogs as XML
"""

__revision__ = "$Revision: 736 $"


# 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
try:
    # Python 2.5 (thanks to Daisuke Yamashita)
    from xml.etree.ElementTree import Element,SubElement,ElementTree
    from xml.etree.cElementTree import Element,SubElement,ElementTree
except ImportError:
    from elementtree.ElementTree import Element,SubElement,ElementTree
    from cElementTree import Element,SubElement,ElementTree

import ctypes
import re
import PIL.Image
import controls

# reported that they are not used - but in fact they are
# through a search of globals()
from win32structures import LOGFONTW,RECT

class XMLParsingError(RuntimeError):
    "Wrap parsing Exceptions"
    pass



#DONE: 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.
#
# - 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):
    "Set the properties of the node based on the type of object"

    # if it is a ctypes structure
    if isinstance(value, ctypes.Structure):

        # create an element for the structure
        struct_elem = SubElement(element, name)
        #clsModule = value.__class__.__module__
        cls_name = value.__class__.__name__
        struct_elem.set("__type__", "%s" % cls_name)

        # iterate over the fields in the structure
        for prop_name in value._fields_:
            prop_name = prop_name[0]
            item_val = getattr(value, prop_name)

            if isinstance(item_val, (int, long)):
                prop_name += "_LONG"
                item_val = unicode(item_val)

            struct_elem.set(prop_name, _EscapeSpecials(item_val))

    elif hasattr(value, 'tostring') and hasattr(value, 'size'):
        try:
            # if the image is too big then don't try to
            # write it out - it would probably product a MemoryError
            # anyway
            if value.size[0] * value.size[1] > (5000*5000):
                raise MemoryError

            image_data = value.tostring().encode("bz2").encode("base64")
            _SetNodeProps(
                element,
                name + "_IMG",
                {
                    "mode": value.mode,
                    "size_x":value.size[0],
                    "size_y":value.size[1],
                    "data":image_data
                })

        # 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, MemoryError):
            pass


    elif isinstance(value, (list, tuple)):
        # add the element to hold the values
        # we do this to be able to support empty lists
        listelem = SubElement(element, name + "_LIST")

        for i, attrval in enumerate(value):
            _SetNodeProps(listelem, "%s_%05d"%(name, i), attrval)

    elif isinstance(value, dict):
        dict_elem = SubElement(element, name)

        for item_name, val in value.items():
            _SetNodeProps(dict_elem, item_name, val)

    else:
        if isinstance(value, bool):
            value = long(value)

        if isinstance(value, (int, long)):
            name += "_LONG"

        element.set(name, _EscapeSpecials(value))


#-----------------------------------------------------------------------------
def WriteDialogToFile(filename, props):
    """Write the props to the file

    props can be either a dialog of a dictionary
    """
    # if we are passed in a wrapped handle then
    # get the properties
    try:
        props[0].keys()
    except (TypeError, AttributeError):
        props = controls.GetDialogPropsFromHandle(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(string):
    "Ensure that some characters are escaped before writing to XML"

    # ensure it is unicode
    string = unicode(string)

    # escape backslashs
    string = string.replace('\\', r'\\')

    # escape non printable characters (chars below 30)
    for i in range(0, 32):
        string = string.replace(unichr(i), "\\%02d"%i)

    return string


#-----------------------------------------------------------------------------
def _UnEscapeSpecials(string):
    "Replace escaped characters with real character"

    # Unescape all the escape characters
    for i in range(0, 32):
        string = string.replace("\\%02d"%i, unichr(i))

    # convert doubled backslashes to a single backslash
    string = string.replace(r'\\', '\\')

    return unicode(string)



#-----------------------------------------------------------------------------
def _XMLToStruct(element, struct_type = None):
    """Convert an ElementTree to a ctypes Struct

    If struct_type 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
    try:
        attribs = element.attrib
    except AttributeError:
        attribs = element

    # if the type has not been passed in
    if not struct_type:
        # get the type and create an instance of the type
        struct = globals()[attribs["__type__"]]()
    else:
        # create an instance of the type
        struct = globals()[struct_type]()

    # get the attribute and set them upper case
    struct_attribs = dict([(at.upper(), at) for at in dir(struct)])

    # for each of the attributes in the element
    for prop_name in attribs:

        # get the value
        val = attribs[prop_name]

        # if the value ends with "_long"
        if prop_name.endswith("_LONG"):
            # get an long attribute out of the value
            val = long(val)
            prop_name = prop_name[:-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 prop_name.upper() in struct_attribs:
            prop_name = struct_attribs[prop_name.upper()]

            # set the appropriate attribute of the Struct
            setattr(struct, prop_name, val)

    # reutrn the struct
    return struct



#====================================================================
def _OLD_XMLToTitles(element):
    "For OLD XML files convert the titles as a list"
    # get all the attribute names
    title_names = element.keys()

    # sort them to make sure we get them in the right order
    title_names.sort()

    # build up the array
    titles = []
    for name in title_names:
        val = element[name]
        val = val.replace('\\n', '\n')
        val = val.replace('\\x12', '\x12')
        val = val.replace('\\\\', '\\')

        titles.append(unicode(val))

    return titles


#====================================================================
# TODO: this function should be broken up into smaller functions
#       for each type of processing e.g.
#       ElementTo
def _ExtractProperties(properties, prop_name, prop_value):
    """Hmmm - confusing - can't remember exactly how
    all these similar functions call each other"""

    # get the base property name and number if it in the form
    #  "PROPNAME_00001" = ('PROPNAME', 1)
    prop_name, reqd_index = _SplitNumber(prop_name)

    # 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 reqd_index == None:
        # Have we hit a property with this name already
        if prop_name in properties:
            # try to append current value to the property
            try:
                properties[prop_name].append(prop_value)

            # if that fails then we need to make sure that
            # the curruen property is a list and then
            # append it
            except AttributeError:
                new_val = [properties[prop_name], prop_value]
                properties[prop_name] = new_val
        # No index, no previous property with that name
        #  - just set the property
        else:
            properties[prop_name] = prop_value

    # OK - so it HAS an index
    else:

        # make sure that the property is a list
        properties.setdefault(prop_name, [])

        # make sure that the list has enough elements
        while 1:
            if len(properties[prop_name]) <= reqd_index:
                properties[prop_name].append('')
            else:
                break

        # put our value in at the right index
        properties[prop_name][reqd_index] = prop_value


#====================================================================
def _GetAttributes(element):
    "Get the attributes from an element"

    properties = {}

    # get all the attributes
    for attrib_name, val in element.attrib.items():

        # if it is 'Long' element convert it to an long
        if attrib_name.endswith("_LONG"):
            val = long(val)
            attrib_name = attrib_name[:-5]

        else:
            # otherwise it is a string - make sure we get it as a unicode string
            val = _UnEscapeSpecials(val)

        _ExtractProperties(properties, attrib_name, val)

    return properties


#====================================================================
number = re.compile(r"^(.*)_(\d{5})$")
def _SplitNumber(prop_name):
    """Return (string, number) for a prop_name in the format string_number

    The number part has to be 5 digits long
    None is returned if there is no _number part

    e.g.
    >>> _SplitNumber("NoNumber")
    ('NoNumber', None)
    >>> _SplitNumber("Anumber_00003")
    ('Anumber', 3)
    >>> _SplitNumber("notEnoughDigits_0003")
    ('notEnoughDigits_0003', None)
    """
    found = number.search(prop_name)

    if not found:
        return prop_name, None

    return found.group(1), int(found.group(2))



#====================================================================
def _ReadXMLStructure(control_element):
    """Convert an element into nested Python objects

    The values will be returned in a dictionary as following:

     - the attributes will be items of the dictionary
       for each subelement

       + if it has a __type__ attribute then it is converted to a
         ctypes structure
       + if the element tag ends with _IMG then it is converted to
         a PIL image

     - If there are elements with the same name or attributes with
       ordering e.g. texts_00001, texts_00002 they will be put into a
       list (in the correct order)
    """

    # get the attributes for the current element
    properties = _GetAttributes(control_element)

    for elem in control_element:
        # 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_x'], img['size_y']),
                data)

        elif elem.tag.endswith("_LIST"):
            # All this is just to handle the edge case of
            # an empty list
            elem.tag = elem.tag[:-5]

            # read the structure
            propval = _ReadXMLStructure(elem)

            # if it was empty then convert the returned dict
            # to a list
            if propval == {}:
                propval = list()

            # otherwise extract the list out of the returned dict
            else:
                propval = propval[elem.tag]

        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']
    if not isinstance(props, list):
        props = [props]


    # 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 ctrl_prop in props:

            ctrl_prop['Fonts'] = [_XMLToStruct(ctrl_prop['FONT'], "LOGFONTW"), ]

            ctrl_prop['Rectangle'] = \
                _XMLToStruct(ctrl_prop["RECTANGLE"], "RECT")

            ctrl_prop['ClientRects'] = [
                _XMLToStruct(ctrl_prop["CLIENTRECT"], "RECT"),]

            ctrl_prop['Texts'] = _OLD_XMLToTitles(ctrl_prop["TITLES"])

            ctrl_prop['Class'] = ctrl_prop['CLASS']
            ctrl_prop['ContextHelpID'] = ctrl_prop['HELPID']
            ctrl_prop['ControlID'] = ctrl_prop['CTRLID']
            ctrl_prop['ExStyle'] = ctrl_prop['EXSTYLE']
            ctrl_prop['FriendlyClassName'] = ctrl_prop['FRIENDLYCLASS']
            ctrl_prop['IsUnicode'] = ctrl_prop['ISUNICODE']
            ctrl_prop['IsVisible'] = ctrl_prop['ISVISIBLE']
            ctrl_prop['Style'] = ctrl_prop['STYLE']
            ctrl_prop['UserData'] = ctrl_prop['USERDATA']

            for prop_name in [
                'CLASS',
                'CLIENTRECT',
                'CTRLID',
                'EXSTYLE',
                'FONT',
                'FRIENDLYCLASS',
                'HELPID',
                'ISUNICODE',
                'ISVISIBLE',
                'RECTANGLE',
                'STYLE',
                'TITLES',
                'USERDATA',
                ]:
                del(ctrl_prop[prop_name])

    return props


www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.