From 4a34aaa30d72249d6ba65182e67c876597d077f9 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Fri, 6 Dec 2019 09:03:21 -0800 Subject: [PATCH] working on shapes and texts --- coretk/coretk/coreclient.py | 10 +- coretk/coretk/dialogs/shapemod.py | 225 ++++++++++++++++++++---------- coretk/coretk/dialogs/textmod.py | 26 ++++ coretk/coretk/graph/graph.py | 26 ++-- coretk/coretk/graph/shape.py | 58 +++----- 5 files changed, 222 insertions(+), 123 deletions(-) create mode 100644 coretk/coretk/dialogs/textmod.py diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index 05a36c82..efbc6a39 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -10,7 +10,7 @@ from core.api.grpc import client, core_pb2 from coretk import appconfig from coretk.dialogs.mobilityplayer import MobilityPlayer from coretk.dialogs.sessions import SessionsDialog -from coretk.graph.shape import Shape, ShapeData +from coretk.graph.shape import AnnotationData, Shape from coretk.interface import InterfaceManager from coretk.nodeutils import NodeDraw, NodeUtils @@ -136,6 +136,7 @@ class CoreClient: self.custom_observers[observer.name] = observer def handle_events(self, event): + print(event) if event.HasField("link_event"): logging.info("link event: %s", event) self.handle_link_event(event.link_event) @@ -160,6 +161,8 @@ class CoreClient: self.handle_node_event(event.node_event) elif event.HasField("config_event"): logging.info("config event: %s", event) + elif event.HasField("throughput_event"): + print("throughput") else: logging.info("unhandled event: %s", event) @@ -189,7 +192,7 @@ class CoreClient: interface_throughputs = event.interface_throughputs for i in interface_throughputs: print("") - return + # return throughputs_belong_to_session = [] for if_tp in interface_throughputs: if if_tp.node_id in self.node_ids: @@ -312,8 +315,7 @@ class CoreClient: config_type = annotation_config["type"] if config_type in ["rectangle", "oval"]: coords = tuple(annotation_config["iconcoords"]) - data = ShapeData( - False, + data = AnnotationData( annotation_config["label"], annotation_config["fontfamily"], annotation_config["fontsize"], diff --git a/coretk/coretk/dialogs/shapemod.py b/coretk/coretk/dialogs/shapemod.py index ebeb7139..4b507dfa 100644 --- a/coretk/coretk/dialogs/shapemod.py +++ b/coretk/coretk/dialogs/shapemod.py @@ -5,6 +5,7 @@ import tkinter as tk from tkinter import colorchooser, font, ttk from coretk.dialogs.dialog import Dialog +from coretk.images import ImageEnum 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] @@ -12,9 +13,17 @@ BORDER_WIDTH = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] class ShapeDialog(Dialog): def __init__(self, master, app, shape): - super().__init__(master, app, "Add a new shape", modal=True) + self.annotation_type = app.canvas.annotation_type self.canvas = app.canvas - self.id = shape.id + if self.is_shape(): + super().__init__(master, app, "Add a new shape", modal=True) + self.id = shape.id + self.fill = None + self.border = None + else: + super().__init__(master, app, "Add a new text", modal=True) + + self.shape = shape data = shape.shape_data self.shape_text = tk.StringVar(value=data.text) self.font = tk.StringVar(value=data.font) @@ -26,11 +35,18 @@ class ShapeDialog(Dialog): self.bold = tk.IntVar(value=data.bold) self.italic = tk.IntVar(value=data.italic) self.underline = tk.IntVar(value=data.underline) - self.fill = None - self.border = None self.top.columnconfigure(0, weight=1) self.draw() + def is_shape(self): + return ( + self.annotation_type == ImageEnum.OVAL + or self.annotation_type == ImageEnum.RECTANGLE + ) + + def is_text(self): + return self.annotation_type == ImageEnum.TEXT + def draw(self): frame = ttk.Frame(self.top) frame.columnconfigure(0, weight=1) @@ -69,47 +85,53 @@ class ShapeDialog(Dialog): 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=self.fill_color) - 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) + if self.is_shape(): + 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=self.fill_color + ) + 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) - 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", command=self.add_shape) + button = ttk.Button(frame, text="Add shape", command=self.click_add) button.grid(row=0, column=0, sticky="e", padx=3) button = ttk.Button(frame, text="Cancel", command=self.cancel) button.grid(row=0, column=1, sticky="w", pady=3) @@ -130,11 +152,86 @@ class ShapeDialog(Dialog): self.border.config(background=color[1], text=color[1]) def cancel(self): - if not self.canvas.shapes[self.id].created: + if self.is_shape() and not self.canvas.shapes[self.id].created: self.canvas.delete(self.id) self.canvas.shapes.pop(self.id) self.destroy() + def click_add(self): + if self.is_shape(): + self.add_shape() + elif self.is_text(): + self.add_text() + self.destroy() + + def make_font(self): + """ + create font for text or shape label + :return: list(font specifications) + """ + size = int(self.font_size.get()) + text_font = [self.font.get(), size] + if self.bold.get() == 1: + text_font.append("bold") + if self.italic.get() == 1: + text_font.append("italic") + if self.underline.get() == 1: + text_font.append("underline") + return text_font + + def save_text(self): + """ + save info related to text or shape label + + :return: nothing + """ + data = self.shape.shape_data + data.text = self.shape_text.get() + data.font = self.font.get() + data.font_size = int(self.font_size.get()) + data.text_color = self.text_color + data.bold = self.bold.get() + data.italic = self.italic.get() + data.underline = self.underline.get() + + def save_shape(self): + """ + save info related to shape + + :return: nothing + """ + data = self.shape.shape_data + data.fill_color = self.fill_color + data.border_color = self.border_color + data.border_width = int(self.border_width.get()) + + def add_text(self): + """ + add text to canvas + + :return: nothing + """ + text = self.shape_text.get() + x = self.shape.x0 + y = self.shape.y0 + text_font = self.make_font() + if self.shape.text_id is None: + tid = self.canvas.create_text( + x, y, text=text, fill=self.text_color, font=text_font, tags="text" + ) + self.shape.text_id = tid + self.id = tid + self.shape.id = tid + self.canvas.texts[tid] = self.shape + self.shape.created = True + self.save_text() + print(self.canvas.texts) + # self.canvas.shapes[self.id].created = True + # else: + # self.canvas.itemconfig( + # self.shape.text_id, text=text, fill=self.text_color, font=f + # ) + def add_shape(self): self.canvas.itemconfig( self.id, @@ -143,42 +240,28 @@ class ShapeDialog(Dialog): outline=self.border_color, width=int(self.border_width.get()), ) - shape = self.canvas.shapes[self.id] shape_text = self.shape_text.get() size = int(self.font_size.get()) x0, y0, x1, y1 = self.canvas.bbox(self.id) - text_y = y0 + 1.5 * size - text_x = (x0 + x1) / 2 - f = [self.font.get(), size] - if self.bold.get() == 1: - f.append("bold") - if self.italic.get() == 1: - f.append("italic") - if self.underline.get() == 1: - f.append("underline") - if shape.text_id is None: - shape.text_id = self.canvas.create_text( - text_x, - text_y, + _y = y0 + 1.5 * size + _x = (x0 + x1) / 2 + text_font = self.make_font() + if self.shape.text_id is None: + self.shape.text_id = self.canvas.create_text( + _x, + _y, text=shape_text, fill=self.text_color, - font=f, + font=text_font, tags="shapetext", ) - self.canvas.shapes[self.id].created = True + self.shape.created = True else: self.canvas.itemconfig( - shape.text_id, text=shape_text, fill=self.text_color, font=f + self.shape.text_id, + text=shape_text, + fill=self.text_color, + font=text_font, ) - data = self.canvas.shapes[self.id].shape_data - data.text = shape_text - data.font = self.font.get() - data.font_size = int(self.font_size.get()) - data.text_color = self.text_color - data.fill_color = self.fill_color - data.border_color = self.border_color - data.border_width = int(self.border_width.get()) - data.bold = self.bold.get() - data.italic = self.italic.get() - data.underline = self.underline.get() - self.destroy() + self.save_text() + self.save_shape() diff --git a/coretk/coretk/dialogs/textmod.py b/coretk/coretk/dialogs/textmod.py new file mode 100644 index 00000000..03af38fb --- /dev/null +++ b/coretk/coretk/dialogs/textmod.py @@ -0,0 +1,26 @@ +""" +text dialog +""" +import tkinter as tk +from tkinter import ttk + +from coretk.dialogs.dialog import Dialog + + +class TextDialog(Dialog): + def __init__(self, master, app): + super().__init__(master, app, "Add a new text", modal=True) + self.canvas = app.canvas + self.text = tk.StringVar(value="") + + self.draw() + + def draw(self): + frame = ttk.Frame(self.top) + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=4) + label = ttk.Label(frame, text="Text for top of text: ") + label.grid(row=0, column=0) + entry = ttk.Entry(frame, textvariable=self.text) + entry.grid(row=0, column=1) + frame.grid(row=0, column=0, sticky="nsew") diff --git a/coretk/coretk/graph/graph.py b/coretk/coretk/graph/graph.py index 95ad19dc..688e89da 100644 --- a/coretk/coretk/graph/graph.py +++ b/coretk/coretk/graph/graph.py @@ -34,6 +34,7 @@ class CanvasGraph(tk.Canvas): self.nodes = {} self.edges = {} self.shapes = {} + self.texts = {} self.wireless_edges = {} self.drawing_edge = None self.grid = None @@ -276,6 +277,8 @@ class CanvasGraph(tk.Canvas): if self.shape_drawing: self.shapes[self.selected].shape_complete(x, y) self.shape_drawing = False + elif self.annotation_type == ImageEnum.TEXT: + self.text.shape_complete(self.text.cursor_x, self.text.cursor_y) else: self.focus_set() self.selected = self.get_selected(event) @@ -424,16 +427,19 @@ class CanvasGraph(tk.Canvas): if self.mode == GraphMode.EDGE and is_node: x, y = self.coords(selected) self.drawing_edge = CanvasEdge(x, y, x, y, selected, self) - if ( - self.mode == GraphMode.ANNOTATION - and self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE] - and selected is None - ): - 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.ANNOTATION and selected is None: + if self.annotation_type in [ImageEnum.OVAL, ImageEnum.RECTANGLE]: + 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 + elif self.annotation_type == ImageEnum.TEXT: + x, y = self.canvas_xy(event) + self.text = Shape(self.app, self, x, y) + # self.shapes[shape.id] = shape + if self.mode == GraphMode.SELECT: if selected is not None: if selected in self.shapes: diff --git a/coretk/coretk/graph/shape.py b/coretk/coretk/graph/shape.py index 91020ecb..660ef291 100644 --- a/coretk/coretk/graph/shape.py +++ b/coretk/coretk/graph/shape.py @@ -1,51 +1,37 @@ """ class for shapes """ -import logging - from coretk.dialogs.shapemod import ShapeDialog from coretk.images import ImageEnum ABOVE_COMPONENT = ["gridline", "edge", "linkinfo", "antenna", "node", "nodename"] -class ShapeData: +class AnnotationData: def __init__( self, - is_default=True, - text=None, - font=None, - font_size=None, - text_color=None, - fill_color=None, - border_color=None, - border_width=None, + text="", + font="Arial", + font_size=12, + text_color="#000000", + fill_color="#CFCFFF", + border_color="#000000", + border_width=0, bold=0, italic=0, underline=0, ): - if is_default: - self.text = "" - self.font = "Arial" - self.font_size = 12 - self.text_color = "#000000" - self.fill_color = "#CFCFFF" - self.border_color = "#000000" - self.border_width = 0 - self.bold = 0 - self.italic = 0 - self.underline = 0 - else: - self.text = text - self.font = font - self.font_size = font_size - self.text_color = text_color - self.fill_color = fill_color - self.border_color = border_color - self.border_width = border_width - self.bold = bold - self.italic = italic - self.underline = underline + + self.text = text + self.font = font + self.font_size = font_size + self.text_color = text_color + self.fill_color = fill_color + self.border_color = border_color + self.border_width = border_width + self.bold = bold + self.italic = italic + self.underline = underline class Shape: @@ -66,7 +52,7 @@ class Shape: self.y0 = top_y self.created = False self.text_id = None - self.shape_data = ShapeData() + self.shape_data = AnnotationData() canvas.delete(canvas.find_withtag("selectednodes")) annotation_type = self.canvas.annotation_type if annotation_type == ImageEnum.OVAL: @@ -112,7 +98,6 @@ class Shape: self.shape_data = data self.cursor_x = None self.cursor_y = None - self.canvas.tag_bind(self.id, "", self.click_release) def shape_motion(self, x1, y1): self.canvas.coords(self.id, self.x0, self.y0, x1, y1) @@ -123,9 +108,6 @@ class Shape: s = ShapeDialog(self.app, self.app, self) s.show() - def click_release(self, event): - logging.debug("Click release on shape %s", self.id) - def motion(self, event, delta_x=None, delta_y=None): if event is not None: delta_x = event.x - self.cursor_x