pygui: implemented toolbar based marker configuration, fixed some issues when switching between different node bar states

This commit is contained in:
Blake Harnden 2020-05-17 23:08:53 -07:00
parent bd897efd05
commit cde053da73
4 changed files with 119 additions and 128 deletions

View file

@ -1,72 +0,0 @@
"""
marker dialog
"""
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from core.gui.dialogs.colorpicker import ColorPickerDialog
from core.gui.dialogs.dialog import Dialog
from core.gui.graph import tags
if TYPE_CHECKING:
from core.gui.app import Application
MARKER_THICKNESS = [3, 5, 8, 10]
class MarkerDialog(Dialog):
def __init__(self, app: "Application", initcolor: str = "#000000"):
super().__init__(app, "Marker Tool", modal=False)
self.color = initcolor
self.radius = MARKER_THICKNESS[0]
self.marker_thickness = tk.IntVar(value=MARKER_THICKNESS[0])
self.draw()
def draw(self):
button = ttk.Button(self.top, text="clear", command=self.clear_marker)
button.grid(row=0, column=0, sticky="nsew")
frame = ttk.Frame(self.top)
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=4)
frame.grid(row=1, column=0, sticky="nsew")
label = ttk.Label(frame, text="Thickness: ")
label.grid(row=0, column=0, sticky="nsew")
combobox = ttk.Combobox(
frame,
textvariable=self.marker_thickness,
values=MARKER_THICKNESS,
state="readonly",
)
combobox.grid(row=0, column=1, sticky="nsew")
combobox.bind("<<ComboboxSelected>>", self.change_thickness)
frame = ttk.Frame(self.top)
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=4)
frame.grid(row=2, column=0, sticky="nsew")
label = ttk.Label(frame, text="Color: ")
label.grid(row=0, column=0, sticky="nsew")
label = ttk.Label(frame, background=self.color)
label.grid(row=0, column=1, sticky="nsew")
label.bind("<Button-1>", self.change_color)
def clear_marker(self):
canvas = self.app.canvas
canvas.delete(tags.MARKER)
def change_color(self, event: tk.Event):
color_picker = ColorPickerDialog(self, self.app, self.color)
color = color_picker.askcolor()
event.widget.configure(background=color)
self.color = color
def change_thickness(self, event: tk.Event):
self.radius = self.marker_thickness.get()
def show(self):
super().show()
self.geometry(
f"+{self.app.canvas.winfo_rootx()}+{self.app.canvas.master.winfo_rooty()}"
)

View file

@ -596,13 +596,13 @@ class CanvasGraph(tk.Canvas):
if self.mode == GraphMode.ANNOTATION:
if is_marker(self.annotation_type):
r = self.app.toolbar.marker_tool.radius
r = self.app.toolbar.marker_frame.size.get()
self.create_oval(
x - r,
y - r,
x + r,
y + r,
fill=self.app.toolbar.marker_tool.color,
fill=self.app.toolbar.marker_frame.color,
outline="",
tags=(tags.MARKER, tags.ANNOTATION),
state=self.show_annotations.state(),
@ -678,13 +678,13 @@ class CanvasGraph(tk.Canvas):
shape.shape_motion(x, y)
return
elif is_marker(self.annotation_type):
r = self.app.toolbar.marker_tool.radius
r = self.app.toolbar.marker_frame.size.get()
self.create_oval(
x - r,
y - r,
x + r,
y + r,
fill=self.app.toolbar.marker_tool.color,
fill=self.app.toolbar.marker_frame.color,
outline="",
tags=(tags.MARKER, tags.ANNOTATION),
)

View file

@ -22,6 +22,7 @@ class NodeDraw:
self.node_type: core_pb2.NodeType = None
self.model: Optional[str] = None
self.services: Set[str] = set()
self.label = None
@classmethod
def from_setup(

View file

@ -8,8 +8,9 @@ from typing import TYPE_CHECKING, Callable
from PIL.ImageTk import PhotoImage
from core.api.grpc import core_pb2
from core.gui.dialogs.marker import MarkerDialog
from core.gui.dialogs.colorpicker import ColorPickerDialog
from core.gui.dialogs.runtool import RunToolDialog
from core.gui.graph import tags
from core.gui.graph.enums import GraphMode
from core.gui.graph.shapeutils import ShapeType, is_marker
from core.gui.images import ImageEnum
@ -43,19 +44,24 @@ class PickerFrame(ttk.Frame):
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_node_button(self, node_draw: NodeDraw, func: Callable) -> None:
self.create_button(
node_draw.label, func, node_draw.image_enum, node_draw.image_file
)
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
def create_button(
self,
label: str,
func: Callable,
image_enum: ImageEnum = None,
image_file: str = None,
) -> None:
if image_enum:
bar_image = self.app.get_icon(image_enum, TOOLBAR_SIZE)
image = self.app.get_icon(image_enum, PICKER_SIZE)
else:
bar_image = self.app.get_custom_icon(image_file, TOOLBAR_SIZE)
image = self.app.get_custom_icon(image_file, PICKER_SIZE)
button = ttk.Button(
self, image=image, text=label, compound=tk.TOP, style=Styles.picker_button
)
@ -101,6 +107,51 @@ class ButtonBar(ttk.Frame):
selected.state(["pressed"])
class MarkerFrame(ttk.Frame):
PAD = 3
def __init__(self, master: tk.BaseWidget, app: "Application") -> None:
super().__init__(master, padding=self.PAD)
self.app = app
self.color = "#000000"
self.size = tk.DoubleVar()
self.color_frame = None
self.draw()
def draw(self) -> None:
self.columnconfigure(0, weight=1)
image = self.app.get_icon(ImageEnum.DELETE, 16)
button = ttk.Button(self, image=image, width=2, command=self.click_clear)
button.image = image
button.grid(sticky="ew", pady=self.PAD)
Tooltip(button, "Delete Marker")
sizes = [1, 3, 8, 10]
self.size.set(sizes[0])
sizes = ttk.Combobox(
self, state="readonly", textvariable=self.size, value=sizes, width=2
)
sizes.grid(sticky="ew", pady=self.PAD)
Tooltip(sizes, "Marker Size")
frame_size = TOOLBAR_SIZE
self.color_frame = tk.Frame(
self, background=self.color, height=frame_size, width=frame_size
)
self.color_frame.grid(sticky="ew")
self.color_frame.bind("<Button-1>", self.click_color)
Tooltip(self.color_frame, "Marker Color")
def click_clear(self):
self.app.canvas.delete(tags.MARKER)
def click_color(self, _event: tk.Event) -> None:
dialog = ColorPickerDialog(self.app, self.app, self.color)
self.color = dialog.askcolor()
self.color_frame.config(background=self.color)
class Toolbar(ttk.Frame):
"""
Core toolbar class
@ -130,17 +181,15 @@ class Toolbar(ttk.Frame):
# frames
self.design_frame = None
self.runtime_frame = None
self.marker_frame = None
self.picker = None
# dialog
self.marker_tool = None
# these variables help keep track of what images being drawn so that scaling
# is possible since PhotoImage does not have resize method
self.node_enum = None
self.node_file = None
self.network_enum = None
self.annotation_enum = None
self.current_node = NodeUtils.NODES[0]
self.current_network = NodeUtils.NETWORK_NODES[0]
self.current_annotation = ShapeType.MARKER
self.annotation_enum = ImageEnum.MARKER
# draw components
self.draw()
@ -151,6 +200,7 @@ class Toolbar(ttk.Frame):
self.draw_design_frame()
self.draw_runtime_frame()
self.design_frame.tkraise()
self.marker_frame = MarkerFrame(self, self.app)
def draw_design_frame(self) -> None:
self.design_frame = ButtonBar(self, self.app)
@ -165,15 +215,18 @@ class Toolbar(ttk.Frame):
self.link_button = self.design_frame.create_button(
ImageEnum.LINK, self.click_link, "Link Tool", radio=True
)
self.node_enum = ImageEnum.ROUTER
self.node_button = self.design_frame.create_button(
self.node_enum, self.draw_node_picker, "Container Nodes", radio=True
self.current_node.image_enum,
self.draw_node_picker,
"Container Nodes",
radio=True,
)
self.network_enum = ImageEnum.HUB
self.network_button = self.design_frame.create_button(
self.network_enum, self.draw_network_picker, "Link Layer Nodes", radio=True
self.current_network.image_enum,
self.draw_network_picker,
"Link Layer Nodes",
radio=True,
)
self.annotation_enum = ImageEnum.MARKER
self.annotation_button = self.design_frame.create_button(
self.annotation_enum,
self.draw_annotation_picker,
@ -199,6 +252,9 @@ class Toolbar(ttk.Frame):
)
def draw_node_picker(self) -> None:
self.hide_marker()
self.app.canvas.mode = GraphMode.NODE
self.app.canvas.node_draw = self.current_node
self.design_frame.select_radio(self.node_button)
self.picker = PickerFrame(self.app, self.node_button)
# draw default nodes
@ -206,25 +262,25 @@ class Toolbar(ttk.Frame):
func = partial(
self.update_button, self.node_button, node_draw, NodeTypeEnum.NODE
)
self.picker.create_button(node_draw.label, node_draw.image_enum, func)
self.picker.create_node_button(node_draw, func)
# draw custom nodes
for name in sorted(self.app.core.custom_nodes):
node_draw = self.app.core.custom_nodes[name]
func = partial(
self.update_button, self.node_button, node_draw, NodeTypeEnum.NODE
)
self.picker.create_custom_button(
node_draw.label, node_draw.image_file, func
)
self.picker.create_node_button(node_draw, func)
self.picker.show()
def click_selection(self) -> None:
self.design_frame.select_radio(self.select_button)
self.app.canvas.mode = GraphMode.SELECT
self.hide_marker()
def click_runtime_selection(self) -> None:
self.runtime_frame.select_radio(self.runtime_select_button)
self.app.canvas.mode = GraphMode.SELECT
self.hide_marker()
def click_start(self) -> None:
"""
@ -253,15 +309,18 @@ class Toolbar(ttk.Frame):
enable_buttons(self.runtime_frame, enabled=True)
self.runtime_frame.tkraise()
self.click_runtime_selection()
self.hide_marker()
def set_design(self) -> None:
enable_buttons(self.design_frame, enabled=True)
self.design_frame.tkraise()
self.click_selection()
self.hide_marker()
def click_link(self) -> None:
self.design_frame.select_radio(self.link_button)
self.app.canvas.mode = GraphMode.EDGE
self.hide_marker()
def update_button(
self,
@ -273,29 +332,26 @@ class Toolbar(ttk.Frame):
logging.debug("update button(%s): %s", button, node_draw)
button.configure(image=image)
button.image = image
self.app.canvas.mode = GraphMode.NODE
self.app.canvas.node_draw = node_draw
if type_enum == NodeTypeEnum.NODE:
if node_draw.image_enum:
self.node_enum = node_draw.image_enum
self.node_file = None
elif node_draw.image_file:
self.node_file = node_draw.image_file
self.node_enum = None
self.current_node = node_draw
elif type_enum == NodeTypeEnum.NETWORK:
self.network_enum = node_draw.image_enum
self.current_network = node_draw
def draw_network_picker(self) -> None:
"""
Draw the options for link-layer button.
"""
self.hide_marker()
self.app.canvas.mode = GraphMode.NODE
self.app.canvas.node_draw = self.current_network
self.design_frame.select_radio(self.network_button)
self.picker = PickerFrame(self.app, self.network_button)
for node_draw in NodeUtils.NETWORK_NODES:
func = partial(
self.update_button, self.network_button, node_draw, NodeTypeEnum.NETWORK
)
self.picker.create_button(node_draw.label, node_draw.image_enum, func)
self.picker.create_node_button(node_draw, func)
self.picker.show()
def draw_annotation_picker(self) -> None:
@ -303,6 +359,10 @@ class Toolbar(ttk.Frame):
Draw the options for marker button.
"""
self.design_frame.select_radio(self.annotation_button)
self.app.canvas.mode = GraphMode.ANNOTATION
self.app.canvas.annotation_type = self.current_annotation
if is_marker(self.current_annotation):
self.show_marker()
self.picker = PickerFrame(self.app, self.annotation_button)
nodes = [
(ImageEnum.MARKER, ShapeType.MARKER),
@ -313,7 +373,7 @@ class Toolbar(ttk.Frame):
for image_enum, shape_type in nodes:
label = shape_type.value
func = partial(self.update_annotation, shape_type, image_enum)
self.picker.create_button(label, image_enum, func)
self.picker.create_button(label, func, image_enum)
self.picker.show()
def create_observe_button(self) -> None:
@ -364,14 +424,19 @@ class Toolbar(ttk.Frame):
logging.debug("clicked annotation")
self.annotation_button.configure(image=image)
self.annotation_button.image = image
self.app.canvas.mode = GraphMode.ANNOTATION
self.app.canvas.annotation_type = shape_type
self.current_annotation = shape_type
self.annotation_enum = image_enum
if is_marker(shape_type):
if self.marker_tool:
self.marker_tool.destroy()
self.marker_tool = MarkerDialog(self.app)
self.marker_tool.show()
self.show_marker()
else:
self.hide_marker()
def hide_marker(self) -> None:
self.marker_frame.grid_forget()
def show_marker(self) -> None:
self.marker_frame.grid()
def click_run_button(self) -> None:
logging.debug("Click on RUN button")
@ -382,10 +447,7 @@ class Toolbar(ttk.Frame):
self.runtime_frame.select_radio(self.runtime_marker_button)
self.app.canvas.mode = GraphMode.ANNOTATION
self.app.canvas.annotation_type = ShapeType.MARKER
if self.marker_tool:
self.marker_tool.destroy()
self.marker_tool = MarkerDialog(self.app)
self.marker_tool.show()
self.show_marker()
def scale_button(
self, button: ttk.Button, image_enum: ImageEnum = None, image_file: str = None
@ -403,11 +465,11 @@ class Toolbar(ttk.Frame):
self.scale_button(self.play_button, ImageEnum.START)
self.scale_button(self.select_button, ImageEnum.SELECT)
self.scale_button(self.link_button, ImageEnum.LINK)
if self.node_enum:
self.scale_button(self.node_button, self.node_enum)
if self.node_file:
self.scale_button(self.node_button, image_file=self.node_file)
self.scale_button(self.network_button, self.network_enum)
if self.current_node.image_enum:
self.scale_button(self.node_button, self.current_node)
else:
self.scale_button(self.node_button, image_file=self.current_node.image_file)
self.scale_button(self.network_button, self.current_network.image_enum)
self.scale_button(self.annotation_button, self.annotation_enum)
self.scale_button(self.runtime_select_button, ImageEnum.SELECT)
self.scale_button(self.stop_button, ImageEnum.STOP)