-m
This commit is contained in:
parent
b9b8e3a5f1
commit
6c8a2526d9
40 changed files with 219 additions and 141 deletions
|
@ -1,6 +1,5 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from core.gui import appconfig, themes
|
from core.gui import appconfig, themes
|
||||||
from core.gui.coreclient import CoreClient
|
from core.gui.coreclient import CoreClient
|
||||||
|
@ -18,7 +17,7 @@ HEIGHT = 800
|
||||||
|
|
||||||
|
|
||||||
class Application(tk.Frame):
|
class Application(tk.Frame):
|
||||||
def __init__(self, master: Optional[tk.Widget] = None):
|
def __init__(self, master: tk.Widget = None):
|
||||||
super().__init__(master)
|
super().__init__(master)
|
||||||
# load node icons
|
# load node icons
|
||||||
NodeUtils.setup()
|
NodeUtils.setup()
|
||||||
|
|
|
@ -5,7 +5,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional
|
from typing import TYPE_CHECKING, Dict, List, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
|
@ -22,6 +22,9 @@ from core.gui.graph.shapeutils import ShapeType
|
||||||
from core.gui.interface import InterfaceManager
|
from core.gui.interface import InterfaceManager
|
||||||
from core.gui.nodeutils import NodeDraw, NodeUtils
|
from core.gui.nodeutils import NodeDraw, NodeUtils
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
|
||||||
GUI_SOURCE = "gui"
|
GUI_SOURCE = "gui"
|
||||||
OBSERVERS = {
|
OBSERVERS = {
|
||||||
"processes": "ps",
|
"processes": "ps",
|
||||||
|
@ -50,7 +53,7 @@ class Observer:
|
||||||
|
|
||||||
|
|
||||||
class CoreClient:
|
class CoreClient:
|
||||||
def __init__(self, app):
|
def __init__(self, app: "Application"):
|
||||||
"""
|
"""
|
||||||
Create a CoreGrpc instance
|
Create a CoreGrpc instance
|
||||||
"""
|
"""
|
||||||
|
@ -137,7 +140,7 @@ class CoreClient:
|
||||||
|
|
||||||
def handle_events(self, event: core_pb2.Event):
|
def handle_events(self, event: core_pb2.Event):
|
||||||
if event.session_id != self.session_id:
|
if event.session_id != self.session_id:
|
||||||
logging.warn(
|
logging.warning(
|
||||||
"ignoring event session(%s) current(%s)",
|
"ignoring event session(%s) current(%s)",
|
||||||
event.session_id,
|
event.session_id,
|
||||||
self.session_id,
|
self.session_id,
|
||||||
|
|
|
@ -35,7 +35,7 @@ THE POSSIBILITY OF SUCH DAMAGE.\
|
||||||
|
|
||||||
|
|
||||||
class AboutDialog(Dialog):
|
class AboutDialog(Dialog):
|
||||||
def __init__(self, master: tk.Widget, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "About CORE", modal=True)
|
super().__init__(master, app, "About CORE", modal=True)
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class AlertsDialog(Dialog):
|
class AlertsDialog(Dialog):
|
||||||
def __init__(self, master: tk.Widget, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "Alerts", modal=True)
|
super().__init__(master, app, "Alerts", modal=True)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.tree = None
|
self.tree = None
|
||||||
|
@ -124,7 +124,7 @@ class AlertsDialog(Dialog):
|
||||||
|
|
||||||
|
|
||||||
class DaemonLog(Dialog):
|
class DaemonLog(Dialog):
|
||||||
def __init__(self, master, app):
|
def __init__(self, master: tk.Widget, app: "Application"):
|
||||||
super().__init__(master, app, "core-daemon log", modal=True)
|
super().__init__(master, app, "core-daemon log", modal=True)
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1)
|
||||||
self.path = tk.StringVar(value="/var/log/core-daemon.log")
|
self.path = tk.StringVar(value="/var/log/core-daemon.log")
|
||||||
|
|
|
@ -17,11 +17,9 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class CanvasWallpaperDialog(Dialog):
|
class CanvasWallpaperDialog(Dialog):
|
||||||
def __init__(self, master: tk.Widget, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
"""
|
"""
|
||||||
create an instance of CanvasWallpaper object
|
create an instance of CanvasWallpaper object
|
||||||
|
|
||||||
:param coretk.app.Application app: root application
|
|
||||||
"""
|
"""
|
||||||
super().__init__(master, app, "Canvas Background", modal=True)
|
super().__init__(master, app, "Canvas Background", modal=True)
|
||||||
self.canvas = self.app.canvas
|
self.canvas = self.app.canvas
|
||||||
|
|
|
@ -4,7 +4,7 @@ custom color picker
|
||||||
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, Optional
|
from typing import TYPE_CHECKING, Any, Optional
|
||||||
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
|
|
||||||
|
@ -14,10 +14,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class ColorPickerDialog(Dialog):
|
class ColorPickerDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, master: Any, app: "Application", initcolor: Optional[str] = "#000000"
|
||||||
master: tk.Widget,
|
|
||||||
app: "Application",
|
|
||||||
initcolor: Optional[str] = "#000000",
|
|
||||||
):
|
):
|
||||||
super().__init__(master, app, "color picker", modal=True)
|
super().__init__(master, app, "color picker", modal=True)
|
||||||
self.red_entry = None
|
self.red_entry = None
|
||||||
|
|
|
@ -5,7 +5,7 @@ copy service config 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, Tuple
|
from typing import TYPE_CHECKING, Any, Tuple
|
||||||
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import FRAME_PAD, PADX
|
from core.gui.themes import FRAME_PAD, PADX
|
||||||
|
@ -13,11 +13,10 @@ from core.gui.widgets import CodeText
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.dialogs.serviceconfig import ServiceConfigDialog
|
|
||||||
|
|
||||||
|
|
||||||
class CopyServiceConfigDialog(Dialog):
|
class CopyServiceConfigDialog(Dialog):
|
||||||
def __init__(self, master: "ServiceConfigDialog", app: "Application", node_id: int):
|
def __init__(self, master: Any, app: "Application", node_id: int):
|
||||||
super().__init__(master, app, f"Copy services to node {node_id}", modal=True)
|
super().__init__(master, app, f"Copy services to node {node_id}", modal=True)
|
||||||
self.parent = master
|
self.parent = master
|
||||||
self.app = app
|
self.app = app
|
||||||
|
@ -133,6 +132,7 @@ class CopyServiceConfigDialog(Dialog):
|
||||||
|
|
||||||
def click_view(self):
|
def click_view(self):
|
||||||
selected = self.tree.selection()
|
selected = self.tree.selection()
|
||||||
|
data = ""
|
||||||
if selected:
|
if selected:
|
||||||
item = self.tree.item(selected[0])
|
item = self.tree.item(selected[0])
|
||||||
if "file" in item["tags"]:
|
if "file" in item["tags"]:
|
||||||
|
@ -161,7 +161,7 @@ class CopyServiceConfigDialog(Dialog):
|
||||||
|
|
||||||
|
|
||||||
class ViewConfigDialog(Dialog):
|
class ViewConfigDialog(Dialog):
|
||||||
def __init__(self, master, app, node_id, data):
|
def __init__(self, master: Any, app: "Application", node_id: int, data: bytes):
|
||||||
super().__init__(master, app, f"n{node_id} config data", modal=True)
|
super().__init__(master, app, f"n{node_id} config data", modal=True)
|
||||||
self.data = data
|
self.data = data
|
||||||
self.service_data = None
|
self.service_data = None
|
||||||
|
|
|
@ -2,7 +2,7 @@ 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, Any, Set
|
||||||
|
|
||||||
from core.gui import nodeutils
|
from core.gui import nodeutils
|
||||||
from core.gui.appconfig import ICONS_PATH
|
from core.gui.appconfig import ICONS_PATH
|
||||||
|
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class ServicesSelectDialog(Dialog):
|
class ServicesSelectDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application", current_services: Set[str]):
|
def __init__(self, master: Any, app: "Application", current_services: Set[str]):
|
||||||
super().__init__(master, app, "Node Services", modal=True)
|
super().__init__(master, app, "Node Services", modal=True)
|
||||||
self.groups = None
|
self.groups = None
|
||||||
self.services = None
|
self.services = None
|
||||||
|
@ -100,7 +100,7 @@ class ServicesSelectDialog(Dialog):
|
||||||
|
|
||||||
|
|
||||||
class CustomNodesDialog(Dialog):
|
class CustomNodesDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "Custom Nodes", modal=True)
|
super().__init__(master, app, "Custom Nodes", modal=True)
|
||||||
self.edit_button = None
|
self.edit_button = None
|
||||||
self.delete_button = None
|
self.delete_button = None
|
||||||
|
@ -245,7 +245,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):
|
def handle_node_select(self, event: tk.Event):
|
||||||
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]
|
||||||
|
|
|
@ -5,7 +5,7 @@ import logging
|
||||||
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, Optional
|
from typing import TYPE_CHECKING, Any, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class GlobalEmaneDialog(Dialog):
|
class GlobalEmaneDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: Any, app: "Application"):
|
||||||
super().__init__(master, app, "EMANE Configuration", modal=True)
|
super().__init__(master, app, "EMANE Configuration", modal=True)
|
||||||
self.config_frame = None
|
self.config_frame = None
|
||||||
self.draw()
|
self.draw()
|
||||||
|
@ -55,7 +55,7 @@ class GlobalEmaneDialog(Dialog):
|
||||||
class EmaneModelDialog(Dialog):
|
class EmaneModelDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
master,
|
master: Any,
|
||||||
app: "Application",
|
app: "Application",
|
||||||
node: core_pb2.Node,
|
node: core_pb2.Node,
|
||||||
model: str,
|
model: str,
|
||||||
|
@ -104,7 +104,9 @@ class EmaneModelDialog(Dialog):
|
||||||
|
|
||||||
|
|
||||||
class EmaneConfigDialog(Dialog):
|
class EmaneConfigDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application", canvas_node: "CanvasNode"):
|
def __init__(
|
||||||
|
self, master: "Application", app: "Application", canvas_node: "CanvasNode"
|
||||||
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
master, app, f"{canvas_node.core_node.name} EMANE Configuration", modal=True
|
master, app, f"{canvas_node.core_node.name} EMANE Configuration", modal=True
|
||||||
)
|
)
|
||||||
|
|
|
@ -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, Union
|
||||||
|
|
||||||
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,7 +12,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class HookDialog(Dialog):
|
class HookDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: Union[tk.Widget, Dialog], app: "Application"):
|
||||||
super().__init__(master, app, "Hook", modal=True)
|
super().__init__(master, app, "Hook", modal=True)
|
||||||
self.name = tk.StringVar()
|
self.name = tk.StringVar()
|
||||||
self.codetext = None
|
self.codetext = None
|
||||||
|
@ -88,7 +88,7 @@ class HookDialog(Dialog):
|
||||||
|
|
||||||
|
|
||||||
class HooksDialog(Dialog):
|
class HooksDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "Hooks", modal=True)
|
super().__init__(master, app, "Hooks", modal=True)
|
||||||
self.listbox = None
|
self.listbox = None
|
||||||
self.edit_button = None
|
self.edit_button = None
|
||||||
|
|
|
@ -13,7 +13,7 @@ from core.gui.themes import PADX, PADY
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.graph.edges import CanvasEdge
|
from core.gui.graph.graph import CanvasGraph, CanvasEdge
|
||||||
|
|
||||||
|
|
||||||
def get_int(var: tk.StringVar) -> Union[int, None]:
|
def get_int(var: tk.StringVar) -> Union[int, None]:
|
||||||
|
@ -33,7 +33,7 @@ def get_float(var: tk.StringVar) -> Union[float, None]:
|
||||||
|
|
||||||
|
|
||||||
class LinkConfigurationDialog(Dialog):
|
class LinkConfigurationDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application", edge: "CanvasEdge"):
|
def __init__(self, master: "CanvasGraph", app: "Application", edge: "CanvasEdge"):
|
||||||
super().__init__(master, app, "Link Configuration", modal=True)
|
super().__init__(master, app, "Link Configuration", modal=True)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.edge = edge
|
self.edge = edge
|
||||||
|
|
|
@ -17,7 +17,10 @@ MARKER_THICKNESS = [3, 5, 8, 10]
|
||||||
|
|
||||||
class MarkerDialog(Dialog):
|
class MarkerDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, master, app: "Application", initcolor: Optional[str] = "#000000"
|
self,
|
||||||
|
master: "Application",
|
||||||
|
app: "Application",
|
||||||
|
initcolor: Optional[str] = "#000000",
|
||||||
):
|
):
|
||||||
super().__init__(master, app, "marker tool", modal=False)
|
super().__init__(master, app, "marker tool", modal=False)
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
|
@ -17,7 +17,9 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class MobilityConfigDialog(Dialog):
|
class MobilityConfigDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application", canvas_node: "CanvasNode"):
|
def __init__(
|
||||||
|
self, master: "Application", app: "Application", canvas_node: "CanvasNode"
|
||||||
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
master,
|
master,
|
||||||
app,
|
app,
|
||||||
|
|
|
@ -18,7 +18,13 @@ ICON_SIZE = 16
|
||||||
|
|
||||||
|
|
||||||
class MobilityPlayer:
|
class MobilityPlayer:
|
||||||
def __init__(self, master, app: "Application", canvas_node: "CanvasNode", config):
|
def __init__(
|
||||||
|
self,
|
||||||
|
master: "Application",
|
||||||
|
app: "Application",
|
||||||
|
canvas_node: "CanvasNode",
|
||||||
|
config,
|
||||||
|
):
|
||||||
self.master = master
|
self.master = master
|
||||||
self.app = app
|
self.app = app
|
||||||
self.canvas_node = canvas_node
|
self.canvas_node = canvas_node
|
||||||
|
@ -62,7 +68,9 @@ class MobilityPlayer:
|
||||||
|
|
||||||
|
|
||||||
class MobilityPlayerDialog(Dialog):
|
class MobilityPlayerDialog(Dialog):
|
||||||
def __init__(self, master, app, canvas_node, config):
|
def __init__(
|
||||||
|
self, master: Dialog, app: "Application", canvas_node: "CanvasNode", config
|
||||||
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
master, app, f"{canvas_node.core_node.name} Mobility Player", modal=False
|
master, app, f"{canvas_node.core_node.name} Mobility Player", modal=False
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
||||||
from core.gui.graph.node import CanvasNode
|
from core.gui.graph.node import CanvasNode
|
||||||
|
|
||||||
|
|
||||||
def mac_auto(is_auto, entry: ttk.Entry):
|
def mac_auto(is_auto, entry):
|
||||||
logging.info("mac auto clicked")
|
logging.info("mac auto clicked")
|
||||||
if is_auto.get():
|
if is_auto.get():
|
||||||
logging.info("disabling mac")
|
logging.info("disabling mac")
|
||||||
|
@ -38,13 +38,11 @@ class InterfaceData:
|
||||||
|
|
||||||
|
|
||||||
class NodeConfigDialog(Dialog):
|
class NodeConfigDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application", canvas_node: "CanvasNode"):
|
def __init__(
|
||||||
|
self, master: "Application", app: "Application", canvas_node: "CanvasNode"
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
create an instance of node configuration
|
create an instance of node configuration
|
||||||
|
|
||||||
:param master: dialog master
|
|
||||||
:param coretk.app.Application: main app
|
|
||||||
:param coretk.graph.CanvasNode canvas_node: canvas node object
|
|
||||||
"""
|
"""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
master, app, f"{canvas_node.core_node.name} Configuration", modal=True
|
master, app, f"{canvas_node.core_node.name} Configuration", modal=True
|
||||||
|
|
|
@ -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, Any
|
||||||
|
|
||||||
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
|
||||||
|
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class NodeServiceDialog(Dialog):
|
class NodeServiceDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, master, app: "Application", canvas_node: "CanvasNode", services=None
|
self, master: Any, app: "Application", canvas_node: "CanvasNode", services=None
|
||||||
):
|
):
|
||||||
title = f"{canvas_node.core_node.name} Services"
|
title = f"{canvas_node.core_node.name} Services"
|
||||||
super().__init__(master, app, title, modal=True)
|
super().__init__(master, app, title, modal=True)
|
||||||
|
|
|
@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class ObserverDialog(Dialog):
|
class ObserverDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "Observer Widgets", modal=True)
|
super().__init__(master, app, "Observer Widgets", modal=True)
|
||||||
self.observers = None
|
self.observers = None
|
||||||
self.save_button = None
|
self.save_button = None
|
||||||
|
|
|
@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class PreferencesDialog(Dialog):
|
class PreferencesDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "Preferences", modal=True)
|
super().__init__(master, app, "Preferences", modal=True)
|
||||||
preferences = self.app.guiconfig["preferences"]
|
preferences = self.app.guiconfig["preferences"]
|
||||||
self.editor = tk.StringVar(value=preferences["editor"])
|
self.editor = tk.StringVar(value=preferences["editor"])
|
||||||
|
|
|
@ -16,7 +16,7 @@ DEFAULT_PORT = 50051
|
||||||
|
|
||||||
|
|
||||||
class ServersDialog(Dialog):
|
class ServersDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "CORE Servers", modal=True)
|
super().__init__(master, app, "CORE Servers", modal=True)
|
||||||
self.name = tk.StringVar(value=DEFAULT_NAME)
|
self.name = tk.StringVar(value=DEFAULT_NAME)
|
||||||
self.address = tk.StringVar(value=DEFAULT_ADDRESS)
|
self.address = tk.StringVar(value=DEFAULT_ADDRESS)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
"Service configuration dialog"
|
"""
|
||||||
|
Service configuration dialog
|
||||||
|
"""
|
||||||
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, Any, List
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
|
@ -18,7 +20,9 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class ServiceConfigDialog(Dialog):
|
class ServiceConfigDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application", service_name: str, node_id: int):
|
def __init__(
|
||||||
|
self, master: Any, app: "Application", service_name: str, node_id: int
|
||||||
|
):
|
||||||
title = f"{service_name} Service"
|
title = f"{service_name} Service"
|
||||||
super().__init__(master, app, title, modal=True)
|
super().__init__(master, app, title, modal=True)
|
||||||
self.master = master
|
self.master = master
|
||||||
|
@ -229,7 +233,7 @@ class ServiceConfigDialog(Dialog):
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
tab.rowconfigure(i, weight=1)
|
tab.rowconfigure(i, weight=1)
|
||||||
self.notebook.add(tab, text="Startup/Shutdown")
|
self.notebook.add(tab, text="Startup/Shutdown")
|
||||||
|
commands = []
|
||||||
# tab 3
|
# tab 3
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
label_frame = None
|
label_frame = None
|
||||||
|
|
|
@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class SessionOptionsDialog(Dialog):
|
class SessionOptionsDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "Session Options", modal=True)
|
super().__init__(master, app, "Session Options", modal=True)
|
||||||
self.config_frame = None
|
self.config_frame = None
|
||||||
self.config = self.get_config()
|
self.config = self.get_config()
|
||||||
|
|
|
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class SessionsDialog(Dialog):
|
class SessionsDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "Sessions", modal=True)
|
super().__init__(master, app, "Sessions", modal=True)
|
||||||
self.selected = False
|
self.selected = False
|
||||||
self.selected_id = None
|
self.selected_id = None
|
||||||
|
|
|
@ -20,7 +20,7 @@ BORDER_WIDTH = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
|
||||||
|
|
||||||
class ShapeDialog(Dialog):
|
class ShapeDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application", shape: "Shape"):
|
def __init__(self, master: "Application", app: "Application", shape: "Shape"):
|
||||||
if is_draw_shape(shape.shape_type):
|
if is_draw_shape(shape.shape_type):
|
||||||
title = "Add Shape"
|
title = "Add Shape"
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class ThroughputDialog(Dialog):
|
class ThroughputDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application"):
|
def __init__(self, master: "Application", app: "Application"):
|
||||||
super().__init__(master, app, "Throughput Config", modal=False)
|
super().__init__(master, app, "Throughput Config", modal=False)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.canvas = app.canvas
|
self.canvas = app.canvas
|
||||||
|
|
|
@ -18,7 +18,9 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class WlanConfigDialog(Dialog):
|
class WlanConfigDialog(Dialog):
|
||||||
def __init__(self, master, app: "Application", canvas_node: "CanvasNode"):
|
def __init__(
|
||||||
|
self, master: "Application", app: "Application", canvas_node: "CanvasNode"
|
||||||
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
master, app, f"{canvas_node.core_node.name} Wlan Configuration", modal=True
|
master, app, f"{canvas_node.core_node.name} Wlan Configuration", modal=True
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
from tkinter import messagebox
|
from tkinter import messagebox
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import grpc
|
if TYPE_CHECKING:
|
||||||
|
import grpc
|
||||||
|
|
||||||
|
|
||||||
def show_grpc_error(e: grpc.RpcError):
|
def show_grpc_error(e: "grpc.RpcError"):
|
||||||
title = [x.capitalize() for x in e.code().name.lower().split("_")]
|
title = [x.capitalize() for x in e.code().name.lower().split("_")]
|
||||||
title = " ".join(title)
|
title = " ".join(title)
|
||||||
title = f"GRPC {title}"
|
title = f"GRPC {title}"
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter.font import Font
|
from tkinter.font import Font
|
||||||
from typing import Tuple
|
from typing import TYPE_CHECKING, Any, Tuple
|
||||||
|
|
||||||
from core.gui import themes
|
from core.gui import themes
|
||||||
from core.gui.dialogs.linkconfig import LinkConfigurationDialog
|
from core.gui.dialogs.linkconfig import LinkConfigurationDialog
|
||||||
from core.gui.graph import tags
|
from core.gui.graph import tags
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeUtils
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.graph.graph import CanvasGraph
|
||||||
|
|
||||||
TEXT_DISTANCE = 0.30
|
TEXT_DISTANCE = 0.30
|
||||||
EDGE_WIDTH = 3
|
EDGE_WIDTH = 3
|
||||||
EDGE_COLOR = "#ff0000"
|
EDGE_COLOR = "#ff0000"
|
||||||
|
@ -16,11 +19,11 @@ EDGE_COLOR = "#ff0000"
|
||||||
class CanvasWirelessEdge:
|
class CanvasWirelessEdge:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
token: Tuple[int, int],
|
token: Tuple[Any, ...],
|
||||||
position: Tuple[int, int, int, int],
|
position: Tuple[float, float, float, float],
|
||||||
src: int,
|
src: int,
|
||||||
dst: int,
|
dst: int,
|
||||||
canvas,
|
canvas: "CanvasGraph",
|
||||||
):
|
):
|
||||||
self.token = token
|
self.token = token
|
||||||
self.src = src
|
self.src = src
|
||||||
|
@ -39,10 +42,17 @@ class CanvasEdge:
|
||||||
Canvas edge class
|
Canvas edge class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, x1: int, y1: int, x2: int, y2: int, src: int, canvas):
|
def __init__(
|
||||||
|
self,
|
||||||
|
x1: float,
|
||||||
|
y1: float,
|
||||||
|
x2: float,
|
||||||
|
y2: float,
|
||||||
|
src: int,
|
||||||
|
canvas: "CanvasGraph",
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create an instance of canvas edge object
|
Create an instance of canvas edge object
|
||||||
:param coretk.graph.graph.GraphCanvas canvas: canvas object
|
|
||||||
"""
|
"""
|
||||||
self.src = src
|
self.src = src
|
||||||
self.dst = None
|
self.dst = None
|
||||||
|
@ -69,7 +79,7 @@ class CanvasEdge:
|
||||||
self.link = link
|
self.link = link
|
||||||
self.draw_labels()
|
self.draw_labels()
|
||||||
|
|
||||||
def get_coordinates(self) -> [int, int, int, int]:
|
def get_coordinates(self) -> [float, float, float, float]:
|
||||||
x1, y1, x2, y2 = self.canvas.coords(self.id)
|
x1, y1, x2, y2 = self.canvas.coords(self.id)
|
||||||
v1 = x2 - x1
|
v1 = x2 - x1
|
||||||
v2 = y2 - y1
|
v2 = y2 - y1
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from typing import List, Optional
|
from typing import TYPE_CHECKING, List, Optional, Tuple
|
||||||
|
|
||||||
from PIL import Image, ImageTk
|
from PIL import Image, ImageTk
|
||||||
|
|
||||||
|
@ -16,12 +16,15 @@ from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
|
||||||
from core.gui.images import Images
|
from core.gui.images import Images
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeUtils
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.coreclient import CoreClient
|
||||||
|
|
||||||
ZOOM_IN = 1.1
|
ZOOM_IN = 1.1
|
||||||
ZOOM_OUT = 0.9
|
ZOOM_OUT = 0.9
|
||||||
|
|
||||||
|
|
||||||
class CanvasGraph(tk.Canvas):
|
class CanvasGraph(tk.Canvas):
|
||||||
def __init__(self, master, core, width, height):
|
def __init__(self, master, core: "CoreClient", width: int, height: int):
|
||||||
super().__init__(master, highlightthickness=0, background="#cccccc")
|
super().__init__(master, highlightthickness=0, background="#cccccc")
|
||||||
self.app = master
|
self.app = master
|
||||||
self.core = core
|
self.core = core
|
||||||
|
@ -68,7 +71,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.draw_canvas()
|
self.draw_canvas()
|
||||||
self.draw_grid()
|
self.draw_grid()
|
||||||
|
|
||||||
def draw_canvas(self, dimensions=None):
|
def draw_canvas(self, dimensions: Optional[Tuple[int, int]] = None):
|
||||||
if self.grid is not None:
|
if self.grid is not None:
|
||||||
self.delete(self.grid)
|
self.delete(self.grid)
|
||||||
if not dimensions:
|
if not dimensions:
|
||||||
|
@ -185,9 +188,6 @@ class CanvasGraph(tk.Canvas):
|
||||||
def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode):
|
def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode):
|
||||||
"""
|
"""
|
||||||
add a wireless edge between 2 canvas nodes
|
add a wireless edge between 2 canvas nodes
|
||||||
|
|
||||||
:param CanvasNode src: source node
|
|
||||||
:param CanvasNode dst: destination node
|
|
||||||
"""
|
"""
|
||||||
token = tuple(sorted((src.id, dst.id)))
|
token = tuple(sorted((src.id, dst.id)))
|
||||||
x1, y1 = self.coords(src.id)
|
x1, y1 = self.coords(src.id)
|
||||||
|
@ -319,9 +319,6 @@ class CanvasGraph(tk.Canvas):
|
||||||
def click_release(self, event: tk.Event):
|
def click_release(self, event: tk.Event):
|
||||||
"""
|
"""
|
||||||
Draw a node or finish drawing an edge according to the current graph mode
|
Draw a node or finish drawing an edge according to the current graph mode
|
||||||
|
|
||||||
:param event: mouse event
|
|
||||||
:return: nothing
|
|
||||||
"""
|
"""
|
||||||
logging.debug("click release")
|
logging.debug("click release")
|
||||||
x, y = self.canvas_xy(event)
|
x, y = self.canvas_xy(event)
|
||||||
|
@ -760,7 +757,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.redraw_canvas((image.width(), image.height()))
|
self.redraw_canvas((image.width(), image.height()))
|
||||||
self.draw_wallpaper(image)
|
self.draw_wallpaper(image)
|
||||||
|
|
||||||
def redraw_canvas(self, dimensions: Optional[List[int]] = None):
|
def redraw_canvas(self, dimensions: Tuple[int, int] = None):
|
||||||
logging.info("redrawing canvas to dimensions: %s", dimensions)
|
logging.info("redrawing canvas to dimensions: %s", dimensions)
|
||||||
|
|
||||||
# reset scale and move back to original position
|
# reset scale and move back to original position
|
||||||
|
|
|
@ -253,7 +253,7 @@ class CanvasNode:
|
||||||
dialog = NodeServiceDialog(self.app.master, self.app, self)
|
dialog = NodeServiceDialog(self.app.master, self.app, self)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def has_emane_link(self, interface_id: int) -> bool:
|
def has_emane_link(self, interface_id: int) -> core_pb2.Node:
|
||||||
result = None
|
result = None
|
||||||
for edge in self.edges:
|
for edge in self.edges:
|
||||||
if self.id == edge.src:
|
if self.id == edge.src:
|
||||||
|
|
|
@ -1,24 +1,28 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Optional, Union
|
from typing import TYPE_CHECKING, Dict, List, Union
|
||||||
|
|
||||||
from core.gui.dialogs.shapemod import ShapeDialog
|
from core.gui.dialogs.shapemod import ShapeDialog
|
||||||
from core.gui.graph import tags
|
from core.gui.graph import tags
|
||||||
from core.gui.graph.shapeutils import ShapeType
|
from core.gui.graph.shapeutils import ShapeType
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
from core.gui.graph.graph import CanvasGraph
|
||||||
|
|
||||||
|
|
||||||
class AnnotationData:
|
class AnnotationData:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
text: Optional[str] = "",
|
text: str = "",
|
||||||
font: Optional[str] = "Arial",
|
font: str = "Arial",
|
||||||
font_size: Optional[int] = 12,
|
font_size: int = 12,
|
||||||
text_color: Optional[str] = "#000000",
|
text_color: str = "#000000",
|
||||||
fill_color: Optional[str] = "",
|
fill_color: str = "",
|
||||||
border_color: Optional[str] = "#000000",
|
border_color: str = "#000000",
|
||||||
border_width: Optional[int] = 1,
|
border_width: int = 1,
|
||||||
bold: Optional[bool] = False,
|
bold: bool = False,
|
||||||
italic: Optional[bool] = False,
|
italic: bool = False,
|
||||||
underline: Optional[bool] = False,
|
underline: bool = False,
|
||||||
):
|
):
|
||||||
self.text = text
|
self.text = text
|
||||||
self.font = font
|
self.font = font
|
||||||
|
@ -33,7 +37,17 @@ class AnnotationData:
|
||||||
|
|
||||||
|
|
||||||
class Shape:
|
class Shape:
|
||||||
def __init__(self, app, canvas, shape_type, x1, y1, x2=None, y2=None, data=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
app: "Application",
|
||||||
|
canvas: "CanvasGraph",
|
||||||
|
shape_type: ShapeType,
|
||||||
|
x1: float,
|
||||||
|
y1: float,
|
||||||
|
x2: float = None,
|
||||||
|
y2: float = None,
|
||||||
|
data: AnnotationData = None,
|
||||||
|
):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.canvas = canvas
|
self.canvas = canvas
|
||||||
self.shape_type = shape_type
|
self.shape_type = shape_type
|
||||||
|
@ -152,7 +166,7 @@ class Shape:
|
||||||
self.canvas.delete(self.id)
|
self.canvas.delete(self.id)
|
||||||
self.canvas.delete(self.text_id)
|
self.canvas.delete(self.text_id)
|
||||||
|
|
||||||
def metadata(self):
|
def metadata(self) -> Dict[str, Union[str, int, bool]]:
|
||||||
coords = self.canvas.coords(self.id)
|
coords = self.canvas.coords(self.id)
|
||||||
# update coords to actual positions
|
# update coords to actual positions
|
||||||
if len(coords) == 4:
|
if len(coords) == 4:
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import Optional
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from core.gui.themes import Styles
|
from core.gui.themes import Styles
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.graph.graph import CanvasGraph
|
||||||
|
|
||||||
|
|
||||||
class CanvasTooltip:
|
class CanvasTooltip:
|
||||||
"""
|
"""
|
||||||
|
@ -20,7 +23,14 @@ class CanvasTooltip:
|
||||||
Alberto Vassena on 2016.12.10.
|
Alberto Vassena on 2016.12.10.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, canvas, *, pad=(5, 3, 5, 3), waittime=400, wraplength=600):
|
def __init__(
|
||||||
|
self,
|
||||||
|
canvas: "CanvasGraph",
|
||||||
|
*,
|
||||||
|
pad=(5, 3, 5, 3),
|
||||||
|
waittime: int = 400,
|
||||||
|
wraplength: int = 600
|
||||||
|
):
|
||||||
# in miliseconds, originally 500
|
# in miliseconds, originally 500
|
||||||
self.waittime = waittime
|
self.waittime = waittime
|
||||||
# in pixels, originally 180
|
# in pixels, originally 180
|
||||||
|
@ -31,10 +41,10 @@ class CanvasTooltip:
|
||||||
self.id = None
|
self.id = None
|
||||||
self.tw = None
|
self.tw = None
|
||||||
|
|
||||||
def on_enter(self, event: Optional[tk.Event] = None):
|
def on_enter(self, event: tk.Event = None):
|
||||||
self.schedule()
|
self.schedule()
|
||||||
|
|
||||||
def on_leave(self, event: Optional[tk.Event] = None):
|
def on_leave(self, event: tk.Event = None):
|
||||||
self.unschedule()
|
self.unschedule()
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
|
@ -48,7 +58,7 @@ class CanvasTooltip:
|
||||||
if id_:
|
if id_:
|
||||||
self.canvas.after_cancel(id_)
|
self.canvas.after_cancel(id_)
|
||||||
|
|
||||||
def show(self, event: Optional[tk.Event] = None):
|
def show(self, event: tk.Event = None):
|
||||||
def tip_pos_calculator(canvas, label, *, tip_delta=(10, 5), pad=(5, 3, 5, 3)):
|
def tip_pos_calculator(canvas, label, *, tip_delta=(10, 5), pad=(5, 3, 5, 3)):
|
||||||
c = canvas
|
c = canvas
|
||||||
s_width, s_height = c.winfo_screenwidth(), c.winfo_screenheight()
|
s_width, s_height = c.winfo_screenwidth(), c.winfo_screenheight()
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
from typing import Optional, Set
|
from typing import TYPE_CHECKING, Set, Union
|
||||||
|
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
|
|
||||||
from core.gui.graph.node import CanvasNode
|
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeUtils
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
from core.api.grpc import core_pb2
|
||||||
|
from core.gui.graph.node import CanvasNode
|
||||||
|
|
||||||
|
|
||||||
def random_mac():
|
def random_mac():
|
||||||
return ("{:02x}" * 6).format(*[random.randrange(256) for _ in range(6)])
|
return ("{:02x}" * 6).format(*[random.randrange(256) for _ in range(6)])
|
||||||
|
|
||||||
|
|
||||||
class InterfaceManager:
|
class InterfaceManager:
|
||||||
def __init__(
|
def __init__(self, app: "Application", address: str = "10.0.0.0", mask: int = 24):
|
||||||
self, app, address: Optional[str] = "10.0.0.0", mask: Optional[int] = 24
|
|
||||||
):
|
|
||||||
self.app = app
|
self.app = app
|
||||||
self.mask = mask
|
self.mask = mask
|
||||||
self.base_prefix = max(self.mask - 8, 0)
|
self.base_prefix = max(self.mask - 8, 0)
|
||||||
|
@ -49,11 +51,11 @@ class InterfaceManager:
|
||||||
return str(ip4), str(ip6), prefix
|
return str(ip4), str(ip6), prefix
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_subnet(cls, interface):
|
def get_subnet(cls, interface: "core_pb2.Interface") -> IPNetwork:
|
||||||
return IPNetwork(f"{interface.ip4}/{interface.ip4mask}").cidr
|
return IPNetwork(f"{interface.ip4}/{interface.ip4mask}").cidr
|
||||||
|
|
||||||
def determine_subnet(
|
def determine_subnet(
|
||||||
self, canvas_src_node: CanvasNode, canvas_dst_node: CanvasNode
|
self, canvas_src_node: "CanvasNode", canvas_dst_node: "CanvasNode"
|
||||||
):
|
):
|
||||||
src_node = canvas_src_node.core_node
|
src_node = canvas_src_node.core_node
|
||||||
dst_node = canvas_dst_node.core_node
|
dst_node = canvas_dst_node.core_node
|
||||||
|
@ -76,7 +78,9 @@ class InterfaceManager:
|
||||||
else:
|
else:
|
||||||
logging.info("ignoring subnet change for link between network nodes")
|
logging.info("ignoring subnet change for link between network nodes")
|
||||||
|
|
||||||
def find_subnet(self, canvas_node: CanvasNode, visited: Optional[Set[int]] = None):
|
def find_subnet(
|
||||||
|
self, canvas_node: "CanvasNode", visited: Set[int] = None
|
||||||
|
) -> Union[IPNetwork, None]:
|
||||||
logging.info("finding subnet for node: %s", canvas_node.core_node.name)
|
logging.info("finding subnet for node: %s", canvas_node.core_node.name)
|
||||||
canvas = self.app.canvas
|
canvas = self.app.canvas
|
||||||
cidr = None
|
cidr = None
|
||||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from tkinter import filedialog, messagebox
|
from tkinter import filedialog, messagebox
|
||||||
from typing import Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from core.gui.appconfig import XMLS_PATH
|
from core.gui.appconfig import XMLS_PATH
|
||||||
from core.gui.dialogs.about import AboutDialog
|
from core.gui.dialogs.about import AboutDialog
|
||||||
|
@ -21,13 +21,16 @@ from core.gui.dialogs.sessions import SessionsDialog
|
||||||
from core.gui.dialogs.throughput import ThroughputDialog
|
from core.gui.dialogs.throughput import ThroughputDialog
|
||||||
from core.gui.task import BackgroundTask
|
from core.gui.task import BackgroundTask
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
|
||||||
|
|
||||||
class MenuAction:
|
class MenuAction:
|
||||||
"""
|
"""
|
||||||
Actions performed when choosing menu items
|
Actions performed when choosing menu items
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app, master):
|
def __init__(self, app: "Application", master: tk.Tk):
|
||||||
self.master = master
|
self.master = master
|
||||||
self.app = app
|
self.app = app
|
||||||
self.canvas = app.canvas
|
self.canvas = app.canvas
|
||||||
|
@ -39,11 +42,9 @@ class MenuAction:
|
||||||
# if quitapp:
|
# if quitapp:
|
||||||
# self.app.quit()
|
# self.app.quit()
|
||||||
|
|
||||||
def prompt_save_running_session(self, quitapp: Optional[bool] = False):
|
def prompt_save_running_session(self, quitapp: bool = False):
|
||||||
"""
|
"""
|
||||||
Prompt use to stop running session before application is closed
|
Prompt use to stop running session before application is closed
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
"""
|
||||||
result = True
|
result = True
|
||||||
if self.app.core.is_runtime():
|
if self.app.core.is_runtime():
|
||||||
|
@ -58,15 +59,13 @@ class MenuAction:
|
||||||
elif quitapp:
|
elif quitapp:
|
||||||
self.app.quit()
|
self.app.quit()
|
||||||
|
|
||||||
def on_quit(self, event: Optional[tk.Event] = None):
|
def on_quit(self, event: tk.Event = None):
|
||||||
"""
|
"""
|
||||||
Prompt user whether so save running session, and then close the application
|
Prompt user whether so save running session, and then close the application
|
||||||
|
|
||||||
:return: nothing
|
|
||||||
"""
|
"""
|
||||||
self.prompt_save_running_session(quitapp=True)
|
self.prompt_save_running_session(quitapp=True)
|
||||||
|
|
||||||
def file_save_as_xml(self, event: Optional[tk.Event] = None):
|
def file_save_as_xml(self, event: tk.Event = None):
|
||||||
logging.info("menuaction.py file_save_as_xml()")
|
logging.info("menuaction.py file_save_as_xml()")
|
||||||
file_path = filedialog.asksaveasfilename(
|
file_path = filedialog.asksaveasfilename(
|
||||||
initialdir=str(XMLS_PATH),
|
initialdir=str(XMLS_PATH),
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import core.gui.menuaction as action
|
import core.gui.menuaction as action
|
||||||
from core.gui.coreclient import OBSERVERS
|
from core.gui.coreclient import OBSERVERS
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
|
||||||
|
|
||||||
class Menubar(tk.Menu):
|
class Menubar(tk.Menu):
|
||||||
"""
|
"""
|
||||||
Core menubar
|
Core menubar
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, master, app, cnf={}, **kwargs):
|
def __init__(self, master: tk.Tk, app: "Application", cnf={}, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create a CoreMenubar instance
|
Create a CoreMenubar instance
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -3,13 +3,17 @@ status bar
|
||||||
"""
|
"""
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from core.gui.dialogs.alerts import AlertsDialog
|
from core.gui.dialogs.alerts import AlertsDialog
|
||||||
from core.gui.themes import Styles
|
from core.gui.themes import Styles
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
|
||||||
|
|
||||||
class StatusBar(ttk.Frame):
|
class StatusBar(ttk.Frame):
|
||||||
def __init__(self, master: tk.Widget, app, **kwargs):
|
def __init__(self, master: tk.Widget, app: "Application", **kwargs):
|
||||||
super().__init__(master, **kwargs)
|
super().__init__(master, **kwargs)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.status = None
|
self.status = None
|
||||||
|
|
|
@ -159,7 +159,7 @@ def style_menu(widget: tk.Widget):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def style_listbox(widget: ttk.Widget):
|
def style_listbox(widget: tk.Widget):
|
||||||
style = ttk.Style()
|
style = ttk.Style()
|
||||||
bg = style.lookup(".", "background")
|
bg = style.lookup(".", "background")
|
||||||
fg = style.lookup(".", "foreground")
|
fg = style.lookup(".", "foreground")
|
||||||
|
|
|
@ -4,9 +4,7 @@ import tkinter as tk
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from tkinter import messagebox, ttk
|
from tkinter import messagebox, ttk
|
||||||
from tkinter.font import Font
|
from tkinter.font import Font
|
||||||
from typing import Callable
|
from typing import TYPE_CHECKING, Callable
|
||||||
|
|
||||||
from PIL import ImageTk
|
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import core_pb2
|
||||||
from core.gui.dialogs.customnodes import CustomNodesDialog
|
from core.gui.dialogs.customnodes import CustomNodesDialog
|
||||||
|
@ -14,11 +12,15 @@ from core.gui.dialogs.marker import MarkerDialog
|
||||||
from core.gui.graph.enums import GraphMode
|
from core.gui.graph.enums import GraphMode
|
||||||
from core.gui.graph.shapeutils import ShapeType, is_marker
|
from core.gui.graph.shapeutils import ShapeType, is_marker
|
||||||
from core.gui.images import ImageEnum, Images
|
from core.gui.images import ImageEnum, Images
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeDraw, NodeUtils
|
||||||
from core.gui.task import BackgroundTask
|
from core.gui.task import BackgroundTask
|
||||||
from core.gui.themes import Styles
|
from core.gui.themes import Styles
|
||||||
from core.gui.tooltip import Tooltip
|
from core.gui.tooltip import Tooltip
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
from PIL import ImageTk
|
||||||
|
|
||||||
TOOLBAR_SIZE = 32
|
TOOLBAR_SIZE = 32
|
||||||
PICKER_SIZE = 24
|
PICKER_SIZE = 24
|
||||||
|
|
||||||
|
@ -32,11 +34,9 @@ class Toolbar(ttk.Frame):
|
||||||
Core toolbar class
|
Core toolbar class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, master, app, **kwargs):
|
def __init__(self, master: "Application", app: "Application", **kwargs):
|
||||||
"""
|
"""
|
||||||
Create a CoreToolbar instance
|
Create a CoreToolbar instance
|
||||||
|
|
||||||
:param tkinter.Frame edit_frame: edit frame
|
|
||||||
"""
|
"""
|
||||||
super().__init__(master, **kwargs)
|
super().__init__(master, **kwargs)
|
||||||
self.app = app
|
self.app = app
|
||||||
|
@ -200,7 +200,7 @@ class Toolbar(ttk.Frame):
|
||||||
self.app.unbind_all("<ButtonRelease-1>")
|
self.app.unbind_all("<ButtonRelease-1>")
|
||||||
|
|
||||||
def create_picker_button(
|
def create_picker_button(
|
||||||
self, image: ImageTk.PhotoImage, func: Callable, frame: ttk.Frame, label: str
|
self, image: "ImageTk.PhotoImage", func: Callable, frame: ttk.Frame, label: str
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Create button and put it on the frame
|
Create button and put it on the frame
|
||||||
|
@ -218,7 +218,11 @@ class Toolbar(ttk.Frame):
|
||||||
button.grid(pady=1)
|
button.grid(pady=1)
|
||||||
|
|
||||||
def create_button(
|
def create_button(
|
||||||
self, frame: ttk.Frame, image: ImageTk.PhotoImage, func: Callable, tooltip: str
|
self,
|
||||||
|
frame: ttk.Frame,
|
||||||
|
image: "ImageTk.PhotoImage",
|
||||||
|
func: Callable,
|
||||||
|
tooltip: str,
|
||||||
):
|
):
|
||||||
button = ttk.Button(frame, image=image, command=func)
|
button = ttk.Button(frame, image=image, command=func)
|
||||||
button.image = image
|
button.image = image
|
||||||
|
@ -280,7 +284,7 @@ class Toolbar(ttk.Frame):
|
||||||
dialog = CustomNodesDialog(self.app, self.app)
|
dialog = CustomNodesDialog(self.app, self.app)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def update_button(self, button: ttk.Button, image, node_draw):
|
def update_button(self, button: ttk.Button, image: "ImageTk", node_draw: NodeDraw):
|
||||||
logging.info("update button(%s): %s", button, node_draw)
|
logging.info("update button(%s): %s", button, node_draw)
|
||||||
self.hide_pickers()
|
self.hide_pickers()
|
||||||
button.configure(image=image)
|
button.configure(image=image)
|
||||||
|
@ -429,7 +433,7 @@ class Toolbar(ttk.Frame):
|
||||||
if not response.result:
|
if not response.result:
|
||||||
messagebox.showerror("Stop Error", "Errors stopping session")
|
messagebox.showerror("Stop Error", "Errors stopping session")
|
||||||
|
|
||||||
def update_annotation(self, image: ImageTk.PhotoImage, shape_type: str):
|
def update_annotation(self, image: "ImageTk.PhotoImage", shape_type: ShapeType):
|
||||||
logging.info("clicked annotation: ")
|
logging.info("clicked annotation: ")
|
||||||
self.hide_pickers()
|
self.hide_pickers()
|
||||||
self.annotation_button.configure(image=image)
|
self.annotation_button.configure(image=image)
|
||||||
|
@ -439,7 +443,7 @@ class Toolbar(ttk.Frame):
|
||||||
if is_marker(shape_type):
|
if is_marker(shape_type):
|
||||||
if self.marker_tool:
|
if self.marker_tool:
|
||||||
self.marker_tool.destroy()
|
self.marker_tool.destroy()
|
||||||
self.marker_tool = MarkerDialog(self.master, self.app)
|
self.marker_tool = MarkerDialog(self.app, self.app)
|
||||||
self.marker_tool.show()
|
self.marker_tool.show()
|
||||||
|
|
||||||
def click_run_button(self):
|
def click_run_button(self):
|
||||||
|
@ -455,7 +459,7 @@ class Toolbar(ttk.Frame):
|
||||||
self.app.canvas.annotation_type = ShapeType.MARKER
|
self.app.canvas.annotation_type = ShapeType.MARKER
|
||||||
if self.marker_tool:
|
if self.marker_tool:
|
||||||
self.marker_tool.destroy()
|
self.marker_tool.destroy()
|
||||||
self.marker_tool = MarkerDialog(self.master, self.app)
|
self.marker_tool = MarkerDialog(self.app, self.app)
|
||||||
self.marker_tool.show()
|
self.marker_tool.show()
|
||||||
|
|
||||||
def click_two_node_button(self):
|
def click_two_node_button(self):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from core.gui.themes import Styles
|
from core.gui.themes import Styles
|
||||||
|
|
||||||
|
@ -19,10 +18,10 @@ class Tooltip(object):
|
||||||
self.id = None
|
self.id = None
|
||||||
self.tw = None
|
self.tw = None
|
||||||
|
|
||||||
def on_enter(self, event: Optional[tk.Event] = None):
|
def on_enter(self, event: tk.Event = None):
|
||||||
self.schedule()
|
self.schedule()
|
||||||
|
|
||||||
def on_leave(self, event: Optional[tk.Event] = None):
|
def on_leave(self, event: tk.Event = None):
|
||||||
self.unschedule()
|
self.unschedule()
|
||||||
self.close(event)
|
self.close(event)
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ class Tooltip(object):
|
||||||
if id_:
|
if id_:
|
||||||
self.widget.after_cancel(id_)
|
self.widget.after_cancel(id_)
|
||||||
|
|
||||||
def enter(self, event: Optional[tk.Event] = None):
|
def enter(self, event: tk.Event = None):
|
||||||
x, y, cx, cy = self.widget.bbox("insert")
|
x, y, cx, cy = self.widget.bbox("insert")
|
||||||
x += self.widget.winfo_rootx()
|
x += self.widget.winfo_rootx()
|
||||||
y += self.widget.winfo_rooty() + 32
|
y += self.widget.winfo_rooty() + 32
|
||||||
|
@ -51,6 +50,6 @@ class Tooltip(object):
|
||||||
label = ttk.Label(frame, text=self.text, style=Styles.tooltip)
|
label = ttk.Label(frame, text=self.text, style=Styles.tooltip)
|
||||||
label.grid()
|
label.grid()
|
||||||
|
|
||||||
def close(self, event: Optional[tk.Event] = None):
|
def close(self, event: tk.Event = None):
|
||||||
if self.tw:
|
if self.tw:
|
||||||
self.tw.destroy()
|
self.tw.destroy()
|
||||||
|
|
|
@ -3,13 +3,17 @@ input validation
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
|
||||||
|
|
||||||
class InputValidation:
|
class InputValidation:
|
||||||
def __init__(self, app):
|
def __init__(self, app: "Application"):
|
||||||
self.master = app.master
|
self.master = app.master
|
||||||
self.positive_int = None
|
self.positive_int = None
|
||||||
self.positive_float = None
|
self.positive_float = None
|
||||||
|
|
|
@ -3,11 +3,16 @@ import tkinter as tk
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from pathlib import PosixPath
|
from pathlib import PosixPath
|
||||||
from tkinter import filedialog, font, ttk
|
from tkinter import filedialog, font, ttk
|
||||||
|
from typing import TYPE_CHECKING, Dict, Optional
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import core_pb2
|
||||||
from core.gui import themes
|
from core.gui import themes
|
||||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
from core.gui.dialogs.dialog import Dialog
|
||||||
|
|
||||||
INT_TYPES = {
|
INT_TYPES = {
|
||||||
core_pb2.ConfigOptionType.UINT8,
|
core_pb2.ConfigOptionType.UINT8,
|
||||||
core_pb2.ConfigOptionType.UINT16,
|
core_pb2.ConfigOptionType.UINT16,
|
||||||
|
@ -27,7 +32,7 @@ def file_button_click(value: tk.StringVar, parent: tk.Widget):
|
||||||
|
|
||||||
|
|
||||||
class FrameScroll(ttk.Frame):
|
class FrameScroll(ttk.Frame):
|
||||||
def __init__(self, master, app, _cls=ttk.Frame, **kw):
|
def __init__(self, master: ttk.Widget, app: "Application", _cls=ttk.Frame, **kw):
|
||||||
super().__init__(master, **kw)
|
super().__init__(master, **kw)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.rowconfigure(0, weight=1)
|
self.rowconfigure(0, weight=1)
|
||||||
|
@ -65,7 +70,13 @@ class FrameScroll(ttk.Frame):
|
||||||
|
|
||||||
|
|
||||||
class ConfigFrame(ttk.Notebook):
|
class ConfigFrame(ttk.Notebook):
|
||||||
def __init__(self, master, app, config, **kw):
|
def __init__(
|
||||||
|
self,
|
||||||
|
master: ttk.Widget,
|
||||||
|
app: "Application",
|
||||||
|
config: Dict[str, core_pb2.ConfigOption],
|
||||||
|
**kw
|
||||||
|
):
|
||||||
super().__init__(master, **kw)
|
super().__init__(master, **kw)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.config = config
|
self.config = config
|
||||||
|
@ -175,7 +186,7 @@ class ConfigFrame(ttk.Notebook):
|
||||||
|
|
||||||
|
|
||||||
class ListboxScroll(ttk.Frame):
|
class ListboxScroll(ttk.Frame):
|
||||||
def __init__(self, master=None, **kw):
|
def __init__(self, master: Optional[ttk.Widget] = None, **kw):
|
||||||
super().__init__(master, **kw)
|
super().__init__(master, **kw)
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1)
|
||||||
self.rowconfigure(0, weight=1)
|
self.rowconfigure(0, weight=1)
|
||||||
|
@ -190,7 +201,7 @@ class ListboxScroll(ttk.Frame):
|
||||||
|
|
||||||
|
|
||||||
class CheckboxList(FrameScroll):
|
class CheckboxList(FrameScroll):
|
||||||
def __init__(self, master, app, clicked=None, **kw):
|
def __init__(self, master: ttk.Widget, app: "Application", clicked=None, **kw):
|
||||||
super().__init__(master, app, **kw)
|
super().__init__(master, app, **kw)
|
||||||
self.clicked = clicked
|
self.clicked = clicked
|
||||||
self.frame.columnconfigure(0, weight=1)
|
self.frame.columnconfigure(0, weight=1)
|
||||||
|
@ -208,7 +219,7 @@ class CodeFont(font.Font):
|
||||||
|
|
||||||
|
|
||||||
class CodeText(ttk.Frame):
|
class CodeText(ttk.Frame):
|
||||||
def __init__(self, master, **kwargs):
|
def __init__(self, master: ttk.Widget, **kwargs):
|
||||||
super().__init__(master, **kwargs)
|
super().__init__(master, **kwargs)
|
||||||
self.rowconfigure(0, weight=1)
|
self.rowconfigure(0, weight=1)
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1)
|
||||||
|
@ -232,14 +243,14 @@ class CodeText(ttk.Frame):
|
||||||
|
|
||||||
|
|
||||||
class Spinbox(ttk.Entry):
|
class Spinbox(ttk.Entry):
|
||||||
def __init__(self, master=None, **kwargs):
|
def __init__(self, master: Optional[ttk.Widget] = None, **kwargs):
|
||||||
super().__init__(master, "ttk::spinbox", **kwargs)
|
super().__init__(master, "ttk::spinbox", **kwargs)
|
||||||
|
|
||||||
def set(self, value):
|
def set(self, value):
|
||||||
self.tk.call(self._w, "set", value)
|
self.tk.call(self._w, "set", value)
|
||||||
|
|
||||||
|
|
||||||
def image_chooser(parent: tk.Widget, path: PosixPath):
|
def image_chooser(parent: "Dialog", path: PosixPath):
|
||||||
return filedialog.askopenfilename(
|
return filedialog.askopenfilename(
|
||||||
parent=parent,
|
parent=parent,
|
||||||
initialdir=str(path),
|
initialdir=str(path),
|
||||||
|
|
Loading…
Reference in a new issue