Commit 28379b664a7167f5fa9625a9c1b24b5366b23d5d

Authored by Alexandre Schulz
1 parent 27f0d5bd
Exists in better_forms

copied from amda_xml_manager project

xsd2tkform/core/annotation.py
@@ -7,6 +7,8 @@ class Annotation: @@ -7,6 +7,8 @@ class Annotation:
7 self.documentation = docs # dictionary where keys are language tags and values are documentation text 7 self.documentation = docs # dictionary where keys are language tags and values are documentation text
8 def languages(self): 8 def languages(self):
9 return [k for k in self.documentation] 9 return [k for k in self.documentation]
  10 + def __eq__(self, e):
  11 + return self.docs==e.docs
10 def __str__(self): 12 def __str__(self):
11 return "Annotation(languages={})".format(self.languages()) 13 return "Annotation(languages={})".format(self.languages())
12 14
xsd2tkform/core/choice.py
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 from .element import Element 4 from .element import Element
5 5
6 class Choice: 6 class Choice:
7 - def __init__(self, min_occurs=0, max_occurs=0): 7 + def __init__(self, min_occurs=1, max_occurs=1):
8 self.min_occurs=min_occurs 8 self.min_occurs=min_occurs
9 self.max_occurs=max_occurs 9 self.max_occurs=max_occurs
10 self.elements=[] 10 self.elements=[]
xsd2tkform/core/element.py
1 """Element definition 1 """Element definition
2 """ 2 """
  3 +from lxml import etree
  4 +
  5 +class Attribute:
  6 + def __init__(self, name=None, type="string", use=None, default=None):
  7 + self.name=name
  8 + self.type=type
  9 + self.use=use
  10 + self.default=default
  11 + def mandatory(self):
  12 + return self.use=="required"
  13 + def __eq__(self, e):
  14 + if not isinstance(e, Attribute):
  15 + return False
  16 + return self.name==e.name and self.type==e.type and self.use==e.use and self.default==e.default
  17 + def __str__(self):
  18 + return "Attribute (name={}, type={}, use={}, default={})".format(self.name, self.type, self.use, self.default)
  19 + @staticmethod
  20 + def from_element(element):
  21 + att=dict(element.attrib)
  22 + return Attribute(**att)
  23 +
  24 +class AnyElement:
  25 + def __init__(self, **kwargs):
  26 + self.attributes=kwargs
  27 + def __str__(self, *args, **kwargs):
  28 + return "AnyElement"
3 29
4 class Element: 30 class Element:
5 - def __init__(self, name=None, etype=None, min_occurs=0, max_occurs=0): 31 + def __init__(self, name=None, etype=None, min_occurs=1, max_occurs=1, abstract=False, typedef=None, ref=None, substitution_group=None,**kwargs):
6 self.name=name 32 self.name=name
7 self.type=etype 33 self.type=etype
8 self.min_occurs=min_occurs 34 self.min_occurs=min_occurs
9 self.max_occurs=max_occurs 35 self.max_occurs=max_occurs
  36 + self.typedef = typedef
  37 + self.abstract=abstract
  38 + self.ref=ref
  39 + self.substitution_group=substitution_group
  40 + if self.type is None:
  41 + if self.typedef is None:
  42 + self.type = self.name
  43 + else:
  44 + self.type = self.typedef.name
  45 + def __eq__(self, e):
  46 + return self.name==e.name and self.type==e.type and self.min_occurs==e.min_occurs and \
  47 + self.max_occurs==e.max_occurs and self.typedef==e.typedef and self.abstract==e.abstract and \
  48 + self.ref==e.ref and self.substitution_group==e.substitution_group
10 def __str__(self, tab_n=0): 49 def __str__(self, tab_n=0):
11 - return "\t"*tab_n+"Element(name={}, type={}, min_occurs={}, max_occurs={})".format(self.name,  
12 - self.type,self.min_occurs, self.max_occurs) 50 + return "\t"*tab_n+"Element(name={}, type={}, min_occurs={}, max_occurs={}, abstract={}, typedef={}, ref={})".format(self.name,
  51 + self.type,self.min_occurs, self.max_occurs, self.abstract, self.typedef, self.ref)
13 @staticmethod 52 @staticmethod
14 def from_element(element): 53 def from_element(element):
15 att=dict(element.attrib) 54 att=dict(element.attrib)
@@ -23,4 +62,44 @@ class Element: @@ -23,4 +62,44 @@ class Element:
23 att["max_occurs"]=att.pop("maxOccurs") 62 att["max_occurs"]=att.pop("maxOccurs")
24 if att["max_occurs"].isdigit(): 63 if att["max_occurs"].isdigit():
25 att["max_occurs"]=int(att["max_occurs"]) 64 att["max_occurs"]=int(att["max_occurs"])
26 - return Element(**att) 65 + if "substitutionGroup" in att:
  66 + att["substitution_group"]=att.pop("substitutionGroup")
  67 + if "abstract" in att:
  68 + print("ABSTRACT val ", att["abstract"], type(att["abstract"]))
  69 + if att["abstract"]=="true":
  70 + att["abstract"]=True
  71 + if att["abstract"]=="false":
  72 + att["abstract"]=False
  73 + # check if element contains type definition
  74 + ct=None
  75 + for child in element:
  76 + if child.tag.endswith("complexType"):
  77 + from .type import ComplexType, SimpleType
  78 + from .utils import get_sequence, get_extension
  79 + from .sequence import Sequence
  80 + sequence = get_sequence(child)
  81 + attributes= get_attributes(child)
  82 + extension=get_extension(child)
  83 + if extension is None:
  84 + ct=ComplexType(att["name"], annotation=None, sequence=sequence, attributes=attributes, extension=extension)
  85 + elif isinstance(extension[1], Sequence):
  86 + ct=ComplexType(att["name"], annotation=None, sequence=sequence, attributes=attributes, extension=extension)
  87 + elif isinstance(extension[1], list):
  88 + from .restriction import Restriction
  89 + ct=SimpleType(att["name"], restriction=Restriction(base=extension[0]), attributes=extension[1])
  90 + else:
  91 + ct=ComplexType(att["name"], annotation=None, sequence=sequence, attributes=attributes, extension=extension)
  92 + continue
  93 +
  94 +
  95 +
  96 + return Element(typedef = ct, **att)
  97 +
  98 +def get_attributes(element):
  99 + attributes=[]
  100 + for child in element:
  101 + if isinstance(child, etree._Comment):
  102 + continue
  103 + if child.tag.endswith("}attribute"):
  104 + attributes.append(Attribute.from_element(child))
  105 + return attributes
xsd2tkform/core/parser.py
@@ -4,9 +4,9 @@ For now we suppose the file contains a single schema @@ -4,9 +4,9 @@ For now we suppose the file contains a single schema
4 """ 4 """
5 from lxml import etree 5 from lxml import etree
6 6
7 -from xsd2tkform.core.type import SimpleType, ComplexType, Group, from_element  
8 -from xsd2tkform.core.annotation import Annotation  
9 -from xsd2tkform.core.schema import Schema 7 +from .type import SimpleType, ComplexType, Group, from_element
  8 +from .annotation import Annotation
  9 +from .schema import Schema
10 10
11 class XSDParser: 11 class XSDParser:
12 def __init__(self, filename=None): 12 def __init__(self, filename=None):
@@ -21,7 +21,10 @@ class XSDParser: @@ -21,7 +21,10 @@ class XSDParser:
21 ns=root.nsmap 21 ns=root.nsmap
22 # if root is a schema 22 # if root is a schema
23 if root.tag.endswith("schema"): 23 if root.tag.endswith("schema"):
24 - self.schemas.append(Schema.from_element(root)) 24 + self.schemas.append(Schema.from_element(root, filename))
  25 + # apply substitutions
  26 + self.schemas[-1].apply_substitutions()
  27 + self.schemas[-1].apply_extensions()
25 return 28 return
26 def get(self, typename): 29 def get(self, typename):
27 for schema in self.schemas: 30 for schema in self.schemas:
xsd2tkform/core/restriction.py
@@ -6,6 +6,8 @@ class Restriction: @@ -6,6 +6,8 @@ class Restriction:
6 def __init__(self, base=None, enum=[]): 6 def __init__(self, base=None, enum=[]):
7 self.base=base 7 self.base=base
8 self.enum=enum 8 self.enum=enum
  9 + def __eq__(self, e):
  10 + return self.base==e.base and self.enum==e.enum
9 def __str__(self): 11 def __str__(self):
10 return "Restriction(base={}, enum={})".format(self.base, self.enum) 12 return "Restriction(base={}, enum={})".format(self.base, self.enum)
11 def possible_values(self): 13 def possible_values(self):
xsd2tkform/core/schema.py
@@ -2,27 +2,116 @@ @@ -2,27 +2,116 @@
2 """ 2 """
3 from lxml import etree 3 from lxml import etree
4 4
5 -from xsd2tkform.core.type import SimpleType, ComplexType, Group, from_element  
6 -from xsd2tkform.core.element import Element 5 +from .type import SimpleType, ComplexType, Group, from_element
  6 +from .element import Element
7 7
8 class Schema: 8 class Schema:
9 - def __init__(self): 9 + def __init__(self, filename):
  10 + self.filename=filename # need filename to manage include statements
  11 + self.root_items = []
10 self.items=[] 12 self.items=[]
  13 + #self.elements={} # map element names to their type
11 self.simple_types={} 14 self.simple_types={}
12 self.complex_types={} 15 self.complex_types={}
13 self.groups={} 16 self.groups={}
14 - def parse_element(self, element): 17 + def find_ref(self, name):
  18 + for i in self.items:
  19 + if i.name==name:
  20 + return i
  21 + def find_substitutions(self, name):
  22 + return [e for e in self.items if e.substitution_group==name]
  23 + def include_schema(self, element):
  24 + schema_file = element.attrib["schemaLocation"]
  25 + from .parser import XSDParser
  26 + import os
  27 + path = os.path.join(os.path.dirname(self.filename), schema_file)
  28 + parser = XSDParser(path)
  29 + for schema in parser.schemas:
  30 + self.add_schema(schema)
  31 + def add_schema(self, schema):
  32 + for i in schema.items:
  33 + if not i in self.items:
  34 + self.items.append(i)
  35 + for ri in schema.root_items:
  36 + if not ri in self.root_items:
  37 + self.root_items.append(ri)
  38 + # simple types
  39 + for k in schema.simple_types:
  40 + self.simple_types[k]=schema.simple_types[k]
  41 + # complex types
  42 + for k in schema.complex_types:
  43 + self.complex_types[k]=schema.complex_types[k]
  44 + # groups
  45 + for k in schema.groups:
  46 + self.groups[k]=schema.groups[k]
  47 +
  48 +
  49 +
  50 + def parse_element(self, element, root=True, name=None):
  51 + # if element is an include statement
  52 + if element.tag.endswith("}include"):
  53 + self.include_schema(element)
15 td = from_element(element) 54 td = from_element(element)
  55 + if td is not None:
  56 + if td.name=="get":
  57 + pass
16 if isinstance(td, SimpleType): 58 if isinstance(td, SimpleType):
17 self.simple_types[td.name]=td 59 self.simple_types[td.name]=td
18 elif isinstance(td, ComplexType): 60 elif isinstance(td, ComplexType):
19 self.complex_types[td.name]=td 61 self.complex_types[td.name]=td
20 elif isinstance(td, Group): 62 elif isinstance(td, Group):
21 self.groups[td.name]=td 63 self.groups[td.name]=td
  64 +
22 elif isinstance(td, Element): 65 elif isinstance(td, Element):
  66 + if td.type is None:
  67 + # get type definition in this element
  68 + td.type=td.name
  69 + ct = td.typedef
  70 + self.complex_types[ct.name]=ct
  71 + #for child in element:
  72 + # self.parse_element(child, name=td.name, root=False)
  73 + #td.type = td.name
  74 +
23 self.items.append(td) 75 self.items.append(td)
  76 + if root:
  77 + self.root_items.append(td)
24 else: 78 else:
25 - print("Unknown type defined : ", element, element.attrib, element.tag) 79 + pass
  80 +
  81 + # WARNING : should not do this
  82 + for child in element:
  83 + if isinstance(child, etree._Comment):
  84 + continue
  85 + self.parse_element(child, root=False)
  86 + def apply_substitutions(self):
  87 + for i in self.items:
  88 + if i.substitution_group is not None:
  89 + # get the current object type definition
  90 + td = self.get(i.type)
  91 + # get the base type
  92 + bases = [e for e in self.items if e.name==i.substitution_group]
  93 + if len(bases):
  94 + # get the corresponding type
  95 + base_t = self.get(bases[0].type)
  96 + for att in base_t.attributes:
  97 + if not att in td.attributes:
  98 + td.attributes.append(att)
  99 + self.complex_types[td.name]=td
  100 + def apply_extensions(self):
  101 + for k in self.complex_types:
  102 + t = self.complex_types[k]
  103 + if t.extension is not None:
  104 + # get base type
  105 + base_type = self.get(t.extension[0])
  106 + # add attributes and sequence element
  107 + for att in base_type.attributes:
  108 + if not att in t.attributes:
  109 + t.attributes.append(att)
  110 + for item in base_type.sequence.items:
  111 + if not item in t.sequence.items:
  112 + t.sequence.add(item)
  113 + t.sequence = t.extension[1]
  114 + t.extension=None
26 def get(self, typename): 115 def get(self, typename):
27 if ":" in typename: 116 if ":" in typename:
28 typename = typename.split(":")[-1] 117 typename = typename.split(":")[-1]
@@ -32,6 +121,10 @@ class Schema: @@ -32,6 +121,10 @@ class Schema:
32 return self.complex_types[typename] 121 return self.complex_types[typename]
33 if typename in self.groups: 122 if typename in self.groups:
34 return self.groups[typename] 123 return self.groups[typename]
  124 + for item in self.items:
  125 + if typename==item.name:
  126 + return item
  127 +
35 def __contains__(self, typename): 128 def __contains__(self, typename):
36 if typename in self.simple_types: 129 if typename in self.simple_types:
37 return True 130 return True
@@ -39,13 +132,16 @@ class Schema: @@ -39,13 +132,16 @@ class Schema:
39 return True 132 return True
40 if typename in self.groups: 133 if typename in self.groups:
41 return True 134 return True
  135 + item_names = [i.name for i in self.items]
  136 + if typename in item_names:
  137 + return True
42 return False 138 return False
43 139
44 @staticmethod 140 @staticmethod
45 - def from_element(element): 141 + def from_element(element, filename):
46 if not element.tag.endswith("schema"): 142 if not element.tag.endswith("schema"):
47 raise Exception("Cannot initialize Schema from {}".format(element)) 143 raise Exception("Cannot initialize Schema from {}".format(element))
48 - schema = Schema() 144 + schema = Schema(filename=filename)
49 # load type definitions and components 145 # load type definitions and components
50 for child in element: 146 for child in element:
51 # ignore comments 147 # ignore comments
xsd2tkform/core/sequence.py
@@ -2,14 +2,16 @@ @@ -2,14 +2,16 @@
2 2
3 """ 3 """
4 4
5 -from xsd2tkform.core.element import Element  
6 -from xsd2tkform.core.choice import Choice 5 +from .element import Element
  6 +from .choice import Choice
7 7
8 class Sequence: 8 class Sequence:
9 def __init__(self, items=[]): 9 def __init__(self, items=[]):
10 self.items = items 10 self.items = items
11 def add(self, i): 11 def add(self, i):
12 self.items.append(i) 12 self.items.append(i)
  13 + def __eq__(self, e):
  14 + return self.items==e.items
13 def __str__(self, tab_n=0): 15 def __str__(self, tab_n=0):
14 r="\t"*tab_n+"Sequence" 16 r="\t"*tab_n+"Sequence"
15 if len(self.items): 17 if len(self.items):
@@ -28,6 +30,8 @@ class Sequence: @@ -28,6 +30,8 @@ class Sequence:
28 s.add(Element.from_element(child)) 30 s.add(Element.from_element(child))
29 elif child.tag.endswith("choice"): 31 elif child.tag.endswith("choice"):
30 s.add(Choice.from_element(child)) 32 s.add(Choice.from_element(child))
  33 + elif child.tag.endswith("any"):
  34 + s.add(AnyElement(**dict(child.attrib)))
31 else: 35 else:
32 pass 36 pass
33 return s 37 return s
xsd2tkform/core/type.py
@@ -2,47 +2,68 @@ @@ -2,47 +2,68 @@
2 2
3 XSDType class is the base class for all XSD types, inherited by the XSDSimpleType, XSDComplexType classes""" 3 XSDType class is the base class for all XSD types, inherited by the XSDSimpleType, XSDComplexType classes"""
4 4
5 -from xsd2tkform.core.element import Element  
6 -from xsd2tkform.core.annotation import Annotation  
7 -from xsd2tkform.core.restriction import Restriction  
8 -from xsd2tkform.core.list import List  
9 -from xsd2tkform.core.utils import get_annotation, get_restriction, get_list, get_sequence 5 +from .element import Element, get_attributes
  6 +from .annotation import Annotation
  7 +from .restriction import Restriction
  8 +from .list import List
  9 +from .utils import get_annotation, get_restriction, get_list, get_sequence, get_extension
10 10
11 class XSDType: 11 class XSDType:
12 def __init__(self, name=None, annotation=None): 12 def __init__(self, name=None, annotation=None):
13 self.name = name 13 self.name = name
14 self.annotation = annotation 14 self.annotation = annotation
  15 + def __eq__(self, e):
  16 + if not isinstance(e, XSDType):
  17 + return False
  18 + return self.name==e.name and self.annotation==e.annotation
15 def __str__(self): 19 def __str__(self):
16 return "XSDType(name={})".format(self.name) 20 return "XSDType(name={})".format(self.name)
17 21
18 class SimpleType(XSDType): 22 class SimpleType(XSDType):
19 - def __init__(self, name=None, restriction=None, item_list=None, *args, **kwargs): 23 + def __init__(self, name=None, restriction=None, item_list=None, attributes=[], *args, **kwargs):
20 XSDType.__init__(self, name=name, *args, **kwargs) 24 XSDType.__init__(self, name=name, *args, **kwargs)
21 self.restriction=restriction 25 self.restriction=restriction
22 self.item_list = item_list 26 self.item_list = item_list
  27 + self.attributes=attributes
  28 + def __eq__(self, e):
  29 + if not isinstance(e, SimpleType):
  30 + return False
  31 + return super().__eq__(e) and self.restriction==e.restriction and self.item_list==e.item_list and self.attributes==e.attributes
23 def __str__(self): 32 def __str__(self):
24 att=["name={})".format(self.name)] 33 att=["name={})".format(self.name)]
25 if self.restriction is not None: 34 if self.restriction is not None:
26 att.append("\n\trestriction={}".format(str(self.restriction))) 35 att.append("\n\trestriction={}".format(str(self.restriction)))
27 if self.item_list is not None: 36 if self.item_list is not None:
28 att.append("\n\tlist={}".format(self.item_list)) 37 att.append("\n\tlist={}".format(self.item_list))
  38 + if len(self.attributes):
  39 + att.append("\n\tattributes={}".format(self.attributes))
29 a = "SimpleType({}".format(", ".join(att)) 40 a = "SimpleType({}".format(", ".join(att))
30 return a 41 return a
31 42
32 class ComplexType(XSDType): 43 class ComplexType(XSDType):
33 - def __init__(self, name=None, sequence=None, *args, **kwargs): 44 + def __init__(self, name=None, sequence=None, attributes=[], extension=None, *args, **kwargs):
34 XSDType.__init__(self, name=name, *args, **kwargs) 45 XSDType.__init__(self, name=name, *args, **kwargs)
35 self.sequence=sequence 46 self.sequence=sequence
  47 + self.attributes=attributes
  48 + self.extension=extension
  49 + def __eq__(self, e):
  50 + if not isinstance(e, ComplexType):
  51 + return False
  52 + return super().__eq__(e) and self.sequence==e.sequence
36 def __str__(self, tab_n=0): 53 def __str__(self, tab_n=0):
37 r = "ComplexType(name={})".format(self.name) 54 r = "ComplexType(name={})".format(self.name)
38 if len(self.sequence): 55 if len(self.sequence):
39 r+="\n"+self.sequence.__str__(tab_n=tab_n+1) 56 r+="\n"+self.sequence.__str__(tab_n=tab_n+1)
  57 + if len(self.attributes):
  58 + r+="\nAttributes {}".format(self.attributes)
40 return r 59 return r
41 60
42 class Group(XSDType): 61 class Group(XSDType):
43 def __init__(self, name=None, sequence=None, *args, **kwargs): 62 def __init__(self, name=None, sequence=None, *args, **kwargs):
44 self.name = name 63 self.name = name
45 self.sequence=sequence 64 self.sequence=sequence
  65 + def __eq__(self, e):
  66 + return super().__eq__(e) and self.name==e.name and self.sequence==e.sequence
46 def __str__(self, tab_n=0): 67 def __str__(self, tab_n=0):
47 r="Group (name={})".format(self.name) 68 r="Group (name={})".format(self.name)
48 if len(self.sequence): 69 if len(self.sequence):
@@ -53,19 +74,31 @@ class Group(XSDType): @@ -53,19 +74,31 @@ class Group(XSDType):
53 def from_element(element): 74 def from_element(element):
54 """Parse a XML element to XSDType object 75 """Parse a XML element to XSDType object
55 """ 76 """
  77 + if "name" not in element.attrib:
  78 + return
56 name = element.attrib["name"] 79 name = element.attrib["name"]
57 annotation = get_annotation(element) 80 annotation = get_annotation(element)
  81 + attributes = get_attributes(element)
58 if element.tag.endswith("simpleType"): 82 if element.tag.endswith("simpleType"):
59 restriction = get_restriction(element) 83 restriction = get_restriction(element)
60 l= get_list(element) 84 l= get_list(element)
61 - return SimpleType(name=name, annotation=annotation, restriction=restriction, item_list=l) 85 + return SimpleType(name=name, annotation=annotation, restriction=restriction, item_list=l, attributes=attributes)
62 elif element.tag.endswith("complexType"): 86 elif element.tag.endswith("complexType"):
63 sequence = get_sequence(element) 87 sequence = get_sequence(element)
64 - return ComplexType(name, annotation=annotation, sequence=sequence) 88 + extension = get_extension(element)
  89 + if extension is None:
  90 + return ComplexType(name, annotation=annotation, sequence=sequence, attributes=attributes)
  91 + from .sequence import Sequence
  92 + if isinstance(extension[1], Sequence):
  93 + return ComplexType(name, annotation=annotation, sequence=sequence, attributes=attributes, extension=extension)
  94 + if isinstance(extension[1], list):
  95 + r=SimpleType(name, type=extensions[0], attributes=extension[1])
  96 + return r
65 elif element.tag.endswith("group"): 97 elif element.tag.endswith("group"):
66 sequence = get_sequence(element) 98 sequence = get_sequence(element)
67 return Group(name, annotation=annotation, sequence=sequence) 99 return Group(name, annotation=annotation, sequence=sequence)
68 elif element.tag.endswith("element"): 100 elif element.tag.endswith("element"):
69 - return Element.from_element(element) 101 + e= Element.from_element(element)
  102 + return e
70 else: 103 else:
71 return None 104 return None
xsd2tkform/core/utils.py
1 -from xsd2tkform.core.annotation import Annotation  
2 -from xsd2tkform.core.restriction import Restriction  
3 -from xsd2tkform.core.list import List  
4 -from xsd2tkform.core.sequence import Sequence  
5 -from xsd2tkform.core.choice import Choice  
6 -from xsd2tkform.core.element import Element 1 +from lxml import etree
  2 +
  3 +from .annotation import Annotation
  4 +from .restriction import Restriction
  5 +from .list import List
  6 +from .sequence import Sequence
  7 +from .choice import Choice
  8 +from .element import Element, AnyElement
7 9
8 def get_annotation(element): 10 def get_annotation(element):
9 """Get annotations from a type definition element 11 """Get annotations from a type definition element
@@ -39,6 +41,8 @@ def get_list(element): @@ -39,6 +41,8 @@ def get_list(element):
39 def get_sequence(element): 41 def get_sequence(element):
40 sequence = [] 42 sequence = []
41 for child in element: 43 for child in element:
  44 + if isinstance(child, etree._Comment):
  45 + continue
42 if child.tag.endswith("sequence"): 46 if child.tag.endswith("sequence"):
43 # children should be elements of choices 47 # children should be elements of choices
44 for gchild in child: 48 for gchild in child:
@@ -46,8 +50,28 @@ def get_sequence(element): @@ -46,8 +50,28 @@ def get_sequence(element):
46 sequence.append(Element.from_element(gchild)) 50 sequence.append(Element.from_element(gchild))
47 elif gchild.tag.endswith("choice"): 51 elif gchild.tag.endswith("choice"):
48 sequence.append(Choice.from_element(gchild)) 52 sequence.append(Choice.from_element(gchild))
  53 + elif gchild.tag.endswith("any"):
  54 + sequence.append(AnyElement(**dict(gchild.attrib)))
49 else: 55 else:
50 pass 56 pass
51 return Sequence(sequence) 57 return Sequence(sequence)
52 58
  59 +def get_extension(element):
  60 + from .element import get_attributes
  61 + for child in element:
  62 + if isinstance(child, etree._Comment):
  63 + continue
  64 + if child.tag.endswith("}complexContent"):
  65 + # extension
  66 + if child[0].tag.endswith("}extension"):
  67 + base_type = child[0].attrib["base"]
  68 + sequence = get_sequence(child[0])
  69 + return (base_type, sequence)
  70 + if child.tag.endswith("}simpleContent"):
  71 + # extension
  72 + if child[0].tag.endswith("}extension"):
  73 + base_type = child[0].attrib["base"].split(":")[-1]
  74 + attributes = get_attributes(child[0])
  75 + return (base_type, attributes)
53 76
  77 + return None
xsd2tkform/factory.py
@@ -2,25 +2,41 @@ @@ -2,25 +2,41 @@
2 """ 2 """
3 import tkinter as tk 3 import tkinter as tk
4 4
5 -from xsd2tkform.form import XSDForm  
6 -from xsd2tkform.core.parser import XSDParser  
7 -from xsd2tkform.core.type import SimpleType, ComplexType 5 +from .form import XSDForm
  6 +from .core.parser import XSDParser
  7 +from .core.type import SimpleType, ComplexType
8 8
9 -from xsd2tkform.ui.simpletype import XSDSimpleTypeFrame  
10 -from xsd2tkform.ui.complextype import XSDComplexTypeFrame  
11 -from xsd2tkform.ui.scrollframe import ScrollFrame 9 +from .ui.simpletype import XSDSimpleTypeFrame
  10 +from .ui.complextype import XSDComplexTypeFrame
  11 +from .ui.scrollframe import ScrollFrame
12 class XSDFormFactory: 12 class XSDFormFactory:
13 - def __init__(self, parser=None): 13 + def __init__(self, parser=None, widget_config={}):
  14 + self.widget_config = widget_config
14 self.parser = parser 15 self.parser = parser
15 -  
16 - def get_form(self, typename, parent): 16 + def get_schema_form(self, parent):
  17 + # get form for schema
  18 + schema = self.parser.schemas[0]
  19 + root_frame = tk.Frame(parent)
  20 + for item in schema.root_items:
  21 + item_form = self.get_form(item.name, parent=root_frame)
  22 + item_form.pack(side=tk.TOP, fill=tk.X, expand=1)
  23 + return root_frame
  24 + def get_form(self, typename, parent, filename=None):
17 if not typename in self.parser.schemas[0]: 25 if not typename in self.parser.schemas[0]:
18 - raise Exception("Type {} not found") 26 + raise Exception("Type '{}' not found".format(typename))
19 typedef = self.parser.get(typename) 27 typedef = self.parser.get(typename)
20 if isinstance(typedef, SimpleType): 28 if isinstance(typedef, SimpleType):
21 - return XSDSimpleTypeFrame(typename, schema=self.parser.schemas[0], parent=parent, delete_button=False) 29 + return XSDSimpleTypeFrame(typename, schema=self.parser.schemas[0], parent=parent, delete_button=False, filename=filename, widget_config=self.widget_config)
22 if isinstance(typedef, ComplexType): 30 if isinstance(typedef, ComplexType):
23 - return XSDComplexTypeFrame(typename, schema=self.parser.schemas[0], parent=parent, delete_button=False) 31 + return XSDComplexTypeFrame(typename, schema=self.parser.schemas[0], parent=parent, delete_button=False, filename=filename, widget_config=self.widget_config)
  32 + from .core.element import Element
  33 + if isinstance(typedef, Element):
  34 + if typedef.typedef is None:
  35 + return self.get_form(typedef.type, parent=parent, filename=filename)
  36 + if isinstance(typedef.typedef, ComplexType):
  37 + return XSDComplexTypeFrame(typedef.name, schema=self.parser.schemas[0], parent=parent, delete_button=False, filename=filename, widget_config=self.widget_config, typedef=typedef)
  38 + if isinstance(typedef.typedef, SimpleType):
  39 + return XSDSimpleTypeFrame(typedef.name, schema=self.parser.schemas[0], parent=parent, delete_button=False, filename=filename, widget_config=self.widget_config, typedef=typedef.typedef)
24 40
25 41
26 # example app 42 # example app
@@ -42,28 +58,46 @@ if __name__=="__main__": @@ -42,28 +58,46 @@ if __name__=="__main__":
42 parser = XSDParser(xsd_filename) 58 parser = XSDParser(xsd_filename)
43 # list complex types 59 # list complex types
44 typenames = [k for k in parser.schemas[0].complex_types] 60 typenames = [k for k in parser.schemas[0].complex_types]
  61 + print("Simple types : \n", [k for k in parser.schemas[0].simple_types])
  62 + print("Complex types : \n", [k for k in parser.schemas[0].complex_types])
  63 + print("Elements : \n", [e.type for e in parser.schemas[0].items])
  64 + print("Root elements : \n", parser.schemas[0].root_items)
45 print("Creating form for type : {}".format(typenames[0])) 65 print("Creating form for type : {}".format(typenames[0]))
46 - print(typenames)  
47 - 66 + print()
  67 + for e in parser.schemas[0].items:
  68 + if e.type=="get":
  69 + print(e)
  70 + print()
  71 + print(parser.schemas[0].complex_types["GetterType"])
  72 + #exit()
48 73
49 # form factory 74 # form factory
50 factory=XSDFormFactory(parser) 75 factory=XSDFormFactory(parser)
51 76
  77 +
52 app = ExampleApp() 78 app = ExampleApp()
53 mainframe = tk.Frame(app) 79 mainframe = tk.Frame(app)
54 scrollframe = ScrollFrame(mainframe) # add a new scrollable frame. 80 scrollframe = ScrollFrame(mainframe) # add a new scrollable frame.
55 81
56 # populate window with all simpleTypes found in the XSD schema 82 # populate window with all simpleTypes found in the XSD schema
  83 + #form_frame = factory.get_schema_form(parent = scrollframe.viewPort)
  84 + #form_frame.pack(side=tk.TOP, fill=tk.X, expand=1)
  85 +
  86 + tk.Label(scrollframe.viewPort, text="FILLED").pack(side=tk.TOP, fill=tk.X, expand=1)
  87 +
  88 + filled_form = factory.get_form("param", parent=scrollframe.viewPort, filename="ace_r.xml")
  89 + filled_form.pack(side=tk.TOP, fill=tk.X, expand=1)
57 #for t in typenames: 90 #for t in typenames:
58 - form_frame = factory.get_form(typename = typenames[0], parent = scrollframe.viewPort)  
59 - form_frame.pack(side=tk.TOP, fill=tk.X, expand=1) 91 + # #form_frame = factory.get_form(typename = t, parent = scrollframe.viewPort)
  92 + # form_frame = factory.get_schema_form(parent = scrollframe.viewPort)
  93 + # form_frame.pack(side=tk.TOP, fill=tk.X, expand=1)
60 94
61 # add a submit and cancel button 95 # add a submit and cancel button
62 def save_form(): 96 def save_form():
63 - tree = etree.ElementTree(form_frame.get_content()) 97 + tree = etree.ElementTree(filled_form.get_content())
64 from tkinter.filedialog import askopenfilename 98 from tkinter.filedialog import askopenfilename
65 filename = askopenfilename() 99 filename = askopenfilename()
66 - tree.write(filename, pretty_print=True) 100 + tree.write(filename, pretty_print=True, xml_declaration=True, encoding="utf-8")
67 submit_button = tk.Button(scrollframe.viewPort, text="Submit", command=save_form) 101 submit_button = tk.Button(scrollframe.viewPort, text="Submit", command=save_form)
68 cancel_button = tk.Button(scrollframe.viewPort, text="Cancel", command=app.quit) 102 cancel_button = tk.Button(scrollframe.viewPort, text="Cancel", command=app.quit)
69 submit_button.pack(side=tk.LEFT, fill=tk.X, expand=1) 103 submit_button.pack(side=tk.LEFT, fill=tk.X, expand=1)
xsd2tkform/ui/adaptivetextentry.py 0 โ†’ 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +"""Text input widget with size adaptive height
  2 +"""
  3 +
  4 +import tkinter as tk
  5 +from tkinter import ttk
  6 +
  7 +class AdaptiveHeightText(tk.Text):
  8 + def __init__(self, parent, height=None, width=None, *args, **kwargs):
  9 + tk.Text.__init__(self, parent, height=height, width=width, *args, **kwargs)
  10 + self.bind("<KeyRelease>", self.update_height)
  11 + def update_height(self, event=None):
  12 + text = self.get("1.0", tk.END)
  13 + n_lines = len(text.split("\n"))+1
  14 + self.configure(height=n_lines)
  15 + def insert(self, *args, **kwargs):
  16 + super().insert(*args, **kwargs)
  17 + self.update_height()
  18 + def delete(self, *args, **kwargs):
  19 + super().delete(*args, **kwargs)
  20 + self.update_height()
  21 +
  22 +if __name__=="__main__":
  23 + root = tk.Tk()
  24 + mainframe = tk.Frame(root)
  25 +
  26 + text=AdaptiveHeightText(mainframe, height=2)
  27 + text.pack(side=tk.TOP, fill=tk.X, expand=1)
  28 +
  29 + text.insert("1.0", "a\nb\nc\n")
  30 +
  31 + mainframe.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
  32 +
  33 + root.mainloop()
  34 +
xsd2tkform/ui/attributeframe.py 0 โ†’ 100644
@@ -0,0 +1,126 @@ @@ -0,0 +1,126 @@
  1 +"""Frame for containing list of attributes
  2 +"""
  3 +
  4 +import tkinter as tk
  5 +from tkinter import ttk
  6 +
  7 +from .simpletype import XSDAttributeFrame
  8 +from .entrywidgets import FloatEntry, IntEntry, BoolEntry
  9 +from amda_xml_manager import collapse_image_file, expand_image_file
  10 +
  11 +class AttributeButtonFrame(tk.Frame):
  12 + def __init__(self, parent):
  13 + tk.Frame.__init__(self, parent)
  14 + tk.Label(self, text="Add :", anchor=tk.W).grid(row=0, sticky=tk.W)
  15 + def count_visible_children(self):
  16 + c=0
  17 + for child in self.winfo_children():
  18 + ginfo=child.grid_info()
  19 + if "row" in ginfo:
  20 + c+=1
  21 + return c
  22 +
  23 +class AttributeFrame(tk.Frame):
  24 + def __init__(self, parent, attributes=[]):
  25 + tk.Frame.__init__(self, parent, highlightbackground="black", highlightthickness=1)
  26 +
  27 + #tk.Label(self, text="Attributes").grid(row=0, columnspan=2, sticky=tk.W)
  28 + self.grid_columnconfigure(0, weight=0)
  29 + self.grid_columnconfigure(1, weight=1)
  30 +
  31 + #images
  32 + self.collapse_img = tk.PhotoImage(file=collapse_image_file)
  33 + self.expand_img = tk.PhotoImage(file=expand_image_file)
  34 +
  35 + # add header
  36 + self.get_header_frame()
  37 +
  38 + # content frame : content that is hidden when collapsed
  39 + self.content_frame = tk.Frame(self)
  40 + self.content_frame.grid_columnconfigure(0, weight=0)
  41 + self.content_frame.grid_columnconfigure(1, weight=1)
  42 +
  43 +
  44 + self.attributes=attributes
  45 + # attribute counts
  46 + self.counts={a.name:0 for a in self.attributes}
  47 +
  48 + # button container
  49 + self.button_frame = AttributeButtonFrame(self.content_frame)
  50 +
  51 + self.attribute_inputs=[XSDAttributeFrame(a, parent=self.content_frame,\
  52 + on_delete=lambda t=a.name: self.delete_attribute(t),\
  53 + on_add=self.update_grid) for a in self.attributes]
  54 +
  55 + self.add_attribute_widgets()
  56 +
  57 + self.content_frame.grid(row=1, columnspan=2, sticky=tk.EW)
  58 + def set_attribute_content(self, name, value):
  59 + if "}" in name:
  60 + name = name.split("}")[-1]
  61 + print("attrib Setting {} : {}".format(name, value))
  62 + for i in range(len(self.attributes)):
  63 + if name==self.attributes[i].name:
  64 + A=self.attribute_inputs[i]
  65 + A.set(value)
  66 +
  67 + ginfo=self.attribute_inputs[i].grid_info()
  68 + if not "row" in ginfo:
  69 + self.attribute_inputs[i].grid()
  70 + def get_attribute_values(self):
  71 + r={}
  72 + for inp in self.attribute_inputs:
  73 + if inp.is_visible():
  74 + v=inp.get()
  75 + if len(v):
  76 + r[inp.name]=v
  77 + return r
  78 + def add_attribute_widgets(self):
  79 + # add button for each non required attributes
  80 + row,col=0,1
  81 + for att in self.attribute_inputs:
  82 + att.grid(row=row, column=1, sticky=tk.EW)
  83 + l=att.get_label(self.content_frame)
  84 + l.grid(row=row, column=0, sticky=tk.W)
  85 + if not att.mandatory:
  86 + att.grid_remove()
  87 + l.grid_remove()
  88 +
  89 + b=att.get_add_button(parent=self.button_frame)
  90 + b.grid(row=0, column=col)
  91 + row+=1
  92 + col+=1
  93 +
  94 + def get_header_frame(self):
  95 + self.header_frame=tk.Frame(self)
  96 + self.header_label=tk.Label(self.header_frame, text="Attributes", anchor=tk.W)
  97 + self.header_label.pack(side=tk.LEFT, fill=tk.X, expand=1)
  98 + self.collapse_button=tk.Button(self.header_frame, image=self.collapse_img, command=self.collapse)
  99 + self.collapse_button.pack(side=tk.RIGHT, expand=0)
  100 +
  101 + self.header_frame.grid(row=0, columnspan=2, sticky=tk.EW)
  102 + def collapse(self):
  103 + self.content_frame.grid_remove()
  104 + #for c in self.winfo_children():
  105 + # c.grid_remove()
  106 + #self.header_frame.grid()
  107 + self.collapse_button.configure(image=self.expand_img, command=self.expand)
  108 + def expand(self):
  109 + self.content_frame.grid()
  110 + self.collapse_button.configure(image=self.collapse_img, command=self.collapse)
  111 + def delete_attribute(self, name):
  112 + self.counts[name]-=1
  113 + #self.update_grid()
  114 + def update_grid(self):
  115 + row=1
  116 + for f in self.attribute_inputs:
  117 + row+=1
  118 + ttk.Separator(self, orient="horizontal").grid(row=row, columnspan=2, sticky=tk.EW)
  119 + row+=1
  120 + if self.button_frame.count_visible_children()>1:
  121 + self.button_frame.grid(row=row, columnspan=2, sticky=tk.EW)
  122 + else:
  123 + self.button_frame.grid_remove()
  124 +
  125 + def delete_attribute(self, name):
  126 + self.update_grid()
xsd2tkform/ui/autocompleteentry.py 0 โ†’ 100644
@@ -0,0 +1,152 @@ @@ -0,0 +1,152 @@
  1 +"""
  2 +tkentrycomplete.py
  3 +
  4 +A Tkinter widget that features autocompletion.
  5 +
  6 +Created by Mitja Martini on 2008-11-29.
  7 +Updated by Russell Adams, 2011/01/24 to support Python 3 and Combobox.
  8 +Updated by Dominic Kexel to use Tkinter and ttk instead of tkinter and tkinter.ttk
  9 + Licensed same as original (not specified?), or public domain, whichever is less restrictive.
  10 +
  11 +"""
  12 +import sys
  13 +import os
  14 +import tkinter
  15 +from tkinter import ttk
  16 +
  17 +__version__ = "1.1"
  18 +
  19 +# I may have broken the unicode...
  20 +tkinter_umlauts=['odiaeresis', 'adiaeresis', 'udiaeresis', 'Odiaeresis', 'Adiaeresis', 'Udiaeresis', 'ssharp']
  21 +
  22 +class AutocompleteEntry(tkinter.Entry):
  23 + """
  24 + Subclass of Tkinter.Entry that features autocompletion.
  25 +
  26 + To enable autocompletion use set_completion_list(list) to define
  27 + a list of possible strings to hit.
  28 + To cycle through hits use down and up arrow keys.
  29 + """
  30 + def set_completion_list(self, completion_list):
  31 + self._completion_list = sorted(completion_list, key=str.lower) # Work with a sorted list
  32 + self._hits = []
  33 + self._hit_index = 0
  34 + self.position = 0
  35 + self.bind('<KeyRelease>', self.handle_keyrelease)
  36 +
  37 + def autocomplete(self, delta=0):
  38 + """autocomplete the Entry, delta may be 0/1/-1 to cycle through possible hits"""
  39 + if delta: # need to delete selection otherwise we would fix the current position
  40 + self.delete(self.position, tkinter.END)
  41 + else: # set position to end so selection starts where textentry ended
  42 + self.position = len(self.get())
  43 + # collect hits
  44 + _hits = []
  45 + for element in self._completion_list:
  46 + if element.lower().startswith(self.get().lower()): # Match case-insensitively
  47 + _hits.append(element)
  48 + # if we have a new hit list, keep this in mind
  49 + if _hits != self._hits:
  50 + self._hit_index = 0
  51 + self._hits=_hits
  52 + # only allow cycling if we are in a known hit list
  53 + if _hits == self._hits and self._hits:
  54 + self._hit_index = (self._hit_index + delta) % len(self._hits)
  55 + # now finally perform the auto completion
  56 + if self._hits:
  57 + self.delete(0,tkinter.END)
  58 + self.insert(0,self._hits[self._hit_index])
  59 + self.select_range(self.position,tkinter.END)
  60 +
  61 + def handle_keyrelease(self, event):
  62 + """event handler for the keyrelease event on this widget"""
  63 + if event.keysym == "BackSpace":
  64 + self.delete(self.index(tkinter.INSERT), tkinter.END)
  65 + self.position = self.index(tkinter.END)
  66 + if event.keysym == "Left":
  67 + if self.position < self.index(tkinter.END): # delete the selection
  68 + self.delete(self.position, tkinter.END)
  69 + else:
  70 + self.position = self.position-1 # delete one character
  71 + self.delete(self.position, tkinter.END)
  72 + if event.keysym == "Right":
  73 + self.position = self.index(tkinter.END) # go to end (no selection)
  74 + if event.keysym == "Down":
  75 + self.autocomplete(1) # cycle to next hit
  76 + if event.keysym == "Up":
  77 + self.autocomplete(-1) # cycle to previous hit
  78 + if len(event.keysym) == 1 or event.keysym in tkinter_umlauts:
  79 + self.autocomplete()
  80 +
  81 +class AutocompleteCombobox(ttk.Combobox):
  82 +
  83 + def set_completion_list(self, completion_list):
  84 + """Use our completion list as our drop down selection menu, arrows move through menu."""
  85 + self._completion_list = sorted(completion_list, key=str.lower) # Work with a sorted list
  86 + self._hits = []
  87 + self._hit_index = 0
  88 + self.position = 0
  89 + self.bind('<KeyRelease>', self.handle_keyrelease)
  90 + self['values'] = self._completion_list # Setup our popup menu
  91 +
  92 + def autocomplete(self, delta=0):
  93 + """autocomplete the Combobox, delta may be 0/1/-1 to cycle through possible hits"""
  94 + if delta: # need to delete selection otherwise we would fix the current position
  95 + self.delete(self.position, tkinter.END)
  96 + else: # set position to end so selection starts where textentry ended
  97 + self.position = len(self.get())
  98 + # collect hits
  99 + _hits = []
  100 + for element in self._completion_list:
  101 + if element.lower().startswith(self.get().lower()): # Match case insensitively
  102 + _hits.append(element)
  103 + # if we have a new hit list, keep this in mind
  104 + if _hits != self._hits:
  105 + self._hit_index = 0
  106 + self._hits=_hits
  107 + # only allow cycling if we are in a known hit list
  108 + if _hits == self._hits and self._hits:
  109 + self._hit_index = (self._hit_index + delta) % len(self._hits)
  110 + # now finally perform the auto completion
  111 + if self._hits:
  112 + self.delete(0,tkinter.END)
  113 + self.insert(0,self._hits[self._hit_index])
  114 + self.select_range(self.position,tkinter.END)
  115 +
  116 + def handle_keyrelease(self, event):
  117 + """event handler for the keyrelease event on this widget"""
  118 + if event.keysym == "BackSpace":
  119 + self.delete(self.index(tkinter.INSERT), tkinter.END)
  120 + self.position = self.index(tkinter.END)
  121 + if event.keysym == "Left":
  122 + if self.position < self.index(tkinter.END): # delete the selection
  123 + self.delete(self.position, tkinter.END)
  124 + else:
  125 + self.position = self.position-1 # delete one character
  126 + self.delete(self.position, tkinter.END)
  127 + if event.keysym == "Right":
  128 + self.position = self.index(tkinter.END) # go to end (no selection)
  129 + if len(event.keysym) == 1:
  130 + self.autocomplete()
  131 + # No need for up/down, we'll jump to the popup
  132 + # list at the position of the autocompletion
  133 +
  134 +def test(test_list):
  135 + """Run a mini application to test the AutocompleteEntry Widget."""
  136 + root = tkinter.Tk(className=' AutocompleteEntry demo')
  137 + entry = AutocompleteEntry(root)
  138 + entry.set_completion_list(test_list)
  139 + entry.pack()
  140 + entry.focus_set()
  141 + combo = AutocompleteCombobox(root)
  142 + combo.set_completion_list(test_list)
  143 + combo.pack()
  144 + combo.focus_set()
  145 + # I used a tiling WM with no controls, added a shortcut to quit
  146 + root.bind('<Control-Q>', lambda event=None: root.destroy())
  147 + root.bind('<Control-q>', lambda event=None: root.destroy())
  148 + root.mainloop()
  149 +
  150 +if __name__ == '__main__':
  151 + test_list = ('apple', 'banana', 'CranBerry', 'dogwood', 'alpha', 'Acorn', 'Anise' )
  152 + test(test_list)
xsd2tkform/ui/buttoncontainer.py 0 โ†’ 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +"""Frame for containing optional field buttons
  2 +"""
  3 +import tkinter as tk
  4 +
  5 +class ButtonContainer(tk.Frame):
  6 + def __init__(self, parent, *args, **kwargs):
  7 + #tk.Frame.__init__(self, parent, highlightbackground="magenta", highlightthickness=3, *args, **kwargs)
  8 + tk.Frame.__init__(self, parent, highlightbackground=None, highlightthickness=None, *args, **kwargs)
  9 +
  10 + self.buttons = []
  11 +
  12 + self.bind("<Configure>", self.update_layout)
  13 + def add_button(self, button):
  14 + self.buttons.append(button)
  15 + def update_layout(self, event):
  16 + pass
  17 + #print("Update layout {}".format(event))
  18 +
  19 +
  20 +
xsd2tkform/ui/choice.py
1 import tkinter as tk 1 import tkinter as tk
2 from tkinter import ttk 2 from tkinter import ttk
3 3
4 -from xsd2tkform.core.type import SimpleType, ComplexType 4 +from ..core.type import SimpleType, ComplexType
5 5
6 -from xsd2tkform.ui.simpletype import XSDSimpleTypeFrame  
7 -from xsd2tkform.ui.complextype import XSDComplexTypeFrame 6 +from .simpletype import XSDSimpleTypeFrame
  7 +from .complextype import XSDComplexTypeFrame
8 8
9 -from xsd2tkform.ui.field import XSDInputField 9 +from .field import XSDInputField
10 10
11 class ChoiceInput(XSDInputField): 11 class ChoiceInput(XSDInputField):
12 def __init__(self, parent, choice, schema, *args, **kwargs): 12 def __init__(self, parent, choice, schema, *args, **kwargs):
13 - tk.Frame.__init__(self, parent, *args, **kwargs) 13 + XSDInputField.__init__(self, parent, highlightbackground="green", highlightthickness=2, *args, **kwargs)
14 self.schema=schema 14 self.schema=schema
15 # get the choice types 15 # get the choice types
16 self.choice_inputs=[] 16 self.choice_inputs=[]
17 # get the list of choice type 17 # get the list of choice type
18 - choice_types = [t.type for t in choice.elements] 18 + self.choice_types = [t.type for t in choice.elements]
  19 + self.choice_names = [t.name for t in choice.elements]
19 # get occurence bounds for choices 20 # get occurence bounds for choices
20 #choice_occ_bounds = self.get_element_occurence_limits(item) 21 #choice_occ_bounds = self.get_element_occurence_limits(item)
21 choice_occ_bounds = choice.min_occurs, choice.max_occurs 22 choice_occ_bounds = choice.min_occurs, choice.max_occurs
@@ -23,7 +24,7 @@ class ChoiceInput(XSDInputField): @@ -23,7 +24,7 @@ class ChoiceInput(XSDInputField):
23 self.field_counts={} 24 self.field_counts={}
24 self.field_min_counts={} 25 self.field_min_counts={}
25 self.field_max_counts={} 26 self.field_max_counts={}
26 - for _type in choice_types: 27 + for _type in self.choice_types:
27 # TODO : check if bounds are correct, if choice type is present somewhere else in the type definition then the bounds are overwritten here, which can be bad 28 # TODO : check if bounds are correct, if choice type is present somewhere else in the type definition then the bounds are overwritten here, which can be bad
28 if _type in self.field_min_counts: 29 if _type in self.field_min_counts:
29 print("Not good if you see this") 30 print("Not good if you see this")
@@ -38,9 +39,13 @@ class ChoiceInput(XSDInputField): @@ -38,9 +39,13 @@ class ChoiceInput(XSDInputField):
38 choice_select_frame = tk.Frame(self) 39 choice_select_frame = tk.Frame(self)
39 40
40 # add a choice selector : combobox and button 41 # add a choice selector : combobox and button
41 - self.choice_type = ttk.Combobox(choice_select_frame, values=choice_types, state="readonly") 42 + #self.choice_type = ttk.Combobox(choice_select_frame, values=self.choice_types, state="readonly")
  43 + self.choice_type = ttk.Combobox(choice_select_frame, values=self.choice_names, state="readonly")
  44 +
42 self.choice_type.current(0) 45 self.choice_type.current(0)
43 - choice_add = tk.Button(choice_select_frame, text="Add", command=lambda: self.add_field()) 46 + from amda_xml_manager import add_image_file
  47 + self.add_img = tk.PhotoImage(file=add_image_file)
  48 + choice_add = tk.Button(choice_select_frame, image=self.add_img, command=lambda: self.add_field())
44 49
45 self.choice_type.pack(side=tk.LEFT, fill=tk.X, expand=1) 50 self.choice_type.pack(side=tk.LEFT, fill=tk.X, expand=1)
46 choice_add.pack(side=tk.RIGHT, expand=0) 51 choice_add.pack(side=tk.RIGHT, expand=0)
@@ -48,16 +53,40 @@ class ChoiceInput(XSDInputField): @@ -48,16 +53,40 @@ class ChoiceInput(XSDInputField):
48 ##choice_frame.pack(side=tk.TOP, fill=tk.X, expand=1) 53 ##choice_frame.pack(side=tk.TOP, fill=tk.X, expand=1)
49 #sequence_inputs.append(choice_inputs) 54 #sequence_inputs.append(choice_inputs)
50 #return choice_frame 55 #return choice_frame
51 - def add_field(self):  
52 - _type = self.choice_type.get() 56 + def has_type(self, t):
  57 + # TODO watch out
  58 + if t in self.choice_names:
  59 + return True
  60 + for _type in self.choice_types:
  61 + if t==_type or t==_type.split(":")[-1]:
  62 + return True
  63 + return False
  64 + def set_content(self, content, update_grid=True):
  65 + # get the name of the item
  66 + ct=content.tag.split("}")[-1]
  67 +
  68 + for c in self.choice_names:
  69 + #for c in self.choice_types:
  70 + ctt=c.split(":")[-1]
  71 + if ct==ctt:
  72 + self.choice_type.set(c)
  73 + self.add_field(content)
  74 +
  75 + #self.add_field(content)
  76 +
  77 + def add_field(self, content=None):
  78 + _name = self.choice_type.get()
  79 + #_type = self.choice_type.get()
  80 + _type = self.choice_types[self.choice_names.index(_name)]
53 81
54 # add the frame 82 # add the frame
55 - self.add_frame_by_type(_type)  
56 - def add_frame_by_type(self, t): 83 + self.add_frame_by_type(_type, content=content)
  84 + def add_frame_by_type(self, t, content=None):
57 # check if the maximum occurences of this field is achieved 85 # check if the maximum occurences of this field is achieved
58 86
59 if self.field_max_counts[t]=="unbounded": 87 if self.field_max_counts[t]=="unbounded":
60 - print("No limit on {} fields".format(t)) 88 + pass
  89 + #print("No limit on {} fields".format(t))
61 elif t in self.field_counts: 90 elif t in self.field_counts:
62 if self.get_field_count_by_type(t)==self.field_max_counts[t]: 91 if self.get_field_count_by_type(t)==self.field_max_counts[t]:
63 showerror(title="{} maximum occurences reached".format(t), message="Type {} supports a maximum of {} occurences of type {}".format(self.type, self.field_max_counts[t], t)) 92 showerror(title="{} maximum occurences reached".format(t), message="Type {} supports a maximum of {} occurences of type {}".format(self.type, self.field_max_counts[t], t))
@@ -67,27 +96,38 @@ class ChoiceInput(XSDInputField): @@ -67,27 +96,38 @@ class ChoiceInput(XSDInputField):
67 96
68 self.increment_field_count_by_type(t) 97 self.increment_field_count_by_type(t)
69 98
70 - f = self.get_frame_by_type(t, parent=self, delete_button=True) 99 + f = self.get_frame_by_type(t, parent=self, delete_button=True, content=content)
  100 + f.configure(highlightbackground="blue", highlightthickness=2)
71 f.pack(side=tk.TOP, fill=tk.X, expand=1) 101 f.pack(side=tk.TOP, fill=tk.X, expand=1)
72 - #self.choice_inputs.append(f.winfo_children()[0])  
73 - print("in choice_input append : {}".format(type(f)))  
74 self.choice_inputs.append(f) 102 self.choice_inputs.append(f)
75 #if container is not None: 103 #if container is not None:
76 # container.append(f.winfo_children()[0]) 104 # container.append(f.winfo_children()[0])
77 - def get_frame_by_type(self, t, parent=None, delete_button=False): 105 + #self.master.update_grid()
  106 +
  107 + def get_frame_by_type(self, t, parent=None, delete_button=False, content=None):
78 if parent is None: 108 if parent is None:
79 parent = self 109 parent = self
80 110
81 td=self.schema.get(t) 111 td=self.schema.get(t)
  112 + nn=self.choice_names[self.choice_types.index(t)]
  113 + print("IN CHOICE type={}, name={}".format(t, nn))
82 if isinstance(td, SimpleType):# in self.simple_types: 114 if isinstance(td, SimpleType):# in self.simple_types:
83 return XSDSimpleTypeFrame(t, parent=parent,\ 115 return XSDSimpleTypeFrame(t, parent=parent,\
  116 + name=nn,
84 schema=self.schema, 117 schema=self.schema,
85 - delete_button=delete_button) 118 + delete_button=delete_button,
  119 + content=content,
  120 + widget_config=self.widget_config,
  121 + on_delete=lambda x=t: self.delete_field(x))
86 elif isinstance(td, ComplexType): 122 elif isinstance(td, ComplexType):
87 return XSDComplexTypeFrame(t, parent=parent,\ 123 return XSDComplexTypeFrame(t, parent=parent,\
  124 + name=nn,
88 schema=self.schema, 125 schema=self.schema,
89 delete_button=delete_button, 126 delete_button=delete_button,
90 - collapsable=True) 127 + collapsable=True,
  128 + content=content,
  129 + widget_config=self.widget_config,
  130 + on_delete=lambda x=t: self.delete_field(x))
91 else: 131 else:
92 # TODO : add Group support 132 # TODO : add Group support
93 print("Group support not yet implemented") 133 print("Group support not yet implemented")
@@ -102,20 +142,10 @@ class ChoiceInput(XSDInputField): @@ -102,20 +142,10 @@ class ChoiceInput(XSDInputField):
102 def decrement_field_count_by_type(self, t): 142 def decrement_field_count_by_type(self, t):
103 self.field_counts[t]=self.get_field_count_by_type(t)-1 143 self.field_counts[t]=self.get_field_count_by_type(t)-1
104 144
105 - def delete_field(self, t, field, container=None):  
106 - field_dims = (field.winfo_width(), field.winfo_height())  
107 - field.destroy() 145 + def delete_field(self, t, field=None, container=None):
108 self.decrement_field_count_by_type(t) 146 self.decrement_field_count_by_type(t)
109 - current_scroll_region=self.master.master.bbox("all")  
110 - new_scrollregion= (current_scroll_region[0],  
111 - current_scroll_region[1],  
112 - current_scroll_region[2]-field_dims[0],  
113 - current_scroll_region[3]-field_dims[1])  
114 -  
115 - # WORKS  
116 - self.master.master.master.configure(scrollregion=new_scrollregion)  
117 -  
118 - 147 + self.choice_inputs=[w for w in self.choice_inputs if w.winfo_exists()]
  148 +
119 def add_content(self, root, content): 149 def add_content(self, root, content):
120 if isinstance(content, list): 150 if isinstance(content, list):
121 for item in content: 151 for item in content:
@@ -123,5 +153,6 @@ class ChoiceInput(XSDInputField): @@ -123,5 +153,6 @@ class ChoiceInput(XSDInputField):
123 else: 153 else:
124 if content is not None: 154 if content is not None:
125 root.append(content) 155 root.append(content)
126 - def get_content(self):  
127 - return [i.get_content(i) for i in self.choice_inputs] 156 + def get_content(self, nsmap=None, qname_attrs=None):
  157 + return [i.get_content(nsmap=nsmap, qname_attrs=qname_attrs) for i in self.choice_inputs]
  158 + #return [i.get_content(i, nsmap=nsmap, qname_attrs=qname_attrs) for i in self.choice_inputs]
xsd2tkform/ui/complextype.py
@@ -4,173 +4,488 @@ from tkinter.messagebox import showerror @@ -4,173 +4,488 @@ from tkinter.messagebox import showerror
4 4
5 from lxml import etree 5 from lxml import etree
6 6
7 -from xsd2tkform.core.type import SimpleType, ComplexType  
8 -from xsd2tkform.core.element import Element  
9 -from xsd2tkform.core.choice import Choice 7 +from ..core.type import SimpleType, ComplexType
  8 +from ..core.element import Element, AnyElement
  9 +from ..core.choice import Choice
10 10
11 -from xsd2tkform.ui.simpletype import XSDSimpleTypeFrame  
12 -from xsd2tkform.ui.tooltip import ToolTip 11 +from .simpletype import XSDSimpleTypeFrame, XSDAttributeFrame, XSDAnyInputFrame
  12 +from .tooltip import ToolTip
13 13
14 from .field import XSDInputField 14 from .field import XSDInputField
15 15
  16 +from amda_xml_manager import add_image_file, delete_image_file, collapse_image_file, expand_image_file
  17 +
  18 +from .attributeframe import AttributeFrame
  19 +
16 class XSDComplexTypeFrame(XSDInputField): 20 class XSDComplexTypeFrame(XSDInputField):
17 - def __init__(self, typename, parent=None, schema=None, delete_button=False, collapsable=False, *args, **kwargs): 21 + def __init__(self, typename, parent=None, schema=None, delete_button=False, collapsable=False, filename=None, content=None, name=None, widget_config={}, typedef=None, *args, **kwargs):
18 XSDInputField.__init__(self, parent, borderwidth=1,\ 22 XSDInputField.__init__(self, parent, borderwidth=1,\
19 highlightbackground="black",\ 23 highlightbackground="black",\
20 - highlightthickness=2,\ 24 + highlightthickness=1,\
  25 + widget_config=widget_config,\
21 *args, **kwargs) 26 *args, **kwargs)
  27 + # flag indicating that the fields content has been set
  28 + self.content_has_been_set = False
  29 +
  30 + self.grid_columnconfigure(0, weight=1)
  31 + #self.grid_columnconfigure(1, weight=1)
  32 +
  33 + # collapsed state
  34 + self.collapsed = False
  35 +
22 # store type of the current field 36 # store type of the current field
  37 + self.name = name
23 self.type = typename 38 self.type = typename
24 self.schema = schema 39 self.schema = schema
25 - 40 +
  41 + # keep reference to the label
  42 + self.label=None
26 # store the number of fields of each type, and min and max 43 # store the number of fields of each type, and min and max
27 self.field_counts={} 44 self.field_counts={}
28 self.field_min_counts={} 45 self.field_min_counts={}
29 self.field_max_counts={} 46 self.field_max_counts={}
30 47
31 - # storage for optional frame  
32 - self.subframes=[]  
33 -  
34 # frame for containing label, collapse and delete buttons 48 # frame for containing label, collapse and delete buttons
35 - lf = tk.Frame(self)  
36 - self.label=tk.Label(lf, text=self.sanitize_type(typename), font="bold")  
37 - self.label.pack(side=tk.LEFT, fill=tk.X, expand=1)  
38 - 49 + self.header_frame = self.get_header_frame()
39 50
  51 + self.delete_img = None
40 if delete_button: 52 if delete_button:
41 - tk.Button(lf, text="X", command=self.delete).pack(side=tk.RIGHT) 53 + #tk.Button(self.header_frame, text="X", command=self.delete).pack(side=tk.RIGHT)
  54 + self.delete_img = tk.PhotoImage(file=delete_image_file)
  55 + tk.Button(self.header_frame, image=self.delete_img, command=self.delete).pack(side=tk.RIGHT)
  56 +
  57 +
42 # add a collapse button 58 # add a collapse button
  59 + self.expand_img = None
  60 + self.collapse_img = None
43 self.collapse_button=None 61 self.collapse_button=None
44 if collapsable: 62 if collapsable:
45 - self.collapse_button = tk.Button(lf, text="_", command=self.collapse) 63 + self.expand_img = tk.PhotoImage(file=expand_image_file)
  64 + self.collapse_img = tk.PhotoImage(file=collapse_image_file)
  65 + #self.collapse_button = tk.Button(self.header_frame, text="_", command=self.collapse)
  66 + self.collapse_button = tk.Button(self.header_frame, image=self.collapse_img, command=self.collapse)
46 self.collapse_button.pack(side=tk.RIGHT) 67 self.collapse_button.pack(side=tk.RIGHT)
47 68
48 -  
49 - lf.pack(side=tk.TOP, fill=tk.X, expand=1)  
50 69
51 self.inputs=[] # list of inputs used for constructing the tree 70 self.inputs=[] # list of inputs used for constructing the tree
52 71
  72 + # option button frame
  73 + #self.option_button_frame = tk.Frame(self)
  74 + from .buttoncontainer import ButtonContainer
  75 + self.option_button_frame2 = ButtonContainer(self)
  76 +
  77 +
  78 +
  79 + #tk.Label(self.option_button_frame, text="Add :").grid(row=0, column=0)
  80 + tk.Label(self.option_button_frame2, text="Add :").grid(row=0, column=0)
  81 +
  82 +
53 # get type definition 83 # get type definition
54 - self.typedef = schema.get(typename) 84 + if typedef is None:
  85 + self.typedef = schema.get(typename)
  86 + else:
  87 + self.typedef = typedef
  88 + #from ..core.element import Element
  89 + if isinstance(typedef, Element):
  90 + if typedef.typedef is not None:
  91 + self.typedef = typedef.typedef
55 if self.typedef is None: 92 if self.typedef is None:
56 raise Exception("Typename {} not found".format(typename)) 93 raise Exception("Typename {} not found".format(typename))
  94 +
  95 + # attribute container
  96 + if len(self.typedef.attributes):
  97 + self.attribute_frame = AttributeFrame(self, self.typedef.attributes)
  98 + self.attribute_frame.collapse()
  99 + else:
  100 + self.attribute_frame = None
  101 + # field container
  102 + #self.field_frame = tk.Frame(self, highlightbackground="red", highlightthickness=2)
  103 + self.field_frame = tk.Frame(self, highlightbackground=None, highlightthickness=None)
  104 + self.field_frame.grid_columnconfigure(0, weight=0)
  105 + self.field_frame.grid_columnconfigure(1, weight=1)
  106 +
  107 +
  108 +
57 self.set_tooltip() 109 self.set_tooltip()
58 - sequence_inputs=[] 110 +
  111 + # grid content
  112 + #c=0
  113 + #self.next_row = 1
  114 + option_button_column=1
  115 + # attributes first
  116 +
59 for item in self.typedef.sequence.items: 117 for item in self.typedef.sequence.items:
  118 + #print("item type {} {}".format(type(item), type(item.typedef)))
60 if isinstance(item, Element): 119 if isinstance(item, Element):
  120 + if item.ref is not None:
  121 + # there can be only ONE
  122 + refered_element = self.schema.find_ref(item.ref)
  123 + print("Refered element : {}".format(refered_element))
  124 + if refered_element.abstract:
  125 + # find substitutions
  126 + substitutions = self.schema.find_substitutions(refered_element.name)
  127 + print("Substitutions : {}".format(substitutions))
  128 + print("\tnames : {}".format([s.name for s in substitutions]))
  129 + if len(substitutions)==1:
  130 + item = substitutions[0]
  131 + else:
  132 + #create a choice item
  133 + item = Choice()
  134 + for s in substitutions:
  135 + item.add(s)
  136 + from .choice import ChoiceInput
  137 + chh = ChoiceInput(self.field_frame, item, self.schema, widget_config=self.widget_config)
  138 + #self.grid_contents.append(chh)
  139 + self.inputs.append(chh)
  140 + continue
  141 +
61 self.set_occurrence_bounds(item) 142 self.set_occurrence_bounds(item)
62 - element_field = self.get_element_field(item, sequence_inputs)  
63 - element_field.pack(side=tk.TOP, fill=tk.X, expand=1) 143 +
  144 + for element_field in self.get_element_field(item, self.inputs):
  145 + # add to grid contents
  146 + #self.grid_contents.append(element_field)
  147 +
  148 + from .optional import OptionalInput
  149 + if isinstance(element_field, OptionalInput):
  150 + # pack the button at the bottom of the field
  151 + but = element_field.get_add_button(parent = self.option_button_frame2)
  152 + self.option_button_frame2.add_button(but)
  153 + but.grid(row=0, column=option_button_column)
  154 + option_button_column+=1
  155 +
64 elif isinstance(item, Choice): 156 elif isinstance(item, Choice):
65 - from xsd2tkform.ui.choice import ChoiceInput  
66 - chh = ChoiceInput(self, item, self.schema)  
67 - chh.pack(side=tk.TOP, fill=tk.X, expand=1)  
68 - sequence_inputs.append(chh) 157 + from .choice import ChoiceInput
  158 + chh = ChoiceInput(self.field_frame, item, self.schema, widget_config=self.widget_config)
  159 + #self.grid_contents.append(chh)
  160 + self.inputs.append(chh)
  161 + elif isinstance(item, AnyElement):
  162 + from .adaptivetextentry import AdaptiveHeightText
  163 + r=XSDAnyInputFrame(parent=self.field_frame,\
  164 + input_widget_type=lambda p: AdaptiveHeightText(p, height=3))
  165 + self.inputs.append(r)
  166 +
69 else: 167 else:
70 # TODO : add groups 168 # TODO : add groups
71 print("Group support not implemeneted yet") 169 print("Group support not implemeneted yet")
72 - self.inputs.append(sequence_inputs)  
73 - def collapse(self):  
74 - # hide all inputs  
75 - for i in self.inputs:  
76 - if isinstance(i, list):  
77 - for e in i:  
78 - e.pack_forget() 170 +
  171 + #c+=1
  172 +
  173 + row=1
  174 + if self.attribute_frame is not None:
  175 + self.attribute_frame.grid(row=1, sticky=tk.EW)
  176 + row+=1
  177 + self.field_frame.grid(row=row, sticky=tk.EW)
  178 +
  179 +
  180 +
  181 + # if filename or content was given
  182 + if filename is not None:
  183 + print("Setting content from {}".format(filename))
  184 + self.set_content(filename=filename)
  185 + if content is not None:
  186 + self.set_content(content=content)
  187 +
  188 + def get_header_frame(self):
  189 + hf=tk.Frame(self)
  190 + if self.name is None:
  191 + label_text=self.sanitize_type(self.type)
  192 + else:
  193 + label_text=self.name
  194 +
  195 + #self.header_label=tk.Label(hf, text=label_text, font="bold", anchor="w")
  196 + self.header_label=tk.Label(hf, text=label_text, font=("Helvetica",11,"bold"), anchor="w")
  197 +
  198 + self.header_label.pack(side=tk.LEFT, fill=tk.X, expand=1)
  199 +
  200 + self.header_label.bind("<Button-1>", self.collapse)
  201 + return hf
  202 +
  203 +
  204 + def clear_grid(self):
  205 + for child in self.field_frame.winfo_children():
  206 + # delete labels
  207 + if isinstance(child, tk.Label):
  208 + child.destroy()
  209 + elif isinstance(child, ttk.Separator):
  210 + child.destroy()
79 else: 211 else:
80 - i.pack_forget() 212 + child.grid_forget()
  213 + def get_label(self, parent):
  214 + print(" ------> ComplexType name={}, type={}".format(self.name, self.type))
  215 + label_text=self.sanitize_type(self.type)
  216 + if isinstance(self.typedef, Element):
  217 + label_text=self.typedef.name
  218 + if self.name is not None:
  219 + label_text=self.name
  220 +
  221 + self.label = tk.Label(parent, text=label_text+" :")
  222 + self.set_tooltip()
  223 + return self.label
  224 + def get_fields(self):
  225 + from .optional import OptionalInput
  226 + l=[]
  227 + #for item in self.grid_contents:
  228 + for item in self.inputs:
  229 + if isinstance(item, OptionalInput):
  230 + l+=[w for w in item.get_fields()]
  231 + else:
  232 + l.append(item)
  233 + return l
  234 + def remove_separators(self):
  235 + for child in self.field_frame.winfo_children():
  236 + if isinstance(child, ttk.Separator):
  237 + child.destroy()
  238 + def count_grid_contents(self):
  239 + c={}
  240 + for child in self.field_frame.winfo_children():
  241 + if type(child) in c:
  242 + c[type(child)]+=1
  243 + else:
  244 + c[type(child)]=1
  245 + for k in c:
  246 + print("{} : {}".format(k, c[k]))
  247 +
  248 + def update_attribute_grid(self):
  249 + if self.attribute_frame is not None:
  250 + self.attribute_frame.update_grid()
  251 +
  252 + def update_grid(self):
  253 + print("update_grid name={}, type={}, self.type={}".format(self.name, self.type, type(self)))
  254 + from .optional import OptionalInput
  255 + from .choice import ChoiceInput
  256 +
  257 + self.remove_separators()
  258 + # attribute grid update
  259 + self.update_attribute_grid()
  260 + # get fields
  261 + new_fields = self.get_fields()
  262 +
  263 + # add the contents of the grid
  264 + self.header_frame.grid(row=0, columnspan=2, sticky=tk.EW)
  265 + if not self.collapsed:
  266 + #ttk.Separator(self, orient="horizontal").grid(row=1, columnspan=2, sticky=tk.EW)
  267 + row=2
  268 + for f in new_fields:
  269 + # add the input field
  270 + grid_info = f.grid_info()
  271 + if "row" in grid_info:
  272 + if grid_info["row"]!=row:
  273 + f.grid_forget() # remove the label too
  274 + f.grid(row=row, column=1, sticky=tk.EW)
  275 + else:
  276 + f.grid(row=row, column=1, sticky=tk.EW)
  277 +
  278 + # check the label
  279 + if not isinstance(f, ChoiceInput):
  280 + if f.label is None:
  281 + f.get_label(self.field_frame).grid(row=row, column=0, sticky=tk.NW)
  282 + else:
  283 + label_grid_info = f.label.grid_info()
  284 + if "row" in label_grid_info:
  285 + if label_grid_info["row"]!=row:
  286 + f.label.grid_forget()
  287 + f.label.grid(row=row, column=0, sticky=tk.NW)
  288 + else:
  289 + f.label.grid(row=row, column=0, sticky=tk.NW)
  290 + row+=1
  291 + ttk.Separator(self.field_frame, orient="horizontal").grid(row=row, columnspan=2, sticky=tk.EW)
  292 + row+=1
  293 + n=len(list(self.option_button_frame2.winfo_children()))
  294 + if n>1:
  295 + self.option_button_frame2.grid(row=row, columnspan=2, sticky=tk.EW)
  296 +
  297 + def grid(self, *args, **kwargs):
  298 + self.update_grid()
  299 + return super().grid(pady=1,*args, **kwargs)
  300 + def pack(self, *args, **kwargs):
  301 + self.update_grid()
  302 + return super().pack(pady=1, *args, **kwargs)
  303 + if isinstance(self.master, XSDComplexTypeFrame):
  304 + #super().pack(*args, padx=(100,0), **kwargs)
  305 + super().pack(*args, **kwargs)
  306 +
  307 + else:
  308 + super().pack(*args, **kwargs)
  309 + def iter_inputs(self):
  310 + for e in self.inputs:
  311 + if isinstance(e, list):
  312 + for el in e:
  313 + yield el
  314 + else:
  315 + yield e
  316 + def set_content(self, filename=None, content=None, update_grid=True):
  317 + if filename is not None:
  318 + root = etree.parse(filename).getroot()
  319 + return self.set_content(content=root, update_grid=update_grid)
  320 + if content is not None:
  321 + # set attribute content
  322 + for att in content.attrib:
  323 + self.attribute_frame.set_attribute_content(att, content.attrib[att])
  324 + # go over children of content
  325 + for child in content:
  326 + if isinstance(child, etree._Comment):
  327 + continue
  328 + self.set_child_content(child, update_grid=True)
  329 + self.content_has_been_set=True
  330 + if update_grid:
  331 + self.update_grid()
  332 + def is_full(self):
  333 + # check if this field is full : all mandatory fields are filled
  334 + return self.content_has_been_set
  335 + def set_child_content(self, child, update_grid=True):
  336 + ct=child.tag.split("}")[-1]
  337 + ctype = self.schema.get(ct)
  338 + from ..core.element import Element
  339 + for minp in self.iter_inputs():
  340 + if isinstance(minp, XSDSimpleTypeFrame) or isinstance(minp, XSDComplexTypeFrame):
  341 + if minp.name is None:
  342 + ctt=minp.type.split(":")[-1]
  343 + else:
  344 + ctt=minp.name
  345 + if ct==ctt:
  346 + # if the field is already filled skip
  347 + if minp.is_full():
  348 + continue
  349 + minp.set_content(content=child, update_grid=True)
  350 + if isinstance(minp, XSDComplexTypeFrame):
  351 + minp.collapse()
  352 + return
  353 + from .optional import OptionalInput
  354 + if isinstance(minp, OptionalInput):
  355 + if minp.is_full():
  356 + continue
  357 + ctt=minp.type.split(":")[-1]
  358 + if ct==ctt:
  359 + minp.set_content(content=child, update_grid=True)
  360 + return
  361 + from .choice import ChoiceInput
  362 + if isinstance(minp, ChoiceInput):
  363 + if minp.has_type(ct):
  364 + minp.set_content(content=child, update_grid=True)
  365 + return
  366 +
  367 + def collapse(self, event=None):
  368 + self.collapsed = True
  369 + if self.attribute_frame is not None:
  370 + self.attribute_frame.grid_remove()
  371 + for item in self.field_frame.winfo_children():
  372 + if isinstance(item, tk.Label):
  373 + item.grid_remove()
  374 + if isinstance(item, XSDInputField):
  375 + item.grid_remove()
  376 + # collapse the optional button frame
  377 + self.option_button_frame2.grid_remove()
  378 +
81 # change button action to expand 379 # change button action to expand
82 - self.collapse_button.configure(text="+", command=self.expand) 380 + self.collapse_button.configure(image=self.expand_img, command=self.expand)
83 # change the label text 381 # change the label text
84 in_val=[] 382 in_val=[]
85 for i in self.inputs: 383 for i in self.inputs:
86 - if isinstance(i, list):  
87 - for e in i:  
88 - if isinstance(e, XSDSimpleTypeFrame):  
89 - in_val+=["{}:{}".format(self.sanitize_type(e.type),e.get_value())]  
90 - new_lab = "{}({})".format(self.sanitize_type(self.type), 384 + if isinstance(i, XSDSimpleTypeFrame):
  385 + tval = str(i.get_value())
  386 + if len(tval)>100:
  387 + tval=tval[:30]+"..."
  388 + in_val+=["{}:{}".format(self.sanitize_type(i.type),tval)]
  389 + l=self.sanitize_type(self.type)
  390 + if self.name is not None:
  391 + l=self.name
  392 + new_lab = "{}({})".format(l,
91 ",".join(in_val)) 393 ",".join(in_val))
92 - self.label.configure(text=new_lab)  
93 - def expand(self):  
94 - # hide all inputs  
95 - for i in self.inputs:  
96 - if isinstance(i, list):  
97 - for e in i:  
98 - e.pack(side=tk.TOP, fill=tk.X, expand=1)  
99 - else:  
100 - i.pack(side=tk.TOP, fill=tk.X, expand=1) 394 + w=int(self.winfo_width()*.8)
  395 + self.header_label.configure(text=new_lab, wraplength=w, justify="left")
  396 + self.header_label.bind("<Button-1>", self.expand)
  397 + def expand(self, event=None):
  398 + #self.update_grid()
  399 + from .optional import OptionalInput
  400 + self.collapsed = False
  401 + if self.attribute_frame is not None:
  402 + self.attribute_frame.grid()
  403 + for item in self.field_frame.winfo_children():
  404 + if isinstance(item, tk.Label):
  405 + item.grid()
  406 + if isinstance(item, XSDInputField):
  407 + if isinstance(item, OptionalInput):
  408 + continue
  409 + item.grid()
  410 +
  411 +
  412 + # option button frame
  413 + #self.option_button_frame.pack(side=tk.BOTTOM, fill=tk.X, expand=1)
  414 + self.option_button_frame2.grid()
  415 +
101 # change button action to collapse 416 # change button action to collapse
102 - self.collapse_button.configure(text="_", command=self.collapse) 417 + self.collapse_button.configure(image=self.collapse_img, command=self.collapse)
103 # set original lable 418 # set original lable
104 - self.label.configure(text=self.sanitize_type(self.type)) 419 + l=self.sanitize_type(self.type)
  420 + if self.name is not None:
  421 + l=self.name
  422 + self.header_label.configure(text=l, justify="center")
  423 + self.header_label.bind("<Button-1>", self.collapse)
  424 + self.update_grid()
  425 +
105 def delete(self): 426 def delete(self):
106 - self.master.decrement_field_count_by_type(self.type) 427 + #self.master.decrement_field_count_by_type(self.type)
107 self.destroy() 428 self.destroy()
108 - def get_choice_field(self, item, sequence_inputs):  
109 - choice_inputs=[]  
110 - # get the list of choice type  
111 - choice_types = [t.type for t in item.elements]  
112 - # get occurence bounds for choices  
113 - #choice_occ_bounds = self.get_element_occurence_limits(item)  
114 - choice_occ_bounds = item.min_occurs, item.max_occurs  
115 - # set those bounds for all types  
116 - for _type in choice_types:  
117 - # TODO : check if bounds are correct, if choice type is present somewhere else in the type definition then the bounds are overwritten here, which can be bad  
118 - if _type in self.field_min_counts:  
119 - print("Not good if you see this")  
120 - if _type in self.field_max_counts:  
121 - print("Not good if you see this")  
122 - self.field_min_counts[_type]=choice_occ_bounds[0]  
123 - self.field_max_counts[_type]=choice_occ_bounds[1]  
124 - # frame for storing the selector and choices  
125 - choice_frame = tk.Frame(self)  
126 - # create a frame to store the choice selector  
127 - choice_select_frame = tk.Frame(choice_frame) 429 + if self.on_delete is not None:
  430 + self.on_delete()
  431 + if self.label is not None:
  432 + self.label.destroy()
128 433
129 - # add a choice selector : combobox and button  
130 - choice_type = ttk.Combobox(choice_select_frame, values=choice_types, state="readonly")  
131 - choice_type.current(0)  
132 - choice_add = tk.Button(choice_select_frame, text="Add", command=lambda w=choice_type, frame=choice_frame, container=choice_inputs: self.add_frame_by_type(w.get(), frame, container))  
133 -  
134 - choice_type.pack(side=tk.LEFT, fill=tk.X, expand=1)  
135 - choice_add.pack(side=tk.RIGHT, expand=0)  
136 - choice_select_frame.pack(side=tk.TOP, fill=tk.X, expand=1)  
137 - ##choice_frame.pack(side=tk.TOP, fill=tk.X, expand=1)  
138 - sequence_inputs.append(choice_inputs)  
139 - return choice_frame  
140 434
141 def get_element_field(self, item, sequence_inputs): 435 def get_element_field(self, item, sequence_inputs):
142 - if item.min_occurs==0:  
143 - # optional field, add a frame to contain the eventual fields, with a button  
144 - b_frame= tk.Frame(self)  
145 - b=tk.Button(b_frame, text="Add {}".format(item.type), command=lambda t=item.type, frame=b_frame, container=sequence_inputs: self.add_frame_by_type(t, frame, container))  
146 - # add tooltip  
147 - doc_str = self.get_type_docstring(item.type)  
148 - if len(doc_str):  
149 - ttt = ToolTip(b, doc_str)  
150 - b.pack(side=tk.TOP, fill=tk.X, expand=1) 436 + from .optional import OptionalInput
  437 + if isinstance(item, AnyElement):
  438 + of = self.get_frame_by_type(item)
  439 + sequence_inputs.append(of) # store the field for later use
  440 + yield of
151 441
  442 + if item.min_occurs==0:
152 # temp 443 # temp
153 - from xsd2tkform.ui.optional import OptionalInput  
154 bounds=(self.field_min_counts[item.type], self.field_max_counts[item.type]) 444 bounds=(self.field_min_counts[item.type], self.field_max_counts[item.type])
155 - of=OptionalInput(self, item, self.schema, bounds=bounds)  
156 - of.pack(side=tk.TOP, fill=tk.X, expand=1) 445 + of=OptionalInput(self.field_frame, item, self.schema, bounds=bounds, widget_config=self.widget_config, on_add_field=self.update_grid,\
  446 + on_delete_field=lambda t=item.type: self.decrement_field_count_by_type(t)\
  447 + )
  448 +
157 sequence_inputs.append(of) 449 sequence_inputs.append(of)
158 - return of  
159 - return b_frame 450 + yield of
160 else: 451 else:
161 - # mandatory field  
162 - f = self.get_frame_by_type(item.type)  
163 - sequence_inputs.append(f) # store the field for later use  
164 - return f 452 + # yield mandatory items
  453 + for i in range(item.min_occurs):
  454 + of = self.get_frame_by_type(item)
  455 + if of is None:
  456 + continue
  457 + sequence_inputs.append(of) # store the field for later use
  458 + yield of
  459 + # get optional bounds
  460 + bounds=[0, self.field_max_counts[item.type]]
  461 + if bounds[1]!="unbounded":
  462 + bounds[1]=bounds[1]-item.min_occurs
  463 + a = (bounds[1]=="unbounded")
  464 + b = False
  465 + if not a:
  466 + b = bounds[1]
  467 +
  468 + if a or b:
  469 + # yield optional items
  470 + of=OptionalInput(self.field_frame, item, self.schema, bounds=tuple(bounds), widget_config=self.widget_config, on_add_field=self.update_grid, \
  471 + on_delete_field=lambda t=item.type: self.decrement_field_count_by_type(t)\
  472 + )
  473 + sequence_inputs.append(of)
  474 + yield of
  475 +
165 476
166 def set_occurrence_bounds(self, item): 477 def set_occurrence_bounds(self, item):
167 self.field_min_counts[item.type]=item.min_occurs 478 self.field_min_counts[item.type]=item.min_occurs
168 self.field_max_counts[item.type]=item.max_occurs 479 self.field_max_counts[item.type]=item.max_occurs
169 480
170 def set_tooltip(self): 481 def set_tooltip(self):
  482 + if self.label is None or self.typedef is None:
  483 + return
  484 + if self.typedef.annotation is None:
  485 + return
171 if len(self.typedef.annotation.documentation): 486 if len(self.typedef.annotation.documentation):
172 langs = [k for k in self.typedef.annotation.documentation] 487 langs = [k for k in self.typedef.annotation.documentation]
173 - tt = ToolTip(self.label, self.typedef.annotation.documentation[langs[0]]) 488 + tt = ToolTip(self.header_label, self.typedef.annotation.documentation[langs[0]])
174 489
175 def get_type_docstring(self, t): 490 def get_type_docstring(self, t):
176 td=self.schema.get(t) 491 td=self.schema.get(t)
@@ -209,8 +524,6 @@ class XSDComplexTypeFrame(XSDInputField): @@ -209,8 +524,6 @@ class XSDComplexTypeFrame(XSDInputField):
209 def increment_field_count_by_type(self, t): 524 def increment_field_count_by_type(self, t):
210 self.field_counts[t]=self.get_field_count_by_type(t)+1 525 self.field_counts[t]=self.get_field_count_by_type(t)+1
211 def decrement_field_count_by_type(self, t): 526 def decrement_field_count_by_type(self, t):
212 - print("Current {} field count {}".format(t, self.get_field_count_by_type(t)))  
213 - print([k for k in self.field_counts])  
214 self.field_counts[t]=self.get_field_count_by_type(t)-1 527 self.field_counts[t]=self.get_field_count_by_type(t)-1
215 def delete_field(self, t, field, container=None): 528 def delete_field(self, t, field, container=None):
216 field_dims = (field.winfo_width(), field.winfo_height()) 529 field_dims = (field.winfo_width(), field.winfo_height())
@@ -226,47 +539,64 @@ class XSDComplexTypeFrame(XSDInputField): @@ -226,47 +539,64 @@ class XSDComplexTypeFrame(XSDInputField):
226 self.master.master.configure(scrollregion=new_scrollregion) 539 self.master.master.configure(scrollregion=new_scrollregion)
227 540
228 541
229 - def add_frame_by_type(self, t, frame=None, container=None):  
230 - print("in add frame by type : " , t)  
231 - # check if the maximum occurences of this field is achieved  
232 -  
233 - if self.field_max_counts[t]=="unbounded":  
234 - print("No limit on {} fields".format(t))  
235 - elif t in self.field_counts:  
236 - if self.get_field_count_by_type(t)==self.field_max_counts[t]:  
237 - showerror(title="{} maximum occurences reached".format(t), message="Type {} supports a maximum of {} occurences of type {}".format(self.type, self.field_max_counts[t], t))  
238 - return  
239 - else:  
240 - pass  
241 -  
242 - self.increment_field_count_by_type(t)  
243 -  
244 - if frame is None:  
245 - f = self.get_frame_by_type(t, delete_button=True)  
246 - f.pack(side=tk.TOP, fill=tk.X, expand=1)  
247 - else:  
248 - f = self.get_frame_by_type(t, parent=frame, delete_button=True)  
249 - f.pack(side=tk.TOP, fill=tk.X, expand=1)  
250 - if container is not None:  
251 - container.append(f)  
252 - #container.append(f.winfo_children()[0])  
253 def get_frame_by_type(self, t, parent=None, delete_button=False): 542 def get_frame_by_type(self, t, parent=None, delete_button=False):
  543 + #from ..core.type import SimpleType
254 if parent is None: 544 if parent is None:
255 - parent = self  
256 -  
257 - td=self.schema.get(t) 545 + parent = self.field_frame
  546 + if isinstance(t, AnyElement):
  547 + from .adaptivetextentry import AdaptiveHeightText
  548 + return XSDAnyInputFrame(parent=parent,\
  549 + input_widget=AdaptiveHeightText)
  550 +
  551 +
  552 + if t.type is None:
  553 + td=self.schema.get(t.name)
  554 + else:
  555 + td=self.schema.get(t.type)
  556 + if td is None:
  557 + # if type is native type
  558 + if t.type.split(":")[-1]=="string":
  559 + # return a SimpleType object
  560 + from ..core.restriction import Restriction
  561 + #from ..core.type import SimpleType
  562 + td=SimpleType(t.name, restriction=Restriction(base="string"))
  563 + return XSDSimpleTypeFrame(t.name, name=t.name, parent=parent,\
  564 + schema=self.schema, delete_button=delete_button,\
  565 + widget_config=self.widget_config,\
  566 + typedef=td)
  567 +
258 if isinstance(td, SimpleType):# in self.simple_types: 568 if isinstance(td, SimpleType):# in self.simple_types:
259 - return XSDSimpleTypeFrame(t, parent=parent,\ 569 + return XSDSimpleTypeFrame(t.type, name=t.name, parent=parent,\
260 schema=self.schema, 570 schema=self.schema,
261 - delete_button=delete_button) 571 + delete_button=delete_button,
  572 + widget_config=self.widget_config)
262 elif isinstance(td, ComplexType): 573 elif isinstance(td, ComplexType):
263 - return XSDComplexTypeFrame(t, parent=parent,\ 574 + return XSDComplexTypeFrame(t.type, name=t.name, parent=parent,\
264 schema=self.schema, 575 schema=self.schema,
265 delete_button=delete_button, 576 delete_button=delete_button,
266 - collapsable=True) 577 + collapsable=True,
  578 + widget_config=self.widget_config)
267 else: 579 else:
268 # TODO : add Group support 580 # TODO : add Group support
269 print("Group support not yet implemented") 581 print("Group support not yet implemented")
  582 + #if td.typedef is None:
  583 + # return None
  584 + if td.ref is not None:
  585 + return None
  586 + if isinstance(td.typedef, SimpleType):
  587 + return XSDSimpleTypeFrame(t.type, name=t.name, parent=parent,\
  588 + schema=self.schema,
  589 + delete_button=delete_button,
  590 + widget_config=self.widget_config, typedef=td.typedef)
  591 +
  592 +
  593 + return XSDComplexTypeFrame(td.type, name=td.name, parent=parent,\
  594 + schema=self.schema,
  595 + delete_button=delete_button,
  596 + collapsable=True,
  597 + widget_config=self.widget_config,
  598 + typedef=td.typedef)
  599 +
270 def get_value(self): 600 def get_value(self):
271 return "" 601 return ""
272 def add_content(self, root, content): 602 def add_content(self, root, content):
@@ -276,31 +606,46 @@ class XSDComplexTypeFrame(XSDInputField): @@ -276,31 +606,46 @@ class XSDComplexTypeFrame(XSDInputField):
276 else: 606 else:
277 if content is not None: 607 if content is not None:
278 root.append(content) 608 root.append(content)
279 - def get_content(self, obj=None): 609 + def get_attribute_values(self):
  610 + if self.attribute_frame is None:
  611 + return {}
  612 + return self.attribute_frame.get_attribute_values()
  613 + def get_content(self, obj=None, nsmap=None, qname_attrs=None):
280 if obj is not None: 614 if obj is not None:
281 - from xsd2tkform.ui.choice import ChoiceInput 615 + from .choice import ChoiceInput
282 if isinstance(obj, list): 616 if isinstance(obj, list):
283 - return [self.get_content(i) for i in obj]  
284 - if isinstance(obj, XSDSimpleTypeFrame): 617 + return [self.get_content(i, nsmap=nsmap) for i in obj]
  618 + if isinstance(obj, XSDAnyInputFrame):
285 if obj.winfo_exists(): 619 if obj.winfo_exists():
286 return obj.get_content() 620 return obj.get_content()
  621 + if isinstance(obj, XSDSimpleTypeFrame):
  622 + if obj.winfo_exists():
  623 + return obj.get_content(nsmap=nsmap)
287 if isinstance(obj, XSDComplexTypeFrame): 624 if isinstance(obj, XSDComplexTypeFrame):
288 if obj.winfo_exists(): 625 if obj.winfo_exists():
289 - return obj.get_content() 626 + return obj.get_content(nsmap=nsmap)
290 if isinstance(obj, ChoiceInput): 627 if isinstance(obj, ChoiceInput):
291 if obj.winfo_exists(): 628 if obj.winfo_exists():
292 - return obj.get_content()  
293 - from xsd2tkform.ui.optional import OptionalInput 629 + return obj.get_content(nsmap=nsmap)
  630 + from .optional import OptionalInput
294 if isinstance(obj, OptionalInput): 631 if isinstance(obj, OptionalInput):
295 if obj.winfo_exists(): 632 if obj.winfo_exists():
296 - return obj.get_content() 633 + return obj.get_content(nsmap=nsmap)
297 634
298 return 635 return
299 636
300 -  
301 - root = etree.Element(self.sanitize_type(self.type)) 637 + if nsmap is None:
  638 + root = etree.Element(self.sanitize_type(self.type))
  639 + else:
  640 + root = etree.Element(self.sanitize_type(self.type), qname_attrs, nsmap=nsmap)
  641 + attrib_values=self.get_attribute_values()
  642 + for k in attrib_values:
  643 + root.set(k, attrib_values[k])
302 # returns tree type 644 # returns tree type
303 - for c in self.get_content(self.inputs): 645 + for c in self.get_content(self.inputs, nsmap=nsmap):
  646 + if isinstance(c, str):
  647 + root.text=c
  648 + continue
304 self.add_content(root, c) 649 self.add_content(root, c)
305 return root 650 return root
306 651
xsd2tkform/ui/datetime_selector.py
@@ -31,5 +31,27 @@ class DatetimeEntry(tk.Frame): @@ -31,5 +31,27 @@ class DatetimeEntry(tk.Frame):
31 def get(self): 31 def get(self):
32 # return time value 32 # return time value
33 return "{}T{}:{}:{}Z".format(self.date.get(), self.hours.get(), self.minutes.get(), self.seconds.get()) 33 return "{}T{}:{}:{}Z".format(self.date.get(), self.hours.get(), self.minutes.get(), self.seconds.get())
34 - 34 + def set(self, content):
  35 + content=content.strip()
  36 + from datetime import datetime
  37 + if content.endswith("Z"):
  38 + if "." in content:
  39 + d=datetime.strptime(content, "%Y-%m-%dT%H:%M:%S.%fZ")
  40 + else:
  41 + d=datetime.strptime(content, "%Y-%m-%dT%H:%M:%SZ")
  42 + else:
  43 + if "." in content:
  44 + d=datetime.strptime(content, "%Y-%m-%dT%H:%M:%S.%f")
  45 + else:
  46 + d=datetime.strptime(content, "%Y-%m-%dT%H:%M:%S")
  47 +
  48 +
  49 + self.date.set_date(d)
  50 + t=d.time()
  51 + self.hours.delete(0,"end")
  52 + self.minutes.delete(0,"end")
  53 + self.seconds.delete(0,"end")
  54 + self.hours.insert(0, "{0:02d}".format(t.hour))
  55 + self.minutes.insert(0, "{0:02d}".format(t.minute))
  56 + self.seconds.insert(0, "{0:02d}".format(t.second))
35 57
xsd2tkform/ui/entrywidgets.py 0 โ†’ 100644
@@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
  1 +"""Some Entry widgets with validation depending on type
  2 +"""
  3 +import tkinter as tk
  4 +
  5 +
  6 +class FloatEntry(tk.Entry):
  7 + def __init__(self, parent):
  8 + tk.Entry.__init__(self, parent)
  9 + vcmd = (self.register(self.validate),
  10 + '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
  11 + self.configure(validate="all", validatecommand=vcmd)
  12 +
  13 + def validate(self, *args):
  14 + try:
  15 + i=float(args[2])
  16 + self.config(fg="black")
  17 + return True
  18 + except:
  19 + self.config(fg="red")
  20 + return True
  21 + def get(self, *args, **kwargs):
  22 + return float(self.get(*args, **kwargs))
  23 + def set(self, value):
  24 + self.delete(0, tk.END)
  25 + self.insert(0, value)
  26 +
  27 +
  28 +class IntEntry(tk.Entry):
  29 + def __init__(self, parent):
  30 + tk.Entry.__init__(self, parent)
  31 + vcmd = (self.register(self.validate),
  32 + '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
  33 + self.configure(validate="all", validatecommand=vcmd)
  34 + def set(self, value):
  35 + self.delete(0, tk.END)
  36 + self.insert(0, value)
  37 + def get(self, *args, **kwargs):
  38 + return int(self.get(*args, **kwargs))
  39 +
  40 +
  41 + def validate(self, *args):
  42 + try:
  43 + i=int(args[2])
  44 + self.config(fg="black")
  45 + return True
  46 + except:
  47 + self.config(fg="red")
  48 + return True
  49 +
  50 +class BoolEntry(tk.Frame):
  51 + def __init__(self, parent):
  52 + tk.Frame.__init__(self, parent)#highlightbackground="bleu", highlightthickness=2)
  53 + vals = ["true", "false"]
  54 + labels=["True", "False"]
  55 + self.var = tk.StringVar()
  56 + self.var.set(vals[0])
  57 + for i in range(2):
  58 + tk.Radiobutton(self, variable=self.var, text=labels[i], value=vals[i]).pack(side=tk.LEFT, fill=tk.X, expand=1)
  59 + def get(self, *args):
  60 + return self.var.get()
  61 + def set(self, value):
  62 + self.var.set(value)
  63 +
  64 +if __name__=="__main__":
  65 + root = tk.Tk()
  66 +
  67 + float_entry = FloatEntry(root)
  68 + float_entry.pack(side=tk.TOP, fill=tk.X, expand=1)
  69 +
  70 + int_entry = IntEntry(root)
  71 + int_entry.pack(side=tk.TOP, fill=tk.X, expand=1)
  72 +
  73 + root.mainloop()
xsd2tkform/ui/field.py
@@ -4,10 +4,18 @@ import tkinter as tk @@ -4,10 +4,18 @@ import tkinter as tk
4 4
5 5
6 class XSDInputField(tk.Frame): 6 class XSDInputField(tk.Frame):
7 - def __init__(self, parent, *args, **kwargs): 7 + def __init__(self, parent, content=None, widget_config={}, on_delete=None, *args, **kwargs):
8 tk.Frame.__init__(self, parent, *args, **kwargs) 8 tk.Frame.__init__(self, parent, *args, **kwargs)
  9 + self.widget_config=widget_config
  10 + self.on_delete = on_delete
  11 + if content is not None:
  12 + self.set_content(content)
9 def sanitize_type(self, t): 13 def sanitize_type(self, t):
  14 + if t is None:
  15 + return "?"
10 if ":" in t: 16 if ":" in t:
11 return t.split(":")[-1] 17 return t.split(":")[-1]
12 return t 18 return t
  19 + def set_content(self, content=None):
  20 + print("XSDInputField::set_content {}".format(content))
13 21
xsd2tkform/ui/optional.py
1 import tkinter as tk 1 import tkinter as tk
2 from tkinter.messagebox import showerror 2 from tkinter.messagebox import showerror
3 3
4 -from xsd2tkform.ui.field import XSDInputField  
5 -from xsd2tkform.ui.tooltip import ToolTip 4 +from .field import XSDInputField
  5 +from .tooltip import ToolTip
6 6
7 -from xsd2tkform.core.type import SimpleType, ComplexType 7 +from ..core.type import SimpleType, ComplexType
8 8
9 -from xsd2tkform.ui.simpletype import XSDSimpleTypeFrame  
10 -from xsd2tkform.ui.complextype import XSDComplexTypeFrame 9 +from .simpletype import XSDSimpleTypeFrame
  10 +from .complextype import XSDComplexTypeFrame
11 11
12 12
13 class OptionalInput(XSDInputField): 13 class OptionalInput(XSDInputField):
14 - def __init__(self, parent, item, schema, bounds=None, *args, **kwargs):  
15 - XSDInputField.__init__(self, parent , *args, **kwargs) 14 + def __init__(self, parent, item, schema, bounds=None, on_add_field=None, on_delete_field=None, *args, **kwargs):
  15 + XSDInputField.__init__(self, parent , \
  16 + highlightbackground="red",\
  17 + highlightthickness=1,\
  18 +
  19 + *args, **kwargs)
  20 + self.grid_columnconfigure(0, weight=1)
16 self.count=0 21 self.count=0
17 self.bounds=bounds 22 self.bounds=bounds
18 self.type = item.type 23 self.type = item.type
  24 + self.item=item
19 self.schema = schema 25 self.schema = schema
  26 +
20 self.fields=[] 27 self.fields=[]
21 - b=tk.Button(self, text="Add {}".format(self.sanitize_type(item.type)), command=self.add_field)  
22 - # add tooltip  
23 - doc_str = self.get_type_docstring(item.type) 28 + self.add_button2=None
  29 + self.next_row=0
  30 + self.label_frame=None
  31 + self.label=None
  32 +
  33 + self.on_add_field=on_add_field
  34 + self.on_delete_field=on_delete_field
  35 + def is_full(self):
  36 + return self.count == self.bounds[1]
  37 + def remove_label(self):
  38 + if self.label is not None:
  39 + self.label.destroy()
  40 + self.label=None
  41 +
  42 + def set_label_frame(self, frame):
  43 + self.label_frame=frame
  44 +
  45 + def get_add_button(self, parent):
  46 + button_text=self.sanitize_type(self.type)
  47 + if button_text in ["float", "string", "integer", "?"]:
  48 + button_text=self.item.name
  49 + self.add_button2= tk.Button(parent, text=button_text, command=self.add_field)
  50 + doc_str = self.get_type_docstring(self.type)
24 if len(doc_str): 51 if len(doc_str):
25 - ttt = ToolTip(b, doc_str)  
26 - b.pack(side=tk.TOP, fill=tk.X, expand=1)  
27 - def add_field(self): 52 + ttt = ToolTip(self.add_button2, doc_str)
  53 +
  54 +
  55 + return self.add_button2
  56 + def set_content(self, content, update_grid=True):
  57 + self.add_field(content=content, update_grid=update_grid)
  58 + def add_label(self):
  59 + if self.label_frame is not None and self.label is None:
  60 + self.label=tk.Label(self.label_frame, text=self.sanitize_type(self.type)+":")
  61 + self.label.grid()
  62 + def get_fields(self):
  63 + self.fields = [w for w in self.fields if w.winfo_exists()]
  64 + return self.fields
  65 + def add_field(self, content=None, update_grid=True):
28 if self.bounds[1]=="unbounded": 66 if self.bounds[1]=="unbounded":
29 - print("No limit on {} fields".format(self.type)) 67 + pass
30 elif self.count==self.bounds[1]: 68 elif self.count==self.bounds[1]:
31 showerror(title="{} maximum occurences reached".format(self.type), message="A maximum of {} occurences of type {}".format(self.bounds[1], self.type)) 69 showerror(title="{} maximum occurences reached".format(self.type), message="A maximum of {} occurences of type {}".format(self.bounds[1], self.type))
32 return 70 return
33 else: 71 else:
34 pass 72 pass
35 - 73 + self.add_label()
36 self.count+=1 74 self.count+=1
37 -  
38 - f=self.get_frame_by_type(self.type, delete_button=True)  
39 - f.pack(side=tk.TOP, fill=tk.X, expand=1)  
40 - #self.fields.append(f.winfo_children()[0]) 75 +
  76 + if self.count <= self.bounds[0]:
  77 + f=self.get_frame_by_type(self.type, delete_button=False, content=content, parent=self.master, update_grid=update_grid)
  78 + else:
  79 + f=self.get_frame_by_type(self.type, delete_button=True, content=content, parent=self.master, update_grid=update_grid)
  80 + if f is None:
  81 + return
41 self.fields.append(f) 82 self.fields.append(f)
42 - def get_frame_by_type(self, t, parent=None, delete_button=False): 83 + if isinstance(f, XSDComplexTypeFrame):
  84 + f.collapse()
  85 +
  86 +
  87 + # if the maximum number of fields of this type is attained then remove the add button
  88 + if self.count == self.bounds[1] and self.add_button2 is not None:
  89 + self.add_button2.grid_remove()
  90 +
  91 + # update grid in parent
  92 + if content is None:
  93 + if self.on_add_field is not None:
  94 + self.on_add_field()
  95 + #self.master.update_grid()
  96 +
  97 + def get_frame_by_type(self, t, parent=None, delete_button=False, content=None, update_grid=True):
43 if parent is None: 98 if parent is None:
44 parent = self 99 parent = self
45 100
46 td=self.schema.get(t) 101 td=self.schema.get(t)
  102 + if td is None:
  103 + # if type is native type
  104 + ttt=t.split(":")[-1]
  105 + if ttt=="string" or ttt=="float" or ttt=="integer" or ttt=="int":
  106 + # return a SimpleType object
  107 + from ..core.restriction import Restriction
  108 + #from ..core.type import SimpleType
  109 + td=SimpleType(self.item.name, restriction=Restriction(base="string"))
  110 + #return XSDSimpleTypeFrame(self.item.name, name=self.item.name, parent=parent,\
  111 + return XSDSimpleTypeFrame(ttt, name=self.item.name, parent=parent,\
  112 + schema=self.schema, delete_button=delete_button,\
  113 + widget_config=self.widget_config,\
  114 + typedef=td,\
  115 + on_delete=self.delete_field)
  116 +
  117 +
47 if isinstance(td, SimpleType):# in self.simple_types: 118 if isinstance(td, SimpleType):# in self.simple_types:
48 return XSDSimpleTypeFrame(t, parent=parent,\ 119 return XSDSimpleTypeFrame(t, parent=parent,\
49 schema=self.schema, 120 schema=self.schema,
50 - delete_button=delete_button) 121 + delete_button=delete_button,
  122 + content=content,
  123 + widget_config=self.widget_config,
  124 + on_delete=self.delete_field)
51 elif isinstance(td, ComplexType): 125 elif isinstance(td, ComplexType):
52 return XSDComplexTypeFrame(t, parent=parent,\ 126 return XSDComplexTypeFrame(t, parent=parent,\
53 schema=self.schema, 127 schema=self.schema,
54 - delete_button=delete_button) 128 + delete_button=delete_button,
  129 + content=content,
  130 + collapsable=True,
  131 + widget_config=self.widget_config,
  132 + on_delete=self.delete_field)
55 else: 133 else:
56 # TODO : add Group support 134 # TODO : add Group support
57 print("Group support not yet implemented") 135 print("Group support not yet implemented")
58 - def delete_field(self, t, widget):  
59 - print("in delete_field : {}".format(type(widget)))  
60 - print(self.fields)  
61 - widget.destroy()  
62 - self.count-=1  
63 - def get_content(self):  
64 - return [i.get_content() for i in self.fields if i.winfo_exists()] 136 + def delete_field(self, t=None, widget=None):
  137 + # doesn't seem to be used
  138 + self.fields = [w for w in self.fields if w.winfo_exists()]
  139 + self.decrement_field_count_by_type(self.type)
  140 + #self.count-=1
  141 +
  142 + if self.bounds[1]!="unbounded":
  143 + if self.bounds[1]-self.count == 1:
  144 + #self.add_button2.pack(side=tk.LEFT)
  145 + self.add_button2.grid()
  146 + if self.on_delete_field is not None:
  147 + self.on_delete_field()
  148 + #self.master.update_grid()
  149 + def get_content(self, nsmap=None, qname_attrs=None):
  150 + return [i.get_content(nsmap=nsmap, qname_attrs=qname_attrs) for i in self.fields if i.winfo_exists()]
65 def get_type_docstring(self, t): 151 def get_type_docstring(self, t):
  152 + if t is None:
  153 + return ""
66 td=self.schema.get(t) 154 td=self.schema.get(t)
  155 + if td is None or (not hasattr(td, "annotation")):
  156 + return ""
67 a=td.annotation.documentation 157 a=td.annotation.documentation
68 if len(a): 158 if len(a):
69 ls=[l for l in a] 159 ls=[l for l in a]
70 return td.annotation.documentation[ls[0]] 160 return td.annotation.documentation[ls[0]]
71 return "" 161 return ""
72 - def decrement_field_count_by_type(self, t): 162 + def decrement_field_count_by_type(self, t, widget=None):
73 self.count-=1 163 self.count-=1
74 -  
75 164
xsd2tkform/ui/scrollframe.py
@@ -7,7 +7,7 @@ import platform @@ -7,7 +7,7 @@ import platform
7 class ScrollFrame(tk.Frame): 7 class ScrollFrame(tk.Frame):
8 def __init__(self, parent): 8 def __init__(self, parent):
9 super().__init__(parent) # create a frame (self) 9 super().__init__(parent) # create a frame (self)
10 - 10 + self.bind_ids=[]
11 self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") #place canvas on self 11 self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") #place canvas on self
12 self.viewPort = tk.Frame(self.canvas, background="#ffffff") #place a frame on the canvas, this frame will hold the child widgets 12 self.viewPort = tk.Frame(self.canvas, background="#ffffff") #place a frame on the canvas, this frame will hold the child widgets
13 self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) #place a scrollbar on self 13 self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) #place a scrollbar on self
@@ -18,11 +18,11 @@ class ScrollFrame(tk.Frame): @@ -18,11 +18,11 @@ class ScrollFrame(tk.Frame):
18 self.canvas_window = self.canvas.create_window((4,4), window=self.viewPort, anchor="nw", #add view port frame to canvas 18 self.canvas_window = self.canvas.create_window((4,4), window=self.viewPort, anchor="nw", #add view port frame to canvas
19 tags="self.viewPort") 19 tags="self.viewPort")
20 20
21 - self.viewPort.bind("<Configure>", self.onFrameConfigure) #bind an event whenever the size of the viewPort frame changes.  
22 - self.canvas.bind("<Configure>", self.onCanvasConfigure) #bind an event whenever the size of the canvas frame changes. 21 + self.bind_ids.append((self.viewPort, self.viewPort.bind("<Configure>", self.onFrameConfigure))) #bind an event whenever the size of the viewPort frame changes.
  22 + self.bind_ids.append((self.canvas, self.canvas.bind("<Configure>", self.onCanvasConfigure))) #bind an event whenever the size of the canvas frame changes.
23 23
24 - self.viewPort.bind('<Enter>', self.onEnter) # bind wheel events when the cursor enters the control  
25 - self.viewPort.bind('<Leave>', self.onLeave) # unbind wheel events when the cursorl leaves the control 24 + self.bind_ids.append((self.viewPort, self.viewPort.bind('<Enter>', self.onEnter))) # bind wheel events when the cursor enters the control
  25 + self.bind_ids.append((self.viewPort, self.viewPort.bind('<Leave>', self.onLeave))) # unbind wheel events when the cursorl leaves the control
26 26
27 self.onFrameConfigure(None) #perform an initial stretch on render, otherwise the scroll region has a tiny border until the first resize 27 self.onFrameConfigure(None) #perform an initial stretch on render, otherwise the scroll region has a tiny border until the first resize
28 28
@@ -49,18 +49,25 @@ class ScrollFrame(tk.Frame): @@ -49,18 +49,25 @@ class ScrollFrame(tk.Frame):
49 49
50 def onEnter(self, event): # bind wheel events when the cursor enters the control 50 def onEnter(self, event): # bind wheel events when the cursor enters the control
51 if platform.system() == 'Linux': 51 if platform.system() == 'Linux':
52 - self.canvas.bind_all("<Button-4>", self.onMouseWheel)  
53 - self.canvas.bind_all("<Button-5>", self.onMouseWheel) 52 + self.bind_ids.append((self.canvas,self.canvas.bind_all("<Button-4>", self.onMouseWheel)))
  53 + self.bind_ids.append((self.canvas,self.canvas.bind_all("<Button-5>", self.onMouseWheel)))
54 else: 54 else:
55 - self.canvas.bind_all("<MouseWheel>", self.onMouseWheel) 55 + self.bind_ids.append((self.canvas,self.canvas.bind_all("<MouseWheel>", self.onMouseWheel)))
56 56
57 - def onLeave(self, event): # unbind wheel events when the cursorl leaves the control 57 + def onLeave(self, event): # unbind wheel events when the cursorl leaves the control
58 if platform.system() == 'Linux': 58 if platform.system() == 'Linux':
59 self.canvas.unbind_all("<Button-4>") 59 self.canvas.unbind_all("<Button-4>")
60 self.canvas.unbind_all("<Button-5>") 60 self.canvas.unbind_all("<Button-5>")
61 else: 61 else:
62 self.canvas.unbind_all("<MouseWheel>") 62 self.canvas.unbind_all("<MouseWheel>")
63 - 63 + def destroy(self):
  64 + # unbind all
  65 + for w,fid in self.bind_ids:
  66 + w.unbind(fid)
  67 + self.canvas.unbind_all("<Button-4>")
  68 + self.canvas.unbind_all("<Button-5>")
  69 + self.canvas.unbind_all("<MouseWheel>")
  70 + super().destroy()
64 71
65 72
66 # ******************************** 73 # ********************************
xsd2tkform/ui/simpletype.py
@@ -3,22 +3,142 @@ from tkinter import ttk @@ -3,22 +3,142 @@ from tkinter import ttk
3 3
4 from lxml import etree 4 from lxml import etree
5 5
6 -from xsd2tkform.ui.tooltip import ToolTip 6 +from .tooltip import ToolTip
7 7
8 from tkcalendar import DateEntry 8 from tkcalendar import DateEntry
9 9
10 -from xsd2tkform.ui.datetime_selector import DatetimeEntry 10 +from .datetime_selector import DatetimeEntry
11 11
12 from .field import XSDInputField 12 from .field import XSDInputField
  13 +
  14 +from amda_xml_manager import delete_image_file
  15 +
  16 +from .entrywidgets import IntEntry, FloatEntry, BoolEntry
  17 +
  18 +class XSDAnyInputFrame(XSDInputField):
  19 + def __init__(self, parent, input_widget_type, content=None):
  20 + XSDInputField.__init__(self, parent)
  21 +
  22 + self.label=None
  23 + self.add_button=None
  24 +
  25 + self.grid_columnconfigure(0, weight=1)
  26 + self.input_widget = input_widget_type(self)
  27 + self.input_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
  28 +
  29 + self.set_content(content)
  30 + def set_content(self, content=None):
  31 + if content is None:
  32 + return
  33 + if isinstance(content, etree._Element):
  34 + self.input_widget.insert(0, etree.tostring(content, pretty_print=True))
  35 + return
  36 + self.input_widget.insert(0, str(content))
  37 +
  38 +
  39 + def get_label(self, parent):
  40 + label_text="any :"
  41 + self.label = tk.Label(parent, text=label_text)
  42 + return self.label
  43 + def get_content(self):
  44 + return self.input_widget.get("1.0", tk.END)
  45 +
  46 +
  47 +class XSDAttributeFrame(XSDInputField):
  48 + def __init__(self, attribute, parent, on_delete=None, on_add=None):
  49 + XSDInputField.__init__(self, parent)
  50 +
  51 + self.on_delete=on_delete
  52 + self.on_add=on_add
  53 + self.name = attribute.name
  54 + self.type = attribute.type
  55 + self.attribute = attribute
  56 + if attribute.use=="required":
  57 + self.mandatory = True
  58 + else:
  59 + self.mandatory = False
  60 + self.label=None
  61 + self.add_button=None
  62 +
  63 + self.grid_columnconfigure(0, weight=1)
  64 + if self.type.endswith("float"):
  65 + self.input_widget=FloatEntry(self)
  66 + elif self.type.endswith("int") or self.type.endswith("integer"):
  67 + self.input_widget=IntEntry(self)
  68 + elif self.type.endswith("boolean"):
  69 + self.input_widget=BoolEntry(self)
  70 + else:
  71 + self.input_widget=tk.Entry(self)
  72 + self.input_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
  73 +
  74 + if not self.mandatory:
  75 + self.delete_img = tk.PhotoImage(file=delete_image_file)
  76 + #self.db = tk.Button(self, text="X", command=self.delete)
  77 + self.db = tk.Button(self, image=self.delete_img, command=self.delete)
  78 + self.db.pack(side=tk.RIGHT, expand=0)
  79 + def is_visible(self):
  80 + return "row" in self.grid_info()
  81 + def get(self):
  82 + return self.input_widget.get()
  83 + def set(self, value):
  84 + if isinstance(self.input_widget, FloatEntry) or isinstance(self.input_widget, IntEntry) or \
  85 + isinstance(self.input_widget, BoolEntry):
  86 + self.input_widget.set(value)
  87 + else:
  88 + self.input_widget.insert(0, value)
  89 + def get_add_button(self, parent):
  90 + self.add_button = tk.Button(parent, text=self.name, command=self.add)
  91 + return self.add_button
  92 + def add(self):
  93 + # remove the button
  94 + self.grid()
  95 + if self.label is not None:
  96 + self.label.grid()
  97 + if self.add_button is not None:
  98 + self.add_button.grid_remove()
  99 + if self.on_add is not None:
  100 + self.on_add()
  101 + def delete(self, *args):
  102 + self.grid_remove()
  103 + if self.label is not None:
  104 + self.label.grid_remove()
  105 + # replace the add button
  106 + if self.add_button is not None:
  107 + self.add_button.grid()
  108 +
  109 + if self.on_delete is not None:
  110 + self.on_delete()
  111 +
  112 + def get_label(self, parent):
  113 + label_text=self.sanitize_type(self.name)
  114 + label_text+=" :"
  115 + self.label = tk.Label(parent, text=label_text)
  116 + return self.label
  117 +
13 class XSDSimpleTypeFrame(XSDInputField): 118 class XSDSimpleTypeFrame(XSDInputField):
14 - def __init__(self, typename, parent=None, schema=None, delete_button=False, *args, **kwargs):  
15 - XSDInputField.__init__(self, parent, *args, **kwargs) 119 + def __init__(self, typename, parent=None, schema=None, delete_button=False, filename=None, content=None, name=None, widget_config={}, typedef=None, input_widget=None,*args, **kwargs):
  120 + XSDInputField.__init__(self, parent, widget_config=widget_config,\
  121 + # highlightbackground="blue",\
  122 + # highlightthickness=1,\
  123 + *args, **kwargs)
  124 + # flag indicating that the content has been set
  125 + self.content_has_been_set = False
  126 +
  127 + self.grid_columnconfigure(0, weight=1)
  128 + self.input_widget_type=input_widget
16 129
17 self.type = typename 130 self.type = typename
18 - self.label = tk.Label(self, text="{}: ".format(self.sanitize_type(typename)))  
19 - self.label.pack(side=tk.LEFT, expand=0) 131 + self.name = name
  132 +
  133 + print("XSDSimpleTypeFrame name={}, type={}".format(self.name, self.type))
  134 +
  135 + # label reference
  136 + self.label=None
20 137
21 - self.typedef = schema.get(typename) 138 + if typedef is None:
  139 + self.typedef = schema.get(typename)
  140 + else:
  141 + self.typedef = typedef
22 142
23 self.set_tooltip() 143 self.set_tooltip()
24 144
@@ -27,41 +147,126 @@ class XSDSimpleTypeFrame(XSDInputField): @@ -27,41 +147,126 @@ class XSDSimpleTypeFrame(XSDInputField):
27 self.input_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) 147 self.input_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
28 148
29 # delete button 149 # delete button
  150 + self.delete_img = None
30 if delete_button: 151 if delete_button:
31 - self.db = tk.Button(self, text="X", command=self.delete) 152 + self.delete_img = tk.PhotoImage(file=delete_image_file)
  153 + #self.db = tk.Button(self, text="X", command=self.delete)
  154 + self.db = tk.Button(self, image=self.delete_img, command=self.delete)
32 self.db.pack(side=tk.RIGHT, expand=0) 155 self.db.pack(side=tk.RIGHT, expand=0)
  156 +
  157 + # if a filename was given then load the content
  158 + if filename is not None:
  159 + print("Setting SimpleType content from file : {}".format(filename))
  160 + if content is not None:
  161 + self.set_content(content)
  162 + def get_label(self, parent):
  163 + label_text=self.sanitize_type(self.type)
  164 + if label_text in ["string", "float", "integer"]:
  165 + label_text=self.name
  166 +
  167 + if self.name is not None:
  168 + label_text=self.name
  169 + else:
  170 + label_text=self.sanitize_type(self.type)
  171 + if label_text in ["string", "float", "integer"]:
  172 + label_text=self.name
  173 +
  174 + label_text+=" :"
  175 + self.label = tk.Label(parent, text=label_text)
  176 + self.set_tooltip()
  177 + return self.label
33 def delete(self): 178 def delete(self):
34 # destroy this widget and decrement the field count in 179 # destroy this widget and decrement the field count in
35 # the master frame 180 # the master frame
36 - print("self id : ", id(self))  
37 - print([id(e) for e in self.master.fields])  
38 - print(self.master.fields)  
39 - self.master.decrement_field_count_by_type(self.type) 181 + #self.master.decrement_field_count_by_type(self.type)
40 self.destroy() 182 self.destroy()
  183 + if self.on_delete is not None:
  184 + self.on_delete()
  185 + if self.label is not None:
  186 + self.label.destroy()
  187 + def is_full(self):
  188 + return self.content_has_been_set
  189 + def set_content(self, content, update_grid=True):
  190 + text=content.text
  191 + if text is None:
  192 + text=""
  193 + if isinstance(self.input_widget, ttk.Combobox):
  194 + if not content.text is None:
  195 + self.input_widget.set(content.text)
  196 + elif isinstance(self.input_widget, tk.Text):
  197 + n_lines = len(text.split("\n"))+1
  198 + self.input_widget.insert("1.0", text)
  199 + #self.input_widget.configure(height=n_lines)
  200 +
  201 + elif isinstance(self.input_widget, tk.Entry):
  202 + self.input_widget.insert(0, text)
  203 + else:
  204 + self.input_widget.set(content.text)
  205 + self.content_has_been_set = True
41 def set_tooltip(self): 206 def set_tooltip(self):
  207 + if self.label is None:
  208 + return
  209 + if self.typedef is None:
  210 + return
  211 + if self.typedef.annotation is None:
  212 + return
42 if len(self.typedef.annotation.documentation): 213 if len(self.typedef.annotation.documentation):
43 langs = [k for k in self.typedef.annotation.documentation] 214 langs = [k for k in self.typedef.annotation.documentation]
44 - tt = ToolTip(self.label, self.typedef.annotation.documentation[langs[0]]) 215 + tt=ToolTip(self.label, self.typedef.annotation.documentation[langs[0]])
45 def get_input_widget(self): 216 def get_input_widget(self):
  217 + if self.input_widget_type is not None:
  218 + return self.input_widget_type(self)
  219 + if self.sanitize_type(self.type) in self.widget_config:
  220 + persot = self.widget_config[self.sanitize_type(self.type)]
  221 + if isinstance(persot, tuple):
  222 + return persot[0](self, *persot[1])
  223 + return self.widget_config[self.sanitize_type(self.type)](self)
46 if self.typedef.restriction is not None: 224 if self.typedef.restriction is not None:
47 if self.typedef.restriction.base=="xsd:string": 225 if self.typedef.restriction.base=="xsd:string":
48 if len(self.typedef.restriction.enum): 226 if len(self.typedef.restriction.enum):
49 - b=ttk.Combobox(self, values=self.typedef.restriction.enum, state="readonly") 227 + #b=ttk.Combobox(self, values=self.typedef.restriction.enum, state="readonly")
  228 + from .autocompleteentry import AutocompleteCombobox
  229 + b=AutocompleteCombobox(self)
  230 + b.set_completion_list(self.typedef.restriction.enum)
  231 +
50 b.current(0) 232 b.current(0)
  233 + b.unbind_class("TCombobox","<MouseWheel>")
  234 + b.unbind_class("TCombobox","<ButtonPress-4>")
  235 + b.unbind_class("TCombobox","<ButtonPress-5>")
  236 +
51 return b 237 return b
  238 + #if "Description" in self.type:
  239 + # return tk.Text(self)
52 return tk.Entry(self) 240 return tk.Entry(self)
53 if self.typedef.restriction.base=="xsd:dateTime": 241 if self.typedef.restriction.base=="xsd:dateTime":
54 return DatetimeEntry(self) 242 return DatetimeEntry(self)
55 if len(self.typedef.restriction.enum): 243 if len(self.typedef.restriction.enum):
56 return ttk.Combobox(self, values=self.typedef.restriction.enum, state="readonly") 244 return ttk.Combobox(self, values=self.typedef.restriction.enum, state="readonly")
  245 + if self.type.endswith("float"):
  246 + return FloatEntry(self)
  247 + if self.type.endswith("int") or self.type.endswith("integer"):
  248 + return IntEntry(self)
57 return tk.Entry(self) 249 return tk.Entry(self)
58 250
59 def get_value(self): 251 def get_value(self):
  252 + if isinstance(self.input_widget, tk.Text):
  253 + return self.input_widget.get("1.0",tk.END).strip()
60 return self.input_widget.get() 254 return self.input_widget.get()
61 - def get_content(self): 255 + def get_attribute_values(self):
  256 + return {}
  257 + def get_content(self, nsmap=None, qname_attrs=None):
62 # returns tree type 258 # returns tree type
63 - t = self.sanitize_type(self.type)  
64 - root = etree.Element(t) 259 + if self.name is None:
  260 + t = self.sanitize_type(self.type)
  261 + else:
  262 + t=self.name
  263 + if nsmap is not None:
  264 + root = etree.Element(t,qname_attrs, nsmap=nsmap)
  265 + else:
  266 + root = etree.Element(t)
  267 + attrib_values=self.get_attribute_values()
  268 + for k in attrib_values:
  269 + root.set(k, attrib_values[k])
65 root.text = self.get_value() 270 root.text = self.get_value()
66 return root 271 return root
67 272