pygui: added support for a details pane, can be toggled on/off, can be used to quickly view details for nodes or links

This commit is contained in:
Blake Harnden 2020-06-25 10:35:01 -07:00
parent bb2ceaf993
commit f582306bb9
12 changed files with 226 additions and 17 deletions

View file

View file

@ -0,0 +1,36 @@
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from core.gui.themes import FRAME_PAD, PADX, PADY
if TYPE_CHECKING:
from core.gui.app import Application
class InfoFrameBase(ttk.Frame):
def __init__(self, master: tk.BaseWidget, app: "Application") -> None:
super().__init__(master, padding=FRAME_PAD)
self.app: "Application" = app
def draw(self) -> None:
raise NotImplementedError
class DetailsFrame(ttk.Frame):
def __init__(self, master: tk.BaseWidget) -> None:
super().__init__(master)
self.columnconfigure(1, weight=1)
self.row = 0
def add_detail(self, label: str, value: str) -> None:
label = ttk.Label(self, text=label, anchor=tk.W)
label.grid(row=self.row, sticky=tk.EW, column=0, padx=PADX)
label = ttk.Label(self, text=value, anchor=tk.W, state=tk.DISABLED)
label.grid(row=self.row, sticky=tk.EW, column=1)
self.row += 1
def add_separator(self) -> None:
separator = ttk.Separator(self)
separator.grid(row=self.row, sticky=tk.EW, columnspan=2, pady=PADY)
self.row += 1

View file

@ -0,0 +1,19 @@
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from core.gui.frames.base import InfoFrameBase
if TYPE_CHECKING:
from core.gui.app import Application
class DefaultInfoFrame(InfoFrameBase):
def __init__(self, master: tk.BaseWidget, app: "Application") -> None:
super().__init__(master, app)
def draw(self) -> None:
label = ttk.Label(self, text="Click a Node/Link", anchor=tk.CENTER)
label.grid(sticky=tk.EW)
label = ttk.Label(self, text="to see details", anchor=tk.CENTER)
label.grid(sticky=tk.EW)

View file

@ -0,0 +1,58 @@
import tkinter as tk
from typing import TYPE_CHECKING
from core.gui.frames.base import DetailsFrame, InfoFrameBase
from core.gui.utils import bandwidth_text
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.edges import CanvasEdge
class EdgeInfoFrame(InfoFrameBase):
def __init__(
self, master: tk.BaseWidget, app: "Application", edge: "CanvasEdge"
) -> None:
super().__init__(master, app)
self.edge: "CanvasEdge" = edge
def draw(self) -> None:
self.columnconfigure(0, weight=1)
link = self.edge.link
options = link.options
src_canvas_node = self.app.core.canvas_nodes[link.node1_id]
src_node = src_canvas_node.core_node
dst_canvas_node = self.app.core.canvas_nodes[link.node2_id]
dst_node = dst_canvas_node.core_node
frame = DetailsFrame(self)
frame.grid(sticky="ew")
frame.add_detail("Source", src_node.name)
iface1 = link.iface1
if iface1:
mac = iface1.mac if iface1.mac else "auto"
frame.add_detail("MAC", mac)
ip4 = f"{iface1.ip4}/{iface1.ip4_mask}" if iface1.ip4 else ""
frame.add_detail("IP4", ip4)
ip6 = f"{iface1.ip6}/{iface1.ip6_mask}" if iface1.ip6 else ""
frame.add_detail("IP6", ip6)
frame.add_separator()
frame.add_detail("Destination", dst_node.name)
iface2 = link.iface2
if iface2:
mac = iface2.mac if iface2.mac else "auto"
frame.add_detail("MAC", mac)
ip4 = f"{iface2.ip4}/{iface2.ip4_mask}" if iface2.ip4 else ""
frame.add_detail("IP4", ip4)
ip6 = f"{iface2.ip6}/{iface2.ip6_mask}" if iface2.ip6 else ""
frame.add_detail("IP6", ip6)
if link.HasField("options"):
frame.add_separator()
bandwidth = bandwidth_text(options.bandwidth)
frame.add_detail("Bandwidth", bandwidth)
frame.add_detail("Delay", f"{options.delay} us")
frame.add_detail("Jitter", f"\u00B1{options.jitter} us")
frame.add_detail("Loss", f"{options.loss}%")
frame.add_detail("Duplicate", f"{options.dup}%")

View file

@ -0,0 +1,33 @@
from typing import TYPE_CHECKING
from core.api.grpc.core_pb2 import NodeType
from core.gui.frames.base import DetailsFrame, InfoFrameBase
from core.gui.nodeutils import NodeUtils
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.graph.node import CanvasNode
class NodeInfoFrame(InfoFrameBase):
def __init__(self, master, app: "Application", canvas_node: "CanvasNode") -> None:
super().__init__(master, app)
self.canvas_node: "CanvasNode" = canvas_node
def draw(self) -> None:
self.columnconfigure(0, weight=1)
node = self.canvas_node.core_node
frame = DetailsFrame(self)
frame.grid(sticky="ew")
frame.add_detail("ID", node.id)
frame.add_detail("Name", node.name)
if NodeUtils.is_model_node(node.type):
frame.add_detail("Type", node.model)
if node.type == NodeType.EMANE:
emane = node.emane.split("_")[1:]
frame.add_detail("EMANE", emane)
if NodeUtils.is_image_node(node.type):
frame.add_detail("Image", node.image)
if NodeUtils.is_container_node(node.type):
server = node.server if node.server else "localhost"
frame.add_detail("Server", server)