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 import tkinter as tk
from coretk.nodeutils import NodeUtils
class CanvasWirelessEdge: class CanvasWirelessEdge:
def __init__(self, token, position, src, dst, canvas): def __init__(self, token, position, src, dst, canvas):
@ -22,7 +24,7 @@ class CanvasEdge:
width = 1.4 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 Create an instance of canvas edge object
:param int x1: source x-coord :param int x1: source x-coord
@ -30,41 +32,56 @@ class CanvasEdge:
:param int x2: destination x-coord :param int x2: destination x-coord
:param int y2: destination y-coord :param int y2: destination y-coord
:param int src: source id :param int src: source id
:param tkinter.Canvas canvas: canvas object :param coretk.graph.graph.GraphCanvas canvas: canvas object
""" """
self.src = src self.src = src
self.dst = None self.dst = None
self.src_interface = None self.src_interface = None
self.dst_interface = None self.dst_interface = None
self.canvas = canvas self.canvas = canvas
if is_wired is None or is_wired is True: self.id = self.canvas.create_line(
self.id = self.canvas.create_line( x1, y1, x2, y2, tags="edge", width=self.width, fill="#ff0000"
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.token = None
self.link_info = None self.link_info = None
self.throughput = None self.throughput = None
self.wired = is_wired
def complete(self, dst, x, y): def complete(self, dst):
self.dst = dst self.dst = dst
self.token = tuple(sorted((self.src, self.dst))) self.token = tuple(sorted((self.src, self.dst)))
x, y = self.canvas.coords(self.dst)
x1, y1, _, _ = self.canvas.coords(self.id) x1, y1, _, _ = self.canvas.coords(self.id)
self.canvas.coords(self.id, x1, y1, x, y) 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.src)
self.canvas.tag_raise(self.dst) 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): def delete(self):
self.canvas.delete(self.id) 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.dialogs.shapemod import ShapeDialog
from coretk.graph.edges import CanvasEdge, CanvasWirelessEdge from coretk.graph.edges import CanvasEdge, CanvasWirelessEdge
from coretk.graph.enums import GraphMode, ScaleOption from coretk.graph.enums import GraphMode, ScaleOption
from coretk.graph.graph_helper import GraphHelper
from coretk.graph.linkinfo import LinkInfo, Throughput from coretk.graph.linkinfo import LinkInfo, Throughput
from coretk.graph.node import CanvasNode from coretk.graph.node import CanvasNode
from coretk.graph.shape import Shape from coretk.graph.shape import Shape
@ -16,6 +15,18 @@ from coretk.images import ImageEnum, Images
from coretk.nodeutils import NodeUtils from coretk.nodeutils import NodeUtils
ABOVE_WALLPAPER = ["edge", "linkinfo", "wireless", "antenna", "nodename", "node"] 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): class CanvasGraph(tk.Canvas):
@ -40,7 +51,6 @@ class CanvasGraph(tk.Canvas):
self.setup_bindings() self.setup_bindings()
self.draw_grid(width, height) self.draw_grid(width, height)
self.core = core self.core = core
self.helper = GraphHelper(self, core)
self.throughput_draw = Throughput(self, core) self.throughput_draw = Throughput(self, core)
self.shape_drawing = False self.shape_drawing = False
@ -96,7 +106,8 @@ class CanvasGraph(tk.Canvas):
:return: nothing :return: nothing
""" """
# delete any existing drawn items # 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 # set the private variables to default value
self.mode = GraphMode.SELECT self.mode = GraphMode.SELECT
@ -195,9 +206,6 @@ class CanvasGraph(tk.Canvas):
if link.type == core_pb2.LinkType.WIRELESS: if link.type == core_pb2.LinkType.WIRELESS:
self.add_wireless_edge(canvas_node_one, canvas_node_two) self.add_wireless_edge(canvas_node_one, canvas_node_two)
else: 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( edge = CanvasEdge(
node_one.position.x, node_one.position.x,
node_one.position.y, node_one.position.y,
@ -205,15 +213,14 @@ class CanvasGraph(tk.Canvas):
node_two.position.y, node_two.position.y,
canvas_node_one.id, canvas_node_one.id,
self, self,
is_wired=has_no_wireless,
) )
edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id))) edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id)))
edge.dst = canvas_node_two.id edge.dst = canvas_node_two.id
edge.check_wireless()
canvas_node_one.edges.add(edge) canvas_node_one.edges.add(edge)
canvas_node_two.edges.add(edge) canvas_node_two.edges.add(edge)
self.edges[edge.token] = edge self.edges[edge.token] = edge
self.core.links[edge.token] = link self.core.links[edge.token] = link
self.helper.redraw_antenna(canvas_node_one, canvas_node_two)
edge.link_info = LinkInfo(self, edge, link) edge.link_info = LinkInfo(self, edge, link)
if link.HasField("interface_one"): if link.HasField("interface_one"):
canvas_node_one.interfaces.append(link.interface_one) canvas_node_one.interfaces.append(link.interface_one)
@ -311,21 +318,23 @@ class CanvasGraph(tk.Canvas):
edge.delete() edge.delete()
return return
# set dst node and snap edge to center # ignore repeated edges
x, y = self.coords(self.selected) token = tuple(sorted((edge.src, self.selected)))
edge.complete(self.selected, x, y) if token in self.edges:
logging.debug(f"drawing edge token: {edge.token}")
if edge.token in self.edges:
edge.delete() edge.delete()
else: return
self.edges[edge.token] = edge
node_src = self.nodes[edge.src] # set dst node and snap edge to center
node_src.edges.add(edge) edge.complete(self.selected)
node_dst = self.nodes[edge.dst] logging.debug("drawing edge token: %s", edge.token)
node_dst.edges.add(edge)
link = self.core.create_link(edge, node_src, node_dst) self.edges[edge.token] = edge
edge.link_info = LinkInfo(self, edge, link) node_src = self.nodes[edge.src]
logging.debug(f"edges: {self.find_withtag('edge')}") node_src.edges.add(edge)
node_dst = self.nodes[edge.dst]
node_dst.edges.add(edge)
link = self.core.create_link(edge, node_src, node_dst)
edge.link_info = LinkInfo(self, edge, link)
def select_object(self, object_id, choose_multiple=False): def select_object(self, object_id, choose_multiple=False):
""" """
@ -374,11 +383,8 @@ class CanvasGraph(tk.Canvas):
self.delete(object_id) self.delete(object_id)
self.delete(selection_id) self.delete(selection_id)
self.delete(canvas_node.text_id) self.delete(canvas_node.text_id)
canvas_node.delete_antennae()
# delete antennas
is_wireless = NodeUtils.is_wireless_node(canvas_node.core_node.type) is_wireless = NodeUtils.is_wireless_node(canvas_node.core_node.type)
if is_wireless:
canvas_node.antenna_draw.delete_antennas()
# delete related edges # delete related edges
for edge in canvas_node.edges: for edge in canvas_node.edges:
@ -401,7 +407,7 @@ class CanvasGraph(tk.Canvas):
except ValueError: except ValueError:
pass pass
if is_wireless: if is_wireless:
other_node.antenna_draw.delete_antenna() other_node.delete_antenna()
if object_id in self.shapes: if object_id in self.shapes:
selection_id = self.selection[object_id] selection_id = self.selection[object_id]
self.shapes[object_id].delete() self.shapes[object_id].delete()
@ -424,6 +430,7 @@ class CanvasGraph(tk.Canvas):
if self.mode == GraphMode.EDGE and is_node: if self.mode == GraphMode.EDGE and is_node:
x, y = self.coords(selected) x, y = self.coords(selected)
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self) self.drawing_edge = CanvasEdge(x, y, x, y, selected, self)
self.tag_raise(selected)
if ( if (
self.mode == GraphMode.ANNOTATION self.mode == GraphMode.ANNOTATION
and self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE] 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.nodeconfig import NodeConfigDialog
from coretk.dialogs.wlanconfig import WlanConfigDialog from coretk.dialogs.wlanconfig import WlanConfigDialog
from coretk.graph.enums import GraphMode from coretk.graph.enums import GraphMode
from coretk.graph.graph_helper import WlanAntennaManager
from coretk.graph.tooltip import CanvasTooltip from coretk.graph.tooltip import CanvasTooltip
from coretk.nodeutils import NodeUtils
NODE_TEXT_OFFSET = 5 NODE_TEXT_OFFSET = 5
@ -35,7 +35,6 @@ class CanvasNode:
font=text_font, font=text_font,
fill="#0000CD", fill="#0000CD",
) )
self.antenna_draw = WlanAntennaManager(self.canvas, self.id)
self.tooltip = CanvasTooltip(self.canvas) self.tooltip = CanvasTooltip(self.canvas)
self.canvas.tag_bind(self.id, "<ButtonPress-1>", self.click_press) self.canvas.tag_bind(self.id, "<ButtonPress-1>", self.click_press)
self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release) self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release)
@ -48,6 +47,50 @@ class CanvasNode:
self.interfaces = [] self.interfaces = []
self.wireless_edges = set() self.wireless_edges = set()
self.moving = None 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): def redraw(self):
self.canvas.itemconfig(self.id, image=self.image) self.canvas.itemconfig(self.id, image=self.image)
@ -62,7 +105,7 @@ class CanvasNode:
self.core_node.position.y = int(y) self.core_node.position.y = int(y)
self.canvas.move(self.id, x_offset, y_offset) self.canvas.move(self.id, x_offset, y_offset)
self.canvas.move(self.text_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) self.canvas.object_drag(self.id, x_offset, y_offset)
for edge in self.edges: for edge in self.edges:
x1, y1, x2, y2 = self.canvas.coords(edge.id) 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 from coretk.images import ImageEnum, Images
ICON_SIZE = 48 ICON_SIZE = 48
ANTENNA_SIZE = 32
class NodeDraw: class NodeDraw:
@ -49,6 +50,7 @@ class NodeUtils:
WIRELESS_NODES = {NodeType.WIRELESS_LAN, NodeType.EMANE} WIRELESS_NODES = {NodeType.WIRELESS_LAN, NodeType.EMANE}
IGNORE_NODES = {NodeType.CONTROL_NET, NodeType.PEER_TO_PEER} IGNORE_NODES = {NodeType.CONTROL_NET, NodeType.PEER_TO_PEER}
NODE_MODELS = {"router", "host", "PC", "mdr", "prouter"} NODE_MODELS = {"router", "host", "PC", "mdr", "prouter"}
ANTENNA_ICON = None
@classmethod @classmethod
def is_ignore_node(cls, node_type): def is_ignore_node(cls, node_type):
@ -104,3 +106,4 @@ class NodeUtils:
node_draw = NodeDraw.from_setup(image_enum, node_type, tooltip=tooltip) node_draw = NodeDraw.from_setup(image_enum, node_type, tooltip=tooltip)
cls.NETWORK_NODES.append(node_draw) cls.NETWORK_NODES.append(node_draw)
cls.NODE_ICONS[(node_type, None)] = node_draw.image cls.NODE_ICONS[(node_type, None)] = node_draw.image
cls.ANTENNA_ICON = Images.get(ImageEnum.ANTENNA, ANTENNA_SIZE)