import sys import tkinter from typing import Union from .customtkinter_canvas import CTkCanvas class DrawEngine: def __init__(self, canvas: CTkCanvas, rendering_method: str): self.canvas = canvas self.rendering_method = rendering_method # "polygon_shapes" (macOS), "font_shapes" (Windows, Linux), "circle_shapes" (backup) def calc_optimal_corner_radius(self, user_corner_radius: Union[float, int]) -> Union[float, int]: # optimize for drawing with polygon shapes if self.rendering_method == "polygon_shapes": if sys.platform == "darwin": return user_corner_radius else: return round(user_corner_radius) # optimize forx drawing with antialiased font shapes elif self.rendering_method == "font_shapes": return round(user_corner_radius) # optimize for drawing with circles and rects elif self.rendering_method == "circle_shapes": user_corner_radius = 0.5 * round(user_corner_radius / 0.5) # round to 0.5 steps # make sure the value is always with .5 at the end for smoother corners if user_corner_radius == 0: return 0 elif user_corner_radius % 1 == 0: return user_corner_radius + 0.5 else: return user_corner_radius def draw_rounded_rect_with_border(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int]) -> bool: """ Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag, the main foreground elements have an 'inner_parts' tag to color the elements accordingly. returns bool if recoloring is necessary """ if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger corner_radius = min(width / 2, height / 2) border_width = round(border_width) corner_radius = self.calc_optimal_corner_radius(corner_radius) # optimize corner_radius for different drawing methods (different rounding) if corner_radius >= border_width: inner_corner_radius = corner_radius - border_width else: inner_corner_radius = 0 if self.rendering_method == "polygon_shapes": return self._draw_rounded_rect_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius) elif self.rendering_method == "font_shapes": return self._draw_rounded_rect_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius) elif self.rendering_method == "circle_shapes": return self._draw_rounded_rect_with_border_circle_shapes(width, height, corner_radius, border_width, inner_corner_radius) def _draw_rounded_rect_with_border_polygon_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int) -> bool: requires_recoloring = False # create border button parts (only if border exists) if border_width > 0: if not self.canvas.find_withtag("border_parts"): self.canvas.create_polygon((0, 0, 0, 0), tags=("border_line_1", "border_parts")) self.canvas.tag_lower("border_parts") requires_recoloring = True self.canvas.coords("border_line_1", (corner_radius, corner_radius, width - corner_radius, corner_radius, width - corner_radius, height - corner_radius, corner_radius, height - corner_radius)) self.canvas.itemconfig("border_line_1", joinstyle=tkinter.ROUND, width=corner_radius * 2) else: self.canvas.delete("border_parts") # create inner button parts if not self.canvas.find_withtag("inner_parts"): self.canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_1", "inner_parts")) self.canvas.tag_raise("inner_parts") requires_recoloring = True if corner_radius <= border_width: bottom_right_shift = -1 # weird canvas rendering inaccuracy that has to be corrected in some cases else: bottom_right_shift = 0 self.canvas.coords("inner_line_1", (border_width + inner_corner_radius, border_width + inner_corner_radius, width - (border_width + inner_corner_radius) + bottom_right_shift, border_width + inner_corner_radius, width - (border_width + inner_corner_radius) + bottom_right_shift, height - (border_width + inner_corner_radius) + bottom_right_shift, border_width + inner_corner_radius, height - (border_width + inner_corner_radius) + bottom_right_shift)) self.canvas.itemconfig("inner_line_1", joinstyle=tkinter.ROUND, width=inner_corner_radius * 2) return requires_recoloring def _draw_rounded_rect_with_border_font_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int) -> bool: requires_recoloring = False # create border button parts if border_width > 0: if corner_radius > 0: # create canvas border corner parts if not already created if not self.canvas.find_withtag("border_oval_1_a"): self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.tag_lower("border_corner_part") requires_recoloring = True if not self.canvas.find_withtag("border_oval_3_a") and round(corner_radius) * 2 < height: self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.tag_lower("border_corner_part") requires_recoloring = True elif self.canvas.find_withtag("border_oval_3_a") and not round(corner_radius) * 2 < height: self.canvas.delete(["border_oval_3_a", "border_oval_3_b", "border_oval_4_a", "border_oval_4_b"]) # change position of border corner parts self.canvas.coords("border_oval_1_a", corner_radius, corner_radius, corner_radius) self.canvas.coords("border_oval_1_b", corner_radius, corner_radius, corner_radius) self.canvas.coords("border_oval_2_a", width - corner_radius, corner_radius, corner_radius) self.canvas.coords("border_oval_2_b", width - corner_radius, corner_radius, corner_radius) self.canvas.coords("border_oval_3_a", width - corner_radius, height - corner_radius, corner_radius) self.canvas.coords("border_oval_3_b", width - corner_radius, height - corner_radius, corner_radius) self.canvas.coords("border_oval_4_a", corner_radius, height - corner_radius, corner_radius) self.canvas.coords("border_oval_4_b", corner_radius, height - corner_radius, corner_radius) else: self.canvas.delete("border_corner_part") # delete border corner parts if not needed # create canvas border rectangle parts if not already created if not self.canvas.find_withtag("border_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_rectangle_part", "border_parts"), width=0) self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts"), width=0) self.canvas.tag_lower("border_rectangle_part") requires_recoloring = True # change position of border rectangle parts self.canvas.coords("border_rectangle_1", (0, corner_radius, width, height - corner_radius)) self.canvas.coords("border_rectangle_2", (corner_radius, 0, width - corner_radius, height)) else: self.canvas.delete("border_parts") # create inner button parts if inner_corner_radius > 0: # create canvas border corner parts if not already created if not self.canvas.find_withtag("inner_oval_1_a"): self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.tag_raise("inner_corner_part") requires_recoloring = True if not self.canvas.find_withtag("inner_oval_3_a") and round(inner_corner_radius) * 2 < height - (2 * border_width): self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.tag_raise("inner_corner_part") requires_recoloring = True elif self.canvas.find_withtag("inner_oval_3_a") and not round(inner_corner_radius) * 2 < height - (2 * border_width): self.canvas.delete(["inner_oval_3_a", "inner_oval_3_b", "inner_oval_4_a", "inner_oval_4_b"]) # change position of border corner parts self.canvas.coords("inner_oval_1_a", border_width + inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_1_b", border_width + inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_2_a", width - border_width - inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_2_b", width - border_width - inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_3_a", width - border_width - inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_3_b", width - border_width - inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_4_a", border_width + inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_4_b", border_width + inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) else: self.canvas.delete("inner_corner_part") # delete inner corner parts if not needed # create canvas inner rectangle parts if not already created if not self.canvas.find_withtag("inner_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_1", "inner_rectangle_part", "inner_parts"), width=0) self.canvas.tag_raise("inner_rectangle_part") requires_recoloring = True if not self.canvas.find_withtag("inner_rectangle_2") and inner_corner_radius * 2 < height - (border_width * 2): self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts"), width=0) self.canvas.tag_raise("inner_rectangle_part") requires_recoloring = True elif self.canvas.find_withtag("inner_rectangle_2") and not inner_corner_radius * 2 < height - (border_width * 2): self.canvas.delete("inner_rectangle_2") # change position of inner rectangle parts self.canvas.coords("inner_rectangle_1", (border_width + inner_corner_radius, border_width, width - border_width - inner_corner_radius, height - border_width)) self.canvas.coords("inner_rectangle_2", (border_width, border_width + inner_corner_radius, width - border_width, height - inner_corner_radius - border_width)) return requires_recoloring def _draw_rounded_rect_with_border_circle_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int) -> bool: requires_recoloring = False # border button parts if border_width > 0: if corner_radius > 0: if not self.canvas.find_withtag("border_oval_1"): self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_1", "border_corner_part", "border_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_2", "border_corner_part", "border_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_3", "border_corner_part", "border_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_4", "border_corner_part", "border_parts"), width=0) self.canvas.tag_lower("border_parts") requires_recoloring = True self.canvas.coords("border_oval_1", 0, 0, corner_radius * 2 - 1, corner_radius * 2 - 1) self.canvas.coords("border_oval_2", width - corner_radius * 2, 0, width - 1, corner_radius * 2 - 1) self.canvas.coords("border_oval_3", 0, height - corner_radius * 2, corner_radius * 2 - 1, height - 1) self.canvas.coords("border_oval_4", width - corner_radius * 2, height - corner_radius * 2, width - 1, height - 1) else: self.canvas.delete("border_corner_part") if not self.canvas.find_withtag("border_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_rectangle_part", "border_parts"), width=0) self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts"), width=0) self.canvas.tag_lower("border_parts") requires_recoloring = True self.canvas.coords("border_rectangle_1", (0, corner_radius, width, height - corner_radius )) self.canvas.coords("border_rectangle_2", (corner_radius, 0, width - corner_radius, height )) else: self.canvas.delete("border_parts") # inner button parts if inner_corner_radius > 0: if not self.canvas.find_withtag("inner_oval_1"): self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_1", "inner_corner_part", "inner_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_2", "inner_corner_part", "inner_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_3", "inner_corner_part", "inner_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_4", "inner_corner_part", "inner_parts"), width=0) self.canvas.tag_raise("inner_parts") requires_recoloring = True self.canvas.coords("inner_oval_1", (border_width, border_width, border_width + inner_corner_radius * 2 - 1, border_width + inner_corner_radius * 2 - 1)) self.canvas.coords("inner_oval_2", (width - border_width - inner_corner_radius * 2, border_width, width - border_width - 1, border_width + inner_corner_radius * 2 - 1)) self.canvas.coords("inner_oval_3", (border_width, height - border_width - inner_corner_radius * 2, border_width + inner_corner_radius * 2 - 1, height - border_width - 1)) self.canvas.coords("inner_oval_4", (width - border_width - inner_corner_radius * 2, height - border_width - inner_corner_radius * 2, width - border_width - 1, height - border_width - 1)) else: self.canvas.delete("inner_corner_part") # delete inner corner parts if not needed if not self.canvas.find_withtag("inner_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_1", "inner_rectangle_part", "inner_parts"), width=0) self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts"), width=0) self.canvas.tag_raise("inner_parts") requires_recoloring = True self.canvas.coords("inner_rectangle_1", (border_width + inner_corner_radius, border_width, width - border_width - inner_corner_radius, height - border_width)) self.canvas.coords("inner_rectangle_2", (border_width, border_width + inner_corner_radius, width - border_width , height - inner_corner_radius - border_width)) return requires_recoloring def draw_rounded_progress_bar_with_border(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int], progress_value: float, orientation: str) -> bool: """ Draws a rounded bar on the canvas, which is splitted in half according to the argument 'progress_value' (0 - 1). The border elements get the 'border_parts' tag", the main elements get the 'inner_parts' tag and the progress elements get the 'progress_parts' tag. The 'orientation' argument defines from which direction the progress starts (n, w, s, e). returns bool if recoloring is necessary """ if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger corner_radius = min(width / 2, height / 2) border_width = round(border_width) corner_radius = self.calc_optimal_corner_radius(corner_radius) # optimize corner_radius for different drawing methods (different rounding) if corner_radius >= border_width: inner_corner_radius = corner_radius - border_width else: inner_corner_radius = 0 if self.rendering_method == "polygon_shapes": return self._draw_rounded_progress_bar_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius, progress_value, orientation) elif self.rendering_method == "font_shapes": return self._draw_rounded_progress_bar_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius, progress_value, orientation) elif self.rendering_method == "circle_shapes": return self._draw_rounded_progress_bar_with_border_circle_shapes(width, height, corner_radius, border_width, inner_corner_radius) def _draw_rounded_progress_bar_with_border_polygon_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int, progress_value: float, orientation: str) -> bool: requires_recoloring = False print(border_width, corner_radius, inner_corner_radius) # create border button parts (only if border exists) if border_width > 0: if not self.canvas.find_withtag("border_parts"): self.canvas.create_polygon((0, 0, 0, 0), tags=("border_line_1", "border_parts"), joinstyle=tkinter.ROUND) print("create border") self.canvas.tag_lower("border_parts") requires_recoloring = True self.canvas.coords("border_line_1", (corner_radius, corner_radius, width - corner_radius, corner_radius, width - corner_radius, height - corner_radius, corner_radius, height - corner_radius)) self.canvas.itemconfig("border_line_1", width=corner_radius * 2) else: self.canvas.delete("border_parts") # create inner button parts if not self.canvas.find_withtag("inner_parts"): self.canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_1", "inner_parts"), joinstyle=tkinter.ROUND) print("create inner") if self.canvas.find_withtag("border_parts"): self.canvas.tag_raise("inner_parts", "border_parts") requires_recoloring = True if corner_radius <= border_width: bottom_right_shift = -1 # weird canvas rendering inaccuracy that has to be corrected in some cases else: bottom_right_shift = 0 self.canvas.coords("inner_line_1", border_width + inner_corner_radius, border_width + inner_corner_radius, width - (border_width + inner_corner_radius) + bottom_right_shift, border_width + inner_corner_radius, width - (border_width + inner_corner_radius) + bottom_right_shift, height - (border_width + inner_corner_radius) + bottom_right_shift, border_width + inner_corner_radius, height - (border_width + inner_corner_radius) + bottom_right_shift) self.canvas.itemconfig("inner_line_1", width=inner_corner_radius * 2) # create progress parts if not self.canvas.find_withtag("progress_parts"): self.canvas.create_polygon((0, 0, 0, 0), tags=("progress_line_1", "progress_parts"), joinstyle=tkinter.ROUND) self.canvas.tag_raise("progress_parts", "inner_parts") requires_recoloring = True if orientation == "w": self.canvas.coords("progress_line_1", border_width + inner_corner_radius, border_width + inner_corner_radius, border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value, border_width + inner_corner_radius, border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value, height - (border_width + inner_corner_radius) + bottom_right_shift, border_width + inner_corner_radius, height - (border_width + inner_corner_radius) + bottom_right_shift) elif orientation == "s": self.canvas.coords("progress_line_1", border_width + inner_corner_radius, border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), width - (border_width + inner_corner_radius), border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), width - (border_width + inner_corner_radius), height - (border_width + inner_corner_radius) + bottom_right_shift, border_width + inner_corner_radius, height - (border_width + inner_corner_radius) + bottom_right_shift) self.canvas.itemconfig("progress_line_1", width=inner_corner_radius * 2) return requires_recoloring def _draw_rounded_progress_bar_with_border_font_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int, progress_value: float, orientation: str) -> bool: print(width, height, border_width, corner_radius, inner_corner_radius) requires_recoloring = False # create border button parts if border_width > 0: if corner_radius > 0: # create canvas border corner parts if not already created if not self.canvas.find_withtag("border_oval_1_a"): self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.tag_lower("border_corner_part") requires_recoloring = True if not self.canvas.find_withtag("border_oval_3_a") and round(corner_radius) * 2 < height: self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.tag_lower("border_corner_part") requires_recoloring = True elif self.canvas.find_withtag("border_oval_3_a") and not round(corner_radius) * 2 < height: self.canvas.delete(["border_oval_3_a", "border_oval_3_b", "border_oval_4_a", "border_oval_4_b"]) # change position of border corner parts self.canvas.coords("border_oval_1_a", corner_radius, corner_radius, corner_radius) self.canvas.coords("border_oval_1_b", corner_radius, corner_radius, corner_radius) self.canvas.coords("border_oval_2_a", width - corner_radius, corner_radius, corner_radius) self.canvas.coords("border_oval_2_b", width - corner_radius, corner_radius, corner_radius) self.canvas.coords("border_oval_3_a", width - corner_radius, height - corner_radius, corner_radius) self.canvas.coords("border_oval_3_b", width - corner_radius, height - corner_radius, corner_radius) self.canvas.coords("border_oval_4_a", corner_radius, height - corner_radius, corner_radius) self.canvas.coords("border_oval_4_b", corner_radius, height - corner_radius, corner_radius) else: self.canvas.delete("border_corner_part") # delete border corner parts if not needed # create canvas border rectangle parts if not already created if not self.canvas.find_withtag("border_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_rectangle_part", "border_parts"), width=0) self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts"), width=0) self.canvas.tag_lower("border_rectangle_part") requires_recoloring = True # change position of border rectangle parts self.canvas.coords("border_rectangle_1", (0, corner_radius, width, height - corner_radius)) self.canvas.coords("border_rectangle_2", (corner_radius, 0, width - corner_radius, height)) else: self.canvas.delete("border_parts") # create inner button parts if inner_corner_radius > 0: # create canvas border corner parts if not already created if not self.canvas.find_withtag("inner_oval_1_a"): self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.tag_raise("inner_corner_part") requires_recoloring = True if not self.canvas.find_withtag("inner_oval_3_a") and round(inner_corner_radius) * 2 < height - (2 * border_width): self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER) self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180) self.canvas.tag_raise("inner_corner_part") requires_recoloring = True elif self.canvas.find_withtag("inner_oval_3_a") and not round(inner_corner_radius) * 2 < height - (2 * border_width): self.canvas.delete(["inner_oval_3_a", "inner_oval_3_b", "inner_oval_4_a", "inner_oval_4_b"]) # change position of border corner parts self.canvas.coords("inner_oval_1_a", border_width + inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_1_b", border_width + inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_2_a", width - border_width - inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_2_b", width - border_width - inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_3_a", width - border_width - inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_3_b", width - border_width - inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_4_a", border_width + inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) self.canvas.coords("inner_oval_4_b", border_width + inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) else: self.canvas.delete("inner_corner_part") # delete inner corner parts if not needed # create canvas inner rectangle parts if not already created if not self.canvas.find_withtag("inner_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_1", "inner_rectangle_part", "inner_parts"), width=0) self.canvas.tag_raise("inner_rectangle_part") requires_recoloring = True if not self.canvas.find_withtag("inner_rectangle_2") and inner_corner_radius * 2 < height - (border_width * 2): self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts"), width=0) self.canvas.tag_raise("inner_rectangle_part") requires_recoloring = True elif self.canvas.find_withtag("inner_rectangle_2") and not inner_corner_radius * 2 < height - (border_width * 2): self.canvas.delete("inner_rectangle_2") # change position of inner rectangle parts self.canvas.coords("inner_rectangle_1", (border_width + inner_corner_radius, border_width, width - border_width - inner_corner_radius, height - border_width)) self.canvas.coords("inner_rectangle_2", (border_width, border_width + inner_corner_radius, width - border_width, height - inner_corner_radius - border_width)) return requires_recoloring def _draw_rounded_rect_with_border_circle_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int) -> bool: requires_recoloring = False # border button parts if border_width > 0: if corner_radius > 0: if not self.canvas.find_withtag("border_oval_1"): self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_1", "border_corner_part", "border_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_2", "border_corner_part", "border_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_3", "border_corner_part", "border_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_4", "border_corner_part", "border_parts"), width=0) self.canvas.tag_lower("border_parts") requires_recoloring = True self.canvas.coords("border_oval_1", 0, 0, corner_radius * 2 - 1, corner_radius * 2 - 1) self.canvas.coords("border_oval_2", width - corner_radius * 2, 0, width - 1, corner_radius * 2 - 1) self.canvas.coords("border_oval_3", 0, height - corner_radius * 2, corner_radius * 2 - 1, height - 1) self.canvas.coords("border_oval_4", width - corner_radius * 2, height - corner_radius * 2, width - 1, height - 1) else: self.canvas.delete("border_corner_part") if not self.canvas.find_withtag("border_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_rectangle_part", "border_parts"), width=0) self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts"), width=0) self.canvas.tag_lower("border_parts") requires_recoloring = True self.canvas.coords("border_rectangle_1", (0, corner_radius, width, height - corner_radius)) self.canvas.coords("border_rectangle_2", (corner_radius, 0, width - corner_radius, height)) else: self.canvas.delete("border_parts") # inner button parts if inner_corner_radius > 0: if not self.canvas.find_withtag("inner_oval_1"): self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_1", "inner_corner_part", "inner_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_2", "inner_corner_part", "inner_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_3", "inner_corner_part", "inner_parts"), width=0) self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_4", "inner_corner_part", "inner_parts"), width=0) self.canvas.tag_raise("inner_parts") requires_recoloring = True self.canvas.coords("inner_oval_1", (border_width, border_width, border_width + inner_corner_radius * 2 - 1, border_width + inner_corner_radius * 2 - 1)) self.canvas.coords("inner_oval_2", (width - border_width - inner_corner_radius * 2, border_width, width - border_width - 1, border_width + inner_corner_radius * 2 - 1)) self.canvas.coords("inner_oval_3", (border_width, height - border_width - inner_corner_radius * 2, border_width + inner_corner_radius * 2 - 1, height - border_width - 1)) self.canvas.coords("inner_oval_4", (width - border_width - inner_corner_radius * 2, height - border_width - inner_corner_radius * 2, width - border_width - 1, height - border_width - 1)) else: self.canvas.delete("inner_corner_part") # delete inner corner parts if not needed if not self.canvas.find_withtag("inner_rectangle_1"): self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_1", "inner_rectangle_part", "inner_parts"), width=0) self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts"), width=0) self.canvas.tag_raise("inner_parts") requires_recoloring = True self.canvas.coords("inner_rectangle_1", (border_width + inner_corner_radius, border_width, width - border_width - inner_corner_radius, height - border_width)) self.canvas.coords("inner_rectangle_2", (border_width, border_width + inner_corner_radius, width - border_width, height - inner_corner_radius - border_width)) return requires_recoloring def draw_rounded_button(self, canvas, width, height): pass