예제 #1
0
파일: gml.py 프로젝트: ulethHCI/GLuskap
def load(graph, fobj, queryCallback=None, warningCallback=None):
    """Load .gml file

    Arguments:
    graph -- the graph to be created from the loaded file
    fobj -- a file object of the GML file to be read
    queryCallback -- function to display a default query dialog (default None)
    warningCallback -- function to display a default warning dialog
                       (default None)

    """

    try:
        root = GMLList(Stream(fobj))
    except EOF:
        raise ImportError, "Premature end of file"
    except Unbalanced:
        raise ImportError, "Unbalanced delimiters in file"
    except TypeMismatch:
        raise ImportError, "Unintelligible value in file"
    except EOList:
        #!!! shouldn't happen
        raise ImportError, "Internal error"

    #XXX#
    #XXX# Debugging print the tree
    #XXX#
    def traverse(a, i=0):
        if i <= 0:
            print ""
            print ""

        if isinstance(a, GMLList):
            print ("    " * i) + repr(a)
            for k, v in a.data.items():
                print "    " * i + "  " + repr(k)
                for x in v:
                    traverse(x.data, i=i + 1)
        else:
            print ("    " * i) + repr(a)

    #XXX#
    #XXX#
    #XXX#

    if 'graph' not in root.data:
        raise ImportError, "No graphs in file." % str(root.data)

    # We only load the first graph in the file.
    if len(root.data['graph']) > 1 and warningCallback:
        warningCallback("GLuskap does not support multiple graphs per file. "
                        "The first graph in the file will be processed.")

    # Check GML version if it is supported
    if 'version' in root.data and root.data['version'][0].data != 1:
        if queryCallback:
            result = queryCallback("Unsupported GML version %s."
                                   "\n\nWould you like to continue?" % str(root.data['version'][0].data),
                                   override=True,
                                   buttons=['Yes', 'No'])
            if result == CustomButtonDialog.ID['Yes']:
                pass
            else:
                raise ImportError, "Import aborted."
        else:
            raise ImportError, "Unsupported GML version %s." % str(root.data['version'][0].data)

    graphbranch = root.data['graph'][0].data
    if not isinstance(graphbranch, GMLList):
        raise ImportError, "GML format error: graph is not a List"

    if 'directed' in graphbranch.data and graphbranch.data['directed'] != 0 and warningCallback:
        warningCallback("GLuskap does not support directed graphs. "
                        "All edges will be processed as undirected.")

    # Boolean variables to control if the rest of the import errors are handled
    # automatically
    yestoall = False
    yesall_duplicate_vert = False
    yesall_duplicate_edge = False
    yesall_degenerate_edge = False
    yesall_color = False
    yesall_nonexist_vert = False
    yesall_invalid_bends = False

    #
    # Vertices (Nodes)
    #
    vertexIndex = {}

    for vxb in graphbranch.data['node']:
        vxbranch = vxb.data
        if not isinstance(vxbranch, GMLList):
            raise ImportError, ("GML format error: node '%s' is not a List" %
                                (str(vxbranch)))
        if 'id' not in vxbranch.data:
            raise ImportError, "GML format error: node without ID"
        #
        # Extension - we allow node IDs of type String as well as
        # integer.
        #
        if not isinstance(vxbranch.data['id'][0].data, (int, GMLString)):
            raise ImportError, ("GML format error: node ID '%s' of "
                                "unsupportedtype" % (vxbranch.data['id'][0].data))

        id = str(vxbranch.data['id'][0].data)
        if id in vertexIndex:
            # Duplicate vertex
            if yesall_duplicate_vert or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Duplicate vertex detected: id %s" % (id))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_duplicate_vert = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, "Duplicate vertex detected: id %s" % (id)

        v = Vertex(id=id)

        if 'label' in vxbranch.data:
            v.name = str(vxbranch.data['label'][0].data)
        if 'graphics' in vxbranch.data:
            pos = list(v.pos)
            rad = v.radius
            color = list(v.color)
            grbranch = vxbranch.data['graphics'][0].data

            if 'x' in grbranch.data:
                pos[X_AXIS] = float(grbranch.data['x'][0].data)
            if 'y' in grbranch.data:
                pos[Y_AXIS] = float(grbranch.data['y'][0].data)
            if 'z' in grbranch.data:
                pos[Z_AXIS] = float(grbranch.data['z'][0].data)
            if 'w' in grbranch.data:
                radius = float(grbranch.data['w'][0].data)
            if 'h' in grbranch.data:
                radius = max(radius, float(grbranch.data['h'][0].data))
            if 'd' in grbranch.data:
                radius = max(radius, float(grbranch.data['d'][0].data))
            if 'fill' in grbranch.data:
                fail = False
                fillstr = grbranch.data['fill'][0].data
                if not isinstance(fillstr, GMLString):
                    fail = True
                    fail = "fill must be a String"
                elif not len(str(fillstr)) > 0:
                    fail = "fill string too short"
                elif str(fillstr)[0] != '#':
                    # Named color
                    try:
                        color = colorByName(str(fillstr))
                    except UnknownColor:
                        fail = "unrecognized named color"
                else:
                    # Hex color
                    fillstr = str(fillstr)
                    if len(fillstr) == 7:
                        r = fillstr[1:3]
                        g = fillstr[3:5]
                        b = fillstr[5:7]
                    elif len(fillstr) == 4:
                        r = fillstr[1]
                        g = fillstr[2]
                        b = fillstr[3]
                    else:
                        fail = "fill must be of form '#fff' or '#ffffff'"

                    if not fail:
                        color = [x / 255.0 for x in (int(r, 16), int(g, 16), int(b, 16))]
                if fail and not yesall_color and not yestoall:
                    # Invalid vertex color
                    if queryCallback:
                        result = queryCallback("Invalid vertex fill color (%s): "
                                               "id %s, fill '%s' "
                                               "\n\nUse default color and continue?" %
                                               (fail, id, fillstr),
                                               override=True)
                        if result == CustomButtonDialog.ID['Yes']:
                            pass
                        elif result == CustomButtonDialog.ID['Yes to similar errors']:
                            yesall_color = True
                        elif result == CustomButtonDialog.ID['Yes to all errors']:
                            yestoall = True
                        else:
                            raise ImportError, "Import aborted."
                    else:
                        raise ImportError, ("Invalid vertex fill color (%s): "
                                            "id %s, fill '%s'" %
                                            (fail, id, fillstr))
            #
            # Apply attributes
            #
            v.pos = tuple(pos)
            v.radius = radius
            v.color = tuple(color)

        graph.addVertex(v)
        vertexIndex[v.id] = v

    #
    # Edges
    #
    edgeIndex = {}

    for edgeb in graphbranch.data['edge']:
        edgebranch = edgeb.data
        if not isinstance(edgebranch, GMLList):
            raise ImportError, ("GML format error: edge '%s' is not a List" %
                                (str(edgebranch)))
        if 'source' not in edgebranch.data:
            raise ImportError, "GML format error: edge without source"
        if 'target' not in edgebranch.data:
            raise ImportError, "GML format error: edge without target"
        #
        # Extension - we allow node IDs of type String as well as
        # integer.
        #
        if not isinstance(edgebranch.data['source'][0].data, (int, GMLString)):
            raise ImportError, ("GML format error: edge source ID '%s' "
                                "of unsupported type" %
                                (edgebranch.data['source'][0].data))
        if not isinstance(edgebranch.data['target'][0].data, (int, GMLString)):
            raise ImportError, ("GML format error: edge target ID '%s' "
                                "of unsupported type" %
                                (edgebranch.data['target'][0].data))

        src = str(edgebranch.data['source'][0].data)
        tgt = str(edgebranch.data['target'][0].data)
        if src not in vertexIndex:
            # Nonexistent source vertex
            if yesall_nonexist_vert or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Edge specifies nonexistent "
                                       "source vertex %s" % (src))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_nonexist_vert = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Edge specifies nonexistent "
                                    "source vertex %s" % (src))
        if tgt not in vertexIndex:
            # Nonexistent target vertex
            if yesall_nonexist_vert or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Edge specifies nonexistent target "
                                       "vertex %s" % (tgt))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_nonexist_vert = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Edge specifies nonexistent "
                                    "target vertex %s" % (tgt))

        e = Edge(source=vertexIndex[src], target=vertexIndex[tgt])

        if (src, tgt) in edgeIndex:
            # Duplicate edge
            if yesall_duplicate_edge or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Duplicate edge detected: "
                                       "%s -> %s" % (src, tgt))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_duplicate_edge = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Duplicate edge detected:"
                                    "%s -> %s" % (src, tgt))
        if (tgt, src) in edgeIndex:
            # Reversed Duplicate edge
            if yesall_duplicate_edge or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Duplicate (reversed) edge detected: "
                                       "%s -> %s" % (src, tgt))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_duplicate_edge = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Duplicate (reversed) edge detected: "
                                    "%s -> %s" % (src, tgt))
        if src == tgt:
            # Degenerate edge
            if yesall_degenerate_edge or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Degenerate edge detected: %s -> %s" %
                                       (src, tgt))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_degenerate_edge = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Degenerate edge detected: %s -> %s" %
                                    (src, tgt))

        if 'label' in edgebranch.data:
            e.attribute = str(edgebranch.data['label'][0].data)

        grbranch = None
        if 'graphics' in edgebranch.data:
            rad = e.radius
            color = list(e.color)
            grbranch = edgebranch.data['graphics'][0].data

            if 'width' in grbranch.data:
                e.radius = float(grbranch.data['width'][0].data)
            if ('type' in grbranch.data and
                        str(grbranch.data['type'][0].data) != "line" and
                    warningCallback):
                warningCallback("Edge of graphic type '%s' converted to line."
                                % str(grbranch.data['type'][0].data))

            if 'fill' in grbranch.data:
                fail = False
                fillstr = grbranch.data['fill'][0].data
                if not isinstance(fillstr, GMLString):
                    fail = True
                    fail = "fill must be a String"
                elif not len(str(fillstr)) > 0:
                    fail = "fill string too short"
                elif str(fillstr)[0] != '#':
                    # Named color
                    try:
                        color = colorByName(str(fillstr))
                    except UnknownColor:
                        fail = "unrecognized named color"
                else:
                    # Hex color
                    fillstr = str(fillstr)
                    if len(fillstr) == 7:
                        r = fillstr[1:3]
                        g = fillstr[3:5]
                        b = fillstr[5:7]
                    elif len(fillstr) == 4:
                        r = fillstr[1]
                        g = fillstr[2]
                        b = fillstr[3]
                    else:
                        fail = "fill must be of form '#fff' or '#ffffff'"

                    if not fail:
                        color = [x / 255.0 for x in (int(r, 16), int(g, 16), int(b, 16))]
                if fail and not yesall_color and not yestoall:
                    # Invalid edge fill color
                    if queryCallback:
                        result = queryCallback("Invalid edge fill color (%s): "
                                               "%s -> %s, fill '%s'"
                                               "\n\nUse default color and continue?" %
                                               (fail, src, tgt, fillstr),
                                               override=True)
                        if result == CustomButtonDialog.ID['Yes']:
                            pass
                        elif result == CustomButtonDialog.ID['Yes to similar errors']:
                            yesall_color = True
                        elif result == CustomButtonDialog.ID['Yes to all errors']:
                            yestoall = True
                        else:
                            raise ImportError, "Import aborted."
                    else:
                        raise ImportError, ("Invalid edge fill color (%s): "
                                            "%s -> %s, fill '%s'" %
                                            (fail, src, tgt, fillstr))


            #
            # Apply attributes
            #
            e.color = tuple(color)

        graph.addEdge(e)
        edgeIndex[(src, tgt)] = e

        #
        # Handle bending edges here
        #
        if grbranch != None and 'line' in grbranch.data:
            linebranch = grbranch.data['line'][0].data
            if 'point' in linebranch.data:
                if len(linebranch.data['point']) < 2:
                    if yestoall or yesall_invalid_bends:
                        continue
                    # Insufficient number of bendpoints
                    if queryCallback:
                        result = queryCallback("Insufficient number of points"
                                               " for edge %s -> %s" % (src, tgt))
                        if result == CustomButtonDialog.ID['Yes']:
                            pass
                        elif result == CustomButtonDialog.ID['Yes to similar errors']:
                            yesall_invalid_bends = True
                        elif result == CustomButtonDialog.ID['Yes to all errors']:
                            yestoall = True
                        else:
                            raise ImportError, "Import aborted."
                    else:
                        raise ImportError, ("Insufficient number of points"
                                            "for edge %s -> %s" % (src, tgt))
                else:
                    # Delete the first and last points - those are the src and tgt
                    del linebranch.data['point'][0]
                    del linebranch.data['point'][-1]
                    tmpEdge = e
                    tgtVx = e.target
                    for ptb in linebranch.data['point']:
                        ptbranch = ptb.data
                        pos = [0.0, 0.0, 0.0]
                        if 'x' in ptbranch.data:
                            pos[X_AXIS] = float(ptbranch.data['x'][0].data)
                        if 'y' in ptbranch.data:
                            pos[Y_AXIS] = float(ptbranch.data['y'][0].data)
                        if 'z' in ptbranch.data:
                            pos[Z_AXIS] = float(ptbranch.data['z'][0].data)
                        bendVx = graph.bendEdge(tmpEdge, tuple(pos))

                        tmpEdge = graph.findEdgeBetween(bendVx, tgtVx)[0]
예제 #2
0
파일: dot.py 프로젝트: ulethHCI/GLuskap
def load(graph, fobj, queryCallback=None, warningCallback=None):
    """Load .gv or .dot file

    Arguments:
    graph -- the graph to be created from the loaded file
    fobj -- a file object of the DOT file to be read
    queryCallback -- function to display a default query dialog (default None)
    warningCallback -- function to display a default warning dialog
                       (default None)

    """

    # Set global variables to in DOTGraph for automatically replacing
    # attributes of duplicate nodes and edges
    global _yestoall_nodes
    global _yestoall_edges
    _yestoall_nodes = None
    _yestoall_edges = None

    try:
        dotgraph = DOTGraph(Stream(fobj), queryCallback=queryCallback,
                            warningCallback=warningCallback)
    except EOF:
        raise ImportError, "Premature end of file"
    except Unbalanced:
        raise ImportError, "Unbalanced delimiters in file"

    # Boolean variables to control if the rest of the import errors are handled
    # automatically
    yestoall = False
    yesall_color = False
    yesall_degenerate_edge = False

    def getColor(color):
        """Retun an RGB float triple of a color string. Return False if invalid

        Arguments:
        color -- a string representing a color
        """

        # Hex color
        if color[0] == '#' and len(color) == 7:
            fillstr = color
            r = fillstr[1:3]
            g = fillstr[3:5]
            b = fillstr[5:7]
            try:
                color = [x / 255.0 for x in (int(r, 16), int(g, 16), int(b, 16))]
                return color
            except:
                return False

        # HSV float triple
        elif ',' in color or ' ' in color:
            color = color.replace(" ", "").split(',', 3)
            try:
                color = [float(x) for x in color]
                color = colorsys.hsv_to_rgb(color[0], color[1], color[2])
                return color
            except:
                return False
        # X11 Color Name
        else:
            try:
                color = colorByName(color)
                return color
            except UnknownColor:
                return False

    # Add all the vertices in the dotgraph to the context graph
    for vertex, attributes in dotgraph.nodes.items():
        v = Vertex(id=vertex)
        if 'label' in attributes:
            v.name = attributes['label']
        if 'width' in attributes:
            v.radius = float(attributes['width'])
        if 'pos' in attributes:
            # remove ! and spaces, then split numbers
            pos = attributes['pos'][:-1].replace(" ", "").split(',', 3)
            if len(pos) < 3:
                pos.append(0.0)
            v.pos = (float(pos[0]), float(pos[1]), float(pos[2]))
        if 'color' in attributes:
            color = getColor(attributes['color'])
            if color != False:
                v.color = tuple(color)
            elif not yesall_color and not yestoall:
                # Invalid color
                if queryCallback:
                    result = queryCallback("Invalid vertex color %s for %s. "
                                           "Assign default color?" %
                                           (attributes['color'], vertex), True)
                    if result == CustomButtonDialog.ID['Yes']:
                        pass
                    elif result == CustomButtonDialog.ID['Yes to similar errors']:
                        yesall_color = True
                    elif result == CustomButtonDialog.ID['Yes to all errors']:
                        yestoall = True
                    else:
                        raise ImportError, "Import aborted."
                else:
                    raise ImportError("Invalid vertex color %s for %s" %
                                      (attributes['color'], vertex))

        graph.addVertex(v)

    # Add all the edges in the dotgraph to the context grpah
    for edge, attributes in dotgraph.edges.items():
        source = graph.findVertexByID(edge[0])
        target = graph.findVertexByID(edge[1])

        if source == target:
            # Degenerate edge
            if yesall_degenerate_edge or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Degenerate edge detected: %s -> %s" %
                                       (source.id, target.id))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_degenerate_edge = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError("Degenerate edge detected: %s -> %s" %
                                  (source.id, target.id))

        e = Edge(source, target)

        if 'penwidth' in attributes:
            e.radius = float(attributes['penwidth'])
        if 'color' in attributes:
            color = getColor(attributes['color'])
            if color != False:
                e.color = tuple(color)
            elif not yesall_color and not yestoall:
                # Invalid color
                if queryCallback:
                    result = queryCallback("Invalid edge color %s for (%s,%s) "
                                           "Assign default color?" %
                                           (attributes['color'],
                                            edge[0], edge[1]), True)
                    if result == CustomButtonDialog.ID['Yes']:
                        pass
                    elif result == CustomButtonDialog.ID['Yes to similar errors']:
                        yesall_color = True
                    elif result == CustomButtonDialog.ID['Yes to all errors']:
                        yestoall = True
                    else:
                        raise ImportError, "Import aborted."
                else:
                    raise ImportError("Invalid edge color %s for (%s,%s)" %
                                      (attributes['color'], edge[0], edge[1]))
        graph.addEdge(e)
예제 #3
0
파일: tlp.py 프로젝트: ulethHCI/GLuskap
def load(graph, fobj, queryCallback=None, warningCallback=None):
    """ Load a TLP file and create a graph from it

    Arguments:
    graph -- the graph to be created from the loaded file
    fobj -- a file object of the TLP file to be read
    queryCallback -- function to display a default query dialog (default None)
    warningCallback -- function to display a default warning dialog
                       (default None)
    
    """

    try:
        tlpgraph = TLPGraph(Stream(fobj))
    except Unbalanced:
        raise ImportError, "Unbalanced delimiters in file"
    except TypeMismatch:
        raise ImportError, "Unintelligible value in file"
    except EOF:
        raise ImportError, "Premature end of file"

    # Boolean variables to control if the rest of the import errors are handled
    # automatically
    yestoall = False
    yesall_duplicate_edge = False
    yesall_degenerate_edge = False
    yesall_nonexist_vert = False

    # Make vertices and apply properties
    for vertex, properties in tlpgraph.nodes.items():
        v = Vertex(vertex)
        if "viewLabel" in properties:
            v.name = properties["viewLabel"]
        if "viewLayout" in properties:
            pos = string_to_tuple(properties["viewLayout"])
            v.pos = tuple([float(x) for x in pos])
        if "viewSize" in properties:
            v.radius = float(string_to_tuple(properties["viewSize"])[0])
        if "viewColor" in properties:
            color = string_to_tuple(properties["viewColor"])[:3]
            v.color = tuple([float(x) / 255 for x in color])
        graph.addVertex(v)

    # Make edges and apply properties
    edges = tlpgraph.edges.values()
    edges.extend(tlpgraph.lost_edges)
    for properties in edges:

        source = properties["source"]
        target = properties["target"]

        # Raise an error if edge is degenerate, or source or target vertices
        # are non existent. Automatically handle errors if yes all is selected
        if source == target:
            # Degenerate Edge
            if yesall_degenerate_edge or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Degenerate edge detected: %s -> %s" % (source, target))
                if result == CustomButtonDialog.ID["Yes"]:
                    continue
                elif result == CustomButtonDialog.ID["Yes to similar errors"]:
                    yesall_degenerate_edge = True
                    continue
                elif result == CustomButtonDialog.ID["Yes to all errors"]:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError("Degenerate edge detected: %s -> %s" % (source, target))
        if source not in tlpgraph.nodes.keys():
            # Nonexistent vertex
            if yesall_nonexist_vert or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Edge specifies nonexistent " "source vertex %s" % (source))
                if result == CustomButtonDialog.ID["Yes"]:
                    continue
                elif result == CustomButtonDialog.ID["Yes to similar errors"]:
                    yesall_nonexist_vert = True
                    continue
                elif result == CustomButtonDialog.ID["Yes to all errors"]:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Edge specifies nonexistent source vertex" " %s" % (source))
        if target not in tlpgraph.nodes.keys():
            # Nonexistent vertex
            if yesall_nonexist_vert or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Edge specifies nonexistent " "target vertex %s" % (target))
                if result == CustomButtonDialog.ID["Yes"]:
                    continue
                elif result == CustomButtonDialog.ID["Yes to similar errors"]:
                    yesall_nonexist_vert = True
                    continue
                elif result == CustomButtonDialog.ID["Yes to all errors"]:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Edge specifies nonexistent target vertex" " %s" % (target))
        source = graph.findVertexByID(source)
        target = graph.findVertexByID(target)
        found_edges = graph.findEdgeBetween(source, target)
        if len(found_edges) == 0:
            e = Edge(source, target)
        else:
            # Raise error for duplicate edges
            if source.id == found_edges[0].source.id:
                if yesall_duplicate_edge or yestoall:
                    continue
                if queryCallback:
                    result = queryCallback("Duplicate edge detected: %s -> %s" % (source.id, target.id))
                    if result == CustomButtonDialog.ID["Yes"]:
                        continue
                    elif result == CustomButtonDialog.ID["Yes to " "similar errors"]:
                        yesall_duplicate_edge = True
                        continue
                    elif result == CustomButtonDialog.ID["Yes to all errors"]:
                        yestoall = True
                        continue
                    else:
                        raise ImportError, "Import aborted."
                else:
                    raise ImportError, ("Duplicate edge detected: %s -> %s" % (source.id, target.id))
            # Raise error for reversed duplicate edge
            elif source.id == found_edges[0].target.id:
                if yesall_duplicate_edge or yestoall:
                    continue
                if queryCallback:
                    result = queryCallback("Duplicate edge detected " "(reversed): %s -> %s" % (target.id, source.id))
                    if result == CustomButtonDialog.ID["Yes"]:
                        continue
                    elif result == CustomButtonDialog.ID["Yes to " "similar errors"]:
                        yesall_duplicate_edge = True
                        continue
                    elif result == CustomButtonDialog.ID["Yes to all errors"]:
                        yestoall = True
                        continue
                    else:
                        raise ImportError, "Import aborted."
                else:
                    raise ImportError, ("Duplicate edge detected (reversed): " "%s -> %s" % (target.id, source.id))

        if "viewColor" in properties:
            color = string_to_tuple(properties["viewColor"])[:3]
            e.color = tuple([float(x) / 255 for x in color])
        if "viewSize" in properties:
            radius = string_to_tuple(properties["viewSize"])
            e.radius = (float(radius[0]) + float(radius[1])) / 2
        graph.addEdge(e)
        if "viewLayout" in properties and properties["viewLayout"] != "()":
            # Convert the string of bendpoint tuples to a tuple of strings
            # By removing whitespace and inserting a pipe between
            # each tuple, then splitting at each pipe
            bends = properties["viewLayout"]
            bends = bends.replace(" ", "").replace(")(", ");(")
            bends = bends.replace("),(", ");(")
            bends = tuple(bends[1:-1].split(";"))
            # Create bends
            for bend in bends:
                bend = string_to_tuple(bend)
                bend = tuple([float(x) for x in bend])
                graph.bendEdge(e, bend)
예제 #4
0
파일: graphml.py 프로젝트: ulethHCI/GLuskap
def load(graph, fobj, queryCallback=None, warningCallback=None):
    """ Load a GraphML file and create a graph from it

    Arguments:
    graph -- the graph to be created from the loaded file
    fobj -- a file object of the GraphML file to be read
    queryCallback -- function to display a default query dialog (default None)
    warningCallback -- function to display a default warning dialog
                       (default None)
    """

    from xml.dom.minidom import parse

    grml_dom = parse(fobj)
    GraphML = grml_dom.documentElement

    typeTable = {
    'int': int,
    'long': long,
    'float': float,
    'double': float,
    'string': str,
    'boolean': bool,
    }
    #
    # Read the keys part of the GraphML file
    #
    keyInfo = {}
    defaults = {}

    # Boolean variables to control if the rest of the import errors are handled
    # automatically
    yestoall = False
    yesall_duplicate_vert = False
    yesall_duplicate_edge = False
    yesall_degenerate_edge = False
    yesall_color = False
    yesall_nonexist_vert = False

    # Store the graph attributes in a dictionary named keyInfo,
    # and storing any defaults in the dictionary named defaults
    for keyNode in GraphML.getElementsByTagName("key"):
        attrs = dict(
            [(str(a), str(b)) for a, b in keyNode.attributes.items()]
        )
        id = attrs.pop('id')

        if 'for' in attrs and attrs['for'] not in defaults:
            defaults[attrs['for']] = {}

        # Add the desc to the info for this key
        desc = keyNode.getElementsByTagName("desc")
        if len(desc) > 0:
            attrs['desc'] = str(desc[0].childNodes[0].wholeText).strip()

        # Convert the type to a python native type
        if 'attr.type' in attrs.keys():
            attrs['attr.type'] = typeTable[attrs['attr.type']]

        # If there's a default, store it
        default = keyNode.getElementsByTagName("default")
        if len(default) > 0:
            defaults[attrs['for']][id] = attrs['attr.type'](default[0].childNodes[0].wholeText.strip())

        # Dupicate id's are mapped depending on 'for' type
        if id not in keyInfo:
            keyInfo[id] = {}
        if 'for' in attrs:
            keyInfo[id][attrs['for']] = attrs
        else:
            keyInfo[id]['forNotSpecified'] = attrs

    # We read only the first graph in a GraphML file.
    graphs = GraphML.getElementsByTagName('graph')
    if len(graphs) < 1:
        raise ImportError, "No graphs in this file!"

    if len(graphs) > 1 and warningCallback:
        warningCallback("Multiple graphs per file are not supported. "
                        "The first graph in the file will be processed.")

    graphNode = graphs[0]
    attrs = dict(
        [(str(a), str(b)) for a, b in graphNode.attributes.items()]
    )

    if 'edgedefault' not in attrs:
        raise ImportError, "No edge default directedness found!"

    if attrs['edgedefault'] != 'undirected' and warningCallback:
        warningCallback("Directed graphs are not supported. "
                        "Directed edges will be converted.")

    #
    # Set up an index of vertex IDs and edge tuples - this allows
    # us to check uniqueness and also eliminates costly
    # findVertexByID calls.
    #
    vertexIndex = {}
    edgeIndex = {}

    # Read vertices (aka nodes)
    for vertNode in graphNode.getElementsByTagName('node'):
        vertAttrs = dict(
            [(str(a), str(b)) for a, b in vertNode.attributes.items()]
        )
        # Create a dict that maps vertex attribute names to their values
        for dataNode in vertNode.getElementsByTagName('data'):
            dataAttrs = dict(
                [(str(a), str(b)) for a, b in dataNode.attributes.items()]
            )
            if 'node' in keyInfo[dataAttrs['key']]:
                key = keyInfo[dataAttrs['key']]['node']
            else:
                key = keyInfo[dataAttrs['key']]['forNotSpecified']
            if dataNode.childNodes != []:
                if 'attr.type' in key.keys():
                    value = key['attr.type'](dataNode.childNodes[0].wholeText.strip())
                else:
                    value = str(dataNode.childNodes[0].wholeText.strip())
                if 'attr.name' in key.keys():
                    vertAttrs[key['attr.name']] = value

        # Assign defaults
        if len(defaults) > 0 and defaults['node']:
            for prop, value in defaults['node'].items():
                if prop not in vertAttrs:
                    vertAttrs[prop] = value

        # Raise ImportError if vertex is a duplicate
        if vertAttrs['id'] in vertexIndex:
            if yesall_duplicate_vert or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Duplicate vertex detected: id %s" %
                                       (vertAttrs['id']))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_duplicate_vert = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Duplicate vertex detected: id %s" %
                                    (vertAttrs['id']))

        v = Vertex(id=vertAttrs['id'])

        # Determine the color of the vertex, accepting either
        # r,g,b or red,green,blue for the names of the color attributes
        if ('red' in vertAttrs or 'blue' in vertAttrs or 'green' in vertAttrs or
                    'r' in vertAttrs or 'b' in vertAttrs or 'g' in vertAttrs):
            color = [0.0, 0.0, 0.0]
            if 'red' in vertAttrs:
                color[0] = color[0] + vertAttrs['red']
            elif 'r' in vertAttrs:
                color[0] = color[0] + vertAttrs['r']
            if 'green' in vertAttrs:
                color[1] = color[1] + vertAttrs['green']
            elif 'g' in vertAttrs:
                color[1] = color[1] + vertAttrs['g']
            if 'blue' in vertAttrs:
                color[2] = color[2] + vertAttrs['blue']
            elif 'b' in vertAttrs:
                color[2] = color[2] + vertAttrs['b']
            # If color uses 0 to 255 for colors instead of 0.0 to 1.0, convert
            if color[0] > 1.0 or color[1] > 1.0 or color[2] > 1.0:
                color[0] /= 255
                color[1] /= 255
                color[2] /= 255
            v.color = tuple(color)
        # Determine the color of the vertex if using 'fill' and hex
        elif 'fill' in vertAttrs:
            fail = False
            fillstr = vertAttrs['fill']
            if len(fillstr) == 7:
                r = fillstr[1:3]
                g = fillstr[3:5]
                b = fillstr[5:7]
            elif len(fillstr) == 4:
                r = fillstr[1]
                g = fillstr[2]
                b = fillstr[3]
            else:
                fail = "fill must be of form '#fff' or '#ffffff'"
            if not fail:
                color = [x / 255.0 for x in (int(r, 16), int(g, 16), int(b, 16))]
                v.color = tuple(color)
            elif fail and not yesall_color and not yestoall:
                if queryCallback:
                    result = queryCallback("Invalid vertex fill color (%s): id "
                                           "%s, fill '%s' \n\nUse default color"
                                           "and continue?" %
                                           (fail, id, fillstr), override=True)
                    if result == CustomButtonDialog.ID['Yes']:
                        pass
                    elif result == CustomButtonDialog.ID['Yes to similar errors']:
                        yesall_color = True
                    elif result == CustomButtonDialog.ID['Yes to all errors']:
                        yestoall = True
                    else:
                        raise ImportError, "Import aborted."
                else:
                    raise ImportError, ("Invalid vertex fill color (%s): id %s, "
                                        "fill '%s'" % (fail, id, fillstr))

        # Determine the position of each vertex
        if 'x' in vertAttrs or 'y' in vertAttrs or 'z' in vertAttrs:
            pos = [0.0, 0.0, 0.0]
            if 'x' in vertAttrs and (isinstance(vertAttrs['x'], float) or
                                         isinstance(vertAttrs['x'], int)):
                pos[0] = pos[0] + vertAttrs['x']
            if 'y' in vertAttrs and (isinstance(vertAttrs['y'], float) or
                                         isinstance(vertAttrs['y'], int)):
                pos[1] = pos[1] + vertAttrs['y']
            if 'z' in vertAttrs and (isinstance(vertAttrs['z'], float) or
                                         isinstance(vertAttrs['z'], int)):
                pos[2] = pos[2] + vertAttrs['z']

            v.pos = tuple(pos)

        # Accept either radius or size for the radius of a vertex
        if 'radius' in vertAttrs:
            v.radius = vertAttrs['radius']
        elif 'size' in vertAttrs:
            v.radius = vertAttrs['size']
        # Accept either name or label for the label of a vertex
        if 'name' in vertAttrs:
            v.name = vertAttrs['name']
        elif 'label' in vertAttrs:
            v.name = vertAttrs['label']
        graph.addVertex(v)
        vertexIndex[v.id] = v

    # Read edges
    for edgeNode in graphNode.getElementsByTagName('edge'):
        edgeAttrs = dict(
            [(str(a), str(b)) for a, b in edgeNode.attributes.items()]
        )
        # Create a dict edgeAttrs that maps edge attributes to their values
        for dataNode in edgeNode.getElementsByTagName('data'):
            dataAttrs = dict(
                [(str(a), str(b)) for a, b in dataNode.attributes.items()]
            )
            if 'edge' in keyInfo[dataAttrs['key']]:
                key = keyInfo[dataAttrs['key']]['edge']
            else:
                key = keyInfo[dataAttrs['key']]['forNotSpecified']
            if dataNode.childNodes != []:
                if 'attr.type' in key.keys():
                    value = key['attr.type'](dataNode.childNodes[0].wholeText.strip())
                else:
                    value = str(dataNode.childNodes[0].wholeText.strip())
                if 'attr.name' in key.keys():
                    edgeAttrs[key['attr.name']] = value

        # Assign defaults
        if len(defaults) > 0 and defaults['edge']:
            for prop, value in defaults['edge'].items():
                if prop not in edgeAttrs:
                    edgeAttrs[prop] = value

        # Raise ImportError if edge references a nonexistent vertex, or if
        # it is a duplicate edge, or if source is the same as the target
        if edgeAttrs['source'] not in vertexIndex:
            if yesall_nonexist_vert or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Edge specifies nonexistent source "
                                       "vertex %s" % (edgeAttrs['source']))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_nonexist_vert = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Edge specifies nonexistent source vertex "
                                    "%s" % (edgeAttrs['source']))
        if edgeAttrs['target'] not in vertexIndex:
            if yesall_nonexist_vert or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Edge specifies nonexistent target "
                                       "vertex %s" % (edgeAttrs['target']))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_nonexist_vert = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Edge specifies nonexistent target vertex "
                                    "%s" % (edgeAttrs['target']))
        if (edgeAttrs['source'], edgeAttrs['target']) in edgeIndex:
            if yesall_duplicate_edge or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Duplicate edge detected: %s -> %s" %
                                       (edgeAttrs['source'], edgeAttrs['target']))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_duplicate_edge = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Duplicate edge detected: %s -> %s" %
                                    (edgeAttrs['source'], edgeAttrs['target']))
        if (edgeAttrs['target'], edgeAttrs['source']) in edgeIndex:
            if yesall_duplicate_edge or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Duplicate (reversed) edge detected: "
                                       "%s -> %s" %
                                       (edgeAttrs['source'], edgeAttrs['target']))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_duplicate_edge = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError, ("Duplicate (reversed) edge detected: %s -> %s" %
                                    (edgeAttrs['source'], edgeAttrs['target']))
        if edgeAttrs['source'] == edgeAttrs['target']:
            if yesall_degenerate_edge or yestoall:
                continue
            if queryCallback:
                result = queryCallback("Degenerate edge detected: %s -> %s" %
                                       (edgeAttrs['source'], edgeAttrs['target']))
                if result == CustomButtonDialog.ID['Yes']:
                    continue
                elif result == CustomButtonDialog.ID['Yes to similar errors']:
                    yesall_degenerate_edge = True
                    continue
                elif result == CustomButtonDialog.ID['Yes to all errors']:
                    yestoall = True
                    continue
                else:
                    raise ImportError, "Import aborted."
            else:
                raise ImportError("Degenerate edge detected: %s -> %s" %
                                  (edgeAttrs['source'], edgeAttrs['target']))

        e = Edge(source=vertexIndex[edgeAttrs['source']], \
                 target=vertexIndex[edgeAttrs['target']])

        # Determine the color of the vertex, accepting either
        # r,g,b or red,green,blue for the names of the color attributes
        if ('red' in edgeAttrs or 'blue' in edgeAttrs or 'green' in edgeAttrs or
                    'r' in edgeAttrs or 'b' in edgeAttrs or 'g' in edgeAttrs):
            color = [0.0, 0.0, 0.0]
            if 'red' in edgeAttrs:
                color[0] = color[0] + edgeAttrs['red']
            elif 'r' in edgeAttrs:
                color[0] = color[0] + edgeAttrs['r']
            if 'green' in edgeAttrs:
                color[1] = color[1] + edgeAttrs['green']
            elif 'g' in edgeAttrs:
                color[1] = color[1] + edgeAttrs['g']
            if 'blue' in edgeAttrs:
                color[2] = color[2] + edgeAttrs['blue']
            elif 'b' in edgeAttrs:
                color[2] = color[2] + edgeAttrs['b']
            # If color uses 0 to 255 for colors instead of 0.0 to 1.0, convert
            if color[0] > 1.0 or color[1] > 1.0 or color[2] > 1.0:
                color[0] /= 255
                color[1] /= 255
                color[2] /= 255
            e.color = tuple(color)
        # Determine the color of the edge if using 'fill' and hex
        elif 'fill' in edgeAttrs:
            fail = False
            fillstr = edgeAttrs['fill']
            if len(fillstr) == 7:
                r = fillstr[1:3]
                g = fillstr[3:5]
                b = fillstr[5:7]
            elif len(fillstr) == 4:
                r = fillstr[1]
                g = fillstr[2]
                b = fillstr[3]
            else:
                fail = "fill must be of form '#fff' or '#ffffff'"
            if not fail:
                color = [x / 255.0 for x in (int(r, 16), int(g, 16), int(b, 16))]
                e.color = tuple(color)
            if fail and not yesall_color and not yestoall:
                if queryCallback:
                    result = queryCallback("Invalid edge fill color (%s): "
                                           "%s -> %s, fill '%s' "
                                           "\n\nUse default color and continue?" %
                                           (fail, edgeAttrs['source'],
                                            edgeAttrs['target'], fillstr),
                                           override=True)
                    if result == CustomButtonDialog.ID['Yes']:
                        pass
                    elif result == CustomButtonDialog.ID['Yes to similar errors']:
                        yesall_color = True
                    elif result == CustomButtonDialog.ID['Yes to all errors']:
                        yestoall = True
                    else:
                        raise ImportError, "Import aborted."
                else:
                    raise ImportError, ("Invalid edge fill color (%s): "
                                        "%s -> %s, fill '%s'" %
                                        (fail, edgeAttrs['source'],
                                         edgeAttrs['target'], fillstr))

                # Accept radius, size, or weight for the name of edge radius
        if 'radius' in edgeAttrs:
            e.radius = edgeAttrs['radius']
        elif 'size' in edgeAttrs:
            e.radius = edgeAttrs['size']
        elif 'weight' in edgeAttrs:
            e.radius = edgeAttrs['weight']
        if 'attribute' in edgeAttrs:
            e.attribute = edgeAttrs['attribute']

        graph.addEdge(e)
        edgeIndex[(edgeAttrs['source'], edgeAttrs['target'])] = e