From d4f77a01e30c559f765a29fc98648a85701ef8f0 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Sat, 2 Nov 2019 23:47:43 -0700 Subject: [PATCH] moved all modules creating dialogs under dialogs package, updated node config and node icon dialogs to use common dialog class, fixed common dialog class show order to fix delayed cases with grab_set --- coretk/coretk/canvasaction.py | 8 +- coretk/coretk/dialogs/dialog.py | 5 +- coretk/coretk/dialogs/nodeconfig.py | 94 ++++++++++ coretk/coretk/dialogs/nodeicon.py | 69 ++++++++ coretk/coretk/{ => dialogs}/nodeservice.py | 0 coretk/coretk/{ => dialogs}/setwallpaper.py | 0 coretk/coretk/{ => dialogs}/sizeandscale.py | 2 +- .../wlanconfig.py} | 13 +- coretk/coretk/imagemodification.py | 91 ---------- coretk/coretk/images.py | 8 +- coretk/coretk/menuaction.py | 4 +- coretk/coretk/nodeconfigtable.py | 164 ------------------ 12 files changed, 184 insertions(+), 274 deletions(-) create mode 100644 coretk/coretk/dialogs/nodeconfig.py create mode 100644 coretk/coretk/dialogs/nodeicon.py rename coretk/coretk/{ => dialogs}/nodeservice.py (100%) rename coretk/coretk/{ => dialogs}/setwallpaper.py (100%) rename coretk/coretk/{ => dialogs}/sizeandscale.py (99%) rename coretk/coretk/{wlanconfiguration.py => dialogs/wlanconfig.py} (97%) delete mode 100644 coretk/coretk/imagemodification.py delete mode 100644 coretk/coretk/nodeconfigtable.py diff --git a/coretk/coretk/canvasaction.py b/coretk/coretk/canvasaction.py index 0c56d4ba..b5f09333 100644 --- a/coretk/coretk/canvasaction.py +++ b/coretk/coretk/canvasaction.py @@ -5,8 +5,8 @@ canvas graph action # import tkinter as tk from core.api.grpc import core_pb2 -from coretk.nodeconfigtable import NodeConfig -from coretk.wlanconfiguration import WlanConfiguration +from coretk.dialogs.nodeconfig import NodeConfigDialog +from coretk.dialogs.wlanconfig import WlanConfiguration # TODO, finish classifying node types NODE_TO_TYPE = { @@ -18,7 +18,6 @@ NODE_TO_TYPE = { class CanvasAction: def __init__(self, master, canvas): self.master = master - self.canvas = canvas self.node_to_show_config = None @@ -31,7 +30,8 @@ class CanvasAction: self.display_wlan_configuration(canvas_node) def display_node_configuration(self): - NodeConfig(self.canvas, self.node_to_show_config) + dialog = NodeConfigDialog(self.master, self.master, self.node_to_show_config) + dialog.show() self.node_to_show_config = None def display_wlan_configuration(self, canvas_node): diff --git a/coretk/coretk/dialogs/dialog.py b/coretk/coretk/dialogs/dialog.py index b218d8c7..f9cfcabe 100644 --- a/coretk/coretk/dialogs/dialog.py +++ b/coretk/coretk/dialogs/dialog.py @@ -17,8 +17,9 @@ class Dialog(tk.Toplevel): def show(self): self.transient(self.master) self.focus_force() - if self.modal: - self.grab_set() self.update() self.deiconify() + if self.modal: + self.wait_visibility() + self.grab_set() self.wait_window() diff --git a/coretk/coretk/dialogs/nodeconfig.py b/coretk/coretk/dialogs/nodeconfig.py new file mode 100644 index 00000000..d670853c --- /dev/null +++ b/coretk/coretk/dialogs/nodeconfig.py @@ -0,0 +1,94 @@ +import tkinter as tk +from tkinter import ttk + +from coretk.dialogs.dialog import Dialog +from coretk.dialogs.nodeicon import NodeIconDialog +from coretk.dialogs.nodeservice import NodeServices + +NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"] +DEFAULTNODES = ["router", "host", "PC"] + + +class NodeConfigDialog(Dialog): + def __init__(self, master, app, canvas_node): + """ + create an instance of node configuration + + :param master: dialog master + :param coretk.app.Application: main app + :param coretk.graph.CanvasNode canvas_node: canvas node object + """ + super().__init__(master, app, f"{canvas_node.name} Configuration", modal=True) + self.canvas_node = canvas_node + self.image = canvas_node.image + self.image_button = None + self.name = tk.StringVar(value=canvas_node.name) + self.type = tk.StringVar(value=canvas_node.node_type) + self.server = tk.StringVar() + self.draw() + + def draw(self): + self.columnconfigure(0, weight=1) + self.draw_first_row() + self.draw_second_row() + self.draw_third_row() + + def draw_first_row(self): + frame = tk.Frame(self) + frame.grid(row=0, column=0, pady=2, sticky="ew") + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + frame.columnconfigure(2, weight=1) + + entry = tk.Entry(frame, textvariable=self.name) + entry.grid(row=0, column=0, padx=2, sticky="ew") + + combobox = ttk.Combobox(frame, textvariable=self.type, values=DEFAULTNODES) + combobox.grid(row=0, column=1, padx=2, sticky="ew") + + combobox = ttk.Combobox(frame, textvariable=self.server, values=["localhost"]) + combobox.current(0) + combobox.grid(row=0, column=2, sticky="ew") + + def draw_second_row(self): + frame = tk.Frame(self) + frame.grid(row=1, column=0, pady=2, sticky="ew") + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + + button = tk.Button(frame, text="Services", command=lambda: NodeServices()) + button.grid(row=0, column=0, padx=2, sticky="ew") + + self.image_button = tk.Button( + frame, + text="Icon", + image=self.image, + compound=tk.LEFT, + command=self.click_icon, + ) + self.image_button.grid(row=0, column=1, sticky="ew") + + def draw_third_row(self): + frame = tk.Frame(self) + frame.grid(row=2, column=0, sticky="ew") + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + + button = tk.Button(frame, text="Apply", command=self.config_apply) + button.grid(row=0, column=0, padx=2, sticky="ew") + + button = tk.Button(frame, text="Cancel", command=self.destroy) + button.grid(row=0, column=1, sticky="ew") + + def click_icon(self): + dialog = NodeIconDialog(self, self.app, self.canvas_node) + dialog.show() + if dialog.image: + self.image = dialog.image + self.image_button.config(image=self.image) + + def config_apply(self): + self.canvas_node.name = self.name.get() + self.canvas_node.image = self.image + self.canvas_node.canvas.itemconfig(self.canvas_node.id, image=self.image) + self.destroy() diff --git a/coretk/coretk/dialogs/nodeicon.py b/coretk/coretk/dialogs/nodeicon.py new file mode 100644 index 00000000..d82c0756 --- /dev/null +++ b/coretk/coretk/dialogs/nodeicon.py @@ -0,0 +1,69 @@ +import tkinter as tk +from tkinter import filedialog + +from coretk.appdirs import ICONS_PATH +from coretk.dialogs.dialog import Dialog +from coretk.images import Images + + +class NodeIconDialog(Dialog): + def __init__(self, master, app, canvas_node): + """ + create an instance of ImageModification + :param master: dialog master + :param coretk.app.Application: main app + :param coretk.graph.CanvasNode canvas_node: node object + """ + super().__init__(master, app, f"{canvas_node.name} Icon", modal=True) + self.file_path = tk.StringVar() + self.image_label = None + self.image = canvas_node.image + self.draw() + + def draw(self): + self.columnconfigure(0, weight=1) + + # row one + frame = tk.Frame(self) + frame.grid(row=0, column=0, pady=2, sticky="ew") + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=3) + label = tk.Label(frame, text="Image") + label.grid(row=0, column=0, sticky="ew") + entry = tk.Entry(frame, textvariable=self.file_path) + entry.grid(row=0, column=1, sticky="ew") + button = tk.Button(frame, text="...", command=self.click_file) + button.grid(row=0, column=2) + + # row two + self.image_label = tk.Label(self, image=self.image) + self.image_label.grid(row=1, column=0, pady=2, sticky="ew") + + # row three + frame = tk.Frame(self) + frame.grid(row=2, column=0, sticky="ew") + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + button = tk.Button(frame, text="Apply", command=self.destroy) + button.grid(row=0, column=0, sticky="ew") + + button = tk.Button(frame, text="Cancel", command=self.click_cancel) + button.grid(row=0, column=1, sticky="ew") + + def click_file(self): + file_path = filedialog.askopenfilename( + initialdir=str(ICONS_PATH), + title="Open", + filetypes=( + ("images", "*.gif *.jpg *.png *.bmp *pcx *.tga ..."), + ("All Files", "*"), + ), + ) + if file_path: + self.image = Images.create(file_path) + self.image_label.config(image=self.image) + self.file_path.set(file_path) + + def click_cancel(self): + self.image = None + self.destroy() diff --git a/coretk/coretk/nodeservice.py b/coretk/coretk/dialogs/nodeservice.py similarity index 100% rename from coretk/coretk/nodeservice.py rename to coretk/coretk/dialogs/nodeservice.py diff --git a/coretk/coretk/setwallpaper.py b/coretk/coretk/dialogs/setwallpaper.py similarity index 100% rename from coretk/coretk/setwallpaper.py rename to coretk/coretk/dialogs/setwallpaper.py diff --git a/coretk/coretk/sizeandscale.py b/coretk/coretk/dialogs/sizeandscale.py similarity index 99% rename from coretk/coretk/sizeandscale.py rename to coretk/coretk/dialogs/sizeandscale.py index 5c746c39..0267a300 100644 --- a/coretk/coretk/sizeandscale.py +++ b/coretk/coretk/dialogs/sizeandscale.py @@ -4,7 +4,7 @@ size and scale import tkinter as tk from functools import partial -from coretk.setwallpaper import ScaleOption +from coretk.dialogs.setwallpaper import ScaleOption DRAW_OBJECT_TAGS = ["edge", "node", "nodename", "linkinfo", "antenna"] diff --git a/coretk/coretk/wlanconfiguration.py b/coretk/coretk/dialogs/wlanconfig.py similarity index 97% rename from coretk/coretk/wlanconfiguration.py rename to coretk/coretk/dialogs/wlanconfig.py index 32bb56e2..8065a5fa 100644 --- a/coretk/coretk/wlanconfiguration.py +++ b/coretk/coretk/dialogs/wlanconfig.py @@ -5,7 +5,7 @@ wlan configuration import tkinter as tk from functools import partial -from coretk.imagemodification import ImageModification +from coretk.dialogs.nodeicon import NodeIconDialog class WlanConfiguration: @@ -57,16 +57,13 @@ class WlanConfiguration: e.grid(row=0, column=1, padx=3, pady=3) b = tk.Button(f, text="None") b.grid(row=0, column=2, padx=3, pady=3) - b = tk.Button( - f, - image=self.image, - command=lambda: ImageModification( - canvas=self.canvas, canvas_node=self.canvas_node, node_config=self - ), - ) + b = tk.Button(f, image=self.image, command=lambda: self.click_image) b.grid(row=0, column=3, padx=3, pady=3) f.grid(padx=2, pady=2, ipadx=2, ipady=2) + def click_image(self): + NodeIconDialog(self.app, canvas_node=self.canvas_node, node_config=self) + def create_string_var(self, val): """ create string variable for convenience diff --git a/coretk/coretk/imagemodification.py b/coretk/coretk/imagemodification.py deleted file mode 100644 index 646f31b7..00000000 --- a/coretk/coretk/imagemodification.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -node image modification -""" - - -import os -import tkinter as tk -from tkinter import filedialog - -from PIL import Image, ImageTk - -PATH = os.path.abspath(os.path.dirname(__file__)) -ICONS_DIR = os.path.join(PATH, "icons") - - -class ImageModification: - def __init__(self, canvas, canvas_node, node_config): - """ - create an instance of ImageModification - :param coretk.graph.CanvasGraph canvas: canvas object - :param coretk.graph.CanvasNode canvas_node: node object - :param coretk.nodeconfigtable.NodeConfig node_config: node configuration object - """ - self.canvas = canvas - self.image = canvas_node.image - self.node_type = canvas_node.node_type - self.name = canvas_node.name - self.canvas_node = canvas_node - self.node_configuration = node_config - self.p_top = node_config.top - - self.top = tk.Toplevel() - self.top.title(self.name + " image") - self.image_modification() - - def open_icon_dir(self, toplevel, entry_text): - filename = filedialog.askopenfilename( - initialdir=ICONS_DIR, - title="Open", - filetypes=( - ("images", "*.gif *.jpg *.png *.bmp *pcx *.tga ..."), - ("All Files", "*"), - ), - ) - if len(filename) > 0: - img = Image.open(filename) - tk_img = ImageTk.PhotoImage(img) - lb = toplevel.grid_slaves(1, 0)[0] - lb.configure(image=tk_img) - lb.image = tk_img - entry_text.set(filename) - - def click_apply(self, toplevel, entry_text): - imgfile = entry_text.get() - if imgfile: - img = Image.open(imgfile) - tk_img = ImageTk.PhotoImage(img) - f = self.p_top.grid_slaves(row=0, column=0)[0] - lb = f.grid_slaves(row=0, column=3)[0] - lb.configure(image=tk_img) - lb.image = tk_img - self.image = tk_img - self.node_configuration.image = tk_img - toplevel.destroy() - - def image_modification(self): - f = tk.Frame(self.top) - entry_text = tk.StringVar() - image_file_label = tk.Label(f, text="Image file: ") - image_file_label.grid(row=0, column=0) - image_file_entry = tk.Entry(f, textvariable=entry_text, width=32, bg="white") - image_file_entry.grid(row=0, column=1) - image_file_button = tk.Button( - f, text="...", command=lambda: self.open_icon_dir(self.top, entry_text) - ) - image_file_button.grid(row=0, column=2) - f.grid() - - img = tk.Label(self.top, image=self.image) - img.grid() - - f = tk.Frame(self.top) - apply_button = tk.Button( - f, text="Apply", command=lambda: self.click_apply(self.top, entry_text) - ) - apply_button.grid(row=0, column=0) - apply_to_multiple_button = tk.Button(f, text="Apply to multiple...") - apply_to_multiple_button.grid(row=0, column=1) - cancel_button = tk.Button(f, text="Cancel", command=self.top.destroy) - cancel_button.grid(row=0, column=2) - f.grid() diff --git a/coretk/coretk/images.py b/coretk/coretk/images.py index f9b5f870..768b33ba 100644 --- a/coretk/coretk/images.py +++ b/coretk/coretk/images.py @@ -10,6 +10,11 @@ from coretk.appdirs import LOCAL_ICONS_PATH class Images: images = {} + @classmethod + def create(cls, file_path): + image = Image.open(file_path) + return ImageTk.PhotoImage(image) + @classmethod def load_all(cls): for image in LOCAL_ICONS_PATH.glob("*"): @@ -17,8 +22,7 @@ class Images: @classmethod def load(cls, name, file_path): - image = Image.open(file_path) - tk_image = ImageTk.PhotoImage(image) + tk_image = cls.create(file_path) cls.images[name] = tk_image @classmethod diff --git a/coretk/coretk/menuaction.py b/coretk/coretk/menuaction.py index 64caf8ae..1d9adca1 100644 --- a/coretk/coretk/menuaction.py +++ b/coretk/coretk/menuaction.py @@ -11,8 +11,8 @@ from coretk.appdirs import XML_PATH from coretk.dialogs.hooks import HooksDialog from coretk.dialogs.sessionoptions import SessionOptionsDialog from coretk.dialogs.sessions import SessionsDialog -from coretk.setwallpaper import CanvasWallpaper -from coretk.sizeandscale import SizeAndScale +from coretk.dialogs.setwallpaper import CanvasWallpaper +from coretk.dialogs.sizeandscale import SizeAndScale def sub_menu_items(): diff --git a/coretk/coretk/nodeconfigtable.py b/coretk/coretk/nodeconfigtable.py deleted file mode 100644 index a1a7c1d2..00000000 --- a/coretk/coretk/nodeconfigtable.py +++ /dev/null @@ -1,164 +0,0 @@ -""" -Create toplevel for node configuration -""" -import logging -import os -import tkinter as tk -from tkinter import filedialog - -from PIL import Image, ImageTk - -from coretk.imagemodification import ImageModification -from coretk.nodeservice import NodeServices - -PATH = os.path.abspath(os.path.dirname(__file__)) -ICONS_DIR = os.path.join(PATH, "icons") - -NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"] -DEFAULTNODES = ["router", "host", "PC"] - - -class NodeConfig: - def __init__(self, canvas, canvas_node): - """ - create an instance of node configuration - - :param coretk.graph.CanvasGraph canvas: canvas object - :param coretk.graph.CanvasNode canvas_node: canvas node object - """ - self.canvas = canvas - self.image = canvas_node.image - self.node_type = canvas_node.node_type - self.name = canvas_node.name - self.canvas_node = canvas_node - - self.top = tk.Toplevel() - self.top.title(canvas_node.node_type + " configuration") - self.namevar = tk.StringVar(self.top, value="default name") - self.name_and_image_definition() - self.type_and_service_definition() - self.select_definition() - - def open_icon_dir(self, toplevel, entry_text): - filename = filedialog.askopenfilename( - initialdir=ICONS_DIR, - title="Open", - filetypes=( - ("images", "*.gif *.jpg *.png *.bmp *pcx *.tga ..."), - ("All Files", "*"), - ), - ) - if len(filename) > 0: - img = Image.open(filename) - tk_img = ImageTk.PhotoImage(img) - lb = toplevel.grid_slaves(1, 0)[0] - lb.configure(image=tk_img) - lb.image = tk_img - entry_text.set(filename) - - def click_apply(self, toplevel, entry_text): - imgfile = entry_text.get() - if imgfile: - img = Image.open(imgfile) - tk_img = ImageTk.PhotoImage(img) - lb = self.top.grid_slaves(row=0, column=3)[0] - lb.configure(image=tk_img) - lb.image = tk_img - self.image = tk_img - toplevel.destroy() - - def img_modification(self): - t = tk.Toplevel() - t.title(self.name + " image") - - f = tk.Frame(t) - entry_text = tk.StringVar() - image_file_label = tk.Label(f, text="Image file: ") - image_file_label.grid(row=0, column=0) - image_file_entry = tk.Entry(f, textvariable=entry_text, width=32, bg="white") - image_file_entry.grid(row=0, column=1) - image_file_button = tk.Button( - f, text="...", command=lambda: self.open_icon_dir(t, entry_text) - ) - image_file_button.grid(row=0, column=2) - f.grid() - - img = tk.Label(t, image=self.image) - img.grid() - - f = tk.Frame(t) - apply_button = tk.Button( - f, text="Apply", command=lambda: self.click_apply(t, entry_text) - ) - apply_button.grid(row=0, column=0) - apply_to_multiple_button = tk.Button(f, text="Apply to multiple...") - apply_to_multiple_button.grid(row=0, column=1) - cancel_button = tk.Button(f, text="Cancel", command=t.destroy) - cancel_button.grid(row=0, column=2) - f.grid() - - def name_and_image_definition(self): - f = tk.Frame(self.top, bg="#d9d9d9") - name_label = tk.Label(f, text="Node name: ", bg="#d9d9d9") - name_label.grid(padx=2, pady=2) - name_entry = tk.Entry(f, textvariable=self.namevar) - name_entry.grid(row=0, column=1, padx=2, pady=2) - - core_button = tk.Button(f, text="None") - core_button.grid(row=0, column=2, padx=2, pady=2) - img_button = tk.Button( - f, - image=self.image, - width=40, - height=40, - command=lambda: ImageModification(self.canvas, self.canvas_node, self), - bg="#d9d9d9", - ) - img_button.grid(row=0, column=3, padx=4, pady=4) - f.grid(padx=4, pady=4) - - def type_and_service_definition(self): - f = tk.Frame(self.top) - type_label = tk.Label(f, text="Type: ") - type_label.grid(row=0, column=0) - - type_button = tk.Button(f, text="None") - type_button.grid(row=0, column=1) - - service_button = tk.Button( - f, text="Services...", command=lambda: NodeServices() - ) - service_button.grid(row=0, column=2) - - f.grid(padx=2, pady=2) - - def config_apply(self): - """ - modify image of the canvas node - :return: nothing - """ - logging.debug("nodeconfigtable.py configuration apply") - self.canvas_node.image = self.image - self.canvas_node.canvas.itemconfig(self.canvas_node.id, image=self.image) - self.top.destroy() - - def config_cancel(self): - """ - save chosen image but not modify canvas node - :return: nothing - """ - logging.debug("nodeconfigtable.py configuration cancel") - self.canvas_node.image = self.image - self.top.destroy() - - def select_definition(self): - f = tk.Frame(self.top) - apply_button = tk.Button(f, text="Apply", command=self.config_apply) - apply_button.grid(row=0, column=0) - cancel_button = tk.Button(f, text="Cancel", command=self.config_cancel) - cancel_button.grid(row=0, column=1) - f.grid() - - def network_node_config(self): - self.name_and_image_definition() - self.select_definition()