4. Scrollable Frames
Checkboxes on frame
In the chapter before we created the following frame, which creates checkboxes for a given list of values:
class MyCheckboxFrame(customtkinter.CTkFrame):
def __init__(self, master, values):
super().__init__(master)
self.values = values
self.checkboxes = []
for i, value in enumerate(self.values):
checkbox = customtkinter.CTkCheckBox(self, text=value)
checkbox.grid(row=i, column=0, padx=10, pady=(10, 0), sticky="w")
self.checkboxes.append(checkbox)
def get(self):
checked_checkboxes = []
for checkbox in self.checkboxes:
if checkbox.get() == 1:
checked_checkboxes.append(checkbox.cget("text"))
return checked_checkboxes
Scrollable frame
If you want to display more checkboxes than there is space in the layout or on the screen, then you can use a scrollable frame. The CTkScrollableFrame automatically adds a scrollbar so you can add more widgets than actually fit inside the frame. The frame will not expand with the widgets in it. The CTkScrollableFrame also comes with an option for a title, with the label_text argument you can pass text for a title label like we created manually in the above example.
class MyScrollableCheckboxFrame(customtkinter.CTkScrollableFrame):
def __init__(self, master, title, values):
super().__init__(master, label_text=title)
self.grid_columnconfigure(0, weight=1)
self.values = values
self.checkboxes = []
for i, value in enumerate(self.values):
checkbox = customtkinter.CTkCheckBox(self, text=value)
checkbox.grid(row=i, column=0, padx=10, pady=(10, 0), sticky="w")
self.checkboxes.append(checkbox)
def get(self):
checked_checkboxes = []
for checkbox in self.checkboxes:
if checkbox.get() == 1:
checked_checkboxes.append(checkbox.cget("text"))
return checked_checkboxes
Now we can create an instance of MyScrollableCheckboxFrame in the App class and add more values to test the scrollbar functionality:
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.title("my app")
self.geometry("400x220")
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
values = ["value 1", "value 2", "value 3", "value 4", "value 5", "value 6"]
self.scrollable_checkbox_frame = MyScrollableCheckboxFrame(self, title="Values", values=values)
self.scrollable_checkbox_frame.grid(row=0, column=0, padx=10, pady=(10, 0), sticky="nsew")
self.button = customtkinter.CTkButton(self, text="my button", command=self.button_callback)
self.button.grid(row=3, column=0, padx=10, pady=10, sticky="ew", columnspan=2)
def button_callback(self):
print("checked:", self.scrollable_checkbox_frame.get())
app = App()
app.mainloop()
Add and remove widgets
Often the content of a scrollable frame changes while the app is running, for example when the user adds new entries to a list. Therefore we move the creation of a checkbox into its own method add_checkbox, which can also be called from outside the class. The row of the new checkbox is simply the number of checkboxes that already exist:
def add_checkbox(self, value):
checkbox = customtkinter.CTkCheckBox(self, text=value)
checkbox.grid(row=len(self.checkboxes), column=0, padx=10, pady=(10, 0), sticky="w")
self.checkboxes.append(checkbox)
To remove all checkboxes, we destroy them and empty the list:
def clear(self):
for checkbox in self.checkboxes:
checkbox.destroy()
self.checkboxes = []
Scroll to the end
When a new checkbox is added at the bottom, it is not visible if the frame is already full. The CTkScrollableFrame has no method for scrolling yet, but you can move the canvas inside of it with yview_moveto. The value 1.0 means the end, 0.0 the beginning:
def scroll_to_end(self):
# _parent_canvas is the canvas inside the scrollable frame, there is no public method for this yet
self.after(10, lambda: self._parent_canvas.yview_moveto(1.0))
The small delay with after is necessary, because the scrollable frame needs a moment to update its size after the new checkbox was added. Note that _parent_canvas is an internal attribute, so this could change in a future version.
With two new buttons in the App class, the complete program looks like this:
import customtkinter
class MyScrollableCheckboxFrame(customtkinter.CTkScrollableFrame):
def __init__(self, master, title, values):
super().__init__(master, label_text=title)
self.grid_columnconfigure(0, weight=1)
self.checkboxes = []
for value in values:
self.add_checkbox(value)
def add_checkbox(self, value):
checkbox = customtkinter.CTkCheckBox(self, text=value)
checkbox.grid(row=len(self.checkboxes), column=0, padx=10, pady=(10, 0), sticky="w")
self.checkboxes.append(checkbox)
def clear(self):
for checkbox in self.checkboxes:
checkbox.destroy()
self.checkboxes = []
def scroll_to_end(self):
# _parent_canvas is the canvas inside the scrollable frame, there is no public method for this yet
self.after(10, lambda: self._parent_canvas.yview_moveto(1.0))
def get(self):
checked_checkboxes = []
for checkbox in self.checkboxes:
if checkbox.get() == 1:
checked_checkboxes.append(checkbox.cget("text"))
return checked_checkboxes
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.title("my app")
self.geometry("400x260")
self.grid_columnconfigure((0, 1), weight=1)
self.grid_rowconfigure(0, weight=1)
values = ["value 1", "value 2", "value 3", "value 4", "value 5", "value 6"]
self.scrollable_checkbox_frame = MyScrollableCheckboxFrame(self, title="Values", values=values)
self.scrollable_checkbox_frame.grid(row=0, column=0, padx=10, pady=(10, 0), sticky="nsew", columnspan=2)
self.add_button = customtkinter.CTkButton(self, text="Add value", command=self.add_value)
self.add_button.grid(row=1, column=0, padx=(10, 5), pady=10, sticky="ew")
self.clear_button = customtkinter.CTkButton(self, text="Clear", command=self.scrollable_checkbox_frame.clear)
self.clear_button.grid(row=1, column=1, padx=(5, 10), pady=10, sticky="ew")
self.button = customtkinter.CTkButton(self, text="my button", command=self.button_callback)
self.button.grid(row=2, column=0, padx=10, pady=(0, 10), sticky="ew", columnspan=2)
def add_value(self):
number = len(self.scrollable_checkbox_frame.checkboxes) + 1
self.scrollable_checkbox_frame.add_checkbox(f"value {number}")
self.scrollable_checkbox_frame.scroll_to_end()
def button_callback(self):
print("checked:", self.scrollable_checkbox_frame.get())
app = App()
app.mainloop()
More examples for scrollable frames can be found in this example program on Github: https://github.com/TomSchimansky/CustomTkinter/blob/master/examples/scrollable_frame_example.py