13 Commits

14 changed files with 249 additions and 107 deletions

View File

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [4.6.0] - 2022-09-17
### Added
- CTkProgressBar indeterminate mode, automatic progress loop with .start() and .stop()
## [4.5.0] - 2022-06-23 ## [4.5.0] - 2022-06-23
### Added ### Added
- CTkScrollbar (vertical, horizontal) - CTkScrollbar (vertical, horizontal)

View File

@ -31,7 +31,9 @@ pip3 install customtkinter
## Documentation ## Documentation
A detailed documentation can be found in the Wiki Tab here: **[Documentation](https://github.com/TomSchimansky/CustomTkinter/wiki)**. The **official** documentation can be found in the Wiki Tab here:
**--> [Documentation](https://github.com/TomSchimansky/CustomTkinter/wiki)**.
## Example Program ## Example Program
To test customtkinter you can try this simple example with only a single button: To test customtkinter you can try this simple example with only a single button:

View File

@ -1,4 +1,4 @@
__version__ = "4.5.11" __version__ = "4.6.1"
import os import os
import sys import sys

View File

@ -645,8 +645,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: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
border_width: Union[float, int], progress_value: float, orientation: str) -> bool: border_width: Union[float, int], progress_value_1: float, progress_value_2: float, orientation: str) -> bool:
""" Draws a rounded bar on the canvas, which is split in half according to the argument 'progress_value' (0 - 1). """ 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).
@ -668,13 +668,13 @@ class DrawEngine:
if self.preferred_drawing_method == "polygon_shapes" or self.preferred_drawing_method == "circle_shapes": if self.preferred_drawing_method == "polygon_shapes" or self.preferred_drawing_method == "circle_shapes":
return self.__draw_rounded_progress_bar_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius, return self.__draw_rounded_progress_bar_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius,
progress_value, orientation) progress_value_1, progress_value_2, orientation)
elif self.preferred_drawing_method == "font_shapes": elif self.preferred_drawing_method == "font_shapes":
return self.__draw_rounded_progress_bar_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius, return self.__draw_rounded_progress_bar_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius,
progress_value, orientation) progress_value_1, progress_value_2, orientation)
def __draw_rounded_progress_bar_with_border_polygon_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int, def __draw_rounded_progress_bar_with_border_polygon_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int,
progress_value: float, orientation: str) -> bool: progress_value_1: float, progress_value_2: float, orientation: str) -> bool:
requires_recoloring = self.__draw_rounded_rect_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius) requires_recoloring = self.__draw_rounded_rect_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius)
@ -691,32 +691,32 @@ class DrawEngine:
if orientation == "w": if orientation == "w":
self._canvas.coords("progress_line_1", self._canvas.coords("progress_line_1",
border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_1,
border_width + inner_corner_radius, border_width + inner_corner_radius,
border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_2,
border_width + inner_corner_radius, border_width + inner_corner_radius,
border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value, border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_2,
border_width + inner_corner_radius,
border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value,
height - (border_width + inner_corner_radius) + bottom_right_shift, height - (border_width + inner_corner_radius) + bottom_right_shift,
border_width + inner_corner_radius, border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_1,
height - (border_width + inner_corner_radius) + bottom_right_shift) height - (border_width + inner_corner_radius) + bottom_right_shift)
elif orientation == "s": elif orientation == "s":
self._canvas.coords("progress_line_1", self._canvas.coords("progress_line_1",
border_width + inner_corner_radius, border_width + inner_corner_radius,
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_2),
width - (border_width + inner_corner_radius), width - (border_width + inner_corner_radius),
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_2),
width - (border_width + inner_corner_radius), width - (border_width + inner_corner_radius),
height - (border_width + inner_corner_radius) + bottom_right_shift, border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_1),
border_width + inner_corner_radius, border_width + inner_corner_radius,
height - (border_width + inner_corner_radius) + bottom_right_shift) border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_1))
self._canvas.itemconfig("progress_line_1", width=inner_corner_radius * 2) self._canvas.itemconfig("progress_line_1", width=inner_corner_radius * 2)
return requires_recoloring return requires_recoloring
def __draw_rounded_progress_bar_with_border_font_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int, def __draw_rounded_progress_bar_with_border_font_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int,
progress_value: float, orientation: str) -> bool: progress_value_1: float, progress_value_2: float, orientation: str) -> bool:
requires_recoloring, requires_recoloring_2 = False, False requires_recoloring, requires_recoloring_2 = False, False
@ -751,64 +751,72 @@ class DrawEngine:
# horizontal orientation from the bottom # horizontal orientation from the bottom
if orientation == "w": if orientation == "w":
requires_recoloring_2 = self.__draw_rounded_rect_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius, requires_recoloring_2 = self.__draw_rounded_rect_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius,
("inner_oval_1", "inner_oval_4")) ())
# set positions of progress corner parts # set positions of progress corner parts
self._canvas.coords("progress_oval_1_a", border_width + inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius) self._canvas.coords("progress_oval_1_a", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_1,
self._canvas.coords("progress_oval_1_b", border_width + inner_corner_radius, border_width + inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_2_a", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value,
border_width + inner_corner_radius, inner_corner_radius) border_width + inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_2_b", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value, self._canvas.coords("progress_oval_1_b", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_1,
border_width + inner_corner_radius, inner_corner_radius) border_width + inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_3_a", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value, self._canvas.coords("progress_oval_2_a", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_2,
border_width + inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_2_b", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_2,
border_width + inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_3_a", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_2,
height - border_width - inner_corner_radius, inner_corner_radius) height - border_width - inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_3_b", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value, self._canvas.coords("progress_oval_3_b", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_2,
height - border_width - inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_4_a", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_1,
height - border_width - inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_4_b", border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_1,
height - border_width - inner_corner_radius, inner_corner_radius) height - border_width - inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_4_a", border_width + inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius)
self._canvas.coords("progress_oval_4_b", border_width + inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius)
# set positions of progress rect parts # set positions of progress rect parts
self._canvas.coords("progress_rectangle_1", self._canvas.coords("progress_rectangle_1",
border_width + inner_corner_radius, border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_1,
border_width, border_width,
border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value, border_width + inner_corner_radius + (width - 2 * border_width - 2 * inner_corner_radius) * progress_value_2,
height - border_width) height - border_width)
self._canvas.coords("progress_rectangle_2", self._canvas.coords("progress_rectangle_2",
border_width, border_width + 2 * inner_corner_radius + (width - 2 * inner_corner_radius - 2 * border_width) * progress_value_1,
border_width + inner_corner_radius, border_width + inner_corner_radius,
border_width + 2 * inner_corner_radius + (width - 2 * inner_corner_radius - 2 * border_width) * progress_value, border_width + 2 * inner_corner_radius + (width - 2 * inner_corner_radius - 2 * border_width) * progress_value_2,
height - inner_corner_radius - border_width) height - inner_corner_radius - border_width)
# vertical orientation from the bottom # vertical orientation from the bottom
if orientation == "s": if orientation == "s":
requires_recoloring_2 = self.__draw_rounded_rect_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius, requires_recoloring_2 = self.__draw_rounded_rect_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius,
("inner_oval_3", "inner_oval_4")) ())
# set positions of progress corner parts # set positions of progress corner parts
self._canvas.coords("progress_oval_1_a", border_width + inner_corner_radius, self._canvas.coords("progress_oval_1_a", border_width + inner_corner_radius,
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), inner_corner_radius) border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_2), inner_corner_radius)
self._canvas.coords("progress_oval_1_b", border_width + inner_corner_radius, self._canvas.coords("progress_oval_1_b", border_width + inner_corner_radius,
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), inner_corner_radius) border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_2), inner_corner_radius)
self._canvas.coords("progress_oval_2_a", width - border_width - inner_corner_radius, self._canvas.coords("progress_oval_2_a", width - border_width - inner_corner_radius,
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), inner_corner_radius) border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_2), inner_corner_radius)
self._canvas.coords("progress_oval_2_b", width - border_width - inner_corner_radius, self._canvas.coords("progress_oval_2_b", width - border_width - inner_corner_radius,
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), inner_corner_radius) border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_2), inner_corner_radius)
self._canvas.coords("progress_oval_3_a", width - border_width - inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) self._canvas.coords("progress_oval_3_a", width - border_width - inner_corner_radius,
self._canvas.coords("progress_oval_3_b", width - border_width - inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_1), inner_corner_radius)
self._canvas.coords("progress_oval_4_a", border_width + inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) self._canvas.coords("progress_oval_3_b", width - border_width - inner_corner_radius,
self._canvas.coords("progress_oval_4_b", border_width + inner_corner_radius, height - border_width - inner_corner_radius, inner_corner_radius) border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_1), inner_corner_radius)
self._canvas.coords("progress_oval_4_a", border_width + inner_corner_radius,
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_1), inner_corner_radius)
self._canvas.coords("progress_oval_4_b", border_width + inner_corner_radius,
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_1), inner_corner_radius)
# set positions of progress rect parts # set positions of progress rect parts
self._canvas.coords("progress_rectangle_1", self._canvas.coords("progress_rectangle_1",
border_width + inner_corner_radius, border_width + inner_corner_radius,
border_width + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), border_width + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_2),
width - border_width - inner_corner_radius, width - border_width - inner_corner_radius,
height - border_width) border_width + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_1))
self._canvas.coords("progress_rectangle_2", self._canvas.coords("progress_rectangle_2",
border_width, border_width,
border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value), border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_2),
width - border_width, width - border_width,
height - inner_corner_radius - border_width) border_width + inner_corner_radius + (height - 2 * border_width - 2 * inner_corner_radius) * (1 - progress_value_1))
return requires_recoloring or requires_recoloring_2 return requires_recoloring or requires_recoloring_2
@ -847,7 +855,7 @@ class DrawEngine:
# draw normal progressbar # draw normal progressbar
requires_recoloring = self.__draw_rounded_progress_bar_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius, requires_recoloring = self.__draw_rounded_progress_bar_with_border_polygon_shapes(width, height, corner_radius, border_width, inner_corner_radius,
slider_value, orientation) 0, slider_value, orientation)
# create slider button part # create slider button part
if not self._canvas.find_withtag("slider_parts"): if not self._canvas.find_withtag("slider_parts"):
@ -886,7 +894,7 @@ class DrawEngine:
# draw normal progressbar # draw normal progressbar
requires_recoloring = self.__draw_rounded_progress_bar_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius, requires_recoloring = self.__draw_rounded_progress_bar_with_border_font_shapes(width, height, corner_radius, border_width, inner_corner_radius,
slider_value, orientation) 0, slider_value, orientation)
# create 4 circles (if not needed, then less) # create 4 circles (if not needed, then less)
if not self._canvas.find_withtag("slider_oval_1_a"): if not self._canvas.find_withtag("slider_oval_1_a"):

View File

@ -6,7 +6,7 @@ from typing import Union
class FontManager: class FontManager:
linux_font_path = "~/.local/share/fonts/" linux_font_path = "~/.fonts/"
@classmethod @classmethod
def init_font_manager(cls): def init_font_manager(cls):

View File

@ -63,14 +63,9 @@ class CTkComboBox(CTkBaseClass):
else: else:
self.values = values self.values = values
if len(self.values) > 0:
self.current_value = self.values[0]
else:
self.current_value = "CTkComboBox"
self.dropdown_menu = DropdownMenu(master=self, self.dropdown_menu = DropdownMenu(master=self,
values=self.values, values=self.values,
command=self.set, command=self.dropdown_callback,
fg_color=dropdown_color, fg_color=dropdown_color,
hover_color=dropdown_hover_color, hover_color=dropdown_hover_color,
text_color=dropdown_text_color, text_color=dropdown_text_color,
@ -98,8 +93,11 @@ class CTkComboBox(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))))
self.entry.delete(0, tkinter.END) # insert default value
self.entry.insert(0, self.current_value) if len(self.values) > 0:
self.entry.insert(0, self.values[0])
else:
self.entry.insert(0, "CTkComboBox")
self.draw() # initial draw self.draw() # initial draw
@ -268,21 +266,28 @@ class CTkComboBox(CTkBaseClass):
outline=ThemeManager.single_color(self.button_color, self._appearance_mode), outline=ThemeManager.single_color(self.button_color, self._appearance_mode),
fill=ThemeManager.single_color(self.button_color, self._appearance_mode)) fill=ThemeManager.single_color(self.button_color, self._appearance_mode))
def set(self, value: str, from_variable_callback: bool = False): def dropdown_callback(self, value: str):
self.current_value = value
if self.state == "readonly": if self.state == "readonly":
self.entry.configure(state="normal") self.entry.configure(state="normal")
self.entry.delete(0, tkinter.END) self.entry.delete(0, tkinter.END)
self.entry.insert(0, self.current_value) self.entry.insert(0, value)
self.entry.configure(state="readonly") self.entry.configure(state="readonly")
else: else:
self.entry.delete(0, tkinter.END) self.entry.delete(0, tkinter.END)
self.entry.insert(0, self.current_value) self.entry.insert(0, value)
if not from_variable_callback: if self.command is not None:
if self.command is not None: self.command(value)
self.command(self.current_value)
def set(self, value: str):
if self.state == "readonly":
self.entry.configure(state="normal")
self.entry.delete(0, tkinter.END)
self.entry.insert(0, value)
self.entry.configure(state="readonly")
else:
self.entry.delete(0, tkinter.END)
self.entry.insert(0, value)
def get(self) -> str: def get(self) -> str:
return self.entry.get() return self.entry.get()

View File

@ -72,7 +72,7 @@ class CTkOptionMenu(CTkBaseClass):
self.dropdown_menu = DropdownMenu(master=self, self.dropdown_menu = DropdownMenu(master=self,
values=self.values, values=self.values,
command=self.set, command=self.dropdown_callback,
fg_color=dropdown_color, fg_color=dropdown_color,
hover_color=dropdown_hover_color, hover_color=dropdown_hover_color,
text_color=dropdown_text_color, text_color=dropdown_text_color,
@ -124,7 +124,8 @@ class CTkOptionMenu(CTkBaseClass):
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)
self.set(self.variable.get(), from_variable_callback=True) self.current_value = self.variable.get()
self.text_label.configure(text=self.current_value)
def set_scaling(self, *args, **kwargs): def set_scaling(self, *args, **kwargs):
super().set_scaling(*args, **kwargs) super().set_scaling(*args, **kwargs)
@ -225,7 +226,7 @@ class CTkOptionMenu(CTkBaseClass):
if self.variable is not None and self.variable != "": if self.variable is not None and self.variable != "":
self.variable_callback_name = self.variable.trace_add("write", self.variable_callback) self.variable_callback_name = self.variable.trace_add("write", self.variable_callback)
self.set(self.variable.get(), from_variable_callback=True) self.set(self.variable.get(), block_set_variable=True)
else: else:
self.variable = None self.variable = None
@ -277,21 +278,29 @@ class CTkOptionMenu(CTkBaseClass):
def variable_callback(self, var_name, index, mode): def variable_callback(self, var_name, index, mode):
if not self.variable_callback_blocked: if not self.variable_callback_blocked:
self.set(self.variable.get(), from_variable_callback=True) self.current_value = self.variable.get()
self.text_label.configure(text=self.current_value)
def set(self, value: str, from_variable_callback: bool = False): def dropdown_callback(self, value: str):
self.current_value = value self.current_value = value
self.text_label.configure(text=self.current_value) self.text_label.configure(text=self.current_value)
if self.variable is not None and not from_variable_callback: if self.variable is not None:
self.variable_callback_blocked = True self.variable_callback_blocked = True
self.variable.set(self.current_value) self.variable.set(self.current_value)
self.variable_callback_blocked = False self.variable_callback_blocked = False
if not from_variable_callback: if self.command is not None:
if self.command is not None: self.command(self.current_value)
self.command(self.current_value)
def set(self, value: str):
self.current_value = value
self.text_label.configure(text=self.current_value)
if self.variable is not None:
self.variable_callback_blocked = True
self.variable.set(self.current_value)
self.variable_callback_blocked = False
def get(self) -> str: def get(self) -> str:
return self.current_value return self.current_value

View File

@ -1,4 +1,5 @@
import tkinter import tkinter
import math
from .ctk_canvas import CTkCanvas from .ctk_canvas import CTkCanvas
from ..theme_manager import ThemeManager from ..theme_manager import ThemeManager
@ -20,6 +21,9 @@ class CTkProgressBar(CTkBaseClass):
height=None, height=None,
border_width="default_theme", border_width="default_theme",
orient="horizontal", orient="horizontal",
mode="determinate",
determinate_speed=1,
indeterminate_speed=1,
**kwargs): **kwargs):
# set default dimensions according to orientation # set default dimensions according to orientation
@ -50,8 +54,14 @@ class CTkProgressBar(CTkBaseClass):
# shape # shape
self.corner_radius = ThemeManager.theme["shape"]["progressbar_corner_radius"] if corner_radius == "default_theme" else corner_radius self.corner_radius = ThemeManager.theme["shape"]["progressbar_corner_radius"] if corner_radius == "default_theme" else corner_radius
self.border_width = ThemeManager.theme["shape"]["progressbar_border_width"] if border_width == "default_theme" else border_width self.border_width = ThemeManager.theme["shape"]["progressbar_border_width"] if border_width == "default_theme" else border_width
self.value = 0.5 self.determinate_value = 0.5 # range 0-1
self.determinate_speed = determinate_speed # range 0-1
self.indeterminate_value = 0 # range 0-inf
self.indeterminate_width = 0.4 # range 0-1
self.indeterminate_speed = indeterminate_speed # range 0-1 to travel in 50ms
self.loop_running = False
self.orient = orient self.orient = orient
self.mode = mode # "determinate" or "indeterminate"
self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1)
@ -101,12 +111,26 @@ class CTkProgressBar(CTkBaseClass):
else: else:
orientation = "w" orientation = "w"
requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self._current_width), if self.mode == "determinate":
self.apply_widget_scaling(self._current_height), requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.corner_radius),
self.value, self.apply_widget_scaling(self.border_width),
orientation) 0,
self.determinate_value,
orientation)
else: # indeterminate mode
progress_value = (math.sin(self.indeterminate_value * math.pi / 40) + 1) / 2
progress_value_1 = min(1.0, progress_value + (self.indeterminate_width / 2))
progress_value_2 = max(0.0, progress_value - (self.indeterminate_width / 2))
requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self._current_width),
self.apply_widget_scaling(self._current_height),
self.apply_widget_scaling(self.corner_radius),
self.apply_widget_scaling(self.border_width),
progress_value_1,
progress_value_2,
orientation)
if no_color_updates is False or requires_recoloring: if no_color_updates is False or requires_recoloring:
self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode))
@ -155,6 +179,16 @@ class CTkProgressBar(CTkBaseClass):
del kwargs["variable"] del kwargs["variable"]
if "mode" in kwargs:
self.mode = kwargs.pop("mode")
require_redraw = True
if "determinate_speed" in kwargs:
self.determinate_speed = kwargs.pop("determinate_speed")
if "indeterminate_speed" in kwargs:
self.indeterminate_speed = kwargs.pop("indeterminate_speed")
if "width" in kwargs: if "width" in kwargs:
self.set_dimensions(width=kwargs["width"]) self.set_dimensions(width=kwargs["width"])
del kwargs["width"] del kwargs["width"]
@ -170,16 +204,54 @@ class CTkProgressBar(CTkBaseClass):
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=False):
self.value = value """ set determinate value """
self.determinate_value = value
if self.value > 1: if self.determinate_value > 1:
self.value = 1 self.determinate_value = 1
elif self.value < 0: elif self.determinate_value < 0:
self.value = 0 self.determinate_value = 0
self.draw(no_color_updates=True) self.draw(no_color_updates=True)
if self.variable is not None and not from_variable_callback: if self.variable is not None and not from_variable_callback:
self.variable_callback_blocked = True self.variable_callback_blocked = True
self.variable.set(round(self.value) if isinstance(self.variable, tkinter.IntVar) else self.value) self.variable.set(round(self.determinate_value) if isinstance(self.variable, tkinter.IntVar) else self.determinate_value)
self.variable_callback_blocked = False self.variable_callback_blocked = False
def get(self):
""" get determinate value """
return self.determinate_value
def start(self):
""" start indeterminate mode """
if not self.loop_running:
self.loop_running = True
self.internal_loop()
def stop(self):
""" stop indeterminate mode """
self.loop_running = False
def internal_loop(self):
if self.loop_running:
if self.mode == "determinate":
self.determinate_value += self.determinate_speed / 50
if self.determinate_value > 1:
self.determinate_value -= 1
self.draw()
self.after(20, self.internal_loop)
else:
self.indeterminate_value += self.indeterminate_speed
self.draw()
self.after(20, self.internal_loop)
def step(self):
if self.mode == "determinate":
self.determinate_value += self.determinate_speed / 50
if self.determinate_value > 1:
self.determinate_value -= 1
self.draw()
else:
self.indeterminate_value += self.indeterminate_speed
self.draw()

View File

@ -228,9 +228,6 @@ class CTkSwitch(CTkBaseClass):
self.variable.set(self.onvalue) self.variable.set(self.onvalue)
self.variable_callback_blocked = False self.variable_callback_blocked = False
if self.command is not None:
self.command()
def deselect(self, from_variable_callback=False): def deselect(self, from_variable_callback=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
@ -242,9 +239,6 @@ class CTkSwitch(CTkBaseClass):
self.variable.set(self.offvalue) self.variable.set(self.offvalue)
self.variable_callback_blocked = False self.variable_callback_blocked = False
if self.command is not None:
self.command()
def get(self): def get(self):
return self.onvalue if self.check_state is True else self.offvalue return self.onvalue if self.check_state is True else self.offvalue

View File

@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
github_url = "https://github.com/TomSchimansky/CustomTkinter" github_url = "https://github.com/TomSchimansky/CustomTkinter"
[tool.tbump.version] [tool.tbump.version]
current = "4.5.11" current = "4.6.1"
# Example of a semver regexp. # Example of a semver regexp.
# Make sure this matches current_version before # Make sure this matches current_version before

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = customtkinter name = customtkinter
version = 4.5.11 version = 4.6.1
description = Create modern looking GUIs with Python description = Create modern looking GUIs with Python
long_description = CustomTkinter UI-Library\n\n[](https://github.com/TomSchimansky/CustomTkinter/blob/master/documentation_images/Windows_dark.png)\n\nMore Information: https://github.com/TomSchimansky/CustomTkinter long_description = CustomTkinter UI-Library\n\n[](https://github.com/TomSchimansky/CustomTkinter/blob/master/documentation_images/Windows_dark.png)\n\nMore Information: https://github.com/TomSchimansky/CustomTkinter
long_description_content_type = text/markdown long_description_content_type = text/markdown

View File

@ -87,7 +87,7 @@ class App(customtkinter.CTk):
self.checkbox_1.grid(row=1, column=0, pady=(20, 10), padx=20, sticky="n") self.checkbox_1.grid(row=1, column=0, pady=(20, 10), padx=20, sticky="n")
self.checkbox_2 = customtkinter.CTkCheckBox(master=self.checkbox_slider_frame) self.checkbox_2 = customtkinter.CTkCheckBox(master=self.checkbox_slider_frame)
self.checkbox_2.grid(row=2, column=0, pady=10, padx=20, sticky="n") self.checkbox_2.grid(row=2, column=0, pady=10, padx=20, sticky="n")
self.switch_1 = customtkinter.CTkSwitch(master=self.checkbox_slider_frame) self.switch_1 = customtkinter.CTkSwitch(master=self.checkbox_slider_frame, command=lambda: print("switch 1 toggle"))
self.switch_1.grid(row=3, column=0, pady=10, padx=20, sticky="n") self.switch_1.grid(row=3, column=0, pady=10, padx=20, sticky="n")
self.switch_2 = customtkinter.CTkSwitch(master=self.checkbox_slider_frame) self.switch_2 = customtkinter.CTkSwitch(master=self.checkbox_slider_frame)
self.switch_2.grid(row=4, column=0, pady=(10, 20), padx=20, sticky="n") self.switch_2.grid(row=4, column=0, pady=(10, 20), padx=20, sticky="n")
@ -99,14 +99,14 @@ class App(customtkinter.CTk):
self.slider_progressbar_frame.grid_rowconfigure(3, weight=1) self.slider_progressbar_frame.grid_rowconfigure(3, weight=1)
self.progressbar_1 = customtkinter.CTkProgressBar(self.slider_progressbar_frame) self.progressbar_1 = customtkinter.CTkProgressBar(self.slider_progressbar_frame)
self.progressbar_1.grid(row=0, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") self.progressbar_1.grid(row=0, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
self.slider_1 = customtkinter.CTkSlider(self.slider_progressbar_frame) self.progressbar_2 = customtkinter.CTkProgressBar(self.slider_progressbar_frame)
self.slider_1.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") self.progressbar_2.grid(row=1, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
self.slider_2 = customtkinter.CTkSlider(self.slider_progressbar_frame, from_=0, to=4, number_of_steps=4) self.slider_1 = customtkinter.CTkSlider(self.slider_progressbar_frame, from_=0, to=1, number_of_steps=4)
self.slider_2.grid(row=2, column=0, padx=(20, 10), pady=(10, 10), sticky="ew") self.slider_1.grid(row=2, column=0, padx=(20, 10), pady=(10, 10), sticky="ew")
self.slider_3 = customtkinter.CTkSlider(self.slider_progressbar_frame, orient="vertical") self.slider_2 = customtkinter.CTkSlider(self.slider_progressbar_frame, orient="vertical")
self.slider_3.grid(row=0, column=1, rowspan=4, padx=(10, 10), pady=(10, 10), sticky="ns") self.slider_2.grid(row=0, column=1, rowspan=4, padx=(10, 10), pady=(10, 10), sticky="ns")
self.progressbar_2 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orient="vertical") self.progressbar_3 = customtkinter.CTkProgressBar(self.slider_progressbar_frame, orient="vertical")
self.progressbar_2.grid(row=0, column=2, rowspan=4, padx=(10, 20), pady=(10, 10), sticky="ns") self.progressbar_3.grid(row=0, column=2, rowspan=4, padx=(10, 20), pady=(10, 10), sticky="ns")
# set default values # set default values
self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton") self.sidebar_button_3.configure(state="disabled", text="Disabled CTkButton")
@ -119,10 +119,11 @@ class App(customtkinter.CTk):
self.scaling_optionemenu.set("100%") self.scaling_optionemenu.set("100%")
self.optionmenu_1.set("CTkOptionmenu") self.optionmenu_1.set("CTkOptionmenu")
self.combobox_1.set("CTkComboBox") self.combobox_1.set("CTkComboBox")
self.textbox.insert("1.0", self.textbox.insert("1.0", "CTkTextbox\n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.")
"CTkTextbox\n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.", ) self.slider_1.configure(command=self.progressbar_2.set)
#self.textbox.tag_add("headline", "1.0", "1.end") self.slider_2.configure(command=self.progressbar_3.set)
#self.textbox.tag_config("headline", foreground="red") self.progressbar_1.configure(mode="indeterminnate")
self.progressbar_1.start()
def open_input_dialog(self): def open_input_dialog(self):
dialog = customtkinter.CTkInputDialog(master=None, text="Type in a number:", title="CTkInputDialog") dialog = customtkinter.CTkInputDialog(master=None, text="Type in a number:", title="CTkInputDialog")

View File

@ -27,10 +27,10 @@ optionmenu_2 = customtkinter.CTkOptionMenu(app, variable=variable, values=countr
dynamic_resizing=False) dynamic_resizing=False)
optionmenu_2.pack(pady=20, padx=10) optionmenu_2.pack(pady=20, padx=10)
combobox_tk = ttk.Combobox(app, values=countries) combobox_tk = ttk.Combobox(app, values=countries, textvariable=variable)
combobox_tk.pack(pady=10, padx=10) combobox_tk.pack(pady=10, padx=10)
combobox_1 = customtkinter.CTkComboBox(app, variable=None, values=countries, command=select_callback, width=300) combobox_1 = customtkinter.CTkComboBox(app, variable=variable, values=countries, command=select_callback, width=300)
combobox_1.pack(pady=20, padx=10) combobox_1.pack(pady=20, padx=10)
def set_new_scaling(scaling): def set_new_scaling(scaling):

View File

@ -0,0 +1,47 @@
import customtkinter
import tkinter.ttk as ttk
app = customtkinter.CTk()
app.geometry("400x600")
p1 = customtkinter.CTkProgressBar(app)
p1.pack(pady=20)
p2 = ttk.Progressbar(app)
p2.pack(pady=20)
s1 = customtkinter.CTkSlider(app, command=p1.set)
s1.pack(pady=20)
def switch_func():
if sw1.get() == 1:
p1.configure(mode="indeterminate")
p2.configure(mode="indeterminate")
else:
p1.configure(mode="determinate")
p2.configure(mode="determinate")
def start():
p1.start()
p2.start()
def stop():
p1.stop()
p2.stop()
def step():
p1.step()
p2.step(10)
sw1 = customtkinter.CTkSwitch(app, text="intermediate mode", command=switch_func)
sw1.pack(pady=20)
b1 = customtkinter.CTkButton(app, text="start", command=start)
b1.pack(pady=20)
b2 = customtkinter.CTkButton(app, text="stop", command=stop)
b2.pack(pady=20)
b3 = customtkinter.CTkButton(app, text="step", command=step)
b3.pack(pady=20)
app.mainloop()