Merge pull request #444 from coreemu/enhancement/pygui-config

Enhancement/pygui config
This commit is contained in:
bharnden 2020-05-08 08:21:05 -07:00 committed by GitHub
commit 944040608e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 251 additions and 209 deletions

View file

@ -43,7 +43,7 @@ class Application(ttk.Frame):
# setup # setup
self.guiconfig = appconfig.read() self.guiconfig = appconfig.read()
self.app_scale = self.guiconfig["scale"] self.app_scale = self.guiconfig.scale
self.setup_scaling() self.setup_scaling()
self.style = ttk.Style() self.style = ttk.Style()
self.setup_theme() self.setup_theme()
@ -65,7 +65,7 @@ class Application(ttk.Frame):
themes.load(self.style) themes.load(self.style)
self.master.bind_class("Menu", "<<ThemeChanged>>", themes.theme_change_menu) self.master.bind_class("Menu", "<<ThemeChanged>>", themes.theme_change_menu)
self.master.bind("<<ThemeChanged>>", themes.theme_change) self.master.bind("<<ThemeChanged>>", themes.theme_change)
self.style.theme_use(self.guiconfig["preferences"]["theme"]) self.style.theme_use(self.guiconfig.preferences.theme)
def setup_app(self): def setup_app(self):
self.master.title("CORE") self.master.title("CORE")
@ -103,8 +103,8 @@ class Application(ttk.Frame):
self.menubar = Menubar(self.master, self) self.menubar = Menubar(self.master, self)
def draw_canvas(self): def draw_canvas(self):
width = self.guiconfig["preferences"]["width"] width = self.guiconfig.preferences.width
height = self.guiconfig["preferences"]["height"] height = self.guiconfig.preferences.height
canvas_frame = ttk.Frame(self.right_frame) canvas_frame = ttk.Frame(self.right_frame)
canvas_frame.rowconfigure(0, weight=1) canvas_frame.rowconfigure(0, weight=1)
canvas_frame.columnconfigure(0, weight=1) canvas_frame.columnconfigure(0, weight=1)

View file

@ -1,10 +1,10 @@
import os import os
import shutil import shutil
from pathlib import Path from pathlib import Path
from typing import List, Optional
import yaml import yaml
# gui home paths
from core.gui import themes from core.gui import themes
HOME_PATH = Path.home().joinpath(".coretk") HOME_PATH = Path.home().joinpath(".coretk")
@ -37,11 +37,6 @@ TERMINALS = {
"gnome-terminal": "gnome-terminal --window --", "gnome-terminal": "gnome-terminal --window --",
} }
EDITORS = ["$EDITOR", "vim", "emacs", "gedit", "nano", "vi"] EDITORS = ["$EDITOR", "vim", "emacs", "gedit", "nano", "vi"]
DEFAULT_IP4S = ["10.0.0.0", "192.168.0.0", "172.16.0.0"]
DEFAULT_IP4 = DEFAULT_IP4S[0]
DEFAULT_IP6S = ["2001::", "2002::", "a::"]
DEFAULT_IP6 = DEFAULT_IP6S[0]
DEFAULT_MAC = "00:00:00:aa:00:00"
class IndentDumper(yaml.Dumper): class IndentDumper(yaml.Dumper):
@ -49,13 +44,151 @@ class IndentDumper(yaml.Dumper):
return super().increase_indent(flow, False) return super().increase_indent(flow, False)
def copy_files(current_path, new_path): class CustomNode(yaml.YAMLObject):
yaml_tag = "!CustomNode"
yaml_loader = yaml.SafeLoader
def __init__(self, name: str, image: str, services: List[str]) -> None:
self.name = name
self.image = image
self.services = services
class CoreServer(yaml.YAMLObject):
yaml_tag = "!CoreServer"
yaml_loader = yaml.SafeLoader
def __init__(self, name: str, address: str) -> None:
self.name = name
self.address = address
class Observer(yaml.YAMLObject):
yaml_tag = "!Observer"
yaml_loader = yaml.SafeLoader
def __init__(self, name: str, cmd: str) -> None:
self.name = name
self.cmd = cmd
class PreferencesConfig(yaml.YAMLObject):
yaml_tag = "!PreferencesConfig"
yaml_loader = yaml.SafeLoader
def __init__(
self,
editor: str = EDITORS[1],
terminal: str = None,
theme: str = themes.THEME_DARK,
gui3d: str = "/usr/local/bin/std3d.sh",
width: int = 1000,
height: int = 750,
) -> None:
self.theme = theme
self.editor = editor
self.terminal = terminal
self.gui3d = gui3d
self.width = width
self.height = height
class LocationConfig(yaml.YAMLObject):
yaml_tag = "!LocationConfig"
yaml_loader = yaml.SafeLoader
def __init__(
self,
x: float = 0.0,
y: float = 0.0,
z: float = 0.0,
lat: float = 47.5791667,
lon: float = -122.132322,
alt: float = 2.0,
scale: float = 150.0,
) -> None:
self.x = x
self.y = y
self.z = z
self.lat = lat
self.lon = lon
self.alt = alt
self.scale = scale
class IpConfigs(yaml.YAMLObject):
yaml_tag = "!IpConfigs"
yaml_loader = yaml.SafeLoader
def __init__(
self,
ip4: str = None,
ip6: str = None,
ip4s: List[str] = None,
ip6s: List[str] = None,
) -> None:
if ip4s is None:
ip4s = ["10.0.0.0", "192.168.0.0", "172.16.0.0"]
self.ip4s = ip4s
if ip6s is None:
ip6s = ["2001::", "2002::", "a::"]
self.ip6s = ip6s
if ip4 is None:
ip4 = self.ip4s[0]
self.ip4 = ip4
if ip6 is None:
ip6 = self.ip6s[0]
self.ip6 = ip6
class GuiConfig(yaml.YAMLObject):
yaml_tag = "!GuiConfig"
yaml_loader = yaml.SafeLoader
def __init__(
self,
preferences: PreferencesConfig = None,
location: LocationConfig = None,
servers: List[CoreServer] = None,
nodes: List[CustomNode] = None,
recentfiles: List[str] = None,
observers: List[Observer] = None,
scale: float = 1.0,
ips: IpConfigs = None,
mac: str = "00:00:00:aa:00:00",
) -> None:
if preferences is None:
preferences = PreferencesConfig()
self.preferences = preferences
if location is None:
location = LocationConfig()
self.location = location
if servers is None:
servers = []
self.servers = servers
if nodes is None:
nodes = []
self.nodes = nodes
if recentfiles is None:
recentfiles = []
self.recentfiles = recentfiles
if observers is None:
observers = []
self.observers = observers
self.scale = scale
if ips is None:
ips = IpConfigs()
self.ips = ips
self.mac = mac
def copy_files(current_path, new_path) -> None:
for current_file in current_path.glob("*"): for current_file in current_path.glob("*"):
new_file = new_path.joinpath(current_file.name) new_file = new_path.joinpath(current_file.name)
shutil.copy(current_file, new_file) shutil.copy(current_file, new_file)
def find_terminal(): def find_terminal() -> Optional[str]:
for term in sorted(TERMINALS): for term in sorted(TERMINALS):
cmd = TERMINALS[term] cmd = TERMINALS[term]
if shutil.which(term): if shutil.which(term):
@ -63,7 +196,7 @@ def find_terminal():
return None return None
def check_directory(): def check_directory() -> None:
if HOME_PATH.exists(): if HOME_PATH.exists():
return return
HOME_PATH.mkdir() HOME_PATH.mkdir()
@ -85,45 +218,16 @@ def check_directory():
editor = EDITORS[0] editor = EDITORS[0]
else: else:
editor = EDITORS[1] editor = EDITORS[1]
config = { preferences = PreferencesConfig(editor, terminal)
"preferences": { config = GuiConfig(preferences=preferences)
"theme": themes.THEME_DARK,
"editor": editor,
"terminal": terminal,
"gui3d": "/usr/local/bin/std3d.sh",
"width": 1000,
"height": 750,
},
"location": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"lat": 47.5791667,
"lon": -122.132322,
"alt": 2.0,
"scale": 150.0,
},
"servers": [],
"nodes": [],
"recentfiles": [],
"observers": [],
"scale": 1.0,
"ips": {
"ip4": DEFAULT_IP4,
"ip6": DEFAULT_IP6,
"ip4s": DEFAULT_IP4S,
"ip6s": DEFAULT_IP6S,
},
"mac": DEFAULT_MAC,
}
save(config) save(config)
def read(): def read() -> GuiConfig:
with CONFIG_PATH.open("r") as f: with CONFIG_PATH.open("r") as f:
return yaml.load(f, Loader=yaml.SafeLoader) return yaml.load(f, Loader=yaml.SafeLoader)
def save(config): def save(config: GuiConfig) -> None:
with CONFIG_PATH.open("w") as f: with CONFIG_PATH.open("w") as f:
yaml.dump(config, f, Dumper=IndentDumper, default_flow_style=False) yaml.dump(config, f, Dumper=IndentDumper, default_flow_style=False)

View file

@ -34,19 +34,6 @@ if TYPE_CHECKING:
GUI_SOURCE = "gui" GUI_SOURCE = "gui"
class CoreServer:
def __init__(self, name: str, address: str, port: int):
self.name = name
self.address = address
self.port = port
class Observer:
def __init__(self, name: str, cmd: str):
self.name = name
self.cmd = cmd
class CoreClient: class CoreClient:
def __init__(self, app: "Application", proxy: bool): def __init__(self, app: "Application", proxy: bool):
""" """
@ -126,22 +113,17 @@ class CoreClient:
self.observer = value self.observer = value
def read_config(self): def read_config(self):
# read distributed server # read distributed servers
for config in self.app.guiconfig.get("servers", []): for server in self.app.guiconfig.servers:
server = CoreServer(config["name"], config["address"], config["port"])
self.servers[server.name] = server self.servers[server.name] = server
# read custom nodes # read custom nodes
for config in self.app.guiconfig.get("nodes", []): for custom_node in self.app.guiconfig.nodes:
name = config["name"] node_draw = NodeDraw.from_custom(custom_node)
image_file = config["image"] self.custom_nodes[custom_node.name] = node_draw
services = set(config["services"])
node_draw = NodeDraw.from_custom(name, image_file, services)
self.custom_nodes[name] = node_draw
# read observers # read observers
for config in self.app.guiconfig.get("observers", []): for observer in self.app.guiconfig.observers:
observer = Observer(config["name"], config["cmd"])
self.custom_observers[observer.name] = observer self.custom_observers[observer.name] = observer
def handle_events(self, event: core_pb2.Event): def handle_events(self, event: core_pb2.Event):
@ -367,8 +349,8 @@ class CoreClient:
self.app.canvas.adjust_to_dim.set(fit_image) self.app.canvas.adjust_to_dim.set(fit_image)
wallpaper_style = canvas_config.get("wallpaper-style", 1) wallpaper_style = canvas_config.get("wallpaper-style", 1)
self.app.canvas.scale_option.set(wallpaper_style) self.app.canvas.scale_option.set(wallpaper_style)
width = self.app.guiconfig["preferences"]["width"] width = self.app.guiconfig.preferences.width
height = self.app.guiconfig["preferences"]["height"] height = self.app.guiconfig.preferences.height
dimensions = canvas_config.get("dimensions", [width, height]) dimensions = canvas_config.get("dimensions", [width, height])
self.app.canvas.redraw_canvas(dimensions) self.app.canvas.redraw_canvas(dimensions)
wallpaper = canvas_config.get("wallpaper") wallpaper = canvas_config.get("wallpaper")
@ -418,15 +400,15 @@ class CoreClient:
try: try:
response = self.client.create_session() response = self.client.create_session()
logging.info("created session: %s", response) logging.info("created session: %s", response)
location_config = self.app.guiconfig["location"] location_config = self.app.guiconfig.location
self.location = core_pb2.SessionLocation( self.location = core_pb2.SessionLocation(
x=location_config["x"], x=location_config.x,
y=location_config["y"], y=location_config.y,
z=location_config["z"], z=location_config.z,
lat=location_config["lat"], lat=location_config.lat,
lon=location_config["lon"], lon=location_config.lon,
alt=location_config["alt"], alt=location_config.alt,
scale=location_config["scale"], scale=location_config.scale,
) )
self.join_session(response.session_id, query_location=False) self.join_session(response.session_id, query_location=False)
except grpc.RpcError as e: except grpc.RpcError as e:
@ -585,7 +567,7 @@ class CoreClient:
def launch_terminal(self, node_id: int): def launch_terminal(self, node_id: int):
try: try:
terminal = self.app.guiconfig["preferences"]["terminal"] terminal = self.app.guiconfig.preferences.terminal
if not terminal: if not terminal:
messagebox.showerror( messagebox.showerror(
"Terminal Error", "Terminal Error",

View file

@ -241,16 +241,16 @@ class SizeAndScaleDialog(Dialog):
location.alt = self.alt.get() location.alt = self.alt.get()
location.scale = self.scale.get() location.scale = self.scale.get()
if self.save_default.get(): if self.save_default.get():
location_config = self.app.guiconfig["location"] location_config = self.app.guiconfig.location
location_config["x"] = location.x location_config.x = location.x
location_config["y"] = location.y location_config.y = location.y
location_config["z"] = location.z location_config.z = location.z
location_config["lat"] = location.lat location_config.lat = location.lat
location_config["lon"] = location.lon location_config.lon = location.lon
location_config["alt"] = location.alt location_config.alt = location.alt
location_config["scale"] = location.scale location_config.scale = location.scale
preferences = self.app.guiconfig["preferences"] preferences = self.app.guiconfig.preferences
preferences["width"] = width preferences.width = width
preferences["height"] = height preferences.height = height
self.app.save_config() self.app.save_config()
self.destroy() self.destroy()

View file

@ -5,7 +5,7 @@ from tkinter import ttk
from typing import TYPE_CHECKING, Set from typing import TYPE_CHECKING, Set
from core.gui import nodeutils from core.gui import nodeutils
from core.gui.appconfig import ICONS_PATH from core.gui.appconfig import ICONS_PATH, CustomNode
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.images import Images from core.gui.images import Images
from core.gui.nodeutils import NodeDraw from core.gui.nodeutils import NodeDraw
@ -201,17 +201,12 @@ class CustomNodesDialog(Dialog):
self.services.update(dialog.current_services) self.services.update(dialog.current_services)
def click_save(self): def click_save(self):
self.app.guiconfig["nodes"].clear() self.app.guiconfig.nodes.clear()
for name in sorted(self.app.core.custom_nodes): for name in self.app.core.custom_nodes:
node_draw = self.app.core.custom_nodes[name] node_draw = self.app.core.custom_nodes[name]
self.app.guiconfig["nodes"].append( custom_node = CustomNode(name, node_draw.image_file, node_draw.services)
{ self.app.guiconfig.nodes.append(custom_node)
"name": name, logging.info("saving custom nodes: %s", self.app.guiconfig.nodes)
"image": node_draw.image_file,
"services": list(node_draw.services),
}
)
logging.info("saving custom nodes: %s", self.app.guiconfig["nodes"])
self.app.save_config() self.app.save_config()
self.destroy() self.destroy()
@ -219,7 +214,8 @@ class CustomNodesDialog(Dialog):
name = self.name.get() name = self.name.get()
if name not in self.app.core.custom_nodes: if name not in self.app.core.custom_nodes:
image_file = Path(self.image_file).stem image_file = Path(self.image_file).stem
node_draw = NodeDraw.from_custom(name, image_file, set(self.services)) custom_node = CustomNode(name, image_file, list(self.services))
node_draw = NodeDraw.from_custom(custom_node)
logging.info( logging.info(
"created new custom node (%s), image file (%s), services: (%s)", "created new custom node (%s), image file (%s), services: (%s)",
name, name,

View file

@ -45,7 +45,7 @@ class FindDialog(Dialog):
) )
self.tree.grid(sticky="nsew", pady=PADY) self.tree.grid(sticky="nsew", pady=PADY)
style = ttk.Style() style = ttk.Style()
heading_size = int(self.app.guiconfig["scale"] * 10) heading_size = int(self.app.guiconfig.scale * 10)
style.configure("Treeview.Heading", font=(None, heading_size, "bold")) style.configure("Treeview.Heading", font=(None, heading_size, "bold"))
self.tree.column("nodeid", stretch=tk.YES, anchor="center") self.tree.column("nodeid", stretch=tk.YES, anchor="center")
self.tree.heading("nodeid", text="Node ID") self.tree.heading("nodeid", text="Node ID")
@ -124,7 +124,7 @@ class FindDialog(Dialog):
canvas_node = self.app.core.canvas_nodes[node_id] canvas_node = self.app.core.canvas_nodes[node_id]
x0, y0, x1, y1 = self.app.canvas.bbox(canvas_node.id) x0, y0, x1, y1 = self.app.canvas.bbox(canvas_node.id)
dist = 5 * self.app.guiconfig["scale"] dist = 5 * self.app.guiconfig.scale
self.app.canvas.create_oval( self.app.canvas.create_oval(
x0 - dist, x0 - dist,
y0 - dist, y0 - dist,
@ -132,7 +132,7 @@ class FindDialog(Dialog):
y1 + dist, y1 + dist,
tags="find", tags="find",
outline="red", outline="red",
width=3.0 * self.app.guiconfig["scale"], width=3.0 * self.app.guiconfig.scale,
) )
_x, _y, _, _ = self.app.canvas.bbox(canvas_node.id) _x, _y, _, _ = self.app.canvas.bbox(canvas_node.id)

View file

@ -4,7 +4,6 @@ from typing import TYPE_CHECKING
import netaddr import netaddr
from core.gui import appconfig
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY from core.gui.themes import FRAME_PAD, PADX, PADY
from core.gui.widgets import ListboxScroll from core.gui.widgets import ListboxScroll
@ -16,11 +15,10 @@ if TYPE_CHECKING:
class IpConfigDialog(Dialog): class IpConfigDialog(Dialog):
def __init__(self, app: "Application") -> None: def __init__(self, app: "Application") -> None:
super().__init__(app, "IP Configuration") super().__init__(app, "IP Configuration")
ip_config = self.app.guiconfig.setdefault("ips") self.ip4 = self.app.guiconfig.ips.ip4
self.ip4 = ip_config.setdefault("ip4", appconfig.DEFAULT_IP4) self.ip6 = self.app.guiconfig.ips.ip6
self.ip6 = ip_config.setdefault("ip6", appconfig.DEFAULT_IP6) self.ip4s = self.app.guiconfig.ips.ip4s
self.ip4s = ip_config.setdefault("ip4s", appconfig.DEFAULT_IP4S) self.ip6s = self.app.guiconfig.ips.ip6s
self.ip6s = ip_config.setdefault("ip6s", appconfig.DEFAULT_IP6S)
self.ip4_entry = None self.ip4_entry = None
self.ip4_listbox = None self.ip4_listbox = None
self.ip6_entry = None self.ip6_entry = None
@ -143,11 +141,11 @@ class IpConfigDialog(Dialog):
for index in range(self.ip6_listbox.listbox.size()): for index in range(self.ip6_listbox.listbox.size()):
ip6 = self.ip6_listbox.listbox.get(index) ip6 = self.ip6_listbox.listbox.get(index)
ip6s.append(ip6) ip6s.append(ip6)
ip_config = self.app.guiconfig["ips"] ip_config = self.app.guiconfig.ips
ip_config["ip4"] = self.ip4 ip_config.ip4 = self.ip4
ip_config["ip6"] = self.ip6 ip_config.ip6 = self.ip6
ip_config["ip4s"] = ip4s ip_config.ip4s = ip4s
ip_config["ip6s"] = ip6s ip_config.ip6s = ip6s
self.app.core.interfaces_manager.update_ips(self.ip4, self.ip6) self.app.core.interfaces_manager.update_ips(self.ip4, self.ip6)
self.app.save_config() self.app.save_config()
self.destroy() self.destroy()

View file

@ -4,7 +4,6 @@ from typing import TYPE_CHECKING
import netaddr import netaddr
from core.gui import appconfig
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY from core.gui.themes import PADX, PADY
@ -15,7 +14,7 @@ if TYPE_CHECKING:
class MacConfigDialog(Dialog): class MacConfigDialog(Dialog):
def __init__(self, app: "Application") -> None: def __init__(self, app: "Application") -> None:
super().__init__(app, "MAC Configuration") super().__init__(app, "MAC Configuration")
mac = self.app.guiconfig.get("mac", appconfig.DEFAULT_MAC) mac = self.app.guiconfig.mac
self.mac_var = tk.StringVar(value=mac) self.mac_var = tk.StringVar(value=mac)
self.draw() self.draw()
@ -57,6 +56,6 @@ class MacConfigDialog(Dialog):
messagebox.showerror("MAC Error", f"{mac} is an invalid mac") messagebox.showerror("MAC Error", f"{mac} is an invalid mac")
else: else:
self.app.core.interfaces_manager.mac = netaddr.EUI(mac) self.app.core.interfaces_manager.mac = netaddr.EUI(mac)
self.app.guiconfig["mac"] = mac self.app.guiconfig.mac = mac
self.app.save_config() self.app.save_config()
self.destroy() self.destroy()

View file

@ -2,7 +2,7 @@ import tkinter as tk
from tkinter import messagebox, ttk from tkinter import messagebox, ttk
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from core.gui.coreclient import Observer from core.gui.appconfig import Observer
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY from core.gui.themes import PADX, PADY
from core.gui.widgets import ListboxScroll from core.gui.widgets import ListboxScroll
@ -89,11 +89,9 @@ class ObserverDialog(Dialog):
button.grid(row=0, column=1, sticky="ew") button.grid(row=0, column=1, sticky="ew")
def click_save_config(self): def click_save_config(self):
observers = [] self.app.guiconfig.observers.clear()
for name in sorted(self.app.core.custom_observers): for observer in self.app.core.custom_observers.values():
observer = self.app.core.custom_observers[name] self.app.guiconfig.observers.append(observer)
observers.append({"name": observer.name, "cmd": observer.cmd})
self.app.guiconfig["observers"] = observers
self.app.save_config() self.app.save_config()
self.destroy() self.destroy()

View file

@ -19,11 +19,11 @@ class PreferencesDialog(Dialog):
def __init__(self, app: "Application"): def __init__(self, app: "Application"):
super().__init__(app, "Preferences") super().__init__(app, "Preferences")
self.gui_scale = tk.DoubleVar(value=self.app.app_scale) self.gui_scale = tk.DoubleVar(value=self.app.app_scale)
preferences = self.app.guiconfig["preferences"] preferences = self.app.guiconfig.preferences
self.editor = tk.StringVar(value=preferences["editor"]) self.editor = tk.StringVar(value=preferences.editor)
self.theme = tk.StringVar(value=preferences["theme"]) self.theme = tk.StringVar(value=preferences.theme)
self.terminal = tk.StringVar(value=preferences["terminal"]) self.terminal = tk.StringVar(value=preferences.terminal)
self.gui3d = tk.StringVar(value=preferences["gui3d"]) self.gui3d = tk.StringVar(value=preferences.gui3d)
self.draw() self.draw()
def draw(self): def draw(self):
@ -110,15 +110,14 @@ class PreferencesDialog(Dialog):
self.app.style.theme_use(theme) self.app.style.theme_use(theme)
def click_save(self): def click_save(self):
preferences = self.app.guiconfig["preferences"] preferences = self.app.guiconfig.preferences
preferences["terminal"] = self.terminal.get() preferences.terminal = self.terminal.get()
preferences["editor"] = self.editor.get() preferences.editor = self.editor.get()
preferences["gui3d"] = self.gui3d.get() preferences.gui3d = self.gui3d.get()
preferences["theme"] = self.theme.get() preferences.theme = self.theme.get()
self.gui_scale.set(round(self.gui_scale.get(), 2)) self.gui_scale.set(round(self.gui_scale.get(), 2))
app_scale = self.gui_scale.get() app_scale = self.gui_scale.get()
self.app.guiconfig["scale"] = app_scale self.app.guiconfig.scale = app_scale
self.app.save_config() self.app.save_config()
self.scale_adjust() self.scale_adjust()
self.destroy() self.destroy()

View file

@ -2,7 +2,7 @@ import tkinter as tk
from tkinter import ttk from tkinter import ttk
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from core.gui.coreclient import CoreServer from core.gui.appconfig import CoreServer
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY from core.gui.themes import FRAME_PAD, PADX, PADY
from core.gui.widgets import ListboxScroll from core.gui.widgets import ListboxScroll
@ -20,7 +20,6 @@ class ServersDialog(Dialog):
super().__init__(app, "CORE Servers") super().__init__(app, "CORE Servers")
self.name = tk.StringVar(value=DEFAULT_NAME) self.name = tk.StringVar(value=DEFAULT_NAME)
self.address = tk.StringVar(value=DEFAULT_ADDRESS) self.address = tk.StringVar(value=DEFAULT_ADDRESS)
self.port = tk.IntVar(value=DEFAULT_PORT)
self.servers = None self.servers = None
self.selected_index = None self.selected_index = None
self.selected = None self.selected = None
@ -54,31 +53,17 @@ class ServersDialog(Dialog):
frame.grid(pady=PADY, sticky="ew") frame.grid(pady=PADY, sticky="ew")
frame.columnconfigure(1, weight=1) frame.columnconfigure(1, weight=1)
frame.columnconfigure(3, weight=1) frame.columnconfigure(3, weight=1)
frame.columnconfigure(5, weight=1)
label = ttk.Label(frame, text="Name") label = ttk.Label(frame, text="Name")
label.grid(row=0, column=0, sticky="w", padx=PADX, pady=PADY) label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.name) entry = ttk.Entry(frame, textvariable=self.name)
entry.grid(row=0, column=1, sticky="ew") entry.grid(row=0, column=1, sticky="ew")
label = ttk.Label(frame, text="Address") label = ttk.Label(frame, text="Address")
label.grid(row=0, column=2, sticky="w", padx=PADX, pady=PADY) label.grid(row=0, column=2, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.address) entry = ttk.Entry(frame, textvariable=self.address)
entry.grid(row=0, column=3, sticky="ew") entry.grid(row=0, column=3, sticky="ew")
label = ttk.Label(frame, text="Port")
label.grid(row=0, column=4, sticky="w", padx=PADX, pady=PADY)
entry = ttk.Entry(
frame,
textvariable=self.port,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
)
entry.bind(
"<FocusOut>", lambda event: self.app.validation.focus_out(event, "50051")
)
entry.grid(row=0, column=5, sticky="ew")
def draw_servers_buttons(self): def draw_servers_buttons(self):
frame = ttk.Frame(self.top) frame = ttk.Frame(self.top)
frame.grid(pady=PADY, sticky="ew") frame.grid(pady=PADY, sticky="ew")
@ -113,13 +98,9 @@ class ServersDialog(Dialog):
button.grid(row=0, column=1, sticky="ew") button.grid(row=0, column=1, sticky="ew")
def click_save_configuration(self): def click_save_configuration(self):
servers = [] self.app.guiconfig.servers.clear()
for name in sorted(self.app.core.servers): for server in self.app.core.servers.values():
server = self.app.core.servers[name] self.app.guiconfig.servers.append(server)
servers.append(
{"name": server.name, "address": server.address, "port": server.port}
)
self.app.guiconfig["servers"] = servers
self.app.save_config() self.app.save_config()
self.destroy() self.destroy()
@ -127,8 +108,7 @@ class ServersDialog(Dialog):
name = self.name.get() name = self.name.get()
if name not in self.app.core.servers: if name not in self.app.core.servers:
address = self.address.get() address = self.address.get()
port = self.port.get() server = CoreServer(name, address)
server = CoreServer(name, address, port)
self.app.core.servers[name] = server self.app.core.servers[name] = server
self.servers.insert(tk.END, name) self.servers.insert(tk.END, name)
@ -140,7 +120,6 @@ class ServersDialog(Dialog):
server = self.app.core.servers.pop(previous_name) server = self.app.core.servers.pop(previous_name)
server.name = name server.name = name
server.address = self.address.get() server.address = self.address.get()
server.port = self.port.get()
self.app.core.servers[name] = server self.app.core.servers[name] = server
self.servers.delete(self.selected_index) self.servers.delete(self.selected_index)
self.servers.insert(self.selected_index, name) self.servers.insert(self.selected_index, name)
@ -154,7 +133,6 @@ class ServersDialog(Dialog):
self.selected_index = None self.selected_index = None
self.name.set(DEFAULT_NAME) self.name.set(DEFAULT_NAME)
self.address.set(DEFAULT_ADDRESS) self.address.set(DEFAULT_ADDRESS)
self.port.set(DEFAULT_PORT)
self.servers.selection_clear(0, tk.END) self.servers.selection_clear(0, tk.END)
self.save_button.config(state=tk.DISABLED) self.save_button.config(state=tk.DISABLED)
self.delete_button.config(state=tk.DISABLED) self.delete_button.config(state=tk.DISABLED)
@ -167,7 +145,6 @@ class ServersDialog(Dialog):
server = self.app.core.servers[self.selected] server = self.app.core.servers[self.selected]
self.name.set(server.name) self.name.set(server.name)
self.address.set(server.address) self.address.set(server.address)
self.port.set(server.port)
self.save_button.config(state=tk.NORMAL) self.save_button.config(state=tk.NORMAL)
self.delete_button.config(state=tk.NORMAL) self.delete_button.config(state=tk.NORMAL)
else: else:

View file

@ -70,7 +70,7 @@ class SessionsDialog(Dialog):
selectmode=tk.BROWSE, selectmode=tk.BROWSE,
) )
style = ttk.Style() style = ttk.Style()
heading_size = int(self.app.guiconfig["scale"] * 10) heading_size = int(self.app.guiconfig.scale * 10)
style.configure("Treeview.Heading", font=(None, heading_size, "bold")) style.configure("Treeview.Heading", font=(None, heading_size, "bold"))
self.tree.grid(sticky="nsew") self.tree.grid(sticky="nsew")
self.tree.column("id", stretch=tk.YES, anchor="center") self.tree.column("id", stretch=tk.YES, anchor="center")

View file

@ -1002,10 +1002,10 @@ class CanvasGraph(tk.Canvas):
if NodeUtils.is_custom( if NodeUtils.is_custom(
canvas_node.core_node.type, canvas_node.core_node.model canvas_node.core_node.type, canvas_node.core_node.model
): ):
for custom_node in self.app.guiconfig["nodes"]: for custom_node in self.app.guiconfig.nodes:
if custom_node["name"] == canvas_node.core_node.model: if custom_node.name == canvas_node.core_node.model:
img = Images.get_custom( img = Images.get_custom(
custom_node["image"], int(ICON_SIZE * self.app.app_scale) custom_node.image, int(ICON_SIZE * self.app.app_scale)
) )
else: else:
image_enum = TypeToImage.get( image_enum = TypeToImage.get(

View file

@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, Any, List, Optional, Set, Tuple
import netaddr import netaddr
from netaddr import EUI, IPNetwork from netaddr import EUI, IPNetwork
from core.gui import appconfig
from core.gui.nodeutils import NodeUtils from core.gui.nodeutils import NodeUtils
if TYPE_CHECKING: if TYPE_CHECKING:
@ -44,14 +43,13 @@ class Subnets:
class InterfaceManager: class InterfaceManager:
def __init__(self, app: "Application") -> None: def __init__(self, app: "Application") -> None:
self.app = app self.app = app
ip_config = self.app.guiconfig.get("ips", {}) ip4 = self.app.guiconfig.ips.ip4
ip4 = ip_config.get("ip4", appconfig.DEFAULT_IP4) ip6 = self.app.guiconfig.ips.ip6
ip6 = ip_config.get("ip6", appconfig.DEFAULT_IP6)
self.ip4_mask = 24 self.ip4_mask = 24
self.ip6_mask = 64 self.ip6_mask = 64
self.ip4_subnets = IPNetwork(f"{ip4}/{self.ip4_mask}") self.ip4_subnets = IPNetwork(f"{ip4}/{self.ip4_mask}")
self.ip6_subnets = IPNetwork(f"{ip6}/{self.ip6_mask}") self.ip6_subnets = IPNetwork(f"{ip6}/{self.ip6_mask}")
mac = self.app.guiconfig.get("mac", appconfig.DEFAULT_MAC) mac = self.app.guiconfig.mac
self.mac = EUI(mac, dialect=netaddr.mac_unix_expanded) self.mac = EUI(mac, dialect=netaddr.mac_unix_expanded)
self.current_mac = None self.current_mac = None
self.current_subnets = None self.current_subnets = None

View file

@ -93,7 +93,7 @@ class Menubar(tk.Menu):
) )
self.app.bind_all("<Control-o>", self.click_open_xml) self.app.bind_all("<Control-o>", self.click_open_xml)
self.recent_menu = tk.Menu(menu) self.recent_menu = tk.Menu(menu)
for i in self.app.guiconfig["recentfiles"]: for i in self.app.guiconfig.recentfiles:
self.recent_menu.add_command( self.recent_menu.add_command(
label=i, command=partial(self.open_recent_files, i) label=i, command=partial(self.open_recent_files, i)
) )
@ -298,7 +298,7 @@ class Menubar(tk.Menu):
def update_recent_files(self) -> None: def update_recent_files(self) -> None:
self.recent_menu.delete(0, tk.END) self.recent_menu.delete(0, tk.END)
for i in self.app.guiconfig["recentfiles"]: for i in self.app.guiconfig.recentfiles:
self.recent_menu.add_command( self.recent_menu.add_command(
label=i, command=partial(self.open_recent_files, i) label=i, command=partial(self.open_recent_files, i)
) )
@ -350,7 +350,7 @@ class Menubar(tk.Menu):
dialog.show() dialog.show()
def add_recent_file_to_gui_config(self, file_path) -> None: def add_recent_file_to_gui_config(self, file_path) -> None:
recent_files = self.app.guiconfig["recentfiles"] recent_files = self.app.guiconfig.recentfiles
num_files = len(recent_files) num_files = len(recent_files)
if num_files == 0: if num_files == 0:
recent_files.insert(0, file_path) recent_files.insert(0, file_path)

View file

@ -1,7 +1,8 @@
import logging import logging
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Union from typing import TYPE_CHECKING, List, Optional, Set
from core.api.grpc.core_pb2 import Node, NodeType from core.api.grpc.core_pb2 import Node, NodeType
from core.gui.appconfig import CustomNode, GuiConfig
from core.gui.images import ImageEnum, Images, TypeToImage from core.gui.images import ImageEnum, Images, TypeToImage
if TYPE_CHECKING: if TYPE_CHECKING:
@ -41,16 +42,16 @@ class NodeDraw:
return node_draw return node_draw
@classmethod @classmethod
def from_custom(cls, name: str, image_file: str, services: Set[str]): def from_custom(cls, custom_node: CustomNode):
node_draw = NodeDraw() node_draw = NodeDraw()
node_draw.custom = True node_draw.custom = True
node_draw.image_file = image_file node_draw.image_file = custom_node.image
node_draw.image = Images.get_custom(image_file, ICON_SIZE) node_draw.image = Images.get_custom(custom_node.image, ICON_SIZE)
node_draw.node_type = NodeType.DEFAULT node_draw.node_type = NodeType.DEFAULT
node_draw.services = services node_draw.services = custom_node.services
node_draw.label = name node_draw.label = custom_node.name
node_draw.model = name node_draw.model = custom_node.name
node_draw.tooltip = name node_draw.tooltip = custom_node.name
return node_draw return node_draw
@ -97,11 +98,7 @@ class NodeUtils:
@classmethod @classmethod
def node_icon( def node_icon(
cls, cls, node_type: NodeType, model: str, gui_config: GuiConfig, scale=1.0
node_type: NodeType,
model: str,
gui_config: Dict[str, List[Dict[str, str]]],
scale=1.0,
) -> "ImageTk.PhotoImage": ) -> "ImageTk.PhotoImage":
image_enum = TypeToImage.get(node_type, model) image_enum = TypeToImage.get(node_type, model)
@ -114,10 +111,7 @@ class NodeUtils:
@classmethod @classmethod
def node_image( def node_image(
cls, cls, core_node: "core_pb2.Node", gui_config: GuiConfig, scale=1.0
core_node: "core_pb2.Node",
gui_config: Dict[str, List[Dict[str, str]]],
scale=1.0,
) -> "ImageTk.PhotoImage": ) -> "ImageTk.PhotoImage":
image = cls.node_icon(core_node.type, core_node.model, gui_config, scale) image = cls.node_icon(core_node.type, core_node.model, gui_config, scale)
if core_node.icon: if core_node.icon:
@ -132,20 +126,17 @@ class NodeUtils:
return node_type == NodeType.DEFAULT and model not in cls.NODE_MODELS return node_type == NodeType.DEFAULT and model not in cls.NODE_MODELS
@classmethod @classmethod
def get_custom_node_services( def get_custom_node_services(cls, gui_config: GuiConfig, name: str) -> List[str]:
cls, gui_config: Dict[str, List[Dict[str, str]]], name: str for custom_node in gui_config.nodes:
) -> List[str]: if custom_node.name == name:
for m in gui_config["nodes"]: return custom_node.services
if m["name"] == name:
return m["services"]
return [] return []
@classmethod @classmethod
def get_image_file(cls, gui_config, name: str) -> Union[str, None]: def get_image_file(cls, gui_config: GuiConfig, name: str) -> Optional[str]:
if "nodes" in gui_config: for custom_node in gui_config.nodes:
for m in gui_config["nodes"]: if custom_node.name == name:
if m["name"] == name: return custom_node.image
return m["image"]
return None return None
@classmethod @classmethod