Search code examples
pythonuser-interfacetkinterttk

Expandable and contracting frame in Tkinter


Does anyone know if there is already a widget/class to handle expanding/contracting a frame based on a toggled button (checkbutton) in tkinter/ttk?

This question stems from my attempt to clean up a cluttered gui that has lots of options categorized by specific actions. I would like something along the lines of:

enter image description here
example found on google

However instead of just text, allow for buttons, entries, any of tkinter's widgets. If this doesn't already exist, would it be possible/useful to create a class that inherits the tkinter Frame:

import tkinter as tk
import ttk

class toggledFrame(tk.Frame):
    def __init__(self):
        self.show=tk.IntVar()
        self.show.set(0)
        self.toggleButton=tk.Checkbutton(self, command=self.toggle, variable=self.show)
        self.toggleButton.pack()
        self.subFrame=tk.Frame(self)

    def toggle(self):
        if bool(self.show.get()):
            self.subFrame.pack()
        else:
            self.subFrame.forget()

Note: this code is untested, just presenting concept


Solution

  • I am actually surprised at how close I was to getting functioning code. I decided to work on it some more and have develop a simple little class to perform exactly what I wanted (comments and suggestions on the code are welcome):

    import tkinter as tk
    from tkinter import ttk 
    
    
    class ToggledFrame(tk.Frame):
    
        def __init__(self, parent, text="", *args, **options):
            tk.Frame.__init__(self, parent, *args, **options)
    
            self.show = tk.IntVar()
            self.show.set(0)
    
            self.title_frame = ttk.Frame(self)
            self.title_frame.pack(fill="x", expand=1)
    
            ttk.Label(self.title_frame, text=text).pack(side="left", fill="x", expand=1)
    
            self.toggle_button = ttk.Checkbutton(self.title_frame, width=2, text='+', command=self.toggle,
                                                variable=self.show, style='Toolbutton')
            self.toggle_button.pack(side="left")
    
            self.sub_frame = tk.Frame(self, relief="sunken", borderwidth=1)
    
        def toggle(self):
            if bool(self.show.get()):
                self.sub_frame.pack(fill="x", expand=1)
                self.toggle_button.configure(text='-')
            else:
                self.sub_frame.forget()
                self.toggle_button.configure(text='+')
    
    
    if __name__ == "__main__":
        root = tk.Tk()
    
        t = ToggledFrame(root, text='Rotate', relief="raised", borderwidth=1)
        t.pack(fill="x", expand=1, pady=2, padx=2, anchor="n")
    
        ttk.Label(t.sub_frame, text='Rotation [deg]:').pack(side="left", fill="x", expand=1)
        ttk.Entry(t.sub_frame).pack(side="left")
    
        t2 = ToggledFrame(root, text='Resize', relief="raised", borderwidth=1)
        t2.pack(fill="x", expand=1, pady=2, padx=2, anchor="n")
    
        for i in range(10):
            ttk.Label(t2.sub_frame, text='Test' + str(i)).pack()
    
        t3 = ToggledFrame(root, text='Fooo', relief="raised", borderwidth=1)
        t3.pack(fill="x", expand=1, pady=2, padx=2, anchor="n")
    
        for i in range(10):
            ttk.Label(t3.sub_frame, text='Bar' + str(i)).pack()
    
        root.mainloop()
    

    This code produces:
    enter image description here