Merge pull request #357 from coreemu/feature/config-service

Feature/config service
This commit is contained in:
bharnden 2020-01-27 17:31:03 -08:00 committed by GitHub
commit c5666f9112
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 5044 additions and 187 deletions

View file

@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Dict, List
import grpc
from core.api.grpc import client, core_pb2
from core.api.grpc import client, common_pb2, configservices_pb2, core_pb2
from core.gui import appconfig
from core.gui.dialogs.mobilityplayer import MobilityPlayer
from core.gui.dialogs.sessions import SessionsDialog
@ -74,6 +74,8 @@ class CoreClient:
self.app = app
self.master = app.master
self.services = {}
self.config_services_groups = {}
self.config_services = {}
self.default_services = {}
self.emane_models = []
self.observer = None
@ -99,6 +101,7 @@ class CoreClient:
self.emane_model_configs = {}
self.emane_config = None
self.service_configs = {}
self.config_service_configs = {}
self.file_configs = {}
self.mobility_players = {}
self.handling_throughputs = None
@ -307,6 +310,18 @@ class CoreClient:
data = config.files[file_name]
files[file_name] = data
# get config service configurations
response = self.client.get_node_config_service_configs(self.session_id)
for config in response.configs:
node_configs = self.config_service_configs.setdefault(
config.node_id, {}
)
service_config = node_configs.setdefault(config.name, {})
if config.templates:
service_config["templates"] = config.templates
if config.config:
service_config["config"] = config.config
# draw session
self.app.canvas.reset_and_redraw(session)
@ -427,6 +442,15 @@ class CoreClient:
group_services = self.services.setdefault(service.group, set())
group_services.add(service.name)
# get config service informations
response = self.client.get_config_services()
for service in response.services:
self.config_services[service.name] = service
group_services = self.config_services_groups.setdefault(
service.group, set()
)
group_services.add(service.name)
# if there are no sessions, create a new session, else join a session
response = self.client.get_sessions()
logging.info("current sessions: %s", response)
@ -464,6 +488,7 @@ class CoreClient:
asymmetric_links = [
x.asymmetric_link for x in self.links.values() if x.asymmetric_link
]
config_service_configs = self.get_config_service_configs_proto()
if self.emane_config:
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
else:
@ -484,6 +509,7 @@ class CoreClient:
service_configs,
file_configs,
asymmetric_links,
config_service_configs,
)
logging.debug(
"start session(%s), result: %s", self.session_id, response.result
@ -878,18 +904,34 @@ class CoreClient:
configs.append(config_proto)
return configs
def get_config_service_configs_proto(
self
) -> List[configservices_pb2.ConfigServiceConfig]:
config_service_protos = []
for node_id, node_config in self.config_service_configs.items():
for name, service_config in node_config.items():
config = service_config.get("config", {})
config_proto = configservices_pb2.ConfigServiceConfig(
node_id=node_id,
name=name,
templates=service_config["templates"],
config=config,
)
config_service_protos.append(config_proto)
return config_service_protos
def run(self, node_id: int) -> str:
logging.info("running node(%s) cmd: %s", node_id, self.observer)
return self.client.node_command(self.session_id, node_id, self.observer).output
def get_wlan_config(self, node_id: int) -> Dict[str, core_pb2.ConfigOption]:
def get_wlan_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
config = self.wlan_configs.get(node_id)
if not config:
response = self.client.get_wlan_config(self.session_id, node_id)
config = response.config
return config
def get_mobility_config(self, node_id: int) -> Dict[str, core_pb2.ConfigOption]:
def get_mobility_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
config = self.mobility_configs.get(node_id)
if not config:
response = self.client.get_mobility_config(self.session_id, node_id)
@ -898,7 +940,7 @@ class CoreClient:
def get_emane_model_config(
self, node_id: int, model: str, interface: int = None
) -> Dict[str, core_pb2.ConfigOption]:
) -> Dict[str, common_pb2.ConfigOption]:
logging.info("getting emane model config: %s %s %s", node_id, model, interface)
config = self.emane_model_configs.get((node_id, model, interface))
if not config:
@ -914,7 +956,7 @@ class CoreClient:
self,
node_id: int,
model: str,
config: Dict[str, core_pb2.ConfigOption],
config: Dict[str, common_pb2.ConfigOption],
interface: int = None,
):
logging.info("setting emane model config: %s %s %s", node_id, model, interface)

View file

@ -0,0 +1,381 @@
"""
Service configuration dialog
"""
import logging
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Any, List
import grpc
from core.api.grpc import core_pb2
from core.gui.dialogs.dialog import Dialog
from core.gui.errors import show_grpc_error
from core.gui.themes import FRAME_PAD, PADX, PADY
from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll
if TYPE_CHECKING:
from core.gui.app import Application
class ConfigServiceConfigDialog(Dialog):
def __init__(
self, master: Any, app: "Application", service_name: str, node_id: int
):
title = f"{service_name} Config Service"
super().__init__(master, app, title, modal=True)
self.master = master
self.app = app
self.core = app.core
self.node_id = node_id
self.service_name = service_name
self.service_configs = app.core.config_service_configs
self.radiovar = tk.IntVar()
self.radiovar.set(2)
self.directories = []
self.templates = []
self.dependencies = []
self.executables = []
self.startup_commands = []
self.validation_commands = []
self.shutdown_commands = []
self.default_startup = []
self.default_validate = []
self.default_shutdown = []
self.validation_mode = None
self.validation_time = None
self.validation_period = tk.StringVar()
self.modes = []
self.mode_configs = {}
self.notebook = None
self.templates_combobox = None
self.modes_combobox = None
self.startup_commands_listbox = None
self.shutdown_commands_listbox = None
self.validate_commands_listbox = None
self.validation_time_entry = None
self.validation_mode_entry = None
self.template_text = None
self.validation_period_entry = None
self.original_service_files = {}
self.temp_service_files = {}
self.modified_files = set()
self.config_frame = None
self.default_config = None
self.config = None
self.load()
self.draw()
def load(self):
try:
self.core.create_nodes_and_links()
service = self.core.config_services[self.service_name]
self.dependencies = service.dependencies[:]
self.executables = service.executables[:]
self.directories = service.directories[:]
self.templates = service.files[:]
self.startup_commands = service.startup[:]
self.validation_commands = service.validate[:]
self.shutdown_commands = service.shutdown[:]
self.validation_mode = service.validation_mode
self.validation_time = service.validation_timer
self.validation_period.set(service.validation_period)
response = self.core.client.get_config_service_defaults(self.service_name)
self.original_service_files = response.templates
self.temp_service_files = dict(self.original_service_files)
self.modes = sorted(x.name 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 = node_configs.get(self.service_name, {})
self.config = response.config
self.default_config = {x.name: x.value for x in self.config.values()}
custom_config = service_config.get("config")
if custom_config:
for key, value in custom_config.items():
self.config[key].value = value
logging.info("default config: %s", self.default_config)
custom_templates = service_config.get("templates", {})
for file, data in custom_templates.items():
self.modified_files.add(file)
self.temp_service_files[file] = data
except grpc.RpcError as e:
show_grpc_error(e)
def draw(self):
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
# draw notebook
self.notebook = ttk.Notebook(self.top)
self.notebook.grid(sticky="nsew", pady=PADY)
self.draw_tab_files()
if self.config:
self.draw_tab_config()
self.draw_tab_startstop()
self.draw_tab_validation()
self.draw_buttons()
def draw_tab_files(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
self.notebook.add(tab, text="Directories/Files")
label = ttk.Label(
tab, text="Directories and templates that will be used for this service."
)
label.grid(pady=PADY)
frame = ttk.Frame(tab)
frame.grid(sticky="ew", pady=PADY)
frame.columnconfigure(1, weight=1)
label = ttk.Label(frame, text="Directories")
label.grid(row=0, column=0, sticky="w", padx=PADX)
directories_combobox = ttk.Combobox(
frame, values=self.directories, state="readonly"
)
directories_combobox.grid(row=0, column=1, sticky="ew", pady=PADY)
if self.directories:
directories_combobox.current(0)
label = ttk.Label(frame, text="Templates")
label.grid(row=1, column=0, sticky="w", padx=PADX)
self.templates_combobox = ttk.Combobox(
frame, values=self.templates, state="readonly"
)
self.templates_combobox.bind(
"<<ComboboxSelected>>", self.handle_template_changed
)
self.templates_combobox.grid(row=1, column=1, sticky="ew", pady=PADY)
self.template_text = CodeText(tab)
self.template_text.grid(sticky="nsew")
tab.rowconfigure(self.template_text.grid_info()["row"], weight=1)
if self.templates:
self.templates_combobox.current(0)
self.template_text.text.delete(1.0, "end")
self.template_text.text.insert(
"end", self.temp_service_files[self.templates[0]]
)
self.template_text.text.bind("<FocusOut>", self.update_template_file_data)
def draw_tab_config(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
self.notebook.add(tab, text="Configuration")
if self.modes:
frame = ttk.Frame(tab)
frame.grid(sticky="ew", pady=PADY)
frame.columnconfigure(1, weight=1)
label = ttk.Label(frame, text="Modes")
label.grid(row=0, column=0, padx=PADX)
self.modes_combobox = ttk.Combobox(
frame, values=self.modes, state="readonly"
)
self.modes_combobox.bind("<<ComboboxSelected>>", self.handle_mode_changed)
self.modes_combobox.grid(row=0, column=1, sticky="ew", pady=PADY)
logging.info("config service config: %s", self.config)
self.config_frame = ConfigFrame(tab, self.app, self.config)
self.config_frame.draw_config()
self.config_frame.grid(sticky="nsew", pady=PADY)
tab.rowconfigure(self.config_frame.grid_info()["row"], weight=1)
def draw_tab_startstop(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
for i in range(3):
tab.rowconfigure(i, weight=1)
self.notebook.add(tab, text="Startup/Shutdown")
commands = []
# tab 3
for i in range(3):
label_frame = None
if i == 0:
label_frame = ttk.LabelFrame(
tab, text="Startup Commands", padding=FRAME_PAD
)
commands = self.startup_commands
elif i == 1:
label_frame = ttk.LabelFrame(
tab, text="Shutdown Commands", padding=FRAME_PAD
)
commands = self.shutdown_commands
elif i == 2:
label_frame = ttk.LabelFrame(
tab, text="Validation Commands", padding=FRAME_PAD
)
commands = self.validation_commands
label_frame.columnconfigure(0, weight=1)
label_frame.rowconfigure(0, weight=1)
label_frame.grid(row=i, column=0, sticky="nsew", pady=PADY)
listbox_scroll = ListboxScroll(label_frame)
for command in commands:
listbox_scroll.listbox.insert("end", command)
listbox_scroll.listbox.config(height=4)
listbox_scroll.grid(sticky="nsew")
if i == 0:
self.startup_commands_listbox = listbox_scroll.listbox
elif i == 1:
self.shutdown_commands_listbox = listbox_scroll.listbox
elif i == 2:
self.validate_commands_listbox = listbox_scroll.listbox
def draw_tab_validation(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="ew")
tab.columnconfigure(0, weight=1)
self.notebook.add(tab, text="Validation", sticky="nsew")
frame = ttk.Frame(tab)
frame.grid(sticky="ew", pady=PADY)
frame.columnconfigure(1, weight=1)
label = ttk.Label(frame, text="Validation Time")
label.grid(row=0, column=0, sticky="w", padx=PADX)
self.validation_time_entry = ttk.Entry(frame)
self.validation_time_entry.insert("end", self.validation_time)
self.validation_time_entry.config(state=tk.DISABLED)
self.validation_time_entry.grid(row=0, column=1, sticky="ew", pady=PADY)
label = ttk.Label(frame, text="Validation Mode")
label.grid(row=1, column=0, sticky="w", padx=PADX)
if self.validation_mode == core_pb2.ServiceValidationMode.BLOCKING:
mode = "BLOCKING"
elif self.validation_mode == core_pb2.ServiceValidationMode.NON_BLOCKING:
mode = "NON_BLOCKING"
else:
mode = "TIMER"
self.validation_mode_entry = ttk.Entry(
frame, textvariable=tk.StringVar(value=mode)
)
self.validation_mode_entry.insert("end", mode)
self.validation_mode_entry.config(state=tk.DISABLED)
self.validation_mode_entry.grid(row=1, column=1, sticky="ew", pady=PADY)
label = ttk.Label(frame, text="Validation Period")
label.grid(row=2, column=0, sticky="w", padx=PADX)
self.validation_period_entry = ttk.Entry(
frame, state=tk.DISABLED, textvariable=self.validation_period
)
self.validation_period_entry.grid(row=2, column=1, sticky="ew", pady=PADY)
label_frame = ttk.LabelFrame(tab, text="Executables", padding=FRAME_PAD)
label_frame.grid(sticky="nsew", pady=PADY)
label_frame.columnconfigure(0, weight=1)
label_frame.rowconfigure(0, weight=1)
listbox_scroll = ListboxScroll(label_frame)
listbox_scroll.grid(sticky="nsew")
tab.rowconfigure(listbox_scroll.grid_info()["row"], weight=1)
for executable in self.executables:
listbox_scroll.listbox.insert("end", executable)
label_frame = ttk.LabelFrame(tab, text="Dependencies", padding=FRAME_PAD)
label_frame.grid(sticky="nsew", pady=PADY)
label_frame.columnconfigure(0, weight=1)
label_frame.rowconfigure(0, weight=1)
listbox_scroll = ListboxScroll(label_frame)
listbox_scroll.grid(sticky="nsew")
tab.rowconfigure(listbox_scroll.grid_info()["row"], weight=1)
for dependency in self.dependencies:
listbox_scroll.listbox.insert("end", dependency)
def draw_buttons(self):
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(4):
frame.columnconfigure(i, weight=1)
button = ttk.Button(frame, text="Apply", command=self.click_apply)
button.grid(row=0, column=0, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Defaults", command=self.click_defaults)
button.grid(row=0, column=1, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Copy...", command=self.click_copy)
button.grid(row=0, column=2, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=3, sticky="ew")
def click_apply(self):
current_listbox = self.master.current.listbox
if not self.is_custom():
if self.node_id in self.service_configs:
self.service_configs[self.node_id].pop(self.service_name, None)
current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
self.destroy()
return
try:
node_config = self.service_configs.setdefault(self.node_id, {})
service_config = node_config.setdefault(self.service_name, {})
if self.config_frame:
self.config_frame.parse_config()
service_config["config"] = {
x.name: x.value for x in self.config.values()
}
templates_config = service_config.setdefault("templates", {})
for file in self.modified_files:
templates_config[file] = self.temp_service_files[file]
all_current = current_listbox.get(0, tk.END)
current_listbox.itemconfig(all_current.index(self.service_name), bg="green")
except grpc.RpcError as e:
show_grpc_error(e)
self.destroy()
def handle_template_changed(self, event: tk.Event):
template = self.templates_combobox.get()
self.template_text.text.delete(1.0, "end")
self.template_text.text.insert("end", self.temp_service_files[template])
def handle_mode_changed(self, event: tk.Event):
mode = self.modes_combobox.get()
config = self.mode_configs[mode]
logging.info("mode config: %s", config)
self.config_frame.set_values(config)
def update_template_file_data(self, event: tk.Event):
scrolledtext = event.widget
template = self.templates_combobox.get()
self.temp_service_files[template] = scrolledtext.get(1.0, "end")
if self.temp_service_files[template] != self.original_service_files[template]:
self.modified_files.add(template)
else:
self.modified_files.discard(template)
def is_custom(self):
has_custom_templates = len(self.modified_files) > 0
has_custom_config = False
if self.config_frame:
current = self.config_frame.parse_config()
has_custom_config = self.default_config != current
return has_custom_templates or has_custom_config
def click_defaults(self):
if self.node_id in self.service_configs:
node_config = self.service_configs.get(self.node_id, {})
node_config.pop(self.service_name, None)
self.temp_service_files = dict(self.original_service_files)
filename = self.templates_combobox.get()
self.template_text.text.delete(1.0, "end")
self.template_text.text.insert("end", self.temp_service_files[filename])
if self.config_frame:
logging.info("resetting defaults: %s", self.default_config)
self.config_frame.set_values(self.default_config)
def click_copy(self):
pass
def append_commands(
self, commands: List[str], listbox: tk.Listbox, to_add: List[str]
):
for cmd in to_add:
commands.append(cmd)
listbox.insert(tk.END, cmd)

View file

@ -0,0 +1,161 @@
"""
core node services
"""
import logging
import tkinter as tk
from tkinter import messagebox, ttk
from typing import TYPE_CHECKING, Any, Set
from core.gui.dialogs.configserviceconfig import ConfigServiceConfigDialog
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY
from core.gui.widgets import CheckboxList, ListboxScroll
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
class NodeConfigServiceDialog(Dialog):
def __init__(
self,
master: Any,
app: "Application",
canvas_node: "CanvasNode",
services: Set[str] = None,
):
title = f"{canvas_node.core_node.name} Config Services"
super().__init__(master, app, title, modal=True)
self.app = app
self.canvas_node = canvas_node
self.node_id = canvas_node.core_node.id
self.groups = None
self.services = None
self.current = None
if services is None:
services = set(canvas_node.core_node.config_services)
self.current_services = services
self.draw()
def draw(self):
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
frame = ttk.Frame(self.top)
frame.grid(stick="nsew", pady=PADY)
frame.rowconfigure(0, weight=1)
for i in range(3):
frame.columnconfigure(i, weight=1)
label_frame = ttk.LabelFrame(frame, text="Groups", padding=FRAME_PAD)
label_frame.grid(row=0, column=0, sticky="nsew")
label_frame.rowconfigure(0, weight=1)
label_frame.columnconfigure(0, weight=1)
self.groups = ListboxScroll(label_frame)
self.groups.grid(sticky="nsew")
for group in sorted(self.app.core.config_services_groups):
self.groups.listbox.insert(tk.END, group)
self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change)
self.groups.listbox.selection_set(0)
label_frame = ttk.LabelFrame(frame, text="Services")
label_frame.grid(row=0, column=1, sticky="nsew")
label_frame.columnconfigure(0, weight=1)
label_frame.rowconfigure(0, weight=1)
self.services = CheckboxList(
label_frame, self.app, clicked=self.service_clicked, padding=FRAME_PAD
)
self.services.grid(sticky="nsew")
label_frame = ttk.LabelFrame(frame, text="Selected", padding=FRAME_PAD)
label_frame.grid(row=0, column=2, sticky="nsew")
label_frame.rowconfigure(0, weight=1)
label_frame.columnconfigure(0, weight=1)
self.current = ListboxScroll(label_frame)
self.current.grid(sticky="nsew")
for service in sorted(self.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.grid(stick="ew")
for i in range(4):
frame.columnconfigure(i, weight=1)
button = ttk.Button(frame, text="Configure", command=self.click_configure)
button.grid(row=0, column=0, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Save", command=self.click_save)
button.grid(row=0, column=1, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Remove", command=self.click_remove)
button.grid(row=0, column=2, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Cancel", command=self.click_cancel)
button.grid(row=0, column=3, sticky="ew")
# trigger group change
self.groups.listbox.event_generate("<<ListboxSelect>>")
def handle_group_change(self, event: tk.Event = None):
selection = self.groups.listbox.curselection()
if selection:
index = selection[0]
group = self.groups.listbox.get(index)
self.services.clear()
for name in sorted(self.app.core.config_services_groups[group]):
checked = name in self.current_services
self.services.add(name, checked)
def service_clicked(self, name: str, var: tk.IntVar):
if var.get() and name not in self.current_services:
self.current_services.add(name)
elif not var.get() and name in self.current_services:
self.current_services.remove(name)
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")
self.canvas_node.core_node.config_services[:] = self.current_services
def click_configure(self):
current_selection = self.current.listbox.curselection()
if len(current_selection):
dialog = ConfigServiceConfigDialog(
master=self,
app=self.app,
service_name=self.current.listbox.get(current_selection[0]),
node_id=self.node_id,
)
dialog.show()
else:
messagebox.showinfo(
"Node service configuration", "Select a service to configure"
)
def click_save(self):
self.canvas_node.core_node.config_services[:] = self.current_services
logging.info(
"saved node config services: %s", self.canvas_node.core_node.config_services
)
self.destroy()
def click_cancel(self):
self.current_services = None
self.destroy()
def click_remove(self):
cur = self.current.listbox.curselection()
if cur:
service = self.current.listbox.get(cur[0])
self.current.listbox.delete(cur[0])
self.current_services.remove(service)
for checkbutton in self.services.frame.winfo_children():
if checkbutton["text"] == service:
checkbutton.invoke()
return
def is_custom_service(self, service: str) -> bool:
node_configs = self.app.core.config_service_configs.get(self.node_id, {})
service_config = node_configs.get(service)
if node_configs and service_config:
return True
else:
return False

View file

@ -10,6 +10,7 @@ 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
from core.gui.dialogs.nodeconfigservice import NodeConfigServiceDialog
from core.gui.dialogs.nodeservice import NodeServiceDialog
from core.gui.dialogs.wlanconfig import WlanConfigDialog
from core.gui.errors import show_grpc_error
@ -180,6 +181,7 @@ class CanvasNode:
context.add_command(label="Configure", command=self.show_config)
if NodeUtils.is_container_node(self.core_node.type):
context.add_command(label="Services", state=tk.DISABLED)
context.add_command(label="Config Services", state=tk.DISABLED)
if is_wlan:
context.add_command(label="WLAN Config", command=self.show_wlan_config)
if is_wlan and self.core_node.id in self.app.core.mobility_players:
@ -198,6 +200,9 @@ class CanvasNode:
context.add_command(label="Configure", command=self.show_config)
if NodeUtils.is_container_node(self.core_node.type):
context.add_command(label="Services", command=self.show_services)
context.add_command(
label="Config Services", command=self.show_config_services
)
if is_emane:
context.add_command(
label="EMANE Config", command=self.show_emane_config
@ -253,6 +258,11 @@ class CanvasNode:
dialog = NodeServiceDialog(self.app.master, self.app, self)
dialog.show()
def show_config_services(self):
self.canvas.context = None
dialog = NodeConfigServiceDialog(self.app.master, self.app, self)
dialog.show()
def has_emane_link(self, interface_id: int) -> core_pb2.Node:
result = None
for edge in self.edges:

View file

@ -5,7 +5,7 @@ from pathlib import PosixPath
from tkinter import filedialog, font, ttk
from typing import TYPE_CHECKING, Dict
from core.api.grpc import core_pb2
from core.api.grpc import common_pb2, core_pb2
from core.gui import themes
from core.gui.themes import FRAME_PAD, PADX, PADY
@ -74,7 +74,7 @@ class ConfigFrame(ttk.Notebook):
self,
master: tk.Widget,
app: "Application",
config: Dict[str, core_pb2.ConfigOption],
config: Dict[str, common_pb2.ConfigOption],
**kw
):
super().__init__(master, **kw)
@ -99,7 +99,7 @@ class ConfigFrame(ttk.Notebook):
label.grid(row=index, pady=PADY, padx=PADX, sticky="w")
value = tk.StringVar()
if option.type == core_pb2.ConfigOptionType.BOOL:
select = tuple(option.select)
select = ("On", "Off")
combobox = ttk.Combobox(
tab.frame, textvariable=value, values=select, state="readonly"
)
@ -184,6 +184,17 @@ class ConfigFrame(ttk.Notebook):
return {x: self.config[x].value for x in self.config}
def set_values(self, config: Dict[str, str]) -> None:
for name, data in config.items():
option = self.config[name]
value = self.values[name]
if option.type == core_pb2.ConfigOptionType.BOOL:
if data == "1":
data = "On"
else:
data = "Off"
value.set(data)
class ListboxScroll(ttk.Frame):
def __init__(self, master: tk.Widget = None, **kw):