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()