Merge branch 'coretk-config' into coretk-nodedelete

This commit is contained in:
Huy Pham 2019-11-07 15:21:13 -08:00
commit 9898e50739
10 changed files with 377 additions and 534 deletions

View file

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

View file

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

View file

@ -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,

View file

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

View file

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

View file

@ -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"]

View file

@ -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):

View file

@ -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

View file

@ -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):
"""