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