pygui: further refactoring to work towards multiple canvas support

This commit is contained in:
Blake Harnden 2020-12-11 18:39:58 -08:00
parent e34c00a431
commit 886bfc093b
8 changed files with 161 additions and 134 deletions

View file

@ -14,6 +14,7 @@ from core.gui.dialogs.error import ErrorDialog
from core.gui.frames.base import InfoFrameBase from core.gui.frames.base import InfoFrameBase
from core.gui.frames.default import DefaultInfoFrame from core.gui.frames.default import DefaultInfoFrame
from core.gui.graph.graph import CanvasGraph from core.gui.graph.graph import CanvasGraph
from core.gui.graph.manager import CanvasManager
from core.gui.images import ImageEnum, Images from core.gui.images import ImageEnum, Images
from core.gui.menubar import Menubar from core.gui.menubar import Menubar
from core.gui.nodeutils import NodeUtils from core.gui.nodeutils import NodeUtils
@ -35,6 +36,7 @@ class Application(ttk.Frame):
self.menubar: Optional[Menubar] = None self.menubar: Optional[Menubar] = None
self.toolbar: Optional[Toolbar] = None self.toolbar: Optional[Toolbar] = None
self.right_frame: Optional[ttk.Frame] = None self.right_frame: Optional[ttk.Frame] = None
self.manager: Optional[CanvasManager] = None
self.canvas: Optional[CanvasGraph] = None self.canvas: Optional[CanvasGraph] = None
self.statusbar: Optional[StatusBar] = None self.statusbar: Optional[StatusBar] = None
self.progress: Optional[Progressbar] = None self.progress: Optional[Progressbar] = None
@ -140,16 +142,18 @@ class Application(ttk.Frame):
canvas_frame.rowconfigure(0, weight=1) canvas_frame.rowconfigure(0, weight=1)
canvas_frame.columnconfigure(0, weight=1) canvas_frame.columnconfigure(0, weight=1)
canvas_frame.grid(row=0, column=0, sticky=tk.NSEW, pady=1) canvas_frame.grid(row=0, column=0, sticky=tk.NSEW, pady=1)
self.canvas = CanvasGraph(canvas_frame, self, self.core) self.manager = CanvasManager(canvas_frame, self, self.core)
self.canvas.grid(sticky=tk.NSEW) self.manager.notebook.grid(sticky=tk.NSEW)
scroll_y = ttk.Scrollbar(canvas_frame, command=self.canvas.yview) # self.canvas = CanvasGraph(canvas_frame, self, self.core)
scroll_y.grid(row=0, column=1, sticky=tk.NS) # self.canvas.grid(sticky=tk.NSEW)
scroll_x = ttk.Scrollbar( # scroll_y = ttk.Scrollbar(canvas_frame, command=self.canvas.yview)
canvas_frame, orient=tk.HORIZONTAL, command=self.canvas.xview # scroll_y.grid(row=0, column=1, sticky=tk.NS)
) # scroll_x = ttk.Scrollbar(
scroll_x.grid(row=1, column=0, sticky=tk.EW) # canvas_frame, orient=tk.HORIZONTAL, command=self.canvas.xview
self.canvas.configure(xscrollcommand=scroll_x.set) # )
self.canvas.configure(yscrollcommand=scroll_y.set) # scroll_x.grid(row=1, column=0, sticky=tk.EW)
# self.canvas.configure(xscrollcommand=scroll_x.set)
# self.canvas.configure(yscrollcommand=scroll_y.set)
def draw_status(self) -> None: def draw_status(self) -> None:
self.statusbar = StatusBar(self.right_frame, self) self.statusbar = StatusBar(self.right_frame, self)

View file

@ -47,6 +47,7 @@ from core.gui.dialogs.error import ErrorDialog
from core.gui.dialogs.mobilityplayer import MobilityPlayer from core.gui.dialogs.mobilityplayer import MobilityPlayer
from core.gui.dialogs.sessions import SessionsDialog from core.gui.dialogs.sessions import SessionsDialog
from core.gui.graph.edges import CanvasEdge from core.gui.graph.edges import CanvasEdge
from core.gui.graph.graph import CanvasGraph
from core.gui.graph.node import CanvasNode from core.gui.graph.node import CanvasNode
from core.gui.graph.shape import AnnotationData, Shape from core.gui.graph.shape import AnnotationData, Shape
from core.gui.graph.shapeutils import ShapeType from core.gui.graph.shapeutils import ShapeType
@ -315,9 +316,7 @@ class CoreClient:
self.session.id, self.handle_events self.session.id, self.handle_events
) )
self.ifaces_manager.joined(self.session.links) self.ifaces_manager.joined(self.session.links)
self.app.canvas.reset_and_redraw(self.session) self.app.manager.join(self.session)
self.parse_metadata()
self.app.canvas.organize()
if self.is_runtime(): if self.is_runtime():
self.show_mobility_players() self.show_mobility_players()
self.app.after(0, self.app.joined_session_update) self.app.after(0, self.app.joined_session_update)
@ -327,7 +326,7 @@ class CoreClient:
def is_runtime(self) -> bool: def is_runtime(self) -> bool:
return self.session and self.session.state == SessionState.RUNTIME return self.session and self.session.state == SessionState.RUNTIME
def parse_metadata(self) -> None: def parse_metadata(self, canvas: CanvasGraph) -> None:
# canvas setting # canvas setting
config = self.session.metadata config = self.session.metadata
canvas_config = config.get("canvas") canvas_config = config.get("canvas")
@ -347,10 +346,10 @@ class CoreClient:
wallpaper = canvas_config.get("wallpaper") wallpaper = canvas_config.get("wallpaper")
if wallpaper: if wallpaper:
wallpaper = str(appconfig.BACKGROUNDS_PATH.joinpath(wallpaper)) wallpaper = str(appconfig.BACKGROUNDS_PATH.joinpath(wallpaper))
self.app.canvas.set_wallpaper(wallpaper) canvas.set_wallpaper(wallpaper)
else: else:
self.app.canvas.redraw_canvas() canvas.redraw_canvas()
self.app.canvas.set_wallpaper(None) canvas.set_wallpaper(None)
# load saved shapes # load saved shapes
shapes_config = config.get("shapes") shapes_config = config.get("shapes")
@ -377,7 +376,7 @@ class CoreClient:
shape = Shape( shape = Shape(
self.app, self.app.canvas, shape_type, *coords, data=data self.app, self.app.canvas, shape_type, *coords, data=data
) )
self.app.canvas.shapes[shape.id] = shape canvas.shapes[shape.id] = shape
except ValueError: except ValueError:
logging.exception("unknown shape: %s", shape_type) logging.exception("unknown shape: %s", shape_type)

View file

@ -1,7 +1,6 @@
import logging import logging
import tkinter as tk import tkinter as tk
from copy import deepcopy from copy import deepcopy
from tkinter import BooleanVar
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
from PIL import Image from PIL import Image
@ -31,10 +30,11 @@ from core.gui.graph.node import CanvasNode
from core.gui.graph.shape import Shape from core.gui.graph.shape import Shape
from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
from core.gui.images import ImageEnum, TypeToImage from core.gui.images import ImageEnum, TypeToImage
from core.gui.nodeutils import NodeDraw, NodeUtils from core.gui.nodeutils import NodeUtils
if TYPE_CHECKING: if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
from core.gui.graph.manager import CanvasManager
from core.gui.coreclient import CoreClient from core.gui.coreclient import CoreClient
ZOOM_IN = 1.1 ZOOM_IN = 1.1
@ -44,32 +44,23 @@ MOVE_NODE_MODES = {GraphMode.NODE, GraphMode.SELECT}
MOVE_SHAPE_MODES = {GraphMode.ANNOTATION, GraphMode.SELECT} MOVE_SHAPE_MODES = {GraphMode.ANNOTATION, GraphMode.SELECT}
class ShowVar(BooleanVar):
def __init__(self, canvas: "CanvasGraph", tag: str, value: bool) -> None:
super().__init__(value=value)
self.canvas = canvas
self.tag = tag
def state(self) -> str:
return tk.NORMAL if self.get() else tk.HIDDEN
def click_handler(self) -> None:
self.canvas.itemconfigure(self.tag, state=self.state())
class CanvasGraph(tk.Canvas): class CanvasGraph(tk.Canvas):
def __init__( def __init__(
self, master: tk.BaseWidget, app: "Application", core: "CoreClient" self,
master: tk.BaseWidget,
app: "Application",
canvas_manager: "CanvasManager",
core: "CoreClient",
_id: int,
) -> None: ) -> None:
super().__init__(master, highlightthickness=0, background="#cccccc") super().__init__(master, highlightthickness=0, background="#cccccc")
self.id: int = _id
self.app: "Application" = app self.app: "Application" = app
self.manager: "CanvasManager" = canvas_manager
self.core: "CoreClient" = core self.core: "CoreClient" = core
self.mode: GraphMode = GraphMode.SELECT
self.annotation_type: Optional[ShapeType] = None
self.selection: Dict[int, int] = {} self.selection: Dict[int, int] = {}
self.select_box: Optional[Shape] = None self.select_box: Optional[Shape] = None
self.selected: Optional[int] = None self.selected: Optional[int] = None
self.node_draw: Optional[NodeDraw] = None
self.nodes: Dict[int, CanvasNode] = {} self.nodes: Dict[int, CanvasNode] = {}
self.edges: Dict[str, CanvasEdge] = {} self.edges: Dict[str, CanvasEdge] = {}
self.shapes: Dict[int, Shape] = {} self.shapes: Dict[int, Shape] = {}
@ -103,18 +94,6 @@ class CanvasGraph(tk.Canvas):
self.throughput_width: int = 10 self.throughput_width: int = 10
self.throughput_color: str = "#FF0000" self.throughput_color: str = "#FF0000"
# drawing related
self.show_node_labels: ShowVar = ShowVar(self, tags.NODE_LABEL, value=True)
self.show_link_labels: ShowVar = ShowVar(self, tags.LINK_LABEL, value=True)
self.show_links: ShowVar = ShowVar(self, tags.EDGE, value=True)
self.show_wireless: ShowVar = ShowVar(self, tags.WIRELESS_EDGE, value=True)
self.show_grid: ShowVar = ShowVar(self, tags.GRIDLINE, value=True)
self.show_annotations: ShowVar = ShowVar(self, tags.ANNOTATION, value=True)
self.show_loss_links: ShowVar = ShowVar(self, tags.LOSS_EDGES, value=True)
self.show_iface_names: BooleanVar = BooleanVar(value=False)
self.show_ip4s: BooleanVar = BooleanVar(value=True)
self.show_ip6s: BooleanVar = BooleanVar(value=True)
# bindings # bindings
self.setup_bindings() self.setup_bindings()
@ -140,24 +119,11 @@ class CanvasGraph(tk.Canvas):
self.configure(scrollregion=self.bbox(tk.ALL)) self.configure(scrollregion=self.bbox(tk.ALL))
def reset_and_redraw(self, session: Session) -> None: def reset_and_redraw(self, session: Session) -> None:
# reset view options to default state
self.show_node_labels.set(True)
self.show_link_labels.set(True)
self.show_grid.set(True)
self.show_annotations.set(True)
self.show_iface_names.set(False)
self.show_ip4s.set(True)
self.show_ip6s.set(True)
self.show_loss_links.set(True)
# delete any existing drawn items # delete any existing drawn items
for tag in tags.RESET_TAGS: for tag in tags.RESET_TAGS:
self.delete(tag) self.delete(tag)
# set the private variables to default value # set the private variables to default value
self.mode = GraphMode.SELECT
self.annotation_type = None
self.node_draw = None
self.selected = None self.selected = None
self.nodes.clear() self.nodes.clear()
self.edges.clear() self.edges.clear()
@ -307,7 +273,7 @@ class CanvasGraph(tk.Canvas):
image = self.app.get_icon(ImageEnum.EDITNODE, ICON_SIZE) image = self.app.get_icon(ImageEnum.EDITNODE, ICON_SIZE)
x = core_node.position.x x = core_node.position.x
y = core_node.position.y y = core_node.position.y
node = CanvasNode(self.app, x, y, core_node, image) node = CanvasNode(self.app, self, x, y, core_node, image)
self.nodes[node.id] = node self.nodes[node.id] = node
self.core.set_canvas_node(core_node, node) self.core.set_canvas_node(core_node, node)
@ -381,13 +347,13 @@ class CanvasGraph(tk.Canvas):
x, y = self.canvas_xy(event) x, y = self.canvas_xy(event)
if not self.inside_canvas(x, y): if not self.inside_canvas(x, y):
return return
if self.mode == GraphMode.ANNOTATION: if self.manager.mode == GraphMode.ANNOTATION:
self.focus_set() self.focus_set()
if self.shape_drawing: if self.shape_drawing:
shape = self.shapes[self.selected] shape = self.shapes[self.selected]
shape.shape_complete(x, y) shape.shape_complete(x, y)
self.shape_drawing = False self.shape_drawing = False
elif self.mode == GraphMode.SELECT: elif self.manager.mode == GraphMode.SELECT:
self.focus_set() self.focus_set()
if self.select_box: if self.select_box:
x0, y0, x1, y1 = self.coords(self.select_box.id) x0, y0, x1, y1 = self.coords(self.select_box.id)
@ -403,13 +369,15 @@ class CanvasGraph(tk.Canvas):
else: else:
self.focus_set() self.focus_set()
self.selected = self.get_selected(event) self.selected = self.get_selected(event)
logging.debug(f"click release selected({self.selected}) mode({self.mode})") logging.debug(
if self.mode == GraphMode.EDGE: "click release selected(%s) mode(%s)", self.selected, self.manager.mode
)
if self.manager.mode == GraphMode.EDGE:
self.handle_edge_release(event) self.handle_edge_release(event)
elif self.mode == GraphMode.NODE: elif self.manager.mode == GraphMode.NODE:
self.add_node(x, y) self.add_node(x, y)
elif self.mode == GraphMode.PICKNODE: elif self.manager.mode == GraphMode.PICKNODE:
self.mode = GraphMode.NODE self.manager.mode = GraphMode.NODE
self.selected = None self.selected = None
def handle_edge_release(self, _event: tk.Event) -> None: def handle_edge_release(self, _event: tk.Event) -> None:
@ -588,13 +556,13 @@ class CanvasGraph(tk.Canvas):
y_check = self.cursor[1] - self.offset[1] y_check = self.cursor[1] - self.offset[1]
logging.debug("click press offset(%s, %s)", x_check, y_check) logging.debug("click press offset(%s, %s)", x_check, y_check)
is_node = selected in self.nodes is_node = selected in self.nodes
if self.mode == GraphMode.EDGE and is_node: if self.manager.mode == GraphMode.EDGE and is_node:
pos = self.coords(selected) pos = self.coords(selected)
self.drawing_edge = CanvasEdge(self, selected, pos, pos) self.drawing_edge = CanvasEdge(self, selected, pos, pos)
self.organize() self.organize()
if self.mode == GraphMode.ANNOTATION: if self.manager.mode == GraphMode.ANNOTATION:
if is_marker(self.annotation_type): if is_marker(self.manager.annotation_type):
r = self.app.toolbar.marker_frame.size.get() r = self.app.toolbar.marker_frame.size.get()
self.create_oval( self.create_oval(
x - r, x - r,
@ -604,11 +572,11 @@ class CanvasGraph(tk.Canvas):
fill=self.app.toolbar.marker_frame.color, fill=self.app.toolbar.marker_frame.color,
outline="", outline="",
tags=(tags.MARKER, tags.ANNOTATION), tags=(tags.MARKER, tags.ANNOTATION),
state=self.show_annotations.state(), state=self.manager.show_annotations.state(),
) )
return return
if selected is None: if selected is None:
shape = Shape(self.app, self, self.annotation_type, x, y) shape = Shape(self.app, self, self.manager.annotation_type, x, y)
self.selected = shape.id self.selected = shape.id
self.shape_drawing = True self.shape_drawing = True
self.shapes[shape.id] = shape self.shapes[shape.id] = shape
@ -630,7 +598,7 @@ class CanvasGraph(tk.Canvas):
node.core_node.position.y, node.core_node.position.y,
) )
else: else:
if self.mode == GraphMode.SELECT: if self.manager.mode == GraphMode.SELECT:
shape = Shape(self.app, self, ShapeType.RECTANGLE, x, y) shape = Shape(self.app, self, ShapeType.RECTANGLE, x, y)
self.select_box = shape self.select_box = shape
self.clear_selection() self.clear_selection()
@ -659,7 +627,7 @@ class CanvasGraph(tk.Canvas):
if self.select_box: if self.select_box:
self.select_box.delete() self.select_box.delete()
self.select_box = None self.select_box = None
if is_draw_shape(self.annotation_type) and self.shape_drawing: if is_draw_shape(self.manager.annotation_type) and self.shape_drawing:
shape = self.shapes.pop(self.selected) shape = self.shapes.pop(self.selected)
shape.delete() shape.delete()
self.shape_drawing = False self.shape_drawing = False
@ -669,14 +637,14 @@ class CanvasGraph(tk.Canvas):
y_offset = y - self.cursor[1] y_offset = y - self.cursor[1]
self.cursor = x, y self.cursor = x, y
if self.mode == GraphMode.EDGE and self.drawing_edge is not None: if self.manager.mode == GraphMode.EDGE and self.drawing_edge is not None:
self.drawing_edge.move_dst(self.cursor) self.drawing_edge.move_dst(self.cursor)
if self.mode == GraphMode.ANNOTATION: if self.manager.mode == GraphMode.ANNOTATION:
if is_draw_shape(self.annotation_type) and self.shape_drawing: if is_draw_shape(self.manager.annotation_type) and self.shape_drawing:
shape = self.shapes[self.selected] shape = self.shapes[self.selected]
shape.shape_motion(x, y) shape.shape_motion(x, y)
return return
elif is_marker(self.annotation_type): elif is_marker(self.manager.annotation_type):
r = self.app.toolbar.marker_frame.size.get() r = self.app.toolbar.marker_frame.size.get()
self.create_oval( self.create_oval(
x - r, x - r,
@ -689,21 +657,21 @@ class CanvasGraph(tk.Canvas):
) )
return return
if self.mode == GraphMode.EDGE: if self.manager.mode == GraphMode.EDGE:
return return
# move selected objects # move selected objects
if self.selection: if self.selection:
for selected_id in self.selection: for selected_id in self.selection:
if self.mode in MOVE_SHAPE_MODES and selected_id in self.shapes: if self.manager.mode in MOVE_SHAPE_MODES and selected_id in self.shapes:
shape = self.shapes[selected_id] shape = self.shapes[selected_id]
shape.motion(x_offset, y_offset) shape.motion(x_offset, y_offset)
if self.mode in MOVE_NODE_MODES and selected_id in self.nodes: if self.manager.mode in MOVE_NODE_MODES and selected_id in self.nodes:
node = self.nodes[selected_id] node = self.nodes[selected_id]
node.motion(x_offset, y_offset, update=self.core.is_runtime()) node.motion(x_offset, y_offset, update=self.core.is_runtime())
else: else:
if self.select_box and self.mode == GraphMode.SELECT: if self.select_box and self.manager.mode == GraphMode.SELECT:
self.select_box.shape_motion(x, y) self.select_box.shape_motion(x, y)
def press_delete(self, _event: tk.Event) -> None: def press_delete(self, _event: tk.Event) -> None:
@ -729,17 +697,22 @@ class CanvasGraph(tk.Canvas):
return return
actual_x, actual_y = self.get_actual_coords(x, y) actual_x, actual_y = self.get_actual_coords(x, y)
core_node = self.core.create_node( core_node = self.core.create_node(
actual_x, actual_y, self.node_draw.node_type, self.node_draw.model actual_x,
actual_y,
self.manager.node_draw.node_type,
self.manager.node_draw.model,
) )
if not core_node: if not core_node:
return return
try: try:
image_enum = self.node_draw.image_enum image_enum = self.manager.node_draw.image_enum
self.node_draw.image = self.app.get_icon(image_enum, ICON_SIZE) self.manager.node_draw.image = self.app.get_icon(image_enum, ICON_SIZE)
except AttributeError: except AttributeError:
image_file = self.node_draw.image_file image_file = self.manager.node_draw.image_file
self.node_draw.image = self.app.get_custom_icon(image_file, ICON_SIZE) self.manager.node_draw.image = self.app.get_custom_icon(
node = CanvasNode(self.app, x, y, core_node, self.node_draw.image) image_file, ICON_SIZE
)
node = CanvasNode(self.app, self, x, y, core_node, self.manager.node_draw.image)
self.nodes[node.id] = node self.nodes[node.id] = node
self.core.set_canvas_node(core_node, node) self.core.set_canvas_node(core_node, node)
@ -847,7 +820,7 @@ class CanvasGraph(tk.Canvas):
# redraw gridlines to new canvas size # redraw gridlines to new canvas size
self.delete(tags.GRIDLINE) self.delete(tags.GRIDLINE)
self.draw_grid() self.draw_grid()
self.app.canvas.show_grid.click_handler() self.app.manager.show_grid.click_handler()
def redraw_wallpaper(self) -> None: def redraw_wallpaper(self) -> None:
if self.adjust_to_dim.get(): if self.adjust_to_dim.get():
@ -884,7 +857,7 @@ class CanvasGraph(tk.Canvas):
self.wallpaper_file = None self.wallpaper_file = None
def is_selection_mode(self) -> bool: def is_selection_mode(self) -> bool:
return self.mode == GraphMode.SELECT return self.manager.mode == GraphMode.SELECT
def create_edge(self, src: CanvasNode, dst: CanvasNode) -> CanvasEdge: def create_edge(self, src: CanvasNode, dst: CanvasNode) -> CanvasEdge:
""" """

View file

@ -1,16 +1,18 @@
import logging
import tkinter as tk import tkinter as tk
from tkinter import BooleanVar from tkinter import BooleanVar, ttk
from typing import Dict, Optional, Set, Tuple from typing import TYPE_CHECKING, Dict, Optional, Set, Tuple
from core.gui.app import Application from core.api.grpc.wrappers import Session
from core.gui.coreclient import CoreClient
from core.gui.graph import tags from core.gui.graph import tags
from core.gui.graph.edges import CanvasEdge
from core.gui.graph.enums import GraphMode from core.gui.graph.enums import GraphMode
from core.gui.graph.graph import CanvasGraph from core.gui.graph.graph import CanvasGraph
from core.gui.graph.node import CanvasNode
from core.gui.graph.shape import Shape
from core.gui.graph.shapeutils import ShapeType from core.gui.graph.shapeutils import ShapeType
from core.gui.nodeutils import NodeDraw
if TYPE_CHECKING:
from core.gui.app import Application
from core.gui.coreclient import CoreClient
class ShowVar(BooleanVar): class ShowVar(BooleanVar):
@ -38,18 +40,18 @@ class CanvasManager:
# canvas interactions # canvas interactions
self.mode: GraphMode = GraphMode.SELECT self.mode: GraphMode = GraphMode.SELECT
self.annotation_type: Optional[ShapeType] = None self.annotation_type: Optional[ShapeType] = None
self.node_draw: Optional[NodeDraw] = None
self.canvases: Dict[int, CanvasGraph] = {} self.canvases: Dict[int, CanvasGraph] = {}
# canvas object storage # canvas object storage
self.nodes: Dict[int, CanvasNode] = {} # TODO: validate this
self.edges: Dict[str, CanvasEdge] = {}
self.shapes: Dict[int, Shape] = {}
self.wireless_network: Dict[int, Set[int]] = {} self.wireless_network: Dict[int, Set[int]] = {}
# global canvas settings # global canvas settings
width = self.app.guiconfig.preferences.width self.default_dimensions: Tuple[int, int] = (
height = self.app.guiconfig.preferences.height self.app.guiconfig.preferences.width,
self.default_dimensions: Tuple[int, int] = (width, height) self.app.guiconfig.preferences.height,
)
self.show_node_labels: ShowVar = ShowVar(self, tags.NODE_LABEL, value=True) self.show_node_labels: ShowVar = ShowVar(self, tags.NODE_LABEL, value=True)
self.show_link_labels: ShowVar = ShowVar(self, tags.LINK_LABEL, value=True) self.show_link_labels: ShowVar = ShowVar(self, tags.LINK_LABEL, value=True)
self.show_links: ShowVar = ShowVar(self, tags.EDGE, value=True) self.show_links: ShowVar = ShowVar(self, tags.EDGE, value=True)
@ -65,3 +67,46 @@ class CanvasManager:
self.throughput_threshold: float = 250.0 self.throughput_threshold: float = 250.0
self.throughput_width: int = 10 self.throughput_width: int = 10
self.throughput_color: str = "#FF0000" self.throughput_color: str = "#FF0000"
# widget
self.notebook: Optional[ttk.Notebook] = None
self.draw()
def draw(self) -> None:
self.notebook = ttk.Notebook(self.master)
self.notebook.grid(sticky=tk.NSEW)
def join(self, session: Session) -> None:
# clear out all canvas
for tab_id in self.notebook.tabs():
self.notebook.forget(tab_id)
self.canvases.clear()
# reset settings
self.show_node_labels.set(True)
self.show_link_labels.set(True)
self.show_grid.set(True)
self.show_annotations.set(True)
self.show_iface_names.set(False)
self.show_ip4s.set(True)
self.show_ip6s.set(True)
self.show_loss_links.set(True)
self.mode = GraphMode.SELECT
self.annotation_type = None
self.node_draw = None
# draw initial tab(s) and session
tab = ttk.Frame(self.notebook, padding=0)
tab.grid(sticky=tk.NSEW)
tab.columnconfigure(0, weight=1)
tab.rowconfigure(0, weight=1)
tab_id = len(self.notebook.tabs())
self.notebook.add(tab, text=f"Canvas {tab_id}")
logging.info("canvas tab id: %s", tab_id)
canvas = CanvasGraph(tab, self.app, self, self.core, tab_id)
canvas.grid(sticky=tk.NSEW)
self.canvases[tab_id] = canvas
canvas.reset_and_redraw(session)
self.core.parse_metadata(canvas)
canvas.organize()

View file

@ -31,10 +31,16 @@ NODE_TEXT_OFFSET: int = 5
class CanvasNode: class CanvasNode:
def __init__( def __init__(
self, app: "Application", x: float, y: float, core_node: Node, image: PhotoImage self,
app: "Application",
canvas: "CanvasGraph",
x: float,
y: float,
core_node: Node,
image: PhotoImage,
): ):
self.app: "Application" = app self.app: "Application" = app
self.canvas: "CanvasGraph" = app.canvas self.canvas: "CanvasGraph" = canvas
self.image: PhotoImage = image self.image: PhotoImage = image
self.core_node: Node = core_node self.core_node: Node = core_node
self.id: int = self.canvas.create_image( self.id: int = self.canvas.create_image(
@ -49,7 +55,7 @@ class CanvasNode:
tags=tags.NODE_LABEL, tags=tags.NODE_LABEL,
font=self.app.icon_text_font, font=self.app.icon_text_font,
fill="#0000CD", fill="#0000CD",
state=self.canvas.show_node_labels.state(), state=self.app.manager.show_node_labels.state(),
) )
self.tooltip: CanvasTooltip = CanvasTooltip(self.canvas) self.tooltip: CanvasTooltip = CanvasTooltip(self.canvas)
self.edges: Set[CanvasEdge] = set() self.edges: Set[CanvasEdge] = set()

View file

@ -22,7 +22,7 @@ from core.gui.dialogs.servers import ServersDialog
from core.gui.dialogs.sessionoptions import SessionOptionsDialog from core.gui.dialogs.sessionoptions import SessionOptionsDialog
from core.gui.dialogs.sessions import SessionsDialog from core.gui.dialogs.sessions import SessionsDialog
from core.gui.dialogs.throughput import ThroughputDialog from core.gui.dialogs.throughput import ThroughputDialog
from core.gui.graph.graph import CanvasGraph from core.gui.graph.manager import CanvasManager
from core.gui.nodeutils import ICON_SIZE from core.gui.nodeutils import ICON_SIZE
from core.gui.observers import ObserversMenu from core.gui.observers import ObserversMenu
from core.gui.task import ProgressTask from core.gui.task import ProgressTask
@ -45,7 +45,7 @@ class Menubar(tk.Menu):
super().__init__(app) super().__init__(app)
self.app: "Application" = app self.app: "Application" = app
self.core: CoreClient = app.core self.core: CoreClient = app.core
self.canvas: CanvasGraph = app.canvas self.canvas_manager: CanvasManager = app.manager
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[ObserversMenu] = None self.observers_menu: Optional[ObserversMenu] = None
@ -145,52 +145,52 @@ class Menubar(tk.Menu):
menu.add_checkbutton( menu.add_checkbutton(
label="Interface Names", label="Interface Names",
command=self.click_edge_label_change, command=self.click_edge_label_change,
variable=self.canvas.show_iface_names, variable=self.canvas_manager.show_iface_names,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="IPv4 Addresses", label="IPv4 Addresses",
command=self.click_edge_label_change, command=self.click_edge_label_change,
variable=self.canvas.show_ip4s, variable=self.canvas_manager.show_ip4s,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="IPv6 Addresses", label="IPv6 Addresses",
command=self.click_edge_label_change, command=self.click_edge_label_change,
variable=self.canvas.show_ip6s, variable=self.canvas_manager.show_ip6s,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="Node Labels", label="Node Labels",
command=self.canvas.show_node_labels.click_handler, command=self.canvas_manager.show_node_labels.click_handler,
variable=self.canvas.show_node_labels, variable=self.canvas_manager.show_node_labels,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="Link Labels", label="Link Labels",
command=self.canvas.show_link_labels.click_handler, command=self.canvas_manager.show_link_labels.click_handler,
variable=self.canvas.show_link_labels, variable=self.canvas_manager.show_link_labels,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="Links", label="Links",
command=self.canvas.show_links.click_handler, command=self.canvas_manager.show_links.click_handler,
variable=self.canvas.show_links, variable=self.canvas_manager.show_links,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="Loss Links", label="Loss Links",
command=self.canvas.show_loss_links.click_handler, command=self.canvas_manager.show_loss_links.click_handler,
variable=self.canvas.show_loss_links, variable=self.canvas_manager.show_loss_links,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="Wireless Links", label="Wireless Links",
command=self.canvas.show_wireless.click_handler, command=self.canvas_manager.show_wireless.click_handler,
variable=self.canvas.show_wireless, variable=self.canvas_manager.show_wireless,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="Annotations", label="Annotations",
command=self.canvas.show_annotations.click_handler, command=self.canvas_manager.show_annotations.click_handler,
variable=self.canvas.show_annotations, variable=self.canvas_manager.show_annotations,
) )
menu.add_checkbutton( menu.add_checkbutton(
label="Canvas Grid", label="Canvas Grid",
command=self.canvas.show_grid.click_handler, command=self.canvas_manager.show_grid.click_handler,
variable=self.canvas.show_grid, variable=self.canvas_manager.show_grid,
) )
self.add_cascade(label="View", menu=menu) self.add_cascade(label="View", menu=menu)

View file

@ -48,7 +48,7 @@ class StatusBar(ttk.Frame):
self.zoom = ttk.Label(self, anchor=tk.CENTER, borderwidth=1, relief=tk.RIDGE) self.zoom = ttk.Label(self, anchor=tk.CENTER, borderwidth=1, relief=tk.RIDGE)
self.zoom.grid(row=0, column=1, sticky=tk.EW) self.zoom.grid(row=0, column=1, sticky=tk.EW)
self.set_zoom(self.app.canvas.ratio) # self.set_zoom(self.app.canvas.ratio)
self.cpu_label = ttk.Label( self.cpu_label = ttk.Label(
self, anchor=tk.CENTER, borderwidth=1, relief=tk.RIDGE self, anchor=tk.CENTER, borderwidth=1, relief=tk.RIDGE

View file

@ -257,8 +257,8 @@ class Toolbar(ttk.Frame):
def draw_node_picker(self) -> None: def draw_node_picker(self) -> None:
self.hide_marker() self.hide_marker()
self.app.canvas.mode = GraphMode.NODE self.app.manager.mode = GraphMode.NODE
self.app.canvas.node_draw = self.current_node self.app.manager.node_draw = self.current_node
self.design_frame.select_radio(self.node_button) self.design_frame.select_radio(self.node_button)
self.picker = PickerFrame(self.app, self.node_button) self.picker = PickerFrame(self.app, self.node_button)
# draw default nodes # draw default nodes
@ -278,7 +278,7 @@ class Toolbar(ttk.Frame):
def click_selection(self) -> None: def click_selection(self) -> None:
self.design_frame.select_radio(self.select_button) self.design_frame.select_radio(self.select_button)
self.app.canvas.mode = GraphMode.SELECT self.app.manager.mode = GraphMode.SELECT
self.hide_marker() self.hide_marker()
def click_runtime_selection(self) -> None: def click_runtime_selection(self) -> None:
@ -324,7 +324,7 @@ class Toolbar(ttk.Frame):
def click_link(self) -> None: def click_link(self) -> None:
self.design_frame.select_radio(self.link_button) self.design_frame.select_radio(self.link_button)
self.app.canvas.mode = GraphMode.EDGE self.app.manager.mode = GraphMode.EDGE
self.hide_marker() self.hide_marker()
def update_button( def update_button(
@ -337,7 +337,7 @@ class Toolbar(ttk.Frame):
logging.debug("update button(%s): %s", button, node_draw) logging.debug("update button(%s): %s", button, node_draw)
button.configure(image=image) button.configure(image=image)
button.image = image button.image = image
self.app.canvas.node_draw = node_draw self.app.manager.node_draw = node_draw
if type_enum == NodeTypeEnum.NODE: if type_enum == NodeTypeEnum.NODE:
self.current_node = node_draw self.current_node = node_draw
elif type_enum == NodeTypeEnum.NETWORK: elif type_enum == NodeTypeEnum.NETWORK: