Merge branch 'coretk-config' into coretk-nodedelete
This commit is contained in:
commit
9898e50739
10 changed files with 377 additions and 534 deletions
|
@ -50,10 +50,8 @@ class Application(tk.Frame):
|
|||
self.master.config(menu=self.menubar)
|
||||
|
||||
def draw_toolbar(self):
|
||||
edit_frame = tk.Frame(self)
|
||||
edit_frame.pack(side=tk.LEFT, fill=tk.Y, ipadx=2, ipady=2)
|
||||
self.core_editbar = CoreToolbar(self, edit_frame, self.menubar)
|
||||
self.core_editbar.create_toolbar()
|
||||
self.core_editbar = CoreToolbar(self, self)
|
||||
self.core_editbar.pack(side=tk.LEFT, fill=tk.Y, ipadx=2, ipady=2)
|
||||
|
||||
def draw_canvas(self):
|
||||
self.canvas = CanvasGraph(
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -8,12 +8,13 @@ from core.api.grpc import client, core_pb2
|
|||
from coretk.coretocanvas import CoreToCanvasMapping
|
||||
from coretk.dialogs.sessions import SessionsDialog
|
||||
from coretk.emaneodelnodeconfig import EmaneModelNodeConfig
|
||||
from coretk.images import Images
|
||||
from coretk.interface import Interface, InterfaceManager
|
||||
from coretk.mobilitynodeconfig import MobilityNodeConfig
|
||||
from coretk.wlannodeconfig import WlanNodeConfig
|
||||
|
||||
link_layer_nodes = ["switch", "hub", "wlan", "rj45", "tunnel", "emane"]
|
||||
network_layer_nodes = ["router", "host", "PC", "mdr", "prouter", "OVS"]
|
||||
network_layer_nodes = ["router", "host", "PC", "mdr", "prouter"]
|
||||
|
||||
|
||||
class Node:
|
||||
|
@ -64,6 +65,14 @@ class CoreServer:
|
|||
self.port = port
|
||||
|
||||
|
||||
class CustomNode:
|
||||
def __init__(self, name, image, image_file, services):
|
||||
self.name = name
|
||||
self.image = image
|
||||
self.image_file = image_file
|
||||
self.services = services
|
||||
|
||||
|
||||
class CoreClient:
|
||||
def __init__(self, app):
|
||||
"""
|
||||
|
@ -77,13 +86,10 @@ class CoreClient:
|
|||
self.interface_helper = None
|
||||
self.services = {}
|
||||
|
||||
# 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 = {}
|
||||
|
@ -99,6 +105,23 @@ class CoreClient:
|
|||
self.emaneconfig_management = EmaneModelNodeConfig(app)
|
||||
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:
|
||||
|
@ -185,11 +208,9 @@ class CoreClient:
|
|||
|
||||
# draw tool bar appropritate with session state
|
||||
if session_state == core_pb2.SessionState.RUNTIME:
|
||||
self.app.core_editbar.destroy_children_widgets()
|
||||
self.app.core_editbar.create_runtime_toolbar()
|
||||
self.app.core_editbar.runtime_frame.tkraise()
|
||||
else:
|
||||
self.app.core_editbar.destroy_children_widgets()
|
||||
self.app.core_editbar.create_toolbar()
|
||||
self.app.core_editbar.design_frame.tkraise()
|
||||
|
||||
def create_new_session(self):
|
||||
"""
|
||||
|
@ -217,7 +238,9 @@ class CoreClient:
|
|||
s = self.client.get_session(sid).session
|
||||
# delete links and nodes from running session
|
||||
if s.state == core_pb2.SessionState.RUNTIME:
|
||||
self.set_session_state("datacollect", sid)
|
||||
self.client.set_session_state(
|
||||
self.session_id, core_pb2.SessionState.DATACOLLECT
|
||||
)
|
||||
self.delete_links(sid)
|
||||
self.delete_nodes(sid)
|
||||
self.delete_session(sid)
|
||||
|
@ -251,48 +274,6 @@ class CoreClient:
|
|||
# logging.info("get session: %s", response)
|
||||
return response.session.state
|
||||
|
||||
def set_session_state(self, state, custom_session_id=None):
|
||||
"""
|
||||
Set session state
|
||||
|
||||
:param str state: session state to set
|
||||
:return: nothing
|
||||
"""
|
||||
if custom_session_id is None:
|
||||
sid = self.session_id
|
||||
else:
|
||||
sid = custom_session_id
|
||||
|
||||
response = None
|
||||
if state == "configuration":
|
||||
response = self.client.set_session_state(
|
||||
sid, core_pb2.SessionState.CONFIGURATION
|
||||
)
|
||||
elif state == "instantiation":
|
||||
response = self.client.set_session_state(
|
||||
sid, core_pb2.SessionState.INSTANTIATION
|
||||
)
|
||||
elif state == "datacollect":
|
||||
response = self.client.set_session_state(
|
||||
sid, core_pb2.SessionState.DATACOLLECT
|
||||
)
|
||||
elif state == "shutdown":
|
||||
response = self.client.set_session_state(
|
||||
sid, core_pb2.SessionState.SHUTDOWN
|
||||
)
|
||||
elif state == "runtime":
|
||||
response = self.client.set_session_state(sid, core_pb2.SessionState.RUNTIME)
|
||||
elif state == "definition":
|
||||
response = self.client.set_session_state(
|
||||
sid, core_pb2.SessionState.DEFINITION
|
||||
)
|
||||
elif state == "none":
|
||||
response = self.client.set_session_state(sid, core_pb2.SessionState.NONE)
|
||||
else:
|
||||
logging.error("coregrpc.py: set_session_state: INVALID STATE")
|
||||
|
||||
logging.info("set session state: %s", response)
|
||||
|
||||
def edit_node(self, node_id, x, y):
|
||||
position = core_pb2.Position(x=x, y=y)
|
||||
response = self.client.edit_node(self.session_id, node_id, position)
|
||||
|
@ -451,6 +432,8 @@ class CoreClient:
|
|||
node_type = core_pb2.NodeType.WIRELESS_LAN
|
||||
elif name == "rj45":
|
||||
node_type = core_pb2.NodeType.RJ45
|
||||
elif name == "emane":
|
||||
node_type = core_pb2.NodeType.EMANE
|
||||
elif name == "tunnel":
|
||||
node_type = core_pb2.NodeType.TUNNEL
|
||||
elif name == "emane":
|
||||
|
@ -459,7 +442,7 @@ class CoreClient:
|
|||
node_type = core_pb2.NodeType.DEFAULT
|
||||
node_model = name
|
||||
else:
|
||||
logging.error("grpcmanagemeny.py INVALID node name")
|
||||
logging.error("invalid node name: %s", name)
|
||||
nid = self.get_id()
|
||||
create_node = Node(session_id, nid, node_type, node_model, x, y, name)
|
||||
|
||||
|
@ -473,9 +456,8 @@ class CoreClient:
|
|||
|
||||
self.nodes[canvas_id] = create_node
|
||||
self.core_mapping.map_core_id_to_canvas_id(nid, canvas_id)
|
||||
# self.core_id_to_canvas_id[nid] = canvas_id
|
||||
logging.debug(
|
||||
"Adding node to GrpcManager.. Session id: %s, Coords: (%s, %s), Name: %s",
|
||||
"Adding node to core.. session id: %s, coords: (%s, %s), name: %s",
|
||||
session_id,
|
||||
x,
|
||||
y,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
import tkinter as tk
|
||||
from functools import partial
|
||||
|
||||
from coretk.coretoolbarhelp import CoreToolbarHelp
|
||||
from coretk.dialogs.customnodes import CustomNodesDialog
|
||||
|
@ -8,21 +9,20 @@ from coretk.images import ImageEnum, Images
|
|||
from coretk.tooltip import CreateToolTip
|
||||
|
||||
|
||||
class CoreToolbar(object):
|
||||
class CoreToolbar(tk.Frame):
|
||||
"""
|
||||
Core toolbar class
|
||||
"""
|
||||
|
||||
def __init__(self, app, edit_frame, menubar):
|
||||
def __init__(self, master, app, cnf={}, **kwargs):
|
||||
"""
|
||||
Create a CoreToolbar instance
|
||||
|
||||
:param tkinter.Frame edit_frame: edit frame
|
||||
"""
|
||||
super().__init__(master, cnf, **kwargs)
|
||||
self.app = app
|
||||
self.master = app.master
|
||||
self.edit_frame = edit_frame
|
||||
self.menubar = menubar
|
||||
self.radio_value = tk.IntVar()
|
||||
self.exec_radio_value = tk.IntVar()
|
||||
|
||||
|
@ -30,44 +30,144 @@ class CoreToolbar(object):
|
|||
self.width = 32
|
||||
self.height = 32
|
||||
|
||||
self.selection_tool_button = None
|
||||
|
||||
# Reference to the option menus
|
||||
self.selection_tool_button = None
|
||||
self.link_layer_option_menu = None
|
||||
self.marker_option_menu = None
|
||||
self.network_layer_option_menu = None
|
||||
|
||||
self.canvas = None
|
||||
self.node_button = None
|
||||
self.network_button = None
|
||||
self.annotation_button = None
|
||||
|
||||
def destroy_previous_frame(self):
|
||||
"""
|
||||
Destroy any extra frame from previous before drawing a new one
|
||||
# frames
|
||||
self.design_frame = None
|
||||
self.runtime_frame = None
|
||||
self.node_picker = None
|
||||
self.network_picker = None
|
||||
self.annotation_picker = None
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if (
|
||||
self.network_layer_option_menu
|
||||
and self.network_layer_option_menu.winfo_exists()
|
||||
):
|
||||
self.network_layer_option_menu.destroy()
|
||||
if self.link_layer_option_menu and self.link_layer_option_menu.winfo_exists():
|
||||
self.link_layer_option_menu.destroy()
|
||||
if self.marker_option_menu and self.marker_option_menu.winfo_exists():
|
||||
self.marker_option_menu.destroy()
|
||||
# draw components
|
||||
self.draw()
|
||||
|
||||
def destroy_children_widgets(self):
|
||||
"""
|
||||
Destroy all children of a parent widget
|
||||
def draw(self):
|
||||
self.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(0, weight=1)
|
||||
self.draw_design_frame()
|
||||
self.draw_runtime_frame()
|
||||
self.design_frame.tkraise()
|
||||
|
||||
:param tkinter.Frame parent: parent frame
|
||||
:return: nothing
|
||||
"""
|
||||
def draw_design_frame(self):
|
||||
self.design_frame = tk.Frame(self)
|
||||
self.design_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.design_frame.columnconfigure(0, weight=1)
|
||||
|
||||
for i in self.edit_frame.winfo_children():
|
||||
if i.winfo_name() != "!frame":
|
||||
i.destroy()
|
||||
self.create_regular_button(
|
||||
self.design_frame,
|
||||
Images.get(ImageEnum.START),
|
||||
self.click_start_session_tool,
|
||||
"start the session",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.design_frame,
|
||||
Images.get(ImageEnum.SELECT),
|
||||
self.click_selection_tool,
|
||||
self.radio_value,
|
||||
1,
|
||||
"selection tool",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.design_frame,
|
||||
Images.get(ImageEnum.LINK),
|
||||
self.click_link_tool,
|
||||
self.radio_value,
|
||||
2,
|
||||
"link tool",
|
||||
)
|
||||
self.create_node_button()
|
||||
self.create_link_layer_button()
|
||||
self.create_marker_button()
|
||||
self.radio_value.set(1)
|
||||
|
||||
def create_button(self, img, func, frame, main_button, btt_message):
|
||||
def draw_runtime_frame(self):
|
||||
self.runtime_frame = tk.Frame(self)
|
||||
self.runtime_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.runtime_frame.columnconfigure(0, weight=1)
|
||||
|
||||
self.create_regular_button(
|
||||
self.runtime_frame,
|
||||
Images.get(ImageEnum.STOP),
|
||||
self.click_stop_button,
|
||||
"stop the session",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.runtime_frame,
|
||||
Images.get(ImageEnum.SELECT),
|
||||
self.click_selection_tool,
|
||||
self.exec_radio_value,
|
||||
1,
|
||||
"selection tool",
|
||||
)
|
||||
self.create_observe_button()
|
||||
self.create_radio_button(
|
||||
self.runtime_frame,
|
||||
Images.get(ImageEnum.PLOT),
|
||||
self.click_plot_button,
|
||||
self.exec_radio_value,
|
||||
2,
|
||||
"plot",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.runtime_frame,
|
||||
Images.get(ImageEnum.MARKER),
|
||||
self.click_marker_button,
|
||||
self.exec_radio_value,
|
||||
3,
|
||||
"marker",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.runtime_frame,
|
||||
Images.get(ImageEnum.TWONODE),
|
||||
self.click_two_node_button,
|
||||
self.exec_radio_value,
|
||||
4,
|
||||
"run command from one node to another",
|
||||
)
|
||||
self.create_regular_button(
|
||||
self.runtime_frame, Images.get(ImageEnum.RUN), self.click_run_button, "run"
|
||||
)
|
||||
self.exec_radio_value.set(1)
|
||||
|
||||
def draw_node_picker(self):
|
||||
self.hide_pickers()
|
||||
self.node_picker = tk.Frame(self.master, padx=1, pady=1)
|
||||
nodes = [
|
||||
(ImageEnum.ROUTER, "router"),
|
||||
(ImageEnum.HOST, "host"),
|
||||
(ImageEnum.PC, "PC"),
|
||||
(ImageEnum.MDR, "mdr"),
|
||||
(ImageEnum.PROUTER, "prouter"),
|
||||
(ImageEnum.EDITNODE, "custom node types"),
|
||||
]
|
||||
for image_enum, tooltip in nodes:
|
||||
self.create_button(
|
||||
Images.get(image_enum),
|
||||
partial(self.update_button, self.node_button, image_enum, tooltip),
|
||||
self.node_picker,
|
||||
tooltip,
|
||||
)
|
||||
self.show_picker(self.node_button, self.node_picker)
|
||||
|
||||
def show_picker(self, button, picker):
|
||||
first_button = self.winfo_children()[0]
|
||||
x = button.winfo_rootx() - first_button.winfo_rootx() + 40
|
||||
y = button.winfo_rooty() - first_button.winfo_rooty() - 1
|
||||
picker.place(x=x, y=y)
|
||||
self.app.bind_all("<Button-1>", lambda e: self.hide_pickers())
|
||||
self.wait_window(picker)
|
||||
self.app.unbind_all("<Button-1>")
|
||||
|
||||
def create_button(self, img, func, frame, tooltip):
|
||||
"""
|
||||
Create button and put it on the frame
|
||||
|
||||
|
@ -78,9 +178,9 @@ class CoreToolbar(object):
|
|||
:return: nothing
|
||||
"""
|
||||
button = tk.Button(frame, width=self.width, height=self.height, image=img)
|
||||
button.bind("<Button-1>", lambda e: func())
|
||||
button.pack(side=tk.LEFT, pady=1)
|
||||
CreateToolTip(button, btt_message)
|
||||
button.bind("<Button-1>", lambda mb: func(main_button))
|
||||
CreateToolTip(button, tooltip)
|
||||
|
||||
def create_radio_button(self, frame, image, func, variable, value, tooltip_msg):
|
||||
button = tk.Radiobutton(
|
||||
|
@ -93,326 +193,108 @@ class CoreToolbar(object):
|
|||
variable=variable,
|
||||
command=func,
|
||||
)
|
||||
button.pack(side=tk.TOP, pady=1)
|
||||
button.grid()
|
||||
CreateToolTip(button, tooltip_msg)
|
||||
|
||||
def create_regular_button(self, frame, image, func, btt_message):
|
||||
def create_regular_button(self, frame, image, func, tooltip):
|
||||
button = tk.Button(
|
||||
frame, width=self.width, height=self.height, image=image, command=func
|
||||
)
|
||||
button.pack(side=tk.TOP, pady=1)
|
||||
CreateToolTip(button, btt_message)
|
||||
|
||||
def draw_button_menu_frame(self, edit_frame, option_frame, main_button):
|
||||
"""
|
||||
Draw option menu frame right next to the main button
|
||||
|
||||
:param tkinter.Frame edit_frame: parent frame of the main button
|
||||
:param tkinter.Frame option_frame: option frame to draw
|
||||
:param tkinter.Radiobutton main_button: the main button
|
||||
:return: nothing
|
||||
"""
|
||||
|
||||
first_button = edit_frame.winfo_children()[0]
|
||||
_x = main_button.winfo_rootx() - first_button.winfo_rootx() + 40
|
||||
_y = main_button.winfo_rooty() - first_button.winfo_rooty() - 1
|
||||
option_frame.place(x=_x, y=_y)
|
||||
|
||||
def bind_widgets_before_frame_hide(self, frame):
|
||||
"""
|
||||
Bind the widgets to a left click, when any of the widgets is clicked, the menu option frame is destroyed before
|
||||
any further action is performed
|
||||
|
||||
:param tkinter.Frame frame: the frame to be destroyed
|
||||
:return: nothing
|
||||
"""
|
||||
self.menubar.bind("<Button-1>", lambda e: frame.destroy())
|
||||
self.master.bind("<Button-1>", lambda e: frame.destroy())
|
||||
|
||||
def unbind_widgets_after_frame_hide(self):
|
||||
"""
|
||||
Unbind the widgets to make sure everything works normally again after the menu option frame is destroyed
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.master.unbind("<Button-1>")
|
||||
self.menubar.unbind("Button-1>")
|
||||
button.grid()
|
||||
CreateToolTip(button, tooltip)
|
||||
|
||||
def click_selection_tool(self):
|
||||
logging.debug("Click SELECTION TOOL")
|
||||
logging.debug("clicked selection tool")
|
||||
self.canvas.mode = GraphMode.SELECT
|
||||
|
||||
def click_start_session_tool(self):
|
||||
"""
|
||||
Start session handler: redraw buttons, send node and link messages to grpc server
|
||||
Start session handler redraw buttons, send node and link messages to grpc
|
||||
server.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("Click START STOP SESSION button")
|
||||
logging.debug("clicked start button")
|
||||
helper = CoreToolbarHelp(self.app)
|
||||
self.destroy_children_widgets()
|
||||
self.canvas.mode = GraphMode.SELECT
|
||||
|
||||
# set configuration state
|
||||
# state = self.canvas.core_grpc.get_session_state()
|
||||
# if state == core_pb2.SessionState.SHUTDOWN or self.application.is_open_xml:
|
||||
# self.canvas.core_grpc.set_session_state(SessionStateEnum.DEFINITION.value)
|
||||
# self.application.is_open_xml = False
|
||||
#
|
||||
# self.canvas.core_grpc.set_session_state(SessionStateEnum.CONFIGURATION.value)
|
||||
# helper.add_nodes()
|
||||
# helper.add_edges()
|
||||
# self.canvas.core_grpc.set_session_state(SessionStateEnum.INSTANTIATION.value)
|
||||
helper.gui_start_session()
|
||||
self.create_runtime_toolbar()
|
||||
|
||||
# for node in self.canvas.grpc_manager.nodes.values():
|
||||
# print(node.type, node.model, int(node.x), int(node.y), node.name, node.node_id)
|
||||
# self.canvas.core_grpc.add_node(
|
||||
# node.type, node.model, int(node.x), int(node.y), node.name, node.node_id
|
||||
# )
|
||||
|
||||
# print(len(self.canvas.grpc_manager.edges))
|
||||
# for edge in self.canvas.grpc_manager.edges.values():
|
||||
# print(edge.id1, edge.id2, edge.type1, edge.type2)
|
||||
# self.canvas.core_grpc.add_link(
|
||||
# edge.id1, edge.id2, edge.type1, edge.type2, edge
|
||||
# )
|
||||
# self.canvas.core_grpc.get_session()
|
||||
# self.application.is_open_xml = False
|
||||
self.runtime_frame.tkraise()
|
||||
|
||||
def click_link_tool(self):
|
||||
logging.debug("Click LINK button")
|
||||
self.canvas.mode = GraphMode.EDGE
|
||||
|
||||
def pick_router(self, main_button):
|
||||
logging.debug("Pick router option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.ROUTER))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.ROUTER)
|
||||
self.canvas.draw_node_name = "router"
|
||||
|
||||
def pick_host(self, main_button):
|
||||
logging.debug("Pick host option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.HOST))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.HOST)
|
||||
self.canvas.draw_node_name = "host"
|
||||
|
||||
def pick_pc(self, main_button):
|
||||
logging.debug("Pick PC option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.PC))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.PC)
|
||||
self.canvas.draw_node_name = "PC"
|
||||
|
||||
def pick_mdr(self, main_button):
|
||||
logging.debug("Pick MDR option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.MDR))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.MDR)
|
||||
self.canvas.draw_node_name = "mdr"
|
||||
|
||||
def pick_prouter(self, main_button):
|
||||
logging.debug("Pick prouter option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.PROUTER))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.PROUTER)
|
||||
self.canvas.draw_node_name = "prouter"
|
||||
|
||||
def pick_ovs(self, main_button):
|
||||
logging.debug("Pick OVS option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.OVS))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.OVS)
|
||||
self.canvas.draw_node_name = "OVS"
|
||||
|
||||
def pick_editnode(self, main_button):
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.EDITNODE))
|
||||
logging.debug("Pick editnode option")
|
||||
def update_button(self, button, image_enum, name):
|
||||
logging.info("update button(%s): %s, %s", button, image_enum, name)
|
||||
self.hide_pickers()
|
||||
if image_enum == ImageEnum.EDITNODE:
|
||||
dialog = CustomNodesDialog(self.app, self.app)
|
||||
dialog.show()
|
||||
else:
|
||||
image = Images.get(image_enum)
|
||||
logging.info("updating button(%s): %s", button, name)
|
||||
button.configure(image=image)
|
||||
self.canvas.mode = GraphMode.NODE
|
||||
self.canvas.draw_node_image = image
|
||||
self.canvas.draw_node_name = name
|
||||
|
||||
def draw_network_layer_options(self, network_layer_button):
|
||||
"""
|
||||
Draw the options for network-layer button
|
||||
def hide_pickers(self):
|
||||
logging.info("hiding pickers")
|
||||
if self.node_picker:
|
||||
self.node_picker.destroy()
|
||||
self.node_picker = None
|
||||
if self.network_picker:
|
||||
self.network_picker.destroy()
|
||||
self.network_picker = None
|
||||
if self.annotation_picker:
|
||||
self.annotation_picker.destroy()
|
||||
self.annotation_picker = None
|
||||
|
||||
:param tkinter.Radiobutton network_layer_button: network-layer button
|
||||
:return: nothing
|
||||
"""
|
||||
# create a frame and add buttons to it
|
||||
self.destroy_previous_frame()
|
||||
option_frame = tk.Frame(self.master, padx=1, pady=1)
|
||||
img_list = [
|
||||
Images.get(ImageEnum.ROUTER),
|
||||
Images.get(ImageEnum.HOST),
|
||||
Images.get(ImageEnum.PC),
|
||||
Images.get(ImageEnum.MDR),
|
||||
Images.get(ImageEnum.PROUTER),
|
||||
Images.get(ImageEnum.OVS),
|
||||
Images.get(ImageEnum.EDITNODE),
|
||||
]
|
||||
func_list = [
|
||||
self.pick_router,
|
||||
self.pick_host,
|
||||
self.pick_pc,
|
||||
self.pick_mdr,
|
||||
self.pick_prouter,
|
||||
self.pick_ovs,
|
||||
self.pick_editnode,
|
||||
]
|
||||
tooltip_list = [
|
||||
"router",
|
||||
"host",
|
||||
"PC",
|
||||
"mdr",
|
||||
"prouter",
|
||||
"OVS",
|
||||
"edit node types",
|
||||
]
|
||||
for i in range(len(img_list)):
|
||||
self.create_button(
|
||||
img_list[i],
|
||||
func_list[i],
|
||||
option_frame,
|
||||
network_layer_button,
|
||||
tooltip_list[i],
|
||||
)
|
||||
|
||||
# place frame at a calculated position as well as keep a reference of that frame
|
||||
self.draw_button_menu_frame(self.edit_frame, option_frame, network_layer_button)
|
||||
self.network_layer_option_menu = option_frame
|
||||
|
||||
# destroy the frame before any further actions on other widgets
|
||||
self.bind_widgets_before_frame_hide(option_frame)
|
||||
option_frame.wait_window(option_frame)
|
||||
self.unbind_widgets_after_frame_hide()
|
||||
|
||||
def create_network_layer_button(self):
|
||||
def create_node_button(self):
|
||||
"""
|
||||
Create network layer button
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
router_image = Images.get(ImageEnum.ROUTER)
|
||||
network_layer_button = tk.Radiobutton(
|
||||
self.edit_frame,
|
||||
self.node_button = tk.Radiobutton(
|
||||
self.design_frame,
|
||||
indicatoron=False,
|
||||
variable=self.radio_value,
|
||||
value=3,
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
image=router_image,
|
||||
command=lambda: self.draw_network_layer_options(network_layer_button),
|
||||
command=self.draw_node_picker,
|
||||
)
|
||||
network_layer_button.pack(side=tk.TOP, pady=1)
|
||||
CreateToolTip(network_layer_button, "Network-layer virtual nodes")
|
||||
self.node_button.grid()
|
||||
CreateToolTip(self.node_button, "Network-layer virtual nodes")
|
||||
|
||||
def pick_hub(self, main_button):
|
||||
logging.debug("Pick link-layer node HUB")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.HUB))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.HUB)
|
||||
self.canvas.draw_node_name = "hub"
|
||||
|
||||
def pick_switch(self, main_button):
|
||||
logging.debug("Pick link-layer node SWITCH")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.SWITCH))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.SWITCH)
|
||||
self.canvas.draw_node_name = "switch"
|
||||
|
||||
def pick_wlan(self, main_button):
|
||||
logging.debug("Pick link-layer node WLAN")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.WLAN))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.WLAN)
|
||||
self.canvas.draw_node_name = "wlan"
|
||||
|
||||
def pick_rj45(self, main_button):
|
||||
logging.debug("Pick link-layer node RJ45")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.RJ45))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.RJ45)
|
||||
self.canvas.draw_node_name = "rj45"
|
||||
|
||||
def pick_tunnel(self, main_button):
|
||||
logging.debug("Pick link-layer node TUNNEL")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.TUNNEL))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.TUNNEL)
|
||||
self.canvas.draw_node_name = "tunnel"
|
||||
|
||||
def pick_emane(self, main_button):
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.EMANE))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.EMANE)
|
||||
self.canvas.draw_node_name = "emane"
|
||||
|
||||
def draw_link_layer_options(self, link_layer_button):
|
||||
def draw_network_picker(self):
|
||||
"""
|
||||
Draw the options for link-layer button
|
||||
|
||||
:param tkinter.RadioButton link_layer_button: link-layer button
|
||||
:return: nothing
|
||||
"""
|
||||
# create a frame and add buttons to it
|
||||
self.destroy_previous_frame()
|
||||
option_frame = tk.Frame(self.master, padx=1, pady=1)
|
||||
img_list = [
|
||||
Images.get(ImageEnum.HUB),
|
||||
Images.get(ImageEnum.SWITCH),
|
||||
Images.get(ImageEnum.WLAN),
|
||||
Images.get(ImageEnum.EMANE),
|
||||
Images.get(ImageEnum.RJ45),
|
||||
Images.get(ImageEnum.TUNNEL),
|
||||
self.hide_pickers()
|
||||
self.network_picker = tk.Frame(self.master, padx=1, pady=1)
|
||||
nodes = [
|
||||
(ImageEnum.HUB, "hub", "ethernet hub"),
|
||||
(ImageEnum.SWITCH, "switch", "ethernet switch"),
|
||||
(ImageEnum.WLAN, "wlan", "wireless LAN"),
|
||||
(ImageEnum.EMANE, "emane", "EMANE"),
|
||||
(ImageEnum.RJ45, "rj45", "rj45 physical interface tool"),
|
||||
(ImageEnum.TUNNEL, "tunnel", "tunnel tool"),
|
||||
]
|
||||
func_list = [
|
||||
self.pick_hub,
|
||||
self.pick_switch,
|
||||
self.pick_wlan,
|
||||
self.pick_emane,
|
||||
self.pick_rj45,
|
||||
self.pick_tunnel,
|
||||
]
|
||||
tooltip_list = [
|
||||
"ethernet hub",
|
||||
"ethernet switch",
|
||||
"wireless LAN",
|
||||
"emane",
|
||||
"rj45 physical interface tool",
|
||||
"tunnel tool",
|
||||
]
|
||||
for i in range(len(img_list)):
|
||||
for image_enum, name, tooltip in nodes:
|
||||
self.create_button(
|
||||
img_list[i],
|
||||
func_list[i],
|
||||
option_frame,
|
||||
link_layer_button,
|
||||
tooltip_list[i],
|
||||
Images.get(image_enum),
|
||||
partial(self.update_button, self.network_button, image_enum, name),
|
||||
self.network_picker,
|
||||
tooltip,
|
||||
)
|
||||
|
||||
# place frame at a calculated position as well as keep a reference of the frame
|
||||
self.draw_button_menu_frame(self.edit_frame, option_frame, link_layer_button)
|
||||
self.link_layer_option_menu = option_frame
|
||||
|
||||
# destroy the frame before any further actions on other widgets
|
||||
self.bind_widgets_before_frame_hide(option_frame)
|
||||
option_frame.wait_window(option_frame)
|
||||
self.unbind_widgets_after_frame_hide()
|
||||
self.show_picker(self.network_button, self.network_picker)
|
||||
|
||||
def create_link_layer_button(self):
|
||||
"""
|
||||
|
@ -421,75 +303,42 @@ class CoreToolbar(object):
|
|||
:return: nothing
|
||||
"""
|
||||
hub_image = Images.get(ImageEnum.HUB)
|
||||
link_layer_button = tk.Radiobutton(
|
||||
self.edit_frame,
|
||||
self.network_button = tk.Radiobutton(
|
||||
self.design_frame,
|
||||
indicatoron=False,
|
||||
variable=self.radio_value,
|
||||
value=4,
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
image=hub_image,
|
||||
command=lambda: self.draw_link_layer_options(link_layer_button),
|
||||
command=self.draw_network_picker,
|
||||
)
|
||||
link_layer_button.pack(side=tk.TOP, pady=1)
|
||||
CreateToolTip(link_layer_button, "link-layer nodes")
|
||||
self.network_button.grid()
|
||||
CreateToolTip(self.network_button, "link-layer nodes")
|
||||
|
||||
def pick_marker(self, main_button):
|
||||
self.marker_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.MARKER))
|
||||
logging.debug("Pick MARKER")
|
||||
|
||||
def pick_oval(self, main_button):
|
||||
self.marker_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.OVAL))
|
||||
logging.debug("Pick OVAL")
|
||||
|
||||
def pick_rectangle(self, main_button):
|
||||
self.marker_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.RECTANGLE))
|
||||
logging.debug("Pick RECTANGLE")
|
||||
|
||||
def pick_text(self, main_button):
|
||||
self.marker_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.TEXT))
|
||||
logging.debug("Pick TEXT")
|
||||
|
||||
def draw_marker_options(self, main_button):
|
||||
def draw_annotation_picker(self):
|
||||
"""
|
||||
Draw the options for marker button
|
||||
|
||||
:param tkinter.Radiobutton main_button: the main button
|
||||
:return: nothing
|
||||
"""
|
||||
# create a frame and add buttons to it
|
||||
self.destroy_previous_frame()
|
||||
option_frame = tk.Frame(self.master, padx=1, pady=1)
|
||||
img_list = [
|
||||
Images.get(ImageEnum.MARKER),
|
||||
Images.get(ImageEnum.OVAL),
|
||||
Images.get(ImageEnum.RECTANGLE),
|
||||
Images.get(ImageEnum.TEXT),
|
||||
self.hide_pickers()
|
||||
self.annotation_picker = tk.Frame(self.master, padx=1, pady=1)
|
||||
nodes = [
|
||||
(ImageEnum.MARKER, "marker"),
|
||||
(ImageEnum.OVAL, "oval"),
|
||||
(ImageEnum.RECTANGLE, "rectangle"),
|
||||
(ImageEnum.TEXT, "text"),
|
||||
]
|
||||
func_list = [
|
||||
self.pick_marker,
|
||||
self.pick_oval,
|
||||
self.pick_rectangle,
|
||||
self.pick_text,
|
||||
]
|
||||
tooltip_list = ["marker", "oval", "rectangle", "text"]
|
||||
for i in range(len(img_list)):
|
||||
for image_enum, tooltip in nodes:
|
||||
self.create_button(
|
||||
img_list[i], func_list[i], option_frame, main_button, tooltip_list[i]
|
||||
Images.get(image_enum),
|
||||
partial(self.update_annotation, image_enum),
|
||||
self.annotation_picker,
|
||||
tooltip,
|
||||
)
|
||||
|
||||
# place the frame at a calculated position as well as keep a reference of that frame
|
||||
self.draw_button_menu_frame(self.edit_frame, option_frame, main_button)
|
||||
self.marker_option_menu = option_frame
|
||||
|
||||
# destroy the frame before any further actions on other widgets
|
||||
self.bind_widgets_before_frame_hide(option_frame)
|
||||
option_frame.wait_window(option_frame)
|
||||
self.unbind_widgets_after_frame_hide()
|
||||
self.show_picker(self.annotation_button, self.annotation_picker)
|
||||
|
||||
def create_marker_button(self):
|
||||
"""
|
||||
|
@ -498,55 +347,22 @@ class CoreToolbar(object):
|
|||
:return: nothing
|
||||
"""
|
||||
marker_image = Images.get(ImageEnum.MARKER)
|
||||
marker_main_button = tk.Radiobutton(
|
||||
self.edit_frame,
|
||||
self.annotation_button = tk.Radiobutton(
|
||||
self.design_frame,
|
||||
indicatoron=False,
|
||||
variable=self.radio_value,
|
||||
value=5,
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
image=marker_image,
|
||||
command=lambda: self.draw_marker_options(marker_main_button),
|
||||
command=self.draw_annotation_picker,
|
||||
)
|
||||
marker_main_button.pack(side=tk.TOP, pady=1)
|
||||
CreateToolTip(marker_main_button, "background annotation tools")
|
||||
|
||||
def create_toolbar(self):
|
||||
"""
|
||||
Create buttons for toolbar in edit mode
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.create_regular_button(
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.START),
|
||||
self.click_start_session_tool,
|
||||
"start the session",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.SELECT),
|
||||
self.click_selection_tool,
|
||||
self.radio_value,
|
||||
1,
|
||||
"selection tool",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.LINK),
|
||||
self.click_link_tool,
|
||||
self.radio_value,
|
||||
2,
|
||||
"link tool",
|
||||
)
|
||||
self.create_network_layer_button()
|
||||
self.create_link_layer_button()
|
||||
self.create_marker_button()
|
||||
self.radio_value.set(1)
|
||||
self.annotation_button.grid()
|
||||
CreateToolTip(self.annotation_button, "background annotation tools")
|
||||
|
||||
def create_observe_button(self):
|
||||
menu_button = tk.Menubutton(
|
||||
self.edit_frame,
|
||||
self.runtime_frame,
|
||||
image=Images.get(ImageEnum.OBSERVE),
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
|
@ -555,7 +371,7 @@ class CoreToolbar(object):
|
|||
)
|
||||
menu_button.menu = tk.Menu(menu_button, tearoff=0)
|
||||
menu_button["menu"] = menu_button.menu
|
||||
menu_button.pack(side=tk.TOP, pady=1)
|
||||
menu_button.grid()
|
||||
|
||||
menu_button.menu.add_command(label="None")
|
||||
menu_button.menu.add_command(label="processes")
|
||||
|
@ -581,9 +397,13 @@ class CoreToolbar(object):
|
|||
:return: nothing
|
||||
"""
|
||||
logging.debug("Click on STOP button ")
|
||||
self.destroy_children_widgets()
|
||||
self.app.core.stop_session()
|
||||
self.create_toolbar()
|
||||
self.design_frame.tkraise()
|
||||
|
||||
def update_annotation(self, image_enum):
|
||||
logging.info("clicked annotation: ")
|
||||
self.hide_pickers()
|
||||
self.annotation_button.configure(image=Images.get(image_enum))
|
||||
|
||||
def click_run_button(self):
|
||||
logging.debug("Click on RUN button")
|
||||
|
@ -596,48 +416,3 @@ class CoreToolbar(object):
|
|||
|
||||
def click_two_node_button(self):
|
||||
logging.debug("Click TWONODE button")
|
||||
|
||||
def create_runtime_toolbar(self):
|
||||
self.create_regular_button(
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.STOP),
|
||||
self.click_stop_button,
|
||||
"stop the session",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.SELECT),
|
||||
self.click_selection_tool,
|
||||
self.exec_radio_value,
|
||||
1,
|
||||
"selection tool",
|
||||
)
|
||||
self.create_observe_button()
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.PLOT),
|
||||
self.click_plot_button,
|
||||
self.exec_radio_value,
|
||||
2,
|
||||
"plot",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.MARKER),
|
||||
self.click_marker_button,
|
||||
self.exec_radio_value,
|
||||
3,
|
||||
"marker",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.TWONODE),
|
||||
self.click_two_node_button,
|
||||
self.exec_radio_value,
|
||||
4,
|
||||
"run command from one node to another",
|
||||
)
|
||||
self.create_regular_button(
|
||||
self.edit_frame, Images.get(ImageEnum.RUN), self.click_run_button, "run"
|
||||
)
|
||||
self.exec_radio_value.set(1)
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
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
|
||||
|
||||
|
||||
class ServicesSelectDialog(Dialog):
|
||||
def __init__(self, master, app):
|
||||
def __init__(self, master, app, current_services):
|
||||
super().__init__(master, app, "Node Services", modal=True)
|
||||
self.groups = None
|
||||
self.services = None
|
||||
self.current = None
|
||||
self.current_services = set()
|
||||
self.current_services = current_services
|
||||
self.draw()
|
||||
|
||||
def draw(self):
|
||||
|
@ -37,14 +41,16 @@ class ServicesSelectDialog(Dialog):
|
|||
|
||||
self.current = ListboxScroll(frame, text="Selected")
|
||||
self.current.grid(row=0, column=2, sticky="nsew")
|
||||
for service in sorted(self.current_services):
|
||||
self.current.listbox.insert(tk.END, service)
|
||||
|
||||
frame = tk.Frame(self)
|
||||
frame.grid(stick="ew")
|
||||
for i in range(2):
|
||||
frame.columnconfigure(i, weight=1)
|
||||
button = tk.Button(frame, text="Save")
|
||||
button = tk.Button(frame, text="Save", command=self.click_cancel)
|
||||
button.grid(row=0, column=0, sticky="ew")
|
||||
button = tk.Button(frame, text="Cancel", command=self.destroy)
|
||||
button = tk.Button(frame, text="Cancel", command=self.click_cancel)
|
||||
button.grid(row=0, column=1, sticky="ew")
|
||||
|
||||
# trigger group change
|
||||
|
@ -69,15 +75,24 @@ class ServicesSelectDialog(Dialog):
|
|||
for name in sorted(self.current_services):
|
||||
self.current.listbox.insert(tk.END, name)
|
||||
|
||||
def click_cancel(self):
|
||||
self.current_services = None
|
||||
self.destroy()
|
||||
|
||||
|
||||
class CustomNodesDialog(Dialog):
|
||||
def __init__(self, master, app):
|
||||
super().__init__(master, app, "Custom Nodes", modal=True)
|
||||
self.save_button = None
|
||||
self.edit_button = None
|
||||
self.delete_button = None
|
||||
self.nodes_list = None
|
||||
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
|
||||
self.draw()
|
||||
|
||||
def draw(self):
|
||||
|
@ -93,13 +108,11 @@ class CustomNodesDialog(Dialog):
|
|||
frame.columnconfigure(0, weight=1)
|
||||
frame.rowconfigure(0, weight=1)
|
||||
|
||||
scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL)
|
||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||
|
||||
listbox = tk.Listbox(frame, selectmode=tk.SINGLE, yscrollcommand=scrollbar.set)
|
||||
listbox.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
scrollbar.config(command=listbox.yview)
|
||||
self.nodes_list = ListboxScroll(frame)
|
||||
self.nodes_list.grid(row=0, column=0, sticky="nsew")
|
||||
self.nodes_list.listbox.bind("<<ListboxSelect>>", self.handle_node_select)
|
||||
for name in sorted(self.app.core.custom_nodes):
|
||||
self.nodes_list.listbox.insert(tk.END, name)
|
||||
|
||||
frame = tk.Frame(frame)
|
||||
frame.grid(row=0, column=2, sticky="nsew")
|
||||
|
@ -120,10 +133,10 @@ class CustomNodesDialog(Dialog):
|
|||
button = tk.Button(frame, text="Create", command=self.click_create)
|
||||
button.grid(row=0, column=0, sticky="ew")
|
||||
|
||||
self.save_button = tk.Button(
|
||||
frame, text="Save", state=tk.DISABLED, command=self.click_save
|
||||
self.edit_button = tk.Button(
|
||||
frame, text="Edit", state=tk.DISABLED, command=self.click_edit
|
||||
)
|
||||
self.save_button.grid(row=0, column=1, sticky="ew")
|
||||
self.edit_button.grid(row=0, column=1, sticky="ew")
|
||||
|
||||
self.delete_button = tk.Button(
|
||||
frame, text="Delete", state=tk.DISABLED, command=self.click_delete
|
||||
|
@ -142,22 +155,89 @@ class CustomNodesDialog(Dialog):
|
|||
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):
|
||||
dialog = ServicesSelectDialog(self, self.app)
|
||||
dialog = ServicesSelectDialog(self, self.app, self.services)
|
||||
dialog.show()
|
||||
|
||||
def click_create(self):
|
||||
pass
|
||||
if dialog.current_services is not None:
|
||||
self.services = dialog.current_services
|
||||
|
||||
def click_save(self):
|
||||
pass
|
||||
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, 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 click_edit(self):
|
||||
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):
|
||||
pass
|
||||
if self.selected and self.selected in self.app.core.custom_nodes:
|
||||
self.nodes_list.listbox.delete(self.selected_index)
|
||||
del self.app.core.custom_nodes[self.selected]
|
||||
self.reset_values()
|
||||
self.nodes_list.listbox.selection_clear(0, tk.END)
|
||||
self.nodes_list.listbox.event_generate("<<ListboxSelect>>")
|
||||
|
||||
def handle_node_select(self, event):
|
||||
selection = self.nodes_list.listbox.curselection()
|
||||
if selection:
|
||||
self.selected_index = selection[0]
|
||||
self.selected = self.nodes_list.listbox.get(self.selected_index)
|
||||
custom_node = self.app.core.custom_nodes[self.selected]
|
||||
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)
|
||||
else:
|
||||
self.selected = None
|
||||
self.selected_index = None
|
||||
self.edit_button.config(state=tk.DISABLED)
|
||||
self.delete_button.config(state=tk.DISABLED)
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -278,7 +278,7 @@ class CanvasGraph(tk.Canvas):
|
|||
else:
|
||||
self.focus_set()
|
||||
self.selected = self.get_selected(event)
|
||||
logging.debug(f"click release selected: {self.selected}")
|
||||
logging.debug(f"click release selected({self.selected}) mode({self.mode})")
|
||||
if self.mode == GraphMode.EDGE:
|
||||
self.handle_edge_release(event)
|
||||
elif self.mode == GraphMode.NODE:
|
||||
|
@ -416,6 +416,7 @@ class CanvasGraph(tk.Canvas):
|
|||
|
||||
def add_node(self, x, y, image, node_name):
|
||||
plot_id = self.find_all()[0]
|
||||
logging.info("add node event: %s - %s", plot_id, self.selected)
|
||||
if self.selected == plot_id:
|
||||
node = CanvasNode(
|
||||
x=x,
|
||||
|
@ -539,13 +540,13 @@ class CanvasNode:
|
|||
self.x_coord, self.y_coord = self.canvas.coords(self.id)
|
||||
|
||||
def click_press(self, event):
|
||||
logging.debug(f"click press {self.name}: {event}")
|
||||
logging.debug(f"node click press {self.name}: {event}")
|
||||
self.moving = self.canvas.canvas_xy(event)
|
||||
|
||||
self.canvas.canvas_management.node_select(self)
|
||||
|
||||
def click_release(self, event):
|
||||
logging.debug(f"click release {self.name}: {event}")
|
||||
logging.debug(f"node click release {self.name}: {event}")
|
||||
self.update_coords()
|
||||
self.canvas.core.update_node_location(self.id, self.x_coord, self.y_coord)
|
||||
self.moving = None
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
Loading…
Add table
Reference in a new issue