Add basic / prototype annotators

older_libs
LEdoian 2 years ago
parent e60413bdf1
commit 105f00705c

@ -0,0 +1,185 @@
"""Annotators for visualising.
Many of these should be somewhere else, because they make assumptions not
related to visualisation. Also, only the currently needed annotators were
written."""
from enum import Enum, auto
from dataclasses import dataclass
import re
import random
from PySide6 import QtCore, QtGui, QtWidgets
# Classification
class DifferenceStatus:
"""Describes differences between two topologies"""
NORMAL = auto()
MISSING = auto()
EXTRA = auto()
DISCREPANCY = auto()
def difference_annotator(at, reference_src='reference', actual_src='actual'):
"""Adds DifferenceStatuses according to which sources provided which part
of topology"""
topo = at.topology
for data, annot in [
(topo.routers, at.router_annotations),
(topo.networks, at.network_annotations),
(topo.links, at.link_annotations),
]:
for k, v in data.items():
verdict = DifferenceStatus.NORMAL
if actual_src not in v.sources:
verdict = DifferenceStatus.MISSING
if reference_src not in v.sources:
verdict = DifferenceStatus.EXTRA
# Nodes currently cannot have discrepant information (provided the
# Topology.is_valid())
if data is topo.links:
if k.metric < 0:
verdict = DifferenceStatus.DISCREPANCY
# TODO: We should probably disallow mutating previous AnnotatedTopologies.
annot[k].append(verdict)
return at
# TODO: annotator for routing trees
# Layouting
@dataclass
class Position:
x: float
y: float
def extract_positions(at, directive='visualisation default'):
topo = at.topology
for data, annot in [
(topo.routers, at.router_annotations),
(topo.networks, at.network_annotations),
]:
for k, v in data.items():
visu_directive = None
for details in k.all_details:
visu_directives = list(filter(lambda x: x[0] == directive, details))
if len(visu_directives) > 0:
visu_directive = visu_directives[-1] # Use the last one found.
position = None
if visu_directive is not None:
for pos in visu_directive[1]:
m = re.match(r'position \[([0-9.]+) ([0-9.]+)\]', pos)
if pos is not None:
x, y = m.groups()
position = Position(x = float(x), y = float(y))
if position is not None:
annot[k].append(position)
return at
def random_posiiton(at):
# Fallback when no position could be extracted
# TODO: this should use some kind of heuristic or existing layout engine.
topo = at.topology
for data, annot in [
(topo.routers, at.router_annotations),
(topo.networks, at.network_annotations),
]:
for k, v in data.items():
if len(annot[k]) > 0 and not isinstance(annot[k][-1], Position):
annot[k].append(Position(
# This is really last resort, so expecting the canvas to be
# FullHD is as good assumption as any.
x = float(random.randint(0, 1920)),
y = float(random.randint(0, 1080)),
))
return at
# Rendering
def assign_brushes(at):
for annot in [
at.router_annotations,
at.network_annotations,
at.link_annotations,
]:
for tags in annot.values():
statuses = list(filter(lambda x: isinstance(x, DifferenceStatus), tags))
status = statuses[-1] if len(statuses) > 0 else DifferenceStatus.DISCREPANCY # should always have something.
color = {
DifferenceStatus.NORMAL: 'black',
DifferenceStatus.EXTRA: 'green',
DifferenceStatus.MISSING: 'red',
DifferenceStatus.DISCREPANCY: 'blue',
}[status]
tags.append(QtGui.QBrush(QtGui.QColor(color)))
return at
def create_qgritems(at):
# qgritem = QGraphicsItem
# TODO: reasonable visualisation, not just squares :-)
# - this probably involves creatin custom qgritems
# - We will need own qgritems anyway to handle interaction (e.g.
# resolving to Router objects)
topo = at.topology
for rk, r in topo.routers.items():
size = 30
brush = None
for tag in at.router_annotations[rk][-1::-1]:
if isinstance(tag, QtGui.QBrush):
brush = tag
break
pos = None
for tag in at.router_annotations[rk][-1::-1]:
if isinstance(tag, Position):
pos = tag
break
x = pos.x
y = pos.y
shape = QtWidgets.QGraphicsRectItem(x, y, size, size)
shape.setBrush(brush)
label = QtWidgets.QGraphicsSimpleTextItem(rk, parent=shape)
at.router_annotations[rk].append(shape)
for nk, n in topo.networks.items():
size = 10
brush = None
for tag in at.network_annotations[nk][-1::-1]:
if isinstance(tag, QtGui.QBrush):
brush = tag
break
pos = None
for tag in at.network_annotations[nk][-1::-1]:
if isinstance(tag, Position):
pos = tag
break
x = pos.x
y = pos.y
shape = QtWidgets.QGraphicsRectItem(x, y, size, size)
shape.setBrush(brush)
label = QtWidgets.QGraphicsSimpleTextItem(nk, parent=shape)
at.network_annotations[nk].append(shape)
for lk, l in topo.links.items():
rid = l.router.ident
nid = l.network.ident
rpos = None
for tag in at.router_annotations[rid][-1::-1]:
if isinstance(tag, Position):
rpos = tag
break
npos = None
for tag in at.network_annotations[nid][-1::-1]:
if isinstance(tag, Position):
npos = tag
break
line = QtWidgets.QGraphicsLineItem(rpos.x, rpos.y, npos.x, npos.y)
at.link_annotations[lk].append(line)
Loading…
Cancel
Save