pygui: added class variable type hinting to core.gui.graph

This commit is contained in:
Blake Harnden 2020-06-19 23:24:07 -07:00
parent 0356f3b19c
commit 11be40bc90
8 changed files with 256 additions and 249 deletions

View file

@ -1,12 +1,14 @@
import functools
import logging
import tkinter as tk
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
import grpc
from PIL.ImageTk import PhotoImage
from core.api.grpc import core_pb2
from core.api.grpc.core_pb2 import NodeType
from core.api.grpc.common_pb2 import ConfigOption
from core.api.grpc.core_pb2 import Interface, Node, NodeType
from core.api.grpc.services_pb2 import NodeServiceData
from core.gui import themes
from core.gui.dialogs.emaneconfig import EmaneConfigDialog
from core.gui.dialogs.mobilityconfig import MobilityConfigDialog
@ -15,36 +17,31 @@ from core.gui.dialogs.nodeconfigservice import NodeConfigServiceDialog
from core.gui.dialogs.nodeservice import NodeServiceDialog
from core.gui.dialogs.wlanconfig import WlanConfigDialog
from core.gui.graph import tags
from core.gui.graph.edges import CanvasEdge
from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge
from core.gui.graph.tooltip import CanvasTooltip
from core.gui.images import ImageEnum
from core.gui.nodeutils import ANTENNA_SIZE, NodeUtils
if TYPE_CHECKING:
from core.gui.app import Application
from PIL.ImageTk import PhotoImage
from core.gui.graph.graph import CanvasGraph
NODE_TEXT_OFFSET = 5
NODE_TEXT_OFFSET: int = 5
class CanvasNode:
def __init__(
self,
app: "Application",
x: float,
y: float,
core_node: core_pb2.Node,
image: "PhotoImage",
self, app: "Application", x: float, y: float, core_node: Node, image: PhotoImage
):
self.app = app
self.canvas = app.canvas
self.image = image
self.core_node = core_node
self.id = self.canvas.create_image(
self.app: "Application" = app
self.canvas: "CanvasGraph" = app.canvas
self.image: PhotoImage = image
self.core_node: Node = core_node
self.id: int = self.canvas.create_image(
x, y, anchor=tk.CENTER, image=self.image, tags=tags.NODE
)
label_y = self._get_label_y()
self.text_id = self.canvas.create_text(
self.text_id: int = self.canvas.create_text(
x,
label_y,
text=self.core_node.name,
@ -53,21 +50,21 @@ class CanvasNode:
fill="#0000CD",
state=self.canvas.show_node_labels.state(),
)
self.tooltip = CanvasTooltip(self.canvas)
self.edges = set()
self.ifaces = {}
self.wireless_edges = set()
self.antennas = []
self.antenna_images = {}
self.tooltip: CanvasTooltip = CanvasTooltip(self.canvas)
self.edges: Set[CanvasEdge] = set()
self.ifaces: Dict[int, Interface] = {}
self.wireless_edges: Set[CanvasWirelessEdge] = set()
self.antennas: List[int] = []
self.antenna_images: Dict[int, PhotoImage] = {}
# possible configurations
self.emane_model_configs = {}
self.wlan_config = {}
self.mobility_config = {}
self.service_configs = {}
self.service_file_configs = {}
self.config_service_configs = {}
self.emane_model_configs: Dict[Tuple[str, Optional[int]], ConfigOption] = {}
self.wlan_config: Dict[str, ConfigOption] = {}
self.mobility_config: Dict[str, ConfigOption] = {}
self.service_configs: Dict[str, NodeServiceData] = {}
self.service_file_configs: Dict[str, Dict[str, str]] = {}
self.config_service_configs: Dict[str, Any] = {}
self.setup_bindings()
self.context = tk.Menu(self.canvas)
self.context: tk.Menu = tk.Menu(self.canvas)
themes.style_menu(self.context)
def next_iface_id(self) -> int:
@ -76,19 +73,19 @@ class CanvasNode:
i += 1
return i
def setup_bindings(self):
def setup_bindings(self) -> None:
self.canvas.tag_bind(self.id, "<Double-Button-1>", self.double_click)
self.canvas.tag_bind(self.id, "<Enter>", self.on_enter)
self.canvas.tag_bind(self.id, "<Leave>", self.on_leave)
self.canvas.tag_bind(self.id, "<ButtonRelease-3>", self.show_context)
def delete(self):
def delete(self) -> None:
logging.debug("Delete canvas node for %s", self.core_node)
self.canvas.delete(self.id)
self.canvas.delete(self.text_id)
self.delete_antennas()
def add_antenna(self):
def add_antenna(self) -> None:
x, y = self.canvas.coords(self.id)
offset = len(self.antennas) * 8 * self.app.app_scale
img = self.app.get_icon(ImageEnum.ANTENNA, ANTENNA_SIZE)
@ -102,7 +99,7 @@ class CanvasNode:
self.antennas.append(antenna_id)
self.antenna_images[antenna_id] = img
def delete_antenna(self):
def delete_antenna(self) -> None:
"""
delete one antenna
"""
@ -112,7 +109,7 @@ class CanvasNode:
self.canvas.delete(antenna_id)
self.antenna_images.pop(antenna_id, None)
def delete_antennas(self):
def delete_antennas(self) -> None:
"""
delete all antennas
"""
@ -122,30 +119,30 @@ class CanvasNode:
self.antennas.clear()
self.antenna_images.clear()
def redraw(self):
def redraw(self) -> None:
self.canvas.itemconfig(self.id, image=self.image)
self.canvas.itemconfig(self.text_id, text=self.core_node.name)
for edge in self.edges:
edge.redraw()
def _get_label_y(self):
def _get_label_y(self) -> int:
image_box = self.canvas.bbox(self.id)
return image_box[3] + NODE_TEXT_OFFSET
def scale_text(self):
def scale_text(self) -> None:
text_bound = self.canvas.bbox(self.text_id)
prev_y = (text_bound[3] + text_bound[1]) / 2
new_y = self._get_label_y()
self.canvas.move(self.text_id, 0, new_y - prev_y)
def move(self, x: int, y: int):
def move(self, x: int, y: int) -> None:
x, y = self.canvas.get_scaled_coords(x, y)
current_x, current_y = self.canvas.coords(self.id)
x_offset = x - current_x
y_offset = y - current_y
self.motion(x_offset, y_offset, update=False)
def motion(self, x_offset: int, y_offset: int, update: bool = True):
def motion(self, x_offset: float, y_offset: float, update: bool = True) -> None:
original_position = self.canvas.coords(self.id)
self.canvas.move(self.id, x_offset, y_offset)
pos = self.canvas.coords(self.id)
@ -177,7 +174,7 @@ class CanvasNode:
if self.app.core.is_runtime() and update:
self.app.core.edit_node(self.core_node)
def on_enter(self, event: tk.Event):
def on_enter(self, event: tk.Event) -> None:
if self.app.core.is_runtime() and self.app.core.observer:
self.tooltip.text.set("waiting...")
self.tooltip.on_enter(event)
@ -187,10 +184,10 @@ class CanvasNode:
except grpc.RpcError as e:
self.app.show_grpc_exception("Observer Error", e)
def on_leave(self, event: tk.Event):
def on_leave(self, event: tk.Event) -> None:
self.tooltip.on_leave(event)
def double_click(self, event: tk.Event):
def double_click(self, event: tk.Event) -> None:
if self.app.core.is_runtime():
self.canvas.core.launch_terminal(self.core_node.id)
else:
@ -270,37 +267,37 @@ class CanvasNode:
self.canvas.selection[self.id] = self
self.canvas.copy()
def show_config(self):
def show_config(self) -> None:
dialog = NodeConfigDialog(self.app, self)
dialog.show()
def show_wlan_config(self):
def show_wlan_config(self) -> None:
dialog = WlanConfigDialog(self.app, self)
if not dialog.has_error:
dialog.show()
def show_mobility_config(self):
def show_mobility_config(self) -> None:
dialog = MobilityConfigDialog(self.app, self)
if not dialog.has_error:
dialog.show()
def show_mobility_player(self):
def show_mobility_player(self) -> None:
mobility_player = self.app.core.mobility_players[self.core_node.id]
mobility_player.show()
def show_emane_config(self):
def show_emane_config(self) -> None:
dialog = EmaneConfigDialog(self.app, self)
dialog.show()
def show_services(self):
def show_services(self) -> None:
dialog = NodeServiceDialog(self.app, self)
dialog.show()
def show_config_services(self):
def show_config_services(self) -> None:
dialog = NodeConfigServiceDialog(self.app, self)
dialog.show()
def has_emane_link(self, iface_id: int) -> core_pb2.Node:
def has_emane_link(self, iface_id: int) -> Node:
result = None
for edge in self.edges:
if self.id == edge.src:
@ -317,14 +314,14 @@ class CanvasNode:
break
return result
def wireless_link_selected(self):
def wireless_link_selected(self) -> None:
nodes = [x for x in self.canvas.selection if x in self.canvas.nodes]
for node_id in nodes:
canvas_node = self.canvas.nodes[node_id]
self.canvas.create_edge(self, canvas_node)
self.canvas.clear_selection()
def scale_antennas(self):
def scale_antennas(self) -> None:
for i in range(len(self.antennas)):
antenna_id = self.antennas[i]
image = self.app.get_icon(ImageEnum.ANTENNA, ANTENNA_SIZE)