core-extra/daemon/core/gui/dialogs/emaneconfig.py

191 lines
6.7 KiB
Python

"""
emane configuration
"""
import tkinter as tk
import webbrowser
from tkinter import ttk
from typing import TYPE_CHECKING, Optional
import grpc
from core.api.grpc.wrappers import ConfigOption, Node
from core.gui import images
from core.gui.dialogs.dialog import Dialog
from core.gui.images import ImageEnum
from core.gui.themes import PADX, PADY
from core.gui.widgets import ConfigFrame
if TYPE_CHECKING:
from core.gui.app import Application
class EmaneModelDialog(Dialog):
def __init__(
self,
master: tk.BaseWidget,
app: "Application",
node: Node,
model: str,
iface_id: int = None,
) -> None:
super().__init__(app, f"{node.name} {model} Configuration", master=master)
self.node: Node = node
self.model: str = f"emane_{model}"
self.iface_id: int = iface_id
self.config_frame: Optional[ConfigFrame] = None
self.enabled: bool = not self.app.core.is_runtime()
self.has_error: bool = False
try:
config = self.node.emane_model_configs.get((self.model, self.iface_id))
if not config:
config = self.node.emane_model_configs.get((self.model, None))
if not config:
config = self.app.core.get_emane_model_config(
self.node.id, self.model, self.iface_id
)
self.config: dict[str, ConfigOption] = config
self.draw()
except grpc.RpcError as e:
self.app.show_grpc_exception("Get EMANE Config Error", e)
self.has_error: bool = True
self.destroy()
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
self.config_frame = ConfigFrame(self.top, self.app, self.config, self.enabled)
self.config_frame.draw_config()
self.config_frame.grid(sticky=tk.NSEW, pady=PADY)
self.draw_buttons()
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky=tk.EW)
for i in range(2):
frame.columnconfigure(i, weight=1)
state = tk.NORMAL if self.enabled else tk.DISABLED
button = ttk.Button(frame, text="Apply", command=self.click_apply, state=state)
button.grid(row=0, column=0, sticky=tk.EW, padx=PADX)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky=tk.EW)
def click_apply(self) -> None:
self.config_frame.parse_config()
key = (self.model, self.iface_id)
self.node.emane_model_configs[key] = self.config
self.destroy()
class EmaneConfigDialog(Dialog):
def __init__(self, app: "Application", node: Node) -> None:
super().__init__(app, f"{node.name} EMANE Configuration")
self.node: Node = node
self.radiovar: tk.IntVar = tk.IntVar()
self.radiovar.set(1)
self.emane_models: list[str] = [
x.split("_")[1] for x in self.app.core.emane_models
]
model = self.node.emane.split("_")[1]
self.emane_model: tk.StringVar = tk.StringVar(value=model)
self.emane_model_button: Optional[ttk.Button] = None
self.enabled: bool = not self.app.core.is_runtime()
self.draw()
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.draw_emane_configuration()
self.draw_emane_models()
self.draw_emane_buttons()
self.draw_spacer()
self.draw_apply_and_cancel()
def draw_emane_configuration(self) -> None:
"""
draw the main frame for emane configuration
"""
label = ttk.Label(
self.top,
text="The EMANE emulation system provides more complex wireless radio "
"emulation \nusing pluggable MAC and PHY modules. Refer to the wiki "
"for configuration option details",
justify=tk.CENTER,
)
label.grid(pady=PADY)
image = images.from_enum(ImageEnum.EDITNODE, width=images.BUTTON_SIZE)
button = ttk.Button(
self.top,
image=image,
text="EMANE Wiki",
compound=tk.RIGHT,
command=lambda: webbrowser.open_new(
"https://github.com/adjacentlink/emane/wiki"
),
)
button.image = image
button.grid(sticky=tk.EW, pady=PADY)
def draw_emane_models(self) -> None:
"""
create a combobox that has all the known emane models
"""
frame = ttk.Frame(self.top)
frame.grid(sticky=tk.EW, pady=PADY)
frame.columnconfigure(1, weight=1)
label = ttk.Label(frame, text="Model")
label.grid(row=0, column=0, sticky=tk.W)
# create combo box and its binding
state = "readonly" if self.enabled else tk.DISABLED
combobox = ttk.Combobox(
frame, textvariable=self.emane_model, values=self.emane_models, state=state
)
combobox.grid(row=0, column=1, sticky=tk.EW)
combobox.bind("<<ComboboxSelected>>", self.emane_model_change)
def draw_emane_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky=tk.EW, pady=PADY)
frame.columnconfigure(0, weight=1)
image = images.from_enum(ImageEnum.EDITNODE, width=images.BUTTON_SIZE)
self.emane_model_button = ttk.Button(
frame,
text=f"{self.emane_model.get()} options",
image=image,
compound=tk.RIGHT,
command=self.click_model_config,
)
self.emane_model_button.image = image
self.emane_model_button.grid(padx=PADX, sticky=tk.EW)
def draw_apply_and_cancel(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky=tk.EW)
for i in range(2):
frame.columnconfigure(i, weight=1)
state = tk.NORMAL if self.enabled else tk.DISABLED
button = ttk.Button(frame, text="Apply", command=self.click_apply, state=state)
button.grid(row=0, column=0, padx=PADX, sticky=tk.EW)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky=tk.EW)
def click_model_config(self) -> None:
"""
draw emane model configuration
"""
model_name = self.emane_model.get()
dialog = EmaneModelDialog(self, self.app, self.node, model_name)
if not dialog.has_error:
dialog.show()
def emane_model_change(self, event: tk.Event) -> None:
"""
update emane model options button
"""
model_name = self.emane_model.get()
self.emane_model_button.config(text=f"{model_name} options")
def click_apply(self) -> None:
self.node.emane = f"emane_{self.emane_model.get()}"
self.destroy()