mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
created DrawEngine, drawing CTkButton, CTkFrame, CTkEntry, CTkLabel, CTkCheckBox now with the DrawEngine
This commit is contained in:
parent
b3d43bff10
commit
a52599bf53
@ -62,21 +62,24 @@ def set_default_color_theme(color_string):
|
|||||||
CTkThemeManager.initialize_color_theme(color_string)
|
CTkThemeManager.initialize_color_theme(color_string)
|
||||||
|
|
||||||
|
|
||||||
def load_font_windows(fontpath: str, private=True, enumerable=False):
|
import warnings
|
||||||
from ctypes import windll, byref, create_string_buffer
|
warnings.simplefilter("ignore", category=UserWarning)
|
||||||
|
|
||||||
FR_PRIVATE = 0x10
|
import pyglet.font
|
||||||
FR_NOT_ENUM = 0x20
|
|
||||||
|
|
||||||
pathbuf = create_string_buffer(bytes(fontpath, "utf-8"))
|
|
||||||
add_font_resource_ex = windll.gdi32.AddFontResourceExA
|
|
||||||
flags = (FR_PRIVATE if private else 0) | (FR_NOT_ENUM if not enumerable else 0)
|
|
||||||
num_fonts_added = add_font_resource_ex(byref(pathbuf), flags, 0)
|
|
||||||
return bool(num_fonts_added)
|
|
||||||
|
|
||||||
|
|
||||||
if sys.platform.startswith("win"):
|
# load text fonts and custom font with circle shapes for round corner rendering
|
||||||
# load custom font for rendering circles on the tkinter.Canvas with antialiasing
|
script_directory = os.path.dirname(os.path.abspath(__file__))
|
||||||
script_directory = os.path.dirname(os.path.abspath(__file__))
|
pyglet.font.add_file(os.path.join(script_directory, "assets", "CustomTkinter_shapes_font-Regular.otf"))
|
||||||
print("load_font_windows:", load_font_windows(os.path.join(script_directory, "assets", "CustomTkinter_shapes_font-Regular.otf"), private=True))
|
pyglet.font.add_file(os.path.join(script_directory, "assets", "Roboto", "Roboto-Regular.ttf"))
|
||||||
CTkSettings.circle_font_is_ready = True
|
pyglet.font.add_file(os.path.join(script_directory, "assets", "Roboto", "Roboto-Medium.ttf"))
|
||||||
|
CTkSettings.circle_font_is_ready = pyglet.font.have_font("CustomTkinter_shapes_font")
|
||||||
|
|
||||||
|
warnings.simplefilter("default")
|
||||||
|
|
||||||
|
# correct drawing method if font could not be loaded
|
||||||
|
if not CTkSettings.circle_font_is_ready:
|
||||||
|
if CTkSettings.preferred_drawing_method == "font_shapes":
|
||||||
|
sys.stderr.write("WARNING (customtkinter.CTkSettings): " +
|
||||||
|
"Preferred drawing method 'font_shapes' can not be used because the font file could not be loaded. Using 'circle_shapes' instead.")
|
||||||
|
CTkSettings.preferred_drawing_method = "circle_shapes"
|
||||||
|
@ -7,6 +7,7 @@ from .appearance_mode_tracker import AppearanceModeTracker
|
|||||||
from .customtkinter_theme_manager import CTkThemeManager
|
from .customtkinter_theme_manager import CTkThemeManager
|
||||||
from .customtkinter_canvas import CTkCanvas
|
from .customtkinter_canvas import CTkCanvas
|
||||||
from .customtkinter_settings import CTkSettings
|
from .customtkinter_settings import CTkSettings
|
||||||
|
from .customtkinter_draw_engine import DrawEngine
|
||||||
|
|
||||||
|
|
||||||
class CTkButton(tkinter.Frame):
|
class CTkButton(tkinter.Frame):
|
||||||
@ -69,8 +70,8 @@ class CTkButton(tkinter.Frame):
|
|||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.configure(width=self.width, height=self.height)
|
self.configure(width=self.width, height=self.height)
|
||||||
self.corner_radius = self.calc_optimal_corner_radius(corner_radius) # optimise for less artifacts
|
self.corner_radius = corner_radius
|
||||||
self.border_width = round(border_width) # round border_width (inner parts not centered otherwise)
|
self.border_width = border_width
|
||||||
|
|
||||||
if self.corner_radius * 2 > self.height:
|
if self.corner_radius * 2 > self.height:
|
||||||
self.corner_radius = self.height / 2
|
self.corner_radius = self.height / 2
|
||||||
@ -109,6 +110,8 @@ class CTkButton(tkinter.Frame):
|
|||||||
height=self.height)
|
height=self.height)
|
||||||
self.canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew")
|
self.canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew")
|
||||||
|
|
||||||
|
self.draw_engine = DrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
|
||||||
|
|
||||||
# event bindings
|
# event bindings
|
||||||
if self.hover is True:
|
if self.hover is True:
|
||||||
self.canvas.bind("<Enter>", self.on_enter)
|
self.canvas.bind("<Enter>", self.on_enter)
|
||||||
@ -148,41 +151,12 @@ class CTkButton(tkinter.Frame):
|
|||||||
else:
|
else:
|
||||||
return self.master.cget("bg")
|
return self.master.cget("bg")
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def calc_optimal_corner_radius(user_corner_radius):
|
|
||||||
if sys.platform == "darwin":
|
|
||||||
return user_corner_radius # on macOS just use given value (canvas has Antialiasing)
|
|
||||||
elif sys.platform.startswith("win"):
|
|
||||||
return round(user_corner_radius)
|
|
||||||
else:
|
|
||||||
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(self, no_color_updates=False):
|
def draw(self, no_color_updates=False):
|
||||||
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
|
||||||
draw_color_update = False
|
|
||||||
|
|
||||||
# decide the drawing method
|
if no_color_updates is False or requires_recoloring:
|
||||||
if sys.platform == "darwin":
|
|
||||||
# on macOS draw button with polygons (positions are more accurate, macOS has Antialiasing)
|
|
||||||
self.draw_with_polygon_shapes()
|
|
||||||
elif sys.platform.startswith("win"):
|
|
||||||
if CTkSettings.circle_font_is_ready:
|
|
||||||
draw_color_update = self.draw_with_font_shapes_and_rects()
|
|
||||||
else:
|
|
||||||
self.draw_with_ovals_and_rects()
|
|
||||||
else:
|
|
||||||
# on other draw with ovals (corner_radius can be optimised to look better than with polygons)
|
|
||||||
self.draw_with_ovals_and_rects()
|
|
||||||
|
|
||||||
if no_color_updates is False or draw_color_update:
|
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
|
|
||||||
# set color for the button border parts (outline)
|
# set color for the button border parts (outline)
|
||||||
self.canvas.itemconfig("border_parts",
|
self.canvas.itemconfig("border_parts",
|
||||||
@ -197,10 +171,8 @@ class CTkButton(tkinter.Frame):
|
|||||||
fill=CTkThemeManager.darken_hex_color(CTkThemeManager.single_color(self.bg_color, self.appearance_mode)))
|
fill=CTkThemeManager.darken_hex_color(CTkThemeManager.single_color(self.bg_color, self.appearance_mode)))
|
||||||
else:
|
else:
|
||||||
self.canvas.itemconfig("inner_parts",
|
self.canvas.itemconfig("inner_parts",
|
||||||
outline=CTkThemeManager.darken_hex_color(
|
outline=CTkThemeManager.darken_hex_color(CTkThemeManager.single_color(self.fg_color, self.appearance_mode)),
|
||||||
CTkThemeManager.single_color(self.fg_color, self.appearance_mode)),
|
fill=CTkThemeManager.darken_hex_color(CTkThemeManager.single_color(self.fg_color, self.appearance_mode)))
|
||||||
fill=CTkThemeManager.darken_hex_color(
|
|
||||||
CTkThemeManager.single_color(self.fg_color, self.appearance_mode)))
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if self.fg_color is None:
|
if self.fg_color is None:
|
||||||
@ -230,11 +202,9 @@ class CTkButton(tkinter.Frame):
|
|||||||
# set text_label bg color (label color)
|
# set text_label bg color (label color)
|
||||||
if self.state == tkinter.DISABLED:
|
if self.state == tkinter.DISABLED:
|
||||||
if self.fg_color is None:
|
if self.fg_color is None:
|
||||||
self.text_label.configure(
|
self.text_label.configure(bg=CTkThemeManager.darken_hex_color(CTkThemeManager.single_color(self.bg_color, self.appearance_mode)))
|
||||||
bg=CTkThemeManager.darken_hex_color(CTkThemeManager.single_color(self.bg_color, self.appearance_mode)))
|
|
||||||
else:
|
else:
|
||||||
self.text_label.configure(
|
self.text_label.configure(bg=CTkThemeManager.darken_hex_color(CTkThemeManager.single_color(self.fg_color, self.appearance_mode)))
|
||||||
bg=CTkThemeManager.darken_hex_color(CTkThemeManager.single_color(self.fg_color, self.appearance_mode)))
|
|
||||||
else:
|
else:
|
||||||
if self.fg_color is None:
|
if self.fg_color is None:
|
||||||
self.text_label.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
self.text_label.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
@ -298,250 +268,6 @@ class CTkButton(tkinter.Frame):
|
|||||||
self.image_label.grid(row=1, column=0, padx=self.corner_radius, sticky="n", columnspan=2, rowspan=1)
|
self.image_label.grid(row=1, column=0, padx=self.corner_radius, sticky="n", columnspan=2, rowspan=1)
|
||||||
self.text_label.grid(row=0, column=0, padx=self.corner_radius, sticky="s", columnspan=2, rowspan=1, pady=self.border_width)
|
self.text_label.grid(row=0, column=0, padx=self.corner_radius, sticky="s", columnspan=2, rowspan=1, pady=self.border_width)
|
||||||
|
|
||||||
def draw_with_polygon_shapes(self):
|
|
||||||
""" draw the button parts with just two polygons that have a rounded border """
|
|
||||||
|
|
||||||
# create border button parts (only if border exists)
|
|
||||||
if self.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.coords("border_line_1",
|
|
||||||
(self.corner_radius,
|
|
||||||
self.corner_radius,
|
|
||||||
self.width - self.corner_radius,
|
|
||||||
self.corner_radius,
|
|
||||||
self.width - self.corner_radius,
|
|
||||||
self.height - self.corner_radius,
|
|
||||||
self.corner_radius,
|
|
||||||
self.height - self.corner_radius))
|
|
||||||
self.canvas.itemconfig("border_line_1",
|
|
||||||
joinstyle=tkinter.ROUND,
|
|
||||||
width=self.corner_radius * 2)
|
|
||||||
|
|
||||||
# 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"))
|
|
||||||
|
|
||||||
if self.corner_radius <= self.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",
|
|
||||||
(self.border_width + self.inner_corner_radius,
|
|
||||||
self.border_width + self.inner_corner_radius,
|
|
||||||
self.width - (self.border_width + self.inner_corner_radius) + bottom_right_shift,
|
|
||||||
self.border_width + self.inner_corner_radius,
|
|
||||||
self.width - (self.border_width + self.inner_corner_radius) + bottom_right_shift,
|
|
||||||
self.height - (self.border_width + self.inner_corner_radius) + bottom_right_shift,
|
|
||||||
self.border_width + self.inner_corner_radius,
|
|
||||||
self.height - (self.border_width + self.inner_corner_radius) + bottom_right_shift))
|
|
||||||
self.canvas.itemconfig("inner_line_1",
|
|
||||||
joinstyle=tkinter.ROUND,
|
|
||||||
width=self.inner_corner_radius * 2)
|
|
||||||
|
|
||||||
def draw_with_ovals_and_rects(self):
|
|
||||||
""" draw the button parts with ovals at the corner and rectangles in the middle """
|
|
||||||
|
|
||||||
if sys.platform == "darwin":
|
|
||||||
oval_bottom_right_shift = 0
|
|
||||||
rect_bottom_right_shift = 0
|
|
||||||
else:
|
|
||||||
# ovals and rects are always rendered too large on Windows and need to be made smaller by -1
|
|
||||||
oval_bottom_right_shift = -1
|
|
||||||
rect_bottom_right_shift = -1
|
|
||||||
|
|
||||||
# create border button parts
|
|
||||||
if self.border_width > 0:
|
|
||||||
if self.corner_radius > 0:
|
|
||||||
# create canvas border corner parts if not already created
|
|
||||||
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"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_2", "border_corner_part", "border_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_3", "border_corner_part", "border_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_4", "border_corner_part", "border_parts"))
|
|
||||||
|
|
||||||
# change position of border corner parts
|
|
||||||
self.canvas.coords("border_oval_1", (0,
|
|
||||||
0,
|
|
||||||
self.corner_radius * 2 + oval_bottom_right_shift,
|
|
||||||
self.corner_radius * 2 + oval_bottom_right_shift))
|
|
||||||
self.canvas.coords("border_oval_2", (self.width - self.corner_radius * 2,
|
|
||||||
0,
|
|
||||||
self.width + oval_bottom_right_shift,
|
|
||||||
self.corner_radius * 2 + oval_bottom_right_shift))
|
|
||||||
self.canvas.coords("border_oval_3", (0,
|
|
||||||
self.height - self.corner_radius * 2,
|
|
||||||
self.corner_radius * 2 + oval_bottom_right_shift,
|
|
||||||
self.height + oval_bottom_right_shift))
|
|
||||||
self.canvas.coords("border_oval_4", (self.width - self.corner_radius * 2,
|
|
||||||
self.height - self.corner_radius * 2,
|
|
||||||
self.width + oval_bottom_right_shift,
|
|
||||||
self.height + oval_bottom_right_shift))
|
|
||||||
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"))
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts"))
|
|
||||||
|
|
||||||
# change position of border rectangle parts
|
|
||||||
self.canvas.coords("border_rectangle_1", (0,
|
|
||||||
self.corner_radius,
|
|
||||||
self.width + rect_bottom_right_shift,
|
|
||||||
self.height - self.corner_radius + rect_bottom_right_shift))
|
|
||||||
self.canvas.coords("border_rectangle_2", (self.corner_radius,
|
|
||||||
0,
|
|
||||||
self.width - self.corner_radius + rect_bottom_right_shift,
|
|
||||||
self.height + rect_bottom_right_shift))
|
|
||||||
|
|
||||||
# create inner button parts
|
|
||||||
if self.inner_corner_radius > 0:
|
|
||||||
|
|
||||||
# create canvas border corner parts if not already created
|
|
||||||
if not self.canvas.find_withtag("inner_corner_part"):
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_1", "inner_corner_part", "inner_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_2", "inner_corner_part", "inner_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_3", "inner_corner_part", "inner_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_4", "inner_corner_part", "inner_parts"))
|
|
||||||
|
|
||||||
# change position of border corner parts
|
|
||||||
self.canvas.coords("inner_oval_1", (self.border_width,
|
|
||||||
self.border_width,
|
|
||||||
self.border_width + self.inner_corner_radius * 2 + oval_bottom_right_shift,
|
|
||||||
self.border_width + self.inner_corner_radius * 2 + oval_bottom_right_shift))
|
|
||||||
self.canvas.coords("inner_oval_2", (self.width - self.border_width - self.inner_corner_radius * 2,
|
|
||||||
self.border_width,
|
|
||||||
self.width - self.border_width + oval_bottom_right_shift,
|
|
||||||
self.border_width + self.inner_corner_radius * 2 + oval_bottom_right_shift))
|
|
||||||
self.canvas.coords("inner_oval_3", (self.border_width,
|
|
||||||
self.height - self.border_width - self.inner_corner_radius * 2,
|
|
||||||
self.border_width + self.inner_corner_radius * 2 + oval_bottom_right_shift,
|
|
||||||
self.height - self.border_width + oval_bottom_right_shift))
|
|
||||||
self.canvas.coords("inner_oval_4", (self.width - self.border_width - self.inner_corner_radius * 2,
|
|
||||||
self.height - self.border_width - self.inner_corner_radius * 2,
|
|
||||||
self.width - self.border_width + oval_bottom_right_shift,
|
|
||||||
self.height - self.border_width + oval_bottom_right_shift))
|
|
||||||
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_part"):
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_1", "inner_rectangle_part", "inner_parts"))
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts"))
|
|
||||||
|
|
||||||
# change position of inner rectangle parts
|
|
||||||
self.canvas.coords("inner_rectangle_1", (self.border_width + self.inner_corner_radius,
|
|
||||||
self.border_width,
|
|
||||||
self.width - self.border_width - self.inner_corner_radius + rect_bottom_right_shift,
|
|
||||||
self.height - self.border_width + rect_bottom_right_shift))
|
|
||||||
self.canvas.coords("inner_rectangle_2", (self.border_width,
|
|
||||||
self.border_width + self.inner_corner_radius,
|
|
||||||
self.width - self.border_width + rect_bottom_right_shift,
|
|
||||||
self.height - self.inner_corner_radius - self.border_width + rect_bottom_right_shift))
|
|
||||||
|
|
||||||
def draw_with_font_shapes_and_rects(self):
|
|
||||||
draw_color_update = False
|
|
||||||
|
|
||||||
# create border button parts
|
|
||||||
if self.border_width > 0:
|
|
||||||
if self.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)
|
|
||||||
|
|
||||||
if not self.canvas.find_withtag("border_oval_3_a") and round(self.corner_radius) * 2 < self.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)
|
|
||||||
draw_color_update = True
|
|
||||||
self.canvas.tag_lower("border_parts")
|
|
||||||
elif self.canvas.find_withtag("border_oval_3_a") and not round(self.corner_radius) * 2 < self.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", self.corner_radius, self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_1_b", self.corner_radius, self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_2_a", self.width - self.corner_radius, self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_2_b", self.width - self.corner_radius, self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_3_a", self.width - self.corner_radius, self.height - self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_3_b", self.width - self.corner_radius, self.height - self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_4_a", self.corner_radius, self.height - self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_4_b", self.corner_radius, self.height - self.corner_radius, self.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"))
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts"))
|
|
||||||
|
|
||||||
# change position of border rectangle parts
|
|
||||||
self.canvas.coords("border_rectangle_1", (0, self.corner_radius, self.width -1, self.height - self.corner_radius -1))
|
|
||||||
self.canvas.coords("border_rectangle_2", (self.corner_radius, 0, self.width - self.corner_radius -1, self.height -1))
|
|
||||||
|
|
||||||
# create inner button parts
|
|
||||||
if self.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)
|
|
||||||
|
|
||||||
if not self.canvas.find_withtag("inner_oval_3_a") and round(self.inner_corner_radius) * 2 < self.height - (2 * self.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_parts")
|
|
||||||
draw_color_update = True
|
|
||||||
elif self.canvas.find_withtag("inner_oval_3_a") and not round(self.inner_corner_radius) * 2 < self.height - (2 * self.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", self.border_width + self.inner_corner_radius, self.border_width + self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_1_b", self.border_width + self.inner_corner_radius, self.border_width + self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_2_a", self.width - self.border_width - self.inner_corner_radius, self.border_width + self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_2_b", self.width - self.border_width - self.inner_corner_radius, self.border_width + self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_3_a", self.width - self.border_width - self.inner_corner_radius, self.height - self.border_width - self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_3_b", self.width - self.border_width - self.inner_corner_radius, self.height - self.border_width - self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_4_a", self.border_width + self.inner_corner_radius, self.height - self.border_width - self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_4_b", self.border_width + self.inner_corner_radius, self.height - self.border_width - self.inner_corner_radius, self.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"))
|
|
||||||
|
|
||||||
if not self.canvas.find_withtag("inner_rectangle_2") and self.inner_corner_radius * 2 < self.height - (self.border_width * 2):
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts"))
|
|
||||||
self.canvas.tag_raise("inner_parts")
|
|
||||||
draw_color_update = True
|
|
||||||
elif self.canvas.find_withtag("inner_rectangle_2") and not self.inner_corner_radius * 2 < self.height - (self.border_width * 2):
|
|
||||||
self.canvas.delete("inner_rectangle_2")
|
|
||||||
|
|
||||||
# change position of inner rectangle parts
|
|
||||||
self.canvas.coords("inner_rectangle_1", (self.border_width + self.inner_corner_radius,
|
|
||||||
self.border_width,
|
|
||||||
self.width - self.border_width - self.inner_corner_radius -1,
|
|
||||||
self.height - self.border_width -1))
|
|
||||||
self.canvas.coords("inner_rectangle_2", (self.border_width,
|
|
||||||
self.border_width + self.inner_corner_radius,
|
|
||||||
self.width - self.border_width -1,
|
|
||||||
self.height - self.inner_corner_radius - self.border_width -1))
|
|
||||||
|
|
||||||
return draw_color_update
|
|
||||||
|
|
||||||
def config(self, *args, **kwargs):
|
def config(self, *args, **kwargs):
|
||||||
self.configure(*args, **kwargs)
|
self.configure(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ from .customtkinter_settings import CTkSettings
|
|||||||
|
|
||||||
class CTkCanvas(tkinter.Canvas):
|
class CTkCanvas(tkinter.Canvas):
|
||||||
|
|
||||||
|
# This dict maps a corner_radius of a circle to a specific font character, which is circle shape which fills the space
|
||||||
|
# of one monospace character to a specific amount from 100% to 90% (A to I).
|
||||||
radius_to_char = {19: 'B', 18: 'B', 17: 'B', 16: 'B', 15: 'B', 14: 'B', 13: 'B', 12: 'B', 11: 'B', 10: 'B',
|
radius_to_char = {19: 'B', 18: 'B', 17: 'B', 16: 'B', 15: 'B', 14: 'B', 13: 'B', 12: 'B', 11: 'B', 10: 'B',
|
||||||
9: 'C', 8: 'D', 7: 'C', 6: 'E', 5: 'F', 4: 'F', 3: 'H', 2: 'H', 1: 'H', 0: 'A'}
|
9: 'C', 8: 'D', 7: 'C', 6: 'E', 5: 'F', 4: 'F', 3: 'H', 2: 'H', 1: 'H', 0: 'A'}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ from .appearance_mode_tracker import AppearanceModeTracker
|
|||||||
from .customtkinter_theme_manager import CTkThemeManager
|
from .customtkinter_theme_manager import CTkThemeManager
|
||||||
from .customtkinter_canvas import CTkCanvas
|
from .customtkinter_canvas import CTkCanvas
|
||||||
from .customtkinter_settings import CTkSettings
|
from .customtkinter_settings import CTkSettings
|
||||||
|
from .customtkinter_draw_engine import DrawEngine
|
||||||
|
|
||||||
|
|
||||||
class CTkCheckBox(tkinter.Frame):
|
class CTkCheckBox(tkinter.Frame):
|
||||||
@ -65,14 +66,14 @@ class CTkCheckBox(tkinter.Frame):
|
|||||||
|
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.corner_radius = self.calc_optimal_corner_radius(corner_radius) # optimise for less artifacts
|
self.corner_radius = corner_radius
|
||||||
|
|
||||||
if self.corner_radius*2 > self.height:
|
if self.corner_radius*2 > self.height:
|
||||||
self.corner_radius = self.height/2
|
self.corner_radius = self.height/2
|
||||||
elif self.corner_radius*2 > self.width:
|
elif self.corner_radius*2 > self.width:
|
||||||
self.corner_radius = self.width/2
|
self.corner_radius = self.width/2
|
||||||
|
|
||||||
self.border_width = round(border_width)
|
self.border_width = border_width
|
||||||
|
|
||||||
if self.corner_radius >= self.border_width:
|
if self.corner_radius >= self.border_width:
|
||||||
self.inner_corner_radius = self.corner_radius - self.border_width
|
self.inner_corner_radius = self.corner_radius - self.border_width
|
||||||
@ -100,6 +101,8 @@ class CTkCheckBox(tkinter.Frame):
|
|||||||
height=self.height)
|
height=self.height)
|
||||||
self.canvas.pack(side='left')
|
self.canvas.pack(side='left')
|
||||||
|
|
||||||
|
self.draw_engine = DrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
|
||||||
|
|
||||||
if sys.platform == "darwin" and self.state == tkinter.NORMAL and CTkSettings.hand_cursor_enabled:
|
if sys.platform == "darwin" and self.state == tkinter.NORMAL and CTkSettings.hand_cursor_enabled:
|
||||||
self.canvas.configure(cursor="pointinghand")
|
self.canvas.configure(cursor="pointinghand")
|
||||||
elif sys.platform.startswith("win") and self.state == tkinter.NORMAL and CTkSettings.hand_cursor_enabled:
|
elif sys.platform.startswith("win") and self.state == tkinter.NORMAL and CTkSettings.hand_cursor_enabled:
|
||||||
@ -137,33 +140,11 @@ class CTkCheckBox(tkinter.Frame):
|
|||||||
else:
|
else:
|
||||||
return self.master.cget("bg")
|
return self.master.cget("bg")
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def calc_optimal_corner_radius(user_corner_radius):
|
|
||||||
if sys.platform == "darwin":
|
|
||||||
return user_corner_radius # on macOS just use given value (canvas has Antialiasing)
|
|
||||||
elif sys.platform.startswith("win"):
|
|
||||||
return round(user_corner_radius)
|
|
||||||
else:
|
|
||||||
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(self):
|
def draw(self):
|
||||||
if sys.platform == "darwin":
|
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
|
||||||
self.draw_with_ovals_and_rects()
|
|
||||||
elif sys.platform.startswith("win"):
|
|
||||||
self.draw_with_font_shapes_and_rects()
|
|
||||||
else:
|
|
||||||
self.draw_with_ovals_and_rects()
|
|
||||||
|
|
||||||
self.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
|
||||||
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
|
self.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
|
|
||||||
if self.check_state is False:
|
if self.check_state is False:
|
||||||
self.canvas.itemconfig("inner_parts",
|
self.canvas.itemconfig("inner_parts",
|
||||||
@ -192,142 +173,6 @@ class CTkCheckBox(tkinter.Frame):
|
|||||||
|
|
||||||
self.set_text(self.text)
|
self.set_text(self.text)
|
||||||
|
|
||||||
def draw_with_ovals_and_rects(self):
|
|
||||||
# border button parts
|
|
||||||
if self.border_width > 0:
|
|
||||||
if self.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"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_2", "border_corner_part", "border_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_3", "border_corner_part", "border_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("border_oval_4", "border_corner_part", "border_parts"))
|
|
||||||
|
|
||||||
self.canvas.coords("border_oval_1", 0, 0, self.corner_radius * 2 - 1, self.corner_radius * 2 - 1)
|
|
||||||
self.canvas.coords("border_oval_2", self.width - self.corner_radius * 2, 0, self.width - 1, self.corner_radius * 2 - 1)
|
|
||||||
self.canvas.coords("border_oval_3", 0, self.height - self.corner_radius * 2, self.corner_radius * 2 - 1, self.height - 1)
|
|
||||||
self.canvas.coords("border_oval_4", self.width - self.corner_radius * 2, self.height - self.corner_radius * 2, self.width - 1, self.height - 1)
|
|
||||||
|
|
||||||
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"))
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts"))
|
|
||||||
|
|
||||||
self.canvas.coords("border_rectangle_1", (0, self.corner_radius, self.width - 1, self.height - self.corner_radius - 1))
|
|
||||||
self.canvas.coords("border_rectangle_2", (self.corner_radius, 0, self.width - self.corner_radius - 1, self.height - 1))
|
|
||||||
|
|
||||||
# inner button parts
|
|
||||||
if self.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"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_2", "inner_corner_part", "inner_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_3", "inner_corner_part", "inner_parts"))
|
|
||||||
self.canvas.create_oval(0, 0, 0, 0, tags=("inner_oval_4", "inner_corner_part", "inner_parts"))
|
|
||||||
|
|
||||||
self.canvas.coords("inner_oval_1", (self.border_width, self.border_width,
|
|
||||||
self.border_width + self.inner_corner_radius * 2 - 1, self.border_width + self.inner_corner_radius * 2 - 1))
|
|
||||||
self.canvas.coords("inner_oval_2", (self.width - self.border_width - self.inner_corner_radius * 2, self.border_width,
|
|
||||||
self.width - self.border_width - 1, self.border_width + self.inner_corner_radius * 2 - 1))
|
|
||||||
self.canvas.coords("inner_oval_3", (self.border_width, self.height - self.border_width - self.inner_corner_radius * 2,
|
|
||||||
self.border_width + self.inner_corner_radius * 2 - 1, self.height - self.border_width - 1))
|
|
||||||
self.canvas.coords("inner_oval_4", (self.width - self.border_width - self.inner_corner_radius * 2, self.height - self.border_width - self.inner_corner_radius * 2,
|
|
||||||
self.width - self.border_width - 1, self.height - self.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"))
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts"))
|
|
||||||
|
|
||||||
self.canvas.coords("inner_rectangle_1", (self.border_width + self.inner_corner_radius,
|
|
||||||
self.border_width,
|
|
||||||
self.width - self.border_width - self.inner_corner_radius - 1,
|
|
||||||
self.height - self.border_width - 1))
|
|
||||||
self.canvas.coords("inner_rectangle_2", (self.border_width,
|
|
||||||
self.border_width + self.inner_corner_radius,
|
|
||||||
self.width - self.border_width - 1,
|
|
||||||
self.height - self.inner_corner_radius - self.border_width - 1))
|
|
||||||
|
|
||||||
def draw_with_font_shapes_and_rects(self):
|
|
||||||
# border button parts
|
|
||||||
if self.border_width > 0:
|
|
||||||
if self.corner_radius > 0:
|
|
||||||
|
|
||||||
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"))
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts"), angle=180)
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts"))
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts"), angle=180)
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts"))
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts"), angle=180)
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts"))
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts"), angle=180)
|
|
||||||
self.canvas.tag_lower("border_parts")
|
|
||||||
|
|
||||||
self.canvas.coords("border_oval_1_a", self.corner_radius, self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_1_b", self.corner_radius, self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_2_a", self.width - self.corner_radius, self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_2_b", self.width - self.corner_radius, self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_3_a", self.width - self.corner_radius, self.height - self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_3_b", self.width - self.corner_radius, self.height - self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_4_a", self.corner_radius, self.height - self.corner_radius, self.corner_radius)
|
|
||||||
self.canvas.coords("border_oval_4_b", self.corner_radius, self.height - self.corner_radius, self.corner_radius)
|
|
||||||
|
|
||||||
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"))
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_2", "border_rectangle_part", "border_parts"))
|
|
||||||
self.canvas.tag_lower("border_parts")
|
|
||||||
|
|
||||||
self.canvas.coords("border_rectangle_1", (0, self.corner_radius, self.width - 1, self.height - self.corner_radius - 1))
|
|
||||||
self.canvas.coords("border_rectangle_2", (self.corner_radius, 0, self.width - self.corner_radius - 1, self.height - 1))
|
|
||||||
|
|
||||||
# inner button parts
|
|
||||||
if self.inner_corner_radius > 0:
|
|
||||||
|
|
||||||
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"))
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts"), angle=180)
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part", "inner_parts"))
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts"), angle=180)
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts"))
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts"), angle=180)
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_a", "inner_corner_part", "inner_parts"))
|
|
||||||
self.canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts"), angle=180)
|
|
||||||
self.canvas.tag_raise("inner_parts")
|
|
||||||
|
|
||||||
self.canvas.coords("inner_oval_1_a", self.border_width + self.inner_corner_radius, self.border_width + self.inner_corner_radius,
|
|
||||||
self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_1_b", self.border_width + self.inner_corner_radius, self.border_width + self.inner_corner_radius,
|
|
||||||
self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_2_a", self.width - self.border_width - self.inner_corner_radius, self.border_width + self.inner_corner_radius,
|
|
||||||
self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_2_b", self.width - self.border_width - self.inner_corner_radius, self.border_width + self.inner_corner_radius,
|
|
||||||
self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_3_a", self.width - self.border_width - self.inner_corner_radius,
|
|
||||||
self.height - self.border_width - self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_3_b", self.width - self.border_width - self.inner_corner_radius,
|
|
||||||
self.height - self.border_width - self.inner_corner_radius, self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_4_a", self.border_width + self.inner_corner_radius, self.height - self.border_width - self.inner_corner_radius,
|
|
||||||
self.inner_corner_radius)
|
|
||||||
self.canvas.coords("inner_oval_4_b", self.border_width + self.inner_corner_radius, self.height - self.border_width - self.inner_corner_radius,
|
|
||||||
self.inner_corner_radius)
|
|
||||||
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"))
|
|
||||||
self.canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_2", "inner_rectangle_part", "inner_parts"))
|
|
||||||
self.canvas.tag_raise("inner_parts")
|
|
||||||
|
|
||||||
self.canvas.coords("inner_rectangle_1", (self.border_width + self.inner_corner_radius,
|
|
||||||
self.border_width,
|
|
||||||
self.width - self.border_width - self.inner_corner_radius - 1,
|
|
||||||
self.height - self.border_width - 1))
|
|
||||||
self.canvas.coords("inner_rectangle_2", (self.border_width,
|
|
||||||
self.border_width + self.inner_corner_radius,
|
|
||||||
self.width - self.border_width - 1,
|
|
||||||
self.height - self.inner_corner_radius - self.border_width - 1))
|
|
||||||
|
|
||||||
def config(self, *args, **kwargs):
|
def config(self, *args, **kwargs):
|
||||||
self.configure(*args, **kwargs)
|
self.configure(*args, **kwargs)
|
||||||
|
|
||||||
|
294
customtkinter/customtkinter_draw_engine.py
Normal file
294
customtkinter/customtkinter_draw_engine.py
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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":
|
||||||
|
return 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:
|
||||||
|
""" returns bool if recoloring is necessary """
|
||||||
|
|
||||||
|
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_bar_with_border(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int]) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def draw_rounded_button(self, canvas, width, height):
|
||||||
|
pass
|
@ -5,6 +5,9 @@ from .customtkinter_tk import CTk
|
|||||||
from .customtkinter_frame import CTkFrame
|
from .customtkinter_frame import CTkFrame
|
||||||
from .appearance_mode_tracker import AppearanceModeTracker
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
from .customtkinter_theme_manager import CTkThemeManager
|
from .customtkinter_theme_manager import CTkThemeManager
|
||||||
|
from .customtkinter_canvas import CTkCanvas
|
||||||
|
from .customtkinter_settings import CTkSettings
|
||||||
|
from .customtkinter_draw_engine import DrawEngine
|
||||||
|
|
||||||
|
|
||||||
class CTkEntry(tkinter.Frame):
|
class CTkEntry(tkinter.Frame):
|
||||||
@ -14,7 +17,7 @@ class CTkEntry(tkinter.Frame):
|
|||||||
fg_color="default_theme",
|
fg_color="default_theme",
|
||||||
text_color="default_theme",
|
text_color="default_theme",
|
||||||
placeholder_text_color="default_theme",
|
placeholder_text_color="default_theme",
|
||||||
text_font=None,
|
text_font="default_theme",
|
||||||
placeholder_text=None,
|
placeholder_text=None,
|
||||||
corner_radius=8,
|
corner_radius=8,
|
||||||
width=120,
|
width=120,
|
||||||
@ -55,16 +58,7 @@ class CTkEntry(tkinter.Frame):
|
|||||||
self.fg_color = CTkThemeManager.ENTRY_COLOR if fg_color == "default_theme" else fg_color
|
self.fg_color = CTkThemeManager.ENTRY_COLOR if fg_color == "default_theme" else fg_color
|
||||||
self.text_color = CTkThemeManager.TEXT_COLOR if text_color == "default_theme" else text_color
|
self.text_color = CTkThemeManager.TEXT_COLOR if text_color == "default_theme" else text_color
|
||||||
self.placeholder_text_color = CTkThemeManager.PLACEHOLDER_TEXT_COLOR if placeholder_text_color == "default_theme" else placeholder_text_color
|
self.placeholder_text_color = CTkThemeManager.PLACEHOLDER_TEXT_COLOR if placeholder_text_color == "default_theme" else placeholder_text_color
|
||||||
|
self.text_font = (CTkThemeManager.TEXT_FONT_NAME, CTkThemeManager.TEXT_FONT_SIZE) if text_font == "default_theme" else text_font
|
||||||
if text_font is None:
|
|
||||||
if sys.platform == "darwin": # macOS
|
|
||||||
self.text_font = ("Avenir", 13)
|
|
||||||
elif "win" in sys.platform: # Windows
|
|
||||||
self.text_font = ("Century Gothic", 10)
|
|
||||||
else:
|
|
||||||
self.text_font = ("TkDefaultFont", 10)
|
|
||||||
else:
|
|
||||||
self.text_font = text_font
|
|
||||||
|
|
||||||
self.placeholder_text = placeholder_text
|
self.placeholder_text = placeholder_text
|
||||||
self.placeholder_text_active = False
|
self.placeholder_text_active = False
|
||||||
@ -72,7 +66,7 @@ class CTkEntry(tkinter.Frame):
|
|||||||
|
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.corner_radius = self.calc_optimal_corner_radius(corner_radius) # optimise for less artifacts
|
self.corner_radius = corner_radius
|
||||||
|
|
||||||
if self.corner_radius*2 > self.height:
|
if self.corner_radius*2 > self.height:
|
||||||
self.corner_radius = self.height/2
|
self.corner_radius = self.height/2
|
||||||
@ -81,10 +75,10 @@ class CTkEntry(tkinter.Frame):
|
|||||||
|
|
||||||
super().configure(width=self.width, height=self.height)
|
super().configure(width=self.width, height=self.height)
|
||||||
|
|
||||||
self.canvas = tkinter.Canvas(master=self,
|
self.canvas = CTkCanvas(master=self,
|
||||||
highlightthickness=0,
|
highlightthickness=0,
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=self.height)
|
height=self.height)
|
||||||
self.canvas.grid(column=0, row=0, sticky="we")
|
self.canvas.grid(column=0, row=0, sticky="we")
|
||||||
|
|
||||||
self.entry = tkinter.Entry(master=self,
|
self.entry = tkinter.Entry(master=self,
|
||||||
@ -95,7 +89,7 @@ class CTkEntry(tkinter.Frame):
|
|||||||
**kwargs)
|
**kwargs)
|
||||||
self.entry.grid(column=0, row=0, sticky="we", padx=self.corner_radius if self.corner_radius >= 6 else 6)
|
self.entry.grid(column=0, row=0, sticky="we", padx=self.corner_radius if self.corner_radius >= 6 else 6)
|
||||||
|
|
||||||
self.fg_parts = []
|
self.draw_engine = DrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
|
||||||
|
|
||||||
super().bind('<Configure>', self.update_dimensions)
|
super().bind('<Configure>', self.update_dimensions)
|
||||||
self.entry.bind('<FocusOut>', self.set_placeholder)
|
self.entry.bind('<FocusOut>', self.set_placeholder)
|
||||||
@ -123,21 +117,6 @@ class CTkEntry(tkinter.Frame):
|
|||||||
#print(self.master["style"])
|
#print(self.master["style"])
|
||||||
#return self.master.cget("background")
|
#return self.master.cget("background")
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def calc_optimal_corner_radius(user_corner_radius):
|
|
||||||
if sys.platform == "darwin":
|
|
||||||
return user_corner_radius # on macOS just use given value (canvas has Antialiasing)
|
|
||||||
else:
|
|
||||||
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 update_dimensions(self, event):
|
def update_dimensions(self, event):
|
||||||
# only redraw if dimensions changed (for performance)
|
# only redraw if dimensions changed (for performance)
|
||||||
if self.width != event.width or self.height != event.height:
|
if self.width != event.width or self.height != event.height:
|
||||||
@ -165,61 +144,17 @@ class CTkEntry(tkinter.Frame):
|
|||||||
self.entry[argument] = value
|
self.entry[argument] = value
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
self.canvas.delete("all")
|
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
self.fg_parts = []
|
self.entry.configure(bg=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
|
||||||
|
highlightcolor=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
|
||||||
|
fg=CTkThemeManager.single_color(self.text_color, self.appearance_mode),
|
||||||
|
insertbackground=CTkThemeManager.single_color(self.text_color, self.appearance_mode))
|
||||||
|
|
||||||
if sys.platform == "darwin":
|
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, 0)
|
||||||
oval_size_corr_br = 0
|
|
||||||
else:
|
|
||||||
oval_size_corr_br = -1 # correct canvas oval draw size on bottom and right by 1 pixel (too large otherwise)
|
|
||||||
|
|
||||||
# frame_border
|
self.canvas.itemconfig("inner_parts",
|
||||||
self.fg_parts.append(self.canvas.create_oval(0,
|
fill=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
|
||||||
0,
|
outline=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
|
||||||
self.corner_radius*2 + oval_size_corr_br,
|
|
||||||
self.corner_radius*2 + oval_size_corr_br))
|
|
||||||
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2,
|
|
||||||
0,
|
|
||||||
self.width + oval_size_corr_br,
|
|
||||||
self.corner_radius*2 + oval_size_corr_br))
|
|
||||||
self.fg_parts.append(self.canvas.create_oval(0,
|
|
||||||
self.height-self.corner_radius*2,
|
|
||||||
self.corner_radius*2 + oval_size_corr_br,
|
|
||||||
self.height + oval_size_corr_br))
|
|
||||||
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2,
|
|
||||||
self.height-self.corner_radius*2,
|
|
||||||
self.width + oval_size_corr_br,
|
|
||||||
self.height + oval_size_corr_br))
|
|
||||||
|
|
||||||
self.fg_parts.append(self.canvas.create_rectangle(0, self.corner_radius,
|
|
||||||
self.width, self.height-self.corner_radius))
|
|
||||||
self.fg_parts.append(self.canvas.create_rectangle(self.corner_radius, 0,
|
|
||||||
self.width-self.corner_radius, self.height))
|
|
||||||
|
|
||||||
for part in self.fg_parts:
|
|
||||||
if type(self.fg_color) == tuple:
|
|
||||||
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
|
||||||
else:
|
|
||||||
self.canvas.itemconfig(part, fill=self.fg_color, width=0)
|
|
||||||
|
|
||||||
if type(self.bg_color) == tuple:
|
|
||||||
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
|
||||||
else:
|
|
||||||
self.canvas.configure(bg=self.bg_color)
|
|
||||||
|
|
||||||
if type(self.fg_color) == tuple:
|
|
||||||
self.entry.configure(bg=self.fg_color[self.appearance_mode],
|
|
||||||
highlightcolor=self.fg_color[self.appearance_mode])
|
|
||||||
else:
|
|
||||||
self.entry.configure(bg=self.fg_color,
|
|
||||||
highlightcolor=self.fg_color)
|
|
||||||
|
|
||||||
if type(self.text_color) == tuple:
|
|
||||||
self.entry.configure(fg=self.text_color[self.appearance_mode],
|
|
||||||
insertbackground=self.text_color[self.appearance_mode])
|
|
||||||
else:
|
|
||||||
self.entry.configure(fg=self.text_color,
|
|
||||||
insertbackground=self.text_color)
|
|
||||||
|
|
||||||
def bind(self, *args, **kwargs):
|
def bind(self, *args, **kwargs):
|
||||||
self.entry.bind(*args, **kwargs)
|
self.entry.bind(*args, **kwargs)
|
||||||
@ -246,7 +181,7 @@ class CTkEntry(tkinter.Frame):
|
|||||||
require_redraw = True
|
require_redraw = True
|
||||||
|
|
||||||
if "corner_radius" in kwargs:
|
if "corner_radius" in kwargs:
|
||||||
self.corner_radius = self.calc_optimal_corner_radius(kwargs["corner_radius"]) # optimise for less artifacts
|
self.corner_radius = kwargs["corner_radius"]
|
||||||
|
|
||||||
if self.corner_radius * 2 > self.height:
|
if self.corner_radius * 2 > self.height:
|
||||||
self.corner_radius = self.height / 2
|
self.corner_radius = self.height / 2
|
||||||
|
@ -4,12 +4,17 @@ import sys
|
|||||||
from .customtkinter_tk import CTk
|
from .customtkinter_tk import CTk
|
||||||
from .appearance_mode_tracker import AppearanceModeTracker
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
from .customtkinter_theme_manager import CTkThemeManager
|
from .customtkinter_theme_manager import CTkThemeManager
|
||||||
|
from .customtkinter_canvas import CTkCanvas
|
||||||
|
from .customtkinter_settings import CTkSettings
|
||||||
|
from .customtkinter_draw_engine import DrawEngine
|
||||||
|
|
||||||
|
|
||||||
class CTkFrame(tkinter.Frame):
|
class CTkFrame(tkinter.Frame):
|
||||||
def __init__(self, *args,
|
def __init__(self, *args,
|
||||||
bg_color=None,
|
bg_color=None,
|
||||||
fg_color="default_theme",
|
fg_color="default_theme",
|
||||||
|
border_color="default_theme",
|
||||||
|
border_width=0,
|
||||||
corner_radius=10,
|
corner_radius=10,
|
||||||
width=200,
|
width=200,
|
||||||
height=200,
|
height=200,
|
||||||
@ -41,6 +46,7 @@ class CTkFrame(tkinter.Frame):
|
|||||||
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||||
|
|
||||||
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
|
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
|
||||||
|
self.border_color = CTkThemeManager.CHECKBOX_LINES_COLOR if border_color == "default_theme" else border_color
|
||||||
|
|
||||||
if fg_color == "default_theme":
|
if fg_color == "default_theme":
|
||||||
if isinstance(self.master, CTkFrame):
|
if isinstance(self.master, CTkFrame):
|
||||||
@ -55,28 +61,29 @@ class CTkFrame(tkinter.Frame):
|
|||||||
|
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
self.configure(width=self.width, height=self.height)
|
||||||
|
|
||||||
self.corner_radius = self.calc_optimal_corner_radius(corner_radius) # optimise for less artifacts
|
self.corner_radius = corner_radius
|
||||||
|
self.border_width = border_width
|
||||||
|
|
||||||
if self.corner_radius * 2 > self.height:
|
if self.corner_radius * 2 > self.height:
|
||||||
self.corner_radius = self.height / 2
|
self.corner_radius = self.height / 2
|
||||||
elif self.corner_radius * 2 > self.width:
|
elif self.corner_radius * 2 > self.width:
|
||||||
self.corner_radius = self.width / 2
|
self.corner_radius = self.width / 2
|
||||||
|
|
||||||
self.configure(width=self.width, height=self.height)
|
if self.corner_radius >= self.border_width:
|
||||||
|
self.inner_corner_radius = self.corner_radius - self.border_width
|
||||||
self.canvas = tkinter.Canvas(master=self,
|
|
||||||
highlightthicknes=0,
|
|
||||||
width=self.width,
|
|
||||||
height=self.height)
|
|
||||||
self.canvas.place(x=0, y=0)
|
|
||||||
|
|
||||||
if type(self.bg_color) == tuple:
|
|
||||||
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
|
||||||
else:
|
else:
|
||||||
self.canvas.configure(bg=self.bg_color)
|
self.inner_corner_radius = 0
|
||||||
|
|
||||||
self.fg_parts = []
|
self.canvas = CTkCanvas(master=self,
|
||||||
|
highlightthickness=0,
|
||||||
|
width=self.width,
|
||||||
|
height=self.height)
|
||||||
|
self.canvas.place(x=0, y=0, relwidth=1, relheight=1)
|
||||||
|
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
|
|
||||||
|
self.draw_engine = DrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
|
||||||
|
|
||||||
self.bind('<Configure>', self.update_dimensions)
|
self.bind('<Configure>', self.update_dimensions)
|
||||||
|
|
||||||
@ -92,76 +99,28 @@ class CTkFrame(tkinter.Frame):
|
|||||||
else:
|
else:
|
||||||
return self.master.cget("bg")
|
return self.master.cget("bg")
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def calc_optimal_corner_radius(user_corner_radius):
|
|
||||||
if sys.platform == "darwin":
|
|
||||||
return user_corner_radius # on macOS just use given value (canvas has Antialiasing)
|
|
||||||
else:
|
|
||||||
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 update_dimensions(self, event):
|
def update_dimensions(self, event):
|
||||||
# only redraw if dimensions changed (for performance)
|
# only redraw if dimensions changed (for performance)
|
||||||
if self.width != event.width or self.height != event.height:
|
if self.width != event.width or self.height != event.height:
|
||||||
self.width = event.width
|
self.width = event.width
|
||||||
self.height = event.height
|
self.height = event.height
|
||||||
|
|
||||||
self.canvas.config(width=self.width, height=self.height)
|
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
for part in self.fg_parts:
|
|
||||||
self.canvas.delete(part)
|
|
||||||
self.fg_parts = []
|
|
||||||
|
|
||||||
if sys.platform == "darwin":
|
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
|
||||||
oval_size_corr_br = 0
|
|
||||||
else:
|
|
||||||
oval_size_corr_br = -1 # correct canvas oval draw size on bottom and right by 1 pixel (too large otherwise)
|
|
||||||
|
|
||||||
# frame_border
|
self.canvas.itemconfig("inner_parts",
|
||||||
self.fg_parts.append(self.canvas.create_oval(0,
|
fill=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
|
||||||
0,
|
outline=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
|
||||||
self.corner_radius*2 + oval_size_corr_br,
|
self.canvas.itemconfig("border_parts",
|
||||||
self.corner_radius*2 + oval_size_corr_br))
|
fill=CTkThemeManager.single_color(self.border_color, self.appearance_mode),
|
||||||
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2,
|
outline=CTkThemeManager.single_color(self.border_color, self.appearance_mode))
|
||||||
0,
|
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
self.width + oval_size_corr_br,
|
|
||||||
self.corner_radius*2 + oval_size_corr_br))
|
|
||||||
self.fg_parts.append(self.canvas.create_oval(0,
|
|
||||||
self.height-self.corner_radius*2,
|
|
||||||
self.corner_radius*2 + oval_size_corr_br,
|
|
||||||
self.height + oval_size_corr_br))
|
|
||||||
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2,
|
|
||||||
self.height-self.corner_radius*2,
|
|
||||||
self.width + oval_size_corr_br,
|
|
||||||
self.height + oval_size_corr_br))
|
|
||||||
|
|
||||||
self.fg_parts.append(self.canvas.create_rectangle(0, self.corner_radius,
|
self.canvas.tag_lower("inner_parts")
|
||||||
self.width, self.height-self.corner_radius))
|
self.canvas.tag_lower("border_parts")
|
||||||
self.fg_parts.append(self.canvas.create_rectangle(self.corner_radius, 0,
|
|
||||||
self.width-self.corner_radius, self.height))
|
|
||||||
|
|
||||||
for part in self.fg_parts:
|
|
||||||
if type(self.fg_color) == tuple:
|
|
||||||
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
|
||||||
else:
|
|
||||||
self.canvas.itemconfig(part, fill=self.fg_color, width=0)
|
|
||||||
|
|
||||||
if type(self.bg_color) == tuple:
|
|
||||||
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
|
||||||
else:
|
|
||||||
self.canvas.configure(bg=self.bg_color)
|
|
||||||
|
|
||||||
for part in self.fg_parts:
|
|
||||||
self.canvas.tag_lower(part)
|
|
||||||
|
|
||||||
def config(self, *args, **kwargs):
|
def config(self, *args, **kwargs):
|
||||||
self.configure(*args, **kwargs)
|
self.configure(*args, **kwargs)
|
||||||
|
@ -5,6 +5,9 @@ from .customtkinter_tk import CTk
|
|||||||
from .customtkinter_frame import CTkFrame
|
from .customtkinter_frame import CTkFrame
|
||||||
from .appearance_mode_tracker import AppearanceModeTracker
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
from .customtkinter_theme_manager import CTkThemeManager
|
from .customtkinter_theme_manager import CTkThemeManager
|
||||||
|
from .customtkinter_canvas import CTkCanvas
|
||||||
|
from .customtkinter_settings import CTkSettings
|
||||||
|
from .customtkinter_draw_engine import DrawEngine
|
||||||
|
|
||||||
|
|
||||||
class CTkLabel(tkinter.Frame):
|
class CTkLabel(tkinter.Frame):
|
||||||
@ -50,12 +53,13 @@ class CTkLabel(tkinter.Frame):
|
|||||||
|
|
||||||
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
|
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
|
||||||
self.fg_color = CTkThemeManager.LABEL_BG_COLOR if fg_color == "default_theme" else fg_color
|
self.fg_color = CTkThemeManager.LABEL_BG_COLOR if fg_color == "default_theme" else fg_color
|
||||||
|
if self.fg_color is None:
|
||||||
|
self.fg_color = self.bg_color
|
||||||
self.text_color = CTkThemeManager.TEXT_COLOR if text_color == "default_theme" else text_color
|
self.text_color = CTkThemeManager.TEXT_COLOR if text_color == "default_theme" else text_color
|
||||||
|
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
self.corner_radius = corner_radius
|
||||||
self.corner_radius = self.calc_optimal_corner_radius(corner_radius) # optimise for less artifacts
|
|
||||||
|
|
||||||
if self.corner_radius * 2 > self.height:
|
if self.corner_radius * 2 > self.height:
|
||||||
self.corner_radius = self.height / 2
|
self.corner_radius = self.height / 2
|
||||||
@ -65,10 +69,10 @@ class CTkLabel(tkinter.Frame):
|
|||||||
self.text = text
|
self.text = text
|
||||||
self.text_font = (CTkThemeManager.TEXT_FONT_NAME, CTkThemeManager.TEXT_FONT_SIZE) if text_font == "default_theme" else text_font
|
self.text_font = (CTkThemeManager.TEXT_FONT_NAME, CTkThemeManager.TEXT_FONT_SIZE) if text_font == "default_theme" else text_font
|
||||||
|
|
||||||
self.canvas = tkinter.Canvas(master=self,
|
self.canvas = CTkCanvas(master=self,
|
||||||
highlightthickness=0,
|
highlightthickness=0,
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=self.height)
|
height=self.height)
|
||||||
self.canvas.place(relx=0, rely=0, anchor=tkinter.NW)
|
self.canvas.place(relx=0, rely=0, anchor=tkinter.NW)
|
||||||
|
|
||||||
self.text_label = tkinter.Label(master=self,
|
self.text_label = tkinter.Label(master=self,
|
||||||
@ -79,7 +83,7 @@ class CTkLabel(tkinter.Frame):
|
|||||||
**kwargs)
|
**kwargs)
|
||||||
self.text_label.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
|
self.text_label.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
self.fg_parts = []
|
self.draw_engine = DrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
|
||||||
|
|
||||||
super().configure(width=self.width, height=self.height)
|
super().configure(width=self.width, height=self.height)
|
||||||
|
|
||||||
@ -96,21 +100,6 @@ class CTkLabel(tkinter.Frame):
|
|||||||
else:
|
else:
|
||||||
return self.master.cget("bg")
|
return self.master.cget("bg")
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def calc_optimal_corner_radius(user_corner_radius):
|
|
||||||
if sys.platform == "darwin":
|
|
||||||
return user_corner_radius # on macOS just use given value (canvas has Antialiasing)
|
|
||||||
else:
|
|
||||||
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 update_dimensions(self, event):
|
def update_dimensions(self, event):
|
||||||
# only redraw if dimensions changed (for performance)
|
# only redraw if dimensions changed (for performance)
|
||||||
if self.width != event.width or self.height != event.height:
|
if self.width != event.width or self.height != event.height:
|
||||||
@ -121,62 +110,16 @@ class CTkLabel(tkinter.Frame):
|
|||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
self.canvas.delete("all")
|
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, 0)
|
||||||
self.fg_parts = []
|
|
||||||
|
|
||||||
if sys.platform == "darwin":
|
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
oval_size_corr_br = 0
|
|
||||||
else:
|
|
||||||
oval_size_corr_br = -1 # correct canvas oval draw size on bottom and right by 1 pixel (too large otherwise)
|
|
||||||
|
|
||||||
# frame_border
|
self.canvas.itemconfig("inner_parts",
|
||||||
self.fg_parts.append(self.canvas.create_oval(0,
|
fill=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
|
||||||
0,
|
outline=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
|
||||||
self.corner_radius*2 + oval_size_corr_br,
|
|
||||||
self.corner_radius*2 + oval_size_corr_br))
|
|
||||||
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2,
|
|
||||||
0,
|
|
||||||
self.width + oval_size_corr_br,
|
|
||||||
self.corner_radius*2 + oval_size_corr_br))
|
|
||||||
self.fg_parts.append(self.canvas.create_oval(0,
|
|
||||||
self.height-self.corner_radius*2,
|
|
||||||
self.corner_radius*2 + oval_size_corr_br,
|
|
||||||
self.height + oval_size_corr_br))
|
|
||||||
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2,
|
|
||||||
self.height-self.corner_radius*2,
|
|
||||||
self.width + oval_size_corr_br,
|
|
||||||
self.height + oval_size_corr_br))
|
|
||||||
|
|
||||||
self.fg_parts.append(self.canvas.create_rectangle(0, self.corner_radius,
|
self.text_label.configure(fg=CTkThemeManager.single_color(self.text_color, self.appearance_mode),
|
||||||
self.width, self.height-self.corner_radius))
|
bg=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
|
||||||
self.fg_parts.append(self.canvas.create_rectangle(self.corner_radius, 0,
|
|
||||||
self.width-self.corner_radius, self.height))
|
|
||||||
|
|
||||||
if type(self.bg_color) == tuple:
|
|
||||||
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
|
||||||
else:
|
|
||||||
self.canvas.configure(bg=self.bg_color)
|
|
||||||
|
|
||||||
if self.fg_color is None:
|
|
||||||
new_fg_color = self.bg_color
|
|
||||||
else:
|
|
||||||
new_fg_color = self.fg_color
|
|
||||||
|
|
||||||
for part in self.fg_parts:
|
|
||||||
if type(new_fg_color) == tuple:
|
|
||||||
self.canvas.itemconfig(part, fill=new_fg_color[self.appearance_mode], width=0)
|
|
||||||
else:
|
|
||||||
self.canvas.itemconfig(part, fill=new_fg_color, width=0)
|
|
||||||
|
|
||||||
if type(self.text_color) == tuple:
|
|
||||||
self.text_label.configure(fg=self.text_color[self.appearance_mode])
|
|
||||||
else:
|
|
||||||
self.text_label.configure(fg=self.text_color)
|
|
||||||
|
|
||||||
if type(new_fg_color) == tuple:
|
|
||||||
self.text_label.configure(bg=new_fg_color[self.appearance_mode])
|
|
||||||
else:
|
|
||||||
self.text_label.configure(bg=new_fg_color)
|
|
||||||
|
|
||||||
def config(self, *args, **kwargs):
|
def config(self, *args, **kwargs):
|
||||||
self.configure(*args, **kwargs)
|
self.configure(*args, **kwargs)
|
||||||
@ -191,6 +134,8 @@ class CTkLabel(tkinter.Frame):
|
|||||||
if "fg_color" in kwargs:
|
if "fg_color" in kwargs:
|
||||||
self.fg_color = kwargs["fg_color"]
|
self.fg_color = kwargs["fg_color"]
|
||||||
require_redraw = True
|
require_redraw = True
|
||||||
|
if self.fg_color is None:
|
||||||
|
self.fg_color = self.bg_color
|
||||||
del kwargs["fg_color"]
|
del kwargs["fg_color"]
|
||||||
|
|
||||||
if "bg_color" in kwargs:
|
if "bg_color" in kwargs:
|
||||||
|
@ -6,3 +6,16 @@ class CTkSettings:
|
|||||||
scaling_factor = 1
|
scaling_factor = 1
|
||||||
circle_font_is_ready = False
|
circle_font_is_ready = False
|
||||||
hand_cursor_enabled = True
|
hand_cursor_enabled = True
|
||||||
|
preferred_drawing_method = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_drawing_method(cls):
|
||||||
|
""" possible: 'polygon_shapes', 'font_shapes', 'circle_shapes' """
|
||||||
|
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
cls.preferred_drawing_method = "polygon_shapes"
|
||||||
|
else:
|
||||||
|
cls.preferred_drawing_method = "font_shapes"
|
||||||
|
|
||||||
|
|
||||||
|
CTkSettings.init_drawing_method()
|
||||||
|
@ -3,6 +3,9 @@ import sys
|
|||||||
|
|
||||||
class CTkThemeManager:
|
class CTkThemeManager:
|
||||||
|
|
||||||
|
TEXT_FONT_NAME = None
|
||||||
|
TEXT_FONT_SIZE = None
|
||||||
|
|
||||||
WINDOW_BG_COLOR = None
|
WINDOW_BG_COLOR = None
|
||||||
MAIN_COLOR = None
|
MAIN_COLOR = None
|
||||||
MAIN_HOVER_COLOR = None
|
MAIN_HOVER_COLOR = None
|
||||||
@ -17,8 +20,6 @@ class CTkThemeManager:
|
|||||||
FRAME_2_COLOR = None
|
FRAME_2_COLOR = None
|
||||||
CHECKBOX_LINES_COLOR = None
|
CHECKBOX_LINES_COLOR = None
|
||||||
DARKEN_COLOR_FACTOR = None
|
DARKEN_COLOR_FACTOR = None
|
||||||
TEXT_FONT_NAME = None
|
|
||||||
TEXT_FONT_SIZE = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def initialize_color_theme(cls, theme_name):
|
def initialize_color_theme(cls, theme_name):
|
||||||
@ -26,11 +27,11 @@ class CTkThemeManager:
|
|||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
cls.TEXT_FONT_NAME = "Avenir"
|
cls.TEXT_FONT_NAME = "Avenir"
|
||||||
elif sys.platform.startswith("win"):
|
elif sys.platform.startswith("win"):
|
||||||
cls.TEXT_FONT_NAME = "Segoe UI"
|
cls.TEXT_FONT_NAME = "Roboto"
|
||||||
else:
|
else:
|
||||||
cls.TEXT_FONT_NAME = "TkDefaultFont"
|
cls.TEXT_FONT_NAME = "Roboto"
|
||||||
|
|
||||||
cls.TEXT_FONT_SIZE = -14
|
cls.TEXT_FONT_SIZE = -14 # px height
|
||||||
|
|
||||||
if theme_name.lower() == "blue":
|
if theme_name.lower() == "blue":
|
||||||
cls.WINDOW_BG_COLOR = ("#ECECEC", "#323232") # macOS standard light and dark window bg colors
|
cls.WINDOW_BG_COLOR = ("#ECECEC", "#323232") # macOS standard light and dark window bg colors
|
||||||
@ -69,8 +70,8 @@ class CTkThemeManager:
|
|||||||
cls.MAIN_COLOR = ("#608BD5", "#395E9C")
|
cls.MAIN_COLOR = ("#608BD5", "#395E9C")
|
||||||
cls.MAIN_HOVER_COLOR = ("#A4BDE6", "#748BB3")
|
cls.MAIN_HOVER_COLOR = ("#A4BDE6", "#748BB3")
|
||||||
cls.ENTRY_COLOR = ("#FCFCFC", "#111116")
|
cls.ENTRY_COLOR = ("#FCFCFC", "#111116")
|
||||||
cls.TEXT_COLOR = ("black", "white")
|
cls.TEXT_COLOR = ("gray18", "gray75")
|
||||||
cls.PLACEHOLDER_TEXT_COLOR = ("gray52", "gray62")
|
cls.PLACEHOLDER_TEXT_COLOR = ("gray52", "gray60")
|
||||||
cls.LABEL_BG_COLOR = ("white", "#444444")
|
cls.LABEL_BG_COLOR = ("white", "#444444")
|
||||||
cls.SLIDER_BG_COLOR = ("#444444", "#444444")
|
cls.SLIDER_BG_COLOR = ("#444444", "#444444")
|
||||||
cls.SLIDER_PROGRESS_COLOR = ("white", "#AAAAAA")
|
cls.SLIDER_PROGRESS_COLOR = ("white", "#AAAAAA")
|
||||||
|
@ -6,6 +6,8 @@ import sys
|
|||||||
customtkinter.set_appearance_mode("Light") # Modes: "System" (standard), "Dark", "Light"
|
customtkinter.set_appearance_mode("Light") # Modes: "System" (standard), "Dark", "Light"
|
||||||
customtkinter.set_default_color_theme("dark-blue") # Themes: "blue" (standard), "green", "dark-blue"
|
customtkinter.set_default_color_theme("dark-blue") # Themes: "blue" (standard), "green", "dark-blue"
|
||||||
|
|
||||||
|
customtkinter.CTkSettings.preferred_drawing_method = "font_shapes"
|
||||||
|
|
||||||
|
|
||||||
class App(customtkinter.CTk):
|
class App(customtkinter.CTk):
|
||||||
|
|
||||||
@ -36,7 +38,9 @@ class App(customtkinter.CTk):
|
|||||||
self.frame_right = customtkinter.CTkFrame(master=self,
|
self.frame_right = customtkinter.CTkFrame(master=self,
|
||||||
width=420,
|
width=420,
|
||||||
height=App.HEIGHT-40,
|
height=App.HEIGHT-40,
|
||||||
corner_radius=10)
|
border_width=0,
|
||||||
|
border_color=customtkinter.CTkThemeManager.MAIN_COLOR,
|
||||||
|
corner_radius=12)
|
||||||
self.frame_right.grid(row=0, column=1, sticky="nswe", padx=20, pady=20)
|
self.frame_right.grid(row=0, column=1, sticky="nswe", padx=20, pady=20)
|
||||||
|
|
||||||
self.grid_columnconfigure(1, weight=1)
|
self.grid_columnconfigure(1, weight=1)
|
||||||
@ -49,7 +53,7 @@ class App(customtkinter.CTk):
|
|||||||
self.frame_left.grid_rowconfigure(8, minsize=10)
|
self.frame_left.grid_rowconfigure(8, minsize=10)
|
||||||
|
|
||||||
self.label_1 = customtkinter.CTkLabel(master=self.frame_left,
|
self.label_1 = customtkinter.CTkLabel(master=self.frame_left,
|
||||||
text="CustomTkinter",
|
text="CustomTkinter",
|
||||||
fg_color=None)
|
fg_color=None)
|
||||||
self.label_1.grid(row=1, column=0, pady=10, padx=10)
|
self.label_1.grid(row=1, column=0, pady=10, padx=10)
|
||||||
|
|
||||||
@ -159,11 +163,13 @@ class App(customtkinter.CTk):
|
|||||||
self.entry.grid(row=4, column=0, columnspan=2, pady=20, padx=20, sticky="we")
|
self.entry.grid(row=4, column=0, columnspan=2, pady=20, padx=20, sticky="we")
|
||||||
|
|
||||||
self.button_5 = customtkinter.CTkButton(master=self.frame_right,
|
self.button_5 = customtkinter.CTkButton(master=self.frame_right,
|
||||||
height=25,
|
height=26,
|
||||||
text="CTkButton",
|
text="CTkButton",
|
||||||
command=self.button_event,
|
command=self.button_event,
|
||||||
border_width=0,
|
fg_color="gray30",
|
||||||
corner_radius=8)
|
border_width=2,
|
||||||
|
border_color=("gray30", "gray50"),
|
||||||
|
corner_radius=13)
|
||||||
self.button_5.grid(row=4, column=2, columnspan=1, pady=20, padx=20, sticky="we")
|
self.button_5.grid(row=4, column=2, columnspan=1, pady=20, padx=20, sticky="we")
|
||||||
|
|
||||||
def button_event(self):
|
def button_event(self):
|
||||||
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pyglet~=1.5.21
|
||||||
|
darkdetect~=0.3.1
|
Loading…
x
Reference in New Issue
Block a user