pygui initial implementation for supporting the view menu for showing and hiding canvas elements

This commit is contained in:
Blake Harnden 2020-04-19 15:47:07 -07:00
parent f45a11076f
commit d26c4fc4ab
9 changed files with 119 additions and 61 deletions

View file

@ -78,11 +78,11 @@ class Application(tk.Frame):
def draw(self): def draw(self):
self.master.option_add("*tearOff", tk.FALSE) self.master.option_add("*tearOff", tk.FALSE)
self.menubar = Menubar(self.master, self)
self.toolbar = Toolbar(self, self) self.toolbar = Toolbar(self, self)
self.toolbar.pack(side=tk.LEFT, fill=tk.Y, ipadx=2, ipady=2) self.toolbar.pack(side=tk.LEFT, fill=tk.Y, ipadx=2, ipady=2)
self.draw_canvas() self.draw_canvas()
self.draw_status() self.draw_status()
self.menubar = Menubar(self.master, self)
def draw_canvas(self): def draw_canvas(self):
width = self.guiconfig["preferences"]["width"] width = self.guiconfig["preferences"]["width"]

View file

@ -369,21 +369,16 @@ class CoreClient:
logging.debug("canvas metadata: %s", canvas_config) logging.debug("canvas metadata: %s", canvas_config)
if canvas_config: if canvas_config:
canvas_config = json.loads(canvas_config) canvas_config = json.loads(canvas_config)
gridlines = canvas_config.get("gridlines", True) gridlines = canvas_config.get("gridlines", True)
self.app.canvas.show_grid.set(gridlines) self.app.canvas.show_grid.set(gridlines)
fit_image = canvas_config.get("fit_image", False) fit_image = canvas_config.get("fit_image", False)
self.app.canvas.adjust_to_dim.set(fit_image) self.app.canvas.adjust_to_dim.set(fit_image)
wallpaper_style = canvas_config.get("wallpaper-style", 1) wallpaper_style = canvas_config.get("wallpaper-style", 1)
self.app.canvas.scale_option.set(wallpaper_style) self.app.canvas.scale_option.set(wallpaper_style)
width = self.app.guiconfig["preferences"]["width"] width = self.app.guiconfig["preferences"]["width"]
height = self.app.guiconfig["preferences"]["height"] height = self.app.guiconfig["preferences"]["height"]
dimensions = canvas_config.get("dimensions", [width, height]) dimensions = canvas_config.get("dimensions", [width, height])
self.app.canvas.redraw_canvas(dimensions) self.app.canvas.redraw_canvas(dimensions)
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))

View file

@ -24,7 +24,6 @@ class CanvasWallpaperDialog(Dialog):
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
self.scale_option = tk.IntVar(value=self.canvas.scale_option.get()) self.scale_option = tk.IntVar(value=self.canvas.scale_option.get())
self.show_grid = tk.BooleanVar(value=self.canvas.show_grid.get())
self.adjust_to_dim = tk.BooleanVar(value=self.canvas.adjust_to_dim.get()) self.adjust_to_dim = tk.BooleanVar(value=self.canvas.adjust_to_dim.get())
self.filename = tk.StringVar(value=self.canvas.wallpaper_file) self.filename = tk.StringVar(value=self.canvas.wallpaper_file)
self.image_label = None self.image_label = None
@ -103,11 +102,6 @@ class CanvasWallpaperDialog(Dialog):
self.options.append(button) self.options.append(button)
def draw_additional_options(self): def draw_additional_options(self):
checkbutton = ttk.Checkbutton(
self.top, text="Show grid", variable=self.show_grid
)
checkbutton.grid(sticky="ew", padx=PADX)
checkbutton = ttk.Checkbutton( checkbutton = ttk.Checkbutton(
self.top, self.top,
text="Adjust canvas size to image dimensions", text="Adjust canvas size to image dimensions",
@ -163,17 +157,13 @@ class CanvasWallpaperDialog(Dialog):
def click_apply(self): def click_apply(self):
self.canvas.scale_option.set(self.scale_option.get()) self.canvas.scale_option.set(self.scale_option.get())
self.canvas.show_grid.set(self.show_grid.get())
self.canvas.adjust_to_dim.set(self.adjust_to_dim.get()) self.canvas.adjust_to_dim.set(self.adjust_to_dim.get())
self.canvas.update_grid() self.canvas.show_grid.click_handler()
filename = self.filename.get() filename = self.filename.get()
if not filename: if not filename:
filename = None filename = None
try: try:
self.canvas.set_wallpaper(filename) self.canvas.set_wallpaper(filename)
except FileNotFoundError: except FileNotFoundError:
logging.error("invalid background: %s", filename) logging.error("invalid background: %s", filename)
self.destroy() self.destroy()

View file

@ -20,15 +20,6 @@ WIRELESS_COLOR = "#009933"
ARC_DISTANCE = 50 ARC_DISTANCE = 50
def interface_label(interface: core_pb2.Interface) -> str:
label = ""
if interface.ip4:
label = f"{interface.ip4}/{interface.ip4mask}"
if interface.ip6:
label = f"{label}\n{interface.ip6}/{interface.ip6mask}"
return label
def create_edge_token(src: int, dst: int, network: int = None) -> Tuple[int, ...]: def create_edge_token(src: int, dst: int, network: int = None) -> Tuple[int, ...]:
values = [src, dst] values = [src, dst]
if network is not None: if network is not None:
@ -143,7 +134,12 @@ class Edge:
if self.middle_label is None: if self.middle_label is None:
x, y = self.middle_label_pos() x, y = self.middle_label_pos()
self.middle_label = self.canvas.create_text( self.middle_label = self.canvas.create_text(
x, y, font=self.canvas.app.edge_font, text=text x,
y,
font=self.canvas.app.edge_font,
text=text,
tags=tags.LINK_LABEL,
state=self.canvas.show_link_labels.state(),
) )
else: else:
self.canvas.itemconfig(self.middle_label, text=text) self.canvas.itemconfig(self.middle_label, text=text)
@ -168,7 +164,8 @@ class Edge:
text=text, text=text,
justify=tk.CENTER, justify=tk.CENTER,
font=self.canvas.app.edge_font, font=self.canvas.app.edge_font,
tags=tags.LINK_INFO, tags=tags.LINK_LABEL,
state=self.canvas.show_link_labels.state(),
) )
else: else:
self.canvas.itemconfig(self.src_label, text=text) self.canvas.itemconfig(self.src_label, text=text)
@ -181,7 +178,8 @@ class Edge:
text=text, text=text,
justify=tk.CENTER, justify=tk.CENTER,
font=self.canvas.app.edge_font, font=self.canvas.app.edge_font,
tags=tags.LINK_INFO, tags=tags.LINK_LABEL,
state=self.canvas.show_link_labels.state(),
) )
else: else:
self.canvas.itemconfig(self.dst_label, text=text) self.canvas.itemconfig(self.dst_label, text=text)
@ -278,13 +276,25 @@ class CanvasEdge(Edge):
self.link = link self.link = link
self.draw_labels() self.draw_labels()
def interface_label(self, interface: core_pb2.Interface) -> str:
label = ""
if interface.name and self.canvas.show_interface_names.get():
label = f"{interface.name}"
if interface.ip4 and self.canvas.show_ip4s.get():
label = f"{label}\n" if label else ""
label += f"{interface.ip4}/{interface.ip4mask}"
if interface.ip6 and self.canvas.show_ip6s.get():
label = f"{label}\n" if label else ""
label += f"{interface.ip6}/{interface.ip6mask}"
return label
def create_node_labels(self) -> Tuple[str, str]: def create_node_labels(self) -> Tuple[str, str]:
label_one = None label_one = None
if self.link.HasField("interface_one"): if self.link.HasField("interface_one"):
label_one = interface_label(self.link.interface_one) label_one = self.interface_label(self.link.interface_one)
label_two = None label_two = None
if self.link.HasField("interface_two"): if self.link.HasField("interface_two"):
label_two = interface_label(self.link.interface_two) label_two = self.interface_label(self.link.interface_two)
return label_one, label_two return label_one, label_two
def draw_labels(self) -> None: def draw_labels(self) -> None:

View file

@ -1,5 +1,6 @@
import logging import logging
import tkinter as tk import tkinter as tk
from tkinter import BooleanVar
from typing import TYPE_CHECKING, Tuple from typing import TYPE_CHECKING, Tuple
from PIL import Image, ImageTk from PIL import Image, ImageTk
@ -30,6 +31,19 @@ ZOOM_OUT = 0.9
ICON_SIZE = 48 ICON_SIZE = 48
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):
self.canvas.itemconfigure(self.tag, state=self.state())
class CanvasGraph(tk.Canvas): class CanvasGraph(tk.Canvas):
def __init__( def __init__(
self, master: "Application", core: "CoreClient", width: int, height: int self, master: "Application", core: "CoreClient", width: int, height: int
@ -69,7 +83,6 @@ class CanvasGraph(tk.Canvas):
self.wallpaper_drawn = None self.wallpaper_drawn = None
self.wallpaper_file = "" self.wallpaper_file = ""
self.scale_option = tk.IntVar(value=1) self.scale_option = tk.IntVar(value=1)
self.show_grid = tk.BooleanVar(value=True)
self.adjust_to_dim = tk.BooleanVar(value=False) self.adjust_to_dim = tk.BooleanVar(value=False)
# throughput related # throughput related
@ -77,6 +90,17 @@ class CanvasGraph(tk.Canvas):
self.throughput_width = 10 self.throughput_width = 10
self.throughput_color = "#FF0000" self.throughput_color = "#FF0000"
# drawing related
self.show_node_labels = ShowVar(self, tags.NODE_LABEL, value=True)
self.show_link_labels = ShowVar(self, tags.LINK_LABEL, value=True)
self.show_grid = ShowVar(self, tags.GRIDLINE, value=True)
self.show_shapes = ShowVar(self, tags.SHAPE, value=True)
self.show_shape_labels = ShowVar(self, tags.SHAPE_TEXT, value=True)
self.show_marker = ShowVar(self, tags.MARKER, value=True)
self.show_interface_names = BooleanVar(value=False)
self.show_ip4s = BooleanVar(value=True)
self.show_ip6s = BooleanVar(value=True)
# bindings # bindings
self.setup_bindings() self.setup_bindings()
@ -562,6 +586,7 @@ class CanvasGraph(tk.Canvas):
fill=self.app.toolbar.marker_tool.color, fill=self.app.toolbar.marker_tool.color,
outline="", outline="",
tags=tags.MARKER, tags=tags.MARKER,
state=self.show_marker.state(),
) )
return return
if selected is None: if selected is None:
@ -818,7 +843,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.update_grid() self.app.canvas.show_grid.click_handler()
def redraw_wallpaper(self): def redraw_wallpaper(self):
if self.adjust_to_dim.get(): if self.adjust_to_dim.get():
@ -840,13 +865,6 @@ class CanvasGraph(tk.Canvas):
for component in tags.ABOVE_WALLPAPER_TAGS: for component in tags.ABOVE_WALLPAPER_TAGS:
self.tag_raise(component) self.tag_raise(component)
def update_grid(self):
logging.debug("updating grid show grid: %s", self.show_grid.get())
if self.show_grid.get():
self.itemconfig(tags.GRIDLINE, state=tk.NORMAL)
else:
self.itemconfig(tags.GRIDLINE, state=tk.HIDDEN)
def set_wallpaper(self, filename: str): def set_wallpaper(self, filename: str):
logging.debug("setting wallpaper: %s", filename) logging.debug("setting wallpaper: %s", filename)
if filename: if filename:
@ -906,7 +924,8 @@ class CanvasGraph(tk.Canvas):
self.master, scaled_x, scaled_y, copy, self.nodes[canvas_nid].image self.master, scaled_x, scaled_y, copy, self.nodes[canvas_nid].image
) )
# add new node to modified_service_nodes set if that set contains the to_copy node # add new node to modified_service_nodes set if that set contains the
# to_copy node
if self.app.core.service_been_modified(core_node.id): if self.app.core.service_been_modified(core_node.id):
self.app.core.modified_service_nodes.add(copy.id) self.app.core.modified_service_nodes.add(copy.id)

View file

@ -47,9 +47,10 @@ class CanvasNode:
x, x,
label_y, label_y,
text=self.core_node.name, text=self.core_node.name,
tags=tags.NODE_NAME, 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(),
) )
self.tooltip = CanvasTooltip(self.canvas) self.tooltip = CanvasTooltip(self.canvas)
self.edges = set() self.edges = set()
@ -195,7 +196,6 @@ class CanvasNode:
label="Mobility Player", command=self.show_mobility_player label="Mobility Player", command=self.show_mobility_player
) )
context.add_command(label="Select Adjacent", state=tk.DISABLED) context.add_command(label="Select Adjacent", state=tk.DISABLED)
context.add_command(label="Hide", state=tk.DISABLED)
if NodeUtils.is_container_node(self.core_node.type): if NodeUtils.is_container_node(self.core_node.type):
context.add_command(label="Shell Window", state=tk.DISABLED) context.add_command(label="Shell Window", state=tk.DISABLED)
context.add_command(label="Tcpdump", state=tk.DISABLED) context.add_command(label="Tcpdump", state=tk.DISABLED)
@ -228,7 +228,6 @@ class CanvasNode:
edit_menu.add_command(label="Cut", state=tk.DISABLED) edit_menu.add_command(label="Cut", state=tk.DISABLED)
edit_menu.add_command(label="Copy", command=self.canvas_copy) edit_menu.add_command(label="Copy", command=self.canvas_copy)
edit_menu.add_command(label="Delete", command=self.canvas_delete) edit_menu.add_command(label="Delete", command=self.canvas_delete)
edit_menu.add_command(label="Hide", state=tk.DISABLED)
context.add_cascade(label="Edit", menu=edit_menu) context.add_cascade(label="Edit", menu=edit_menu)
return context return context

View file

@ -85,6 +85,7 @@ class Shape:
fill=self.shape_data.fill_color, fill=self.shape_data.fill_color,
outline=self.shape_data.border_color, outline=self.shape_data.border_color,
width=self.shape_data.border_width, width=self.shape_data.border_width,
state=self.canvas.show_shapes.state(),
) )
self.draw_shape_text() self.draw_shape_text()
elif self.shape_type == ShapeType.RECTANGLE: elif self.shape_type == ShapeType.RECTANGLE:
@ -98,6 +99,7 @@ class Shape:
fill=self.shape_data.fill_color, fill=self.shape_data.fill_color,
outline=self.shape_data.border_color, outline=self.shape_data.border_color,
width=self.shape_data.border_width, width=self.shape_data.border_width,
state=self.canvas.show_shapes.state(),
) )
self.draw_shape_text() self.draw_shape_text()
elif self.shape_type == ShapeType.TEXT: elif self.shape_type == ShapeType.TEXT:
@ -109,6 +111,7 @@ class Shape:
text=self.shape_data.text, text=self.shape_data.text,
fill=self.shape_data.text_color, fill=self.shape_data.text_color,
font=font, font=font,
state=self.canvas.show_shapes.state(),
) )
else: else:
logging.error("unknown shape type: %s", self.shape_type) logging.error("unknown shape type: %s", self.shape_type)
@ -136,6 +139,7 @@ class Shape:
text=self.shape_data.text, text=self.shape_data.text,
fill=self.shape_data.text_color, fill=self.shape_data.text_color,
font=font, font=font,
state=self.canvas.show_shape_labels.state(),
) )
def shape_motion(self, x1: float, y1: float): def shape_motion(self, x1: float, y1: float):

View file

@ -2,10 +2,10 @@ GRIDLINE = "gridline"
SHAPE = "shape" SHAPE = "shape"
SHAPE_TEXT = "shapetext" SHAPE_TEXT = "shapetext"
EDGE = "edge" EDGE = "edge"
LINK_INFO = "linkinfo" LINK_LABEL = "linklabel"
WIRELESS_EDGE = "wireless" WIRELESS_EDGE = "wireless"
ANTENNA = "antenna" ANTENNA = "antenna"
NODE_NAME = "nodename" NODE_LABEL = "nodename"
NODE = "node" NODE = "node"
WALLPAPER = "wallpaper" WALLPAPER = "wallpaper"
SELECTION = "selectednodes" SELECTION = "selectednodes"
@ -15,19 +15,19 @@ ABOVE_WALLPAPER_TAGS = [
SHAPE, SHAPE,
SHAPE_TEXT, SHAPE_TEXT,
EDGE, EDGE,
LINK_INFO, LINK_LABEL,
WIRELESS_EDGE, WIRELESS_EDGE,
ANTENNA, ANTENNA,
NODE, NODE,
NODE_NAME, NODE_LABEL,
] ]
ABOVE_SHAPE = [GRIDLINE, EDGE, LINK_INFO, WIRELESS_EDGE, ANTENNA, NODE, NODE_NAME] ABOVE_SHAPE = [GRIDLINE, EDGE, LINK_LABEL, WIRELESS_EDGE, ANTENNA, NODE, NODE_LABEL]
COMPONENT_TAGS = [ COMPONENT_TAGS = [
EDGE, EDGE,
NODE, NODE,
NODE_NAME, NODE_LABEL,
WALLPAPER, WALLPAPER,
LINK_INFO, LINK_LABEL,
ANTENNA, ANTENNA,
WIRELESS_EDGE, WIRELESS_EDGE,
SELECTION, SELECTION,

View file

@ -3,7 +3,7 @@ import os
import tkinter as tk import tkinter as tk
import webbrowser import webbrowser
from functools import partial from functools import partial
from tkinter import filedialog, messagebox from tkinter import BooleanVar, filedialog, messagebox
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from core.gui.appconfig import XMLS_PATH from core.gui.appconfig import XMLS_PATH
@ -41,8 +41,10 @@ class Menubar(tk.Menu):
self.master.config(menu=self) self.master.config(menu=self)
self.app = app self.app = app
self.core = app.core self.core = app.core
self.canvas = app.canvas
self.recent_menu = None self.recent_menu = None
self.edit_menu = None self.edit_menu = None
self.show_annotations = BooleanVar(value=True)
self.draw() self.draw()
def draw(self) -> None: def draw(self) -> None:
@ -129,15 +131,41 @@ class Menubar(tk.Menu):
Create view menu Create view menu
""" """
menu = tk.Menu(self) menu = tk.Menu(self)
menu.add_command(label="All", state=tk.DISABLED) menu.add_checkbutton(
menu.add_command(label="None", state=tk.DISABLED) label="Interface Names",
menu.add_separator() command=self.click_edge_label_change,
menu.add_command(label="Interface Names", state=tk.DISABLED) variable=self.canvas.show_interface_names,
menu.add_command(label="IPv4 Addresses", state=tk.DISABLED) )
menu.add_command(label="IPv6 Addresses", state=tk.DISABLED) menu.add_checkbutton(
menu.add_command(label="Node Labels", state=tk.DISABLED) label="IPv4 Addresses",
menu.add_command(label="Annotations", state=tk.DISABLED) command=self.click_edge_label_change,
menu.add_command(label="Grid", state=tk.DISABLED) variable=self.canvas.show_ip4s,
)
menu.add_checkbutton(
label="IPv6 Addresses",
command=self.click_edge_label_change,
variable=self.canvas.show_ip6s,
)
menu.add_checkbutton(
label="Node Labels",
command=self.canvas.show_node_labels.click_handler,
variable=self.canvas.show_node_labels,
)
menu.add_checkbutton(
label="Link Labels",
command=self.canvas.show_link_labels.click_handler,
variable=self.canvas.show_link_labels,
)
menu.add_checkbutton(
label="Annotations",
command=self.click_show_annotations,
variable=self.show_annotations,
)
menu.add_checkbutton(
label="Canvas Grid",
command=self.canvas.show_grid.click_handler,
variable=self.canvas.show_grid,
)
self.add_cascade(label="View", menu=menu) self.add_cascade(label="View", menu=menu)
def draw_tools_menu(self) -> None: def draw_tools_menu(self) -> None:
@ -430,3 +458,16 @@ class Menubar(tk.Menu):
x = (col * layout_size) + padding x = (col * layout_size) + padding
y = (row * layout_size) + padding y = (row * layout_size) + padding
node.move(x, y) node.move(x, y)
def click_edge_label_change(self) -> None:
for edge in self.canvas.edges.values():
edge.draw_labels()
def click_show_annotations(self) -> None:
value = self.show_annotations.get()
self.canvas.show_shapes.set(value)
self.canvas.show_shape_labels.set(value)
self.canvas.show_marker.set(value)
self.canvas.show_shapes.click_handler()
self.canvas.show_shape_labels.click_handler()
self.canvas.show_marker.click_handler()