Merge pull request #433 from coreemu/enhancement/pygui-config-cleanup
Enhancement/pygui config cleanup
This commit is contained in:
commit
63b8b7e8a0
14 changed files with 264 additions and 269 deletions
|
@ -90,13 +90,7 @@ class CoreClient:
|
||||||
self.location = None
|
self.location = None
|
||||||
self.links = {}
|
self.links = {}
|
||||||
self.hooks = {}
|
self.hooks = {}
|
||||||
self.wlan_configs = {}
|
|
||||||
self.mobility_configs = {}
|
|
||||||
self.emane_model_configs = {}
|
|
||||||
self.emane_config = None
|
self.emane_config = None
|
||||||
self.service_configs = {}
|
|
||||||
self.config_service_configs = {}
|
|
||||||
self.file_configs = {}
|
|
||||||
self.mobility_players = {}
|
self.mobility_players = {}
|
||||||
self.handling_throughputs = None
|
self.handling_throughputs = None
|
||||||
self.handling_events = None
|
self.handling_events = None
|
||||||
|
@ -128,12 +122,7 @@ class CoreClient:
|
||||||
self.canvas_nodes.clear()
|
self.canvas_nodes.clear()
|
||||||
self.links.clear()
|
self.links.clear()
|
||||||
self.hooks.clear()
|
self.hooks.clear()
|
||||||
self.wlan_configs.clear()
|
|
||||||
self.mobility_configs.clear()
|
|
||||||
self.emane_model_configs.clear()
|
|
||||||
self.emane_config = None
|
self.emane_config = None
|
||||||
self.service_configs.clear()
|
|
||||||
self.file_configs.clear()
|
|
||||||
self.modified_service_nodes.clear()
|
self.modified_service_nodes.clear()
|
||||||
for mobility_player in self.mobility_players.values():
|
for mobility_player in self.mobility_players.values():
|
||||||
mobility_player.handle_close()
|
mobility_player.handle_close()
|
||||||
|
@ -303,63 +292,66 @@ class CoreClient:
|
||||||
for hook in response.hooks:
|
for hook in response.hooks:
|
||||||
self.hooks[hook.file] = hook
|
self.hooks[hook.file] = hook
|
||||||
|
|
||||||
# get mobility configs
|
|
||||||
response = self.client.get_mobility_configs(self.session_id)
|
|
||||||
for node_id in response.configs:
|
|
||||||
node_config = response.configs[node_id].config
|
|
||||||
self.mobility_configs[node_id] = node_config
|
|
||||||
|
|
||||||
# get emane config
|
# get emane config
|
||||||
response = self.client.get_emane_config(self.session_id)
|
response = self.client.get_emane_config(self.session_id)
|
||||||
self.emane_config = response.config
|
self.emane_config = response.config
|
||||||
|
|
||||||
|
# draw session
|
||||||
|
self.app.canvas.reset_and_redraw(session)
|
||||||
|
|
||||||
|
# get mobility configs
|
||||||
|
response = self.client.get_mobility_configs(self.session_id)
|
||||||
|
for node_id in response.configs:
|
||||||
|
config = response.configs[node_id].config
|
||||||
|
canvas_node = self.canvas_nodes[node_id]
|
||||||
|
canvas_node.mobility_config = dict(config)
|
||||||
|
|
||||||
# get emane model config
|
# get emane model config
|
||||||
response = self.client.get_emane_model_configs(self.session_id)
|
response = self.client.get_emane_model_configs(self.session_id)
|
||||||
for config in response.configs:
|
for config in response.configs:
|
||||||
interface = None
|
interface = None
|
||||||
if config.interface != -1:
|
if config.interface != -1:
|
||||||
interface = config.interface
|
interface = config.interface
|
||||||
self.set_emane_model_config(
|
canvas_node = self.canvas_nodes[config.node_id]
|
||||||
config.node_id, config.model, config.config, interface
|
canvas_node.emane_model_configs[(config.model, interface)] = dict(
|
||||||
|
config.config
|
||||||
)
|
)
|
||||||
|
|
||||||
# get wlan configurations
|
# get wlan configurations
|
||||||
response = self.client.get_wlan_configs(self.session_id)
|
response = self.client.get_wlan_configs(self.session_id)
|
||||||
for _id in response.configs:
|
for _id in response.configs:
|
||||||
mapped_config = response.configs[_id]
|
mapped_config = response.configs[_id]
|
||||||
self.wlan_configs[_id] = mapped_config.config
|
canvas_node = self.canvas_nodes[_id]
|
||||||
|
canvas_node.wlan_config = dict(mapped_config.config)
|
||||||
|
|
||||||
# get service configurations
|
# get service configurations
|
||||||
response = self.client.get_node_service_configs(self.session_id)
|
response = self.client.get_node_service_configs(self.session_id)
|
||||||
for config in response.configs:
|
for config in response.configs:
|
||||||
service_configs = self.service_configs.setdefault(config.node_id, {})
|
canvas_node = self.canvas_nodes[config.node_id]
|
||||||
service_configs[config.service] = config.data
|
canvas_node.service_configs[config.service] = config.data
|
||||||
logging.debug("service file configs: %s", config.files)
|
logging.debug("service file configs: %s", config.files)
|
||||||
for file_name in config.files:
|
for file_name in config.files:
|
||||||
file_configs = self.file_configs.setdefault(config.node_id, {})
|
|
||||||
files = file_configs.setdefault(config.service, {})
|
|
||||||
data = config.files[file_name]
|
data = config.files[file_name]
|
||||||
|
files = canvas_node.service_file_configs.setdefault(
|
||||||
|
config.service, {}
|
||||||
|
)
|
||||||
files[file_name] = data
|
files[file_name] = data
|
||||||
|
|
||||||
# get config service configurations
|
# get config service configurations
|
||||||
response = self.client.get_node_config_service_configs(self.session_id)
|
response = self.client.get_node_config_service_configs(self.session_id)
|
||||||
for config in response.configs:
|
for config in response.configs:
|
||||||
node_configs = self.config_service_configs.setdefault(
|
canvas_node = self.canvas_nodes[config.node_id]
|
||||||
config.node_id, {}
|
service_config = canvas_node.config_service_configs.setdefault(
|
||||||
|
config.name, {}
|
||||||
)
|
)
|
||||||
service_config = node_configs.setdefault(config.name, {})
|
|
||||||
if config.templates:
|
if config.templates:
|
||||||
service_config["templates"] = config.templates
|
service_config["templates"] = config.templates
|
||||||
if config.config:
|
if config.config:
|
||||||
service_config["config"] = config.config
|
service_config["config"] = config.config
|
||||||
|
|
||||||
# draw session
|
|
||||||
self.app.canvas.reset_and_redraw(session)
|
|
||||||
|
|
||||||
# get metadata
|
# get metadata
|
||||||
response = self.client.get_session_metadata(self.session_id)
|
response = self.client.get_session_metadata(self.session_id)
|
||||||
self.parse_metadata(response.config)
|
self.parse_metadata(response.config)
|
||||||
|
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.after(0, show_grpc_error, e, self.app, self.app)
|
self.app.after(0, show_grpc_error, e, self.app, self.app)
|
||||||
|
|
||||||
|
@ -556,11 +548,16 @@ class CoreClient:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def show_mobility_players(self):
|
def show_mobility_players(self):
|
||||||
for node_id, config in self.mobility_configs.items():
|
for canvas_node in self.canvas_nodes.values():
|
||||||
canvas_node = self.canvas_nodes[node_id]
|
if canvas_node.core_node.type != core_pb2.NodeType.WIRELESS_LAN:
|
||||||
mobility_player = MobilityPlayer(self.app, self.app, canvas_node, config)
|
continue
|
||||||
mobility_player.show()
|
if canvas_node.mobility_config:
|
||||||
self.mobility_players[node_id] = mobility_player
|
mobility_player = MobilityPlayer(
|
||||||
|
self.app, self.app, canvas_node, canvas_node.mobility_config
|
||||||
|
)
|
||||||
|
node_id = canvas_node.core_node.id
|
||||||
|
self.mobility_players[node_id] = mobility_player
|
||||||
|
mobility_player.show()
|
||||||
|
|
||||||
def set_metadata(self):
|
def set_metadata(self):
|
||||||
# create canvas data
|
# create canvas data
|
||||||
|
@ -841,18 +838,7 @@ class CoreClient:
|
||||||
logging.error("unknown node: %s", node_id)
|
logging.error("unknown node: %s", node_id)
|
||||||
continue
|
continue
|
||||||
del self.canvas_nodes[node_id]
|
del self.canvas_nodes[node_id]
|
||||||
|
|
||||||
self.modified_service_nodes.discard(node_id)
|
self.modified_service_nodes.discard(node_id)
|
||||||
|
|
||||||
if node_id in self.mobility_configs:
|
|
||||||
del self.mobility_configs[node_id]
|
|
||||||
if node_id in self.wlan_configs:
|
|
||||||
del self.wlan_configs[node_id]
|
|
||||||
for key in list(self.emane_model_configs):
|
|
||||||
node_id, _, _ = key
|
|
||||||
if node_id == node_id:
|
|
||||||
del self.emane_model_configs[key]
|
|
||||||
|
|
||||||
for edge in canvas_node.edges:
|
for edge in canvas_node.edges:
|
||||||
if edge in edges:
|
if edge in edges:
|
||||||
continue
|
continue
|
||||||
|
@ -922,37 +908,54 @@ class CoreClient:
|
||||||
|
|
||||||
def get_wlan_configs_proto(self) -> List[WlanConfig]:
|
def get_wlan_configs_proto(self) -> List[WlanConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for node_id, config in self.wlan_configs.items():
|
for canvas_node in self.canvas_nodes.values():
|
||||||
|
if canvas_node.core_node.type != core_pb2.NodeType.WIRELESS_LAN:
|
||||||
|
continue
|
||||||
|
config = canvas_node.wlan_config
|
||||||
config = {x: config[x].value for x in config}
|
config = {x: config[x].value for x in config}
|
||||||
|
node_id = canvas_node.core_node.id
|
||||||
wlan_config = WlanConfig(node_id=node_id, config=config)
|
wlan_config = WlanConfig(node_id=node_id, config=config)
|
||||||
configs.append(wlan_config)
|
configs.append(wlan_config)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
def get_mobility_configs_proto(self) -> List[MobilityConfig]:
|
def get_mobility_configs_proto(self) -> List[MobilityConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for node_id, config in self.mobility_configs.items():
|
for canvas_node in self.canvas_nodes.values():
|
||||||
|
if canvas_node.core_node.type != core_pb2.NodeType.WIRELESS_LAN:
|
||||||
|
continue
|
||||||
|
config = canvas_node.mobility_config
|
||||||
config = {x: config[x].value for x in config}
|
config = {x: config[x].value for x in config}
|
||||||
|
node_id = canvas_node.core_node.id
|
||||||
mobility_config = MobilityConfig(node_id=node_id, config=config)
|
mobility_config = MobilityConfig(node_id=node_id, config=config)
|
||||||
configs.append(mobility_config)
|
configs.append(mobility_config)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
def get_emane_model_configs_proto(self) -> List[EmaneModelConfig]:
|
def get_emane_model_configs_proto(self) -> List[EmaneModelConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for key, config in self.emane_model_configs.items():
|
for canvas_node in self.canvas_nodes.values():
|
||||||
node_id, model, interface = key
|
if canvas_node.core_node.type != core_pb2.NodeType.EMANE:
|
||||||
config = {x: config[x].value for x in config}
|
continue
|
||||||
if interface is None:
|
node_id = canvas_node.core_node.id
|
||||||
interface = -1
|
for key, config in canvas_node.emane_model_configs.items():
|
||||||
config_proto = EmaneModelConfig(
|
model, interface = key
|
||||||
node_id=node_id, interface_id=interface, model=model, config=config
|
config = {x: config[x].value for x in config}
|
||||||
)
|
if interface is None:
|
||||||
configs.append(config_proto)
|
interface = -1
|
||||||
|
config_proto = EmaneModelConfig(
|
||||||
|
node_id=node_id, interface_id=interface, model=model, config=config
|
||||||
|
)
|
||||||
|
configs.append(config_proto)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
def get_service_configs_proto(self) -> List[ServiceConfig]:
|
def get_service_configs_proto(self) -> List[ServiceConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for node_id, services in self.service_configs.items():
|
for canvas_node in self.canvas_nodes.values():
|
||||||
for name, config in services.items():
|
if not NodeUtils.is_container_node(canvas_node.core_node.type):
|
||||||
|
continue
|
||||||
|
if not canvas_node.service_configs:
|
||||||
|
continue
|
||||||
|
node_id = canvas_node.core_node.id
|
||||||
|
for name, config in canvas_node.service_configs.items():
|
||||||
config_proto = ServiceConfig(
|
config_proto = ServiceConfig(
|
||||||
node_id=node_id,
|
node_id=node_id,
|
||||||
service=name,
|
service=name,
|
||||||
|
@ -967,9 +970,14 @@ class CoreClient:
|
||||||
|
|
||||||
def get_service_file_configs_proto(self) -> List[ServiceFileConfig]:
|
def get_service_file_configs_proto(self) -> List[ServiceFileConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for (node_id, file_configs) in self.file_configs.items():
|
for canvas_node in self.canvas_nodes.values():
|
||||||
for service, file_config in file_configs.items():
|
if not NodeUtils.is_container_node(canvas_node.core_node.type):
|
||||||
for file, data in file_config.items():
|
continue
|
||||||
|
if not canvas_node.service_file_configs:
|
||||||
|
continue
|
||||||
|
node_id = canvas_node.core_node.id
|
||||||
|
for service, file_configs in canvas_node.service_file_configs.items():
|
||||||
|
for file, data in file_configs.items():
|
||||||
config_proto = ServiceFileConfig(
|
config_proto = ServiceFileConfig(
|
||||||
node_id=node_id, service=service, file=file, data=data
|
node_id=node_id, service=service, file=file, data=data
|
||||||
)
|
)
|
||||||
|
@ -980,8 +988,13 @@ class CoreClient:
|
||||||
self
|
self
|
||||||
) -> List[configservices_pb2.ConfigServiceConfig]:
|
) -> List[configservices_pb2.ConfigServiceConfig]:
|
||||||
config_service_protos = []
|
config_service_protos = []
|
||||||
for node_id, node_config in self.config_service_configs.items():
|
for canvas_node in self.canvas_nodes.values():
|
||||||
for name, service_config in node_config.items():
|
if not NodeUtils.is_container_node(canvas_node.core_node.type):
|
||||||
|
continue
|
||||||
|
if not canvas_node.config_service_configs:
|
||||||
|
continue
|
||||||
|
node_id = canvas_node.core_node.id
|
||||||
|
for name, service_config in canvas_node.config_service_configs.items():
|
||||||
config = service_config.get("config", {})
|
config = service_config.get("config", {})
|
||||||
config_proto = configservices_pb2.ConfigServiceConfig(
|
config_proto = configservices_pb2.ConfigServiceConfig(
|
||||||
node_id=node_id,
|
node_id=node_id,
|
||||||
|
@ -997,40 +1010,34 @@ class CoreClient:
|
||||||
return self.client.node_command(self.session_id, node_id, self.observer).output
|
return self.client.node_command(self.session_id, node_id, self.observer).output
|
||||||
|
|
||||||
def get_wlan_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
|
def get_wlan_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
|
||||||
config = self.wlan_configs.get(node_id)
|
response = self.client.get_wlan_config(self.session_id, node_id)
|
||||||
if not config:
|
config = response.config
|
||||||
response = self.client.get_wlan_config(self.session_id, node_id)
|
|
||||||
config = response.config
|
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"get wlan configuration from node %s, result configuration: %s",
|
"get wlan configuration from node %s, result configuration: %s",
|
||||||
node_id,
|
node_id,
|
||||||
config,
|
config,
|
||||||
)
|
)
|
||||||
return config
|
return dict(config)
|
||||||
|
|
||||||
def get_mobility_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
|
def get_mobility_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
|
||||||
config = self.mobility_configs.get(node_id)
|
response = self.client.get_mobility_config(self.session_id, node_id)
|
||||||
if not config:
|
config = response.config
|
||||||
response = self.client.get_mobility_config(self.session_id, node_id)
|
|
||||||
config = response.config
|
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"get mobility config from node %s, result configuration: %s",
|
"get mobility config from node %s, result configuration: %s",
|
||||||
node_id,
|
node_id,
|
||||||
config,
|
config,
|
||||||
)
|
)
|
||||||
return config
|
return dict(config)
|
||||||
|
|
||||||
def get_emane_model_config(
|
def get_emane_model_config(
|
||||||
self, node_id: int, model: str, interface: int = None
|
self, node_id: int, model: str, interface: int = None
|
||||||
) -> Dict[str, common_pb2.ConfigOption]:
|
) -> Dict[str, common_pb2.ConfigOption]:
|
||||||
config = self.emane_model_configs.get((node_id, model, interface))
|
if interface is None:
|
||||||
if not config:
|
interface = -1
|
||||||
if interface is None:
|
response = self.client.get_emane_model_config(
|
||||||
interface = -1
|
self.session_id, node_id, model, interface
|
||||||
response = self.client.get_emane_model_config(
|
)
|
||||||
self.session_id, node_id, model, interface
|
config = response.config
|
||||||
)
|
|
||||||
config = response.config
|
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"get emane model config: node id: %s, EMANE model: %s, interface: %s, config: %s",
|
"get emane model config: node id: %s, EMANE model: %s, interface: %s, config: %s",
|
||||||
node_id,
|
node_id,
|
||||||
|
@ -1038,54 +1045,7 @@ class CoreClient:
|
||||||
interface,
|
interface,
|
||||||
config,
|
config,
|
||||||
)
|
)
|
||||||
return config
|
return dict(config)
|
||||||
|
|
||||||
def set_emane_model_config(
|
|
||||||
self,
|
|
||||||
node_id: int,
|
|
||||||
model: str,
|
|
||||||
config: Dict[str, common_pb2.ConfigOption],
|
|
||||||
interface: int = None,
|
|
||||||
):
|
|
||||||
logging.info(
|
|
||||||
"set emane model config: node id: %s, EMANE Model: %s, interface: %s, config: %s",
|
|
||||||
node_id,
|
|
||||||
model,
|
|
||||||
interface,
|
|
||||||
config,
|
|
||||||
)
|
|
||||||
self.emane_model_configs[(node_id, model, interface)] = config
|
|
||||||
|
|
||||||
def copy_node_service(self, _from: int, _to: int):
|
|
||||||
services = self.canvas_nodes[_from].core_node.services
|
|
||||||
self.canvas_nodes[_to].core_node.services[:] = services
|
|
||||||
logging.debug("copying node %s service to node %s", _from, _to)
|
|
||||||
|
|
||||||
def copy_node_config(self, _from: int, _to: int):
|
|
||||||
node_type = self.canvas_nodes[_from].core_node.type
|
|
||||||
if node_type == core_pb2.NodeType.DEFAULT:
|
|
||||||
services = self.canvas_nodes[_from].core_node.services
|
|
||||||
self.canvas_nodes[_to].core_node.services[:] = services
|
|
||||||
config = self.service_configs.get(_from)
|
|
||||||
if config:
|
|
||||||
self.service_configs[_to] = config
|
|
||||||
file_configs = self.file_configs.get(_from)
|
|
||||||
if file_configs:
|
|
||||||
for key, value in file_configs.items():
|
|
||||||
if _to not in self.file_configs:
|
|
||||||
self.file_configs[_to] = {}
|
|
||||||
self.file_configs[_to][key] = value
|
|
||||||
elif node_type == core_pb2.NodeType.WIRELESS_LAN:
|
|
||||||
config = self.wlan_configs.get(_from)
|
|
||||||
if config:
|
|
||||||
self.wlan_configs[_to] = config
|
|
||||||
config = self.mobility_configs.get(_from)
|
|
||||||
if config:
|
|
||||||
self.mobility_configs[_to] = config
|
|
||||||
elif node_type == core_pb2.NodeType.EMANE:
|
|
||||||
config = self.emane_model_configs.get(_from)
|
|
||||||
if config:
|
|
||||||
self.emane_model_configs[_to] = config
|
|
||||||
|
|
||||||
def service_been_modified(self, node_id: int) -> bool:
|
def service_been_modified(self, node_id: int) -> bool:
|
||||||
return node_id in self.modified_service_nodes
|
return node_id in self.modified_service_nodes
|
||||||
|
|
|
@ -16,21 +16,26 @@ from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
from core.gui.graph.node import CanvasNode
|
||||||
|
|
||||||
|
|
||||||
class ConfigServiceConfigDialog(Dialog):
|
class ConfigServiceConfigDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, master: Any, app: "Application", service_name: str, node_id: int
|
self,
|
||||||
|
master: Any,
|
||||||
|
app: "Application",
|
||||||
|
service_name: str,
|
||||||
|
canvas_node: "CanvasNode",
|
||||||
|
node_id: int,
|
||||||
):
|
):
|
||||||
title = f"{service_name} Config Service"
|
title = f"{service_name} Config Service"
|
||||||
super().__init__(master, app, title, modal=True)
|
super().__init__(master, app, title, modal=True)
|
||||||
self.master = master
|
self.master = master
|
||||||
self.app = app
|
self.app = app
|
||||||
self.core = app.core
|
self.core = app.core
|
||||||
|
self.canvas_node = canvas_node
|
||||||
self.node_id = node_id
|
self.node_id = node_id
|
||||||
self.service_name = service_name
|
self.service_name = service_name
|
||||||
self.service_configs = app.core.config_service_configs
|
|
||||||
|
|
||||||
self.radiovar = tk.IntVar()
|
self.radiovar = tk.IntVar()
|
||||||
self.radiovar.set(2)
|
self.radiovar.set(2)
|
||||||
self.directories = []
|
self.directories = []
|
||||||
|
@ -95,9 +100,9 @@ class ConfigServiceConfigDialog(Dialog):
|
||||||
self.modes = sorted(x.name for x in response.modes)
|
self.modes = sorted(x.name for x in response.modes)
|
||||||
self.mode_configs = {x.name: x.config for x in response.modes}
|
self.mode_configs = {x.name: x.config for x in response.modes}
|
||||||
|
|
||||||
node_configs = self.service_configs.get(self.node_id, {})
|
service_config = self.canvas_node.config_service_configs.get(
|
||||||
service_config = node_configs.get(self.service_name, {})
|
self.service_name, {}
|
||||||
|
)
|
||||||
self.config = response.config
|
self.config = response.config
|
||||||
self.default_config = {x.name: x.value for x in self.config.values()}
|
self.default_config = {x.name: x.value for x in self.config.values()}
|
||||||
custom_config = service_config.get("config")
|
custom_config = service_config.get("config")
|
||||||
|
@ -313,15 +318,15 @@ class ConfigServiceConfigDialog(Dialog):
|
||||||
def click_apply(self):
|
def click_apply(self):
|
||||||
current_listbox = self.master.current.listbox
|
current_listbox = self.master.current.listbox
|
||||||
if not self.is_custom():
|
if not self.is_custom():
|
||||||
if self.node_id in self.service_configs:
|
self.canvas_node.config_service_configs.pop(self.service_name, None)
|
||||||
self.service_configs[self.node_id].pop(self.service_name, None)
|
|
||||||
current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
|
current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node_config = self.service_configs.setdefault(self.node_id, {})
|
service_config = self.canvas_node.config_service_configs.setdefault(
|
||||||
service_config = node_config.setdefault(self.service_name, {})
|
self.service_name, {}
|
||||||
|
)
|
||||||
if self.config_frame:
|
if self.config_frame:
|
||||||
self.config_frame.parse_config()
|
self.config_frame.parse_config()
|
||||||
service_config["config"] = {
|
service_config["config"] = {
|
||||||
|
@ -365,9 +370,10 @@ class ConfigServiceConfigDialog(Dialog):
|
||||||
return has_custom_templates or has_custom_config
|
return has_custom_templates or has_custom_config
|
||||||
|
|
||||||
def click_defaults(self):
|
def click_defaults(self):
|
||||||
if self.node_id in self.service_configs:
|
self.canvas_node.config_service_configs.pop(self.service_name, None)
|
||||||
node_config = self.service_configs.get(self.node_id, {})
|
logging.info(
|
||||||
node_config.pop(self.service_name, None)
|
"cleared config service config: %s", self.canvas_node.config_service_configs
|
||||||
|
)
|
||||||
self.temp_service_files = dict(self.original_service_files)
|
self.temp_service_files = dict(self.original_service_files)
|
||||||
filename = self.templates_combobox.get()
|
filename = self.templates_combobox.get()
|
||||||
self.template_text.text.delete(1.0, "end")
|
self.template_text.text.delete(1.0, "end")
|
||||||
|
|
|
@ -8,7 +8,6 @@ from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.errors import show_grpc_error
|
from core.gui.errors import show_grpc_error
|
||||||
from core.gui.images import ImageEnum, Images
|
from core.gui.images import ImageEnum, Images
|
||||||
|
@ -56,20 +55,30 @@ class EmaneModelDialog(Dialog):
|
||||||
self,
|
self,
|
||||||
master: Any,
|
master: Any,
|
||||||
app: "Application",
|
app: "Application",
|
||||||
node: core_pb2.Node,
|
canvas_node: "CanvasNode",
|
||||||
model: str,
|
model: str,
|
||||||
interface: int = None,
|
interface: int = None,
|
||||||
):
|
):
|
||||||
super().__init__(master, app, f"{node.name} {model} Configuration", modal=True)
|
super().__init__(
|
||||||
self.node = node
|
master,
|
||||||
|
app,
|
||||||
|
f"{canvas_node.core_node.name} {model} Configuration",
|
||||||
|
modal=True,
|
||||||
|
)
|
||||||
|
self.canvas_node = canvas_node
|
||||||
|
self.node = canvas_node.core_node
|
||||||
self.model = f"emane_{model}"
|
self.model = f"emane_{model}"
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
self.config_frame = None
|
self.config_frame = None
|
||||||
self.has_error = False
|
self.has_error = False
|
||||||
try:
|
try:
|
||||||
self.config = self.app.core.get_emane_model_config(
|
self.config = self.canvas_node.emane_model_configs.get(
|
||||||
self.node.id, self.model, self.interface
|
(self.model, self.interface)
|
||||||
)
|
)
|
||||||
|
if not self.config:
|
||||||
|
self.config = self.app.core.get_emane_model_config(
|
||||||
|
self.node.id, self.model, self.interface
|
||||||
|
)
|
||||||
self.draw()
|
self.draw()
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
show_grpc_error(e, self.app, self.app)
|
show_grpc_error(e, self.app, self.app)
|
||||||
|
@ -98,9 +107,8 @@ class EmaneModelDialog(Dialog):
|
||||||
|
|
||||||
def click_apply(self):
|
def click_apply(self):
|
||||||
self.config_frame.parse_config()
|
self.config_frame.parse_config()
|
||||||
self.app.core.set_emane_model_config(
|
key = (self.model, self.interface)
|
||||||
self.node.id, self.model, self.config, self.interface
|
self.canvas_node.emane_model_configs[key] = self.config
|
||||||
)
|
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
@ -224,9 +232,7 @@ class EmaneConfigDialog(Dialog):
|
||||||
draw emane model configuration
|
draw emane model configuration
|
||||||
"""
|
"""
|
||||||
model_name = self.emane_model.get()
|
model_name = self.emane_model.get()
|
||||||
dialog = EmaneModelDialog(
|
dialog = EmaneModelDialog(self, self.app, self.canvas_node, model_name)
|
||||||
self, self.app, self.canvas_node.core_node, model_name
|
|
||||||
)
|
|
||||||
if not dialog.has_error:
|
if not dialog.has_error:
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,9 @@ class MobilityConfigDialog(Dialog):
|
||||||
self.config_frame = None
|
self.config_frame = None
|
||||||
self.has_error = False
|
self.has_error = False
|
||||||
try:
|
try:
|
||||||
self.config = self.app.core.get_mobility_config(self.node.id)
|
self.config = self.canvas_node.mobility_config
|
||||||
|
if not self.config:
|
||||||
|
self.config = self.app.core.get_mobility_config(self.node.id)
|
||||||
self.draw()
|
self.draw()
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.has_error = True
|
self.has_error = True
|
||||||
|
@ -60,5 +62,5 @@ class MobilityConfigDialog(Dialog):
|
||||||
|
|
||||||
def click_apply(self):
|
def click_apply(self):
|
||||||
self.config_frame.parse_config()
|
self.config_frame.parse_config()
|
||||||
self.app.core.mobility_configs[self.node.id] = self.config
|
self.canvas_node.mobility_config = self.config
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
|
@ -48,8 +48,9 @@ class MobilityPlayer:
|
||||||
self.dialog.show()
|
self.dialog.show()
|
||||||
|
|
||||||
def handle_close(self):
|
def handle_close(self):
|
||||||
self.dialog.destroy()
|
if self.dialog:
|
||||||
self.dialog = None
|
self.dialog.destroy()
|
||||||
|
self.dialog = None
|
||||||
|
|
||||||
def set_play(self):
|
def set_play(self):
|
||||||
self.state = MobilityAction.START
|
self.state = MobilityAction.START
|
||||||
|
|
|
@ -290,7 +290,9 @@ class NodeConfigDialog(Dialog):
|
||||||
button.grid(row=0, column=1, sticky="ew")
|
button.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
def click_emane_config(self, emane_model: str, interface_id: int):
|
def click_emane_config(self, emane_model: str, interface_id: int):
|
||||||
dialog = EmaneModelDialog(self, self.app, self.node, emane_model, interface_id)
|
dialog = EmaneModelDialog(
|
||||||
|
self, self.app, self.canvas_node, emane_model, interface_id
|
||||||
|
)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def click_icon(self):
|
def click_icon(self):
|
||||||
|
|
|
@ -70,12 +70,10 @@ class NodeConfigServiceDialog(Dialog):
|
||||||
label_frame.grid(row=0, column=2, sticky="nsew")
|
label_frame.grid(row=0, column=2, sticky="nsew")
|
||||||
label_frame.rowconfigure(0, weight=1)
|
label_frame.rowconfigure(0, weight=1)
|
||||||
label_frame.columnconfigure(0, weight=1)
|
label_frame.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
self.current = ListboxScroll(label_frame)
|
self.current = ListboxScroll(label_frame)
|
||||||
self.current.grid(sticky="nsew")
|
self.current.grid(sticky="nsew")
|
||||||
for service in sorted(self.current_services):
|
self.draw_current_services()
|
||||||
self.current.listbox.insert(tk.END, service)
|
|
||||||
if self.is_custom_service(service):
|
|
||||||
self.current.listbox.itemconfig(tk.END, bg="green")
|
|
||||||
|
|
||||||
frame = ttk.Frame(self.top)
|
frame = ttk.Frame(self.top)
|
||||||
frame.grid(stick="ew")
|
frame.grid(stick="ew")
|
||||||
|
@ -108,24 +106,22 @@ class NodeConfigServiceDialog(Dialog):
|
||||||
self.current_services.add(name)
|
self.current_services.add(name)
|
||||||
elif not var.get() and name in self.current_services:
|
elif not var.get() and name in self.current_services:
|
||||||
self.current_services.remove(name)
|
self.current_services.remove(name)
|
||||||
self.current.listbox.delete(0, tk.END)
|
self.draw_current_services()
|
||||||
for name in sorted(self.current_services):
|
|
||||||
self.current.listbox.insert(tk.END, name)
|
|
||||||
if self.is_custom_service(name):
|
|
||||||
self.current.listbox.itemconfig(tk.END, bg="green")
|
|
||||||
self.canvas_node.core_node.config_services[:] = self.current_services
|
self.canvas_node.core_node.config_services[:] = self.current_services
|
||||||
|
|
||||||
def click_configure(self):
|
def click_configure(self):
|
||||||
current_selection = self.current.listbox.curselection()
|
current_selection = self.current.listbox.curselection()
|
||||||
if len(current_selection):
|
if len(current_selection):
|
||||||
dialog = ConfigServiceConfigDialog(
|
dialog = ConfigServiceConfigDialog(
|
||||||
master=self,
|
self,
|
||||||
app=self.app,
|
self.app,
|
||||||
service_name=self.current.listbox.get(current_selection[0]),
|
self.current.listbox.get(current_selection[0]),
|
||||||
node_id=self.node_id,
|
self.canvas_node,
|
||||||
|
self.node_id,
|
||||||
)
|
)
|
||||||
if not dialog.has_error:
|
if not dialog.has_error:
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
self.draw_current_services()
|
||||||
else:
|
else:
|
||||||
messagebox.showinfo(
|
messagebox.showinfo(
|
||||||
"Config Service Configuration",
|
"Config Service Configuration",
|
||||||
|
@ -133,6 +129,13 @@ class NodeConfigServiceDialog(Dialog):
|
||||||
parent=self,
|
parent=self,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def draw_current_services(self):
|
||||||
|
self.current.listbox.delete(0, tk.END)
|
||||||
|
for name in sorted(self.current_services):
|
||||||
|
self.current.listbox.insert(tk.END, name)
|
||||||
|
if self.is_custom_service(name):
|
||||||
|
self.current.listbox.itemconfig(tk.END, bg="green")
|
||||||
|
|
||||||
def click_save(self):
|
def click_save(self):
|
||||||
self.canvas_node.core_node.config_services[:] = self.current_services
|
self.canvas_node.core_node.config_services[:] = self.current_services
|
||||||
logging.info(
|
logging.info(
|
||||||
|
@ -156,9 +159,4 @@ class NodeConfigServiceDialog(Dialog):
|
||||||
return
|
return
|
||||||
|
|
||||||
def is_custom_service(self, service: str) -> bool:
|
def is_custom_service(self, service: str) -> bool:
|
||||||
node_configs = self.app.core.config_service_configs.get(self.node_id, {})
|
return service in self.canvas_node.config_service_configs
|
||||||
service_config = node_configs.get(service)
|
|
||||||
if node_configs and service_config:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
|
@ -135,10 +135,11 @@ class NodeServiceDialog(Dialog):
|
||||||
current_selection = self.current.listbox.curselection()
|
current_selection = self.current.listbox.curselection()
|
||||||
if len(current_selection):
|
if len(current_selection):
|
||||||
dialog = ServiceConfigDialog(
|
dialog = ServiceConfigDialog(
|
||||||
master=self,
|
self,
|
||||||
app=self.app,
|
self.app,
|
||||||
service_name=self.current.listbox.get(current_selection[0]),
|
self.current.listbox.get(current_selection[0]),
|
||||||
node_id=self.node_id,
|
self.canvas_node,
|
||||||
|
self.node_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# if error occurred when creating ServiceConfigDialog, don't show the dialog
|
# if error occurred when creating ServiceConfigDialog, don't show the dialog
|
||||||
|
@ -182,14 +183,6 @@ class NodeServiceDialog(Dialog):
|
||||||
return
|
return
|
||||||
|
|
||||||
def is_custom_service(self, service: str) -> bool:
|
def is_custom_service(self, service: str) -> bool:
|
||||||
service_configs = self.app.core.service_configs
|
has_service_config = service in self.canvas_node.service_configs
|
||||||
file_configs = self.app.core.file_configs
|
has_file_config = service in self.canvas_node.service_file_configs
|
||||||
if self.node_id in service_configs and service in service_configs[self.node_id]:
|
return has_service_config or has_file_config
|
||||||
return True
|
|
||||||
if (
|
|
||||||
self.node_id in file_configs
|
|
||||||
and service in file_configs[self.node_id]
|
|
||||||
and file_configs[self.node_id][service]
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
|
@ -16,22 +16,26 @@ from core.gui.widgets import CodeText, ListboxScroll
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
from core.gui.graph.node import CanvasNode
|
||||||
|
|
||||||
|
|
||||||
class ServiceConfigDialog(Dialog):
|
class ServiceConfigDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, master: Any, app: "Application", service_name: str, node_id: int
|
self,
|
||||||
|
master: Any,
|
||||||
|
app: "Application",
|
||||||
|
service_name: str,
|
||||||
|
canvas_node: "CanvasNode",
|
||||||
|
node_id: int,
|
||||||
):
|
):
|
||||||
title = f"{service_name} Service"
|
title = f"{service_name} Service"
|
||||||
super().__init__(master, app, title, modal=True)
|
super().__init__(master, app, title, modal=True)
|
||||||
self.master = master
|
self.master = master
|
||||||
self.app = app
|
self.app = app
|
||||||
self.core = app.core
|
self.core = app.core
|
||||||
|
self.canvas_node = canvas_node
|
||||||
self.node_id = node_id
|
self.node_id = node_id
|
||||||
self.service_name = service_name
|
self.service_name = service_name
|
||||||
self.service_configs = app.core.service_configs
|
|
||||||
self.file_configs = app.core.file_configs
|
|
||||||
|
|
||||||
self.radiovar = tk.IntVar()
|
self.radiovar = tk.IntVar()
|
||||||
self.radiovar.set(2)
|
self.radiovar.set(2)
|
||||||
self.metadata = ""
|
self.metadata = ""
|
||||||
|
@ -54,7 +58,6 @@ class ServiceConfigDialog(Dialog):
|
||||||
ImageEnum.DOCUMENTNEW, int(16 * app.app_scale)
|
ImageEnum.DOCUMENTNEW, int(16 * app.app_scale)
|
||||||
)
|
)
|
||||||
self.editdelete_img = Images.get(ImageEnum.EDITDELETE, int(16 * app.app_scale))
|
self.editdelete_img = Images.get(ImageEnum.EDITDELETE, int(16 * app.app_scale))
|
||||||
|
|
||||||
self.notebook = None
|
self.notebook = None
|
||||||
self.metadata_entry = None
|
self.metadata_entry = None
|
||||||
self.filename_combobox = None
|
self.filename_combobox = None
|
||||||
|
@ -70,9 +73,7 @@ class ServiceConfigDialog(Dialog):
|
||||||
self.default_config = None
|
self.default_config = None
|
||||||
self.temp_service_files = {}
|
self.temp_service_files = {}
|
||||||
self.modified_files = set()
|
self.modified_files = set()
|
||||||
|
|
||||||
self.has_error = False
|
self.has_error = False
|
||||||
|
|
||||||
self.load()
|
self.load()
|
||||||
if not self.has_error:
|
if not self.has_error:
|
||||||
self.draw()
|
self.draw()
|
||||||
|
@ -87,8 +88,8 @@ class ServiceConfigDialog(Dialog):
|
||||||
self.default_validate = default_config.validate[:]
|
self.default_validate = default_config.validate[:]
|
||||||
self.default_shutdown = default_config.shutdown[:]
|
self.default_shutdown = default_config.shutdown[:]
|
||||||
self.default_directories = default_config.dirs[:]
|
self.default_directories = default_config.dirs[:]
|
||||||
custom_service_config = self.service_configs.get(self.node_id, {}).get(
|
custom_service_config = self.canvas_node.service_configs.get(
|
||||||
self.service_name, None
|
self.service_name
|
||||||
)
|
)
|
||||||
self.default_config = default_config
|
self.default_config = default_config
|
||||||
service_config = (
|
service_config = (
|
||||||
|
@ -111,10 +112,11 @@ class ServiceConfigDialog(Dialog):
|
||||||
for x in default_config.configs
|
for x in default_config.configs
|
||||||
}
|
}
|
||||||
self.temp_service_files = dict(self.original_service_files)
|
self.temp_service_files = dict(self.original_service_files)
|
||||||
file_config = self.file_configs.get(self.node_id, {}).get(
|
|
||||||
|
file_configs = self.canvas_node.service_file_configs.get(
|
||||||
self.service_name, {}
|
self.service_name, {}
|
||||||
)
|
)
|
||||||
for file, data in file_config.items():
|
for file, data in file_configs.items():
|
||||||
self.temp_service_files[file] = data
|
self.temp_service_files[file] = data
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.has_error = True
|
self.has_error = True
|
||||||
|
@ -449,7 +451,7 @@ class ServiceConfigDialog(Dialog):
|
||||||
and not self.has_new_files()
|
and not self.has_new_files()
|
||||||
and not self.is_custom_directory()
|
and not self.is_custom_directory()
|
||||||
):
|
):
|
||||||
self.service_configs.get(self.node_id, {}).pop(self.service_name, None)
|
self.canvas_node.service_configs.pop(self.service_name, None)
|
||||||
self.current_service_color("")
|
self.current_service_color("")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
return
|
return
|
||||||
|
@ -470,17 +472,13 @@ class ServiceConfigDialog(Dialog):
|
||||||
validations=validate,
|
validations=validate,
|
||||||
shutdowns=shutdown,
|
shutdowns=shutdown,
|
||||||
)
|
)
|
||||||
if self.node_id not in self.service_configs:
|
self.canvas_node.service_configs[self.service_name] = config
|
||||||
self.service_configs[self.node_id] = {}
|
|
||||||
self.service_configs[self.node_id][self.service_name] = config
|
|
||||||
for file in self.modified_files:
|
for file in self.modified_files:
|
||||||
if self.node_id not in self.file_configs:
|
file_configs = self.canvas_node.service_file_configs.setdefault(
|
||||||
self.file_configs[self.node_id] = {}
|
self.service_name, {}
|
||||||
if self.service_name not in self.file_configs[self.node_id]:
|
)
|
||||||
self.file_configs[self.node_id][self.service_name] = {}
|
file_configs[file] = self.temp_service_files[file]
|
||||||
self.file_configs[self.node_id][self.service_name][
|
# TODO: check if this is really needed
|
||||||
file
|
|
||||||
] = self.temp_service_files[file]
|
|
||||||
self.app.core.set_node_service_file(
|
self.app.core.set_node_service_file(
|
||||||
self.node_id, self.service_name, file, self.temp_service_files[file]
|
self.node_id, self.service_name, file, self.temp_service_files[file]
|
||||||
)
|
)
|
||||||
|
@ -526,8 +524,9 @@ class ServiceConfigDialog(Dialog):
|
||||||
clears out any custom configuration permanently
|
clears out any custom configuration permanently
|
||||||
"""
|
"""
|
||||||
# clear coreclient data
|
# clear coreclient data
|
||||||
self.service_configs.get(self.node_id, {}).pop(self.service_name, None)
|
self.canvas_node.service_configs.pop(self.service_name, None)
|
||||||
self.file_configs.get(self.node_id, {}).pop(self.service_name, None)
|
file_configs = self.canvas_node.service_file_configs.pop(self.service_name, {})
|
||||||
|
file_configs.pop(self.service_name, None)
|
||||||
self.temp_service_files = dict(self.original_service_files)
|
self.temp_service_files = dict(self.original_service_files)
|
||||||
self.modified_files.clear()
|
self.modified_files.clear()
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,9 @@ class WlanConfigDialog(Dialog):
|
||||||
self.ranges = {}
|
self.ranges = {}
|
||||||
self.positive_int = self.app.master.register(self.validate_and_update)
|
self.positive_int = self.app.master.register(self.validate_and_update)
|
||||||
try:
|
try:
|
||||||
self.config = self.app.core.get_wlan_config(self.node.id)
|
self.config = self.canvas_node.wlan_config
|
||||||
|
if not self.config:
|
||||||
|
self.config = self.app.core.get_wlan_config(self.node.id)
|
||||||
self.init_draw_range()
|
self.init_draw_range()
|
||||||
self.draw()
|
self.draw()
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
|
@ -83,7 +85,7 @@ class WlanConfigDialog(Dialog):
|
||||||
retrieve user's wlan configuration and store the new configuration values
|
retrieve user's wlan configuration and store the new configuration values
|
||||||
"""
|
"""
|
||||||
config = self.config_frame.parse_config()
|
config = self.config_frame.parse_config()
|
||||||
self.app.core.wlan_configs[self.node.id] = self.config
|
self.canvas_node.wlan_config = self.config
|
||||||
if self.app.core.is_runtime():
|
if self.app.core.is_runtime():
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session_id
|
||||||
self.app.core.client.set_wlan_config(session_id, self.node.id, config)
|
self.app.core.client.set_wlan_config(session_id, self.node.id, config)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
from copy import deepcopy
|
||||||
from tkinter import BooleanVar
|
from tkinter import BooleanVar
|
||||||
from typing import TYPE_CHECKING, Tuple
|
from typing import TYPE_CHECKING, Tuple
|
||||||
|
|
||||||
|
@ -304,7 +305,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
token = create_edge_token(canvas_node_one.id, canvas_node_two.id)
|
token = create_edge_token(canvas_node_one.id, canvas_node_two.id)
|
||||||
|
|
||||||
if link.type == core_pb2.LinkType.WIRELESS:
|
if link.type == core_pb2.LinkType.WIRELESS:
|
||||||
self.add_wireless_edge(canvas_node_one, canvas_node_two)
|
self.add_wireless_edge(canvas_node_one, canvas_node_two, link)
|
||||||
else:
|
else:
|
||||||
if token not in self.edges:
|
if token not in self.edges:
|
||||||
src_pos = (node_one.position.x, node_one.position.y)
|
src_pos = (node_one.position.x, node_one.position.y)
|
||||||
|
@ -420,7 +421,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.mode = GraphMode.NODE
|
self.mode = GraphMode.NODE
|
||||||
self.selected = None
|
self.selected = None
|
||||||
|
|
||||||
def handle_edge_release(self, event: tk.Event):
|
def handle_edge_release(self, _event: tk.Event):
|
||||||
edge = self.drawing_edge
|
edge = self.drawing_edge
|
||||||
self.drawing_edge = None
|
self.drawing_edge = None
|
||||||
|
|
||||||
|
@ -701,7 +702,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
else:
|
else:
|
||||||
self.hide_context()
|
self.hide_context()
|
||||||
|
|
||||||
def press_delete(self, event: tk.Event):
|
def press_delete(self, _event: tk.Event):
|
||||||
"""
|
"""
|
||||||
delete selected nodes and any data that relates to it
|
delete selected nodes and any data that relates to it
|
||||||
"""
|
"""
|
||||||
|
@ -894,61 +895,72 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.core.create_link(edge, source, dest)
|
self.core.create_link(edge, source, dest)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
if self.app.core.is_runtime():
|
if self.core.is_runtime():
|
||||||
logging.info("copy is disabled during runtime state")
|
logging.info("copy is disabled during runtime state")
|
||||||
return
|
return
|
||||||
if self.selection:
|
if self.selection:
|
||||||
logging.debug("to copy %s nodes", len(self.selection))
|
logging.info("to copy nodes: %s", self.selection)
|
||||||
self.to_copy = self.selection.keys()
|
self.to_copy.clear()
|
||||||
|
for node_id in self.selection.keys():
|
||||||
|
canvas_node = self.nodes[node_id]
|
||||||
|
self.to_copy.append(canvas_node)
|
||||||
|
|
||||||
def paste(self):
|
def paste(self):
|
||||||
if self.app.core.is_runtime():
|
if self.core.is_runtime():
|
||||||
logging.info("paste is disabled during runtime state")
|
logging.info("paste is disabled during runtime state")
|
||||||
return
|
return
|
||||||
# maps original node canvas id to copy node canvas id
|
# maps original node canvas id to copy node canvas id
|
||||||
copy_map = {}
|
copy_map = {}
|
||||||
# the edges that will be copy over
|
# the edges that will be copy over
|
||||||
to_copy_edges = []
|
to_copy_edges = []
|
||||||
for canvas_nid in self.to_copy:
|
for canvas_node in self.to_copy:
|
||||||
core_node = self.nodes[canvas_nid].core_node
|
core_node = canvas_node.core_node
|
||||||
actual_x = core_node.position.x + 50
|
actual_x = core_node.position.x + 50
|
||||||
actual_y = core_node.position.y + 50
|
actual_y = core_node.position.y + 50
|
||||||
scaled_x, scaled_y = self.get_scaled_coords(actual_x, actual_y)
|
scaled_x, scaled_y = self.get_scaled_coords(actual_x, actual_y)
|
||||||
|
|
||||||
copy = self.core.create_node(
|
copy = self.core.create_node(
|
||||||
actual_x, actual_y, core_node.type, core_node.model
|
actual_x, actual_y, core_node.type, core_node.model
|
||||||
)
|
)
|
||||||
node = CanvasNode(
|
node = CanvasNode(self.master, scaled_x, scaled_y, copy, canvas_node.image)
|
||||||
self.master, scaled_x, scaled_y, copy, self.nodes[canvas_nid].image
|
|
||||||
)
|
# copy configurations and services
|
||||||
|
node.core_node.services[:] = canvas_node.core_node.services
|
||||||
|
node.core_node.config_services[:] = canvas_node.core_node.config_services
|
||||||
|
node.emane_model_configs = deepcopy(canvas_node.emane_model_configs)
|
||||||
|
node.wlan_config = deepcopy(canvas_node.wlan_config)
|
||||||
|
node.mobility_config = deepcopy(canvas_node.mobility_config)
|
||||||
|
node.service_configs = deepcopy(canvas_node.service_configs)
|
||||||
|
node.service_file_configs = deepcopy(canvas_node.service_file_configs)
|
||||||
|
node.config_service_configs = deepcopy(canvas_node.config_service_configs)
|
||||||
|
|
||||||
# add new node to modified_service_nodes set if that set contains the
|
# add new node to modified_service_nodes set if that set contains the
|
||||||
# to_copy node
|
# to_copy node
|
||||||
if self.app.core.service_been_modified(core_node.id):
|
if self.core.service_been_modified(core_node.id):
|
||||||
self.app.core.modified_service_nodes.add(copy.id)
|
self.core.modified_service_nodes.add(copy.id)
|
||||||
|
|
||||||
copy_map[canvas_nid] = node.id
|
copy_map[canvas_node.id] = node.id
|
||||||
self.core.canvas_nodes[copy.id] = node
|
self.core.canvas_nodes[copy.id] = node
|
||||||
self.nodes[node.id] = node
|
self.nodes[node.id] = node
|
||||||
self.core.copy_node_config(core_node.id, copy.id)
|
for edge in canvas_node.edges:
|
||||||
|
|
||||||
edges = self.nodes[canvas_nid].edges
|
|
||||||
for edge in edges:
|
|
||||||
if edge.src not in self.to_copy or edge.dst not in self.to_copy:
|
if edge.src not in self.to_copy or edge.dst not in self.to_copy:
|
||||||
if canvas_nid == edge.src:
|
if canvas_node.id == edge.src:
|
||||||
self.create_edge(node, self.nodes[edge.dst])
|
dst_node = self.nodes[edge.dst]
|
||||||
elif canvas_nid == edge.dst:
|
self.create_edge(node, dst_node)
|
||||||
self.create_edge(self.nodes[edge.src], node)
|
elif canvas_node.id == edge.dst:
|
||||||
|
src_node = self.nodes[edge.src]
|
||||||
|
self.create_edge(src_node, node)
|
||||||
else:
|
else:
|
||||||
to_copy_edges.append(edge)
|
to_copy_edges.append(edge)
|
||||||
|
|
||||||
# copy link and link config
|
# copy link and link config
|
||||||
for edge in to_copy_edges:
|
for edge in to_copy_edges:
|
||||||
source_node_copy = self.nodes[copy_map[edge.token[0]]]
|
src_node_id = copy_map[edge.token[0]]
|
||||||
dest_node_copy = self.nodes[copy_map[edge.token[1]]]
|
dst_node_id = copy_map[edge.token[1]]
|
||||||
self.create_edge(source_node_copy, dest_node_copy)
|
src_node_copy = self.nodes[src_node_id]
|
||||||
copy_edge = self.edges[
|
dst_node_copy = self.nodes[dst_node_id]
|
||||||
create_edge_token(source_node_copy.id, dest_node_copy.id)
|
self.create_edge(src_node_copy, dst_node_copy)
|
||||||
]
|
token = create_edge_token(src_node_copy.id, dst_node_copy.id)
|
||||||
|
copy_edge = self.edges[token]
|
||||||
copy_link = copy_edge.link
|
copy_link = copy_edge.link
|
||||||
options = edge.link.options
|
options = edge.link.options
|
||||||
copy_link.options.CopyFrom(options)
|
copy_link.options.CopyFrom(options)
|
||||||
|
|
|
@ -58,6 +58,13 @@ class CanvasNode:
|
||||||
self.wireless_edges = set()
|
self.wireless_edges = set()
|
||||||
self.antennas = []
|
self.antennas = []
|
||||||
self.antenna_images = {}
|
self.antenna_images = {}
|
||||||
|
# possible configurations
|
||||||
|
self.emane_model_configs = {}
|
||||||
|
self.wlan_config = {}
|
||||||
|
self.mobility_config = {}
|
||||||
|
self.service_configs = {}
|
||||||
|
self.service_file_configs = {}
|
||||||
|
self.config_service_configs = {}
|
||||||
self.setup_bindings()
|
self.setup_bindings()
|
||||||
|
|
||||||
def setup_bindings(self):
|
def setup_bindings(self):
|
||||||
|
@ -225,12 +232,16 @@ class CanvasNode:
|
||||||
context.add_command(label="Select Members", state=tk.DISABLED)
|
context.add_command(label="Select Members", state=tk.DISABLED)
|
||||||
edit_menu = tk.Menu(context)
|
edit_menu = tk.Menu(context)
|
||||||
themes.style_menu(edit_menu)
|
themes.style_menu(edit_menu)
|
||||||
edit_menu.add_command(label="Cut", state=tk.DISABLED)
|
edit_menu.add_command(label="Cut", command=self.click_cut)
|
||||||
edit_menu.add_command(label="Copy", command=self.canvas_copy)
|
edit_menu.add_command(label="Copy", command=self.canvas_copy)
|
||||||
edit_menu.add_command(label="Delete", command=self.canvas_delete)
|
edit_menu.add_command(label="Delete", command=self.canvas_delete)
|
||||||
context.add_cascade(label="Edit", menu=edit_menu)
|
context.add_cascade(label="Edit", menu=edit_menu)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def click_cut(self) -> None:
|
||||||
|
self.canvas_copy()
|
||||||
|
self.canvas_delete()
|
||||||
|
|
||||||
def canvas_delete(self) -> None:
|
def canvas_delete(self) -> None:
|
||||||
self.canvas.clear_selection()
|
self.canvas.clear_selection()
|
||||||
self.canvas.selection[self.id] = self
|
self.canvas.selection[self.id] = self
|
||||||
|
|
|
@ -103,14 +103,14 @@ class Menubar(tk.Menu):
|
||||||
menu.add_command(label="Undo", accelerator="Ctrl+Z", state=tk.DISABLED)
|
menu.add_command(label="Undo", accelerator="Ctrl+Z", state=tk.DISABLED)
|
||||||
menu.add_command(label="Redo", accelerator="Ctrl+Y", state=tk.DISABLED)
|
menu.add_command(label="Redo", accelerator="Ctrl+Y", state=tk.DISABLED)
|
||||||
menu.add_separator()
|
menu.add_separator()
|
||||||
menu.add_command(label="Cut", accelerator="Ctrl+X", state=tk.DISABLED)
|
menu.add_command(label="Cut", accelerator="Ctrl+X", command=self.click_cut)
|
||||||
menu.add_command(label="Copy", accelerator="Ctrl+C", command=self.click_copy)
|
menu.add_command(label="Copy", accelerator="Ctrl+C", command=self.click_copy)
|
||||||
menu.add_command(label="Paste", accelerator="Ctrl+V", command=self.click_paste)
|
menu.add_command(label="Paste", accelerator="Ctrl+V", command=self.click_paste)
|
||||||
menu.add_command(
|
menu.add_command(
|
||||||
label="Delete", accelerator="Ctrl+D", command=self.click_delete
|
label="Delete", accelerator="Ctrl+D", command=self.click_delete
|
||||||
)
|
)
|
||||||
self.add_cascade(label="Edit", menu=menu)
|
self.add_cascade(label="Edit", menu=menu)
|
||||||
|
self.app.master.bind_all("<Control-x>", self.click_cut)
|
||||||
self.app.master.bind_all("<Control-c>", self.click_copy)
|
self.app.master.bind_all("<Control-c>", self.click_copy)
|
||||||
self.app.master.bind_all("<Control-v>", self.click_paste)
|
self.app.master.bind_all("<Control-v>", self.click_paste)
|
||||||
self.app.master.bind_all("<Control-d>", self.click_delete)
|
self.app.master.bind_all("<Control-d>", self.click_delete)
|
||||||
|
@ -343,16 +343,16 @@ class Menubar(tk.Menu):
|
||||||
self.app.menubar.update_recent_files()
|
self.app.menubar.update_recent_files()
|
||||||
|
|
||||||
def change_menubar_item_state(self, is_runtime: bool) -> None:
|
def change_menubar_item_state(self, is_runtime: bool) -> None:
|
||||||
for i in range(self.edit_menu.index("end")):
|
labels = {"Copy", "Paste", "Delete", "Cut"}
|
||||||
|
for i in range(self.edit_menu.index(tk.END) + 1):
|
||||||
try:
|
try:
|
||||||
label_name = self.edit_menu.entrycget(i, "label")
|
label = self.edit_menu.entrycget(i, "label")
|
||||||
if label_name in ["Copy", "Paste"]:
|
if label not in labels:
|
||||||
if is_runtime:
|
continue
|
||||||
self.edit_menu.entryconfig(i, state="disabled")
|
state = tk.DISABLED if is_runtime else tk.NORMAL
|
||||||
else:
|
self.edit_menu.entryconfig(i, state=state)
|
||||||
self.edit_menu.entryconfig(i, state="normal")
|
|
||||||
except tk.TclError:
|
except tk.TclError:
|
||||||
logging.debug("Ignore separators")
|
pass
|
||||||
|
|
||||||
def prompt_save_running_session(self, quit_app: bool = False) -> None:
|
def prompt_save_running_session(self, quit_app: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -410,13 +410,17 @@ class Menubar(tk.Menu):
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def click_copy(self, _event: tk.Event = None) -> None:
|
def click_copy(self, _event: tk.Event = None) -> None:
|
||||||
self.app.canvas.copy()
|
self.canvas.copy()
|
||||||
|
|
||||||
def click_paste(self, _event: tk.Event = None) -> None:
|
def click_paste(self, _event: tk.Event = None) -> None:
|
||||||
self.app.canvas.paste()
|
self.canvas.paste()
|
||||||
|
|
||||||
def click_delete(self, _event: tk.Event = None) -> None:
|
def click_delete(self, _event: tk.Event = None) -> None:
|
||||||
self.app.canvas.delete_selected_objects()
|
self.canvas.delete_selected_objects()
|
||||||
|
|
||||||
|
def click_cut(self, _event: tk.Event = None) -> None:
|
||||||
|
self.canvas.copy()
|
||||||
|
self.canvas.delete_selected_objects()
|
||||||
|
|
||||||
def click_session_options(self) -> None:
|
def click_session_options(self) -> None:
|
||||||
logging.debug("Click options")
|
logging.debug("Click options")
|
||||||
|
@ -444,14 +448,14 @@ class Menubar(tk.Menu):
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def click_autogrid(self) -> None:
|
def click_autogrid(self) -> None:
|
||||||
width, height = self.app.canvas.current_dimensions
|
width, height = self.canvas.current_dimensions
|
||||||
padding = (ICON_SIZE / 2) + 10
|
padding = (ICON_SIZE / 2) + 10
|
||||||
layout_size = padding + ICON_SIZE
|
layout_size = padding + ICON_SIZE
|
||||||
col_count = width // layout_size
|
col_count = width // layout_size
|
||||||
logging.info(
|
logging.info(
|
||||||
"auto grid layout: dimens(%s, %s) col(%s)", width, height, col_count
|
"auto grid layout: dimension(%s, %s) col(%s)", width, height, col_count
|
||||||
)
|
)
|
||||||
for i, node in enumerate(self.app.canvas.nodes.values()):
|
for i, node in enumerate(self.canvas.nodes.values()):
|
||||||
col = i % col_count
|
col = i % col_count
|
||||||
row = i // col_count
|
row = i // col_count
|
||||||
x = (col * layout_size) + padding
|
x = (col * layout_size) + padding
|
||||||
|
|
|
@ -181,7 +181,6 @@ class ConfigFrame(ttk.Notebook):
|
||||||
option.value = "0"
|
option.value = "0"
|
||||||
else:
|
else:
|
||||||
option.value = config_value
|
option.value = config_value
|
||||||
|
|
||||||
return {x: self.config[x].value for x in self.config}
|
return {x: self.config[x].value for x in self.config}
|
||||||
|
|
||||||
def set_values(self, config: Dict[str, str]) -> None:
|
def set_values(self, config: Dict[str, str]) -> None:
|
||||||
|
|
Loading…
Reference in a new issue