import logging from typing import TYPE_CHECKING, Dict, List, Union from core.gui.dialogs.shapemod import ShapeDialog from core.gui.graph import tags from core.gui.graph.shapeutils import ShapeType if TYPE_CHECKING: from core.gui.app import Application from core.gui.graph.graph import CanvasGraph class AnnotationData: def __init__( self, text: str = "", font: str = "Arial", font_size: int = 12, text_color: str = "#000000", fill_color: str = "", border_color: str = "#000000", border_width: int = 1, bold: bool = False, italic: bool = False, underline: bool = False, ): 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: def __init__( self, app: "Application", canvas: "CanvasGraph", shape_type: ShapeType, x1: float, y1: float, x2: float = None, y2: float = None, data: AnnotationData = None, ): self.app = app self.canvas = canvas self.shape_type = shape_type self.id = None self.text_id = None self.x1 = x1 self.y1 = y1 if x2 is None: x2 = x1 self.x2 = x2 if y2 is None: y2 = y1 self.y2 = y2 if data is None: self.created = False self.shape_data = AnnotationData() else: self.created = True self.shape_data = data self.draw() def draw(self): if self.created: dash = None else: dash = "-" if self.shape_type == ShapeType.OVAL: self.id = self.canvas.create_oval( self.x1, self.y1, self.x2, self.y2, tags=tags.SHAPE, dash=dash, fill=self.shape_data.fill_color, outline=self.shape_data.border_color, width=self.shape_data.border_width, ) self.draw_shape_text() elif self.shape_type == ShapeType.RECTANGLE: self.id = self.canvas.create_rectangle( self.x1, self.y1, self.x2, self.y2, tags=tags.SHAPE, dash=dash, fill=self.shape_data.fill_color, outline=self.shape_data.border_color, width=self.shape_data.border_width, ) self.draw_shape_text() elif self.shape_type == ShapeType.TEXT: font = self.get_font() self.id = self.canvas.create_text( self.x1, self.y1, tags=tags.SHAPE_TEXT, text=self.shape_data.text, fill=self.shape_data.text_color, font=font, ) else: logging.error("unknown shape type: %s", self.shape_type) self.created = True def get_font(self) -> List[Union[int, str]]: font = [self.shape_data.font, self.shape_data.font_size] if self.shape_data.bold: font.append("bold") if self.shape_data.italic: font.append("italic") if self.shape_data.underline: font.append("underline") return font def draw_shape_text(self): if self.shape_data.text: x = (self.x1 + self.x2) / 2 y = self.y1 + 1.5 * self.shape_data.font_size font = self.get_font() self.text_id = self.canvas.create_text( x, y, tags=tags.SHAPE_TEXT, text=self.shape_data.text, fill=self.shape_data.text_color, font=font, ) def shape_motion(self, x1: float, y1: float): self.canvas.coords(self.id, self.x1, self.y1, x1, y1) def shape_complete(self, x: float, y: float): for component in tags.ABOVE_SHAPE: self.canvas.tag_raise(component) s = ShapeDialog(self.app, self.app, self) s.show() def disappear(self): self.canvas.delete(self.id) def motion(self, x_offset: float, y_offset: float): original_position = self.canvas.coords(self.id) self.canvas.move(self.id, x_offset, y_offset) coords = self.canvas.coords(self.id) if not self.canvas.valid_position(*coords): self.canvas.coords(self.id, original_position) return self.canvas.move_selection(self.id, x_offset, y_offset) if self.text_id is not None: self.canvas.move(self.text_id, x_offset, y_offset) def delete(self): logging.debug("Delete shape, id(%s)", self.id) self.canvas.delete(self.id) self.canvas.delete(self.text_id) def metadata(self) -> Dict[str, Union[str, int, bool]]: coords = self.canvas.coords(self.id) # update coords to actual positions if len(coords) == 4: x1, y1, x2, y2 = coords x1, y1 = self.canvas.get_actual_coords(x1, y1) x2, y2 = self.canvas.get_actual_coords(x2, y2) coords = (x1, y1, x2, y2) else: x1, y1 = coords x1, y1 = self.canvas.get_actual_coords(x1, y1) coords = (x1, y1) return { "type": self.shape_type.value, "iconcoords": coords, "label": self.shape_data.text, "fontfamily": self.shape_data.font, "fontsize": self.shape_data.font_size, "labelcolor": self.shape_data.text_color, "color": self.shape_data.fill_color, "border": self.shape_data.border_color, "width": self.shape_data.border_width, "bold": self.shape_data.bold, "italic": self.shape_data.italic, "underline": self.shape_data.underline, }