removed nodedelete module and added logic to the graph canvas itself

This commit is contained in:
Blake Harnden 2019-12-05 13:13:35 -08:00
parent d970d5ee85
commit 7c8f9dac0f
5 changed files with 129 additions and 139 deletions

View file

@ -11,7 +11,6 @@ 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.nodedelete import CanvasComponentManagement
from coretk.graph.shape import Shape
from coretk.images import ImageEnum, Images
from coretk.nodeutils import NodeUtils
@ -28,6 +27,7 @@ class CanvasGraph(tk.Canvas):
self.app = master
self.mode = GraphMode.SELECT
self.annotation_type = None
self.selection = {}
self.selected = None
self.node_draw = None
self.context = None
@ -37,7 +37,6 @@ class CanvasGraph(tk.Canvas):
self.wireless_edges = {}
self.drawing_edge = None
self.grid = None
self.canvas_management = CanvasComponentManagement(self, core)
self.setup_bindings()
self.draw_grid(width, height)
self.core = core
@ -328,6 +327,90 @@ class CanvasGraph(tk.Canvas):
edge.link_info = LinkInfo(self, edge, link)
logging.debug(f"edges: {self.find_withtag('edge')}")
def select_object(self, object_id, choose_multiple=False):
"""
create a bounding box when a node is selected
"""
if not choose_multiple:
self.clear_selection()
# draw a bounding box if node hasn't been selected yet
if object_id not in self.selection:
x0, y0, x1, y1 = self.bbox(object_id)
selection_id = self.create_rectangle(
(x0 - 6, y0 - 6, x1 + 6, y1 + 6),
activedash=True,
dash="-",
tags="selectednodes",
)
self.selection[object_id] = selection_id
else:
selection_id = self.selection.pop(object_id)
self.delete(selection_id)
def clear_selection(self):
"""
Clear current selection boxes.
:return: nothing
"""
for _id in self.selection.values():
self.delete(_id)
self.selection.clear()
def object_drag(self, object_id, offset_x, offset_y):
select_id = self.selection.get(object_id)
if select_id is not None:
self.move(select_id, offset_x, offset_y)
def delete_selection_objects(self):
edges = set()
nodes = []
for object_id in self.selection:
if object_id in self.nodes:
selection_id = self.selection[object_id]
canvas_node = self.nodes.pop(object_id)
nodes.append(canvas_node)
self.delete(object_id)
self.delete(selection_id)
self.delete(canvas_node.text_id)
# delete antennas
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:
if edge in edges:
continue
edges.add(edge)
self.edges.pop(edge.token)
self.delete(edge.id)
self.delete(edge.link_info.id1)
self.delete(edge.link_info.id2)
other_id = edge.src
other_interface = edge.src_interface
if edge.src == object_id:
other_id = edge.dst
other_interface = edge.dst_interface
other_node = self.nodes[other_id]
other_node.edges.remove(edge)
try:
other_node.interfaces.remove(other_interface)
except ValueError:
pass
if is_wireless:
other_node.antenna_draw.delete_antenna()
if object_id in self.shapes:
selection_id = self.selection[object_id]
self.shapes[object_id].delete()
self.delete(selection_id)
self.shapes.pop(object_id)
self.selection.clear()
return nodes
def click_press(self, event):
"""
Start drawing an edge if mouse click is on a node
@ -337,7 +420,7 @@ class CanvasGraph(tk.Canvas):
"""
logging.debug(f"click press: {event}")
selected = self.get_selected(event)
is_node = selected in self.find_withtag("node")
is_node = selected in self.nodes
if self.mode == GraphMode.EDGE and is_node:
x, y = self.coords(selected)
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self)
@ -353,27 +436,26 @@ class CanvasGraph(tk.Canvas):
self.shape_drawing = True
if self.mode == GraphMode.SELECT:
if selected is not None:
if "shape" in self.gettags(selected):
if selected in self.shapes:
x, y = self.canvas_xy(event)
self.shapes[selected].cursor_x = x
self.shapes[selected].cursor_y = y
if selected not in self.canvas_management.selected:
self.canvas_management.node_select(self.shapes[selected])
shape = self.shapes[selected]
shape.cursor_x = x
shape.cursor_y = y
if selected not in self.selection:
self.select_object(shape.id)
self.selected = selected
else:
for i in self.find_withtag("selectednodes"):
self.delete(i)
self.canvas_management.selected.clear()
self.clear_selection()
def ctrl_click(self, event):
logging.debug("Control left click %s", event)
logging.debug("control left click: %s", event)
selected = self.get_selected(event)
if (
self.mode == GraphMode.SELECT
and selected is not None
and "shape" in self.gettags(selected)
and selected in self.shapes
):
self.canvas_management.node_select(self.shapes[selected], True)
self.select_object(selected, choose_multiple=True)
def click_motion(self, event):
"""
@ -396,21 +478,24 @@ class CanvasGraph(tk.Canvas):
if (
self.mode == GraphMode.SELECT
and self.selected is not None
and "shape" in self.gettags(self.selected)
and self.selected in self.shapes
):
x, y = self.canvas_xy(event)
shape = self.shapes[self.selected]
delta_x = x - shape.cursor_x
delta_y = y - shape.cursor_y
shape.motion(event)
# move other selected components
for nid in self.canvas_management.selected:
if nid != self.selected and nid in self.shapes:
self.shapes[nid].motion(None, delta_x, delta_y)
if nid != self.selected and nid in self.nodes:
node_x = self.nodes[nid].core_node.position.x
node_y = self.nodes[nid].core_node.position.y
self.nodes[nid].move(node_x + delta_x, node_y + delta_y)
for _id in self.selection:
if _id != self.selected and _id in self.shapes:
shape = self.shapes[_id]
shape.motion(None, delta_x, delta_y)
if _id != self.selected and _id in self.nodes:
node = self.nodes[_id]
node_x = node.core_node.position.x
node_y = node.core_node.position.y
node.move(node_x + delta_x, node_y + delta_y)
def click_context(self, event):
logging.info("context event: %s", self.context)
@ -432,17 +517,18 @@ class CanvasGraph(tk.Canvas):
:return:
"""
logging.debug("press delete key")
nodes = self.canvas_management.delete_selected_nodes()
nodes = self.delete_selection_objects()
self.core.delete_graph_nodes(nodes)
def double_click(self, event):
selected = self.get_selected(event)
if selected is not None and "shape" in self.gettags(selected):
s = ShapeDialog(self.app, self.app, self.shapes[selected])
s.show()
if selected is not None and selected in self.shapes:
shape = self.shapes[selected]
dialog = ShapeDialog(self.app, self.app, shape)
dialog.show()
def add_node(self, x, y):
if self.selected is None or "shape" in self.gettags(self.selected):
if self.selected is None or self.selected in self.shapes:
core_node = self.core.create_node(
int(x), int(y), self.node_draw.node_type, self.node_draw.model
)

View file

@ -6,9 +6,9 @@ from coretk.dialogs.emaneconfig import EmaneConfigDialog
from coretk.dialogs.mobilityconfig import MobilityConfigDialog
from coretk.dialogs.nodeconfig import NodeConfigDialog
from coretk.dialogs.wlanconfig import WlanConfigDialog
from coretk.graph.canvastooltip import CanvasTooltip
from coretk.graph.enums import GraphMode
from coretk.graph.graph_helper import WlanAntennaManager
from coretk.graph.tooltip import CanvasTooltip
NODE_TEXT_OFFSET = 5
@ -63,7 +63,7 @@ class CanvasNode:
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.canvas.canvas_management.node_drag(self, 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)
if edge.src == self.id:
@ -107,8 +107,8 @@ class CanvasNode:
def click_press(self, event):
logging.debug(f"node click press {self.core_node.name}: {event}")
self.moving = self.canvas.canvas_xy(event)
if self.id not in self.canvas.canvas_management.selected:
self.canvas.canvas_management.node_select(self)
if self.id not in self.canvas.selection:
self.canvas.select_object(self.id)
self.canvas.selected = self.id
def click_release(self, event):
@ -123,19 +123,22 @@ class CanvasNode:
my_x = self.core_node.position.x
my_y = self.core_node.position.y
self.move(x, y)
# move other selected components
for nid, bboxid in self.canvas.canvas_management.selected.items():
if nid != self.id and nid in self.canvas.nodes:
other_old_x = self.canvas.nodes[nid].core_node.position.x
other_old_y = self.canvas.nodes[nid].core_node.position.y
for object_id, selection_id in self.canvas.selection.items():
if object_id != self.id and object_id in self.canvas.nodes:
canvas_node = self.canvas.nodes[object_id]
other_old_x = canvas_node.core_node.position.x
other_old_y = canvas_node.core_node.position.y
other_new_x = x + other_old_x - my_x
other_new_y = y + other_old_y - my_y
self.canvas.nodes[nid].move(other_new_x, other_new_y)
if nid != self.id and nid in self.canvas.shapes:
self.canvas.shapes[nid].motion(None, x - my_x, y - my_y)
self.canvas.nodes[object_id].move(other_new_x, other_new_y)
elif object_id in self.canvas.shapes:
shape = self.canvas.shapes[object_id]
shape.motion(None, x - my_x, y - my_y)
def select_multiple(self, event):
self.canvas.canvas_management.node_select(self, True)
self.canvas.select_object(self.id, choose_multiple=True)
def show_config(self):
self.canvas.context = None

View file

@ -1,98 +0,0 @@
"""
manage deletion
"""
from coretk.nodeutils import NodeUtils
class CanvasComponentManagement:
def __init__(self, canvas, core):
self.app = core
self.canvas = canvas
# dictionary that maps node to box
self.selected = {}
def node_select(self, canvas_node, choose_multiple=False):
"""
create a bounding box when a node is selected
:param coretk.graph.CanvasNode canvas_node: canvas node object
:return: nothing
"""
if not choose_multiple:
self.delete_current_bbox()
# draw a bounding box if node hasn't been selected yet
if canvas_node.id not in self.selected:
x0, y0, x1, y1 = self.canvas.bbox(canvas_node.id)
bbox_id = self.canvas.create_rectangle(
(x0 - 6, y0 - 6, x1 + 6, y1 + 6),
activedash=True,
dash="-",
tags="selectednodes",
)
self.selected[canvas_node.id] = bbox_id
else:
bbox_id = self.selected.pop(canvas_node.id)
self.canvas.delete(bbox_id)
def node_drag(self, canvas_node, offset_x, offset_y):
select_id = self.selected.get(canvas_node.id)
if select_id is not None:
self.canvas.move(select_id, offset_x, offset_y)
def delete_current_bbox(self):
for bbid in self.selected.values():
self.canvas.delete(bbid)
self.selected.clear()
def delete_selected_nodes(self):
edges = set()
nodes = []
for node_id in self.selected:
if "node" in self.canvas.gettags(node_id):
bbox_id = self.selected[node_id]
canvas_node = self.canvas.nodes.pop(node_id)
nodes.append(canvas_node)
self.canvas.delete(node_id)
self.canvas.delete(bbox_id)
self.canvas.delete(canvas_node.text_id)
# delete antennas
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:
if edge in edges:
continue
edges.add(edge)
self.canvas.edges.pop(edge.token)
self.canvas.delete(edge.id)
self.canvas.delete(edge.link_info.id1)
self.canvas.delete(edge.link_info.id2)
other_id = edge.src
other_interface = edge.src_interface
if edge.src == node_id:
other_id = edge.dst
other_interface = edge.dst_interface
other_node = self.canvas.nodes[other_id]
other_node.edges.remove(edge)
try:
other_node.interfaces.remove(other_interface)
except ValueError:
pass
if is_wireless:
other_node.antenna_draw.delete_antenna()
for shape_id in self.selected:
if "shape" in self.canvas.gettags(shape_id):
bbox_id = self.selected[node_id]
self.canvas.shapes[shape_id].delete()
self.canvas.delete(bbox_id)
self.canvas.shapes.pop(shape_id)
self.selected.clear()
return nodes

View file

@ -127,14 +127,13 @@ class Shape:
logging.debug("Click release on shape %s", self.id)
def motion(self, event, delta_x=None, delta_y=None):
logging.debug("motion on shape %s", self.id)
if event is not None:
delta_x = event.x - self.cursor_x
delta_y = event.y - self.cursor_y
self.cursor_x = event.x
self.cursor_y = event.y
self.canvas.move(self.id, delta_x, delta_y)
self.canvas.canvas_management.node_drag(self, delta_x, delta_y)
self.canvas.object_drag(self.id, delta_x, delta_y)
if self.text_id is not None:
self.canvas.move(self.text_id, delta_x, delta_y)