mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
Merge 79cd10ddfd
into 09e584634c
This commit is contained in:
commit
baaad651a0
@ -2,49 +2,30 @@ __version__ = "5.1.2"
|
||||
|
||||
import os
|
||||
import sys
|
||||
from tkinter import Variable, StringVar, IntVar, DoubleVar, BooleanVar
|
||||
from tkinter.constants import *
|
||||
import tkinter.filedialog as filedialog
|
||||
|
||||
# import manager classes
|
||||
from .windows.widgets.appearance_mode import AppearanceModeTracker
|
||||
from .windows.widgets.font import FontManager
|
||||
from .windows.widgets.scaling import ScalingTracker
|
||||
from .windows.widgets.theme import ThemeManager
|
||||
from .windows.widgets.core_rendering import DrawEngine
|
||||
|
||||
# import base widgets
|
||||
from .windows.widgets.core_rendering import CTkCanvas
|
||||
from .windows.widgets.core_widget_classes import CTkBaseClass
|
||||
|
||||
# import widgets
|
||||
from .windows.widgets import CTkButton
|
||||
from .windows.widgets import CTkCheckBox
|
||||
from .windows.widgets import CTkComboBox
|
||||
from .windows.widgets import CTkEntry
|
||||
from .windows.widgets import CTkFrame
|
||||
from .windows.widgets import CTkLabel
|
||||
from .windows.widgets import CTkOptionMenu
|
||||
from .windows.widgets import CTkProgressBar
|
||||
from .windows.widgets import CTkRadioButton
|
||||
from .windows.widgets import CTkScrollbar
|
||||
from .windows.widgets import CTkSegmentedButton
|
||||
from .windows.widgets import CTkSlider
|
||||
from .windows.widgets import CTkSwitch
|
||||
from .windows.widgets import CTkTabview
|
||||
from .windows.widgets import CTkTextbox
|
||||
from .windows.widgets import CTkScrollableFrame
|
||||
from tkinter import BooleanVar, DoubleVar, IntVar, StringVar, Variable
|
||||
from tkinter.constants import *
|
||||
|
||||
# import windows
|
||||
from .windows import CTk
|
||||
from .windows import CTkToplevel
|
||||
from .windows import CTkInputDialog
|
||||
|
||||
from .windows import CTk, CTkInputDialog, CTkToplevel
|
||||
# import widgets
|
||||
from .windows.widgets import (CTkButton, CTkCheckBox, CTkComboBox, CTkEntry,
|
||||
CTkFrame, CTkLabel, CTkOptionMenu,
|
||||
CTkProgressBar, CTkRadioButton,
|
||||
CTkScrollableFrame, CTkScrollbar,
|
||||
CTkSegmentedButton, CTkSlider, CTkSwitch,
|
||||
CTkTabview, CTkTextbox)
|
||||
# import manager classes
|
||||
from .windows.widgets.appearance_mode import AppearanceModeTracker
|
||||
# import base widgets
|
||||
from .windows.widgets.core_rendering import CTkCanvas, DrawEngine
|
||||
from .windows.widgets.core_widget_classes import CTkBaseClass
|
||||
# import font classes
|
||||
from .windows.widgets.font import CTkFont
|
||||
|
||||
from .windows.widgets.font import CTkFont, FontManager
|
||||
# import image classes
|
||||
from .windows.widgets.image import CTkImage
|
||||
from .windows.widgets.scaling import ScalingTracker
|
||||
from .windows.widgets.theme import ThemeManager
|
||||
|
||||
_ = Variable, StringVar, IntVar, DoubleVar, BooleanVar, CENTER, filedialog # prevent IDE from removing unused imports
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
from .ctk_input_dialog import CTkInputDialog
|
||||
from .ctk_tk import CTk
|
||||
from .ctk_toplevel import CTkToplevel
|
||||
from .ctk_input_dialog import CTkInputDialog
|
||||
|
@ -1,10 +1,11 @@
|
||||
from typing import Union, Tuple, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
import tkinter
|
||||
from typing import Any
|
||||
|
||||
from .widgets import CTkLabel
|
||||
from .widgets import CTkEntry
|
||||
from .widgets import CTkButton
|
||||
from .widgets.theme import ThemeManager
|
||||
from .ctk_toplevel import CTkToplevel
|
||||
from .widgets import CTkButton, CTkEntry, CTkLabel
|
||||
from .widgets.theme import ThemeManager
|
||||
|
||||
|
||||
class CTkInputDialog(CTkToplevel):
|
||||
@ -14,14 +15,14 @@ class CTkInputDialog(CTkToplevel):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
entry_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
entry_border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
entry_text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
button_fg_color: str | tuple[str, str] | None = None,
|
||||
button_hover_color: str | tuple[str, str] | None = None,
|
||||
button_text_color: str | tuple[str, str] | None = None,
|
||||
entry_fg_color: str | tuple[str, str] | None = None,
|
||||
entry_border_color: str | tuple[str, str] | None = None,
|
||||
entry_text_color: str | tuple[str, str] | None = None,
|
||||
|
||||
title: str = "CTkDialog",
|
||||
text: str = "CTkDialog"):
|
||||
@ -37,7 +38,7 @@ class CTkInputDialog(CTkToplevel):
|
||||
self._entry_border_color = ThemeManager.theme["CTkEntry"]["border_color"] if entry_border_color is None else self._check_color_type(entry_border_color)
|
||||
self._entry_text_color = ThemeManager.theme["CTkEntry"]["text_color"] if entry_text_color is None else self._check_color_type(entry_text_color)
|
||||
|
||||
self._user_input: Union[str, None] = None
|
||||
self._user_input: str | None = None
|
||||
self._running: bool = False
|
||||
self._text = text
|
||||
|
||||
@ -50,7 +51,6 @@ class CTkInputDialog(CTkToplevel):
|
||||
self.grab_set() # make other windows not clickable
|
||||
|
||||
def _create_widgets(self):
|
||||
|
||||
self.grid_columnconfigure((0, 1), weight=1)
|
||||
self.rowconfigure(0, weight=1)
|
||||
|
||||
@ -92,7 +92,7 @@ class CTkInputDialog(CTkToplevel):
|
||||
self.after(150, lambda: self._entry.focus()) # set focus to entry with slight delay, otherwise it won't work
|
||||
self._entry.bind("<Return>", self._ok_event)
|
||||
|
||||
def _ok_event(self, event=None):
|
||||
def _ok_event(self, event: tkinter.Event[Any] | None = None):
|
||||
self._user_input = self._entry.get()
|
||||
self.grab_release()
|
||||
self.destroy()
|
||||
|
@ -1,16 +1,19 @@
|
||||
import tkinter
|
||||
from distutils.version import StrictVersion as Version
|
||||
import sys
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import platform
|
||||
import ctypes
|
||||
from typing import Union, Tuple, Optional
|
||||
import sys
|
||||
import tkinter
|
||||
from distutils.version import StrictVersion as Version
|
||||
from typing import Any
|
||||
|
||||
from customtkinter.windows.widgets.utility.utility_functions import (
|
||||
check_kwargs_empty, pop_from_dict_by_set)
|
||||
|
||||
from .widgets.theme import ThemeManager
|
||||
from .widgets.scaling import CTkScalingBaseClass
|
||||
from .widgets.appearance_mode import CTkAppearanceModeBaseClass
|
||||
|
||||
from customtkinter.windows.widgets.utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty
|
||||
from .widgets.scaling import CTkScalingBaseClass
|
||||
from .widgets.theme import ThemeManager
|
||||
|
||||
|
||||
class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
@ -19,9 +22,9 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
For detailed information check out the documentation.
|
||||
"""
|
||||
|
||||
_valid_tk_constructor_arguments: set = {"screenName", "baseName", "className", "useTk", "sync", "use"}
|
||||
_valid_tk_constructor_arguments: set[str] = {"screenName", "baseName", "className", "useTk", "sync", "use"}
|
||||
|
||||
_valid_tk_configure_arguments: set = {'bd', 'borderwidth', 'class', 'menu', 'relief', 'screen',
|
||||
_valid_tk_configure_arguments: set[str] = {'bd', 'borderwidth', 'class', 'menu', 'relief', 'screen',
|
||||
'use', 'container', 'cursor', 'height',
|
||||
'highlightthickness', 'padx', 'pady', 'takefocus', 'visual', 'width'}
|
||||
|
||||
@ -29,8 +32,8 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
_deactivate_windows_window_header_manipulation: bool = False
|
||||
|
||||
def __init__(self,
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
**kwargs):
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
**kwargs: Any):
|
||||
|
||||
self._enable_macos_dark_title_bar()
|
||||
|
||||
@ -46,7 +49,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
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._last_resizable_args: tuple[list, dict] | None = None # (args, kwargs)
|
||||
|
||||
self._fg_color = ThemeManager.theme["CTk"]["fg_color"] if fg_color is None else self._check_color_type(fg_color)
|
||||
|
||||
@ -86,12 +89,12 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
CTkAppearanceModeBaseClass.destroy(self)
|
||||
CTkScalingBaseClass.destroy(self)
|
||||
|
||||
def _focus_in_event(self, event):
|
||||
def _focus_in_event(self, event: tkinter.Event[Any] | None = None):
|
||||
# sometimes window looses jumps back on macOS if window is selected from Mission Control, so has to be lifted again
|
||||
if sys.platform == "darwin":
|
||||
self.lift()
|
||||
|
||||
def _update_dimensions_event(self, event=None):
|
||||
def _update_dimensions_event(self, event: tkinter.Event[Any] | None = None):
|
||||
if not self._block_update_dimensions_event:
|
||||
|
||||
detected_width = super().winfo_width() # detect current window size
|
||||
@ -104,7 +107,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
self._current_width = self._reverse_window_scaling(detected_width) # adjust current size according to new size given by event
|
||||
self._current_height = self._reverse_window_scaling(detected_height) # _current_width and _current_height are independent of the scale
|
||||
|
||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||
def _set_scaling(self, new_widget_scaling: float, new_window_scaling: float):
|
||||
super()._set_scaling(new_widget_scaling, new_window_scaling)
|
||||
|
||||
# Force new dimensions on window by using min, max, and geometry. Without min, max it won't work.
|
||||
@ -149,7 +152,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
|
||||
super().update()
|
||||
|
||||
def mainloop(self, *args, **kwargs):
|
||||
def mainloop(self, *args: Any, **kwargs: Any):
|
||||
if not self._window_exists:
|
||||
if sys.platform.startswith("win"):
|
||||
self._windows_set_titlebar_color(self._get_appearance_mode())
|
||||
@ -171,7 +174,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
|
||||
return current_resizable_values
|
||||
|
||||
def minsize(self, width: int = None, height: int = None):
|
||||
def minsize(self, width: int | None = None, height: int | None = None):
|
||||
self._min_width = width
|
||||
self._min_height = height
|
||||
if self._current_width < width:
|
||||
@ -180,7 +183,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
self._current_height = height
|
||||
super().minsize(self._apply_window_scaling(self._min_width), self._apply_window_scaling(self._min_height))
|
||||
|
||||
def maxsize(self, width: int = None, height: int = None):
|
||||
def maxsize(self, width: int | None = None, height: int | None = None):
|
||||
self._max_width = width
|
||||
self._max_height = height
|
||||
if self._current_width > width:
|
||||
@ -189,19 +192,19 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
self._current_height = height
|
||||
super().maxsize(self._apply_window_scaling(self._max_width), self._apply_window_scaling(self._max_height))
|
||||
|
||||
def geometry(self, geometry_string: str = None):
|
||||
def geometry(self, geometry_string: str | None = None):
|
||||
if geometry_string is not None:
|
||||
super().geometry(self._apply_geometry_scaling(geometry_string))
|
||||
|
||||
# update width and height attributes
|
||||
width, height, x, y = self._parse_geometry_string(geometry_string)
|
||||
width, height, *_ = self._parse_geometry_string(geometry_string)
|
||||
if width is not None and height is not None:
|
||||
self._current_width = max(self._min_width, min(width, self._max_width)) # bound value between min and max
|
||||
self._current_height = max(self._min_height, min(height, self._max_height))
|
||||
else:
|
||||
return self._reverse_geometry_scaling(super().geometry())
|
||||
|
||||
def configure(self, **kwargs):
|
||||
def configure(self, **kwargs: Any):
|
||||
if "fg_color" in kwargs:
|
||||
self._fg_color = self._check_color_type(kwargs.pop("fg_color"))
|
||||
super().configure(bg=self._apply_appearance_mode(self._fg_color))
|
||||
@ -215,7 +218,7 @@ class CTk(tkinter.Tk, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
super().configure(**pop_from_dict_by_set(kwargs, self._valid_tk_configure_arguments))
|
||||
check_kwargs_empty(kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "fg_color":
|
||||
return self._fg_color
|
||||
else:
|
||||
|
@ -1,16 +1,24 @@
|
||||
import tkinter
|
||||
from distutils.version import StrictVersion as Version
|
||||
import sys
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import platform
|
||||
import ctypes
|
||||
from typing import Union, Tuple, Optional
|
||||
import sys
|
||||
import tkinter
|
||||
from distutils.version import StrictVersion as Version
|
||||
from typing import Any
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from customtkinter.windows.widgets.utility.utility_functions import (
|
||||
check_kwargs_empty, pop_from_dict_by_set)
|
||||
|
||||
from .widgets.theme import ThemeManager
|
||||
from .widgets.scaling import CTkScalingBaseClass
|
||||
from .widgets.appearance_mode import CTkAppearanceModeBaseClass
|
||||
|
||||
from customtkinter.windows.widgets.utility.utility_functions import pop_from_dict_by_set, check_kwargs_empty
|
||||
from .widgets.scaling import CTkScalingBaseClass
|
||||
from .widgets.theme import ThemeManager
|
||||
|
||||
|
||||
class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
@ -19,16 +27,16 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
For detailed information check out the documentation.
|
||||
"""
|
||||
|
||||
_valid_tk_toplevel_arguments: set = {"bd", "borderwidth", "class", "container", "cursor", "height",
|
||||
"highlightbackground", "highlightthickness", "menu", "relief",
|
||||
"screen", "takefocus", "use", "visual", "width"}
|
||||
_valid_tk_toplevel_arguments: set[str] = {"bd", "borderwidth", "class", "container", "cursor", "height",
|
||||
"highlightbackground", "highlightthickness", "menu", "relief",
|
||||
"screen", "takefocus", "use", "visual", "width"}
|
||||
|
||||
_deactivate_macos_window_header_manipulation: bool = False
|
||||
_deactivate_windows_window_header_manipulation: bool = False
|
||||
|
||||
def __init__(self, *args,
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
**kwargs):
|
||||
def __init__(self, *args: Any,
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
**kwargs: Any):
|
||||
|
||||
self._enable_macos_dark_title_bar()
|
||||
|
||||
@ -48,11 +56,11 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
|
||||
self._current_width = 200 # initial window size, always without scaling
|
||||
self._current_height = 200
|
||||
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._min_width: int | None = 0
|
||||
self._min_height: int | None = 0
|
||||
self._max_width: int | None = 1_000_000
|
||||
self._max_height: int | None = 1_000_000
|
||||
self._last_resizable_args: tuple[list[int], dict[str, float]] | None = None # (args, kwargs)
|
||||
|
||||
self._fg_color = ThemeManager.theme["CTkToplevel"]["fg_color"] if fg_color is None else self._check_color_type(fg_color)
|
||||
|
||||
@ -92,12 +100,12 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
CTkAppearanceModeBaseClass.destroy(self)
|
||||
CTkScalingBaseClass.destroy(self)
|
||||
|
||||
def _focus_in_event(self, event):
|
||||
def _focus_in_event(self, event: tkinter.Event[Any] | None = None):
|
||||
# sometimes window looses jumps back on macOS if window is selected from Mission Control, so has to be lifted again
|
||||
if sys.platform == "darwin":
|
||||
self.lift()
|
||||
|
||||
def _update_dimensions_event(self, event=None):
|
||||
def _update_dimensions_event(self, event: tkinter.Event[Any] | None = None):
|
||||
if not self._block_update_dimensions_event:
|
||||
detected_width = self.winfo_width() # detect current window size
|
||||
detected_height = self.winfo_height()
|
||||
@ -106,7 +114,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
self._current_width = self._reverse_window_scaling(detected_width) # adjust current size according to new size given by event
|
||||
self._current_height = self._reverse_window_scaling(detected_height) # _current_width and _current_height are independent of the scale
|
||||
|
||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||
def _set_scaling(self, new_widget_scaling: float, new_window_scaling: float):
|
||||
super()._set_scaling(new_widget_scaling, new_window_scaling)
|
||||
|
||||
# Force new dimensions on window by using min, max, and geometry. Without min, max it won't work.
|
||||
@ -130,7 +138,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
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 geometry(self, geometry_string: str = None):
|
||||
def geometry(self, geometry_string: str | None = None):
|
||||
if geometry_string is not None:
|
||||
super().geometry(self._apply_geometry_scaling(geometry_string))
|
||||
|
||||
@ -152,7 +160,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
self._iconify_called_after_windows_set_titlebar_color = True
|
||||
super().iconify()
|
||||
|
||||
def resizable(self, width: bool = None, height: bool = None):
|
||||
def resizable(self, width: bool | None = None, height: bool | None = None):
|
||||
current_resizable_values = super().resizable(width, height)
|
||||
self._last_resizable_args = ([], {"width": width, "height": height})
|
||||
|
||||
@ -161,7 +169,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
|
||||
return current_resizable_values
|
||||
|
||||
def minsize(self, width=None, height=None):
|
||||
def minsize(self, width: int | None = None, height: int | None = None):
|
||||
self._min_width = width
|
||||
self._min_height = height
|
||||
if self._current_width < width:
|
||||
@ -170,7 +178,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
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):
|
||||
def maxsize(self, width: int | None = None, height: int | None = None):
|
||||
self._max_width = width
|
||||
self._max_height = height
|
||||
if self._current_width > width:
|
||||
@ -179,7 +187,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
self._current_height = height
|
||||
super().maxsize(self._apply_window_scaling(self._max_width), self._apply_window_scaling(self._max_height))
|
||||
|
||||
def configure(self, **kwargs):
|
||||
def configure(self, **kwargs: Any):
|
||||
if "fg_color" in kwargs:
|
||||
self._fg_color = self._check_color_type(kwargs.pop("fg_color"))
|
||||
super().configure(bg=self._apply_appearance_mode(self._fg_color))
|
||||
@ -193,15 +201,15 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
super().configure(**pop_from_dict_by_set(kwargs, self._valid_tk_toplevel_arguments))
|
||||
check_kwargs_empty(kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "fg_color":
|
||||
return self._fg_color
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def wm_iconbitmap(self, bitmap=None, default=None):
|
||||
def wm_iconbitmap(self, bitmap: Any = None, default: Any = None):
|
||||
self._iconbitmap_method_called = True
|
||||
super().wm_iconbitmap(bitmap, default)
|
||||
super().wm_iconbitmap(bitmap, default) # type: ignore
|
||||
|
||||
def _windows_set_titlebar_icon(self):
|
||||
try:
|
||||
@ -298,7 +306,7 @@ class CTkToplevel(tkinter.Toplevel, CTkAppearanceModeBaseClass, CTkScalingBaseCl
|
||||
self._withdraw_called_after_windows_set_titlebar_color = False
|
||||
self._iconify_called_after_windows_set_titlebar_color = False
|
||||
|
||||
def _set_appearance_mode(self, mode_string):
|
||||
def _set_appearance_mode(self, mode_string: Literal["light", "dark"]):
|
||||
super()._set_appearance_mode(mode_string)
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
|
@ -7,10 +7,10 @@ from .ctk_label import CTkLabel
|
||||
from .ctk_optionmenu import CTkOptionMenu
|
||||
from .ctk_progressbar import CTkProgressBar
|
||||
from .ctk_radiobutton import CTkRadioButton
|
||||
from .ctk_scrollable_frame import CTkScrollableFrame
|
||||
from .ctk_scrollbar import CTkScrollbar
|
||||
from .ctk_segmented_button import CTkSegmentedButton
|
||||
from .ctk_slider import CTkSlider
|
||||
from .ctk_switch import CTkSwitch
|
||||
from .ctk_tabview import CTkTabview
|
||||
from .ctk_textbox import CTkTextbox
|
||||
from .ctk_scrollable_frame import CTkScrollableFrame
|
||||
|
@ -1,4 +1,11 @@
|
||||
from typing import Union, Tuple, List
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .appearance_mode_tracker import AppearanceModeTracker
|
||||
|
||||
@ -19,7 +26,7 @@ class CTkAppearanceModeBaseClass:
|
||||
def destroy(self):
|
||||
AppearanceModeTracker.remove(self._set_appearance_mode)
|
||||
|
||||
def _set_appearance_mode(self, mode_string: str):
|
||||
def _set_appearance_mode(self, mode_string: Literal["light", "dark"]):
|
||||
""" can be overridden but super method must be called at the beginning """
|
||||
if mode_string.lower() == "dark":
|
||||
self.__appearance_mode = 1
|
||||
@ -33,7 +40,7 @@ class CTkAppearanceModeBaseClass:
|
||||
else:
|
||||
return "dark"
|
||||
|
||||
def _apply_appearance_mode(self, color: Union[str, Tuple[str, str], List[str]]) -> str:
|
||||
def _apply_appearance_mode(self, color: str | tuple[str, str] | list[str]) -> str:
|
||||
"""
|
||||
color can be either a single hex color string or a color name or it can be a
|
||||
tuple color with (light_color, dark_color). The functions returns
|
||||
@ -46,7 +53,7 @@ class CTkAppearanceModeBaseClass:
|
||||
return color
|
||||
|
||||
@staticmethod
|
||||
def _check_color_type(color: any, transparency: bool = False):
|
||||
def _check_color_type(color: Literal["transparent"] | str | tuple[str, str] | None, transparency: bool = False):
|
||||
if color is None:
|
||||
raise ValueError(f"color is None, for transparency set color='transparent'")
|
||||
elif isinstance(color, (tuple, list)) and (color[0] == "transparent" or color[1] == "transparent"):
|
||||
|
@ -1,10 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import tkinter
|
||||
from distutils.version import StrictVersion as Version
|
||||
from typing import Callable
|
||||
from typing import Callable, TYPE_CHECKING
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from customtkinter.windows.ctk_tk import CTk
|
||||
|
||||
try:
|
||||
import darkdetect
|
||||
import darkdetect # type: ignore
|
||||
|
||||
if Version(darkdetect.__version__) < Version("0.3.1"):
|
||||
sys.stderr.write("WARNING: You have to upgrade the darkdetect library: pip3 install --upgrade darkdetect\n")
|
||||
@ -18,13 +28,13 @@ except Exception:
|
||||
|
||||
class AppearanceModeTracker:
|
||||
|
||||
callback_list = []
|
||||
app_list = []
|
||||
callback_list: list[Callable[[str], None]] = []
|
||||
app_list: list[CTk] = []
|
||||
update_loop_running = False
|
||||
update_loop_interval = 30 # milliseconds
|
||||
|
||||
appearance_mode_set_by = "system"
|
||||
appearance_mode = 0 # Light (standard)
|
||||
appearance_mode_set_by: Literal["system", "user"] = "system"
|
||||
appearance_mode: Literal[0, 1] = 0 # 0 = Light, 1 = Dark
|
||||
|
||||
@classmethod
|
||||
def init_appearance_mode(cls):
|
||||
@ -36,7 +46,7 @@ class AppearanceModeTracker:
|
||||
cls.update_callbacks()
|
||||
|
||||
@classmethod
|
||||
def add(cls, callback: Callable, widget=None):
|
||||
def add(cls, callback: Callable[[str], None], widget: CTk | None = None):
|
||||
cls.callback_list.append(callback)
|
||||
|
||||
if widget is not None:
|
||||
@ -49,16 +59,16 @@ class AppearanceModeTracker:
|
||||
cls.update_loop_running = True
|
||||
|
||||
@classmethod
|
||||
def remove(cls, callback: Callable):
|
||||
def remove(cls, callback: Callable[[str], None]):
|
||||
try:
|
||||
cls.callback_list.remove(callback)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def detect_appearance_mode() -> int:
|
||||
def detect_appearance_mode() -> Literal[0, 1]:
|
||||
try:
|
||||
if darkdetect.theme() == "Dark":
|
||||
if darkdetect.theme() == "Dark": # type: ignore
|
||||
return 1 # Dark
|
||||
else:
|
||||
return 0 # Light
|
||||
@ -66,7 +76,7 @@ class AppearanceModeTracker:
|
||||
return 0 # Light
|
||||
|
||||
@classmethod
|
||||
def get_tk_root_of_widget(cls, widget):
|
||||
def get_tk_root_of_widget(cls, widget: CTk) -> CTk:
|
||||
current_widget = widget
|
||||
|
||||
while isinstance(current_widget, tkinter.Tk) is False:
|
||||
|
@ -1,6 +1,9 @@
|
||||
import tkinter
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Union, Tuple
|
||||
import tkinter
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
class CTkCanvas(tkinter.Canvas):
|
||||
@ -25,11 +28,11 @@ class CTkCanvas(tkinter.Canvas):
|
||||
not can be a problem when using only a single circle character.
|
||||
"""
|
||||
|
||||
radius_to_char_fine: dict = None # dict to map radius to font circle character
|
||||
radius_to_char_fine: dict[int, str] = {} # dict to map radius to font circle character
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, *args: Any, **kwargs: Any):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._aa_circle_canvas_ids = set()
|
||||
self._aa_circle_canvas_ids: set[str | int] = set()
|
||||
|
||||
@classmethod
|
||||
def init_font_character_mapping(cls):
|
||||
@ -71,7 +74,7 @@ class CTkCanvas(tkinter.Canvas):
|
||||
return self.radius_to_char_fine[radius]
|
||||
|
||||
def create_aa_circle(self, x_pos: int, y_pos: int, radius: int, angle: int = 0, fill: str = "white",
|
||||
tags: Union[str, Tuple[str, ...]] = "", anchor: str = tkinter.CENTER) -> int:
|
||||
tags: str | tuple[str, ...] = "", anchor: str = tkinter.CENTER) -> int:
|
||||
# create a circle with a font element
|
||||
circle_1 = self.create_text(x_pos, y_pos, text=self._get_char_from_radius(radius), anchor=anchor, fill=fill,
|
||||
font=("CustomTkinter_shapes_font", -radius * 2), tags=tags, angle=angle)
|
||||
@ -80,8 +83,7 @@ class CTkCanvas(tkinter.Canvas):
|
||||
|
||||
return circle_1
|
||||
|
||||
def coords(self, tag_or_id, *args):
|
||||
|
||||
def coords(self, tag_or_id: int | str, *args: Any):
|
||||
if type(tag_or_id) == str and "ctk_aa_circle_font_element" in self.gettags(tag_or_id):
|
||||
coords_id = self.find_withtag(tag_or_id)[0] # take the lowest id for the given tag
|
||||
super().coords(coords_id, *args[:2])
|
||||
@ -98,7 +100,7 @@ class CTkCanvas(tkinter.Canvas):
|
||||
else:
|
||||
super().coords(tag_or_id, *args)
|
||||
|
||||
def itemconfig(self, tag_or_id, *args, **kwargs):
|
||||
def itemconfig(self, tag_or_id: int | str, *args: Any, **kwargs: Any):
|
||||
kwargs_except_outline = kwargs.copy()
|
||||
if "outline" in kwargs_except_outline:
|
||||
del kwargs_except_outline["outline"]
|
||||
|
@ -1,8 +1,14 @@
|
||||
from __future__ import annotations
|
||||
import sys
|
||||
|
||||
import math
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Union, TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..core_rendering import CTkCanvas
|
||||
@ -26,7 +32,7 @@ class DrawEngine:
|
||||
|
||||
"""
|
||||
|
||||
preferred_drawing_method: str = None # 'polygon_shapes', 'font_shapes', 'circle_shapes'
|
||||
preferred_drawing_method: Literal["polygon_shapes", "font_shapes", "circle_shapes"] = None
|
||||
|
||||
def __init__(self, canvas: CTkCanvas):
|
||||
self._canvas = canvas
|
||||
@ -37,7 +43,7 @@ class DrawEngine:
|
||||
self._round_width_to_even_numbers: bool = round_width_to_even_numbers
|
||||
self._round_height_to_even_numbers: bool = round_height_to_even_numbers
|
||||
|
||||
def __calc_optimal_corner_radius(self, user_corner_radius: Union[float, int]) -> Union[float, int]:
|
||||
def __calc_optimal_corner_radius(self, user_corner_radius: float | int) -> float | int:
|
||||
# optimize for drawing with polygon shapes
|
||||
if self.preferred_drawing_method == "polygon_shapes":
|
||||
if sys.platform == "darwin":
|
||||
@ -61,7 +67,7 @@ class DrawEngine:
|
||||
else:
|
||||
return user_corner_radius
|
||||
|
||||
def draw_background_corners(self, width: Union[float, int], height: Union[float, int], ):
|
||||
def draw_background_corners(self, width: float | int, height: float | int):
|
||||
if self._round_width_to_even_numbers:
|
||||
width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only
|
||||
if self._round_height_to_even_numbers:
|
||||
@ -93,8 +99,8 @@ class DrawEngine:
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def draw_rounded_rect_with_border(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
|
||||
border_width: Union[float, int], overwrite_preferred_drawing_method: str = None) -> bool:
|
||||
def draw_rounded_rect_with_border(self, width: float | int, height: float | int, corner_radius: float | int,
|
||||
border_width: float | int, overwrite_preferred_drawing_method: str | None = None) -> bool:
|
||||
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag,
|
||||
the main foreground elements have an 'inner_parts' tag to color the elements accordingly.
|
||||
|
||||
@ -184,7 +190,7 @@ class DrawEngine:
|
||||
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) -> bool:
|
||||
exclude_parts: tuple[str, ...]) -> bool:
|
||||
requires_recoloring = False
|
||||
|
||||
# create border button parts
|
||||
@ -396,8 +402,8 @@ class DrawEngine:
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def draw_rounded_rect_with_border_vertical_split(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
|
||||
border_width: Union[float, int], left_section_width: Union[float, int]) -> bool:
|
||||
def draw_rounded_rect_with_border_vertical_split(self, width: float | int, height: float | int, corner_radius: float | int,
|
||||
border_width: float | int, left_section_width: float | int) -> bool:
|
||||
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas which is split at left_section_width.
|
||||
The border elements have the tags 'border_parts_left', 'border_parts_lright',
|
||||
the main foreground elements have an 'inner_parts_left' and inner_parts_right' tag,
|
||||
@ -690,8 +696,8 @@ class DrawEngine:
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def draw_rounded_progress_bar_with_border(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
|
||||
border_width: Union[float, int], progress_value_1: float, progress_value_2: float, orientation: str) -> bool:
|
||||
def draw_rounded_progress_bar_with_border(self, width: float | int, height: float | int, corner_radius: float | int,
|
||||
border_width: float | int, progress_value_1: float, progress_value_2: float, orientation: str) -> bool:
|
||||
""" Draws a rounded bar on the canvas, and onntop sits a progress bar from value 1 to value 2 (range 0-1, left to right, bottom to top).
|
||||
The border elements get the 'border_parts' tag", the main elements get the 'inner_parts' tag and
|
||||
the progress elements get the 'progress_parts' tag. The 'orientation' argument defines from which direction the progress starts (n, w, s, e).
|
||||
@ -868,8 +874,8 @@ class DrawEngine:
|
||||
|
||||
return requires_recoloring or requires_recoloring_2
|
||||
|
||||
def draw_rounded_slider_with_border_and_button(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
|
||||
border_width: Union[float, int], button_length: Union[float, int], button_corner_radius: Union[float, int],
|
||||
def draw_rounded_slider_with_border_and_button(self, width: float | int, height: float | int, corner_radius: float | int,
|
||||
border_width: float | int, button_length: float | int, button_corner_radius: float | int,
|
||||
slider_value: float, orientation: str) -> bool:
|
||||
|
||||
if self._round_width_to_even_numbers:
|
||||
@ -1028,8 +1034,8 @@ class DrawEngine:
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def draw_rounded_scrollbar(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
|
||||
border_spacing: Union[float, int], start_value: float, end_value: float, orientation: str) -> bool:
|
||||
def draw_rounded_scrollbar(self, width: float | int, height: float | int, corner_radius: float | int,
|
||||
border_spacing: float | int, start_value: float, end_value: float, orientation: str) -> bool:
|
||||
|
||||
if self._round_width_to_even_numbers:
|
||||
width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only
|
||||
@ -1171,7 +1177,7 @@ class DrawEngine:
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def draw_checkmark(self, width: Union[float, int], height: Union[float, int], size: Union[int, float]) -> bool:
|
||||
def draw_checkmark(self, width: float | int, height: float | int, size: int | float) -> bool:
|
||||
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag,
|
||||
the main foreground elements have an 'inner_parts' tag to color the elements accordingly.
|
||||
|
||||
@ -1201,7 +1207,7 @@ class DrawEngine:
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def draw_dropdown_arrow(self, x_position: Union[int, float], y_position: Union[int, float], size: Union[int, float]) -> bool:
|
||||
def draw_dropdown_arrow(self, x_position: int | float, y_position: int | float, size: int | float) -> bool:
|
||||
""" Draws a dropdown bottom facing arrow at (x_position, y_position) in a given size
|
||||
|
||||
returns bool if recoloring is necessary """
|
||||
|
@ -1,2 +1,2 @@
|
||||
from .dropdown_menu import DropdownMenu
|
||||
from .ctk_base_class import CTkBaseClass
|
||||
from .dropdown_menu import DropdownMenu
|
||||
|
@ -1,8 +1,9 @@
|
||||
import sys
|
||||
import warnings
|
||||
from __future__ import annotations
|
||||
|
||||
import tkinter
|
||||
import tkinter.ttk as ttk
|
||||
from typing import Union, Callable, Tuple
|
||||
import warnings
|
||||
from typing import Any, Callable
|
||||
|
||||
try:
|
||||
from typing import TypedDict
|
||||
@ -10,14 +11,12 @@ except ImportError:
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from .... import windows # import windows for isinstance checks
|
||||
|
||||
from ..theme import ThemeManager
|
||||
from ..appearance_mode import CTkAppearanceModeBaseClass
|
||||
from ..font import CTkFont
|
||||
from ..image import CTkImage
|
||||
from ..appearance_mode import CTkAppearanceModeBaseClass
|
||||
from ..scaling import CTkScalingBaseClass
|
||||
|
||||
from ..utility import pop_from_dict_by_set, check_kwargs_empty
|
||||
from ..theme import ThemeManager
|
||||
from ..utility import check_kwargs_empty, pop_from_dict_by_set
|
||||
|
||||
|
||||
class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
@ -25,17 +24,17 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
appearance_mode changes, scaling, bg changes of master if master is not a CTk widget """
|
||||
|
||||
# attributes that are passed to and managed by the tkinter frame only:
|
||||
_valid_tk_frame_attributes: set = {"cursor"}
|
||||
_valid_tk_frame_attributes: set[str] = {"cursor"}
|
||||
|
||||
_cursor_manipulation_enabled: bool = True
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
width: int = 0,
|
||||
height: int = 0,
|
||||
master: Any,
|
||||
width: int | str = 0,
|
||||
height: int | str = 0,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
**kwargs):
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
**kwargs: Any):
|
||||
|
||||
# call init methods of super classes
|
||||
tkinter.Frame.__init__(self, master=master, width=width, height=height, **pop_from_dict_by_set(kwargs, self._valid_tk_frame_attributes))
|
||||
@ -57,12 +56,12 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
|
||||
# save latest geometry function and kwargs
|
||||
class GeometryCallDict(TypedDict):
|
||||
function: Callable
|
||||
kwargs: dict
|
||||
self._last_geometry_manager_call: Union[GeometryCallDict, None] = None
|
||||
function: Callable[..., None]
|
||||
kwargs: dict[Any, Any]
|
||||
self._last_geometry_manager_call: GeometryCallDict | None = None
|
||||
|
||||
# background color
|
||||
self._bg_color: Union[str, Tuple[str, str]] = self._detect_color_of_master() if bg_color == "transparent" else self._check_color_type(bg_color, transparency=True)
|
||||
self._bg_color: str | tuple[str, str] = self._detect_color_of_master() if bg_color == "transparent" else self._check_color_type(bg_color, transparency=True)
|
||||
|
||||
# set bg color of tkinter.Frame
|
||||
super().configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||
@ -74,7 +73,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
if isinstance(self.master, (tkinter.Tk, tkinter.Toplevel, tkinter.Frame, tkinter.LabelFrame, ttk.Frame, ttk.LabelFrame, ttk.Notebook)) and not isinstance(self.master, (CTkBaseClass, CTkAppearanceModeBaseClass)):
|
||||
master_old_configure = self.master.config
|
||||
|
||||
def new_configure(*args, **kwargs):
|
||||
def new_configure(*args, **kwargs: Any):
|
||||
if "bg" in kwargs:
|
||||
self.configure(bg_color=kwargs["bg"])
|
||||
elif "background" in kwargs:
|
||||
@ -107,10 +106,10 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
# super().configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||
pass
|
||||
|
||||
def config(self, *args, **kwargs):
|
||||
def config(self, *args: Any, **kwargs: Any):
|
||||
raise AttributeError("'config' is not implemented for CTk widgets. For consistency, always use 'configure' instead.")
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool =False, **kwargs: Any):
|
||||
""" basic configure with bg_color, width, height support, calls configure of tkinter.Frame, updates in the end """
|
||||
|
||||
if "width" in kwargs:
|
||||
@ -150,7 +149,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
else:
|
||||
raise ValueError(f"'{attribute_name}' is not a supported argument. Look at the documentation for supported arguments.")
|
||||
|
||||
def _check_font_type(self, font: any):
|
||||
def _check_font_type(self, font: CTkFont | tuple[Any, ...]):
|
||||
""" check font type when passed to widget """
|
||||
if isinstance(font, CTkFont):
|
||||
return font
|
||||
@ -169,7 +168,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
f"font=customtkinter.CTkFont(family='<name>', size=<size in px>)\n" +
|
||||
f"font=('<name>', <size in px>)\n")
|
||||
|
||||
def _check_image_type(self, image: any):
|
||||
def _check_image_type(self, image: Any):
|
||||
""" check image type when passed to widget """
|
||||
if image is None:
|
||||
return image
|
||||
@ -179,7 +178,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
warnings.warn(f"{type(self).__name__} Warning: Given image is not CTkImage but {type(image)}. Image can not be scaled on HighDPI displays, use CTkImage instead.\n")
|
||||
return image
|
||||
|
||||
def _update_dimensions_event(self, event):
|
||||
def _update_dimensions_event(self, event: tkinter.Event[Any] | None = None):
|
||||
# only redraw if dimensions changed (for performance), independent of scaling
|
||||
if round(self._current_width) != round(self._reverse_widget_scaling(event.width)) or round(self._current_height) != round(self._reverse_widget_scaling(event.height)):
|
||||
self._current_width = self._reverse_widget_scaling(event.width) # adjust current size according to new size given by event
|
||||
@ -187,7 +186,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
|
||||
self._draw(no_color_updates=True) # faster drawing without color changes
|
||||
|
||||
def _detect_color_of_master(self, master_widget=None) -> Union[str, Tuple[str, str]]:
|
||||
def _detect_color_of_master(self, master_widget=None) -> str | tuple[str, str]:
|
||||
""" detect foreground color of master widget for bg_color and transparent color """
|
||||
|
||||
if master_widget is None:
|
||||
@ -222,7 +221,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
self._draw()
|
||||
super().update_idletasks()
|
||||
|
||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||
def _set_scaling(self, new_widget_scaling: float, new_window_scaling: float):
|
||||
super()._set_scaling(new_widget_scaling, new_window_scaling)
|
||||
|
||||
super().configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -231,7 +230,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
if self._last_geometry_manager_call is not None:
|
||||
self._last_geometry_manager_call["function"](**self._apply_argument_scaling(self._last_geometry_manager_call["kwargs"]))
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
if width is not None:
|
||||
self._desired_width = width
|
||||
if height is not None:
|
||||
@ -240,19 +239,19 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
super().configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
|
||||
def bind(self, sequence=None, command=None, add=None):
|
||||
def bind(self, sequence: str, command: Callable[..., None], add: bool = False):
|
||||
raise NotImplementedError
|
||||
|
||||
def unbind(self, sequence=None, funcid=None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
raise NotImplementedError
|
||||
|
||||
def unbind_all(self, sequence):
|
||||
def unbind_all(self, sequence: str):
|
||||
raise AttributeError("'unbind_all' is not allowed, because it would delete necessary internal callbacks for all widgets")
|
||||
|
||||
def bind_all(self, sequence=None, func=None, add=None):
|
||||
def bind_all(self, sequence: str, func: Callable[..., None], add: bool = False):
|
||||
raise AttributeError("'bind_all' is not allowed, could result in undefined behavior")
|
||||
|
||||
def place(self, **kwargs):
|
||||
def place(self, **kwargs: Any):
|
||||
"""
|
||||
Place a widget in the parent widget. Use as options:
|
||||
in=master - master relative to which the widget is placed
|
||||
@ -278,7 +277,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
self._last_geometry_manager_call = None
|
||||
return super().place_forget()
|
||||
|
||||
def pack(self, **kwargs):
|
||||
def pack(self, **kwargs: Any):
|
||||
"""
|
||||
Pack a widget in the parent widget. Use as options:
|
||||
after=widget - pack it after you have packed widget
|
||||
@ -302,7 +301,7 @@ class CTkBaseClass(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClas
|
||||
self._last_geometry_manager_call = None
|
||||
return super().pack_forget()
|
||||
|
||||
def grid(self, **kwargs):
|
||||
def grid(self, **kwargs: Any):
|
||||
"""
|
||||
Position a widget in the parent widget in a grid. Use as options:
|
||||
column=number - use cell identified with given column (starting with 0)
|
||||
|
@ -1,25 +1,27 @@
|
||||
import tkinter
|
||||
import sys
|
||||
from typing import Union, Tuple, Callable, List, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
from ..theme import ThemeManager
|
||||
from ..font import CTkFont
|
||||
from ..appearance_mode import CTkAppearanceModeBaseClass
|
||||
from ..font import CTkFont
|
||||
from ..scaling import CTkScalingBaseClass
|
||||
from ..theme import ThemeManager
|
||||
|
||||
|
||||
class DropdownMenu(tkinter.Menu, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
def __init__(self, *args,
|
||||
def __init__(self, *args: Any,
|
||||
min_character_width: int = 18,
|
||||
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
hover_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
command: Union[Callable, None] = None,
|
||||
values: Optional[List[str]] = None,
|
||||
**kwargs):
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
command: Callable[..., None] | None = None,
|
||||
values: list[str] | None = None,
|
||||
**kwargs: Any):
|
||||
|
||||
# call init methods of super classes
|
||||
tkinter.Menu.__init__(self, *args, **kwargs)
|
||||
@ -105,7 +107,7 @@ class DropdownMenu(tkinter.Menu, CTkAppearanceModeBaseClass, CTkScalingBaseClass
|
||||
if self._command is not None:
|
||||
self._command(value)
|
||||
|
||||
def open(self, x: Union[int, float], y: Union[int, float]):
|
||||
def open(self, x: int | float, y: int | float):
|
||||
|
||||
if sys.platform == "darwin":
|
||||
y += self._apply_widget_scaling(8)
|
||||
@ -117,7 +119,7 @@ class DropdownMenu(tkinter.Menu, CTkAppearanceModeBaseClass, CTkScalingBaseClass
|
||||
else: # Linux
|
||||
self.tk_popup(int(x), int(y))
|
||||
|
||||
def configure(self, **kwargs):
|
||||
def configure(self, **kwargs: Any):
|
||||
if "fg_color" in kwargs:
|
||||
self._fg_color = self._check_color_type(kwargs.pop("fg_color"))
|
||||
super().configure(bg=self._apply_appearance_mode(self._fg_color))
|
||||
@ -148,7 +150,7 @@ class DropdownMenu(tkinter.Menu, CTkAppearanceModeBaseClass, CTkScalingBaseClass
|
||||
|
||||
super().configure(**kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "min_character_width":
|
||||
return self._min_character_width
|
||||
|
||||
@ -170,7 +172,7 @@ class DropdownMenu(tkinter.Menu, CTkAppearanceModeBaseClass, CTkScalingBaseClass
|
||||
return super().cget(attribute_name)
|
||||
|
||||
@staticmethod
|
||||
def _check_font_type(font: any):
|
||||
def _check_font_type(font: Any):
|
||||
if isinstance(font, CTkFont):
|
||||
return font
|
||||
|
||||
@ -188,7 +190,7 @@ class DropdownMenu(tkinter.Menu, CTkAppearanceModeBaseClass, CTkScalingBaseClass
|
||||
f"font=customtkinter.CTkFont(family='<name>', size=<size in px>)\n" +
|
||||
f"font=('<name>', <size in px>)\n")
|
||||
|
||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||
def _set_scaling(self, new_widget_scaling: float, new_window_scaling: float):
|
||||
super()._set_scaling(new_widget_scaling, new_window_scaling)
|
||||
self._configure_menu_for_platforms()
|
||||
|
||||
|
@ -1,14 +1,22 @@
|
||||
import tkinter
|
||||
import sys
|
||||
from typing import Union, Tuple, Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable, TYPE_CHECKING
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .font import CTkFont
|
||||
from .image import CTkImage
|
||||
from .theme import ThemeManager
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from PIL import ImageTk
|
||||
|
||||
class CTkButton(CTkBaseClass):
|
||||
"""
|
||||
@ -19,34 +27,34 @@ class CTkButton(CTkBaseClass):
|
||||
_image_label_spacing: int = 6
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 140,
|
||||
height: int = 28,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
border_spacing: int = 2,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
hover_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
text_color_disabled: str | tuple[str, str] | None = None,
|
||||
|
||||
background_corner_colors: Union[Tuple[Union[str, Tuple[str, str]]], None] = None,
|
||||
background_corner_colors: tuple[str | tuple[str, str]] | None = None,
|
||||
round_width_to_even_numbers: bool = True,
|
||||
round_height_to_even_numbers: bool = True,
|
||||
|
||||
text: str = "CTkButton",
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
textvariable: Union[tkinter.Variable, None] = None,
|
||||
image: Union[CTkImage, "ImageTk.PhotoImage", None] = None,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
textvariable: tkinter.Variable | None = None,
|
||||
image: CTkImage | ImageTk.PhotoImage | None = None,
|
||||
state: str = "normal",
|
||||
hover: bool = True,
|
||||
command: Union[Callable[[], None], None] = None,
|
||||
command: Callable[[], None] | None = None,
|
||||
compound: str = "left",
|
||||
anchor: str = "center",
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
@ -58,38 +66,38 @@ class CTkButton(CTkBaseClass):
|
||||
self._border_spacing: int = border_spacing
|
||||
|
||||
# color
|
||||
self._fg_color: Union[str, Tuple[str, str]] = ThemeManager.theme["CTkButton"]["fg_color"] if fg_color is None else self._check_color_type(fg_color, transparency=True)
|
||||
self._hover_color: Union[str, Tuple[str, str]] = ThemeManager.theme["CTkButton"]["hover_color"] if hover_color is None else self._check_color_type(hover_color)
|
||||
self._border_color: Union[str, Tuple[str, str]] = ThemeManager.theme["CTkButton"]["border_color"] if border_color is None else self._check_color_type(border_color)
|
||||
self._text_color: Union[str, Tuple[str, str]] = ThemeManager.theme["CTkButton"]["text_color"] if text_color is None else self._check_color_type(text_color)
|
||||
self._text_color_disabled: Union[str, Tuple[str, str]] = ThemeManager.theme["CTkButton"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled)
|
||||
self._fg_color: str | tuple[str, str] = ThemeManager.theme["CTkButton"]["fg_color"] if fg_color is None else self._check_color_type(fg_color, transparency=True)
|
||||
self._hover_color: str | tuple[str, str] = ThemeManager.theme["CTkButton"]["hover_color"] if hover_color is None else self._check_color_type(hover_color)
|
||||
self._border_color: str | tuple[str, str] = ThemeManager.theme["CTkButton"]["border_color"] if border_color is None else self._check_color_type(border_color)
|
||||
self._text_color: str | tuple[str, str] = ThemeManager.theme["CTkButton"]["text_color"] if text_color is None else self._check_color_type(text_color)
|
||||
self._text_color_disabled: str | tuple[str, str] = ThemeManager.theme["CTkButton"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled)
|
||||
|
||||
# rendering options
|
||||
self._background_corner_colors: Union[Tuple[Union[str, Tuple[str, str]]], None] = background_corner_colors # rendering options for DrawEngine
|
||||
self._background_corner_colors: tuple[str | tuple[str, str]] | None = background_corner_colors # rendering options for DrawEngine
|
||||
self._round_width_to_even_numbers: bool = round_width_to_even_numbers # rendering options for DrawEngine
|
||||
self._round_height_to_even_numbers: bool = round_height_to_even_numbers # rendering options for DrawEngine
|
||||
|
||||
# text, font
|
||||
self._text = text
|
||||
self._text_label: Union[tkinter.Label, None] = None
|
||||
self._textvariable: tkinter.Variable = textvariable
|
||||
self._font: Union[tuple, CTkFont] = CTkFont() if font is None else self._check_font_type(font)
|
||||
self._text_label: tkinter.Label | None = None
|
||||
self._textvariable = textvariable
|
||||
self._font = CTkFont() if font is None else self._check_font_type(font)
|
||||
if isinstance(self._font, CTkFont):
|
||||
self._font.add_size_configure_callback(self._update_font)
|
||||
|
||||
# image
|
||||
self._image = self._check_image_type(image)
|
||||
self._image_label: Union[tkinter.Label, None] = None
|
||||
self._image_label: tkinter.Label | None = None
|
||||
if isinstance(self._image, CTkImage):
|
||||
self._image.add_configure_callback(self._update_image)
|
||||
|
||||
# other
|
||||
self._state: str = state
|
||||
self._hover: bool = hover
|
||||
self._command: Callable = command
|
||||
self._compound: str = compound
|
||||
self._anchor: str = anchor
|
||||
self._click_animation_running: bool = False
|
||||
self._state = state
|
||||
self._hover = hover
|
||||
self._command = command
|
||||
self._compound = compound
|
||||
self._anchor = anchor
|
||||
self._click_animation_running = False
|
||||
|
||||
# canvas and draw engine
|
||||
self._canvas = CTkCanvas(master=self,
|
||||
@ -105,7 +113,7 @@ class CTkButton(CTkBaseClass):
|
||||
self._set_cursor()
|
||||
self._draw()
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
|
||||
if sequence is None or sequence == "<Enter>":
|
||||
@ -132,7 +140,7 @@ class CTkButton(CTkBaseClass):
|
||||
if self._image_label is not None:
|
||||
self._image_label.bind("<Button-1>", self._clicked)
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._create_grid()
|
||||
@ -146,11 +154,11 @@ class CTkButton(CTkBaseClass):
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_appearance_mode(self, mode_string):
|
||||
def _set_appearance_mode(self, mode_string: Literal["light", "dark"]):
|
||||
super()._set_appearance_mode(mode_string)
|
||||
self._update_image()
|
||||
|
||||
def _set_dimensions(self, width: int = None, height: int = None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -180,7 +188,7 @@ class CTkButton(CTkBaseClass):
|
||||
self._font.remove_size_configure_callback(self._update_font)
|
||||
super().destroy()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
if self._background_corner_colors is not None:
|
||||
@ -349,7 +357,7 @@ class CTkButton(CTkBaseClass):
|
||||
if self._text_label is not None:
|
||||
self._text_label.grid(row=1, column=2, sticky="s")
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
self._create_grid()
|
||||
@ -440,7 +448,7 @@ class CTkButton(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -496,7 +504,7 @@ class CTkButton(CTkBaseClass):
|
||||
elif sys.platform.startswith("win") and self._command is not None:
|
||||
self.configure(cursor="hand2")
|
||||
|
||||
def _on_enter(self, event=None):
|
||||
def _on_enter(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._hover is True and self._state == "normal":
|
||||
if self._hover_color is None:
|
||||
inner_parts_color = self._fg_color
|
||||
@ -516,7 +524,7 @@ class CTkButton(CTkBaseClass):
|
||||
if self._image_label is not None:
|
||||
self._image_label.configure(bg=self._apply_appearance_mode(inner_parts_color))
|
||||
|
||||
def _on_leave(self, event=None):
|
||||
def _on_leave(self, event: tkinter.Event[Any] | None = None):
|
||||
self._click_animation_running = False
|
||||
|
||||
if self._fg_color == "transparent":
|
||||
@ -541,7 +549,7 @@ class CTkButton(CTkBaseClass):
|
||||
if self._click_animation_running:
|
||||
self._on_enter()
|
||||
|
||||
def _clicked(self, event=None):
|
||||
def _clicked(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._state != tkinter.DISABLED:
|
||||
|
||||
# click animation: change color with .on_leave() and back to normal after 100ms with click_animation()
|
||||
@ -558,7 +566,7 @@ class CTkButton(CTkBaseClass):
|
||||
if self._command is not None:
|
||||
return self._command()
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
@ -569,7 +577,7 @@ class CTkButton(CTkBaseClass):
|
||||
if self._image_label is not None:
|
||||
self._image_label.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: str = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,12 +1,18 @@
|
||||
import tkinter
|
||||
import sys
|
||||
from typing import Union, Tuple, Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .font import CTkFont
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkCheckBox(CTkBaseClass):
|
||||
@ -16,32 +22,32 @@ class CTkCheckBox(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 100,
|
||||
height: int = 24,
|
||||
checkbox_width: int = 24,
|
||||
checkbox_height: int = 24,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
checkmark_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
hover_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
checkmark_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
text_color_disabled: str | tuple[str, str] | None = None,
|
||||
|
||||
text: str = "CTkCheckBox",
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
textvariable: Union[tkinter.Variable, None] = None,
|
||||
state: str = tkinter.NORMAL,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
textvariable: tkinter.Variable | None = None,
|
||||
state: Literal["normal", "disabled", "readonly"] = "normal",
|
||||
hover: bool = True,
|
||||
command: Union[Callable[[], None], None] = None,
|
||||
onvalue: Union[int, str] = 1,
|
||||
offvalue: Union[int, str] = 0,
|
||||
variable: Union[tkinter.Variable, None] = None,
|
||||
**kwargs):
|
||||
command: Callable[[], None] | None = None,
|
||||
onvalue: int | str = 1,
|
||||
offvalue: int | str = 0,
|
||||
variable: tkinter.Variable | None = None,
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
@ -62,7 +68,6 @@ class CTkCheckBox(CTkBaseClass):
|
||||
|
||||
# text
|
||||
self._text = text
|
||||
self._text_label: Union[tkinter.Label, None] = None
|
||||
self._text_color = ThemeManager.theme["CTkCheckbox"]["text_color"] if text_color is None else self._check_color_type(text_color)
|
||||
self._text_color_disabled = ThemeManager.theme["CTkCheckbox"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled)
|
||||
|
||||
@ -79,9 +84,9 @@ class CTkCheckBox(CTkBaseClass):
|
||||
|
||||
self._onvalue = onvalue
|
||||
self._offvalue = offvalue
|
||||
self._variable: tkinter.Variable = variable
|
||||
self._variable = variable
|
||||
self._variable_callback_blocked = False
|
||||
self._textvariable: tkinter.Variable = textvariable
|
||||
self._textvariable = textvariable
|
||||
self._variable_callback_name = None
|
||||
|
||||
# configure grid system (1x3)
|
||||
@ -123,7 +128,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
self._set_cursor()
|
||||
self._draw()
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
if sequence is None or sequence == "<Enter>":
|
||||
self._canvas.bind("<Enter>", self._on_enter)
|
||||
@ -135,7 +140,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
self._canvas.bind("<Button-1>", self.toggle)
|
||||
self._text_label.bind("<Button-1>", self.toggle)
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(6))
|
||||
@ -148,7 +153,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
height=self._apply_widget_scaling(self._checkbox_height))
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width: int = None, height: int = None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._bg_canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -173,7 +178,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
requires_recoloring_1 = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._checkbox_width),
|
||||
@ -220,22 +225,22 @@ class CTkCheckBox(CTkBaseClass):
|
||||
|
||||
self._text_label.configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
require_redraw = True
|
||||
|
||||
if "border_width" in kwargs:
|
||||
self._border_width = kwargs.pop("border_width")
|
||||
self._border_width: int = kwargs.pop("border_width")
|
||||
require_redraw = True
|
||||
|
||||
if "checkbox_width" in kwargs:
|
||||
self._checkbox_width = kwargs.pop("checkbox_width")
|
||||
self._checkbox_width: int = kwargs.pop("checkbox_width")
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._checkbox_width))
|
||||
require_redraw = True
|
||||
|
||||
if "checkbox_height" in kwargs:
|
||||
self._checkbox_height = kwargs.pop("checkbox_height")
|
||||
self._checkbox_height: int = kwargs.pop("checkbox_height")
|
||||
self._canvas.configure(height=self._apply_widget_scaling(self._checkbox_height))
|
||||
require_redraw = True
|
||||
|
||||
@ -296,7 +301,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -360,7 +365,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
if self._text_label is not None:
|
||||
self._text_label.configure(cursor="hand2")
|
||||
|
||||
def _on_enter(self, event=0):
|
||||
def _on_enter(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._hover is True and self._state == tkinter.NORMAL:
|
||||
if self._check_state is True:
|
||||
self._canvas.itemconfig("inner_parts",
|
||||
@ -374,7 +379,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
fill=self._apply_appearance_mode(self._hover_color),
|
||||
outline=self._apply_appearance_mode(self._hover_color))
|
||||
|
||||
def _on_leave(self, event=0):
|
||||
def _on_leave(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._check_state is True:
|
||||
self._canvas.itemconfig("inner_parts",
|
||||
fill=self._apply_appearance_mode(self._fg_color),
|
||||
@ -390,14 +395,14 @@ class CTkCheckBox(CTkBaseClass):
|
||||
fill=self._apply_appearance_mode(self._border_color),
|
||||
outline=self._apply_appearance_mode(self._border_color))
|
||||
|
||||
def _variable_callback(self, var_name, index, mode):
|
||||
def _variable_callback(self, var_name: str, index: int, mode: Any):
|
||||
if not self._variable_callback_blocked:
|
||||
if self._variable.get() == self._onvalue:
|
||||
self.select(from_variable_callback=True)
|
||||
elif self._variable.get() == self._offvalue:
|
||||
self.deselect(from_variable_callback=True)
|
||||
|
||||
def toggle(self, event=0):
|
||||
def toggle(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._state == tkinter.NORMAL:
|
||||
if self._check_state is True:
|
||||
self._check_state = False
|
||||
@ -414,7 +419,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
if self._command is not None:
|
||||
self._command()
|
||||
|
||||
def select(self, from_variable_callback=False):
|
||||
def select(self, from_variable_callback: bool =False):
|
||||
self._check_state = True
|
||||
self._draw()
|
||||
|
||||
@ -423,7 +428,7 @@ class CTkCheckBox(CTkBaseClass):
|
||||
self._variable.set(self._onvalue)
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def deselect(self, from_variable_callback=False):
|
||||
def deselect(self, from_variable_callback: bool = False):
|
||||
self._check_state = False
|
||||
self._draw()
|
||||
|
||||
@ -432,17 +437,17 @@ class CTkCheckBox(CTkBaseClass):
|
||||
self._variable.set(self._offvalue)
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def get(self) -> Union[int, str]:
|
||||
def get(self) -> int | str:
|
||||
return self._onvalue if self._check_state is True else self._offvalue
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._canvas.bind(sequence, command, add=True)
|
||||
self._text_label.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: str = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,14 +1,19 @@
|
||||
import tkinter
|
||||
import sys
|
||||
import copy
|
||||
from typing import Union, Tuple, Callable, List, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_widget_classes import DropdownMenu
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
import copy
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass, DropdownMenu
|
||||
from .font import CTkFont
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkComboBox(CTkBaseClass):
|
||||
@ -18,32 +23,32 @@ class CTkComboBox(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 140,
|
||||
height: int = 28,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
dropdown_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
dropdown_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
dropdown_text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
button_color: str | tuple[str, str] | None = None,
|
||||
button_hover_color: str | tuple[str, str] | None = None,
|
||||
dropdown_fg_color: str | tuple[str, str] | None = None,
|
||||
dropdown_hover_color: str | tuple[str, str] | None = None,
|
||||
dropdown_text_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
text_color_disabled: str | tuple[str, str] | None = None,
|
||||
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
dropdown_font: Optional[Union[tuple, CTkFont]] = None,
|
||||
values: Optional[List[str]] = None,
|
||||
state: str = tkinter.NORMAL,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
dropdown_font: tuple[Any, ...] | CTkFont | None = None,
|
||||
values: list[str] | None = None,
|
||||
state: Literal["normal", "disabled", "readonly"] = "normal",
|
||||
hover: bool = True,
|
||||
variable: Union[tkinter.Variable, None] = None,
|
||||
command: Union[Callable[[str], None], None] = None,
|
||||
justify: str = "left",
|
||||
**kwargs):
|
||||
variable: tkinter.Variable | None = None,
|
||||
command: Callable[[str], None] | None = None,
|
||||
justify: Literal["left", "center", "right"] = "left",
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
@ -70,11 +75,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
self._variable = variable
|
||||
self._state = state
|
||||
self._hover = hover
|
||||
|
||||
if values is None:
|
||||
self._values = ["CTkComboBox"]
|
||||
else:
|
||||
self._values = values
|
||||
self._values = values or ["CTkComboBox"]
|
||||
|
||||
self._dropdown_menu = DropdownMenu(master=self,
|
||||
values=self._values,
|
||||
@ -116,7 +117,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
else:
|
||||
self._entry.insert(0, "CTkComboBox")
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
if sequence is None:
|
||||
self._canvas.tag_bind("right_parts", "<Enter>", self._on_enter)
|
||||
@ -135,7 +136,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
max(self._apply_widget_scaling(self._current_width - left_section_width + 3), self._apply_widget_scaling(3))),
|
||||
pady=self._apply_widget_scaling(self._border_width))
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
# change entry font size and grid padding
|
||||
@ -146,7 +147,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width: int = None, height: int = None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -168,7 +169,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
left_section_width = self._current_width - self._current_height
|
||||
@ -218,7 +219,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
self._dropdown_menu.open(self.winfo_rootx(),
|
||||
self.winfo_rooty() + self._apply_widget_scaling(self._current_height + 0))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
require_redraw = True
|
||||
@ -297,7 +298,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -341,7 +342,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def _on_enter(self, event=0):
|
||||
def _on_enter(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._hover is True and self._state == tkinter.NORMAL and len(self._values) > 0:
|
||||
if sys.platform == "darwin" and len(self._values) > 0 and self._cursor_manipulation_enabled:
|
||||
self._canvas.configure(cursor="pointinghand")
|
||||
@ -356,7 +357,7 @@ class CTkComboBox(CTkBaseClass):
|
||||
outline=self._apply_appearance_mode(self._button_hover_color),
|
||||
fill=self._apply_appearance_mode(self._button_hover_color))
|
||||
|
||||
def _on_leave(self, event=0):
|
||||
def _on_leave(self, event: tkinter.Event[Any] | None = None):
|
||||
if sys.platform == "darwin" and len(self._values) > 0 and self._cursor_manipulation_enabled:
|
||||
self._canvas.configure(cursor="arrow")
|
||||
elif sys.platform.startswith("win") and len(self._values) > 0 and self._cursor_manipulation_enabled:
|
||||
@ -396,17 +397,17 @@ class CTkComboBox(CTkBaseClass):
|
||||
def get(self) -> str:
|
||||
return self._entry.get()
|
||||
|
||||
def _clicked(self, event=None):
|
||||
def _clicked(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._state is not tkinter.DISABLED and len(self._values) > 0:
|
||||
self._open_dropdown_menu()
|
||||
|
||||
def bind(self, sequence=None, command=None, add=True):
|
||||
def bind(self, sequence: str | None = None, command=None, add: Literal["+"] | bool = True):
|
||||
""" called on the tkinter.Entry """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._entry.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence=None, funcid=None):
|
||||
def unbind(self, sequence=None, funcid: str | None = None):
|
||||
""" called on the tkinter.Entry """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,12 +1,19 @@
|
||||
import tkinter
|
||||
from typing import Union, Tuple, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .font import CTkFont
|
||||
from .utility import pop_from_dict_by_set, check_kwargs_empty
|
||||
from .theme import ThemeManager
|
||||
from .utility import check_kwargs_empty, pop_from_dict_by_set
|
||||
|
||||
|
||||
class CTkEntry(CTkBaseClass):
|
||||
@ -23,23 +30,23 @@ class CTkEntry(CTkBaseClass):
|
||||
"show", "takefocus", "validate", "validatecommand", "xscrollcommand"}
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 140,
|
||||
height: int = 28,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
placeholder_text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
placeholder_text_color: str | tuple[str, str] | None = None,
|
||||
|
||||
textvariable: Union[tkinter.Variable, None] = None,
|
||||
placeholder_text: Union[str, None] = None,
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
state: str = tkinter.NORMAL,
|
||||
**kwargs):
|
||||
textvariable: tkinter.Variable | None = None,
|
||||
placeholder_text: str | None = None,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
state: Literal["normal", "disabled", "readonly"] = "normal",
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height)
|
||||
@ -97,7 +104,7 @@ class CTkEntry(CTkBaseClass):
|
||||
self._create_bindings()
|
||||
self._draw()
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
if sequence is None or sequence == "<FocusIn>":
|
||||
self._entry.bind("<FocusIn>", self._entry_focus_in)
|
||||
@ -116,11 +123,11 @@ class CTkEntry(CTkBaseClass):
|
||||
padx=self._apply_widget_scaling(self._minimum_x_padding),
|
||||
pady=(self._apply_widget_scaling(self._border_width), self._apply_widget_scaling(self._border_width + 1)))
|
||||
|
||||
def _textvariable_callback(self, var_name, index, mode):
|
||||
def _textvariable_callback(self, var_name: str, index: int, mode: Any):
|
||||
if self._textvariable.get() == "":
|
||||
self._activate_placeholder()
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._entry.configure(font=self._apply_font_scaling(self._font))
|
||||
@ -128,7 +135,7 @@ class CTkEntry(CTkBaseClass):
|
||||
self._create_grid()
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -150,7 +157,7 @@ class CTkEntry(CTkBaseClass):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
|
||||
@ -191,7 +198,7 @@ class CTkEntry(CTkBaseClass):
|
||||
disabledforeground=self._apply_appearance_mode(self._text_color),
|
||||
insertbackground=self._apply_appearance_mode(self._text_color))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "state" in kwargs:
|
||||
self._state = kwargs.pop("state")
|
||||
self._entry.configure(state=self._state)
|
||||
@ -252,7 +259,7 @@ class CTkEntry(CTkBaseClass):
|
||||
self._entry.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_entry_attributes)) # configure Tkinter.Entry
|
||||
super().configure(require_redraw=require_redraw, **kwargs) # configure CTkBaseClass
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -281,7 +288,7 @@ class CTkEntry(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name) # cget of CTkBaseClass
|
||||
|
||||
def bind(self, sequence=None, command=None, add=True):
|
||||
def bind(self, sequence=None, command=None, add: Literal["+"] | bool = True):
|
||||
""" called on the tkinter.Entry """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
@ -316,11 +323,11 @@ class CTkEntry(CTkBaseClass):
|
||||
for argument, value in self._pre_placeholder_arguments.items():
|
||||
self._entry[argument] = value
|
||||
|
||||
def _entry_focus_out(self, event=None):
|
||||
def _entry_focus_out(self, event: tkinter.Event[Any] | None = None):
|
||||
self._activate_placeholder()
|
||||
self._is_focused = False
|
||||
|
||||
def _entry_focus_in(self, event=None):
|
||||
def _entry_focus_in(self, event: tkinter.Event[Any] | None = None):
|
||||
self._deactivate_placeholder()
|
||||
self._is_focused = True
|
||||
|
||||
@ -330,7 +337,7 @@ class CTkEntry(CTkBaseClass):
|
||||
if not self._is_focused and self._entry.get() == "":
|
||||
self._activate_placeholder()
|
||||
|
||||
def insert(self, index, string):
|
||||
def insert(self, index: int, string):
|
||||
self._deactivate_placeholder()
|
||||
|
||||
return self._entry.insert(index, string)
|
||||
|
@ -1,9 +1,17 @@
|
||||
from typing import Union, Tuple, List, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkFrame(CTkBaseClass):
|
||||
@ -15,19 +23,19 @@ class CTkFrame(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 200,
|
||||
height: int = 200,
|
||||
corner_radius: Optional[Union[int, str]] = None,
|
||||
border_width: Optional[Union[int, str]] = None,
|
||||
corner_radius: int | str | None = None,
|
||||
border_width: int | str | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
|
||||
background_corner_colors: Union[Tuple[Union[str, Tuple[str, str]]], None] = None,
|
||||
overwrite_preferred_drawing_method: Union[str, None] = None,
|
||||
**kwargs):
|
||||
background_corner_colors: tuple[str | tuple[str, str]] | None = None,
|
||||
overwrite_preferred_drawing_method: str | None = None,
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
@ -64,7 +72,7 @@ class CTkFrame(CTkBaseClass):
|
||||
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def winfo_children(self) -> List[any]:
|
||||
def winfo_children(self) -> list[Any]:
|
||||
"""
|
||||
winfo_children of CTkFrame without self.canvas widget,
|
||||
because it's not a child but part of the CTkFrame itself
|
||||
@ -77,21 +85,21 @@ class CTkFrame(CTkBaseClass):
|
||||
except ValueError:
|
||||
return child_widgets
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
self._draw()
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
self._draw()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
if not self._canvas.winfo_exists():
|
||||
@ -131,7 +139,7 @@ class CTkFrame(CTkBaseClass):
|
||||
# self._canvas.tag_lower("inner_parts") # maybe unnecessary, I don't know ???
|
||||
# self._canvas.tag_lower("border_parts")
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "fg_color" in kwargs:
|
||||
self._fg_color = self._check_color_type(kwargs.pop("fg_color"), transparency=True)
|
||||
require_redraw = True
|
||||
@ -166,7 +174,7 @@ class CTkFrame(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -182,13 +190,13 @@ class CTkFrame(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def bind(self, sequence=None, command=None, add=True):
|
||||
def bind(self, sequence: str | None = None, command: Callable[[tkinter.Event[Any]], None] | None = None, add: Literal["+"] | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._canvas.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence=None, funcid=None):
|
||||
def unbind(self, sequence=None, funcid: str | None = None):
|
||||
""" called on the tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,13 +1,14 @@
|
||||
import tkinter
|
||||
from typing import Union, Tuple, Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .font import CTkFont
|
||||
from .image import CTkImage
|
||||
from .utility import pop_from_dict_by_set, check_kwargs_empty
|
||||
from .theme import ThemeManager
|
||||
from .utility import check_kwargs_empty, pop_from_dict_by_set
|
||||
|
||||
|
||||
class CTkLabel(CTkBaseClass):
|
||||
@ -21,22 +22,22 @@ class CTkLabel(CTkBaseClass):
|
||||
"textvariable", "state", "takefocus", "underline"}
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 0,
|
||||
height: int = 28,
|
||||
corner_radius: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
|
||||
text: str = "CTkLabel",
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
image: Union[CTkImage, None] = None,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
image: CTkImage | None = None,
|
||||
compound: str = "center",
|
||||
anchor: str = "center", # label anchor: center, n, e, s, w
|
||||
wraplength: int = 0,
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height)
|
||||
@ -93,7 +94,7 @@ class CTkLabel(CTkBaseClass):
|
||||
self._update_image()
|
||||
self._draw()
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width), height=self._apply_widget_scaling(self._desired_height))
|
||||
@ -108,7 +109,7 @@ class CTkLabel(CTkBaseClass):
|
||||
super()._set_appearance_mode(mode_string)
|
||||
self._update_image()
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -144,7 +145,7 @@ class CTkLabel(CTkBaseClass):
|
||||
self._label.grid(row=0, column=0, sticky=text_label_grid_sticky,
|
||||
padx=self._apply_widget_scaling(min(self._corner_radius, round(self._current_height / 2))))
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
|
||||
@ -170,7 +171,7 @@ class CTkLabel(CTkBaseClass):
|
||||
|
||||
self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
self._create_grid()
|
||||
@ -220,7 +221,7 @@ class CTkLabel(CTkBaseClass):
|
||||
self._label.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_label_attributes)) # configure tkinter.Label
|
||||
super().configure(require_redraw=require_redraw, **kwargs) # configure CTkBaseClass
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
|
||||
@ -247,14 +248,14 @@ class CTkLabel(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name) # cget of CTkBaseClass
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: str = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str = True):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._canvas.bind(sequence, command, add=True)
|
||||
self._label.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: Optional[str] = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,14 +1,19 @@
|
||||
import tkinter
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import sys
|
||||
from typing import Union, Tuple, Callable, Optional
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .core_widget_classes import DropdownMenu
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass, DropdownMenu
|
||||
from .font import CTkFont
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkOptionMenu(CTkBaseClass):
|
||||
@ -18,31 +23,31 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 140,
|
||||
height: int = 28,
|
||||
corner_radius: Optional[Union[int]] = None,
|
||||
corner_radius: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
dropdown_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
dropdown_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
dropdown_text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
button_color: str | tuple[str, str] | None = None,
|
||||
button_hover_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
text_color_disabled: str | tuple[str, str] | None = None,
|
||||
dropdown_fg_color: str | tuple[str, str] | None = None,
|
||||
dropdown_hover_color: str | tuple[str, str] | None = None,
|
||||
dropdown_text_color: str | tuple[str, str] | None = None,
|
||||
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
dropdown_font: Optional[Union[tuple, CTkFont]] = None,
|
||||
values: Optional[list] = None,
|
||||
variable: Union[tkinter.Variable, None] = None,
|
||||
state: str = tkinter.NORMAL,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
dropdown_font: tuple[Any, ...] | CTkFont | None = None,
|
||||
values: list | None = None,
|
||||
variable: tkinter.Variable | None = None,
|
||||
state: Literal["normal", "disabled", "readonly"] = "normal",
|
||||
hover: bool = True,
|
||||
command: Union[Callable[[str], None], None] = None,
|
||||
command: Callable[[str], None] | None = None,
|
||||
dynamic_resizing: bool = True,
|
||||
anchor: str = "w",
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
@ -68,7 +73,7 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
self._command = command
|
||||
self._variable = variable
|
||||
self._variable_callback_blocked: bool = False
|
||||
self._variable_callback_name: Union[str, None] = None
|
||||
self._variable_callback_name: str | None = None
|
||||
self._state = state
|
||||
self._hover = hover
|
||||
self._dynamic_resizing = dynamic_resizing
|
||||
@ -127,7 +132,7 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
self._current_value = self._variable.get()
|
||||
self._text_label.configure(text=self._current_value)
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
if sequence is None or sequence == "<Enter>":
|
||||
self._canvas.bind("<Enter>", self._on_enter)
|
||||
@ -147,7 +152,7 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
padx=(max(self._apply_widget_scaling(self._corner_radius), self._apply_widget_scaling(3)),
|
||||
max(self._apply_widget_scaling(self._current_width - left_section_width + 3), self._apply_widget_scaling(3))))
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
# change label font size and grid padding
|
||||
@ -157,7 +162,7 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
self._create_grid()
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width: int = None, height: int = None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -182,7 +187,7 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
left_section_width = self._current_width - self._current_height
|
||||
@ -221,7 +226,7 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
|
||||
self._canvas.update_idletasks()
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
self._create_grid()
|
||||
@ -303,7 +308,7 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
|
||||
@ -350,20 +355,20 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
self._dropdown_menu.open(self.winfo_rootx(),
|
||||
self.winfo_rooty() + self._apply_widget_scaling(self._current_height + 0))
|
||||
|
||||
def _on_enter(self, event=0):
|
||||
def _on_enter(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._hover is True and self._state == tkinter.NORMAL and len(self._values) > 0:
|
||||
# set color of inner button parts to hover color
|
||||
self._canvas.itemconfig("inner_parts_right",
|
||||
outline=self._apply_appearance_mode(self._button_hover_color),
|
||||
fill=self._apply_appearance_mode(self._button_hover_color))
|
||||
|
||||
def _on_leave(self, event=0):
|
||||
def _on_leave(self, event: tkinter.Event[Any] | None = None):
|
||||
# set color of inner button parts
|
||||
self._canvas.itemconfig("inner_parts_right",
|
||||
outline=self._apply_appearance_mode(self._button_color),
|
||||
fill=self._apply_appearance_mode(self._button_color))
|
||||
|
||||
def _variable_callback(self, var_name, index, mode):
|
||||
def _variable_callback(self, var_name: str, index: int, mode: Any):
|
||||
if not self._variable_callback_blocked:
|
||||
self._current_value = self._variable.get()
|
||||
self._text_label.configure(text=self._current_value)
|
||||
@ -392,18 +397,18 @@ class CTkOptionMenu(CTkBaseClass):
|
||||
def get(self) -> str:
|
||||
return self._current_value
|
||||
|
||||
def _clicked(self, event=0):
|
||||
def _clicked(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._state is not tkinter.DISABLED and len(self._values) > 0:
|
||||
self._open_dropdown_menu()
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._canvas.bind(sequence, command, add=True)
|
||||
self._text_label.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: str = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,15 +1,17 @@
|
||||
import tkinter
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import Union, Tuple, Optional, Callable
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
try:
|
||||
from typing import Literal
|
||||
except ImportError:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkProgressBar(CTkBaseClass):
|
||||
@ -20,23 +22,23 @@ class CTkProgressBar(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
master: CTkBaseClass,
|
||||
width: int | None = None,
|
||||
height: int | None = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
progress_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
progress_color: str | tuple[str, str] | None = None,
|
||||
|
||||
variable: Union[tkinter.Variable, None] = None,
|
||||
variable: tkinter.Variable | None = None,
|
||||
orientation: str = "horizontal",
|
||||
mode: Literal["determinate", "indeterminate"] = "determinate",
|
||||
determinate_speed: float = 1,
|
||||
indeterminate_speed: float = 1,
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
# set default dimensions according to orientation
|
||||
if width is None:
|
||||
@ -94,14 +96,14 @@ class CTkProgressBar(CTkBaseClass):
|
||||
self.set(self._variable.get(), from_variable_callback=True)
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -114,7 +116,7 @@ class CTkProgressBar(CTkBaseClass):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
if self._orientation.lower() == "horizontal":
|
||||
@ -157,7 +159,7 @@ class CTkProgressBar(CTkBaseClass):
|
||||
fill=self._apply_appearance_mode(self._progress_color),
|
||||
outline=self._apply_appearance_mode(self._progress_color))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
require_redraw = True
|
||||
@ -202,7 +204,7 @@ class CTkProgressBar(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -229,11 +231,11 @@ class CTkProgressBar(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def _variable_callback(self, var_name, index, mode):
|
||||
def _variable_callback(self, var_name: str, index: int, mode: Any):
|
||||
if not self._variable_callback_blocked:
|
||||
self.set(self._variable.get(), from_variable_callback=True)
|
||||
|
||||
def set(self, value, from_variable_callback=False):
|
||||
def set(self, value, from_variable_callback: bool = False):
|
||||
""" set determinate value """
|
||||
self._determinate_value = value
|
||||
|
||||
@ -289,13 +291,13 @@ class CTkProgressBar(CTkBaseClass):
|
||||
self._indeterminate_value += self._indeterminate_speed
|
||||
self._draw()
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._canvas.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: str = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,12 +1,18 @@
|
||||
import tkinter
|
||||
import sys
|
||||
from typing import Union, Tuple, Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .font import CTkFont
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkRadioButton(CTkBaseClass):
|
||||
@ -16,31 +22,31 @@ class CTkRadioButton(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 100,
|
||||
height: int = 22,
|
||||
radiobutton_width: int = 22,
|
||||
radiobutton_height: int = 22,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width_unchecked: Optional[int] = None,
|
||||
border_width_checked: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width_unchecked: int | None = None,
|
||||
border_width_checked: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
hover_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
text_color_disabled: str | tuple[str, str] | None = None,
|
||||
|
||||
text: str = "CTkRadioButton",
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
textvariable: Union[tkinter.Variable, None] = None,
|
||||
variable: Union[tkinter.Variable, None] = None,
|
||||
value: Union[int, str] = 0,
|
||||
state: str = tkinter.NORMAL,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
textvariable: tkinter.Variable | None = None,
|
||||
variable: tkinter.Variable | None = None,
|
||||
value: int | str = 0,
|
||||
state: Literal["normal", "disabled", "readonly"] = "normal",
|
||||
hover: bool = True,
|
||||
command: Union[Callable, None] = None,
|
||||
**kwargs):
|
||||
command: Callable[..., None] | None = None,
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
@ -61,7 +67,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
|
||||
# text
|
||||
self._text = text
|
||||
self._text_label: Union[tkinter.Label, None] = None
|
||||
self._text_label: tkinter.Label | None = None
|
||||
self._text_color = ThemeManager.theme["CTkRadiobutton"]["text_color"] if text_color is None else self._check_color_type(text_color)
|
||||
self._text_color_disabled = ThemeManager.theme["CTkRadiobutton"]["text_color_disabled"] if text_color_disabled is None else self._check_color_type(text_color_disabled)
|
||||
|
||||
@ -79,7 +85,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
self._variable: tkinter.Variable = variable
|
||||
self._variable_callback_blocked: bool = False
|
||||
self._textvariable = textvariable
|
||||
self._variable_callback_name: Union[str, None] = None
|
||||
self._variable_callback_name: str | None = None
|
||||
|
||||
# configure grid system (3x1)
|
||||
self.grid_columnconfigure(0, weight=0)
|
||||
@ -119,7 +125,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
self._set_cursor()
|
||||
self._draw()
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
if sequence is None or sequence == "<Enter>":
|
||||
self._canvas.bind("<Enter>", self._on_enter)
|
||||
@ -131,7 +137,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
self._canvas.bind("<Button-1>", self.invoke)
|
||||
self._text_label.bind("<Button-1>", self.invoke)
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(6))
|
||||
@ -143,7 +149,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
height=self._apply_widget_scaling(self._radiobutton_height))
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width: int = None, height: int = None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._bg_canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -167,7 +173,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
if self._check_state is True:
|
||||
@ -205,7 +211,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
|
||||
self._text_label.configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
require_redraw = True
|
||||
@ -289,7 +295,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width_unchecked":
|
||||
@ -354,13 +360,13 @@ class CTkRadioButton(CTkBaseClass):
|
||||
if self._text_label is not None:
|
||||
self._text_label.configure(cursor="hand2")
|
||||
|
||||
def _on_enter(self, event=0):
|
||||
def _on_enter(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._hover is True and self._state == tkinter.NORMAL:
|
||||
self._canvas.itemconfig("border_parts",
|
||||
fill=self._apply_appearance_mode(self._hover_color),
|
||||
outline=self._apply_appearance_mode(self._hover_color))
|
||||
|
||||
def _on_leave(self, event=0):
|
||||
def _on_leave(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._check_state is True:
|
||||
self._canvas.itemconfig("border_parts",
|
||||
fill=self._apply_appearance_mode(self._fg_color),
|
||||
@ -370,14 +376,14 @@ class CTkRadioButton(CTkBaseClass):
|
||||
fill=self._apply_appearance_mode(self._border_color),
|
||||
outline=self._apply_appearance_mode(self._border_color))
|
||||
|
||||
def _variable_callback(self, var_name, index, mode):
|
||||
def _variable_callback(self, var_name: str, index: int, mode: Any):
|
||||
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):
|
||||
def invoke(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._state == tkinter.NORMAL:
|
||||
if self._check_state is False:
|
||||
self._check_state = True
|
||||
@ -386,7 +392,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
if self._command is not None:
|
||||
self._command()
|
||||
|
||||
def select(self, from_variable_callback=False):
|
||||
def select(self, from_variable_callback: bool = False):
|
||||
self._check_state = True
|
||||
self._draw()
|
||||
|
||||
@ -395,7 +401,7 @@ class CTkRadioButton(CTkBaseClass):
|
||||
self._variable.set(self._value)
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def deselect(self, from_variable_callback=False):
|
||||
def deselect(self, from_variable_callback: bool = False):
|
||||
self._check_state = False
|
||||
self._draw()
|
||||
|
||||
@ -404,14 +410,14 @@ class CTkRadioButton(CTkBaseClass):
|
||||
self._variable.set("")
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._canvas.bind(sequence, command, add=True)
|
||||
self._text_label.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: str = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,40 +1,43 @@
|
||||
from typing import Union, Tuple, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any
|
||||
|
||||
try:
|
||||
from typing import Literal
|
||||
except ImportError:
|
||||
from typing_extensions import Literal
|
||||
import tkinter
|
||||
import sys
|
||||
|
||||
from .ctk_frame import CTkFrame
|
||||
from .ctk_scrollbar import CTkScrollbar
|
||||
from .appearance_mode import CTkAppearanceModeBaseClass
|
||||
from .scaling import CTkScalingBaseClass
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .ctk_frame import CTkFrame
|
||||
from .ctk_label import CTkLabel
|
||||
from .ctk_scrollbar import CTkScrollbar
|
||||
from .font import CTkFont
|
||||
from .scaling import CTkScalingBaseClass
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBaseClass):
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 200,
|
||||
height: int = 200,
|
||||
corner_radius: Optional[Union[int, str]] = None,
|
||||
border_width: Optional[Union[int, str]] = None,
|
||||
corner_radius: int | str | None = None,
|
||||
border_width: int | str | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
scrollbar_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
scrollbar_button_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
scrollbar_button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
label_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
label_text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
scrollbar_fg_color: str | tuple[str, str] | None = None,
|
||||
scrollbar_button_color: str | tuple[str, str] | None = None,
|
||||
scrollbar_button_hover_color: str | tuple[str, str] | None = None,
|
||||
label_fg_color: str | tuple[str, str] | None = None,
|
||||
label_text_color: str | tuple[str, str] | None = None,
|
||||
|
||||
label_text: str = "",
|
||||
label_font: Optional[Union[tuple, CTkFont]] = None,
|
||||
label_font: tuple[Any, ...] | CTkFont | None = None,
|
||||
label_anchor: str = "center",
|
||||
orientation: Literal["vertical", "horizontal"] = "vertical"):
|
||||
|
||||
@ -120,7 +123,7 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
|
||||
else:
|
||||
self._label.grid_forget()
|
||||
|
||||
def _set_appearance_mode(self, mode_string):
|
||||
def _set_appearance_mode(self, mode_string: Literal["light", "dark"]):
|
||||
super()._set_appearance_mode(mode_string)
|
||||
|
||||
if self._parent_frame.cget("fg_color") == "transparent":
|
||||
@ -130,13 +133,13 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
|
||||
tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color")))
|
||||
self._parent_canvas.configure(bg=self._apply_appearance_mode(self._parent_frame.cget("fg_color")))
|
||||
|
||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||
def _set_scaling(self, new_widget_scaling: float, new_window_scaling: float):
|
||||
super()._set_scaling(new_widget_scaling, new_window_scaling)
|
||||
|
||||
self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
if width is not None:
|
||||
self._desired_width = width
|
||||
if height is not None:
|
||||
@ -145,7 +148,7 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
|
||||
self._parent_canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
|
||||
def configure(self, **kwargs):
|
||||
def configure(self, **kwargs: Any):
|
||||
if "width" in kwargs:
|
||||
self._set_dimensions(width=kwargs.pop("width"))
|
||||
|
||||
@ -232,7 +235,7 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
|
||||
else:
|
||||
return self._parent_frame.cget(attribute_name)
|
||||
|
||||
def _fit_frame_dimensions_to_canvas(self, event):
|
||||
def _fit_frame_dimensions_to_canvas(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._orientation == "horizontal":
|
||||
self._parent_canvas.itemconfigure(self._create_window_id, height=self._parent_canvas.winfo_height())
|
||||
elif self._orientation == "vertical":
|
||||
@ -244,7 +247,7 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
|
||||
elif sys.platform == "darwin":
|
||||
self._parent_canvas.configure(xscrollincrement=4, yscrollincrement=8)
|
||||
|
||||
def _mouse_wheel_all(self, event):
|
||||
def _mouse_wheel_all(self, event: tkinter.Event[Any] | None = None):
|
||||
if self.check_if_master_is_canvas(event.widget):
|
||||
if sys.platform.startswith("win"):
|
||||
if self._shift_pressed:
|
||||
@ -268,10 +271,10 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
|
||||
if self._parent_canvas.yview() != (0.0, 1.0):
|
||||
self._parent_canvas.yview("scroll", -event.delta, "units")
|
||||
|
||||
def _keyboard_shift_press_all(self, event):
|
||||
def _keyboard_shift_press_all(self, event: tkinter.Event[Any] | None = None):
|
||||
self._shift_pressed = True
|
||||
|
||||
def _keyboard_shift_release_all(self, event):
|
||||
def _keyboard_shift_release_all(self, event: tkinter.Event[Any] | None = None):
|
||||
self._shift_pressed = False
|
||||
|
||||
def check_if_master_is_canvas(self, widget):
|
||||
@ -282,35 +285,35 @@ class CTkScrollableFrame(tkinter.Frame, CTkAppearanceModeBaseClass, CTkScalingBa
|
||||
else:
|
||||
return False
|
||||
|
||||
def pack(self, **kwargs):
|
||||
def pack(self, **kwargs: Any):
|
||||
self._parent_frame.pack(**kwargs)
|
||||
|
||||
def place(self, **kwargs):
|
||||
def place(self, **kwargs: Any):
|
||||
self._parent_frame.place(**kwargs)
|
||||
|
||||
def grid(self, **kwargs):
|
||||
def grid(self, **kwargs: Any):
|
||||
self._parent_frame.grid(**kwargs)
|
||||
|
||||
def pack_forget(self):
|
||||
self._parent_frame.pack_forget()
|
||||
|
||||
def place_forget(self, **kwargs):
|
||||
def place_forget(self, **kwargs: Any):
|
||||
self._parent_frame.place_forget()
|
||||
|
||||
def grid_forget(self, **kwargs):
|
||||
def grid_forget(self, **kwargs: Any):
|
||||
self._parent_frame.grid_forget()
|
||||
|
||||
def grid_remove(self, **kwargs):
|
||||
def grid_remove(self, **kwargs: Any):
|
||||
self._parent_frame.grid_remove()
|
||||
|
||||
def grid_propagate(self, **kwargs):
|
||||
def grid_propagate(self, **kwargs: Any):
|
||||
self._parent_frame.grid_propagate()
|
||||
|
||||
def grid_info(self, **kwargs):
|
||||
def grid_info(self, **kwargs: Any):
|
||||
return self._parent_frame.grid_info()
|
||||
|
||||
def lift(self, aboveThis=None):
|
||||
def lift(self, aboveThis: Any =None):
|
||||
self._parent_frame.lift(aboveThis)
|
||||
|
||||
def lower(self, belowThis=None):
|
||||
def lower(self, belowThis: Any = None):
|
||||
self._parent_frame.lower(belowThis)
|
||||
|
@ -1,10 +1,17 @@
|
||||
import sys
|
||||
from typing import Union, Tuple, Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkScrollbar(CTkBaseClass):
|
||||
@ -15,22 +22,22 @@ class CTkScrollbar(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
width: Optional[Union[int, str]] = None,
|
||||
height: Optional[Union[int, str]] = None,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_spacing: Optional[int] = None,
|
||||
master: CTkBaseClass,
|
||||
width: int | str | None = None,
|
||||
height: int | str | None = None,
|
||||
corner_radius: int | None = None,
|
||||
border_spacing: int | None = None,
|
||||
minimum_pixel_length: int = 20,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
button_color: str | tuple[str, str] | None = None,
|
||||
button_hover_color: str | tuple[str, str] | None = None,
|
||||
|
||||
hover: bool = True,
|
||||
command: Union[Callable, None] = None,
|
||||
command: Callable[..., None] | None = None,
|
||||
orientation: str = "vertical",
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
# set default dimensions according to orientation
|
||||
if width is None:
|
||||
@ -74,7 +81,7 @@ class CTkScrollbar(CTkBaseClass):
|
||||
self._create_bindings()
|
||||
self._draw()
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
if sequence is None:
|
||||
self._canvas.tag_bind("border_parts", "<Button-1>", self._clicked)
|
||||
@ -87,14 +94,14 @@ class CTkScrollbar(CTkBaseClass):
|
||||
if sequence is None or sequence == "<MouseWheel>":
|
||||
self._canvas.bind("<MouseWheel>", self._mouse_scroll_event)
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -125,7 +132,7 @@ class CTkScrollbar(CTkBaseClass):
|
||||
else:
|
||||
return self._start_value, self._end_value
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
corrected_start_value, corrected_end_value = self._get_scrollbar_values_for_minimum_pixel_size()
|
||||
@ -160,7 +167,7 @@ class CTkScrollbar(CTkBaseClass):
|
||||
|
||||
self._canvas.update_idletasks()
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "fg_color" in kwargs:
|
||||
self._fg_color = self._check_color_type(kwargs.pop("fg_color"), transparency=True)
|
||||
require_redraw = True
|
||||
@ -189,7 +196,7 @@ class CTkScrollbar(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_spacing":
|
||||
@ -214,24 +221,24 @@ class CTkScrollbar(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def _on_enter(self, event=0):
|
||||
def _on_enter(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._hover is True:
|
||||
self._hover_state = True
|
||||
self._canvas.itemconfig("scrollbar_parts",
|
||||
outline=self._apply_appearance_mode(self._button_hover_color),
|
||||
fill=self._apply_appearance_mode(self._button_hover_color))
|
||||
|
||||
def _on_leave(self, event=0):
|
||||
def _on_leave(self, event: tkinter.Event[Any] | None = None):
|
||||
self._hover_state = False
|
||||
self._canvas.itemconfig("scrollbar_parts",
|
||||
outline=self._apply_appearance_mode(self._button_color),
|
||||
fill=self._apply_appearance_mode(self._button_color))
|
||||
|
||||
def _clicked(self, event):
|
||||
def _clicked(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._orientation == "vertical":
|
||||
value = self._reverse_widget_scaling(((event.y - self._border_spacing) / (self._current_height - 2 * self._border_spacing)))
|
||||
value = self._reverse_widget_scaling((event.y - self._border_spacing) / (self._current_height - 2 * self._border_spacing))
|
||||
else:
|
||||
value = self._reverse_widget_scaling(((event.x - self._border_spacing) / (self._current_width - 2 * self._border_spacing)))
|
||||
value = self._reverse_widget_scaling((event.x - self._border_spacing) / (self._current_width - 2 * self._border_spacing))
|
||||
|
||||
current_scrollbar_length = self._end_value - self._start_value
|
||||
value = max(current_scrollbar_length / 2, min(value, 1 - (current_scrollbar_length / 2)))
|
||||
@ -242,7 +249,7 @@ class CTkScrollbar(CTkBaseClass):
|
||||
if self._command is not None:
|
||||
self._command('moveto', self._start_value)
|
||||
|
||||
def _mouse_scroll_event(self, event=None):
|
||||
def _mouse_scroll_event(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._command is not None:
|
||||
if sys.platform.startswith("win"):
|
||||
self._command('scroll', -int(event.delta/40), 'units')
|
||||
@ -257,7 +264,7 @@ class CTkScrollbar(CTkBaseClass):
|
||||
def get(self):
|
||||
return self._start_value, self._end_value
|
||||
|
||||
def bind(self, sequence=None, command=None, add=True):
|
||||
def bind(self, sequence=None, command: Callable[..., None] | None = None, add: Literal["+"] | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
|
@ -1,15 +1,13 @@
|
||||
import tkinter
|
||||
import copy
|
||||
from typing import Union, Tuple, List, Dict, Callable, Optional
|
||||
try:
|
||||
from typing import Literal
|
||||
except ImportError:
|
||||
from typing_extensions import Literal
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
from .theme import ThemeManager
|
||||
from .font import CTkFont
|
||||
from .ctk_button import CTkButton
|
||||
from .ctk_frame import CTkFrame
|
||||
from .font import CTkFont
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkSegmentedButton(CTkFrame):
|
||||
@ -19,29 +17,29 @@ class CTkSegmentedButton(CTkFrame):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 140,
|
||||
height: int = 28,
|
||||
corner_radius: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int = 3,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
selected_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
selected_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
unselected_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
unselected_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
background_corner_colors: Union[Tuple[Union[str, Tuple[str, str]]], None] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
selected_color: str | tuple[str, str] | None = None,
|
||||
selected_hover_color: str | tuple[str, str] | None = None,
|
||||
unselected_color: str | tuple[str, str] | None = None,
|
||||
unselected_hover_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
text_color_disabled: str | tuple[str, str] | None = None,
|
||||
background_corner_colors: tuple[str | tuple[str, str]] | None = None,
|
||||
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
values: Optional[list] = None,
|
||||
variable: Union[tkinter.Variable, None] = None,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
values: list[str] | None = None,
|
||||
variable: tkinter.Variable | None = None,
|
||||
dynamic_resizing: bool = True,
|
||||
command: Union[Callable[[str], None], None] = None,
|
||||
command: Callable[[str], None] | None = None,
|
||||
state: str = "normal",
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
|
||||
@ -61,15 +59,15 @@ class CTkSegmentedButton(CTkFrame):
|
||||
|
||||
self._background_corner_colors = background_corner_colors # rendering options for DrawEngine
|
||||
|
||||
self._command: Callable[[str], None] = command
|
||||
self._command = command
|
||||
self._font = CTkFont() if font is None else font
|
||||
self._state = state
|
||||
|
||||
self._buttons_dict: Dict[str, CTkButton] = {} # mapped from value to button object
|
||||
self._buttons_dict: dict[str, CTkButton] = {} # mapped from value to button object
|
||||
if values is None:
|
||||
self._value_list: List[str] = ["CTkSegmentedButton"]
|
||||
self._value_list: list[str] = ["CTkSegmentedButton"]
|
||||
else:
|
||||
self._value_list: List[str] = values # Values ordered like buttons rendered on widget
|
||||
self._value_list: list[str] = values # Values ordered like buttons rendered on widget
|
||||
|
||||
self._dynamic_resizing = dynamic_resizing
|
||||
if not self._dynamic_resizing:
|
||||
@ -83,7 +81,7 @@ class CTkSegmentedButton(CTkFrame):
|
||||
|
||||
self._variable = variable
|
||||
self._variable_callback_blocked: bool = False
|
||||
self._variable_callback_name: Union[str, None] = None
|
||||
self._variable_callback_name: str | None = None
|
||||
|
||||
if self._variable is not None:
|
||||
self._variable_callback_name = self._variable.trace_add("write", self._variable_callback)
|
||||
@ -97,13 +95,13 @@ class CTkSegmentedButton(CTkFrame):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _set_dimensions(self, width: int = None, height: int = None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
for button in self._buttons_dict.values():
|
||||
button.configure(height=height)
|
||||
|
||||
def _variable_callback(self, var_name, index, mode):
|
||||
def _variable_callback(self, var_name: str, index: int, mode: Any):
|
||||
if not self._variable_callback_blocked:
|
||||
self.set(self._variable.get(), from_variable_callback=True)
|
||||
|
||||
@ -172,7 +170,7 @@ class CTkSegmentedButton(CTkFrame):
|
||||
return new_button
|
||||
|
||||
@staticmethod
|
||||
def _check_unique_values(values: List[str]):
|
||||
def _check_unique_values(values: list[str]):
|
||||
""" raises exception if values are not unique """
|
||||
if len(values) != len(set(values)):
|
||||
raise ValueError("CTkSegmentedButton values are not unique")
|
||||
@ -196,7 +194,7 @@ class CTkSegmentedButton(CTkFrame):
|
||||
self._buttons_dict[value] = self._create_button(index, value)
|
||||
self._configure_button_corners_for_index(index)
|
||||
|
||||
def configure(self, **kwargs):
|
||||
def configure(self, **kwargs: Any):
|
||||
if "bg_color" in kwargs:
|
||||
super().configure(bg_color=kwargs.pop("bg_color"))
|
||||
|
||||
@ -298,7 +296,7 @@ class CTkSegmentedButton(CTkFrame):
|
||||
|
||||
super().configure(**kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._sb_corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -413,9 +411,8 @@ class CTkSegmentedButton(CTkFrame):
|
||||
else:
|
||||
raise ValueError(f"CTkSegmentedButton does not contain value '{value}'")
|
||||
|
||||
def bind(self, sequence=None, command=None, add=None):
|
||||
def bind(self, sequence: Any =None, command: Any =None, add: Any = None):
|
||||
raise NotImplementedError
|
||||
|
||||
def unbind(self, sequence=None, funcid=None):
|
||||
def unbind(self, sequence: Any = None, funcid: Any = None):
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
import tkinter
|
||||
import sys
|
||||
from typing import Union, Tuple, Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkSlider(CTkBaseClass):
|
||||
@ -15,30 +16,30 @@ class CTkSlider(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
corner_radius: Optional[int] = None,
|
||||
button_corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
button_length: Optional[int] = None,
|
||||
master: CTkBaseClass,
|
||||
width: int | None = None,
|
||||
height: int | None = None,
|
||||
corner_radius: int | None = None,
|
||||
button_corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
button_length: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
progress_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] = "transparent",
|
||||
progress_color: str | tuple[str, str] | None = None,
|
||||
button_color: str | tuple[str, str] | None = None,
|
||||
button_hover_color: str | tuple[str, str] | None = None,
|
||||
|
||||
from_: int = 0,
|
||||
to: int = 1,
|
||||
state: str = "normal",
|
||||
number_of_steps: Union[int, None] = None,
|
||||
number_of_steps: int | None = None,
|
||||
hover: bool = True,
|
||||
command: Union[Callable[[float], None], None] = None,
|
||||
variable: Union[tkinter.Variable, None] = None,
|
||||
command: Callable[[float], None] | None = None,
|
||||
variable: tkinter.Variable | None = None,
|
||||
orientation: str = "horizontal",
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
# set default dimensions according to orientation
|
||||
if width is None:
|
||||
@ -81,9 +82,9 @@ class CTkSlider(CTkBaseClass):
|
||||
|
||||
# callback and control variables
|
||||
self._command = command
|
||||
self._variable: tkinter.Variable = variable
|
||||
self._variable_callback_blocked: bool = False
|
||||
self._variable_callback_name: Union[bool, None] = None
|
||||
self._variable = variable
|
||||
self._variable_callback_blocked = False
|
||||
self._variable_callback_name: bool | None = None
|
||||
self._state = state
|
||||
|
||||
self.grid_rowconfigure(0, weight=1)
|
||||
@ -106,7 +107,7 @@ class CTkSlider(CTkBaseClass):
|
||||
self.set(self._variable.get(), from_variable_callback=True)
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
if sequence is None or sequence == "<Enter>":
|
||||
self._canvas.bind("<Enter>", self._on_enter)
|
||||
@ -117,14 +118,14 @@ class CTkSlider(CTkBaseClass):
|
||||
if sequence is None or sequence == "<B1-Motion>":
|
||||
self._canvas.bind("<B1-Motion>", self._clicked)
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
height=self._apply_widget_scaling(self._desired_height))
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -151,7 +152,7 @@ class CTkSlider(CTkBaseClass):
|
||||
elif sys.platform.startswith("win"):
|
||||
self.configure(cursor="arrow")
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
if self._orientation.lower() == "horizontal":
|
||||
@ -198,7 +199,7 @@ class CTkSlider(CTkBaseClass):
|
||||
fill=self._apply_appearance_mode(self._button_color),
|
||||
outline=self._apply_appearance_mode(self._button_color))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "state" in kwargs:
|
||||
self._state = kwargs.pop("state")
|
||||
self._set_cursor()
|
||||
@ -257,7 +258,7 @@ class CTkSlider(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "button_corner_radius":
|
||||
@ -298,7 +299,7 @@ class CTkSlider(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def _clicked(self, event=None):
|
||||
def _clicked(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._state == "normal":
|
||||
if self._orientation.lower() == "horizontal":
|
||||
self._value = self._reverse_widget_scaling(event.x / self._current_width)
|
||||
@ -323,20 +324,20 @@ class CTkSlider(CTkBaseClass):
|
||||
if self._command is not None:
|
||||
self._command(self._output_value)
|
||||
|
||||
def _on_enter(self, event=0):
|
||||
def _on_enter(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._hover is True and self._state == "normal":
|
||||
self._hover_state = True
|
||||
self._canvas.itemconfig("slider_parts",
|
||||
fill=self._apply_appearance_mode(self._button_hover_color),
|
||||
outline=self._apply_appearance_mode(self._button_hover_color))
|
||||
|
||||
def _on_leave(self, event=0):
|
||||
def _on_leave(self, event: tkinter.Event[Any] | None = None):
|
||||
self._hover_state = False
|
||||
self._canvas.itemconfig("slider_parts",
|
||||
fill=self._apply_appearance_mode(self._button_color),
|
||||
outline=self._apply_appearance_mode(self._button_color))
|
||||
|
||||
def _round_to_step_size(self, value) -> float:
|
||||
def _round_to_step_size(self, value: float) -> float:
|
||||
if self._number_of_steps is not None:
|
||||
step_size = (self._to - self._from_) / self._number_of_steps
|
||||
value = self._to - (round((self._to - value) / step_size) * step_size)
|
||||
@ -347,7 +348,7 @@ class CTkSlider(CTkBaseClass):
|
||||
def get(self) -> float:
|
||||
return self._output_value
|
||||
|
||||
def set(self, output_value, from_variable_callback=False):
|
||||
def set(self, output_value: float, from_variable_callback: bool = False):
|
||||
if self._from_ < self._to:
|
||||
if output_value > self._to:
|
||||
output_value = self._to
|
||||
@ -369,17 +370,17 @@ class CTkSlider(CTkBaseClass):
|
||||
self._variable.set(round(self._output_value) if isinstance(self._variable, tkinter.IntVar) else self._output_value)
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def _variable_callback(self, var_name, index, mode):
|
||||
def _variable_callback(self, var_name: str, index: int, mode: Any):
|
||||
if not self._variable_callback_blocked:
|
||||
self.set(self._variable.get(), from_variable_callback=True)
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._canvas.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: str = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,12 +1,18 @@
|
||||
import tkinter
|
||||
import sys
|
||||
from typing import Union, Tuple, Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .font import CTkFont
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkSwitch(CTkBaseClass):
|
||||
@ -16,34 +22,34 @@ class CTkSwitch(CTkBaseClass):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 100,
|
||||
height: int = 24,
|
||||
switch_width: int = 36,
|
||||
switch_height: int = 18,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
button_length: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
button_length: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
progress_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] = "transparent",
|
||||
progress_color: str | tuple[str, str] | None = None,
|
||||
button_color: str | tuple[str, str] | None = None,
|
||||
button_hover_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
text_color_disabled: str | tuple[str, str] | None = None,
|
||||
|
||||
text: str = "CTkSwitch",
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
textvariable: Union[tkinter.Variable, None] = None,
|
||||
onvalue: Union[int, str] = 1,
|
||||
offvalue: Union[int, str] = 0,
|
||||
variable: Union[tkinter.Variable, None] = None,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
textvariable: tkinter.Variable | None = None,
|
||||
onvalue: int | str = 1,
|
||||
offvalue: int | str = 0,
|
||||
variable: tkinter.Variable | None = None,
|
||||
hover: bool = True,
|
||||
command: Union[Callable, None] = None,
|
||||
state: str = tkinter.NORMAL,
|
||||
**kwargs):
|
||||
command: Callable[..., None] | None = None,
|
||||
state: Literal["normal", "disabled", "readonly"] = "normal",
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
@ -126,7 +132,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
self._set_cursor()
|
||||
self._draw() # initial draw
|
||||
|
||||
def _create_bindings(self, sequence: Optional[str] = None):
|
||||
def _create_bindings(self, sequence: str | None = None):
|
||||
""" set necessary bindings for functionality of widget, will overwrite other bindings """
|
||||
if sequence is None or sequence == "<Enter>":
|
||||
self._canvas.bind("<Enter>", self._on_enter)
|
||||
@ -138,7 +144,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
self._canvas.bind("<Button-1>", self.toggle)
|
||||
self._text_label.bind("<Button-1>", self.toggle)
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(6))
|
||||
@ -150,7 +156,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
height=self._apply_widget_scaling(self._switch_height))
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width: int = None, height: int = None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._bg_canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -197,7 +203,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
if self._text_label is not None:
|
||||
self._text_label.configure(cursor="hand2")
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
if self._check_state is True:
|
||||
@ -254,7 +260,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
|
||||
self._text_label.configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
require_redraw = True
|
||||
@ -338,7 +344,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -387,7 +393,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def toggle(self, event=None):
|
||||
def toggle(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._state is not tkinter.DISABLED:
|
||||
if self._check_state is True:
|
||||
self._check_state = False
|
||||
@ -404,7 +410,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
if self._command is not None:
|
||||
self._command()
|
||||
|
||||
def select(self, from_variable_callback=False):
|
||||
def select(self, from_variable_callback: bool = False):
|
||||
if self._state is not tkinter.DISABLED or from_variable_callback:
|
||||
self._check_state = True
|
||||
|
||||
@ -415,7 +421,7 @@ class CTkSwitch(CTkBaseClass):
|
||||
self._variable.set(self._onvalue)
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def deselect(self, from_variable_callback=False):
|
||||
def deselect(self, from_variable_callback: bool = False):
|
||||
if self._state is not tkinter.DISABLED or from_variable_callback:
|
||||
self._check_state = False
|
||||
|
||||
@ -426,37 +432,37 @@ class CTkSwitch(CTkBaseClass):
|
||||
self._variable.set(self._offvalue)
|
||||
self._variable_callback_blocked = False
|
||||
|
||||
def get(self) -> Union[int, str]:
|
||||
def get(self) -> int | str:
|
||||
return self._onvalue if self._check_state is True else self._offvalue
|
||||
|
||||
def _on_enter(self, event=0):
|
||||
def _on_enter(self, event: tkinter.Event[Any] | None = None):
|
||||
if self._hover is True and self._state == "normal":
|
||||
self._hover_state = True
|
||||
self._canvas.itemconfig("slider_parts",
|
||||
fill=self._apply_appearance_mode(self._button_hover_color),
|
||||
outline=self._apply_appearance_mode(self._button_hover_color))
|
||||
|
||||
def _on_leave(self, event=0):
|
||||
def _on_leave(self, event: tkinter.Event[Any] | None = None):
|
||||
self._hover_state = False
|
||||
self._canvas.itemconfig("slider_parts",
|
||||
fill=self._apply_appearance_mode(self._button_color),
|
||||
outline=self._apply_appearance_mode(self._button_color))
|
||||
|
||||
def _variable_callback(self, var_name, index, mode):
|
||||
def _variable_callback(self, var_name: str, index: int, mode: Any):
|
||||
if not self._variable_callback_blocked:
|
||||
if self._variable.get() == self._onvalue:
|
||||
self.select(from_variable_callback=True)
|
||||
elif self._variable.get() == self._offvalue:
|
||||
self.deselect(from_variable_callback=True)
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._canvas.bind(sequence, command, add=True)
|
||||
self._text_label.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: str = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
|
@ -1,12 +1,13 @@
|
||||
import tkinter
|
||||
from typing import Union, Tuple, Dict, List, Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from .theme import ThemeManager
|
||||
from .ctk_frame import CTkFrame
|
||||
from .core_rendering import CTkCanvas
|
||||
from .core_rendering import DrawEngine
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .ctk_frame import CTkFrame
|
||||
from .ctk_segmented_button import CTkSegmentedButton
|
||||
from .theme import ThemeManager
|
||||
|
||||
|
||||
class CTkTabview(CTkBaseClass):
|
||||
@ -21,28 +22,28 @@ class CTkTabview(CTkBaseClass):
|
||||
_segmented_button_border_width: int = 3
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 300,
|
||||
height: int = 250,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
|
||||
segmented_button_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
segmented_button_selected_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
segmented_button_selected_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
segmented_button_unselected_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
segmented_button_unselected_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
segmented_button_fg_color: str | tuple[str, str] | None = None,
|
||||
segmented_button_selected_color: str | tuple[str, str] | None = None,
|
||||
segmented_button_selected_hover_color: str | tuple[str, str] | None = None,
|
||||
segmented_button_unselected_color: str | tuple[str, str] | None = None,
|
||||
segmented_button_unselected_hover_color: str | tuple[str, str] | None = None,
|
||||
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color_disabled: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: str | tuple[str, str] | None = None,
|
||||
text_color_disabled: str | tuple[str, str] | None = None,
|
||||
|
||||
command: Union[Callable, None] = None,
|
||||
command: Callable[..., None] | None = None,
|
||||
state: str = "normal",
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer some functionality to CTkFrame
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
@ -91,14 +92,14 @@ class CTkTabview(CTkBaseClass):
|
||||
self._configure_grid()
|
||||
self._set_grid_canvas()
|
||||
|
||||
self._tab_dict: Dict[str, CTkFrame] = {}
|
||||
self._name_list: List[str] = [] # list of unique tab names in order of tabs
|
||||
self._tab_dict: dict[str, CTkFrame] = {}
|
||||
self._name_list: list[str] = [] # list of unique tab names in order of tabs
|
||||
self._current_name: str = ""
|
||||
self._command = command
|
||||
|
||||
self._draw()
|
||||
|
||||
def _segmented_button_callback(self, selected_name):
|
||||
def _segmented_button_callback(self, selected_name: str):
|
||||
self._current_name = selected_name
|
||||
self._grid_forget_all_tabs()
|
||||
self._set_grid_tab_by_name(self._current_name)
|
||||
@ -106,7 +107,7 @@ class CTkTabview(CTkBaseClass):
|
||||
if self._command is not None:
|
||||
self._command()
|
||||
|
||||
def winfo_children(self) -> List[any]:
|
||||
def winfo_children(self) -> list[Any]:
|
||||
"""
|
||||
winfo_children of CTkTabview without canvas and segmented button widgets,
|
||||
because it's not a child but part of the CTkTabview itself
|
||||
@ -120,7 +121,7 @@ class CTkTabview(CTkBaseClass):
|
||||
except ValueError:
|
||||
return child_widgets
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -128,7 +129,7 @@ class CTkTabview(CTkBaseClass):
|
||||
self._configure_grid()
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -211,7 +212,7 @@ class CTkTabview(CTkBaseClass):
|
||||
self._canvas.configure(bg=self._apply_appearance_mode(self._bg_color))
|
||||
tkinter.Frame.configure(self, bg=self._apply_appearance_mode(self._bg_color)) # configure bg color of tkinter.Frame, cuase canvas does not fill frame
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "corner_radius" in kwargs:
|
||||
self._corner_radius = kwargs.pop("corner_radius")
|
||||
require_redraw = True
|
||||
|
@ -1,13 +1,20 @@
|
||||
import tkinter
|
||||
from typing import Union, Tuple, Optional, Callable
|
||||
from __future__ import annotations
|
||||
|
||||
from .core_rendering import CTkCanvas
|
||||
from .ctk_scrollbar import CTkScrollbar
|
||||
from .theme import ThemeManager
|
||||
from .core_rendering import DrawEngine
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Any, Callable
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .core_rendering import CTkCanvas, DrawEngine
|
||||
from .core_widget_classes import CTkBaseClass
|
||||
from .ctk_scrollbar import CTkScrollbar
|
||||
from .font import CTkFont
|
||||
from .utility import pop_from_dict_by_set, check_kwargs_empty
|
||||
from .theme import ThemeManager
|
||||
from .utility import check_kwargs_empty, pop_from_dict_by_set
|
||||
|
||||
|
||||
class CTkTextbox(CTkBaseClass):
|
||||
@ -32,23 +39,23 @@ class CTkTextbox(CTkBaseClass):
|
||||
"xscrollcommand", "yscrollcommand"}
|
||||
|
||||
def __init__(self,
|
||||
master: any,
|
||||
master: CTkBaseClass,
|
||||
width: int = 200,
|
||||
height: int = 200,
|
||||
corner_radius: Optional[int] = None,
|
||||
border_width: Optional[int] = None,
|
||||
corner_radius: int | None = None,
|
||||
border_width: int | None = None,
|
||||
border_spacing: int = 3,
|
||||
|
||||
bg_color: Union[str, Tuple[str, str]] = "transparent",
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, str]] = None,
|
||||
scrollbar_button_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
scrollbar_button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
bg_color: str | tuple[str, str] = "transparent",
|
||||
fg_color: str | tuple[str, str] | None = None,
|
||||
border_color: str | tuple[str, str] | None = None,
|
||||
text_color: str | str | None = None,
|
||||
scrollbar_button_color: str | tuple[str, str] | None = None,
|
||||
scrollbar_button_hover_color: str | tuple[str, str] | None = None,
|
||||
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
font: tuple[Any, ...] | CTkFont | None = None,
|
||||
activate_scrollbars: bool = True,
|
||||
**kwargs):
|
||||
**kwargs: Any):
|
||||
|
||||
# transfer basic functionality (_bg_color, size, __appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(master=master, bg_color=bg_color, width=width, height=height)
|
||||
@ -122,7 +129,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
self.after(50, self._check_if_scrollbars_needed, None, True)
|
||||
self._draw()
|
||||
|
||||
def _create_grid_for_text_and_scrollbars(self, re_grid_textbox=False, re_grid_x_scrollbar=False, re_grid_y_scrollbar=False):
|
||||
def _create_grid_for_text_and_scrollbars(self, re_grid_textbox: bool = False, re_grid_x_scrollbar: bool = False, re_grid_y_scrollbar: bool = False):
|
||||
|
||||
# configure 2x2 grid
|
||||
self.grid_rowconfigure(0, weight=1)
|
||||
@ -151,7 +158,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
else:
|
||||
self._y_scrollbar.grid_forget()
|
||||
|
||||
def _check_if_scrollbars_needed(self, event=None, continue_loop: bool = False):
|
||||
def _check_if_scrollbars_needed(self, event: tkinter.Event[Any] | None = None, continue_loop: bool = False):
|
||||
""" Method hides or places the scrollbars if they are needed on key release event of tkinter.text widget """
|
||||
|
||||
if self._scrollbars_activated:
|
||||
@ -176,7 +183,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
if self._textbox.winfo_exists() and continue_loop is True:
|
||||
self.after(self._scrollbar_update_time, lambda: self._check_if_scrollbars_needed(continue_loop=True))
|
||||
|
||||
def _set_scaling(self, *args, **kwargs):
|
||||
def _set_scaling(self, *args: Any, **kwargs: Any):
|
||||
super()._set_scaling(*args, **kwargs)
|
||||
|
||||
self._textbox.configure(font=self._apply_font_scaling(self._font))
|
||||
@ -185,7 +192,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
self._create_grid_for_text_and_scrollbars(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
||||
self._draw(no_color_updates=True)
|
||||
|
||||
def _set_dimensions(self, width=None, height=None):
|
||||
def _set_dimensions(self, width: int | None = None, height: int | None = None):
|
||||
super()._set_dimensions(width, height)
|
||||
|
||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||
@ -207,7 +214,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
|
||||
super().destroy()
|
||||
|
||||
def _draw(self, no_color_updates=False):
|
||||
def _draw(self, no_color_updates: bool = False):
|
||||
super()._draw(no_color_updates)
|
||||
|
||||
if not self._canvas.winfo_exists():
|
||||
@ -250,7 +257,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
self._canvas.tag_lower("inner_parts")
|
||||
self._canvas.tag_lower("border_parts")
|
||||
|
||||
def configure(self, require_redraw=False, **kwargs):
|
||||
def configure(self, require_redraw: bool = False, **kwargs: Any):
|
||||
if "fg_color" in kwargs:
|
||||
self._fg_color = self._check_color_type(kwargs.pop("fg_color"), transparency=True)
|
||||
require_redraw = True
|
||||
@ -305,7 +312,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
self._textbox.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_text_attributes))
|
||||
super().configure(require_redraw=require_redraw, **kwargs)
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "corner_radius":
|
||||
return self._corner_radius
|
||||
elif attribute_name == "border_width":
|
||||
@ -326,13 +333,13 @@ class CTkTextbox(CTkBaseClass):
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = True):
|
||||
def bind(self, sequence: str, command: Callable[..., None] | None = None, add: str | bool = True):
|
||||
""" called on the tkinter.Canvas """
|
||||
if not (add == "+" or add is True):
|
||||
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
|
||||
self._textbox.bind(sequence, command, add=True)
|
||||
|
||||
def unbind(self, sequence: str = None, funcid: str = None):
|
||||
def unbind(self, sequence: str, funcid: str | None = None):
|
||||
""" called on the tkinter.Label and tkinter.Canvas """
|
||||
if funcid is not None:
|
||||
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
|
||||
@ -348,7 +355,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
def focus_force(self):
|
||||
return self._textbox.focus_force()
|
||||
|
||||
def insert(self, index, text, tags=None):
|
||||
def insert(self, index: int, text: str, tags=None):
|
||||
return self._textbox.insert(index, text, tags)
|
||||
|
||||
def get(self, index1, index2=None):
|
||||
@ -357,7 +364,7 @@ class CTkTextbox(CTkBaseClass):
|
||||
def bbox(self, index):
|
||||
return self._textbox.bbox(index)
|
||||
|
||||
def compare(self, index, op, index2):
|
||||
def compare(self, index: int, op, index2):
|
||||
return self._textbox.compare(index, op, index2)
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
@ -383,10 +390,10 @@ class CTkTextbox(CTkBaseClass):
|
||||
self._check_if_scrollbars_needed()
|
||||
return self._textbox.edit_undo()
|
||||
|
||||
def image_create(self, index, **kwargs):
|
||||
def image_create(self, index: int, **kwargs: Any):
|
||||
raise AttributeError("embedding images is forbidden, because would be incompatible with scaling")
|
||||
|
||||
def image_cget(self, index, option):
|
||||
def image_cget(self, index: int, option):
|
||||
raise AttributeError("embedding images is forbidden, because would be incompatible with scaling")
|
||||
|
||||
def image_configure(self, index):
|
||||
@ -422,78 +429,78 @@ class CTkTextbox(CTkBaseClass):
|
||||
def scan_mark(self, x, y):
|
||||
return self._textbox.scan_mark(x, y)
|
||||
|
||||
def search(self, pattern, index, *args, **kwargs):
|
||||
def search(self, pattern, index: int, *args: Any, **kwargs: Any):
|
||||
return self._textbox.search(pattern, index, *args, **kwargs)
|
||||
|
||||
def see(self, index):
|
||||
return self._textbox.see(index)
|
||||
|
||||
def tag_add(self, tagName, index1, index2=None):
|
||||
def tag_add(self, tagName: str, index1, index2=None):
|
||||
return self._textbox.tag_add(tagName, index1, index2)
|
||||
|
||||
def tag_bind(self, tagName, sequence, func, add=None):
|
||||
def tag_bind(self, tagName: str, sequence: str, func, add: bool | Literal["", "+"] | None = None):
|
||||
return self._textbox.tag_bind(tagName, sequence, func, add)
|
||||
|
||||
def tag_cget(self, tagName, option):
|
||||
def tag_cget(self, tagName: str, option: str):
|
||||
return self._textbox.tag_cget(tagName, option)
|
||||
|
||||
def tag_config(self, tagName, **kwargs):
|
||||
def tag_config(self, tagName: str, **kwargs: Any):
|
||||
if "font" in kwargs:
|
||||
raise AttributeError("'font' option forbidden, because would be incompatible with scaling")
|
||||
return self._textbox.tag_config(tagName, **kwargs)
|
||||
|
||||
def tag_delete(self, *tagName):
|
||||
def tag_delete(self, *tagName: str):
|
||||
return self._textbox.tag_delete(*tagName)
|
||||
|
||||
def tag_lower(self, tagName, belowThis=None):
|
||||
def tag_lower(self, tagName: str, belowThis=None):
|
||||
return self._textbox.tag_lower(tagName, belowThis)
|
||||
|
||||
def tag_names(self, index=None):
|
||||
return self._textbox.tag_names(index)
|
||||
|
||||
def tag_nextrange(self, tagName, index1, index2=None):
|
||||
def tag_nextrange(self, tagName: str, index1, index2=None):
|
||||
return self._textbox.tag_nextrange(tagName, index1, index2)
|
||||
|
||||
def tag_prevrange(self, tagName, index1, index2=None):
|
||||
def tag_prevrange(self, tagName: str, index1, index2=None):
|
||||
return self._textbox.tag_prevrange(tagName, index1, index2)
|
||||
|
||||
def tag_raise(self, tagName, aboveThis=None):
|
||||
def tag_raise(self, tagName: str, aboveThis: str | None = None):
|
||||
return self._textbox.tag_raise(tagName, aboveThis)
|
||||
|
||||
def tag_ranges(self, tagName):
|
||||
def tag_ranges(self, tagName: str):
|
||||
return self._textbox.tag_ranges(tagName)
|
||||
|
||||
def tag_remove(self, tagName, index1, index2=None):
|
||||
def tag_remove(self, tagName: str, index1, index2=None):
|
||||
return self._textbox.tag_remove(tagName, index1, index2)
|
||||
|
||||
def tag_unbind(self, tagName, sequence, funcid=None):
|
||||
def tag_unbind(self, tagName: str, sequence, funcid=None):
|
||||
return self._textbox.tag_unbind(tagName, sequence, funcid)
|
||||
|
||||
def window_cget(self, index, option):
|
||||
def window_cget(self, index: int, option: str):
|
||||
raise AttributeError("embedding widgets is forbidden, would probably cause all kinds of problems ;)")
|
||||
|
||||
def window_configure(self, index, option):
|
||||
def window_configure(self, index: int, option: str):
|
||||
raise AttributeError("embedding widgets is forbidden, would probably cause all kinds of problems ;)")
|
||||
|
||||
def window_create(self, index, **kwargs):
|
||||
def window_create(self, index: int, **kwargs: Any):
|
||||
raise AttributeError("embedding widgets is forbidden, would probably cause all kinds of problems ;)")
|
||||
|
||||
def window_names(self):
|
||||
raise AttributeError("embedding widgets is forbidden, would probably cause all kinds of problems ;)")
|
||||
|
||||
def xview(self, *args):
|
||||
def xview(self, *args: Any):
|
||||
return self._textbox.xview(*args)
|
||||
|
||||
def xview_moveto(self, fraction):
|
||||
def xview_moveto(self, fraction: float):
|
||||
return self._textbox.xview_moveto(fraction)
|
||||
|
||||
def xview_scroll(self, n, what):
|
||||
def xview_scroll(self, n: int, what: Literal["units", "pages"]):
|
||||
return self._textbox.xview_scroll(n, what)
|
||||
|
||||
def yview(self, *args):
|
||||
return self._textbox.yview(*args)
|
||||
|
||||
def yview_moveto(self, fraction):
|
||||
def yview_moveto(self, fraction: float):
|
||||
return self._textbox.yview_moveto(fraction)
|
||||
|
||||
def yview_scroll(self, n, what):
|
||||
|
@ -1,11 +1,10 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .ctk_font import CTkFont
|
||||
from .font_manager import FontManager
|
||||
|
||||
# import DrawEngine to set preferred_drawing_method if loading shapes font fails
|
||||
from ..core_rendering import DrawEngine
|
||||
from .ctk_font import CTkFont
|
||||
from .font_manager import FontManager
|
||||
|
||||
FontManager.init_font_manager()
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
from tkinter.font import Font
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
from typing import List, Callable, Tuple, Optional
|
||||
from tkinter.font import Font
|
||||
from typing import Any, Callable
|
||||
|
||||
try:
|
||||
from typing import Literal
|
||||
except ImportError:
|
||||
@ -25,14 +28,14 @@ class CTkFont(Font):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
family: Optional[str] = None,
|
||||
size: Optional[int] = None,
|
||||
weight: Literal["normal", "bold"] = None,
|
||||
family: str | None = None,
|
||||
size: int | None = None,
|
||||
weight: Literal["normal", "bold"] | None = None,
|
||||
slant: Literal["italic", "roman"] = "roman",
|
||||
underline: bool = False,
|
||||
overstrike: bool = False):
|
||||
|
||||
self._size_configure_callback_list: List[Callable] = []
|
||||
self._size_configure_callback_list: list[Callable[..., None]] = []
|
||||
|
||||
self._size = ThemeManager.theme["CTkFont"]["size"] if size is None else size
|
||||
|
||||
@ -46,22 +49,22 @@ class CTkFont(Font):
|
||||
self._family = super().cget("family")
|
||||
self._tuple_style_string = f"{super().cget('weight')} {slant} {'underline' if underline else ''} {'overstrike' if overstrike else ''}"
|
||||
|
||||
def add_size_configure_callback(self, callback: Callable):
|
||||
def add_size_configure_callback(self, callback: Callable[..., None]):
|
||||
""" add function, that gets called when font got configured """
|
||||
self._size_configure_callback_list.append(callback)
|
||||
|
||||
def remove_size_configure_callback(self, callback: Callable):
|
||||
def remove_size_configure_callback(self, callback: Callable[..., None]):
|
||||
""" remove function, that gets called when font got configured """
|
||||
self._size_configure_callback_list.remove(callback)
|
||||
|
||||
def create_scaled_tuple(self, font_scaling: float) -> Tuple[str, int, str]:
|
||||
def create_scaled_tuple(self, font_scaling: float) -> tuple[str, int, str]:
|
||||
""" return scaled tuple representation of font in the form (family: str, size: int, style: str)"""
|
||||
return self._family, round(-abs(self._size) * font_scaling), self._tuple_style_string
|
||||
|
||||
def config(self, *args, **kwargs):
|
||||
def config(self, *args: Any, **kwargs: Any):
|
||||
raise AttributeError("'config' is not implemented for CTk widgets. For consistency, always use 'configure' instead.")
|
||||
|
||||
def configure(self, **kwargs):
|
||||
def configure(self, **kwargs: Any):
|
||||
if "size" in kwargs:
|
||||
self._size = kwargs.pop("size")
|
||||
super().configure(size=-abs(self._size))
|
||||
@ -79,7 +82,7 @@ class CTkFont(Font):
|
||||
for callback in self._size_configure_callback_list:
|
||||
callback()
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "size":
|
||||
return self._size
|
||||
if attribute_name == "family":
|
||||
@ -87,5 +90,5 @@ class CTkFont(Font):
|
||||
else:
|
||||
return super().cget(attribute_name)
|
||||
|
||||
def copy(self) -> "CTkFont":
|
||||
def copy(self) -> CTkFont:
|
||||
return copy.deepcopy(self)
|
||||
|
@ -1,11 +1,11 @@
|
||||
import sys
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from typing import Union
|
||||
import sys
|
||||
|
||||
|
||||
class FontManager:
|
||||
|
||||
linux_font_path = "~/.fonts/"
|
||||
|
||||
@classmethod
|
||||
@ -25,10 +25,11 @@ class FontManager:
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def windows_load_font(cls, font_path: Union[str, bytes], private: bool = True, enumerable: bool = False) -> bool:
|
||||
""" Function taken from: https://stackoverflow.com/questions/11993290/truly-custom-font-in-tkinter/30631309#30631309 """
|
||||
def windows_load_font(cls, font_path: str | bytes, private: bool = True, enumerable: bool = False) -> bool:
|
||||
""" Function taken from: https://stackoverflow.com/a/30631309/ """
|
||||
|
||||
from ctypes import windll, byref, create_unicode_buffer, create_string_buffer
|
||||
from ctypes import (byref, create_string_buffer, create_unicode_buffer,
|
||||
windll)
|
||||
|
||||
FR_PRIVATE = 0x10
|
||||
FR_NOT_ENUM = 0x20
|
||||
|
@ -1,4 +1,7 @@
|
||||
from typing import Tuple, Dict, Callable, List
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageTk
|
||||
except ImportError:
|
||||
@ -19,9 +22,9 @@ class CTkImage:
|
||||
_checked_PIL_import = False
|
||||
|
||||
def __init__(self,
|
||||
light_image: "Image.Image" = None,
|
||||
dark_image: "Image.Image" = None,
|
||||
size: Tuple[int, int] = (20, 20)):
|
||||
light_image: Image.Image | None = None,
|
||||
dark_image: Image.Image | None = None,
|
||||
size: tuple[int, int] = (20, 20)):
|
||||
|
||||
if not self._checked_PIL_import:
|
||||
self._check_pil_import()
|
||||
@ -31,9 +34,9 @@ class CTkImage:
|
||||
self._check_images()
|
||||
self._size = size
|
||||
|
||||
self._configure_callback_list: List[Callable] = []
|
||||
self._scaled_light_photo_images: Dict[Tuple[int, int], ImageTk.PhotoImage] = {}
|
||||
self._scaled_dark_photo_images: Dict[Tuple[int, int], ImageTk.PhotoImage] = {}
|
||||
self._configure_callback_list: list[Callable[..., None]] = []
|
||||
self._scaled_light_photo_images: dict[tuple[int, int], ImageTk.PhotoImage] = {}
|
||||
self._scaled_dark_photo_images: dict[tuple[int, int], ImageTk.PhotoImage] = {}
|
||||
|
||||
@classmethod
|
||||
def _check_pil_import(cls):
|
||||
@ -42,15 +45,15 @@ class CTkImage:
|
||||
except NameError:
|
||||
raise ImportError("PIL.Image and PIL.ImageTk couldn't be imported")
|
||||
|
||||
def add_configure_callback(self, callback: Callable):
|
||||
def add_configure_callback(self, callback: Callable[..., None]):
|
||||
""" add function, that gets called when image got configured """
|
||||
self._configure_callback_list.append(callback)
|
||||
|
||||
def remove_configure_callback(self, callback: Callable):
|
||||
def remove_configure_callback(self, callback: Callable[..., None]):
|
||||
""" remove function, that gets called when image got configured """
|
||||
self._configure_callback_list.remove(callback)
|
||||
|
||||
def configure(self, **kwargs):
|
||||
def configure(self, **kwargs: Any):
|
||||
if "light_image" in kwargs:
|
||||
self._light_image = kwargs.pop("light_image")
|
||||
self._scaled_light_photo_images = {}
|
||||
@ -66,7 +69,7 @@ class CTkImage:
|
||||
for callback in self._configure_callback_list:
|
||||
callback()
|
||||
|
||||
def cget(self, attribute_name: str) -> any:
|
||||
def cget(self, attribute_name: str) -> Any:
|
||||
if attribute_name == "light_image":
|
||||
return self._light_image
|
||||
if attribute_name == "dark_image":
|
||||
@ -89,24 +92,24 @@ class CTkImage:
|
||||
if self._light_image is not None and self._dark_image is not None and self._light_image.size != self._dark_image.size:
|
||||
raise ValueError(f"CTkImage: light_image size {self._light_image.size} must be the same as dark_image size {self._dark_image.size}.")
|
||||
|
||||
def _get_scaled_size(self, widget_scaling: float) -> Tuple[int, int]:
|
||||
def _get_scaled_size(self, widget_scaling: float) -> tuple[int, int]:
|
||||
return round(self._size[0] * widget_scaling), round(self._size[1] * widget_scaling)
|
||||
|
||||
def _get_scaled_light_photo_image(self, scaled_size: Tuple[int, int]) -> "ImageTk.PhotoImage":
|
||||
def _get_scaled_light_photo_image(self, scaled_size: tuple[int, int]) -> ImageTk.PhotoImage:
|
||||
if scaled_size in self._scaled_light_photo_images:
|
||||
return self._scaled_light_photo_images[scaled_size]
|
||||
else:
|
||||
self._scaled_light_photo_images[scaled_size] = ImageTk.PhotoImage(self._light_image.resize(scaled_size))
|
||||
return self._scaled_light_photo_images[scaled_size]
|
||||
|
||||
def _get_scaled_dark_photo_image(self, scaled_size: Tuple[int, int]) -> "ImageTk.PhotoImage":
|
||||
def _get_scaled_dark_photo_image(self, scaled_size: tuple[int, int]) -> ImageTk.PhotoImage:
|
||||
if scaled_size in self._scaled_dark_photo_images:
|
||||
return self._scaled_dark_photo_images[scaled_size]
|
||||
else:
|
||||
self._scaled_dark_photo_images[scaled_size] = ImageTk.PhotoImage(self._dark_image.resize(scaled_size))
|
||||
return self._scaled_dark_photo_images[scaled_size]
|
||||
|
||||
def create_scaled_photo_image(self, widget_scaling: float, appearance_mode: str) -> "ImageTk.PhotoImage":
|
||||
def create_scaled_photo_image(self, widget_scaling: float, appearance_mode: str) -> ImageTk.PhotoImage:
|
||||
scaled_size = self._get_scaled_size(widget_scaling)
|
||||
|
||||
if appearance_mode == "light" and self._light_image is not None:
|
||||
@ -118,5 +121,3 @@ class CTkImage:
|
||||
return self._get_scaled_dark_photo_image(scaled_size)
|
||||
elif appearance_mode == "dark" and self._dark_image is None:
|
||||
return self._get_scaled_light_photo_image(scaled_size)
|
||||
|
||||
|
||||
|
@ -1,14 +1,20 @@
|
||||
from typing import Union, Tuple
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import re
|
||||
try:
|
||||
import sys
|
||||
from typing import Any, TypeVar
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
except ImportError:
|
||||
else:
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .scaling_tracker import ScalingTracker
|
||||
from ..font import CTkFont
|
||||
from .scaling_tracker import ScalingTracker
|
||||
|
||||
KT = TypeVar("KT")
|
||||
VT = TypeVar("VT")
|
||||
|
||||
class CTkScalingBaseClass:
|
||||
"""
|
||||
@ -46,7 +52,7 @@ class CTkScalingBaseClass:
|
||||
elif self.__scaling_type == "window":
|
||||
ScalingTracker.remove_window(self._set_scaling, self)
|
||||
|
||||
def _set_scaling(self, new_widget_scaling, new_window_scaling):
|
||||
def _set_scaling(self, new_widget_scaling: float, new_window_scaling: float):
|
||||
""" can be overridden, but super method must be called at the beginning """
|
||||
self.__widget_scaling = new_widget_scaling
|
||||
self.__window_scaling = new_window_scaling
|
||||
@ -57,23 +63,23 @@ class CTkScalingBaseClass:
|
||||
def _get_window_scaling(self) -> float:
|
||||
return self.__window_scaling
|
||||
|
||||
def _apply_widget_scaling(self, value: Union[int, float]) -> Union[float]:
|
||||
def _apply_widget_scaling(self, value: int | float) -> float:
|
||||
assert self.__scaling_type == "widget"
|
||||
return value * self.__widget_scaling
|
||||
|
||||
def _reverse_widget_scaling(self, value: Union[int, float]) -> Union[float]:
|
||||
def _reverse_widget_scaling(self, value: int | float) -> float:
|
||||
assert self.__scaling_type == "widget"
|
||||
return value / self.__widget_scaling
|
||||
|
||||
def _apply_window_scaling(self, value: Union[int, float]) -> int:
|
||||
def _apply_window_scaling(self, value: int | float) -> int:
|
||||
assert self.__scaling_type == "window"
|
||||
return int(value * self.__window_scaling)
|
||||
|
||||
def _reverse_window_scaling(self, scaled_value: Union[int, float]) -> int:
|
||||
def _reverse_window_scaling(self, scaled_value: int | float) -> int:
|
||||
assert self.__scaling_type == "window"
|
||||
return int(scaled_value / self.__window_scaling)
|
||||
|
||||
def _apply_font_scaling(self, font: Union[Tuple, CTkFont]) -> tuple:
|
||||
def _apply_font_scaling(self, font: tuple[Any, ...] | CTkFont) -> tuple[str, int, str]:
|
||||
""" Takes CTkFont object and returns tuple font with scaled size, has to be called again for every change of font object """
|
||||
assert self.__scaling_type == "widget"
|
||||
|
||||
@ -92,7 +98,7 @@ class CTkScalingBaseClass:
|
||||
else:
|
||||
raise ValueError(f"Can not scale font '{font}' of type {type(font)}. font needs to be tuple or instance of CTkFont")
|
||||
|
||||
def _apply_argument_scaling(self, kwargs: dict) -> dict:
|
||||
def _apply_argument_scaling(self, kwargs: dict[KT, VT]) -> dict[KT, VT]:
|
||||
assert self.__scaling_type == "widget"
|
||||
|
||||
scaled_kwargs = copy.copy(kwargs)
|
||||
@ -118,7 +124,7 @@ class CTkScalingBaseClass:
|
||||
return scaled_kwargs
|
||||
|
||||
@staticmethod
|
||||
def _parse_geometry_string(geometry_string: str) -> tuple:
|
||||
def _parse_geometry_string(geometry_string: str) -> tuple[int | None, int | None, int | None, int | None]:
|
||||
# index: 1 2 3 4 5 6
|
||||
# regex group structure: ('<width>x<height>', '<width>', '<height>', '+-<x>+-<y>', '-<x>', '-<y>')
|
||||
result = re.search(r"((\d+)x(\d+)){0,1}(\+{0,1}([+-]{0,1}\d+)\+{0,1}([+-]{0,1}\d+)){0,1}", geometry_string)
|
||||
|
@ -1,13 +1,17 @@
|
||||
import tkinter
|
||||
import sys
|
||||
from typing import Callable
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import tkinter
|
||||
from typing import Callable, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from customtkinter.windows import CTk
|
||||
|
||||
class ScalingTracker:
|
||||
deactivate_automatic_dpi_awareness = False
|
||||
|
||||
window_widgets_dict = {} # contains window objects as keys with list of widget callbacks as elements
|
||||
window_dpi_scaling_dict = {} # contains window objects as keys and corresponding scaling factors
|
||||
window_widgets_dict: dict[CTk, list[Callable[..., None]]] = {} # contains window objects as keys with list of widget callbacks as elements
|
||||
window_dpi_scaling_dict: dict[CTk, float] = {} # contains window objects as keys and corresponding scaling factors
|
||||
|
||||
widget_scaling = 1 # user values which multiply to detected window scaling factor
|
||||
window_scaling = 1
|
||||
@ -17,12 +21,12 @@ class ScalingTracker:
|
||||
loop_pause_after_new_scaling = 1500 # ms
|
||||
|
||||
@classmethod
|
||||
def get_widget_scaling(cls, widget) -> float:
|
||||
def get_widget_scaling(cls, widget: CTk) -> float:
|
||||
window_root = cls.get_window_root_of_widget(widget)
|
||||
return cls.window_dpi_scaling_dict[window_root] * cls.widget_scaling
|
||||
|
||||
@classmethod
|
||||
def get_window_scaling(cls, window) -> float:
|
||||
def get_window_scaling(cls, window: CTk) -> float:
|
||||
window_root = cls.get_window_root_of_widget(window)
|
||||
return cls.window_dpi_scaling_dict[window_root] * cls.window_scaling
|
||||
|
||||
@ -37,7 +41,7 @@ class ScalingTracker:
|
||||
cls.update_scaling_callbacks_all()
|
||||
|
||||
@classmethod
|
||||
def get_window_root_of_widget(cls, widget):
|
||||
def get_window_root_of_widget(cls, widget: CTk):
|
||||
current_widget = widget
|
||||
|
||||
while isinstance(current_widget, tkinter.Tk) is False and\
|
||||
@ -58,7 +62,7 @@ class ScalingTracker:
|
||||
cls.window_scaling)
|
||||
|
||||
@classmethod
|
||||
def update_scaling_callbacks_for_window(cls, window):
|
||||
def update_scaling_callbacks_for_window(cls, window: CTk):
|
||||
for set_scaling_callback in cls.window_widgets_dict[window]:
|
||||
if not cls.deactivate_automatic_dpi_awareness:
|
||||
set_scaling_callback(cls.window_dpi_scaling_dict[window] * cls.widget_scaling,
|
||||
@ -68,7 +72,7 @@ class ScalingTracker:
|
||||
cls.window_scaling)
|
||||
|
||||
@classmethod
|
||||
def add_widget(cls, widget_callback: Callable, widget):
|
||||
def add_widget(cls, widget_callback: Callable[..., None], widget: CTk):
|
||||
window_root = cls.get_window_root_of_widget(widget)
|
||||
|
||||
if window_root not in cls.window_widgets_dict:
|
||||
@ -84,7 +88,7 @@ class ScalingTracker:
|
||||
cls.update_loop_running = True
|
||||
|
||||
@classmethod
|
||||
def remove_widget(cls, widget_callback, widget):
|
||||
def remove_widget(cls, widget_callback: Callable[..., None], widget: CTk):
|
||||
window_root = cls.get_window_root_of_widget(widget)
|
||||
try:
|
||||
cls.window_widgets_dict[window_root].remove(widget_callback)
|
||||
@ -92,14 +96,14 @@ class ScalingTracker:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def remove_window(cls, window_callback, window):
|
||||
def remove_window(cls, window_callback: Callable[..., None], window: CTk):
|
||||
try:
|
||||
del cls.window_widgets_dict[window]
|
||||
except:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def add_window(cls, window_callback, window):
|
||||
def add_window(cls, window_callback: Callable[..., None], window: CTk):
|
||||
if window not in cls.window_widgets_dict:
|
||||
cls.window_widgets_dict[window] = [window_callback]
|
||||
else:
|
||||
@ -135,11 +139,9 @@ class ScalingTracker:
|
||||
# DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18,
|
||||
# DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 34
|
||||
# }
|
||||
|
||||
# ctypes.windll.user32.SetProcessDpiAwarenessContext(34) # Non client area scaling at runtime (titlebar)
|
||||
# does not work with resizable(False, False), window starts growing on monitor with different scaling (weird tkinter bug...)
|
||||
# ctypes.windll.user32.EnableNonClientDpiScaling(hwnd) does not work for some reason (tested on Windows 11)
|
||||
|
||||
# It's too bad, that these Windows API methods don't work properly with tkinter. But I tested days with multiple monitor setups,
|
||||
# and I don't think there is anything left to do. So this is the best option at the moment:
|
||||
|
||||
@ -148,13 +150,13 @@ class ScalingTracker:
|
||||
pass # DPI awareness on Linux not implemented
|
||||
|
||||
@classmethod
|
||||
def get_window_dpi_scaling(cls, window) -> float:
|
||||
def get_window_dpi_scaling(cls, window: CTk) -> float:
|
||||
if not cls.deactivate_automatic_dpi_awareness:
|
||||
if sys.platform == "darwin":
|
||||
return 1 # scaling works automatically on macOS
|
||||
|
||||
elif sys.platform.startswith("win"):
|
||||
from ctypes import windll, pointer, wintypes
|
||||
from ctypes import pointer, windll, wintypes
|
||||
|
||||
DPI100pc = 96 # DPI 96 is 100% scaling
|
||||
DPI_type = 0 # MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2
|
||||
@ -181,14 +183,14 @@ class ScalingTracker:
|
||||
cls.window_dpi_scaling_dict[window] = current_dpi_scaling_value
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
window.attributes("-alpha", 0.15)
|
||||
window.attributes("-alpha", 0.15) # type: ignore
|
||||
|
||||
window.block_update_dimensions_event()
|
||||
cls.update_scaling_callbacks_for_window(window)
|
||||
window.unblock_update_dimensions_event()
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
window.attributes("-alpha", 1)
|
||||
window.attributes("-alpha", 1) # type: ignore
|
||||
|
||||
new_scaling_detected = True
|
||||
|
||||
|
@ -1,24 +1,25 @@
|
||||
import sys
|
||||
import os
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import List, Union
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ThemeManager:
|
||||
|
||||
theme: dict = {} # contains all the theme data
|
||||
_built_in_themes: List[str] = ["blue", "green", "dark-blue", "sweetkind"]
|
||||
_currently_loaded_theme: Union[str, None] = None
|
||||
theme: dict[str, Any] = {} # contains all the theme data
|
||||
_built_in_themes: list[str] = ["blue", "green", "dark-blue", "sweetkind"]
|
||||
_currently_loaded_theme: str | None = None
|
||||
|
||||
@classmethod
|
||||
def load_theme(cls, theme_name_or_path: str):
|
||||
script_directory = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
if theme_name_or_path in cls._built_in_themes:
|
||||
with open(os.path.join(script_directory, "../../../assets", "themes", f"{theme_name_or_path}.json"), "r") as f:
|
||||
with open(os.path.join(script_directory, "../../../assets", "themes", f"{theme_name_or_path}.json")) as f:
|
||||
cls.theme = json.load(f)
|
||||
else:
|
||||
with open(theme_name_or_path, "r") as f:
|
||||
with open(theme_name_or_path) as f:
|
||||
cls.theme = json.load(f)
|
||||
|
||||
# store theme path for saving
|
||||
@ -39,9 +40,9 @@ class ThemeManager:
|
||||
def save_theme(cls):
|
||||
if cls._currently_loaded_theme is not None:
|
||||
if cls._currently_loaded_theme not in cls._built_in_themes:
|
||||
with open(cls._currently_loaded_theme, "r") as f:
|
||||
with open(cls._currently_loaded_theme) as f:
|
||||
json.dump(cls.theme, f, indent=2)
|
||||
else:
|
||||
raise ValueError(f"cannot modify builtin theme '{cls._currently_loaded_theme}'")
|
||||
else:
|
||||
raise ValueError(f"cannot save theme, no theme is loaded")
|
||||
raise ValueError("cannot save theme, no theme is loaded")
|
||||
|
@ -1 +1 @@
|
||||
from .utility_functions import pop_from_dict_by_set, check_kwargs_empty
|
||||
from .utility_functions import check_kwargs_empty, pop_from_dict_by_set
|
||||
|
@ -1,7 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
def pop_from_dict_by_set(dictionary: dict, valid_keys: set) -> dict:
|
||||
from typing import Any, TypeVar
|
||||
|
||||
KT = TypeVar("KT")
|
||||
VT = TypeVar("VT")
|
||||
|
||||
|
||||
def pop_from_dict_by_set(dictionary: dict[KT, VT], valid_keys: set[KT]) -> dict[KT, VT]:
|
||||
""" remove and create new dict with key value pairs of dictionary, where key is in valid_keys """
|
||||
new_dictionary = {}
|
||||
new_dictionary: dict[KT, VT] = {}
|
||||
|
||||
for key in list(dictionary.keys()):
|
||||
if key in valid_keys:
|
||||
@ -10,7 +17,7 @@ def pop_from_dict_by_set(dictionary: dict, valid_keys: set) -> dict:
|
||||
return new_dictionary
|
||||
|
||||
|
||||
def check_kwargs_empty(kwargs_dict, raise_error=False) -> bool:
|
||||
def check_kwargs_empty(kwargs_dict: dict[Any, Any], raise_error: bool =False) -> bool:
|
||||
""" returns True if kwargs are empty, False otherwise, raises error if not empty """
|
||||
|
||||
if len(kwargs_dict) > 0:
|
||||
|
@ -1,3 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import customtkinter
|
||||
from PIL import Image
|
||||
import os
|
||||
@ -9,7 +13,7 @@ class App(customtkinter.CTk):
|
||||
width = 900
|
||||
height = 600
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, *args: Any, **kwargs: Any):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.title("CustomTkinter example_background_image.py")
|
||||
|
@ -1,3 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import customtkinter
|
||||
import os
|
||||
from PIL import Image
|
||||
@ -79,7 +83,7 @@ class App(customtkinter.CTk):
|
||||
# select default frame
|
||||
self.select_frame_by_name("home")
|
||||
|
||||
def select_frame_by_name(self, name):
|
||||
def select_frame_by_name(self, name: str):
|
||||
# set button color for selected button
|
||||
self.home_button.configure(fg_color=("gray75", "gray25") if name == "home" else "transparent")
|
||||
self.frame_2_button.configure(fg_color=("gray75", "gray25") if name == "frame_2" else "transparent")
|
||||
@ -108,11 +112,10 @@ class App(customtkinter.CTk):
|
||||
def frame_3_button_event(self):
|
||||
self.select_frame_by_name("frame_3")
|
||||
|
||||
def change_appearance_mode_event(self, new_appearance_mode):
|
||||
def change_appearance_mode_event(self, new_appearance_mode: Any):
|
||||
customtkinter.set_appearance_mode(new_appearance_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = App()
|
||||
app.mainloop()
|
||||
|
||||
|
@ -1,25 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
import customtkinter
|
||||
import os
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class ScrollableCheckBoxFrame(customtkinter.CTkScrollableFrame):
|
||||
def __init__(self, master, item_list, command=None, **kwargs):
|
||||
def __init__(self, master, item_list: list[str], command: Callable[..., None] | None = None, **kwargs: Any):
|
||||
super().__init__(master, **kwargs)
|
||||
|
||||
self.command = command
|
||||
self.checkbox_list = []
|
||||
for i, item in enumerate(item_list):
|
||||
self.checkbox_list: list[customtkinter.CTkCheckBox] = []
|
||||
for item in item_list:
|
||||
self.add_item(item)
|
||||
|
||||
def add_item(self, item):
|
||||
def add_item(self, item: str):
|
||||
checkbox = customtkinter.CTkCheckBox(self, text=item)
|
||||
if self.command is not None:
|
||||
checkbox.configure(command=self.command)
|
||||
checkbox.grid(row=len(self.checkbox_list), column=0, pady=(0, 10))
|
||||
self.checkbox_list.append(checkbox)
|
||||
|
||||
def remove_item(self, item):
|
||||
def remove_item(self, item: str):
|
||||
for checkbox in self.checkbox_list:
|
||||
if item == checkbox.cget("text"):
|
||||
checkbox.destroy()
|
||||
@ -31,7 +35,7 @@ class ScrollableCheckBoxFrame(customtkinter.CTkScrollableFrame):
|
||||
|
||||
|
||||
class ScrollableRadiobuttonFrame(customtkinter.CTkScrollableFrame):
|
||||
def __init__(self, master, item_list, command=None, **kwargs):
|
||||
def __init__(self, master, item_list: list[str], command: Callable[..., None] | None = None, **kwargs: Any):
|
||||
super().__init__(master, **kwargs)
|
||||
|
||||
self.command = command
|
||||
@ -59,7 +63,7 @@ class ScrollableRadiobuttonFrame(customtkinter.CTkScrollableFrame):
|
||||
|
||||
|
||||
class ScrollableLabelButtonFrame(customtkinter.CTkScrollableFrame):
|
||||
def __init__(self, master, command=None, **kwargs):
|
||||
def __init__(self, master, command=None, **kwargs: Any):
|
||||
super().__init__(master, **kwargs)
|
||||
self.grid_columnconfigure(0, weight=1)
|
||||
|
||||
|
14
setup.cfg
14
setup.cfg
@ -2,19 +2,24 @@
|
||||
name = customtkinter
|
||||
version = 5.1.2
|
||||
description = Create modern looking GUIs with Python
|
||||
long_description = A modern and customizable python UI-library based on Tkinter: https://github.com/TomSchimansky/CustomTkinter
|
||||
long_description = file: Readme.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://github.com/TomSchimansky/CustomTkinter
|
||||
author = Tom Schimansky
|
||||
license = Creative Commons Zero v1.0 Universal
|
||||
license = MIT
|
||||
license_file = LICENSE
|
||||
classifiers =
|
||||
License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
||||
License :: OSI Approved :: MIT License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
|
||||
[options]
|
||||
python_requires = >=3.7
|
||||
packages =
|
||||
customtkinter
|
||||
customtkinter.windows
|
||||
@ -29,5 +34,6 @@ packages =
|
||||
customtkinter.windows.widgets.utility
|
||||
install_requires =
|
||||
darkdetect
|
||||
typing_extensions; python_version<="3.7"
|
||||
typing-extensions;python_version=="3.7"
|
||||
python_requires = >=3.7
|
||||
include_package_data = True
|
||||
|
@ -1,10 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
import customtkinter
|
||||
|
||||
customtkinter.set_appearance_mode("dark")
|
||||
|
||||
|
||||
class ToplevelWindow(customtkinter.CTkToplevel):
|
||||
def __init__(self, *args, closing_event=None, **kwargs):
|
||||
def __init__(self, *args: Any, closing_event: Callable[[], None] | None = None, **kwargs: Any):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.protocol("WM_DELETE_WINDOW", self.closing)
|
||||
self.geometry("500x300")
|
||||
|
@ -1,12 +1,11 @@
|
||||
import tkinter.messagebox
|
||||
import customtkinter
|
||||
|
||||
customtkinter.set_appearance_mode("dark")
|
||||
|
||||
|
||||
class App(customtkinter.CTk):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, *args: Any, **kwargs: Any):
|
||||
super().__init__(*args, **kwargs: Any)
|
||||
|
||||
self.title("test filedialog")
|
||||
|
||||
|
@ -40,4 +40,3 @@ label_3 = customtkinter.CTkLabel(app, image=ImageTk.PhotoImage(Image.open(file_p
|
||||
label_3.pack(padx=20, pady=20)
|
||||
|
||||
app.mainloop()
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import time
|
||||
import customtkinter
|
||||
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import time
|
||||
import customtkinter
|
||||
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import customtkinter
|
||||
import time
|
||||
|
||||
app = customtkinter.CTk()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user