shape dialog
This commit is contained in:
parent
a0caff6ca2
commit
ff473b9748
4 changed files with 215 additions and 49 deletions
126
coretk/coretk/dialogs/shapemod.py
Normal file
126
coretk/coretk/dialogs/shapemod.py
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
"""
|
||||||
|
shape input dialog
|
||||||
|
"""
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import colorchooser, font, ttk
|
||||||
|
|
||||||
|
from coretk.dialogs.dialog import Dialog
|
||||||
|
|
||||||
|
FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
|
||||||
|
BORDER_WIDTH = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
|
||||||
|
|
||||||
|
class ShapeDialog(Dialog):
|
||||||
|
def __init__(self, master, app):
|
||||||
|
super().__init__(master, app, "Add a new shape", modal=True)
|
||||||
|
self.shape_text = tk.StringVar(value="")
|
||||||
|
self.font = tk.StringVar(value="Arial")
|
||||||
|
self.font_size = tk.IntVar(value=12)
|
||||||
|
self.text_color = "#000000"
|
||||||
|
self.fill_color = "#CFCFFF"
|
||||||
|
self.border_color = "black"
|
||||||
|
self.border_width = tk.IntVar(value=0)
|
||||||
|
|
||||||
|
self.fill = None
|
||||||
|
self.border = None
|
||||||
|
|
||||||
|
self.top.columnconfigure(0, weight=1)
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
frame = ttk.Frame(self.top)
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
frame.columnconfigure(1, weight=2)
|
||||||
|
label = ttk.Label(frame, text="Text for top of shape: ")
|
||||||
|
label.grid(row=0, column=0, sticky="nsew")
|
||||||
|
entry = ttk.Entry(frame, textvariable=self.shape_text)
|
||||||
|
entry.grid(row=0, column=1, sticky="nsew")
|
||||||
|
frame.grid(row=0, column=0, sticky="nsew", padx=3, pady=3)
|
||||||
|
|
||||||
|
frame = ttk.Frame(self.top)
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
frame.columnconfigure(1, weight=1)
|
||||||
|
frame.columnconfigure(2, weight=1)
|
||||||
|
combobox = ttk.Combobox(
|
||||||
|
frame,
|
||||||
|
textvariable=self.font,
|
||||||
|
values=sorted(font.families()),
|
||||||
|
state="readonly",
|
||||||
|
)
|
||||||
|
combobox.grid(row=0, column=0, sticky="nsew")
|
||||||
|
combobox = ttk.Combobox(
|
||||||
|
frame, textvariable=self.font_size, values=FONT_SIZES, state="readonly"
|
||||||
|
)
|
||||||
|
combobox.grid(row=0, column=1, padx=3, sticky="nsew")
|
||||||
|
button = ttk.Button(frame, text="Text color", command=self.choose_text_color)
|
||||||
|
button.grid(row=0, column=2, sticky="nsew")
|
||||||
|
frame.grid(row=1, column=0, sticky="nsew", padx=3, pady=3)
|
||||||
|
|
||||||
|
frame = ttk.Frame(self.top)
|
||||||
|
button = ttk.Checkbutton(frame, text="Bold")
|
||||||
|
button.grid(row=0, column=0)
|
||||||
|
button = ttk.Checkbutton(frame, text="Italic")
|
||||||
|
button.grid(row=0, column=1, padx=3)
|
||||||
|
button = ttk.Checkbutton(frame, text="Underline")
|
||||||
|
button.grid(row=0, column=2)
|
||||||
|
frame.grid(row=2, column=0, sticky="nsew", padx=3, pady=3)
|
||||||
|
|
||||||
|
frame = ttk.Frame(self.top)
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
frame.columnconfigure(1, weight=1)
|
||||||
|
frame.columnconfigure(2, weight=1)
|
||||||
|
label = ttk.Label(frame, text="Fill color")
|
||||||
|
label.grid(row=0, column=0, sticky="nsew")
|
||||||
|
self.fill = ttk.Label(frame, text=self.fill_color, background="#CFCFFF")
|
||||||
|
self.fill.grid(row=0, column=1, sticky="nsew", padx=3)
|
||||||
|
button = ttk.Button(frame, text="Color", command=self.choose_fill_color)
|
||||||
|
button.grid(row=0, column=2, sticky="nsew")
|
||||||
|
frame.grid(row=3, column=0, sticky="nsew", padx=3, pady=3)
|
||||||
|
|
||||||
|
frame = ttk.Frame(self.top)
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
frame.columnconfigure(1, weight=1)
|
||||||
|
frame.columnconfigure(2, weight=1)
|
||||||
|
label = ttk.Label(frame, text="Border color:")
|
||||||
|
label.grid(row=0, column=0, sticky="nsew")
|
||||||
|
self.border = ttk.Label(
|
||||||
|
frame, text=self.border_color, background=self.fill_color
|
||||||
|
)
|
||||||
|
self.border.grid(row=0, column=1, sticky="nsew", padx=3)
|
||||||
|
button = ttk.Button(frame, text="Color", command=self.choose_border_color)
|
||||||
|
button.grid(row=0, column=2, sticky="nsew")
|
||||||
|
frame.grid(row=4, column=0, sticky="nsew", padx=3, pady=3)
|
||||||
|
|
||||||
|
frame = ttk.Frame(self.top)
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
frame.columnconfigure(1, weight=2)
|
||||||
|
label = ttk.Label(frame, text="Border width:")
|
||||||
|
label.grid(row=0, column=0, sticky="nsew")
|
||||||
|
combobox = ttk.Combobox(
|
||||||
|
frame, textvariable=self.border_width, values=BORDER_WIDTH, state="readonly"
|
||||||
|
)
|
||||||
|
combobox.grid(row=0, column=1, sticky="nsew")
|
||||||
|
frame.grid(row=5, column=0, sticky="nsew", padx=3, pady=3)
|
||||||
|
|
||||||
|
frame = ttk.Frame(self.top)
|
||||||
|
frame.columnconfigure(0, weight=1)
|
||||||
|
frame.columnconfigure(1, weight=1)
|
||||||
|
button = ttk.Button(frame, text="Add shape")
|
||||||
|
button.grid(row=0, column=0, sticky="e", padx=3)
|
||||||
|
button = ttk.Button(frame, text="Cancel", command=self.destroy)
|
||||||
|
button.grid(row=0, column=1, sticky="w", pady=3)
|
||||||
|
frame.grid(row=6, column=0, sticky="nsew", padx=3, pady=3)
|
||||||
|
|
||||||
|
def choose_text_color(self):
|
||||||
|
color = colorchooser.askcolor(color="black")
|
||||||
|
self.text_color = color[1]
|
||||||
|
|
||||||
|
def choose_fill_color(self):
|
||||||
|
color = colorchooser.askcolor(color=self.fill_color)
|
||||||
|
self.fill_color = color[1]
|
||||||
|
self.fill.config(background=color[1], text=color[1])
|
||||||
|
|
||||||
|
def choose_border_color(self):
|
||||||
|
color = colorchooser.askcolor(color="black")
|
||||||
|
self.border_color = color[1]
|
||||||
|
self.border.config(background=color[1], text=color[1])
|
|
@ -68,6 +68,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.core = core
|
self.core = core
|
||||||
self.helper = GraphHelper(self, core)
|
self.helper = GraphHelper(self, core)
|
||||||
self.throughput_draw = Throughput(self, core)
|
self.throughput_draw = Throughput(self, core)
|
||||||
|
self.shape_drawing = False
|
||||||
|
|
||||||
# background related
|
# background related
|
||||||
self.wallpaper_id = None
|
self.wallpaper_id = None
|
||||||
|
@ -144,6 +145,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.bind("<B1-Motion>", self.click_motion)
|
self.bind("<B1-Motion>", self.click_motion)
|
||||||
self.bind("<Button-3>", self.click_context)
|
self.bind("<Button-3>", self.click_context)
|
||||||
self.bind("<Delete>", self.press_delete)
|
self.bind("<Delete>", self.press_delete)
|
||||||
|
self.bind("<Control-1>", self.ctrl_click)
|
||||||
|
|
||||||
def draw_grid(self, width=1000, height=800):
|
def draw_grid(self, width=1000, height=800):
|
||||||
"""
|
"""
|
||||||
|
@ -275,6 +277,9 @@ class CanvasGraph(tk.Canvas):
|
||||||
selected = _id
|
selected = _id
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if _id in self.shapes:
|
||||||
|
selected = _id
|
||||||
|
|
||||||
return selected
|
return selected
|
||||||
|
|
||||||
def click_release(self, event):
|
def click_release(self, event):
|
||||||
|
@ -290,8 +295,10 @@ class CanvasGraph(tk.Canvas):
|
||||||
else:
|
else:
|
||||||
if self.mode == GraphMode.ANNOTATION:
|
if self.mode == GraphMode.ANNOTATION:
|
||||||
if self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE]:
|
if self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE]:
|
||||||
|
self.focus_set()
|
||||||
x, y = self.canvas_xy(event)
|
x, y = self.canvas_xy(event)
|
||||||
self.shapes[self.selected].shape_complete(x, y)
|
self.shapes[self.selected].shape_complete(x, y)
|
||||||
|
self.shape_drawing = False
|
||||||
else:
|
else:
|
||||||
self.focus_set()
|
self.focus_set()
|
||||||
self.selected = self.get_selected(event)
|
self.selected = self.get_selected(event)
|
||||||
|
@ -355,12 +362,28 @@ class CanvasGraph(tk.Canvas):
|
||||||
if self.mode == GraphMode.EDGE and is_node:
|
if self.mode == GraphMode.EDGE and is_node:
|
||||||
x, y = self.coords(selected)
|
x, y = self.coords(selected)
|
||||||
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self)
|
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self)
|
||||||
if self.mode == GraphMode.ANNOTATION:
|
if (
|
||||||
if self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE]:
|
self.mode == GraphMode.ANNOTATION
|
||||||
x, y = self.canvas_xy(event)
|
and self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE]
|
||||||
shape = Shape(self.app, self, x, y)
|
and selected is None
|
||||||
self.selected = shape.id
|
):
|
||||||
self.shapes[shape.id] = shape
|
x, y = self.canvas_xy(event)
|
||||||
|
shape = Shape(self.app, self, x, y)
|
||||||
|
self.selected = shape.id
|
||||||
|
self.shapes[shape.id] = shape
|
||||||
|
self.shape_drawing = True
|
||||||
|
if self.mode == GraphMode.SELECT and "shape" in self.gettags(selected):
|
||||||
|
x, y = self.canvas_xy(event)
|
||||||
|
self.shapes[selected].cursor_x = x
|
||||||
|
self.shapes[selected].cursor_y = y
|
||||||
|
self.canvas_management.node_select(self.shapes[selected])
|
||||||
|
self.selected = selected
|
||||||
|
|
||||||
|
def ctrl_click(self, event):
|
||||||
|
logging.debug("Control left click %s", event)
|
||||||
|
selected = self.get_selected(event)
|
||||||
|
if self.mode == GraphMode.SELECT and "shape" in self.gettags(selected):
|
||||||
|
self.canvas_management.node_select(self.shapes[selected], True)
|
||||||
|
|
||||||
def click_motion(self, event):
|
def click_motion(self, event):
|
||||||
"""
|
"""
|
||||||
|
@ -374,9 +397,14 @@ class CanvasGraph(tk.Canvas):
|
||||||
x1, y1, _, _ = self.coords(self.drawing_edge.id)
|
x1, y1, _, _ = self.coords(self.drawing_edge.id)
|
||||||
self.coords(self.drawing_edge.id, x1, y1, x2, y2)
|
self.coords(self.drawing_edge.id, x1, y1, x2, y2)
|
||||||
if self.mode == GraphMode.ANNOTATION:
|
if self.mode == GraphMode.ANNOTATION:
|
||||||
if self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE]:
|
if (
|
||||||
|
self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE]
|
||||||
|
and self.shape_drawing
|
||||||
|
):
|
||||||
x, y = self.canvas_xy(event)
|
x, y = self.canvas_xy(event)
|
||||||
self.shapes[self.selected].shape_motion(x, y)
|
self.shapes[self.selected].shape_motion(x, y)
|
||||||
|
if self.mode == GraphMode.SELECT and "shape" in self.gettags(self.selected):
|
||||||
|
self.shapes[self.selected].motion(event)
|
||||||
|
|
||||||
def click_context(self, event):
|
def click_context(self, event):
|
||||||
logging.info("context event: %s", self.context)
|
logging.info("context event: %s", self.context)
|
||||||
|
@ -399,11 +427,12 @@ class CanvasGraph(tk.Canvas):
|
||||||
:param event:
|
:param event:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
logging.debug("press delete key")
|
||||||
nodes = self.canvas_management.delete_selected_nodes()
|
nodes = self.canvas_management.delete_selected_nodes()
|
||||||
self.core.delete_graph_nodes(nodes)
|
self.core.delete_graph_nodes(nodes)
|
||||||
|
|
||||||
def add_node(self, x, y):
|
def add_node(self, x, y):
|
||||||
if self.selected is None:
|
if self.selected is None or "shape" in self.gettags(self.selected):
|
||||||
core_node = self.core.create_node(
|
core_node = self.core.create_node(
|
||||||
int(x), int(y), self.node_draw.node_type, self.node_draw.model
|
int(x), int(y), self.node_draw.node_type, self.node_draw.model
|
||||||
)
|
)
|
||||||
|
@ -536,6 +565,9 @@ class CanvasGraph(tk.Canvas):
|
||||||
else:
|
else:
|
||||||
self.itemconfig("gridline", state=tk.HIDDEN)
|
self.itemconfig("gridline", state=tk.HIDDEN)
|
||||||
|
|
||||||
|
def is_selection_mode(self):
|
||||||
|
return self.mode == GraphMode.SELECT
|
||||||
|
|
||||||
|
|
||||||
class CanvasWirelessEdge:
|
class CanvasWirelessEdge:
|
||||||
def __init__(self, token, position, src, dst, canvas):
|
def __init__(self, token, position, src, dst, canvas):
|
||||||
|
@ -701,6 +733,7 @@ class CanvasNode:
|
||||||
logging.debug(f"node click press {self.core_node.name}: {event}")
|
logging.debug(f"node click press {self.core_node.name}: {event}")
|
||||||
self.moving = self.canvas.canvas_xy(event)
|
self.moving = self.canvas.canvas_xy(event)
|
||||||
self.canvas.canvas_management.node_select(self)
|
self.canvas.canvas_management.node_select(self)
|
||||||
|
self.canvas.selected = self.id
|
||||||
|
|
||||||
def click_release(self, event):
|
def click_release(self, event):
|
||||||
logging.debug(f"node click release {self.core_node.name}: {event}")
|
logging.debug(f"node click release {self.core_node.name}: {event}")
|
||||||
|
|
|
@ -47,41 +47,49 @@ class CanvasComponentManagement:
|
||||||
def delete_selected_nodes(self):
|
def delete_selected_nodes(self):
|
||||||
edges = set()
|
edges = set()
|
||||||
nodes = []
|
nodes = []
|
||||||
|
for node_id in self.selected:
|
||||||
|
if "node" in self.canvas.gettags(node_id):
|
||||||
|
bbox_id = self.selected[node_id]
|
||||||
|
canvas_node = self.canvas.nodes.pop(node_id)
|
||||||
|
nodes.append(canvas_node)
|
||||||
|
self.canvas.delete(node_id)
|
||||||
|
self.canvas.delete(bbox_id)
|
||||||
|
self.canvas.delete(canvas_node.text_id)
|
||||||
|
|
||||||
for node_id in list(self.selected):
|
# delete antennas
|
||||||
bbox_id = self.selected[node_id]
|
is_wireless = NodeUtils.is_wireless_node(canvas_node.core_node.type)
|
||||||
canvas_node = self.canvas.nodes.pop(node_id)
|
|
||||||
nodes.append(canvas_node)
|
|
||||||
self.canvas.delete(node_id)
|
|
||||||
self.canvas.delete(bbox_id)
|
|
||||||
self.canvas.delete(canvas_node.text_id)
|
|
||||||
|
|
||||||
# delete antennas
|
|
||||||
is_wireless = NodeUtils.is_wireless_node(canvas_node.core_node.type)
|
|
||||||
if is_wireless:
|
|
||||||
canvas_node.antenna_draw.delete_antennas()
|
|
||||||
|
|
||||||
# delete related edges
|
|
||||||
for edge in canvas_node.edges:
|
|
||||||
if edge in edges:
|
|
||||||
continue
|
|
||||||
edges.add(edge)
|
|
||||||
self.canvas.edges.pop(edge.token)
|
|
||||||
self.canvas.delete(edge.id)
|
|
||||||
self.canvas.delete(edge.link_info.id1)
|
|
||||||
self.canvas.delete(edge.link_info.id2)
|
|
||||||
other_id = edge.src
|
|
||||||
other_interface = edge.src_interface
|
|
||||||
if edge.src == node_id:
|
|
||||||
other_id = edge.dst
|
|
||||||
other_interface = edge.dst_interface
|
|
||||||
other_node = self.canvas.nodes[other_id]
|
|
||||||
other_node.edges.remove(edge)
|
|
||||||
try:
|
|
||||||
other_node.interfaces.remove(other_interface)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
if is_wireless:
|
if is_wireless:
|
||||||
other_node.antenna_draw.delete_antenna()
|
canvas_node.antenna_draw.delete_antennas()
|
||||||
|
|
||||||
|
# delete related edges
|
||||||
|
for edge in canvas_node.edges:
|
||||||
|
if edge in edges:
|
||||||
|
continue
|
||||||
|
edges.add(edge)
|
||||||
|
self.canvas.edges.pop(edge.token)
|
||||||
|
self.canvas.delete(edge.id)
|
||||||
|
self.canvas.delete(edge.link_info.id1)
|
||||||
|
self.canvas.delete(edge.link_info.id2)
|
||||||
|
other_id = edge.src
|
||||||
|
other_interface = edge.src_interface
|
||||||
|
if edge.src == node_id:
|
||||||
|
other_id = edge.dst
|
||||||
|
other_interface = edge.dst_interface
|
||||||
|
other_node = self.canvas.nodes[other_id]
|
||||||
|
other_node.edges.remove(edge)
|
||||||
|
try:
|
||||||
|
other_node.interfaces.remove(other_interface)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if is_wireless:
|
||||||
|
other_node.antenna_draw.delete_antenna()
|
||||||
|
|
||||||
|
for shape_id in self.selected:
|
||||||
|
if "shape" in self.canvas.gettags(shape_id):
|
||||||
|
bbox_id = self.selected[node_id]
|
||||||
|
self.canvas.delete(shape_id)
|
||||||
|
self.canvas.delete(bbox_id)
|
||||||
|
self.canvas.shapes.pop(shape_id)
|
||||||
|
|
||||||
self.selected.clear()
|
self.selected.clear()
|
||||||
return nodes
|
return nodes
|
||||||
|
|
|
@ -3,6 +3,7 @@ class for shapes
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from coretk.dialogs.shapemod import ShapeDialog
|
||||||
from coretk.images import ImageEnum
|
from coretk.images import ImageEnum
|
||||||
|
|
||||||
ABOVE_COMPONENT = ["gridline", "edge", "linkinfo", "antenna", "node", "nodename"]
|
ABOVE_COMPONENT = ["gridline", "edge", "linkinfo", "antenna", "node", "nodename"]
|
||||||
|
@ -16,6 +17,7 @@ class Shape:
|
||||||
self.y0 = top_y
|
self.y0 = top_y
|
||||||
self.cursor_x = None
|
self.cursor_x = None
|
||||||
self.cursor_y = None
|
self.cursor_y = None
|
||||||
|
canvas.delete(canvas.find_withtag("selectednodes"))
|
||||||
annotation_type = self.canvas.annotation_type
|
annotation_type = self.canvas.annotation_type
|
||||||
if annotation_type == ImageEnum.OVAL:
|
if annotation_type == ImageEnum.OVAL:
|
||||||
self.id = canvas.create_oval(
|
self.id = canvas.create_oval(
|
||||||
|
@ -25,9 +27,8 @@ class Shape:
|
||||||
self.id = canvas.create_rectangle(
|
self.id = canvas.create_rectangle(
|
||||||
top_x, top_y, top_x, top_y, tags="shape", dash="-"
|
top_x, top_y, top_x, top_y, tags="shape", dash="-"
|
||||||
)
|
)
|
||||||
self.canvas.tag_bind(self.id, "<ButtonPress-1>", self.click_press)
|
|
||||||
self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release)
|
self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release)
|
||||||
self.canvas.tag_bind(self.id, "<B1-Motion>", self.motion)
|
# self.canvas.tag_bind(self.id, "<B1-Motion>", self.motion)
|
||||||
|
|
||||||
def shape_motion(self, x1, y1):
|
def shape_motion(self, x1, y1):
|
||||||
self.canvas.coords(self.id, self.x0, self.y0, x1, y1)
|
self.canvas.coords(self.id, self.x0, self.y0, x1, y1)
|
||||||
|
@ -36,11 +37,8 @@ class Shape:
|
||||||
self.canvas.itemconfig(self.id, width=0, fill="#ccccff")
|
self.canvas.itemconfig(self.id, width=0, fill="#ccccff")
|
||||||
for component in ABOVE_COMPONENT:
|
for component in ABOVE_COMPONENT:
|
||||||
self.canvas.tag_raise(component)
|
self.canvas.tag_raise(component)
|
||||||
|
s = ShapeDialog(self.app, self.app)
|
||||||
def click_press(self, event):
|
s.show()
|
||||||
logging.debug("Click on shape %s", self.id)
|
|
||||||
self.cursor_x = event.x
|
|
||||||
self.cursor_y = event.y
|
|
||||||
|
|
||||||
def click_release(self, event):
|
def click_release(self, event):
|
||||||
logging.debug("Click release on shape %s", self.id)
|
logging.debug("Click release on shape %s", self.id)
|
||||||
|
@ -53,5 +51,6 @@ class Shape:
|
||||||
self.canvas.coords(
|
self.canvas.coords(
|
||||||
self.id, x0 + delta_x, y0 + delta_y, x1 + delta_x, y1 + delta_y
|
self.id, x0 + delta_x, y0 + delta_y, x1 + delta_x, y1 + delta_y
|
||||||
)
|
)
|
||||||
|
self.canvas.canvas_management.node_drag(self, delta_x, delta_y)
|
||||||
self.cursor_x = event.x
|
self.cursor_x = event.x
|
||||||
self.cursor_y = event.y
|
self.cursor_y = event.y
|
||||||
|
|
Loading…
Add table
Reference in a new issue