From 1f55432ba2e383e333a5289efc4dac7d25191614 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Tue, 21 Jul 2020 09:16:00 -0700 Subject: [PATCH] pygui: added layer menu to nodes, simple toggle support for nodes alone --- daemon/core/gui/dialogs/layers.py | 48 ++++++++++++++++++++----------- daemon/core/gui/dialogs/simple.py | 1 + daemon/core/gui/graph/layers.py | 46 +++++++++++++++++++++++++---- daemon/core/gui/graph/node.py | 4 +++ 4 files changed, 77 insertions(+), 22 deletions(-) diff --git a/daemon/core/gui/dialogs/layers.py b/daemon/core/gui/dialogs/layers.py index fc72899a..66ebcce0 100644 --- a/daemon/core/gui/dialogs/layers.py +++ b/daemon/core/gui/dialogs/layers.py @@ -15,6 +15,10 @@ class LayersDialog(Dialog): def __init__(self, app: "Application") -> None: super().__init__(app, "Canvas Layers", modal=False) self.list: Optional[ListboxScroll] = None + self.selection: Optional[str] = None + self.selection_index: Optional[int] = None + self.delete_button: Optional[ttk.Button] = None + self.toggle_button: Optional[ttk.Button] = None self.draw() def draw(self) -> None: @@ -23,18 +27,23 @@ class LayersDialog(Dialog): self.list.grid(sticky=tk.EW, pady=PADY) for name in self.app.canvas.layers.names(): self.list.listbox.insert(tk.END, name) + self.list.listbox.bind("<>", self.list_select) frame = ttk.Frame(self.top) frame.grid(sticky=tk.EW) for i in range(3): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Add", command=self.click_add) button.grid(row=0, column=0, sticky=tk.EW, padx=PADX) - button = ttk.Button(frame, text="Delete", command=self.click_delete) - button.grid(row=0, column=1, sticky=tk.EW, padx=PADX) - button = ttk.Button(frame, text="Toggle", command=self.click_toggle) - button.grid(row=0, column=2, sticky=tk.EW) + self.delete_button = ttk.Button( + frame, text="Delete", command=self.click_delete, state=tk.DISABLED + ) + self.delete_button.grid(row=0, column=1, sticky=tk.EW, padx=PADX) + self.toggle_button = ttk.Button( + frame, text="Toggle", command=self.click_toggle, state=tk.DISABLED + ) + self.toggle_button.grid(row=0, column=2, sticky=tk.EW) - def click_add(self): + def click_add(self) -> None: name = SimpleStringDialog(self, self.app, "Add Layer", "Layer Name").ask() if name: result = self.app.canvas.layers.add_layer(name) @@ -45,16 +54,21 @@ class LayersDialog(Dialog): "Add Layer", f"Duplicate Layer: {name}", parent=self ) - def click_delete(self): - selection = self.list.listbox.curselection() - if not selection: - return - name = self.list.listbox.get(selection) - print(name) + def list_select(self, event: tk.Event) -> None: + self.selection_index = self.list.listbox.curselection() + if not self.selection_index: + self.selection = None + state = tk.DISABLED + else: + self.selection = self.list.listbox.get(self.selection_index) + state = tk.NORMAL + self.toggle_button.config(state=state) + self.delete_button.config(state=state) - def click_toggle(self): - selection = self.list.listbox.curselection() - if not selection: - return - name = self.list.listbox.get(selection) - print(name) + def click_delete(self) -> None: + self.app.canvas.layers.delete_layer(self.selection) + self.list.listbox.delete(self.selection_index) + self.list.listbox.event_generate("<>") + + def click_toggle(self) -> None: + self.app.canvas.layers.toggle_layer(self.selection) diff --git a/daemon/core/gui/dialogs/simple.py b/daemon/core/gui/dialogs/simple.py index 0068f398..2d893e64 100644 --- a/daemon/core/gui/dialogs/simple.py +++ b/daemon/core/gui/dialogs/simple.py @@ -14,6 +14,7 @@ class SimpleStringDialog(Dialog): self, master: tk.BaseWidget, app: "Application", title: str, prompt: str ): super().__init__(app, title, master=master) + self.bind("", lambda e: self.destroy()) self.prompt: str = prompt self.value = tk.StringVar() self.entry: Optional[ttk.Entry] = None diff --git a/daemon/core/gui/graph/layers.py b/daemon/core/gui/graph/layers.py index ba8031b0..b57899fe 100644 --- a/daemon/core/gui/graph/layers.py +++ b/daemon/core/gui/graph/layers.py @@ -1,10 +1,40 @@ import tkinter as tk +from functools import partial from typing import TYPE_CHECKING, Dict, Iterable, Set +from core.gui import themes + if TYPE_CHECKING: + from core.gui.app import Application from core.gui.graph.graph import CanvasGraph +class LayersMenu(tk.Menu): + def __init__(self, master: tk.BaseWidget, app: "Application", item: int) -> None: + super().__init__(master) + themes.style_menu(self) + self.app: "Application" = app + self.item: int = item + self.buttons: Dict[str, tk.BooleanVar] = {} + self.draw() + + def draw(self) -> None: + for name in self.app.canvas.layers.names(): + value = self.app.canvas.layers.in_layer(name, self.item) + var = tk.BooleanVar(value=value) + self.buttons[name] = var + self.add_checkbutton( + label=name, variable=var, command=partial(self.click_layer, name) + ) + + def click_layer(self, name): + value = self.buttons[name].get() + if value: + self.app.canvas.layers.add_item(name, self.item) + else: + self.app.canvas.layers.delete_item(name, self.item) + + class CanvasLayers: def __init__(self, canvas: "CanvasGraph"): self.canvas: "CanvasGraph" = canvas @@ -25,20 +55,24 @@ class CanvasLayers: items = self.layers.pop(name, set()) hidden_items = self.all_hidden() items -= hidden_items - self.canvas.config(items, state=tk.NORMAL) + for item in items: + self.canvas.itemconfig(item, state=tk.NORMAL) + + def in_layer(self, name: str, item: int) -> bool: + return item in self.layers.get(name, set()) def add_item(self, name: str, item: int) -> None: if name in self.layers: self.layers[name].add(item) if name in self.hidden: - self.canvas.config(item, state=tk.HIDDEN) + self.canvas.itemconfig(item, state=tk.HIDDEN) def delete_item(self, name: str, item: int) -> None: if name in self.layers: self.layers[name].remove(item) hidden_items = self.all_hidden() if item not in hidden_items: - self.canvas.config(item, state=tk.NORMAL) + self.canvas.itemconfig(item, state=tk.NORMAL) def toggle_layer(self, name: str) -> None: items = self.layers[name] @@ -46,10 +80,12 @@ class CanvasLayers: self.hidden.remove(name) hidden_items = self.all_hidden() items -= hidden_items - self.canvas.config(items, state=tk.NORMAL) + for item in items: + self.canvas.itemconfig(item, state=tk.NORMAL) else: self.hidden.add(name) - self.canvas.config(items, state=tk.HIDDEN) + for item in items: + self.canvas.itemconfig(item, state=tk.HIDDEN) def all_hidden(self) -> Set[int]: items = set() diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index 7b5cd2f3..189e9263 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -19,6 +19,7 @@ from core.gui.dialogs.wlanconfig import WlanConfigDialog from core.gui.frames.node import NodeInfoFrame from core.gui.graph import tags from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge +from core.gui.graph.layers import LayersMenu from core.gui.graph.tooltip import CanvasTooltip from core.gui.images import ImageEnum from core.gui.nodeutils import ANTENNA_SIZE, NodeUtils @@ -271,6 +272,9 @@ class CanvasNode: edit_menu.add_command(label="Copy", command=self.canvas_copy) edit_menu.add_command(label="Delete", command=self.canvas_delete) self.context.add_cascade(label="Edit", menu=edit_menu) + + layer_menu = LayersMenu(self.context, self.app, self.id) + self.context.add_cascade(label="Layer", menu=layer_menu) self.context.tk_popup(event.x_root, event.y_root) def click_cut(self) -> None: