def create_scenario(): ''' Create something as: v4 v0 \ / | \ v1 | \ / | v5 / \ / v2 | v3 ''' E = [] data_to_vertex = {} vertices = [] for i in range(6): data = 'v%s' % (i, ) v = Vertex(data) data_to_vertex[data] = v v.view = VertexViewer(100, 50) vertices.append(v) edge = Edge(vertices[0], vertices[1]) edge.view = EdgeViewer() E.append(edge) edge = Edge(vertices[0], vertices[2]) edge.view = EdgeViewer() E.append(edge) edge = Edge(vertices[1], vertices[5]) edge.view = EdgeViewer() E.append(edge) edge = Edge(vertices[2], vertices[3]) edge.view = EdgeViewer() E.append(edge) edge = Edge(vertices[4], vertices[5]) edge.view = EdgeViewer() E.append(edge) edge = Edge(vertices[5], vertices[2]) edge.view = EdgeViewer() E.append(edge) G = Graph(vertices, E) assert len(G.C) == 1 gr = G.C[0] # not needed anymore... #r = filter(lambda x: len(x.e_in()) == 0, gr.sV) #if len(r) == 0: # r = [gr.sV[0]] return gr, data_to_vertex
def initView(self): cfg = self.func.cfg vertexs = {} done = set() for block in cfg.blocks: if block in done: continue done.add(block) startLockey = block.loc_key endLockey = block.loc_key blocks = [block] while (block.lines[-1].name == 'CALL') and (len(cfg.predecessors(block.get_next())) == 1): nextLockey = block.get_next() if block.loc_key in cfg.successors(nextLockey): break block = cfg.loc_key_to_block(nextLockey) blocks.append(block) endLockey = block.loc_key done.add(block) vertex = Vertex(startLockey) vertexs[startLockey] = vertex vertex.view = AsmBlockView(startLockey, endLockey, blocks, self.func) edges = [] for src in vertexs.values(): successLocKeys = cfg.successors(src.view.endLockey) for key in successLocKeys: vSrc = vertexs[src.view.lockey] vDst = vertexs[key] edge = Edge(vSrc, vDst) edge.view = BasicEdge(vDst) edges.append(edge) vSrc.view.outBlocks.append(vDst.view) vDst.view.inBlocks.append(vSrc.view) self.graph = Graph(vertexs.values(), edges) sugLayout = SugiyamaLayout(self.graph.C[0]) sugLayout.route_edge = route_with_lines sugLayout.init_all() sugLayout.draw() for v in vertexs.values(): self.addBlock(v.view, v.view.xy[0] - (v.view.w / 2), v.view.xy[1] - (v.view.h / 2)) v.view.gotoAddress.connect(self.selectAddress) for e in edges: srcView = e.v[0].view srcBlock = e.v[0].data dstBlock = e.v[1].data if len(srcView.outBlocks) == 1: color = Qt.darkBlue elif dstBlock == cfg.successors(srcBlock)[0]: color = Qt.darkRed else: color = Qt.darkGreen edge_view = e.view edge_view.color = color self.scene.addItem(edge_view)
def sample_G01(): v = 'abc' V = [Vertex(x) for x in v] D = dict(zip(v,V)) e = ['ab','ac'] E = [Edge(D[xy[0]],D[xy[1]]) for xy in e] return (V,E)
def sample_G03(): v = 'abcd' V = [Vertex(x) for x in v] D = dict(zip(v,V)) e = ['ab','bc','cd','bd','da'] E = [Edge(D[xy[0]],D[xy[1]]) for xy in e] return (V,E)
def sample_G05(): v = 'abcdefgh' V = [Vertex(x) for x in v] D = dict(zip(v,V)) e = ['ab','ae','af','bc','cd','dh','eg','fg','gh'] E = [Edge(D[xy[0]],D[xy[1]]) for xy in e] return (V,E)
def run(self): with SDHistoryUtils.UndoGroup("Auto-Format Layout"): selected_nodes = uiMgr.getCurrentGraphSelection() hierarchy = NodesHierarchy(ctx, selected_nodes) vertexes = [Vertex(data) for data in range(len(hierarchy.nodes))] nodesConnections = hierarchy.getEdges() edges = [Edge(vertexes[vertex], vertexes[w]) for (vertex, w) in nodesConnections] graph = SubstanceGraph(vertexes, edges) for vertex in vertexes: vertex.view = VertexView(hierarchy.nodes[vertex.data]) # For each separate graph for subGraph in range(len(graph.C)): center = graph.getSubgraphCenter(subGraph) sug = SugiyamaLayout(graph.C[subGraph]) # Vertexes without input edges roots = list(filter(lambda x: len(x.e_in()) == 0, graph.C[subGraph].sV)) sug.init_all(roots=roots) sug.draw() newCenter = graph.getSubgraphCenter(subGraph) if len(selected_nodes) == 1: # Offset everything to save the selected node initial position rootPosition = hierarchy.nodes[roots[0].data].getPosition() # Have to compensate the final root node offset offset = float2(rootPosition[1] - roots[0].view.xy[0], -rootPosition[0] - roots[0].view.xy[1]) else: offset = float2(center[0] - newCenter[0], center[1] - newCenter[1]) for i, vertex in enumerate(graph.C[subGraph].sV): vertex.view.node.setPosition(float2(-vertex.view.xy[1] - offset[1], vertex.view.xy[0] + offset[0]))
def test_networkx(): try: import networkx except ImportError: return # Only run test if networkx is present v = ('a', 'b', 'c', 'd') V = [Vertex(x) for x in v] D = dict(zip(v, V)) e = ['ab', 'ac', 'bc', 'cd'] E = [Edge(D[xy[0]], D[xy[1]], data=xy) for xy in e] g = Graph(V, E) nx_graph = convert_grandalf_graph_to_networkx_graph(g) assert set(n for n in nx_graph.nodes()) == set( v) # nodes returns the nodes available (node data) assert set(n[0] + n[1] for n in nx_graph.edges()) == set( e) # edges returns list(tuple(node1, node2)) assert nx_graph.number_of_edges() == len(E) assert nx_graph.number_of_nodes() == len(V) # Now, let's go back from networkx to grandalf grandalf_graph = convert_nextworkx_graph_to_grandalf(nx_graph) assert len(list(grandalf_graph.V())) == len(v) assert len(list(grandalf_graph.E())) == len(E) assert set(n.data for n in grandalf_graph.V()) == set(v) assert set(n.v[0].data + n.v[1].data for n in grandalf_graph.E()) == set(e)
def convert_nextworkx_graph_to_grandalf(G): from grandalf.graphs import Graph, Vertex, Edge V = [] data_to_V = {} for x in G.nodes(): vertex = Vertex(x) V.append(vertex) data_to_V[x] = vertex E = [Edge(data_to_V[xy[0]], data_to_V[xy[1]], data=xy) for xy in G.edges()] g = Graph(V, E) return g
def sample_G07(): v = range(1, 24) V = [Vertex(x) for x in map(str, v)] D = dict(zip(v, V)) e = [(1, 13), (1, 21), (1, 4), (1, 3), (2, 3), (2, 20), (3, 4), (3, 5), (3, 23), (4, 6), (5, 7), (6, 8), (6, 16), (6, 23), (7, 9), (8, 10), (8, 11), (9, 12), (10, 13), (10, 14), (10, 15), (11, 15), (11, 16), (12, 20), (13, 17), (14, 17), (14, 18), (16, 18), (16, 19), (16, 20), (18, 21), (19, 22), (21, 23), (22, 23)] E = [Edge(D[x], D[y]) for x, y in e] return (V, E)
def sample_G06(): v = range(30) V = [Vertex(x) for x in map(str, v)] D = dict(zip(v, V)) e = [(0, 5), (0, 29), (0, 6), (0, 20), (0, 4), (17, 3), (5, 2), (5, 10), (5, 14), (5, 26), (5, 4), (5, 3), (2, 23), (2, 8), (14, 10), (26, 18), (3, 4), (23, 9), (23, 24), (10, 27), (18, 13), (1, 12), (24, 28), (24, 12), (24, 15), (12, 9), (12, 6), (12, 19), (6, 9), (6, 29), (6, 25), (6, 21), (6, 13), (29, 25), (21, 22), (25, 11), (22, 9), (22, 8), (11, 9), (11, 16), (8, 20), (8, 16), (15, 16), (15, 27), (16, 27), (27, 19), (27, 13), (19, 9), (13, 20), (9, 28), (9, 4), (20, 4), (28, 7)] E = [Edge(D[x], D[y]) for x, y in e] return (V, E)
def initView(self): vertexs = {} for lockey, block in self.ircfg.blocks.items(): vertexs[lockey] = Vertex(block) vertexs[lockey].view = IRBlockView(lockey, block, False) edges = [] for src in vertexs: successLocKeys = self.ircfg.successors(src) for key in successLocKeys: if key in vertexs: vSrc = vertexs[src] vDst = vertexs[key] edge = Edge(vSrc, vDst) edge.view = BasicEdge(vDst) edges.append(edge) vSrc.view.outBlocks.append(vDst.view) vDst.view.inBlocks.append(vSrc.view) self.graph = Graph(vertexs.values(), edges) sugLayout = SugiyamaLayout(self.graph.C[0]) sugLayout.route_edge = route_with_lines sugLayout.init_all() sugLayout.draw() for v in vertexs.values(): self.addBlock(v.view, v.view.xy[0] - (v.view.w / 2), v.view.xy[1] - (v.view.h / 2)) for e in edges: srcView = e.v[0].view srcBlock = e.v[0].data dstBlock = e.v[1].data if len(srcView.outBlocks) == 1: color = Qt.darkBlue elif dstBlock.loc_key == self.ircfg.successors(srcBlock.loc_key)[0]: color = Qt.darkRed else: color = Qt.darkGreen edge_view = e.view edge_view.color = color self.scene.addItem(edge_view)
def convert_nextworkx_graph_to_grandalf(G): ''' Converts a networkx graph to a grandalf graph. Note that the edge concept is the same, but a vertex in grandalf is called a node in networkx. ''' V = [] data_to_V = {} for x in G.nodes(): vertex = Vertex(x) V.append(vertex) data_to_V[x] = vertex E = [Edge(data_to_V[xy[0]], data_to_V[xy[1]], data=xy) for xy in G.edges()] g = Graph(V, E) return g
def __init__(self, vertexes, edges): # # Just a reminder about naming conventions: # +------------X # | # | # | # | # Y # V = {v: Vertex(" {} ".format(v)) for v in vertexes} # NOTE: reverting edges to correctly orientate the graph E = [Edge(V[e], V[s]) for s, e in edges] V = V.values() g = Graph(V, E) class VertexViewer(object): h = 3 # top and bottom box edges + text def __init__(self, name): self.w = len(name) + 2 # right and left bottom edges + text for v in V: v.view = VertexViewer(v.data) # NOTE: determine min box length to create the best layout minw = min([v.view.w for v in V]) for e in E: e.view = EdgeViewer() sug = SugiyamaLayout(g.C[0]) gr = g.C[0] r = list(filter(lambda x: len(x.e_in()) == 0, gr.sV)) sug.init_all(roots=r, optimize=True) sug.yspace = VertexViewer.h sug.xspace = minw sug.route_edge = route_with_lines sug.draw() self.sug = sug
def sugiyama_layout(g): """ Position nodes using Sugiyama algorithm. Returns dictionary of positions keyed by node. :param g: NetworkX graph. A position will be assigned to every node in G. :type g: networkx.classes.digraph.DiGraph :return: dict """ # verteces (for grandalf lib) vertices = {node: Vertex(node) for node in g.nodes} # edges (for grandalf lib) edges = [ Edge(vertices[v_from], vertices[v_to]) for v_from, v_to in g.edges ] # build graph for v_name in vertices: vertices[v_name].view = _DefaultView() g = Graph(vertices.values(), edges) sug = SugiyamaLayout(g.C[0]) sug.init_all(optimize=True) sug.draw() pos_names = [] pos_xs = [] pos_ys = [] for v in g.C[0].sV: pos_names.append(v.data) pos_xs.append(v.view.xy[0]) pos_ys.append(v.view.xy[1]) scaler = MinMaxScaler() scaled_x = scaler.fit_transform(np.array(pos_xs).reshape(-1, 1))[:, 0] scaled_y = scaler.fit_transform(np.array(pos_ys).reshape(-1, 1))[:, 0] pos = { pos_names[i]: np.array([scaled_x[i], scaled_y[i]]) for i in range(len(pos_names)) } return pos
def _build_sugiyama_layout(vertexes: Union[List, Dict, ValuesView], edges: List) -> SugiyamaLayout: # # Just a reminder about naming conventions: # +------------X # | # | # | # | # Y # vertexes = {v: Vertex(f" {v} ") for v in vertexes} # NOTE: reverting edges to correctly orientate the graph edges = [Edge(vertexes[e], vertexes[s]) for s, e in edges] vertexes = vertexes.values() graph = Graph(vertexes, edges) for vertex in vertexes: vertex.view = VertexViewer(vertex.data) # NOTE: determine min box length to create the best layout minw = min(v.view.w for v in vertexes) for edge in edges: edge.view = EdgeViewer() sug = SugiyamaLayout(graph.C[0]) graph = graph.C[0] roots = list(filter(lambda x: len(x.e_in()) == 0, graph.sV)) sug.init_all(roots=roots, optimize=True) sug.yspace = VertexViewer.HEIGHT sug.xspace = minw sug.route_edge = route_with_lines sug.draw() return sug
def graphchain(chain, start, thres=0.0, limit=4): from grandalf.graphs import Vertex, Edge, Graph L = len(chain[0]) done = [False] * L V = [Vertex(x) for x in range(L)] if isinstance(start, str): start = data.index(start) E = [] pool = [start] done[start] = True while len(pool) > 0: v = pool.pop(0) c = chain[v] l = filter(lambda x: x[1] > thres, c) d = [n[0] for n in l[:limit]] for i, n in enumerate(d): E.append(Edge(V[v], V[n], w=l[i][1])) if not done[n]: pool.append(n) done[n] = True if not pool and False in done: n = done.index(False) pool.append(n) done[n] = True return Graph(V, E)
def __init__(self, acode): Vertex.__init__(self, data=acode) self.name = self.data.name self.view = self.data.view
#!/usr/bin/env python from grandalf.graphs import Vertex, Edge # define a very simple graph : v = map(chr, range(ord('a'), ord('h') + 1)) V = [Vertex(x) for x in v] D = dict(zip(v, V)) e = ['ab', 'ae', 'af', 'bc', 'cd', 'dh', 'eg', 'fg', 'gh'] E = [Edge(D[xy[0]], D[xy[1]]) for xy in e] G1 = (V, E)
from grandalf.graphs import Vertex, Edge, Graph import random def shuffle(lis): result = [] while lis: p = random.randrange(0, len(lis)) result.append(lis[p]) lis.pop(p) return result V = [Vertex(data) for data in range(15)] X = [ (1, 5), (1, 9), (1, 7), (8, 13), (1, 6), (1, 0), (1, 3), (8, 14), (2, 11), (1, 4), (8, 12), (2, 10), (1, 8), (2, 1), ] E = [Edge(V[v], V[w]) for (v, w) in X]
def dotnode(seq): _start = Vertex(seq) _start.view = Node(_start) return _start
def _layout_graph_sugiyama(graph): """ Arrange the graph automatically using grandalf package """ from grandalf.graphs import Graph, Vertex, Edge from grandalf.layouts import SugiyamaLayout # Build the viez class for grandalf vertices class View(object): def __init__(self, w, h): self.w = w self.h = h self.xy = (0, 0) # Build the grandalf graph ggraph = Graph() # Vertices vertices_by_id = {} all_nodes_by_id = {n.id: n for n in graph.all_nodes()} # Get average width for distance between nodes avg_width = 0 avg_height = 0 for n in all_nodes_by_id.values(): v = Vertex(n.id) if NODE_LAYOUT_DIRECTION == NODE_LAYOUT_HORIZONTAL: view = View(n.view.height, n.view.width) else: view = View(n.view.width, n.view.height) avg_width += n.view.width avg_height += n.view.height v.view = view vertices_by_id[n.id] = v avg_width /= len(all_nodes_by_id) avg_height /= len(all_nodes_by_id) for v in vertices_by_id.values(): ggraph.add_vertex(v) # Edges inverted_edges = [] looked_up_nodes = set([]) for n in all_nodes_by_id.values(): for cns in n.connected_output_nodes().values(): for cn in cns: e = Edge(vertices_by_id[n.id], vertices_by_id[cn.id]) if cn.id in looked_up_nodes: inverted_edges.append(e) else: looked_up_nodes.add(cn.id) ggraph.add_edge(e) # Build the layout sug = SugiyamaLayout(ggraph.C[0]) sug.yspace = avg_width if NODE_LAYOUT_DIRECTION == NODE_LAYOUT_HORIZONTAL else avg_height sug.xspace = avg_height if NODE_LAYOUT_DIRECTION == NODE_LAYOUT_HORIZONTAL else avg_width print(inverted_edges) # sug.init_all(inverted_edges=inverted_edges or None) sug.init_all() sug.draw(10) for v in ggraph.C[0].sV: node = all_nodes_by_id[v.data] if NODE_LAYOUT_DIRECTION == NODE_LAYOUT_HORIZONTAL: node.set_pos(*reversed(v.view.xy)) else: node.set_pos(*v.view.xy)
def __init__(self, acode): Vertex.__init__(self, data=acode)
def updateLayeredLayoutWithComp(self): class Vtx(object): def __init__(self, name, idx, radius = 1, height = 1): self.inNodes = set() self.outNodes = set() self.name = name self.idx = idx self.comp = None self.compIdx = None self.pos = None self.radius = radius self.fontHeight = height self.height = max(radius, height) self.firstKey = 0.0 self.secondKey = 0.0 self.thirdKey = 0.0 def getLayoutHeight(self): return self.height + self.radius def setLayoutPos(self,x,y): self.pos = (x, y-0.5*(self.height - self.radius)) def getMinX(self): return self.pos[0] - self.radius def getMaxX(self): return self.pos[0] + self.radius def getMinY(self): return self.pos[1] - self.radius def getMaxY(self): return self.pos[1] + self.height def getPos(self): return self.pos if len(self.scene.itemDict) == 0: return vtxName2Id = {} vtxList = [] edgeList = [] for name, item in self.scene.itemDict.items(): ithVtx = len(vtxList) v = Vtx(name, ithVtx, item.getRadius(), item.getHeight()) v.dispName = item.name vtxList.append(v) vtxName2Id[name] = ithVtx for edgeKey, edge in self.scene.edgeDict.items(): v1 = vtxName2Id[edgeKey[0]] v2 = vtxName2Id[edgeKey[1]] vtxList[v1].outNodes.add(v2) vtxList[v2].inNodes.add(v1) edgeList.append((v1,v2)) # 构造连通分量 remainSet = set([i for i in range(len(vtxList))]) compList = [] # [[idx1,idx2,...],[...]] while len(remainSet) > 0: # 找出剩余一个顶点并加入队列 ids = [list(remainSet)[0]] ithComp = len(compList) vtxList[ids[0]].comp = ithComp compMap = [] # 遍历一个连通分量 while len(ids) > 0: newIds = [] for id in ids: # 把一个顶点加入连通分量 vtx = vtxList[id] vtx.compIdx = len(compMap) compMap.append(id) # 把周围未遍历顶点加入队列 for inId in vtx.inNodes: inVtx = vtxList[inId] if inVtx.comp is None: inVtx.comp = ithComp newIds.append(inId) for outId in vtx.outNodes: outVtx = vtxList[outId] if outVtx.comp is None: outVtx.comp = ithComp newIds.append(outId) remainSet.discard(id) ids = newIds # 增加一个连通分量 compList.append(compMap) from grandalf.graphs import Vertex, Edge, Graph class VtxView(object): def __init__(self, w, h): self.w = w self.h = h # 构造每个连通分量的图结构 offset = (0,0) bboxMin = [1e6,1e6] bboxMax = [-1e6,-1e6] self.scene.boxTest = [] for ithComp, compMap in enumerate(compList): minPnt = [1e6,1e6] maxPnt = [-1e6,-1e6] # 构造图数据并布局 V = [] for oldId in compMap: w = vtxList[oldId].getLayoutHeight() vtx = Vertex(oldId) height = 200 vtx.view = VtxView(w, height) V.append(vtx) E = [] for edgeKey in edgeList: if vtxList[edgeKey[0]].comp == ithComp: E.append(Edge(V[vtxList[edgeKey[0]].compIdx], V[vtxList[edgeKey[1]].compIdx])) g = Graph(V,E) from grandalf.layouts import SugiyamaLayout packSpace = 4 sug = SugiyamaLayout(g.C[0]) sug.xspace = packSpace sug.yspace = packSpace #sug.order_iter = 32 sug.dirvh = 3 sug.init_all() sug.draw(10) # 统计包围盒 for v in g.C[0].sV: oldV = vtxList[v.data] x= v.view.xy[1] y= v.view.xy[0] oldV.setLayoutPos(x,y) minPnt[0] = min(minPnt[0],oldV.getMinX()) minPnt[1] = min(minPnt[1],oldV.getMinY()) maxPnt[0] = max(maxPnt[0],oldV.getMaxX()) maxPnt[1] = max(maxPnt[1],oldV.getMaxY()) for v in g.C[0].sV: oldV = vtxList[v.data] posInComp = oldV.getPos() newPos = (posInComp[0], posInComp[1]-minPnt[1]+offset[1]) self.scene.itemDict[oldV.name].setTargetPos(QtCore.QPointF(newPos[0], newPos[1])) bboxMin[0] = min(bboxMin[0], newPos[0]) bboxMin[1] = min(bboxMin[1], newPos[1]) bboxMax[0] = max(bboxMax[0], newPos[0]) bboxMax[1] = max(bboxMax[1], newPos[1]) offset = (offset[0], offset[1]+maxPnt[1]-minPnt[1]+packSpace) # 设置四个角的item cornerList = self.scene.cornerItem mar = 2000 cornerList[0].setPos(bboxMin[0]-mar, bboxMin[1]-mar) cornerList[1].setPos(bboxMin[0]-mar, bboxMax[1]+mar) cornerList[2].setPos(bboxMax[0]+mar, bboxMin[1]-mar) cornerList[3].setPos(bboxMax[0]+mar, bboxMax[1]+mar) # 更新标志数据 self.itemSet = set(self.scene.itemDict.keys()) self.edgeNum = len(self.scene.edgeDict)
#!/usr/bin/env python from grandalf.graphs import Vertex, Edge # horizontal coord assigment verifier: v = range(1, 24) V = [Vertex(x) for x in map(str, v)] D = dict(zip(v, V)) e = [(1, 13), (1, 21), (1, 4), (1, 3), (2, 3), (2, 20), (3, 4), (3, 5), (3, 23), (4, 6), (5, 7), (6, 8), (6, 16), (6, 23), (7, 9), (8, 10), (8, 11), (9, 12), (10, 13), (10, 14), (10, 15), (11, 15), (11, 16), (12, 20), (13, 17), (14, 17), (14, 18), (16, 18), (16, 19), (16, 20), (18, 21), (19, 22), (21, 23), (22, 23)] E = [Edge(D[x], D[y]) for x, y in e] G3 = (V, E)
def __init__(self,acode): Vertex.__init__(self,data=acode) pre = 'blck_' if acode._is_block else 'func_' self.name = '{}{}'.format(pre,str(self.data.address)) self._map = None self.misc = defaultdict(_code_misc_default)
def updateLayeredLayoutWithComp(self): class Vtx(object): def __init__(self, name, idx, radius = 1, height = 1): self.inNodes = set() self.outNodes = set() self.name = name self.idx = idx self.comp = None self.compIdx = None self.pos = None self.radius = radius self.fontHeight = height self.height = max(radius, height) self.firstKey = 0.0 self.secondKey = 0.0 self.thirdKey = 0.0 def getLayoutHeight(self): return self.height + self.radius def setLayoutPos(self,x,y): self.pos = (x, y-0.5*(self.height - self.radius)) def getMinX(self): return self.pos[0] - self.radius def getMaxX(self): return self.pos[0] + self.radius def getMinY(self): return self.pos[1] - self.radius def getMaxY(self): return self.pos[1] + self.height def getPos(self): return self.pos if len(self.scene.itemDict) == 0: return vtxName2Id = {} vtxList = [] edgeList = [] for name, item in self.scene.itemDict.items(): ithVtx = len(vtxList) v = Vtx(name, ithVtx, item.getRadius(), item.getHeight()) v.dispName = item.name vtxList.append(v) vtxName2Id[name] = ithVtx for edgeKey, edge in self.scene.edgeDict.items(): v1 = vtxName2Id[edgeKey[0]] v2 = vtxName2Id[edgeKey[1]] vtxList[v1].outNodes.add(v2) vtxList[v2].inNodes.add(v1) edgeList.append((v1,v2)) # 构造连通分量 remainSet = set([i for i in range(len(vtxList))]) compList = [] # [[idx1,idx2,...],[...]] while len(remainSet) > 0: # 找出剩余一个顶点并加入队列 ids = [list(remainSet)[0]] ithComp = len(compList) vtxList[ids[0]].comp = ithComp compMap = [] # 遍历一个连通分量 while len(ids) > 0: newIds = [] for id in ids: # 把一个顶点加入连通分量 vtx = vtxList[id] vtx.compIdx = len(compMap) compMap.append(id) # 把周围未遍历顶点加入队列 for inId in vtx.inNodes: inVtx = vtxList[inId] if inVtx.comp is None: inVtx.comp = ithComp newIds.append(inId) for outId in vtx.outNodes: outVtx = vtxList[outId] if outVtx.comp is None: outVtx.comp = ithComp newIds.append(outId) remainSet.discard(id) ids = newIds # 增加一个连通分量 compList.append(compMap) from grandalf.graphs import Vertex, Edge, Graph class VtxView(object): def __init__(self, w, h): self.w = w self.h = h # 构造每个连通分量的图结构 offset = (0,0) bboxMin = [1e6,1e6] bboxMax = [-1e6,-1e6] self.scene.boxTest = [] for ithComp, compMap in enumerate(compList): minPnt = [1e6,1e6] maxPnt = [-1e6,-1e6] # 构造图数据并布局 V = [] for oldId in compMap: w = vtxList[oldId].getLayoutHeight() vtx = Vertex(oldId) height = 200 vtx.view = VtxView(w, height) V.append(vtx) E = [] for edgeKey in edgeList: if vtxList[edgeKey[0]].comp == ithComp: E.append(Edge(V[vtxList[edgeKey[0]].compIdx], V[vtxList[edgeKey[1]].compIdx])) g = Graph(V,E) from grandalf.layouts import SugiyamaLayout packSpace = 4 sug = SugiyamaLayout(g.C[0]) sug.xspace = packSpace sug.yspace = packSpace #sug.order_iter = 32 sug.dirvh = 3 sug.init_all() sug.draw(10) # 统计包围盒 for v in g.C[0].sV: oldV = vtxList[v.data] x= v.view.xy[1] y= v.view.xy[0] oldV.setLayoutPos(x,y) minPnt[0] = min(minPnt[0],oldV.getMinX()) minPnt[1] = min(minPnt[1],oldV.getMinY()) maxPnt[0] = max(maxPnt[0],oldV.getMaxX()) maxPnt[1] = max(maxPnt[1],oldV.getMaxY()) for v in g.C[0].sV: oldV = vtxList[v.data] posInComp = oldV.getPos() newPos = (posInComp[0], posInComp[1]-minPnt[1]+offset[1]) self.scene.itemDict[oldV.name].setTargetPos(QtCore.QPointF(newPos[0], newPos[1])) bboxMin[0] = min(bboxMin[0], newPos[0]) bboxMin[1] = min(bboxMin[1], newPos[1]) bboxMax[0] = max(bboxMax[0], newPos[0]) bboxMax[1] = max(bboxMax[1], newPos[1]) offset = (offset[0], offset[1]+maxPnt[1]-minPnt[1]+packSpace) # 设置四个角的item cornerList = self.scene.cornerItem mar = 2000 cornerList[0].setPos(bboxMin[0]-mar, bboxMin[1]-mar) cornerList[1].setPos(bboxMin[0]-mar, bboxMax[1]+mar) cornerList[2].setPos(bboxMax[0]+mar, bboxMin[1]-mar) cornerList[3].setPos(bboxMax[0]+mar, bboxMax[1]+mar) # 更新标志数据 self.itemSet = set(self.scene.itemDict.keys()) self.edgeNum = len(self.scene.edgeDict)