removed nodedelete module and added logic to the graph canvas itself
This commit is contained in:
parent
d970d5ee85
commit
7c8f9dac0f
5 changed files with 129 additions and 139 deletions
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue