merged drawing antenna code to graph node class

This commit is contained in:
Blake Harnden 2019-12-05 16:37:48 -08:00
parent 7e2ebb4a2c
commit 81eeac9ec6
5 changed files with 120 additions and 194 deletions

View file

@ -1,5 +1,7 @@
import tkinter as tk
from coretk.nodeutils import NodeUtils
class CanvasWirelessEdge:
def __init__(self, token, position, src, dst, canvas):
@ -22,7 +24,7 @@ class CanvasEdge:
width = 1.4
def __init__(self, x1, y1, x2, y2, src, canvas, is_wired=None):
def __init__(self, x1, y1, x2, y2, src, canvas):
"""
Create an instance of canvas edge object
:param int x1: source x-coord
@ -30,41 +32,56 @@ class CanvasEdge:
:param int x2: destination x-coord
:param int y2: destination y-coord
:param int src: source id
:param tkinter.Canvas canvas: canvas object
:param coretk.graph.graph.GraphCanvas canvas: canvas object
"""
self.src = src
self.dst = None
self.src_interface = None
self.dst_interface = None
self.canvas = canvas
if is_wired is None or is_wired is True:
self.id = self.canvas.create_line(
x1, y1, x2, y2, tags="edge", width=self.width, fill="#ff0000"
)
else:
self.id = self.canvas.create_line(
x1,
y1,
x2,
y2,
tags="edge",
width=self.width,
fill="#ff0000",
state=tk.HIDDEN,
)
self.token = None
self.link_info = None
self.throughput = None
self.wired = is_wired
def complete(self, dst, x, y):
def complete(self, dst):
self.dst = dst
self.token = tuple(sorted((self.src, self.dst)))
x, y = self.canvas.coords(self.dst)
x1, y1, _, _ = self.canvas.coords(self.id)
self.canvas.coords(self.id, x1, y1, x, y)
self.canvas.helper.draw_wireless_case(self.src, self.dst, self)
self.check_wireless()
self.canvas.tag_raise(self.src)
self.canvas.tag_raise(self.dst)
def check_wireless(self):
src_node = self.canvas.nodes[self.src]
dst_node = self.canvas.nodes[self.dst]
src_node_type = src_node.core_node.type
dst_node_type = dst_node.core_node.type
is_src_wireless = NodeUtils.is_wireless_node(src_node_type)
is_dst_wireless = NodeUtils.is_wireless_node(dst_node_type)
if is_src_wireless or is_dst_wireless:
self.canvas.itemconfig(self.id, state=tk.HIDDEN)
self._check_antenna()
def _check_antenna(self):
src_node = self.canvas.nodes[self.src]
dst_node = self.canvas.nodes[self.dst]
src_node_type = src_node.core_node.type
dst_node_type = dst_node.core_node.type
is_src_wireless = NodeUtils.is_wireless_node(src_node_type)
is_dst_wireless = NodeUtils.is_wireless_node(dst_node_type)
if is_src_wireless or is_dst_wireless:
if is_src_wireless and not is_dst_wireless:
dst_node.add_antenna()
elif not is_src_wireless and is_dst_wireless:
src_node.add_antenna()
# TODO: remove this? dont allow linking wireless nodes?
else:
src_node.add_antenna()
def delete(self):
self.canvas.delete(self.id)

View file

@ -8,7 +8,6 @@ from core.api.grpc.core_pb2 import NodeType
from coretk.dialogs.shapemod import ShapeDialog
from coretk.graph.edges import CanvasEdge, CanvasWirelessEdge
from coretk.graph.enums import GraphMode, ScaleOption
from coretk.graph.graph_helper import GraphHelper
from coretk.graph.linkinfo import LinkInfo, Throughput
from coretk.graph.node import CanvasNode
from coretk.graph.shape import Shape
@ -16,6 +15,18 @@ from coretk.images import ImageEnum, Images
from coretk.nodeutils import NodeUtils
ABOVE_WALLPAPER = ["edge", "linkinfo", "wireless", "antenna", "nodename", "node"]
CANVAS_COMPONENT_TAGS = [
"edge",
"node",
"nodename",
"wallpaper",
"linkinfo",
"antenna",
"wireless",
"selectednodes",
"shape",
"shapetext",
]
class CanvasGraph(tk.Canvas):
@ -40,7 +51,6 @@ class CanvasGraph(tk.Canvas):
self.setup_bindings()
self.draw_grid(width, height)
self.core = core
self.helper = GraphHelper(self, core)
self.throughput_draw = Throughput(self, core)
self.shape_drawing = False
@ -96,7 +106,8 @@ class CanvasGraph(tk.Canvas):
:return: nothing
"""
# delete any existing drawn items
self.helper.delete_canvas_components()
for tag in CANVAS_COMPONENT_TAGS:
self.delete(tag)
# set the private variables to default value
self.mode = GraphMode.SELECT
@ -195,9 +206,6 @@ class CanvasGraph(tk.Canvas):
if link.type == core_pb2.LinkType.WIRELESS:
self.add_wireless_edge(canvas_node_one, canvas_node_two)
else:
is_node_one_wireless = NodeUtils.is_wireless_node(node_one.type)
is_node_two_wireless = NodeUtils.is_wireless_node(node_two.type)
has_no_wireless = not (is_node_one_wireless or is_node_two_wireless)
edge = CanvasEdge(
node_one.position.x,
node_one.position.y,
@ -205,15 +213,14 @@ class CanvasGraph(tk.Canvas):
node_two.position.y,
canvas_node_one.id,
self,
is_wired=has_no_wireless,
)
edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id)))
edge.dst = canvas_node_two.id
edge.check_wireless()
canvas_node_one.edges.add(edge)
canvas_node_two.edges.add(edge)
self.edges[edge.token] = edge
self.core.links[edge.token] = link
self.helper.redraw_antenna(canvas_node_one, canvas_node_two)
edge.link_info = LinkInfo(self, edge, link)
if link.HasField("interface_one"):
canvas_node_one.interfaces.append(link.interface_one)
@ -311,13 +318,16 @@ class CanvasGraph(tk.Canvas):
edge.delete()
return
# set dst node and snap edge to center
x, y = self.coords(self.selected)
edge.complete(self.selected, x, y)
logging.debug(f"drawing edge token: {edge.token}")
if edge.token in self.edges:
# ignore repeated edges
token = tuple(sorted((edge.src, self.selected)))
if token in self.edges:
edge.delete()
else:
return
# set dst node and snap edge to center
edge.complete(self.selected)
logging.debug("drawing edge token: %s", edge.token)
self.edges[edge.token] = edge
node_src = self.nodes[edge.src]
node_src.edges.add(edge)
@ -325,7 +335,6 @@ class CanvasGraph(tk.Canvas):
node_dst.edges.add(edge)
link = self.core.create_link(edge, node_src, node_dst)
edge.link_info = LinkInfo(self, edge, link)
logging.debug(f"edges: {self.find_withtag('edge')}")
def select_object(self, object_id, choose_multiple=False):
"""
@ -374,11 +383,8 @@ class CanvasGraph(tk.Canvas):
self.delete(object_id)
self.delete(selection_id)
self.delete(canvas_node.text_id)
# delete antennas
canvas_node.delete_antennae()
is_wireless = NodeUtils.is_wireless_node(canvas_node.core_node.type)
if is_wireless:
canvas_node.antenna_draw.delete_antennas()
# delete related edges
for edge in canvas_node.edges:
@ -401,7 +407,7 @@ class CanvasGraph(tk.Canvas):
except ValueError:
pass
if is_wireless:
other_node.antenna_draw.delete_antenna()
other_node.delete_antenna()
if object_id in self.shapes:
selection_id = self.selection[object_id]
self.shapes[object_id].delete()
@ -424,6 +430,7 @@ class CanvasGraph(tk.Canvas):
if self.mode == GraphMode.EDGE and is_node:
x, y = self.coords(selected)
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self)
self.tag_raise(selected)
if (
self.mode == GraphMode.ANNOTATION
and self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE]

View file

@ -1,144 +0,0 @@
"""
Some graph helper functions
"""
import logging
import tkinter as tk
from coretk.images import ImageEnum, Images
from coretk.nodeutils import NodeUtils
CANVAS_COMPONENT_TAGS = [
"edge",
"node",
"nodename",
"wallpaper",
"linkinfo",
"antenna",
"wireless",
"selectednodes",
"shape",
"shapetext",
]
class GraphHelper:
def __init__(self, canvas, core):
"""
create an instance of GraphHelper object
"""
self.canvas = canvas
self.core = core
def delete_canvas_components(self):
"""
delete the components of the graph leaving only the blank canvas
:return: nothing
"""
for tag in CANVAS_COMPONENT_TAGS:
for i in self.canvas.find_withtag(tag):
self.canvas.delete(i)
def draw_wireless_case(self, src_id, dst_id, edge):
src_node_type = self.canvas.nodes[src_id].core_node.type
dst_node_type = self.canvas.nodes[dst_id].core_node.type
is_src_wireless = NodeUtils.is_wireless_node(src_node_type)
is_dst_wireless = NodeUtils.is_wireless_node(dst_node_type)
if is_src_wireless or is_dst_wireless:
self.canvas.itemconfig(edge.id, state=tk.HIDDEN)
edge.wired = False
if edge.token not in self.canvas.edges:
if is_src_wireless and is_dst_wireless:
self.canvas.nodes[src_id].antenna_draw.add_antenna()
elif is_src_wireless:
self.canvas.nodes[dst_id].antenna_draw.add_antenna()
else:
self.canvas.nodes[src_id].antenna_draw.add_antenna()
edge.wired = True
def redraw_antenna(self, node_one, node_two):
is_node_one_wireless = NodeUtils.is_wireless_node(node_one.core_node.type)
is_node_two_wireless = NodeUtils.is_wireless_node(node_two.core_node.type)
if is_node_one_wireless or is_node_two_wireless:
if is_node_one_wireless and not is_node_two_wireless:
node_two.antenna_draw.add_antenna()
elif not is_node_one_wireless and is_node_two_wireless:
node_one.antenna_draw.add_antenna()
else:
logging.error("bad link between two wireless nodes")
def update_wlan_connection(self, old_x, old_y, new_x, new_y, edge_ids):
for eid in edge_ids:
x1, y1, x2, y2 = self.canvas.coords(eid)
if x1 == old_x and y1 == old_y:
self.canvas.coords(eid, new_x, new_y, x2, y2)
else:
self.canvas.coords(eid, x1, y1, new_x, new_y)
class WlanAntennaManager:
def __init__(self, canvas, node_id):
"""
crate an instance for AntennaManager
"""
self.canvas = canvas
self.node_id = node_id
self.quantity = 0
self._max = 5
self.antennas = []
self.image = Images.get(ImageEnum.ANTENNA, 32)
# distance between each antenna
self.offset = 0
def add_antenna(self):
"""
add an antenna to a node
:return: nothing
"""
if self.quantity < 5:
x, y = self.canvas.coords(self.node_id)
aid = self.canvas.create_image(
x - 16 + self.offset,
y - 23,
anchor=tk.CENTER,
image=self.image,
tags="antenna",
)
# self.canvas.tag_raise("antenna")
self.antennas.append(aid)
self.quantity = self.quantity + 1
self.offset = self.offset + 8
def delete_antenna(self):
"""
delete one antenna
:return: nothing
"""
if len(self.antennas) > 0:
self.canvas.delete(self.antennas.pop())
self.quantity -= 1
self.offset -= 8
def delete_antennas(self):
"""
delete all antennas
:return: nothing
"""
for aid in self.antennas:
self.canvas.delete(aid)
self.antennas.clear()
self.quantity = 0
self.offset = 0
def update_antennas_position(self, offset_x, offset_y):
"""
redraw antennas of a node according to the new node position
:return: nothing
"""
for i in self.antennas:
self.canvas.move(i, offset_x, offset_y)

View file

@ -7,8 +7,8 @@ from coretk.dialogs.mobilityconfig import MobilityConfigDialog
from coretk.dialogs.nodeconfig import NodeConfigDialog
from coretk.dialogs.wlanconfig import WlanConfigDialog
from coretk.graph.enums import GraphMode
from coretk.graph.graph_helper import WlanAntennaManager
from coretk.graph.tooltip import CanvasTooltip
from coretk.nodeutils import NodeUtils
NODE_TEXT_OFFSET = 5
@ -35,7 +35,6 @@ class CanvasNode:
font=text_font,
fill="#0000CD",
)
self.antenna_draw = WlanAntennaManager(self.canvas, self.id)
self.tooltip = CanvasTooltip(self.canvas)
self.canvas.tag_bind(self.id, "<ButtonPress-1>", self.click_press)
self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release)
@ -48,6 +47,50 @@ class CanvasNode:
self.interfaces = []
self.wireless_edges = set()
self.moving = None
self.antennae = []
def add_antenna(self):
x, y = self.canvas.coords(self.id)
offset = len(self.antennae) * 8
antenna_id = self.canvas.create_image(
x - 16 + offset,
y - 23,
anchor=tk.CENTER,
image=NodeUtils.ANTENNA_ICON,
tags="antenna",
)
self.antennae.append(antenna_id)
def delete_antenna(self):
"""
delete one antenna
:return: nothing
"""
if self.antennae:
antenna_id = self.antennae.pop()
self.canvas.delete(antenna_id)
def delete_antennae(self):
"""
delete all antennas
:return: nothing
"""
logging.info("deleting antennae: %s", self.antennae)
for antenna_id in self.antennae:
self.canvas.delete(antenna_id)
self.antennae.clear()
def move_antennae(self, x_offset, y_offset):
"""
redraw antennas of a node according to the new node position
:return: nothing
"""
for antenna_id in self.antennae:
self.canvas.move(antenna_id, x_offset, y_offset)
def redraw(self):
self.canvas.itemconfig(self.id, image=self.image)
@ -62,7 +105,7 @@ class CanvasNode:
self.core_node.position.y = int(y)
self.canvas.move(self.id, x_offset, y_offset)
self.canvas.move(self.text_id, x_offset, y_offset)
self.antenna_draw.update_antennas_position(x_offset, y_offset)
self.move_antennae(x_offset, y_offset)
self.canvas.object_drag(self.id, x_offset, y_offset)
for edge in self.edges:
x1, y1, x2, y2 = self.canvas.coords(edge.id)

View file

@ -2,6 +2,7 @@ from core.api.grpc.core_pb2 import NodeType
from coretk.images import ImageEnum, Images
ICON_SIZE = 48
ANTENNA_SIZE = 32
class NodeDraw:
@ -49,6 +50,7 @@ class NodeUtils:
WIRELESS_NODES = {NodeType.WIRELESS_LAN, NodeType.EMANE}
IGNORE_NODES = {NodeType.CONTROL_NET, NodeType.PEER_TO_PEER}
NODE_MODELS = {"router", "host", "PC", "mdr", "prouter"}
ANTENNA_ICON = None
@classmethod
def is_ignore_node(cls, node_type):
@ -104,3 +106,4 @@ class NodeUtils:
node_draw = NodeDraw.from_setup(image_enum, node_type, tooltip=tooltip)
cls.NETWORK_NODES.append(node_draw)
cls.NODE_ICONS[(node_type, None)] = node_draw.image
cls.ANTENNA_ICON = Images.get(ImageEnum.ANTENNA, ANTENNA_SIZE)