pygui: added type hinting to class variables for core.gui.dialogs

This commit is contained in:
Blake Harnden 2020-06-22 11:04:33 -07:00
parent 11be40bc90
commit 527d34e374
37 changed files with 664 additions and 613 deletions

View file

@ -38,7 +38,7 @@ from core.api.grpc.mobility_pb2 import MobilityConfig
from core.api.grpc.services_pb2 import NodeServiceData, ServiceConfig, ServiceFileConfig
from core.api.grpc.wlan_pb2 import WlanConfig
from core.gui import appconfig
from core.gui.appconfig import CoreServer
from core.gui.appconfig import CoreServer, Observer
from core.gui.dialogs.emaneinstall import EmaneInstallDialog
from core.gui.dialogs.error import ErrorDialog
from core.gui.dialogs.mobilityplayer import MobilityPlayer
@ -75,7 +75,7 @@ class CoreClient:
# loaded configuration data
self.servers: Dict[str, CoreServer] = {}
self.custom_nodes: Dict[str, NodeDraw] = {}
self.custom_observers: Dict[str, str] = {}
self.custom_observers: Dict[str, Observer] = {}
self.read_config()
# helpers

View file

@ -35,11 +35,11 @@ THE POSSIBILITY OF SUCH DAMAGE.\
class AboutDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "About CORE")
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)

View file

@ -3,9 +3,9 @@ check engine light
"""
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Optional
from core.api.grpc.core_pb2 import ExceptionLevel
from core.api.grpc.core_pb2 import ExceptionEvent, ExceptionLevel
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY
from core.gui.widgets import CodeText
@ -15,14 +15,14 @@ if TYPE_CHECKING:
class AlertsDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Alerts")
self.tree = None
self.codetext = None
self.alarm_map = {}
self.tree: Optional[ttk.Treeview] = None
self.codetext: Optional[CodeText] = None
self.alarm_map: Dict[int, ExceptionEvent] = {}
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
self.top.rowconfigure(1, weight=1)
@ -97,13 +97,13 @@ class AlertsDialog(Dialog):
button = ttk.Button(frame, text="Close", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def reset_alerts(self):
def reset_alerts(self) -> None:
self.codetext.text.delete("1.0", tk.END)
for item in self.tree.get_children():
self.tree.delete(item)
self.app.statusbar.core_alarms.clear()
def click_select(self, event: tk.Event):
def click_select(self, event: tk.Event) -> None:
current = self.tree.selection()[0]
alarm = self.alarm_map[current]
self.codetext.text.config(state=tk.NORMAL)

View file

@ -7,38 +7,43 @@ from typing import TYPE_CHECKING
from core.gui import validation
from core.gui.dialogs.dialog import Dialog
from core.gui.graph.graph import CanvasGraph
from core.gui.themes import FRAME_PAD, PADX, PADY
if TYPE_CHECKING:
from core.gui.app import Application
PIXEL_SCALE = 100
PIXEL_SCALE: int = 100
class SizeAndScaleDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
"""
create an instance for size and scale object
"""
super().__init__(app, "Canvas Size and Scale")
self.canvas = self.app.canvas
self.section_font = font.Font(weight="bold")
self.canvas: CanvasGraph = self.app.canvas
self.section_font: font.Font = font.Font(weight="bold")
width, height = self.canvas.current_dimensions
self.pixel_width = tk.IntVar(value=width)
self.pixel_height = tk.IntVar(value=height)
self.pixel_width: tk.IntVar = tk.IntVar(value=width)
self.pixel_height: tk.IntVar = tk.IntVar(value=height)
location = self.app.core.location
self.x = tk.DoubleVar(value=location.x)
self.y = tk.DoubleVar(value=location.y)
self.lat = tk.DoubleVar(value=location.lat)
self.lon = tk.DoubleVar(value=location.lon)
self.alt = tk.DoubleVar(value=location.alt)
self.scale = tk.DoubleVar(value=location.scale)
self.meters_width = tk.IntVar(value=width / PIXEL_SCALE * location.scale)
self.meters_height = tk.IntVar(value=height / PIXEL_SCALE * location.scale)
self.save_default = tk.BooleanVar(value=False)
self.x: tk.DoubleVar = tk.DoubleVar(value=location.x)
self.y: tk.DoubleVar = tk.DoubleVar(value=location.y)
self.lat: tk.DoubleVar = tk.DoubleVar(value=location.lat)
self.lon: tk.DoubleVar = tk.DoubleVar(value=location.lon)
self.alt: tk.DoubleVar = tk.DoubleVar(value=location.alt)
self.scale: tk.DoubleVar = tk.DoubleVar(value=location.scale)
self.meters_width: tk.IntVar = tk.IntVar(
value=width / PIXEL_SCALE * location.scale
)
self.meters_height: tk.IntVar = tk.IntVar(
value=height / PIXEL_SCALE * location.scale
)
self.save_default: tk.BooleanVar = tk.BooleanVar(value=False)
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.draw_size()
self.draw_scale()
@ -47,7 +52,7 @@ class SizeAndScaleDialog(Dialog):
self.draw_spacer()
self.draw_buttons()
def draw_size(self):
def draw_size(self) -> None:
label_frame = ttk.Labelframe(self.top, text="Size", padding=FRAME_PAD)
label_frame.grid(sticky="ew")
label_frame.columnconfigure(0, weight=1)
@ -84,7 +89,7 @@ class SizeAndScaleDialog(Dialog):
label = ttk.Label(frame, text="Meters")
label.grid(row=0, column=4, sticky="w")
def draw_scale(self):
def draw_scale(self) -> None:
label_frame = ttk.Labelframe(self.top, text="Scale", padding=FRAME_PAD)
label_frame.grid(sticky="ew")
label_frame.columnconfigure(0, weight=1)
@ -99,7 +104,7 @@ class SizeAndScaleDialog(Dialog):
label = ttk.Label(frame, text="Meters")
label.grid(row=0, column=2, sticky="w")
def draw_reference_point(self):
def draw_reference_point(self) -> None:
label_frame = ttk.Labelframe(
self.top, text="Reference Point", padding=FRAME_PAD
)
@ -150,13 +155,13 @@ class SizeAndScaleDialog(Dialog):
entry = validation.FloatEntry(frame, textvariable=self.alt)
entry.grid(row=0, column=5, sticky="ew")
def draw_save_as_default(self):
def draw_save_as_default(self) -> None:
button = ttk.Checkbutton(
self.top, text="Save as default?", variable=self.save_default
)
button.grid(sticky="w", pady=PADY)
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)
@ -168,7 +173,7 @@ class SizeAndScaleDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_apply(self):
def click_apply(self) -> None:
width, height = self.pixel_width.get(), self.pixel_height.get()
self.canvas.redraw_canvas((width, height))
if self.canvas.wallpaper:

View file

@ -4,10 +4,11 @@ set wallpaper
import logging
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, List, Optional
from core.gui.appconfig import BACKGROUNDS_PATH
from core.gui.dialogs.dialog import Dialog
from core.gui.graph.graph import CanvasGraph
from core.gui.images import Images
from core.gui.themes import PADX, PADY
from core.gui.widgets import image_chooser
@ -17,20 +18,22 @@ if TYPE_CHECKING:
class CanvasWallpaperDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
"""
create an instance of CanvasWallpaper object
"""
super().__init__(app, "Canvas Background")
self.canvas = self.app.canvas
self.scale_option = tk.IntVar(value=self.canvas.scale_option.get())
self.adjust_to_dim = tk.BooleanVar(value=self.canvas.adjust_to_dim.get())
self.filename = tk.StringVar(value=self.canvas.wallpaper_file)
self.image_label = None
self.options = []
self.canvas: CanvasGraph = self.app.canvas
self.scale_option: tk.IntVar = tk.IntVar(value=self.canvas.scale_option.get())
self.adjust_to_dim: tk.BooleanVar = tk.BooleanVar(
value=self.canvas.adjust_to_dim.get()
)
self.filename: tk.StringVar = tk.StringVar(value=self.canvas.wallpaper_file)
self.image_label: Optional[ttk.Label] = None
self.options: List[ttk.Radiobutton] = []
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.draw_image()
self.draw_image_label()
@ -40,19 +43,19 @@ class CanvasWallpaperDialog(Dialog):
self.draw_spacer()
self.draw_buttons()
def draw_image(self):
def draw_image(self) -> None:
self.image_label = ttk.Label(
self.top, text="(image preview)", width=32, anchor=tk.CENTER
)
self.image_label.grid(pady=PADY)
def draw_image_label(self):
def draw_image_label(self) -> None:
label = ttk.Label(self.top, text="Image filename: ")
label.grid(sticky="ew")
if self.filename.get():
self.draw_preview()
def draw_image_selection(self):
def draw_image_selection(self) -> None:
frame = ttk.Frame(self.top)
frame.columnconfigure(0, weight=2)
frame.columnconfigure(1, weight=1)
@ -69,7 +72,7 @@ class CanvasWallpaperDialog(Dialog):
button = ttk.Button(frame, text="Clear", command=self.click_clear)
button.grid(row=0, column=2, sticky="ew")
def draw_options(self):
def draw_options(self) -> None:
frame = ttk.Frame(self.top)
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)
@ -101,7 +104,7 @@ class CanvasWallpaperDialog(Dialog):
button.grid(row=0, column=3, sticky="ew")
self.options.append(button)
def draw_additional_options(self):
def draw_additional_options(self) -> None:
checkbutton = ttk.Checkbutton(
self.top,
text="Adjust canvas size to image dimensions",
@ -110,7 +113,7 @@ class CanvasWallpaperDialog(Dialog):
)
checkbutton.grid(sticky="ew", padx=PADX)
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(pady=PADY, sticky="ew")
frame.columnconfigure(0, weight=1)
@ -122,18 +125,18 @@ class CanvasWallpaperDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_open_image(self):
def click_open_image(self) -> None:
filename = image_chooser(self, BACKGROUNDS_PATH)
if filename:
self.filename.set(filename)
self.draw_preview()
def draw_preview(self):
def draw_preview(self) -> None:
image = Images.create(self.filename.get(), 250, 135)
self.image_label.config(image=image)
self.image_label.image = image
def click_clear(self):
def click_clear(self) -> None:
"""
delete like shown in image link entry if there is any
"""
@ -143,7 +146,7 @@ class CanvasWallpaperDialog(Dialog):
self.image_label.config(image="", width=32)
self.image_label.image = None
def click_adjust_canvas(self):
def click_adjust_canvas(self) -> None:
# deselect all radio buttons and grey them out
if self.adjust_to_dim.get():
self.scale_option.set(0)
@ -155,7 +158,7 @@ class CanvasWallpaperDialog(Dialog):
for option in self.options:
option.config(state=tk.NORMAL)
def click_apply(self):
def click_apply(self) -> None:
self.canvas.scale_option.set(self.scale_option.get())
self.canvas.adjust_to_dim.set(self.adjust_to_dim.get())
self.canvas.show_grid.click_handler()

View file

@ -3,7 +3,7 @@ custom color picker
"""
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional, Tuple
from core.gui import validation
from core.gui.dialogs.dialog import Dialog
@ -18,23 +18,23 @@ class ColorPickerDialog(Dialog):
self, master: tk.BaseWidget, app: "Application", initcolor: str = "#000000"
):
super().__init__(app, "Color Picker", master=master)
self.red_entry = None
self.blue_entry = None
self.green_entry = None
self.hex_entry = None
self.red_label = None
self.green_label = None
self.blue_label = None
self.display = None
self.color = initcolor
self.red_entry: Optional[validation.RgbEntry] = None
self.blue_entry: Optional[validation.RgbEntry] = None
self.green_entry: Optional[validation.RgbEntry] = None
self.hex_entry: Optional[validation.HexEntry] = None
self.red_label: Optional[ttk.Label] = None
self.green_label: Optional[ttk.Label] = None
self.blue_label: Optional[ttk.Label] = None
self.display: Optional[tk.Frame] = None
self.color: str = initcolor
red, green, blue = self.get_rgb(initcolor)
self.red = tk.IntVar(value=red)
self.blue = tk.IntVar(value=blue)
self.green = tk.IntVar(value=green)
self.hex = tk.StringVar(value=initcolor)
self.red_scale = tk.IntVar(value=red)
self.green_scale = tk.IntVar(value=green)
self.blue_scale = tk.IntVar(value=blue)
self.red: tk.IntVar = tk.IntVar(value=red)
self.blue: tk.IntVar = tk.IntVar(value=blue)
self.green: tk.IntVar = tk.IntVar(value=green)
self.hex: tk.StringVar = tk.StringVar(value=initcolor)
self.red_scale: tk.IntVar = tk.IntVar(value=red)
self.green_scale: tk.IntVar = tk.IntVar(value=green)
self.blue_scale: tk.IntVar = tk.IntVar(value=blue)
self.draw()
self.set_bindings()
@ -42,7 +42,7 @@ class ColorPickerDialog(Dialog):
self.show()
return self.color
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(3, weight=1)
@ -136,7 +136,7 @@ class ColorPickerDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def set_bindings(self):
def set_bindings(self) -> None:
self.red_entry.bind("<FocusIn>", lambda x: self.current_focus("rgb"))
self.green_entry.bind("<FocusIn>", lambda x: self.current_focus("rgb"))
self.blue_entry.bind("<FocusIn>", lambda x: self.current_focus("rgb"))
@ -146,7 +146,7 @@ class ColorPickerDialog(Dialog):
self.blue.trace_add("write", self.update_color)
self.hex.trace_add("write", self.update_color)
def button_ok(self):
def button_ok(self) -> None:
self.color = self.hex.get()
self.destroy()
@ -159,10 +159,10 @@ class ColorPickerDialog(Dialog):
green = self.green_entry.get()
return "#%02x%02x%02x" % (int(red), int(green), int(blue))
def current_focus(self, focus: str):
def current_focus(self, focus: str) -> None:
self.focus = focus
def update_color(self, arg1=None, arg2=None, arg3=None):
def update_color(self, arg1=None, arg2=None, arg3=None) -> None:
if self.focus == "rgb":
red = self.red_entry.get()
blue = self.blue_entry.get()
@ -184,7 +184,7 @@ class ColorPickerDialog(Dialog):
self.display.config(background=hex_code)
self.set_label(str(red), str(green), str(blue))
def scale_callback(self, var: tk.IntVar, color_var: tk.IntVar):
def scale_callback(self, var: tk.IntVar, color_var: tk.IntVar) -> None:
color_var.set(var.get())
self.focus = "rgb"
self.update_color()
@ -194,17 +194,17 @@ class ColorPickerDialog(Dialog):
self.green_scale.set(green)
self.blue_scale.set(blue)
def set_entry(self, red: int, green: int, blue: int):
def set_entry(self, red: int, green: int, blue: int) -> None:
self.red.set(red)
self.green.set(green)
self.blue.set(blue)
def set_label(self, red: str, green: str, blue: str):
def set_label(self, red: str, green: str, blue: str) -> None:
self.red_label.configure(background="#%02x%02x%02x" % (int(red), 0, 0))
self.green_label.configure(background="#%02x%02x%02x" % (0, int(green), 0))
self.blue_label.configure(background="#%02x%02x%02x" % (0, 0, int(blue)))
def get_rgb(self, hex_code: str) -> [int, int, int]:
def get_rgb(self, hex_code: str) -> Tuple[int, int, int]:
"""
convert a valid hex code to RGB values
"""

View file

@ -4,10 +4,11 @@ Service configuration dialog
import logging
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, List
from typing import TYPE_CHECKING, Dict, List, Optional, Set
import grpc
from core.api.grpc.common_pb2 import ConfigOption
from core.api.grpc.services_pb2 import ServiceValidationMode
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY
@ -16,6 +17,7 @@ from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
from core.gui.coreclient import CoreClient
class ConfigServiceConfigDialog(Dialog):
@ -26,56 +28,53 @@ class ConfigServiceConfigDialog(Dialog):
service_name: str,
canvas_node: "CanvasNode",
node_id: int,
):
) -> None:
title = f"{service_name} Config Service"
super().__init__(app, title, master=master)
self.core = app.core
self.canvas_node = canvas_node
self.node_id = node_id
self.service_name = service_name
self.radiovar = tk.IntVar()
self.core: "CoreClient" = app.core
self.canvas_node: "CanvasNode" = canvas_node
self.node_id: int = node_id
self.service_name: str = service_name
self.radiovar: tk.IntVar = 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.has_error = False
self.directories: List[str] = []
self.templates: List[str] = []
self.dependencies: List[str] = []
self.executables: List[str] = []
self.startup_commands: List[str] = []
self.validation_commands: List[str] = []
self.shutdown_commands: List[str] = []
self.default_startup: List[str] = []
self.default_validate: List[str] = []
self.default_shutdown: List[str] = []
self.validation_mode: Optional[ServiceValidationMode] = None
self.validation_time: Optional[int] = None
self.validation_period: tk.StringVar = tk.StringVar()
self.modes: List[str] = []
self.mode_configs: Dict[str, str] = {}
self.notebook: Optional[ttk.Notebook] = None
self.templates_combobox: Optional[ttk.Combobox] = None
self.modes_combobox: Optional[ttk.Combobox] = None
self.startup_commands_listbox: Optional[tk.Listbox] = None
self.shutdown_commands_listbox: Optional[tk.Listbox] = None
self.validate_commands_listbox: Optional[tk.Listbox] = None
self.validation_time_entry: Optional[ttk.Entry] = None
self.validation_mode_entry: Optional[ttk.Entry] = None
self.template_text: Optional[CodeText] = None
self.validation_period_entry: Optional[ttk.Entry] = None
self.original_service_files: Dict[str, str] = {}
self.temp_service_files: Dict[str, str] = {}
self.modified_files: Set[str] = set()
self.config_frame: Optional[ConfigFrame] = None
self.default_config: Dict[str, str] = {}
self.config: Dict[str, ConfigOption] = {}
self.has_error: bool = False
self.load()
if not self.has_error:
self.draw()
def load(self):
def load(self) -> None:
try:
self.core.create_nodes_and_links()
service = self.core.config_services[self.service_name]
@ -116,7 +115,7 @@ class ConfigServiceConfigDialog(Dialog):
self.app.show_grpc_exception("Get Config Service Error", e)
self.has_error = True
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
@ -130,7 +129,7 @@ class ConfigServiceConfigDialog(Dialog):
self.draw_tab_validation()
self.draw_buttons()
def draw_tab_files(self):
def draw_tab_files(self) -> None:
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
@ -174,7 +173,7 @@ class ConfigServiceConfigDialog(Dialog):
)
self.template_text.text.bind("<FocusOut>", self.update_template_file_data)
def draw_tab_config(self):
def draw_tab_config(self) -> None:
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
@ -198,7 +197,7 @@ class ConfigServiceConfigDialog(Dialog):
self.config_frame.grid(sticky="nsew", pady=PADY)
tab.rowconfigure(self.config_frame.grid_info()["row"], weight=1)
def draw_tab_startstop(self):
def draw_tab_startstop(self) -> None:
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
@ -239,7 +238,7 @@ class ConfigServiceConfigDialog(Dialog):
elif i == 2:
self.validate_commands_listbox = listbox_scroll.listbox
def draw_tab_validation(self):
def draw_tab_validation(self) -> None:
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="ew")
tab.columnconfigure(0, weight=1)
@ -298,7 +297,7 @@ class ConfigServiceConfigDialog(Dialog):
for dependency in self.dependencies:
listbox_scroll.listbox.insert("end", dependency)
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(4):
@ -312,7 +311,7 @@ class ConfigServiceConfigDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=3, sticky="ew")
def click_apply(self):
def click_apply(self) -> None:
current_listbox = self.master.current.listbox
if not self.is_custom():
self.canvas_node.config_service_configs.pop(self.service_name, None)
@ -333,18 +332,18 @@ class ConfigServiceConfigDialog(Dialog):
current_listbox.itemconfig(all_current.index(self.service_name), bg="green")
self.destroy()
def handle_template_changed(self, event: tk.Event):
def handle_template_changed(self, event: tk.Event) -> None:
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):
def handle_mode_changed(self, event: tk.Event) -> None:
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):
def update_template_file_data(self, event: tk.Event) -> None:
scrolledtext = event.widget
template = self.templates_combobox.get()
self.temp_service_files[template] = scrolledtext.get(1.0, "end")
@ -353,7 +352,7 @@ class ConfigServiceConfigDialog(Dialog):
else:
self.modified_files.discard(template)
def is_custom(self):
def is_custom(self) -> bool:
has_custom_templates = len(self.modified_files) > 0
has_custom_config = False
if self.config_frame:
@ -361,7 +360,7 @@ class ConfigServiceConfigDialog(Dialog):
has_custom_config = self.default_config != current
return has_custom_templates or has_custom_config
def click_defaults(self):
def click_defaults(self) -> None:
self.canvas_node.config_service_configs.pop(self.service_name, None)
logging.info(
"cleared config service config: %s", self.canvas_node.config_service_configs
@ -374,12 +373,12 @@ class ConfigServiceConfigDialog(Dialog):
logging.info("resetting defaults: %s", self.default_config)
self.config_frame.set_values(self.default_config)
def click_copy(self):
def click_copy(self) -> None:
pass
def append_commands(
self, commands: List[str], listbox: tk.Listbox, to_add: List[str]
):
) -> None:
for cmd in to_add:
commands.append(cmd)
listbox.insert(tk.END, cmd)

View file

@ -15,17 +15,16 @@ if TYPE_CHECKING:
class CopyServiceConfigDialog(Dialog):
def __init__(self, master: tk.BaseWidget, app: "Application", node_id: int):
def __init__(self, master: tk.BaseWidget, app: "Application", node_id: int) -> None:
super().__init__(app, f"Copy services to node {node_id}", master=master)
self.parent = master
self.node_id = node_id
self.service_configs = app.core.service_configs
self.file_configs = app.core.file_configs
self.tree = None
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.tree = ttk.Treeview(self.top)
self.tree.grid(row=0, column=0, sticky="ew", padx=PADX)
@ -88,7 +87,7 @@ class CopyServiceConfigDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=2, sticky="ew", padx=PADX)
def click_copy(self):
def click_copy(self) -> None:
selected = self.tree.selection()
if selected:
item = self.tree.item(selected[0])
@ -127,7 +126,7 @@ class CopyServiceConfigDialog(Dialog):
)
self.destroy()
def click_view(self):
def click_view(self) -> None:
selected = self.tree.selection()
data = ""
if selected:
@ -159,7 +158,7 @@ class CopyServiceConfigDialog(Dialog):
)
dialog.show()
def get_node_service(self, selected: Tuple[str]) -> [int, str]:
def get_node_service(self, selected: Tuple[str]) -> Tuple[int, str]:
service_tree_id = self.tree.parent(selected[0])
service_name = self.tree.item(service_tree_id)["text"]
node_tree_id = self.tree.parent(service_tree_id)
@ -175,14 +174,14 @@ class ViewConfigDialog(Dialog):
node_id: int,
data: str,
filename: str = None,
):
) -> None:
super().__init__(app, f"n{node_id} config data", master=master)
self.data = data
self.service_data = None
self.filepath = tk.StringVar(value=f"/tmp/services.tmp-n{node_id}-{filename}")
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
frame = ttk.Frame(self.top, padding=FRAME_PAD)
frame.columnconfigure(0, weight=1)

View file

@ -2,7 +2,9 @@ import logging
import tkinter as tk
from pathlib import Path
from tkinter import ttk
from typing import TYPE_CHECKING, Set
from typing import TYPE_CHECKING, Optional, Set
from PIL.ImageTk import PhotoImage
from core.gui import nodeutils
from core.gui.appconfig import ICONS_PATH, CustomNode
@ -19,15 +21,15 @@ if TYPE_CHECKING:
class ServicesSelectDialog(Dialog):
def __init__(
self, master: tk.BaseWidget, app: "Application", current_services: Set[str]
):
) -> None:
super().__init__(app, "Node Services", master=master)
self.groups = None
self.services = None
self.current = None
self.current_services = set(current_services)
self.groups: Optional[ListboxScroll] = None
self.services: Optional[CheckboxList] = None
self.current: Optional[ListboxScroll] = None
self.current_services: Set[str] = current_services
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
@ -77,7 +79,7 @@ class ServicesSelectDialog(Dialog):
# trigger group change
self.groups.listbox.event_generate("<<ListboxSelect>>")
def handle_group_change(self, event: tk.Event):
def handle_group_change(self, event: tk.Event) -> None:
selection = self.groups.listbox.curselection()
if selection:
index = selection[0]
@ -87,7 +89,7 @@ class ServicesSelectDialog(Dialog):
checked = name in self.current_services
self.services.add(name, checked)
def service_clicked(self, name: str, var: tk.BooleanVar):
def service_clicked(self, name: str, var: tk.BooleanVar) -> None:
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:
@ -96,34 +98,34 @@ class ServicesSelectDialog(Dialog):
for name in sorted(self.current_services):
self.current.listbox.insert(tk.END, name)
def click_cancel(self):
def click_cancel(self) -> None:
self.current_services = None
self.destroy()
class CustomNodesDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Custom Nodes")
self.edit_button = None
self.delete_button = None
self.nodes_list = None
self.name = tk.StringVar()
self.image_button = None
self.image = None
self.image_file = None
self.services = set()
self.selected = None
self.selected_index = None
self.edit_button: Optional[ttk.Button] = None
self.delete_button: Optional[ttk.Button] = None
self.nodes_list: Optional[ListboxScroll] = None
self.name: tk.StringVar = tk.StringVar()
self.image_button: Optional[ttk.Button] = None
self.image: Optional[PhotoImage] = None
self.image_file: Optional[str] = None
self.services: Set[str] = set()
self.selected: Optional[str] = None
self.selected_index: Optional[int] = None
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
self.draw_node_config()
self.draw_node_buttons()
self.draw_buttons()
def draw_node_config(self):
def draw_node_config(self) -> None:
frame = ttk.LabelFrame(self.top, text="Nodes", padding=FRAME_PAD)
frame.grid(sticky="nsew", pady=PADY)
frame.columnconfigure(0, weight=1)
@ -147,7 +149,7 @@ class CustomNodesDialog(Dialog):
button = ttk.Button(frame, text="Services", command=self.click_services)
button.grid(sticky="ew")
def draw_node_buttons(self):
def draw_node_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew", pady=PADY)
for i in range(3):
@ -166,7 +168,7 @@ class CustomNodesDialog(Dialog):
)
self.delete_button.grid(row=0, column=2, sticky="ew")
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(2):
@ -178,14 +180,14 @@ class CustomNodesDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def reset_values(self):
def reset_values(self) -> None:
self.name.set("")
self.image = None
self.image_file = None
self.services = set()
self.image_button.config(image="")
def click_icon(self):
def click_icon(self) -> None:
file_path = image_chooser(self, ICONS_PATH)
if file_path:
image = Images.create(file_path, nodeutils.ICON_SIZE)
@ -193,24 +195,26 @@ class CustomNodesDialog(Dialog):
self.image_file = file_path
self.image_button.config(image=self.image)
def click_services(self):
def click_services(self) -> None:
dialog = ServicesSelectDialog(self, self.app, self.services)
dialog.show()
if dialog.current_services is not None:
self.services.clear()
self.services.update(dialog.current_services)
def click_save(self):
def click_save(self) -> None:
self.app.guiconfig.nodes.clear()
for name in self.app.core.custom_nodes:
node_draw = self.app.core.custom_nodes[name]
custom_node = CustomNode(name, node_draw.image_file, node_draw.services)
custom_node = CustomNode(
name, node_draw.image_file, list(node_draw.services)
)
self.app.guiconfig.nodes.append(custom_node)
logging.info("saving custom nodes: %s", self.app.guiconfig.nodes)
self.app.save_config()
self.destroy()
def click_create(self):
def click_create(self) -> None:
name = self.name.get()
if name not in self.app.core.custom_nodes:
image_file = Path(self.image_file).stem
@ -226,7 +230,7 @@ class CustomNodesDialog(Dialog):
self.nodes_list.listbox.insert(tk.END, name)
self.reset_values()
def click_edit(self):
def click_edit(self) -> None:
name = self.name.get()
if self.selected:
previous_name = self.selected
@ -247,7 +251,7 @@ class CustomNodesDialog(Dialog):
self.nodes_list.listbox.insert(self.selected_index, name)
self.nodes_list.listbox.selection_set(self.selected_index)
def click_delete(self):
def click_delete(self) -> None:
if self.selected and self.selected in self.app.core.custom_nodes:
self.nodes_list.listbox.delete(self.selected_index)
del self.app.core.custom_nodes[self.selected]
@ -255,7 +259,7 @@ class CustomNodesDialog(Dialog):
self.nodes_list.listbox.selection_clear(0, tk.END)
self.nodes_list.listbox.event_generate("<<ListboxSelect>>")
def handle_node_select(self, event: tk.Event):
def handle_node_select(self, event: tk.Event) -> None:
selection = self.nodes_list.listbox.curselection()
if selection:
self.selected_index = selection[0]

View file

@ -16,23 +16,23 @@ class Dialog(tk.Toplevel):
title: str,
modal: bool = True,
master: tk.BaseWidget = None,
):
) -> None:
if master is None:
master = app
super().__init__(master)
self.withdraw()
self.app = app
self.modal = modal
self.app: "Application" = app
self.modal: bool = modal
self.title(title)
self.protocol("WM_DELETE_WINDOW", self.destroy)
image = Images.get(ImageEnum.CORE, 16)
self.tk.call("wm", "iconphoto", self._w, image)
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.top = ttk.Frame(self, padding=DIALOG_PAD)
self.top: ttk.Frame = ttk.Frame(self, padding=DIALOG_PAD)
self.top.grid(sticky="nsew")
def show(self):
def show(self) -> None:
self.transient(self.master)
self.focus_force()
self.update()
@ -42,7 +42,7 @@ class Dialog(tk.Toplevel):
self.grab_set()
self.wait_window()
def draw_spacer(self, row: int = None):
def draw_spacer(self, row: int = None) -> None:
frame = ttk.Frame(self.top)
frame.grid(row=row, sticky="nsew")
frame.rowconfigure(0, weight=1)

View file

@ -4,10 +4,12 @@ emane configuration
import tkinter as tk
import webbrowser
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, List, Optional
import grpc
from core.api.grpc.common_pb2 import ConfigOption
from core.api.grpc.core_pb2 import Node
from core.gui.dialogs.dialog import Dialog
from core.gui.images import ImageEnum, Images
from core.gui.themes import PADX, PADY
@ -19,12 +21,12 @@ if TYPE_CHECKING:
class GlobalEmaneDialog(Dialog):
def __init__(self, master: tk.BaseWidget, app: "Application"):
def __init__(self, master: tk.BaseWidget, app: "Application") -> None:
super().__init__(app, "EMANE Configuration", master=master)
self.config_frame = None
self.config_frame: Optional[ConfigFrame] = None
self.draw()
def draw(self):
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.app.core.emane_config)
@ -33,7 +35,7 @@ class GlobalEmaneDialog(Dialog):
self.draw_spacer()
self.draw_buttons()
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(2):
@ -44,7 +46,7 @@ class GlobalEmaneDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_apply(self):
def click_apply(self) -> None:
self.config_frame.parse_config()
self.destroy()
@ -57,31 +59,32 @@ class EmaneModelDialog(Dialog):
canvas_node: "CanvasNode",
model: str,
iface_id: int = None,
):
) -> None:
super().__init__(
app, f"{canvas_node.core_node.name} {model} Configuration", master=master
)
self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.model = f"emane_{model}"
self.iface_id = iface_id
self.config_frame = None
self.has_error = False
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
self.model: str = f"emane_{model}"
self.iface_id: int = iface_id
self.config_frame: Optional[ConfigFrame] = None
self.has_error: bool = False
try:
self.config = self.canvas_node.emane_model_configs.get(
config = self.canvas_node.emane_model_configs.get(
(self.model, self.iface_id)
)
if not self.config:
self.config = self.app.core.get_emane_model_config(
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 = True
self.has_error: bool = True
self.destroy()
def draw(self):
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)
@ -90,7 +93,7 @@ class EmaneModelDialog(Dialog):
self.draw_spacer()
self.draw_buttons()
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(2):
@ -101,7 +104,7 @@ class EmaneModelDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_apply(self):
def click_apply(self) -> None:
self.config_frame.parse_config()
key = (self.model, self.iface_id)
self.canvas_node.emane_model_configs[key] = self.config
@ -109,18 +112,21 @@ class EmaneModelDialog(Dialog):
class EmaneConfigDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode"):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
super().__init__(app, f"{canvas_node.core_node.name} EMANE Configuration")
self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.radiovar = tk.IntVar()
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
self.radiovar: tk.IntVar = tk.IntVar()
self.radiovar.set(1)
self.emane_models = [x.split("_")[1] for x in self.app.core.emane_models]
self.emane_model = tk.StringVar(value=self.node.emane.split("_")[1])
self.emane_model_button = None
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.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.draw_emane_configuration()
self.draw_emane_models()
@ -128,7 +134,7 @@ class EmaneConfigDialog(Dialog):
self.draw_spacer()
self.draw_apply_and_cancel()
def draw_emane_configuration(self):
def draw_emane_configuration(self) -> None:
"""
draw the main frame for emane configuration
"""
@ -153,7 +159,7 @@ class EmaneConfigDialog(Dialog):
button.image = image
button.grid(sticky="ew", pady=PADY)
def draw_emane_models(self):
def draw_emane_models(self) -> None:
"""
create a combobox that has all the known emane models
"""
@ -174,7 +180,7 @@ class EmaneConfigDialog(Dialog):
combobox.grid(row=0, column=1, sticky="ew")
combobox.bind("<<ComboboxSelected>>", self.emane_model_change)
def draw_emane_buttons(self):
def draw_emane_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew", pady=PADY)
for i in range(2):
@ -202,7 +208,7 @@ class EmaneConfigDialog(Dialog):
button.image = image
button.grid(row=0, column=1, sticky="ew")
def draw_apply_and_cancel(self):
def draw_apply_and_cancel(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(2):
@ -214,11 +220,11 @@ class EmaneConfigDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_emane_config(self):
def click_emane_config(self) -> None:
dialog = GlobalEmaneDialog(self, self.app)
dialog.show()
def click_model_config(self):
def click_model_config(self) -> None:
"""
draw emane model configuration
"""
@ -227,13 +233,13 @@ class EmaneConfigDialog(Dialog):
if not dialog.has_error:
dialog.show()
def emane_model_change(self, event: tk.Event):
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):
def click_apply(self) -> None:
self.node.emane = f"emane_{self.emane_model.get()}"
self.destroy()

View file

@ -10,7 +10,7 @@ class EmaneInstallDialog(Dialog):
super().__init__(app, "EMANE Error")
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
label = ttk.Label(self.top, text="EMANE needs to be installed!")
label.grid(sticky="ew", pady=PADY)
@ -21,5 +21,5 @@ class EmaneInstallDialog(Dialog):
button = ttk.Button(self.top, text="Close", command=self.destroy)
button.grid(sticky="ew")
def click_doc(self):
def click_doc(self) -> None:
webbrowser.open_new("https://coreemu.github.io/core/emane.html")

View file

@ -1,5 +1,5 @@
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from core.gui.dialogs.dialog import Dialog
from core.gui.images import ImageEnum, Images
@ -13,9 +13,9 @@ if TYPE_CHECKING:
class ErrorDialog(Dialog):
def __init__(self, app: "Application", title: str, details: str) -> None:
super().__init__(app, "CORE Exception")
self.title = title
self.details = details
self.error_message = None
self.title: str = title
self.details: str = details
self.error_message: Optional[CodeText] = None
self.draw()
def draw(self) -> None:

View file

@ -1,7 +1,7 @@
import logging
import tkinter as tk
from tkinter import filedialog, ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from core.gui.appconfig import SCRIPT_PATH
from core.gui.dialogs.dialog import Dialog
@ -12,15 +12,15 @@ if TYPE_CHECKING:
class ExecutePythonDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Execute Python Script")
self.with_options = tk.IntVar(value=0)
self.options = tk.StringVar(value="")
self.option_entry = None
self.file_entry = None
self.with_options: tk.IntVar = tk.IntVar(value=0)
self.options: tk.StringVar = tk.StringVar(value="")
self.option_entry: Optional[ttk.Entry] = None
self.file_entry: Optional[ttk.Entry] = None
self.draw()
def draw(self):
def draw(self) -> None:
i = 0
frame = ttk.Frame(self.top, padding=FRAME_PAD)
frame.columnconfigure(0, weight=1)
@ -63,13 +63,13 @@ class ExecutePythonDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew", padx=PADX)
def add_options(self):
def add_options(self) -> None:
if self.with_options.get():
self.option_entry.configure(state="normal")
else:
self.option_entry.configure(state="disabled")
def select_file(self):
def select_file(self) -> None:
file = filedialog.askopenfilename(
parent=self.top,
initialdir=str(SCRIPT_PATH),
@ -80,7 +80,7 @@ class ExecutePythonDialog(Dialog):
self.file_entry.delete(0, "end")
self.file_entry.insert("end", file)
def script_execute(self):
def script_execute(self) -> None:
file = self.file_entry.get()
options = self.option_entry.get()
logging.info("Execute %s with options %s", file, options)

View file

@ -1,7 +1,7 @@
import logging
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY
@ -13,8 +13,8 @@ if TYPE_CHECKING:
class FindDialog(Dialog):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Find", modal=False)
self.find_text = tk.StringVar(value="")
self.tree = None
self.find_text: tk.StringVar = tk.StringVar(value="")
self.tree: Optional[ttk.Treeview] = None
self.draw()
self.protocol("WM_DELETE_WINDOW", self.close_dialog)
self.bind("<Return>", self.find_node)

View file

@ -1,6 +1,6 @@
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from core.api.grpc import core_pb2
from core.gui.dialogs.dialog import Dialog
@ -12,15 +12,15 @@ if TYPE_CHECKING:
class HookDialog(Dialog):
def __init__(self, master: tk.BaseWidget, app: "Application"):
def __init__(self, master: tk.BaseWidget, app: "Application") -> None:
super().__init__(app, "Hook", master=master)
self.name = tk.StringVar()
self.codetext = None
self.hook = core_pb2.Hook()
self.state = tk.StringVar()
self.name: tk.StringVar = tk.StringVar()
self.codetext: Optional[CodeText] = None
self.hook: core_pb2.Hook = core_pb2.Hook()
self.state: tk.StringVar = tk.StringVar()
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(1, weight=1)
@ -66,11 +66,11 @@ class HookDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=lambda: self.destroy())
button.grid(row=0, column=1, sticky="ew")
def state_change(self, event: tk.Event):
def state_change(self, event: tk.Event) -> None:
state_name = self.state.get()
self.name.set(f"{state_name.lower()}_hook.sh")
def set(self, hook: core_pb2.Hook):
def set(self, hook: core_pb2.Hook) -> None:
self.hook = hook
self.name.set(hook.file)
self.codetext.text.delete(1.0, tk.END)
@ -78,7 +78,7 @@ class HookDialog(Dialog):
state_name = core_pb2.SessionState.Enum.Name(hook.state)
self.state.set(state_name)
def save(self):
def save(self) -> None:
data = self.codetext.text.get("1.0", tk.END).strip()
state_value = core_pb2.SessionState.Enum.Value(self.state.get())
self.hook.file = self.name.get()
@ -88,15 +88,15 @@ class HookDialog(Dialog):
class HooksDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Hooks")
self.listbox = None
self.edit_button = None
self.delete_button = None
self.selected = None
self.listbox: Optional[tk.Listbox] = None
self.edit_button: Optional[ttk.Button] = None
self.delete_button: Optional[ttk.Button] = None
self.selected: Optional[str] = None
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
@ -124,7 +124,7 @@ class HooksDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=lambda: self.destroy())
button.grid(row=0, column=3, sticky="ew")
def click_create(self):
def click_create(self) -> None:
dialog = HookDialog(self, self.app)
dialog.show()
hook = dialog.hook
@ -132,19 +132,19 @@ class HooksDialog(Dialog):
self.app.core.hooks[hook.file] = hook
self.listbox.insert(tk.END, hook.file)
def click_edit(self):
def click_edit(self) -> None:
hook = self.app.core.hooks[self.selected]
dialog = HookDialog(self, self.app)
dialog.set(hook)
dialog.show()
def click_delete(self):
def click_delete(self) -> None:
del self.app.core.hooks[self.selected]
self.listbox.delete(tk.ANCHOR)
self.edit_button.config(state=tk.DISABLED)
self.delete_button.config(state=tk.DISABLED)
def select(self, event: tk.Event):
def select(self, event: tk.Event) -> None:
if self.listbox.curselection():
index = self.listbox.curselection()[0]
self.selected = self.listbox.get(index)

View file

@ -1,6 +1,6 @@
import tkinter as tk
from tkinter import messagebox, ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, List, Optional
import netaddr
@ -15,14 +15,14 @@ if TYPE_CHECKING:
class IpConfigDialog(Dialog):
def __init__(self, app: "Application") -> None:
super().__init__(app, "IP Configuration")
self.ip4 = self.app.guiconfig.ips.ip4
self.ip6 = self.app.guiconfig.ips.ip6
self.ip4s = self.app.guiconfig.ips.ip4s
self.ip6s = self.app.guiconfig.ips.ip6s
self.ip4_entry = None
self.ip4_listbox = None
self.ip6_entry = None
self.ip6_listbox = None
self.ip4: str = self.app.guiconfig.ips.ip4
self.ip6: str = self.app.guiconfig.ips.ip6
self.ip4s: List[str] = self.app.guiconfig.ips.ip4s
self.ip6s: List[str] = self.app.guiconfig.ips.ip6s
self.ip4_entry: Optional[ttk.Entry] = None
self.ip4_listbox: Optional[ListboxScroll] = None
self.ip6_entry: Optional[ttk.Entry] = None
self.ip6_listbox: Optional[ListboxScroll] = None
self.draw()
def draw(self) -> None:

View file

@ -3,7 +3,7 @@ link configuration
"""
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Union
from typing import TYPE_CHECKING, Optional
from core.api.grpc import core_pb2
from core.gui import validation
@ -16,7 +16,7 @@ if TYPE_CHECKING:
from core.gui.graph.graph import CanvasEdge
def get_int(var: tk.StringVar) -> Union[int, None]:
def get_int(var: tk.StringVar) -> Optional[int]:
value = var.get()
if value != "":
return int(value)
@ -24,7 +24,7 @@ def get_int(var: tk.StringVar) -> Union[int, None]:
return None
def get_float(var: tk.StringVar) -> Union[float, None]:
def get_float(var: tk.StringVar) -> Optional[float]:
value = var.get()
if value != "":
return float(value)
@ -33,38 +33,39 @@ def get_float(var: tk.StringVar) -> Union[float, None]:
class LinkConfigurationDialog(Dialog):
def __init__(self, app: "Application", edge: "CanvasEdge"):
def __init__(self, app: "Application", edge: "CanvasEdge") -> None:
super().__init__(app, "Link Configuration")
self.edge = edge
self.is_symmetric = edge.link.options.unidirectional is False
self.edge: "CanvasEdge" = edge
self.is_symmetric: bool = edge.link.options.unidirectional is False
if self.is_symmetric:
self.symmetry_var = tk.StringVar(value=">>")
symmetry_var = tk.StringVar(value=">>")
else:
self.symmetry_var = tk.StringVar(value="<<")
symmetry_var = tk.StringVar(value="<<")
self.symmetry_var: tk.StringVar = symmetry_var
self.bandwidth = tk.StringVar()
self.delay = tk.StringVar()
self.jitter = tk.StringVar()
self.loss = tk.StringVar()
self.duplicate = tk.StringVar()
self.bandwidth: tk.StringVar = tk.StringVar()
self.delay: tk.StringVar = tk.StringVar()
self.jitter: tk.StringVar = tk.StringVar()
self.loss: tk.StringVar = tk.StringVar()
self.duplicate: tk.StringVar = tk.StringVar()
self.down_bandwidth = tk.StringVar()
self.down_delay = tk.StringVar()
self.down_jitter = tk.StringVar()
self.down_loss = tk.StringVar()
self.down_duplicate = tk.StringVar()
self.down_bandwidth: tk.StringVar = tk.StringVar()
self.down_delay: tk.StringVar = tk.StringVar()
self.down_jitter: tk.StringVar = tk.StringVar()
self.down_loss: tk.StringVar = tk.StringVar()
self.down_duplicate: tk.StringVar = tk.StringVar()
self.color = tk.StringVar(value="#000000")
self.color_button = None
self.width = tk.DoubleVar()
self.color: tk.StringVar = tk.StringVar(value="#000000")
self.color_button: Optional[tk.Button] = None
self.width: tk.DoubleVar = tk.DoubleVar()
self.load_link_config()
self.symmetric_frame = None
self.asymmetric_frame = None
self.symmetric_frame: Optional[ttk.Frame] = None
self.asymmetric_frame: Optional[ttk.Frame] = None
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
source_name = self.app.canvas.nodes[self.edge.src].core_node.name
dest_name = self.app.canvas.nodes[self.edge.dst].core_node.name
@ -207,13 +208,13 @@ class LinkConfigurationDialog(Dialog):
return frame
def click_color(self):
def click_color(self) -> None:
dialog = ColorPickerDialog(self, self.app, self.color.get())
color = dialog.askcolor()
self.color.set(color)
self.color_button.config(background=color)
def click_apply(self):
def click_apply(self) -> None:
self.app.canvas.itemconfigure(self.edge.id, width=self.width.get())
self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get())
link = self.edge.link
@ -288,7 +289,7 @@ class LinkConfigurationDialog(Dialog):
self.destroy()
def change_symmetry(self):
def change_symmetry(self) -> None:
if self.is_symmetric:
self.is_symmetric = False
self.symmetry_var.set("<<")
@ -304,7 +305,7 @@ class LinkConfigurationDialog(Dialog):
self.asymmetric_frame.grid_forget()
self.symmetric_frame.grid(row=2, column=0)
def load_link_config(self):
def load_link_config(self) -> None:
"""
populate link config to the table
"""

View file

@ -15,7 +15,7 @@ class MacConfigDialog(Dialog):
def __init__(self, app: "Application") -> None:
super().__init__(app, "MAC Configuration")
mac = self.app.guiconfig.mac
self.mac_var = tk.StringVar(value=mac)
self.mac_var: tk.StringVar = tk.StringVar(value=mac)
self.draw()
def draw(self) -> None:

View file

@ -2,10 +2,12 @@
mobility configuration
"""
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Optional
import grpc
from core.api.grpc.common_pb2 import ConfigOption
from core.api.grpc.core_pb2 import Node
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY
from core.gui.widgets import ConfigFrame
@ -16,23 +18,24 @@ if TYPE_CHECKING:
class MobilityConfigDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode"):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
super().__init__(app, f"{canvas_node.core_node.name} Mobility Configuration")
self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.config_frame = None
self.has_error = False
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
self.config_frame: Optional[ConfigFrame] = None
self.has_error: bool = False
try:
self.config = self.canvas_node.mobility_config
if not self.config:
self.config = self.app.core.get_mobility_config(self.node.id)
config = self.canvas_node.mobility_config
if not config:
config = self.app.core.get_mobility_config(self.node.id)
self.config: Dict[str, ConfigOption] = config
self.draw()
except grpc.RpcError as e:
self.app.show_grpc_exception("Get Mobility Config Error", e)
self.has_error = True
self.has_error: bool = True
self.destroy()
def draw(self):
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)
@ -40,7 +43,7 @@ class MobilityConfigDialog(Dialog):
self.config_frame.grid(sticky="nsew", pady=PADY)
self.draw_apply_buttons()
def draw_apply_buttons(self):
def draw_apply_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(2):
@ -52,7 +55,7 @@ class MobilityConfigDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_apply(self):
def click_apply(self) -> None:
self.config_frame.parse_config()
self.canvas_node.mobility_config = self.config
self.destroy()

View file

@ -1,9 +1,11 @@
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Optional
import grpc
from core.api.grpc.common_pb2 import ConfigOption
from core.api.grpc.core_pb2 import Node
from core.api.grpc.mobility_pb2 import MobilityAction
from core.gui.dialogs.dialog import Dialog
from core.gui.images import ImageEnum
@ -13,18 +15,23 @@ if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
ICON_SIZE = 16
ICON_SIZE: int = 16
class MobilityPlayer:
def __init__(self, app: "Application", canvas_node: "CanvasNode", config):
self.app = app
self.canvas_node = canvas_node
self.config = config
self.dialog = None
self.state = None
def __init__(
self,
app: "Application",
canvas_node: "CanvasNode",
config: Dict[str, ConfigOption],
) -> None:
self.app: "Application" = app
self.canvas_node: "CanvasNode" = canvas_node
self.config: Dict[str, ConfigOption] = config
self.dialog: Optional[MobilityPlayerDialog] = None
self.state: Optional[MobilityAction] = None
def show(self):
def show(self) -> None:
if self.dialog:
self.dialog.destroy()
self.dialog = MobilityPlayerDialog(self.app, self.canvas_node, self.config)
@ -37,44 +44,49 @@ class MobilityPlayer:
self.set_stop()
self.dialog.show()
def close(self):
def close(self) -> None:
if self.dialog:
self.dialog.destroy()
self.dialog = None
def set_play(self):
def set_play(self) -> None:
self.state = MobilityAction.START
if self.dialog:
self.dialog.set_play()
def set_pause(self):
def set_pause(self) -> None:
self.state = MobilityAction.PAUSE
if self.dialog:
self.dialog.set_pause()
def set_stop(self):
def set_stop(self) -> None:
self.state = MobilityAction.STOP
if self.dialog:
self.dialog.set_stop()
class MobilityPlayerDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode", config):
def __init__(
self,
app: "Application",
canvas_node: "CanvasNode",
config: Dict[str, ConfigOption],
) -> None:
super().__init__(
app, f"{canvas_node.core_node.name} Mobility Player", modal=False
)
self.resizable(False, False)
self.geometry("")
self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.config = config
self.play_button = None
self.pause_button = None
self.stop_button = None
self.progressbar = None
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
self.config: Dict[str, ConfigOption] = config
self.play_button: Optional[ttk.Button] = None
self.pause_button: Optional[ttk.Button] = None
self.stop_button: Optional[ttk.Button] = None
self.progressbar: Optional[ttk.Progressbar] = None
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
file_name = self.config["file"].value
@ -114,27 +126,27 @@ class MobilityPlayerDialog(Dialog):
label = ttk.Label(frame, text=f"rate {rate} ms")
label.grid(row=0, column=4)
def clear_buttons(self):
def clear_buttons(self) -> None:
self.play_button.state(["!pressed"])
self.pause_button.state(["!pressed"])
self.stop_button.state(["!pressed"])
def set_play(self):
def set_play(self) -> None:
self.clear_buttons()
self.play_button.state(["pressed"])
self.progressbar.start()
def set_pause(self):
def set_pause(self) -> None:
self.clear_buttons()
self.pause_button.state(["pressed"])
self.progressbar.stop()
def set_stop(self):
def set_stop(self) -> None:
self.clear_buttons()
self.stop_button.state(["pressed"])
self.progressbar.stop()
def click_play(self):
def click_play(self) -> None:
self.set_play()
session_id = self.app.core.session_id
try:
@ -144,7 +156,7 @@ class MobilityPlayerDialog(Dialog):
except grpc.RpcError as e:
self.app.show_grpc_exception("Mobility Error", e)
def click_pause(self):
def click_pause(self) -> None:
self.set_pause()
session_id = self.app.core.session_id
try:
@ -154,7 +166,7 @@ class MobilityPlayerDialog(Dialog):
except grpc.RpcError as e:
self.app.show_grpc_exception("Mobility Error", e)
def click_stop(self):
def click_stop(self) -> None:
self.set_stop()
session_id = self.app.core.session_id
try:

View file

@ -2,10 +2,12 @@ import logging
import tkinter as tk
from functools import partial
from tkinter import messagebox, ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Optional
import netaddr
from PIL.ImageTk import PhotoImage
from core.api.grpc.core_pb2 import Node
from core.gui import nodeutils, validation
from core.gui.appconfig import ICONS_PATH
from core.gui.dialogs.dialog import Dialog
@ -86,35 +88,35 @@ class InterfaceData:
mac: tk.StringVar,
ip4: tk.StringVar,
ip6: tk.StringVar,
):
self.is_auto = is_auto
self.mac = mac
self.ip4 = ip4
self.ip6 = ip6
) -> None:
self.is_auto: tk.BooleanVar = is_auto
self.mac: tk.StringVar = mac
self.ip4: tk.StringVar = ip4
self.ip6: tk.StringVar = ip6
class NodeConfigDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode"):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
"""
create an instance of node configuration
"""
super().__init__(app, f"{canvas_node.core_node.name} Configuration")
self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.image = canvas_node.image
self.image_file = None
self.image_button = None
self.name = tk.StringVar(value=self.node.name)
self.type = tk.StringVar(value=self.node.model)
self.container_image = tk.StringVar(value=self.node.image)
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
self.image: PhotoImage = canvas_node.image
self.image_file: Optional[str] = None
self.image_button: Optional[ttk.Button] = None
self.name: tk.StringVar = tk.StringVar(value=self.node.name)
self.type: tk.StringVar = tk.StringVar(value=self.node.model)
self.container_image: tk.StringVar = tk.StringVar(value=self.node.image)
server = "localhost"
if self.node.server:
server = self.node.server
self.server = tk.StringVar(value=server)
self.ifaces = {}
self.server: tk.StringVar = tk.StringVar(value=server)
self.ifaces: Dict[int, InterfaceData] = {}
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
row = 0
@ -202,7 +204,7 @@ class NodeConfigDialog(Dialog):
self.draw_spacer()
self.draw_buttons()
def draw_ifaces(self):
def draw_ifaces(self) -> None:
notebook = ttk.Notebook(self.top)
notebook.grid(sticky="nsew", pady=PADY)
self.top.rowconfigure(notebook.grid_info()["row"], weight=1)
@ -265,7 +267,7 @@ class NodeConfigDialog(Dialog):
self.ifaces[iface.id] = InterfaceData(is_auto, mac, ip4, ip6)
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
frame.columnconfigure(0, weight=1)
@ -277,20 +279,20 @@ class NodeConfigDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_emane_config(self, emane_model: str, iface_id: int):
def click_emane_config(self, emane_model: str, iface_id: int) -> None:
dialog = EmaneModelDialog(
self, self.app, self.canvas_node, emane_model, iface_id
)
dialog.show()
def click_icon(self):
def click_icon(self) -> None:
file_path = image_chooser(self, ICONS_PATH)
if file_path:
self.image = Images.create(file_path, nodeutils.ICON_SIZE)
self.image_button.config(image=self.image)
self.image_file = file_path
def click_apply(self):
def click_apply(self) -> None:
error = False
# update core node
@ -354,7 +356,7 @@ class NodeConfigDialog(Dialog):
self.canvas_node.redraw()
self.destroy()
def iface_select(self, event: tk.Event):
def iface_select(self, event: tk.Event) -> None:
listbox = event.widget
cur = listbox.curselection()
if cur:

View file

@ -4,7 +4,7 @@ core node services
import logging
import tkinter as tk
from tkinter import messagebox, ttk
from typing import TYPE_CHECKING, Set
from typing import TYPE_CHECKING, Optional, Set
from core.gui.dialogs.configserviceconfig import ConfigServiceConfigDialog
from core.gui.dialogs.dialog import Dialog
@ -19,20 +19,20 @@ if TYPE_CHECKING:
class NodeConfigServiceDialog(Dialog):
def __init__(
self, app: "Application", canvas_node: "CanvasNode", services: Set[str] = None
):
) -> None:
title = f"{canvas_node.core_node.name} Config Services"
super().__init__(app, title)
self.canvas_node = canvas_node
self.node_id = canvas_node.core_node.id
self.groups = None
self.services = None
self.current = None
self.canvas_node: "CanvasNode" = canvas_node
self.node_id: int = canvas_node.core_node.id
self.groups: Optional[ListboxScroll] = None
self.services: Optional[CheckboxList] = None
self.current: Optional[ListboxScroll] = None
if services is None:
services = set(canvas_node.core_node.config_services)
self.current_services = services
self.current_services: Set[str] = services
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
@ -86,7 +86,7 @@ class NodeConfigServiceDialog(Dialog):
# trigger group change
self.groups.listbox.event_generate("<<ListboxSelect>>")
def handle_group_change(self, event: tk.Event = None):
def handle_group_change(self, event: tk.Event = None) -> None:
selection = self.groups.listbox.curselection()
if selection:
index = selection[0]
@ -96,7 +96,7 @@ class NodeConfigServiceDialog(Dialog):
checked = name in self.current_services
self.services.add(name, checked)
def service_clicked(self, name: str, var: tk.IntVar):
def service_clicked(self, name: str, var: tk.IntVar) -> None:
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:
@ -104,7 +104,7 @@ class NodeConfigServiceDialog(Dialog):
self.draw_current_services()
self.canvas_node.core_node.config_services[:] = self.current_services
def click_configure(self):
def click_configure(self) -> None:
current_selection = self.current.listbox.curselection()
if len(current_selection):
dialog = ConfigServiceConfigDialog(
@ -124,25 +124,25 @@ class NodeConfigServiceDialog(Dialog):
parent=self,
)
def draw_current_services(self):
def draw_current_services(self) -> None:
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) -> None:
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):
def click_cancel(self) -> None:
self.current_services = None
self.destroy()
def click_remove(self):
def click_remove(self) -> None:
cur = self.current.listbox.curselection()
if cur:
service = self.current.listbox.get(cur[0])

View file

@ -3,7 +3,7 @@ core node services
"""
import tkinter as tk
from tkinter import messagebox, ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional, Set
from core.gui.dialogs.dialog import Dialog
from core.gui.dialogs.serviceconfig import ServiceConfigDialog
@ -16,19 +16,19 @@ if TYPE_CHECKING:
class NodeServiceDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode"):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
title = f"{canvas_node.core_node.name} Services"
super().__init__(app, title)
self.canvas_node = canvas_node
self.node_id = canvas_node.core_node.id
self.groups = None
self.services = None
self.current = None
self.canvas_node: "CanvasNode" = canvas_node
self.node_id: int = canvas_node.core_node.id
self.groups: Optional[ListboxScroll] = None
self.services: Optional[CheckboxList] = None
self.current: Optional[ListboxScroll] = None
services = set(canvas_node.core_node.services)
self.current_services = services
self.current_services: Set[str] = services
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
@ -84,7 +84,7 @@ class NodeServiceDialog(Dialog):
# trigger group change
self.groups.listbox.event_generate("<<ListboxSelect>>")
def handle_group_change(self, event: tk.Event = None):
def handle_group_change(self, event: tk.Event = None) -> None:
selection = self.groups.listbox.curselection()
if selection:
index = selection[0]
@ -94,7 +94,7 @@ class NodeServiceDialog(Dialog):
checked = name in self.current_services
self.services.add(name, checked)
def service_clicked(self, name: str, var: tk.IntVar):
def service_clicked(self, name: str, var: tk.IntVar) -> None:
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:
@ -106,7 +106,7 @@ class NodeServiceDialog(Dialog):
self.current.listbox.itemconfig(tk.END, bg="green")
self.canvas_node.core_node.services[:] = self.current_services
def click_configure(self):
def click_configure(self) -> None:
current_selection = self.current.listbox.curselection()
if len(current_selection):
dialog = ServiceConfigDialog(
@ -127,12 +127,12 @@ class NodeServiceDialog(Dialog):
"Service Configuration", "Select a service to configure", parent=self
)
def click_save(self):
def click_save(self) -> None:
core_node = self.canvas_node.core_node
core_node.services[:] = self.current_services
self.destroy()
def click_remove(self):
def click_remove(self) -> None:
cur = self.current.listbox.curselection()
if cur:
service = self.current.listbox.get(cur[0])

View file

@ -1,6 +1,6 @@
import tkinter as tk
from tkinter import messagebox, ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from core.gui.appconfig import Observer
from core.gui.dialogs.dialog import Dialog
@ -12,18 +12,18 @@ if TYPE_CHECKING:
class ObserverDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Observer Widgets")
self.observers = None
self.save_button = None
self.delete_button = None
self.selected = None
self.selected_index = None
self.name = tk.StringVar()
self.cmd = tk.StringVar()
self.observers: Optional[tk.Listbox] = None
self.save_button: Optional[ttk.Button] = None
self.delete_button: Optional[ttk.Button] = None
self.selected: Optional[str] = None
self.selected_index: Optional[int] = None
self.name: tk.StringVar = tk.StringVar()
self.cmd: tk.StringVar = tk.StringVar()
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
self.draw_listbox()
@ -31,7 +31,7 @@ class ObserverDialog(Dialog):
self.draw_config_buttons()
self.draw_apply_buttons()
def draw_listbox(self):
def draw_listbox(self) -> None:
listbox_scroll = ListboxScroll(self.top)
listbox_scroll.grid(sticky="nsew", pady=PADY)
listbox_scroll.columnconfigure(0, weight=1)
@ -42,7 +42,7 @@ class ObserverDialog(Dialog):
for name in sorted(self.app.core.custom_observers):
self.observers.insert(tk.END, name)
def draw_form_fields(self):
def draw_form_fields(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew", pady=PADY)
frame.columnconfigure(1, weight=1)
@ -57,7 +57,7 @@ class ObserverDialog(Dialog):
entry = ttk.Entry(frame, textvariable=self.cmd)
entry.grid(row=1, column=1, sticky="ew")
def draw_config_buttons(self):
def draw_config_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew", pady=PADY)
for i in range(3):
@ -76,7 +76,7 @@ class ObserverDialog(Dialog):
)
self.delete_button.grid(row=0, column=2, sticky="ew")
def draw_apply_buttons(self):
def draw_apply_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(2):
@ -88,14 +88,14 @@ class ObserverDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_save_config(self):
def click_save_config(self) -> None:
self.app.guiconfig.observers.clear()
for observer in self.app.core.custom_observers.values():
self.app.guiconfig.observers.append(observer)
self.app.save_config()
self.destroy()
def click_create(self):
def click_create(self) -> None:
name = self.name.get()
if name not in self.app.core.custom_observers:
cmd = self.cmd.get()
@ -109,7 +109,7 @@ class ObserverDialog(Dialog):
else:
messagebox.showerror("Observer Error", f"{name} already exists")
def click_save(self):
def click_save(self) -> None:
name = self.name.get()
if self.selected:
previous_name = self.selected
@ -122,7 +122,7 @@ class ObserverDialog(Dialog):
self.observers.insert(self.selected_index, name)
self.observers.selection_set(self.selected_index)
def click_delete(self):
def click_delete(self) -> None:
if self.selected:
self.observers.delete(self.selected_index)
del self.app.core.custom_observers[self.selected]
@ -136,7 +136,7 @@ class ObserverDialog(Dialog):
self.app.menubar.observers_menu.draw_custom()
self.app.toolbar.observers_menu.draw_custom()
def handle_observer_change(self, event: tk.Event):
def handle_observer_change(self, event: tk.Event) -> None:
selection = self.observers.curselection()
if selection:
self.selected_index = selection[0]

View file

@ -12,27 +12,27 @@ from core.gui.validation import LARGEST_SCALE, SMALLEST_SCALE
if TYPE_CHECKING:
from core.gui.app import Application
SCALE_INTERVAL = 0.01
SCALE_INTERVAL: float = 0.01
class PreferencesDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Preferences")
self.gui_scale = tk.DoubleVar(value=self.app.app_scale)
self.gui_scale: tk.DoubleVar = tk.DoubleVar(value=self.app.app_scale)
preferences = self.app.guiconfig.preferences
self.editor = tk.StringVar(value=preferences.editor)
self.theme = tk.StringVar(value=preferences.theme)
self.terminal = tk.StringVar(value=preferences.terminal)
self.gui3d = tk.StringVar(value=preferences.gui3d)
self.editor: tk.StringVar = tk.StringVar(value=preferences.editor)
self.theme: tk.StringVar = tk.StringVar(value=preferences.theme)
self.terminal: tk.StringVar = tk.StringVar(value=preferences.terminal)
self.gui3d: tk.StringVar = tk.StringVar(value=preferences.gui3d)
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
self.draw_preferences()
self.draw_buttons()
def draw_preferences(self):
def draw_preferences(self) -> None:
frame = ttk.LabelFrame(self.top, text="Preferences", padding=FRAME_PAD)
frame.grid(sticky="nsew", pady=PADY)
frame.columnconfigure(1, weight=1)
@ -88,7 +88,7 @@ class PreferencesDialog(Dialog):
scrollbar = ttk.Scrollbar(scale_frame, command=self.adjust_scale)
scrollbar.grid(row=0, column=2)
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(2):
@ -100,12 +100,12 @@ class PreferencesDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def theme_change(self, event: tk.Event):
def theme_change(self, event: tk.Event) -> None:
theme = self.theme.get()
logging.info("changing theme: %s", theme)
self.app.style.theme_use(theme)
def click_save(self):
def click_save(self) -> None:
preferences = self.app.guiconfig.preferences
preferences.terminal = self.terminal.get()
preferences.editor = self.editor.get()
@ -118,7 +118,7 @@ class PreferencesDialog(Dialog):
self.scale_adjust()
self.destroy()
def scale_adjust(self):
def scale_adjust(self) -> None:
app_scale = self.gui_scale.get()
self.app.app_scale = app_scale
self.app.master.tk.call("tk", "scaling", app_scale)
@ -136,7 +136,7 @@ class PreferencesDialog(Dialog):
self.app.toolbar.scale()
self.app.canvas.scale_graph()
def adjust_scale(self, arg1: str, arg2: str, arg3: str):
def adjust_scale(self, arg1: str, arg2: str, arg3: str) -> None:
scale_value = self.gui_scale.get()
if arg2 == "-1":
if scale_value <= LARGEST_SCALE - SCALE_INTERVAL:

View file

@ -1,6 +1,6 @@
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Optional
from core.gui.dialogs.dialog import Dialog
from core.gui.nodeutils import NodeUtils
@ -14,10 +14,10 @@ if TYPE_CHECKING:
class RunToolDialog(Dialog):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Run Tool")
self.cmd = tk.StringVar(value="ps ax")
self.result = None
self.node_list = None
self.executable_nodes = {}
self.cmd: tk.StringVar = tk.StringVar(value="ps ax")
self.result: Optional[CodeText] = None
self.node_list: Optional[ListboxScroll] = None
self.executable_nodes: Dict[str, int] = {}
self.store_nodes()
self.draw()

View file

@ -1,6 +1,6 @@
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from core.gui.appconfig import CoreServer
from core.gui.dialogs.dialog import Dialog
@ -10,24 +10,24 @@ from core.gui.widgets import ListboxScroll
if TYPE_CHECKING:
from core.gui.app import Application
DEFAULT_NAME = "example"
DEFAULT_ADDRESS = "127.0.0.1"
DEFAULT_PORT = 50051
DEFAULT_NAME: str = "example"
DEFAULT_ADDRESS: str = "127.0.0.1"
DEFAULT_PORT: int = 50051
class ServersDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "CORE Servers")
self.name = tk.StringVar(value=DEFAULT_NAME)
self.address = tk.StringVar(value=DEFAULT_ADDRESS)
self.servers = None
self.selected_index = None
self.selected = None
self.save_button = None
self.delete_button = None
self.name: tk.StringVar = tk.StringVar(value=DEFAULT_NAME)
self.address: tk.StringVar = tk.StringVar(value=DEFAULT_ADDRESS)
self.servers: Optional[tk.Listbox] = None
self.selected_index: Optional[int] = None
self.selected: Optional[str] = None
self.save_button: Optional[ttk.Button] = None
self.delete_button: Optional[ttk.Button] = None
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
self.draw_servers()
@ -35,7 +35,7 @@ class ServersDialog(Dialog):
self.draw_server_configuration()
self.draw_apply_buttons()
def draw_servers(self):
def draw_servers(self) -> None:
listbox_scroll = ListboxScroll(self.top)
listbox_scroll.grid(pady=PADY, sticky="nsew")
listbox_scroll.columnconfigure(0, weight=1)
@ -48,7 +48,7 @@ class ServersDialog(Dialog):
for server in self.app.core.servers:
self.servers.insert(tk.END, server)
def draw_server_configuration(self):
def draw_server_configuration(self) -> None:
frame = ttk.LabelFrame(self.top, text="Server Configuration", padding=FRAME_PAD)
frame.grid(pady=PADY, sticky="ew")
frame.columnconfigure(1, weight=1)
@ -64,7 +64,7 @@ class ServersDialog(Dialog):
entry = ttk.Entry(frame, textvariable=self.address)
entry.grid(row=0, column=3, sticky="ew")
def draw_servers_buttons(self):
def draw_servers_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(pady=PADY, sticky="ew")
for i in range(3):
@ -83,7 +83,7 @@ class ServersDialog(Dialog):
)
self.delete_button.grid(row=0, column=2, sticky="ew")
def draw_apply_buttons(self):
def draw_apply_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(2):
@ -104,7 +104,7 @@ class ServersDialog(Dialog):
self.app.save_config()
self.destroy()
def click_create(self):
def click_create(self) -> None:
name = self.name.get()
if name not in self.app.core.servers:
address = self.address.get()
@ -112,7 +112,7 @@ class ServersDialog(Dialog):
self.app.core.servers[name] = server
self.servers.insert(tk.END, name)
def click_save(self):
def click_save(self) -> None:
name = self.name.get()
if self.selected:
previous_name = self.selected
@ -125,7 +125,7 @@ class ServersDialog(Dialog):
self.servers.insert(self.selected_index, name)
self.servers.selection_set(self.selected_index)
def click_delete(self):
def click_delete(self) -> None:
if self.selected:
self.servers.delete(self.selected_index)
del self.app.core.servers[self.selected]
@ -137,7 +137,7 @@ class ServersDialog(Dialog):
self.save_button.config(state=tk.DISABLED)
self.delete_button.config(state=tk.DISABLED)
def handle_server_change(self, event: tk.Event):
def handle_server_change(self, event: tk.Event) -> None:
selection = self.servers.curselection()
if selection:
self.selected_index = selection[0]

View file

@ -2,11 +2,12 @@ import logging
import os
import tkinter as tk
from tkinter import filedialog, ttk
from typing import TYPE_CHECKING, List
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
import grpc
from PIL.ImageTk import PhotoImage
from core.api.grpc.services_pb2 import ServiceValidationMode
from core.api.grpc.services_pb2 import NodeServiceData, ServiceValidationMode
from core.gui.dialogs.copyserviceconfig import CopyServiceConfigDialog
from core.gui.dialogs.dialog import Dialog
from core.gui.images import ImageEnum, Images
@ -16,8 +17,9 @@ from core.gui.widgets import CodeText, ListboxScroll
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
from core.gui.coreclient import CoreClient
ICON_SIZE = 16
ICON_SIZE: int = 16
class ServiceConfigDialog(Dialog):
@ -28,54 +30,57 @@ class ServiceConfigDialog(Dialog):
service_name: str,
canvas_node: "CanvasNode",
node_id: int,
):
) -> None:
title = f"{service_name} Service"
super().__init__(app, title, master=master)
self.core = app.core
self.canvas_node = canvas_node
self.node_id = node_id
self.service_name = service_name
self.radiovar = tk.IntVar()
self.radiovar.set(2)
self.metadata = ""
self.filenames = []
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 = None
self.directory_entry = None
self.default_directories = []
self.temp_directories = []
self.documentnew_img = self.app.get_icon(ImageEnum.DOCUMENTNEW, ICON_SIZE)
self.editdelete_img = self.app.get_icon(ImageEnum.EDITDELETE, ICON_SIZE)
self.notebook = None
self.metadata_entry = None
self.filename_combobox = None
self.dir_list = 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.service_file_data = None
self.validation_period_entry = None
self.original_service_files = {}
self.default_config = None
self.temp_service_files = {}
self.modified_files = set()
self.has_error = False
self.core: "CoreClient" = app.core
self.canvas_node: "CanvasNode" = canvas_node
self.node_id: int = node_id
self.service_name: str = service_name
self.radiovar: tk.IntVar = tk.IntVar(value=2)
self.metadata: str = ""
self.filenames: List[str] = []
self.dependencies: List[str] = []
self.executables: List[str] = []
self.startup_commands: List[str] = []
self.validation_commands: List[str] = []
self.shutdown_commands: List[str] = []
self.default_startup: List[str] = []
self.default_validate: List[str] = []
self.default_shutdown: List[str] = []
self.validation_mode: Optional[ServiceValidationMode] = None
self.validation_time: Optional[int] = None
self.validation_period: Optional[float] = None
self.directory_entry: Optional[ttk.Entry] = None
self.default_directories: List[str] = []
self.temp_directories: List[str] = []
self.documentnew_img: PhotoImage = self.app.get_icon(
ImageEnum.DOCUMENTNEW, ICON_SIZE
)
self.editdelete_img: PhotoImage = self.app.get_icon(
ImageEnum.EDITDELETE, ICON_SIZE
)
self.notebook: Optional[ttk.Notebook] = None
self.metadata_entry: Optional[ttk.Entry] = None
self.filename_combobox: Optional[ttk.Combobox] = None
self.dir_list: Optional[ListboxScroll] = None
self.startup_commands_listbox: Optional[tk.Listbox] = None
self.shutdown_commands_listbox: Optional[tk.Listbox] = None
self.validate_commands_listbox: Optional[tk.Listbox] = None
self.validation_time_entry: Optional[ttk.Entry] = None
self.validation_mode_entry: Optional[ttk.Entry] = None
self.service_file_data: Optional[CodeText] = None
self.validation_period_entry: Optional[ttk.Entry] = None
self.original_service_files: Dict[str, str] = {}
self.default_config: NodeServiceData = None
self.temp_service_files: Dict[str, str] = {}
self.modified_files: Set[str] = set()
self.has_error: bool = False
self.load()
if not self.has_error:
self.draw()
def load(self):
def load(self) -> None:
try:
self.app.core.create_nodes_and_links()
default_config = self.app.core.get_node_service(
@ -119,7 +124,7 @@ class ServiceConfigDialog(Dialog):
self.app.show_grpc_exception("Get Node Service Error", e)
self.has_error = True
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(1, weight=1)
@ -142,7 +147,7 @@ class ServiceConfigDialog(Dialog):
self.draw_buttons()
def draw_tab_files(self):
def draw_tab_files(self) -> None:
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
@ -222,7 +227,7 @@ class ServiceConfigDialog(Dialog):
"<FocusOut>", self.update_temp_service_file_data
)
def draw_tab_directories(self):
def draw_tab_directories(self) -> None:
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
@ -257,7 +262,7 @@ class ServiceConfigDialog(Dialog):
button = ttk.Button(frame, text="Remove", command=self.remove_directory)
button.grid(row=0, column=1, sticky="ew")
def draw_tab_startstop(self):
def draw_tab_startstop(self) -> None:
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
@ -311,7 +316,7 @@ class ServiceConfigDialog(Dialog):
elif i == 2:
self.validate_commands_listbox = listbox_scroll.listbox
def draw_tab_configuration(self):
def draw_tab_configuration(self) -> None:
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
@ -370,7 +375,7 @@ class ServiceConfigDialog(Dialog):
for dependency in self.dependencies:
listbox_scroll.listbox.insert("end", dependency)
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="ew")
for i in range(4):
@ -384,7 +389,7 @@ class ServiceConfigDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=3, sticky="ew")
def add_filename(self):
def add_filename(self) -> None:
filename = self.filename_combobox.get()
if filename not in self.filename_combobox["values"]:
self.filename_combobox["values"] += (filename,)
@ -395,7 +400,7 @@ class ServiceConfigDialog(Dialog):
else:
logging.debug("file already existed")
def delete_filename(self):
def delete_filename(self) -> None:
cbb = self.filename_combobox
filename = cbb.get()
if filename in cbb["values"]:
@ -407,7 +412,7 @@ class ServiceConfigDialog(Dialog):
self.modified_files.remove(filename)
@classmethod
def add_command(cls, event: tk.Event):
def add_command(cls, event: tk.Event) -> None:
frame_contains_button = event.widget.master
listbox = frame_contains_button.master.grid_slaves(row=1, column=0)[0].listbox
command_to_add = frame_contains_button.grid_slaves(row=0, column=0)[0].get()
@ -419,7 +424,7 @@ class ServiceConfigDialog(Dialog):
listbox.insert(tk.END, command_to_add)
@classmethod
def update_entry(cls, event: tk.Event):
def update_entry(cls, event: tk.Event) -> None:
listbox = event.widget
current_selection = listbox.curselection()
if len(current_selection) > 0:
@ -431,7 +436,7 @@ class ServiceConfigDialog(Dialog):
entry.insert(0, cmd)
@classmethod
def delete_command(cls, event: tk.Event):
def delete_command(cls, event: tk.Event) -> None:
button = event.widget
frame_contains_button = button.master
listbox = frame_contains_button.master.grid_slaves(row=1, column=0)[0].listbox
@ -441,7 +446,7 @@ class ServiceConfigDialog(Dialog):
entry = frame_contains_button.grid_slaves(row=0, column=0)[0]
entry.delete(0, tk.END)
def click_apply(self):
def click_apply(self) -> None:
if (
not self.is_custom_command()
and not self.is_custom_service_file()
@ -484,12 +489,12 @@ class ServiceConfigDialog(Dialog):
self.app.show_grpc_exception("Save Service Config Error", e)
self.destroy()
def display_service_file_data(self, event: tk.Event):
def display_service_file_data(self, event: tk.Event) -> None:
filename = self.filename_combobox.get()
self.service_file_data.text.delete(1.0, "end")
self.service_file_data.text.insert("end", self.temp_service_files[filename])
def update_temp_service_file_data(self, event: tk.Event):
def update_temp_service_file_data(self, event: tk.Event) -> None:
filename = self.filename_combobox.get()
self.temp_service_files[filename] = self.service_file_data.text.get(1.0, "end")
if self.temp_service_files[filename] != self.original_service_files.get(
@ -499,7 +504,7 @@ class ServiceConfigDialog(Dialog):
else:
self.modified_files.discard(filename)
def is_custom_command(self):
def is_custom_command(self) -> bool:
startup, validate, shutdown = self.get_commands()
return (
set(self.default_startup) != set(startup)
@ -507,16 +512,16 @@ class ServiceConfigDialog(Dialog):
or set(self.default_shutdown) != set(shutdown)
)
def has_new_files(self):
def has_new_files(self) -> bool:
return set(self.filenames) != set(self.filename_combobox["values"])
def is_custom_service_file(self):
def is_custom_service_file(self) -> bool:
return len(self.modified_files) > 0
def is_custom_directory(self):
def is_custom_directory(self) -> bool:
return set(self.default_directories) != set(self.dir_list.listbox.get(0, "end"))
def click_defaults(self):
def click_defaults(self) -> None:
"""
clears out any custom configuration permanently
"""
@ -557,37 +562,37 @@ class ServiceConfigDialog(Dialog):
self.current_service_color("")
def click_copy(self):
def click_copy(self) -> None:
dialog = CopyServiceConfigDialog(self, self.app, self.node_id)
dialog.show()
@classmethod
def append_commands(
cls, commands: List[str], listbox: tk.Listbox, to_add: List[str]
):
) -> None:
for cmd in to_add:
commands.append(cmd)
listbox.insert(tk.END, cmd)
def get_commands(self):
def get_commands(self) -> Tuple[List[str], List[str], List[str]]:
startup = self.startup_commands_listbox.get(0, "end")
shutdown = self.shutdown_commands_listbox.get(0, "end")
validate = self.validate_commands_listbox.get(0, "end")
return startup, validate, shutdown
def find_directory_button(self):
def find_directory_button(self) -> None:
d = filedialog.askdirectory(initialdir="/")
self.directory_entry.delete(0, "end")
self.directory_entry.insert("end", d)
def add_directory(self):
def add_directory(self) -> None:
d = self.directory_entry.get()
if os.path.isdir(d):
if d not in self.temp_directories:
self.dir_list.listbox.insert("end", d)
self.temp_directories.append(d)
def remove_directory(self):
def remove_directory(self) -> None:
d = self.directory_entry.get()
dirs = self.dir_list.listbox.get(0, "end")
if d and d in self.temp_directories:
@ -599,14 +604,14 @@ class ServiceConfigDialog(Dialog):
logging.debug("directory is not in the list")
self.directory_entry.delete(0, "end")
def directory_select(self, event):
def directory_select(self, event) -> None:
i = self.dir_list.listbox.curselection()
if i:
d = self.dir_list.listbox.get(i)
self.directory_entry.delete(0, "end")
self.directory_entry.insert("end", d)
def current_service_color(self, color=""):
def current_service_color(self, color="") -> None:
"""
change the current service label color
"""

View file

@ -1,9 +1,10 @@
import logging
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Optional
import grpc
from core.api.grpc.common_pb2 import ConfigOption
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY
from core.gui.widgets import ConfigFrame
@ -13,15 +14,15 @@ if TYPE_CHECKING:
class SessionOptionsDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Session Options")
self.config_frame = None
self.has_error = False
self.config = self.get_config()
self.config_frame: Optional[ConfigFrame] = None
self.has_error: bool = False
self.config: Dict[str, ConfigOption] = self.get_config()
if not self.has_error:
self.draw()
def get_config(self):
def get_config(self) -> Dict[str, ConfigOption]:
try:
session_id = self.app.core.session_id
response = self.app.core.client.get_session_options(session_id)
@ -31,7 +32,7 @@ class SessionOptionsDialog(Dialog):
self.has_error = True
self.destroy()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1)
@ -48,7 +49,7 @@ class SessionOptionsDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def save(self):
def save(self) -> None:
config = self.config_frame.parse_config()
try:
session_id = self.app.core.session_id

View file

@ -1,11 +1,12 @@
import logging
import tkinter as tk
from tkinter import messagebox, ttk
from typing import TYPE_CHECKING, List
from typing import TYPE_CHECKING, List, Optional
import grpc
from core.api.grpc import core_pb2
from core.api.grpc.core_pb2 import SessionSummary
from core.gui.dialogs.dialog import Dialog
from core.gui.images import ImageEnum, Images
from core.gui.task import ProgressTask
@ -18,17 +19,17 @@ if TYPE_CHECKING:
class SessionsDialog(Dialog):
def __init__(self, app: "Application", is_start_app: bool = False) -> None:
super().__init__(app, "Sessions")
self.is_start_app = is_start_app
self.selected_session = None
self.selected_id = None
self.tree = None
self.sessions = self.get_sessions()
self.connect_button = None
self.delete_button = None
self.is_start_app: bool = is_start_app
self.selected_session: Optional[int] = None
self.selected_id: Optional[int] = None
self.tree: Optional[ttk.Treeview] = None
self.sessions: List[SessionSummary] = self.get_sessions()
self.connect_button: Optional[ttk.Button] = None
self.delete_button: Optional[ttk.Button] = None
self.protocol("WM_DELETE_WINDOW", self.on_closing)
self.draw()
def get_sessions(self) -> List[core_pb2.SessionSummary]:
def get_sessions(self) -> List[SessionSummary]:
try:
response = self.app.core.client.get_sessions()
logging.info("sessions: %s", response)

View file

@ -3,7 +3,7 @@ shape input dialog
"""
import tkinter as tk
from tkinter import font, ttk
from typing import TYPE_CHECKING, List, Union
from typing import TYPE_CHECKING, List, Optional, Union
from core.gui.dialogs.colorpicker import ColorPickerDialog
from core.gui.dialogs.dialog import Dialog
@ -13,40 +13,41 @@ from core.gui.themes import FRAME_PAD, PADX, PADY
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.graph import CanvasGraph
from core.gui.graph.shape import Shape
FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
BORDER_WIDTH = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
FONT_SIZES: List[int] = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
BORDER_WIDTH: List[int] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
class ShapeDialog(Dialog):
def __init__(self, app: "Application", shape: "Shape"):
def __init__(self, app: "Application", shape: "Shape") -> None:
if is_draw_shape(shape.shape_type):
title = "Add Shape"
else:
title = "Add Text"
super().__init__(app, title)
self.canvas = app.canvas
self.fill = None
self.border = None
self.shape = shape
self.canvas: "CanvasGraph" = app.canvas
self.fill: Optional[ttk.Label] = None
self.border: Optional[ttk.Label] = None
self.shape: "Shape" = shape
data = shape.shape_data
self.shape_text = tk.StringVar(value=data.text)
self.font = tk.StringVar(value=data.font)
self.font_size = tk.IntVar(value=data.font_size)
self.text_color = data.text_color
self.shape_text: tk.StringVar = tk.StringVar(value=data.text)
self.font: tk.StringVar = tk.StringVar(value=data.font)
self.font_size: tk.IntVar = tk.IntVar(value=data.font_size)
self.text_color: str = data.text_color
fill_color = data.fill_color
if not fill_color:
fill_color = "#CFCFFF"
self.fill_color = fill_color
self.border_color = data.border_color
self.border_width = tk.IntVar(value=0)
self.bold = tk.BooleanVar(value=data.bold)
self.italic = tk.BooleanVar(value=data.italic)
self.underline = tk.BooleanVar(value=data.underline)
self.fill_color: str = fill_color
self.border_color: str = data.border_color
self.border_width: tk.IntVar = tk.IntVar(value=0)
self.bold: tk.BooleanVar = tk.BooleanVar(value=data.bold)
self.italic: tk.BooleanVar = tk.BooleanVar(value=data.italic)
self.underline: tk.BooleanVar = tk.BooleanVar(value=data.underline)
self.draw()
def draw(self):
def draw(self) -> None:
self.top.columnconfigure(0, weight=1)
self.draw_label_options()
if is_draw_shape(self.shape.shape_type):
@ -54,7 +55,7 @@ class ShapeDialog(Dialog):
self.draw_spacer()
self.draw_buttons()
def draw_label_options(self):
def draw_label_options(self) -> None:
label_frame = ttk.LabelFrame(self.top, text="Label", padding=FRAME_PAD)
label_frame.grid(sticky="ew")
label_frame.columnconfigure(0, weight=1)
@ -94,7 +95,7 @@ class ShapeDialog(Dialog):
button = ttk.Checkbutton(frame, variable=self.underline, text="Underline")
button.grid(row=0, column=2, sticky="ew")
def draw_shape_options(self):
def draw_shape_options(self) -> None:
label_frame = ttk.LabelFrame(self.top, text="Shape", padding=FRAME_PAD)
label_frame.grid(sticky="ew", pady=PADY)
label_frame.columnconfigure(0, weight=1)
@ -129,7 +130,7 @@ class ShapeDialog(Dialog):
)
combobox.grid(row=0, column=1, sticky="nsew")
def draw_buttons(self):
def draw_buttons(self) -> None:
frame = ttk.Frame(self.top)
frame.grid(sticky="nsew")
frame.columnconfigure(0, weight=1)
@ -139,28 +140,28 @@ class ShapeDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.cancel)
button.grid(row=0, column=1, sticky="ew")
def choose_text_color(self):
def choose_text_color(self) -> None:
color_picker = ColorPickerDialog(self, self.app, self.text_color)
self.text_color = color_picker.askcolor()
def choose_fill_color(self):
def choose_fill_color(self) -> None:
color_picker = ColorPickerDialog(self, self.app, self.fill_color)
color = color_picker.askcolor()
self.fill_color = color
self.fill.config(background=color, text=color)
def choose_border_color(self):
def choose_border_color(self) -> None:
color_picker = ColorPickerDialog(self, self.app, self.border_color)
color = color_picker.askcolor()
self.border_color = color
self.border.config(background=color, text=color)
def cancel(self):
def cancel(self) -> None:
self.shape.delete()
self.canvas.shapes.pop(self.shape.id)
self.destroy()
def click_add(self):
def click_add(self) -> None:
if is_draw_shape(self.shape.shape_type):
self.add_shape()
elif is_shape_text(self.shape.shape_type):
@ -181,7 +182,7 @@ class ShapeDialog(Dialog):
text_font.append("underline")
return text_font
def save_text(self):
def save_text(self) -> None:
"""
save info related to text or shape label
"""
@ -194,7 +195,7 @@ class ShapeDialog(Dialog):
data.italic = self.italic.get()
data.underline = self.underline.get()
def save_shape(self):
def save_shape(self) -> None:
"""
save info related to shape
"""
@ -203,7 +204,7 @@ class ShapeDialog(Dialog):
data.border_color = self.border_color
data.border_width = int(self.border_width.get())
def add_text(self):
def add_text(self) -> None:
"""
add text to canvas
"""
@ -214,7 +215,7 @@ class ShapeDialog(Dialog):
)
self.save_text()
def add_shape(self):
def add_shape(self) -> None:
self.canvas.itemconfig(
self.shape.id,
fill=self.fill_color,

View file

@ -3,10 +3,11 @@ throughput dialog
"""
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from core.gui.dialogs.colorpicker import ColorPickerDialog
from core.gui.dialogs.dialog import Dialog
from core.gui.graph.graph import CanvasGraph
from core.gui.themes import FRAME_PAD, PADX, PADY
if TYPE_CHECKING:
@ -14,21 +15,23 @@ if TYPE_CHECKING:
class ThroughputDialog(Dialog):
def __init__(self, app: "Application"):
def __init__(self, app: "Application") -> None:
super().__init__(app, "Throughput Config")
self.canvas = app.canvas
self.show_throughput = tk.IntVar(value=1)
self.exponential_weight = tk.IntVar(value=1)
self.transmission = tk.IntVar(value=1)
self.reception = tk.IntVar(value=1)
self.threshold = tk.DoubleVar(value=self.canvas.throughput_threshold)
self.width = tk.IntVar(value=self.canvas.throughput_width)
self.color = self.canvas.throughput_color
self.color_button = None
self.canvas: CanvasGraph = app.canvas
self.show_throughput: tk.IntVar = tk.IntVar(value=1)
self.exponential_weight: tk.IntVar = tk.IntVar(value=1)
self.transmission: tk.IntVar = tk.IntVar(value=1)
self.reception: tk.IntVar = tk.IntVar(value=1)
self.threshold: tk.DoubleVar = tk.DoubleVar(
value=self.canvas.throughput_threshold
)
self.width: tk.IntVar = tk.IntVar(value=self.canvas.throughput_width)
self.color: str = self.canvas.throughput_color
self.color_button: Optional[tk.Button] = None
self.top.columnconfigure(0, weight=1)
self.draw()
def draw(self):
def draw(self) -> None:
button = ttk.Checkbutton(
self.top,
variable=self.show_throughput,
@ -97,12 +100,12 @@ class ThroughputDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_color(self):
def click_color(self) -> None:
color_picker = ColorPickerDialog(self, self.app, self.color)
self.color = color_picker.askcolor()
self.color_button.config(bg=self.color, text=self.color, bd=0)
def click_save(self):
def click_save(self) -> None:
self.canvas.throughput_threshold = self.threshold.get()
self.canvas.throughput_width = self.width.get()
self.canvas.throughput_color = self.color

View file

@ -1,8 +1,10 @@
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Optional
import grpc
from core.api.grpc.common_pb2 import ConfigOption
from core.api.grpc.core_pb2 import Node
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY
from core.gui.widgets import ConfigFrame
@ -10,34 +12,36 @@ from core.gui.widgets import ConfigFrame
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
from core.gui.graph.graph import CanvasGraph
RANGE_COLOR = "#009933"
RANGE_WIDTH = 3
RANGE_COLOR: str = "#009933"
RANGE_WIDTH: int = 3
class WlanConfigDialog(Dialog):
def __init__(self, app: "Application", canvas_node: "CanvasNode"):
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
super().__init__(app, f"{canvas_node.core_node.name} WLAN Configuration")
self.canvas_node = canvas_node
self.node = canvas_node.core_node
self.config_frame = None
self.range_entry = None
self.has_error = False
self.canvas = app.canvas
self.ranges = {}
self.positive_int = self.app.master.register(self.validate_and_update)
self.canvas: "CanvasGraph" = app.canvas
self.canvas_node: "CanvasNode" = canvas_node
self.node: Node = canvas_node.core_node
self.config_frame: Optional[ConfigFrame] = None
self.range_entry: Optional[ttk.Entry] = None
self.has_error: bool = False
self.ranges: Dict[int, int] = {}
self.positive_int: int = self.app.master.register(self.validate_and_update)
try:
self.config = self.canvas_node.wlan_config
if not self.config:
self.config = self.app.core.get_wlan_config(self.node.id)
config = self.canvas_node.wlan_config
if not config:
config = self.app.core.get_wlan_config(self.node.id)
self.config: Dict[str, ConfigOption] = config
self.init_draw_range()
self.draw()
except grpc.RpcError as e:
self.app.show_grpc_exception("WLAN Config Error", e)
self.has_error = True
self.has_error: bool = True
self.destroy()
def init_draw_range(self):
def init_draw_range(self) -> None:
if self.canvas_node.id in self.canvas.wireless_network:
for cid in self.canvas.wireless_network[self.canvas_node.id]:
x, y = self.canvas.coords(cid)
@ -46,7 +50,7 @@ class WlanConfigDialog(Dialog):
)
self.ranges[cid] = range_id
def draw(self):
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)
@ -55,7 +59,7 @@ class WlanConfigDialog(Dialog):
self.draw_apply_buttons()
self.top.bind("<Destroy>", self.remove_ranges)
def draw_apply_buttons(self):
def draw_apply_buttons(self) -> None:
"""
create node configuration options
"""
@ -75,7 +79,7 @@ class WlanConfigDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_apply(self):
def click_apply(self) -> None:
"""
retrieve user's wlan configuration and store the new configuration values
"""
@ -87,7 +91,7 @@ class WlanConfigDialog(Dialog):
self.remove_ranges()
self.destroy()
def remove_ranges(self, event=None):
def remove_ranges(self, event=None) -> None:
for cid in self.canvas.find_withtag("range"):
self.canvas.delete(cid)
self.ranges.clear()

View file

@ -57,7 +57,9 @@ class CanvasNode:
self.antennas: List[int] = []
self.antenna_images: Dict[int, PhotoImage] = {}
# possible configurations
self.emane_model_configs: Dict[Tuple[str, Optional[int]], ConfigOption] = {}
self.emane_model_configs: Dict[
Tuple[str, Optional[int]], Dict[str, ConfigOption]
] = {}
self.wlan_config: Dict[str, ConfigOption] = {}
self.mobility_config: Dict[str, ConfigOption] = {}
self.service_configs: Dict[str, NodeServiceData] = {}
@ -135,7 +137,7 @@ class CanvasNode:
new_y = self._get_label_y()
self.canvas.move(self.text_id, 0, new_y - prev_y)
def move(self, x: int, y: int) -> None:
def move(self, x: float, y: float) -> None:
x, y = self.canvas.get_scaled_coords(x, y)
current_x, current_y = self.canvas.coords(self.id)
x_offset = x - current_x

View file

@ -49,7 +49,7 @@ class Menubar(tk.Menu):
self.canvas: CanvasGraph = app.canvas
self.recent_menu: Optional[tk.Menu] = None
self.edit_menu: Optional[tk.Menu] = None
self.observers_menu: Optional[tk.Menu] = None
self.observers_menu: Optional[ObserversMenu] = None
self.draw()
def draw(self) -> None:

View file

@ -14,7 +14,7 @@ ANTENNA_SIZE: int = 32
class NodeDraw:
def __init__(self) -> None:
self.custom: bool = False
self.image: Optional[str] = None
self.image: Optional[PhotoImage] = None
self.image_enum: Optional[ImageEnum] = None
self.image_file: Optional[str] = None
self.node_type: NodeType = None