From 14187ba79c4471302a53660e248242668e139268 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Tue, 12 Nov 2019 17:32:34 -0800 Subject: [PATCH] tweaks to retrieving images/icons, updates to toolbar to use ttk widgets --- coretk/coretk/app.py | 9 +- coretk/coretk/coreclient.py | 4 +- coretk/coretk/dialogs/dialog.py | 2 +- coretk/coretk/dialogs/emaneconfig.py | 14 +- coretk/coretk/dialogs/icondialog.py | 2 +- coretk/coretk/dialogs/sessions.py | 20 +- coretk/coretk/graph.py | 4 +- coretk/coretk/graph_helper.py | 3 +- coretk/coretk/images.py | 90 ++++---- coretk/coretk/toolbar.py | 307 ++++++++++++--------------- 10 files changed, 221 insertions(+), 234 deletions(-) diff --git a/coretk/coretk/app.py b/coretk/coretk/app.py index 276dbd6d..452d5397 100644 --- a/coretk/coretk/app.py +++ b/coretk/coretk/app.py @@ -1,5 +1,6 @@ import logging import tkinter as tk +from tkinter import ttk from coretk import appconfig from coretk.coreclient import CoreClient @@ -36,7 +37,7 @@ class Application(tk.Frame): self.master.title("CORE") self.master.geometry("1000x800") 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.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.canvas.pack(fill=tk.BOTH, expand=True) - scroll_x = tk.Scrollbar( + scroll_x = ttk.Scrollbar( self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview ) 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) self.canvas.configure(xscrollcommand=scroll_x.set) self.canvas.configure(yscrollcommand=scroll_y.set) def draw_status(self): - self.statusbar = tk.Frame(self) + self.statusbar = ttk.Frame(self) self.statusbar.pack(side=tk.BOTTOM, fill=tk.X) def on_closing(self): diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index 9e18a413..d28ad49e 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -8,7 +8,7 @@ from core.api.grpc import client, core_pb2 from coretk.coretocanvas import CoreToCanvasMapping from coretk.dialogs.sessions import SessionsDialog 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.mobilitynodeconfig import MobilityNodeConfig from coretk.wlannodeconfig import WlanNodeConfig @@ -137,7 +137,7 @@ class CoreClient: # read custom nodes for config in self.app.config.get("nodes", []): image_file = config["image"] - image = Images.get_custom(image_file) + image = Images.get_custom(image_file, NODE_WIDTH) custom_node = CustomNode( config["name"], image, image_file, set(config["services"]) ) diff --git a/coretk/coretk/dialogs/dialog.py b/coretk/coretk/dialogs/dialog.py index 908523f2..c043a47a 100644 --- a/coretk/coretk/dialogs/dialog.py +++ b/coretk/coretk/dialogs/dialog.py @@ -13,7 +13,7 @@ class Dialog(tk.Toplevel): self.modal = modal self.title(title) 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) def show(self): diff --git a/coretk/coretk/dialogs/emaneconfig.py b/coretk/coretk/dialogs/emaneconfig.py index 7fd577e2..8673d3fc 100644 --- a/coretk/coretk/dialogs/emaneconfig.py +++ b/coretk/coretk/dialogs/emaneconfig.py @@ -182,25 +182,31 @@ class EmaneConfiguration(Dialog): def draw_option_buttons(self, parent): f = ttk.Frame(parent) + f.grid(row=4, column=0, sticky="nsew") f.columnconfigure(0, weight=1) f.columnconfigure(1, weight=1) + + image = Images.get(ImageEnum.EDITNODE, 16) b = ttk.Button( f, text=self.emane_models[0] + " options", - image=Images.get(ImageEnum.EDITNODE), + image=image, compound=tk.RIGHT, command=self.draw_model_options, ) + b.image = image b.grid(row=0, column=0, padx=10, pady=2, sticky="nsew") + + image = Images.get(ImageEnum.EDITNODE, 16) b = ttk.Button( f, text="EMANE options", - image=Images.get(ImageEnum.EDITNODE), + image=image, compound=tk.RIGHT, command=self.draw_emane_options, ) + b.image = image 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): """ @@ -271,7 +277,7 @@ class EmaneConfiguration(Dialog): b = ttk.Button( f, - image=Images.get(ImageEnum.EDITNODE), + image=Images.get(ImageEnum.EDITNODE, 8), text="EMANE Wiki", compound=tk.RIGHT, command=lambda: webbrowser.open_new( diff --git a/coretk/coretk/dialogs/icondialog.py b/coretk/coretk/dialogs/icondialog.py index fb6fb6bb..15d8000b 100644 --- a/coretk/coretk/dialogs/icondialog.py +++ b/coretk/coretk/dialogs/icondialog.py @@ -54,7 +54,7 @@ class IconDialog(Dialog): ), ) 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.file_path.set(file_path) diff --git a/coretk/coretk/dialogs/sessions.py b/coretk/coretk/dialogs/sessions.py index 40d09c44..b9b7d77b 100644 --- a/coretk/coretk/dialogs/sessions.py +++ b/coretk/coretk/dialogs/sessions.py @@ -73,30 +73,36 @@ class SessionsDialog(Dialog): for i in range(4): frame.columnconfigure(i, weight=1) frame.grid(row=3, sticky="ew") + + image = Images.get(ImageEnum.DOCUMENTNEW, 16) b = ttk.Button( - frame, - image=Images.get(ImageEnum.DOCUMENTNEW), - text="New", - compound=tk.LEFT, - command=self.click_new, + frame, image=image, text="New", compound=tk.LEFT, command=self.click_new ) + b.image = image b.grid(row=0, padx=2, sticky="ew") + + image = Images.get(ImageEnum.FILEOPEN, 16) b = ttk.Button( frame, - image=Images.get(ImageEnum.FILEOPEN), + image=image, text="Connect", compound=tk.LEFT, command=self.click_connect, ) + b.image = image b.grid(row=0, column=1, padx=2, sticky="ew") + + image = Images.get(ImageEnum.EDITDELETE, 16) b = ttk.Button( frame, - image=Images.get(ImageEnum.EDITDELETE), + image=image, text="Shutdown", compound=tk.LEFT, command=self.click_shutdown, ) + b.image = image b.grid(row=0, column=2, padx=2, sticky="ew") + b = ttk.Button(frame, text="Cancel", command=self.click_new) b.grid(row=0, column=3, padx=2, sticky="ew") diff --git a/coretk/coretk/graph.py b/coretk/coretk/graph.py index e813ae48..5cdd2eec 100644 --- a/coretk/coretk/graph.py +++ b/coretk/coretk/graph.py @@ -146,9 +146,7 @@ class CanvasGraph(tk.Canvas): # peer to peer node is not drawn on the GUI if node.type != core_pb2.NodeType.PEER_TO_PEER: # draw nodes on the canvas - image, name = Images.convert_type_and_model_to_image( - node.type, node.model - ) + image, name = Images.node_icon(node.type, node.model) n = CanvasNode( node.position.x, node.position.y, image, name, self.master, node.id ) diff --git a/coretk/coretk/graph_helper.py b/coretk/coretk/graph_helper.py index bdedd65e..c1f59815 100644 --- a/coretk/coretk/graph_helper.py +++ b/coretk/coretk/graph_helper.py @@ -77,6 +77,7 @@ class WlanAntennaManager: self.quantity = 0 self._max = 5 self.antennas = [] + self.image = Images.get(ImageEnum.ANTENNA, 32) # distance between each antenna self.offset = 0 @@ -94,7 +95,7 @@ class WlanAntennaManager: x - 16 + self.offset, y - 16, anchor=tk.CENTER, - image=Images.get(ImageEnum.ANTENNA), + image=self.image, tags="antenna", ) ) diff --git a/coretk/coretk/images.py b/coretk/coretk/images.py index d064f77e..cebcb49c 100644 --- a/coretk/coretk/images.py +++ b/coretk/coretk/images.py @@ -6,35 +6,37 @@ from PIL import Image, ImageTk from core.api.grpc import core_pb2 from coretk.appconfig import LOCAL_ICONS_PATH +NODE_WIDTH = 32 + class Images: images = {} @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.resize((width, height), Image.ANTIALIAS) return ImageTk.PhotoImage(image) @classmethod def load_all(cls): for image in LOCAL_ICONS_PATH.glob("*"): - cls.load(image.stem, str(image)) + cls.images[image.stem] = str(image) @classmethod - def load(cls, name, file_path): - tk_image = cls.create(file_path) - cls.images[name] = tk_image + def get(cls, image_enum, width, height=None): + file_path = cls.images[image_enum.value] + return cls.create(file_path, width, height) @classmethod - def get(cls, image): - return cls.images[image.value] + def get_custom(cls, name, width, height): + file_path = cls.images[name] + return cls.create(file_path, width, height) @classmethod - def get_custom(cls, name): - return cls.images[name] - - @classmethod - def convert_type_and_model_to_image(cls, node_type, node_model): + def node_icon(cls, node_type, node_model): """ Retrieve image based on type and model :param core_pb2.NodeType node_type: core node type @@ -43,34 +45,48 @@ class Images: :rtype: tuple(PhotoImage, str) :return: the matching image and its name """ + image_enum = ImageEnum.ROUTER + name = "unknown" if node_type == core_pb2.NodeType.SWITCH: - return Images.get(ImageEnum.SWITCH), "switch" - if node_type == core_pb2.NodeType.HUB: - return Images.get(ImageEnum.HUB), "hub" - if node_type == core_pb2.NodeType.WIRELESS_LAN: - return Images.get(ImageEnum.WLAN), "wlan" - if node_type == core_pb2.NodeType.EMANE: - return Images.get(ImageEnum.EMANE), "emane" - - if node_type == core_pb2.NodeType.RJ45: - return Images.get(ImageEnum.RJ45), "rj45" - if node_type == core_pb2.NodeType.TUNNEL: - return Images.get(ImageEnum.TUNNEL), "tunnel" - if node_type == core_pb2.NodeType.DEFAULT: + image_enum = ImageEnum.SWITCH + name = "switch" + elif node_type == core_pb2.NodeType.HUB: + image_enum = ImageEnum.HUB + name = "hub" + elif node_type == core_pb2.NodeType.WIRELESS_LAN: + image_enum = ImageEnum.WLAN + name = "wlan" + elif node_type == core_pb2.NodeType.EMANE: + image_enum = ImageEnum.EMANE + name = "emane" + elif node_type == core_pb2.NodeType.RJ45: + 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": - return Images.get(ImageEnum.ROUTER), "router" - if node_model == "host": - return Images.get(ImageEnum.HOST), "host" - if node_model == "PC": - return Images.get(ImageEnum.PC), "PC" - if node_model == "mdr": - return Images.get(ImageEnum.MDR), "mdr" - if node_model == "prouter": - return Images.get(ImageEnum.PROUTER), "prouter" - if node_model == "OVS": - return Images.get(ImageEnum.OVS), "ovs" + image_enum = ImageEnum.ROUTER + name = "router" + elif node_model == "host": + image_enum = ImageEnum.HOST + name = "host" + elif node_model == "PC": + image_enum = ImageEnum.PC + name = "PC" + elif node_model == "mdr": + image_enum = ImageEnum.MDR + name = "mdr" + elif node_model == "prouter": + image_enum = ImageEnum.PROUTER + name = "prouter" + else: + logging.error("invalid node model: %s", node_model) else: - logging.debug("INVALID INPUT OR NOT CONSIDERED YET") + logging.error("invalid node type: %s", node_type) + + return Images.get(image_enum, NODE_WIDTH), name class ImageEnum(Enum): diff --git a/coretk/coretk/toolbar.py b/coretk/coretk/toolbar.py index 752b1000..89393c3e 100644 --- a/coretk/coretk/toolbar.py +++ b/coretk/coretk/toolbar.py @@ -1,43 +1,44 @@ import logging import tkinter as tk from functools import partial +from tkinter import ttk from coretk.dialogs.customnodes import CustomNodesDialog from coretk.graph import GraphMode from coretk.images import ImageEnum, Images 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 """ - def __init__(self, master, app, cnf={}, **kwargs): + def __init__(self, master, app, **kwargs): """ Create a CoreToolbar instance :param tkinter.Frame edit_frame: edit frame """ - super().__init__(master, cnf, **kwargs) + super().__init__(master, **kwargs) self.app = app self.master = app.master - self.radio_value = tk.IntVar() - self.exec_radio_value = tk.IntVar() - # button dimension - self.width = 32 - self.height = 32 - - # 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 + # design buttons + self.select_button = None + self.link_button = None self.node_button = None self.network_button = None self.annotation_button = None + # runtime buttons + # frames self.design_frame = None self.runtime_frame = None @@ -56,89 +57,77 @@ class Toolbar(tk.Frame): self.design_frame.tkraise() 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.columnconfigure(0, weight=1) - - self.create_regular_button( + self.create_button( self.design_frame, - Images.get(ImageEnum.START), - self.click_start_session_tool, + icon(ImageEnum.START), + self.click_start, "start the session", ) - self.create_radio_button( + self.select_button = self.create_button( self.design_frame, - Images.get(ImageEnum.SELECT), - self.click_selection_tool, - self.radio_value, - 1, + icon(ImageEnum.SELECT), + self.click_selection, "selection tool", ) - self.create_radio_button( - self.design_frame, - Images.get(ImageEnum.LINK), - self.click_link_tool, - self.radio_value, - 2, - "link tool", + self.link_button = self.create_button( + self.design_frame, icon(ImageEnum.LINK), self.click_link, "link tool" ) self.create_node_button() self.create_network_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): - 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.columnconfigure(0, weight=1) - self.create_regular_button( + self.create_button( self.runtime_frame, - Images.get(ImageEnum.STOP), - self.click_stop_button, + icon(ImageEnum.STOP), + self.click_stop, "stop the session", ) - self.create_radio_button( + self.create_button( self.runtime_frame, - Images.get(ImageEnum.SELECT), - self.click_selection_tool, - self.exec_radio_value, - 1, + icon(ImageEnum.SELECT), + self.click_selection, "selection tool", ) - self.create_observe_button() - self.create_radio_button( - self.runtime_frame, - Images.get(ImageEnum.PLOT), - self.click_plot_button, - self.exec_radio_value, - 2, - "plot", + # self.create_observe_button() + self.create_button( + self.runtime_frame, icon(ImageEnum.PLOT), self.click_plot_button, "plot" ) - self.create_radio_button( + self.create_button( self.runtime_frame, - Images.get(ImageEnum.MARKER), + icon(ImageEnum.MARKER), self.click_marker_button, - self.exec_radio_value, - 3, "marker", ) - self.create_radio_button( + self.create_button( self.runtime_frame, - Images.get(ImageEnum.TWONODE), + icon(ImageEnum.TWONODE), self.click_two_node_button, - self.exec_radio_value, - 4, "run command from one node to another", ) - self.create_regular_button( - self.runtime_frame, Images.get(ImageEnum.RUN), self.click_run_button, "run" + self.create_button( + self.runtime_frame, icon(ImageEnum.RUN), self.click_run_button, "run" ) - self.exec_radio_value.set(1) def draw_node_picker(self): self.hide_pickers() - self.node_picker = tk.Frame(self.master, padx=1, pady=1) + self.node_picker = ttk.Frame(self.master) nodes = [ (ImageEnum.ROUTER, "router"), (ImageEnum.HOST, "host"), @@ -148,26 +137,28 @@ class Toolbar(tk.Frame): ] # draw default 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) - self.create_button(image, func, self.node_picker, tooltip) + self.create_picker_button(image, func, self.node_picker, tooltip) # draw custom nodes for name in sorted(self.app.core.custom_nodes): custom_node = self.app.core.custom_nodes[name] image = custom_node.image 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 - image = Images.get(ImageEnum.EDITNODE) - self.create_button( + image = icon(ImageEnum.EDITNODE) + self.create_picker_button( 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): - first_button = self.winfo_children()[0] - x = button.winfo_rootx() - first_button.winfo_rootx() + 40 - y = button.winfo_rooty() - first_button.winfo_rooty() - 1 + x = self.winfo_width() + 1 + y = button.winfo_rooty() - picker.master.winfo_rooty() - 1 picker.place(x=x, y=y) self.app.bind_all("", lambda e: self.hide_pickers()) picker.wait_visibility() @@ -175,7 +166,7 @@ class Toolbar(tk.Frame): self.wait_window(picker) self.app.unbind_all("") - 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 @@ -185,37 +176,25 @@ class Toolbar(tk.Frame): :param str tooltip: tooltip text :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("", lambda e: func()) button.grid(pady=1) Tooltip(button, tooltip) - def create_radio_button(self, frame, image, func, variable, value, tooltip_msg): - button = tk.Radiobutton( - frame, - indicatoron=False, - 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() + def create_button(self, frame, image, func, tooltip): + button = ttk.Button(frame, image=image, command=func) + button.image = image + button.grid(sticky="ew") Tooltip(button, tooltip) + return button - def click_selection_tool(self): + def click_selection(self): logging.debug("clicked selection tool") + self.design_select(self.select_button) 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 server. @@ -227,8 +206,9 @@ class Toolbar(tk.Frame): self.app.core.start_session() self.runtime_frame.tkraise() - def click_link_tool(self): + def click_link(self): logging.debug("Click LINK button") + self.design_select(self.link_button) self.app.canvas.mode = GraphMode.EDGE def click_edit_node(self): @@ -240,6 +220,7 @@ class Toolbar(tk.Frame): logging.info("update button(%s): %s", button, name) self.hide_pickers() button.configure(image=image) + button.image = image self.app.canvas.mode = GraphMode.NODE self.app.canvas.draw_node_image = image self.app.canvas.draw_node_name = name @@ -262,29 +243,22 @@ class Toolbar(tk.Frame): :return: nothing """ - router_image = Images.get(ImageEnum.ROUTER) - self.node_button = tk.Radiobutton( - self.design_frame, - indicatoron=False, - variable=self.radio_value, - value=3, - width=self.width, - height=self.height, - image=router_image, + image = icon(ImageEnum.ROUTER) + self.node_button = ttk.Button( + self.design_frame, image=image, command=self.draw_node_picker ) - self.node_button.bind("", lambda e: self.draw_node_picker()) - self.node_button.grid() + self.node_button.image = image + self.node_button.grid(sticky="ew") Tooltip(self.node_button, "Network-layer virtual nodes") 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 """ self.hide_pickers() - self.network_picker = tk.Frame(self.master, padx=1, pady=1) + self.network_picker = ttk.Frame(self.master) nodes = [ (ImageEnum.HUB, "hub", "ethernet hub"), (ImageEnum.SWITCH, "switch", "ethernet switch"), @@ -294,14 +268,17 @@ class Toolbar(tk.Frame): (ImageEnum.TUNNEL, "tunnel", "tunnel tool"), ] for image_enum, name, tooltip in nodes: - image = Images.get(image_enum) - self.create_button( + image = icon(image_enum) + self.create_picker_button( image, partial(self.update_button, self.network_button, image, name), self.network_picker, 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): """ @@ -309,31 +286,22 @@ class Toolbar(tk.Frame): :return: nothing """ - hub_image = Images.get(ImageEnum.HUB) - self.network_button = tk.Radiobutton( - self.design_frame, - indicatoron=False, - variable=self.radio_value, - value=4, - width=self.width, - height=self.height, - image=hub_image, + image = icon(ImageEnum.HUB) + self.network_button = ttk.Button( + self.design_frame, image=image, command=self.draw_network_picker ) - self.network_button.bind( - "", lambda e: self.draw_network_picker() - ) - self.network_button.grid() + self.network_button.image = image + self.network_button.grid(sticky="ew") Tooltip(self.network_button, "link-layer nodes") 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 """ self.hide_pickers() - self.annotation_picker = tk.Frame(self.master, padx=1, pady=1) + self.annotation_picker = ttk.Frame(self.master) nodes = [ (ImageEnum.MARKER, "marker"), (ImageEnum.OVAL, "oval"), @@ -341,13 +309,17 @@ class Toolbar(tk.Frame): (ImageEnum.TEXT, "text"), ] for image_enum, tooltip in nodes: - self.create_button( - Images.get(image_enum), - partial(self.update_annotation, image_enum), + image = icon(image_enum) + self.create_picker_button( + image, + partial(self.update_annotation, image), self.annotation_picker, 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): """ @@ -355,53 +327,39 @@ class Toolbar(tk.Frame): :return: nothing """ - marker_image = Images.get(ImageEnum.MARKER) - self.annotation_button = tk.Radiobutton( - self.design_frame, - indicatoron=False, - variable=self.radio_value, - value=5, - width=self.width, - height=self.height, - image=marker_image, + image = icon(ImageEnum.MARKER) + self.annotation_button = ttk.Button( + self.design_frame, image=image, command=self.draw_annotation_picker ) - self.annotation_button.bind( - "", lambda e: self.draw_annotation_picker() - ) - self.annotation_button.grid() + self.annotation_button.image = image + self.annotation_button.grid(sticky="ew") Tooltip(self.annotation_button, "background annotation tools") def create_observe_button(self): - menu_button = tk.Menubutton( - self.runtime_frame, - image=Images.get(ImageEnum.OBSERVE), - width=self.width, - height=self.height, - direction=tk.RIGHT, - relief=tk.RAISED, + menu_button = ttk.Menubutton( + self.runtime_frame, image=icon(ImageEnum.OBSERVE), direction=tk.RIGHT ) - menu_button.menu = tk.Menu(menu_button, tearoff=0) - menu_button["menu"] = menu_button.menu - menu_button.grid() + menu_button.grid(sticky="ew") + menu = tk.Menu(menu_button, tearoff=0) + 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") - 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): + def click_stop(self): """ 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.design_frame.tkraise() - def update_annotation(self, image_enum): + def update_annotation(self, image): logging.info("clicked annotation: ") 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): logging.debug("Click on RUN button")