From 35cc8fab658abc4cf1f550cbd896be0812a91d00 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 8 Jan 2021 10:10:46 -0800 Subject: [PATCH] pygui: fix redrawing edge labels for multiple canvases, updated edit menu to account for multiple canvases, fixed issue with not setting menubar state when joining a session, starting point for hide/show nodes and related edges --- daemon/core/gui/app.py | 2 ++ daemon/core/gui/graph/edges.py | 4 +-- daemon/core/gui/graph/graph.py | 26 ++++++++++++++++++ daemon/core/gui/graph/manager.py | 12 +++++++-- daemon/core/gui/graph/node.py | 11 ++++++++ daemon/core/gui/graph/tags.py | 1 + daemon/core/gui/menubar.py | 46 ++++++++++++++++++++------------ daemon/core/gui/toolbar.py | 4 +-- 8 files changed, 83 insertions(+), 23 deletions(-) diff --git a/daemon/core/gui/app.py b/daemon/core/gui/app.py index a3629b1e..d404d923 100644 --- a/daemon/core/gui/app.py +++ b/daemon/core/gui/app.py @@ -189,8 +189,10 @@ class Application(ttk.Frame): def joined_session_update(self) -> None: if self.core.is_runtime(): + self.menubar.set_state(is_runtime=True) self.toolbar.set_runtime() else: + self.menubar.set_state(is_runtime=False) self.toolbar.set_design() def get_icon(self, image_enum: ImageEnum, width: int) -> PhotoImage: diff --git a/daemon/core/gui/graph/edges.py b/daemon/core/gui/graph/edges.py index 06bf5127..c448289d 100644 --- a/daemon/core/gui/graph/edges.py +++ b/daemon/core/gui/graph/edges.py @@ -634,10 +634,10 @@ class CanvasEdge(Edge): if not self.linked_wireless: return if self.id: - # self.src.canvas.itemconfig(self.id, state=tk.HIDDEN) + self.src.canvas.itemconfig(self.id, state=tk.HIDDEN) self.src.canvas.dtag(self.id, tags.EDGE) if self.id2: - # self.dst.canvas.itemconfig(self.id2, state=tk.HIDDEN) + self.dst.canvas.itemconfig(self.id2, state=tk.HIDDEN) self.dst.canvas.dtag(self.id2, tags.EDGE) # add antenna to node if self.src.is_wireless() and not self.dst.is_wireless(): diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index 75f4e641..b4ad16df 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -306,6 +306,32 @@ class CanvasGraph(tk.Canvas): self.selection.clear() self.core.deleted_canvas_nodes(nodes) + def hide_selected_objects(self) -> None: + edges = set() + for object_id in self.selection: + # delete selection box + selection_id = self.selection[object_id] + self.delete(selection_id) + + # hide node and related edges + if object_id in self.nodes: + canvas_node = self.nodes[object_id] + canvas_node.hide() + # hide related edges + for edge in canvas_node.edges: + if edge in edges: + continue + edges.add(edge) + + def show_hidden(self) -> None: + edges = set() + for node in self.nodes.values(): + node.show() + for edge in node.edges: + if edge in edges: + continue + edges.add(edge) + def zoom(self, event: tk.Event, factor: float = None) -> None: if not factor: factor = ZOOM_IN if event.delta > 0 else ZOOM_OUT diff --git a/daemon/core/gui/graph/manager.py b/daemon/core/gui/graph/manager.py index dc029d94..e85c01d6 100644 --- a/daemon/core/gui/graph/manager.py +++ b/daemon/core/gui/graph/manager.py @@ -165,8 +165,16 @@ class CanvasManager: unique_id = self.notebook.select() self.notebook.forget(unique_id) canvas_id = self.canvas_ids.pop(unique_id) - self.canvases.pop(canvas_id) - # TODO: handle clearing out canvas related nodes and links from core client + canvas = self.canvases.pop(canvas_id) + edges = set() + for node in canvas.nodes.values(): + node.delete() + while node.edges: + edge = node.edges.pop() + if edge in edges: + continue + edges.add(edge) + edge.delete() def join(self, session: Session) -> None: # clear out all canvas diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index a90d7583..3f6d49dd 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -63,6 +63,7 @@ class CanvasNode: self.wireless_edges: Set[CanvasWirelessEdge] = set() self.antennas: List[int] = [] self.antenna_images: Dict[int, PhotoImage] = {} + self.hidden: bool = False self.setup_bindings() self.context: tk.Menu = tk.Menu(self.canvas) themes.style_menu(self.context) @@ -409,3 +410,13 @@ class CanvasNode: def is_wireless(self) -> bool: return NodeUtils.is_wireless_node(self.core_node.type) + + def hide(self) -> None: + self.hidden = True + self.canvas.itemconfig(self.id, state=tk.HIDDEN) + self.canvas.itemconfig(self.text_id, state=tk.HIDDEN) + + def show(self) -> None: + self.hidden = False + self.canvas.itemconfig(self.id, state=tk.NORMAL) + self.canvas.itemconfig(self.text_id, state=tk.NORMAL) diff --git a/daemon/core/gui/graph/tags.py b/daemon/core/gui/graph/tags.py index 3d3c3611..803b969e 100644 --- a/daemon/core/gui/graph/tags.py +++ b/daemon/core/gui/graph/tags.py @@ -14,6 +14,7 @@ NODE: str = "node" WALLPAPER: str = "wallpaper" SELECTION: str = "selectednodes" MARKER: str = "marker" +HIDDEN: str = "hidden" ORGANIZE_TAGS: List[str] = [ WALLPAPER, GRIDLINE, diff --git a/daemon/core/gui/menubar.py b/daemon/core/gui/menubar.py index e07cf091..323b260e 100644 --- a/daemon/core/gui/menubar.py +++ b/daemon/core/gui/menubar.py @@ -48,6 +48,7 @@ class Menubar(tk.Menu): self.manager: CanvasManager = app.manager self.recent_menu: Optional[tk.Menu] = None self.edit_menu: Optional[tk.Menu] = None + self.canvas_menu: Optional[tk.Menu] = None self.observers_menu: Optional[ObserversMenu] = None self.draw() @@ -106,6 +107,7 @@ class Menubar(tk.Menu): menu = tk.Menu(self) menu.add_command(label="Preferences", command=self.click_preferences) menu.add_command(label="Custom Nodes", command=self.click_custom_nodes) + menu.add_command(label="Show Hidden Nodes", command=self.click_show_hidden) menu.add_separator() menu.add_command(label="Undo", accelerator="Ctrl+Z", state=tk.DISABLED) menu.add_command(label="Redo", accelerator="Ctrl+Y", state=tk.DISABLED) @@ -116,11 +118,13 @@ class Menubar(tk.Menu): menu.add_command( label="Delete", accelerator="Ctrl+D", command=self.click_delete ) + menu.add_command(label="Hide", accelerator="Ctrl+H", command=self.click_hide) 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) + self.app.master.bind_all("", self.click_hide) self.edit_menu = menu def draw_canvas_menu(self) -> None: @@ -134,6 +138,7 @@ class Menubar(tk.Menu): menu.add_command(label="Delete", command=self.click_canvas_delete) menu.add_command(label="Wallpaper", command=self.click_canvas_wallpaper) self.add_cascade(label="Canvas", menu=menu) + self.canvas_menu = menu def draw_view_menu(self) -> None: """ @@ -337,17 +342,12 @@ class Menubar(tk.Menu): self.app.save_config() self.app.menubar.update_recent_files() - def change_menubar_item_state(self, is_runtime: bool) -> None: - labels = {"Copy", "Paste", "Delete", "Cut"} - for i in range(self.edit_menu.index(tk.END) + 1): - try: - label = self.edit_menu.entrycget(i, "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: - pass + def set_state(self, is_runtime: bool) -> None: + state = tk.DISABLED if is_runtime else tk.NORMAL + for entry in {"Copy", "Paste", "Delete", "Cut"}: + self.edit_menu.entryconfigure(entry, state=state) + for entry in {"Delete"}: + self.canvas_menu.entryconfigure(entry, state=state) def prompt_save_running_session(self, quit_app: bool = False) -> None: """ @@ -410,17 +410,29 @@ class Menubar(tk.Menu): dialog.show() def click_copy(self, _event: tk.Event = None) -> None: - self.canvas.copy() + canvas = self.manager.current() + canvas.copy() def click_paste(self, _event: tk.Event = None) -> None: - self.canvas.paste() + canvas = self.manager.current() + canvas.paste() def click_delete(self, _event: tk.Event = None) -> None: - self.canvas.delete_selected_objects() + canvas = self.manager.current() + canvas.delete_selected_objects() + + def click_hide(self, _event: tk.Event = None) -> None: + canvas = self.manager.current() + canvas.hide_selected_objects() def click_cut(self, _event: tk.Event = None) -> None: - self.canvas.copy() - self.canvas.delete_selected_objects() + canvas = self.manager.current() + canvas.copy() + canvas.delete_selected_objects() + + def click_show_hidden(self, _event: tk.Event = None) -> None: + canvas = self.manager.current() + canvas.show_hidden() def click_session_options(self) -> None: logging.debug("Click options") @@ -470,7 +482,7 @@ class Menubar(tk.Menu): self.app.hide_info() def click_edge_label_change(self) -> None: - for edge in self.canvas.edges.values(): + for edge in self.manager.edges.values(): edge.draw_labels() def click_mac_config(self) -> None: diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index e75ebfc7..79a0a2f0 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -292,7 +292,7 @@ class Toolbar(ttk.Frame): Start session handler redraw buttons, send node and link messages to grpc server. """ - self.app.menubar.change_menubar_item_state(is_runtime=True) + self.app.menubar.set_state(is_runtime=True) self.app.manager.mode = GraphMode.SELECT enable_buttons(self.design_frame, enabled=False) task = ProgressTask( @@ -397,7 +397,7 @@ class Toolbar(ttk.Frame): redraw buttons on the toolbar, send node and link messages to grpc server """ logging.info("clicked stop button") - self.app.menubar.change_menubar_item_state(is_runtime=False) + self.app.menubar.set_state(is_runtime=False) self.app.core.close_mobility_players() enable_buttons(self.runtime_frame, enabled=False) task = ProgressTask(