Skip to content

Commit

Permalink
fixed radiobutton disabled command call bug TomSchimansky#677, fixed …
Browse files Browse the repository at this point in the history
…key error for theme in scrollbar TomSchimansky#711, removed bind_all and unbind_all from baseclass, added CTkCanvas and CTkBaseClass for top level import
  • Loading branch information
TomSchimansky committed Dec 6, 2022
1 parent f4af512 commit 2c7b2c5
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 38 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
ToDo:
- cursor configuring
- overwrite winfo methods
- set icon (self.call("wm", "iconphoto", self._w, tkinter.PhotoImage(file="test_images/CustomTkinter_logo_single.png")))
- add option to change label position for checkbox, switch, radiobutton #628


## [5.0.0] - 2022-11-13
Expand Down
1 change: 1 addition & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
![PyPI - Downloads](https://img.shields.io/pypi/dm/customtkinter?color=green&label=downloads)
![Downloads](https://static.pepy.tech/personalized-badge/customtkinter?period=total&units=international_system&left_color=grey&right_color=green&left_text=downloads)
![PyPI - License](https://img.shields.io/pypi/l/customtkinter)
![](https://tokei.rs/b1/github/tomschimansky/customtkinter)
![Total lines](https://img.shields.io/tokei/lines/github.com/tomschimansky/customtkinter?color=green&label=total%20lines)

</div>
Expand Down
4 changes: 4 additions & 0 deletions customtkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ def _set_dimensions(self, width=None, height=None):
super().configure(width=self._apply_widget_scaling(self._desired_width),
height=self._apply_widget_scaling(self._desired_height))

def unbind_all(self, sequence):
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):
raise AttributeError("'bind_all' is not allowed, could result in undefined behavior")

def place(self, **kwargs):
"""
Place a widget in the parent widget. Use as options:
Expand Down
37 changes: 24 additions & 13 deletions customtkinter/windows/widgets/ctk_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,22 @@ def __init__(self,
self._draw_engine = DrawEngine(self._canvas)
self._draw_engine.set_round_to_even_numbers(self._round_width_to_even_numbers, self._round_height_to_even_numbers) # rendering options

# canvas event bindings
self._canvas.bind("<Enter>", self._on_enter)
self._canvas.bind("<Leave>", self._on_leave)
self._canvas.bind("<Button-1>", self._clicked)
self._canvas.bind("<Button-1>", self._clicked)

# configure cursor and initial draw
self._create_bindings()
self._set_cursor()
self._draw()

def _create_bindings(self, sequence: Optional[str] = None):
""" set necessary bindings for functionality of widget, will overwrite other bindings """
if sequence is None or sequence == "<Enter>":
self._canvas.bind("<Enter>", self._on_enter)
if sequence is None or sequence == "<Leave>":
self._canvas.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self._clicked)
if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self._clicked)

def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs)

Expand Down Expand Up @@ -532,17 +538,22 @@ def invoke(self):
if self._command is not None:
return self._command()

def bind(self, sequence: str = None, command: Callable = None, add: str = None) -> str:
""" called on the tkinter.Label and tkinter.Canvas """
canvas_bind_return = self._canvas.bind(sequence, command, add)
label_bind_return = self._text_label.bind(sequence, command, add)
def bind(self, sequence: str = None, command: Callable = None, add: Union[str, bool] = "+") -> str:
""" called on the tkinter.Canvas """
if add != "+" or add is not True:
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
canvas_bind_return = self._canvas.bind(sequence, command, add="+")
label_bind_return = self._text_label.bind(sequence, command, add="+")
return canvas_bind_return + " + " + label_bind_return

def unbind(self, sequence: str, funcid: str = None):
""" called on the tkinter.Label and tkinter.Canvas """
canvas_bind_return, label_bind_return = funcid.split(" + ")
self._canvas.unbind(sequence, canvas_bind_return)
self._text_label.unbind(sequence, label_bind_return)
if funcid is not None:
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
" tkinter and its not clear whether the internal callbacks will be unbinded or not")
self._canvas.unbind(sequence, None)
self._text_label.unbind(sequence, None)
self._create_bindings(sequence=sequence) # restore internal callbacks for sequence

def focus(self):
return self._text_label.focus()
Expand Down
30 changes: 21 additions & 9 deletions customtkinter/windows/widgets/ctk_checkbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,23 @@ def __init__(self,
self._text_label.grid(row=0, column=2, sticky="w")
self._text_label["anchor"] = "w"

self._text_label.bind("<Enter>", self._on_enter)
self._text_label.bind("<Leave>", self._on_leave)
self._text_label.bind("<Button-1>", self.toggle)

# register variable callback and set state according to variable
if self._variable is not None and self._variable != "":
self._variable_callback_name = self._variable.trace_add("write", self._variable_callback)
self._check_state = True if self._variable.get() == self._onvalue else False

self._draw() # initial draw
self._create_bindings()
self._set_cursor()
self._draw()

def _create_bindings(self, sequence: Optional[str] = None):
""" set necessary bindings for functionality of widget, will overwrite other bindings """
if sequence is None or sequence == "<Enter>":
self._canvas.bind("<Enter>", self._on_enter)
if sequence is None or sequence == "<Leave>":
self._canvas.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<Button-1>":
self._canvas.bind("<Button-1>", self.toggle)

def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs)
Expand Down Expand Up @@ -430,13 +436,19 @@ def deselect(self, from_variable_callback=False):
def get(self) -> Union[int, str]:
return self._onvalue if self._check_state is True else self._offvalue

def bind(self, sequence=None, command=None, add=None):
def bind(self, sequence=None, command=None, add="+"):
""" called on the tkinter.Canvas """
return self._canvas.bind(sequence, command, add)
if add != "+" or add is not True:
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
return self._canvas.bind(sequence, command, add="+")

def unbind(self, sequence, funcid=None):
""" called on the tkinter.Canvas """
return self._canvas.unbind(sequence, funcid)
""" called on the tkinter.Canvas, restores internal callbacks """
if funcid is not None:
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
" tkinter and its not clear whether the internal callbacks will be unbinded or not")
self._canvas.unbind(sequence, None) # unbind all callbacks for sequence
self._create_bindings(sequence=sequence) # restore internal callbacks for sequence

def focus(self):
return self._text_label.focus()
Expand Down
4 changes: 2 additions & 2 deletions customtkinter/windows/widgets/ctk_progressbar.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import tkinter
import math
from typing import Union, Tuple, Optional
from typing import Union, Tuple, Optional, Literal

from .core_rendering import CTkCanvas
from .theme import ThemeManager
Expand Down Expand Up @@ -29,7 +29,7 @@ def __init__(self,

variable: Union[tkinter.Variable, None] = None,
orientation: str = "horizontal",
mode: str = "determinate",
mode: Literal["determinate", "indeterminate"] = "determinate",
determinate_speed: float = 1,
indeterminate_speed: float = 1,
**kwargs):
Expand Down
4 changes: 2 additions & 2 deletions customtkinter/windows/widgets/ctk_radiobutton.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,8 @@ def invoke(self, event=0):
self._check_state = True
self.select()

if self._command is not None:
self._command()
if self._command is not None:
self._command()

def select(self, from_variable_callback=False):
self._check_state = True
Expand Down
38 changes: 26 additions & 12 deletions customtkinter/windows/widgets/ctk_scrollbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ def __init__(self,

# color
self._fg_color = ThemeManager.theme["CTkScrollbar"]["fg_color"] if fg_color is None else self._check_color_type(fg_color, transparency=True)
self._button_color = ThemeManager.theme["CTkScrollbar"]["scrollbar_color"] if button_color is None else self._check_color_type(button_color)
self._button_hover_color = ThemeManager.theme["CTkScrollbar"]["scrollbar_hover_color"] if button_hover_color is None else self._check_color_type(button_hover_color)
self._button_color = ThemeManager.theme["CTkScrollbar"]["button_color"] if button_color is None else self._check_color_type(button_color)
self._button_hover_color = ThemeManager.theme["CTkScrollbar"]["button_hover_color"] if button_hover_color is None else self._check_color_type(button_hover_color)

# shape
self._corner_radius = ThemeManager.theme["CTkScrollbar"]["corner_radius"] if corner_radius is None else corner_radius
Expand All @@ -71,14 +71,22 @@ def __init__(self,
self._canvas.place(x=0, y=0, relwidth=1, relheight=1)
self._draw_engine = DrawEngine(self._canvas)

self._canvas.bind("<Enter>", self._on_enter)
self._canvas.bind("<Leave>", self._on_leave)
self._canvas.tag_bind("border_parts", "<Button-1>", self._clicked)
self._canvas.bind("<B1-Motion>", self._clicked)
self._canvas.bind("<MouseWheel>", self._mouse_scroll_event)

self._create_bindings()
self._draw()

def _create_bindings(self, sequence: Optional[str] = None):
""" set necessary bindings for functionality of widget, will overwrite other bindings """
if sequence is None:
self._canvas.tag_bind("border_parts", "<Button-1>", self._clicked)
if sequence is None or sequence == "<Enter>":
self._canvas.bind("<Enter>", self._on_enter)
if sequence is None or sequence == "<Leave>":
self._canvas.bind("<Leave>", self._on_leave)
if sequence is None or sequence == "<B1-Motion>":
self._canvas.bind("<B1-Motion>", self._clicked)
if sequence is None or sequence == "<MouseWheel>":
self._canvas.bind("<MouseWheel>", self._mouse_scroll_event)

def _set_scaling(self, *args, **kwargs):
super()._set_scaling(*args, **kwargs)

Expand Down Expand Up @@ -249,13 +257,19 @@ def set(self, start_value: float, end_value: float):
def get(self):
return self._start_value, self._end_value

def bind(self, sequence=None, command=None, add=None):
def bind(self, sequence=None, command=None, add="+"):
""" called on the tkinter.Canvas """
return self._canvas.bind(sequence, command, add)
if add != "+" or add is not True:
raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
return self._canvas.bind(sequence, command, add="+")

def unbind(self, sequence, funcid=None):
""" called on the tkinter.Canvas """
return self._canvas.unbind(sequence, funcid)
""" called on the tkinter.Canvas, restores internal callbacks """
if funcid is not None:
raise ValueError("'funcid' argument can only be None, because there is a bug in" +
" tkinter and its not clear whether the internal callbacks will be unbinded or not")
self._canvas.unbind(sequence, None) # unbind all callbacks for sequence
self._create_bindings(sequence=sequence) # restore internal callbacks for sequence

def focus(self):
return self._canvas.focus()
Expand Down
2 changes: 2 additions & 0 deletions examples/image_example.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import tkinter

import customtkinter
import os
from PIL import Image
Expand Down
7 changes: 7 additions & 0 deletions examples/simple_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,11 @@ def slider_callback(value):
tabview_1.add("CTkTabview")
tabview_1.add("Tab 2")

progressbar_1.configure(mode="indeterminate")
progressbar_1.start()
#progressbar_1.stop()
#progressbar_1.start()
#progressbar_1.stop()
#progressbar_1.start()

app.mainloop()

0 comments on commit 2c7b2c5

Please sign in to comment.