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.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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue