Blame view

test/xsd_complextype.py 10.6 KB
d73bf339   Alexandre Schulz   first commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showerror

from lxml import etree

from xsd_simpletype import XSDSimpleTypeFrame
from tooltip import ToolTip

class XSDComplexTypeFrame(tk.Frame):
    def __init__(self, parent=None, element=None, simple_types=None, complex_types=None, *args, **kwargs):
        tk.Frame.__init__(self, parent, borderwidth=1,\
                highlightbackground="black",\
                highlightthickness=1,\
                *args, **kwargs)
        # store type of the current field
        self.type = element.attrib["name"]
        self.simple_types=simple_types
        self.complex_types=complex_types

        # store the number of fields of each type, and min and max
        self.field_counts={}
        self.field_min_counts={}
        self.field_max_counts={}
        
        # storage for optional frame
        self.subframes=[]

        self.label=tk.Label(self, text=element.attrib["name"])
        self.label.pack()
        
        self.inputs=[] # list of inputs used for constructing the tree

        # print children
        for child in element:
            # ignore annotations
            if child.tag.endswith("annotation"):
                # add tooltip
                if child[0].text is not None:
                    # add tool tip
                    tt = ToolTip(self.label, child[0].text)
                
            # sequence
            if child.tag.endswith("sequence"):
                sequence_inputs = []
                # print elements in the sequence
                for item in child:
                    if item.tag.endswith("element"):
                        t = self.get_element_type(item)
 
                        # check if item is mandatory or not
                        (min_oc, max_oc)=self.get_element_occurence_limits(item)
                        self.field_min_counts[t]=min_oc
                        self.field_max_counts[t]=max_oc
                        
                        if min_oc==0:
                            # optional field, add a frame to contain the eventual fields, with a button
                            b_frame= tk.Frame(self)
                            b=tk.Button(b_frame, text="Add {}".format(t), command=lambda t=t, frame=b_frame, container=sequence_inputs: self.add_frame_by_type(t, frame, container))
                            # add tooltip
                            doc_str = self.get_type_docstring(t)
                            if len(doc_str):
                                ttt = ToolTip(b, doc_str)
                            b.pack(side=tk.TOP, fill=tk.X, expand=1)
                            b_frame.pack(side=tk.TOP, fill=tk.X, expand=1)
                        else:
                            # mandatory field
                            f = self.get_frame_by_type(t)
                            sequence_inputs.append(f) # store the field for later use
                            f.pack(side=tk.TOP, fill=tk.X, expand=1)
 
                    if item.tag.endswith("choice"):
                        choice_inputs=[]
                        # get the list of choice type
                        choice_types = self.get_choice_types(item)
                        # get occurence bounds for choices
                        choice_occ_bounds = self.get_element_occurence_limits(item)
                        # set those bounds for all types 
                        for _type in choice_types:
                            # 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
                            if _type in self.field_min_counts:
                                print("AAAAAAAAAAAa")
                            if _type in self.field_max_counts:
                                print("BBBBBBBBBBBBBb")
                            self.field_min_counts[_type]=choice_occ_bounds[0]
                            self.field_max_counts[_type]=choice_occ_bounds[1]
                        # frame for storing the selector and choices
                        choice_frame = tk.Frame(self)
                        # create a frame to store the choice selector
                        choice_select_frame = tk.Frame(choice_frame)
                        
                        # add a choice selector : combobox and button
                        choice_type = ttk.Combobox(choice_select_frame, values=choice_types, state="readonly")
                        choice_type.current(0)
                        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))

                        choice_type.pack(side=tk.LEFT, fill=tk.X, expand=1)
                        choice_add.pack(side=tk.RIGHT, expand=0)
                        choice_select_frame.pack(side=tk.TOP, fill=tk.X, expand=1)
                        choice_frame.pack(side=tk.TOP, fill=tk.X, expand=1)
                        sequence_inputs.append(choice_inputs)
                self.inputs.append(sequence_inputs)
    def get_type_docstring(self, t):
        tree=self.get_type_definition(t)
        for e in tree.iter("{*}documentation"):
            return e.text
        return ""
    def get_type_definition(self, t):
        if t in self.simple_types:
            return self.simple_types[t]
        return self.complex_types[t]
    def get_choice_types(self, element):
        ans=[]
        for child in element:
            if child.tag.endswith("element"):
                ans.append(self.get_element_type(child))
        return ans
    def get_element_occurence_limits(self, element):
        min_oc = int(element.attrib["minOccurs"])
        if "maxOccurs" in element.attrib:
            max_oc=element.attrib["maxOccurs"]
            if max_oc.isdigit():
                max_oc=int(max_oc)
        else:
            max_oc=None
        return min_oc, max_oc

    def get_element_type(self, element):
        t=element.attrib["type"]
        if ":" in t:
            return t.split(":")[-1]
        return t
    def get_field_count_by_type(self, t):
        if t in self.field_counts:
            return self.field_counts[t]
        return 0
    def increment_field_count_by_type(self, t):
        self.field_counts[t]=self.get_field_count_by_type(t)+1
    def decrement_field_count_by_type(self, t):
        self.field_counts[t]=self.get_field_count_by_type(t)-1
    def delete_field(self, t, field, container=None):
        field_dims = (field.winfo_width(), field.winfo_height())
        field.destroy()
        self.decrement_field_count_by_type(t)
        print(self.winfo_width(), self.winfo_height())
        print(self.master, type(self.master))
        print("master id : ", id(self.master))
        #self.master.master.master.onFrameConfigure(None)
        #self.master.master.event_generate("<Configure>", x=1, y=4, width=600, height=108)
        #self.master.event_generate("<Configure>", x=1, y=4, width=600, height=108)
        current_scroll_region=self.master.master.bbox("all")
        print("current scrollregion : {}".format(current_scroll_region))
        print("field dims ", field_dims)
        new_scrollregion= (current_scroll_region[0],
                current_scroll_region[1],
                current_scroll_region[2]-field_dims[0],
                current_scroll_region[3]-field_dims[1])

        #print("field dimensions : {}".format((field.winfo_width(), field.winfo_height())))
        # WORKS
        #self.master.master.configure(scrollregion=(4,4,604,112))
        self.master.master.configure(scrollregion=new_scrollregion)


    def add_frame_by_type(self, t, frame=None, container=None):
        # check if the maximum occurences of this field is achieved
        
        if self.field_max_counts[t]=="unbounded":
            print("No limit on {} fields".format(t))
        elif t in self.field_counts:
            if self.get_field_count_by_type(t)==self.field_max_counts[t]:
                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))
                return
        else:
            pass
        
        self.increment_field_count_by_type(t)
                
        if frame is None:
            f = self.get_frame_by_type(t, delete_button=True)
            f.pack(side=tk.TOP, fill=tk.X, expand=1)
        else:
            f = self.get_frame_by_type(t, parent=frame, delete_button=True)
            f.pack(side=tk.TOP, fill=tk.X, expand=1)
        if container is not None:
            container.append(f.winfo_children()[0])
    def get_frame_by_type(self, t, parent=None, delete_button=False):
        if parent is None:
            parent = self
        if delete_button:
            # create a frame to hold the fiel and the delete button
            tf=tk.Frame(parent)
            # add the fields without button
            fields=self.get_frame_by_type(t, tf)
            fields.pack(side=tk.LEFT, fill=tk.X, expand=True)
            # button
            del_button = tk.Button(tf, text="Delete", command=lambda t=t, widget=tf: self.delete_field(t, widget))
            del_button.pack(side=tk.RIGHT, expand=False)
            return tf

        if t in self.simple_types:
            return XSDSimpleTypeFrame(parent=parent,\
                    element=self.simple_types[t])
        else:
            return XSDComplexTypeFrame(parent=parent,\
                    simple_types=self.simple_types,\
                    complex_types=self.complex_types,\
                    element=self.complex_types[t])
    def get_value(self):
        return ""
    def add_content(self, root, content):
        if isinstance(content, list):
            for item in content:
                self.add_content(root, item)
        else:
            if content is not None:
                root.append(content)
    def get_content(self, obj=None):
        if obj is not None:
            if isinstance(obj, list):
                return [self.get_content(i) for i in obj]
            if isinstance(obj, XSDSimpleTypeFrame):
                if obj.winfo_exists():
                    return obj.get_content()
            if isinstance(obj, XSDComplexTypeFrame):
                if obj.winfo_exists():
                    return obj.get_content()
            return
        root = etree.Element(self.type)
        # returns tree type
        for c in self.get_content(self.inputs):
            self.add_content(root, c)
        def inlen(i):
            c=0
            for item in i:
                if isinstance(item, list):
                    c+=inlen(item)
                else:
                    c+=1
            return c
        return root