core-extra/daemon/core/gui/graph/edges.py

219 lines
7.2 KiB
Python

import logging
import tkinter as tk
from tkinter.font import Font
from core.gui import themes
from core.gui.dialogs.linkconfig import LinkConfigurationDialog
from core.gui.graph import tags
from core.gui.nodeutils import NodeUtils
TEXT_DISTANCE = 0.30
EDGE_WIDTH = 3
EDGE_COLOR = "#ff0000"
class CanvasWirelessEdge:
def __init__(self, token, position, src, dst, canvas):
self.token = token
self.src = src
self.dst = dst
self.canvas = canvas
self.id = self.canvas.create_line(
*position, tags=tags.WIRELESS_EDGE, width=1.5, fill="#009933"
)
def delete(self):
self.canvas.delete(self.id)
class CanvasEdge:
"""
Canvas edge class
"""
def __init__(self, x1, y1, x2, y2, src, canvas):
"""
Create an instance of canvas edge object
:param int x1: source x-coord
:param int y1: source y-coord
:param int x2: destination x-coord
:param int y2: destination y-coord
:param int src: source id
: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
self.id = self.canvas.create_line(
x1, y1, x2, y2, tags=tags.EDGE, width=EDGE_WIDTH, fill=EDGE_COLOR
)
self.text_src = None
self.text_dst = None
self.text_middle = None
self.token = None
self.font = Font(size=8)
self.link = None
self.asymmetric_link = None
self.throughput = None
self.set_binding()
def set_binding(self):
self.canvas.tag_bind(self.id, "<ButtonRelease-3>", self.create_context)
def set_link(self, link):
self.link = link
self.draw_labels()
def get_coordinates(self):
x1, y1, x2, y2 = self.canvas.coords(self.id)
v1 = x2 - x1
v2 = y2 - y1
ux = TEXT_DISTANCE * v1
uy = TEXT_DISTANCE * v2
x1 = x1 + ux
y1 = y1 + uy
x2 = x2 - ux
y2 = y2 - uy
return x1, y1, x2, y2
def get_midpoint(self):
x1, y1, x2, y2 = self.canvas.coords(self.id)
x = (x1 + x2) / 2
y = (y1 + y2) / 2
return x, y
def draw_labels(self):
x1, y1, x2, y2 = self.get_coordinates()
label_one = None
if self.link.HasField("interface_one"):
label_one = (
f"{self.link.interface_one.ip4}/{self.link.interface_one.ip4mask}\n"
f"{self.link.interface_one.ip6}/{self.link.interface_one.ip6mask}\n"
)
label_two = None
if self.link.HasField("interface_two"):
label_two = (
f"{self.link.interface_two.ip4}/{self.link.interface_two.ip4mask}\n"
f"{self.link.interface_two.ip6}/{self.link.interface_two.ip6mask}\n"
)
self.text_src = self.canvas.create_text(
x1,
y1,
text=label_one,
justify=tk.CENTER,
font=self.font,
tags=tags.LINK_INFO,
)
self.text_dst = self.canvas.create_text(
x2,
y2,
text=label_two,
justify=tk.CENTER,
font=self.font,
tags=tags.LINK_INFO,
)
def update_labels(self):
"""
Move edge labels based on current position.
:return: nothing
"""
x1, y1, x2, y2 = self.get_coordinates()
self.canvas.coords(self.text_src, x1, y1)
self.canvas.coords(self.text_dst, x2, y2)
if self.text_middle is not None:
x, y = self.get_midpoint()
self.canvas.coords(self.text_middle, x, y)
def set_throughput(self, throughput):
throughput = 0.001 * throughput
value = f"{throughput:.3f} kbps"
if self.text_middle is None:
x, y = self.get_midpoint()
self.text_middle = self.canvas.create_text(
x, y, tags=tags.THROUGHPUT, font=self.font, text=value
)
else:
self.canvas.itemconfig(self.text_middle, text=value)
if throughput > self.canvas.throughput_threshold:
color = self.canvas.throughput_color
width = self.canvas.throughput_width
else:
color = EDGE_COLOR
width = EDGE_WIDTH
self.canvas.itemconfig(self.id, fill=color, width=width)
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.check_wireless()
self.canvas.tag_raise(self.src)
self.canvas.tag_raise(self.dst)
def is_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)
return is_src_wireless or is_dst_wireless
def check_wireless(self):
if self.is_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)
if self.link:
self.canvas.delete(self.text_src)
self.canvas.delete(self.text_dst)
self.canvas.delete(self.text_middle)
def reset(self):
self.canvas.delete(self.text_middle)
self.text_middle = None
self.canvas.itemconfig(self.id, fill=EDGE_COLOR, width=EDGE_WIDTH)
def create_context(self, event):
logging.debug("create link context")
context = tk.Menu(self.canvas)
themes.style_menu(context)
context.add_command(label="Configure", command=self.configure)
context.add_command(label="Delete")
context.add_command(label="Split")
context.add_command(label="Merge")
if self.canvas.app.core.is_runtime():
context.entryconfigure(1, state="disabled")
context.entryconfigure(2, state="disabled")
context.entryconfigure(3, state="disabled")
context.post(event.x_root, event.y_root)
def configure(self):
logging.debug("link configuration")
dialog = LinkConfigurationDialog(self.canvas, self.canvas.app, self)
dialog.show()