pygui: removed duplicate get_icon functionality, added more type hints, added enable/disable of toolbar button when running start/stop

This commit is contained in:
Blake Harnden 2020-05-15 11:41:18 -07:00
parent 5e69ea48b3
commit ee5d5b9864

View file

@ -5,6 +5,8 @@ from functools import partial
from tkinter import ttk from tkinter import ttk
from typing import TYPE_CHECKING, Callable from typing import TYPE_CHECKING, Callable
from PIL.ImageTk import PhotoImage
from core.api.grpc import core_pb2 from core.api.grpc import core_pb2
from core.gui.dialogs.marker import MarkerDialog from core.gui.dialogs.marker import MarkerDialog
from core.gui.dialogs.runtool import RunToolDialog from core.gui.dialogs.runtool import RunToolDialog
@ -18,7 +20,6 @@ from core.gui.tooltip import Tooltip
if TYPE_CHECKING: if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
from PIL import ImageTk
TOOLBAR_SIZE = 32 TOOLBAR_SIZE = 32
PICKER_SIZE = 24 PICKER_SIZE = 24
@ -30,8 +31,10 @@ class NodeTypeEnum(Enum):
OTHER = 2 OTHER = 2
def icon(image_enum, width=TOOLBAR_SIZE): def enable_buttons(frame: ttk.Frame, enabled: bool) -> None:
return Images.get(image_enum, width) state = tk.NORMAL if enabled else tk.DISABLED
for child in frame.winfo_children():
child.configure(state=state)
class Toolbar(ttk.Frame): class Toolbar(ttk.Frame):
@ -39,7 +42,7 @@ class Toolbar(ttk.Frame):
Core toolbar class Core toolbar class
""" """
def __init__(self, master: tk.Widget, app: "Application", **kwargs): def __init__(self, master: tk.Widget, app: "Application", **kwargs) -> None:
""" """
Create a CoreToolbar instance Create a CoreToolbar instance
""" """
@ -71,7 +74,7 @@ class Toolbar(ttk.Frame):
self.marker_tool = None self.marker_tool = None
# these variables help keep track of what images being drawn so that scaling # these variables help keep track of what images being drawn so that scaling
# is possible since ImageTk.PhotoImage does not have resize method # is possible since PhotoImage does not have resize method
self.node_enum = None self.node_enum = None
self.network_enum = None self.network_enum = None
self.annotation_enum = None self.annotation_enum = None
@ -79,35 +82,35 @@ class Toolbar(ttk.Frame):
# draw components # draw components
self.draw() self.draw()
def get_icon(self, image_enum, width=TOOLBAR_SIZE): def get_icon(self, image_enum: ImageEnum, width: int) -> PhotoImage:
return Images.get(image_enum, int(width * self.app.app_scale)) return Images.get(image_enum, int(width * self.app.app_scale))
def draw(self): def draw(self) -> None:
self.columnconfigure(0, weight=1) self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1) self.rowconfigure(0, weight=1)
self.draw_design_frame() self.draw_design_frame()
self.draw_runtime_frame() self.draw_runtime_frame()
self.design_frame.tkraise() self.design_frame.tkraise()
def draw_design_frame(self): def draw_design_frame(self) -> None:
self.design_frame = ttk.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.play_button = self.create_button( self.play_button = self.create_button(
self.design_frame, self.design_frame,
self.get_icon(ImageEnum.START), self.get_icon(ImageEnum.START, TOOLBAR_SIZE),
self.click_start, self.click_start,
"start the session", "start the session",
) )
self.select_button = self.create_button( self.select_button = self.create_button(
self.design_frame, self.design_frame,
self.get_icon(ImageEnum.SELECT), self.get_icon(ImageEnum.SELECT, TOOLBAR_SIZE),
self.click_selection, self.click_selection,
"selection tool", "selection tool",
) )
self.link_button = self.create_button( self.link_button = self.create_button(
self.design_frame, self.design_frame,
self.get_icon(ImageEnum.LINK), self.get_icon(ImageEnum.LINK, TOOLBAR_SIZE),
self.click_link, self.click_link,
"link tool", "link tool",
) )
@ -115,7 +118,7 @@ class Toolbar(ttk.Frame):
self.create_network_button() self.create_network_button()
self.create_annotation_button() self.create_annotation_button()
def design_select(self, button: ttk.Button): def design_select(self, button: ttk.Button) -> None:
logging.debug("selecting design button: %s", button) logging.debug("selecting design button: %s", button)
self.select_button.state(["!pressed"]) self.select_button.state(["!pressed"])
self.link_button.state(["!pressed"]) self.link_button.state(["!pressed"])
@ -124,7 +127,7 @@ class Toolbar(ttk.Frame):
self.annotation_button.state(["!pressed"]) self.annotation_button.state(["!pressed"])
button.state(["pressed"]) button.state(["pressed"])
def runtime_select(self, button: ttk.Button): def runtime_select(self, button: ttk.Button) -> None:
logging.debug("selecting runtime button: %s", button) logging.debug("selecting runtime button: %s", button)
self.runtime_select_button.state(["!pressed"]) self.runtime_select_button.state(["!pressed"])
self.stop_button.state(["!pressed"]) self.stop_button.state(["!pressed"])
@ -132,33 +135,36 @@ class Toolbar(ttk.Frame):
self.run_command_button.state(["!pressed"]) self.run_command_button.state(["!pressed"])
button.state(["pressed"]) button.state(["pressed"])
def draw_runtime_frame(self): def draw_runtime_frame(self) -> None:
self.runtime_frame = ttk.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.stop_button = self.create_button( self.stop_button = self.create_button(
self.runtime_frame, self.runtime_frame,
self.get_icon(ImageEnum.STOP), self.get_icon(ImageEnum.STOP, TOOLBAR_SIZE),
self.click_stop, self.click_stop,
"stop the session", "stop the session",
) )
self.runtime_select_button = self.create_button( self.runtime_select_button = self.create_button(
self.runtime_frame, self.runtime_frame,
self.get_icon(ImageEnum.SELECT), self.get_icon(ImageEnum.SELECT, TOOLBAR_SIZE),
self.click_runtime_selection, self.click_runtime_selection,
"selection tool", "selection tool",
) )
self.runtime_marker_button = self.create_button( self.runtime_marker_button = self.create_button(
self.runtime_frame, self.runtime_frame,
icon(ImageEnum.MARKER), self.get_icon(ImageEnum.MARKER, TOOLBAR_SIZE),
self.click_marker_button, self.click_marker_button,
"marker", "marker",
) )
self.run_command_button = self.create_button( self.run_command_button = self.create_button(
self.runtime_frame, icon(ImageEnum.RUN), self.click_run_button, "run" self.runtime_frame,
self.get_icon(ImageEnum.RUN, TOOLBAR_SIZE),
self.click_run_button,
"run",
) )
def draw_node_picker(self): def draw_node_picker(self) -> None:
self.hide_pickers() self.hide_pickers()
self.node_picker = ttk.Frame(self.master) self.node_picker = ttk.Frame(self.master)
# draw default nodes # draw default nodes
@ -197,7 +203,7 @@ class Toolbar(ttk.Frame):
0, lambda: self.show_picker(self.node_button, self.node_picker) 0, lambda: self.show_picker(self.node_button, self.node_picker)
) )
def show_picker(self, button: ttk.Button, picker: ttk.Frame): def show_picker(self, button: ttk.Button, picker: ttk.Frame) -> None:
x = self.winfo_width() + 1 x = self.winfo_width() + 1
y = button.winfo_rooty() - picker.master.winfo_rooty() - 1 y = button.winfo_rooty() - picker.master.winfo_rooty() - 1
picker.place(x=x, y=y) picker.place(x=x, y=y)
@ -208,8 +214,8 @@ class Toolbar(ttk.Frame):
self.app.unbind_all("<ButtonRelease-1>") self.app.unbind_all("<ButtonRelease-1>")
def create_picker_button( def create_picker_button(
self, image: "ImageTk.PhotoImage", func: Callable, frame: ttk.Frame, label: str self, image: PhotoImage, func: Callable, frame: ttk.Frame, label: str
): ) -> None:
""" """
Create button and put it on the frame Create button and put it on the frame
@ -226,58 +232,58 @@ class Toolbar(ttk.Frame):
button.grid(pady=1) button.grid(pady=1)
def create_button( def create_button(
self, self, frame: ttk.Frame, image: PhotoImage, func: Callable, tooltip: str
frame: ttk.Frame, ) -> ttk.Button:
image: "ImageTk.PhotoImage",
func: Callable,
tooltip: str,
):
button = ttk.Button(frame, image=image, command=func) button = ttk.Button(frame, image=image, command=func)
button.image = image button.image = image
button.grid(sticky="ew") button.grid(sticky="ew")
Tooltip(button, tooltip) Tooltip(button, tooltip)
return button return button
def click_selection(self): def click_selection(self) -> None:
logging.debug("clicked selection tool") logging.debug("clicked selection tool")
self.design_select(self.select_button) self.design_select(self.select_button)
self.app.canvas.mode = GraphMode.SELECT self.app.canvas.mode = GraphMode.SELECT
def click_runtime_selection(self): def click_runtime_selection(self) -> None:
logging.debug("clicked selection tool") logging.debug("clicked selection tool")
self.runtime_select(self.runtime_select_button) self.runtime_select(self.runtime_select_button)
self.app.canvas.mode = GraphMode.SELECT self.app.canvas.mode = GraphMode.SELECT
def click_start(self): def click_start(self) -> None:
""" """
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.
""" """
self.app.menubar.change_menubar_item_state(is_runtime=True) self.app.menubar.change_menubar_item_state(is_runtime=True)
self.app.canvas.mode = GraphMode.SELECT self.app.canvas.mode = GraphMode.SELECT
enable_buttons(self.design_frame, enabled=False)
task = ProgressTask( task = ProgressTask(
self.app, "Start", self.app.core.start_session, self.start_callback self.app, "Start", self.app.core.start_session, self.start_callback
) )
task.start() task.start()
def start_callback(self, response: core_pb2.StartSessionResponse): def start_callback(self, response: core_pb2.StartSessionResponse) -> None:
if response.result: if response.result:
self.set_runtime() self.set_runtime()
self.app.core.set_metadata() self.app.core.set_metadata()
self.app.core.show_mobility_players() self.app.core.show_mobility_players()
else: else:
enable_buttons(self.design_frame, enabled=True)
message = "\n".join(response.exceptions) message = "\n".join(response.exceptions)
self.app.show_error("Start Session Error", message) self.app.show_error("Start Session Error", message)
def set_runtime(self): def set_runtime(self) -> None:
enable_buttons(self.runtime_frame, enabled=True)
self.runtime_frame.tkraise() self.runtime_frame.tkraise()
self.click_runtime_selection() self.click_runtime_selection()
def set_design(self): def set_design(self) -> None:
enable_buttons(self.design_frame, enabled=True)
self.design_frame.tkraise() self.design_frame.tkraise()
self.click_selection() self.click_selection()
def click_link(self): def click_link(self) -> None:
logging.debug("Click LINK button") logging.debug("Click LINK button")
self.design_select(self.link_button) self.design_select(self.link_button)
self.app.canvas.mode = GraphMode.EDGE self.app.canvas.mode = GraphMode.EDGE
@ -285,11 +291,11 @@ class Toolbar(ttk.Frame):
def update_button( def update_button(
self, self,
button: ttk.Button, button: ttk.Button,
image: "ImageTk", image: PhotoImage,
node_draw: NodeDraw, node_draw: NodeDraw,
type_enum, type_enum,
image_enum, image_enum,
): ) -> None:
logging.debug("update button(%s): %s", button, node_draw) logging.debug("update button(%s): %s", button, node_draw)
self.hide_pickers() self.hide_pickers()
button.configure(image=image) button.configure(image=image)
@ -301,7 +307,7 @@ class Toolbar(ttk.Frame):
elif type_enum == NodeTypeEnum.NETWORK: elif type_enum == NodeTypeEnum.NETWORK:
self.network_enum = image_enum self.network_enum = image_enum
def hide_pickers(self): def hide_pickers(self) -> None:
logging.debug("hiding pickers") logging.debug("hiding pickers")
if self.node_picker: if self.node_picker:
self.node_picker.destroy() self.node_picker.destroy()
@ -313,7 +319,7 @@ class Toolbar(ttk.Frame):
self.annotation_picker.destroy() self.annotation_picker.destroy()
self.annotation_picker = None self.annotation_picker = None
def create_node_button(self): def create_node_button(self) -> None:
""" """
Create network layer button Create network layer button
""" """
@ -326,7 +332,7 @@ class Toolbar(ttk.Frame):
Tooltip(self.node_button, "Network-layer virtual nodes") Tooltip(self.node_button, "Network-layer virtual nodes")
self.node_enum = ImageEnum.ROUTER self.node_enum = ImageEnum.ROUTER
def draw_network_picker(self): def draw_network_picker(self) -> None:
""" """
Draw the options for link-layer button. Draw the options for link-layer button.
""" """
@ -353,7 +359,7 @@ class Toolbar(ttk.Frame):
0, lambda: self.show_picker(self.network_button, self.network_picker) 0, lambda: self.show_picker(self.network_button, self.network_picker)
) )
def create_network_button(self): def create_network_button(self) -> None:
""" """
Create link-layer node button and the options that represent different Create link-layer node button and the options that represent different
link-layer node types. link-layer node types.
@ -367,7 +373,7 @@ class Toolbar(ttk.Frame):
Tooltip(self.network_button, "link-layer nodes") Tooltip(self.network_button, "link-layer nodes")
self.network_enum = ImageEnum.HUB self.network_enum = ImageEnum.HUB
def draw_annotation_picker(self): def draw_annotation_picker(self) -> None:
""" """
Draw the options for marker button. Draw the options for marker button.
""" """
@ -393,7 +399,7 @@ class Toolbar(ttk.Frame):
0, lambda: self.show_picker(self.annotation_button, self.annotation_picker) 0, lambda: self.show_picker(self.annotation_button, self.annotation_picker)
) )
def create_annotation_button(self): def create_annotation_button(self) -> None:
""" """
Create marker button and options that represent different marker types Create marker button and options that represent different marker types
""" """
@ -406,9 +412,10 @@ class Toolbar(ttk.Frame):
Tooltip(self.annotation_button, "background annotation tools") Tooltip(self.annotation_button, "background annotation tools")
self.annotation_enum = ImageEnum.MARKER self.annotation_enum = ImageEnum.MARKER
def create_observe_button(self): def create_observe_button(self) -> None:
image = self.get_icon(ImageEnum.OBSERVE, TOOLBAR_SIZE)
menu_button = ttk.Menubutton( menu_button = ttk.Menubutton(
self.runtime_frame, image=icon(ImageEnum.OBSERVE), direction=tk.RIGHT self.runtime_frame, image=image, direction=tk.RIGHT
) )
menu_button.grid(sticky="ew") menu_button.grid(sticky="ew")
menu = tk.Menu(menu_button, tearoff=0) menu = tk.Menu(menu_button, tearoff=0)
@ -430,25 +437,26 @@ class Toolbar(ttk.Frame):
menu.add_command(label="PIM neighbors") menu.add_command(label="PIM neighbors")
menu.add_command(label="Edit...") menu.add_command(label="Edit...")
def click_stop(self): def click_stop(self) -> None:
""" """
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
""" """
logging.info("clicked stop button") logging.info("clicked stop button")
self.app.menubar.change_menubar_item_state(is_runtime=False) self.app.menubar.change_menubar_item_state(is_runtime=False)
self.app.core.close_mobility_players() self.app.core.close_mobility_players()
enable_buttons(self.runtime_frame, enabled=False)
task = ProgressTask( task = ProgressTask(
self.app, "Stop", self.app.core.stop_session, self.stop_callback self.app, "Stop", self.app.core.stop_session, self.stop_callback
) )
task.start() task.start()
def stop_callback(self, response: core_pb2.StopSessionResponse): def stop_callback(self, response: core_pb2.StopSessionResponse) -> None:
self.set_design() self.set_design()
self.app.canvas.stopped_session() self.app.canvas.stopped_session()
def update_annotation( def update_annotation(
self, image: "ImageTk.PhotoImage", shape_type: ShapeType, image_enum self, image: PhotoImage, shape_type: ShapeType, image_enum
): ) -> None:
logging.debug("clicked annotation: ") logging.debug("clicked annotation: ")
self.hide_pickers() self.hide_pickers()
self.annotation_button.configure(image=image) self.annotation_button.configure(image=image)
@ -462,12 +470,12 @@ class Toolbar(ttk.Frame):
self.marker_tool = MarkerDialog(self.app) self.marker_tool = MarkerDialog(self.app)
self.marker_tool.show() self.marker_tool.show()
def click_run_button(self): def click_run_button(self) -> None:
logging.debug("Click on RUN button") logging.debug("Click on RUN button")
dialog = RunToolDialog(self.app) dialog = RunToolDialog(self.app)
dialog.show() dialog.show()
def click_marker_button(self): def click_marker_button(self) -> None:
logging.debug("Click on marker button") logging.debug("Click on marker button")
self.runtime_select(self.runtime_marker_button) self.runtime_select(self.runtime_marker_button)
self.app.canvas.mode = GraphMode.ANNOTATION self.app.canvas.mode = GraphMode.ANNOTATION
@ -477,12 +485,12 @@ class Toolbar(ttk.Frame):
self.marker_tool = MarkerDialog(self.app) self.marker_tool = MarkerDialog(self.app)
self.marker_tool.show() self.marker_tool.show()
def scale_button(self, button, image_enum): def scale_button(self, button, image_enum) -> None:
image = icon(image_enum, int(TOOLBAR_SIZE * self.app.app_scale)) image = self.get_icon(image_enum, TOOLBAR_SIZE)
button.config(image=image) button.config(image=image)
button.image = image button.image = image
def scale(self): def scale(self) -> None:
self.scale_button(self.play_button, ImageEnum.START) self.scale_button(self.play_button, ImageEnum.START)
self.scale_button(self.select_button, ImageEnum.SELECT) self.scale_button(self.select_button, ImageEnum.SELECT)
self.scale_button(self.link_button, ImageEnum.LINK) self.scale_button(self.link_button, ImageEnum.LINK)