added scaling to every widget

This commit is contained in:
Tom Schimansky 2022-04-21 18:34:58 +02:00
parent 59a8574f4c
commit 6342bf8034
15 changed files with 193 additions and 98 deletions

View File

@ -14,9 +14,10 @@ from .widgets.ctk_canvas import CTkCanvas
from .widgets.ctk_switch import CTkSwitch from .widgets.ctk_switch import CTkSwitch
from .widgets.ctk_toplevel import CTkToplevel from .widgets.ctk_toplevel import CTkToplevel
from .customtkinter_settings import CTkSettings from .ctk_settings import CTkSettings
from .appearance_mode_tracker import AppearanceModeTracker from .appearance_mode_tracker import AppearanceModeTracker
from .theme_manager import CTkThemeManager from .theme_manager import CTkThemeManager
from .scaling_tracker import ScalingTracker
from distutils.version import StrictVersion as Version from distutils.version import StrictVersion as Version
import tkinter import tkinter

View File

@ -2,9 +2,9 @@ import tkinter
import sys import sys
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -65,8 +65,8 @@ class CTkButton(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew") self.canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
@ -75,9 +75,7 @@ class CTkButton(CTkBaseClass):
self.canvas.bind("<Leave>", self.on_leave) self.canvas.bind("<Leave>", self.on_leave)
self.canvas.bind("<Button-1>", self.clicked) self.canvas.bind("<Button-1>", self.clicked)
self.canvas.bind("<Button-1>", self.clicked) self.canvas.bind("<Button-1>", self.clicked)
self.bind('<Configure>', self.update_dimensions_event)
# 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.set_cursor()
self.draw() # initial draw self.draw() # initial draw
@ -90,7 +88,10 @@ class CTkButton(CTkBaseClass):
self.grid_columnconfigure(1, weight=1) self.grid_columnconfigure(1, weight=1)
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width) requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width * self.scaling,
self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling)
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
@ -115,7 +116,9 @@ class CTkButton(CTkBaseClass):
if self.text is not None and self.text != "": if self.text is not None and self.text != "":
if self.text_label is None: if self.text_label is None:
self.text_label = tkinter.Label(master=self, font=self.text_font, textvariable=self.textvariable) self.text_label = tkinter.Label(master=self,
font=self.apply_font_scaling(self.text_font),
textvariable=self.textvariable)
self.text_label.bind("<Enter>", self.on_enter) self.text_label.bind("<Enter>", self.on_enter)
self.text_label.bind("<Leave>", self.on_leave) self.text_label.bind("<Leave>", self.on_leave)
@ -172,26 +175,35 @@ class CTkButton(CTkBaseClass):
# create grid layout with just an image given # create grid layout with just an image given
if self.image_label is not None and self.text_label is None: if self.image_label is not None and self.text_label is None:
self.image_label.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="", pady=self.border_width) self.image_label.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="", pady=self.border_width * self.scaling)
# create grid layout with just text given # create grid layout with just text given
if self.image_label is None and self.text_label is not None: if self.image_label is None and self.text_label is not None:
self.text_label.grid(row=0, column=0, padx=self.corner_radius, pady=self.border_width, rowspan=2, columnspan=2, sticky="") self.text_label.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="",
padx=self.corner_radius * self.scaling, pady=self.border_width * self.scaling)
# create grid layout of image and text label in 2x2 grid system with given compound # create grid layout of image and text label in 2x2 grid system with given compound
if self.image_label is not None and self.text_label is not None: if self.image_label is not None and self.text_label is not None:
if self.compound == tkinter.LEFT or self.compound == "left": if self.compound == tkinter.LEFT or self.compound == "left":
self.image_label.grid(row=0, column=0, padx=(max(self.corner_radius, self.border_width), 2), sticky="e", rowspan=2, columnspan=1, pady=self.border_width) self.image_label.grid(row=0, column=0, sticky="e", rowspan=2, columnspan=1,
self.text_label.grid(row=0, column=1, padx=(2, max(self.corner_radius, self.border_width)), sticky="w", rowspan=2, columnspan=1, pady=self.border_width) padx=(max(self.corner_radius * self.scaling, self.border_width * self.scaling), 2), pady=self.border_width * self.scaling)
self.text_label.grid(row=0, column=1, sticky="w", rowspan=2, columnspan=1,
padx=(2, max(self.corner_radius * self.scaling, self.border_width * self.scaling)), pady=self.border_width * self.scaling)
elif self.compound == tkinter.TOP or self.compound == "top": elif self.compound == tkinter.TOP or self.compound == "top":
self.image_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)) self.image_label.grid(row=0, column=0, sticky="s", columnspan=2, rowspan=1,
self.text_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)) padx=max(self.corner_radius * self.scaling, self.border_width * self.scaling), pady=(self.border_width * self.scaling, 2))
self.text_label.grid(row=1, column=0, sticky="n", columnspan=2, rowspan=1,
padx=max(self.corner_radius * self.scaling, self.border_width * self.scaling), pady=(2, self.border_width * self.scaling))
elif self.compound == tkinter.RIGHT or self.compound == "right": elif self.compound == tkinter.RIGHT or self.compound == "right":
self.image_label.grid(row=0, column=1, padx=(2, max(self.corner_radius, self.border_width)), sticky="w", rowspan=2, columnspan=1, pady=self.border_width) self.image_label.grid(row=0, column=1, sticky="w", rowspan=2, columnspan=1,
self.text_label.grid(row=0, column=0, padx=(max(self.corner_radius, self.border_width), 2), sticky="e", rowspan=2, columnspan=1, pady=self.border_width) padx=(2, max(self.corner_radius * self.scaling, self.border_width * self.scaling)), pady=self.border_width * self.scaling)
self.text_label.grid(row=0, column=0, sticky="e", rowspan=2, columnspan=1,
padx=(max(self.corner_radius * self.scaling, self.border_width * self.scaling), 2), pady=self.border_width * self.scaling)
elif self.compound == tkinter.BOTTOM or self.compound == "bottom": elif self.compound == tkinter.BOTTOM or self.compound == "bottom":
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.image_label.grid(row=1, column=0, sticky="n", columnspan=2, rowspan=1,
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)) padx=max(self.corner_radius * self.scaling, self.border_width * self.scaling), pady=(2, self.border_width * self.scaling))
self.text_label.grid(row=0, column=0, sticky="s", columnspan=2, rowspan=1,
padx=max(self.corner_radius * self.scaling, self.border_width * self.scaling), pady=(self.border_width * self.scaling, 2))
def configure(self, *args, **kwargs): def configure(self, *args, **kwargs):
require_redraw = False # some attribute changes require a call of self.draw() at the end require_redraw = False # some attribute changes require a call of self.draw() at the end

View File

@ -1,5 +1,5 @@
import tkinter import tkinter
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
class CTkCanvas(tkinter.Canvas): class CTkCanvas(tkinter.Canvas):

View File

@ -2,9 +2,9 @@ import tkinter
import sys import sys
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -68,14 +68,15 @@ class CTkCheckBox(CTkBaseClass):
# configure grid system (1x3) # configure grid system (1x3)
self.grid_columnconfigure(0, weight=0) self.grid_columnconfigure(0, weight=0)
self.grid_columnconfigure(1, weight=0, minsize=6) self.grid_columnconfigure(1, weight=0, minsize=6 * self.scaling)
self.grid_columnconfigure(2, weight=1) self.grid_columnconfigure(2, weight=1)
self.grid_rowconfigure(0, weight=1)
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1) self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, rowspan=1)
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
if self.hover is True: if self.hover is True:
@ -103,15 +104,20 @@ class CTkCheckBox(CTkBaseClass):
super().destroy() super().destroy()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width) requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width * self.scaling,
self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling)
if self.check_state is True: if self.check_state is True:
self.draw_engine.draw_checkmark(self.width, self.height, self.height * 0.58) self.draw_engine.draw_checkmark(self.width * self.scaling,
self.height * self.scaling,
self.height * 0.58 * self.scaling)
else: else:
self.canvas.delete("checkmark") self.canvas.delete("checkmark")
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)) 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))
if self.check_state is True: if self.check_state is True:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",
@ -134,11 +140,12 @@ class CTkCheckBox(CTkBaseClass):
fill=CTkThemeManager.single_color(self.border_color, self.appearance_mode)) fill=CTkThemeManager.single_color(self.border_color, self.appearance_mode))
if self.text_label is None: if self.text_label is None:
print("create label")
self.text_label = tkinter.Label(master=self, self.text_label = tkinter.Label(master=self,
bd=0, bd=0,
text=self.text, text=self.text,
justify=tkinter.LEFT, justify=tkinter.LEFT,
font=self.text_font) font=self.apply_font_scaling(self.text_font))
self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w")
self.text_label["anchor"] = "w" self.text_label["anchor"] = "w"

View File

@ -1,9 +1,9 @@
import tkinter import tkinter
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -46,8 +46,8 @@ class CTkEntry(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.grid(column=0, row=0, sticky="we") self.canvas.grid(column=0, row=0, sticky="we")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
@ -55,11 +55,12 @@ class CTkEntry(CTkBaseClass):
bd=0, bd=0,
width=1, width=1,
highlightthickness=0, highlightthickness=0,
font=self.text_font, font=self.apply_font_scaling(self.text_font),
**kwargs) **kwargs)
self.entry.grid(column=0, row=0, sticky="we", padx=self.corner_radius if self.corner_radius >= 6 else 6) self.entry.grid(column=0, row=0, sticky="we",
padx=self.corner_radius * self.scaling if self.corner_radius >= 6 * self.scaling else 6 * self.scaling)
super().bind('<Configure>', self.update_dimensions) super().bind('<Configure>', self.update_dimensions_event)
self.entry.bind('<FocusOut>', self.set_placeholder) self.entry.bind('<FocusOut>', self.set_placeholder)
self.entry.bind('<FocusIn>', self.clear_placeholder) self.entry.bind('<FocusIn>', self.clear_placeholder)
@ -90,7 +91,10 @@ class CTkEntry(CTkBaseClass):
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode)) 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) requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width * self.scaling,
self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling)
if CTkThemeManager.single_color(self.fg_color, self.appearance_mode) is not None: if CTkThemeManager.single_color(self.fg_color, self.appearance_mode) is not None:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",

View File

@ -1,9 +1,9 @@
import tkinter import tkinter
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -41,13 +41,13 @@ class CTkFrame(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.place(x=0, y=0, relwidth=1, relheight=1) self.canvas.place(x=0, y=0, relwidth=1, relheight=1)
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
self.bind('<Configure>', self.update_dimensions) self.bind('<Configure>', self.update_dimensions_event)
self.draw() self.draw()
@ -64,7 +64,10 @@ class CTkFrame(CTkBaseClass):
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width) requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width * self.scaling,
self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling)
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
self.canvas.itemconfig("inner_parts", self.canvas.itemconfig("inner_parts",

View File

@ -1,9 +1,9 @@
import tkinter import tkinter
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -45,8 +45,8 @@ class CTkLabel(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.grid(row=0, column=0, sticky="nswe") self.canvas.grid(row=0, column=0, sticky="nswe")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
@ -54,15 +54,18 @@ class CTkLabel(CTkBaseClass):
highlightthickness=0, highlightthickness=0,
bd=0, bd=0,
text=self.text, text=self.text,
font=self.text_font, font=self.apply_font_scaling(self.text_font),
**kwargs) **kwargs)
self.text_label.grid(row=0, column=0, padx=self.corner_radius) self.text_label.grid(row=0, column=0, padx=self.corner_radius)
self.bind('<Configure>', self.update_dimensions) self.bind('<Configure>', self.update_dimensions_event)
self.draw() self.draw()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, 0) requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width * self.scaling,
self.height * self.scaling,
self.corner_radius * self.scaling,
0)
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))

View File

@ -1,9 +1,9 @@
import tkinter import tkinter
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -40,15 +40,18 @@ class CTkProgressBar(CTkBaseClass):
self.border_width = CTkThemeManager.theme["shape"]["progressbar_border_width"] if border_width == "default_theme" else border_width self.border_width = CTkThemeManager.theme["shape"]["progressbar_border_width"] if border_width == "default_theme" else border_width
self.value = 0.5 self.value = 0.5
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(1, weight=1)
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.place(x=0, y=0, relwidth=1, relheight=1) self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1)
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) 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 # 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.bind('<Configure>', self.update_dimensions_event)
self.draw() # initial draw self.draw() # initial draw
@ -65,8 +68,13 @@ class CTkProgressBar(CTkBaseClass):
super().destroy() super().destroy()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
print("progress", self.scaling)
requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.width, self.height, self.corner_radius, self.border_width, self.value, "w") requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.width * self.scaling,
self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling,
self.value, "w")
if no_color_updates is False 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)) self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))

View File

@ -2,9 +2,9 @@ import tkinter
import sys import sys
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -65,13 +65,13 @@ class CTkRadioButton(CTkBaseClass):
# configure grid system (3x1) # configure grid system (3x1)
self.grid_columnconfigure(0, weight=0) self.grid_columnconfigure(0, weight=0)
self.grid_columnconfigure(1, weight=0, minsize=6) self.grid_columnconfigure(1, weight=0, minsize=6 * self.scaling)
self.grid_columnconfigure(2, weight=1) self.grid_columnconfigure(2, weight=1)
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1) self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1)
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
@ -97,7 +97,10 @@ class CTkRadioButton(CTkBaseClass):
super().destroy() super().destroy()
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width, self.height, self.corner_radius, self.border_width) requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.width * self.scaling,
self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling)
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode)) self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
self.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode)) self.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
@ -120,7 +123,7 @@ class CTkRadioButton(CTkBaseClass):
bd=0, bd=0,
text=self.text, text=self.text,
justify=tkinter.LEFT, justify=tkinter.LEFT,
font=self.text_font) font=self.apply_font_scaling(self.text_font))
self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w")
self.text_label["anchor"] = "w" self.text_label["anchor"] = "w"

View File

@ -2,9 +2,9 @@ import tkinter
import sys import sys
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -64,8 +64,8 @@ class CTkSlider(CTkBaseClass):
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.grid(column=0, row=0, sticky="nswe") self.canvas.grid(column=0, row=0, sticky="nswe")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
@ -75,7 +75,7 @@ class CTkSlider(CTkBaseClass):
self.canvas.bind("<B1-Motion>", self.clicked) self.canvas.bind("<B1-Motion>", self.clicked)
# Each time an item is resized due to pack position mode, the binding Configure is called on the widget # 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.bind('<Configure>', self.update_dimensions_event)
self.set_cursor() self.set_cursor()
self.draw() # initial draw self.draw() # initial draw
@ -104,8 +104,13 @@ class CTkSlider(CTkBaseClass):
self.configure(cursor="hand2") self.configure(cursor="hand2")
def draw(self, no_color_updates=False): 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, requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.width * self.scaling,
self.button_length, self.button_corner_radius, self.value, "w") self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling,
self.button_length * self.scaling,
self.button_corner_radius * self.scaling,
self.value, "w")
if no_color_updates is False 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)) self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
@ -131,7 +136,7 @@ class CTkSlider(CTkBaseClass):
outline=CTkThemeManager.single_color(self.button_color, self.appearance_mode)) outline=CTkThemeManager.single_color(self.button_color, self.appearance_mode))
def clicked(self, event=None): def clicked(self, event=None):
self.value = event.x / self.width self.value = (event.x / self.width) / self.scaling
if self.value > 1: if self.value > 1:
self.value = 1 self.value = 1

View File

@ -2,9 +2,9 @@ import tkinter
import sys import sys
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..customtkinter_theme_manager import CTkThemeManager from ..theme_manager import CTkThemeManager
from ..customtkinter_settings import CTkSettings from ..ctk_settings import CTkSettings
from ..customtkinter_draw_engine import CTkDrawEngine from ..ctk_draw_engine import CTkDrawEngine
from .widget_base_class import CTkBaseClass from .widget_base_class import CTkBaseClass
@ -70,13 +70,13 @@ class CTkSwitch(CTkBaseClass):
# configure grid system (3x1) # configure grid system (3x1)
self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=0, minsize=6) self.grid_columnconfigure(1, weight=0, minsize=6 * self.scaling)
self.grid_columnconfigure(2, weight=0) self.grid_columnconfigure(2, weight=0)
self.canvas = CTkCanvas(master=self, self.canvas = CTkCanvas(master=self,
highlightthickness=0, highlightthickness=0,
width=self.width, width=self.width * self.scaling,
height=self.height) height=self.height * self.scaling)
self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, sticky="nswe") self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, sticky="nswe")
self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method) self.draw_engine = CTkDrawEngine(self.canvas, CTkSettings.preferred_drawing_method)
@ -110,11 +110,21 @@ class CTkSwitch(CTkBaseClass):
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
if self.check_state is True: if self.check_state is True:
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.width, self.height, self.corner_radius, self.border_width, requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.width * self.scaling,
self.button_length, self.corner_radius, 1, "w") self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling,
self.button_length * self.scaling,
self.corner_radius * self.scaling
, 1, "w")
else: else:
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.width, self.height, self.corner_radius, self.border_width, requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.width * self.scaling,
self.button_length, self.corner_radius, 0, "w") self.height * self.scaling,
self.corner_radius * self.scaling,
self.border_width * self.scaling,
self.button_length * self.scaling,
self.corner_radius * self.scaling,
0, "w")
if no_color_updates is False 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.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
@ -145,7 +155,7 @@ class CTkSwitch(CTkBaseClass):
bd=0, bd=0,
text=self.text, text=self.text,
justify=tkinter.LEFT, justify=tkinter.LEFT,
font=self.text_font) font=self.apply_font_scaling(self.text_font))
self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w") self.text_label.grid(row=0, column=2, padx=0, pady=0, sticky="w")
self.text_label["anchor"] = "w" self.text_label["anchor"] = "w"
if self.textvariable is not None: if self.textvariable is not None:

View File

@ -1,9 +1,13 @@
import tkinter import tkinter
import tkinter.ttk as ttk import tkinter.ttk as ttk
import copy
import re
from .ctk_tk import CTk from .ctk_tk import CTk
from .ctk_toplevel import CTkToplevel from .ctk_toplevel import CTkToplevel
from ..appearance_mode_tracker import AppearanceModeTracker from ..appearance_mode_tracker import AppearanceModeTracker
from ..scaling_tracker import ScalingTracker
from ..theme_manager import CTkThemeManager
class CTkBaseClass(tkinter.Frame): class CTkBaseClass(tkinter.Frame):
@ -12,12 +16,18 @@ class CTkBaseClass(tkinter.Frame):
self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color
self.width = width # width and height in pixel, represent current size of the widget (not the desired size by init) self.width = width # width and height in pixel, represent current size of the widget (not the desired size by init)
self.height = height self.height = height # width and height are independent of the scale
# add set_scaling method to callback list of ScalingTracker for automatic scaling changes
ScalingTracker.add_widget(self.set_scaling, self)
self.scaling = ScalingTracker.get_widget_scaling(self)
# add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes # add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes
AppearanceModeTracker.add(self.set_appearance_mode, self) AppearanceModeTracker.add(self.set_appearance_mode, self)
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
super().configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
# overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too # 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)): if isinstance(self.master, (tkinter.Tk, tkinter.Toplevel, tkinter.Frame)) and not isinstance(self.master, (CTkBaseClass, CTk, CTkToplevel)):
master_old_configure = self.master.config master_old_configure = self.master.config
@ -64,11 +74,11 @@ class CTkBaseClass(tkinter.Frame):
if require_redraw: if require_redraw:
self.draw() self.draw()
def update_dimensions(self, event): def update_dimensions_event(self, event):
# only redraw if dimensions changed (for performance) # only redraw if dimensions changed (for performance)
if self.width != event.width or self.height != event.height: if self.width != int(event.width * self.scaling) or self.height != int(event.height * self.scaling):
self.width = event.width # adjust current size according to new size given by event self.width = int(event.width / self.scaling) # adjust current size according to new size given by event
self.height = event.height self.height = int(event.height / self.scaling) # width and height are independent of the scale
self.draw(no_color_updates=True) # faster drawing without color changes self.draw(no_color_updates=True) # faster drawing without color changes
@ -104,6 +114,33 @@ class CTkBaseClass(tkinter.Frame):
self.draw() self.draw()
def set_scaling(self, new_scaling):
self.scaling = new_scaling
super().configure(width=self.width * self.scaling, height=self.height * self.scaling)
def apply_font_scaling(self, font):
if type(font) == tuple or type(font) == list:
font_list = list(font)
for i in range(len(font_list)):
if (type(font_list[i]) == int or type(font_list[i]) == float) and font_list[i] < 0:
font_list[i] = int(font_list[i] * self.scaling)
return tuple(font_list)
elif type(font) == str:
for negative_number in re.findall(r" -\d* ", font):
font = font.replace(negative_number, f" {int(int(negative_number) * self.scaling)} ")
return font
elif isinstance(font, tkinter.font.Font):
new_font_object = copy.copy(font)
if font.cget("size") < 0:
new_font_object.config(size=int(font.cget("size") * self.scaling))
return new_font_object
else:
return font
def draw(self, no_color_updates=False): def draw(self, no_color_updates=False):
""" abstract of draw method to be overridden """ """ abstract of draw method to be overridden """
pass pass

View File

@ -4,6 +4,8 @@ import customtkinter # <- import the CustomTkinter module
customtkinter.set_appearance_mode("dark") # Modes: "System" (standard), "Dark", "Light" customtkinter.set_appearance_mode("dark") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue" customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
customtkinter.ScalingTracker.set_user_scaling(0.9)
root_tk = customtkinter.CTk() # create CTk window like you do with the Tk window (you can also use normal tkinter.Tk window) root_tk = customtkinter.CTk() # create CTk window like you do with the Tk window (you can also use normal tkinter.Tk window)
root_tk.geometry("400x480") root_tk.geometry("400x480")
root_tk.title("CustomTkinter Test") root_tk.title("CustomTkinter Test")