Merge branch 'coretk' into coretk-config

This commit is contained in:
Huy Pham 2019-11-11 10:58:30 -08:00
commit 2cc4ef5ac0
13 changed files with 491 additions and 647 deletions

View file

@ -65,12 +65,6 @@ class Application(tk.Frame):
def draw_status(self): def draw_status(self):
self.statusbar = tk.Frame(self) self.statusbar = tk.Frame(self)
self.statusbar.pack(side=tk.BOTTOM, fill=tk.X) self.statusbar.pack(side=tk.BOTTOM, fill=tk.X)
button = tk.Button(self.statusbar, text="Button 1")
button.pack(side=tk.LEFT, padx=1)
button = tk.Button(self.statusbar, text="Button 2")
button.pack(side=tk.LEFT, padx=1)
button = tk.Button(self.statusbar, text="Button 3")
button.pack(side=tk.LEFT, padx=1)
def on_closing(self): def on_closing(self):
menu_action = MenuAction(self, self.master) menu_action = MenuAction(self, self.master)
@ -78,7 +72,8 @@ class Application(tk.Frame):
if __name__ == "__main__": if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG) log_format = "%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s"
logging.basicConfig(level=logging.DEBUG, format=log_format)
appdirs.check_directory() appdirs.check_directory()
app = Application() app = Application()
app.mainloop() app.mainloop()

View file

@ -0,0 +1,134 @@
import tkinter as tk
from tkinter import ttk
class CanvasTooltip:
"""
It creates a tooltip for a given canvas tag or id as the mouse is
above it.
This class has been derived from the original Tooltip class updated
and posted back to StackOverflow at the following link:
https://stackoverflow.com/questions/3221956/
what-is-the-simplest-way-to-make-tooltips-in-tkinter/
41079350#41079350
Alberto Vassena on 2016.12.10.
"""
def __init__(
self,
canvas,
tag_or_id,
*,
bg="#FFFFEA",
pad=(5, 3, 5, 3),
text="canvas info",
waittime=400,
wraplength=250
):
# in miliseconds, originally 500
self.waittime = waittime
# in pixels, originally 180
self.wraplength = wraplength
self.canvas = canvas
self.text = text
self.canvas.tag_bind(tag_or_id, "<Enter>", self.on_enter)
self.canvas.tag_bind(tag_or_id, "<Leave>", self.on_leave)
self.canvas.tag_bind(tag_or_id, "<ButtonPress>", self.on_leave)
self.bg = bg
self.pad = pad
self.id = None
self.tw = None
def on_enter(self, event=None):
self.schedule()
def on_leave(self, event=None):
self.unschedule()
self.hide()
def schedule(self):
self.unschedule()
self.id = self.canvas.after(self.waittime, self.show)
def unschedule(self):
id_ = self.id
self.id = None
if id_:
self.canvas.after_cancel(id_)
def show(self, event=None):
def tip_pos_calculator(canvas, label, *, tip_delta=(10, 5), pad=(5, 3, 5, 3)):
c = canvas
s_width, s_height = c.winfo_screenwidth(), c.winfo_screenheight()
width, height = (
pad[0] + label.winfo_reqwidth() + pad[2],
pad[1] + label.winfo_reqheight() + pad[3],
)
mouse_x, mouse_y = c.winfo_pointerxy()
x1, y1 = mouse_x + tip_delta[0], mouse_y + tip_delta[1]
x2, y2 = x1 + width, y1 + height
x_delta = x2 - s_width
if x_delta < 0:
x_delta = 0
y_delta = y2 - s_height
if y_delta < 0:
y_delta = 0
offscreen = (x_delta, y_delta) != (0, 0)
if offscreen:
if x_delta:
x1 = mouse_x - tip_delta[0] - width
if y_delta:
y1 = mouse_y - tip_delta[1] - height
offscreen_again = y1 < 0 # out on the top
if offscreen_again:
y1 = 0
return x1, y1
bg = self.bg
pad = self.pad
canvas = self.canvas
# creates a toplevel window
self.tw = tk.Toplevel(canvas.master)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
win = tk.Frame(self.tw, background=bg, borderwidth=0)
label = ttk.Label(
win,
text=self.text,
justify=tk.LEFT,
background=bg,
relief=tk.SOLID,
borderwidth=0,
wraplength=self.wraplength,
)
label.grid(padx=(pad[0], pad[2]), pady=(pad[1], pad[3]), sticky=tk.NSEW)
win.grid()
x, y = tip_pos_calculator(canvas, label)
self.tw.wm_geometry("+%d+%d" % (x, y))
def hide(self):
if self.tw:
self.tw.destroy()
self.tw = None

View file

@ -14,8 +14,8 @@ from coretk.mobilitynodeconfig import MobilityNodeConfig
from coretk.servicenodeconfig import ServiceNodeConfig from coretk.servicenodeconfig import ServiceNodeConfig
from coretk.wlannodeconfig import WlanNodeConfig from coretk.wlannodeconfig import WlanNodeConfig
link_layer_nodes = ["switch", "hub", "wlan", "rj45", "tunnel", "emane"] NETWORK_NODES = {"switch", "hub", "wlan", "rj45", "tunnel", "emane"}
network_layer_nodes = ["router", "host", "PC", "mdr", "prouter"] DEFAULT_NODES = {"router", "host", "PC", "mdr", "prouter"}
class Node: class Node:
@ -418,6 +418,9 @@ class CoreClient:
else: else:
return self.reusable.pop(0) return self.reusable.pop(0)
def is_model_node(self, name):
return name in DEFAULT_NODES or name in self.custom_nodes
def add_graph_node(self, session_id, canvas_id, x, y, name): def add_graph_node(self, session_id, canvas_id, x, y, name):
""" """
Add node, with information filled in, to grpc manager Add node, with information filled in, to grpc manager
@ -431,7 +434,7 @@ class CoreClient:
""" """
node_type = None node_type = None
node_model = None node_model = None
if name in link_layer_nodes: if name in NETWORK_NODES:
if name == "switch": if name == "switch":
node_type = core_pb2.NodeType.SWITCH node_type = core_pb2.NodeType.SWITCH
elif name == "hub": elif name == "hub":
@ -446,7 +449,7 @@ class CoreClient:
node_type = core_pb2.NodeType.TUNNEL node_type = core_pb2.NodeType.TUNNEL
elif name == "emane": elif name == "emane":
node_type = core_pb2.NodeType.EMANE node_type = core_pb2.NodeType.EMANE
elif name in network_layer_nodes: elif self.is_model_node(name):
node_type = core_pb2.NodeType.DEFAULT node_type = core_pb2.NodeType.DEFAULT
node_model = name node_model = name
else: else:
@ -621,7 +624,7 @@ class CoreClient:
self.interfaces_manager.new_subnet() self.interfaces_manager.new_subnet()
src_node = self.nodes[src_canvas_id] src_node = self.nodes[src_canvas_id]
if src_node.model in network_layer_nodes: if self.is_model_node(src_node.model):
ifid = len(src_node.interfaces) ifid = len(src_node.interfaces)
name = "eth" + str(ifid) name = "eth" + str(ifid)
src_interface = Interface( src_interface = Interface(
@ -635,7 +638,7 @@ class CoreClient:
) )
dst_node = self.nodes[dst_canvas_id] dst_node = self.nodes[dst_canvas_id]
if dst_node.model in network_layer_nodes: if self.is_model_node(dst_node.model):
ifid = len(dst_node.interfaces) ifid = len(dst_node.interfaces)
name = "eth" + str(ifid) name = "eth" + str(ifid)
dst_interface = Interface( dst_interface = Interface(

View file

@ -15,7 +15,7 @@ class ServicesSelectDialog(Dialog):
self.groups = None self.groups = None
self.services = None self.services = None
self.current = None self.current = None
self.current_services = current_services self.current_services = set(current_services)
self.draw() self.draw()
def draw(self): def draw(self):
@ -48,7 +48,7 @@ class ServicesSelectDialog(Dialog):
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", command=self.click_cancel) button = tk.Button(frame, text="Save", command=self.destroy)
button.grid(row=0, column=0, sticky="ew") button.grid(row=0, column=0, sticky="ew")
button = tk.Button(frame, text="Cancel", command=self.click_cancel) 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")
@ -174,7 +174,8 @@ class CustomNodesDialog(Dialog):
dialog = ServicesSelectDialog(self, self.app, self.services) dialog = ServicesSelectDialog(self, self.app, self.services)
dialog.show() dialog.show()
if dialog.current_services is not None: if dialog.current_services is not None:
self.services = dialog.current_services self.services.clear()
self.services.update(dialog.current_services)
def click_save(self): def click_save(self):
self.app.config["nodes"].clear() self.app.config["nodes"].clear()
@ -208,7 +209,7 @@ class CustomNodesDialog(Dialog):
custom_node = self.app.core.custom_nodes.pop(previous_name) custom_node = self.app.core.custom_nodes.pop(previous_name)
custom_node.name = name custom_node.name = name
custom_node.image = self.image custom_node.image = self.image
custom_node.image_file = Path(self.image_file).name custom_node.image_file = Path(self.image_file).stem
custom_node.services = self.services custom_node.services = self.services
self.app.core.custom_nodes[name] = custom_node self.app.core.custom_nodes[name] = custom_node
self.nodes_list.listbox.delete(self.selected_index) self.nodes_list.listbox.delete(self.selected_index)

View file

@ -4,6 +4,7 @@ import tkinter as tk
from core.api.grpc import core_pb2 from core.api.grpc import core_pb2
from coretk.canvasaction import CanvasAction from coretk.canvasaction import CanvasAction
from coretk.canvastooltip import CanvasTooltip
from coretk.graph_helper import GraphHelper, WlanAntennaManager from coretk.graph_helper import GraphHelper, WlanAntennaManager
from coretk.images import Images from coretk.images import Images
from coretk.interface import Interface from coretk.interface import Interface
@ -512,6 +513,7 @@ class CanvasNode:
self.canvas.tag_bind(self.id, "<Button-3>", self.context) self.canvas.tag_bind(self.id, "<Button-3>", self.context)
self.canvas.tag_bind(self.id, "<Double-Button-1>", self.double_click) self.canvas.tag_bind(self.id, "<Double-Button-1>", self.double_click)
self.canvas.tag_bind(self.id, "<Control-1>", self.select_multiple) self.canvas.tag_bind(self.id, "<Control-1>", self.select_multiple)
self.tooltip = CanvasTooltip(self.canvas, self.id, text=self.name)
self.edges = set() self.edges = set()
self.wlans = [] self.wlans = []

View file

@ -17,214 +17,6 @@ from coretk.dialogs.sessionoptions import SessionOptionsDialog
from coretk.dialogs.sessions import SessionsDialog from coretk.dialogs.sessions import SessionsDialog
def sub_menu_items():
logging.debug("Click on sub menu items")
def file_new(event=None):
logging.debug("Click file New")
def file_reload():
logging.debug("Click file Reload")
def file_export_python_script():
logging.debug("Click file export python script")
def file_execute_xml_or_python_script():
logging.debug("Execute XML or Python script")
def file_execute_python_script_with_options():
logging.debug("Click execute Python script with options")
def file_open_current_file_in_editor():
logging.debug("Click file open current in editor")
def file_print():
logging.debug("Click file Print")
def file_save_screenshot():
logging.debug("Click file save screenshot")
def edit_undo(event=None):
logging.debug("Click edit undo")
def edit_redo(event=None):
logging.debug("Click edit redo")
def edit_cut(event=None):
logging.debug("Click edit cut")
def edit_copy(event=None):
logging.debug("Click edit copy")
def edit_paste(event=None):
logging.debug("Click edit paste")
def edit_select_all(event=None):
logging.debug("Click edit select all")
def edit_select_adjacent(event=None):
logging.debug("Click edit select adjacent")
def edit_find(event=None):
logging.debug("CLick edit find")
def edit_clear_marker():
logging.debug("Click edit clear marker")
def edit_preferences():
logging.debug("Click preferences")
def canvas_new():
logging.debug("Click canvas new")
def canvas_manage():
logging.debug("Click canvas manage")
def canvas_delete():
logging.debug("Click canvas delete")
def canvas_previous(event=None):
logging.debug("Click canvas previous")
def canvas_next(event=None):
logging.debug("Click canvas next")
def canvas_first(event=None):
logging.debug("CLick canvas first")
def canvas_last(event=None):
logging.debug("CLick canvas last")
def view_show():
logging.debug("Click view show")
def view_show_hidden_nodes():
logging.debug("Click view show hidden nodes")
def view_locked():
logging.debug("Click view locked")
def view_3d_gui():
logging.debug("CLick view 3D GUI")
def view_zoom_in(event=None):
logging.debug("Click view zoom in")
def view_zoom_out(event=None):
logging.debug("Click view zoom out")
def tools_auto_rearrange_all():
logging.debug("Click tools, auto rearrange all")
def tools_auto_rearrange_selected():
logging.debug("CLick tools auto rearrange selected")
def tools_align_to_grid():
logging.debug("Click tools align to grid")
def tools_traffic():
logging.debug("Click tools traffic")
def tools_ip_addresses():
logging.debug("Click tools ip addresses")
def tools_mac_addresses():
logging.debug("Click tools mac addresses")
def tools_build_hosts_file():
logging.debug("Click tools build hosts file")
def tools_renumber_nodes():
logging.debug("Click tools renumber nodes")
def tools_experimental():
logging.debug("Click tools experimental")
def tools_topology_generator():
logging.debug("Click tools topology generator")
def tools_debugger():
logging.debug("Click tools debugger")
def widgets_observer_widgets():
logging.debug("Click widgets observer widgets")
def widgets_adjacency():
logging.debug("Click widgets adjacency")
def widgets_throughput():
logging.debug("Click widgets throughput")
def widgets_configure_adjacency():
logging.debug("Click widgets configure adjacency")
def widgets_configure_throughput():
logging.debug("Click widgets configure throughput")
def session_node_types():
logging.debug("Click session node types")
def session_comments():
logging.debug("Click session comments")
def session_reset_node_positions():
logging.debug("Click session reset node positions")
def help_about():
logging.debug("Click help About")
class MenuAction: class MenuAction:
""" """
Actions performed when choosing menu items Actions performed when choosing menu items
@ -258,7 +50,7 @@ class MenuAction:
self.app.core.stop_session() self.app.core.stop_session()
self.app.core.delete_session() self.app.core.delete_session()
def on_quit(self): def on_quit(self, event=None):
""" """
Prompt user whether so save running session, and then close the application Prompt user whether so save running session, and then close the application

View file

@ -43,52 +43,33 @@ class Menubar(tk.Menu):
:return: nothing :return: nothing
""" """
file_menu = tk.Menu(self) menu = tk.Menu(self)
file_menu.add_command( menu.add_command(label="New Session", accelerator="Ctrl+N", state=tk.DISABLED)
label="New Session", menu.add_command(
command=action.file_new, label="Open...", command=self.menuaction.file_open_xml, accelerator="Ctrl+O"
accelerator="Ctrl+N",
underline=0,
)
self.app.bind_all("<Control-n>", action.file_new)
file_menu.add_command(
label="Open...",
command=self.menuaction.file_open_xml,
accelerator="Ctrl+O",
underline=0,
) )
self.app.bind_all("<Control-o>", self.menuaction.file_open_xml) self.app.bind_all("<Control-o>", self.menuaction.file_open_xml)
file_menu.add_command(label="Reload", command=action.file_reload, underline=0) menu.add_command(label="Reload", underline=0, state=tk.DISABLED)
file_menu.add_command( menu.add_command(
label="Save", accelerator="Ctrl+S", command=self.menuaction.file_save_as_xml label="Save", accelerator="Ctrl+S", command=self.menuaction.file_save_as_xml
) )
self.app.bind_all("<Control-s>", self.menuaction.file_save_as_xml) self.app.bind_all("<Control-s>", self.menuaction.file_save_as_xml)
file_menu.add_separator() menu.add_separator()
file_menu.add_command( menu.add_command(label="Export Python script...", state=tk.DISABLED)
label="Export Python script...", command=action.file_export_python_script menu.add_command(label="Execute XML or Python script...", state=tk.DISABLED)
menu.add_command(
label="Execute Python script with options...", state=tk.DISABLED
) )
file_menu.add_command( menu.add_separator()
label="Execute XML or Python script...", menu.add_command(label="Open current file in editor...", state=tk.DISABLED)
command=action.file_execute_xml_or_python_script, menu.add_command(label="Print...", underline=0, state=tk.DISABLED)
menu.add_command(label="Save screenshot...", state=tk.DISABLED)
menu.add_separator()
menu.add_command(
label="Quit", accelerator="Ctrl+Q", command=self.menuaction.on_quit
) )
file_menu.add_command( self.app.bind_all("<Control-q>", self.menuaction.on_quit)
label="Execute Python script with options...", self.add_cascade(label="File", menu=menu)
command=action.file_execute_python_script_with_options,
)
file_menu.add_separator()
file_menu.add_command(
label="Open current file in editor...",
command=action.file_open_current_file_in_editor,
)
file_menu.add_command(label="Print...", command=action.file_print, underline=0)
file_menu.add_command(
label="Save screenshot...", command=action.file_save_screenshot
)
file_menu.add_separator()
file_menu.add_command(
label="Quit", command=self.menuaction.on_quit, underline=0
)
self.add_cascade(label="File", menu=file_menu, underline=0)
def draw_edit_menu(self): def draw_edit_menu(self):
""" """
@ -96,47 +77,23 @@ class Menubar(tk.Menu):
:return: nothing :return: nothing
""" """
edit_menu = tk.Menu(self) menu = tk.Menu(self)
edit_menu.add_command( menu.add_command(label="Undo", accelerator="Ctrl+Z", state=tk.DISABLED)
label="Undo", command=action.edit_undo, accelerator="Ctrl+Z", underline=0 menu.add_command(label="Redo", accelerator="Ctrl+Y", state=tk.DISABLED)
menu.add_separator()
menu.add_command(label="Cut", accelerator="Ctrl+X", state=tk.DISABLED)
menu.add_command(label="Copy", accelerator="Ctrl+C", state=tk.DISABLED)
menu.add_command(label="Paste", accelerator="Ctrl+V", state=tk.DISABLED)
menu.add_separator()
menu.add_command(label="Select all", accelerator="Ctrl+A", state=tk.DISABLED)
menu.add_command(
label="Select Adjacent", accelerator="Ctrl+J", state=tk.DISABLED
) )
self.app.bind_all("<Control-z>", action.edit_undo) menu.add_separator()
edit_menu.add_command( menu.add_command(label="Find...", accelerator="Ctrl+F", state=tk.DISABLED)
label="Redo", command=action.edit_redo, accelerator="Ctrl+Y", underline=0 menu.add_command(label="Clear marker", state=tk.DISABLED)
) menu.add_command(label="Preferences...", state=tk.DISABLED)
self.app.bind_all("<Control-y>", action.edit_redo) self.add_cascade(label="Edit", menu=menu)
edit_menu.add_separator()
edit_menu.add_command(
label="Cut", command=action.edit_cut, accelerator="Ctrl+X", underline=0
)
self.app.bind_all("<Control-x>", action.edit_cut)
edit_menu.add_command(
label="Copy", command=action.edit_copy, accelerator="Ctrl+C", underline=0
)
self.app.bind_all("<Control-c>", action.edit_copy)
edit_menu.add_command(
label="Paste", command=action.edit_paste, accelerator="Ctrl+V", underline=0
)
self.app.bind_all("<Control-v>", action.edit_paste)
edit_menu.add_separator()
edit_menu.add_command(
label="Select all", command=action.edit_select_all, accelerator="Ctrl+A"
)
self.app.bind_all("<Control-a>", action.edit_select_all)
edit_menu.add_command(
label="Select Adjacent",
command=action.edit_select_adjacent,
accelerator="Ctrl+J",
)
self.app.bind_all("<Control-j>", action.edit_select_adjacent)
edit_menu.add_separator()
edit_menu.add_command(
label="Find...", command=action.edit_find, accelerator="Ctrl+F", underline=0
)
self.app.bind_all("<Control-f>", action.edit_find)
edit_menu.add_command(label="Clear marker", command=action.edit_clear_marker)
edit_menu.add_command(label="Preferences...", command=action.edit_preferences)
self.add_cascade(label="Edit", menu=edit_menu, underline=0)
def draw_canvas_menu(self): def draw_canvas_menu(self):
""" """
@ -144,55 +101,23 @@ class Menubar(tk.Menu):
:return: nothing :return: nothing
""" """
canvas_menu = tk.Menu(self) menu = tk.Menu(self)
canvas_menu.add_command(label="New", command=action.canvas_new) menu.add_command(label="New", state=tk.DISABLED)
canvas_menu.add_command(label="Manage...", command=action.canvas_manage) menu.add_command(label="Manage...", state=tk.DISABLED)
canvas_menu.add_command(label="Delete", command=action.canvas_delete) menu.add_command(label="Delete", state=tk.DISABLED)
canvas_menu.add_separator() menu.add_separator()
canvas_menu.add_command( menu.add_command(
label="Size/scale...", command=self.menuaction.canvas_size_and_scale label="Size/scale...", command=self.menuaction.canvas_size_and_scale
) )
canvas_menu.add_command( menu.add_command(
label="Wallpaper...", command=self.menuaction.canvas_set_wallpaper label="Wallpaper...", command=self.menuaction.canvas_set_wallpaper
) )
canvas_menu.add_separator() menu.add_separator()
canvas_menu.add_command( menu.add_command(label="Previous", accelerator="PgUp", state=tk.DISABLED)
label="Previous", command=action.canvas_previous, accelerator="PgUp" menu.add_command(label="Next", accelerator="PgDown", state=tk.DISABLED)
) menu.add_command(label="First", accelerator="Home", state=tk.DISABLED)
self.app.bind_all("<Prior>", action.canvas_previous) menu.add_command(label="Last", accelerator="End", state=tk.DISABLED)
canvas_menu.add_command( self.add_cascade(label="Canvas", menu=menu)
label="Next", command=action.canvas_next, accelerator="PgDown"
)
self.app.bind_all("<Next>", action.canvas_next)
canvas_menu.add_command(
label="First", command=action.canvas_first, accelerator="Home"
)
self.app.bind_all("<Home>", action.canvas_first)
canvas_menu.add_command(
label="Last", command=action.canvas_last, accelerator="End"
)
self.app.bind_all("<End>", action.canvas_last)
self.add_cascade(label="Canvas", menu=canvas_menu, underline=0)
def create_show_menu(self, view_menu):
"""
Create the menu items in View/Show
:param tkinter.Menu view_menu: the view menu
:return: nothing
"""
show_menu = tk.Menu(view_menu)
show_menu.add_command(label="All", command=action.sub_menu_items)
show_menu.add_command(label="None", command=action.sub_menu_items)
show_menu.add_separator()
show_menu.add_command(label="Interface Names", command=action.sub_menu_items)
show_menu.add_command(label="IPv4 Addresses", command=action.sub_menu_items)
show_menu.add_command(label="IPv6 Addresses", command=action.sub_menu_items)
show_menu.add_command(label="Node Labels", command=action.sub_menu_items)
show_menu.add_command(label="Annotations", command=action.sub_menu_items)
show_menu.add_command(label="Grid", command=action.sub_menu_items)
show_menu.add_command(label="API Messages", command=action.sub_menu_items)
view_menu.add_cascade(label="Show", menu=show_menu)
def draw_view_menu(self): def draw_view_menu(self):
""" """
@ -202,21 +127,33 @@ class Menubar(tk.Menu):
""" """
view_menu = tk.Menu(self) view_menu = tk.Menu(self)
self.create_show_menu(view_menu) self.create_show_menu(view_menu)
view_menu.add_command( view_menu.add_command(label="Show hidden nodes", state=tk.DISABLED)
label="Show hidden nodes", command=action.view_show_hidden_nodes view_menu.add_command(label="Locked", state=tk.DISABLED)
) view_menu.add_command(label="3D GUI...", state=tk.DISABLED)
view_menu.add_command(label="Locked", command=action.view_locked)
view_menu.add_command(label="3D GUI...", command=action.view_3d_gui)
view_menu.add_separator() view_menu.add_separator()
view_menu.add_command( view_menu.add_command(label="Zoom in", accelerator="+", state=tk.DISABLED)
label="Zoom in", command=action.view_zoom_in, accelerator="+" view_menu.add_command(label="Zoom out", accelerator="-", state=tk.DISABLED)
) self.add_cascade(label="View", menu=view_menu)
self.app.bind_all("<Control-Shift-plus>", action.view_zoom_in)
view_menu.add_command( def create_show_menu(self, view_menu):
label="Zoom out", command=action.view_zoom_out, accelerator="-" """
) Create the menu items in View/Show
self.app.bind_all("<Control-minus>", action.view_zoom_out)
self.add_cascade(label="View", menu=view_menu, underline=0) :param tkinter.Menu view_menu: the view menu
:return: nothing
"""
menu = tk.Menu(view_menu)
menu.add_command(label="All", state=tk.DISABLED)
menu.add_command(label="None", state=tk.DISABLED)
menu.add_separator()
menu.add_command(label="Interface Names", state=tk.DISABLED)
menu.add_command(label="IPv4 Addresses", state=tk.DISABLED)
menu.add_command(label="IPv6 Addresses", state=tk.DISABLED)
menu.add_command(label="Node Labels", state=tk.DISABLED)
menu.add_command(label="Annotations", state=tk.DISABLED)
menu.add_command(label="Grid", state=tk.DISABLED)
menu.add_command(label="API Messages", state=tk.DISABLED)
view_menu.add_cascade(label="Show", menu=menu)
def create_experimental_menu(self, tools_menu): def create_experimental_menu(self, tools_menu):
""" """
@ -225,19 +162,11 @@ class Menubar(tk.Menu):
:param tkinter.Menu tools_menu: tools menu :param tkinter.Menu tools_menu: tools menu
:return: nothing :return: nothing
""" """
experimental_menu = tk.Menu(tools_menu) menu = tk.Menu(tools_menu)
experimental_menu.add_command( menu.add_command(label="Plugins...", state=tk.DISABLED)
label="Plugins...", command=action.sub_menu_items, underline=0 menu.add_command(label="ns2immunes converter...", state=tk.DISABLED)
) menu.add_command(label="Topology partitioning...", state=tk.DISABLED)
experimental_menu.add_command( tools_menu.add_cascade(label="Experimental", menu=menu)
label="ns2immunes converter...", command=action.sub_menu_items, underline=0
)
experimental_menu.add_command(
label="Topology partitioning...", command=action.sub_menu_items
)
tools_menu.add_cascade(
label="Experimental", menu=experimental_menu, underline=0
)
def create_random_menu(self, topology_generator_menu): def create_random_menu(self, topology_generator_menu):
""" """
@ -246,15 +175,13 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology generator menu :param tkinter.Menu topology_generator_menu: topology generator menu
:return: nothing :return: nothing
""" """
random_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
# list of number of random nodes to create # list of number of random nodes to create
nums = [1, 5, 10, 15, 20, 30, 40, 50, 75, 100] nums = [1, 5, 10, 15, 20, 30, 40, 50, 75, 100]
for i in nums: for i in nums:
the_label = "R(" + str(i) + ")" label = f"R({i})"
random_menu.add_command(label=the_label, command=action.sub_menu_items) menu.add_command(label=label, state=tk.DISABLED)
topology_generator_menu.add_cascade( topology_generator_menu.add_cascade(label="Random", menu=menu)
label="Random", menu=random_menu, underline=0
)
def create_grid_menu(self, topology_generator_menu): def create_grid_menu(self, topology_generator_menu):
""" """
@ -263,13 +190,13 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology_generator_menu :param tkinter.Menu topology_generator_menu: topology_generator_menu
:return: nothing :return: nothing
""" """
grid_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
# list of number of nodes to create # list of number of nodes to create
nums = [1, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 100] nums = [1, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 100]
for i in nums: for i in nums:
the_label = "G(" + str(i) + ")" label = f"G({i})"
grid_menu.add_command(label=the_label, command=action.sub_menu_items) menu.add_command(label=label, state=tk.DISABLED)
topology_generator_menu.add_cascade(label="Grid", menu=grid_menu, underline=0) topology_generator_menu.add_cascade(label="Grid", menu=menu)
def create_connected_grid_menu(self, topology_generator_menu): def create_connected_grid_menu(self, topology_generator_menu):
""" """
@ -278,17 +205,15 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology generator menu :param tkinter.Menu topology_generator_menu: topology generator menu
:return: nothing :return: nothing
""" """
grid_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
for i in range(1, 11, 1): for i in range(1, 11, 1):
i_n_menu = tk.Menu(grid_menu) submenu = tk.Menu(menu)
for j in range(1, 11, 1): for j in range(1, 11, 1):
i_j_label = str(i) + " X " + str(j) label = f"{i} X {j}"
i_n_menu.add_command(label=i_j_label, command=action.sub_menu_items) submenu.add_command(label=label, state=tk.DISABLED)
i_n_label = str(i) + " X N" label = str(i) + " X N"
grid_menu.add_cascade(label=i_n_label, menu=i_n_menu) menu.add_cascade(label=label, menu=submenu)
topology_generator_menu.add_cascade( topology_generator_menu.add_cascade(label="Connected Grid", menu=menu)
label="Connected Grid", menu=grid_menu, underline=0
)
def create_chain_menu(self, topology_generator_menu): def create_chain_menu(self, topology_generator_menu):
""" """
@ -297,13 +222,13 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology generator menu :param tkinter.Menu topology_generator_menu: topology generator menu
:return: nothing :return: nothing
""" """
chain_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
# number of nodes to create # number of nodes to create
nums = list(range(2, 25, 1)) + [32, 64, 128] nums = list(range(2, 25, 1)) + [32, 64, 128]
for i in nums: for i in nums:
the_label = "P(" + str(i) + ")" label = f"P({i})"
chain_menu.add_command(label=the_label, command=action.sub_menu_items) menu.add_command(label=label, state=tk.DISABLED)
topology_generator_menu.add_cascade(label="Chain", menu=chain_menu, underline=0) topology_generator_menu.add_cascade(label="Chain", menu=menu)
def create_star_menu(self, topology_generator_menu): def create_star_menu(self, topology_generator_menu):
""" """
@ -312,11 +237,11 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology generator menu :param tkinter.Menu topology_generator_menu: topology generator menu
:return: nothing :return: nothing
""" """
star_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
for i in range(3, 26, 1): for i in range(3, 26, 1):
the_label = "C(" + str(i) + ")" label = f"C({i})"
star_menu.add_command(label=the_label, command=action.sub_menu_items) menu.add_command(label=label, state=tk.DISABLED)
topology_generator_menu.add_cascade(label="Star", menu=star_menu, underline=0) topology_generator_menu.add_cascade(label="Star", menu=menu)
def create_cycle_menu(self, topology_generator_menu): def create_cycle_menu(self, topology_generator_menu):
""" """
@ -325,11 +250,11 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology generator menu :param tkinter.Menu topology_generator_menu: topology generator menu
:return: nothing :return: nothing
""" """
cycle_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
for i in range(3, 25, 1): for i in range(3, 25, 1):
the_label = "C(" + str(i) + ")" label = f"C({i})"
cycle_menu.add_command(label=the_label, command=action.sub_menu_items) menu.add_command(label=label, state=tk.DISABLED)
topology_generator_menu.add_cascade(label="Cycle", menu=cycle_menu, underline=0) topology_generator_menu.add_cascade(label="Cycle", menu=menu)
def create_wheel_menu(self, topology_generator_menu): def create_wheel_menu(self, topology_generator_menu):
""" """
@ -338,11 +263,11 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology generator menu :param tkinter.Menu topology_generator_menu: topology generator menu
:return: nothing :return: nothing
""" """
wheel_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
for i in range(4, 26, 1): for i in range(4, 26, 1):
the_label = "W(" + str(i) + ")" label = f"W({i})"
wheel_menu.add_command(label=the_label, command=action.sub_menu_items) menu.add_command(label=label, state=tk.DISABLED)
topology_generator_menu.add_cascade(label="Wheel", menu=wheel_menu, underline=0) topology_generator_menu.add_cascade(label="Wheel", menu=menu)
def create_cube_menu(self, topology_generator_menu): def create_cube_menu(self, topology_generator_menu):
""" """
@ -351,11 +276,11 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology generator menu :param tkinter.Menu topology_generator_menu: topology generator menu
:return: nothing :return: nothing
""" """
cube_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
for i in range(2, 7, 1): for i in range(2, 7, 1):
the_label = "Q(" + str(i) + ")" label = f"Q({i})"
cube_menu.add_command(label=the_label, command=action.sub_menu_items) menu.add_command(label=label, state=tk.DISABLED)
topology_generator_menu.add_cascade(label="Cube", menu=cube_menu, underline=0) topology_generator_menu.add_cascade(label="Cube", menu=menu)
def create_clique_menu(self, topology_generator_menu): def create_clique_menu(self, topology_generator_menu):
""" """
@ -364,13 +289,11 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology generator menu :param tkinter.Menu topology_generator_menu: topology generator menu
:return: nothing :return: nothing
""" """
clique_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
for i in range(3, 25, 1): for i in range(3, 25, 1):
the_label = "K(" + str(i) + ")" label = f"K({i})"
clique_menu.add_command(label=the_label, command=action.sub_menu_items) menu.add_command(label=label, state=tk.DISABLED)
topology_generator_menu.add_cascade( topology_generator_menu.add_cascade(label="Clique", menu=menu)
label="Clique", menu=clique_menu, underline=0
)
def create_bipartite_menu(self, topology_generator_menu): def create_bipartite_menu(self, topology_generator_menu):
""" """
@ -379,19 +302,17 @@ class Menubar(tk.Menu):
:param tkinter.Menu topology_generator_menu: topology_generator_menu :param tkinter.Menu topology_generator_menu: topology_generator_menu
:return: nothing :return: nothing
""" """
bipartite_menu = tk.Menu(topology_generator_menu) menu = tk.Menu(topology_generator_menu)
temp = 24 temp = 24
for i in range(1, 13, 1): for i in range(1, 13, 1):
i_n_menu = tk.Menu(bipartite_menu) submenu = tk.Menu(menu)
for j in range(i, temp, 1): for j in range(i, temp, 1):
i_j_label = "K(" + str(i) + " X " + str(j) + ")" label = f"K({i} X {j})"
i_n_menu.add_command(label=i_j_label, command=action.sub_menu_items) submenu.add_command(label=label, state=tk.DISABLED)
i_n_label = "K(" + str(i) + " X N)" label = f"K({i})"
bipartite_menu.add_cascade(label=i_n_label, menu=i_n_menu) menu.add_cascade(label=label, menu=submenu)
temp = temp - 1 temp = temp - 1
topology_generator_menu.add_cascade( topology_generator_menu.add_cascade(label="Bipartite", menu=menu)
label="Bipartite", menu=bipartite_menu, underline=0
)
def create_topology_generator_menu(self, tools_menu): def create_topology_generator_menu(self, tools_menu):
""" """
@ -401,20 +322,18 @@ class Menubar(tk.Menu):
:return: nothing :return: nothing
""" """
topology_generator_menu = tk.Menu(tools_menu) menu = tk.Menu(tools_menu)
self.create_random_menu(topology_generator_menu) self.create_random_menu(menu)
self.create_grid_menu(topology_generator_menu) self.create_grid_menu(menu)
self.create_connected_grid_menu(topology_generator_menu) self.create_connected_grid_menu(menu)
self.create_chain_menu(topology_generator_menu) self.create_chain_menu(menu)
self.create_star_menu(topology_generator_menu) self.create_star_menu(menu)
self.create_cycle_menu(topology_generator_menu) self.create_cycle_menu(menu)
self.create_wheel_menu(topology_generator_menu) self.create_wheel_menu(menu)
self.create_cube_menu(topology_generator_menu) self.create_cube_menu(menu)
self.create_clique_menu(topology_generator_menu) self.create_clique_menu(menu)
self.create_bipartite_menu(topology_generator_menu) self.create_bipartite_menu(menu)
tools_menu.add_cascade( tools_menu.add_cascade(label="Topology generator", menu=menu)
label="Topology generator", menu=topology_generator_menu, underline=0
)
def draw_tools_menu(self): def draw_tools_menu(self):
""" """
@ -422,41 +341,21 @@ class Menubar(tk.Menu):
:return: nothing :return: nothing
""" """
tools_menu = tk.Menu(self) menu = tk.Menu(self)
tools_menu.add_command( menu.add_command(label="Auto rearrange all", state=tk.DISABLED)
label="Auto rearrange all", menu.add_command(label="Auto rearrange selected", state=tk.DISABLED)
command=action.tools_auto_rearrange_all, menu.add_separator()
underline=0, menu.add_command(label="Align to grid", state=tk.DISABLED)
) menu.add_separator()
tools_menu.add_command( menu.add_command(label="Traffic...", state=tk.DISABLED)
label="Auto rearrange selected", menu.add_command(label="IP addresses...", state=tk.DISABLED)
command=action.tools_auto_rearrange_selected, menu.add_command(label="MAC addresses...", state=tk.DISABLED)
underline=0, menu.add_command(label="Build hosts file...", state=tk.DISABLED)
) menu.add_command(label="Renumber nodes...", state=tk.DISABLED)
tools_menu.add_separator() self.create_experimental_menu(menu)
tools_menu.add_command( self.create_topology_generator_menu(menu)
label="Align to grid", command=action.tools_align_to_grid, underline=0 menu.add_command(label="Debugger...", state=tk.DISABLED)
) self.add_cascade(label="Tools", menu=menu)
tools_menu.add_separator()
tools_menu.add_command(label="Traffic...", command=action.tools_traffic)
tools_menu.add_command(
label="IP addresses...", command=action.tools_ip_addresses, underline=0
)
tools_menu.add_command(
label="MAC addresses...", command=action.tools_mac_addresses, underline=0
)
tools_menu.add_command(
label="Build hosts file...",
command=action.tools_build_hosts_file,
underline=0,
)
tools_menu.add_command(
label="Renumber nodes...", command=action.tools_renumber_nodes, underline=0
)
self.create_experimental_menu(tools_menu)
self.create_topology_generator_menu(tools_menu)
tools_menu.add_command(label="Debugger...", command=action.tools_debugger)
self.add_cascade(label="Tools", menu=tools_menu, underline=0)
def create_observer_widgets_menu(self, widget_menu): def create_observer_widgets_menu(self, widget_menu):
""" """
@ -465,54 +364,24 @@ class Menubar(tk.Menu):
:param tkinter.Menu widget_menu: widget_menu :param tkinter.Menu widget_menu: widget_menu
:return: nothing :return: nothing
""" """
observer_widget_menu = tk.Menu(widget_menu) menu = tk.Menu(widget_menu)
observer_widget_menu.add_command(label="None", command=action.sub_menu_items) menu.add_command(label="None", state=tk.DISABLED)
observer_widget_menu.add_command( menu.add_command(label="processes", state=tk.DISABLED)
label="processes", command=action.sub_menu_items menu.add_command(label="ifconfig", state=tk.DISABLED)
) menu.add_command(label="IPv4 routes", state=tk.DISABLED)
observer_widget_menu.add_command( menu.add_command(label="IPv6 routes", state=tk.DISABLED)
label="ifconfig", command=action.sub_menu_items menu.add_command(label="OSPFv2 neighbors", state=tk.DISABLED)
) menu.add_command(label="OSPFv3 neighbors", state=tk.DISABLED)
observer_widget_menu.add_command( menu.add_command(label="Listening sockets", state=tk.DISABLED)
label="IPv4 routes", command=action.sub_menu_items menu.add_command(label="IPv4 MFC entries", state=tk.DISABLED)
) menu.add_command(label="IPv6 MFC entries", state=tk.DISABLED)
observer_widget_menu.add_command( menu.add_command(label="firewall rules", state=tk.DISABLED)
label="IPv6 routes", command=action.sub_menu_items menu.add_command(label="IPsec policies", state=tk.DISABLED)
) menu.add_command(label="docker logs", state=tk.DISABLED)
observer_widget_menu.add_command( menu.add_command(label="OSPFv3 MDR level", state=tk.DISABLED)
label="OSPFv2 neighbors", command=action.sub_menu_items menu.add_command(label="PIM neighbors", state=tk.DISABLED)
) menu.add_command(label="Edit...", command=self.menuaction.edit_observer_widgets)
observer_widget_menu.add_command( widget_menu.add_cascade(label="Observer Widgets", menu=menu)
label="OSPFv3 neighbors", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="Listening sockets", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="IPv4 MFC entries", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="IPv6 MFC entries", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="firewall rules", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="IPsec policies", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="docker logs", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="OSPFv3 MDR level", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="PIM neighbors", command=action.sub_menu_items
)
observer_widget_menu.add_command(
label="Edit...", command=self.menuaction.edit_observer_widgets
)
widget_menu.add_cascade(label="Observer Widgets", menu=observer_widget_menu)
def create_adjacency_menu(self, widget_menu): def create_adjacency_menu(self, widget_menu):
""" """
@ -521,12 +390,12 @@ class Menubar(tk.Menu):
:param tkinter.Menu widget_menu: widget menu :param tkinter.Menu widget_menu: widget menu
:return: nothing :return: nothing
""" """
adjacency_menu = tk.Menu(widget_menu) menu = tk.Menu(widget_menu)
adjacency_menu.add_command(label="OSPFv2", command=action.sub_menu_items) menu.add_command(label="OSPFv2", state=tk.DISABLED)
adjacency_menu.add_command(label="OSPFv3", command=action.sub_menu_items) menu.add_command(label="OSPFv3", state=tk.DISABLED)
adjacency_menu.add_command(label="OSLR", command=action.sub_menu_items) menu.add_command(label="OSLR", state=tk.DISABLED)
adjacency_menu.add_command(label="OSLRv2", command=action.sub_menu_items) menu.add_command(label="OSLRv2", state=tk.DISABLED)
widget_menu.add_cascade(label="Adjacency", menu=adjacency_menu) widget_menu.add_cascade(label="Adjacency", menu=menu)
def draw_widgets_menu(self): def draw_widgets_menu(self):
""" """
@ -534,18 +403,14 @@ class Menubar(tk.Menu):
:return: nothing :return: nothing
""" """
widget_menu = tk.Menu(self) menu = tk.Menu(self)
self.create_observer_widgets_menu(widget_menu) self.create_observer_widgets_menu(menu)
self.create_adjacency_menu(widget_menu) self.create_adjacency_menu(menu)
widget_menu.add_command(label="Throughput", command=action.widgets_throughput) menu.add_command(label="Throughput", state=tk.DISABLED)
widget_menu.add_separator() menu.add_separator()
widget_menu.add_command( menu.add_command(label="Configure Adjacency...", state=tk.DISABLED)
label="Configure Adjacency...", command=action.widgets_configure_adjacency menu.add_command(label="Configure Throughput...", state=tk.DISABLED)
) self.add_cascade(label="Widgets", menu=menu)
widget_menu.add_command(
label="Configure Throughput...", command=action.widgets_configure_throughput
)
self.add_cascade(label="Widgets", menu=widget_menu, underline=0)
def draw_session_menu(self): def draw_session_menu(self):
""" """
@ -553,36 +418,21 @@ class Menubar(tk.Menu):
:return: nothing :return: nothing
""" """
session_menu = tk.Menu(self) menu = tk.Menu(self)
session_menu.add_command( menu.add_command(
label="Change sessions...", label="Change sessions...",
command=self.menuaction.session_change_sessions, command=self.menuaction.session_change_sessions,
underline=0, underline=0,
) )
session_menu.add_separator() menu.add_separator()
session_menu.add_command( menu.add_command(label="Comments...", state=tk.DISABLED)
label="Node types...", command=action.session_node_types, underline=0 menu.add_command(label="Hooks...", command=self.menuaction.session_hooks)
menu.add_command(label="Reset node positions", state=tk.DISABLED)
menu.add_command(
label="Emulation servers...", command=self.menuaction.session_servers
) )
session_menu.add_command( menu.add_command(label="Options...", command=self.menuaction.session_options)
label="Comments...", command=action.session_comments, underline=0 self.add_cascade(label="Session", menu=menu)
)
session_menu.add_command(
label="Hooks...", command=self.menuaction.session_hooks, underline=0
)
session_menu.add_command(
label="Reset node positions",
command=action.session_reset_node_positions,
underline=0,
)
session_menu.add_command(
label="Emulation servers...",
command=self.menuaction.session_servers,
underline=0,
)
session_menu.add_command(
label="Options...", command=self.menuaction.session_options, underline=0
)
self.add_cascade(label="Session", menu=session_menu, underline=0)
def draw_help_menu(self): def draw_help_menu(self):
""" """
@ -590,13 +440,13 @@ class Menubar(tk.Menu):
:return: nothing :return: nothing
""" """
help_menu = tk.Menu(self) menu = tk.Menu(self)
help_menu.add_command( menu.add_command(
label="Core Github (www)", command=self.menuaction.help_core_github label="Core Github (www)", command=self.menuaction.help_core_github
) )
help_menu.add_command( menu.add_command(
label="Core Documentation (www)", label="Core Documentation (www)",
command=self.menuaction.help_core_documentation, command=self.menuaction.help_core_documentation,
) )
help_menu.add_command(label="About", command=action.help_about) menu.add_command(label="About", state=tk.DISABLED)
self.add_cascade(label="Help", menu=help_menu) self.add_cascade(label="Help", menu=menu)

View file

@ -83,8 +83,8 @@ class Toolbar(tk.Frame):
"link tool", "link tool",
) )
self.create_node_button() self.create_node_button()
self.create_link_layer_button() self.create_network_button()
self.create_marker_button() self.create_annotation_button()
self.radio_value.set(1) self.radio_value.set(1)
def draw_runtime_frame(self): def draw_runtime_frame(self):
@ -145,15 +145,23 @@ class Toolbar(tk.Frame):
(ImageEnum.PC, "PC"), (ImageEnum.PC, "PC"),
(ImageEnum.MDR, "mdr"), (ImageEnum.MDR, "mdr"),
(ImageEnum.PROUTER, "prouter"), (ImageEnum.PROUTER, "prouter"),
(ImageEnum.EDITNODE, "custom node types"),
] ]
# draw default nodes
for image_enum, tooltip in nodes: for image_enum, tooltip in nodes:
self.create_button( image = Images.get(image_enum)
Images.get(image_enum), func = partial(self.update_button, self.node_button, image, tooltip)
partial(self.update_button, self.node_button, image_enum, tooltip), self.create_button(image, func, self.node_picker, tooltip)
self.node_picker, # draw custom nodes
tooltip, for name in sorted(self.app.core.custom_nodes):
) custom_node = self.app.core.custom_nodes[name]
image = custom_node.image
func = partial(self.update_button, self.node_button, image, name)
self.create_button(image, func, self.node_picker, name)
# draw edit node
image = Images.get(ImageEnum.EDITNODE)
self.create_button(
image, self.click_edit_node, self.node_picker, "custom nodes"
)
self.show_picker(self.node_button, self.node_picker) self.show_picker(self.node_button, self.node_picker)
def show_picker(self, button, picker): def show_picker(self, button, picker):
@ -161,22 +169,24 @@ class Toolbar(tk.Frame):
x = button.winfo_rootx() - first_button.winfo_rootx() + 40 x = button.winfo_rootx() - first_button.winfo_rootx() + 40
y = button.winfo_rooty() - first_button.winfo_rooty() - 1 y = button.winfo_rooty() - first_button.winfo_rooty() - 1
picker.place(x=x, y=y) picker.place(x=x, y=y)
self.app.bind_all("<Button-1>", lambda e: self.hide_pickers()) self.app.bind_all("<ButtonRelease-1>", lambda e: self.hide_pickers())
picker.wait_visibility()
picker.grab_set()
self.wait_window(picker) self.wait_window(picker)
self.app.unbind_all("<Button-1>") self.app.unbind_all("<ButtonRelease-1>")
def create_button(self, img, func, frame, tooltip): def create_button(self, image, func, frame, tooltip):
""" """
Create button and put it on the frame Create button and put it on the frame
:param PIL.Image img: button image :param PIL.Image image: button image
:param func: the command that is executed when button is clicked :param func: the command that is executed when button is clicked
:param tkinter.Frame frame: frame that contains the button :param tkinter.Frame frame: frame that contains the button
:param str tooltip: tooltip text :param str tooltip: tooltip text
: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=image)
button.bind("<Button-1>", lambda e: func()) button.bind("<ButtonRelease-1>", lambda e: func())
button.grid(pady=1) button.grid(pady=1)
CreateToolTip(button, tooltip) CreateToolTip(button, tooltip)
@ -221,19 +231,18 @@ class Toolbar(tk.Frame):
logging.debug("Click LINK button") logging.debug("Click LINK button")
self.app.canvas.mode = GraphMode.EDGE self.app.canvas.mode = GraphMode.EDGE
def update_button(self, button, image_enum, name): def click_edit_node(self):
logging.info("update button(%s): %s, %s", button, image_enum, name)
self.hide_pickers() self.hide_pickers()
if image_enum == ImageEnum.EDITNODE: dialog = CustomNodesDialog(self.app, self.app)
dialog = CustomNodesDialog(self.app, self.app) dialog.show()
dialog.show()
else: def update_button(self, button, image, name):
image = Images.get(image_enum) logging.info("update button(%s): %s", button, name)
logging.info("updating button(%s): %s", button, name) self.hide_pickers()
button.configure(image=image) button.configure(image=image)
self.app.canvas.mode = GraphMode.NODE self.app.canvas.mode = GraphMode.NODE
self.app.canvas.draw_node_image = image self.app.canvas.draw_node_image = image
self.app.canvas.draw_node_name = name self.app.canvas.draw_node_name = name
def hide_pickers(self): def hide_pickers(self):
logging.info("hiding pickers") logging.info("hiding pickers")
@ -262,8 +271,8 @@ class Toolbar(tk.Frame):
width=self.width, width=self.width,
height=self.height, height=self.height,
image=router_image, image=router_image,
command=self.draw_node_picker,
) )
self.node_button.bind("<ButtonRelease-1>", lambda e: self.draw_node_picker())
self.node_button.grid() self.node_button.grid()
CreateToolTip(self.node_button, "Network-layer virtual nodes") CreateToolTip(self.node_button, "Network-layer virtual nodes")
@ -285,15 +294,16 @@ class Toolbar(tk.Frame):
(ImageEnum.TUNNEL, "tunnel", "tunnel tool"), (ImageEnum.TUNNEL, "tunnel", "tunnel tool"),
] ]
for image_enum, name, tooltip in nodes: for image_enum, name, tooltip in nodes:
image = Images.get(image_enum)
self.create_button( self.create_button(
Images.get(image_enum), image,
partial(self.update_button, self.network_button, image_enum, name), partial(self.update_button, self.network_button, image, name),
self.network_picker, self.network_picker,
tooltip, tooltip,
) )
self.show_picker(self.network_button, self.network_picker) self.show_picker(self.network_button, self.network_picker)
def create_link_layer_button(self): def create_network_button(self):
""" """
Create link-layer node button and the options that represent different link-layer node types Create link-layer node button and the options that represent different link-layer node types
@ -308,7 +318,9 @@ class Toolbar(tk.Frame):
width=self.width, width=self.width,
height=self.height, height=self.height,
image=hub_image, image=hub_image,
command=self.draw_network_picker, )
self.network_button.bind(
"<ButtonRelease-1>", lambda e: self.draw_network_picker()
) )
self.network_button.grid() self.network_button.grid()
CreateToolTip(self.network_button, "link-layer nodes") CreateToolTip(self.network_button, "link-layer nodes")
@ -337,7 +349,7 @@ class Toolbar(tk.Frame):
) )
self.show_picker(self.annotation_button, self.annotation_picker) self.show_picker(self.annotation_button, self.annotation_picker)
def create_marker_button(self): def create_annotation_button(self):
""" """
Create marker button and options that represent different marker types Create marker button and options that represent different marker types
@ -352,7 +364,9 @@ class Toolbar(tk.Frame):
width=self.width, width=self.width,
height=self.height, height=self.height,
image=marker_image, image=marker_image,
command=self.draw_annotation_picker, )
self.annotation_button.bind(
"<ButtonRelease-1>", lambda e: self.draw_annotation_picker()
) )
self.annotation_button.grid() self.annotation_button.grid()
CreateToolTip(self.annotation_button, "background annotation tools") CreateToolTip(self.annotation_button, "background annotation tools")

View file

@ -159,6 +159,8 @@ class CoreGrpcClient:
emane_model_configs=None, emane_model_configs=None,
wlan_configs=None, wlan_configs=None,
mobility_configs=None, mobility_configs=None,
service_configs=None,
service_file_configs=None,
): ):
""" """
Start a session. Start a session.
@ -169,9 +171,11 @@ class CoreGrpcClient:
:param core_pb2.SessionLocation location: location to set :param core_pb2.SessionLocation location: location to set
:param list[core_pb2.Hook] hooks: session hooks to set :param list[core_pb2.Hook] hooks: session hooks to set
:param dict emane_config: emane configuration to set :param dict emane_config: emane configuration to set
:param list emane_model_configs: emane model configurations to set :param list emane_model_configs: node emane model configurations
:param list wlan_configs: wlan configurations to set :param list wlan_configs: node wlan configurations
:param list mobility_configs: mobility configurations to set :param list mobility_configs: node mobility configurations
:param list service_configs: node service configurations
:param list service_file_configs: node service file configurations
:return: start session response :return: start session response
:rtype: core_pb2.StartSessionResponse :rtype: core_pb2.StartSessionResponse
""" """
@ -185,6 +189,8 @@ class CoreGrpcClient:
emane_model_configs=emane_model_configs, emane_model_configs=emane_model_configs,
wlan_configs=wlan_configs, wlan_configs=wlan_configs,
mobility_configs=mobility_configs, mobility_configs=mobility_configs,
service_configs=service_configs,
service_file_configs=service_file_configs,
) )
return self.stub.StartSession(request) return self.stub.StartSession(request)
@ -768,14 +774,14 @@ class CoreGrpcClient:
:rtype: core_pb2.SetNodeServiceResponse :rtype: core_pb2.SetNodeServiceResponse
:raises grpc.RpcError: when session or node doesn't exist :raises grpc.RpcError: when session or node doesn't exist
""" """
request = core_pb2.SetNodeServiceRequest( config = core_pb2.ServiceConfig(
session_id=session_id,
node_id=node_id, node_id=node_id,
service=service, service=service,
startup=startup, startup=startup,
validate=validate, validate=validate,
shutdown=shutdown, shutdown=shutdown,
) )
request = core_pb2.SetNodeServiceRequest(session_id=session_id, config=config)
return self.stub.SetNodeService(request) return self.stub.SetNodeService(request)
def set_node_service_file(self, session_id, node_id, service, file_name, data): def set_node_service_file(self, session_id, node_id, service, file_name, data):
@ -791,12 +797,11 @@ class CoreGrpcClient:
:rtype: core_pb2.SetNodeServiceFileResponse :rtype: core_pb2.SetNodeServiceFileResponse
:raises grpc.RpcError: when session or node doesn't exist :raises grpc.RpcError: when session or node doesn't exist
""" """
config = core_pb2.ServiceFileConfig(
node_id=node_id, service=service, file=file_name, data=data
)
request = core_pb2.SetNodeServiceFileRequest( request = core_pb2.SetNodeServiceFileRequest(
session_id=session_id, session_id=session_id, config=config
node_id=node_id,
service=service,
file=file_name,
data=data,
) )
return self.stub.SetNodeServiceFile(request) return self.stub.SetNodeServiceFile(request)

View file

@ -319,3 +319,18 @@ def session_location(session, location):
session.location.refxyz = (location.x, location.y, location.z) session.location.refxyz = (location.x, location.y, location.z)
session.location.setrefgeo(location.lat, location.lon, location.alt) session.location.setrefgeo(location.lat, location.lon, location.alt)
session.location.refscale = location.scale session.location.refscale = location.scale
def service_configuration(session, config):
"""
Convenience method for setting a node service configuration.
:param core.emulator.session.Session session: session for service configuration
:param core_pb2.ServiceConfig config: service configuration
:return:
"""
session.services.set_service(config.node_id, config.service)
service = session.services.get_service(config.node_id, config.service)
service.startup = tuple(config.startup)
service.validate = tuple(config.validate)
service.shutdown = tuple(config.shutdown)

View file

@ -153,6 +153,16 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
config.node_id, Ns2ScriptedMobility.name, config.config config.node_id, Ns2ScriptedMobility.name, config.config
) )
# service configs
for config in request.service_configs:
grpcutils.service_configuration(session, config)
# service file configs
for config in request.service_file_configs:
session.services.set_service_file(
config.node_id, config.service, config.file, config.data
)
# create links # create links
grpcutils.create_links(session, request.links) grpcutils.create_links(session, request.links)
@ -1172,11 +1182,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
""" """
logging.debug("set node service: %s", request) logging.debug("set node service: %s", request)
session = self.get_session(request.session_id, context) session = self.get_session(request.session_id, context)
session.services.set_service(request.node_id, request.service) config = request.config
service = session.services.get_service(request.node_id, request.service) grpcutils.service_configuration(session, config)
service.startup = tuple(request.startup)
service.validate = tuple(request.validate)
service.shutdown = tuple(request.shutdown)
return core_pb2.SetNodeServiceResponse(result=True) return core_pb2.SetNodeServiceResponse(result=True)
def SetNodeServiceFile(self, request, context): def SetNodeServiceFile(self, request, context):
@ -1191,8 +1198,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
""" """
logging.debug("set node service file: %s", request) logging.debug("set node service file: %s", request)
session = self.get_session(request.session_id, context) session = self.get_session(request.session_id, context)
config = request.config
session.services.set_service_file( session.services.set_service_file(
request.node_id, request.service, request.file, request.data config.node_id, config.service, config.file, config.data
) )
return core_pb2.SetNodeServiceFileResponse(result=True) return core_pb2.SetNodeServiceFileResponse(result=True)

View file

@ -144,6 +144,8 @@ message StartSessionRequest {
repeated WlanConfig wlan_configs = 7; repeated WlanConfig wlan_configs = 7;
repeated EmaneModelConfig emane_model_configs = 8; repeated EmaneModelConfig emane_model_configs = 8;
repeated MobilityConfig mobility_configs = 9; repeated MobilityConfig mobility_configs = 9;
repeated ServiceConfig service_configs = 10;
repeated ServiceFileConfig service_file_configs = 11;
} }
message StartSessionResponse { message StartSessionResponse {
@ -554,11 +556,7 @@ message GetNodeServiceFileResponse {
message SetNodeServiceRequest { message SetNodeServiceRequest {
int32 session_id = 1; int32 session_id = 1;
int32 node_id = 2; ServiceConfig config = 2;
string service = 3;
repeated string startup = 4;
repeated string validate = 5;
repeated string shutdown = 6;
} }
message SetNodeServiceResponse { message SetNodeServiceResponse {
@ -567,10 +565,7 @@ message SetNodeServiceResponse {
message SetNodeServiceFileRequest { message SetNodeServiceFileRequest {
int32 session_id = 1; int32 session_id = 1;
int32 node_id = 2; ServiceFileConfig config = 2;
string service = 3;
string file = 4;
string data = 5;
} }
message SetNodeServiceFileResponse { message SetNodeServiceFileResponse {
@ -718,6 +713,21 @@ message EmaneModelConfig {
map<string, string> config = 4; map<string, string> config = 4;
} }
message ServiceConfig {
int32 node_id = 1;
string service = 2;
repeated string startup = 3;
repeated string validate = 4;
repeated string shutdown = 5;
}
message ServiceFileConfig {
int32 node_id = 1;
string service = 2;
string file = 3;
string data = 4;
}
message MessageType { message MessageType {
enum Enum { enum Enum {
NONE = 0; NONE = 0;

View file

@ -27,7 +27,6 @@ class TestGrpc:
# given # given
client = CoreGrpcClient() client = CoreGrpcClient()
session = grpc_server.coreemu.create_session() session = grpc_server.coreemu.create_session()
nodes = []
position = core_pb2.Position(x=50, y=100) position = core_pb2.Position(x=50, y=100)
node_one = core_pb2.Node(id=1, position=position, model="PC") node_one = core_pb2.Node(id=1, position=position, model="PC")
position = core_pb2.Position(x=100, y=100) position = core_pb2.Position(x=100, y=100)
@ -36,8 +35,7 @@ class TestGrpc:
wlan_node = core_pb2.Node( wlan_node = core_pb2.Node(
id=3, type=NodeTypes.WIRELESS_LAN.value, position=position id=3, type=NodeTypes.WIRELESS_LAN.value, position=position
) )
nodes.extend([node_one, node_two, wlan_node]) nodes = [node_one, node_two, wlan_node]
links = []
interface_helper = InterfaceHelper(ip4_prefix="10.83.0.0/16") interface_helper = InterfaceHelper(ip4_prefix="10.83.0.0/16")
interface_one = interface_helper.create_interface(node_one.id, 0) interface_one = interface_helper.create_interface(node_one.id, 0)
interface_two = interface_helper.create_interface(node_two.id, 0) interface_two = interface_helper.create_interface(node_two.id, 0)
@ -48,12 +46,11 @@ class TestGrpc:
interface_one=interface_one, interface_one=interface_one,
interface_two=interface_two, interface_two=interface_two,
) )
links.append(link) links = [link]
hooks = []
hook = core_pb2.Hook( hook = core_pb2.Hook(
state=core_pb2.SessionState.RUNTIME, file="echo.sh", data="echo hello" state=core_pb2.SessionState.RUNTIME, file="echo.sh", data="echo hello"
) )
hooks.append(hook) hooks = [hook]
location_x = 5 location_x = 5
location_y = 10 location_y = 10
location_z = 15 location_z = 15
@ -73,7 +70,6 @@ class TestGrpc:
emane_config_key = "platform_id_start" emane_config_key = "platform_id_start"
emane_config_value = "2" emane_config_value = "2"
emane_config = {emane_config_key: emane_config_value} emane_config = {emane_config_key: emane_config_value}
model_configs = []
model_node_id = 20 model_node_id = 20
model_config_key = "bandwidth" model_config_key = "bandwidth"
model_config_value = "500000" model_config_value = "500000"
@ -83,21 +79,30 @@ class TestGrpc:
model=EmaneIeee80211abgModel.name, model=EmaneIeee80211abgModel.name,
config={model_config_key: model_config_value}, config={model_config_key: model_config_value},
) )
model_configs.append(model_config) model_configs = [model_config]
wlan_configs = []
wlan_config_key = "range" wlan_config_key = "range"
wlan_config_value = "333" wlan_config_value = "333"
wlan_config = core_pb2.WlanConfig( wlan_config = core_pb2.WlanConfig(
node_id=wlan_node.id, config={wlan_config_key: wlan_config_value} node_id=wlan_node.id, config={wlan_config_key: wlan_config_value}
) )
wlan_configs.append(wlan_config) wlan_configs = [wlan_config]
mobility_config_key = "refresh_ms" mobility_config_key = "refresh_ms"
mobility_config_value = "60" mobility_config_value = "60"
mobility_configs = []
mobility_config = core_pb2.MobilityConfig( mobility_config = core_pb2.MobilityConfig(
node_id=wlan_node.id, config={mobility_config_key: mobility_config_value} node_id=wlan_node.id, config={mobility_config_key: mobility_config_value}
) )
mobility_configs.append(mobility_config) mobility_configs = [mobility_config]
service_config = core_pb2.ServiceConfig(
node_id=node_one.id, service="DefaultRoute", validate=["echo hello"]
)
service_configs = [service_config]
service_file_config = core_pb2.ServiceFileConfig(
node_id=node_one.id,
service="DefaultRoute",
file="defaultroute.sh",
data="echo hello",
)
service_file_configs = [service_file_config]
# when # when
with patch.object(CoreXmlWriter, "write"): with patch.object(CoreXmlWriter, "write"):
@ -112,6 +117,8 @@ class TestGrpc:
model_configs, model_configs,
wlan_configs, wlan_configs,
mobility_configs, mobility_configs,
service_configs,
service_file_configs,
) )
# then # then
@ -139,6 +146,14 @@ class TestGrpc:
model_node_id, EmaneIeee80211abgModel.name model_node_id, EmaneIeee80211abgModel.name
) )
assert set_model_config[model_config_key] == model_config_value assert set_model_config[model_config_key] == model_config_value
service = session.services.get_service(
node_one.id, service_config.service, default_service=True
)
assert service.validate == tuple(service_config.validate)
service_file = session.services.get_service_file(
node_one, service_file_config.service, service_file_config.file
)
assert service_file.data == service_file_config.data
@pytest.mark.parametrize("session_id", [None, 6013]) @pytest.mark.parametrize("session_id", [None, 6013])
def test_create_session(self, grpc_server, session_id): def test_create_session(self, grpc_server, session_id):