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

View file

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

View file

@ -3,9 +3,9 @@ check engine light
""" """
import tkinter as tk import tkinter as tk
from tkinter import ttk 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.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY from core.gui.themes import PADX, PADY
from core.gui.widgets import CodeText from core.gui.widgets import CodeText
@ -15,14 +15,14 @@ if TYPE_CHECKING:
class AlertsDialog(Dialog): class AlertsDialog(Dialog):
def __init__(self, app: "Application"): def __init__(self, app: "Application") -> None:
super().__init__(app, "Alerts") super().__init__(app, "Alerts")
self.tree = None self.tree: Optional[ttk.Treeview] = None
self.codetext = None self.codetext: Optional[CodeText] = None
self.alarm_map = {} self.alarm_map: Dict[int, ExceptionEvent] = {}
self.draw() self.draw()
def draw(self): def draw(self) -> None:
self.top.columnconfigure(0, weight=1) self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(0, weight=1) self.top.rowconfigure(0, weight=1)
self.top.rowconfigure(1, 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 = ttk.Button(frame, text="Close", command=self.destroy)
button.grid(row=0, column=1, sticky="ew") 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) self.codetext.text.delete("1.0", tk.END)
for item in self.tree.get_children(): for item in self.tree.get_children():
self.tree.delete(item) self.tree.delete(item)
self.app.statusbar.core_alarms.clear() 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] current = self.tree.selection()[0]
alarm = self.alarm_map[current] alarm = self.alarm_map[current]
self.codetext.text.config(state=tk.NORMAL) 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 import validation
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.graph.graph import CanvasGraph
from core.gui.themes import FRAME_PAD, PADX, PADY from core.gui.themes import FRAME_PAD, PADX, PADY
if TYPE_CHECKING: if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
PIXEL_SCALE = 100 PIXEL_SCALE: int = 100
class SizeAndScaleDialog(Dialog): class SizeAndScaleDialog(Dialog):
def __init__(self, app: "Application"): def __init__(self, app: "Application") -> None:
""" """
create an instance for size and scale object create an instance for size and scale object
""" """
super().__init__(app, "Canvas Size and Scale") super().__init__(app, "Canvas Size and Scale")
self.canvas = self.app.canvas self.canvas: CanvasGraph = self.app.canvas
self.section_font = font.Font(weight="bold") self.section_font: font.Font = font.Font(weight="bold")
width, height = self.canvas.current_dimensions width, height = self.canvas.current_dimensions
self.pixel_width = tk.IntVar(value=width) self.pixel_width: tk.IntVar = tk.IntVar(value=width)
self.pixel_height = tk.IntVar(value=height) self.pixel_height: tk.IntVar = tk.IntVar(value=height)
location = self.app.core.location location = self.app.core.location
self.x = tk.DoubleVar(value=location.x) self.x: tk.DoubleVar = tk.DoubleVar(value=location.x)
self.y = tk.DoubleVar(value=location.y) self.y: tk.DoubleVar = tk.DoubleVar(value=location.y)
self.lat = tk.DoubleVar(value=location.lat) self.lat: tk.DoubleVar = tk.DoubleVar(value=location.lat)
self.lon = tk.DoubleVar(value=location.lon) self.lon: tk.DoubleVar = tk.DoubleVar(value=location.lon)
self.alt = tk.DoubleVar(value=location.alt) self.alt: tk.DoubleVar = tk.DoubleVar(value=location.alt)
self.scale = tk.DoubleVar(value=location.scale) self.scale: tk.DoubleVar = tk.DoubleVar(value=location.scale)
self.meters_width = tk.IntVar(value=width / PIXEL_SCALE * location.scale) self.meters_width: tk.IntVar = tk.IntVar(
self.meters_height = tk.IntVar(value=height / PIXEL_SCALE * location.scale) value=width / PIXEL_SCALE * location.scale
self.save_default = tk.BooleanVar(value=False) )
self.meters_height: tk.IntVar = tk.IntVar(
value=height / PIXEL_SCALE * location.scale
)
self.save_default: tk.BooleanVar = tk.BooleanVar(value=False)
self.draw() self.draw()
def draw(self): def draw(self) -> None:
self.top.columnconfigure(0, weight=1) self.top.columnconfigure(0, weight=1)
self.draw_size() self.draw_size()
self.draw_scale() self.draw_scale()
@ -47,7 +52,7 @@ class SizeAndScaleDialog(Dialog):
self.draw_spacer() self.draw_spacer()
self.draw_buttons() 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 = ttk.Labelframe(self.top, text="Size", padding=FRAME_PAD)
label_frame.grid(sticky="ew") label_frame.grid(sticky="ew")
label_frame.columnconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1)
@ -84,7 +89,7 @@ class SizeAndScaleDialog(Dialog):
label = ttk.Label(frame, text="Meters") label = ttk.Label(frame, text="Meters")
label.grid(row=0, column=4, sticky="w") 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 = ttk.Labelframe(self.top, text="Scale", padding=FRAME_PAD)
label_frame.grid(sticky="ew") label_frame.grid(sticky="ew")
label_frame.columnconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1)
@ -99,7 +104,7 @@ class SizeAndScaleDialog(Dialog):
label = ttk.Label(frame, text="Meters") label = ttk.Label(frame, text="Meters")
label.grid(row=0, column=2, sticky="w") label.grid(row=0, column=2, sticky="w")
def draw_reference_point(self): def draw_reference_point(self) -> None:
label_frame = ttk.Labelframe( label_frame = ttk.Labelframe(
self.top, text="Reference Point", padding=FRAME_PAD self.top, text="Reference Point", padding=FRAME_PAD
) )
@ -150,13 +155,13 @@ class SizeAndScaleDialog(Dialog):
entry = validation.FloatEntry(frame, textvariable=self.alt) entry = validation.FloatEntry(frame, textvariable=self.alt)
entry.grid(row=0, column=5, sticky="ew") entry.grid(row=0, column=5, sticky="ew")
def draw_save_as_default(self): def draw_save_as_default(self) -> None:
button = ttk.Checkbutton( button = ttk.Checkbutton(
self.top, text="Save as default?", variable=self.save_default self.top, text="Save as default?", variable=self.save_default
) )
button.grid(sticky="w", pady=PADY) button.grid(sticky="w", pady=PADY)
def draw_buttons(self): def draw_buttons(self) -> None:
frame = ttk.Frame(self.top) frame = ttk.Frame(self.top)
frame.columnconfigure(0, weight=1) frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=1) frame.columnconfigure(1, weight=1)
@ -168,7 +173,7 @@ class SizeAndScaleDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy) button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew") 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() width, height = self.pixel_width.get(), self.pixel_height.get()
self.canvas.redraw_canvas((width, height)) self.canvas.redraw_canvas((width, height))
if self.canvas.wallpaper: if self.canvas.wallpaper:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -57,7 +57,9 @@ class CanvasNode:
self.antennas: List[int] = [] self.antennas: List[int] = []
self.antenna_images: Dict[int, PhotoImage] = {} self.antenna_images: Dict[int, PhotoImage] = {}
# possible configurations # 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.wlan_config: Dict[str, ConfigOption] = {}
self.mobility_config: Dict[str, ConfigOption] = {} self.mobility_config: Dict[str, ConfigOption] = {}
self.service_configs: Dict[str, NodeServiceData] = {} self.service_configs: Dict[str, NodeServiceData] = {}
@ -135,7 +137,7 @@ class CanvasNode:
new_y = self._get_label_y() new_y = self._get_label_y()
self.canvas.move(self.text_id, 0, new_y - prev_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) x, y = self.canvas.get_scaled_coords(x, y)
current_x, current_y = self.canvas.coords(self.id) current_x, current_y = self.canvas.coords(self.id)
x_offset = x - current_x x_offset = x - current_x

View file

@ -49,7 +49,7 @@ class Menubar(tk.Menu):
self.canvas: CanvasGraph = app.canvas self.canvas: CanvasGraph = app.canvas
self.recent_menu: Optional[tk.Menu] = None self.recent_menu: Optional[tk.Menu] = None
self.edit_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() self.draw()
def draw(self) -> None: def draw(self) -> None:

View file

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