moved base functionality of widgets to widget_base_class.py

This commit is contained in:
Tom Schimansky 2022-04-20 22:50:57 +02:00
parent 5ede252b2f
commit 992d0cbff0
18 changed files with 378 additions and 985 deletions

View File

@ -1,18 +1,18 @@
__version__ = "3.12"
from .widgets.customtkinter_input_dialog import CTkInputDialog
from .widgets.customtkinter_button import CTkButton
from .widgets.customtkinter_slider import CTkSlider
from .widgets.customtkinter_frame import CTkFrame
from .widgets.customtkinter_progressbar import CTkProgressBar
from .widgets.customtkinter_label import CTkLabel
from .widgets.customtkinter_entry import CTkEntry
from .widgets.customtkinter_checkbox import CTkCheckBox
from .widgets.customtkinter_radiobutton import CTkRadioButton
from .widgets.customtkinter_tk import CTk
from .widgets.customtkinter_canvas import CTkCanvas
from .widgets.customtkinter_switch import CTkSwitch
from .widgets.customtkinter_toplevel import CTkToplevel
from .widgets.ctk_input_dialog import CTkInputDialog
from .widgets.ctk_button import CTkButton
from .widgets.ctk_slider import CTkSlider
from .widgets.ctk_frame import CTkFrame
from .widgets.ctk_progressbar import CTkProgressBar
from .widgets.ctk_label import CTkLabel
from .widgets.ctk_entry import CTkEntry
from .widgets.ctk_checkbox import CTkCheckBox
from .widgets.ctk_radiobutton import CTkRadioButton
from .widgets.ctk_tk import CTk
from .widgets.ctk_canvas import CTkCanvas
from .widgets.ctk_switch import CTkSwitch
from .widgets.ctk_toplevel import CTkToplevel
from .customtkinter_settings import CTkSettings
from .appearance_mode_tracker import AppearanceModeTracker

View File

@ -2,7 +2,7 @@ import sys
import tkinter
from typing import Union
from .widgets.customtkinter_canvas import CTkCanvas
from .widgets.ctk_canvas import CTkCanvas
class CTkDrawEngine:

View File

@ -1,17 +1,14 @@
import tkinter
import tkinter.ttk as ttk
import sys
from .customtkinter_tk import CTk
from .customtkinter_frame import CTkFrame
from .customtkinter_canvas import CTkCanvas
from customtkinter.appearance_mode_tracker import AppearanceModeTracker
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass
class CTkButton(tkinter.Frame):
class CTkButton(CTkBaseClass):
""" tkinter custom button with border, rounded corners and hover effect """
def __init__(self, *args,
@ -34,45 +31,18 @@ class CTkButton(tkinter.Frame):
compound=tkinter.LEFT,
state=tkinter.NORMAL,
**kwargs):
super().__init__(*args, **kwargs)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribute>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.set_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
self.configure_basic_grid()
# color variables
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
self.fg_color = CTkThemeManager.theme["color"]["button"] if fg_color == "default_theme" else fg_color
self.hover_color = CTkThemeManager.theme["color"]["button_hover"] if hover_color == "default_theme" else hover_color
self.border_color = CTkThemeManager.theme["color"]["button_border"] if border_color == "default_theme" else border_color
# shape and size
self.width = width
self.height = height
self.configure(width=self.width, height=self.height)
# shape
self.corner_radius = CTkThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width = CTkThemeManager.theme["shape"]["button_border_width"] if border_width == "default_theme" else border_width
@ -98,7 +68,6 @@ class CTkButton(tkinter.Frame):
width=self.width,
height=self.height)
self.canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
# event bindings
@ -113,45 +82,13 @@ class CTkButton(tkinter.Frame):
self.set_cursor()
self.draw() # initial draw
def destroy(self):
AppearanceModeTracker.remove(self.set_appearance_mode)
super().destroy()
def configure_basic_grid(self):
# Configuration of a basic grid (2x2) in which all elements of CTkButtons are centered on one row and one column
# Configuration of a grid system (2x2) in which all parts of CTkButton are centered on one row and one column
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(1, weight=1)
def update_dimensions(self, event):
# only redraw if dimensions changed (for performance)
if self.width != event.width or self.height != event.height:
self.width = event.width
self.height = event.height
# self.canvas.config(width=self.width, height=self.height)
self.draw(no_color_updates=True) # fast drawing without color changes
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
@ -256,9 +193,6 @@ class CTkButton(tkinter.Frame):
self.image_label.grid(row=1, column=0, padx=max(self.corner_radius, self.border_width), sticky="n", columnspan=2, rowspan=1, pady=(2, self.border_width))
self.text_label.grid(row=0, column=0, padx=max(self.corner_radius, self.border_width), sticky="s", columnspan=2, rowspan=1, pady=(self.border_width, 2))
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
@ -401,16 +335,3 @@ class CTkButton(tkinter.Frame):
self.after(100, self.click_animation)
self.function()
def set_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, (CTkFrame, CTk)):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -1,17 +1,14 @@
import tkinter
import tkinter.ttk as ttk
import sys
from .customtkinter_tk import CTk
from .customtkinter_frame import CTkFrame
from .customtkinter_canvas import CTkCanvas
from ..appearance_mode_tracker import AppearanceModeTracker
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass
class CTkCheckBox(tkinter.Frame):
class CTkCheckBox(CTkBaseClass):
""" tkinter custom checkbox with border, rounded corners and hover effect """
def __init__(self, *args,
@ -36,59 +33,28 @@ class CTkCheckBox(tkinter.Frame):
variable=None,
textvariable=None,
**kwargs):
super().__init__(*args, **kwargs)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribut>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.set_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
# color
self.fg_color = CTkThemeManager.theme["color"]["button"] if fg_color == "default_theme" else fg_color
self.hover_color = CTkThemeManager.theme["color"]["button_hover"] if hover_color == "default_theme" else hover_color
self.border_color = CTkThemeManager.theme["color"]["checkbox_border"] if border_color == "default_theme" else border_color
self.checkmark_color = CTkThemeManager.theme["color"]["checkmark"] if checkmark_color == "default_theme" else checkmark_color
self.width = width
self.height = height
# shape
self.corner_radius = CTkThemeManager.theme["shape"]["checkbox_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width = CTkThemeManager.theme["shape"]["checkbox_border_width"] if border_width == "default_theme" else border_width
if self.corner_radius*2 > self.height:
self.corner_radius = self.height/2
elif self.corner_radius*2 > self.width:
self.corner_radius = self.width/2
if self.corner_radius >= self.border_width:
self.inner_corner_radius = self.corner_radius - self.border_width
else:
self.inner_corner_radius = 0
# text
self.text = text
self.text_label = None
self.text_color = CTkThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
self.text_color_disabled = CTkThemeManager.theme["color"]["text_disabled"] if text_color_disabled == "default_theme" else text_color_disabled
self.text_font = (CTkThemeManager.theme["text"]["font"], CTkThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font
# callback and hover functionality
self.function = command
self.state = state
self.hover = hover
@ -100,7 +66,7 @@ class CTkCheckBox(tkinter.Frame):
self.textvariable = textvariable
self.variable_callback_name = None
# configure grid system
# configure grid system (1x3)
self.grid_columnconfigure(0, weight=0)
self.grid_columnconfigure(1, weight=0, minsize=6)
self.grid_columnconfigure(2, weight=1)
@ -110,7 +76,6 @@ class CTkCheckBox(tkinter.Frame):
width=self.width,
height=self.height)
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1)
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
if self.hover is True:
@ -120,11 +85,7 @@ class CTkCheckBox(tkinter.Frame):
self.canvas.bind("<Button-1>", self.toggle)
self.canvas.bind("<Button-1>", self.toggle)
self.text_label = None
self.set_cursor()
self.draw() # initial draw
# set select state according to variable
if self.variable is not None:
self.variable_callback_name = self.variable.trace_add("write", self.variable_callback)
if self.variable.get() == self.onvalue:
@ -132,34 +93,16 @@ class CTkCheckBox(tkinter.Frame):
elif self.variable.get() == self.offvalue:
self.deselect(from_variable_callback=True)
def destroy(self):
AppearanceModeTracker.remove(self.set_appearance_mode)
self.set_cursor()
self.draw() # initial draw
def destroy(self):
if self.variable is not None:
self.variable.trace_remove("write", self.variable_callback_name)
super().destroy()
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
def draw(self):
def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
if self.check_state is True:
@ -207,9 +150,6 @@ class CTkCheckBox(tkinter.Frame):
self.set_text(self.text)
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw()
@ -378,16 +318,3 @@ class CTkCheckBox(tkinter.Frame):
def get(self):
return self.onvalue if self.check_state is True else self.offvalue
def set_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, (CTkFrame, CTk)):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -1,18 +1,14 @@
import tkinter
import tkinter.ttk as ttk
from .customtkinter_tk import CTk
from .customtkinter_frame import CTkFrame
from .customtkinter_canvas import CTkCanvas
from ..appearance_mode_tracker import AppearanceModeTracker
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass
class CTkEntry(tkinter.Frame):
class CTkEntry(CTkBaseClass):
def __init__(self, *args,
master=None,
bg_color=None,
fg_color="default_theme",
text_color="default_theme",
@ -25,39 +21,16 @@ class CTkEntry(tkinter.Frame):
width=120,
height=30,
**kwargs):
if master is None:
super().__init__(*args)
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
if "master" in kwargs:
super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs["master"])
del kwargs["master"]
else:
super().__init__(*args, master=master)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribut>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.change_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
super().__init__(*args, bg_color=bg_color, width=width, height=height)
self.configure_basic_grid()
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
self.fg_color = CTkThemeManager.theme["color"]["entry"] if fg_color == "default_theme" else fg_color
self.text_color = CTkThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
self.placeholder_text_color = CTkThemeManager.theme["color"]["entry_placeholder_text"] if placeholder_text_color == "default_theme" else placeholder_text_color
@ -68,23 +41,15 @@ class CTkEntry(tkinter.Frame):
self.placeholder_text_active = False
self.pre_placeholder_arguments = {} # some set arguments of the entry will be changed for placeholder and then set back
self.width = width
self.height = height
self.corner_radius = CTkThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width = CTkThemeManager.theme["shape"]["entry_border_width"] if border_width == "default_theme" else border_width
if self.corner_radius*2 > self.height:
self.corner_radius = self.height/2
elif self.corner_radius*2 > self.width:
self.corner_radius = self.width/2
super().configure(width=self.width, height=self.height)
self.canvas = CTkCanvas(master=self,
highlightthickness=0,
width=self.width,
height=self.height)
self.canvas.grid(column=0, row=0, sticky="we")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
self.entry = tkinter.Entry(master=self,
bd=0,
@ -94,8 +59,6 @@ class CTkEntry(tkinter.Frame):
**kwargs)
self.entry.grid(column=0, row=0, sticky="we", padx=self.corner_radius if self.corner_radius >= 6 else 6)
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
super().bind('<Configure>', self.update_dimensions)
self.entry.bind('<FocusOut>', self.set_placeholder)
self.entry.bind('<FocusIn>', self.clear_placeholder)
@ -103,42 +66,10 @@ class CTkEntry(tkinter.Frame):
self.draw()
self.set_placeholder()
def destroy(self):
AppearanceModeTracker.remove(self.change_appearance_mode)
super().destroy()
def configure_basic_grid(self):
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
def update_dimensions(self, event):
# only redraw if dimensions changed (for performance)
if self.width != event.width or self.height != event.height:
# print(event.x, event.width, self.width)
self.width = event.width
self.height = event.height
self.draw()
def set_placeholder(self, event=None):
if self.placeholder_text is not None:
if not self.placeholder_text_active and self.entry.get() == "":
@ -156,7 +87,7 @@ class CTkEntry(tkinter.Frame):
for argument, value in self.pre_placeholder_arguments.items():
self.entry[argument] = value
def draw(self):
def draw(self, no_color_updates=False):
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
@ -188,16 +119,16 @@ class CTkEntry(tkinter.Frame):
def bind(self, *args, **kwargs):
self.entry.bind(*args, **kwargs)
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
if "bg_color" in kwargs:
self.bg_color = kwargs["bg_color"]
del kwargs["bg_color"]
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 "fg_color" in kwargs:
self.fg_color = kwargs["fg_color"]
@ -232,7 +163,6 @@ class CTkEntry(tkinter.Frame):
def delete(self, *args, **kwargs):
self.entry.delete(*args, **kwargs)
self.set_placeholder()
return
def insert(self, *args, **kwargs):
self.clear_placeholder()
@ -243,16 +173,3 @@ class CTkEntry(tkinter.Frame):
return ""
else:
return self.entry.get()
def change_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, (CTkFrame, CTk)):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -0,0 +1,111 @@
import tkinter
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass
class CTkFrame(CTkBaseClass):
def __init__(self, *args,
bg_color=None,
fg_color="default_theme",
border_color="default_theme",
border_width="default_theme",
corner_radius="default_theme",
width=200,
height=200,
**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
self.border_color = CTkThemeManager.theme["color"]["frame_border"] if border_color == "default_theme" else border_color
if fg_color == "default_theme":
if isinstance(self.master, CTkFrame):
if self.master.fg_color == CTkThemeManager.theme["color"]["frame_low"]:
self.fg_color = CTkThemeManager.theme["color"]["frame_high"]
else:
self.fg_color = CTkThemeManager.theme["color"]["frame_low"]
else:
self.fg_color = CTkThemeManager.theme["color"]["frame_low"]
else:
self.fg_color = fg_color
# shape
self.corner_radius = CTkThemeManager.theme["shape"]["frame_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width = CTkThemeManager.theme["shape"]["frame_border_width"] if border_width == "default_theme" else border_width
self.canvas = CTkCanvas(master=self,
highlightthickness=0,
width=self.width,
height=self.height)
self.canvas.place(x=0, y=0, relwidth=1, relheight=1)
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
self.bind('<Configure>', self.update_dimensions)
self.draw()
def winfo_children(self):
""" winfo_children of CTkFrame without self.canvas widget,
because it's not a child but part of the CTkFrame itself """
child_widgets = super().winfo_children()
try:
child_widgets.remove(self.canvas)
return child_widgets
except ValueError:
return child_widgets
def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
if no_color_updates is False or requires_recoloring:
self.canvas.itemconfig("inner_parts",
fill=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
outline=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
self.canvas.itemconfig("border_parts",
fill=CTkThemeManager.single_color(self.border_color, self.appearance_mode),
outline=CTkThemeManager.single_color(self.border_color, self.appearance_mode))
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
self.canvas.tag_lower("inner_parts")
self.canvas.tag_lower("border_parts")
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
if "fg_color" in kwargs:
self.fg_color = kwargs["fg_color"]
require_redraw = True
del kwargs["fg_color"]
# check if CTk widgets are children of the frame and change their bg_color to new frame fg_color
for child in self.winfo_children():
if isinstance(child, CTkBaseClass):
child.configure(bg_color=self.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 "corner_radius" in kwargs:
self.corner_radius = kwargs["corner_radius"]
require_redraw = True
del kwargs["corner_radius"]
super().configure(*args, **kwargs)
if require_redraw:
self.draw()

View File

@ -1,11 +1,11 @@
import tkinter
import time
from .customtkinter_label import CTkLabel
from .customtkinter_entry import CTkEntry
from .customtkinter_frame import CTkFrame
from .customtkinter_toplevel import CTkToplevel
from .customtkinter_button import CTkButton
from .ctk_label import CTkLabel
from .ctk_entry import CTkEntry
from .ctk_frame import CTkFrame
from .ctk_toplevel import CTkToplevel
from .ctk_button import CTkButton
from ..appearance_mode_tracker import AppearanceModeTracker
from ..customtkinter_theme_manager import CTkThemeManager

View File

@ -1,18 +1,14 @@
import tkinter
import tkinter.ttk as ttk
from .customtkinter_tk import CTk
from .customtkinter_frame import CTkFrame
from .customtkinter_canvas import CTkCanvas
from ..appearance_mode_tracker import AppearanceModeTracker
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass
class CTkLabel(tkinter.Frame):
class CTkLabel(CTkBaseClass):
def __init__(self, *args,
master=None,
bg_color=None,
fg_color="default_theme",
text_color="default_theme",
@ -22,54 +18,28 @@ class CTkLabel(tkinter.Frame):
text="CTkLabel",
text_font="default_theme",
**kwargs):
if master is None:
super().__init__(*args)
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
if "master" in kwargs:
super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs["master"])
del kwargs["master"]
else:
super().__init__(*args, master=master)
super().__init__(*args, bg_color=bg_color, width=width, height=height)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribut>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.change_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
# color
self.fg_color = CTkThemeManager.theme["color"]["label"] if fg_color == "default_theme" else fg_color
if self.fg_color is None:
self.fg_color = self.bg_color
self.text_color = CTkThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
self.width = width
self.height = height
# shape
self.corner_radius = CTkThemeManager.theme["shape"]["label_corner_radius"] if corner_radius == "default_theme" else corner_radius
if self.corner_radius * 2 > self.height:
self.corner_radius = self.height / 2
elif self.corner_radius * 2 > self.width:
self.corner_radius = self.width / 2
# text
self.text = text
self.text_font = (CTkThemeManager.theme["text"]["font"], CTkThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font
# configure grid system (1x1)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
@ -78,6 +48,7 @@ class CTkLabel(tkinter.Frame):
width=self.width,
height=self.height)
self.canvas.grid(row=0, column=0, sticky="nswe")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
self.text_label = tkinter.Label(master=self,
highlightthickness=0,
@ -87,46 +58,10 @@ class CTkLabel(tkinter.Frame):
**kwargs)
self.text_label.grid(row=0, column=0, padx=self.corner_radius)
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
super().configure(width=self.width, height=self.height)
self.bind('<Configure>', self.update_dimensions)
self.draw()
def destroy(self):
AppearanceModeTracker.remove(self.change_appearance_mode)
super().destroy()
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
def update_dimensions(self, event):
# only redraw if dimensions changed (for performance)
if self.width != event.width or self.height != event.height:
self.width = event.width
self.height = event.height
# self.canvas.config(width=self.width, height=self.height)
self.draw()
def draw(self):
def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, 0)
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
@ -146,9 +81,6 @@ class CTkLabel(tkinter.Frame):
self.text_label.configure(fg=CTkThemeManager.single_color(self.text_color, self.appearance_mode),
bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
@ -182,16 +114,3 @@ class CTkLabel(tkinter.Frame):
def set_text(self, text):
self.text = text
self.text_label.configure(text=self.text, width=len(self.text))
def change_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, (CTkFrame, CTk)):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -1,17 +1,13 @@
import sys
import tkinter
import tkinter.ttk as ttk
from .customtkinter_tk import CTk
from .customtkinter_frame import CTkFrame
from .customtkinter_canvas import CTkCanvas
from ..appearance_mode_tracker import AppearanceModeTracker
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_draw_engine import CTkDrawEngine
from ..customtkinter_settings import CTkSettings
from .widget_base_class import CTkBaseClass
class CTkProgressBar(tkinter.Frame):
class CTkProgressBar(CTkBaseClass):
""" tkinter custom progressbar, always horizontal, values are from 0 to 1 """
def __init__(self, *args,
@ -25,56 +21,30 @@ class CTkProgressBar(tkinter.Frame):
height=8,
border_width="default_theme",
**kwargs):
super().__init__(*args, **kwargs)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribut>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.change_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
# color
self.border_color = CTkThemeManager.theme["color"]["progressbar_border"] if border_color == "default_theme" else border_color
self.fg_color = CTkThemeManager.theme["color"]["progressbar"] if fg_color == "default_theme" else fg_color
self.progress_color = CTkThemeManager.theme["color"]["progressbar_progress"] if progress_color == "default_theme" else progress_color
# control variable
self.variable = variable
self.variable_callback_blocked = False
self.variable_callback_name = None
self.width = width
self.height = height
# shape
self.corner_radius = CTkThemeManager.theme["shape"]["progressbar_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width = CTkThemeManager.theme["shape"]["progressbar_border_width"] if border_width == "default_theme" else border_width
self.value = 0.5
self.configure(width=self.width, height=self.height)
self.canvas = CTkCanvas(master=self,
highlightthickness=0,
width=self.width,
height=self.height)
self.canvas.place(x=0, y=0, relwidth=1, relheight=1)
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
# Each time an item is resized due to pack position mode, the binding Configure is called on the widget
@ -89,53 +59,11 @@ class CTkProgressBar(tkinter.Frame):
self.variable_callback_blocked = False
def destroy(self):
AppearanceModeTracker.remove(self.change_appearance_mode)
if self.variable is not None:
self.variable.trace_remove("write", self.variable_callback_name)
super().destroy()
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
@staticmethod
def calc_optimal_height(user_height):
if sys.platform == "darwin":
return user_height # on macOS just use given value (canvas has Antialiasing)
else:
# make sure the value is always with uneven for better rendering of the ovals
if user_height == 0:
return 0
elif user_height % 2 == 0:
return user_height + 1
else:
return user_height
def update_dimensions(self, event):
# only redraw if dimensions changed (for performance)
if self.width != event.width or self.height != event.height:
self.width = event.width
self.height = event.height
self.draw()
def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.width, self.height, self.corner_radius, self.border_width, self.value, "w")
@ -156,9 +84,12 @@ class CTkProgressBar(tkinter.Frame):
require_redraw = False # some attribute changes require a call of self.draw() at the end
if "bg_color" in kwargs:
self.bg_color = kwargs["bg_color"]
del kwargs["bg_color"]
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 "fg_color" in kwargs:
self.fg_color = kwargs["fg_color"]
@ -217,16 +148,3 @@ class CTkProgressBar(tkinter.Frame):
self.variable_callback_blocked = True
self.variable.set(round(self.value) if isinstance(self.variable, tkinter.IntVar) else self.value)
self.variable_callback_blocked = False
def change_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, CTkFrame):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -1,17 +1,14 @@
import tkinter
import tkinter.ttk as ttk
import sys
from .customtkinter_tk import CTk
from .customtkinter_frame import CTkFrame
from .customtkinter_canvas import CTkCanvas
from ..appearance_mode_tracker import AppearanceModeTracker
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass
class CTkRadioButton(tkinter.Frame):
class CTkRadioButton(CTkBaseClass):
def __init__(self, *args,
bg_color=None,
fg_color="default_theme",
@ -33,60 +30,29 @@ class CTkRadioButton(tkinter.Frame):
variable=None,
textvariable=None,
**kwargs):
super().__init__(*args, **kwargs)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribut>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.set_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
# color
self.fg_color = CTkThemeManager.theme["color"]["button"] if fg_color == "default_theme" else fg_color
self.hover_color = CTkThemeManager.theme["color"]["button_hover"] if hover_color == "default_theme" else hover_color
self.border_color = CTkThemeManager.theme["color"]["checkbox_border"] if border_color == "default_theme" else border_color
self.width = width
self.height = height
# shape
self.corner_radius = CTkThemeManager.theme["shape"]["radiobutton_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width_unchecked = CTkThemeManager.theme["shape"]["radiobutton_border_width_unchecked"] if border_width_unchecked == "default_theme" else border_width_unchecked
self.border_width_checked = CTkThemeManager.theme["shape"]["radiobutton_border_width_checked"] if border_width_checked == "default_theme" else border_width_checked
self.border_width = self.border_width_unchecked
if self.corner_radius*2 > self.height:
self.corner_radius = self.height/2
elif self.corner_radius*2 > self.width:
self.corner_radius = self.width/2
if self.corner_radius >= self.border_width:
self.inner_corner_radius = self.corner_radius - self.border_width
else:
self.inner_corner_radius = 0
# text
self.text = text
self.text_label = None
self.text_color = CTkThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
self.text_color_disabled = CTkThemeManager.theme["color"]["text_disabled"] if text_color_disabled == "default_theme" else text_color_disabled
self.text_font = (CTkThemeManager.theme["text"]["font"], CTkThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font
# callback and control variables
self.function = command
self.state = state
self.hover = hover
@ -97,7 +63,7 @@ class CTkRadioButton(tkinter.Frame):
self.textvariable = textvariable
self.variable_callback_name = None
# configure grid system
# configure grid system (3x1)
self.grid_columnconfigure(0, weight=0)
self.grid_columnconfigure(1, weight=0, minsize=6)
self.grid_columnconfigure(2, weight=1)
@ -107,7 +73,6 @@ class CTkRadioButton(tkinter.Frame):
width=self.width,
height=self.height)
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1)
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
self.canvas.bind("<Enter>", self.on_enter)
@ -115,8 +80,6 @@ class CTkRadioButton(tkinter.Frame):
self.canvas.bind("<Button-1>", self.invoke)
self.canvas.bind("<Button-1>", self.invoke)
self.text_label = None
self.set_cursor()
self.draw() # initial draw
@ -128,33 +91,12 @@ class CTkRadioButton(tkinter.Frame):
self.deselect(from_variable_callback=True)
def destroy(self):
AppearanceModeTracker.remove(self.set_appearance_mode)
if self.variable is not None:
self.variable.trace_remove("write", self.variable_callback_name)
super().destroy()
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
def draw(self):
def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
@ -191,9 +133,6 @@ class CTkRadioButton(tkinter.Frame):
self.set_text(self.text)
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw()
@ -338,16 +277,3 @@ class CTkRadioButton(tkinter.Frame):
self.variable_callback_blocked = True
self.variable.set("")
self.variable_callback_blocked = False
def set_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, (CTkFrame, CTk)):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -1,17 +1,14 @@
import tkinter
import tkinter.ttk as ttk
import sys
from .customtkinter_tk import CTk
from .customtkinter_frame import CTkFrame
from .customtkinter_canvas import CTkCanvas
from ..appearance_mode_tracker import AppearanceModeTracker
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass
class CTkSlider(tkinter.Frame):
class CTkSlider(CTkBaseClass):
""" tkinter custom slider, always horizontal """
def __init__(self, *args,
@ -33,44 +30,18 @@ class CTkSlider(tkinter.Frame):
command=None,
variable=None,
**kwargs):
super().__init__(*args, **kwargs)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribut>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.change_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.configure_basic_grid()
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
# color
self.border_color = border_color
self.fg_color = CTkThemeManager.theme["color"]["slider"] if fg_color == "default_theme" else fg_color
self.progress_color = CTkThemeManager.theme["color"]["slider_progress"] if progress_color == "default_theme" else progress_color
self.button_color = CTkThemeManager.theme["color"]["slider_button"] if button_color == "default_theme" else button_color
self.button_hover_color = CTkThemeManager.theme["color"]["slider_button_hover"] if button_hover_color == "default_theme" else button_hover_color
self.width = width
self.height = height
# shape
self.corner_radius = CTkThemeManager.theme["shape"]["slider_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.button_corner_radius = CTkThemeManager.theme["shape"]["slider_button_corner_radius"] if button_corner_radius == "default_theme" else button_corner_radius
self.border_width = CTkThemeManager.theme["shape"]["slider_border_width"] if border_width == "default_theme" else border_width
@ -85,23 +56,17 @@ class CTkSlider(tkinter.Frame):
if self.corner_radius < self.button_corner_radius:
self.corner_radius = self.button_corner_radius
# callback and control variables
self.callback_function = command
self.variable: tkinter.Variable = variable
self.variable_callback_blocked = False
self.variable_callback_name = None
self.configure(width=self.width, height=self.height)
if sys.platform == "darwin":
self.configure(cursor="pointinghand")
elif sys.platform.startswith("win"):
self.configure(cursor="hand2")
self.canvas = CTkCanvas(master=self,
highlightthickness=0,
width=self.width,
height=self.height)
self.canvas.grid(column=0, row=0, sticky="nswe")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
self.canvas.bind("<Enter>", self.on_enter)
@ -112,6 +77,7 @@ class CTkSlider(tkinter.Frame):
# Each time an item is resized due to pack position mode, the binding Configure is called on the widget
self.bind('<Configure>', self.update_dimensions)
self.set_cursor()
self.draw() # initial draw
if self.variable is not None:
@ -121,9 +87,6 @@ class CTkSlider(tkinter.Frame):
self.variable_callback_blocked = False
def destroy(self):
# remove change_appearance_mode function from callback list of AppearanceModeTracker
AppearanceModeTracker.remove(self.change_appearance_mode)
# remove variable_callback from variable callbacks if variable exists
if self.variable is not None:
self.variable.trace_remove("write", self.variable_callback_name)
@ -134,52 +97,17 @@ class CTkSlider(tkinter.Frame):
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
@staticmethod
def calc_optimal_height(user_height):
def set_cursor(self):
if sys.platform == "darwin":
return user_height # on macOS just use given value (canvas has Antialiasing)
else:
# make sure the value is always with uneven for better rendering of the ovals
if user_height == 0:
return 0
elif user_height % 2 == 0:
return user_height + 1
else:
return user_height
def update_dimensions(self, event):
# only redraw if dimensions changed (for performance)
if self.width != event.width or self.height != event.height:
self.width = event.width
self.height = event.height
self.draw()
def draw(self, color_updates=True):
self.configure(cursor="pointinghand")
elif sys.platform.startswith("win"):
self.configure(cursor="hand2")
def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.width, self.height, self.corner_radius, self.border_width,
self.button_length, self.button_corner_radius, self.value, "w")
if color_updates or requires_recoloring:
if no_color_updates is False or requires_recoloring:
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
if self.border_color is None:
@ -213,7 +141,7 @@ class CTkSlider(tkinter.Frame):
self.output_value = self.round_to_step_size(self.from_ + (self.value * (self.to - self.from_)))
self.value = (self.output_value - self.from_) / (self.to - self.from_)
self.draw(color_updates=False)
self.draw(no_color_updates=False)
if self.callback_function is not None:
self.callback_function(self.output_value)
@ -259,7 +187,7 @@ class CTkSlider(tkinter.Frame):
self.output_value = self.round_to_step_size(output_value)
self.value = (self.output_value - self.from_) / (self.to - self.from_)
self.draw(color_updates=False)
self.draw(no_color_updates=False)
if self.callback_function is not None:
self.callback_function(self.output_value)
@ -273,9 +201,6 @@ class CTkSlider(tkinter.Frame):
if not self.variable_callback_blocked:
self.set(self.variable.get(), from_variable_callback=True)
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
@ -354,16 +279,3 @@ class CTkSlider(tkinter.Frame):
if require_redraw:
self.draw()
def change_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, CTkFrame):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -1,17 +1,14 @@
import tkinter
import tkinter.ttk as ttk
import sys
from .customtkinter_tk import CTk
from .customtkinter_frame import CTkFrame
from .customtkinter_canvas import CTkCanvas
from ..appearance_mode_tracker import AppearanceModeTracker
from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass
class CTkSwitch(tkinter.Frame):
class CTkSwitch(CTkBaseClass):
def __init__(self, *args,
text="CTkSwitch",
text_font="default_theme",
@ -34,34 +31,11 @@ class CTkSwitch(tkinter.Frame):
variable=None,
textvariable=None,
**kwargs):
super().__init__(*args, **kwargs)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribut>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.change_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
# color
self.border_color = border_color
self.fg_color = CTkThemeManager.theme["color"]["switch"] if fg_color == "default_theme" else fg_color
self.progress_color = CTkThemeManager.theme["color"]["switch_progress"] if progress_color == "default_theme" else progress_color
@ -69,10 +43,12 @@ class CTkSwitch(tkinter.Frame):
self.button_hover_color = CTkThemeManager.theme["color"]["switch_button_hover"] if button_hover_color == "default_theme" else button_hover_color
self.text_color = CTkThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
# text
self.text = text
self.text_label = None
self.text_font = (CTkThemeManager.theme["text"]["font"], CTkThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font
self.width = width
self.height = height
# shape
self.corner_radius = CTkThemeManager.theme["shape"]["switch_corner_radius"] if corner_radius == "default_theme" else corner_radius
# self.button_corner_radius = CTkThemeManager.theme["shape"]["switch_button_corner_radius"] if button_corner_radius == "default_theme" else button_corner_radius
self.border_width = CTkThemeManager.theme["shape"]["switch_border_width"] if border_width == "default_theme" else border_width
@ -82,16 +58,17 @@ class CTkSwitch(tkinter.Frame):
self.onvalue = onvalue
self.offvalue = offvalue
#if self.corner_radius < self.button_corner_radius:
# self.corner_radius = self.button_corner_radius
# if self.corner_radius < self.button_corner_radius:
# self.corner_radius = self.button_corner_radius
# callback and control variables
self.callback_function = command
self.variable: tkinter.Variable = variable
self.variable_callback_blocked = False
self.variable_callback_name = None
self.textvariable = textvariable
# configure grid system
# configure grid system (3x1)
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=0, minsize=6)
self.grid_columnconfigure(2, weight=0)
@ -101,20 +78,13 @@ class CTkSwitch(tkinter.Frame):
width=self.width,
height=self.height)
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, sticky="nswe")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
if sys.platform == "darwin" and CTkSettings.hand_cursor_enabled:
self.canvas.configure(cursor="pointinghand")
elif sys.platform.startswith("win") and CTkSettings.hand_cursor_enabled:
self.canvas.configure(cursor="hand2")
self.canvas.bind("<Enter>", self.on_enter)
self.canvas.bind("<Leave>", self.on_leave)
self.canvas.bind("<Button-1>", self.toggle)
self.text_label = None
self.set_cursor()
self.draw() # initial draw
if self.variable is not None:
@ -125,35 +95,19 @@ class CTkSwitch(tkinter.Frame):
self.deselect(from_variable_callback=True)
def destroy(self):
# remove change_appearance_mode function from callback list of AppearanceModeTracker
AppearanceModeTracker.remove(self.change_appearance_mode)
# remove variable_callback from variable callbacks if variable exists
if self.variable is not None:
self.variable.trace_remove("write", self.variable_callback_name)
super().destroy()
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
def set_cursor(self):
if sys.platform == "darwin" and CTkSettings.hand_cursor_enabled:
self.canvas.configure(cursor="pointinghand")
elif sys.platform.startswith("win") and CTkSettings.hand_cursor_enabled:
self.canvas.configure(cursor="hand2")
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
def draw(self, color_updates=True):
def draw(self, no_color_updates=False):
if self.check_state is True:
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.width, self.height, self.corner_radius, self.border_width,
@ -162,7 +116,7 @@ class CTkSwitch(tkinter.Frame):
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.width, self.height, self.corner_radius, self.border_width,
self.button_length, self.corner_radius, 0, "w")
if color_updates or requires_recoloring:
if no_color_updates is False or requires_recoloring:
self.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
@ -186,8 +140,6 @@ class CTkSwitch(tkinter.Frame):
self.canvas.itemconfig("slider_parts", fill=CTkThemeManager.single_color(self.button_color, self.appearance_mode),
outline=CTkThemeManager.single_color(self.button_color, self.appearance_mode))
# self.canvas.configure(bg="red")
if self.text_label is None:
self.text_label = tkinter.Label(master=self,
bd=0,
@ -208,8 +160,6 @@ class CTkSwitch(tkinter.Frame):
self.text = text
if self.text_label is not None:
self.text_label.configure(text=self.text)
else:
sys.stderr.write("ERROR (CTkSwitch): Cant change text because checkbox has no text.")
def toggle(self, event=None):
if self.check_state is True:
@ -217,7 +167,7 @@ class CTkSwitch(tkinter.Frame):
else:
self.check_state = True
self.draw(color_updates=False)
self.draw(no_color_updates=True)
if self.callback_function is not None:
self.callback_function()
@ -230,7 +180,7 @@ class CTkSwitch(tkinter.Frame):
def select(self, from_variable_callback=False):
self.check_state = True
self.draw(color_updates=False)
self.draw(no_color_updates=True)
if self.callback_function is not None:
self.callback_function()
@ -243,7 +193,7 @@ class CTkSwitch(tkinter.Frame):
def deselect(self, from_variable_callback=False):
self.check_state = False
self.draw(color_updates=False)
self.draw(no_color_updates=True)
if self.callback_function is not None:
self.callback_function()
@ -273,9 +223,6 @@ class CTkSwitch(tkinter.Frame):
elif self.variable.get() == self.offvalue:
self.deselect(from_variable_callback=True)
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
@ -349,16 +296,3 @@ class CTkSwitch(tkinter.Frame):
if require_redraw:
self.draw()
def change_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, CTkFrame):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -97,13 +97,13 @@ class CTk(tkinter.Tk):
args[0]["background"] = CTkThemeManager.single_color(self.fg_color, self.appearance_mode)
if bg_changed:
from .customtkinter_slider import CTkSlider
from .customtkinter_progressbar import CTkProgressBar
from .customtkinter_label import CTkLabel
from .customtkinter_frame import CTkFrame
from .customtkinter_entry import CTkEntry
from customtkinter.widgets.customtkinter_checkbox import CTkCheckBox
from customtkinter.widgets.customtkinter_button import CTkButton
from .ctk_slider import CTkSlider
from .ctk_progressbar import CTkProgressBar
from .ctk_label import CTkLabel
from .ctk_frame import CTkFrame
from .ctk_entry import CTkEntry
from customtkinter.widgets.ctk_checkbox import CTkCheckBox
from customtkinter.widgets.ctk_button import CTkButton
for child in self.winfo_children():
if isinstance(child, (CTkFrame, CTkButton, CTkLabel, CTkSlider, CTkCheckBox, CTkEntry, CTkProgressBar)):

View File

@ -83,13 +83,13 @@ class CTkToplevel(tkinter.Toplevel):
args[0]["background"] = CTkThemeManager.single_color(self.fg_color, self.appearance_mode)
if bg_changed:
from .customtkinter_slider import CTkSlider
from .customtkinter_progressbar import CTkProgressBar
from .customtkinter_label import CTkLabel
from .customtkinter_frame import CTkFrame
from .customtkinter_entry import CTkEntry
from customtkinter.widgets.customtkinter_checkbox import CTkCheckBox
from customtkinter.widgets.customtkinter_button import CTkButton
from .ctk_slider import CTkSlider
from .ctk_progressbar import CTkProgressBar
from .ctk_label import CTkLabel
from .ctk_frame import CTkFrame
from .ctk_entry import CTkEntry
from customtkinter.widgets.ctk_checkbox import CTkCheckBox
from customtkinter.widgets.ctk_button import CTkButton
for child in self.winfo_children():
if isinstance(child, (CTkFrame, CTkButton, CTkLabel, CTkSlider, CTkCheckBox, CTkEntry, CTkProgressBar)):

View File

@ -1,203 +0,0 @@
import tkinter
import tkinter.ttk as ttk
from .customtkinter_tk import CTk
from .customtkinter_canvas import CTkCanvas
from ..appearance_mode_tracker import AppearanceModeTracker
from ..customtkinter_theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine
class CTkFrame(tkinter.Frame):
def __init__(self, *args,
bg_color=None,
fg_color="default_theme",
border_color="default_theme",
border_width="default_theme",
corner_radius="default_theme",
width=200,
height=200,
**kwargs):
super().__init__(*args, **kwargs)
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Frame)) and not isinstance(self.master, (CTk, CTkFrame)):
master_old_configure = self.master.config
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribut>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.change_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
self.border_color = CTkThemeManager.theme["color"]["frame_border"] if border_color == "default_theme" else border_color
if fg_color == "default_theme":
if isinstance(self.master, CTkFrame):
if self.master.fg_color == CTkThemeManager.theme["color"]["frame_low"]:
self.fg_color = CTkThemeManager.theme["color"]["frame_high"]
else:
self.fg_color = CTkThemeManager.theme["color"]["frame_low"]
else:
self.fg_color = CTkThemeManager.theme["color"]["frame_low"]
else:
self.fg_color = fg_color
self.width = width
self.height = height
self.configure(width=self.width, height=self.height)
self.corner_radius = CTkThemeManager.theme["shape"]["frame_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width = CTkThemeManager.theme["shape"]["frame_border_width"] if border_width == "default_theme" else border_width
if self.corner_radius * 2 > self.height:
self.corner_radius = self.height / 2
elif self.corner_radius * 2 > self.width:
self.corner_radius = self.width / 2
if self.corner_radius >= self.border_width:
self.inner_corner_radius = self.corner_radius - self.border_width
else:
self.inner_corner_radius = 0
self.canvas = CTkCanvas(master=self,
highlightthickness=0,
width=self.width,
height=self.height)
self.canvas.place(x=0, y=0, relwidth=1, relheight=1)
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
self.bind('<Configure>', self.update_dimensions)
self.draw()
def destroy(self):
AppearanceModeTracker.remove(self.change_appearance_mode)
super().destroy()
def winfo_children(self):
""" winfo_children of CTkFrame without self.canvas widget,
because it's not a child but part of the CTkFrame itself """
child_widgets = super().winfo_children()
try:
child_widgets.remove(self.canvas)
return child_widgets
except ValueError:
return child_widgets
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkFrame): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
def update_dimensions(self, event):
# only redraw if dimensions changed (for performance)
if self.width != event.width or self.height != event.height:
self.width = event.width
self.height = event.height
self.draw()
def draw(self):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width)
self.canvas.itemconfig("inner_parts",
fill=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
outline=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
self.canvas.itemconfig("border_parts",
fill=CTkThemeManager.single_color(self.border_color, self.appearance_mode),
outline=CTkThemeManager.single_color(self.border_color, self.appearance_mode))
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
self.canvas.tag_lower("inner_parts")
self.canvas.tag_lower("border_parts")
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end
if "fg_color" in kwargs:
self.fg_color = kwargs["fg_color"]
require_redraw = True
del kwargs["fg_color"]
# check if CTk widgets are children of the frame and change their bg_color to new frame fg_color
from .customtkinter_slider import CTkSlider
from .customtkinter_progressbar import CTkProgressBar
from .customtkinter_label import CTkLabel
from .customtkinter_entry import CTkEntry
from customtkinter.widgets.customtkinter_checkbox import CTkCheckBox
from customtkinter.widgets.customtkinter_button import CTkButton
for child in self.winfo_children():
if isinstance(child, (CTkButton, CTkLabel, CTkSlider, CTkCheckBox, CTkEntry, CTkProgressBar, CTkFrame)):
child.configure(bg_color=self.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 "corner_radius" in kwargs:
self.corner_radius = kwargs["corner_radius"]
require_redraw = True
del kwargs["corner_radius"]
super().configure(*args, **kwargs)
if require_redraw:
self.draw()
def change_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, CTkFrame):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()

View File

@ -0,0 +1,111 @@
import tkinter
import tkinter.ttk as ttk
from .ctk_tk import CTk
from .ctk_toplevel import CTkToplevel
from ..appearance_mode_tracker import AppearanceModeTracker
class CTkBaseClass(tkinter.Frame):
def __init__(self, *args, bg_color=None, width, height, **kwargs):
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
self.width = width # width and height in pixel, represent current size of the widget (not the desired size by init)
self.height = height
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.set_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too
if isinstance(self.master, (tkinter.Tk, tkinter.Toplevel, tkinter.Frame)) and not isinstance(self.master, (CTkBaseClass, CTk, CTkToplevel)):
master_old_configure = self.master.config
def new_configure(*args, **kwargs):
if "bg" in kwargs:
self.configure(bg_color=kwargs["bg"])
elif "background" in kwargs:
self.configure(bg_color=kwargs["background"])
# args[0] is dict when attribute gets changed by widget[<attribute>] syntax
elif len(args) > 0 and type(args[0]) == dict:
if "bg" in args[0]:
self.configure(bg_color=args[0]["bg"])
elif "background" in args[0]:
self.configure(bg_color=args[0]["background"])
master_old_configure(*args, **kwargs)
self.master.config = new_configure
self.master.configure = new_configure
def destroy(self):
AppearanceModeTracker.remove(self.set_appearance_mode)
super().destroy()
def config(self, *args, **kwargs):
self.configure(*args, **kwargs)
def configure(self, *args, **kwargs):
""" basic configure with bg_color support, to be overridden """
require_redraw = False
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"]
super().configure(*args, **kwargs)
if require_redraw:
self.draw()
def update_dimensions(self, event):
# only redraw if dimensions changed (for performance)
if self.width != event.width or self.height != event.height:
self.width = event.width # adjust current size according to new size given by event
self.height = event.height
self.draw(no_color_updates=True) # faster drawing without color changes
def detect_color_of_master(self):
""" detect color of self.master widget to set correct bg_color """
if isinstance(self.master, CTkBaseClass) and hasattr(self.master, "fg_color"): # master is CTkFrame
return self.master.fg_color
elif isinstance(self.master, (ttk.Frame, ttk.LabelFrame, ttk.Notebook)): # master is ttk widget
try:
ttk_style = ttk.Style()
return ttk_style.lookup(self.master.winfo_class(), 'background')
except Exception:
return "#FFFFFF", "#000000"
else: # master is normal tkinter widget
try:
return self.master.cget("bg") # try to get bg color by .cget() method
except Exception:
return "#FFFFFF", "#000000"
def set_appearance_mode(self, mode_string):
if mode_string.lower() == "dark":
self.appearance_mode = 1
elif mode_string.lower() == "light":
self.appearance_mode = 0
if isinstance(self.master, (CTkBaseClass, CTk)) and hasattr(self.master, "fg_color"):
self.bg_color = self.master.fg_color
else:
self.bg_color = self.master.cget("bg")
self.draw()
def draw(self, no_color_updates=False):
""" abstract of draw method to be overridden """
pass

View File

@ -10,9 +10,9 @@ app.title("CTkDialog Test")
def change_mode():
if customtkinter.get_appearance_mode().lower() == "dark":
if c1.get() == 0:
customtkinter.set_appearance_mode("light")
elif customtkinter.get_appearance_mode().lower() == "light":
else:
customtkinter.set_appearance_mode("dark")