pygui: implemented toolbar based marker configuration, fixed some issues when switching between different node bar states
This commit is contained in:
parent
bd897efd05
commit
cde053da73
4 changed files with 119 additions and 128 deletions
|
@ -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()}"
|
||||
)
|
|
@ -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),
|
||||
)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue