7 Commits

27 changed files with 837 additions and 483 deletions

View File

@ -1,4 +1,4 @@
__version__ = "4.2.0" __version__ = "4.3.0"
import os import os
import sys import sys
@ -52,6 +52,7 @@ from .widgets.ctk_radiobutton import CTkRadioButton
from .widgets.ctk_canvas import CTkCanvas from .widgets.ctk_canvas import CTkCanvas
from .widgets.ctk_switch import CTkSwitch from .widgets.ctk_switch import CTkSwitch
from .widgets.ctk_optionmenu import CTkOptionMenu from .widgets.ctk_optionmenu import CTkOptionMenu
from .widgets.ctk_combobox import CTkComboBox
# import windows # import windows
from .windows.ctk_tk import CTk from .windows.ctk_tk import CTk

View File

@ -29,8 +29,10 @@
"switch_button_hover": ["gray20", "gray100"], "switch_button_hover": ["gray20", "gray100"],
"optionmenu_button": ["#36719F", "#144870"], "optionmenu_button": ["#36719F", "#144870"],
"optionmenu_button_hover": ["#27577D", "#203A4F"], "optionmenu_button_hover": ["#27577D", "#203A4F"],
"combobox_border": ["#979DA2", "#565B5E"],
"combobox_button_hover": ["#6E7174", "#7A848D"],
"dropdown_color": ["#A8ACB1", "#535353"], "dropdown_color": ["#A8ACB1", "#535353"],
"dropdown_hover": ["#D6DCE2", "#46484A"], "dropdown_hover": ["#D6DCE2", "#393D40"],
"dropdown_text": ["gray10", "#DCE4EE"] "dropdown_text": ["gray10", "#DCE4EE"]
}, },
"text": { "text": {

View File

@ -27,7 +27,13 @@
"switch_progress": ["#608BD5", "#395E9C"], "switch_progress": ["#608BD5", "#395E9C"],
"switch_button": ["gray38", "gray70"], "switch_button": ["gray38", "gray70"],
"switch_button_hover": ["gray30", "gray90"], "switch_button_hover": ["gray30", "gray90"],
"darken_factor": 0.8 "optionmenu_button": ["#36719F", "#144870"],
"optionmenu_button_hover": ["#27577D", "#203A4F"],
"combobox_border": ["gray70", "gray32"],
"combobox_button_hover": ["#6E7174", "#7A848D"],
"dropdown_color": ["#A8ACB1", "#535353"],
"dropdown_hover": ["#D6DCE2", "#393D40"],
"dropdown_text": ["gray12", "gray90"]
}, },
"text": { "text": {
"macOS": { "macOS": {

View File

@ -27,7 +27,13 @@
"switch_progress": ["#72CF9F", "#11B384"], "switch_progress": ["#72CF9F", "#11B384"],
"switch_button": ["gray38", "gray70"], "switch_button": ["gray38", "gray70"],
"switch_button_hover": ["gray30", "gray90"], "switch_button_hover": ["gray30", "gray90"],
"darken_factor": 0.8 "optionmenu_button": ["#0E9670", "#0D8A66"],
"optionmenu_button_hover":["gray40", "gray70"],
"combobox_border": ["gray70", "gray32"],
"combobox_button_hover": ["#6E7174", "#7A848D"],
"dropdown_color": ["#A8ACB1", "#535353"],
"dropdown_hover": ["#D6DCE2", "#393D40"],
"dropdown_text": ["gray12", "gray90"]
}, },
"text": { "text": {
"macOS": { "macOS": {

View File

@ -54,14 +54,14 @@ class DrawEngine:
else: else:
return user_corner_radius 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], def draw_rounded_rect_with_border(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
overwrite_preferred_drawing_method: str = None) -> bool: border_width: Union[float, int], overwrite_preferred_drawing_method: str = None) -> bool:
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag, """ Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag,
the main foreground elements have an 'inner_parts' tag to color the elements accordingly. the main foreground elements have an 'inner_parts' tag to color the elements accordingly.
returns bool if recoloring is necessary """ returns bool if recoloring is necessary """
width = math.floor(width / 2) * 2 # round (floor) current_width and current_height and restrict them to even values only width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only
height = math.floor(height / 2) * 2 height = math.floor(height / 2) * 2
corner_radius = round(corner_radius) corner_radius = round(corner_radius)
@ -353,8 +353,8 @@ class DrawEngine:
return requires_recoloring return requires_recoloring
def draw_rounded_rect_with_border_vertical_split(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int], def draw_rounded_rect_with_border_vertical_split(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
left_section_width: int) -> bool: border_width: Union[float, int], left_section_width: Union[float, int]) -> bool:
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas which is split at left_section_width. """ Draws a rounded rectangle with a corner_radius and border_width on the canvas which is split at left_section_width.
The border elements have the tags 'border_parts_left', 'border_parts_lright', The border elements have the tags 'border_parts_left', 'border_parts_lright',
the main foreground elements have an 'inner_parts_left' and inner_parts_right' tag, the main foreground elements have an 'inner_parts_left' and inner_parts_right' tag,
@ -362,7 +362,8 @@ class DrawEngine:
returns bool if recoloring is necessary """ returns bool if recoloring is necessary """
width = math.floor(width / 2) * 2 # round (floor) current_width and current_height and restrict them to even values only left_section_width = round(left_section_width)
width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only
height = math.floor(height / 2) * 2 height = math.floor(height / 2) * 2
corner_radius = round(corner_radius) corner_radius = round(corner_radius)
@ -394,10 +395,10 @@ class DrawEngine:
# create border button parts (only if border exists) # create border button parts (only if border exists)
if border_width > 0: if border_width > 0:
if not self._canvas.find_withtag("border_parts"): if not self._canvas.find_withtag("border_parts"):
self._canvas.create_polygon((0, 0, 0, 0), tags=("border_line_left_1", "border_parts_left", "border_parts")) self._canvas.create_polygon((0, 0, 0, 0), tags=("border_line_left_1", "border_parts_left", "border_parts", "left_parts"))
self._canvas.create_polygon((0, 0, 0, 0), tags=("border_line_right_1", "border_parts_right", "border_parts")) self._canvas.create_polygon((0, 0, 0, 0), tags=("border_line_right_1", "border_parts_right", "border_parts", "right_parts"))
self._canvas.create_rectangle((0, 0, 0, 0), tags=("border_rect_left_1", "border_parts_left", "border_parts")) self._canvas.create_rectangle((0, 0, 0, 0), tags=("border_rect_left_1", "border_parts_left", "border_parts", "left_parts"))
self._canvas.create_rectangle((0, 0, 0, 0), tags=("border_rect_right_1", "border_parts_right", "border_parts")) self._canvas.create_rectangle((0, 0, 0, 0), tags=("border_rect_right_1", "border_parts_right", "border_parts", "right_parts"))
requires_recoloring = True requires_recoloring = True
self._canvas.coords("border_line_left_1", self._canvas.coords("border_line_left_1",
@ -436,29 +437,29 @@ class DrawEngine:
# create inner button parts # create inner button parts
if not self._canvas.find_withtag("inner_parts"): if not self._canvas.find_withtag("inner_parts"):
self._canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_left_1", "inner_parts_left", "inner_parts"), joinstyle=tkinter.ROUND) self._canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_left_1", "inner_parts_left", "inner_parts", "left_parts"), joinstyle=tkinter.ROUND)
self._canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_right_1", "inner_parts_right", "inner_parts"), joinstyle=tkinter.ROUND) self._canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_right_1", "inner_parts_right", "inner_parts", "right_parts"), joinstyle=tkinter.ROUND)
self._canvas.create_rectangle((0, 0, 0, 0), tags=("inner_rect_left_1", "inner_parts_left", "inner_parts")) self._canvas.create_rectangle((0, 0, 0, 0), tags=("inner_rect_left_1", "inner_parts_left", "inner_parts", "left_parts"), width=0)
self._canvas.create_rectangle((0, 0, 0, 0), tags=("inner_rect_right_1", "inner_parts_right", "inner_parts")) self._canvas.create_rectangle((0, 0, 0, 0), tags=("inner_rect_right_1", "inner_parts_right", "inner_parts", "right_parts"), width=0)
requires_recoloring = True requires_recoloring = True
self._canvas.coords("inner_line_left_1", self._canvas.coords("inner_line_left_1",
corner_radius, corner_radius,
corner_radius, corner_radius,
left_section_width - corner_radius, left_section_width - inner_corner_radius,
corner_radius, corner_radius,
left_section_width - corner_radius, left_section_width - inner_corner_radius,
height - corner_radius, height - corner_radius,
corner_radius, corner_radius,
height - corner_radius) height - corner_radius)
self._canvas.coords("inner_line_right_1", self._canvas.coords("inner_line_right_1",
left_section_width + corner_radius, left_section_width + inner_corner_radius,
corner_radius, corner_radius,
width - corner_radius, width - corner_radius,
corner_radius, corner_radius,
width - corner_radius, width - corner_radius,
height - corner_radius, height - corner_radius,
left_section_width + corner_radius, left_section_width + inner_corner_radius,
height - corner_radius) height - corner_radius)
self._canvas.coords("inner_rect_left_1", self._canvas.coords("inner_rect_left_1",
(left_section_width - inner_corner_radius, (left_section_width - inner_corner_radius,
@ -488,31 +489,31 @@ class DrawEngine:
if corner_radius > 0: if corner_radius > 0:
# create canvas border corner parts if not already created, but only if needed, and delete if not needed # create canvas border corner parts if not already created, but only if needed, and delete if not needed
if not self._canvas.find_withtag("border_oval_1_a") and "border_oval_1" not in exclude_parts: if not self._canvas.find_withtag("border_oval_1_a") and "border_oval_1" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_a", "border_corner_part", "border_parts_left" "border_parts"), anchor=tkinter.CENTER) self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_a", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts_left", "border_parts"), anchor=tkinter.CENTER, angle=180) self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("border_oval_1_a") and "border_oval_1" in exclude_parts: elif self._canvas.find_withtag("border_oval_1_a") and "border_oval_1" in exclude_parts:
self._canvas.delete("border_oval_1_a", "border_oval_1_b") self._canvas.delete("border_oval_1_a", "border_oval_1_b")
if not self._canvas.find_withtag("border_oval_2_a") and width > 2 * corner_radius and "border_oval_2" not in exclude_parts: if not self._canvas.find_withtag("border_oval_2_a") and width > 2 * corner_radius and "border_oval_2" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts_right", "border_parts"), anchor=tkinter.CENTER) self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts_right", "border_parts"), anchor=tkinter.CENTER, angle=180) self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("border_oval_2_a") and (not width > 2 * corner_radius or "border_oval_2" in exclude_parts): elif self._canvas.find_withtag("border_oval_2_a") and (not width > 2 * corner_radius or "border_oval_2" in exclude_parts):
self._canvas.delete("border_oval_2_a", "border_oval_2_b") self._canvas.delete("border_oval_2_a", "border_oval_2_b")
if not self._canvas.find_withtag("border_oval_3_a") and height > 2 * corner_radius \ if not self._canvas.find_withtag("border_oval_3_a") and height > 2 * corner_radius \
and width > 2 * corner_radius and "border_oval_3" not in exclude_parts: and width > 2 * corner_radius and "border_oval_3" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts_right", "border_parts"), anchor=tkinter.CENTER) self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts_right", "border_parts"), anchor=tkinter.CENTER, angle=180) self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("border_oval_3_a") and (not (height > 2 * corner_radius elif self._canvas.find_withtag("border_oval_3_a") and (not (height > 2 * corner_radius
and width > 2 * corner_radius) or "border_oval_3" in exclude_parts): and width > 2 * corner_radius) or "border_oval_3" in exclude_parts):
self._canvas.delete("border_oval_3_a", "border_oval_3_b") self._canvas.delete("border_oval_3_a", "border_oval_3_b")
if not self._canvas.find_withtag("border_oval_4_a") and height > 2 * corner_radius and "border_oval_4" not in exclude_parts: if not self._canvas.find_withtag("border_oval_4_a") and height > 2 * corner_radius and "border_oval_4" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts_left", "border_parts"), anchor=tkinter.CENTER) self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts_left", "border_parts"), anchor=tkinter.CENTER, angle=180) self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("border_oval_4_a") and (not height > 2 * corner_radius or "border_oval_4" in exclude_parts): elif self._canvas.find_withtag("border_oval_4_a") and (not height > 2 * corner_radius or "border_oval_4" in exclude_parts):
self._canvas.delete("border_oval_4_a", "border_oval_4_b") self._canvas.delete("border_oval_4_a", "border_oval_4_b")
@ -532,17 +533,17 @@ class DrawEngine:
# create canvas border rectangle parts if not already created # create canvas border rectangle parts if not already created
if not self._canvas.find_withtag("border_rectangle_1"): if not self._canvas.find_withtag("border_rectangle_1"):
self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_left_1", "border_rectangle_part", "border_parts_left", "border_parts"), width=0) self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_left_1", "border_rectangle_part", "border_parts_left", "border_parts", "left_parts"), width=0)
self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_left_2", "border_rectangle_part", "border_parts_left", "border_parts"), width=0) self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_left_2", "border_rectangle_part", "border_parts_left", "border_parts", "left_parts"), width=0)
self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_right_1", "border_rectangle_part", "border_parts_right", "border_parts"), width=0) self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_right_1", "border_rectangle_part", "border_parts_right", "border_parts", "right_parts"), width=0)
self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_right_2", "border_rectangle_part", "border_parts_right", "border_parts"), width=0) self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_right_2", "border_rectangle_part", "border_parts_right", "border_parts", "right_parts"), width=0)
requires_recoloring = True requires_recoloring = True
# change position of border rectangle parts # change position of border rectangle parts
self._canvas.coords("border_rectangle_left_1", (0, corner_radius, left_section_width, height - corner_radius)) self._canvas.coords("border_rectangle_left_1", (0, corner_radius, left_section_width, height - corner_radius))
self._canvas.coords("border_rectangle_left_2", (corner_radius, 0, left_section_width, height)) self._canvas.coords("border_rectangle_left_2", (corner_radius, 0, left_section_width, height))
self._canvas.coords("border_rectangle_right_1", (left_section_width, corner_radius, width, height - corner_radius)) self._canvas.coords("border_rectangle_right_1", (left_section_width, corner_radius, width, height - corner_radius))
self._canvas.coords("border_rectangle_right_2", (corner_radius, left_section_width, width - corner_radius, height)) self._canvas.coords("border_rectangle_right_2", (left_section_width, 0, width - corner_radius, height))
else: else:
self._canvas.delete("border_parts") self._canvas.delete("border_parts")
@ -552,31 +553,31 @@ class DrawEngine:
# create canvas border corner parts if not already created, but only if they're needed and delete if not needed # create canvas border corner parts if not already created, but only if they're needed and delete if not needed
if not self._canvas.find_withtag("inner_oval_1_a") and "inner_oval_1" not in exclude_parts: if not self._canvas.find_withtag("inner_oval_1_a") and "inner_oval_1" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_a", "inner_corner_part", "inner_parts_left", "inner_parts"), anchor=tkinter.CENTER) self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_a", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts_left", "inner_parts"), anchor=tkinter.CENTER, angle=180) self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("inner_oval_1_a") and "inner_oval_1" in exclude_parts: elif self._canvas.find_withtag("inner_oval_1_a") and "inner_oval_1" in exclude_parts:
self._canvas.delete("inner_oval_1_a", "inner_oval_1_b") self._canvas.delete("inner_oval_1_a", "inner_oval_1_b")
if not self._canvas.find_withtag("inner_oval_2_a") and width - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_2" not in exclude_parts: if not self._canvas.find_withtag("inner_oval_2_a") and width - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_2" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part","inner_parts_right", "inner_parts"), anchor=tkinter.CENTER) self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part","inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts_right","inner_parts"), anchor=tkinter.CENTER, angle=180) self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("inner_oval_2_a") and (not width - (2 * border_width) > 2 * inner_corner_radius or "inner_oval_2" in exclude_parts): elif self._canvas.find_withtag("inner_oval_2_a") and (not width - (2 * border_width) > 2 * inner_corner_radius or "inner_oval_2" in exclude_parts):
self._canvas.delete("inner_oval_2_a", "inner_oval_2_b") self._canvas.delete("inner_oval_2_a", "inner_oval_2_b")
if not self._canvas.find_withtag("inner_oval_3_a") and height - (2 * border_width) > 2 * inner_corner_radius \ if not self._canvas.find_withtag("inner_oval_3_a") and height - (2 * border_width) > 2 * inner_corner_radius \
and width - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_3" not in exclude_parts: and width - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_3" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts_right","inner_parts"), anchor=tkinter.CENTER) self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts_right", "inner_parts"), anchor=tkinter.CENTER, angle=180) self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("inner_oval_3_a") and (not (height - (2 * border_width) > 2 * inner_corner_radius elif self._canvas.find_withtag("inner_oval_3_a") and (not (height - (2 * border_width) > 2 * inner_corner_radius
and width - (2 * border_width) > 2 * inner_corner_radius) or "inner_oval_3" in exclude_parts): and width - (2 * border_width) > 2 * inner_corner_radius) or "inner_oval_3" in exclude_parts):
self._canvas.delete("inner_oval_3_a", "inner_oval_3_b") self._canvas.delete("inner_oval_3_a", "inner_oval_3_b")
if not self._canvas.find_withtag("inner_oval_4_a") and height - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_4" not in exclude_parts: if not self._canvas.find_withtag("inner_oval_4_a") and height - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_4" not in exclude_parts:
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_a", "inner_corner_part", "inner_parts_left", "inner_parts"), anchor=tkinter.CENTER) self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_a", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER)
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts_left", "inner_parts"), anchor=tkinter.CENTER, angle=180) self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("inner_oval_4_a") and (not height - (2 * border_width) > 2 * inner_corner_radius or "inner_oval_4" in exclude_parts): elif self._canvas.find_withtag("inner_oval_4_a") and (not height - (2 * border_width) > 2 * inner_corner_radius or "inner_oval_4" in exclude_parts):
self._canvas.delete("inner_oval_4_a", "inner_oval_4_b") self._canvas.delete("inner_oval_4_a", "inner_oval_4_b")
@ -595,13 +596,13 @@ class DrawEngine:
# create canvas inner rectangle parts if not already created # create canvas inner rectangle parts if not already created
if not self._canvas.find_withtag("inner_rectangle_1"): if not self._canvas.find_withtag("inner_rectangle_1"):
self._canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_left_1", "inner_rectangle_part", "inner_parts_left", "inner_parts"), width=0) self._canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_left_1", "inner_rectangle_part", "inner_parts_left", "inner_parts", "left_parts"), width=0)
self._canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_right_1", "inner_rectangle_part", "inner_parts_right", "inner_parts"), width=0) self._canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_right_1", "inner_rectangle_part", "inner_parts_right", "inner_parts", "right_parts"), width=0)
requires_recoloring = True requires_recoloring = True
if not self._canvas.find_withtag("inner_rectangle_2") and inner_corner_radius * 2 < height - (border_width * 2): 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_left_2", "inner_rectangle_part", "inner_parts_left", "inner_parts"), width=0) self._canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_left_2", "inner_rectangle_part", "inner_parts_left", "inner_parts", "left_parts"), width=0)
self._canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_right_2", "inner_rectangle_part", "inner_parts_right", "inner_parts"), width=0) self._canvas.create_rectangle(0, 0, 0, 0, tags=("inner_rectangle_right_2", "inner_rectangle_part", "inner_parts_right", "inner_parts", "right_parts"), width=0)
requires_recoloring = True requires_recoloring = True
elif self._canvas.find_withtag("inner_rectangle_2") and not inner_corner_radius * 2 < height - (border_width * 2): elif self._canvas.find_withtag("inner_rectangle_2") and not inner_corner_radius * 2 < height - (border_width * 2):
@ -632,15 +633,15 @@ class DrawEngine:
return requires_recoloring return requires_recoloring
def draw_rounded_progress_bar_with_border(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int], def draw_rounded_progress_bar_with_border(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
progress_value: float, orientation: str) -> bool: border_width: Union[float, int], progress_value: float, orientation: str) -> bool:
""" Draws a rounded bar on the canvas, which is split in half according to the argument 'progress_value' (0 - 1). """ Draws a rounded bar on the canvas, which is split in half according to the argument 'progress_value' (0 - 1).
The border elements get the 'border_parts' tag", the main elements get the 'inner_parts' tag and The border elements get the 'border_parts' tag", the main elements get the 'inner_parts' tag and
the progress elements get the 'progress_parts' tag. The 'orientation' argument defines from which direction the progress starts (n, w, s, e). the progress elements get the 'progress_parts' tag. The 'orientation' argument defines from which direction the progress starts (n, w, s, e).
returns bool if recoloring is necessary """ returns bool if recoloring is necessary """
width = math.floor(width / 2) * 2 # round current_width and current_height and restrict them to even values only width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only
height = math.floor(height / 2) * 2 height = math.floor(height / 2) * 2
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger
@ -800,11 +801,11 @@ class DrawEngine:
return requires_recoloring or requires_recoloring_2 return requires_recoloring or requires_recoloring_2
def draw_rounded_slider_with_border_and_button(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int], def draw_rounded_slider_with_border_and_button(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
button_length: Union[float, int], button_corner_radius: Union[float, int], slider_value: float, border_width: Union[float, int], button_length: Union[float, int], button_corner_radius: Union[float, int],
orientation: str) -> bool: slider_value: float, orientation: str) -> bool:
width = math.floor(width / 2) * 2 # round current_width and current_height and restrict them to even values only width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only
height = math.floor(height / 2) * 2 height = math.floor(height / 2) * 2
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger
@ -958,7 +959,7 @@ class DrawEngine:
return requires_recoloring return requires_recoloring
def draw_checkmark(self, width: int, height: int, size: Union[int, float]) -> bool: def draw_checkmark(self, width: Union[float, int], height: Union[float, int], size: Union[int, float]) -> bool:
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag, """ Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag,
the main foreground elements have an 'inner_parts' tag to color the elements accordingly. the main foreground elements have an 'inner_parts' tag to color the elements accordingly.

View File

@ -20,7 +20,7 @@ class CTkButton(CTkBaseClass):
border_width="default_theme", border_width="default_theme",
command=None, command=None,
textvariable=None, textvariable=None,
width=120, width=140,
height=28, height=28,
corner_radius="default_theme", corner_radius="default_theme",
text_font="default_theme", text_font="default_theme",
@ -33,7 +33,7 @@ class CTkButton(CTkBaseClass):
state=tkinter.NORMAL, state=tkinter.NORMAL,
**kwargs): **kwargs):
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
self.configure_basic_grid() self.configure_basic_grid()
@ -66,8 +66,8 @@ class CTkButton(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.desired_width), width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_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) self.draw_engine = DrawEngine(self.canvas)
@ -98,41 +98,41 @@ class CTkButton(CTkBaseClass):
self.image_label.destroy() self.image_label.destroy()
self.image_label = None self.image_label = None
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def set_dimensions(self, width=None, height=None): def set_dimensions(self, width=None, height=None):
super().set_dimensions(width, height) super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width)) self.apply_widget_scaling(self.border_width))
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.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",
outline=ThemeManager.single_color(self.border_color, self.appearance_mode), outline=ThemeManager.single_color(self.border_color, self._appearance_mode),
fill=ThemeManager.single_color(self.border_color, self.appearance_mode)) fill=ThemeManager.single_color(self.border_color, self._appearance_mode))
# set color for inner button parts # set color for inner button parts
if self.fg_color is None: if self.fg_color is None:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode), outline=ThemeManager.single_color(self.bg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.bg_color, self.appearance_mode)) fill=ThemeManager.single_color(self.bg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), outline=ThemeManager.single_color(self.fg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) fill=ThemeManager.single_color(self.fg_color, self._appearance_mode))
# create text label if text given # create text label if text given
if self.text is not None and self.text != "": if self.text is not None and self.text != "":
@ -149,17 +149,17 @@ class CTkButton(CTkBaseClass):
if no_color_updates is False: if no_color_updates is False:
# set text_label fg color (text color) # set text_label fg color (text color)
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
if self.state == tkinter.DISABLED: if self.state == tkinter.DISABLED:
self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode)))
else: else:
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
if self.fg_color is None: if self.fg_color is None:
self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
else: else:
self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.text_label.configure(text=self.text) # set text self.text_label.configure(text=self.text) # set text
@ -183,9 +183,9 @@ class CTkButton(CTkBaseClass):
if no_color_updates is False: if no_color_updates is False:
# set image_label bg color (background color of label) # set image_label bg color (background color of label)
if self.fg_color is None: if self.fg_color is None:
self.image_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.image_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
else: else:
self.image_label.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) self.image_label.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.image_label.configure(image=self.image) # set image self.image_label.configure(image=self.image) # set image
@ -341,16 +341,16 @@ class CTkButton(CTkBaseClass):
# set color of inner button parts to hover color # set color of inner button parts to hover color
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
outline=ThemeManager.single_color(inner_parts_color, self.appearance_mode), outline=ThemeManager.single_color(inner_parts_color, self._appearance_mode),
fill=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) fill=ThemeManager.single_color(inner_parts_color, self._appearance_mode))
# set text_label bg color to button hover color # set text_label bg color to button hover color
if self.text_label is not None: if self.text_label is not None:
self.text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode))
# set image_label bg color to button hover color # set image_label bg color to button hover color
if self.image_label is not None: if self.image_label is not None:
self.image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) self.image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode))
def on_leave(self, event=0): def on_leave(self, event=0):
self.click_animation_running = False self.click_animation_running = False
@ -363,16 +363,16 @@ class CTkButton(CTkBaseClass):
# set color of inner button parts # set color of inner button parts
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
outline=ThemeManager.single_color(inner_parts_color, self.appearance_mode), outline=ThemeManager.single_color(inner_parts_color, self._appearance_mode),
fill=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) fill=ThemeManager.single_color(inner_parts_color, self._appearance_mode))
# set text_label bg color (label color) # set text_label bg color (label color)
if self.text_label is not None: if self.text_label is not None:
self.text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode))
# set image_label bg color (image bg color) # set image_label bg color (image bg color)
if self.image_label is not None: if self.image_label is not None:
self.image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) self.image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode))
def click_animation(self): def click_animation(self):
if self.click_animation_running: if self.click_animation_running:

View File

@ -35,7 +35,7 @@ class CTkCheckBox(CTkBaseClass):
textvariable=None, textvariable=None,
**kwargs): **kwargs):
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color # color
@ -75,14 +75,14 @@ class CTkCheckBox(CTkBaseClass):
self.bg_canvas = CTkCanvas(master=self, self.bg_canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.desired_width), width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe") self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe")
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.desired_width), width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, rowspan=1) self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, rowspan=1)
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
@ -107,8 +107,8 @@ class CTkCheckBox(CTkBaseClass):
self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6)) self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6))
self.text_label.configure(font=self.apply_font_scaling(self.text_font)) self.text_label.configure(font=self.apply_font_scaling(self.text_font))
self.bg_canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.bg_canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def destroy(self): def destroy(self):
@ -118,40 +118,40 @@ class CTkCheckBox(CTkBaseClass):
super().destroy() super().destroy()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width)) self.apply_widget_scaling(self.border_width))
if self.check_state is True: if self.check_state is True:
self.draw_engine.draw_checkmark(self.apply_widget_scaling(self.current_width), self.draw_engine.draw_checkmark(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.current_height * 0.58)) self.apply_widget_scaling(self._current_height * 0.58))
else: else:
self.canvas.delete("checkmark") self.canvas.delete("checkmark")
self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
if self.check_state is True: if self.check_state is True:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), outline=ThemeManager.single_color(self.fg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) fill=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), outline=ThemeManager.single_color(self.fg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) fill=ThemeManager.single_color(self.fg_color, self._appearance_mode))
if "create_line" in self.canvas.gettags("checkmark"): if "create_line" in self.canvas.gettags("checkmark"):
self.canvas.itemconfig("checkmark", fill=ThemeManager.single_color(self.checkmark_color, self.appearance_mode)) self.canvas.itemconfig("checkmark", fill=ThemeManager.single_color(self.checkmark_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("checkmark", fill=ThemeManager.single_color(self.checkmark_color, self.appearance_mode)) self.canvas.itemconfig("checkmark", fill=ThemeManager.single_color(self.checkmark_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode), outline=ThemeManager.single_color(self.bg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.bg_color, self.appearance_mode)) fill=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
outline=ThemeManager.single_color(self.border_color, self.appearance_mode), outline=ThemeManager.single_color(self.border_color, self._appearance_mode),
fill=ThemeManager.single_color(self.border_color, self.appearance_mode)) fill=ThemeManager.single_color(self.border_color, self._appearance_mode))
if self.text_label is None: if self.text_label is None:
self.text_label = tkinter.Label(master=self, self.text_label = tkinter.Label(master=self,
@ -167,10 +167,10 @@ class CTkCheckBox(CTkBaseClass):
self.text_label.bind("<Button-1>", self.toggle) self.text_label.bind("<Button-1>", self.toggle)
if self.state == tkinter.DISABLED: if self.state == tkinter.DISABLED:
self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode)))
else: else:
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.set_text(self.text) self.set_text(self.text)
@ -274,32 +274,32 @@ class CTkCheckBox(CTkBaseClass):
if self.hover is True and self.state == tkinter.NORMAL: if self.hover is True and self.state == tkinter.NORMAL:
if self.check_state is True: if self.check_state is True:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.hover_color, self.appearance_mode), fill=ThemeManager.single_color(self.hover_color, self._appearance_mode),
outline=ThemeManager.single_color(self.hover_color, self.appearance_mode)) outline=ThemeManager.single_color(self.hover_color, self._appearance_mode))
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.hover_color, self.appearance_mode), fill=ThemeManager.single_color(self.hover_color, self._appearance_mode),
outline=ThemeManager.single_color(self.hover_color, self.appearance_mode)) outline=ThemeManager.single_color(self.hover_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.hover_color, self.appearance_mode), fill=ThemeManager.single_color(self.hover_color, self._appearance_mode),
outline=ThemeManager.single_color(self.hover_color, self.appearance_mode)) outline=ThemeManager.single_color(self.hover_color, self._appearance_mode))
def on_leave(self, event=0): def on_leave(self, event=0):
if self.hover is True: if self.hover is True:
if self.check_state is True: if self.check_state is True:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), fill=ThemeManager.single_color(self.bg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.border_color, self.appearance_mode), fill=ThemeManager.single_color(self.border_color, self._appearance_mode),
outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) outline=ThemeManager.single_color(self.border_color, self._appearance_mode))
def variable_callback(self, var_name, index, mode): def variable_callback(self, var_name, index, mode):
if not self.variable_callback_blocked: if not self.variable_callback_blocked:
@ -335,10 +335,7 @@ class CTkCheckBox(CTkBaseClass):
self.variable_callback_blocked = False self.variable_callback_blocked = False
if self.function is not None: if self.function is not None:
try:
self.function() self.function()
except:
pass
def deselect(self, from_variable_callback=False): def deselect(self, from_variable_callback=False):
self.check_state = False self.check_state = False

View File

@ -0,0 +1,305 @@
import tkinter
import sys
from typing import Union
from .dropdown_menu import DropdownMenu
from .ctk_canvas import CTkCanvas
from ..theme_manager import ThemeManager
from ..settings import Settings
from ..draw_engine import DrawEngine
from .widget_base_class import CTkBaseClass
class CTkComboBox(CTkBaseClass):
def __init__(self, *args,
bg_color=None,
fg_color="default_theme",
border_color="default_theme",
button_color="default_theme",
button_hover_color="default_theme",
dropdown_color="default_theme",
dropdown_hover_color="default_theme",
dropdown_text_color="default_theme",
variable=None,
values=None,
command=None,
width=140,
height=28,
corner_radius="default_theme",
border_width="default_theme",
text_font="default_theme",
text_color="default_theme",
text_color_disabled="default_theme",
hover=True,
state=tkinter.NORMAL,
**kwargs):
# transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color variables
self.fg_color = ThemeManager.theme["color"]["entry"] if fg_color == "default_theme" else fg_color
self.border_color = ThemeManager.theme["color"]["combobox_border"] if border_color == "default_theme" else border_color
self.button_color = ThemeManager.theme["color"]["combobox_border"] if button_color == "default_theme" else button_color
self.button_hover_color = ThemeManager.theme["color"]["combobox_button_hover"] if button_hover_color == "default_theme" else button_hover_color
self.dropdown_color = ThemeManager.theme["color"]["dropdown_color"] if dropdown_color == "default_theme" else dropdown_color
self.dropdown_hover_color = ThemeManager.theme["color"]["dropdown_hover"] if dropdown_hover_color == "default_theme" else dropdown_hover_color
self.dropdown_text_color = ThemeManager.theme["color"]["dropdown_text"] if dropdown_text_color == "default_theme" else dropdown_text_color
# shape
self.corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width = ThemeManager.theme["shape"]["entry_border_width"] if border_width == "default_theme" else border_width
# text and font
self.text_label = None
self.text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
self.text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled
self.text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font
# callback and hover functionality
self.function = command
self.variable = variable
self.state = state
self.hover = hover
self.click_animation_running = False
if values is None:
self.values = ["CTkComboBox"]
else:
self.values = values
if len(self.values) > 0:
self.current_value = self.values[0]
else:
self.current_value = "CTkComboBox"
self.dropdown_menu: Union[DropdownMenu, None] = None
# configure grid system (1x1)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.canvas = CTkCanvas(master=self,
highlightthickness=0,
width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self._desired_height))
self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew")
self.draw_engine = DrawEngine(self.canvas)
self.entry = tkinter.Entry(master=self,
state=self.state,
width=1,
bd=0,
highlightthickness=0,
font=self.apply_font_scaling(self.text_font))
left_section_width = self._current_width - self._current_height
self.entry.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="ew",
padx=(self.apply_widget_scaling(max(self.corner_radius, 3)),
self.apply_widget_scaling(max(self._current_width - left_section_width + 3, 3))))
self.draw() # initial draw
# event bindings
self.canvas.tag_bind("right_parts", "<Enter>", self.on_enter)
self.canvas.tag_bind("dropdown_arrow", "<Enter>", self.on_enter)
self.canvas.tag_bind("right_parts", "<Leave>", self.on_leave)
self.canvas.tag_bind("dropdown_arrow", "<Leave>", self.on_leave)
self.canvas.tag_bind("right_parts", "<Button-1>", self.clicked)
self.canvas.tag_bind("dropdown_arrow", "<Button-1>", self.clicked)
self.bind('<Configure>', self.update_dimensions_event)
if self.variable is not None:
self.entry.configure(textvariable=self.variable)
def set_scaling(self, *args, **kwargs):
super().set_scaling(*args, **kwargs)
self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self._desired_height))
self.draw()
def set_dimensions(self, width: int = None, height: int = None):
super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self._desired_height))
self.draw()
def draw(self, no_color_updates=False):
left_section_width = self._current_width - self._current_height
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border_vertical_split(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width),
self.apply_widget_scaling(left_section_width))
requires_recoloring_2 = self.draw_engine.draw_dropdown_arrow(self.apply_widget_scaling(self._current_width - (self._current_height / 2)),
self.apply_widget_scaling(self._current_height / 2),
self.apply_widget_scaling(self._current_height / 3))
if self.current_value is not None:
self.entry.delete(0, tkinter.END)
self.entry.insert(0, self.current_value)
if no_color_updates is False or requires_recoloring or requires_recoloring_2:
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.itemconfig("inner_parts_left",
outline=ThemeManager.single_color(self.fg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.canvas.itemconfig("border_parts_left",
outline=ThemeManager.single_color(self.border_color, self._appearance_mode),
fill=ThemeManager.single_color(self.border_color, self._appearance_mode))
self.canvas.itemconfig("inner_parts_right",
outline=ThemeManager.single_color(self.border_color, self._appearance_mode),
fill=ThemeManager.single_color(self.border_color, self._appearance_mode))
self.canvas.itemconfig("border_parts_right",
outline=ThemeManager.single_color(self.border_color, self._appearance_mode),
fill=ThemeManager.single_color(self.border_color, self._appearance_mode))
self.entry.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.entry.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode))
if self.state == tkinter.DISABLED:
self.entry.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode)))
self.canvas.itemconfig("dropdown_arrow",
fill=ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))
else:
self.entry.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.canvas.itemconfig("dropdown_arrow",
fill=ThemeManager.single_color(self.text_color, self._appearance_mode))
def open_dropdown_menu(self):
self.dropdown_menu = DropdownMenu(x_position=self.winfo_rootx(),
y_position=self.winfo_rooty() + self.apply_widget_scaling(self._current_height + 4),
width=self._current_width,
values=self.values,
command=self.set,
fg_color=self.dropdown_color,
button_hover_color=self.dropdown_hover_color,
button_color=self.dropdown_color,
text_color=self.dropdown_text_color)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
if "state" in kwargs:
self.state = kwargs["state"]
self.entry.configure(state=self.state)
require_redraw = True
del kwargs["state"]
if "fg_color" in kwargs:
self.fg_color = kwargs["fg_color"]
require_redraw = True
del kwargs["fg_color"]
if "bg_color" in kwargs:
if kwargs["bg_color"] is None:
self.bg_color = self.detect_color_of_master()
else:
self.bg_color = kwargs["bg_color"]
require_redraw = True
del kwargs["bg_color"]
if "button_color" in kwargs:
self.button_color = kwargs["button_color"]
require_redraw = True
del kwargs["button_color"]
if "button_hover_color" in kwargs:
self.button_hover_color = kwargs["button_hover_color"]
require_redraw = True
del kwargs["button_hover_color"]
if "text_color" in kwargs:
self.text_color = kwargs["text_color"]
require_redraw = True
del kwargs["text_color"]
if "command" in kwargs:
self.function = kwargs["command"]
del kwargs["command"]
if "variable" in kwargs:
self.variable = kwargs["variable"]
self.entry.configure(textvariable=self.variable)
del kwargs["variable"]
if "width" in kwargs:
self.set_dimensions(width=kwargs["width"])
del kwargs["width"]
if "height" in kwargs:
self.set_dimensions(height=kwargs["height"])
del kwargs["height"]
if "values" in kwargs:
self.values = kwargs["values"]
del kwargs["values"]
super().configure(*args, **kwargs)
if require_redraw:
self.draw()
def on_enter(self, event=0):
if self.hover is True and self.state == tkinter.NORMAL and len(self.values) > 0:
if sys.platform == "darwin" and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.canvas.configure(cursor="pointinghand")
elif sys.platform.startswith("win") and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.canvas.configure(cursor="hand2")
# set color of inner button parts to hover color
self.canvas.itemconfig("inner_parts_right",
outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode),
fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode))
self.canvas.itemconfig("border_parts_right",
outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode),
fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode))
def on_leave(self, event=0):
self.click_animation_running = False
if self.hover is True:
if sys.platform == "darwin" and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.canvas.configure(cursor="arrow")
elif sys.platform.startswith("win") and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.canvas.configure(cursor="arrow")
# set color of inner button parts
self.canvas.itemconfig("inner_parts_right",
outline=ThemeManager.single_color(self.button_color, self._appearance_mode),
fill=ThemeManager.single_color(self.button_color, self._appearance_mode))
self.canvas.itemconfig("border_parts_right",
outline=ThemeManager.single_color(self.button_color, self._appearance_mode),
fill=ThemeManager.single_color(self.button_color, self._appearance_mode))
def click_animation(self):
if self.click_animation_running:
self.on_enter()
def set(self, value: str, from_variable_callback: bool = False):
self.current_value = value
self.entry.delete(0, tkinter.END)
self.entry.insert(0, self.current_value)
if not from_variable_callback:
if self.function is not None:
self.function(self.current_value)
def get(self) -> str:
return self.entry.get()
def clicked(self, event=0):
if self.state is not tkinter.DISABLED and len(self.values) > 0:
self.open_dropdown_menu()
# click animation: change color with .on_leave() and back to normal after 100ms with click_animation()
self.on_leave()
self.click_animation_running = True
self.after(100, self.click_animation)

View File

@ -17,12 +17,12 @@ class CTkEntry(CTkBaseClass):
corner_radius="default_theme", corner_radius="default_theme",
border_width="default_theme", border_width="default_theme",
border_color="default_theme", border_color="default_theme",
width=120, width=140,
height=28, height=28,
state=tkinter.NORMAL, state=tkinter.NORMAL,
**kwargs): **kwargs):
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
if "master" in kwargs: if "master" in kwargs:
super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs["master"]) super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs["master"])
del kwargs["master"] del kwargs["master"]
@ -49,8 +49,8 @@ class CTkEntry(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.current_width), width=self.apply_widget_scaling(self._current_width),
height=self.apply_widget_scaling(self.current_height)) height=self.apply_widget_scaling(self._current_height))
self.canvas.grid(column=0, row=0, sticky="we") self.canvas.grid(column=0, row=0, sticky="we")
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
@ -78,14 +78,14 @@ class CTkEntry(CTkBaseClass):
self.entry.grid(column=0, row=0, sticky="we", self.entry.grid(column=0, row=0, sticky="we",
padx=self.apply_widget_scaling(self.corner_radius) if self.corner_radius >= 6 else self.apply_widget_scaling(6)) padx=self.apply_widget_scaling(self.corner_radius) if self.corner_radius >= 6 else self.apply_widget_scaling(6))
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def set_dimensions(self, width=None, height=None): def set_dimensions(self, width=None, height=None):
super().set_dimensions(width, height) super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def set_placeholder(self, event=None): def set_placeholder(self, event=None):
@ -93,54 +93,54 @@ class CTkEntry(CTkBaseClass):
if not self.placeholder_text_active and self.entry.get() == "": if not self.placeholder_text_active and self.entry.get() == "":
self.placeholder_text_active = True self.placeholder_text_active = True
self.pre_placeholder_arguments = {"show": self.entry.cget("show")} self.pre_placeholder_arguments = {"show": self.entry.cget("show")}
self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self.appearance_mode), show="") self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self._appearance_mode), show="")
self.entry.delete(0, tkinter.END) self.entry.delete(0, tkinter.END)
self.entry.insert(0, self.placeholder_text) self.entry.insert(0, self.placeholder_text)
def clear_placeholder(self, event=None): def clear_placeholder(self, event=None):
if self.placeholder_text_active: if self.placeholder_text_active:
self.placeholder_text_active = False self.placeholder_text_active = False
self.entry.config(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.entry.config(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.entry.delete(0, tkinter.END) self.entry.delete(0, tkinter.END)
for argument, value in self.pre_placeholder_arguments.items(): for argument, value in self.pre_placeholder_arguments.items():
self.entry[argument] = value self.entry[argument] = value
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width)) self.apply_widget_scaling(self.border_width))
if requires_recoloring or no_color_updates is False: if requires_recoloring or no_color_updates is False:
if ThemeManager.single_color(self.fg_color, self.appearance_mode) is not None: if ThemeManager.single_color(self.fg_color, self._appearance_mode) is not None:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.entry.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode), self.entry.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode),
disabledbackground=ThemeManager.single_color(self.fg_color, self.appearance_mode), disabledbackground=ThemeManager.single_color(self.fg_color, self._appearance_mode),
highlightcolor=ThemeManager.single_color(self.fg_color, self.appearance_mode), highlightcolor=ThemeManager.single_color(self.fg_color, self._appearance_mode),
fg=ThemeManager.single_color(self.text_color, self.appearance_mode), fg=ThemeManager.single_color(self.text_color, self._appearance_mode),
disabledforeground=ThemeManager.single_color(self.text_color, self.appearance_mode), disabledforeground=ThemeManager.single_color(self.text_color, self._appearance_mode),
insertbackground=ThemeManager.single_color(self.text_color, self.appearance_mode)) insertbackground=ThemeManager.single_color(self.text_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), fill=ThemeManager.single_color(self.bg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.entry.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode), self.entry.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode),
disabledbackground=ThemeManager.single_color(self.bg_color, self.appearance_mode), disabledbackground=ThemeManager.single_color(self.bg_color, self._appearance_mode),
highlightcolor=ThemeManager.single_color(self.bg_color, self.appearance_mode), highlightcolor=ThemeManager.single_color(self.bg_color, self._appearance_mode),
fg=ThemeManager.single_color(self.text_color, self.appearance_mode), fg=ThemeManager.single_color(self.text_color, self._appearance_mode),
disabledforeground=ThemeManager.single_color(self.text_color, self.appearance_mode), disabledforeground=ThemeManager.single_color(self.text_color, self._appearance_mode),
insertbackground=ThemeManager.single_color(self.text_color, self.appearance_mode)) insertbackground=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.border_color, self.appearance_mode), fill=ThemeManager.single_color(self.border_color, self._appearance_mode),
outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) outline=ThemeManager.single_color(self.border_color, self._appearance_mode))
if self.placeholder_text_active: if self.placeholder_text_active:
self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self.appearance_mode)) self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self._appearance_mode))
def bind(self, *args, **kwargs): def bind(self, *args, **kwargs):
self.entry.bind(*args, **kwargs) self.entry.bind(*args, **kwargs)
@ -179,10 +179,10 @@ class CTkEntry(CTkBaseClass):
if "corner_radius" in kwargs: if "corner_radius" in kwargs:
self.corner_radius = kwargs["corner_radius"] self.corner_radius = kwargs["corner_radius"]
if self.corner_radius * 2 > self.current_height: if self.corner_radius * 2 > self._current_height:
self.corner_radius = self.current_height / 2 self.corner_radius = self._current_height / 2
elif self.corner_radius * 2 > self.current_width: elif self.corner_radius * 2 > self._current_width:
self.corner_radius = self.current_width / 2 self.corner_radius = self._current_width / 2
self.entry.grid(column=0, row=0, sticky="we", padx=self.apply_widget_scaling(self.corner_radius) if self.corner_radius >= 6 else self.apply_widget_scaling(6)) self.entry.grid(column=0, row=0, sticky="we", padx=self.apply_widget_scaling(self.corner_radius) if self.corner_radius >= 6 else self.apply_widget_scaling(6))
del kwargs["corner_radius"] del kwargs["corner_radius"]

View File

@ -16,7 +16,7 @@ class CTkFrame(CTkBaseClass):
overwrite_preferred_drawing_method: str = None, overwrite_preferred_drawing_method: str = None,
**kwargs): **kwargs):
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color # color
@ -40,10 +40,10 @@ class CTkFrame(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.current_width), width=self.apply_widget_scaling(self._current_width),
height=self.apply_widget_scaling(self.current_height)) height=self.apply_widget_scaling(self._current_height))
self.canvas.place(x=0, y=0, relwidth=1, relheight=1) self.canvas.place(x=0, y=0, relwidth=1, relheight=1)
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
self._overwrite_preferred_drawing_method = overwrite_preferred_drawing_method self._overwrite_preferred_drawing_method = overwrite_preferred_drawing_method
@ -65,20 +65,20 @@ class CTkFrame(CTkBaseClass):
def set_scaling(self, *args, **kwargs): def set_scaling(self, *args, **kwargs):
super().set_scaling(*args, **kwargs) super().set_scaling(*args, **kwargs)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def set_dimensions(self, width=None, height=None): def set_dimensions(self, width=None, height=None):
super().set_dimensions(width, height) super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.border_width),
overwrite_preferred_drawing_method=self._overwrite_preferred_drawing_method) overwrite_preferred_drawing_method=self._overwrite_preferred_drawing_method)
@ -86,17 +86,17 @@ class CTkFrame(CTkBaseClass):
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
if self.fg_color is None: if self.fg_color is None:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), fill=ThemeManager.single_color(self.bg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.bg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.border_color, self.appearance_mode), fill=ThemeManager.single_color(self.border_color, self._appearance_mode),
outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) outline=ThemeManager.single_color(self.border_color, self._appearance_mode))
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.tag_lower("inner_parts") self.canvas.tag_lower("inner_parts")
self.canvas.tag_lower("border_parts") self.canvas.tag_lower("border_parts")

View File

@ -12,13 +12,13 @@ class CTkLabel(CTkBaseClass):
fg_color="default_theme", fg_color="default_theme",
text_color="default_theme", text_color="default_theme",
corner_radius="default_theme", corner_radius="default_theme",
width=120, width=140,
height=28, height=28,
text="CTkLabel", text="CTkLabel",
text_font="default_theme", text_font="default_theme",
**kwargs): **kwargs):
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
if "master" in kwargs: if "master" in kwargs:
super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs["master"]) super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs["master"])
del kwargs["master"] del kwargs["master"]
@ -44,8 +44,8 @@ class CTkLabel(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.desired_width), width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.canvas.grid(row=0, column=0, sticky="nswe") self.canvas.grid(row=0, column=0, sticky="nswe")
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
@ -63,7 +63,7 @@ class CTkLabel(CTkBaseClass):
def set_scaling(self, *args, **kwargs): def set_scaling(self, *args, **kwargs):
super().set_scaling(*args, **kwargs) super().set_scaling(*args, **kwargs)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.text_label.configure(font=self.apply_font_scaling(self.text_font)) self.text_label.configure(font=self.apply_font_scaling(self.text_font))
self.text_label.grid(row=0, column=0, padx=self.apply_widget_scaling(self.corner_radius)) self.text_label.grid(row=0, column=0, padx=self.apply_widget_scaling(self.corner_radius))
@ -72,33 +72,33 @@ class CTkLabel(CTkBaseClass):
def set_dimensions(self, width=None, height=None): def set_dimensions(self, width=None, height=None):
super().set_dimensions(width, height) super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
0) 0)
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
if ThemeManager.single_color(self.fg_color, self.appearance_mode) is not None: if ThemeManager.single_color(self.fg_color, self._appearance_mode) is not None:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode), self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode),
bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) bg=ThemeManager.single_color(self.fg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), fill=ThemeManager.single_color(self.bg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode), self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode),
bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
def configure(self, *args, **kwargs): def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end require_redraw = False # some attribute changes require a call of self.draw() at the end

View File

@ -1,5 +1,6 @@
import tkinter import tkinter
import sys import sys
from typing import Union
from .dropdown_menu import DropdownMenu from .dropdown_menu import DropdownMenu
@ -23,7 +24,7 @@ class CTkOptionMenu(CTkBaseClass):
variable=None, variable=None,
values=None, values=None,
command=None, command=None,
width=120, width=140,
height=28, height=28,
corner_radius="default_theme", corner_radius="default_theme",
text_font="default_theme", text_font="default_theme",
@ -33,7 +34,7 @@ class CTkOptionMenu(CTkBaseClass):
state=tkinter.NORMAL, state=tkinter.NORMAL,
**kwargs): **kwargs):
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color variables # color variables
@ -61,13 +62,18 @@ class CTkOptionMenu(CTkBaseClass):
self.state = state self.state = state
self.hover = hover self.hover = hover
self.click_animation_running = False self.click_animation_running = False
if values is None: if values is None:
self.values = ["CTkOptionMenu"] self.values = ["CTkOptionMenu"]
else: else:
self.values = values self.values = values
self.current_value = self.values[0]
self.dropdown_menu = None if len(self.values) > 0:
self.current_value = self.values[0]
else:
self.current_value = "CTkOptionMenu"
self.dropdown_menu: Union[DropdownMenu, None] = None
# configure grid system (1x1) # configure grid system (1x1)
self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1)
@ -75,8 +81,8 @@ class CTkOptionMenu(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.desired_width), width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew") self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew")
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
@ -87,7 +93,6 @@ class CTkOptionMenu(CTkBaseClass):
self.canvas.bind("<Button-1>", self.clicked) self.canvas.bind("<Button-1>", self.clicked)
self.bind('<Configure>', self.update_dimensions_event) self.bind('<Configure>', self.update_dimensions_event)
self.set_cursor()
self.draw() # initial draw self.draw() # initial draw
if self.variable is not None: if self.variable is not None:
@ -101,34 +106,34 @@ class CTkOptionMenu(CTkBaseClass):
self.text_label.destroy() self.text_label.destroy()
self.text_label = None self.text_label = None
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def set_dimensions(self, width: int = None, height: int = None): def set_dimensions(self, width: int = None, height: int = None):
super().set_dimensions(width, height) super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
left_section_width = self.current_width - self.current_height left_section_width = self._current_width - self._current_height
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border_vertical_split(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_rect_with_border_vertical_split(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
0, 0,
self.apply_widget_scaling(left_section_width)) self.apply_widget_scaling(left_section_width))
requires_recoloring_2 = self.draw_engine.draw_dropdown_arrow(self.apply_widget_scaling(self.current_width - (self.current_height / 2)), requires_recoloring_2 = self.draw_engine.draw_dropdown_arrow(self.apply_widget_scaling(self._current_width - (self._current_height / 2)),
self.apply_widget_scaling(self.current_height / 2), self.apply_widget_scaling(self._current_height / 2),
self.apply_widget_scaling(self.current_height / 3)) self.apply_widget_scaling(self._current_height / 3))
if self.text_label is None: if self.text_label is None:
self.text_label = tkinter.Label(master=self, self.text_label = tkinter.Label(master=self,
font=self.apply_font_scaling(self.text_font)) font=self.apply_font_scaling(self.text_font))
self.text_label.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="w", self.text_label.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="w",
padx=(max(self.apply_widget_scaling(self.corner_radius), 3), padx=(max(self.apply_widget_scaling(self.corner_radius), 3),
max(self.current_width - left_section_width + 3, 3))) max(self._current_width - left_section_width + 3, 3)))
self.text_label.bind("<Enter>", self.on_enter) self.text_label.bind("<Enter>", self.on_enter)
self.text_label.bind("<Leave>", self.on_leave) self.text_label.bind("<Leave>", self.on_leave)
@ -140,32 +145,32 @@ class CTkOptionMenu(CTkBaseClass):
if no_color_updates is False or requires_recoloring or requires_recoloring_2: if no_color_updates is False or requires_recoloring or requires_recoloring_2:
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.itemconfig("inner_parts_left", self.canvas.itemconfig("inner_parts_left",
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), outline=ThemeManager.single_color(self.fg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) fill=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.canvas.itemconfig("inner_parts_right", self.canvas.itemconfig("inner_parts_right",
outline=ThemeManager.single_color(self.button_color, self.appearance_mode), outline=ThemeManager.single_color(self.button_color, self._appearance_mode),
fill=ThemeManager.single_color(self.button_color, self.appearance_mode)) fill=ThemeManager.single_color(self.button_color, self._appearance_mode))
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
if self.state == tkinter.DISABLED: if self.state == tkinter.DISABLED:
self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode)))
self.canvas.itemconfig("dropdown_arrow", self.canvas.itemconfig("dropdown_arrow",
fill=ThemeManager.single_color(self.text_color_disabled, self.appearance_mode)) fill=ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))
else: else:
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.canvas.itemconfig("dropdown_arrow", self.canvas.itemconfig("dropdown_arrow",
fill=ThemeManager.single_color(self.text_color, self.appearance_mode)) fill=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode))
def open_dropdown_menu(self): def open_dropdown_menu(self):
self.dropdown_menu = DropdownMenu(x_position=self.winfo_rootx(), self.dropdown_menu = DropdownMenu(x_position=self.winfo_rootx(),
y_position=self.winfo_rooty() + self.apply_widget_scaling(self.current_height + 4), y_position=self.winfo_rooty() + self.apply_widget_scaling(self._current_height + 4),
width=self.current_width, width=self._current_width,
values=self.values, values=self.values,
command=self.set, command=self.set,
fg_color=self.dropdown_color, fg_color=self.dropdown_color,
@ -178,7 +183,6 @@ class CTkOptionMenu(CTkBaseClass):
if "state" in kwargs: if "state" in kwargs:
self.state = kwargs["state"] self.state = kwargs["state"]
self.set_cursor()
require_redraw = True require_redraw = True
del kwargs["state"] del kwargs["state"]
@ -236,40 +240,40 @@ class CTkOptionMenu(CTkBaseClass):
self.set_dimensions(height=kwargs["height"]) self.set_dimensions(height=kwargs["height"])
del kwargs["height"] del kwargs["height"]
if "values" in kwargs:
self.values = kwargs["values"]
del kwargs["values"]
super().configure(*args, **kwargs) super().configure(*args, **kwargs)
if require_redraw: if require_redraw:
self.draw() self.draw()
def set_cursor(self): def on_enter(self, event=0):
if Settings.cursor_manipulation_enabled: if self.hover is True and self.state == tkinter.NORMAL and len(self.values) > 0:
if self.state == tkinter.DISABLED:
if sys.platform == "darwin" and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.configure(cursor="arrow")
elif sys.platform.startswith("win") and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.configure(cursor="arrow")
elif self.state == tkinter.NORMAL:
if sys.platform == "darwin" and len(self.values) > 0 and Settings.cursor_manipulation_enabled: if sys.platform == "darwin" and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.configure(cursor="pointinghand") self.configure(cursor="pointinghand")
elif sys.platform.startswith("win") and len(self.values) > 0 and Settings.cursor_manipulation_enabled: elif sys.platform.startswith("win") and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.configure(cursor="hand2") self.configure(cursor="hand2")
def on_enter(self, event=0):
if self.hover is True and self.state == tkinter.NORMAL:
# set color of inner button parts to hover color # set color of inner button parts to hover color
self.canvas.itemconfig("inner_parts_right", self.canvas.itemconfig("inner_parts_right",
outline=ThemeManager.single_color(self.button_hover_color, self.appearance_mode), outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode),
fill=ThemeManager.single_color(self.button_hover_color, self.appearance_mode)) fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode))
def on_leave(self, event=0): def on_leave(self, event=0):
self.click_animation_running = False self.click_animation_running = False
if self.hover is True: if self.hover is True:
if sys.platform == "darwin" and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.configure(cursor="arrow")
elif sys.platform.startswith("win") and len(self.values) > 0 and Settings.cursor_manipulation_enabled:
self.configure(cursor="arrow")
# set color of inner button parts # set color of inner button parts
self.canvas.itemconfig("inner_parts_right", self.canvas.itemconfig("inner_parts_right",
outline=ThemeManager.single_color(self.button_color, self.appearance_mode), outline=ThemeManager.single_color(self.button_color, self._appearance_mode),
fill=ThemeManager.single_color(self.button_color, self.appearance_mode)) fill=ThemeManager.single_color(self.button_color, self._appearance_mode))
def click_animation(self): def click_animation(self):
if self.click_animation_running: if self.click_animation_running:
@ -294,16 +298,13 @@ class CTkOptionMenu(CTkBaseClass):
if not from_variable_callback: if not from_variable_callback:
if self.function is not None: if self.function is not None:
try:
self.function(self.current_value) self.function(self.current_value)
except Exception:
pass
def get(self) -> str: def get(self) -> str:
return self.current_value return self.current_value
def clicked(self, event=0): def clicked(self, event=0):
if self.state is not tkinter.DISABLED: if self.state is not tkinter.DISABLED and len(self.values) > 0:
self.open_dropdown_menu() self.open_dropdown_menu()
# click animation: change color with .on_leave() and back to normal after 100ms with click_animation() # click animation: change color with .on_leave() and back to normal after 100ms with click_animation()

View File

@ -3,12 +3,11 @@ import tkinter
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..theme_manager import ThemeManager from ..theme_manager import ThemeManager
from ..draw_engine import DrawEngine from ..draw_engine import DrawEngine
from ..settings import Settings
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
class CTkProgressBar(CTkBaseClass): class CTkProgressBar(CTkBaseClass):
""" tkinter custom progressbar, always horizontal, values are from 0 to 1 """ """ tkinter custom progressbar, values from 0 to 1 """
def __init__(self, *args, def __init__(self, *args,
variable=None, variable=None,
@ -35,7 +34,7 @@ class CTkProgressBar(CTkBaseClass):
else: else:
height = 8 height = 8
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color # color
@ -59,8 +58,8 @@ class CTkProgressBar(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.desired_width), width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nswe") self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nswe")
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
@ -78,14 +77,14 @@ class CTkProgressBar(CTkBaseClass):
def set_scaling(self, *args, **kwargs): def set_scaling(self, *args, **kwargs):
super().set_scaling(*args, **kwargs) super().set_scaling(*args, **kwargs)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def set_dimensions(self, width=None, height=None): def set_dimensions(self, width=None, height=None):
super().set_dimensions(width, height) super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def destroy(self): def destroy(self):
@ -102,24 +101,24 @@ class CTkProgressBar(CTkBaseClass):
else: else:
orientation = "w" orientation = "w"
requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.border_width),
self.value, self.value,
orientation) orientation)
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.border_color, self.appearance_mode), fill=ThemeManager.single_color(self.border_color, self._appearance_mode),
outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) outline=ThemeManager.single_color(self.border_color, self._appearance_mode))
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.canvas.itemconfig("progress_parts", self.canvas.itemconfig("progress_parts",
fill=ThemeManager.single_color(self.progress_color, self.appearance_mode), fill=ThemeManager.single_color(self.progress_color, self._appearance_mode),
outline=ThemeManager.single_color(self.progress_color, self.appearance_mode)) outline=ThemeManager.single_color(self.progress_color, self._appearance_mode))
def configure(self, *args, **kwargs): def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end require_redraw = False # some attribute changes require a call of self.draw() at the end

View File

@ -32,7 +32,7 @@ class CTkRadioButton(CTkBaseClass):
textvariable=None, textvariable=None,
**kwargs): **kwargs):
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color # color
@ -71,14 +71,14 @@ class CTkRadioButton(CTkBaseClass):
self.bg_canvas = CTkCanvas(master=self, self.bg_canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.current_width), width=self.apply_widget_scaling(self._current_width),
height=self.apply_widget_scaling(self.current_height)) height=self.apply_widget_scaling(self._current_height))
self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe") self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe")
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.current_width), width=self.apply_widget_scaling(self._current_width),
height=self.apply_widget_scaling(self.current_height)) height=self.apply_widget_scaling(self._current_height))
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1) self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1)
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
@ -102,8 +102,8 @@ class CTkRadioButton(CTkBaseClass):
self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6)) self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6))
self.text_label.configure(font=self.apply_font_scaling(self.text_font)) self.text_label.configure(font=self.apply_font_scaling(self.text_font))
self.bg_canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.bg_canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def destroy(self): def destroy(self):
@ -113,26 +113,26 @@ class CTkRadioButton(CTkBaseClass):
super().destroy() super().destroy()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width)) self.apply_widget_scaling(self.border_width))
self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
if self.check_state is False: if self.check_state is False:
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
outline=ThemeManager.single_color(self.border_color, self.appearance_mode), outline=ThemeManager.single_color(self.border_color, self._appearance_mode),
fill=ThemeManager.single_color(self.border_color, self.appearance_mode)) fill=ThemeManager.single_color(self.border_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), outline=ThemeManager.single_color(self.fg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) fill=ThemeManager.single_color(self.fg_color, self._appearance_mode))
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode), outline=ThemeManager.single_color(self.bg_color, self._appearance_mode),
fill=ThemeManager.single_color(self.bg_color, self.appearance_mode)) fill=ThemeManager.single_color(self.bg_color, self._appearance_mode))
if self.text_label is None: if self.text_label is None:
self.text_label = tkinter.Label(master=self, self.text_label = tkinter.Label(master=self,
@ -148,11 +148,11 @@ class CTkRadioButton(CTkBaseClass):
self.text_label.bind("<Button-1>", self.invoke) self.text_label.bind("<Button-1>", self.invoke)
if self.state == tkinter.DISABLED: if self.state == tkinter.DISABLED:
self.text_label.configure(fg=ThemeManager.single_color(self.text_color_disabled, self.appearance_mode)) self.text_label.configure(fg=ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))
else: else:
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.set_text(self.text) self.set_text(self.text)
@ -260,19 +260,19 @@ class CTkRadioButton(CTkBaseClass):
def on_enter(self, event=0): def on_enter(self, event=0):
if self.hover is True and self.state == tkinter.NORMAL: if self.hover is True and self.state == tkinter.NORMAL:
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.hover_color, self.appearance_mode), fill=ThemeManager.single_color(self.hover_color, self._appearance_mode),
outline=ThemeManager.single_color(self.hover_color, self.appearance_mode)) outline=ThemeManager.single_color(self.hover_color, self._appearance_mode))
def on_leave(self, event=0): def on_leave(self, event=0):
if self.hover is True: if self.hover is True:
if self.check_state is True: if self.check_state is True:
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("border_parts", self.canvas.itemconfig("border_parts",
fill=ThemeManager.single_color(self.border_color, self.appearance_mode), fill=ThemeManager.single_color(self.border_color, self._appearance_mode),
outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) outline=ThemeManager.single_color(self.border_color, self._appearance_mode))
def variable_callback(self, var_name, index, mode): def variable_callback(self, var_name, index, mode):
if not self.variable_callback_blocked: if not self.variable_callback_blocked:

View File

@ -9,7 +9,7 @@ from .widget_base_class import CTkBaseClass
class CTkSlider(CTkBaseClass): class CTkSlider(CTkBaseClass):
""" tkinter custom slider, always horizontal """ """ tkinter custom slider"""
def __init__(self, *args, def __init__(self, *args,
bg_color=None, bg_color=None,
@ -44,7 +44,7 @@ class CTkSlider(CTkBaseClass):
else: else:
height = 16 height = 16
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color # color
@ -81,8 +81,8 @@ class CTkSlider(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.desired_width), width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.canvas.grid(column=0, row=0, rowspan=1, columnspan=1, sticky="nswe") self.canvas.grid(column=0, row=0, rowspan=1, columnspan=1, sticky="nswe")
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
@ -106,14 +106,14 @@ class CTkSlider(CTkBaseClass):
def set_scaling(self, *args, **kwargs): def set_scaling(self, *args, **kwargs):
super().set_scaling(*args, **kwargs) super().set_scaling(*args, **kwargs)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def set_dimensions(self, width=None, height=None): def set_dimensions(self, width=None, height=None):
super().set_dimensions(width, height) super().set_dimensions(width, height)
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def destroy(self): def destroy(self):
@ -138,8 +138,8 @@ class CTkSlider(CTkBaseClass):
else: else:
orientation = "w" orientation = "w"
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.border_width),
self.apply_widget_scaling(self.button_length), self.apply_widget_scaling(self.button_length),
@ -147,33 +147,33 @@ class CTkSlider(CTkBaseClass):
self.value, orientation) self.value, orientation)
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
if self.border_color is None: if self.border_color is None:
self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.bg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.bg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.border_color, self.appearance_mode), self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.border_color, self._appearance_mode),
outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) outline=ThemeManager.single_color(self.border_color, self._appearance_mode))
self.canvas.itemconfig("inner_parts", fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), self.canvas.itemconfig("inner_parts", fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
if self.progress_color is None: if self.progress_color is None:
self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.progress_color, self.appearance_mode), self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.progress_color, self._appearance_mode),
outline=ThemeManager.single_color(self.progress_color, self.appearance_mode)) outline=ThemeManager.single_color(self.progress_color, self._appearance_mode))
self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self.appearance_mode), self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode),
outline=ThemeManager.single_color(self.button_color, self.appearance_mode)) outline=ThemeManager.single_color(self.button_color, self._appearance_mode))
def clicked(self, event=None): def clicked(self, event=None):
if self.orient.lower() == "horizontal": if self.orient.lower() == "horizontal":
self.value = (event.x / self.current_width) / self.widget_scaling self.value = (event.x / self._current_width) / self._widget_scaling
else: else:
self.value = 1 - (event.y / self.current_height) / self.widget_scaling self.value = 1 - (event.y / self._current_height) / self._widget_scaling
if self.value > 1: if self.value > 1:
self.value = 1 self.value = 1
@ -195,13 +195,13 @@ class CTkSlider(CTkBaseClass):
def on_enter(self, event=0): def on_enter(self, event=0):
self.hover_state = True self.hover_state = True
self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_hover_color, self.appearance_mode), self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode),
outline=ThemeManager.single_color(self.button_hover_color, self.appearance_mode)) outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode))
def on_leave(self, event=0): def on_leave(self, event=0):
self.hover_state = False self.hover_state = False
self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self.appearance_mode), self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode),
outline=ThemeManager.single_color(self.button_color, self.appearance_mode)) outline=ThemeManager.single_color(self.button_color, self._appearance_mode))
def round_to_step_size(self, value): def round_to_step_size(self, value):
if self.number_of_steps is not None: if self.number_of_steps is not None:

View File

@ -34,7 +34,7 @@ class CTkSwitch(CTkBaseClass):
state=tkinter.NORMAL, state=tkinter.NORMAL,
**kwargs): **kwargs):
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
# color # color
@ -79,14 +79,14 @@ class CTkSwitch(CTkBaseClass):
self.bg_canvas = CTkCanvas(master=self, self.bg_canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.current_width), width=self.apply_widget_scaling(self._current_width),
height=self.apply_widget_scaling(self.current_height)) height=self.apply_widget_scaling(self._current_height))
self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe") self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe")
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.apply_widget_scaling(self.current_width), width=self.apply_widget_scaling(self._current_width),
height=self.apply_widget_scaling(self.current_height)) height=self.apply_widget_scaling(self._current_height))
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, sticky="nswe") self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, sticky="nswe")
self.draw_engine = DrawEngine(self.canvas) self.draw_engine = DrawEngine(self.canvas)
@ -110,8 +110,8 @@ class CTkSwitch(CTkBaseClass):
self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6)) self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6))
self.text_label.configure(font=self.apply_font_scaling(self.text_font)) self.text_label.configure(font=self.apply_font_scaling(self.text_font))
self.bg_canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.bg_canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
self.draw() self.draw()
def destroy(self): def destroy(self):
@ -146,16 +146,16 @@ class CTkSwitch(CTkBaseClass):
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
if self.check_state is True: if self.check_state is True:
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.border_width),
self.apply_widget_scaling(self.button_length), self.apply_widget_scaling(self.button_length),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
1, "w") 1, "w")
else: else:
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self.current_width), requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.current_height), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.border_width),
self.apply_widget_scaling(self.button_length), self.apply_widget_scaling(self.button_length),
@ -163,28 +163,28 @@ class CTkSwitch(CTkBaseClass):
0, "w") 0, "w")
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
if self.border_color is None: if self.border_color is None:
self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.bg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.bg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.border_color, self.appearance_mode), self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.border_color, self._appearance_mode),
outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) outline=ThemeManager.single_color(self.border_color, self._appearance_mode))
self.canvas.itemconfig("inner_parts", fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), self.canvas.itemconfig("inner_parts", fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
if self.progress_color is None: if self.progress_color is None:
self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
else: else:
self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.progress_color, self.appearance_mode), self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.progress_color, self._appearance_mode),
outline=ThemeManager.single_color(self.progress_color, self.appearance_mode)) outline=ThemeManager.single_color(self.progress_color, self._appearance_mode))
self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self.appearance_mode), self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode),
outline=ThemeManager.single_color(self.button_color, self.appearance_mode)) outline=ThemeManager.single_color(self.button_color, self._appearance_mode))
if self.text_label is None: if self.text_label is None:
self.text_label = tkinter.Label(master=self, self.text_label = tkinter.Label(master=self,
@ -203,11 +203,11 @@ class CTkSwitch(CTkBaseClass):
self.text_label.configure(textvariable=self.textvariable) self.text_label.configure(textvariable=self.textvariable)
if self.state == tkinter.DISABLED: if self.state == tkinter.DISABLED:
self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode)))
else: else:
self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode))
self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
self.set_text(self.text) self.set_text(self.text)
@ -239,28 +239,28 @@ class CTkSwitch(CTkBaseClass):
self.draw(no_color_updates=True) self.draw(no_color_updates=True)
if self.callback_function is not None:
self.callback_function()
if self.variable is not None and not from_variable_callback: if self.variable is not None and not from_variable_callback:
self.variable_callback_blocked = True self.variable_callback_blocked = True
self.variable.set(self.onvalue) self.variable.set(self.onvalue)
self.variable_callback_blocked = False self.variable_callback_blocked = False
if self.callback_function is not None:
self.callback_function()
def deselect(self, from_variable_callback=False): def deselect(self, from_variable_callback=False):
if self.state is not tkinter.DISABLED or from_variable_callback: if self.state is not tkinter.DISABLED or from_variable_callback:
self.check_state = False self.check_state = False
self.draw(no_color_updates=True) self.draw(no_color_updates=True)
if self.callback_function is not None:
self.callback_function()
if self.variable is not None and not from_variable_callback: if self.variable is not None and not from_variable_callback:
self.variable_callback_blocked = True self.variable_callback_blocked = True
self.variable.set(self.offvalue) self.variable.set(self.offvalue)
self.variable_callback_blocked = False self.variable_callback_blocked = False
if self.callback_function is not None:
self.callback_function()
def get(self): def get(self):
return self.onvalue if self.check_state is True else self.offvalue return self.onvalue if self.check_state is True else self.offvalue
@ -268,13 +268,13 @@ class CTkSwitch(CTkBaseClass):
self.hover_state = True self.hover_state = True
if self.state is not tkinter.DISABLED: if self.state is not tkinter.DISABLED:
self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_hover_color, self.appearance_mode), self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode),
outline=ThemeManager.single_color(self.button_hover_color, self.appearance_mode)) outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode))
def on_leave(self, event=0): def on_leave(self, event=0):
self.hover_state = False self.hover_state = False
self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self.appearance_mode), self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode),
outline=ThemeManager.single_color(self.button_color, self.appearance_mode)) outline=ThemeManager.single_color(self.button_color, self._appearance_mode))
def variable_callback(self, var_name, index, mode): def variable_callback(self, var_name, index, mode):
if not self.variable_callback_blocked: if not self.variable_callback_blocked:

View File

@ -1,6 +1,7 @@
import customtkinter import customtkinter
import tkinter import tkinter
import sys import sys
from typing import Union
from ..theme_manager import ThemeManager from ..theme_manager import ThemeManager
from ..appearance_mode_tracker import AppearanceModeTracker from ..appearance_mode_tracker import AppearanceModeTracker
@ -27,7 +28,8 @@ class DropdownMenu(tkinter.Toplevel):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
ScalingTracker.add_widget(self.set_scaling, self) ScalingTracker.add_widget(self.set_scaling, self)
self.widget_scaling = ScalingTracker.get_widget_scaling(self) self._widget_scaling = ScalingTracker.get_widget_scaling(self)
self._spacing_scaling = ScalingTracker.get_spacing_scaling(self)
self.values = values self.values = values
self.command = command self.command = command
@ -40,15 +42,17 @@ class DropdownMenu(tkinter.Toplevel):
self.text_color = text_color self.text_color = text_color
# shape # shape
self.width = width
self.corner_radius = corner_radius self.corner_radius = corner_radius
self.button_corner_radius = button_corner_radius self.button_corner_radius = button_corner_radius
self.button_height = button_height self.button_height = button_height
self.width = width
self.height = max(len(self.values), 1) * (self.button_height + self.apply_spacing_scaling(y_spacing)) + self.apply_spacing_scaling(y_spacing)
self.geometry(f"{round(self.apply_widget_scaling(self.width))}x" + self.geometry(f"{round(self.apply_widget_scaling(self.width))}x" +
f"{round(self.apply_widget_scaling(len(self.values) * (self.button_height + y_spacing) + y_spacing))}+" + f"{round(self.apply_widget_scaling(self.height))}+" +
f"{round(x_position)}+{round(y_position)}") f"{round(x_position)}+{round(y_position)}")
self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
if sys.platform.startswith("darwin"): if sys.platform.startswith("darwin"):
self.overrideredirect(True) # remove title-bar self.overrideredirect(True) # remove title-bar
@ -81,8 +85,8 @@ class DropdownMenu(tkinter.Toplevel):
corner_radius=self.corner_radius, corner_radius=self.corner_radius,
fg_color=self.fg_color, overwrite_preferred_drawing_method="circle_shapes") fg_color=self.fg_color, overwrite_preferred_drawing_method="circle_shapes")
self.frame.grid(row=0, column=0, sticky="nsew", rowspan=len(self.values) + 1) self.frame.grid(row=0, column=0, sticky="nsew", rowspan=1)
self.frame.grid_rowconfigure(len(self.values) + 1, minsize=y_spacing) # add spacing at the bottom self.frame.grid_rowconfigure(len(self.values) + 1, minsize=self.apply_spacing_scaling(y_spacing)) # add spacing at the bottom
self.frame.grid_columnconfigure(0, weight=1) self.frame.grid_columnconfigure(0, weight=1)
self.button_list = [] self.button_list = []
@ -90,24 +94,31 @@ class DropdownMenu(tkinter.Toplevel):
button = customtkinter.CTkButton(self.frame, button = customtkinter.CTkButton(self.frame,
text=option, text=option,
height=self.button_height, height=self.button_height,
width=self.width - 2 * x_spacing, width=self.width - 2 * self.apply_widget_scaling(x_spacing),
fg_color=self.button_color, fg_color=self.button_color,
text_color=self.text_color, text_color=self.text_color,
hover_color=self.button_hover_color, hover_color=self.button_hover_color,
corner_radius=self.button_corner_radius, corner_radius=self.button_corner_radius,
command=lambda i=index: self.button_callback(i)) command=lambda i=index: self.button_callback(i))
button.text_label.configure(anchor="w")
button.text_label.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="w") button.text_label.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="w")
button.grid(row=index, column=0, button.grid(row=index, column=0,
padx=x_spacing, padx=self.apply_widget_scaling(x_spacing),
pady=(y_spacing, 0), sticky="ew") pady=(self.apply_widget_scaling(y_spacing), 0), sticky="ew")
self.button_list.append(button) self.button_list.append(button)
self.bind("<FocusOut>", self.focus_loss_event) self.bind("<FocusOut>", self.focus_loss_event)
self.frame.canvas.bind("<Button-1>", self.focus_loss_event) self.frame.canvas.bind("<Button-1>", self.focus_loss_event)
def apply_widget_scaling(self, value): def apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
if isinstance(value, (int, float)): if isinstance(value, (int, float)):
return value * self.widget_scaling return value * self._widget_scaling
else:
return value
def apply_spacing_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
if isinstance(value, (int, float)):
return value * self._spacing_scaling
else: else:
return value return value

View File

@ -17,38 +17,48 @@ from ..theme_manager import ThemeManager
class CTkBaseClass(tkinter.Frame): class CTkBaseClass(tkinter.Frame):
def __init__(self, *args, bg_color=None, width, height, **kwargs): """ Base class of every Ctk widget, handles the dimensions, bg_color,
appearance_mode changes, scaling, bg changes of master if master is not a CTk widget """
def __init__(self,
*args,
bg_color: Union[str, tuple] = None,
width: int,
height: int,
**kwargs):
super().__init__(*args, width=width, height=height, **kwargs) # set desired size of underlying tkinter.Frame super().__init__(*args, width=width, height=height, **kwargs) # set desired size of underlying tkinter.Frame
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color # dimensions
self._current_width = width # _current_width and _current_height in pixel, represent current size of the widget
self._current_height = height # _current_width and _current_height are independent of the scale
self._desired_width = width # _desired_width and _desired_height, represent desired size set by width and height
self._desired_height = height
self.current_width = width # current_width and current_height in pixel, represent current size of the widget (not the desired size by init) # scaling
self.current_height = height # current_width and current_height are independent of the scale ScalingTracker.add_widget(self.set_scaling, self) # add callback for automatic scaling changes
self.desired_width = width self._widget_scaling = ScalingTracker.get_widget_scaling(self)
self.desired_height = height self._spacing_scaling = ScalingTracker.get_spacing_scaling(self)
# add set_scaling method to callback list of ScalingTracker for automatic scaling changes super().configure(width=self.apply_widget_scaling(self._desired_width),
ScalingTracker.add_widget(self.set_scaling, self) height=self.apply_widget_scaling(self._desired_height))
self.widget_scaling = ScalingTracker.get_widget_scaling(self)
self.spacing_scaling = ScalingTracker.get_spacing_scaling(self)
super().configure(width=self.apply_widget_scaling(self.desired_width),
height=self.apply_widget_scaling(self.desired_height))
# save latest geometry function and kwargs # save latest geometry function and kwargs
class GeometryCallDict(TypedDict): class GeometryCallDict(TypedDict):
function: Callable function: Callable
kwargs: dict kwargs: dict
self.last_geometry_manager_call: Union[GeometryCallDict, None] = None self._last_geometry_manager_call: Union[GeometryCallDict, None] = None
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes # add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.set_appearance_mode, self) AppearanceModeTracker.add(self.set_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
super().configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) # background color
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too super().configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget as well
if isinstance(self.master, (tkinter.Tk, tkinter.Toplevel, tkinter.Frame)) and not isinstance(self.master, (CTkBaseClass, CTk, CTkToplevel)): if isinstance(self.master, (tkinter.Tk, tkinter.Toplevel, tkinter.Frame)) and not isinstance(self.master, (CTkBaseClass, CTk, CTkToplevel)):
master_old_configure = self.master.config master_old_configure = self.master.config
@ -74,15 +84,15 @@ class CTkBaseClass(tkinter.Frame):
super().destroy() super().destroy()
def place(self, **kwargs): def place(self, **kwargs):
self.last_geometry_manager_call = {"function": super().place, "kwargs": kwargs} self._last_geometry_manager_call = {"function": super().place, "kwargs": kwargs}
super().place(**self.apply_argument_scaling(kwargs)) super().place(**self.apply_argument_scaling(kwargs))
def pack(self, **kwargs): def pack(self, **kwargs):
self.last_geometry_manager_call = {"function": super().pack, "kwargs": kwargs} self._last_geometry_manager_call = {"function": super().pack, "kwargs": kwargs}
super().pack(**self.apply_argument_scaling(kwargs)) super().pack(**self.apply_argument_scaling(kwargs))
def grid(self, **kwargs): def grid(self, **kwargs):
self.last_geometry_manager_call = {"function": super().grid, "kwargs": kwargs} self._last_geometry_manager_call = {"function": super().grid, "kwargs": kwargs}
super().grid(**self.apply_argument_scaling(kwargs)) super().grid(**self.apply_argument_scaling(kwargs))
def apply_argument_scaling(self, kwargs: dict) -> dict: def apply_argument_scaling(self, kwargs: dict) -> dict:
@ -129,9 +139,9 @@ class CTkBaseClass(tkinter.Frame):
def update_dimensions_event(self, event): def update_dimensions_event(self, event):
# only redraw if dimensions changed (for performance) # only redraw if dimensions changed (for performance)
if round(self.current_width) != round(event.width / self.widget_scaling) or round(self.current_height) != round(event.height / self.widget_scaling): if round(self._current_width) != round(event.width / self._widget_scaling) or round(self._current_height) != round(event.height / self._widget_scaling):
self.current_width = (event.width / self.widget_scaling) # adjust current size according to new size given by event self._current_width = (event.width / self._widget_scaling) # adjust current size according to new size given by event
self.current_height = (event.height / self.widget_scaling) # current_width and current_height are independent of the scale self._current_height = (event.height / self._widget_scaling) # _current_width and _current_height are independent of the scale
self.draw(no_color_updates=True) # faster drawing without color changes self.draw(no_color_updates=True) # faster drawing without color changes
@ -164,9 +174,9 @@ class CTkBaseClass(tkinter.Frame):
def set_appearance_mode(self, mode_string): def set_appearance_mode(self, mode_string):
if mode_string.lower() == "dark": if mode_string.lower() == "dark":
self.appearance_mode = 1 self._appearance_mode = 1
elif mode_string.lower() == "light": elif mode_string.lower() == "light":
self.appearance_mode = 0 self._appearance_mode = 0
if isinstance(self.master, (CTkBaseClass, CTk)) and hasattr(self.master, "fg_color"): if isinstance(self.master, (CTkBaseClass, CTk)) and hasattr(self.master, "fg_color"):
self.bg_color = self.master.fg_color self.bg_color = self.master.fg_color
@ -176,33 +186,33 @@ class CTkBaseClass(tkinter.Frame):
self.draw() self.draw()
def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling): def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling):
self.widget_scaling = new_widget_scaling self._widget_scaling = new_widget_scaling
self.spacing_scaling = new_spacing_scaling self._spacing_scaling = new_spacing_scaling
super().configure(width=self.apply_widget_scaling(self.desired_width), super().configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
if self.last_geometry_manager_call is not None: if self._last_geometry_manager_call is not None:
self.last_geometry_manager_call["function"](**self.apply_argument_scaling(self.last_geometry_manager_call["kwargs"])) self._last_geometry_manager_call["function"](**self.apply_argument_scaling(self._last_geometry_manager_call["kwargs"]))
def set_dimensions(self, width=None, height=None): def set_dimensions(self, width=None, height=None):
if width is not None: if width is not None:
self.desired_width = width self._desired_width = width
if height is not None: if height is not None:
self.desired_height = height self._desired_height = height
super().configure(width=self.apply_widget_scaling(self.desired_width), super().configure(width=self.apply_widget_scaling(self._desired_width),
height=self.apply_widget_scaling(self.desired_height)) height=self.apply_widget_scaling(self._desired_height))
def apply_widget_scaling(self, value): def apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
if isinstance(value, (int, float)): if isinstance(value, (int, float)):
return value * self.widget_scaling return value * self._widget_scaling
else: else:
return value return value
def apply_spacing_scaling(self, value): def apply_spacing_scaling(self, value: Union[int, float, str]) -> Union[float, str]:
if isinstance(value, (int, float)): if isinstance(value, (int, float)):
return value * self.spacing_scaling return value * self._spacing_scaling
else: else:
return value return value
@ -211,25 +221,23 @@ class CTkBaseClass(tkinter.Frame):
font_list = list(font) font_list = list(font)
for i in range(len(font_list)): for i in range(len(font_list)):
if (type(font_list[i]) == int or type(font_list[i]) == float) and font_list[i] < 0: if (type(font_list[i]) == int or type(font_list[i]) == float) and font_list[i] < 0:
font_list[i] = int(font_list[i] * self.widget_scaling) font_list[i] = int(font_list[i] * self._widget_scaling)
return tuple(font_list) return tuple(font_list)
elif type(font) == str: elif type(font) == str:
for negative_number in re.findall(r" -\d* ", font): for negative_number in re.findall(r" -\d* ", font):
font = font.replace(negative_number, f" {int(int(negative_number) * self.widget_scaling)} ") font = font.replace(negative_number, f" {int(int(negative_number) * self._widget_scaling)} ")
return font return font
elif isinstance(font, tkinter.font.Font): elif isinstance(font, tkinter.font.Font):
new_font_object = copy.copy(font) new_font_object = copy.copy(font)
if font.cget("size") < 0: if font.cget("size") < 0:
new_font_object.config(size=int(font.cget("size") * self.widget_scaling)) new_font_object.config(size=int(font.cget("size") * self._widget_scaling))
return new_font_object return new_font_object
else: else:
return font return font
def draw(self, no_color_updates=False): def draw(self, no_color_updates: bool = False):
""" abstract of draw method to be overridden """ """ abstract of draw method to be overridden """
pass pass

View File

@ -68,7 +68,7 @@ class CTk(tkinter.Tk):
if self.current_width != round(detected_width / self.window_scaling) or self.current_height != round(detected_height / self.window_scaling): if self.current_width != round(detected_width / self.window_scaling) or self.current_height != round(detected_height / self.window_scaling):
self.current_width = round(detected_width / self.window_scaling) # adjust current size according to new size given by event self.current_width = round(detected_width / self.window_scaling) # adjust current size according to new size given by event
self.current_height = round(detected_height / self.window_scaling) # current_width and current_height are independent of the scale self.current_height = round(detected_height / self.window_scaling) # _current_width and _current_height are independent of the scale
def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling): def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling):
self.window_scaling = new_window_scaling self.window_scaling = new_window_scaling

View File

@ -47,7 +47,7 @@ class CTkToplevel(tkinter.Toplevel):
AppearanceModeTracker.add(self.set_appearance_mode, self) AppearanceModeTracker.add(self.set_appearance_mode, self)
super().configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) super().configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode))
super().title("CTkToplevel") super().title("CTkToplevel")
# self.geometry(f"{self.current_width}x{self.current_height}") # self.geometry(f"{self._current_width}x{self._current_height}")
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
if self.appearance_mode == 1: if self.appearance_mode == 1:
@ -63,7 +63,7 @@ class CTkToplevel(tkinter.Toplevel):
if self.current_width != round(detected_width / self.window_scaling) or self.current_height != round(detected_height / self.window_scaling): if self.current_width != round(detected_width / self.window_scaling) or self.current_height != round(detected_height / self.window_scaling):
self.current_width = round(detected_width / self.window_scaling) # adjust current size according to new size given by event self.current_width = round(detected_width / self.window_scaling) # adjust current size according to new size given by event
self.current_height = round(detected_height / self.window_scaling) # current_width and current_height are independent of the scale self.current_height = round(detected_height / self.window_scaling) # _current_width and _current_height are independent of the scale
def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling): def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling):
self.window_scaling = new_window_scaling self.window_scaling = new_window_scaling

View File

@ -4,56 +4,58 @@ import customtkinter
customtkinter.set_appearance_mode("dark") # Modes: "System" (standard), "Dark", "Light" customtkinter.set_appearance_mode("dark") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue" customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
app = customtkinter.CTk() # create CTk window like you do with the Tk window app = customtkinter.CTk()
app.geometry("400x540") app.geometry("400x580")
app.title("CustomTkinter simple_example.py") app.title("CustomTkinter simple_example.py")
def button_function(): def button_callback():
print("Button click", label_1.text_label.cget("text")) print("Button click", combobox_1.get())
def slider_function(value): def slider_callback(value):
progressbar_1.set(value) progressbar_1.set(value)
def check_box_function():
print("checkbox_1:", checkbox_1.get())
y_padding = 13
frame_1 = customtkinter.CTkFrame(master=app) frame_1 = customtkinter.CTkFrame(master=app)
frame_1.pack(pady=20, padx=60, fill="both", expand=True) frame_1.pack(pady=20, padx=60, fill="both", expand=True)
label_1 = customtkinter.CTkLabel(master=frame_1, justify=tkinter.LEFT) label_1 = customtkinter.CTkLabel(master=frame_1, justify=tkinter.LEFT)
label_1.pack(pady=y_padding, padx=10) label_1.pack(pady=12, padx=10)
progressbar_1 = customtkinter.CTkProgressBar(master=frame_1) progressbar_1 = customtkinter.CTkProgressBar(master=frame_1)
progressbar_1.pack(pady=y_padding, padx=10) progressbar_1.pack(pady=12, padx=10)
button_1 = customtkinter.CTkButton(master=frame_1, command=button_function) button_1 = customtkinter.CTkButton(master=frame_1, command=button_callback)
button_1.pack(pady=y_padding, padx=10) button_1.pack(pady=12, padx=10)
slider_1 = customtkinter.CTkSlider(master=frame_1, command=slider_function, from_=0, to=1) slider_1 = customtkinter.CTkSlider(master=frame_1, command=slider_callback, from_=0, to=1)
slider_1.pack(pady=y_padding, padx=10) slider_1.pack(pady=12, padx=10)
slider_1.set(0.5) slider_1.set(0.5)
entry_1 = customtkinter.CTkEntry(master=frame_1, placeholder_text="CTkEntry") entry_1 = customtkinter.CTkEntry(master=frame_1, placeholder_text="CTkEntry")
entry_1.pack(pady=y_padding, padx=10) entry_1.pack(pady=12, padx=10)
checkbox_1 = customtkinter.CTkCheckBox(master=frame_1, command=check_box_function) optionmenu_1 = customtkinter.CTkOptionMenu(frame_1, values=["Option 1", "Option 2", "Option 42"])
checkbox_1.pack(pady=y_padding, padx=10) optionmenu_1.pack(pady=12, padx=10)
optionmenu_1.set("CTkOptionMenu")
combobox_1 = customtkinter.CTkComboBox(frame_1, values=["Option 1", "Option 2", "Option 42"])
combobox_1.pack(pady=12, padx=10)
optionmenu_1.set("CTkComboBox")
checkbox_1 = customtkinter.CTkCheckBox(master=frame_1)
checkbox_1.pack(pady=12, padx=10)
radiobutton_var = tkinter.IntVar(value=1) radiobutton_var = tkinter.IntVar(value=1)
radiobutton_1 = customtkinter.CTkRadioButton(master=frame_1, variable=radiobutton_var, value=1) radiobutton_1 = customtkinter.CTkRadioButton(master=frame_1, variable=radiobutton_var, value=1)
radiobutton_1.pack(pady=y_padding, padx=10) radiobutton_1.pack(pady=12, padx=10)
radiobutton_2 = customtkinter.CTkRadioButton(master=frame_1, variable=radiobutton_var, value=2) radiobutton_2 = customtkinter.CTkRadioButton(master=frame_1, variable=radiobutton_var, value=2)
radiobutton_2.pack(pady=y_padding, padx=10) radiobutton_2.pack(pady=12, padx=10)
switch_1 = customtkinter.CTkSwitch(master=frame_1) switch_1 = customtkinter.CTkSwitch(master=frame_1)
switch_1.pack(pady=y_padding, padx=10) switch_1.pack(pady=12, padx=10)
app.mainloop() app.mainloop()

View File

@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
github_url = "https://github.com/TomSchimansky/CustomTkinter" github_url = "https://github.com/TomSchimansky/CustomTkinter"
[tool.tbump.version] [tool.tbump.version]
current = "4.2.0" current = "4.3.0"
# Example of a semver regexp. # Example of a semver regexp.
# Make sure this matches current_version before # Make sure this matches current_version before

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = customtkinter name = customtkinter
version = 4.2.0 version = 4.3.0
description = Create modern looking GUIs with Python description = Create modern looking GUIs with Python
long_description = '# CustomTkinter UI-Library\nhttps://github.com/TomSchimansky/CustomTkinter/blob/master/documentation_images/Windows_dark.png\n\nMore Information: https://github.com/TomSchimansky/CustomTkinter' long_description = '# CustomTkinter UI-Library\nhttps://github.com/TomSchimansky/CustomTkinter/blob/master/documentation_images/Windows_dark.png\n\nMore Information: https://github.com/TomSchimansky/CustomTkinter'
long_description_content_type = text/markdown long_description_content_type = text/markdown

View File

@ -1,26 +0,0 @@
import tkinter
import customtkinter
app = customtkinter.CTk()
app.title('test_optionmenu.py')
app.geometry('400x300')
def select_callback(choice):
choice = variable.get()
print("display_selected", choice)
countries = ['Bahamas', 'Canada', 'Cuba', 'United States']
variable = tkinter.StringVar()
variable.set("test")
optionmenu_tk = tkinter.OptionMenu(app, variable, *countries, command=select_callback)
optionmenu_tk.pack(pady=10, padx=10)
optionmenu_1 = customtkinter.CTkOptionMenu(app, variable=variable, values=countries, command=select_callback)
optionmenu_1.pack(pady=10, padx=10)
optionmenu_1.set("te")
app.mainloop()

View File

@ -0,0 +1,31 @@
import tkinter
import customtkinter
app = customtkinter.CTk()
app.title('Test OptionMenu ComboBox.py')
app.geometry('400x300')
def select_callback(choice):
choice = variable.get()
print("display_selected", choice)
countries = ['Bahamas', 'Canada', 'Cuba', 'United States']
variable = tkinter.StringVar()
variable.set("test")
# optionmenu_tk = tkinter.OptionMenu(app, variable, *countries, command=select_callback)
# optionmenu_tk.pack(pady=10, padx=10)
optionmenu_1 = customtkinter.CTkOptionMenu(app, variable=variable, values=countries, command=select_callback)
optionmenu_1.pack(pady=20, padx=10)
# combobox_tk = ttk.Combobox(app, values=countries)
# combobox_tk.pack(pady=10, padx=10)
combobox_1 = customtkinter.CTkComboBox(app, variable=variable, values=countries, command=select_callback)
combobox_1.pack(pady=20, padx=10)
app.mainloop()

View File

@ -11,7 +11,7 @@ app.title("Tkinter Variable Test")
txt_var = tkinter.StringVar(value="") txt_var = tkinter.StringVar(value="")
entry_1 = customtkinter.CTkEntry(app, width=200, textvariable=txt_var) entry_1 = customtkinter.CTkEntry(app, width=200, textvariable=txt_var)
entry_1.pack(pady=15) entry_1.pack(pady=15)
txt_var.set("new text wjkfjdshkjfb") txt_var.set("new text test")
if TEST_CONFIGURE: entry_1.configure(textvariable=txt_var) if TEST_CONFIGURE: entry_1.configure(textvariable=txt_var)
if TEST_REMOVING: entry_1.configure(textvariable="") if TEST_REMOVING: entry_1.configure(textvariable="")
@ -73,8 +73,8 @@ switch_1.pack(pady=20, padx=10)
optionmenu_var = tkinter.StringVar(value="test") optionmenu_var = tkinter.StringVar(value="test")
optionmenu_1 = customtkinter.CTkOptionMenu(master=app, variable=optionmenu_var, values=["Option 1", "Option 2", "Option 3"]) optionmenu_1 = customtkinter.CTkOptionMenu(master=app, variable=optionmenu_var, values=["Option 1", "Option 2", "Option 3"])
optionmenu_1.pack(pady=20, padx=10) optionmenu_1.pack(pady=20, padx=10)
optionmenu_2 = customtkinter.CTkOptionMenu(master=app, values=["Option 1", "Option 2", "Option 3"]) combobox_1 = customtkinter.CTkComboBox(master=app, values=["Option 1", "Option 2", "Option 3"])
optionmenu_2.pack(pady=20, padx=10) combobox_1.pack(pady=20, padx=10)
optionmenu_2.configure(variable=optionmenu_var) combobox_1.configure(variable=optionmenu_var)
app.mainloop() app.mainloop()

View File

@ -40,8 +40,18 @@ button_4.pack(padx=20, pady=(10, 20))
radiobutton_1 = customtkinter.CTkRadioButton(master=app, text="radiobutton_1") radiobutton_1 = customtkinter.CTkRadioButton(master=app, text="radiobutton_1")
radiobutton_1.pack(padx=20, pady=(20, 10)) radiobutton_1.pack(padx=20, pady=(20, 10))
button_5 = customtkinter.CTkButton(master=app, text="Disable/Enable entry_1", command=lambda: change_state(radiobutton_1)) button_5 = customtkinter.CTkButton(master=app, text="Disable/Enable radiobutton_1", command=lambda: change_state(radiobutton_1))
button_5.pack(padx=20, pady=(10, 20)) button_5.pack(padx=20, pady=(10, 20))
optionmenu_1 = customtkinter.CTkOptionMenu(app, values=["test 1", "test 2"])
optionmenu_1.pack(pady=10, padx=10)
button_6 = customtkinter.CTkButton(master=app, text="Disable/Enable optionmenu_1", command=lambda: change_state(optionmenu_1))
button_6.pack(padx=20, pady=(10, 20))
combobox_1 = customtkinter.CTkComboBox(app, values=["test 1", "test 2"])
combobox_1.pack(pady=10, padx=10)
button_7 = customtkinter.CTkButton(master=app, text="Disable/Enable combobox_1", command=lambda: change_state(combobox_1))
button_7.pack(padx=20, pady=(10, 20))
app.mainloop() app.mainloop()