tweaks to retrieving images/icons, updates to toolbar to use ttk widgets

This commit is contained in:
Blake Harnden 2019-11-12 17:32:34 -08:00
parent b96f8ff999
commit 14187ba79c
10 changed files with 221 additions and 234 deletions

View file

@ -1,5 +1,6 @@
import logging import logging
import tkinter as tk import tkinter as tk
from tkinter import ttk
from coretk import appconfig from coretk import appconfig
from coretk.coreclient import CoreClient from coretk.coreclient import CoreClient
@ -36,7 +37,7 @@ class Application(tk.Frame):
self.master.title("CORE") self.master.title("CORE")
self.master.geometry("1000x800") self.master.geometry("1000x800")
self.master.protocol("WM_DELETE_WINDOW", self.on_closing) self.master.protocol("WM_DELETE_WINDOW", self.on_closing)
image = Images.get(ImageEnum.CORE) image = Images.get(ImageEnum.CORE, 16)
self.master.tk.call("wm", "iconphoto", self.master._w, image) self.master.tk.call("wm", "iconphoto", self.master._w, image)
self.pack(fill=tk.BOTH, expand=True) self.pack(fill=tk.BOTH, expand=True)
@ -53,17 +54,17 @@ class Application(tk.Frame):
self, self.core, background="#cccccc", scrollregion=(0, 0, 1200, 1000) self, self.core, background="#cccccc", scrollregion=(0, 0, 1200, 1000)
) )
self.canvas.pack(fill=tk.BOTH, expand=True) self.canvas.pack(fill=tk.BOTH, expand=True)
scroll_x = tk.Scrollbar( scroll_x = ttk.Scrollbar(
self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview
) )
scroll_x.pack(side=tk.BOTTOM, fill=tk.X) scroll_x.pack(side=tk.BOTTOM, fill=tk.X)
scroll_y = tk.Scrollbar(self.canvas, command=self.canvas.yview) scroll_y = ttk.Scrollbar(self.canvas, command=self.canvas.yview)
scroll_y.pack(side=tk.RIGHT, fill=tk.Y) scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.configure(xscrollcommand=scroll_x.set) self.canvas.configure(xscrollcommand=scroll_x.set)
self.canvas.configure(yscrollcommand=scroll_y.set) self.canvas.configure(yscrollcommand=scroll_y.set)
def draw_status(self): def draw_status(self):
self.statusbar = tk.Frame(self) self.statusbar = ttk.Frame(self)
self.statusbar.pack(side=tk.BOTTOM, fill=tk.X) self.statusbar.pack(side=tk.BOTTOM, fill=tk.X)
def on_closing(self): def on_closing(self):

View file

@ -8,7 +8,7 @@ from core.api.grpc import client, core_pb2
from coretk.coretocanvas import CoreToCanvasMapping from coretk.coretocanvas import CoreToCanvasMapping
from coretk.dialogs.sessions import SessionsDialog from coretk.dialogs.sessions import SessionsDialog
from coretk.emaneodelnodeconfig import EmaneModelNodeConfig from coretk.emaneodelnodeconfig import EmaneModelNodeConfig
from coretk.images import Images from coretk.images import NODE_WIDTH, Images
from coretk.interface import Interface, InterfaceManager from coretk.interface import Interface, InterfaceManager
from coretk.mobilitynodeconfig import MobilityNodeConfig from coretk.mobilitynodeconfig import MobilityNodeConfig
from coretk.wlannodeconfig import WlanNodeConfig from coretk.wlannodeconfig import WlanNodeConfig
@ -137,7 +137,7 @@ class CoreClient:
# read custom nodes # read custom nodes
for config in self.app.config.get("nodes", []): for config in self.app.config.get("nodes", []):
image_file = config["image"] image_file = config["image"]
image = Images.get_custom(image_file) image = Images.get_custom(image_file, NODE_WIDTH)
custom_node = CustomNode( custom_node = CustomNode(
config["name"], image, image_file, set(config["services"]) config["name"], image, image_file, set(config["services"])
) )

View file

@ -13,7 +13,7 @@ class Dialog(tk.Toplevel):
self.modal = modal self.modal = modal
self.title(title) self.title(title)
self.protocol("WM_DELETE_WINDOW", self.destroy) self.protocol("WM_DELETE_WINDOW", self.destroy)
image = Images.get(ImageEnum.CORE) image = Images.get(ImageEnum.CORE, 16)
self.tk.call("wm", "iconphoto", self._w, image) self.tk.call("wm", "iconphoto", self._w, image)
def show(self): def show(self):

View file

@ -182,25 +182,31 @@ class EmaneConfiguration(Dialog):
def draw_option_buttons(self, parent): def draw_option_buttons(self, parent):
f = ttk.Frame(parent) f = ttk.Frame(parent)
f.grid(row=4, column=0, sticky="nsew")
f.columnconfigure(0, weight=1) f.columnconfigure(0, weight=1)
f.columnconfigure(1, weight=1) f.columnconfigure(1, weight=1)
image = Images.get(ImageEnum.EDITNODE, 16)
b = ttk.Button( b = ttk.Button(
f, f,
text=self.emane_models[0] + " options", text=self.emane_models[0] + " options",
image=Images.get(ImageEnum.EDITNODE), image=image,
compound=tk.RIGHT, compound=tk.RIGHT,
command=self.draw_model_options, command=self.draw_model_options,
) )
b.image = image
b.grid(row=0, column=0, padx=10, pady=2, sticky="nsew") b.grid(row=0, column=0, padx=10, pady=2, sticky="nsew")
image = Images.get(ImageEnum.EDITNODE, 16)
b = ttk.Button( b = ttk.Button(
f, f,
text="EMANE options", text="EMANE options",
image=Images.get(ImageEnum.EDITNODE), image=image,
compound=tk.RIGHT, compound=tk.RIGHT,
command=self.draw_emane_options, command=self.draw_emane_options,
) )
b.image = image
b.grid(row=0, column=1, padx=10, pady=2, sticky="nsew") b.grid(row=0, column=1, padx=10, pady=2, sticky="nsew")
f.grid(row=4, column=0, sticky="nsew")
def combobox_select(self, event): def combobox_select(self, event):
""" """
@ -271,7 +277,7 @@ class EmaneConfiguration(Dialog):
b = ttk.Button( b = ttk.Button(
f, f,
image=Images.get(ImageEnum.EDITNODE), image=Images.get(ImageEnum.EDITNODE, 8),
text="EMANE Wiki", text="EMANE Wiki",
compound=tk.RIGHT, compound=tk.RIGHT,
command=lambda: webbrowser.open_new( command=lambda: webbrowser.open_new(

View file

@ -54,7 +54,7 @@ class IconDialog(Dialog):
), ),
) )
if file_path: if file_path:
self.image = Images.create(file_path) self.image = Images.create(file_path, 32, 32)
self.image_label.config(image=self.image) self.image_label.config(image=self.image)
self.file_path.set(file_path) self.file_path.set(file_path)

View file

@ -73,30 +73,36 @@ class SessionsDialog(Dialog):
for i in range(4): for i in range(4):
frame.columnconfigure(i, weight=1) frame.columnconfigure(i, weight=1)
frame.grid(row=3, sticky="ew") frame.grid(row=3, sticky="ew")
image = Images.get(ImageEnum.DOCUMENTNEW, 16)
b = ttk.Button( b = ttk.Button(
frame, frame, image=image, text="New", compound=tk.LEFT, command=self.click_new
image=Images.get(ImageEnum.DOCUMENTNEW),
text="New",
compound=tk.LEFT,
command=self.click_new,
) )
b.image = image
b.grid(row=0, padx=2, sticky="ew") b.grid(row=0, padx=2, sticky="ew")
image = Images.get(ImageEnum.FILEOPEN, 16)
b = ttk.Button( b = ttk.Button(
frame, frame,
image=Images.get(ImageEnum.FILEOPEN), image=image,
text="Connect", text="Connect",
compound=tk.LEFT, compound=tk.LEFT,
command=self.click_connect, command=self.click_connect,
) )
b.image = image
b.grid(row=0, column=1, padx=2, sticky="ew") b.grid(row=0, column=1, padx=2, sticky="ew")
image = Images.get(ImageEnum.EDITDELETE, 16)
b = ttk.Button( b = ttk.Button(
frame, frame,
image=Images.get(ImageEnum.EDITDELETE), image=image,
text="Shutdown", text="Shutdown",
compound=tk.LEFT, compound=tk.LEFT,
command=self.click_shutdown, command=self.click_shutdown,
) )
b.image = image
b.grid(row=0, column=2, padx=2, sticky="ew") b.grid(row=0, column=2, padx=2, sticky="ew")
b = ttk.Button(frame, text="Cancel", command=self.click_new) b = ttk.Button(frame, text="Cancel", command=self.click_new)
b.grid(row=0, column=3, padx=2, sticky="ew") b.grid(row=0, column=3, padx=2, sticky="ew")

View file

@ -146,9 +146,7 @@ class CanvasGraph(tk.Canvas):
# peer to peer node is not drawn on the GUI # peer to peer node is not drawn on the GUI
if node.type != core_pb2.NodeType.PEER_TO_PEER: if node.type != core_pb2.NodeType.PEER_TO_PEER:
# draw nodes on the canvas # draw nodes on the canvas
image, name = Images.convert_type_and_model_to_image( image, name = Images.node_icon(node.type, node.model)
node.type, node.model
)
n = CanvasNode( n = CanvasNode(
node.position.x, node.position.y, image, name, self.master, node.id node.position.x, node.position.y, image, name, self.master, node.id
) )

View file

@ -77,6 +77,7 @@ class WlanAntennaManager:
self.quantity = 0 self.quantity = 0
self._max = 5 self._max = 5
self.antennas = [] self.antennas = []
self.image = Images.get(ImageEnum.ANTENNA, 32)
# distance between each antenna # distance between each antenna
self.offset = 0 self.offset = 0
@ -94,7 +95,7 @@ class WlanAntennaManager:
x - 16 + self.offset, x - 16 + self.offset,
y - 16, y - 16,
anchor=tk.CENTER, anchor=tk.CENTER,
image=Images.get(ImageEnum.ANTENNA), image=self.image,
tags="antenna", tags="antenna",
) )
) )

View file

@ -6,35 +6,37 @@ from PIL import Image, ImageTk
from core.api.grpc import core_pb2 from core.api.grpc import core_pb2
from coretk.appconfig import LOCAL_ICONS_PATH from coretk.appconfig import LOCAL_ICONS_PATH
NODE_WIDTH = 32
class Images: class Images:
images = {} images = {}
@classmethod @classmethod
def create(cls, file_path): def create(cls, file_path, width, height=None):
if height is None:
height = width
image = Image.open(file_path) image = Image.open(file_path)
image = image.resize((width, height), Image.ANTIALIAS)
return ImageTk.PhotoImage(image) return ImageTk.PhotoImage(image)
@classmethod @classmethod
def load_all(cls): def load_all(cls):
for image in LOCAL_ICONS_PATH.glob("*"): for image in LOCAL_ICONS_PATH.glob("*"):
cls.load(image.stem, str(image)) cls.images[image.stem] = str(image)
@classmethod @classmethod
def load(cls, name, file_path): def get(cls, image_enum, width, height=None):
tk_image = cls.create(file_path) file_path = cls.images[image_enum.value]
cls.images[name] = tk_image return cls.create(file_path, width, height)
@classmethod @classmethod
def get(cls, image): def get_custom(cls, name, width, height):
return cls.images[image.value] file_path = cls.images[name]
return cls.create(file_path, width, height)
@classmethod @classmethod
def get_custom(cls, name): def node_icon(cls, node_type, node_model):
return cls.images[name]
@classmethod
def convert_type_and_model_to_image(cls, node_type, node_model):
""" """
Retrieve image based on type and model Retrieve image based on type and model
:param core_pb2.NodeType node_type: core node type :param core_pb2.NodeType node_type: core node type
@ -43,34 +45,48 @@ class Images:
:rtype: tuple(PhotoImage, str) :rtype: tuple(PhotoImage, str)
:return: the matching image and its name :return: the matching image and its name
""" """
image_enum = ImageEnum.ROUTER
name = "unknown"
if node_type == core_pb2.NodeType.SWITCH: if node_type == core_pb2.NodeType.SWITCH:
return Images.get(ImageEnum.SWITCH), "switch" image_enum = ImageEnum.SWITCH
if node_type == core_pb2.NodeType.HUB: name = "switch"
return Images.get(ImageEnum.HUB), "hub" elif node_type == core_pb2.NodeType.HUB:
if node_type == core_pb2.NodeType.WIRELESS_LAN: image_enum = ImageEnum.HUB
return Images.get(ImageEnum.WLAN), "wlan" name = "hub"
if node_type == core_pb2.NodeType.EMANE: elif node_type == core_pb2.NodeType.WIRELESS_LAN:
return Images.get(ImageEnum.EMANE), "emane" image_enum = ImageEnum.WLAN
name = "wlan"
if node_type == core_pb2.NodeType.RJ45: elif node_type == core_pb2.NodeType.EMANE:
return Images.get(ImageEnum.RJ45), "rj45" image_enum = ImageEnum.EMANE
if node_type == core_pb2.NodeType.TUNNEL: name = "emane"
return Images.get(ImageEnum.TUNNEL), "tunnel" elif node_type == core_pb2.NodeType.RJ45:
if node_type == core_pb2.NodeType.DEFAULT: image_enum = ImageEnum.RJ45
name = "rj45"
elif node_type == core_pb2.NodeType.TUNNEL:
image_enum = ImageEnum.TUNNEL
name = "tunnel"
elif node_type == core_pb2.NodeType.DEFAULT:
if node_model == "router": if node_model == "router":
return Images.get(ImageEnum.ROUTER), "router" image_enum = ImageEnum.ROUTER
if node_model == "host": name = "router"
return Images.get(ImageEnum.HOST), "host" elif node_model == "host":
if node_model == "PC": image_enum = ImageEnum.HOST
return Images.get(ImageEnum.PC), "PC" name = "host"
if node_model == "mdr": elif node_model == "PC":
return Images.get(ImageEnum.MDR), "mdr" image_enum = ImageEnum.PC
if node_model == "prouter": name = "PC"
return Images.get(ImageEnum.PROUTER), "prouter" elif node_model == "mdr":
if node_model == "OVS": image_enum = ImageEnum.MDR
return Images.get(ImageEnum.OVS), "ovs" name = "mdr"
elif node_model == "prouter":
image_enum = ImageEnum.PROUTER
name = "prouter"
else: else:
logging.debug("INVALID INPUT OR NOT CONSIDERED YET") logging.error("invalid node model: %s", node_model)
else:
logging.error("invalid node type: %s", node_type)
return Images.get(image_enum, NODE_WIDTH), name
class ImageEnum(Enum): class ImageEnum(Enum):

View file

@ -1,43 +1,44 @@
import logging import logging
import tkinter as tk import tkinter as tk
from functools import partial from functools import partial
from tkinter import ttk
from coretk.dialogs.customnodes import CustomNodesDialog from coretk.dialogs.customnodes import CustomNodesDialog
from coretk.graph import GraphMode from coretk.graph import GraphMode
from coretk.images import ImageEnum, Images from coretk.images import ImageEnum, Images
from coretk.tooltip import Tooltip from coretk.tooltip import Tooltip
WIDTH = 32
class Toolbar(tk.Frame):
def icon(image_enum):
return Images.get(image_enum, WIDTH)
class Toolbar(ttk.Frame):
""" """
Core toolbar class Core toolbar class
""" """
def __init__(self, master, app, cnf={}, **kwargs): def __init__(self, master, app, **kwargs):
""" """
Create a CoreToolbar instance Create a CoreToolbar instance
:param tkinter.Frame edit_frame: edit frame :param tkinter.Frame edit_frame: edit frame
""" """
super().__init__(master, cnf, **kwargs) super().__init__(master, **kwargs)
self.app = app self.app = app
self.master = app.master self.master = app.master
self.radio_value = tk.IntVar()
self.exec_radio_value = tk.IntVar()
# button dimension # design buttons
self.width = 32 self.select_button = None
self.height = 32 self.link_button = None
# Reference to the option menus
self.selection_tool_button = None
self.link_layer_option_menu = None
self.marker_option_menu = None
self.network_layer_option_menu = None
self.node_button = None self.node_button = None
self.network_button = None self.network_button = None
self.annotation_button = None self.annotation_button = None
# runtime buttons
# frames # frames
self.design_frame = None self.design_frame = None
self.runtime_frame = None self.runtime_frame = None
@ -56,89 +57,77 @@ class Toolbar(tk.Frame):
self.design_frame.tkraise() self.design_frame.tkraise()
def draw_design_frame(self): def draw_design_frame(self):
self.design_frame = tk.Frame(self) self.design_frame = ttk.Frame(self)
self.design_frame.grid(row=0, column=0, sticky="nsew") self.design_frame.grid(row=0, column=0, sticky="nsew")
self.design_frame.columnconfigure(0, weight=1) self.design_frame.columnconfigure(0, weight=1)
self.create_button(
self.create_regular_button(
self.design_frame, self.design_frame,
Images.get(ImageEnum.START), icon(ImageEnum.START),
self.click_start_session_tool, self.click_start,
"start the session", "start the session",
) )
self.create_radio_button( self.select_button = self.create_button(
self.design_frame, self.design_frame,
Images.get(ImageEnum.SELECT), icon(ImageEnum.SELECT),
self.click_selection_tool, self.click_selection,
self.radio_value,
1,
"selection tool", "selection tool",
) )
self.create_radio_button( self.link_button = self.create_button(
self.design_frame, self.design_frame, icon(ImageEnum.LINK), self.click_link, "link tool"
Images.get(ImageEnum.LINK),
self.click_link_tool,
self.radio_value,
2,
"link tool",
) )
self.create_node_button() self.create_node_button()
self.create_network_button() self.create_network_button()
self.create_annotation_button() self.create_annotation_button()
self.radio_value.set(1)
def design_select(self, button):
logging.info("selecting design button: %s", button)
self.select_button.state(["!pressed"])
self.link_button.state(["!pressed"])
self.node_button.state(["!pressed"])
self.network_button.state(["!pressed"])
self.annotation_button.state(["!pressed"])
button.state(["pressed"])
def draw_runtime_frame(self): def draw_runtime_frame(self):
self.runtime_frame = tk.Frame(self) self.runtime_frame = ttk.Frame(self)
self.runtime_frame.grid(row=0, column=0, sticky="nsew") self.runtime_frame.grid(row=0, column=0, sticky="nsew")
self.runtime_frame.columnconfigure(0, weight=1) self.runtime_frame.columnconfigure(0, weight=1)
self.create_regular_button( self.create_button(
self.runtime_frame, self.runtime_frame,
Images.get(ImageEnum.STOP), icon(ImageEnum.STOP),
self.click_stop_button, self.click_stop,
"stop the session", "stop the session",
) )
self.create_radio_button( self.create_button(
self.runtime_frame, self.runtime_frame,
Images.get(ImageEnum.SELECT), icon(ImageEnum.SELECT),
self.click_selection_tool, self.click_selection,
self.exec_radio_value,
1,
"selection tool", "selection tool",
) )
self.create_observe_button() # self.create_observe_button()
self.create_radio_button( self.create_button(
self.runtime_frame, self.runtime_frame, icon(ImageEnum.PLOT), self.click_plot_button, "plot"
Images.get(ImageEnum.PLOT),
self.click_plot_button,
self.exec_radio_value,
2,
"plot",
) )
self.create_radio_button( self.create_button(
self.runtime_frame, self.runtime_frame,
Images.get(ImageEnum.MARKER), icon(ImageEnum.MARKER),
self.click_marker_button, self.click_marker_button,
self.exec_radio_value,
3,
"marker", "marker",
) )
self.create_radio_button( self.create_button(
self.runtime_frame, self.runtime_frame,
Images.get(ImageEnum.TWONODE), icon(ImageEnum.TWONODE),
self.click_two_node_button, self.click_two_node_button,
self.exec_radio_value,
4,
"run command from one node to another", "run command from one node to another",
) )
self.create_regular_button( self.create_button(
self.runtime_frame, Images.get(ImageEnum.RUN), self.click_run_button, "run" self.runtime_frame, icon(ImageEnum.RUN), self.click_run_button, "run"
) )
self.exec_radio_value.set(1)
def draw_node_picker(self): def draw_node_picker(self):
self.hide_pickers() self.hide_pickers()
self.node_picker = tk.Frame(self.master, padx=1, pady=1) self.node_picker = ttk.Frame(self.master)
nodes = [ nodes = [
(ImageEnum.ROUTER, "router"), (ImageEnum.ROUTER, "router"),
(ImageEnum.HOST, "host"), (ImageEnum.HOST, "host"),
@ -148,26 +137,28 @@ class Toolbar(tk.Frame):
] ]
# draw default nodes # draw default nodes
for image_enum, tooltip in nodes: for image_enum, tooltip in nodes:
image = Images.get(image_enum) image = icon(image_enum)
func = partial(self.update_button, self.node_button, image, tooltip) func = partial(self.update_button, self.node_button, image, tooltip)
self.create_button(image, func, self.node_picker, tooltip) self.create_picker_button(image, func, self.node_picker, tooltip)
# draw custom nodes # draw custom nodes
for name in sorted(self.app.core.custom_nodes): for name in sorted(self.app.core.custom_nodes):
custom_node = self.app.core.custom_nodes[name] custom_node = self.app.core.custom_nodes[name]
image = custom_node.image image = custom_node.image
func = partial(self.update_button, self.node_button, image, name) func = partial(self.update_button, self.node_button, image, name)
self.create_button(image, func, self.node_picker, name) self.create_picker_button(image, func, self.node_picker, name)
# draw edit node # draw edit node
image = Images.get(ImageEnum.EDITNODE) image = icon(ImageEnum.EDITNODE)
self.create_button( self.create_picker_button(
image, self.click_edit_node, self.node_picker, "custom nodes" image, self.click_edit_node, self.node_picker, "custom nodes"
) )
self.show_picker(self.node_button, self.node_picker) self.design_select(self.node_button)
self.node_button.after(
0, lambda: self.show_picker(self.node_button, self.node_picker)
)
def show_picker(self, button, picker): def show_picker(self, button, picker):
first_button = self.winfo_children()[0] x = self.winfo_width() + 1
x = button.winfo_rootx() - first_button.winfo_rootx() + 40 y = button.winfo_rooty() - picker.master.winfo_rooty() - 1
y = button.winfo_rooty() - first_button.winfo_rooty() - 1
picker.place(x=x, y=y) picker.place(x=x, y=y)
self.app.bind_all("<ButtonRelease-1>", lambda e: self.hide_pickers()) self.app.bind_all("<ButtonRelease-1>", lambda e: self.hide_pickers())
picker.wait_visibility() picker.wait_visibility()
@ -175,7 +166,7 @@ class Toolbar(tk.Frame):
self.wait_window(picker) self.wait_window(picker)
self.app.unbind_all("<ButtonRelease-1>") self.app.unbind_all("<ButtonRelease-1>")
def create_button(self, image, func, frame, tooltip): def create_picker_button(self, image, func, frame, tooltip):
""" """
Create button and put it on the frame Create button and put it on the frame
@ -185,37 +176,25 @@ class Toolbar(tk.Frame):
:param str tooltip: tooltip text :param str tooltip: tooltip text
:return: nothing :return: nothing
""" """
button = tk.Button(frame, width=self.width, height=self.height, image=image) button = ttk.Button(frame, image=image)
button.image = image
button.bind("<ButtonRelease-1>", lambda e: func()) button.bind("<ButtonRelease-1>", lambda e: func())
button.grid(pady=1) button.grid(pady=1)
Tooltip(button, tooltip) Tooltip(button, tooltip)
def create_radio_button(self, frame, image, func, variable, value, tooltip_msg): def create_button(self, frame, image, func, tooltip):
button = tk.Radiobutton( button = ttk.Button(frame, image=image, command=func)
frame, button.image = image
indicatoron=False, button.grid(sticky="ew")
width=self.width,
height=self.height,
image=image,
value=value,
variable=variable,
command=func,
)
button.grid()
Tooltip(button, tooltip_msg)
def create_regular_button(self, frame, image, func, tooltip):
button = tk.Button(
frame, width=self.width, height=self.height, image=image, command=func
)
button.grid()
Tooltip(button, tooltip) Tooltip(button, tooltip)
return button
def click_selection_tool(self): def click_selection(self):
logging.debug("clicked selection tool") logging.debug("clicked selection tool")
self.design_select(self.select_button)
self.app.canvas.mode = GraphMode.SELECT self.app.canvas.mode = GraphMode.SELECT
def click_start_session_tool(self): def click_start(self):
""" """
Start session handler redraw buttons, send node and link messages to grpc Start session handler redraw buttons, send node and link messages to grpc
server. server.
@ -227,8 +206,9 @@ class Toolbar(tk.Frame):
self.app.core.start_session() self.app.core.start_session()
self.runtime_frame.tkraise() self.runtime_frame.tkraise()
def click_link_tool(self): def click_link(self):
logging.debug("Click LINK button") logging.debug("Click LINK button")
self.design_select(self.link_button)
self.app.canvas.mode = GraphMode.EDGE self.app.canvas.mode = GraphMode.EDGE
def click_edit_node(self): def click_edit_node(self):
@ -240,6 +220,7 @@ class Toolbar(tk.Frame):
logging.info("update button(%s): %s", button, name) logging.info("update button(%s): %s", button, name)
self.hide_pickers() self.hide_pickers()
button.configure(image=image) button.configure(image=image)
button.image = image
self.app.canvas.mode = GraphMode.NODE self.app.canvas.mode = GraphMode.NODE
self.app.canvas.draw_node_image = image self.app.canvas.draw_node_image = image
self.app.canvas.draw_node_name = name self.app.canvas.draw_node_name = name
@ -262,29 +243,22 @@ class Toolbar(tk.Frame):
:return: nothing :return: nothing
""" """
router_image = Images.get(ImageEnum.ROUTER) image = icon(ImageEnum.ROUTER)
self.node_button = tk.Radiobutton( self.node_button = ttk.Button(
self.design_frame, self.design_frame, image=image, command=self.draw_node_picker
indicatoron=False,
variable=self.radio_value,
value=3,
width=self.width,
height=self.height,
image=router_image,
) )
self.node_button.bind("<ButtonRelease-1>", lambda e: self.draw_node_picker()) self.node_button.image = image
self.node_button.grid() self.node_button.grid(sticky="ew")
Tooltip(self.node_button, "Network-layer virtual nodes") Tooltip(self.node_button, "Network-layer virtual nodes")
def draw_network_picker(self): def draw_network_picker(self):
""" """
Draw the options for link-layer button Draw the options for link-layer button.
:param tkinter.RadioButton link_layer_button: link-layer button
:return: nothing :return: nothing
""" """
self.hide_pickers() self.hide_pickers()
self.network_picker = tk.Frame(self.master, padx=1, pady=1) self.network_picker = ttk.Frame(self.master)
nodes = [ nodes = [
(ImageEnum.HUB, "hub", "ethernet hub"), (ImageEnum.HUB, "hub", "ethernet hub"),
(ImageEnum.SWITCH, "switch", "ethernet switch"), (ImageEnum.SWITCH, "switch", "ethernet switch"),
@ -294,14 +268,17 @@ class Toolbar(tk.Frame):
(ImageEnum.TUNNEL, "tunnel", "tunnel tool"), (ImageEnum.TUNNEL, "tunnel", "tunnel tool"),
] ]
for image_enum, name, tooltip in nodes: for image_enum, name, tooltip in nodes:
image = Images.get(image_enum) image = icon(image_enum)
self.create_button( self.create_picker_button(
image, image,
partial(self.update_button, self.network_button, image, name), partial(self.update_button, self.network_button, image, name),
self.network_picker, self.network_picker,
tooltip, tooltip,
) )
self.show_picker(self.network_button, self.network_picker) self.design_select(self.network_button)
self.network_button.after(
0, lambda: self.show_picker(self.network_button, self.network_picker)
)
def create_network_button(self): def create_network_button(self):
""" """
@ -309,31 +286,22 @@ class Toolbar(tk.Frame):
:return: nothing :return: nothing
""" """
hub_image = Images.get(ImageEnum.HUB) image = icon(ImageEnum.HUB)
self.network_button = tk.Radiobutton( self.network_button = ttk.Button(
self.design_frame, self.design_frame, image=image, command=self.draw_network_picker
indicatoron=False,
variable=self.radio_value,
value=4,
width=self.width,
height=self.height,
image=hub_image,
) )
self.network_button.bind( self.network_button.image = image
"<ButtonRelease-1>", lambda e: self.draw_network_picker() self.network_button.grid(sticky="ew")
)
self.network_button.grid()
Tooltip(self.network_button, "link-layer nodes") Tooltip(self.network_button, "link-layer nodes")
def draw_annotation_picker(self): def draw_annotation_picker(self):
""" """
Draw the options for marker button Draw the options for marker button.
:param tkinter.Radiobutton main_button: the main button
:return: nothing :return: nothing
""" """
self.hide_pickers() self.hide_pickers()
self.annotation_picker = tk.Frame(self.master, padx=1, pady=1) self.annotation_picker = ttk.Frame(self.master)
nodes = [ nodes = [
(ImageEnum.MARKER, "marker"), (ImageEnum.MARKER, "marker"),
(ImageEnum.OVAL, "oval"), (ImageEnum.OVAL, "oval"),
@ -341,13 +309,17 @@ class Toolbar(tk.Frame):
(ImageEnum.TEXT, "text"), (ImageEnum.TEXT, "text"),
] ]
for image_enum, tooltip in nodes: for image_enum, tooltip in nodes:
self.create_button( image = icon(image_enum)
Images.get(image_enum), self.create_picker_button(
partial(self.update_annotation, image_enum), image,
partial(self.update_annotation, image),
self.annotation_picker, self.annotation_picker,
tooltip, tooltip,
) )
self.show_picker(self.annotation_button, self.annotation_picker) self.design_select(self.annotation_button)
self.annotation_button.after(
0, lambda: self.show_picker(self.annotation_button, self.annotation_picker)
)
def create_annotation_button(self): def create_annotation_button(self):
""" """
@ -355,53 +327,39 @@ class Toolbar(tk.Frame):
:return: nothing :return: nothing
""" """
marker_image = Images.get(ImageEnum.MARKER) image = icon(ImageEnum.MARKER)
self.annotation_button = tk.Radiobutton( self.annotation_button = ttk.Button(
self.design_frame, self.design_frame, image=image, command=self.draw_annotation_picker
indicatoron=False,
variable=self.radio_value,
value=5,
width=self.width,
height=self.height,
image=marker_image,
) )
self.annotation_button.bind( self.annotation_button.image = image
"<ButtonRelease-1>", lambda e: self.draw_annotation_picker() self.annotation_button.grid(sticky="ew")
)
self.annotation_button.grid()
Tooltip(self.annotation_button, "background annotation tools") Tooltip(self.annotation_button, "background annotation tools")
def create_observe_button(self): def create_observe_button(self):
menu_button = tk.Menubutton( menu_button = ttk.Menubutton(
self.runtime_frame, self.runtime_frame, image=icon(ImageEnum.OBSERVE), direction=tk.RIGHT
image=Images.get(ImageEnum.OBSERVE),
width=self.width,
height=self.height,
direction=tk.RIGHT,
relief=tk.RAISED,
) )
menu_button.menu = tk.Menu(menu_button, tearoff=0) menu_button.grid(sticky="ew")
menu_button["menu"] = menu_button.menu menu = tk.Menu(menu_button, tearoff=0)
menu_button.grid() menu_button["menu"] = menu
menu.add_command(label="None")
menu.add_command(label="processes")
menu.add_command(label="ifconfig")
menu.add_command(label="IPv4 routes")
menu.add_command(label="IPv6 routes")
menu.add_command(label="OSPFv2 neighbors")
menu.add_command(label="OSPFv3 neighbors")
menu.add_command(label="Listening sockets")
menu.add_command(label="IPv4 MFC entries")
menu.add_command(label="IPv6 MFC entries")
menu.add_command(label="firewall rules")
menu.add_command(label="IPSec policies")
menu.add_command(label="docker logs")
menu.add_command(label="OSPFv3 MDR level")
menu.add_command(label="PIM neighbors")
menu.add_command(label="Edit...")
menu_button.menu.add_command(label="None") def click_stop(self):
menu_button.menu.add_command(label="processes")
menu_button.menu.add_command(label="ifconfig")
menu_button.menu.add_command(label="IPv4 routes")
menu_button.menu.add_command(label="IPv6 routes")
menu_button.menu.add_command(label="OSPFv2 neighbors")
menu_button.menu.add_command(label="OSPFv3 neighbors")
menu_button.menu.add_command(label="Listening sockets")
menu_button.menu.add_command(label="IPv4 MFC entries")
menu_button.menu.add_command(label="IPv6 MFC entries")
menu_button.menu.add_command(label="firewall rules")
menu_button.menu.add_command(label="IPSec policies")
menu_button.menu.add_command(label="docker logs")
menu_button.menu.add_command(label="OSPFv3 MDR level")
menu_button.menu.add_command(label="PIM neighbors")
menu_button.menu.add_command(label="Edit...")
def click_stop_button(self):
""" """
redraw buttons on the toolbar, send node and link messages to grpc server redraw buttons on the toolbar, send node and link messages to grpc server
@ -411,10 +369,11 @@ class Toolbar(tk.Frame):
self.app.core.stop_session() self.app.core.stop_session()
self.design_frame.tkraise() self.design_frame.tkraise()
def update_annotation(self, image_enum): def update_annotation(self, image):
logging.info("clicked annotation: ") logging.info("clicked annotation: ")
self.hide_pickers() self.hide_pickers()
self.annotation_button.configure(image=Images.get(image_enum)) self.annotation_button.configure(image=image)
self.annotation_button.image = image
def click_run_button(self): def click_run_button(self):
logging.debug("Click on RUN button") logging.debug("Click on RUN button")