From 2873c32c23148c03c3cda2714f616a6be7c13e2a Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 7 Nov 2019 11:33:40 -0800 Subject: [PATCH] custom nodes dialog works for creating, editing, and saving to config in basic case --- coretk/coretk/appdirs.py | 5 +- coretk/coretk/coreclient.py | 31 ++++++++--- coretk/coretk/dialogs/customnodes.py | 55 +++++++++++++++---- .../dialogs/{nodeicon.py => icondialog.py} | 0 coretk/coretk/dialogs/nodeconfig.py | 2 +- coretk/coretk/dialogs/wlanconfig.py | 2 +- coretk/coretk/images.py | 4 ++ 7 files changed, 78 insertions(+), 21 deletions(-) rename coretk/coretk/dialogs/{nodeicon.py => icondialog.py} (100%) diff --git a/coretk/coretk/appdirs.py b/coretk/coretk/appdirs.py index 51a78f76..553b0949 100644 --- a/coretk/coretk/appdirs.py +++ b/coretk/coretk/appdirs.py @@ -42,7 +42,10 @@ def check_directory(): for background in LOCAL_BACKGROUND_PATH.glob("*"): new_background = BACKGROUNDS_PATH.joinpath(background.name) shutil.copy(background, new_background) - config = {"servers": [{"name": "example", "address": "127.0.0.1", "port": 50051}]} + config = { + "servers": [{"name": "example", "address": "127.0.0.1", "port": 50051}], + "nodes": [], + } save_config(config) diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index b5114462..f4135269 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -8,6 +8,7 @@ from collections import OrderedDict from core.api.grpc import client, core_pb2 from coretk.coretocanvas import CoreToCanvasMapping from coretk.dialogs.sessions import SessionsDialog +from coretk.images import Images from coretk.interface import Interface, InterfaceManager from coretk.mobilitynodeconfig import MobilityNodeConfig from coretk.wlannodeconfig import WlanNodeConfig @@ -65,9 +66,10 @@ class CoreServer: class CustomNode: - def __init__(self, name, image, services): + def __init__(self, name, image, image_file, services): self.name = name self.image = image + self.image_file = image_file self.services = services @@ -83,15 +85,11 @@ class CoreClient: self.master = app.master self.interface_helper = None self.services = {} - self.custom_nodes = {} - # distributed server data + # loaded configuration data self.servers = {} - for server_config in self.app.config["servers"]: - server = CoreServer( - server_config["name"], server_config["address"], server_config["port"] - ) - self.servers[server.name] = server + self.custom_nodes = {} + self.read_config() # data for managing the current session self.nodes = {} @@ -106,6 +104,23 @@ class CoreClient: self.mobilityconfig_management = MobilityNodeConfig() self.emane_config = None + def read_config(self): + # read distributed server + for server_config in self.app.config["servers"]: + server = CoreServer( + server_config["name"], server_config["address"], server_config["port"] + ) + self.servers[server.name] = server + + # read custom nodes + for node in self.app.config["nodes"]: + image_file = node["image"] + image = Images.get_custom(image_file) + custom_node = CustomNode( + node["name"], image, image_file, set(node["services"]) + ) + self.custom_nodes[custom_node.name] = custom_node + def handle_events(self, event): logging.info("event: %s", event) if event.link_event is not None: diff --git a/coretk/coretk/dialogs/customnodes.py b/coretk/coretk/dialogs/customnodes.py index 04931034..f2423bf0 100644 --- a/coretk/coretk/dialogs/customnodes.py +++ b/coretk/coretk/dialogs/customnodes.py @@ -1,8 +1,11 @@ +import logging import tkinter as tk +from pathlib import Path +from coretk import appdirs from coretk.coreclient import CustomNode from coretk.dialogs.dialog import Dialog -from coretk.dialogs.nodeicon import IconDialog +from coretk.dialogs.icondialog import IconDialog from coretk.widgets import CheckboxList, ListboxScroll @@ -86,6 +89,7 @@ class CustomNodesDialog(Dialog): self.name = tk.StringVar() self.image_button = None self.image = None + self.image_file = None self.services = set() self.selected = None self.selected_index = None @@ -145,17 +149,25 @@ class CustomNodesDialog(Dialog): for i in range(2): frame.columnconfigure(i, weight=1) - button = tk.Button(frame, text="Save", command=self.click_edit) + button = tk.Button(frame, text="Save", command=self.click_save) button.grid(row=0, column=0, sticky="ew") button = tk.Button(frame, text="Cancel", command=self.destroy) button.grid(row=0, column=1, sticky="ew") + def reset_values(self): + self.name.set("") + self.image = None + self.image_file = None + self.services = set() + self.image_button.config(image="") + def click_icon(self): dialog = IconDialog(self, self.app, self.name.get(), self.image) dialog.show() if dialog.image: self.image = dialog.image + self.image_file = dialog.file_path.get() self.image_button.config(image=self.image) def click_services(self): @@ -164,22 +176,44 @@ class CustomNodesDialog(Dialog): if dialog.current_services is not None: self.services = dialog.current_services + def click_save(self): + self.app.config["nodes"].clear() + for name in sorted(self.app.core.custom_nodes): + custom_node = self.app.core.custom_nodes[name] + self.app.config["nodes"].append( + { + "name": custom_node.name, + "image": custom_node.image_file, + "services": list(custom_node.services), + } + ) + logging.info("saving custom nodes: %s", self.app.config["nodes"]) + appdirs.save_config(self.app.config) + def click_create(self): name = self.name.get() if name not in self.app.core.custom_nodes: - custom_node = CustomNode(name, self.image, self.services) + custom_node = CustomNode( + name, self.image, Path(self.image_file).name, set(self.services) + ) self.app.core.custom_nodes[name] = custom_node self.nodes_list.listbox.insert(tk.END, name) self.reset_values() - def reset_values(self): - self.name.set("") - self.image = None - self.services = set() - self.image_button.config(image="") - def click_edit(self): - pass + name = self.name.get() + if self.selected: + previous_name = self.selected + self.selected = name + custom_node = self.app.core.custom_nodes.pop(previous_name) + custom_node.name = name + custom_node.image = self.image + custom_node.image_file = Path(self.image_file).name + custom_node.services = self.services + self.app.core.custom_nodes[name] = custom_node + self.nodes_list.listbox.delete(self.selected_index) + self.nodes_list.listbox.insert(self.selected_index, name) + self.nodes_list.listbox.selection_set(self.selected_index) def click_delete(self): if self.selected and self.selected in self.app.core.custom_nodes: @@ -198,6 +232,7 @@ class CustomNodesDialog(Dialog): self.name.set(custom_node.name) self.services = custom_node.services self.image = custom_node.image + self.image_file = custom_node.image_file self.image_button.config(image=self.image) self.edit_button.config(state=tk.NORMAL) self.delete_button.config(state=tk.NORMAL) diff --git a/coretk/coretk/dialogs/nodeicon.py b/coretk/coretk/dialogs/icondialog.py similarity index 100% rename from coretk/coretk/dialogs/nodeicon.py rename to coretk/coretk/dialogs/icondialog.py diff --git a/coretk/coretk/dialogs/nodeconfig.py b/coretk/coretk/dialogs/nodeconfig.py index bc03dc51..3f13488a 100644 --- a/coretk/coretk/dialogs/nodeconfig.py +++ b/coretk/coretk/dialogs/nodeconfig.py @@ -2,7 +2,7 @@ import tkinter as tk from tkinter import ttk from coretk.dialogs.dialog import Dialog -from coretk.dialogs.nodeicon import IconDialog +from coretk.dialogs.icondialog import IconDialog from coretk.dialogs.nodeservice import NodeServicesDialog NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"] diff --git a/coretk/coretk/dialogs/wlanconfig.py b/coretk/coretk/dialogs/wlanconfig.py index d57c8935..dc40e6c7 100644 --- a/coretk/coretk/dialogs/wlanconfig.py +++ b/coretk/coretk/dialogs/wlanconfig.py @@ -5,7 +5,7 @@ wlan configuration import tkinter as tk from coretk.dialogs.dialog import Dialog -from coretk.dialogs.nodeicon import IconDialog +from coretk.dialogs.icondialog import IconDialog class WlanConfigDialog(Dialog): diff --git a/coretk/coretk/images.py b/coretk/coretk/images.py index 768b33ba..f25b0eb1 100644 --- a/coretk/coretk/images.py +++ b/coretk/coretk/images.py @@ -29,6 +29,10 @@ class Images: def get(cls, image): return cls.images[image.value] + @classmethod + def get_custom(cls, name): + return cls.images[name] + @classmethod def convert_type_and_model_to_image(cls, node_type, node_model): """