pygui: cleaned up toolbar picker code, fixed closing app when a picker is showing

This commit is contained in:
Blake Harnden 2020-05-16 01:14:48 -07:00
parent 29fc5acb99
commit 50816b3b80
2 changed files with 71 additions and 112 deletions

View file

@ -149,6 +149,8 @@ class Application(ttk.Frame):
self.after(0, lambda: ErrorDialog(self, title, message).show()) self.after(0, lambda: ErrorDialog(self, title, message).show())
def on_closing(self) -> None: def on_closing(self) -> None:
if self.toolbar.picker:
self.toolbar.picker.destroy()
self.menubar.prompt_save_running_session(True) self.menubar.prompt_save_running_session(True)
def save_config(self) -> None: def save_config(self) -> None:
@ -163,5 +165,8 @@ class Application(ttk.Frame):
def get_icon(self, image_enum: ImageEnum, width: int) -> PhotoImage: def get_icon(self, image_enum: ImageEnum, width: int) -> PhotoImage:
return Images.get(image_enum, int(width * self.app_scale)) return Images.get(image_enum, int(width * self.app_scale))
def get_custom_icon(self, image_file: str, width: int) -> PhotoImage:
return Images.get_custom(image_file, int(width * self.app_scale))
def close(self) -> None: def close(self) -> None:
self.master.destroy() self.master.destroy()

View file

@ -12,7 +12,7 @@ from core.gui.dialogs.marker import MarkerDialog
from core.gui.dialogs.runtool import RunToolDialog from core.gui.dialogs.runtool import RunToolDialog
from core.gui.graph.enums import GraphMode from core.gui.graph.enums import GraphMode
from core.gui.graph.shapeutils import ShapeType, is_marker from core.gui.graph.shapeutils import ShapeType, is_marker
from core.gui.images import ImageEnum, Images from core.gui.images import ImageEnum
from core.gui.nodeutils import NodeDraw, NodeUtils from core.gui.nodeutils import NodeDraw, NodeUtils
from core.gui.task import ProgressTask from core.gui.task import ProgressTask
from core.gui.themes import Styles from core.gui.themes import Styles
@ -37,6 +37,46 @@ def enable_buttons(frame: ttk.Frame, enabled: bool) -> None:
child.configure(state=state) child.configure(state=state)
class PickerFrame(ttk.Frame):
def __init__(self, app: "Application", button: ttk.Button) -> None:
super().__init__(app)
self.app = app
self.button = button
def create_button(self, label: str, image_enum: ImageEnum, func: Callable) -> None:
bar_image = self.app.get_icon(image_enum, TOOLBAR_SIZE)
image = self.app.get_icon(image_enum, PICKER_SIZE)
self._create_button(label, image, bar_image, func)
def create_custom_button(self, label: str, image_file: str, func: Callable) -> None:
bar_image = self.app.get_custom_icon(image_file, TOOLBAR_SIZE)
image = self.app.get_custom_icon(image_file, PICKER_SIZE)
self._create_button(label, image, bar_image, func)
def _create_button(
self, label: str, image: PhotoImage, bar_image: PhotoImage, func: Callable
) -> None:
button = ttk.Button(
self, image=image, text=label, compound=tk.TOP, style=Styles.picker_button
)
button.image = image
button.bind("<ButtonRelease-1>", lambda e: func(bar_image))
button.grid(pady=1)
def show(self) -> None:
self.button.after(0, self._show)
def _show(self) -> None:
x = self.button.winfo_width() + 1
y = self.button.winfo_rooty() - self.app.winfo_rooty() - 1
self.place(x=x, y=y)
self.app.bind_all("<ButtonRelease-1>", lambda e: self.destroy())
self.wait_visibility()
self.grab_set()
self.wait_window()
self.app.unbind_all("<ButtonRelease-1>")
class ButtonBar(ttk.Frame): class ButtonBar(ttk.Frame):
def __init__(self, master: tk.Widget, app: "Application"): def __init__(self, master: tk.Widget, app: "Application"):
super().__init__(master) super().__init__(master)
@ -90,9 +130,7 @@ class Toolbar(ttk.Frame):
# frames # frames
self.design_frame = None self.design_frame = None
self.runtime_frame = None self.runtime_frame = None
self.node_picker = None self.picker = None
self.network_picker = None
self.annotation_picker = None
# dialog # dialog
self.marker_tool = None self.marker_tool = None
@ -161,70 +199,23 @@ class Toolbar(ttk.Frame):
def draw_node_picker(self) -> None: def draw_node_picker(self) -> None:
self.design_frame.select_radio(self.node_button) self.design_frame.select_radio(self.node_button)
self.hide_pickers() self.picker = PickerFrame(self.app, self.node_button)
self.node_picker = ttk.Frame(self.master)
# draw default nodes # draw default nodes
for node_draw in NodeUtils.NODES: for node_draw in NodeUtils.NODES:
toolbar_image = self.app.get_icon(node_draw.image_enum, TOOLBAR_SIZE)
image = self.app.get_icon(node_draw.image_enum, PICKER_SIZE)
func = partial( func = partial(
self.update_button, self.update_button, self.node_button, node_draw, NodeTypeEnum.NODE
self.node_button,
toolbar_image,
node_draw,
NodeTypeEnum.NODE,
node_draw.image_enum,
) )
self.create_picker_button(image, func, self.node_picker, node_draw.label) self.picker.create_button(node_draw.label, node_draw.image_enum, func)
# draw custom nodes # draw custom nodes
for name in sorted(self.app.core.custom_nodes): for name in sorted(self.app.core.custom_nodes):
node_draw = self.app.core.custom_nodes[name] node_draw = self.app.core.custom_nodes[name]
toolbar_image = Images.get_custom(
node_draw.image_file, int(TOOLBAR_SIZE * self.app.app_scale)
)
image = Images.get_custom(
node_draw.image_file, int(PICKER_SIZE * self.app.app_scale)
)
func = partial( func = partial(
self.update_button, self.update_button, self.node_button, node_draw, NodeTypeEnum.NODE
self.node_button,
toolbar_image,
node_draw,
NodeTypeEnum,
node_draw.image_file,
) )
self.create_picker_button(image, func, self.node_picker, name) self.picker.create_custom_button(
self.node_button.after( node_draw.label, node_draw.image_file, func
0, lambda: self.show_picker(self.node_button, self.node_picker) )
) self.picker.show()
def show_picker(self, button: ttk.Button, picker: ttk.Frame) -> None:
x = self.winfo_width() + 1
y = button.winfo_rooty() - picker.master.winfo_rooty() - 1
picker.place(x=x, y=y)
self.app.bind_all("<ButtonRelease-1>", lambda e: self.hide_pickers())
picker.wait_visibility()
picker.grab_set()
self.wait_window(picker)
self.app.unbind_all("<ButtonRelease-1>")
def create_picker_button(
self, image: PhotoImage, func: Callable, frame: ttk.Frame, label: str
) -> None:
"""
Create button and put it on the frame
:param image: button image
:param func: the command that is executed when button is clicked
:param frame: frame that contains the button
:param label: button label
"""
button = ttk.Button(
frame, image=image, text=label, compound=tk.TOP, style=Styles.picker_button
)
button.image = image
button.bind("<ButtonRelease-1>", lambda e: func())
button.grid(pady=1)
def click_selection(self) -> None: def click_selection(self) -> None:
self.design_frame.select_radio(self.select_button) self.design_frame.select_radio(self.select_button)
@ -274,68 +265,39 @@ class Toolbar(ttk.Frame):
def update_button( def update_button(
self, self,
button: ttk.Button, button: ttk.Button,
image: PhotoImage,
node_draw: NodeDraw, node_draw: NodeDraw,
type_enum, type_enum: NodeTypeEnum,
image_enum, image: PhotoImage,
) -> None: ) -> None:
logging.debug("update button(%s): %s", button, node_draw) logging.debug("update button(%s): %s", button, node_draw)
self.hide_pickers()
button.configure(image=image) button.configure(image=image)
button.image = image button.image = image
self.app.canvas.mode = GraphMode.NODE self.app.canvas.mode = GraphMode.NODE
self.app.canvas.node_draw = node_draw self.app.canvas.node_draw = node_draw
if type_enum == NodeTypeEnum.NODE: if type_enum == NodeTypeEnum.NODE:
self.node_enum = image_enum self.node_enum = node_draw.image_enum
elif type_enum == NodeTypeEnum.NETWORK: elif type_enum == NodeTypeEnum.NETWORK:
self.network_enum = image_enum self.network_enum = node_draw.image_enum
def hide_pickers(self) -> None:
logging.debug("hiding pickers")
if self.node_picker:
self.node_picker.destroy()
self.node_picker = None
if self.network_picker:
self.network_picker.destroy()
self.network_picker = None
if self.annotation_picker:
self.annotation_picker.destroy()
self.annotation_picker = None
def draw_network_picker(self) -> None: def draw_network_picker(self) -> None:
""" """
Draw the options for link-layer button. Draw the options for link-layer button.
""" """
self.design_frame.select_radio(self.network_button) self.design_frame.select_radio(self.network_button)
self.hide_pickers() self.picker = PickerFrame(self.app, self.network_button)
self.network_picker = ttk.Frame(self.master)
for node_draw in NodeUtils.NETWORK_NODES: for node_draw in NodeUtils.NETWORK_NODES:
toolbar_image = self.app.get_icon(node_draw.image_enum, TOOLBAR_SIZE) func = partial(
image = self.app.get_icon(node_draw.image_enum, PICKER_SIZE) self.update_button, self.network_button, node_draw, NodeTypeEnum.NETWORK
self.create_picker_button(
image,
partial(
self.update_button,
self.network_button,
toolbar_image,
node_draw,
NodeTypeEnum.NETWORK,
node_draw.image_enum,
),
self.network_picker,
node_draw.label,
) )
self.network_button.after( self.picker.create_button(node_draw.label, node_draw.image_enum, func)
0, lambda: self.show_picker(self.network_button, self.network_picker) self.picker.show()
)
def draw_annotation_picker(self) -> None: def draw_annotation_picker(self) -> None:
""" """
Draw the options for marker button. Draw the options for marker button.
""" """
self.design_frame.select_radio(self.annotation_button) self.design_frame.select_radio(self.annotation_button)
self.hide_pickers() self.picker = PickerFrame(self.app, self.annotation_button)
self.annotation_picker = ttk.Frame(self.master)
nodes = [ nodes = [
(ImageEnum.MARKER, ShapeType.MARKER), (ImageEnum.MARKER, ShapeType.MARKER),
(ImageEnum.OVAL, ShapeType.OVAL), (ImageEnum.OVAL, ShapeType.OVAL),
@ -343,17 +305,10 @@ class Toolbar(ttk.Frame):
(ImageEnum.TEXT, ShapeType.TEXT), (ImageEnum.TEXT, ShapeType.TEXT),
] ]
for image_enum, shape_type in nodes: for image_enum, shape_type in nodes:
toolbar_image = self.app.get_icon(image_enum, TOOLBAR_SIZE) label = shape_type.value
image = self.app.get_icon(image_enum, PICKER_SIZE) func = partial(self.update_annotation, shape_type, image_enum)
self.create_picker_button( self.picker.create_button(label, image_enum, func)
image, self.picker.show()
partial(self.update_annotation, toolbar_image, shape_type, image_enum),
self.annotation_picker,
shape_type.value,
)
self.annotation_button.after(
0, lambda: self.show_picker(self.annotation_button, self.annotation_picker)
)
def create_observe_button(self) -> None: def create_observe_button(self) -> None:
image = self.app.get_icon(ImageEnum.OBSERVE, TOOLBAR_SIZE) image = self.app.get_icon(ImageEnum.OBSERVE, TOOLBAR_SIZE)
@ -398,10 +353,9 @@ class Toolbar(ttk.Frame):
self.app.canvas.stopped_session() self.app.canvas.stopped_session()
def update_annotation( def update_annotation(
self, image: PhotoImage, shape_type: ShapeType, image_enum self, shape_type: ShapeType, image_enum: ImageEnum, image: PhotoImage
) -> None: ) -> None:
logging.debug("clicked annotation: ") logging.debug("clicked annotation")
self.hide_pickers()
self.annotation_button.configure(image=image) self.annotation_button.configure(image=image)
self.annotation_button.image = image self.annotation_button.image = image
self.app.canvas.mode = GraphMode.ANNOTATION self.app.canvas.mode = GraphMode.ANNOTATION