pygui: added logic to reuse shadow nodes when linked between nodes on same canvas, added logic to avoid deleting shadow nodes when still linked to other nodes on same canvas

This commit is contained in:
Blake Harnden 2021-01-02 12:59:15 -08:00
parent a2d9659fb7
commit 63282134f5
2 changed files with 31 additions and 14 deletions

View file

@ -2,7 +2,7 @@ import functools
import logging import logging
import math import math
import tkinter as tk import tkinter as tk
from typing import TYPE_CHECKING, Optional, Tuple from typing import TYPE_CHECKING, Optional, Tuple, Union
from PIL.ImageTk import PhotoImage from PIL.ImageTk import PhotoImage
@ -110,10 +110,20 @@ class ShadowNode:
justify=tk.CENTER, justify=tk.CENTER,
) )
self.canvas.shadow_nodes[self.id] = self self.canvas.shadow_nodes[self.id] = self
self.canvas.shadow_core_nodes[self.node.core_node.id] = self
def position(self) -> Tuple[int, int]: def position(self) -> Tuple[int, int]:
return self.canvas.coords(self.id) return self.canvas.coords(self.id)
def should_delete(self) -> bool:
for edge in self.node.edges:
other_node = edge.src
if self.node == edge.src:
other_node = edge.dst
if other_node.canvas == self.canvas:
return False
return True
def motion(self, x_offset, y_offset) -> None: def motion(self, x_offset, y_offset) -> None:
original_position = self.position() original_position = self.position()
self.canvas.move(self.id, x_offset, y_offset) self.canvas.move(self.id, x_offset, y_offset)
@ -136,6 +146,7 @@ class ShadowNode:
def delete(self): def delete(self):
self.canvas.shadow_nodes.pop(self.id, None) self.canvas.shadow_nodes.pop(self.id, None)
self.canvas.shadow_core_nodes.pop(self.node.core_node.id, None)
self.canvas.delete(self.id) self.canvas.delete(self.id)
self.canvas.delete(self.text_id) self.canvas.delete(self.text_id)
@ -213,10 +224,10 @@ class Edge:
self.id = self.draw_edge(self.src.canvas, self.src, self.src, state) self.id = self.draw_edge(self.src.canvas, self.src, self.src, state)
else: else:
# draw shadow nodes and 2 lines # draw shadow nodes and 2 lines
self.src_shadow = ShadowNode(self.app, self.dst.canvas, self.src) self.src_shadow = self.dst.canvas.get_shadow(self.src)
self.dst_shadow = ShadowNode(self.app, self.src.canvas, self.dst) self.dst_shadow = self.src.canvas.get_shadow(self.dst)
self.id = self.draw_edge(self.src.canvas, self.src, self.dst, state) self.id = self.draw_edge(self.src.canvas, self.src, self.dst_shadow, state)
self.id2 = self.draw_edge(self.dst.canvas, self.src, self.dst, state) self.id2 = self.draw_edge(self.dst.canvas, self.src_shadow, self.dst, state)
logging.info( logging.info(
"drawed edge: src shadow(%s) dst shadow(%s)", "drawed edge: src shadow(%s) dst shadow(%s)",
self.src_shadow, self.src_shadow,
@ -224,7 +235,11 @@ class Edge:
) )
def draw_edge( def draw_edge(
self, canvas: "CanvasGraph", src: "CanvasNode", dst: "CanvasNode", state: str self,
canvas: "CanvasGraph",
src: Union["CanvasNode", "ShadowNode"],
dst: Union["CanvasNode", "ShadowNode"],
state: str,
) -> int: ) -> int:
src_pos = src.position() src_pos = src.position()
dst_pos = dst.position() dst_pos = dst.position()
@ -242,16 +257,11 @@ class Edge:
def redraw(self) -> None: def redraw(self) -> None:
self.src.canvas.itemconfig(self.id, width=self.scaled_width(), fill=self.color) self.src.canvas.itemconfig(self.id, width=self.scaled_width(), fill=self.color)
# src_x, src_y, _, _, _, _ = self.src.canvas.coords(self.id)
# src_pos = src_x, src_y
self.move_src() self.move_src()
if not self.is_same_canvas(): if not self.is_same_canvas():
self.dst.canvas.itemconfig( self.dst.canvas.itemconfig(
self.id2, width=self.scaled_width(), fill=self.color self.id2, width=self.scaled_width(), fill=self.color
) )
# src_x, src_y, _, _, _, _ = self.dst.canvas.coords(self.id2)
# src_pos = src_x, src_y
# self.move_src(src_pos)
self.move_dst() self.move_dst()
def middle_label_text(self, text: str) -> None: def middle_label_text(self, text: str) -> None:
@ -423,10 +433,10 @@ class Edge:
self.dst.canvas.delete(self.id2) self.dst.canvas.delete(self.id2)
self.dst.canvas.delete(self.src_label2) self.dst.canvas.delete(self.src_label2)
self.dst.canvas.delete(self.dst_label2) self.dst.canvas.delete(self.dst_label2)
if self.src_shadow: if self.src_shadow and self.src_shadow.should_delete():
self.src_shadow.delete() self.src_shadow.delete()
self.src_shadow = None self.src_shadow = None
if self.dst_shadow: if self.dst_shadow and self.dst_shadow.should_delete():
self.dst_shadow.delete() self.dst_shadow.delete()
self.dst_shadow = None self.dst_shadow = None
self.clear_middle_label() self.clear_middle_label()
@ -674,7 +684,6 @@ class CanvasEdge(Edge):
self.middle_label_text(label) self.middle_label_text(label)
def delete(self) -> None: def delete(self) -> None:
super().delete()
self.src.edges.discard(self) self.src.edges.discard(self)
if self.dst: if self.dst:
self.dst.edges.discard(self) self.dst.edges.discard(self)
@ -689,4 +698,5 @@ class CanvasEdge(Edge):
if dst_wireless: if dst_wireless:
self.src.delete_antenna() self.src.delete_antenna()
self.app.core.deleted_canvas_edges([self]) self.app.core.deleted_canvas_edges([self])
super().delete()
self.arc_common_edges() self.arc_common_edges()

View file

@ -53,6 +53,7 @@ class CanvasGraph(tk.Canvas):
self.nodes: Dict[int, CanvasNode] = {} self.nodes: Dict[int, CanvasNode] = {}
self.shadow_nodes: Dict[int, ShadowNode] = {} self.shadow_nodes: Dict[int, ShadowNode] = {}
self.shapes: Dict[int, Shape] = {} self.shapes: Dict[int, Shape] = {}
self.shadow_core_nodes: Dict[int, ShadowNode] = {}
# map wireless/EMANE node to the set of MDRs connected to that node # map wireless/EMANE node to the set of MDRs connected to that node
self.wireless_network: Dict[int, Set[int]] = {} self.wireless_network: Dict[int, Set[int]] = {}
@ -114,6 +115,12 @@ class CanvasGraph(tk.Canvas):
self.bind("<ButtonPress-3>", lambda e: self.scan_mark(e.x, e.y)) self.bind("<ButtonPress-3>", lambda e: self.scan_mark(e.x, e.y))
self.bind("<B3-Motion>", lambda e: self.scan_dragto(e.x, e.y, gain=1)) self.bind("<B3-Motion>", lambda e: self.scan_dragto(e.x, e.y, gain=1))
def get_shadow(self, node: CanvasNode) -> ShadowNode:
shadow_node = self.shadow_core_nodes.get(node.core_node.id)
if not shadow_node:
shadow_node = ShadowNode(self.app, self, node)
return shadow_node
def get_actual_coords(self, x: float, y: float) -> Tuple[float, float]: def get_actual_coords(self, x: float, y: float) -> Tuple[float, float]:
actual_x = (x - self.offset[0]) / self.ratio actual_x = (x - self.offset[0]) / self.ratio
actual_y = (y - self.offset[1]) / self.ratio actual_y = (y - self.offset[1]) / self.ratio