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.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.nodedelete import CanvasComponentManagement
from coretk.graph.shape import Shape from coretk.graph.shape import Shape
from coretk.images import ImageEnum, Images from coretk.images import ImageEnum, Images
from coretk.nodeutils import NodeUtils from coretk.nodeutils import NodeUtils
@ -28,6 +27,7 @@ class CanvasGraph(tk.Canvas):
self.app = master self.app = master
self.mode = GraphMode.SELECT self.mode = GraphMode.SELECT
self.annotation_type = None self.annotation_type = None
self.selection = {}
self.selected = None self.selected = None
self.node_draw = None self.node_draw = None
self.context = None self.context = None
@ -37,7 +37,6 @@ class CanvasGraph(tk.Canvas):
self.wireless_edges = {} self.wireless_edges = {}
self.drawing_edge = None self.drawing_edge = None
self.grid = None self.grid = None
self.canvas_management = CanvasComponentManagement(self, core)
self.setup_bindings() self.setup_bindings()
self.draw_grid(width, height) self.draw_grid(width, height)
self.core = core self.core = core
@ -328,6 +327,90 @@ class CanvasGraph(tk.Canvas):
edge.link_info = LinkInfo(self, edge, link) edge.link_info = LinkInfo(self, edge, link)
logging.debug(f"edges: {self.find_withtag('edge')}") 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): def click_press(self, event):
""" """
Start drawing an edge if mouse click is on a node 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}") logging.debug(f"click press: {event}")
selected = self.get_selected(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: 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)
@ -353,27 +436,26 @@ class CanvasGraph(tk.Canvas):
self.shape_drawing = True self.shape_drawing = True
if self.mode == GraphMode.SELECT: if self.mode == GraphMode.SELECT:
if selected is not None: if selected is not None:
if "shape" in self.gettags(selected): if selected in self.shapes:
x, y = self.canvas_xy(event) x, y = self.canvas_xy(event)
self.shapes[selected].cursor_x = x shape = self.shapes[selected]
self.shapes[selected].cursor_y = y shape.cursor_x = x
if selected not in self.canvas_management.selected: shape.cursor_y = y
self.canvas_management.node_select(self.shapes[selected]) if selected not in self.selection:
self.select_object(shape.id)
self.selected = selected self.selected = selected
else: else:
for i in self.find_withtag("selectednodes"): self.clear_selection()
self.delete(i)
self.canvas_management.selected.clear()
def ctrl_click(self, event): def ctrl_click(self, event):
logging.debug("Control left click %s", event) logging.debug("control left click: %s", event)
selected = self.get_selected(event) selected = self.get_selected(event)
if ( if (
self.mode == GraphMode.SELECT self.mode == GraphMode.SELECT
and selected is not None 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): def click_motion(self, event):
""" """
@ -396,21 +478,24 @@ class CanvasGraph(tk.Canvas):
if ( if (
self.mode == GraphMode.SELECT self.mode == GraphMode.SELECT
and self.selected is not None 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) x, y = self.canvas_xy(event)
shape = self.shapes[self.selected] shape = self.shapes[self.selected]
delta_x = x - shape.cursor_x delta_x = x - shape.cursor_x
delta_y = y - shape.cursor_y delta_y = y - shape.cursor_y
shape.motion(event) shape.motion(event)
# move other selected components # move other selected components
for nid in self.canvas_management.selected: for _id in self.selection:
if nid != self.selected and nid in self.shapes: if _id != self.selected and _id in self.shapes:
self.shapes[nid].motion(None, delta_x, delta_y) shape = self.shapes[_id]
if nid != self.selected and nid in self.nodes: shape.motion(None, delta_x, delta_y)
node_x = self.nodes[nid].core_node.position.x if _id != self.selected and _id in self.nodes:
node_y = self.nodes[nid].core_node.position.y node = self.nodes[_id]
self.nodes[nid].move(node_x + delta_x, node_y + delta_y) 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): def click_context(self, event):
logging.info("context event: %s", self.context) logging.info("context event: %s", self.context)
@ -432,17 +517,18 @@ class CanvasGraph(tk.Canvas):
:return: :return:
""" """
logging.debug("press delete key") logging.debug("press delete key")
nodes = self.canvas_management.delete_selected_nodes() nodes = self.delete_selection_objects()
self.core.delete_graph_nodes(nodes) self.core.delete_graph_nodes(nodes)
def double_click(self, event): def double_click(self, event):
selected = self.get_selected(event) selected = self.get_selected(event)
if selected is not None and "shape" in self.gettags(selected): if selected is not None and selected in self.shapes:
s = ShapeDialog(self.app, self.app, self.shapes[selected]) shape = self.shapes[selected]
s.show() dialog = ShapeDialog(self.app, self.app, shape)
dialog.show()
def add_node(self, x, y): 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( core_node = self.core.create_node(
int(x), int(y), self.node_draw.node_type, self.node_draw.model 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.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.canvastooltip import CanvasTooltip
from coretk.graph.enums import GraphMode from coretk.graph.enums import GraphMode
from coretk.graph.graph_helper import WlanAntennaManager from coretk.graph.graph_helper import WlanAntennaManager
from coretk.graph.tooltip import CanvasTooltip
NODE_TEXT_OFFSET = 5 NODE_TEXT_OFFSET = 5
@ -63,7 +63,7 @@ class CanvasNode:
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.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: for edge in self.edges:
x1, y1, x2, y2 = self.canvas.coords(edge.id) x1, y1, x2, y2 = self.canvas.coords(edge.id)
if edge.src == self.id: if edge.src == self.id:
@ -107,8 +107,8 @@ class CanvasNode:
def click_press(self, event): def click_press(self, event):
logging.debug(f"node click press {self.core_node.name}: {event}") logging.debug(f"node click press {self.core_node.name}: {event}")
self.moving = self.canvas.canvas_xy(event) self.moving = self.canvas.canvas_xy(event)
if self.id not in self.canvas.canvas_management.selected: if self.id not in self.canvas.selection:
self.canvas.canvas_management.node_select(self) self.canvas.select_object(self.id)
self.canvas.selected = self.id self.canvas.selected = self.id
def click_release(self, event): def click_release(self, event):
@ -123,19 +123,22 @@ class CanvasNode:
my_x = self.core_node.position.x my_x = self.core_node.position.x
my_y = self.core_node.position.y my_y = self.core_node.position.y
self.move(x, y) self.move(x, y)
# move other selected components # move other selected components
for nid, bboxid in self.canvas.canvas_management.selected.items(): for object_id, selection_id in self.canvas.selection.items():
if nid != self.id and nid in self.canvas.nodes: if object_id != self.id and object_id in self.canvas.nodes:
other_old_x = self.canvas.nodes[nid].core_node.position.x canvas_node = self.canvas.nodes[object_id]
other_old_y = self.canvas.nodes[nid].core_node.position.y 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_x = x + other_old_x - my_x
other_new_y = y + other_old_y - my_y other_new_y = y + other_old_y - my_y
self.canvas.nodes[nid].move(other_new_x, other_new_y) self.canvas.nodes[object_id].move(other_new_x, other_new_y)
if nid != self.id and nid in self.canvas.shapes: elif object_id in self.canvas.shapes:
self.canvas.shapes[nid].motion(None, x - my_x, y - my_y) shape = self.canvas.shapes[object_id]
shape.motion(None, x - my_x, y - my_y)
def select_multiple(self, event): 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): def show_config(self):
self.canvas.context = None 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) logging.debug("Click release on shape %s", self.id)
def motion(self, event, delta_x=None, delta_y=None): def motion(self, event, delta_x=None, delta_y=None):
logging.debug("motion on shape %s", self.id)
if event is not None: if event is not None:
delta_x = event.x - self.cursor_x delta_x = event.x - self.cursor_x
delta_y = event.y - self.cursor_y delta_y = event.y - self.cursor_y
self.cursor_x = event.x self.cursor_x = event.x
self.cursor_y = event.y self.cursor_y = event.y
self.canvas.move(self.id, delta_x, delta_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: if self.text_id is not None:
self.canvas.move(self.text_id, delta_x, delta_y) self.canvas.move(self.text_id, delta_x, delta_y)