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)
|
self.master.config(menu=self.menubar)
|
||||||
|
|
||||||
def draw_toolbar(self):
|
def draw_toolbar(self):
|
||||||
edit_frame = tk.Frame(self)
|
self.core_editbar = CoreToolbar(self, self)
|
||||||
edit_frame.pack(side=tk.LEFT, fill=tk.Y, ipadx=2, ipady=2)
|
self.core_editbar.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()
|
|
||||||
|
|
||||||
def draw_canvas(self):
|
def draw_canvas(self):
|
||||||
self.canvas = CanvasGraph(
|
self.canvas = CanvasGraph(
|
||||||
|
|
|
@ -42,7 +42,10 @@ def check_directory():
|
||||||
for background in LOCAL_BACKGROUND_PATH.glob("*"):
|
for background in LOCAL_BACKGROUND_PATH.glob("*"):
|
||||||
new_background = BACKGROUNDS_PATH.joinpath(background.name)
|
new_background = BACKGROUNDS_PATH.joinpath(background.name)
|
||||||
shutil.copy(background, new_background)
|
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)
|
save_config(config)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,13 @@ from core.api.grpc import client, core_pb2
|
||||||
from coretk.coretocanvas import CoreToCanvasMapping
|
from coretk.coretocanvas import CoreToCanvasMapping
|
||||||
from coretk.dialogs.sessions import SessionsDialog
|
from coretk.dialogs.sessions import SessionsDialog
|
||||||
from coretk.emaneodelnodeconfig import EmaneModelNodeConfig
|
from coretk.emaneodelnodeconfig import EmaneModelNodeConfig
|
||||||
|
from coretk.images import Images
|
||||||
from coretk.interface import Interface, InterfaceManager
|
from coretk.interface import Interface, InterfaceManager
|
||||||
from coretk.mobilitynodeconfig import MobilityNodeConfig
|
from coretk.mobilitynodeconfig import MobilityNodeConfig
|
||||||
from coretk.wlannodeconfig import WlanNodeConfig
|
from coretk.wlannodeconfig import WlanNodeConfig
|
||||||
|
|
||||||
link_layer_nodes = ["switch", "hub", "wlan", "rj45", "tunnel", "emane"]
|
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:
|
class Node:
|
||||||
|
@ -64,6 +65,14 @@ class CoreServer:
|
||||||
self.port = port
|
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:
|
class CoreClient:
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
"""
|
"""
|
||||||
|
@ -77,13 +86,10 @@ class CoreClient:
|
||||||
self.interface_helper = None
|
self.interface_helper = None
|
||||||
self.services = {}
|
self.services = {}
|
||||||
|
|
||||||
# distributed server data
|
# loaded configuration data
|
||||||
self.servers = {}
|
self.servers = {}
|
||||||
for server_config in self.app.config["servers"]:
|
self.custom_nodes = {}
|
||||||
server = CoreServer(
|
self.read_config()
|
||||||
server_config["name"], server_config["address"], server_config["port"]
|
|
||||||
)
|
|
||||||
self.servers[server.name] = server
|
|
||||||
|
|
||||||
# data for managing the current session
|
# data for managing the current session
|
||||||
self.nodes = {}
|
self.nodes = {}
|
||||||
|
@ -99,6 +105,23 @@ class CoreClient:
|
||||||
self.emaneconfig_management = EmaneModelNodeConfig(app)
|
self.emaneconfig_management = EmaneModelNodeConfig(app)
|
||||||
self.emane_config = None
|
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):
|
def handle_events(self, event):
|
||||||
logging.info("event: %s", event)
|
logging.info("event: %s", event)
|
||||||
if event.link_event is not None:
|
if event.link_event is not None:
|
||||||
|
@ -185,11 +208,9 @@ class CoreClient:
|
||||||
|
|
||||||
# draw tool bar appropritate with session state
|
# draw tool bar appropritate with session state
|
||||||
if session_state == core_pb2.SessionState.RUNTIME:
|
if session_state == core_pb2.SessionState.RUNTIME:
|
||||||
self.app.core_editbar.destroy_children_widgets()
|
self.app.core_editbar.runtime_frame.tkraise()
|
||||||
self.app.core_editbar.create_runtime_toolbar()
|
|
||||||
else:
|
else:
|
||||||
self.app.core_editbar.destroy_children_widgets()
|
self.app.core_editbar.design_frame.tkraise()
|
||||||
self.app.core_editbar.create_toolbar()
|
|
||||||
|
|
||||||
def create_new_session(self):
|
def create_new_session(self):
|
||||||
"""
|
"""
|
||||||
|
@ -217,7 +238,9 @@ class CoreClient:
|
||||||
s = self.client.get_session(sid).session
|
s = self.client.get_session(sid).session
|
||||||
# delete links and nodes from running session
|
# delete links and nodes from running session
|
||||||
if s.state == core_pb2.SessionState.RUNTIME:
|
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_links(sid)
|
||||||
self.delete_nodes(sid)
|
self.delete_nodes(sid)
|
||||||
self.delete_session(sid)
|
self.delete_session(sid)
|
||||||
|
@ -251,48 +274,6 @@ class CoreClient:
|
||||||
# logging.info("get session: %s", response)
|
# logging.info("get session: %s", response)
|
||||||
return response.session.state
|
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):
|
def edit_node(self, node_id, x, y):
|
||||||
position = core_pb2.Position(x=x, y=y)
|
position = core_pb2.Position(x=x, y=y)
|
||||||
response = self.client.edit_node(self.session_id, node_id, position)
|
response = self.client.edit_node(self.session_id, node_id, position)
|
||||||
|
@ -451,6 +432,8 @@ class CoreClient:
|
||||||
node_type = core_pb2.NodeType.WIRELESS_LAN
|
node_type = core_pb2.NodeType.WIRELESS_LAN
|
||||||
elif name == "rj45":
|
elif name == "rj45":
|
||||||
node_type = core_pb2.NodeType.RJ45
|
node_type = core_pb2.NodeType.RJ45
|
||||||
|
elif name == "emane":
|
||||||
|
node_type = core_pb2.NodeType.EMANE
|
||||||
elif name == "tunnel":
|
elif name == "tunnel":
|
||||||
node_type = core_pb2.NodeType.TUNNEL
|
node_type = core_pb2.NodeType.TUNNEL
|
||||||
elif name == "emane":
|
elif name == "emane":
|
||||||
|
@ -459,7 +442,7 @@ class CoreClient:
|
||||||
node_type = core_pb2.NodeType.DEFAULT
|
node_type = core_pb2.NodeType.DEFAULT
|
||||||
node_model = name
|
node_model = name
|
||||||
else:
|
else:
|
||||||
logging.error("grpcmanagemeny.py INVALID node name")
|
logging.error("invalid node name: %s", name)
|
||||||
nid = self.get_id()
|
nid = self.get_id()
|
||||||
create_node = Node(session_id, nid, node_type, node_model, x, y, name)
|
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.nodes[canvas_id] = create_node
|
||||||
self.core_mapping.map_core_id_to_canvas_id(nid, canvas_id)
|
self.core_mapping.map_core_id_to_canvas_id(nid, canvas_id)
|
||||||
# self.core_id_to_canvas_id[nid] = canvas_id
|
|
||||||
logging.debug(
|
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,
|
session_id,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from coretk.coretoolbarhelp import CoreToolbarHelp
|
from coretk.coretoolbarhelp import CoreToolbarHelp
|
||||||
from coretk.dialogs.customnodes import CustomNodesDialog
|
from coretk.dialogs.customnodes import CustomNodesDialog
|
||||||
|
@ -8,21 +9,20 @@ from coretk.images import ImageEnum, Images
|
||||||
from coretk.tooltip import CreateToolTip
|
from coretk.tooltip import CreateToolTip
|
||||||
|
|
||||||
|
|
||||||
class CoreToolbar(object):
|
class CoreToolbar(tk.Frame):
|
||||||
"""
|
"""
|
||||||
Core toolbar class
|
Core toolbar class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app, edit_frame, menubar):
|
def __init__(self, master, app, cnf={}, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create a CoreToolbar instance
|
Create a CoreToolbar instance
|
||||||
|
|
||||||
:param tkinter.Frame edit_frame: edit frame
|
:param tkinter.Frame edit_frame: edit frame
|
||||||
"""
|
"""
|
||||||
|
super().__init__(master, cnf, **kwargs)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.master = app.master
|
self.master = app.master
|
||||||
self.edit_frame = edit_frame
|
|
||||||
self.menubar = menubar
|
|
||||||
self.radio_value = tk.IntVar()
|
self.radio_value = tk.IntVar()
|
||||||
self.exec_radio_value = tk.IntVar()
|
self.exec_radio_value = tk.IntVar()
|
||||||
|
|
||||||
|
@ -30,44 +30,144 @@ class CoreToolbar(object):
|
||||||
self.width = 32
|
self.width = 32
|
||||||
self.height = 32
|
self.height = 32
|
||||||
|
|
||||||
self.selection_tool_button = None
|
|
||||||
|
|
||||||
# Reference to the option menus
|
# Reference to the option menus
|
||||||
|
self.selection_tool_button = None
|
||||||
self.link_layer_option_menu = None
|
self.link_layer_option_menu = None
|
||||||
self.marker_option_menu = None
|
self.marker_option_menu = None
|
||||||
self.network_layer_option_menu = None
|
self.network_layer_option_menu = None
|
||||||
|
|
||||||
self.canvas = None
|
self.canvas = None
|
||||||
|
self.node_button = None
|
||||||
|
self.network_button = None
|
||||||
|
self.annotation_button = None
|
||||||
|
|
||||||
def destroy_previous_frame(self):
|
# frames
|
||||||
"""
|
self.design_frame = None
|
||||||
Destroy any extra frame from previous before drawing a new one
|
self.runtime_frame = None
|
||||||
|
self.node_picker = None
|
||||||
|
self.network_picker = None
|
||||||
|
self.annotation_picker = None
|
||||||
|
|
||||||
:return: nothing
|
# draw components
|
||||||
"""
|
self.draw()
|
||||||
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()
|
|
||||||
|
|
||||||
def destroy_children_widgets(self):
|
def draw(self):
|
||||||
"""
|
self.columnconfigure(0, weight=1)
|
||||||
Destroy all children of a parent widget
|
self.rowconfigure(0, weight=1)
|
||||||
|
self.draw_design_frame()
|
||||||
|
self.draw_runtime_frame()
|
||||||
|
self.design_frame.tkraise()
|
||||||
|
|
||||||
:param tkinter.Frame parent: parent frame
|
def draw_design_frame(self):
|
||||||
:return: nothing
|
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():
|
self.create_regular_button(
|
||||||
if i.winfo_name() != "!frame":
|
self.design_frame,
|
||||||
i.destroy()
|
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
|
Create button and put it on the frame
|
||||||
|
|
||||||
|
@ -78,9 +178,9 @@ class CoreToolbar(object):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
button = tk.Button(frame, width=self.width, height=self.height, image=img)
|
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)
|
button.pack(side=tk.LEFT, pady=1)
|
||||||
CreateToolTip(button, btt_message)
|
CreateToolTip(button, tooltip)
|
||||||
button.bind("<Button-1>", lambda mb: func(main_button))
|
|
||||||
|
|
||||||
def create_radio_button(self, frame, image, func, variable, value, tooltip_msg):
|
def create_radio_button(self, frame, image, func, variable, value, tooltip_msg):
|
||||||
button = tk.Radiobutton(
|
button = tk.Radiobutton(
|
||||||
|
@ -93,326 +193,108 @@ class CoreToolbar(object):
|
||||||
variable=variable,
|
variable=variable,
|
||||||
command=func,
|
command=func,
|
||||||
)
|
)
|
||||||
button.pack(side=tk.TOP, pady=1)
|
button.grid()
|
||||||
CreateToolTip(button, tooltip_msg)
|
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(
|
button = tk.Button(
|
||||||
frame, width=self.width, height=self.height, image=image, command=func
|
frame, width=self.width, height=self.height, image=image, command=func
|
||||||
)
|
)
|
||||||
button.pack(side=tk.TOP, pady=1)
|
button.grid()
|
||||||
CreateToolTip(button, btt_message)
|
CreateToolTip(button, tooltip)
|
||||||
|
|
||||||
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>")
|
|
||||||
|
|
||||||
def click_selection_tool(self):
|
def click_selection_tool(self):
|
||||||
logging.debug("Click SELECTION TOOL")
|
logging.debug("clicked selection tool")
|
||||||
self.canvas.mode = GraphMode.SELECT
|
self.canvas.mode = GraphMode.SELECT
|
||||||
|
|
||||||
def click_start_session_tool(self):
|
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
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.debug("Click START STOP SESSION button")
|
logging.debug("clicked start button")
|
||||||
helper = CoreToolbarHelp(self.app)
|
helper = CoreToolbarHelp(self.app)
|
||||||
self.destroy_children_widgets()
|
|
||||||
self.canvas.mode = GraphMode.SELECT
|
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()
|
helper.gui_start_session()
|
||||||
self.create_runtime_toolbar()
|
self.runtime_frame.tkraise()
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
def click_link_tool(self):
|
def click_link_tool(self):
|
||||||
logging.debug("Click LINK button")
|
logging.debug("Click LINK button")
|
||||||
self.canvas.mode = GraphMode.EDGE
|
self.canvas.mode = GraphMode.EDGE
|
||||||
|
|
||||||
def pick_router(self, main_button):
|
def update_button(self, button, image_enum, name):
|
||||||
logging.debug("Pick router option")
|
logging.info("update button(%s): %s, %s", button, image_enum, name)
|
||||||
self.network_layer_option_menu.destroy()
|
self.hide_pickers()
|
||||||
main_button.configure(image=Images.get(ImageEnum.ROUTER))
|
if image_enum == ImageEnum.EDITNODE:
|
||||||
self.canvas.mode = GraphMode.PICKNODE
|
dialog = CustomNodesDialog(self.app, self.app)
|
||||||
self.canvas.draw_node_image = Images.get(ImageEnum.ROUTER)
|
dialog.show()
|
||||||
self.canvas.draw_node_name = "router"
|
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 pick_host(self, main_button):
|
def hide_pickers(self):
|
||||||
logging.debug("Pick host option")
|
logging.info("hiding pickers")
|
||||||
self.network_layer_option_menu.destroy()
|
if self.node_picker:
|
||||||
main_button.configure(image=Images.get(ImageEnum.HOST))
|
self.node_picker.destroy()
|
||||||
self.canvas.mode = GraphMode.PICKNODE
|
self.node_picker = None
|
||||||
self.canvas.draw_node_image = Images.get(ImageEnum.HOST)
|
if self.network_picker:
|
||||||
self.canvas.draw_node_name = "host"
|
self.network_picker.destroy()
|
||||||
|
self.network_picker = None
|
||||||
|
if self.annotation_picker:
|
||||||
|
self.annotation_picker.destroy()
|
||||||
|
self.annotation_picker = None
|
||||||
|
|
||||||
def pick_pc(self, main_button):
|
def create_node_button(self):
|
||||||
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")
|
|
||||||
dialog = CustomNodesDialog(self.app, self.app)
|
|
||||||
dialog.show()
|
|
||||||
|
|
||||||
def draw_network_layer_options(self, network_layer_button):
|
|
||||||
"""
|
|
||||||
Draw the options for network-layer button
|
|
||||||
|
|
||||||
: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):
|
|
||||||
"""
|
"""
|
||||||
Create network layer button
|
Create network layer button
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
router_image = Images.get(ImageEnum.ROUTER)
|
router_image = Images.get(ImageEnum.ROUTER)
|
||||||
network_layer_button = tk.Radiobutton(
|
self.node_button = tk.Radiobutton(
|
||||||
self.edit_frame,
|
self.design_frame,
|
||||||
indicatoron=False,
|
indicatoron=False,
|
||||||
variable=self.radio_value,
|
variable=self.radio_value,
|
||||||
value=3,
|
value=3,
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=self.height,
|
height=self.height,
|
||||||
image=router_image,
|
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)
|
self.node_button.grid()
|
||||||
CreateToolTip(network_layer_button, "Network-layer virtual nodes")
|
CreateToolTip(self.node_button, "Network-layer virtual nodes")
|
||||||
|
|
||||||
def pick_hub(self, main_button):
|
def draw_network_picker(self):
|
||||||
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):
|
|
||||||
"""
|
"""
|
||||||
Draw the options for link-layer button
|
Draw the options for link-layer button
|
||||||
|
|
||||||
:param tkinter.RadioButton link_layer_button: link-layer button
|
:param tkinter.RadioButton link_layer_button: link-layer button
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# create a frame and add buttons to it
|
self.hide_pickers()
|
||||||
self.destroy_previous_frame()
|
self.network_picker = tk.Frame(self.master, padx=1, pady=1)
|
||||||
option_frame = tk.Frame(self.master, padx=1, pady=1)
|
nodes = [
|
||||||
img_list = [
|
(ImageEnum.HUB, "hub", "ethernet hub"),
|
||||||
Images.get(ImageEnum.HUB),
|
(ImageEnum.SWITCH, "switch", "ethernet switch"),
|
||||||
Images.get(ImageEnum.SWITCH),
|
(ImageEnum.WLAN, "wlan", "wireless LAN"),
|
||||||
Images.get(ImageEnum.WLAN),
|
(ImageEnum.EMANE, "emane", "EMANE"),
|
||||||
Images.get(ImageEnum.EMANE),
|
(ImageEnum.RJ45, "rj45", "rj45 physical interface tool"),
|
||||||
Images.get(ImageEnum.RJ45),
|
(ImageEnum.TUNNEL, "tunnel", "tunnel tool"),
|
||||||
Images.get(ImageEnum.TUNNEL),
|
|
||||||
]
|
]
|
||||||
func_list = [
|
for image_enum, name, tooltip in nodes:
|
||||||
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)):
|
|
||||||
self.create_button(
|
self.create_button(
|
||||||
img_list[i],
|
Images.get(image_enum),
|
||||||
func_list[i],
|
partial(self.update_button, self.network_button, image_enum, name),
|
||||||
option_frame,
|
self.network_picker,
|
||||||
link_layer_button,
|
tooltip,
|
||||||
tooltip_list[i],
|
|
||||||
)
|
)
|
||||||
|
self.show_picker(self.network_button, self.network_picker)
|
||||||
# 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()
|
|
||||||
|
|
||||||
def create_link_layer_button(self):
|
def create_link_layer_button(self):
|
||||||
"""
|
"""
|
||||||
|
@ -421,75 +303,42 @@ class CoreToolbar(object):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
hub_image = Images.get(ImageEnum.HUB)
|
hub_image = Images.get(ImageEnum.HUB)
|
||||||
link_layer_button = tk.Radiobutton(
|
self.network_button = tk.Radiobutton(
|
||||||
self.edit_frame,
|
self.design_frame,
|
||||||
indicatoron=False,
|
indicatoron=False,
|
||||||
variable=self.radio_value,
|
variable=self.radio_value,
|
||||||
value=4,
|
value=4,
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=self.height,
|
height=self.height,
|
||||||
image=hub_image,
|
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)
|
self.network_button.grid()
|
||||||
CreateToolTip(link_layer_button, "link-layer nodes")
|
CreateToolTip(self.network_button, "link-layer nodes")
|
||||||
|
|
||||||
def pick_marker(self, main_button):
|
def draw_annotation_picker(self):
|
||||||
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):
|
|
||||||
"""
|
"""
|
||||||
Draw the options for marker button
|
Draw the options for marker button
|
||||||
|
|
||||||
:param tkinter.Radiobutton main_button: the main button
|
:param tkinter.Radiobutton main_button: the main button
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# create a frame and add buttons to it
|
self.hide_pickers()
|
||||||
self.destroy_previous_frame()
|
self.annotation_picker = tk.Frame(self.master, padx=1, pady=1)
|
||||||
option_frame = tk.Frame(self.master, padx=1, pady=1)
|
nodes = [
|
||||||
img_list = [
|
(ImageEnum.MARKER, "marker"),
|
||||||
Images.get(ImageEnum.MARKER),
|
(ImageEnum.OVAL, "oval"),
|
||||||
Images.get(ImageEnum.OVAL),
|
(ImageEnum.RECTANGLE, "rectangle"),
|
||||||
Images.get(ImageEnum.RECTANGLE),
|
(ImageEnum.TEXT, "text"),
|
||||||
Images.get(ImageEnum.TEXT),
|
|
||||||
]
|
]
|
||||||
func_list = [
|
for image_enum, tooltip in nodes:
|
||||||
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)):
|
|
||||||
self.create_button(
|
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,
|
||||||
)
|
)
|
||||||
|
self.show_picker(self.annotation_button, self.annotation_picker)
|
||||||
# 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()
|
|
||||||
|
|
||||||
def create_marker_button(self):
|
def create_marker_button(self):
|
||||||
"""
|
"""
|
||||||
|
@ -498,55 +347,22 @@ class CoreToolbar(object):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
marker_image = Images.get(ImageEnum.MARKER)
|
marker_image = Images.get(ImageEnum.MARKER)
|
||||||
marker_main_button = tk.Radiobutton(
|
self.annotation_button = tk.Radiobutton(
|
||||||
self.edit_frame,
|
self.design_frame,
|
||||||
indicatoron=False,
|
indicatoron=False,
|
||||||
variable=self.radio_value,
|
variable=self.radio_value,
|
||||||
value=5,
|
value=5,
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=self.height,
|
height=self.height,
|
||||||
image=marker_image,
|
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)
|
self.annotation_button.grid()
|
||||||
CreateToolTip(marker_main_button, "background annotation tools")
|
CreateToolTip(self.annotation_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)
|
|
||||||
|
|
||||||
def create_observe_button(self):
|
def create_observe_button(self):
|
||||||
menu_button = tk.Menubutton(
|
menu_button = tk.Menubutton(
|
||||||
self.edit_frame,
|
self.runtime_frame,
|
||||||
image=Images.get(ImageEnum.OBSERVE),
|
image=Images.get(ImageEnum.OBSERVE),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=self.height,
|
height=self.height,
|
||||||
|
@ -555,7 +371,7 @@ class CoreToolbar(object):
|
||||||
)
|
)
|
||||||
menu_button.menu = tk.Menu(menu_button, tearoff=0)
|
menu_button.menu = tk.Menu(menu_button, tearoff=0)
|
||||||
menu_button["menu"] = menu_button.menu
|
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="None")
|
||||||
menu_button.menu.add_command(label="processes")
|
menu_button.menu.add_command(label="processes")
|
||||||
|
@ -581,9 +397,13 @@ class CoreToolbar(object):
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
logging.debug("Click on STOP button ")
|
logging.debug("Click on STOP button ")
|
||||||
self.destroy_children_widgets()
|
|
||||||
self.app.core.stop_session()
|
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):
|
def click_run_button(self):
|
||||||
logging.debug("Click on RUN button")
|
logging.debug("Click on RUN button")
|
||||||
|
@ -596,48 +416,3 @@ class CoreToolbar(object):
|
||||||
|
|
||||||
def click_two_node_button(self):
|
def click_two_node_button(self):
|
||||||
logging.debug("Click TWONODE button")
|
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
|
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.dialog import Dialog
|
||||||
from coretk.dialogs.nodeicon import IconDialog
|
from coretk.dialogs.icondialog import IconDialog
|
||||||
from coretk.widgets import CheckboxList, ListboxScroll
|
from coretk.widgets import CheckboxList, ListboxScroll
|
||||||
|
|
||||||
|
|
||||||
class ServicesSelectDialog(Dialog):
|
class ServicesSelectDialog(Dialog):
|
||||||
def __init__(self, master, app):
|
def __init__(self, master, app, current_services):
|
||||||
super().__init__(master, app, "Node Services", modal=True)
|
super().__init__(master, app, "Node Services", modal=True)
|
||||||
self.groups = None
|
self.groups = None
|
||||||
self.services = None
|
self.services = None
|
||||||
self.current = None
|
self.current = None
|
||||||
self.current_services = set()
|
self.current_services = current_services
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
@ -37,14 +41,16 @@ class ServicesSelectDialog(Dialog):
|
||||||
|
|
||||||
self.current = ListboxScroll(frame, text="Selected")
|
self.current = ListboxScroll(frame, text="Selected")
|
||||||
self.current.grid(row=0, column=2, sticky="nsew")
|
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 = tk.Frame(self)
|
||||||
frame.grid(stick="ew")
|
frame.grid(stick="ew")
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
frame.columnconfigure(i, weight=1)
|
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.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")
|
button.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
# trigger group change
|
# trigger group change
|
||||||
|
@ -69,15 +75,24 @@ class ServicesSelectDialog(Dialog):
|
||||||
for name in sorted(self.current_services):
|
for name in sorted(self.current_services):
|
||||||
self.current.listbox.insert(tk.END, name)
|
self.current.listbox.insert(tk.END, name)
|
||||||
|
|
||||||
|
def click_cancel(self):
|
||||||
|
self.current_services = None
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
class CustomNodesDialog(Dialog):
|
class CustomNodesDialog(Dialog):
|
||||||
def __init__(self, master, app):
|
def __init__(self, master, app):
|
||||||
super().__init__(master, app, "Custom Nodes", modal=True)
|
super().__init__(master, app, "Custom Nodes", modal=True)
|
||||||
self.save_button = None
|
self.edit_button = None
|
||||||
self.delete_button = None
|
self.delete_button = None
|
||||||
|
self.nodes_list = None
|
||||||
self.name = tk.StringVar()
|
self.name = tk.StringVar()
|
||||||
self.image_button = None
|
self.image_button = None
|
||||||
self.image = None
|
self.image = None
|
||||||
|
self.image_file = None
|
||||||
|
self.services = set()
|
||||||
|
self.selected = None
|
||||||
|
self.selected_index = None
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
@ -93,13 +108,11 @@ class CustomNodesDialog(Dialog):
|
||||||
frame.columnconfigure(0, weight=1)
|
frame.columnconfigure(0, weight=1)
|
||||||
frame.rowconfigure(0, weight=1)
|
frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL)
|
self.nodes_list = ListboxScroll(frame)
|
||||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
self.nodes_list.grid(row=0, column=0, sticky="nsew")
|
||||||
|
self.nodes_list.listbox.bind("<<ListboxSelect>>", self.handle_node_select)
|
||||||
listbox = tk.Listbox(frame, selectmode=tk.SINGLE, yscrollcommand=scrollbar.set)
|
for name in sorted(self.app.core.custom_nodes):
|
||||||
listbox.grid(row=0, column=0, sticky="nsew")
|
self.nodes_list.listbox.insert(tk.END, name)
|
||||||
|
|
||||||
scrollbar.config(command=listbox.yview)
|
|
||||||
|
|
||||||
frame = tk.Frame(frame)
|
frame = tk.Frame(frame)
|
||||||
frame.grid(row=0, column=2, sticky="nsew")
|
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 = tk.Button(frame, text="Create", command=self.click_create)
|
||||||
button.grid(row=0, column=0, sticky="ew")
|
button.grid(row=0, column=0, sticky="ew")
|
||||||
|
|
||||||
self.save_button = tk.Button(
|
self.edit_button = tk.Button(
|
||||||
frame, text="Save", state=tk.DISABLED, command=self.click_save
|
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(
|
self.delete_button = tk.Button(
|
||||||
frame, text="Delete", state=tk.DISABLED, command=self.click_delete
|
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 = tk.Button(frame, text="Cancel", command=self.destroy)
|
||||||
button.grid(row=0, column=1, sticky="ew")
|
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):
|
def click_icon(self):
|
||||||
dialog = IconDialog(self, self.app, self.name.get(), self.image)
|
dialog = IconDialog(self, self.app, self.name.get(), self.image)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
if dialog.image:
|
if dialog.image:
|
||||||
self.image = dialog.image
|
self.image = dialog.image
|
||||||
|
self.image_file = dialog.file_path.get()
|
||||||
self.image_button.config(image=self.image)
|
self.image_button.config(image=self.image)
|
||||||
|
|
||||||
def click_services(self):
|
def click_services(self):
|
||||||
dialog = ServicesSelectDialog(self, self.app)
|
dialog = ServicesSelectDialog(self, self.app, self.services)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
if dialog.current_services is not None:
|
||||||
def click_create(self):
|
self.services = dialog.current_services
|
||||||
pass
|
|
||||||
|
|
||||||
def click_save(self):
|
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):
|
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 tkinter import ttk
|
||||||
|
|
||||||
from coretk.dialogs.dialog import Dialog
|
from coretk.dialogs.dialog import Dialog
|
||||||
from coretk.dialogs.nodeicon import IconDialog
|
from coretk.dialogs.icondialog import IconDialog
|
||||||
from coretk.dialogs.nodeservice import NodeServicesDialog
|
from coretk.dialogs.nodeservice import NodeServicesDialog
|
||||||
|
|
||||||
NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"]
|
NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"]
|
||||||
|
|
|
@ -5,7 +5,7 @@ wlan configuration
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
|
||||||
from coretk.dialogs.dialog import Dialog
|
from coretk.dialogs.dialog import Dialog
|
||||||
from coretk.dialogs.nodeicon import IconDialog
|
from coretk.dialogs.icondialog import IconDialog
|
||||||
|
|
||||||
|
|
||||||
class WlanConfigDialog(Dialog):
|
class WlanConfigDialog(Dialog):
|
||||||
|
|
|
@ -278,7 +278,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
else:
|
else:
|
||||||
self.focus_set()
|
self.focus_set()
|
||||||
self.selected = self.get_selected(event)
|
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:
|
if self.mode == GraphMode.EDGE:
|
||||||
self.handle_edge_release(event)
|
self.handle_edge_release(event)
|
||||||
elif self.mode == GraphMode.NODE:
|
elif self.mode == GraphMode.NODE:
|
||||||
|
@ -416,6 +416,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
|
|
||||||
def add_node(self, x, y, image, node_name):
|
def add_node(self, x, y, image, node_name):
|
||||||
plot_id = self.find_all()[0]
|
plot_id = self.find_all()[0]
|
||||||
|
logging.info("add node event: %s - %s", plot_id, self.selected)
|
||||||
if self.selected == plot_id:
|
if self.selected == plot_id:
|
||||||
node = CanvasNode(
|
node = CanvasNode(
|
||||||
x=x,
|
x=x,
|
||||||
|
@ -539,13 +540,13 @@ class CanvasNode:
|
||||||
self.x_coord, self.y_coord = self.canvas.coords(self.id)
|
self.x_coord, self.y_coord = self.canvas.coords(self.id)
|
||||||
|
|
||||||
def click_press(self, event):
|
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.moving = self.canvas.canvas_xy(event)
|
||||||
|
|
||||||
self.canvas.canvas_management.node_select(self)
|
self.canvas.canvas_management.node_select(self)
|
||||||
|
|
||||||
def click_release(self, event):
|
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.update_coords()
|
||||||
self.canvas.core.update_node_location(self.id, self.x_coord, self.y_coord)
|
self.canvas.core.update_node_location(self.id, self.x_coord, self.y_coord)
|
||||||
self.moving = None
|
self.moving = None
|
||||||
|
|
|
@ -29,6 +29,10 @@ class Images:
|
||||||
def get(cls, image):
|
def get(cls, image):
|
||||||
return cls.images[image.value]
|
return cls.images[image.value]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_custom(cls, name):
|
||||||
|
return cls.images[name]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def convert_type_and_model_to_image(cls, node_type, node_model):
|
def convert_type_and_model_to_image(cls, node_type, node_model):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Add table
Reference in a new issue