source: orange/Orange/OrangeCanvas/scheme/readwrite.py @ 11139:3cab9e06f6e5

Revision 11139:3cab9e06f6e5, 9.8 KB checked in by Ales Erjavec <ales.erjavec@…>, 18 months ago (diff)

Fixes for unicode support.

Line 
1"""
2Scheme save/load routines.
3"""
4import sys
5
6from xml.etree.ElementTree import TreeBuilder, Element, ElementTree
7from collections import defaultdict
8
9import cPickle
10
11try:
12    from ast import literal_eval
13except ImportError:
14    literal_eval = eval
15
16import logging
17
18from . import SchemeNode
19from . import SchemeLink
20from .annotations import SchemeTextAnnotation, SchemeArrowAnnotation
21
22from .. import registry
23
24log = logging.getLogger(__name__)
25
26
27def parse_scheme(scheme, stream):
28    """Parse saved scheme string
29    """
30    from xml.etree.ElementTree import parse
31    doc = parse(stream)
32    scheme_el = doc.getroot()
33    version = scheme_el.attrib.get("version", None)
34    if version is None:
35        if scheme_el.find("widgets") is not None:
36            version = "1.0"
37        else:
38            version = "2.0"
39
40    if version == "1.0":
41        parse_scheme_v_1_0(doc, scheme)
42        return scheme
43    else:
44        parse_scheme_v_2_0(doc, scheme)
45        return scheme
46
47
48def scheme_node_from_element(node_el, registry):
49    """Create a SchemeNode from an Element instance.
50    """
51    widget_desc = registry.widget(node_el.get("qualified_name"))
52    title = node_el.get("title")
53    pos = node_el.get("position")
54
55    if pos is not None:
56        pos = literal_eval(pos)
57
58    return SchemeNode(widget_desc, title=title, position=pos)
59
60
61def parse_scheme_v_2_0(etree, scheme, widget_registry=None):
62    """Parse an ElementTree Instance.
63    """
64    if widget_registry is None:
65        widget_registry = registry.global_registry()
66
67    nodes = []
68    links = []
69
70    id_to_node = {}
71
72    scheme_node = etree.getroot()
73    scheme.title = scheme_node.attrib.get("title", "")
74    scheme.description = scheme_node.attrib.get("description", "")
75
76    # Load and create scheme nodes.
77    for node_el in etree.findall("nodes/node"):
78        # TODO: handle errors.
79        try:
80            node = scheme_node_from_element(node_el, widget_registry)
81        except Exception:
82            raise
83
84        nodes.append(node)
85        id_to_node[node_el.get("id")] = node
86
87    # Load and create scheme links.
88    for link_el in etree.findall("links/link"):
89        source = id_to_node[link_el.get("source_node_id")]
90        sink = id_to_node[link_el.get("sink_node_id")]
91        source_channel = link_el.get("source_channel")
92        sink_channel = link_el.get("sink_channel")
93        enabled = link_el.get("enabled") == "true"
94        link = SchemeLink(source, source_channel, sink, sink_channel,
95                          enabled=enabled)
96        links.append(link)
97
98    # Load node properties
99    for property_el in etree.findall("node_properties/properties"):
100        node_id = property_el.attrib.get("node_id")
101        node = id_to_node[node_id]
102        data = property_el.text
103        properties = None
104        try:
105            properties = cPickle.loads(data)
106        except Exception:
107            log.error("Could not load properties for %r.", node.title,
108                      exc_info=True)
109        if properties is not None:
110            node.properties = properties
111
112    annotations = []
113    for annot_el in etree.findall("annotations/*"):
114        if annot_el.tag == "text":
115            rect = annot_el.attrib.get("rect", "(0, 0, 20, 20)")
116            rect = literal_eval(rect)
117            annot = SchemeTextAnnotation(rect, annot_el.text or "")
118        elif annot_el.tag == "arrow":
119            start = annot_el.attrib.get("start", "(0, 0)")
120            end = annot_el.attrib.get("end", "(0, 0)")
121            start, end = map(literal_eval, (start, end))
122            annot = SchemeArrowAnnotation(start, end)
123        annotations.append(annot)
124
125    for node in nodes:
126        scheme.add_node(node)
127
128    for link in links:
129        scheme.add_link(link)
130
131    for annot in annotations:
132        scheme.add_annotation(annot)
133
134
135def parse_scheme_v_1_0(etree, scheme, widget_registry=None):
136    """ElementTree Instance of an old .ows scheme format.
137    """
138    if widget_registry is None:
139        widget_registry = registry.global_registry()
140
141    widgets = widget_registry.widgets()
142    widgets_by_name = [(d.qualified_name.rsplit(".", 1)[-1], d)
143                       for d in widgets]
144    widgets_by_name = dict(widgets_by_name)
145
146    nodes_by_caption = {}
147    nodes = []
148    links = []
149    for widget_el in etree.findall("widgets/widget"):
150        caption = widget_el.get("caption")
151        name = widget_el.get("widgetName")
152        x_pos = widget_el.get("xPos")
153        y_pos = widget_el.get("yPos")
154        desc = widgets_by_name[name]
155        node = SchemeNode(desc, title=caption,
156                          position=(int(x_pos), int(y_pos)))
157        nodes_by_caption[caption] = node
158        nodes.append(node)
159#        scheme.add_node(node)
160
161    for channel_el in etree.findall("channels/channel"):
162        in_caption = channel_el.get("inWidgetCaption")
163        out_caption = channel_el.get("outWidgetCaption")
164        source = nodes_by_caption[out_caption]
165        sink = nodes_by_caption[in_caption]
166        enabled = channel_el.get("enabled") == "1"
167        signals = eval(channel_el.get("signals"))
168        for source_channel, sink_channel in signals:
169            link = SchemeLink(source, source_channel, sink, sink_channel,
170                              enabled=enabled)
171            links.append(link)
172#            scheme.add_link(link)
173
174    settings = etree.find("settings")
175    properties = {}
176    if settings is not None:
177        data = settings.attrib.get("settingsDictionary", None)
178        if data:
179            try:
180                properties = literal_eval(data)
181            except Exception:
182                log.error("Could not load properties for the scheme.",
183                          exc_info=True)
184
185    for node in nodes:
186        if node.title in properties:
187            try:
188                node.properties = cPickle.loads(properties[node.title])
189            except Exception:
190                log.error("Could not unpickle properties for the node %r.",
191                          node.title, exc_info=True)
192
193        scheme.add_node(node)
194
195    for link in links:
196        scheme.add_link(link)
197
198
199def inf_range(start=0, step=1):
200    """Return an infinite range iterator.
201    """
202    while True:
203        yield start
204        start += step
205
206
207def scheme_to_ows_stream(scheme, stream):
208    """Write scheme to a a stream in Orange Scheme .ows format
209    """
210    builder = TreeBuilder(element_factory=Element)
211    builder.start("scheme", {"version": "2.0",
212                             "title": scheme.title or "",
213                             "description": scheme.description or ""})
214
215    ## Nodes
216    node_ids = defaultdict(inf_range().next)
217    builder.start("nodes", {})
218    for node in scheme.nodes:
219        desc = node.description
220        attrs = {"id": str(node_ids[node]),
221                 "name": desc.name,
222                 "qualified_name": desc.qualified_name,
223                 "project_name": desc.project_name or "",
224                 "version": desc.version or "",
225                 "title": node.title,
226                 }
227        if node.position is not None:
228            attrs["position"] = str(node.position)
229
230        if type(node) is not SchemeNode:
231            attrs["scheme_node_type"] = "%s.%s" % (type(node).__name__,
232                                                   type(node).__module__)
233        builder.start("node", attrs)
234        builder.end("node")
235
236    builder.end("nodes")
237
238    ## Links
239    link_ids = defaultdict(inf_range().next)
240    builder.start("links", {})
241    for link in scheme.links:
242        source = link.source_node
243        sink = link.sink_node
244        source_id = node_ids[source]
245        sink_id = node_ids[sink]
246        attrs = {"id": str(link_ids[link]),
247                 "source_node_id": str(source_id),
248                 "sink_node_id": str(sink_id),
249                 "source_channel": link.source_channel.name,
250                 "sink_channel": link.sink_channel.name,
251                 "enabled": "true" if link.enabled else "false",
252                 }
253        builder.start("link", attrs)
254        builder.end("link")
255
256    builder.end("links")
257
258    ## Annotations
259    annotation_ids = defaultdict(inf_range().next)
260    builder.start("annotations", {})
261    for annotation in scheme.annotations:
262        annot_id = annotation_ids[annotation]
263        attrs = {"id": str(annot_id)}
264        data = None
265        if isinstance(annotation, SchemeTextAnnotation):
266            tag = "text"
267            attrs.update({"rect": repr(annotation.rect)})
268            data = annotation.text
269
270        elif isinstance(annotation, SchemeArrowAnnotation):
271            tag = "arrow"
272            attrs.update({"start": repr(annotation.start_pos),
273                          "end": repr(annotation.end_pos)})
274            data = None
275        else:
276            log.warning("Can't save %r", annotation)
277            continue
278        builder.start(tag, attrs)
279        if data is not None:
280            builder.data(data)
281        builder.end(tag)
282
283    builder.end("annotations")
284
285    builder.start("thumbnail", {})
286    builder.end("thumbnail")
287
288    # Node properties/settings
289    builder.start("node_properties", {})
290    for node in scheme.nodes:
291        data = None
292        if node.properties:
293            try:
294                data = cPickle.dumps(node.properties)
295            except Exception:
296                log.error("Error serializing properties for node %r",
297                          node.title, exc_info=True)
298            if data is not None:
299                builder.start("properties",
300                              {"node_id": str(node_ids[node]),
301                               "format": "pickle"})
302                builder.data(data)
303                builder.end("properties")
304
305    builder.end("node_properties")
306    builder.end("scheme")
307    root = builder.close()
308    tree = ElementTree(root)
309
310    if sys.version_info < (2, 7):
311        # in Python 2.6 the write does not have xml_declaration parameter.
312        tree.write(stream, encoding="utf-8")
313    else:
314        tree.write(stream, encoding="utf-8", xml_declaration=True)
Note: See TracBrowser for help on using the repository browser.