pygui: refactoring of nodeutils and its usage, fixed issue with drawing custom nodes and copying services
This commit is contained in:
parent
422a1a500e
commit
47ac4c850d
12 changed files with 194 additions and 191 deletions
|
@ -7,7 +7,9 @@ from typing import Any, Dict, Optional, Type
|
|||
|
||||
import grpc
|
||||
|
||||
from core.gui import appconfig, themes
|
||||
from core.gui import appconfig
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui import themes
|
||||
from core.gui.appconfig import GuiConfig
|
||||
from core.gui.coreclient import CoreClient
|
||||
from core.gui.dialogs.error import ErrorDialog
|
||||
|
@ -16,7 +18,6 @@ from core.gui.frames.default import DefaultInfoFrame
|
|||
from core.gui.graph.manager import CanvasManager
|
||||
from core.gui.images import ImageEnum, Images
|
||||
from core.gui.menubar import Menubar
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
from core.gui.statusbar import StatusBar
|
||||
from core.gui.themes import PADY
|
||||
from core.gui.toolbar import Toolbar
|
||||
|
@ -29,7 +30,7 @@ class Application(ttk.Frame):
|
|||
def __init__(self, proxy: bool, session_id: int = None) -> None:
|
||||
super().__init__()
|
||||
# load node icons
|
||||
NodeUtils.setup()
|
||||
nutils.setup()
|
||||
|
||||
# widgets
|
||||
self.menubar: Optional[Menubar] = None
|
||||
|
|
|
@ -38,6 +38,7 @@ from core.api.grpc.wrappers import (
|
|||
SessionState,
|
||||
ThroughputsEvent,
|
||||
)
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui.appconfig import XMLS_PATH, CoreServer, Observer
|
||||
from core.gui.dialogs.emaneinstall import EmaneInstallDialog
|
||||
from core.gui.dialogs.error import ErrorDialog
|
||||
|
@ -47,7 +48,7 @@ from core.gui.graph.edges import CanvasEdge
|
|||
from core.gui.graph.node import CanvasNode
|
||||
from core.gui.graph.shape import Shape
|
||||
from core.gui.interface import InterfaceManager
|
||||
from core.gui.nodeutils import NodeDraw, NodeUtils
|
||||
from core.gui.nodeutils import NodeDraw
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -515,7 +516,7 @@ class CoreClient:
|
|||
|
||||
def show_mobility_players(self) -> None:
|
||||
for node in self.session.nodes.values():
|
||||
if not NodeUtils.is_mobility(node):
|
||||
if not nutils.is_mobility(node):
|
||||
continue
|
||||
if node.mobility_config:
|
||||
mobility_player = MobilityPlayer(self.app, node)
|
||||
|
@ -779,7 +780,7 @@ class CoreClient:
|
|||
node_id = self.next_node_id()
|
||||
position = Position(x=x, y=y)
|
||||
image = None
|
||||
if NodeUtils.is_image_node(node_type):
|
||||
if nutils.has_image(node_type):
|
||||
image = "ubuntu:latest"
|
||||
emane = None
|
||||
if node_type == NodeType.EMANE:
|
||||
|
@ -804,9 +805,9 @@ class CoreClient:
|
|||
image=image,
|
||||
emane=emane,
|
||||
)
|
||||
if NodeUtils.is_custom(node_type, model):
|
||||
services = NodeUtils.get_custom_node_services(self.app.guiconfig, model)
|
||||
node.services[:] = services
|
||||
if nutils.is_custom(node):
|
||||
services = nutils.get_custom_services(self.app.guiconfig, model)
|
||||
node.services = set(services)
|
||||
# assign default services to CORE node
|
||||
else:
|
||||
services = self.session.default_services.get(model)
|
||||
|
@ -843,10 +844,10 @@ class CoreClient:
|
|||
self.links[edge.token] = edge
|
||||
src_node = edge.src.core_node
|
||||
dst_node = edge.dst.core_node
|
||||
if NodeUtils.is_container_node(src_node):
|
||||
if nutils.is_container(src_node):
|
||||
src_iface_id = edge.link.iface1.id
|
||||
self.iface_to_edge[(src_node.id, src_iface_id)] = edge
|
||||
if NodeUtils.is_container_node(dst_node):
|
||||
if nutils.is_container(dst_node):
|
||||
dst_iface_id = edge.link.iface2.id
|
||||
self.iface_to_edge[(dst_node.id, dst_iface_id)] = edge
|
||||
|
||||
|
@ -865,7 +866,7 @@ class CoreClient:
|
|||
def get_mobility_configs_proto(self) -> List[mobility_pb2.MobilityConfig]:
|
||||
configs = []
|
||||
for node in self.session.nodes.values():
|
||||
if not NodeUtils.is_mobility(node):
|
||||
if not nutils.is_mobility(node):
|
||||
continue
|
||||
if not node.mobility_config:
|
||||
continue
|
||||
|
@ -893,7 +894,7 @@ class CoreClient:
|
|||
def get_service_configs_proto(self) -> List[services_pb2.ServiceConfig]:
|
||||
configs = []
|
||||
for node in self.session.nodes.values():
|
||||
if not NodeUtils.is_container_node(node):
|
||||
if not nutils.is_container(node):
|
||||
continue
|
||||
if not node.service_configs:
|
||||
continue
|
||||
|
@ -913,7 +914,7 @@ class CoreClient:
|
|||
def get_service_file_configs_proto(self) -> List[services_pb2.ServiceFileConfig]:
|
||||
configs = []
|
||||
for node in self.session.nodes.values():
|
||||
if not NodeUtils.is_container_node(node):
|
||||
if not nutils.is_container(node):
|
||||
continue
|
||||
if not node.service_file_configs:
|
||||
continue
|
||||
|
@ -930,7 +931,7 @@ class CoreClient:
|
|||
) -> List[configservices_pb2.ConfigServiceConfig]:
|
||||
config_service_protos = []
|
||||
for node in self.session.nodes.values():
|
||||
if not NodeUtils.is_container_node(node):
|
||||
if not nutils.is_container(node):
|
||||
continue
|
||||
if not node.config_service_configs:
|
||||
continue
|
||||
|
|
|
@ -8,12 +8,12 @@ import netaddr
|
|||
from PIL.ImageTk import PhotoImage
|
||||
|
||||
from core.api.grpc.wrappers import Interface, Node
|
||||
from core.gui import nodeutils, validation
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui import validation
|
||||
from core.gui.appconfig import ICONS_PATH
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.dialogs.emaneconfig import EmaneModelDialog
|
||||
from core.gui.images import Images
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||
from core.gui.widgets import ListboxScroll, image_chooser
|
||||
|
||||
|
@ -225,27 +225,27 @@ class NodeConfigDialog(Dialog):
|
|||
row += 1
|
||||
|
||||
# node type field
|
||||
if NodeUtils.is_model_node(self.node):
|
||||
if nutils.is_model(self.node):
|
||||
label = ttk.Label(frame, text="Type")
|
||||
label.grid(row=row, column=0, sticky=tk.EW, padx=PADX, pady=PADY)
|
||||
combobox = ttk.Combobox(
|
||||
frame,
|
||||
textvariable=self.type,
|
||||
values=list(NodeUtils.NODE_MODELS),
|
||||
values=list(nutils.NODE_MODELS),
|
||||
state=combo_state,
|
||||
)
|
||||
combobox.grid(row=row, column=1, sticky=tk.EW)
|
||||
row += 1
|
||||
|
||||
# container image field
|
||||
if NodeUtils.is_image_node(self.node.type):
|
||||
if nutils.has_image(self.node.type):
|
||||
label = ttk.Label(frame, text="Image")
|
||||
label.grid(row=row, column=0, sticky=tk.EW, padx=PADX, pady=PADY)
|
||||
entry = ttk.Entry(frame, textvariable=self.container_image, state=state)
|
||||
entry.grid(row=row, column=1, sticky=tk.EW)
|
||||
row += 1
|
||||
|
||||
if NodeUtils.is_container_node(self.node):
|
||||
if nutils.is_container(self.node):
|
||||
# server
|
||||
frame.grid(sticky=tk.EW)
|
||||
frame.columnconfigure(1, weight=1)
|
||||
|
@ -259,7 +259,7 @@ class NodeConfigDialog(Dialog):
|
|||
combobox.grid(row=row, column=1, sticky=tk.EW)
|
||||
row += 1
|
||||
|
||||
if NodeUtils.is_rj45_node(self.node):
|
||||
if nutils.is_rj45(self.node):
|
||||
response = self.app.core.client.get_ifaces()
|
||||
logging.debug("host machine available interfaces: %s", response)
|
||||
ifaces = ListboxScroll(frame)
|
||||
|
@ -371,7 +371,7 @@ class NodeConfigDialog(Dialog):
|
|||
def click_icon(self) -> None:
|
||||
file_path = image_chooser(self, ICONS_PATH)
|
||||
if file_path:
|
||||
self.image = Images.create(file_path, nodeutils.ICON_SIZE)
|
||||
self.image = Images.create(file_path, nutils.ICON_SIZE)
|
||||
self.image_button.config(image=self.image)
|
||||
self.image_file = file_path
|
||||
|
||||
|
@ -380,10 +380,10 @@ class NodeConfigDialog(Dialog):
|
|||
|
||||
# update core node
|
||||
self.node.name = self.name.get()
|
||||
if NodeUtils.is_image_node(self.node.type):
|
||||
if nutils.has_image(self.node.type):
|
||||
self.node.image = self.container_image.get()
|
||||
server = self.server.get()
|
||||
if NodeUtils.is_container_node(self.node):
|
||||
if nutils.is_container(self.node):
|
||||
if server == DEFAULT_SERVER:
|
||||
self.node.server = None
|
||||
else:
|
||||
|
|
|
@ -2,8 +2,8 @@ import tkinter as tk
|
|||
from tkinter import ttk
|
||||
from typing import TYPE_CHECKING, Dict, Optional
|
||||
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui.dialogs.dialog import Dialog
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||
from core.gui.widgets import CodeText, ListboxScroll
|
||||
|
||||
|
@ -26,7 +26,7 @@ class RunToolDialog(Dialog):
|
|||
store all CORE nodes (nodes that execute commands) from all existing nodes
|
||||
"""
|
||||
for node in self.app.core.session.nodes.values():
|
||||
if NodeUtils.is_container_node(node):
|
||||
if nutils.is_container(node):
|
||||
self.executable_nodes[node.name] = node.id
|
||||
|
||||
def draw(self) -> None:
|
||||
|
|
|
@ -2,8 +2,8 @@ import tkinter as tk
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from core.api.grpc.wrappers import NodeType
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui.frames.base import DetailsFrame, InfoFrameBase
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -22,9 +22,9 @@ class NodeInfoFrame(InfoFrameBase):
|
|||
frame.grid(sticky=tk.EW)
|
||||
frame.add_detail("ID", str(node.id))
|
||||
frame.add_detail("Name", node.name)
|
||||
if NodeUtils.is_model_node(node):
|
||||
if nutils.is_model(node):
|
||||
frame.add_detail("Type", node.model)
|
||||
if NodeUtils.is_container_node(node):
|
||||
if nutils.is_container(node):
|
||||
for index, service in enumerate(sorted(node.services)):
|
||||
if index == 0:
|
||||
frame.add_detail("Services", service)
|
||||
|
@ -33,8 +33,8 @@ class NodeInfoFrame(InfoFrameBase):
|
|||
if node.type == NodeType.EMANE:
|
||||
emane = "".join(node.emane.split("_")[1:])
|
||||
frame.add_detail("EMANE", emane)
|
||||
if NodeUtils.is_image_node(node.type):
|
||||
if nutils.has_image(node.type):
|
||||
frame.add_detail("Image", node.image)
|
||||
if NodeUtils.is_container_node(node):
|
||||
if nutils.is_container(node):
|
||||
server = node.server if node.server else "localhost"
|
||||
frame.add_detail("Server", server)
|
||||
|
|
|
@ -9,6 +9,7 @@ from PIL.ImageTk import PhotoImage
|
|||
|
||||
from core.api.grpc.wrappers import Interface, Link
|
||||
from core.gui import appconfig
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui.dialogs.shapemod import ShapeDialog
|
||||
from core.gui.graph import tags
|
||||
from core.gui.graph.edges import EDGE_WIDTH, CanvasEdge
|
||||
|
@ -17,7 +18,6 @@ from core.gui.graph.node import CanvasNode, ShadowNode
|
|||
from core.gui.graph.shape import Shape
|
||||
from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
|
||||
from core.gui.images import TypeToImage
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -803,9 +803,7 @@ class CanvasGraph(tk.Canvas):
|
|||
def scale_graph(self) -> None:
|
||||
for nid, canvas_node in self.nodes.items():
|
||||
img = None
|
||||
if NodeUtils.is_custom(
|
||||
canvas_node.core_node.type, canvas_node.core_node.model
|
||||
):
|
||||
if nutils.is_custom(canvas_node.core_node):
|
||||
for custom_node in self.app.guiconfig.nodes:
|
||||
if custom_node.name == canvas_node.core_node.model:
|
||||
img = self.app.get_custom_icon(custom_node.image, ICON_SIZE)
|
||||
|
|
|
@ -5,6 +5,7 @@ from tkinter import BooleanVar, messagebox, ttk
|
|||
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, ValuesView
|
||||
|
||||
from core.api.grpc.wrappers import Link, LinkType, Node, Session, ThroughputsEvent
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui.graph import tags
|
||||
from core.gui.graph.edges import (
|
||||
CanvasEdge,
|
||||
|
@ -17,7 +18,7 @@ from core.gui.graph.graph import CanvasGraph
|
|||
from core.gui.graph.node import CanvasNode
|
||||
from core.gui.graph.shapeutils import ShapeType
|
||||
from core.gui.images import ImageEnum
|
||||
from core.gui.nodeutils import ICON_SIZE, NodeDraw, NodeUtils
|
||||
from core.gui.nodeutils import ICON_SIZE, NodeDraw
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -235,7 +236,7 @@ class CanvasManager:
|
|||
# create session nodes
|
||||
for core_node in session.nodes.values():
|
||||
# add node, avoiding ignored nodes
|
||||
if NodeUtils.is_ignore_node(core_node):
|
||||
if nutils.should_ignore(core_node):
|
||||
continue
|
||||
self.add_core_node(core_node)
|
||||
|
||||
|
@ -300,7 +301,7 @@ class CanvasManager:
|
|||
logging.info("adding core node canvas(%s): %s", core_node.name, canvas_id)
|
||||
canvas = self.get(canvas_id)
|
||||
# if the gui can't find node's image, default to the "edit-node" image
|
||||
image = NodeUtils.node_image(core_node, self.app.guiconfig, self.app.app_scale)
|
||||
image = nutils.get_icon(core_node, self.app.guiconfig, self.app.app_scale)
|
||||
if not image:
|
||||
image = self.app.get_icon(ImageEnum.EDITNODE, ICON_SIZE)
|
||||
x = core_node.position.x
|
||||
|
|
|
@ -9,7 +9,8 @@ from PIL.ImageTk import PhotoImage
|
|||
|
||||
from core.api.grpc.services_pb2 import ServiceAction
|
||||
from core.api.grpc.wrappers import Interface, Node, NodeType
|
||||
from core.gui import nodeutils, themes
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui import themes
|
||||
from core.gui.dialogs.emaneconfig import EmaneConfigDialog
|
||||
from core.gui.dialogs.mobilityconfig import MobilityConfigDialog
|
||||
from core.gui.dialogs.nodeconfig import NodeConfigDialog
|
||||
|
@ -21,7 +22,7 @@ from core.gui.graph import tags
|
|||
from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge
|
||||
from core.gui.graph.tooltip import CanvasTooltip
|
||||
from core.gui.images import ImageEnum, Images
|
||||
from core.gui.nodeutils import ANTENNA_SIZE, ICON_SIZE, NodeUtils
|
||||
from core.gui.nodeutils import ANTENNA_SIZE, ICON_SIZE
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -190,7 +191,7 @@ class CanvasNode:
|
|||
def on_enter(self, event: tk.Event) -> None:
|
||||
is_runtime = self.app.core.is_runtime()
|
||||
has_observer = self.app.core.observer is not None
|
||||
is_container = NodeUtils.is_container_node(self.core_node)
|
||||
is_container = nutils.is_container(self.core_node)
|
||||
if is_runtime and has_observer and is_container:
|
||||
self.tooltip.text.set("waiting...")
|
||||
self.tooltip.on_enter(event)
|
||||
|
@ -205,7 +206,7 @@ class CanvasNode:
|
|||
|
||||
def double_click(self, event: tk.Event) -> None:
|
||||
if self.app.core.is_runtime():
|
||||
if NodeUtils.is_container_node(self.core_node):
|
||||
if nutils.is_container(self.core_node):
|
||||
self.canvas.core.launch_terminal(self.core_node.id)
|
||||
else:
|
||||
self.show_config()
|
||||
|
@ -233,7 +234,7 @@ class CanvasNode:
|
|||
self.context.add_command(
|
||||
label="Mobility Player", command=self.show_mobility_player
|
||||
)
|
||||
if NodeUtils.is_container_node(self.core_node):
|
||||
if nutils.is_container(self.core_node):
|
||||
services_menu = tk.Menu(self.context)
|
||||
for service in sorted(self.core_node.services):
|
||||
service_menu = tk.Menu(services_menu)
|
||||
|
@ -251,7 +252,7 @@ class CanvasNode:
|
|||
self.context.add_cascade(label="Services", menu=services_menu)
|
||||
else:
|
||||
self.context.add_command(label="Configure", command=self.show_config)
|
||||
if NodeUtils.is_container_node(self.core_node):
|
||||
if nutils.is_container(self.core_node):
|
||||
self.context.add_command(label="Services", command=self.show_services)
|
||||
self.context.add_command(
|
||||
label="Config Services", command=self.show_config_services
|
||||
|
@ -268,7 +269,7 @@ class CanvasNode:
|
|||
self.context.add_command(
|
||||
label="Mobility Config", command=self.show_mobility_config
|
||||
)
|
||||
if NodeUtils.is_wireless_node(self.core_node):
|
||||
if nutils.is_wireless(self.core_node):
|
||||
self.context.add_command(
|
||||
label="Link To Selected", command=self.wireless_link_selected
|
||||
)
|
||||
|
@ -402,7 +403,7 @@ class CanvasNode:
|
|||
logging.error(f"node icon does not exist: {icon_path}")
|
||||
return
|
||||
self.core_node.icon = icon_path
|
||||
self.image = Images.create(icon_path, nodeutils.ICON_SIZE)
|
||||
self.image = Images.create(icon_path)
|
||||
self.canvas.itemconfig(self.id, image=self.image)
|
||||
|
||||
def is_linkable(self, node: "CanvasNode") -> bool:
|
||||
|
@ -410,22 +411,19 @@ class CanvasNode:
|
|||
if self == node:
|
||||
return False
|
||||
# rj45 nodes can only support one link
|
||||
if NodeUtils.is_rj45_node(self.core_node) and self.edges:
|
||||
if nutils.is_rj45(self.core_node) and self.edges:
|
||||
return False
|
||||
if NodeUtils.is_rj45_node(node.core_node) and node.edges:
|
||||
if nutils.is_rj45(node.core_node) and node.edges:
|
||||
return False
|
||||
# only 1 link between bridge based nodes
|
||||
is_src_bridge = NodeUtils.is_bridge_node(self.core_node)
|
||||
is_dst_bridge = NodeUtils.is_bridge_node(node.core_node)
|
||||
is_src_bridge = nutils.is_bridge(self.core_node)
|
||||
is_dst_bridge = nutils.is_bridge(node.core_node)
|
||||
common_links = self.edges & node.edges
|
||||
if all([is_src_bridge, is_dst_bridge, common_links]):
|
||||
return False
|
||||
# valid link
|
||||
return True
|
||||
|
||||
def is_wireless(self) -> bool:
|
||||
return NodeUtils.is_wireless_node(self.core_node)
|
||||
|
||||
def hide(self) -> None:
|
||||
self.hidden = True
|
||||
self.canvas.itemconfig(self.id, state=tk.HIDDEN)
|
||||
|
@ -481,6 +479,9 @@ class CanvasNode:
|
|||
def validate_service(self, service: str) -> None:
|
||||
self._service_action(service, ServiceAction.VALIDATE)
|
||||
|
||||
def is_wireless(self) -> bool:
|
||||
return nutils.is_wireless(self.core_node)
|
||||
|
||||
|
||||
class ShadowNode:
|
||||
def __init__(
|
||||
|
|
|
@ -8,12 +8,18 @@ from PIL.ImageTk import PhotoImage
|
|||
from core.api.grpc.wrappers import NodeType
|
||||
from core.gui.appconfig import LOCAL_ICONS_PATH
|
||||
|
||||
ICON_SIZE: int = 48
|
||||
|
||||
|
||||
class Images:
|
||||
images: Dict[str, str] = {}
|
||||
|
||||
@classmethod
|
||||
def create(cls, file_path: str, width: int, height: int = None) -> PhotoImage:
|
||||
def create(
|
||||
cls, file_path: str, width: int = None, height: int = None
|
||||
) -> PhotoImage:
|
||||
if width is None:
|
||||
width = ICON_SIZE
|
||||
if height is None:
|
||||
height = width
|
||||
image = Image.open(file_path)
|
||||
|
|
|
@ -5,9 +5,9 @@ import netaddr
|
|||
from netaddr import EUI, IPNetwork
|
||||
|
||||
from core.api.grpc.wrappers import Interface, Link, LinkType, Node
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui.graph.edges import CanvasEdge
|
||||
from core.gui.graph.node import CanvasNode
|
||||
from core.gui.nodeutils import NodeUtils
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.gui.app import Application
|
||||
|
@ -147,7 +147,7 @@ class InterfaceManager:
|
|||
self.used_subnets[subnets.key()] = subnets
|
||||
|
||||
def next_index(self, node: Node) -> int:
|
||||
if NodeUtils.is_router_node(node):
|
||||
if nutils.is_router(node):
|
||||
index = 1
|
||||
else:
|
||||
index = 20
|
||||
|
@ -179,8 +179,8 @@ class InterfaceManager:
|
|||
) -> None:
|
||||
src_node = canvas_src_node.core_node
|
||||
dst_node = canvas_dst_node.core_node
|
||||
is_src_container = NodeUtils.is_container_node(src_node)
|
||||
is_dst_container = NodeUtils.is_container_node(dst_node)
|
||||
is_src_container = nutils.is_container(src_node)
|
||||
is_dst_container = nutils.is_container(dst_node)
|
||||
if is_src_container and is_dst_container:
|
||||
self.current_subnets = self.next_subnets()
|
||||
elif is_src_container and not is_dst_container:
|
||||
|
@ -232,10 +232,10 @@ class InterfaceManager:
|
|||
dst_node = edge.dst.core_node
|
||||
self.determine_subnets(edge.src, edge.dst)
|
||||
src_iface = None
|
||||
if NodeUtils.is_container_node(src_node):
|
||||
if nutils.is_container(src_node):
|
||||
src_iface = self.create_iface(edge.src, edge.linked_wireless)
|
||||
dst_iface = None
|
||||
if NodeUtils.is_container_node(dst_node):
|
||||
if nutils.is_container(dst_node):
|
||||
dst_iface = self.create_iface(edge.dst, edge.linked_wireless)
|
||||
link = Link(
|
||||
type=LinkType.WIRED,
|
||||
|
|
|
@ -10,6 +10,125 @@ from core.gui.images import ImageEnum, Images, TypeToImage
|
|||
ICON_SIZE: int = 48
|
||||
ANTENNA_SIZE: int = 32
|
||||
|
||||
NODES: List["NodeDraw"] = []
|
||||
NETWORK_NODES: List["NodeDraw"] = []
|
||||
NODE_ICONS = {}
|
||||
CONTAINER_NODES: Set[NodeType] = {NodeType.DEFAULT, NodeType.DOCKER, NodeType.LXC}
|
||||
IMAGE_NODES: Set[NodeType] = {NodeType.DOCKER, NodeType.LXC}
|
||||
WIRELESS_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
||||
RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
|
||||
BRIDGE_NODES: Set[NodeType] = {NodeType.HUB, NodeType.SWITCH}
|
||||
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
|
||||
MOBILITY_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
||||
NODE_MODELS: Set[str] = {"router", "host", "PC", "mdr", "prouter"}
|
||||
ROUTER_NODES: Set[str] = {"router", "mdr"}
|
||||
ANTENNA_ICON: Optional[PhotoImage] = None
|
||||
|
||||
|
||||
def setup() -> None:
|
||||
global ANTENNA_ICON
|
||||
nodes = [
|
||||
(ImageEnum.ROUTER, NodeType.DEFAULT, "Router", "router"),
|
||||
(ImageEnum.HOST, NodeType.DEFAULT, "Host", "host"),
|
||||
(ImageEnum.PC, NodeType.DEFAULT, "PC", "PC"),
|
||||
(ImageEnum.MDR, NodeType.DEFAULT, "MDR", "mdr"),
|
||||
(ImageEnum.PROUTER, NodeType.DEFAULT, "PRouter", "prouter"),
|
||||
(ImageEnum.DOCKER, NodeType.DOCKER, "Docker", None),
|
||||
(ImageEnum.LXC, NodeType.LXC, "LXC", None),
|
||||
]
|
||||
for image_enum, node_type, label, model in nodes:
|
||||
node_draw = NodeDraw.from_setup(image_enum, node_type, label, model)
|
||||
NODES.append(node_draw)
|
||||
NODE_ICONS[(node_type, model)] = node_draw.image
|
||||
network_nodes = [
|
||||
(ImageEnum.HUB, NodeType.HUB, "Hub"),
|
||||
(ImageEnum.SWITCH, NodeType.SWITCH, "Switch"),
|
||||
(ImageEnum.WLAN, NodeType.WIRELESS_LAN, "WLAN"),
|
||||
(ImageEnum.EMANE, NodeType.EMANE, "EMANE"),
|
||||
(ImageEnum.RJ45, NodeType.RJ45, "RJ45"),
|
||||
(ImageEnum.TUNNEL, NodeType.TUNNEL, "Tunnel"),
|
||||
]
|
||||
for image_enum, node_type, label in network_nodes:
|
||||
node_draw = NodeDraw.from_setup(image_enum, node_type, label)
|
||||
NETWORK_NODES.append(node_draw)
|
||||
NODE_ICONS[(node_type, None)] = node_draw.image
|
||||
ANTENNA_ICON = Images.get(ImageEnum.ANTENNA, ANTENNA_SIZE)
|
||||
|
||||
|
||||
def is_bridge(node: Node) -> bool:
|
||||
return node.type in BRIDGE_NODES
|
||||
|
||||
|
||||
def is_mobility(node: Node) -> bool:
|
||||
return node.type in MOBILITY_NODES
|
||||
|
||||
|
||||
def is_router(node: Node) -> bool:
|
||||
return is_model(node) and node.model in ROUTER_NODES
|
||||
|
||||
|
||||
def should_ignore(node: Node) -> bool:
|
||||
return node.type in IGNORE_NODES
|
||||
|
||||
|
||||
def is_container(node: Node) -> bool:
|
||||
return node.type in CONTAINER_NODES
|
||||
|
||||
|
||||
def is_model(node: Node) -> bool:
|
||||
return node.type == NodeType.DEFAULT
|
||||
|
||||
|
||||
def has_image(node_type: NodeType) -> bool:
|
||||
return node_type in IMAGE_NODES
|
||||
|
||||
|
||||
def is_wireless(node: Node) -> bool:
|
||||
return node.type in WIRELESS_NODES
|
||||
|
||||
|
||||
def is_rj45(node: Node) -> bool:
|
||||
return node.type in RJ45_NODES
|
||||
|
||||
|
||||
def is_custom(node: Node) -> bool:
|
||||
return is_model(node) and node.model not in NODE_MODELS
|
||||
|
||||
|
||||
def get_custom_services(gui_config: GuiConfig, name: str) -> List[str]:
|
||||
for custom_node in gui_config.nodes:
|
||||
if custom_node.name == name:
|
||||
return custom_node.services
|
||||
return []
|
||||
|
||||
|
||||
def _get_image_file(config: GuiConfig, name: str) -> Optional[str]:
|
||||
for custom_node in config.nodes:
|
||||
if custom_node.name == name:
|
||||
return custom_node.image
|
||||
return None
|
||||
|
||||
|
||||
def get_icon(node: Node, config: GuiConfig, scale: float) -> Optional[PhotoImage]:
|
||||
image = None
|
||||
# node has defined a custom icon
|
||||
if node.icon:
|
||||
try:
|
||||
image = Images.create(node.icon, int(ICON_SIZE * scale))
|
||||
except OSError:
|
||||
logging.error("invalid icon: %s", node.icon)
|
||||
else:
|
||||
# attempt to find default type/model image
|
||||
image_enum = TypeToImage.get(node.type, node.model)
|
||||
if image_enum:
|
||||
image = Images.get(image_enum, int(ICON_SIZE * scale))
|
||||
# attempt to find custom image file
|
||||
else:
|
||||
image_file = _get_image_file(config, node.model)
|
||||
if image_file:
|
||||
image = Images.get_with_image_file(image_file, int(ICON_SIZE * scale))
|
||||
return image
|
||||
|
||||
|
||||
class NodeDraw:
|
||||
def __init__(self) -> None:
|
||||
|
@ -52,128 +171,3 @@ class NodeDraw:
|
|||
node_draw.model = custom_node.name
|
||||
node_draw.tooltip = custom_node.name
|
||||
return node_draw
|
||||
|
||||
|
||||
class NodeUtils:
|
||||
NODES: List[NodeDraw] = []
|
||||
NETWORK_NODES: List[NodeDraw] = []
|
||||
NODE_ICONS = {}
|
||||
CONTAINER_NODES: Set[NodeType] = {NodeType.DEFAULT, NodeType.DOCKER, NodeType.LXC}
|
||||
IMAGE_NODES: Set[NodeType] = {NodeType.DOCKER, NodeType.LXC}
|
||||
WIRELESS_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
||||
RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
|
||||
BRIDGE_NODES: Set[NodeType] = {NodeType.HUB, NodeType.SWITCH}
|
||||
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
|
||||
MOBILITY_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
||||
NODE_MODELS: Set[str] = {"router", "host", "PC", "mdr", "prouter"}
|
||||
ROUTER_NODES: Set[str] = {"router", "mdr"}
|
||||
ANTENNA_ICON: PhotoImage = None
|
||||
|
||||
@classmethod
|
||||
def is_bridge_node(cls, node: Node) -> bool:
|
||||
return node.type in cls.BRIDGE_NODES
|
||||
|
||||
@classmethod
|
||||
def is_mobility(cls, node: Node) -> bool:
|
||||
return node.type in cls.MOBILITY_NODES
|
||||
|
||||
@classmethod
|
||||
def is_router_node(cls, node: Node) -> bool:
|
||||
return cls.is_model_node(node) and node.model in cls.ROUTER_NODES
|
||||
|
||||
@classmethod
|
||||
def is_ignore_node(cls, node: Node) -> bool:
|
||||
return node.type in cls.IGNORE_NODES
|
||||
|
||||
@classmethod
|
||||
def is_container_node(cls, node: Node) -> bool:
|
||||
return node.type in cls.CONTAINER_NODES
|
||||
|
||||
@classmethod
|
||||
def is_model_node(cls, node: Node) -> bool:
|
||||
return node.type == NodeType.DEFAULT
|
||||
|
||||
@classmethod
|
||||
def is_image_node(cls, node_type: NodeType) -> bool:
|
||||
return node_type in cls.IMAGE_NODES
|
||||
|
||||
@classmethod
|
||||
def is_wireless_node(cls, node: Node) -> bool:
|
||||
return node.type in cls.WIRELESS_NODES
|
||||
|
||||
@classmethod
|
||||
def is_rj45_node(cls, node: Node) -> bool:
|
||||
return node.type in cls.RJ45_NODES
|
||||
|
||||
@classmethod
|
||||
def node_icon(
|
||||
cls, node: Node, gui_config: GuiConfig, scale: float = 1.0
|
||||
) -> PhotoImage:
|
||||
|
||||
image_enum = TypeToImage.get(node.type, node.model)
|
||||
if image_enum:
|
||||
return Images.get(image_enum, int(ICON_SIZE * scale))
|
||||
else:
|
||||
image_stem = cls.get_image_file(gui_config, node.model)
|
||||
if image_stem:
|
||||
return Images.get_with_image_file(image_stem, int(ICON_SIZE * scale))
|
||||
|
||||
@classmethod
|
||||
def node_image(
|
||||
cls, core_node: Node, gui_config: GuiConfig, scale: float = 1.0
|
||||
) -> PhotoImage:
|
||||
image = cls.node_icon(core_node, gui_config, scale)
|
||||
if core_node.icon:
|
||||
try:
|
||||
image = Images.create(core_node.icon, int(ICON_SIZE * scale))
|
||||
except OSError:
|
||||
logging.error("invalid icon: %s", core_node.icon)
|
||||
return image
|
||||
|
||||
@classmethod
|
||||
def is_custom(cls, node_type: NodeType, model: str) -> bool:
|
||||
return node_type == NodeType.DEFAULT and model not in cls.NODE_MODELS
|
||||
|
||||
@classmethod
|
||||
def get_custom_node_services(cls, gui_config: GuiConfig, name: str) -> List[str]:
|
||||
for custom_node in gui_config.nodes:
|
||||
if custom_node.name == name:
|
||||
return custom_node.services
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def get_image_file(cls, gui_config: GuiConfig, name: str) -> Optional[str]:
|
||||
for custom_node in gui_config.nodes:
|
||||
if custom_node.name == name:
|
||||
return custom_node.image
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def setup(cls) -> None:
|
||||
nodes = [
|
||||
(ImageEnum.ROUTER, NodeType.DEFAULT, "Router", "router"),
|
||||
(ImageEnum.HOST, NodeType.DEFAULT, "Host", "host"),
|
||||
(ImageEnum.PC, NodeType.DEFAULT, "PC", "PC"),
|
||||
(ImageEnum.MDR, NodeType.DEFAULT, "MDR", "mdr"),
|
||||
(ImageEnum.PROUTER, NodeType.DEFAULT, "PRouter", "prouter"),
|
||||
(ImageEnum.DOCKER, NodeType.DOCKER, "Docker", None),
|
||||
(ImageEnum.LXC, NodeType.LXC, "LXC", None),
|
||||
]
|
||||
for image_enum, node_type, label, model in nodes:
|
||||
node_draw = NodeDraw.from_setup(image_enum, node_type, label, model)
|
||||
cls.NODES.append(node_draw)
|
||||
cls.NODE_ICONS[(node_type, model)] = node_draw.image
|
||||
|
||||
network_nodes = [
|
||||
(ImageEnum.HUB, NodeType.HUB, "Hub"),
|
||||
(ImageEnum.SWITCH, NodeType.SWITCH, "Switch"),
|
||||
(ImageEnum.WLAN, NodeType.WIRELESS_LAN, "WLAN"),
|
||||
(ImageEnum.EMANE, NodeType.EMANE, "EMANE"),
|
||||
(ImageEnum.RJ45, NodeType.RJ45, "RJ45"),
|
||||
(ImageEnum.TUNNEL, NodeType.TUNNEL, "Tunnel"),
|
||||
]
|
||||
for image_enum, node_type, label in network_nodes:
|
||||
node_draw = NodeDraw.from_setup(image_enum, node_type, label)
|
||||
cls.NETWORK_NODES.append(node_draw)
|
||||
cls.NODE_ICONS[(node_type, None)] = node_draw.image
|
||||
cls.ANTENNA_ICON = Images.get(ImageEnum.ANTENNA, ANTENNA_SIZE)
|
||||
|
|
|
@ -7,13 +7,14 @@ from typing import TYPE_CHECKING, Callable, List, Optional
|
|||
|
||||
from PIL.ImageTk import PhotoImage
|
||||
|
||||
from core.gui import nodeutils as nutils
|
||||
from core.gui.dialogs.colorpicker import ColorPickerDialog
|
||||
from core.gui.dialogs.runtool import RunToolDialog
|
||||
from core.gui.graph import tags
|
||||
from core.gui.graph.enums import GraphMode
|
||||
from core.gui.graph.shapeutils import ShapeType, is_marker
|
||||
from core.gui.images import ImageEnum
|
||||
from core.gui.nodeutils import NodeDraw, NodeUtils
|
||||
from core.gui.nodeutils import NodeDraw
|
||||
from core.gui.observers import ObserversMenu
|
||||
from core.gui.task import ProgressTask
|
||||
from core.gui.themes import Styles
|
||||
|
@ -190,8 +191,8 @@ class Toolbar(ttk.Frame):
|
|||
|
||||
# these variables help keep track of what images being drawn so that scaling
|
||||
# is possible since PhotoImage does not have resize method
|
||||
self.current_node: NodeDraw = NodeUtils.NODES[0]
|
||||
self.current_network: NodeDraw = NodeUtils.NETWORK_NODES[0]
|
||||
self.current_node: NodeDraw = nutils.NODES[0]
|
||||
self.current_network: NodeDraw = nutils.NETWORK_NODES[0]
|
||||
self.current_annotation: ShapeType = ShapeType.MARKER
|
||||
self.annotation_enum: ImageEnum = ImageEnum.MARKER
|
||||
|
||||
|
@ -263,7 +264,7 @@ class Toolbar(ttk.Frame):
|
|||
self.design_frame.select_radio(self.node_button)
|
||||
self.picker = PickerFrame(self.app, self.node_button)
|
||||
# draw default nodes
|
||||
for node_draw in NodeUtils.NODES:
|
||||
for node_draw in nutils.NODES:
|
||||
func = partial(
|
||||
self.update_button, self.node_button, node_draw, NodeTypeEnum.NODE
|
||||
)
|
||||
|
@ -353,7 +354,7 @@ class Toolbar(ttk.Frame):
|
|||
self.app.manager.node_draw = self.current_network
|
||||
self.design_frame.select_radio(self.network_button)
|
||||
self.picker = PickerFrame(self.app, self.network_button)
|
||||
for node_draw in NodeUtils.NETWORK_NODES:
|
||||
for node_draw in nutils.NETWORK_NODES:
|
||||
func = partial(
|
||||
self.update_button, self.network_button, node_draw, NodeTypeEnum.NETWORK
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue