2019-11-20 18:59:30 +00:00
|
|
|
import logging
|
2019-11-03 06:47:43 +00:00
|
|
|
import tkinter as tk
|
2019-11-20 18:59:30 +00:00
|
|
|
from functools import partial
|
2020-03-04 19:38:24 +00:00
|
|
|
from tkinter import messagebox, ttk
|
2020-01-13 23:31:41 +00:00
|
|
|
from typing import TYPE_CHECKING
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2020-03-04 19:38:24 +00:00
|
|
|
import netaddr
|
|
|
|
|
2020-05-12 06:00:52 +01:00
|
|
|
from core.gui import nodeutils, validation
|
2019-12-19 17:30:21 +00:00
|
|
|
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
|
2020-01-08 17:32:39 +00:00
|
|
|
from core.gui.widgets import ListboxScroll, image_chooser
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2020-01-13 23:31:41 +00:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from core.gui.app import Application
|
|
|
|
from core.gui.graph.node import CanvasNode
|
|
|
|
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2020-03-04 19:38:24 +00:00
|
|
|
def check_ip6(parent, name: str, value: str) -> bool:
|
|
|
|
if not value:
|
2020-03-04 22:39:28 +00:00
|
|
|
return True
|
|
|
|
title = f"IP6 Error for {name}"
|
2020-03-04 19:38:24 +00:00
|
|
|
values = value.split("/")
|
|
|
|
if len(values) != 2:
|
|
|
|
messagebox.showerror(
|
|
|
|
title, "Must be in the format address/prefix", parent=parent
|
|
|
|
)
|
|
|
|
return False
|
|
|
|
addr, mask = values
|
|
|
|
if not netaddr.valid_ipv6(addr):
|
|
|
|
messagebox.showerror(title, "Invalid IP6 address", parent=parent)
|
|
|
|
return False
|
|
|
|
try:
|
|
|
|
mask = int(mask)
|
|
|
|
if not (0 <= mask <= 128):
|
|
|
|
messagebox.showerror(title, "Mask must be between 0-128", parent=parent)
|
|
|
|
return False
|
|
|
|
except ValueError:
|
|
|
|
messagebox.showerror(title, "Invalid Mask", parent=parent)
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def check_ip4(parent, name: str, value: str) -> bool:
|
|
|
|
if not value:
|
2020-03-04 22:39:28 +00:00
|
|
|
return True
|
|
|
|
title = f"IP4 Error for {name}"
|
2020-03-04 19:38:24 +00:00
|
|
|
values = value.split("/")
|
|
|
|
if len(values) != 2:
|
|
|
|
messagebox.showerror(
|
|
|
|
title, "Must be in the format address/prefix", parent=parent
|
|
|
|
)
|
|
|
|
return False
|
|
|
|
addr, mask = values
|
|
|
|
if not netaddr.valid_ipv4(addr):
|
|
|
|
messagebox.showerror(title, "Invalid IP4 address", parent=parent)
|
|
|
|
return False
|
|
|
|
try:
|
|
|
|
mask = int(mask)
|
|
|
|
if not (0 <= mask <= 32):
|
|
|
|
messagebox.showerror(title, "Mask must be between 0-32", parent=parent)
|
|
|
|
return False
|
|
|
|
except ValueError:
|
|
|
|
messagebox.showerror(title, "Invalid mask", parent=parent)
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2020-04-23 07:00:07 +01:00
|
|
|
def mac_auto(is_auto: tk.BooleanVar, entry: ttk.Entry, mac: tk.StringVar) -> None:
|
2019-11-20 18:59:30 +00:00
|
|
|
if is_auto.get():
|
2020-04-23 07:00:07 +01:00
|
|
|
mac.set("")
|
2019-11-20 18:59:30 +00:00
|
|
|
entry.config(state=tk.DISABLED)
|
|
|
|
else:
|
2020-04-23 07:00:07 +01:00
|
|
|
mac.set("00:00:00:00:00:00")
|
2019-11-20 18:59:30 +00:00
|
|
|
entry.config(state=tk.NORMAL)
|
|
|
|
|
|
|
|
|
|
|
|
class InterfaceData:
|
2020-01-14 19:59:44 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
is_auto: tk.BooleanVar,
|
|
|
|
mac: tk.StringVar,
|
|
|
|
ip4: tk.StringVar,
|
|
|
|
ip6: tk.StringVar,
|
|
|
|
):
|
2019-11-20 18:59:30 +00:00
|
|
|
self.is_auto = is_auto
|
|
|
|
self.mac = mac
|
|
|
|
self.ip4 = ip4
|
|
|
|
self.ip6 = ip6
|
|
|
|
|
|
|
|
|
2019-11-03 06:47:43 +00:00
|
|
|
class NodeConfigDialog(Dialog):
|
2020-05-05 06:50:59 +01:00
|
|
|
def __init__(self, app: "Application", canvas_node: "CanvasNode"):
|
2019-11-03 06:47:43 +00:00
|
|
|
"""
|
|
|
|
create an instance of node configuration
|
|
|
|
"""
|
2020-05-05 06:50:59 +01:00
|
|
|
super().__init__(app, f"{canvas_node.core_node.name} Configuration")
|
2019-11-03 06:47:43 +00:00
|
|
|
self.canvas_node = canvas_node
|
2019-11-20 18:59:30 +00:00
|
|
|
self.node = canvas_node.core_node
|
2019-11-03 06:47:43 +00:00
|
|
|
self.image = canvas_node.image
|
2019-12-13 16:48:40 +00:00
|
|
|
self.image_file = None
|
2019-11-03 06:47:43 +00:00
|
|
|
self.image_button = None
|
2019-11-20 18:59:30 +00:00
|
|
|
self.name = tk.StringVar(value=self.node.name)
|
|
|
|
self.type = tk.StringVar(value=self.node.model)
|
2019-11-20 19:20:08 +00:00
|
|
|
self.container_image = tk.StringVar(value=self.node.image)
|
2019-11-20 18:59:30 +00:00
|
|
|
server = "localhost"
|
|
|
|
if self.node.server:
|
|
|
|
server = self.node.server
|
|
|
|
self.server = tk.StringVar(value=server)
|
|
|
|
self.interfaces = {}
|
2019-11-03 06:47:43 +00:00
|
|
|
self.draw()
|
|
|
|
|
|
|
|
def draw(self):
|
2019-11-13 18:45:43 +00:00
|
|
|
self.top.columnconfigure(0, weight=1)
|
2019-11-16 16:54:15 +00:00
|
|
|
row = 0
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2020-05-02 02:05:54 +01:00
|
|
|
# field states
|
|
|
|
state = tk.DISABLED if self.app.core.is_runtime() else tk.NORMAL
|
|
|
|
combo_state = tk.DISABLED if self.app.core.is_runtime() else "readonly"
|
|
|
|
|
2019-11-16 16:54:15 +00:00
|
|
|
# field frame
|
2019-11-13 18:45:43 +00:00
|
|
|
frame = ttk.Frame(self.top)
|
2019-11-16 16:54:15 +00:00
|
|
|
frame.grid(sticky="ew")
|
2019-11-03 06:47:43 +00:00
|
|
|
frame.columnconfigure(1, weight=1)
|
|
|
|
|
2019-12-11 22:09:50 +00:00
|
|
|
# icon field
|
|
|
|
label = ttk.Label(frame, text="Icon")
|
2019-12-11 22:36:27 +00:00
|
|
|
label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY)
|
2019-12-11 22:09:50 +00:00
|
|
|
self.image_button = ttk.Button(
|
|
|
|
frame,
|
|
|
|
text="Icon",
|
|
|
|
image=self.image,
|
|
|
|
compound=tk.NONE,
|
|
|
|
command=self.click_icon,
|
|
|
|
)
|
|
|
|
self.image_button.grid(row=row, column=1, sticky="ew")
|
|
|
|
row += 1
|
|
|
|
|
2019-11-16 16:54:15 +00:00
|
|
|
# name field
|
|
|
|
label = ttk.Label(frame, text="Name")
|
2019-12-11 22:36:27 +00:00
|
|
|
label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY)
|
2020-05-12 06:00:52 +01:00
|
|
|
entry = validation.NodeNameEntry(frame, textvariable=self.name, state=state)
|
2019-11-16 16:54:15 +00:00
|
|
|
entry.grid(row=row, column=1, sticky="ew")
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
# node type field
|
2019-11-20 19:20:08 +00:00
|
|
|
if NodeUtils.is_model_node(self.node.type):
|
|
|
|
label = ttk.Label(frame, text="Type")
|
2019-12-11 22:36:27 +00:00
|
|
|
label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY)
|
2019-11-20 19:20:08 +00:00
|
|
|
combobox = ttk.Combobox(
|
|
|
|
frame,
|
|
|
|
textvariable=self.type,
|
|
|
|
values=list(NodeUtils.NODE_MODELS),
|
2020-05-02 02:05:54 +01:00
|
|
|
state=combo_state,
|
2019-11-20 19:20:08 +00:00
|
|
|
)
|
|
|
|
combobox.grid(row=row, column=1, sticky="ew")
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
# container image field
|
|
|
|
if NodeUtils.is_image_node(self.node.type):
|
|
|
|
label = ttk.Label(frame, text="Image")
|
2019-12-11 22:36:27 +00:00
|
|
|
label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY)
|
2020-05-02 02:05:54 +01:00
|
|
|
entry = ttk.Entry(frame, textvariable=self.container_image, state=state)
|
2019-11-20 19:20:08 +00:00
|
|
|
entry.grid(row=row, column=1, sticky="ew")
|
|
|
|
row += 1
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2019-11-20 19:20:08 +00:00
|
|
|
if NodeUtils.is_container_node(self.node.type):
|
2019-11-21 07:16:04 +00:00
|
|
|
# server
|
2019-11-20 19:20:08 +00:00
|
|
|
frame.grid(sticky="ew")
|
|
|
|
frame.columnconfigure(1, weight=1)
|
|
|
|
label = ttk.Label(frame, text="Server")
|
2019-12-11 22:36:27 +00:00
|
|
|
label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY)
|
2019-11-20 19:20:08 +00:00
|
|
|
servers = ["localhost"]
|
|
|
|
servers.extend(list(sorted(self.app.core.servers.keys())))
|
|
|
|
combobox = ttk.Combobox(
|
2020-05-02 02:05:54 +01:00
|
|
|
frame, textvariable=self.server, values=servers, state=combo_state
|
2019-11-20 19:20:08 +00:00
|
|
|
)
|
|
|
|
combobox.grid(row=row, column=1, sticky="ew")
|
|
|
|
row += 1
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2020-01-08 17:32:39 +00:00
|
|
|
if NodeUtils.is_rj45_node(self.node.type):
|
|
|
|
response = self.app.core.client.get_interfaces()
|
|
|
|
logging.debug("host machine available interfaces: %s", response)
|
|
|
|
interfaces = ListboxScroll(frame)
|
2020-05-02 02:05:54 +01:00
|
|
|
interfaces.listbox.config(state=state)
|
2020-01-08 17:32:39 +00:00
|
|
|
interfaces.grid(
|
|
|
|
row=row, column=0, columnspan=2, sticky="ew", padx=PADX, pady=PADY
|
|
|
|
)
|
|
|
|
for inf in sorted(response.interfaces[:]):
|
|
|
|
interfaces.listbox.insert(tk.END, inf)
|
|
|
|
row += 1
|
|
|
|
interfaces.listbox.bind("<<ListboxSelect>>", self.interface_select)
|
|
|
|
|
2019-11-20 18:59:30 +00:00
|
|
|
# interfaces
|
|
|
|
if self.canvas_node.interfaces:
|
|
|
|
self.draw_interfaces()
|
|
|
|
|
2019-12-11 22:09:50 +00:00
|
|
|
self.draw_spacer()
|
2019-11-16 16:54:15 +00:00
|
|
|
self.draw_buttons()
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2019-11-20 18:59:30 +00:00
|
|
|
def draw_interfaces(self):
|
2019-12-17 04:57:46 +00:00
|
|
|
notebook = ttk.Notebook(self.top)
|
|
|
|
notebook.grid(sticky="nsew", pady=PADY)
|
|
|
|
self.top.rowconfigure(notebook.grid_info()["row"], weight=1)
|
2020-05-02 02:05:54 +01:00
|
|
|
state = tk.DISABLED if self.app.core.is_runtime() else tk.NORMAL
|
2020-05-17 07:35:19 +01:00
|
|
|
for interface_id in sorted(self.canvas_node.interfaces):
|
|
|
|
interface = self.canvas_node.interfaces[interface_id]
|
2019-12-17 04:57:46 +00:00
|
|
|
tab = ttk.Frame(notebook, padding=FRAME_PAD)
|
|
|
|
tab.grid(sticky="nsew", pady=PADY)
|
|
|
|
tab.columnconfigure(1, weight=1)
|
|
|
|
tab.columnconfigure(2, weight=1)
|
|
|
|
notebook.add(tab, text=interface.name)
|
|
|
|
|
|
|
|
row = 0
|
|
|
|
emane_node = self.canvas_node.has_emane_link(interface.id)
|
|
|
|
if emane_node:
|
|
|
|
emane_model = emane_node.emane.split("_")[1]
|
|
|
|
button = ttk.Button(
|
|
|
|
tab,
|
|
|
|
text=f"Configure EMANE {emane_model}",
|
|
|
|
command=lambda: self.click_emane_config(emane_model, interface.id),
|
|
|
|
)
|
|
|
|
button.grid(row=row, sticky="ew", columnspan=3, pady=PADY)
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
label = ttk.Label(tab, text="MAC")
|
|
|
|
label.grid(row=row, column=0, padx=PADX, pady=PADY)
|
2020-03-06 05:38:52 +00:00
|
|
|
auto_set = not interface.mac
|
2020-05-02 02:05:54 +01:00
|
|
|
mac_state = tk.DISABLED if auto_set else tk.NORMAL
|
2020-03-06 05:38:52 +00:00
|
|
|
is_auto = tk.BooleanVar(value=auto_set)
|
2020-05-02 02:05:54 +01:00
|
|
|
checkbutton = ttk.Checkbutton(
|
|
|
|
tab, text="Auto?", variable=is_auto, state=state
|
|
|
|
)
|
2019-11-20 18:59:30 +00:00
|
|
|
checkbutton.var = is_auto
|
2019-12-17 04:57:46 +00:00
|
|
|
checkbutton.grid(row=row, column=1, padx=PADX)
|
2019-11-20 18:59:30 +00:00
|
|
|
mac = tk.StringVar(value=interface.mac)
|
2020-05-02 02:05:54 +01:00
|
|
|
entry = ttk.Entry(tab, textvariable=mac, state=mac_state)
|
2019-12-17 04:57:46 +00:00
|
|
|
entry.grid(row=row, column=2, sticky="ew")
|
2020-04-23 07:00:07 +01:00
|
|
|
func = partial(mac_auto, is_auto, entry, mac)
|
2019-11-20 18:59:30 +00:00
|
|
|
checkbutton.config(command=func)
|
2019-12-17 04:57:46 +00:00
|
|
|
row += 1
|
2019-11-20 18:59:30 +00:00
|
|
|
|
2019-12-17 04:57:46 +00:00
|
|
|
label = ttk.Label(tab, text="IPv4")
|
|
|
|
label.grid(row=row, column=0, padx=PADX, pady=PADY)
|
2020-03-04 22:39:28 +00:00
|
|
|
ip4_net = ""
|
|
|
|
if interface.ip4:
|
|
|
|
ip4_net = f"{interface.ip4}/{interface.ip4mask}"
|
|
|
|
ip4 = tk.StringVar(value=ip4_net)
|
2020-05-02 02:05:54 +01:00
|
|
|
entry = ttk.Entry(tab, textvariable=ip4, state=state)
|
2019-12-17 04:57:46 +00:00
|
|
|
entry.grid(row=row, column=1, columnspan=2, sticky="ew")
|
|
|
|
row += 1
|
2019-11-20 18:59:30 +00:00
|
|
|
|
2019-12-17 04:57:46 +00:00
|
|
|
label = ttk.Label(tab, text="IPv6")
|
|
|
|
label.grid(row=row, column=0, padx=PADX, pady=PADY)
|
2020-03-04 22:39:28 +00:00
|
|
|
ip6_net = ""
|
|
|
|
if interface.ip6:
|
|
|
|
ip6_net = f"{interface.ip6}/{interface.ip6mask}"
|
|
|
|
ip6 = tk.StringVar(value=ip6_net)
|
2020-05-02 02:05:54 +01:00
|
|
|
entry = ttk.Entry(tab, textvariable=ip6, state=state)
|
2019-12-17 04:57:46 +00:00
|
|
|
entry.grid(row=row, column=1, columnspan=2, sticky="ew")
|
2019-11-20 18:59:30 +00:00
|
|
|
|
|
|
|
self.interfaces[interface.id] = InterfaceData(is_auto, mac, ip4, ip6)
|
|
|
|
|
2019-11-16 16:54:15 +00:00
|
|
|
def draw_buttons(self):
|
2019-11-13 18:45:43 +00:00
|
|
|
frame = ttk.Frame(self.top)
|
2019-11-16 16:54:15 +00:00
|
|
|
frame.grid(sticky="ew")
|
2019-11-03 06:47:43 +00:00
|
|
|
frame.columnconfigure(0, weight=1)
|
|
|
|
frame.columnconfigure(1, weight=1)
|
|
|
|
|
2020-04-23 07:00:07 +01:00
|
|
|
button = ttk.Button(frame, text="Apply", command=self.click_apply)
|
2019-12-11 22:36:27 +00:00
|
|
|
button.grid(row=0, column=0, padx=PADX, sticky="ew")
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2019-11-12 20:13:53 +00:00
|
|
|
button = ttk.Button(frame, text="Cancel", command=self.destroy)
|
2019-11-03 06:47:43 +00:00
|
|
|
button.grid(row=0, column=1, sticky="ew")
|
|
|
|
|
2020-01-13 20:03:13 +00:00
|
|
|
def click_emane_config(self, emane_model: str, interface_id: int):
|
2020-04-21 08:38:36 +01:00
|
|
|
dialog = EmaneModelDialog(
|
|
|
|
self, self.app, self.canvas_node, emane_model, interface_id
|
|
|
|
)
|
2019-12-17 04:57:46 +00:00
|
|
|
dialog.show()
|
|
|
|
|
2019-11-03 06:47:43 +00:00
|
|
|
def click_icon(self):
|
2019-12-17 19:35:30 +00:00
|
|
|
file_path = image_chooser(self, ICONS_PATH)
|
2019-12-13 04:30:28 +00:00
|
|
|
if file_path:
|
|
|
|
self.image = Images.create(file_path, nodeutils.ICON_SIZE)
|
2019-11-03 06:47:43 +00:00
|
|
|
self.image_button.config(image=self.image)
|
2019-12-13 16:48:40 +00:00
|
|
|
self.image_file = file_path
|
2019-11-03 06:47:43 +00:00
|
|
|
|
2020-04-23 07:00:07 +01:00
|
|
|
def click_apply(self):
|
2020-03-04 19:38:24 +00:00
|
|
|
error = False
|
|
|
|
|
2019-11-20 18:59:30 +00:00
|
|
|
# update core node
|
|
|
|
self.node.name = self.name.get()
|
2019-11-20 19:20:08 +00:00
|
|
|
if NodeUtils.is_image_node(self.node.type):
|
|
|
|
self.node.image = self.container_image.get()
|
2019-11-26 00:50:44 +00:00
|
|
|
server = self.server.get()
|
|
|
|
if NodeUtils.is_container_node(self.node.type) and server != "localhost":
|
|
|
|
self.node.server = server
|
2019-11-20 18:59:30 +00:00
|
|
|
|
2019-12-13 16:48:40 +00:00
|
|
|
# set custom icon
|
|
|
|
if self.image_file:
|
|
|
|
self.node.icon = self.image_file
|
|
|
|
|
2019-11-20 18:59:30 +00:00
|
|
|
# update canvas node
|
2019-11-03 06:47:43 +00:00
|
|
|
self.canvas_node.image = self.image
|
2019-11-20 18:59:30 +00:00
|
|
|
|
2020-03-04 19:38:24 +00:00
|
|
|
# update node interface data
|
2020-05-17 07:35:19 +01:00
|
|
|
for interface in self.canvas_node.interfaces.values():
|
2020-03-04 19:38:24 +00:00
|
|
|
data = self.interfaces[interface.id]
|
2020-03-04 22:39:28 +00:00
|
|
|
|
|
|
|
# validate ip4
|
|
|
|
ip4_net = data.ip4.get()
|
|
|
|
if not check_ip4(self, interface.name, ip4_net):
|
2020-03-04 19:38:24 +00:00
|
|
|
error = True
|
|
|
|
break
|
2020-03-04 22:39:28 +00:00
|
|
|
if ip4_net:
|
|
|
|
ip4, ip4mask = ip4_net.split("/")
|
|
|
|
ip4mask = int(ip4mask)
|
2020-03-04 19:38:24 +00:00
|
|
|
else:
|
2020-03-04 22:39:28 +00:00
|
|
|
ip4, ip4mask = "", 0
|
|
|
|
interface.ip4 = ip4
|
|
|
|
interface.ip4mask = ip4mask
|
|
|
|
|
|
|
|
# validate ip6
|
|
|
|
ip6_net = data.ip6.get()
|
|
|
|
if not check_ip6(self, interface.name, ip6_net):
|
2020-03-04 19:38:24 +00:00
|
|
|
error = True
|
|
|
|
break
|
2020-03-04 22:39:28 +00:00
|
|
|
if ip6_net:
|
|
|
|
ip6, ip6mask = ip6_net.split("/")
|
|
|
|
ip6mask = int(ip6mask)
|
|
|
|
else:
|
|
|
|
ip6, ip6mask = "", 0
|
|
|
|
interface.ip6 = ip6
|
|
|
|
interface.ip6mask = ip6mask
|
|
|
|
|
2020-03-06 05:38:52 +00:00
|
|
|
mac = data.mac.get()
|
2020-04-23 07:00:07 +01:00
|
|
|
auto_mac = data.is_auto.get()
|
|
|
|
if not auto_mac and not netaddr.valid_mac(mac):
|
2020-03-06 05:38:52 +00:00
|
|
|
title = f"MAC Error for {interface.name}"
|
|
|
|
messagebox.showerror(title, "Invalid MAC Address")
|
|
|
|
error = True
|
|
|
|
break
|
2020-04-23 07:00:07 +01:00
|
|
|
elif not auto_mac:
|
2020-05-02 01:40:53 +01:00
|
|
|
mac = netaddr.EUI(mac, dialect=netaddr.mac_unix_expanded)
|
2020-03-06 05:38:52 +00:00
|
|
|
interface.mac = str(mac)
|
2020-03-04 19:38:24 +00:00
|
|
|
|
2019-11-20 18:59:30 +00:00
|
|
|
# redraw
|
2020-03-04 19:38:24 +00:00
|
|
|
if not error:
|
|
|
|
self.canvas_node.redraw()
|
|
|
|
self.destroy()
|
2020-01-08 17:32:39 +00:00
|
|
|
|
2020-01-13 20:03:13 +00:00
|
|
|
def interface_select(self, event: tk.Event):
|
2020-01-08 17:32:39 +00:00
|
|
|
listbox = event.widget
|
|
|
|
cur = listbox.curselection()
|
|
|
|
if cur:
|
|
|
|
interface = listbox.get(cur[0])
|
|
|
|
self.name.set(interface)
|