From 54eab4576df27dae5632a075edabe9637ec9d2e8 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 20 Apr 2020 23:20:39 -0700 Subject: [PATCH] pygui add in cut functionality, currently not including configurations --- daemon/core/gui/coreclient.py | 33 ++++++++++---------- daemon/core/gui/graph/graph.py | 56 ++++++++++++++++++---------------- daemon/core/gui/graph/node.py | 6 +++- daemon/core/gui/menubar.py | 37 ++++++++++++---------- 4 files changed, 72 insertions(+), 60 deletions(-) diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index dc9bb82b..9b1ab9bc 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -1061,31 +1061,32 @@ class CoreClient: self.canvas_nodes[_to].core_node.services[:] = services logging.debug("copying node %s service to node %s", _from, _to) - def copy_node_config(self, _from: int, _to: int): - node_type = self.canvas_nodes[_from].core_node.type + def copy_node_config(self, src_node: core_pb2.Node, dst_id: int): + node_type = src_node.type if node_type == core_pb2.NodeType.DEFAULT: - services = self.canvas_nodes[_from].core_node.services - self.canvas_nodes[_to].core_node.services[:] = services - config = self.service_configs.get(_from) + services = src_node.services + dst_node = self.canvas_nodes[dst_id] + dst_node.core_node.services[:] = services + config = self.service_configs.get(src_node.id) if config: - self.service_configs[_to] = config - file_configs = self.file_configs.get(_from) + self.service_configs[dst_id] = config + file_configs = self.file_configs.get(src_node.id) if file_configs: for key, value in file_configs.items(): - if _to not in self.file_configs: - self.file_configs[_to] = {} - self.file_configs[_to][key] = value + if dst_id not in self.file_configs: + self.file_configs[dst_id] = {} + self.file_configs[dst_id][key] = value elif node_type == core_pb2.NodeType.WIRELESS_LAN: - config = self.wlan_configs.get(_from) + config = self.wlan_configs.get(src_node.id) if config: - self.wlan_configs[_to] = config - config = self.mobility_configs.get(_from) + self.wlan_configs[dst_id] = config + config = self.mobility_configs.get(src_node.id) if config: - self.mobility_configs[_to] = config + self.mobility_configs[dst_id] = config elif node_type == core_pb2.NodeType.EMANE: - config = self.emane_model_configs.get(_from) + config = self.emane_model_configs.get(src_node.id) if config: - self.emane_model_configs[_to] = config + self.emane_model_configs[dst_id] = config def service_been_modified(self, node_id: int) -> bool: return node_id in self.modified_service_nodes diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index e7908d35..abe045a3 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -894,61 +894,63 @@ class CanvasGraph(tk.Canvas): self.core.create_link(edge, source, dest) def copy(self): - if self.app.core.is_runtime(): + if self.core.is_runtime(): logging.info("copy is disabled during runtime state") return if self.selection: - logging.debug("to copy %s nodes", len(self.selection)) - self.to_copy = self.selection.keys() + logging.info("to copy nodes: %s", self.selection) + self.to_copy.clear() + for node_id in self.selection.keys(): + canvas_node = self.nodes[node_id] + self.to_copy.append(canvas_node) def paste(self): - if self.app.core.is_runtime(): + if self.core.is_runtime(): logging.info("paste is disabled during runtime state") return # maps original node canvas id to copy node canvas id copy_map = {} # the edges that will be copy over to_copy_edges = [] - for canvas_nid in self.to_copy: - core_node = self.nodes[canvas_nid].core_node + for canvas_node in self.to_copy: + core_node = canvas_node.core_node actual_x = core_node.position.x + 50 actual_y = core_node.position.y + 50 scaled_x, scaled_y = self.get_scaled_coords(actual_x, actual_y) - copy = self.core.create_node( actual_x, actual_y, core_node.type, core_node.model ) - node = CanvasNode( - self.master, scaled_x, scaled_y, copy, self.nodes[canvas_nid].image - ) + node = CanvasNode(self.master, scaled_x, scaled_y, copy, canvas_node.image) # add new node to modified_service_nodes set if that set contains the # to_copy node - if self.app.core.service_been_modified(core_node.id): - self.app.core.modified_service_nodes.add(copy.id) + if self.core.service_been_modified(core_node.id): + self.core.modified_service_nodes.add(copy.id) - copy_map[canvas_nid] = node.id + copy_map[canvas_node.id] = node.id self.core.canvas_nodes[copy.id] = node self.nodes[node.id] = node - self.core.copy_node_config(core_node.id, copy.id) - - edges = self.nodes[canvas_nid].edges - for edge in edges: + self.core.copy_node_config(core_node, copy.id) + for edge in canvas_node.edges: if edge.src not in self.to_copy or edge.dst not in self.to_copy: - if canvas_nid == edge.src: - self.create_edge(node, self.nodes[edge.dst]) - elif canvas_nid == edge.dst: - self.create_edge(self.nodes[edge.src], node) + if canvas_node.id == edge.src: + dst_node = self.nodes[edge.dst] + self.create_edge(node, dst_node) + elif canvas_node.id == edge.dst: + src_node = self.nodes[edge.src] + self.create_edge(src_node, node) else: to_copy_edges.append(edge) + # copy link and link config for edge in to_copy_edges: - source_node_copy = self.nodes[copy_map[edge.token[0]]] - dest_node_copy = self.nodes[copy_map[edge.token[1]]] - self.create_edge(source_node_copy, dest_node_copy) - copy_edge = self.edges[ - create_edge_token(source_node_copy.id, dest_node_copy.id) - ] + src_node_id = copy_map[edge.token[0]] + dst_node_id = copy_map[edge.token[1]] + src_node_copy = self.nodes[src_node_id] + dst_node_copy = self.nodes[dst_node_id] + self.create_edge(src_node_copy, dst_node_copy) + token = create_edge_token(src_node_copy.id, dst_node_copy.id) + copy_edge = self.edges[token] copy_link = copy_edge.link options = edge.link.options copy_link.options.CopyFrom(options) diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index c9cc3dd0..9bfc621e 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -225,12 +225,16 @@ class CanvasNode: context.add_command(label="Select Members", state=tk.DISABLED) edit_menu = tk.Menu(context) themes.style_menu(edit_menu) - edit_menu.add_command(label="Cut", state=tk.DISABLED) + edit_menu.add_command(label="Cut", command=self.click_cut) edit_menu.add_command(label="Copy", command=self.canvas_copy) edit_menu.add_command(label="Delete", command=self.canvas_delete) context.add_cascade(label="Edit", menu=edit_menu) return context + def click_cut(self) -> None: + self.canvas_copy() + self.canvas_delete() + def canvas_delete(self) -> None: self.canvas.clear_selection() self.canvas.selection[self.id] = self diff --git a/daemon/core/gui/menubar.py b/daemon/core/gui/menubar.py index 0f016374..5f85b91f 100644 --- a/daemon/core/gui/menubar.py +++ b/daemon/core/gui/menubar.py @@ -103,14 +103,14 @@ class Menubar(tk.Menu): menu.add_command(label="Undo", accelerator="Ctrl+Z", state=tk.DISABLED) menu.add_command(label="Redo", accelerator="Ctrl+Y", state=tk.DISABLED) menu.add_separator() - menu.add_command(label="Cut", accelerator="Ctrl+X", state=tk.DISABLED) + menu.add_command(label="Cut", accelerator="Ctrl+X", command=self.click_cut) menu.add_command(label="Copy", accelerator="Ctrl+C", command=self.click_copy) menu.add_command(label="Paste", accelerator="Ctrl+V", command=self.click_paste) menu.add_command( label="Delete", accelerator="Ctrl+D", command=self.click_delete ) self.add_cascade(label="Edit", menu=menu) - + self.app.master.bind_all("", self.click_cut) self.app.master.bind_all("", self.click_copy) self.app.master.bind_all("", self.click_paste) self.app.master.bind_all("", self.click_delete) @@ -343,16 +343,17 @@ class Menubar(tk.Menu): self.app.menubar.update_recent_files() def change_menubar_item_state(self, is_runtime: bool) -> None: - for i in range(self.edit_menu.index("end")): + labels = {"Copy", "Paste", "Delete", "Cut"} + for i in range(self.edit_menu.index(tk.END) + 1): try: - label_name = self.edit_menu.entrycget(i, "label") - if label_name in ["Copy", "Paste"]: - if is_runtime: - self.edit_menu.entryconfig(i, state="disabled") - else: - self.edit_menu.entryconfig(i, state="normal") + label = self.edit_menu.entrycget(i, "label") + logging.info("menu label: %s", label) + if label not in labels: + continue + state = tk.DISABLED if is_runtime else tk.NORMAL + self.edit_menu.entryconfig(i, state=state) except tk.TclError: - logging.debug("Ignore separators") + pass def prompt_save_running_session(self, quit_app: bool = False) -> None: """ @@ -410,13 +411,17 @@ class Menubar(tk.Menu): dialog.show() def click_copy(self, _event: tk.Event = None) -> None: - self.app.canvas.copy() + self.canvas.copy() def click_paste(self, _event: tk.Event = None) -> None: - self.app.canvas.paste() + self.canvas.paste() def click_delete(self, _event: tk.Event = None) -> None: - self.app.canvas.delete_selected_objects() + self.canvas.delete_selected_objects() + + def click_cut(self, _event: tk.Event = None) -> None: + self.canvas.copy() + self.canvas.delete_selected_objects() def click_session_options(self) -> None: logging.debug("Click options") @@ -444,14 +449,14 @@ class Menubar(tk.Menu): dialog.show() def click_autogrid(self) -> None: - width, height = self.app.canvas.current_dimensions + width, height = self.canvas.current_dimensions padding = (ICON_SIZE / 2) + 10 layout_size = padding + ICON_SIZE col_count = width // layout_size logging.info( - "auto grid layout: dimens(%s, %s) col(%s)", width, height, col_count + "auto grid layout: dimension(%s, %s) col(%s)", width, height, col_count ) - for i, node in enumerate(self.app.canvas.nodes.values()): + for i, node in enumerate(self.canvas.nodes.values()): col = i % col_count row = i // col_count x = (col * layout_size) + padding