Merge pull request #429 from coreemu/enhancement/pygui-multi-links

Enhancement/pygui multi links
This commit is contained in:
bharnden 2020-04-15 10:20:05 -07:00 committed by GitHub
commit 18a6442c8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 243 additions and 125 deletions

View file

@ -5,7 +5,7 @@ verify_ssl = true
[scripts] [scripts]
core = "python scripts/core-daemon -f data/core.conf -l data/logging.conf" core = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
coretk = "python scripts/coretk-gui" core-pygui = "python scripts/core-pygui"
test = "pytest -v tests" test = "pytest -v tests"
test-mock = "pytest -v --mock tests" test-mock = "pytest -v --mock tests"
test-emane = "pytest -v tests/emane" test-emane = "pytest -v tests/emane"

View file

@ -84,6 +84,7 @@ def handle_link_event(event: LinkData) -> core_pb2.LinkEvent:
interface_one=interface_one, interface_one=interface_one,
interface_two=interface_two, interface_two=interface_two,
options=options, options=options,
network_id=event.network_id,
) )
return core_pb2.LinkEvent(message_type=event.message_type.value, link=link) return core_pb2.LinkEvent(message_type=event.message_type.value, link=link)

View file

@ -370,6 +370,7 @@ def convert_link(session: Session, link_data: LinkData) -> core_pb2.Link:
interface_one=interface_one, interface_one=interface_one,
interface_two=interface_two, interface_two=interface_two,
options=options, options=options,
network_id=link_data.network_id,
) )

View file

@ -207,13 +207,20 @@ class CoreClient:
logging.debug("Link event: %s", event) logging.debug("Link event: %s", event)
node_one_id = event.link.node_one_id node_one_id = event.link.node_one_id
node_two_id = event.link.node_two_id node_two_id = event.link.node_two_id
network_id = event.link.network_id
if node_one_id == node_two_id:
logging.warning("ignoring invalid link: %s", event)
return
canvas_node_one = self.canvas_nodes[node_one_id] canvas_node_one = self.canvas_nodes[node_one_id]
canvas_node_two = self.canvas_nodes[node_two_id] canvas_node_two = self.canvas_nodes[node_two_id]
if event.message_type == core_pb2.MessageType.ADD: if event.message_type == core_pb2.MessageType.ADD:
self.app.canvas.add_wireless_edge(canvas_node_one, canvas_node_two) self.app.canvas.add_wireless_edge(
canvas_node_one, canvas_node_two, network_id
)
elif event.message_type == core_pb2.MessageType.DELETE: elif event.message_type == core_pb2.MessageType.DELETE:
self.app.canvas.delete_wireless_edge(canvas_node_one, canvas_node_two) self.app.canvas.delete_wireless_edge(
canvas_node_one, canvas_node_two, network_id
)
else: else:
logging.warning("unknown link event: %s", event.message_type) logging.warning("unknown link event: %s", event.message_type)

View file

@ -1,11 +1,13 @@
import logging import logging
import math
import tkinter as tk import tkinter as tk
from typing import TYPE_CHECKING, Any, Tuple from typing import TYPE_CHECKING, Any, Tuple
from core.api.grpc import core_pb2
from core.gui import themes from core.gui import themes
from core.gui.dialogs.linkconfig import LinkConfigurationDialog from core.gui.dialogs.linkconfig import LinkConfigurationDialog
from core.gui.graph import tags from core.gui.graph import tags
from core.gui.nodeutils import EdgeUtils, NodeUtils from core.gui.nodeutils import NodeUtils
if TYPE_CHECKING: if TYPE_CHECKING:
from core.gui.graph.graph import CanvasGraph from core.gui.graph.graph import CanvasGraph
@ -15,82 +17,202 @@ EDGE_WIDTH = 3
EDGE_COLOR = "#ff0000" EDGE_COLOR = "#ff0000"
WIRELESS_WIDTH = 1.5 WIRELESS_WIDTH = 1.5
WIRELESS_COLOR = "#009933" WIRELESS_COLOR = "#009933"
ARC_DISTANCE = 50
class CanvasWirelessEdge: def interface_label(interface: core_pb2.Interface) -> str:
def __init__( label = ""
self, if interface.ip4:
token: Tuple[Any, ...], label = f"{interface.ip4}/{interface.ip4mask}"
position: Tuple[float, float, float, float], if interface.ip6:
src: int, label = f"{label}\n{interface.ip6}/{interface.ip6mask}"
dst: int, return label
canvas: "CanvasGraph",
):
logging.debug("Draw wireless link from node %s to node %s", src, dst) def create_edge_token(src: int, dst: int, network: int = None) -> Tuple[int, ...]:
self.token = token values = [src, dst]
if network is not None:
values.append(network)
return tuple(sorted(values))
def arc_edges(edges) -> None:
if not edges:
return
mid_index = len(edges) // 2
if mid_index == 0:
arc_step = ARC_DISTANCE
else:
arc_step = ARC_DISTANCE / mid_index
# below edges
arc = 0
for edge in edges[:mid_index]:
arc -= arc_step
edge.arc = arc
edge.redraw()
# mid edge
if len(edges) % 2 != 0:
arc = 0
edge = edges[mid_index]
edge.arc = arc
edge.redraw()
mid_index += 1
# above edges
arc = 0
for edge in edges[mid_index:]:
arc += arc_step
edge.arc = arc
edge.redraw()
class Edge:
tag = tags.EDGE
def __init__(self, canvas: "CanvasGraph", src: int, dst: int = None) -> None:
self.canvas = canvas
self.id = None
self.src = src self.src = src
self.dst = dst self.dst = dst
self.canvas = canvas self.arc = 0
self.token = None
self.color = EDGE_COLOR
self.width = EDGE_WIDTH
@classmethod
def create_token(cls, src: int, dst: int) -> Tuple[int, ...]:
return tuple(sorted([src, dst]))
def _get_arcpoint(
self, src_pos: Tuple[float, float], dst_pos: Tuple[float, float]
) -> Tuple[float, float]:
src_x, src_y = src_pos
dst_x, dst_y = dst_pos
mp_x = (src_x + dst_x) / 2
mp_y = (src_y + dst_y) / 2
slope_denominator = src_x - dst_x
slope_numerator = src_y - dst_y
# vertical line
if slope_denominator == 0:
return mp_x + self.arc, mp_y
# horizontal line
if slope_numerator == 0:
return mp_x, mp_y + self.arc
# everything else
m = slope_numerator / slope_denominator
perp_m = -1 / m
b = mp_y - (perp_m * mp_x)
# get arc x and y
offset = math.sqrt(self.arc ** 2 / (1 + (1 / m ** 2)))
arc_x = mp_x
if self.arc >= 0:
arc_x += offset
else:
arc_x -= offset
arc_y = (perp_m * arc_x) + b
return arc_x, arc_y
def draw(self, src_pos: Tuple[float, float], dst_pos: Tuple[float, float]) -> None:
arc_pos = self._get_arcpoint(src_pos, dst_pos)
self.id = self.canvas.create_line( self.id = self.canvas.create_line(
*position, *src_pos,
tags=tags.WIRELESS_EDGE, *arc_pos,
width=WIRELESS_WIDTH * self.canvas.app.app_scale, *dst_pos,
fill=WIRELESS_COLOR, smooth=True,
tags=self.tag,
width=self.width * self.canvas.app.app_scale,
fill=self.color,
) )
def delete(self): def redraw(self):
width = self.width * self.canvas.app.app_scale
self.canvas.itemconfig(self.id, width=width, fill=self.color)
src_x, src_y, _, _, _, _ = self.canvas.coords(self.id)
self.move_src(src_x, src_y)
def move_node(self, node_id: int, x: float, y: float) -> None:
if self.src == node_id:
self.move_src(x, y)
else:
self.move_dst(x, y)
def move_dst(self, x: float, y: float) -> None:
dst_pos = (x, y)
src_x, src_y, _, _, _, _ = self.canvas.coords(self.id)
src_pos = (src_x, src_y)
arc_pos = self._get_arcpoint(src_pos, dst_pos)
self.canvas.coords(self.id, *src_pos, *arc_pos, *dst_pos)
def move_src(self, x: float, y: float) -> None:
src_pos = (x, y)
_, _, _, _, dst_x, dst_y = self.canvas.coords(self.id)
dst_pos = (dst_x, dst_y)
arc_pos = self._get_arcpoint(src_pos, dst_pos)
self.canvas.coords(self.id, *src_pos, *arc_pos, *dst_pos)
def delete(self) -> None:
self.canvas.delete(self.id) self.canvas.delete(self.id)
class CanvasEdge: class CanvasWirelessEdge(Edge):
tag = tags.WIRELESS_EDGE
def __init__(
self,
canvas: "CanvasGraph",
src: int,
dst: int,
src_pos: Tuple[float, float],
dst_pos: Tuple[float, float],
token: Tuple[Any, ...],
) -> None:
logging.debug("drawing wireless link from node %s to node %s", src, dst)
super().__init__(canvas, src, dst)
self.token = token
self.width = WIRELESS_WIDTH
self.color = WIRELESS_COLOR
self.draw(src_pos, dst_pos)
class CanvasEdge(Edge):
""" """
Canvas edge class Canvas edge class
""" """
def __init__( def __init__(
self, self,
x1: float,
y1: float,
x2: float,
y2: float,
src: int,
canvas: "CanvasGraph", canvas: "CanvasGraph",
): src: int,
src_pos: Tuple[float, float],
dst_pos: Tuple[float, float],
) -> None:
""" """
Create an instance of canvas edge object Create an instance of canvas edge object
""" """
self.src = src super().__init__(canvas, src)
self.dst = None
self.src_interface = None self.src_interface = None
self.dst_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 * self.canvas.app.app_scale,
fill=EDGE_COLOR,
)
self.text_src = None self.text_src = None
self.text_dst = None self.text_dst = None
self.text_middle = None self.text_middle = None
self.token = None
self.link = None self.link = None
self.asymmetric_link = None self.asymmetric_link = None
self.throughput = None self.throughput = None
self.draw(src_pos, dst_pos)
self.set_binding() self.set_binding()
def set_binding(self): def move_node(self, node_id: int, x: float, y: float) -> None:
super().move_node(node_id, x, y)
self.update_labels()
def set_binding(self) -> None:
self.canvas.tag_bind(self.id, "<ButtonRelease-3>", self.create_context) self.canvas.tag_bind(self.id, "<ButtonRelease-3>", self.create_context)
def set_link(self, link): def set_link(self, link) -> None:
self.link = link self.link = link
self.draw_labels() self.draw_labels()
def get_coordinates(self) -> [float, float, float, float]: def get_coordinates(self) -> [float, float, float, float]:
x1, y1, x2, y2 = self.canvas.coords(self.id) x1, y1, _, _, x2, y2 = self.canvas.coords(self.id)
v1 = x2 - x1 v1 = x2 - x1
v2 = y2 - y1 v2 = y2 - y1
ux = TEXT_DISTANCE * v1 ux = TEXT_DISTANCE * v1
@ -107,24 +229,16 @@ class CanvasEdge:
y = (y1 + y2) / 2 y = (y1 + y2) / 2
return x, y return x, y
def create_labels(self): def create_labels(self) -> Tuple[str, str]:
label_one = None label_one = None
if self.link.HasField("interface_one"): if self.link.HasField("interface_one"):
label_one = self.create_label(self.link.interface_one) label_one = interface_label(self.link.interface_one)
label_two = None label_two = None
if self.link.HasField("interface_two"): if self.link.HasField("interface_two"):
label_two = self.create_label(self.link.interface_two) label_two = interface_label(self.link.interface_two)
return label_one, label_two return label_one, label_two
def create_label(self, interface): def draw_labels(self) -> None:
label = ""
if interface.ip4:
label = f"{interface.ip4}/{interface.ip4mask}"
if interface.ip6:
label = f"{label}\n{interface.ip6}/{interface.ip6mask}"
return label
def draw_labels(self):
x1, y1, x2, y2 = self.get_coordinates() x1, y1, x2, y2 = self.get_coordinates()
label_one, label_two = self.create_labels() label_one, label_two = self.create_labels()
self.text_src = self.canvas.create_text( self.text_src = self.canvas.create_text(
@ -144,12 +258,13 @@ class CanvasEdge:
tags=tags.LINK_INFO, tags=tags.LINK_INFO,
) )
def redraw(self): def redraw(self) -> None:
super().redraw()
label_one, label_two = self.create_labels() label_one, label_two = self.create_labels()
self.canvas.itemconfig(self.text_src, text=label_one) self.canvas.itemconfig(self.text_src, text=label_one)
self.canvas.itemconfig(self.text_dst, text=label_two) self.canvas.itemconfig(self.text_dst, text=label_two)
def update_labels(self): def update_labels(self) -> None:
""" """
Move edge labels based on current position. Move edge labels based on current position.
""" """
@ -160,7 +275,7 @@ class CanvasEdge:
x, y = self.get_midpoint() x, y = self.get_midpoint()
self.canvas.coords(self.text_middle, x, y) self.canvas.coords(self.text_middle, x, y)
def set_throughput(self, throughput: float): def set_throughput(self, throughput: float) -> None:
throughput = 0.001 * throughput throughput = 0.001 * throughput
value = f"{throughput:.3f} kbps" value = f"{throughput:.3f} kbps"
if self.text_middle is None: if self.text_middle is None:
@ -179,18 +294,17 @@ class CanvasEdge:
width = EDGE_WIDTH width = EDGE_WIDTH
self.canvas.itemconfig(self.id, fill=color, width=width) self.canvas.itemconfig(self.id, fill=color, width=width)
def complete(self, dst: int): def complete(self, dst: int) -> None:
self.dst = dst self.dst = dst
self.token = EdgeUtils.get_token(self.src, self.dst) self.token = create_edge_token(self.src, self.dst)
x, y = self.canvas.coords(self.dst) x, y = self.canvas.coords(self.dst)
x1, y1, _, _ = self.canvas.coords(self.id) self.move_dst(x, y)
self.canvas.coords(self.id, x1, y1, x, y)
self.check_wireless() 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)
logging.debug("Draw wired link from node %s to node %s", self.src, dst) logging.debug("Draw wired link from node %s to node %s", self.src, dst)
def is_wireless(self) -> [bool, bool]: def is_wireless(self) -> bool:
src_node = self.canvas.nodes[self.src] src_node = self.canvas.nodes[self.src]
dst_node = self.canvas.nodes[self.dst] dst_node = self.canvas.nodes[self.dst]
src_node_type = src_node.core_node.type src_node_type = src_node.core_node.type
@ -210,12 +324,12 @@ class CanvasEdge:
wlan_network[self.dst].add(self.src) wlan_network[self.dst].add(self.src)
return is_src_wireless or is_dst_wireless return is_src_wireless or is_dst_wireless
def check_wireless(self): def check_wireless(self) -> None:
if self.is_wireless(): if self.is_wireless():
self.canvas.itemconfig(self.id, state=tk.HIDDEN) self.canvas.itemconfig(self.id, state=tk.HIDDEN)
self._check_antenna() self._check_antenna()
def _check_antenna(self): def _check_antenna(self) -> None:
src_node = self.canvas.nodes[self.src] src_node = self.canvas.nodes[self.src]
dst_node = self.canvas.nodes[self.dst] dst_node = self.canvas.nodes[self.dst]
src_node_type = src_node.core_node.type src_node_type = src_node.core_node.type
@ -230,20 +344,19 @@ class CanvasEdge:
else: else:
src_node.add_antenna() src_node.add_antenna()
def delete(self): def delete(self) -> None:
logging.debug("Delete canvas edge, id: %s", self.id) logging.debug("Delete canvas edge, id: %s", self.id)
self.canvas.delete(self.id) super().delete()
if self.link: self.canvas.delete(self.text_src)
self.canvas.delete(self.text_src) self.canvas.delete(self.text_dst)
self.canvas.delete(self.text_dst)
self.canvas.delete(self.text_middle) self.canvas.delete(self.text_middle)
def reset(self): def reset(self) -> None:
self.canvas.delete(self.text_middle) self.canvas.delete(self.text_middle)
self.text_middle = None self.text_middle = None
self.canvas.itemconfig(self.id, fill=EDGE_COLOR, width=EDGE_WIDTH) self.canvas.itemconfig(self.id, fill=EDGE_COLOR, width=EDGE_WIDTH)
def create_context(self, event: tk.Event): def create_context(self, event: tk.Event) -> None:
context = tk.Menu(self.canvas) context = tk.Menu(self.canvas)
themes.style_menu(context) themes.style_menu(context)
context.add_command(label="Configure", command=self.configure) context.add_command(label="Configure", command=self.configure)
@ -256,6 +369,6 @@ class CanvasEdge:
context.entryconfigure(3, state="disabled") context.entryconfigure(3, state="disabled")
context.post(event.x_root, event.y_root) context.post(event.x_root, event.y_root)
def configure(self): def configure(self) -> None:
dialog = LinkConfigurationDialog(self.canvas, self.canvas.app, self) dialog = LinkConfigurationDialog(self.canvas, self.canvas.app, self)
dialog.show() dialog.show()

View file

@ -7,13 +7,19 @@ from PIL import Image, ImageTk
from core.api.grpc import core_pb2 from core.api.grpc import core_pb2
from core.gui.dialogs.shapemod import ShapeDialog from core.gui.dialogs.shapemod import ShapeDialog
from core.gui.graph import tags from core.gui.graph import tags
from core.gui.graph.edges import EDGE_WIDTH, CanvasEdge, CanvasWirelessEdge from core.gui.graph.edges import (
EDGE_WIDTH,
CanvasEdge,
CanvasWirelessEdge,
arc_edges,
create_edge_token,
)
from core.gui.graph.enums import GraphMode, ScaleOption from core.gui.graph.enums import GraphMode, ScaleOption
from core.gui.graph.node import CanvasNode from core.gui.graph.node import CanvasNode
from core.gui.graph.shape import Shape from core.gui.graph.shape import Shape
from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
from core.gui.images import ImageEnum, Images, TypeToImage from core.gui.images import ImageEnum, Images, TypeToImage
from core.gui.nodeutils import EdgeUtils, NodeUtils from core.gui.nodeutils import NodeUtils
if TYPE_CHECKING: if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
@ -193,27 +199,40 @@ class CanvasGraph(tk.Canvas):
self.tag_lower(tags.GRIDLINE) self.tag_lower(tags.GRIDLINE)
self.tag_lower(self.grid) self.tag_lower(self.grid)
def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode): def add_wireless_edge(
self, src: CanvasNode, dst: CanvasNode, network_id: int = None
):
""" """
add a wireless edge between 2 canvas nodes add a wireless edge between 2 canvas nodes
""" """
token = EdgeUtils.get_token(src.id, dst.id) token = create_edge_token(src.id, dst.id, network_id)
x1, y1 = self.coords(src.id) if token in self.wireless_edges:
x2, y2 = self.coords(dst.id) return
position = (x1, y1, x2, y2) src_pos = self.coords(src.id)
edge = CanvasWirelessEdge(token, position, src.id, dst.id, self) dst_pos = self.coords(dst.id)
edge = CanvasWirelessEdge(self, src.id, dst.id, src_pos, dst_pos, token)
self.wireless_edges[token] = edge self.wireless_edges[token] = edge
src.wireless_edges.add(edge) src.wireless_edges.add(edge)
dst.wireless_edges.add(edge) dst.wireless_edges.add(edge)
self.tag_raise(src.id) self.tag_raise(src.id)
self.tag_raise(dst.id) self.tag_raise(dst.id)
# update arcs when there are multiple links
common_edges = list(src.wireless_edges & dst.wireless_edges)
arc_edges(common_edges)
def delete_wireless_edge(self, src: CanvasNode, dst: CanvasNode): def delete_wireless_edge(
token = EdgeUtils.get_token(src.id, dst.id) self, src: CanvasNode, dst: CanvasNode, network_id: int = None
):
token = create_edge_token(src.id, dst.id, network_id)
if token not in self.wireless_edges:
return
edge = self.wireless_edges.pop(token) edge = self.wireless_edges.pop(token)
edge.delete() edge.delete()
src.wireless_edges.remove(edge) src.wireless_edges.remove(edge)
dst.wireless_edges.remove(edge) dst.wireless_edges.remove(edge)
# update arcs when there are multiple links
common_edges = list(src.wireless_edges & dst.wireless_edges)
arc_edges(common_edges)
def draw_session(self, session: core_pb2.Session): def draw_session(self, session: core_pb2.Session):
""" """
@ -246,20 +265,15 @@ class CanvasGraph(tk.Canvas):
node_one = canvas_node_one.core_node node_one = canvas_node_one.core_node
canvas_node_two = self.core.canvas_nodes[link.node_two_id] canvas_node_two = self.core.canvas_nodes[link.node_two_id]
node_two = canvas_node_two.core_node node_two = canvas_node_two.core_node
token = EdgeUtils.get_token(canvas_node_one.id, canvas_node_two.id) token = create_edge_token(canvas_node_one.id, canvas_node_two.id)
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:
if token not in self.edges: if token not in self.edges:
edge = CanvasEdge( src_pos = (node_one.position.x, node_one.position.y)
node_one.position.x, dst_pos = (node_two.position.x, node_two.position.y)
node_one.position.y, edge = CanvasEdge(self, canvas_node_one.id, src_pos, dst_pos)
node_two.position.x,
node_two.position.y,
canvas_node_one.id,
self,
)
edge.token = token edge.token = token
edge.dst = canvas_node_two.id edge.dst = canvas_node_two.id
edge.set_link(link) edge.set_link(link)
@ -391,7 +405,7 @@ class CanvasGraph(tk.Canvas):
return return
# ignore repeated edges # ignore repeated edges
token = EdgeUtils.get_token(edge.src, self.selected) token = create_edge_token(edge.src, self.selected)
if token in self.edges: if token in self.edges:
edge.delete() edge.delete()
return return
@ -520,11 +534,10 @@ class CanvasGraph(tk.Canvas):
logging.debug("click press offset(%s, %s)", x_check, y_check) logging.debug("click press offset(%s, %s)", x_check, y_check)
is_node = selected in self.nodes is_node = selected in self.nodes
if self.mode == GraphMode.EDGE and is_node: if self.mode == GraphMode.EDGE and is_node:
x, y = self.coords(selected) pos = self.coords(selected)
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self) self.drawing_edge = CanvasEdge(self, selected, pos, pos)
if self.mode == GraphMode.ANNOTATION: if self.mode == GraphMode.ANNOTATION:
if is_marker(self.annotation_type): if is_marker(self.annotation_type):
r = self.app.toolbar.marker_tool.radius r = self.app.toolbar.marker_tool.radius
self.create_oval( self.create_oval(
@ -603,8 +616,7 @@ class CanvasGraph(tk.Canvas):
self.cursor = x, y self.cursor = x, y
if self.mode == GraphMode.EDGE and self.drawing_edge is not None: if self.mode == GraphMode.EDGE and self.drawing_edge is not None:
x1, y1, _, _ = self.coords(self.drawing_edge.id) self.drawing_edge.move_dst(x, y)
self.coords(self.drawing_edge.id, x1, y1, x, y)
if self.mode == GraphMode.ANNOTATION: if self.mode == GraphMode.ANNOTATION:
if is_draw_shape(self.annotation_type) and self.shape_drawing: if is_draw_shape(self.annotation_type) and self.shape_drawing:
shape = self.shapes[self.selected] shape = self.shapes[self.selected]
@ -841,11 +853,10 @@ class CanvasGraph(tk.Canvas):
""" """
create an edge between source node and destination node create an edge between source node and destination node
""" """
if (source.id, dest.id) not in self.edges: token = create_edge_token(source.id, dest.id)
pos0 = source.core_node.position if token not in self.edges:
x0 = pos0.x pos = (source.core_node.position.x, source.core_node.position.y)
y0 = pos0.y edge = CanvasEdge(self, source.id, pos, pos)
edge = CanvasEdge(x0, y0, x0, y0, source.id, self)
edge.complete(dest.id) edge.complete(dest.id)
self.edges[edge.token] = edge self.edges[edge.token] = edge
self.nodes[source.id].edges.add(edge) self.nodes[source.id].edges.add(edge)
@ -905,7 +916,7 @@ class CanvasGraph(tk.Canvas):
dest_node_copy = self.nodes[copy_map[edge.token[1]]] dest_node_copy = self.nodes[copy_map[edge.token[1]]]
self.create_edge(source_node_copy, dest_node_copy) self.create_edge(source_node_copy, dest_node_copy)
copy_edge = self.edges[ copy_edge = self.edges[
EdgeUtils.get_token(source_node_copy.id, dest_node_copy.id) create_edge_token(source_node_copy.id, dest_node_copy.id)
] ]
copy_link = copy_edge.link copy_link = copy_edge.link
options = edge.link.options options = edge.link.options

View file

@ -148,19 +148,9 @@ class CanvasNode:
# move edges # move edges
for edge in self.edges: for edge in self.edges:
x1, y1, x2, y2 = self.canvas.coords(edge.id) edge.move_node(self.id, x, y)
if edge.src == self.id:
self.canvas.coords(edge.id, x, y, x2, y2)
else:
self.canvas.coords(edge.id, x1, y1, x, y)
edge.update_labels()
for edge in self.wireless_edges: for edge in self.wireless_edges:
x1, y1, x2, y2 = self.canvas.coords(edge.id) edge.move_node(self.id, x, y)
if edge.src == self.id:
self.canvas.coords(edge.id, x, y, x2, y2)
else:
self.canvas.coords(edge.id, x1, y1, x, y)
# set actual coords for node and update core is running # set actual coords for node and update core is running
real_x, real_y = self.canvas.get_actual_coords(x, y) real_x, real_y = self.canvas.get_actual_coords(x, y)

View file

@ -1,5 +1,5 @@
import logging import logging
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union from typing import TYPE_CHECKING, Dict, List, Optional, Set, Union
from core.api.grpc.core_pb2 import NodeType from core.api.grpc.core_pb2 import NodeType
from core.gui.images import ImageEnum, Images, TypeToImage from core.gui.images import ImageEnum, Images, TypeToImage
@ -172,9 +172,3 @@ class NodeUtils:
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) cls.ANTENNA_ICON = Images.get(ImageEnum.ANTENNA, ANTENNA_SIZE)
class EdgeUtils:
@classmethod
def get_token(cls, src: int, dst: int) -> Tuple[int, ...]:
return tuple(sorted([src, dst]))

View file

@ -686,6 +686,7 @@ message Link {
Interface interface_one = 4; Interface interface_one = 4;
Interface interface_two = 5; Interface interface_two = 5;
LinkOptions options = 6; LinkOptions options = 6;
int32 network_id = 7;
} }
message LinkOptions { message LinkOptions {