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

View file

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

View file

@ -8,8 +8,9 @@ from typing import TYPE_CHECKING, Callable
from PIL.ImageTk import PhotoImage 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.colorpicker import ColorPickerDialog
from core.gui.dialogs.runtool import RunToolDialog from core.gui.dialogs.runtool import RunToolDialog
from core.gui.graph import tags
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 from core.gui.images import ImageEnum
@ -43,19 +44,24 @@ class PickerFrame(ttk.Frame):
self.app = app self.app = app
self.button = button self.button = button
def create_button(self, label: str, image_enum: ImageEnum, func: Callable) -> None: def create_node_button(self, node_draw: NodeDraw, func: Callable) -> None:
bar_image = self.app.get_icon(image_enum, TOOLBAR_SIZE) self.create_button(
image = self.app.get_icon(image_enum, PICKER_SIZE) node_draw.label, func, node_draw.image_enum, node_draw.image_file
self._create_button(label, image, bar_image, func) )
def create_custom_button(self, label: str, image_file: str, func: Callable) -> None: def create_button(
bar_image = self.app.get_custom_icon(image_file, TOOLBAR_SIZE) self,
image = self.app.get_custom_icon(image_file, PICKER_SIZE) label: str,
self._create_button(label, image, bar_image, func) func: Callable,
image_enum: ImageEnum = None,
def _create_button( image_file: str = None,
self, label: str, image: PhotoImage, bar_image: PhotoImage, func: Callable
) -> 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( button = ttk.Button(
self, image=image, text=label, compound=tk.TOP, style=Styles.picker_button self, image=image, text=label, compound=tk.TOP, style=Styles.picker_button
) )
@ -101,6 +107,51 @@ class ButtonBar(ttk.Frame):
selected.state(["pressed"]) 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): class Toolbar(ttk.Frame):
""" """
Core toolbar class Core toolbar class
@ -130,17 +181,15 @@ class Toolbar(ttk.Frame):
# frames # frames
self.design_frame = None self.design_frame = None
self.runtime_frame = None self.runtime_frame = None
self.marker_frame = None
self.picker = None self.picker = None
# dialog
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 PhotoImage does not have resize method # is possible since PhotoImage does not have resize method
self.node_enum = None self.current_node = NodeUtils.NODES[0]
self.node_file = None self.current_network = NodeUtils.NETWORK_NODES[0]
self.network_enum = None self.current_annotation = ShapeType.MARKER
self.annotation_enum = None self.annotation_enum = ImageEnum.MARKER
# draw components # draw components
self.draw() self.draw()
@ -151,6 +200,7 @@ class Toolbar(ttk.Frame):
self.draw_design_frame() self.draw_design_frame()
self.draw_runtime_frame() self.draw_runtime_frame()
self.design_frame.tkraise() self.design_frame.tkraise()
self.marker_frame = MarkerFrame(self, self.app)
def draw_design_frame(self) -> None: def draw_design_frame(self) -> None:
self.design_frame = ButtonBar(self, self.app) self.design_frame = ButtonBar(self, self.app)
@ -165,15 +215,18 @@ class Toolbar(ttk.Frame):
self.link_button = self.design_frame.create_button( self.link_button = self.design_frame.create_button(
ImageEnum.LINK, self.click_link, "Link Tool", radio=True ImageEnum.LINK, self.click_link, "Link Tool", radio=True
) )
self.node_enum = ImageEnum.ROUTER
self.node_button = self.design_frame.create_button( 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_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_button = self.design_frame.create_button(
self.annotation_enum, self.annotation_enum,
self.draw_annotation_picker, self.draw_annotation_picker,
@ -199,6 +252,9 @@ class Toolbar(ttk.Frame):
) )
def draw_node_picker(self) -> None: 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.design_frame.select_radio(self.node_button)
self.picker = PickerFrame(self.app, self.node_button) self.picker = PickerFrame(self.app, self.node_button)
# draw default nodes # draw default nodes
@ -206,25 +262,25 @@ class Toolbar(ttk.Frame):
func = partial( func = partial(
self.update_button, self.node_button, node_draw, NodeTypeEnum.NODE 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 # 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]
func = partial( func = partial(
self.update_button, self.node_button, node_draw, NodeTypeEnum.NODE self.update_button, self.node_button, node_draw, NodeTypeEnum.NODE
) )
self.picker.create_custom_button( self.picker.create_node_button(node_draw, func)
node_draw.label, node_draw.image_file, func
)
self.picker.show() self.picker.show()
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)
self.app.canvas.mode = GraphMode.SELECT self.app.canvas.mode = GraphMode.SELECT
self.hide_marker()
def click_runtime_selection(self) -> None: def click_runtime_selection(self) -> None:
self.runtime_frame.select_radio(self.runtime_select_button) self.runtime_frame.select_radio(self.runtime_select_button)
self.app.canvas.mode = GraphMode.SELECT self.app.canvas.mode = GraphMode.SELECT
self.hide_marker()
def click_start(self) -> None: def click_start(self) -> None:
""" """
@ -253,15 +309,18 @@ class Toolbar(ttk.Frame):
enable_buttons(self.runtime_frame, enabled=True) enable_buttons(self.runtime_frame, enabled=True)
self.runtime_frame.tkraise() self.runtime_frame.tkraise()
self.click_runtime_selection() self.click_runtime_selection()
self.hide_marker()
def set_design(self) -> None: def set_design(self) -> None:
enable_buttons(self.design_frame, enabled=True) enable_buttons(self.design_frame, enabled=True)
self.design_frame.tkraise() self.design_frame.tkraise()
self.click_selection() self.click_selection()
self.hide_marker()
def click_link(self) -> None: def click_link(self) -> None:
self.design_frame.select_radio(self.link_button) self.design_frame.select_radio(self.link_button)
self.app.canvas.mode = GraphMode.EDGE self.app.canvas.mode = GraphMode.EDGE
self.hide_marker()
def update_button( def update_button(
self, self,
@ -273,29 +332,26 @@ class Toolbar(ttk.Frame):
logging.debug("update button(%s): %s", button, node_draw) logging.debug("update button(%s): %s", button, node_draw)
button.configure(image=image) button.configure(image=image)
button.image = image button.image = image
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:
if node_draw.image_enum: self.current_node = node_draw
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
elif type_enum == NodeTypeEnum.NETWORK: elif type_enum == NodeTypeEnum.NETWORK:
self.network_enum = node_draw.image_enum self.current_network = node_draw
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.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.design_frame.select_radio(self.network_button)
self.picker = PickerFrame(self.app, self.network_button) self.picker = PickerFrame(self.app, self.network_button)
for node_draw in NodeUtils.NETWORK_NODES: for node_draw in NodeUtils.NETWORK_NODES:
func = partial( func = partial(
self.update_button, self.network_button, node_draw, NodeTypeEnum.NETWORK 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() self.picker.show()
def draw_annotation_picker(self) -> None: def draw_annotation_picker(self) -> None:
@ -303,6 +359,10 @@ class Toolbar(ttk.Frame):
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.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) self.picker = PickerFrame(self.app, self.annotation_button)
nodes = [ nodes = [
(ImageEnum.MARKER, ShapeType.MARKER), (ImageEnum.MARKER, ShapeType.MARKER),
@ -313,7 +373,7 @@ class Toolbar(ttk.Frame):
for image_enum, shape_type in nodes: for image_enum, shape_type in nodes:
label = shape_type.value label = shape_type.value
func = partial(self.update_annotation, shape_type, image_enum) 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() self.picker.show()
def create_observe_button(self) -> None: def create_observe_button(self) -> None:
@ -364,14 +424,19 @@ class Toolbar(ttk.Frame):
logging.debug("clicked annotation") logging.debug("clicked annotation")
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.annotation_type = shape_type self.app.canvas.annotation_type = shape_type
self.current_annotation = shape_type
self.annotation_enum = image_enum self.annotation_enum = image_enum
if is_marker(shape_type): if is_marker(shape_type):
if self.marker_tool: self.show_marker()
self.marker_tool.destroy() else:
self.marker_tool = MarkerDialog(self.app) self.hide_marker()
self.marker_tool.show()
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: def click_run_button(self) -> None:
logging.debug("Click on RUN button") logging.debug("Click on RUN button")
@ -382,10 +447,7 @@ class Toolbar(ttk.Frame):
self.runtime_frame.select_radio(self.runtime_marker_button) self.runtime_frame.select_radio(self.runtime_marker_button)
self.app.canvas.mode = GraphMode.ANNOTATION self.app.canvas.mode = GraphMode.ANNOTATION
self.app.canvas.annotation_type = ShapeType.MARKER self.app.canvas.annotation_type = ShapeType.MARKER
if self.marker_tool: self.show_marker()
self.marker_tool.destroy()
self.marker_tool = MarkerDialog(self.app)
self.marker_tool.show()
def scale_button( def scale_button(
self, button: ttk.Button, image_enum: ImageEnum = None, image_file: str = None 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.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)
if self.node_enum: if self.current_node.image_enum:
self.scale_button(self.node_button, self.node_enum) self.scale_button(self.node_button, self.current_node)
if self.node_file: else:
self.scale_button(self.node_button, image_file=self.node_file) self.scale_button(self.node_button, image_file=self.current_node.image_file)
self.scale_button(self.network_button, self.network_enum) self.scale_button(self.network_button, self.current_network.image_enum)
self.scale_button(self.annotation_button, self.annotation_enum) self.scale_button(self.annotation_button, self.annotation_enum)
self.scale_button(self.runtime_select_button, ImageEnum.SELECT) self.scale_button(self.runtime_select_button, ImageEnum.SELECT)
self.scale_button(self.stop_button, ImageEnum.STOP) self.scale_button(self.stop_button, ImageEnum.STOP)