CustomTkinter icon indicating copy to clipboard operation
CustomTkinter copied to clipboard

Navigate through checkbox with "tab" key

Open ericc-dream opened this issue 7 months ago • 8 comments

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

ericc-dream avatar Apr 14 '25 11:04 ericc-dream

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.

dipeshSam avatar Apr 14 '25 12:04 dipeshSam

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

ericc-dream avatar Apr 14 '25 13:04 ericc-dream

In the meantime, I'll post a sample code demonstrating both the approaches.

Regards.

dipeshSam avatar Apr 14 '25 14:04 dipeshSam

@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()

RiceCrispyy avatar Apr 16 '25 17:04 RiceCrispyy

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

ericc-dream avatar Apr 17 '25 07:04 ericc-dream

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.

RiceCrispyy avatar Apr 17 '25 12:04 RiceCrispyy

@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 :)

Image

Image

RiceCrispyy avatar Apr 17 '25 16:04 RiceCrispyy

@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: tab_move2.gif

Regards.

dipeshSam avatar Apr 17 '25 18:04 dipeshSam

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.

bfedie518 avatar Oct 04 '25 22:10 bfedie518