added orientation for CTkSLider, CTkProgressBar
0
__init__.py
Normal file
@ -1,5 +1,6 @@
|
|||||||
__version__ = "3.12"
|
__version__ = "3.12"
|
||||||
|
|
||||||
|
# import widgets
|
||||||
from .widgets.ctk_button import CTkButton
|
from .widgets.ctk_button import CTkButton
|
||||||
from .widgets.ctk_checkbox import CTkCheckBox
|
from .widgets.ctk_checkbox import CTkCheckBox
|
||||||
from .widgets.ctk_entry import CTkEntry
|
from .widgets.ctk_entry import CTkEntry
|
||||||
@ -11,47 +12,22 @@ from .widgets.ctk_radiobutton import CTkRadioButton
|
|||||||
from .widgets.ctk_canvas import CTkCanvas
|
from .widgets.ctk_canvas import CTkCanvas
|
||||||
from .widgets.ctk_switch import CTkSwitch
|
from .widgets.ctk_switch import CTkSwitch
|
||||||
|
|
||||||
|
# import windows
|
||||||
from .windows.ctk_tk import CTk
|
from .windows.ctk_tk import CTk
|
||||||
from .windows.ctk_toplevel import CTkToplevel
|
from .windows.ctk_toplevel import CTkToplevel
|
||||||
from .windows.ctk_input_dialog import CTkInputDialog
|
from .windows.ctk_input_dialog import CTkInputDialog
|
||||||
|
|
||||||
|
# import other classes
|
||||||
from .ctk_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 .ctk_theme_manager import CTkThemeManager
|
||||||
from .scaling_tracker import ScalingTracker
|
from .scaling_tracker import ScalingTracker
|
||||||
|
|
||||||
from distutils.version import StrictVersion as Version
|
|
||||||
import tkinter
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
def enable_macos_darkmode():
|
|
||||||
if sys.platform == "darwin": # macOS
|
|
||||||
if Version(tkinter.Tcl().call("info", "patchlevel")) >= Version("8.6.9"): # Tcl/Tk >= 8.6.9
|
|
||||||
os.system("defaults write -g NSRequiresAquaSystemAppearance -bool No")
|
|
||||||
|
|
||||||
sys.stderr.write("WARNING (customtkinter.enable_macos_darkmode): " +
|
|
||||||
"This command forces macOS dark-mode on all programs. " +
|
|
||||||
"This can cause bugs on some other programs.\n" +
|
|
||||||
"Disable it by calling customtkinter.disable_macos_darkmode() at the end of the program.\n")
|
|
||||||
else:
|
|
||||||
sys.stderr.write("WARNING (customtkinter.enable_macos_darkmode): " +
|
|
||||||
"Currently this works only with anaconda python version (Tcl/Tk >= 8.6.9).\n" +
|
|
||||||
"(python.org Tcl/Tk version is only 8.6.8)\n")
|
|
||||||
else:
|
|
||||||
sys.stderr.write("WARNING (customtkinter.enable_macos_darkmode): " +
|
|
||||||
"System is not macOS, but the following: {}\n".format(sys.platform))
|
|
||||||
|
|
||||||
|
|
||||||
def disable_macos_darkmode():
|
|
||||||
if sys.platform == "darwin": # macOS
|
|
||||||
if Version(tkinter.Tcl().call("info", "patchlevel")) >= Version("8.6.9"): # Tcl/Tk >= 8.6.9
|
|
||||||
os.system("defaults delete -g NSRequiresAquaSystemAppearance")
|
|
||||||
# This command reverts the dark-mode setting for all programs.
|
|
||||||
|
|
||||||
|
|
||||||
def set_appearance_mode(mode_string):
|
def set_appearance_mode(mode_string):
|
||||||
AppearanceModeTracker.set_appearance_mode(mode_string)
|
AppearanceModeTracker.set_appearance_mode(mode_string)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"text": ["gray12", "gray90"],
|
"text": ["gray12", "gray90"],
|
||||||
"text_disabled": ["gray60", "gray50"],
|
"text_disabled": ["gray60", "gray50"],
|
||||||
"text_button_disabled": ["gray40", "gray74"],
|
"text_button_disabled": ["gray40", "gray74"],
|
||||||
"progressbar": ["#6B6B6B", "gray6"],
|
"progressbar": ["#6B6B6B", "gray0"],
|
||||||
"progressbar_progress": ["#608BD5", "#395E9C"],
|
"progressbar_progress": ["#608BD5", "#395E9C"],
|
||||||
"progressbar_border": ["gray", "gray"],
|
"progressbar_border": ["gray", "gray"],
|
||||||
"slider": ["#6B6B6B", "gray6"],
|
"slider": ["#6B6B6B", "gray6"],
|
||||||
|
@ -568,6 +568,15 @@ class CTkDrawEngine:
|
|||||||
slider_x_position - (button_length / 2), height - button_corner_radius)
|
slider_x_position - (button_length / 2), height - button_corner_radius)
|
||||||
self._canvas.itemconfig("slider_line_1",
|
self._canvas.itemconfig("slider_line_1",
|
||||||
width=button_corner_radius * 2)
|
width=button_corner_radius * 2)
|
||||||
|
elif orientation == "s":
|
||||||
|
slider_y_position = corner_radius + (button_length / 2) + (height - 2 * corner_radius - button_length) * (1 - slider_value)
|
||||||
|
self._canvas.coords("slider_line_1",
|
||||||
|
button_corner_radius, slider_y_position - (button_length / 2),
|
||||||
|
button_corner_radius, slider_y_position + (button_length / 2),
|
||||||
|
width - button_corner_radius, slider_y_position + (button_length / 2),
|
||||||
|
width - button_corner_radius, slider_y_position - (button_length / 2))
|
||||||
|
self._canvas.itemconfig("slider_line_1",
|
||||||
|
width=button_corner_radius * 2)
|
||||||
|
|
||||||
return requires_recoloring
|
return requires_recoloring
|
||||||
|
|
||||||
|
@ -81,9 +81,24 @@ class ScalingTracker:
|
|||||||
if window_root not in cls.window_dpi_scaling_dict:
|
if window_root not in cls.window_dpi_scaling_dict:
|
||||||
cls.window_dpi_scaling_dict[window_root] = cls.get_window_dpi_scaling(window_root)
|
cls.window_dpi_scaling_dict[window_root] = cls.get_window_dpi_scaling(window_root)
|
||||||
|
|
||||||
if not cls.update_loop_running:
|
#if not cls.update_loop_running:
|
||||||
window_root.after(100, cls.check_dpi_scaling)
|
# window_root.after(100, cls.check_dpi_scaling)
|
||||||
cls.update_loop_running = True
|
# cls.update_loop_running = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_widget(cls, widget_callback, widget):
|
||||||
|
window_root = cls.get_window_root_of_widget(widget)
|
||||||
|
try:
|
||||||
|
cls.window_widgets_dict[window_root].remove(widget_callback)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_window(cls, window_callback, window):
|
||||||
|
try:
|
||||||
|
del cls.window_widgets_dict[window]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_window(cls, window_callback, window):
|
def add_window(cls, window_callback, window):
|
||||||
@ -133,6 +148,9 @@ class ScalingTracker:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_dpi_scaling(cls):
|
def check_dpi_scaling(cls):
|
||||||
|
# check for every window if scaling value changed
|
||||||
|
# (not implemented yet)
|
||||||
|
|
||||||
# find an existing tkinter object for the next call of .after()
|
# find an existing tkinter object for the next call of .after()
|
||||||
for root_tk in cls.window_widgets_dict.keys():
|
for root_tk in cls.window_widgets_dict.keys():
|
||||||
try:
|
try:
|
||||||
|
@ -3,7 +3,7 @@ import sys
|
|||||||
import math
|
import math
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
|
@ -2,7 +2,7 @@ import tkinter
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
@ -325,7 +325,10 @@ class CTkCheckBox(CTkBaseClass):
|
|||||||
self.variable_callback_blocked = False
|
self.variable_callback_blocked = False
|
||||||
|
|
||||||
if self.function is not None:
|
if self.function is not None:
|
||||||
self.function()
|
try:
|
||||||
|
self.function()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def deselect(self, from_variable_callback=False):
|
def deselect(self, from_variable_callback=False):
|
||||||
self.check_state = False
|
self.check_state = False
|
||||||
@ -337,7 +340,10 @@ class CTkCheckBox(CTkBaseClass):
|
|||||||
self.variable_callback_blocked = False
|
self.variable_callback_blocked = False
|
||||||
|
|
||||||
if self.function is not None:
|
if self.function is not None:
|
||||||
self.function()
|
try:
|
||||||
|
self.function()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
return self.onvalue if self.check_state is True else self.offvalue
|
return self.onvalue if self.check_state is True else self.offvalue
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import tkinter
|
import tkinter
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import tkinter
|
import tkinter
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import tkinter
|
import tkinter
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import tkinter
|
import tkinter
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
@ -17,11 +17,24 @@ class CTkProgressBar(CTkBaseClass):
|
|||||||
fg_color="default_theme",
|
fg_color="default_theme",
|
||||||
progress_color="default_theme",
|
progress_color="default_theme",
|
||||||
corner_radius="default_theme",
|
corner_radius="default_theme",
|
||||||
width=200,
|
width=None,
|
||||||
height=8,
|
height=None,
|
||||||
border_width="default_theme",
|
border_width="default_theme",
|
||||||
|
orient="horizontal",
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
|
# set default dimensions according to orientation
|
||||||
|
if width is None:
|
||||||
|
if orient.lower() == "vertical":
|
||||||
|
width = 8
|
||||||
|
else:
|
||||||
|
width = 200
|
||||||
|
if height is None:
|
||||||
|
if orient.lower() == "vertical":
|
||||||
|
height = 200
|
||||||
|
else:
|
||||||
|
height = 8
|
||||||
|
|
||||||
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
@ -39,6 +52,7 @@ class CTkProgressBar(CTkBaseClass):
|
|||||||
self.corner_radius = CTkThemeManager.theme["shape"]["progressbar_corner_radius"] if corner_radius == "default_theme" else corner_radius
|
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.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.orient = orient
|
||||||
|
|
||||||
self.grid_rowconfigure(0, weight=1)
|
self.grid_rowconfigure(0, weight=1)
|
||||||
self.grid_columnconfigure(0, weight=1)
|
self.grid_columnconfigure(0, weight=1)
|
||||||
@ -74,11 +88,19 @@ class CTkProgressBar(CTkBaseClass):
|
|||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def draw(self, no_color_updates=False):
|
def draw(self, no_color_updates=False):
|
||||||
|
if self.orient.lower() == "horizontal":
|
||||||
|
orientation = "w"
|
||||||
|
elif self.orient.lower() == "vertical":
|
||||||
|
orientation = "s"
|
||||||
|
else:
|
||||||
|
orientation = "w"
|
||||||
|
|
||||||
requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self.current_width),
|
requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self.current_width),
|
||||||
self.apply_widget_scaling(self.current_height),
|
self.apply_widget_scaling(self.current_height),
|
||||||
self.apply_widget_scaling(self.corner_radius),
|
self.apply_widget_scaling(self.corner_radius),
|
||||||
self.apply_widget_scaling(self.border_width),
|
self.apply_widget_scaling(self.border_width),
|
||||||
self.value, "w")
|
self.value,
|
||||||
|
orientation)
|
||||||
|
|
||||||
if no_color_updates is False or requires_recoloring:
|
if no_color_updates is False or requires_recoloring:
|
||||||
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
|
@ -2,7 +2,7 @@ import tkinter
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
|
@ -2,7 +2,7 @@ import tkinter
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
@ -21,16 +21,29 @@ class CTkSlider(CTkBaseClass):
|
|||||||
from_=0,
|
from_=0,
|
||||||
to=1,
|
to=1,
|
||||||
number_of_steps=None,
|
number_of_steps=None,
|
||||||
width=160,
|
width=None,
|
||||||
height=16,
|
height=None,
|
||||||
corner_radius="default_theme",
|
corner_radius="default_theme",
|
||||||
button_corner_radius="default_theme",
|
button_corner_radius="default_theme",
|
||||||
border_width="default_theme",
|
border_width="default_theme",
|
||||||
button_length="default_theme",
|
button_length="default_theme",
|
||||||
command=None,
|
command=None,
|
||||||
variable=None,
|
variable=None,
|
||||||
|
orient="horizontal",
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
|
# set default dimensions according to orientation
|
||||||
|
if width is None:
|
||||||
|
if orient.lower() == "vertical":
|
||||||
|
width = 16
|
||||||
|
else:
|
||||||
|
width = 200
|
||||||
|
if height is None:
|
||||||
|
if orient.lower() == "vertical":
|
||||||
|
height = 200
|
||||||
|
else:
|
||||||
|
height = 16
|
||||||
|
|
||||||
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
|
||||||
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
|
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||||
|
|
||||||
@ -47,6 +60,7 @@ class CTkSlider(CTkBaseClass):
|
|||||||
self.border_width = CTkThemeManager.theme["shape"]["slider_border_width"] if border_width == "default_theme" else border_width
|
self.border_width = CTkThemeManager.theme["shape"]["slider_border_width"] if border_width == "default_theme" else border_width
|
||||||
self.button_length = CTkThemeManager.theme["shape"]["slider_button_length"] if button_length == "default_theme" else button_length
|
self.button_length = CTkThemeManager.theme["shape"]["slider_button_length"] if button_length == "default_theme" else button_length
|
||||||
self.value = 0.5 # initial value of slider in percent
|
self.value = 0.5 # initial value of slider in percent
|
||||||
|
self.orient = orient
|
||||||
self.hover_state = False
|
self.hover_state = False
|
||||||
self.from_ = from_
|
self.from_ = from_
|
||||||
self.to = to
|
self.to = to
|
||||||
@ -110,13 +124,20 @@ 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):
|
||||||
|
if self.orient.lower() == "horizontal":
|
||||||
|
orientation = "w"
|
||||||
|
elif self.orient.lower() == "vertical":
|
||||||
|
orientation = "s"
|
||||||
|
else:
|
||||||
|
orientation = "w"
|
||||||
|
|
||||||
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self.current_width),
|
requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self.current_width),
|
||||||
self.apply_widget_scaling(self.current_height),
|
self.apply_widget_scaling(self.current_height),
|
||||||
self.apply_widget_scaling(self.corner_radius),
|
self.apply_widget_scaling(self.corner_radius),
|
||||||
self.apply_widget_scaling(self.border_width),
|
self.apply_widget_scaling(self.border_width),
|
||||||
self.apply_widget_scaling(self.button_length),
|
self.apply_widget_scaling(self.button_length),
|
||||||
self.apply_widget_scaling(self.button_corner_radius),
|
self.apply_widget_scaling(self.button_corner_radius),
|
||||||
self.value, "w")
|
self.value, orientation)
|
||||||
|
|
||||||
if no_color_updates is False or requires_recoloring:
|
if no_color_updates is False or requires_recoloring:
|
||||||
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
self.canvas.configure(bg=CTkThemeManager.single_color(self.bg_color, self.appearance_mode))
|
||||||
@ -142,7 +163,10 @@ 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.current_width) / self.widget_scaling
|
if self.orient.lower() == "horizontal":
|
||||||
|
self.value = (event.x / self.current_width) / self.widget_scaling
|
||||||
|
else:
|
||||||
|
self.value = 1 - (event.y / self.current_height) / self.widget_scaling
|
||||||
|
|
||||||
if self.value > 1:
|
if self.value > 1:
|
||||||
self.value = 1
|
self.value = 1
|
||||||
|
@ -2,7 +2,7 @@ import tkinter
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..ctk_draw_engine import CTkDrawEngine
|
from ..ctk_draw_engine import CTkDrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
|
@ -9,7 +9,7 @@ from ..windows.ctk_tk import CTk
|
|||||||
from ..windows.ctk_toplevel import CTkToplevel
|
from ..windows.ctk_toplevel import CTkToplevel
|
||||||
from ..appearance_mode_tracker import AppearanceModeTracker
|
from ..appearance_mode_tracker import AppearanceModeTracker
|
||||||
from ..scaling_tracker import ScalingTracker
|
from ..scaling_tracker import ScalingTracker
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
|
|
||||||
|
|
||||||
class CTkBaseClass(tkinter.Frame):
|
class CTkBaseClass(tkinter.Frame):
|
||||||
|
@ -7,7 +7,7 @@ from ..widgets.ctk_frame import CTkFrame
|
|||||||
from ..windows.ctk_toplevel import CTkToplevel
|
from ..windows.ctk_toplevel import CTkToplevel
|
||||||
from ..widgets.ctk_button import CTkButton
|
from ..widgets.ctk_button import CTkButton
|
||||||
from ..appearance_mode_tracker import AppearanceModeTracker
|
from ..appearance_mode_tracker import AppearanceModeTracker
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
|
|
||||||
|
|
||||||
class CTkInputDialog:
|
class CTkInputDialog:
|
||||||
|
@ -8,7 +8,7 @@ import re
|
|||||||
from typing import Union, Tuple
|
from typing import Union, Tuple
|
||||||
|
|
||||||
from ..appearance_mode_tracker import AppearanceModeTracker
|
from ..appearance_mode_tracker import AppearanceModeTracker
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..scaling_tracker import ScalingTracker
|
from ..scaling_tracker import ScalingTracker
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
|
|
||||||
@ -33,10 +33,10 @@ class CTk(tkinter.Tk):
|
|||||||
|
|
||||||
self.current_width = 600 # initial window size, always without scaling
|
self.current_width = 600 # initial window size, always without scaling
|
||||||
self.current_height = 500
|
self.current_height = 500
|
||||||
self.min_width: Union[int, None] = None
|
self.min_width: int = 0
|
||||||
self.min_height: Union[int, None] = None
|
self.min_height: int = 0
|
||||||
self.max_width: Union[int, None] = None
|
self.max_width: int = 1_000_000
|
||||||
self.max_height: Union[int, None] = None
|
self.max_height: int = 1_000_000
|
||||||
self.last_resizable_args: Union[Tuple[list, dict], None] = None # (args, kwargs)
|
self.last_resizable_args: Union[Tuple[list, dict], None] = None # (args, kwargs)
|
||||||
|
|
||||||
self.fg_color = CTkThemeManager.theme["color"]["window_bg_color"] if fg_color == "default_theme" else fg_color
|
self.fg_color = CTkThemeManager.theme["color"]["window_bg_color"] if fg_color == "default_theme" else fg_color
|
||||||
@ -86,7 +86,7 @@ class CTk(tkinter.Tk):
|
|||||||
|
|
||||||
# set scaled min, max sizes and reapply resizable
|
# set scaled min, max sizes and reapply resizable
|
||||||
if self.last_resizable_args is not None:
|
if self.last_resizable_args is not None:
|
||||||
super().resizable(self.last_resizable_args[0], self.last_resizable_args[1]) # args, kwargs
|
super().resizable(*self.last_resizable_args[0], **self.last_resizable_args[1]) # args, kwargs
|
||||||
if self.min_width is not None or self.min_height is not None:
|
if self.min_width is not None or self.min_height is not None:
|
||||||
super().minsize(self.apply_window_scaling(self.min_width), self.apply_window_scaling(self.min_height))
|
super().minsize(self.apply_window_scaling(self.min_width), self.apply_window_scaling(self.min_height))
|
||||||
if self.max_width is not None or self.max_height is not None:
|
if self.max_width is not None or self.max_height is not None:
|
||||||
@ -94,6 +94,7 @@ class CTk(tkinter.Tk):
|
|||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
AppearanceModeTracker.remove(self.set_appearance_mode)
|
AppearanceModeTracker.remove(self.set_appearance_mode)
|
||||||
|
ScalingTracker.remove_window(self.set_scaling, self)
|
||||||
self.disable_macos_dark_title_bar()
|
self.disable_macos_dark_title_bar()
|
||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
@ -122,11 +123,15 @@ class CTk(tkinter.Tk):
|
|||||||
def minsize(self, width=None, height=None):
|
def minsize(self, width=None, height=None):
|
||||||
self.min_width = width
|
self.min_width = width
|
||||||
self.min_height = height
|
self.min_height = height
|
||||||
|
if self.current_width < width: self.current_width = width
|
||||||
|
if self.current_height < height: self.current_height = height
|
||||||
super().minsize(self.apply_window_scaling(self.min_width), self.apply_window_scaling(self.min_height))
|
super().minsize(self.apply_window_scaling(self.min_width), self.apply_window_scaling(self.min_height))
|
||||||
|
|
||||||
def maxsize(self, width=None, height=None):
|
def maxsize(self, width=None, height=None):
|
||||||
self.max_width = width
|
self.max_width = width
|
||||||
self.max_height = height
|
self.max_height = height
|
||||||
|
if self.current_width > width: self.current_width = width
|
||||||
|
if self.current_height > height: self.current_height = height
|
||||||
super().maxsize(self.apply_window_scaling(self.max_width), self.apply_window_scaling(self.max_height))
|
super().maxsize(self.apply_window_scaling(self.max_width), self.apply_window_scaling(self.max_height))
|
||||||
|
|
||||||
def geometry(self, geometry_string):
|
def geometry(self, geometry_string):
|
||||||
@ -134,7 +139,8 @@ class CTk(tkinter.Tk):
|
|||||||
|
|
||||||
# update width and height attributes
|
# update width and height attributes
|
||||||
numbers = list(map(int, re.split(r"[x+]", geometry_string))) # split geometry string into list of numbers
|
numbers = list(map(int, re.split(r"[x+]", geometry_string))) # split geometry string into list of numbers
|
||||||
self.current_width, self.current_height = numbers[0], numbers[1]
|
self.current_width = max(self.min_width, min(numbers[0], self.max_width)) # bound value between min and max
|
||||||
|
self.current_height = max(self.min_height, min(numbers[1], self.max_height))
|
||||||
|
|
||||||
def apply_geometry_scaling(self, geometry_string):
|
def apply_geometry_scaling(self, geometry_string):
|
||||||
numbers = list(map(int, re.split(r"[x+]", geometry_string))) # split geometry string into list of numbers
|
numbers = list(map(int, re.split(r"[x+]", geometry_string))) # split geometry string into list of numbers
|
||||||
|
@ -5,9 +5,10 @@ import os
|
|||||||
import platform
|
import platform
|
||||||
import ctypes
|
import ctypes
|
||||||
import re
|
import re
|
||||||
|
from typing import Union, Tuple
|
||||||
|
|
||||||
from ..appearance_mode_tracker import AppearanceModeTracker
|
from ..appearance_mode_tracker import AppearanceModeTracker
|
||||||
from ..theme_manager import CTkThemeManager
|
from ..ctk_theme_manager import CTkThemeManager
|
||||||
from ..ctk_settings import CTkSettings
|
from ..ctk_settings import CTkSettings
|
||||||
from ..scaling_tracker import ScalingTracker
|
from ..scaling_tracker import ScalingTracker
|
||||||
|
|
||||||
@ -27,6 +28,11 @@ class CTkToplevel(tkinter.Toplevel):
|
|||||||
|
|
||||||
self.current_width = 600 # initial window size, always without scaling
|
self.current_width = 600 # initial window size, always without scaling
|
||||||
self.current_height = 500
|
self.current_height = 500
|
||||||
|
self.min_width: int = 0
|
||||||
|
self.min_height: int = 0
|
||||||
|
self.max_width: int = 1_000_000
|
||||||
|
self.max_height: int = 1_000_000
|
||||||
|
self.last_resizable_args: Union[Tuple[list, dict], None] = None # (args, kwargs)
|
||||||
|
|
||||||
self.fg_color = CTkThemeManager.theme["color"]["window_bg_color"] if fg_color == "default_theme" else fg_color
|
self.fg_color = CTkThemeManager.theme["color"]["window_bg_color"] if fg_color == "default_theme" else fg_color
|
||||||
|
|
||||||
@ -59,9 +65,25 @@ class CTkToplevel(tkinter.Toplevel):
|
|||||||
def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling):
|
def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling):
|
||||||
self.window_scaling = new_window_scaling
|
self.window_scaling = new_window_scaling
|
||||||
|
|
||||||
|
# reset min, max and resizable constraints for applying scaling
|
||||||
|
if self.last_resizable_args is not None:
|
||||||
|
super().resizable(True, True)
|
||||||
|
if self.min_width is not None or self.min_height is not None:
|
||||||
|
super().minsize(0, 0)
|
||||||
|
if self.max_width is not None or self.max_height is not None:
|
||||||
|
super().maxsize(1_000_000, 1_000_000)
|
||||||
|
|
||||||
# set new window size by applying scaling to the current window size
|
# set new window size by applying scaling to the current window size
|
||||||
self.geometry(f"{self.current_width}x{self.current_height}")
|
self.geometry(f"{self.current_width}x{self.current_height}")
|
||||||
|
|
||||||
|
# set scaled min, max sizes and reapply resizable
|
||||||
|
if self.last_resizable_args is not None:
|
||||||
|
super().resizable(*self.last_resizable_args[0], **self.last_resizable_args[1]) # args, kwargs
|
||||||
|
if self.min_width is not None or self.min_height is not None:
|
||||||
|
super().minsize(self.apply_window_scaling(self.min_width), self.apply_window_scaling(self.min_height))
|
||||||
|
if self.max_width is not None or self.max_height is not None:
|
||||||
|
super().maxsize(self.apply_window_scaling(self.max_width), self.apply_window_scaling(self.max_height))
|
||||||
|
|
||||||
def apply_geometry_scaling(self, geometry_string):
|
def apply_geometry_scaling(self, geometry_string):
|
||||||
numbers = list(map(int, re.split(r"[x+]", geometry_string))) # split geometry string into list of numbers
|
numbers = list(map(int, re.split(r"[x+]", geometry_string))) # split geometry string into list of numbers
|
||||||
|
|
||||||
@ -78,7 +100,7 @@ class CTkToplevel(tkinter.Toplevel):
|
|||||||
|
|
||||||
def apply_window_scaling(self, value):
|
def apply_window_scaling(self, value):
|
||||||
if isinstance(value, (int, float)):
|
if isinstance(value, (int, float)):
|
||||||
return value * self.window_scaling
|
return int(value * self.window_scaling)
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@ -87,15 +109,18 @@ class CTkToplevel(tkinter.Toplevel):
|
|||||||
|
|
||||||
# update width and height attributes
|
# update width and height attributes
|
||||||
numbers = list(map(int, re.split(r"[x+]", geometry_string))) # split geometry string into list of numbers
|
numbers = list(map(int, re.split(r"[x+]", geometry_string))) # split geometry string into list of numbers
|
||||||
self.current_width, self.current_height = numbers[0], numbers[1]
|
self.current_width = max(self.min_width, min(numbers[0], self.max_width)) # bound value between min and max
|
||||||
|
self.current_height = max(self.min_height, min(numbers[1], self.max_height))
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
AppearanceModeTracker.remove(self.set_appearance_mode)
|
AppearanceModeTracker.remove(self.set_appearance_mode)
|
||||||
|
ScalingTracker.remove_window(self.set_scaling, self)
|
||||||
self.disable_macos_dark_title_bar()
|
self.disable_macos_dark_title_bar()
|
||||||
super().destroy()
|
super().destroy()
|
||||||
|
|
||||||
def resizable(self, *args, **kwargs):
|
def resizable(self, *args, **kwargs):
|
||||||
super().resizable(*args, **kwargs)
|
super().resizable(*args, **kwargs)
|
||||||
|
self.last_resizable_args = (args, kwargs)
|
||||||
|
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
if self.appearance_mode == 1:
|
if self.appearance_mode == 1:
|
||||||
@ -103,6 +128,20 @@ class CTkToplevel(tkinter.Toplevel):
|
|||||||
else:
|
else:
|
||||||
self.windows_set_titlebar_color("light")
|
self.windows_set_titlebar_color("light")
|
||||||
|
|
||||||
|
def minsize(self, width=None, height=None):
|
||||||
|
self.min_width = width
|
||||||
|
self.min_height = height
|
||||||
|
if self.current_width < width: self.current_width = width
|
||||||
|
if self.current_height < height: self.current_height = height
|
||||||
|
super().minsize(self.apply_window_scaling(self.min_width), self.apply_window_scaling(self.min_height))
|
||||||
|
|
||||||
|
def maxsize(self, width=None, height=None):
|
||||||
|
self.max_width = width
|
||||||
|
self.max_height = height
|
||||||
|
if self.current_width > width: self.current_width = width
|
||||||
|
if self.current_height > height: self.current_height = height
|
||||||
|
super().maxsize(self.apply_window_scaling(self.max_width), self.apply_window_scaling(self.max_height))
|
||||||
|
|
||||||
def config(self, *args, **kwargs):
|
def config(self, *args, **kwargs):
|
||||||
self.configure(*args, **kwargs)
|
self.configure(*args, **kwargs)
|
||||||
|
|
||||||
|
BIN
documentation_images/Ubuntu_dark.png
Normal file
After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 885 KiB After Width: | Height: | Size: 885 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
24
test/manual_integration_tests/test_vertical_widgets.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import customtkinter
|
||||||
|
|
||||||
|
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("400x650")
|
||||||
|
root_tk.title("test_vertical_widgets")
|
||||||
|
|
||||||
|
root_tk.grid_columnconfigure(0, weight=1)
|
||||||
|
root_tk.grid_rowconfigure((0, 1, 2, 3), weight=1)
|
||||||
|
|
||||||
|
progressbar_1 = customtkinter.CTkProgressBar(root_tk, orient="horizontal")
|
||||||
|
progressbar_1.grid(row=0, column=0, pady=20, padx=20)
|
||||||
|
|
||||||
|
progressbar_2 = customtkinter.CTkProgressBar(root_tk, orient="vertical")
|
||||||
|
progressbar_2.grid(row=1, column=0, pady=20, padx=20)
|
||||||
|
|
||||||
|
slider_1 = customtkinter.CTkSlider(root_tk, orient="horizontal", command=progressbar_1.set)
|
||||||
|
slider_1.grid(row=2, column=0, pady=20, padx=20)
|
||||||
|
|
||||||
|
slider_2 = customtkinter.CTkSlider(root_tk, orient="vertical", command=progressbar_2.set,
|
||||||
|
button_corner_radius=2, button_length=20)
|
||||||
|
slider_2.grid(row=3, column=0, pady=20, padx=20)
|
||||||
|
|
||||||
|
|
||||||
|
root_tk.mainloop()
|
0
test/unit_tests/__init__.py
Normal file
7
test/unit_tests/test_all.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from test_ctk import TestCTk
|
||||||
|
from test_ctk_toplevel import TestCTkToplevel
|
||||||
|
from test_ctk_button import TestCTkButton
|
||||||
|
|
||||||
|
TestCTk().main()
|
||||||
|
TestCTkToplevel().main()
|
||||||
|
TestCTkButton().main()
|
112
test/unit_tests/test_ctk.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import time
|
||||||
|
import customtkinter
|
||||||
|
|
||||||
|
|
||||||
|
class TestCTk():
|
||||||
|
def __init__(self):
|
||||||
|
self.root_ctk = customtkinter.CTk()
|
||||||
|
self.root_ctk.title("TestCTk")
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
self.root_ctk.quit()
|
||||||
|
self.root_ctk.withdraw()
|
||||||
|
|
||||||
|
def main(self):
|
||||||
|
self.execute_tests()
|
||||||
|
self.root_ctk.mainloop()
|
||||||
|
|
||||||
|
def execute_tests(self):
|
||||||
|
print("\nTestCTk started:")
|
||||||
|
start_time = 0
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_geometry)
|
||||||
|
start_time += 100
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_scaling)
|
||||||
|
start_time += 100
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_configure)
|
||||||
|
start_time += 100
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_appearance_mode)
|
||||||
|
start_time += 100
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_iconify)
|
||||||
|
start_time += 1500
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.clean)
|
||||||
|
|
||||||
|
def test_geometry(self):
|
||||||
|
print(" -> test_geometry: ", end="")
|
||||||
|
self.root_ctk.geometry("100x200+200+300")
|
||||||
|
assert self.root_ctk.current_width == 100 and self.root_ctk.current_height == 200
|
||||||
|
|
||||||
|
self.root_ctk.minsize(300, 400)
|
||||||
|
assert self.root_ctk.current_width == 300 and self.root_ctk.current_height == 400
|
||||||
|
assert self.root_ctk.min_width == 300 and self.root_ctk.min_height == 400
|
||||||
|
|
||||||
|
self.root_ctk.maxsize(400, 500)
|
||||||
|
self.root_ctk.geometry("600x600")
|
||||||
|
assert self.root_ctk.current_width == 400 and self.root_ctk.current_height == 500
|
||||||
|
assert self.root_ctk.max_width == 400 and self.root_ctk.max_height == 500
|
||||||
|
|
||||||
|
self.root_ctk.maxsize(1000, 1000)
|
||||||
|
self.root_ctk.geometry("300x400")
|
||||||
|
self.root_ctk.resizable(False, False)
|
||||||
|
self.root_ctk.geometry("500x600")
|
||||||
|
assert self.root_ctk.current_width == 500 and self.root_ctk.current_height == 600
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
def test_scaling(self):
|
||||||
|
print(" -> test_scaling: ", end="")
|
||||||
|
|
||||||
|
customtkinter.ScalingTracker.set_window_scaling(1.5)
|
||||||
|
self.root_ctk.geometry("300x400")
|
||||||
|
self.root_ctk.update()
|
||||||
|
assert self.root_ctk.current_width == 300 and self.root_ctk.current_height == 400
|
||||||
|
assert round(self.root_ctk.winfo_width()) == 450 and round(self.root_ctk.winfo_height()) == 600
|
||||||
|
|
||||||
|
self.root_ctk.maxsize(400, 500)
|
||||||
|
self.root_ctk.geometry("500x500")
|
||||||
|
self.root_ctk.update()
|
||||||
|
assert self.root_ctk.current_width == 400 and self.root_ctk.current_height == 500
|
||||||
|
assert round(self.root_ctk.winfo_width()) == 600 and round(self.root_ctk.winfo_height()) == 750
|
||||||
|
|
||||||
|
customtkinter.ScalingTracker.set_window_scaling(1)
|
||||||
|
self.root_ctk.update()
|
||||||
|
assert self.root_ctk.current_width == 400 and self.root_ctk.current_height == 500
|
||||||
|
assert round(self.root_ctk.winfo_width()) == 400 and round(self.root_ctk.winfo_height()) == 500
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
def test_configure(self):
|
||||||
|
print(" -> test_configure: ", end="")
|
||||||
|
self.root_ctk.configure(bg="white")
|
||||||
|
assert self.root_ctk.fg_color == "white"
|
||||||
|
|
||||||
|
self.root_ctk.configure(background="red")
|
||||||
|
assert self.root_ctk.fg_color == "red"
|
||||||
|
assert self.root_ctk.cget("bg") == "red"
|
||||||
|
|
||||||
|
self.root_ctk.config(fg_color=("green", "#FFFFFF"))
|
||||||
|
assert self.root_ctk.fg_color == ("green", "#FFFFFF")
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
def test_appearance_mode(self):
|
||||||
|
print(" -> test_appearance_mode: ", end="")
|
||||||
|
customtkinter.set_appearance_mode("light")
|
||||||
|
self.root_ctk.config(fg_color=("green", "#FFFFFF"))
|
||||||
|
assert self.root_ctk.cget("bg") == "green"
|
||||||
|
|
||||||
|
customtkinter.set_appearance_mode("dark")
|
||||||
|
assert self.root_ctk.cget("bg") == "#FFFFFF"
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
def test_iconify(self):
|
||||||
|
print(" -> test_iconify: ", end="")
|
||||||
|
self.root_ctk.iconify()
|
||||||
|
self.root_ctk.after(100, self.root_ctk.deiconify)
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
TestCTk().main()
|
38
test/unit_tests/test_ctk_button.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import time
|
||||||
|
import customtkinter
|
||||||
|
|
||||||
|
|
||||||
|
class TestCTkButton():
|
||||||
|
def __init__(self):
|
||||||
|
self.root_ctk = customtkinter.CTk()
|
||||||
|
self.ctk_button = customtkinter.CTkButton(self.root_ctk)
|
||||||
|
self.ctk_button.pack(padx=20, pady=20)
|
||||||
|
self.root_ctk.title(self.__class__.__name__)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
self.root_ctk.quit()
|
||||||
|
self.root_ctk.withdraw()
|
||||||
|
|
||||||
|
def main(self):
|
||||||
|
self.execute_tests()
|
||||||
|
self.root_ctk.mainloop()
|
||||||
|
|
||||||
|
def execute_tests(self):
|
||||||
|
print(f"\n{self.__class__.__name__} started:")
|
||||||
|
|
||||||
|
start_time = 0
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_iconify)
|
||||||
|
start_time += 1500
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.clean)
|
||||||
|
|
||||||
|
def test_iconify(self):
|
||||||
|
print(" -> test_iconify: ", end="")
|
||||||
|
self.root_ctk.iconify()
|
||||||
|
self.root_ctk.after(100, self.root_ctk.deiconify)
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
TestCTkButton().main()
|
114
test/unit_tests/test_ctk_toplevel.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import customtkinter
|
||||||
|
|
||||||
|
|
||||||
|
class TestCTkToplevel():
|
||||||
|
def __init__(self):
|
||||||
|
self.root_ctk = customtkinter.CTk()
|
||||||
|
self.root_ctk.title("TestCTkToplevel")
|
||||||
|
self.ctk_toplevel = customtkinter.CTkToplevel()
|
||||||
|
self.ctk_toplevel.title("TestCTkToplevel")
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
self.root_ctk.quit()
|
||||||
|
self.ctk_toplevel.withdraw()
|
||||||
|
self.root_ctk.withdraw()
|
||||||
|
|
||||||
|
def main(self):
|
||||||
|
self.execute_tests()
|
||||||
|
self.root_ctk.mainloop()
|
||||||
|
|
||||||
|
def execute_tests(self):
|
||||||
|
print("\nTestCTkToplevel started:")
|
||||||
|
start_time = 0
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_geometry)
|
||||||
|
start_time += 100
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_scaling)
|
||||||
|
start_time += 100
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_configure)
|
||||||
|
start_time += 100
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_appearance_mode)
|
||||||
|
start_time += 100
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.test_iconify)
|
||||||
|
start_time += 1500
|
||||||
|
|
||||||
|
self.root_ctk.after(start_time, self.clean)
|
||||||
|
|
||||||
|
def test_geometry(self):
|
||||||
|
print(" -> test_geometry: ", end="")
|
||||||
|
self.ctk_toplevel.geometry("200x300+200+300")
|
||||||
|
assert self.ctk_toplevel.current_width == 200 and self.ctk_toplevel.current_height == 300
|
||||||
|
|
||||||
|
self.ctk_toplevel.minsize(300, 400)
|
||||||
|
assert self.ctk_toplevel.current_width == 300 and self.ctk_toplevel.current_height == 400
|
||||||
|
assert self.ctk_toplevel.min_width == 300 and self.ctk_toplevel.min_height == 400
|
||||||
|
|
||||||
|
self.ctk_toplevel.maxsize(400, 500)
|
||||||
|
self.ctk_toplevel.geometry("600x600")
|
||||||
|
assert self.ctk_toplevel.current_width == 400 and self.ctk_toplevel.current_height == 500
|
||||||
|
assert self.ctk_toplevel.max_width == 400 and self.ctk_toplevel.max_height == 500
|
||||||
|
|
||||||
|
self.ctk_toplevel.maxsize(1000, 1000)
|
||||||
|
self.ctk_toplevel.geometry("300x400")
|
||||||
|
self.ctk_toplevel.resizable(False, False)
|
||||||
|
self.ctk_toplevel.geometry("500x600")
|
||||||
|
assert self.ctk_toplevel.current_width == 500 and self.ctk_toplevel.current_height == 600
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
def test_scaling(self):
|
||||||
|
print(" -> test_scaling: ", end="")
|
||||||
|
|
||||||
|
customtkinter.ScalingTracker.set_window_scaling(1.5)
|
||||||
|
self.ctk_toplevel.geometry("300x400")
|
||||||
|
self.ctk_toplevel.update()
|
||||||
|
assert self.ctk_toplevel.current_width == 300 and self.ctk_toplevel.current_height == 400
|
||||||
|
assert round(self.ctk_toplevel.winfo_width()) == 450 and round(self.ctk_toplevel.winfo_height()) == 600
|
||||||
|
|
||||||
|
self.ctk_toplevel.maxsize(400, 500)
|
||||||
|
self.ctk_toplevel.geometry("500x500")
|
||||||
|
self.ctk_toplevel.update()
|
||||||
|
assert self.ctk_toplevel.current_width == 400 and self.ctk_toplevel.current_height == 500
|
||||||
|
assert round(self.ctk_toplevel.winfo_width()) == 600 and round(self.ctk_toplevel.winfo_height()) == 750
|
||||||
|
|
||||||
|
customtkinter.ScalingTracker.set_window_scaling(1)
|
||||||
|
self.ctk_toplevel.update()
|
||||||
|
assert self.ctk_toplevel.current_width == 400 and self.ctk_toplevel.current_height == 500
|
||||||
|
assert round(self.ctk_toplevel.winfo_width()) == 400 and round(self.ctk_toplevel.winfo_height()) == 500
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
def test_configure(self):
|
||||||
|
print(" -> test_configure: ", end="")
|
||||||
|
self.ctk_toplevel.configure(bg="white")
|
||||||
|
assert self.ctk_toplevel.fg_color == "white"
|
||||||
|
|
||||||
|
self.ctk_toplevel.configure(background="red")
|
||||||
|
assert self.ctk_toplevel.fg_color == "red"
|
||||||
|
assert self.ctk_toplevel.cget("bg") == "red"
|
||||||
|
|
||||||
|
self.ctk_toplevel.config(fg_color=("green", "#FFFFFF"))
|
||||||
|
assert self.ctk_toplevel.fg_color == ("green", "#FFFFFF")
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
def test_appearance_mode(self):
|
||||||
|
print(" -> test_appearance_mode: ", end="")
|
||||||
|
customtkinter.set_appearance_mode("light")
|
||||||
|
self.ctk_toplevel.config(fg_color=("green", "#FFFFFF"))
|
||||||
|
assert self.ctk_toplevel.cget("bg") == "green"
|
||||||
|
|
||||||
|
customtkinter.set_appearance_mode("dark")
|
||||||
|
assert self.ctk_toplevel.cget("bg") == "#FFFFFF"
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
def test_iconify(self):
|
||||||
|
print(" -> test_iconify: ", end="")
|
||||||
|
self.ctk_toplevel.iconify()
|
||||||
|
self.ctk_toplevel.after(100, self.ctk_toplevel.deiconify)
|
||||||
|
print("successful")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
TestCTkToplevel()
|