CustomTkinter
CustomTkinter copied to clipboard
Navigate through checkbox with "tab" key
Hi
I try to rewrite an old application that I wrote years ago with Python2 and Tkinter. I'm using "customTkinter" to give it a better look, but I face a stupid blocking point ! Simple example :
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
frame.grid(row=0, column=0)
w1 = tk.Checkbutton(frame, text="check one")
w1.grid(row=0, column=0)
w2 = tk.Checkbutton(frame, text="check two")
w2.grid(row=1, column=0)
w3 = tk.Checkbutton(frame, text="check three")
w3.grid(row=2, column=0)
w4 = tk.Checkbutton(frame, text="check four")
w4.grid(row=3, column=0)
w5 = tk.Checkbutton(frame, text="check five")
w5.grid(row=4, column=0)
root.mainloop()
with this code, when I press the 'Tab' key on my keyboard, I can circle through the 5 checkboxes and (de)activate them with the space bar
same code with customTkinter
import customtkinter as ctk
root = ctk.CTk()
ctk.set_appearance_mode("light")
frame = ctk.CTkFrame(root)
frame.grid(row=0, column=0)
w1 = ctk.CTkCheckBox(frame, text="check one")
w1.grid(row=0, column=0)
w2 = ctk.CTkCheckBox(frame, text="check two")
w2.grid(row=1, column=0)
w3 = ctk.CTkCheckBox(frame, text="check three")
w3.grid(row=2, column=0)
w4 = ctk.CTkCheckBox(frame, text="check four")
w4.grid(row=3, column=0)
w5 = ctk.CTkCheckBox(frame, text="check five")
w5.grid(row=4, column=0)
root.mainloop()
when I press the tab key, nothing happen !! This is a real issue for me
What I'm doing wrong ? Did I forgot something ?
Thanks
Try to take focus on internal canvas. For example:
...
w1 = ctk.CTkCheckBox(frame, text="check one")
w1._canvas.config(takefocus=1)
w1.grid(row=0, column=0)
# Same for w2 and so on.
...
If it does not work, you can put them into a frame of Tkinter or Customtkinter. Implementing own frame that support appearance mode and scaling will be more effective for heavy programs. Classes AppearanceModeTracker and ScalingTracker can be used to do so.
Hi Thanks for the answer
w1._canvas.config(takefocus=1)
this doesn't work ... tried different combination, without any visible improvement
you can put them into a frame of Tkinter or Customtkinter.
there are already in a Customtkinter frame ,and changing it to a Tkinter doesn't help
Implementing own frame that support appearance mode and scaling will be more effective for heavy programs. Classes AppearanceModeTracker and ScalingTracker can be used to do so.
I'm afraid that this exceed my rather limited knowledge in customTkinter and Python
In the meantime, I'll post a sample code demonstrating both the approaches.
Regards.
@ericc-dream
this is how i was able to get it to work, i will see if there is a way to get only the CTkCheckBox to work the way you want.
import tkinter as tk
import customtkinter as ctk
ctk.set_appearance_mode("light") # Instead of _apply_appearance_mode (it's internal)
root = ctk.CTk()
root.geometry("150x200")
frame = ctk.CTkFrame(root, width=500, height=300)
frame.grid(row=0, column=0, padx=10, pady=10)
# List to store checkbox variables if needed later
checkbox_vars = []
# Dynamically create checkboxes with unique toggle detection
for i in range(5):
var = tk.BooleanVar()
checkbox_vars.append(var)
checkbox = tk.Checkbutton(frame,text=f"Check {i+1}",variable=var,font=("Arial", 12))
checkbox.grid(row=i, column=0)
root.mainloop()
Nice ! I was wondering if we couldn't do that ... mixing Tk and CustomTk widgets ... However there is a little problem. It's correct here , because of the "set_appearance_mode("light")" ... if I switch to "dark" , then the checkbox stay "light" As far as I understood, we can play with 'ttk' to change the colors ... something I never did, but can be an interesting challenge
I'm a little bit surprise, I must say. I understood that CustomTkinter was build "on top" of Tkinter ... why a basic feature as navigating through widgets with the Tab key doesn't work anymore ! But I was probably completely wrong , just looking at how different the widgets option can be, this is a rewrite of Tkinter and not something build on top
Thanks for your help
Nice ! I was wondering if we couldn't do that ... mixing Tk and CustomTk widgets ... However there is a little problem. It's correct here , because of the "set_appearance_mode("light")" ... if I switch to "dark" , then the checkbox stay "light" As far as I understood, we can play with 'ttk' to change the colors ... something I never did, but can be an interesting challenge
I believe you can, i have not tried it myself. I've been trying different things to see if i can get the checkboxes to have transparent background, because i normally only work on dark mode. i only used light this time because that's what was on your snippet.
i am working on editing the ctk.CTkCheckBox to work just like the old one did, while having some nice highlight feature. i will try to see if i can fork the project and add my revision of the checkbox adding the old function back. i will try to keep the code the same style as it is writing right now.
I understood that CustomTkinter was build "on top" of Tkinter ... why a basic feature as navigating through widgets with the Tab key doesn't work anymore ! But I was probably completely wrong , just looking at how different the widgets option can be, this is a rewrite of Tkinter and not something build on top
i have noticed this with some other widgets as well, which is not bad at all. i think in this case, it's something that was overlooked.
@ericc-dream i was able to accomplish the same behavior using only CTkCheckBox. i did have to modify it in order for it to work. i will fork this project and throw in my updates in it. never done it before so it might take a while for me to figure it out :)
@ericc-dream Here is the solution. I modified the class by inheriting it. Now, when you select a checkbox by tab key, the background of the checkbox and its text will be highlighted and you can select or deselect it by pressing either the Space Bar key or the Enter key on the keyboard.
An extra argument select_color is added to allow preferences to select color. Here is the full code:
import customtkinter as ctk
class Checkbox(ctk.CTkCheckBox):
def __init__(self,
master,
width = 100,
height = 24,
checkbox_width = 24,
checkbox_height = 24,
corner_radius = None,
border_width = None,
bg_color = "transparent",
fg_color = None,
hover_color = None,
border_color = None,
checkmark_color = None,
text_color = None,
text_color_disabled = None,
text = "CTkCheckBox",
font = None,
textvariable = None,
state = "normal",
hover = True,
command = None,
onvalue = 1,
offvalue = 0,
variable = None,
select_color: str | tuple[str, str] = ["#a5dcff", "#155d91"],
**kwargs):
"""
Modified `CTkCheckbox` to support nevigation through Tab.
On tab selection, background of the checkbox will be highlighted as `select_color`.
Using space bar key or enter key, it can be toggled between the states.
"""
super().__init__(master, width, height, checkbox_width, checkbox_height, corner_radius, border_width, bg_color, fg_color, hover_color, border_color, checkmark_color, text_color, text_color_disabled, text, font, textvariable, state, hover, command, onvalue, offvalue, variable, **kwargs)
self._bg_color_default = bg_color
self._select_color = select_color
self.bind("<FocusIn>", self._on_focus)
self.bind("<FocusOut>", self._on_focus_out)
self.bind("<KeyRelease-space>", self._focus_action) # For Space Bar key release
self.bind("<KeyRelease-Return>", self._focus_action) # For Enter key release
def _on_focus(self, e=None):
self.configure(bg_color=self._select_color)
def _on_focus_out(self, e=None):
self.configure(bg_color=self._bg_color_default)
def _focus_action(self, e=None):
if self.cget("state") == "disabled":
return
if self.get():
self.deselect()
else:
self.select()
def configure(self, require_redraw=False, **kwargs):
if "select_color" in kwargs:
self._select_color = kwargs.pop("select_color")
if "bg_color" in kwargs:
if kwargs["bg_color"] != self._select_color:
self._bg_color_default = kwargs["bg_color"]
return super().configure(require_redraw, **kwargs)
def cget(self, attribute_name):
if attribute_name == "select_color":
return self._select_color
return super().cget(attribute_name)
root = ctk.CTk()
ctk.set_appearance_mode("light")
frame = ctk.CTkFrame(root)
frame.grid(row=0, column=0)
w1 = Checkbox(frame, text="check one")
w1.grid(row=0, column=0)
w2 = Checkbox(frame, text="check two")
w2.grid(row=1, column=0)
w3 = Checkbox(frame, text="check three")
w3.grid(row=2, column=0)
w4 = Checkbox(frame, text="check four")
w4.grid(row=3, column=0)
w5 = Checkbox(frame, text="check five")
w5.grid(row=4, column=0)
w2.configure(state="disabled")
root.mainloop()
Output:
Regards.
Because of the way CtK is built, certain elements like buttons do not handle focus the same way. Here is an old discussion talking about it. I submitted a now-closed pull request #1412 with a dirty implementation of regular Tkinter focus behavior for CTkButtons. The code is still viewable if you want to add it to your own project.