pygui: replaced hook with wrapped hook class, fixed hook dialog edit

This commit is contained in:
Blake Harnden 2020-07-24 22:00:38 -07:00
parent ba3a247495
commit 154fa8b77d
3 changed files with 159 additions and 17 deletions

View file

@ -19,7 +19,6 @@ from core.api.grpc.core_pb2 import (
CpuUsageEvent, CpuUsageEvent,
Event, Event,
ExceptionEvent, ExceptionEvent,
Hook,
Interface, Interface,
Link, Link,
LinkEvent, LinkEvent,
@ -51,6 +50,7 @@ from core.gui.graph.shape import AnnotationData, Shape
from core.gui.graph.shapeutils import ShapeType 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
from core.gui.wrappers import Hook
if TYPE_CHECKING: if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
@ -332,7 +332,8 @@ class CoreClient:
# get hooks # get hooks
response = self.client.get_hooks(self.session_id) response = self.client.get_hooks(self.session_id)
for hook in response.hooks: for hook_proto in response.hooks:
hook = Hook.from_proto(hook_proto)
self.hooks[hook.file] = hook self.hooks[hook.file] = hook
# get emane config # get emane config
@ -570,7 +571,7 @@ class CoreClient:
wlan_configs = self.get_wlan_configs_proto() wlan_configs = self.get_wlan_configs_proto()
mobility_configs = self.get_mobility_configs_proto() mobility_configs = self.get_mobility_configs_proto()
emane_model_configs = self.get_emane_model_configs_proto() emane_model_configs = self.get_emane_model_configs_proto()
hooks = list(self.hooks.values()) hooks = [x.to_proto() for x in self.hooks.values()]
service_configs = self.get_service_configs_proto() service_configs = self.get_service_configs_proto()
file_configs = self.get_service_file_configs_proto() file_configs = self.get_service_file_configs_proto()
asymmetric_links = [ asymmetric_links = [
@ -823,7 +824,9 @@ class CoreClient:
config_proto.data, config_proto.data,
) )
for hook in self.hooks.values(): for hook in self.hooks.values():
self.client.add_hook(self.session_id, hook.state, hook.file, hook.data) self.client.add_hook(
self.session_id, hook.state.value, hook.file, hook.data
)
for config_proto in self.get_emane_model_configs_proto(): for config_proto in self.get_emane_model_configs_proto():
self.client.set_emane_model_config( self.client.set_emane_model_config(
self.session_id, self.session_id,

View file

@ -2,10 +2,10 @@ import tkinter as tk
from tkinter import ttk from tkinter import ttk
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Optional
from core.api.grpc import core_pb2
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, ListboxScroll from core.gui.widgets import CodeText, ListboxScroll
from core.gui.wrappers import Hook, SessionState
if TYPE_CHECKING: if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
@ -16,8 +16,9 @@ class HookDialog(Dialog):
super().__init__(app, "Hook", master=master) super().__init__(app, "Hook", master=master)
self.name: tk.StringVar = tk.StringVar() self.name: tk.StringVar = tk.StringVar()
self.codetext: Optional[CodeText] = None self.codetext: Optional[CodeText] = None
self.hook: core_pb2.Hook = core_pb2.Hook() self.hook: Optional[Hook] = None
self.state: tk.StringVar = tk.StringVar() self.state: tk.StringVar = tk.StringVar()
self.editing: bool = False
self.draw() self.draw()
def draw(self) -> None: def draw(self) -> None:
@ -34,8 +35,8 @@ class HookDialog(Dialog):
label.grid(row=0, column=0, sticky="ew", padx=PADX) label.grid(row=0, column=0, sticky="ew", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.name) entry = ttk.Entry(frame, textvariable=self.name)
entry.grid(row=0, column=1, sticky="ew", padx=PADX) entry.grid(row=0, column=1, sticky="ew", padx=PADX)
values = tuple(x for x in core_pb2.SessionState.Enum.keys() if x != "NONE") values = tuple(x.name for x in SessionState)
initial_state = core_pb2.SessionState.Enum.Name(core_pb2.SessionState.RUNTIME) initial_state = SessionState.RUNTIME.name
self.state.set(initial_state) self.state.set(initial_state)
self.name.set(f"{initial_state.lower()}_hook.sh") self.name.set(f"{initial_state.lower()}_hook.sh")
combobox = ttk.Combobox( combobox = ttk.Combobox(
@ -67,23 +68,30 @@ class HookDialog(Dialog):
button.grid(row=0, column=1, sticky="ew") button.grid(row=0, column=1, sticky="ew")
def state_change(self, event: tk.Event) -> None: def state_change(self, event: tk.Event) -> None:
if self.editing:
return
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) -> None: def set(self, hook: Hook) -> None:
self.editing = True
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)
self.codetext.text.insert(tk.END, hook.data) self.codetext.text.insert(tk.END, hook.data)
state_name = core_pb2.SessionState.Enum.Name(hook.state) state_name = hook.state.name
self.state.set(state_name) self.state.set(state_name)
def save(self) -> None: 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 = SessionState[self.state.get()]
self.hook.file = self.name.get() file_name = self.name.get()
self.hook.data = data if self.editing:
self.hook.state = state_value self.hook.state = state
self.hook.file = file_name
self.hook.data = data
else:
self.hook = Hook(state=state, file=file_name, data=data)
self.destroy() self.destroy()
@ -94,6 +102,7 @@ class HooksDialog(Dialog):
self.edit_button: Optional[ttk.Button] = None self.edit_button: Optional[ttk.Button] = None
self.delete_button: Optional[ttk.Button] = None self.delete_button: Optional[ttk.Button] = None
self.selected: Optional[str] = None self.selected: Optional[str] = None
self.selected_index: Optional[int] = None
self.draw() self.draw()
def draw(self) -> None: def draw(self) -> None:
@ -133,10 +142,13 @@ class HooksDialog(Dialog):
self.listbox.insert(tk.END, hook.file) self.listbox.insert(tk.END, hook.file)
def click_edit(self) -> None: def click_edit(self) -> None:
hook = self.app.core.hooks[self.selected] hook = self.app.core.hooks.pop(self.selected)
dialog = HookDialog(self, self.app) dialog = HookDialog(self, self.app)
dialog.set(hook) dialog.set(hook)
dialog.show() dialog.show()
self.app.core.hooks[hook.file] = hook
self.listbox.delete(self.selected_index)
self.listbox.insert(self.selected_index, hook.file)
def click_delete(self) -> None: def click_delete(self) -> None:
del self.app.core.hooks[self.selected] del self.app.core.hooks[self.selected]
@ -146,11 +158,12 @@ class HooksDialog(Dialog):
def select(self, event: tk.Event) -> None: def select(self, event: tk.Event) -> None:
if self.listbox.curselection(): if self.listbox.curselection():
index = self.listbox.curselection()[0] self.selected_index = self.listbox.curselection()[0]
self.selected = self.listbox.get(index) self.selected = self.listbox.get(self.selected_index)
self.edit_button.config(state=tk.NORMAL) self.edit_button.config(state=tk.NORMAL)
self.delete_button.config(state=tk.NORMAL) self.delete_button.config(state=tk.NORMAL)
else: else:
self.selected = None self.selected = None
self.selected_index = None
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)

126
daemon/core/gui/wrappers.py Normal file
View file

@ -0,0 +1,126 @@
from dataclasses import dataclass
from enum import Enum
from typing import List
from core.api.grpc import core_pb2
class SessionState(Enum):
DEFINITION = 1
CONFIGURATION = 2
INSTANTIATION = 3
RUNTIME = 4
DATACOLLECT = 5
SHUTDOWN = 6
class NodeType(Enum):
DEFAULT = 0
PHYSICAL = 1
SWITCH = 4
HUB = 5
WIRELESS_LAN = 6
RJ45 = 7
TUNNEL = 8
EMANE = 10
TAP_BRIDGE = 11
PEER_TO_PEER = 12
CONTROL_NET = 13
DOCKER = 15
LXC = 16
@dataclass
class Hook:
state: SessionState
file: str
data: str
@classmethod
def from_proto(cls, hook: core_pb2.Hook) -> "Hook":
return Hook(state=SessionState(hook.state), file=hook.file, data=hook.data)
def to_proto(self) -> core_pb2.Hook:
return core_pb2.Hook(state=self.state.value, file=self.file, data=self.data)
@dataclass
class Position:
x: float
y: float
@classmethod
def from_proto(cls, position: core_pb2.Position) -> "Position":
return Position(x=position.x, y=position.y)
def to_proto(self) -> core_pb2.Position:
return core_pb2.Position(x=self.x, y=self.y)
@dataclass
class Geo:
lat: float = None
lon: float = None
alt: float = None
@classmethod
def from_proto(cls, geo: core_pb2.Geo) -> "Geo":
return Geo(lat=geo.lat, lon=geo.lon, alt=geo.alt)
def to_proto(self) -> core_pb2.Geo:
return core_pb2.Geo(lat=self.lat, lon=self.lon, alt=self.alt)
@dataclass
class Node:
id: int
name: str
type: NodeType
model: str = None
position: Position = None
services: List[str] = None
config_services: List[str] = None
emane: str = None
icon: str = None
image: str = None
server: str = None
geo: Geo = None
dir: str = None
channel: str = None
@classmethod
def from_proto(cls, node: core_pb2.Node) -> "Node":
return Node(
id=node.id,
name=node.name,
type=NodeType(node.type),
model=node.model,
position=Position.from_proto(node.position),
services=list(node.services),
config_services=list(node.config_services),
emane=node.emane,
icon=node.icon,
image=node.image,
server=node.server,
geo=Geo.from_proto(node.geo),
dir=node.dir,
channel=node.channel,
)
def to_proto(self) -> core_pb2.Node:
return core_pb2.Node(
id=self.id,
name=self.name,
type=self.type.value,
model=self.model,
position=self.position.to_proto(),
services=self.services,
config_services=self.config_services,
emane=self.emane,
icon=self.icon,
image=self.image,
server=self.server,
geo=self.geo.to_proto(),
dir=self.dir,
channel=self.channel,
)