From 74a4c5e3f0b03bc8c939bd31eaea66602a63e0fb Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 20 Jul 2023 03:11:12 +0200 Subject: [PATCH] It works now! best commit, worst message, thug life. --- birdvisu/annotations/layout.py | 23 +++++++++++++++++----- birdvisu/graphics_items.py | 19 ++++++++++++++++-- poor_mans_visualisation.py | 36 +++++++++++++++++++++------------- 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/birdvisu/annotations/layout.py b/birdvisu/annotations/layout.py index 4086409..5450f31 100644 --- a/birdvisu/annotations/layout.py +++ b/birdvisu/annotations/layout.py @@ -273,11 +273,12 @@ class HighlightSPDAG(StyleAnnotator): def __init__(self, vtxid): self.vtxid = vtxid def annotate(self, topo): - spd_annot = AnnotatorID(ShortestPathTree, vtxid) + spd_annot = AnnotatorID(analysis.ShortestPathTree, (self.vtxid, 'current')) topo.run_annotator(spd_annot) annotation = topo.annotations[spd_annot] result = Annotation() - result.for_edge = {e: {'highlight_colour': (200, 200, 0, 128)} for e in annotation.for_edge.keys()} + result.for_edge = {e: {'highlight_colour': (200, 0, 200, 128)} for e in annotation.for_edge.keys()} + result.for_vertex[self.vtxid] = {'highlight_colour': (0, 200, 0, 128)} return result class HighlightShortestPath(StyleAnnotator): @@ -285,8 +286,20 @@ class HighlightShortestPath(StyleAnnotator): def __init__(self, param): self.start, self.end = param def annotate(self, topo): - spd_annot = AnnotatorID(ShortestPathTree, vtxid) + spd_annot = AnnotatorID(analysis.ShortestPathTree, (self.start, 'current')) topo.run_annotator(spd_annot) - annotation = topo.annotations[spd_annot] + annotation = topo.annotations[spd_annot] result = Annotation() - ... + # Highlight start and end + result.for_vertex[self.start] = {'highlight_colour': (0, 200, 0, 128)} + result.for_vertex[self.end] = {'highlight_colour': (200, 0, 100, 128)} + # Find the path by following backward edges of the shortest path DAG, BFS style. + queue = [self.end] + while len(queue) > 0: + v = queue.pop(0) + if v == self.start: continue # no looking further. + previous_edges = topo.topology.vertices[v].incoming_edges & annotation.for_edge.keys() + result.for_edge |= {e: {'highlight_colour': (200, 0, 200, 128), 'width': 5} for e in previous_edges} + queue.extend(e.source for e in previous_edges) + # It is oriented, so there is no need to mark visited vertices. + return result diff --git a/birdvisu/graphics_items.py b/birdvisu/graphics_items.py index ac5ac8b..c148c78 100644 --- a/birdvisu/graphics_items.py +++ b/birdvisu/graphics_items.py @@ -1,6 +1,6 @@ -from PySide6.QtWidgets import QGraphicsItem, QGraphicsRectItem, QGraphicsSimpleTextItem, QGraphicsLineItem +from PySide6.QtWidgets import QGraphicsItem, QGraphicsRectItem, QGraphicsSimpleTextItem, QGraphicsLineItem, QMenu from PySide6.QtCore import QLineF, Qt -from PySide6.QtGui import QColor, QBrush, QPen +from PySide6.QtGui import QColor, QBrush, QPen, QAction def _rid_to_str(rid): from ipaddress import IPv4Address @@ -71,6 +71,21 @@ class RouterGraphicsItem(QGraphicsItem): for edge in all_edges: self.window.graphicsitems[edge].update_line() super().mouseMoveEvent(evt) + + def contextMenuEvent(self, evt): + menu = QMenu(self.window) + tree_act = QAction('Show routing tree', self.window) + menu.addAction(tree_act) + path_act = object() + if self.window.mode == self.window.Mode.ShortestPathDAG: + path_act = QAction('Show path to here', self.window) + menu.addAction(path_act) + + action = menu.exec(evt.screenPos()) + if action == tree_act: + self.window.dagMode(self.vertex_id) + if action == path_act: + self.window.shortestPathMode(self.vertex_id) # Admit we are basically only a wrapper of the icon. def boundingRect(self): diff --git a/poor_mans_visualisation.py b/poor_mans_visualisation.py index e5fc760..eec131d 100755 --- a/poor_mans_visualisation.py +++ b/poor_mans_visualisation.py @@ -87,10 +87,13 @@ class MainWindow(QtWidgets.QMainWindow): def create_menus(self): print('Creating menus…') self.menubar = self.menuBar() - mode_menu = self.menubar.addMenu('&Mode') - short_path_act = QtGui.QAction("Sh. path &DAG", self) - short_path_act.triggered.connect(self.shortestPathMode) - mode_menu.addAction(short_path_act) + mode_menu = self.menubar.addMenu('&Highlight') + edge_weight_act = QtGui.QAction("Edge costs", self) + edge_weight_act.triggered.connect(self.edgeWeightMode) + mode_menu.addAction(edge_weight_act) + topodiff_act = QtGui.QAction("Topology differences", self) + topodiff_act.triggered.connect(self.topoDiffMode) + mode_menu.addAction(topodiff_act) # Hack! autoload_act = QtGui.QAction("&Load automatically", self) @@ -149,7 +152,7 @@ class MainWindow(QtWidgets.QMainWindow): @Slot() def load_positions(self): - filename = QtWidgets.QFileDialog.getOpenFileName(self, 'Open vertex positions', '.', 'OSPF files(*.visu);;All files(*)')[0] + filename = QtWidgets.QFileDialog.getOpenFileName(self, 'Open vertex positions', '.', 'OSPF visualisation files (*.visu);;All files(*)')[0] if filename == '': return # Do nothing self.positions_from_file(filename) @@ -190,15 +193,18 @@ class MainWindow(QtWidgets.QMainWindow): if oe.cost == 0 or oe.cost > e.cost: for_edge[(a,b)] = (e, sty) # Actually apply the style: - for v, sty in for_vertex.items(): + for v in self.visu_graph[0].keys(): + sty = for_vertex.get(v, {}) self.graphicsitems[v].apply_style(sty) - for e, tup in for_edge.items(): - self.graphicsitems[e].apply_style(tup[1]) + for e in self.visu_graph[1]: + sty = for_edge[e][1] if e in for_edge else {} + self.graphicsitems[e].apply_style(sty) @Slot() - def dagMode(self): + def dagMode(self, vtxid): self.mode = self.Mode.ShortestPathDAG - self.highlighter = HighlightSPDAG(...) + self.highlighter = HighlightSPDAG(vtxid) + self.start_vertex = vtxid self.apply_styles() @Slot() @@ -214,13 +220,15 @@ class MainWindow(QtWidgets.QMainWindow): self.apply_styles() @Slot() - def shortestPathMode(self): + def shortestPathMode(self, vtxid): self.mode = self.Mode.ShortestPath - ... + self.end_vertex = vtxid + self.highlighter = HighlightShortestPath((self.start_vertex, self.end_vertex)) + self.apply_styles() @Slot() def openRefTopology(self): - filename = QtWidgets.QFileDialog.getOpenFileName(self, 'Open reference topology', '.', 'OSPF files(*.ospf);;All files(*)')[0] + filename = QtWidgets.QFileDialog.getOpenFileName(self, 'Open reference topology', '.', 'OSPF files (*.ospf);;All files(*)')[0] if filename == '': return # Do nothing self.ref_topo_provider = OspfFileTopologyProvider(filename) try: @@ -247,7 +255,7 @@ class MainWindow(QtWidgets.QMainWindow): @Slot() def curTopologyFromFile(self): - filename = QtWidgets.QFileDialog.getOpenFileName(self, 'Open current topology', '.', 'OSPF files(*.ospf);;All files(*)')[0] + filename = QtWidgets.QFileDialog.getOpenFileName(self, 'Open current topology', '.', 'OSPF files (*.ospf);;All files(*)')[0] if filename == '': return # Do nothing self.cur_topo_provider = OspfFileTopologyProvider(filename) try: