www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | LICENSE

XternalAppsParametricTool.py (3868B)


      1 import FreeCAD as App
      2 #from xml.etree import ElementTree
      3 from lxml import etree
      4 import ExternalAppsList
      5 from ToolXML import *
      6 import re
      7 
      8 def create(appName, toolName):
      9     name = appName + toolName
     10     obj = App.ActiveDocument.addObject("App::DocumentObjectGroupPython", name)
     11     XternalAppsParametricTool(obj, appName, toolName)
     12     return obj
     13 
     14 # TODO: read-only/immutable
     15 typeToFreeCADTypeDict = {
     16     # TODO:do an XML namespace lookup instead of comparing a constant.
     17     'xsd:decimal': 'App::PropertyFloat',
     18     'xsd:string': 'App::PropertyString',
     19 }
     20 
     21 def typeToFreeCADType(type):
     22     if type.startswith('mime:'):
     23         return MIMETypeToFreeCADType(MIMEType[5:])
     24     if type in typeToFreeCADTypeDict:
     25         return typeToFreeCADTypeDict[type]
     26     else:
     27         raise ArgumentException('Unsupported XForms type')
     28 
     29 def MIMETypeToFreeCADType(MIMEType):
     30     if MIMEType == 'image/svg+xml':
     31         return 'App::PropertyLink'
     32     else:
     33         raise ArgumentException('Unsupported MIME type')
     34 
     35 class XternalAppsParametricTool():
     36     def __init__(self, obj, appName, toolName):
     37         self.Type = "XternalAppsParametricTool"
     38         self.AppName = appName
     39         self.ToolName = toolName
     40         obj.Proxy = self
     41         self.createPropertiesFromXML(obj)
     42 
     43     def interpretXML(self):
     44         types = {}
     45         modelInstance = {}
     46         inputs = {}
     47 
     48         xml = etree.parse(self.Tool.XForms)
     49         model = xml.find('./xforms:model', ns)
     50         instanceDocument = etree.ElementTree(model.find('./xforms:instance/*', ns))
     51 
     52         # Traverse the XForms instance and register all elements in modelInstance[pathToElement]
     53         for element in instanceDocument.findall('.//*'):
     54             path = instanceDocument.getpath(element)
     55             modelInstance[path] = element.text
     56 
     57         # register all xform:bind to types[pathToTargetElement]
     58         for bind in model.findall('xforms:bind', ns):
     59             for bound in instanceDocument.findall(bind.attrib['ref'], namespaces=bind.nsmap):
     60                 path = instanceDocument.getpath(bound)
     61                 # TODO: if has attrib type then …
     62                 type = bind.attrib['type']
     63                 # TODO: I guess XForms implicitly allows intersection types by using several bind statements?
     64                 types[path] = type
     65                 # TODO: "required" field
     66 
     67         # register all inputs to inputs[pathToElement]
     68         for group in xml.findall('./xforms:group', ns):
     69             for input in group.findall('./xforms:input', ns):
     70                 # TODO: is it safe to pass input unprotected here?
     71                 modelElement = instanceDocument.find(input.attrib['ref'], namespaces=input.nsmap)
     72                 if modelElement is None:
     73                     raise Exception('Could not find ' + input.attrib['ref'] + ' in instance document with namespaces=' + repr(input.nsmap))
     74                 type = types[instanceDocument.getpath(modelElement)]
     75                 inputs[xml.getpath(input)] = (input, modelElement, type)
     76         return (xml, types, modelInstance, inputs)
     77 
     78     def createPropertiesFromXML(self, obj):
     79         xml, types, modelInstance, inputs = self.interpretXML()
     80         for (input, modelElement, type) in inputs.values():
     81             simpleName = re.sub(r'( |[^-a-zA-Z0-9])+', ' ', input.attrib['label']).title().replace(' ', '')
     82             input.xpath('ancestor-or-self::group')
     83             obj.addProperty(typeToFreeCADType(type),
     84                             simpleName,
     85                             "/".join(input.xpath('ancestor-or-self::xforms:group/xforms:label/text()', namespaces=ns)) or None,
     86                             input.attrib['label'] + '\nA value of type ' + type)
     87 
     88     @property
     89     def Tool(self):
     90         return ExternalAppsList.apps[self.AppName].Tools[self.ToolName]
     91 
     92 def execute(self, obj):
     93         """This is called when the object is recomputed"""