mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
font character mapping for Windows 11, added , refined CTkDrawEngineCTkRadioButton
This commit is contained in:
parent
eab428f9ef
commit
6c976245aa
@ -8,6 +8,7 @@ from .customtkinter_progressbar import CTkProgressBar
|
||||
from .customtkinter_label import CTkLabel
|
||||
from .customtkinter_entry import CTkEntry
|
||||
from .customtkinter_checkbox import CTkCheckBox
|
||||
from .customtkinter_radiobutton import CTkRadioButton
|
||||
from .customtkinter_tk import CTk
|
||||
from .customtkinter_canvas import CTkCanvas
|
||||
from .customtkinter_toplevel import CTkToplevel
|
||||
@ -70,9 +71,9 @@ if not sys.platform == "darwin":
|
||||
|
||||
# load text fonts and custom font with circle shapes for round corner rendering
|
||||
script_directory = os.path.dirname(os.path.abspath(__file__))
|
||||
pyglet.font.add_file(os.path.join(script_directory, "assets", "CustomTkinter_shapes_font-fine.otf"))
|
||||
pyglet.font.add_file(os.path.join(script_directory, "assets", "Roboto", "Roboto-Regular.ttf"))
|
||||
pyglet.font.add_file(os.path.join(script_directory, "assets", "Roboto", "Roboto-Medium.ttf"))
|
||||
pyglet.font.add_file(os.path.join(script_directory, "assets", "fonts", "CustomTkinter_shapes_font-fine.otf"))
|
||||
pyglet.font.add_file(os.path.join(script_directory, "assets", "fonts", "Roboto", "Roboto-Regular.ttf"))
|
||||
pyglet.font.add_file(os.path.join(script_directory, "assets", "fonts", "Roboto", "Roboto-Medium.ttf"))
|
||||
CTkSettings.circle_font_is_ready = pyglet.font.have_font("CustomTkinter_shapes_font")
|
||||
|
||||
warnings.simplefilter("default")
|
||||
|
@ -1,25 +1,25 @@
|
||||
{
|
||||
"color": {
|
||||
"window_bg_color": ["#ECECEC", "gray14"],
|
||||
"button": ["#3599D6", "#1C94CF"],
|
||||
"button_hover": ["#A7C2E0", "#5FB4DD"],
|
||||
"window_bg_color": ["gray92", "gray12"],
|
||||
"button": ["#1FA6E8", "#1C94CF"],
|
||||
"button_hover": ["#1A89BF", "#1673A1"],
|
||||
"button_border": ["gray25", "gray86"],
|
||||
"checkbox_border": ["gray20", "#ededed"],
|
||||
"entry": ["white", "gray10"],
|
||||
"entry_border": ["gray65", "gray35"],
|
||||
"checkbox_border": ["gray40", "gray60"],
|
||||
"entry": ["white", "gray24"],
|
||||
"entry_border": ["gray70", "gray32"],
|
||||
"entry_placeholder_text": ["gray52", "gray62"],
|
||||
"frame_border": ["#A7C2E0", "#5FB4DD"],
|
||||
"frame_low": ["#D4D5D6", "gray20"],
|
||||
"frame_high": ["gray77", "gray24"],
|
||||
"frame_low": ["gray87", "gray18"],
|
||||
"frame_high": ["gray82", "gray22"],
|
||||
"label": [null, null],
|
||||
"text": ["gray20", "gray90"],
|
||||
"progressbar": ["#6B6B6B", "#222222"],
|
||||
"progressbar_progress": ["#3599D6", "#1C94CF"],
|
||||
"progressbar_border": ["gray", "gray"],
|
||||
"slider": ["#6B6B6B", "#222222"],
|
||||
"slider_progress": ["#A5A6A5", "#555555"],
|
||||
"slider_progress": ["white", "#555555"],
|
||||
"slider_button": ["#3599D6", "#1C94CF"],
|
||||
"slider_button_hover": ["#A7C2E0", "#5FB4DD"],
|
||||
"slider_button_hover": ["#1A89BF", "#1673A1"],
|
||||
"darken_factor": 0.8
|
||||
},
|
||||
|
||||
@ -43,15 +43,18 @@
|
||||
"button_border_width": 0,
|
||||
"checkbox_corner_radius": 7,
|
||||
"checkbox_border_width": 3,
|
||||
"radiobutton_corner_radius": 1000,
|
||||
"radiobutton_border_width_unchecked": 3,
|
||||
"radiobutton_border_width_checked": 6,
|
||||
"entry_border_width": 2,
|
||||
"frame_corner_radius": 10,
|
||||
"frame_border_width": 0,
|
||||
"label_corner_radius": 8,
|
||||
"progressbar_border_width": 0,
|
||||
"progressbar_corner_radius": 100,
|
||||
"progressbar_corner_radius": 1000,
|
||||
"slider_border_width": 6,
|
||||
"slider_corner_radius": 8,
|
||||
"slider_button_length": 0,
|
||||
"slider_button_corner_radius": 100
|
||||
"slider_button_corner_radius": 1000
|
||||
}
|
||||
}
|
@ -4,13 +4,7 @@ from .customtkinter_settings import CTkSettings
|
||||
|
||||
class CTkCanvas(tkinter.Canvas):
|
||||
|
||||
# This dict maps a corner_radius of a circle to a specific font character, which is circle shape which fills the space
|
||||
# of one monospace character to a specific amount from 100% to 90% (A to I).
|
||||
radius_to_char = {19: 'B', 18: 'B', 17: 'B', 16: 'B', 15: 'B', 14: 'B', 13: 'B', 12: 'B', 11: 'B', 10: 'B',
|
||||
9: 'C', 8: 'D', 7: 'C', 6: 'E', 5: 'F', 4: 'G', 3: 'H', 2: 'H', 1: 'H', 0: 'A'}
|
||||
|
||||
radius_to_char_fine = {19: 'A', 18: 'A', 17: 'B', 16: 'B', 15: 'B', 14: 'B', 13: 'C', 12: 'C', 11: 'C', 10: 'C',
|
||||
9: 'D', 8: 'D', 7: 'D', 6: 'F', 5: 'D', 4: 'G', 3: 'G', 2: 'H', 1: 'H', 0: 'A'}
|
||||
radius_to_char_fine = CTkSettings.radius_to_char_fine # dict to map radius to font circle character
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -67,7 +67,7 @@ class CTkDrawEngine:
|
||||
if self._rendering_method == "polygon_shapes":
|
||||
return self._draw_rounded_rect_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius)
|
||||
elif self._rendering_method == "font_shapes":
|
||||
return self._draw_rounded_rect_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius, (), symmetric_circles=True)
|
||||
return self._draw_rounded_rect_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius, ())
|
||||
elif self._rendering_method == "circle_shapes":
|
||||
return self._draw_rounded_rect_with_border_circle_shapes(width, height, corner_radius, border_width, inner_corner_radius)
|
||||
|
||||
@ -125,33 +125,42 @@ class CTkDrawEngine:
|
||||
return requires_recoloring
|
||||
|
||||
def _draw_rounded_rect_with_border_font_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int,
|
||||
exclude_parts: tuple, symmetric_circles: bool = True) -> bool:
|
||||
exclude_parts: tuple) -> bool:
|
||||
requires_recoloring = False
|
||||
|
||||
# create border button parts
|
||||
if border_width > 0:
|
||||
if corner_radius > 0:
|
||||
# create canvas border corner parts if not already created
|
||||
if not self._canvas.find_withtag("border_oval_1_a"):
|
||||
# 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:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER)
|
||||
if symmetric_circles:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
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")
|
||||
|
||||
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"), anchor=tkinter.CENTER)
|
||||
if symmetric_circles:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
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):
|
||||
self._canvas.delete("border_oval_2_a", "border_oval_2_b")
|
||||
|
||||
if not self._canvas.find_withtag("border_oval_3_a") and round(corner_radius) * 2 < height:
|
||||
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:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER)
|
||||
if symmetric_circles:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts"), anchor=tkinter.CENTER)
|
||||
if symmetric_circles:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
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):
|
||||
self._canvas.delete("border_oval_3_a", "border_oval_3_b")
|
||||
|
||||
elif self._canvas.find_withtag("border_oval_3_a") and not round(corner_radius) * 2 < height:
|
||||
self._canvas.delete(["border_oval_3_a", "border_oval_3_b", "border_oval_4_a", "border_oval_4_b"])
|
||||
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"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
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):
|
||||
self._canvas.delete("border_oval_4_a", "border_oval_4_b")
|
||||
|
||||
# change position of border corner parts
|
||||
self._canvas.coords("border_oval_1_a", corner_radius, corner_radius, corner_radius)
|
||||
@ -182,33 +191,35 @@ class CTkDrawEngine:
|
||||
# create inner button parts
|
||||
if inner_corner_radius > 0:
|
||||
|
||||
# create canvas border corner parts if not already created
|
||||
# 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:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER)
|
||||
if symmetric_circles:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
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")
|
||||
|
||||
if not self._canvas.find_withtag("inner_oval_2_a") 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"), anchor=tkinter.CENTER)
|
||||
if symmetric_circles:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
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):
|
||||
self._canvas.delete("inner_oval_2_a", "inner_oval_2_b")
|
||||
|
||||
if not self._canvas.find_withtag("inner_oval_3_a") and round(inner_corner_radius) * 2 < height - (2 * border_width) and "inner_oval_3" not in exclude_parts:
|
||||
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:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER)
|
||||
if symmetric_circles:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("inner_oval_3_a") and not round(inner_corner_radius) * 2 < height - (2 * border_width):
|
||||
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):
|
||||
self._canvas.delete("inner_oval_3_a", "inner_oval_3_b")
|
||||
|
||||
if not self._canvas.find_withtag("inner_oval_4_a") and round(inner_corner_radius) * 2 < height - (2 * border_width) 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"), anchor=tkinter.CENTER)
|
||||
if symmetric_circles:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("inner_oval_4_a") and not round(inner_corner_radius) * 2 < height - (2 * border_width):
|
||||
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")
|
||||
|
||||
# change position of border corner parts
|
||||
|
@ -21,7 +21,7 @@ class CTkProgressBar(tkinter.Frame):
|
||||
progress_color="default_theme",
|
||||
corner_radius="default_theme",
|
||||
width=200,
|
||||
height=10,
|
||||
height=8,
|
||||
border_width="default_theme",
|
||||
**kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
343
customtkinter/customtkinter_radiobutton.py
Normal file
343
customtkinter/customtkinter_radiobutton.py
Normal file
@ -0,0 +1,343 @@
|
||||
import tkinter
|
||||
import sys
|
||||
|
||||
from customtkinter.customtkinter_tk import CTk
|
||||
from customtkinter.customtkinter_frame import CTkFrame
|
||||
from customtkinter.appearance_mode_tracker import AppearanceModeTracker
|
||||
from customtkinter.customtkinter_theme_manager import CTkThemeManager
|
||||
from customtkinter.customtkinter_canvas import CTkCanvas
|
||||
from customtkinter.customtkinter_settings import CTkSettings
|
||||
from customtkinter.customtkinter_draw_engine import CTkDrawEngine
|
||||
|
||||
|
||||
class CTkRadioButton(tkinter.Frame):
|
||||
def __init__(self, *args,
|
||||
bg_color=None,
|
||||
fg_color="default_theme",
|
||||
hover_color="default_theme",
|
||||
border_color="default_theme",
|
||||
border_width_unchecked="default_theme",
|
||||
border_width_checked="default_theme",
|
||||
width=22,
|
||||
height=22,
|
||||
corner_radius="default_theme",
|
||||
text_font="default_theme",
|
||||
text_color="default_theme",
|
||||
text="CTkRadioButton",
|
||||
hover=True,
|
||||
command=None,
|
||||
state=tkinter.NORMAL,
|
||||
value=0,
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
self.text = text
|
||||
self.text_color = CTkThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
|
||||
self.text_font = (CTkThemeManager.theme["text"]["font"], CTkThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font
|
||||
|
||||
self.function = command
|
||||
self.state = state
|
||||
self.hover = hover
|
||||
self.check_state = False
|
||||
self.value = value
|
||||
self.variable: tkinter.Variable = variable
|
||||
self.variable_callback_blocked = False
|
||||
self.textvariable = textvariable
|
||||
self.variable_callback_name = None
|
||||
|
||||
self.canvas = CTkCanvas(master=self,
|
||||
highlightthickness=0,
|
||||
width=self.width,
|
||||
height=self.height)
|
||||
self.canvas.pack(side='left')
|
||||
|
||||
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
|
||||
|
||||
if sys.platform == "darwin" and self.state == tkinter.NORMAL and CTkSettings.hand_cursor_enabled:
|
||||
self.canvas.configure(cursor="pointinghand")
|
||||
elif sys.platform.startswith("win") and self.state == tkinter.NORMAL and CTkSettings.hand_cursor_enabled:
|
||||
self.canvas.configure(cursor="hand2")
|
||||
|
||||
if self.hover is True:
|
||||
self.canvas.bind("<Enter>", self.on_enter)
|
||||
self.canvas.bind("<Leave>", self.on_leave)
|
||||
|
||||
self.canvas.bind("<Button-1>", self.invoke)
|
||||
self.canvas.bind("<Button-1>", self.invoke)
|
||||
|
||||
self.text_label = None
|
||||
|
||||
self.draw() # initial draw
|
||||
|
||||
if self.variable is not None:
|
||||
self.variable_callback_name = self.variable.trace_add("write", self.variable_callback)
|
||||
if self.variable.get() == self.value:
|
||||
self.select(from_variable_callback=True)
|
||||
else:
|
||||
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):
|
||||
if isinstance(self.master, CTkFrame):
|
||||
return self.master.fg_color
|
||||
else:
|
||||
return self.master.cget("bg")
|
||||
|
||||
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.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||
self.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||
|
||||
if self.check_state is False:
|
||||
self.canvas.itemconfig("border_parts",
|
||||
outline=CTkThemeManager.single_color(self.border_color, self.appearance_mode),
|
||||
fill=CTkThemeManager.single_color(self.border_color, self.appearance_mode))
|
||||
else:
|
||||
self.canvas.itemconfig("border_parts",
|
||||
outline=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
|
||||
fill=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
|
||||
|
||||
self.canvas.itemconfig("inner_parts",
|
||||
outline=CTkThemeManager.single_color(self.bg_color, self.appearance_mode),
|
||||
fill=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||
|
||||
if self.text_label is None:
|
||||
self.text_label = tkinter.Label(master=self,
|
||||
text=self.text,
|
||||
justify=tkinter.LEFT,
|
||||
width=len(self.text_color),
|
||||
font=self.text_font)
|
||||
self.text_label.pack(side='right', padx=6)
|
||||
self.text_label["anchor"] = "w"
|
||||
|
||||
self.text_label.configure(fg=CTkThemeManager.single_color(self.text_color, self.appearance_mode))
|
||||
self.text_label.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||
|
||||
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()
|
||||
|
||||
if "text" in kwargs:
|
||||
self.set_text(kwargs["text"])
|
||||
del kwargs["text"]
|
||||
|
||||
if "state" in kwargs:
|
||||
self.set_state(kwargs["state"])
|
||||
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 "hover_color" in kwargs:
|
||||
self.hover_color = kwargs["hover_color"]
|
||||
require_redraw = True
|
||||
del kwargs["hover_color"]
|
||||
|
||||
if "text_color" in kwargs:
|
||||
self.text_color = kwargs["text_color"]
|
||||
require_redraw = True
|
||||
del kwargs["text_color"]
|
||||
|
||||
if "border_color" in kwargs:
|
||||
self.border_color = kwargs["border_color"]
|
||||
require_redraw = True
|
||||
del kwargs["border_color"]
|
||||
|
||||
if "border_width" in kwargs:
|
||||
self.border_width = kwargs["border_width"]
|
||||
require_redraw = True
|
||||
del kwargs["border_width"]
|
||||
|
||||
if "command" in kwargs:
|
||||
self.function = kwargs["command"]
|
||||
del kwargs["command"]
|
||||
|
||||
if "variable" in kwargs:
|
||||
if self.variable is not None:
|
||||
self.variable.trace_remove("write", self.variable_callback_name)
|
||||
|
||||
self.variable = kwargs["variable"]
|
||||
|
||||
if self.variable is not None and self.variable != "":
|
||||
self.variable_callback_name = self.variable.trace_add("write", self.variable_callback)
|
||||
if self.variable.get() == self.value:
|
||||
self.select(from_variable_callback=True)
|
||||
else:
|
||||
self.deselect(from_variable_callback=True)
|
||||
else:
|
||||
self.variable = None
|
||||
|
||||
del kwargs["variable"]
|
||||
|
||||
super().configure(*args, **kwargs)
|
||||
|
||||
if require_redraw:
|
||||
self.draw()
|
||||
|
||||
def set_state(self, state):
|
||||
self.state = state
|
||||
|
||||
if self.state == tkinter.DISABLED:
|
||||
self.hover = False
|
||||
if sys.platform == "darwin" and CTkSettings.hand_cursor_enabled:
|
||||
self.canvas.configure(cursor="arrow")
|
||||
elif sys.platform.startswith("sys") and CTkSettings.hand_cursor_enabled:
|
||||
self.canvas.configure(cursor="arrow")
|
||||
|
||||
elif self.state == tkinter.NORMAL:
|
||||
self.hover = True
|
||||
if sys.platform == "darwin" and CTkSettings.hand_cursor_enabled:
|
||||
self.canvas.configure(cursor="pointinghand")
|
||||
elif sys.platform.startswith("sys") and CTkSettings.hand_cursor_enabled:
|
||||
self.canvas.configure(cursor="hand2")
|
||||
|
||||
self.draw()
|
||||
|
||||
def set_text(self, text):
|
||||
self.text = text
|
||||
if self.text_label is not None:
|
||||
self.text_label.configure(text=self.text, width=len(self.text))
|
||||
else:
|
||||
sys.stderr.write("ERROR (CTkButton): Cant change text because button has no text.")
|
||||
|
||||
def on_enter(self, event=0):
|
||||
if self.hover is True:
|
||||
self.canvas.itemconfig("border_parts",
|
||||
fill=CTkThemeManager.single_color(self.hover_color, self.appearance_mode),
|
||||
outline=CTkThemeManager.single_color(self.hover_color, self.appearance_mode))
|
||||
|
||||
def on_leave(self, event=0):
|
||||
if self.hover is True:
|
||||
if self.check_state is True:
|
||||
self.canvas.itemconfig("border_parts",
|
||||
fill=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
|
||||
outline=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
|
||||
else:
|
||||
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))
|
||||
|
||||
def variable_callback(self, var_name, index, mode):
|
||||
if not self.variable_callback_blocked:
|
||||
if self.variable.get() == self.value:
|
||||
self.select(from_variable_callback=True)
|
||||
else:
|
||||
self.deselect(from_variable_callback=True)
|
||||
|
||||
def invoke(self, event=0):
|
||||
if self.function is not None:
|
||||
self.function()
|
||||
|
||||
if self.state == tkinter.NORMAL:
|
||||
if self.check_state is False:
|
||||
self.check_state = True
|
||||
self.select()
|
||||
|
||||
def select(self, from_variable_callback=False):
|
||||
self.check_state = True
|
||||
self.canvas.itemconfig("border_parts",
|
||||
fill=CTkThemeManager.single_color(self.fg_color, self.appearance_mode),
|
||||
outline=CTkThemeManager.single_color(self.fg_color, self.appearance_mode))
|
||||
self.border_width = self.border_width_checked
|
||||
self.draw()
|
||||
|
||||
if self.variable is not None and not from_variable_callback:
|
||||
self.variable_callback_blocked = True
|
||||
self.variable.set(self.value)
|
||||
self.variable_callback_blocked = False
|
||||
|
||||
def deselect(self, from_variable_callback=False):
|
||||
self.check_state = False
|
||||
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.border_width = self.border_width_unchecked
|
||||
self.draw()
|
||||
|
||||
if self.variable is not None and not from_variable_callback:
|
||||
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()
|
@ -1,4 +1,5 @@
|
||||
import sys
|
||||
import platform
|
||||
|
||||
|
||||
class CTkSettings:
|
||||
@ -8,6 +9,28 @@ class CTkSettings:
|
||||
hand_cursor_enabled = True
|
||||
preferred_drawing_method = None
|
||||
|
||||
radius_to_char_fine = None
|
||||
|
||||
@classmethod
|
||||
def init_font_character_mapping(cls):
|
||||
radius_to_char_warped = {19: 'B', 18: 'B', 17: 'B', 16: 'B', 15: 'B', 14: 'B', 13: 'B', 12: 'B', 11: 'B', 10: 'B',
|
||||
9: 'C', 8: 'D', 7: 'C', 6: 'E', 5: 'F', 4: 'G', 3: 'H', 2: 'H', 1: 'H', 0: 'A'}
|
||||
|
||||
radius_to_char_fine_windows_10 = {19: 'A', 18: 'A', 17: 'B', 16: 'B', 15: 'B', 14: 'B', 13: 'C', 12: 'C', 11: 'C', 10: 'C',
|
||||
9: 'D', 8: 'D', 7: 'D', 6: 'F', 5: 'D', 4: 'G', 3: 'G', 2: 'H', 1: 'H', 0: 'A'}
|
||||
|
||||
radius_to_char_fine_windows_11 = {19: 'A', 18: 'A', 17: 'B', 16: 'B', 15: 'B', 14: 'B', 13: 'C', 12: 'C', 11: 'D', 10: 'D',
|
||||
9: 'D', 8: 'F', 7: 'C', 6: 'I', 5: 'E', 4: 'G', 3: 'P', 2: 'R', 1: 'R', 0: 'A'}
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
if sys.getwindowsversion().build > 20000: # Windows 11
|
||||
cls.radius_to_char_fine = radius_to_char_fine_windows_11
|
||||
else: # < Windows 11
|
||||
cls.radius_to_char_fine = radius_to_char_fine_windows_10
|
||||
else: # macOS and Linux
|
||||
cls.radius_to_char_fine = radius_to_char_fine_windows_10
|
||||
|
||||
|
||||
@classmethod
|
||||
def init_drawing_method(cls):
|
||||
""" possible: 'polygon_shapes', 'font_shapes', 'circle_shapes' """
|
||||
@ -17,5 +40,15 @@ class CTkSettings:
|
||||
else:
|
||||
cls.preferred_drawing_method = "font_shapes"
|
||||
|
||||
@classmethod
|
||||
def print_settings(cls):
|
||||
print(f"CTkSettings current values:")
|
||||
print(f"scaling_factor = {cls.scaling_factor}")
|
||||
print(f"circle_font_is_ready = {cls.circle_font_is_ready}")
|
||||
print(f"hand_cursor_enabled = {cls.hand_cursor_enabled}")
|
||||
print(f"preferred_drawing_method = {cls.preferred_drawing_method}")
|
||||
print(f"radius_to_char_fine = {cls.radius_to_char_fine}")
|
||||
|
||||
|
||||
CTkSettings.init_font_character_mapping()
|
||||
CTkSettings.init_drawing_method()
|
||||
|
@ -9,15 +9,14 @@ customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "gre
|
||||
|
||||
class App(customtkinter.CTk):
|
||||
|
||||
APP_NAME = "CustomTkinter complex example"
|
||||
WIDTH = 700
|
||||
HEIGHT = 500
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.title(App.APP_NAME)
|
||||
self.geometry(str(App.WIDTH) + "x" + str(App.HEIGHT))
|
||||
self.title("CustomTkinter complex example")
|
||||
self.geometry(f"{App.WIDTH}x{App.HEIGHT}")
|
||||
self.minsize(App.WIDTH, App.HEIGHT)
|
||||
|
||||
self.protocol("WM_DELETE_WINDOW", self.on_closing)
|
||||
@ -26,17 +25,14 @@ class App(customtkinter.CTk):
|
||||
self.bind("<Command-w>", self.on_closing)
|
||||
self.createcommand('tk::mac::Quit', self.on_closing)
|
||||
|
||||
# ============ create two CTkFrames ============
|
||||
# ============ create two frames ============
|
||||
|
||||
self.frame_left = customtkinter.CTkFrame(master=self,
|
||||
width=180,
|
||||
corner_radius=0)
|
||||
self.frame_left.grid(row=0, column=0, sticky="nswe")
|
||||
|
||||
self.frame_right = customtkinter.CTkFrame(master=self,
|
||||
width=420,
|
||||
height=App.HEIGHT-40,
|
||||
corner_radius=12)
|
||||
self.frame_right = customtkinter.CTkFrame(master=self)
|
||||
self.frame_right.grid(row=0, column=1, sticky="nswe", padx=20, pady=20)
|
||||
|
||||
self.grid_columnconfigure(1, weight=1)
|
||||
@ -50,29 +46,25 @@ class App(customtkinter.CTk):
|
||||
|
||||
self.label_1 = customtkinter.CTkLabel(master=self.frame_left,
|
||||
text="CustomTkinter",
|
||||
text_font=("Roboto Medium", -16), # font name and size in px
|
||||
fg_color=None)
|
||||
text_font=("Roboto Medium", -16)) # font name and size in px
|
||||
self.label_1.grid(row=1, column=0, pady=10, padx=10)
|
||||
|
||||
self.button_1 = customtkinter.CTkButton(master=self.frame_left,
|
||||
text="CTkButton 1",
|
||||
command=self.button_event,
|
||||
fg_color=None,
|
||||
border_width=2)
|
||||
fg_color=("gray75", "gray30")) # <- custom tuple-color
|
||||
self.button_1.grid(row=2, column=0, pady=10, padx=20)
|
||||
|
||||
self.button_2 = customtkinter.CTkButton(master=self.frame_left,
|
||||
text="CTkButton 2",
|
||||
command=self.button_event,
|
||||
fg_color=None,
|
||||
border_width=2)
|
||||
fg_color=("gray75", "gray30")) # <- custom tuple-color
|
||||
self.button_2.grid(row=3, column=0, pady=10, padx=20)
|
||||
|
||||
self.button_3 = customtkinter.CTkButton(master=self.frame_left,
|
||||
text="CTkButton 3",
|
||||
command=self.button_event,
|
||||
fg_color=None,
|
||||
border_width=2)
|
||||
fg_color=("gray75", "gray30")) # <- custom tuple-color
|
||||
self.button_3.grid(row=4, column=0, pady=10, padx=20)
|
||||
|
||||
self.check_box_1 = customtkinter.CTkCheckBox(master=self.frame_left,
|
||||
@ -86,17 +78,19 @@ class App(customtkinter.CTk):
|
||||
|
||||
# ============ frame_right ============
|
||||
|
||||
self.frame_right.rowconfigure(0, weight=1)
|
||||
self.frame_right.rowconfigure(3, weight=1)
|
||||
for i in [0, 1, 2, 3]:
|
||||
self.frame_right.rowconfigure(i, weight=1)
|
||||
self.frame_right.rowconfigure(6, weight=10)
|
||||
self.frame_right.columnconfigure(0, weight=1)
|
||||
|
||||
self.frame_info = customtkinter.CTkFrame(master=self.frame_right,
|
||||
width=380,
|
||||
height=200)
|
||||
self.frame_info.grid(row=0, column=0, columnspan=3, pady=20, padx=20, sticky="wens")
|
||||
self.frame_info = customtkinter.CTkFrame(master=self.frame_right)
|
||||
self.frame_info.grid(row=0, column=0, columnspan=2, rowspan=4, pady=20, padx=20, sticky="wens")
|
||||
|
||||
# ============ frame_right -> frame_info ============
|
||||
|
||||
self.frame_info.rowconfigure(0, weight=1)
|
||||
self.frame_info.columnconfigure(0, weight=1)
|
||||
|
||||
self.label_info_1 = customtkinter.CTkLabel(master=self.frame_info,
|
||||
text="CTkLabel: Lorem ipsum dolor sit,\n" +
|
||||
"amet consetetur sadipscing elitr,\n" +
|
||||
@ -106,52 +100,71 @@ class App(customtkinter.CTk):
|
||||
height=100,
|
||||
fg_color=("white", "gray38"), # <- custom tuple-color
|
||||
justify=tkinter.LEFT)
|
||||
self.label_info_1.place(relx=0.5, rely=0.15, anchor=tkinter.N)
|
||||
self.label_info_1.grid(column=0, row=0, sticky="nwe", padx=15, pady=15)
|
||||
|
||||
self.progressbar = customtkinter.CTkProgressBar(master=self.frame_info, width=240)
|
||||
self.progressbar.place(relx=0.5, rely=0.85, anchor=tkinter.S)
|
||||
self.progressbar.grid(row=1, column=0, sticky="ew", padx=15, pady=15)
|
||||
|
||||
# ============ frame_right <- ============
|
||||
self.radio_var = tkinter.IntVar(value=0)
|
||||
|
||||
self.label_radio_group = customtkinter.CTkLabel(master=self.frame_right,
|
||||
fg_color=("white", "gray30"), # <- custom tuple-color
|
||||
text="CTkRadioButton Group:")
|
||||
self.label_radio_group.grid(row=0, column=2, columnspan=1, pady=0, padx=20, sticky="wes")
|
||||
|
||||
self.radio_button_1 = customtkinter.CTkRadioButton(master=self.frame_right,
|
||||
variable=self.radio_var,
|
||||
value=0)
|
||||
self.radio_button_1.grid(row=1, column=2, pady=10, padx=20, sticky="")
|
||||
|
||||
self.radio_button_2 = customtkinter.CTkRadioButton(master=self.frame_right,
|
||||
variable=self.radio_var,
|
||||
value=1)
|
||||
self.radio_button_2.grid(row=2, column=2, pady=10, padx=20, sticky="")
|
||||
|
||||
self.radio_button_3 = customtkinter.CTkRadioButton(master=self.frame_right,
|
||||
variable=self.radio_var,
|
||||
value=2)
|
||||
self.radio_button_3.grid(row=3, column=2, pady=10, padx=20, sticky="")
|
||||
|
||||
#self.radio_button_1.select()
|
||||
#self.radio_button_1.deselect()
|
||||
|
||||
self.slider_1 = customtkinter.CTkSlider(master=self.frame_right,
|
||||
from_=1,
|
||||
to=0,
|
||||
number_of_steps=3,
|
||||
command=self.progressbar.set)
|
||||
self.slider_1.grid(row=1, column=0, columnspan=2, pady=10, padx=20, sticky="we")
|
||||
self.slider_1.set(0.5)
|
||||
self.slider_1.grid(row=4, column=0, columnspan=2, pady=10, padx=20, sticky="we")
|
||||
self.slider_1.set(0.7)
|
||||
|
||||
self.slider_2 = customtkinter.CTkSlider(master=self.frame_right,
|
||||
width=160,
|
||||
command=self.progressbar.set)
|
||||
self.slider_2.grid(row=2, column=0, columnspan=2, pady=10, padx=20, sticky="we")
|
||||
self.slider_2.grid(row=5, column=0, columnspan=2, pady=10, padx=20, sticky="we")
|
||||
self.slider_2.set(0.7)
|
||||
|
||||
self.label_info_2 = customtkinter.CTkLabel(master=self.frame_right,
|
||||
text="CTkLabel: Lorem ipsum",
|
||||
fg_color=None,
|
||||
width=180,
|
||||
height=20,
|
||||
justify=tkinter.CENTER)
|
||||
self.label_info_2.grid(row=1, column=2, columnspan=1, pady=10, padx=20, sticky="we")
|
||||
|
||||
self.button_4 = customtkinter.CTkButton(master=self.frame_right,
|
||||
self.slider_button_1 = customtkinter.CTkButton(master=self.frame_right,
|
||||
height=25,
|
||||
text="CTkButton",
|
||||
command=self.button_event)
|
||||
self.button_4.grid(row=2, column=2, columnspan=1, pady=10, padx=20, sticky="we")
|
||||
self.slider_button_1.grid(row=4, column=2, columnspan=1, pady=10, padx=20, sticky="we")
|
||||
|
||||
self.slider_button_2 = customtkinter.CTkButton(master=self.frame_right,
|
||||
height=25,
|
||||
text="CTkButton",
|
||||
command=self.button_event)
|
||||
self.slider_button_2.grid(row=5, column=2, columnspan=1, pady=10, padx=20, sticky="we")
|
||||
|
||||
self.entry = customtkinter.CTkEntry(master=self.frame_right,
|
||||
width=120,
|
||||
height=30,
|
||||
placeholder_text="CTkEntry")
|
||||
self.entry.grid(row=4, column=0, columnspan=2, pady=20, padx=20, sticky="we")
|
||||
self.entry.grid(row=7, column=0, columnspan=2, pady=20, padx=20, sticky="we")
|
||||
|
||||
self.button_5 = customtkinter.CTkButton(master=self.frame_right,
|
||||
height=30,
|
||||
text="CTkButton",
|
||||
command=self.button_event)
|
||||
self.button_5.grid(row=4, column=2, columnspan=1, pady=20, padx=20, sticky="we")
|
||||
self.button_5.grid(row=7, column=2, columnspan=1, pady=20, padx=20, sticky="we")
|
||||
|
||||
self.progressbar.set(0.5)
|
||||
|
||||
|
@ -42,8 +42,8 @@ class App(customtkinter.CTk):
|
||||
corner_radius=0)
|
||||
self.frame.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
|
||||
|
||||
self.label_1 = customtkinter.CTkLabel(master=self.frame, corner_radius=6, width=200, height=60,
|
||||
fg_color=("gray70", "gray20"), text="CustomTkinter\ninterface example")
|
||||
self.label_1 = customtkinter.CTkLabel(master=self.frame, width=200, height=60,
|
||||
fg_color=("gray70", "gray35"), text="CustomTkinter\ninterface example")
|
||||
self.label_1.place(relx=0.5, rely=0.3, anchor=tkinter.CENTER)
|
||||
|
||||
self.entry_1 = customtkinter.CTkEntry(master=self.frame, corner_radius=20, width=200, placeholder_text="username")
|
||||
|
@ -5,7 +5,7 @@ customtkinter.set_default_color_theme("blue")
|
||||
customtkinter.set_appearance_mode("dark")
|
||||
|
||||
app = customtkinter.CTk()
|
||||
app.geometry("600x800")
|
||||
app.geometry("600x1000")
|
||||
|
||||
app.grid_columnconfigure(0, weight=1)
|
||||
app.grid_columnconfigure(1, weight=1)
|
||||
@ -28,7 +28,7 @@ f4 = customtkinter.CTkFrame(app, fg_color="gray90", corner_radius=0)
|
||||
f4.grid(row=0, column=3, rowspan=1, columnspan=1, sticky="nsew")
|
||||
f4.grid_columnconfigure(0, weight=1)
|
||||
|
||||
for i in range(0, 18, 1):
|
||||
for i in range(0, 21, 1):
|
||||
b = customtkinter.CTkButton(f1, corner_radius=i, height=34, border_width=2, text=f"{i} {i-2}",
|
||||
border_color="white", fg_color=None, text_color="white")
|
||||
b.grid(row=i, column=0, pady=5, padx=15, sticky="nsew")
|
||||
@ -45,4 +45,5 @@ for i in range(0, 18, 1):
|
||||
border_color="gray10", fg_color="#228da8")
|
||||
b.grid(row=i, column=0, pady=5, padx=15, sticky="nsew")
|
||||
|
||||
customtkinter.CTkSettings.print_settings()
|
||||
app.mainloop()
|
Loading…
Reference in New Issue
Block a user