Ignore:
Timestamp:
09/03/13 14:53:58 (8 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Refactored scheme parsing.

Using widget description's "replaces" list to resolve widgets.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/scheme/readwrite.py

    r11391 r11683  
    44""" 
    55import sys 
     6import warnings 
    67 
    78from xml.etree.ElementTree import TreeBuilder, Element, ElementTree, parse 
    89 
    9 from collections import defaultdict 
    10 from itertools import chain 
     10from collections import defaultdict, namedtuple 
     11from itertools import chain, count 
    1112 
    1213import cPickle as pickle 
     
    2324from .errors import IncompatibleChannelTypeError 
    2425 
    25 from .. import registry 
     26from ..registry import global_registry 
    2627 
    2728log = logging.getLogger(__name__) 
     
    139140 
    140141    """ 
     142    warnings.warn("Use 'scheme_load' instead", DeprecationWarning, 
     143                  stacklevel=2) 
     144 
    141145    doc = parse(stream) 
    142146    scheme_el = doc.getroot() 
     
    187191    """ 
    188192    if widget_registry is None: 
    189         widget_registry = registry.global_registry() 
     193        widget_registry = global_registry() 
    190194 
    191195    nodes_not_found = [] 
     
    311315    """ 
    312316    if widget_registry is None: 
    313         widget_registry = registry.global_registry() 
     317        widget_registry = global_registry() 
    314318 
    315319    widgets_not_found = [] 
     
    386390    for link in links: 
    387391        scheme.add_link(link) 
     392 
     393 
     394# Intermediate scheme representation 
     395_scheme = namedtuple( 
     396    "_scheme", 
     397    ["title", "version", "description", "nodes", "links", "annotations"]) 
     398 
     399_node = namedtuple( 
     400    "_node", 
     401    ["id", "title", "name", "position", "project_name", "qualified_name", 
     402     "version", "data"]) 
     403 
     404_data = namedtuple( 
     405    "_data", 
     406    ["format", "data"]) 
     407 
     408_link = namedtuple( 
     409    "_link", 
     410    ["id", "source_node_id", "sink_node_id", "source_channel", "sink_channel", 
     411     "enabled"]) 
     412 
     413_annotation = namedtuple( 
     414    "_annotation", 
     415    ["id", "type", "params"]) 
     416 
     417_text_params = namedtuple( 
     418    "_text_params", 
     419    ["geometry", "text", "font"]) 
     420 
     421_arrow_params = namedtuple( 
     422    "_arrow_params", 
     423    ["geometry", "color"]) 
     424 
     425 
     426def parse_ows_etree_v_2_0(tree): 
     427    scheme = tree.getroot() 
     428    nodes, links, annotations = [], [], [] 
     429 
     430    # First collect all properties 
     431    properties = {} 
     432    for property in tree.findall("node_properties/properties"): 
     433        node_id = property.get("node_id") 
     434        format = property.get("format") 
     435        if "data" in property.attrib: 
     436            data = property.get("data") 
     437        else: 
     438            data = property.text 
     439        properties[node_id] = _data(format, data) 
     440 
     441    # Collect all nodes 
     442    for node in tree.findall("nodes/node"): 
     443        node_id = node.get("id") 
     444        node = _node( 
     445            id=node_id, 
     446            title=node.get("title"), 
     447            name=node.get("name"), 
     448            position=tuple_eval(node.get("position")), 
     449            project_name=node.get("project_name"), 
     450            qualified_name=node.get("qualified_name"), 
     451            version=node.get("version", ""), 
     452            data=properties.get(node_id, None) 
     453        ) 
     454        nodes.append(node) 
     455 
     456    for link in tree.findall("links/link"): 
     457        params = _link( 
     458            id=link.get("id"), 
     459            source_node_id=link.get("source_node_id"), 
     460            sink_node_id=link.get("sink_node_id"), 
     461            source_channel=link.get("source_channel"), 
     462            sink_channel=link.get("sink_channel"), 
     463            enabled=link.get("enabled") == "true", 
     464        ) 
     465        links.append(params) 
     466 
     467    for annot in tree.findall("annotations/*"): 
     468        if annot.tag == "text": 
     469            rect = tuple_eval(annot.get("rect", "(0.0, 0.0, 20.0, 20.0)")) 
     470 
     471            font_family = annot.get("font-family", "").strip() 
     472            font_size = annot.get("font-size", "").strip() 
     473 
     474            font = {} 
     475            if font_family: 
     476                font["family"] = font_family 
     477            if font_size: 
     478                font["size"] = int(font_size) 
     479 
     480            annotation = _annotation( 
     481                id=annot.get("id"), 
     482                type="text", 
     483                params=_text_params(rect, annot.text or "", font), 
     484            ) 
     485        elif annot.tag == "arrow": 
     486            start = tuple_eval(annot.get("start", "(0, 0)")) 
     487            end = tuple_eval(annot.get("end", "(0, 0)")) 
     488            color = annot.get("fill", "red") 
     489            annotation = _annotation( 
     490                id=annot.get("id"), 
     491                type="arrow", 
     492                params=_arrow_params((start, end), color) 
     493            ) 
     494        annotations.append(annotation) 
     495 
     496    return _scheme( 
     497        version=scheme.get("version"), 
     498        title=scheme.get("title", ""), 
     499        description=scheme.get("description"), 
     500        nodes=nodes, 
     501        links=links, 
     502        annotations=annotations 
     503    ) 
     504 
     505 
     506def parse_ows_etree_v_1_0(tree): 
     507    nodes, links = [], [] 
     508    id_gen = count() 
     509 
     510    settings = tree.find("settings") 
     511    properties = {} 
     512    if settings is not None: 
     513        data = settings.get("settingsDictionary", None) 
     514        if data: 
     515            try: 
     516                properties = literal_eval(data) 
     517            except Exception: 
     518                log.error("Could not decode properties data.", 
     519                          exc_info=True) 
     520 
     521    for widget in tree.findall("widgets/widget"): 
     522        title = widget.get("caption") 
     523        data = properties.get(title, None) 
     524        node = _node( 
     525            id=next(id_gen), 
     526            title=widget.get("caption"), 
     527            name=None, 
     528            position=(float(widget.get("xPos")), 
     529                      float(widget.get("yPos"))), 
     530            project_name=None, 
     531            qualified_name=widget.get("widgetName"), 
     532            version="", 
     533            data=_data("pickle", data) 
     534        ) 
     535        nodes.append(node) 
     536 
     537    nodes_by_title = dict((node.title, node) for node in nodes) 
     538 
     539    for channel in tree.findall("channels/channel"): 
     540        in_title = channel.get("inWidgetCaption") 
     541        out_title = channel.get("outWidgetCaption") 
     542 
     543        source = nodes_by_title[out_title] 
     544        sink = nodes_by_title[in_title] 
     545        enabled = channel.get("enabled") == "1" 
     546        # repr list of (source_name, sink_name) tuples. 
     547        signals = literal_eval(channel.get("signals")) 
     548 
     549        for source_channel, sink_channel in signals: 
     550            links.append( 
     551                _link(id=next(id_gen), 
     552                      source_node_id=source.id, 
     553                      sink_node_id=sink.id, 
     554                      source_channel=source_channel, 
     555                      sink_channel=sink_channel, 
     556                      enabled=enabled) 
     557            ) 
     558    return _scheme(title="", description="", version="1.0", 
     559                   nodes=nodes, links=links, annotations=[]) 
     560 
     561 
     562def parse_ows_stream(stream): 
     563    doc = parse(stream) 
     564    scheme_el = doc.getroot() 
     565    version = scheme_el.get("version", None) 
     566    if version is None: 
     567        # Fallback: check for "widgets" tag. 
     568        if scheme_el.find("widgets") is not None: 
     569            version = "1.0" 
     570        else: 
     571            log.warning("<scheme> tag does not have a 'version' attribute") 
     572            version = "2.0" 
     573 
     574    if version == "1.0": 
     575        return parse_ows_etree_v_1_0(doc) 
     576    elif version == "2.0": 
     577        return parse_ows_etree_v_2_0(doc) 
     578    else: 
     579        raise ValueError() 
     580 
     581 
     582def resolve_1_0(scheme_desc, registry): 
     583    widgets = registry.widgets() 
     584    widgets_by_name = dict((d.qualified_name.rsplit(".", 1)[-1], d) 
     585                           for d in widgets) 
     586    nodes = scheme_desc.nodes 
     587    for i, node in list(enumerate(nodes)): 
     588        # 1.0's qualified name is the class name only, need to replace it 
     589        # with the full qualified import name 
     590        qname = node.qualified_name 
     591        if qname in widgets_by_name: 
     592            desc = widgets_by_name[qname] 
     593            nodes[i] = node._replace(qualified_name=desc.qualified_name, 
     594                                     project_name=desc.project_name) 
     595 
     596    return scheme_desc._replace(nodes=nodes) 
     597 
     598 
     599def resolve_replaced(scheme_desc, registry): 
     600    widgets = registry.widgets() 
     601    replacements = {} 
     602    for desc in widgets: 
     603        if desc.replaces: 
     604            for repl_qname in desc.replaces: 
     605                replacements[repl_qname] = desc.qualified_name 
     606 
     607    nodes = scheme_desc.nodes 
     608    for i, node in list(enumerate(nodes)): 
     609        if not registry.has_widget(node.qualified_name) and \ 
     610                node.qualified_name in replacements: 
     611            qname = replacements[node.qualified_name] 
     612            desc = registry.widget(qname) 
     613            nodes[i] = node._replace(qualified_name=desc.qualified_name, 
     614                                     project_name=desc.project_name) 
     615 
     616    return scheme_desc._replace(nodes=nodes) 
     617 
     618 
     619def scheme_load(scheme, stream, registry=None, error_handler=None): 
     620    desc = parse_ows_stream(stream) 
     621 
     622    if registry is None: 
     623        registry = global_registry() 
     624 
     625    if error_handler is None: 
     626        def error_handler(exc): 
     627            raise exc 
     628 
     629    if desc.version == "1.0": 
     630        desc = resolve_1_0(desc, registry, error_handler) 
     631 
     632    desc = resolve_replaced(desc, registry) 
     633    nodes_not_found = [] 
     634    nodes = [] 
     635    nodes_by_id = {} 
     636    links = [] 
     637    annotations = [] 
     638 
     639    scheme.title = desc.title 
     640    scheme.description = desc.description 
     641 
     642    for node_d in desc.nodes: 
     643        try: 
     644            w_desc = registry.widget(node_d.qualified_name) 
     645        except KeyError as ex: 
     646            error_handler(UnknownWidgetDefinition(*ex.args)) 
     647            nodes_not_found.append(node_d.id) 
     648        else: 
     649            node = SchemeNode( 
     650                w_desc, title=node_d.title, position=node_d.position) 
     651            data = node_d.data 
     652 
     653            if data: 
     654                try: 
     655                    properties = loads(data.data, data.format) 
     656                except Exception: 
     657                    log.error("Could not load properties for %r.", node.title, 
     658                              exc_info=True) 
     659                else: 
     660                    node.properties = properties 
     661 
     662            nodes.append(node) 
     663            nodes_by_id[node_d.id] = node 
     664 
     665    for link_d in desc.links: 
     666        source_id = link_d.source_node_id 
     667        sink_id = link_d.sink_node_id 
     668 
     669        if source_id in nodes_not_found or sink_id in nodes_not_found: 
     670            continue 
     671 
     672        source = nodes_by_id[source_id] 
     673        sink = nodes_by_id[sink_id] 
     674        try: 
     675            link = SchemeLink(source, link_d.source_channel, 
     676                              sink, link_d.sink_channel, 
     677                              enabled=link_d.enabled) 
     678        except (ValueError, IncompatibleChannelTypeError) as ex: 
     679            error_handler(ex) 
     680        else: 
     681            links.append(link) 
     682 
     683    for annot_d in desc.annotations: 
     684        params = annot_d.params 
     685        if annot_d.type == "text": 
     686            annot = SchemeTextAnnotation(params.geometry, params.text, 
     687                                         params.font) 
     688        elif annot_d.type == "arrow": 
     689            start, end = params.geometry 
     690            annot = SchemeArrowAnnotation(start, end, params.color) 
     691 
     692        else: 
     693            log.warning("Ignoring unknown annotation type: %r", annot_d.type) 
     694        annotations.append(annot) 
     695 
     696    for node in nodes: 
     697        scheme.add_node(node) 
     698 
     699    for link in links: 
     700        scheme.add_link(link) 
     701 
     702    for annot in annotations: 
     703        scheme.add_annotation(annot) 
     704 
     705    return scheme 
    388706 
    389707 
Note: See TracChangeset for help on using the changeset viewer.