Welcome, guest | Sign In | My Account | Store | Cart
try:
    from Tkinter import PhotoImage, Frame, Label
    from Tkconstants import *
except ImportError:
    from tkinter import PhotoImage, Frame, Label
    from tkinter.constants import *

class CollapsibleFrame(Frame):
    def __init__(self, master, text, borderwidth=2, inner_width=0, inner_height=16, ipadx=0, ipady=8, background=None, caption_separation=4, caption_font=None, button_padx=3):
        Frame.__init__(self, master)
        if background is None:
            background = self.cget("background")

        self.configure(background=background)

        self._is_opened = False

        self._ipadx=ipadx
        self._ipady=ipady

        self._iconOpen = PhotoImage(data="R0lGODlhEAAQAKIAAP///9TQyICAgEBAQAAAAAAAAAAAAAAAACwAAAAAEAAQAAADNhi63BMgyinFAy0HC3Xj2EJoIEOM32WeaSeeqFK+say+2azUi+5ttx/QJeQIjshkcsBsOp/MBAA7")
        self._iconClose = PhotoImage(data="R0lGODlhEAAQAKIAAP///9TQyICAgEBAQAAAAAAAAAAAAAAAACwAAAAAEAAQAAADMxi63BMgyinFAy0HC3XjmLeA4ngpRKoSZoeuDLmo38mwtVvKu93rIo5gSCwWB8ikcolMAAA7")
        
        max_height_of_icon = max(self._iconOpen.height(), self._iconClose.height())
        max_width_of_icon = max(self._iconOpen.width(), self._iconClose.width())
        
        containerFrame_pady = (max_height_of_icon//2) +1

        self._inner_height = inner_height
        self._inner_width = inner_width

        self._containerFrame = Frame(self, borderwidth=borderwidth, width=inner_width, height=inner_height, relief=RIDGE, background=background)
        self._containerFrame.pack(expand=True, fill=X, pady=containerFrame_pady)
        
        self.interior = Frame(self._containerFrame, background=background)

        self._collapseButton = Label(self, borderwidth=0, image=self._iconOpen, relief=RAISED)
        self._collapseButton.place(in_= self._containerFrame, x=button_padx, y=-(max_height_of_icon//2), anchor=N+W, bordermode="ignore")
        self._collapseButton.bind("<Button-1>", lambda event: self.toggle())

        self._captionLabel = Label(self, anchor=W, borderwidth=1, text=text)
        if caption_font is not None:
            self._captionLabel.configure(font=caption_font)

        x = caption_separation + button_padx + max_width_of_icon
        y = -(self._captionLabel.winfo_reqheight()//2)
        self._captionLabel.place(in_= self._containerFrame, x=x, y=y, anchor=N+W, bordermode="ignore")

    def update_width(self, width=None):
        # Update could be devil
        # http://wiki.tcl.tk/1255
        self.after(0, lambda width=width:self._update_width(width))
    
    def _update_width(self, width):
        self.update()
        if width is None:
            width=self.interior.winfo_reqwidth()

        if isinstance(self._ipady, (list, tuple)):
            width += self._ipady[0] + self._ipady[1]
        else:
            width += 2*self._ipady
            
        width = max(self._inner_width, width)

        self._containerFrame.configure(width=width)
        
    def open(self):
        self._collapseButton.configure(image=self._iconClose)
        
        self._containerFrame.configure(height=self.interior.winfo_reqheight())
        self.interior.pack(expand=True, fill=X, padx=self._ipadx, pady =self._ipady)

        self._is_opened = True

    def close(self):
        self.interior.pack_forget()
        self._containerFrame.configure(height=self._inner_height)
        self._collapseButton.configure(image=self._iconOpen)

        self._is_opened = False
    
    def toggle(self):
        if self._is_opened:
            self.close()
        else:
            self.open()
    
if __name__ == "__main__":
    try:
        from Tkinter import Tk, Button
    except ImportError:
        from tkinter import Tk, Button

    root = Tk()
    root.wm_geometry("400x300+0+0")
    
    cf1 = CollapsibleFrame(root, text ="Frame1", ipadx=6)
    cf1.pack()
    
    for i in range(5):
        Button(cf1.interior, text="button %s"%i).pack()
    
    cf1.update_width()
    root.mainloop()

Diff to Previous Revision

--- revision 1 2017-03-04 00:34:33
+++ revision 2 2017-03-04 00:36:02
@@ -48,6 +48,7 @@
 
     def update_width(self, width=None):
         # Update could be devil
+        # http://wiki.tcl.tk/1255
         self.after(0, lambda width=width:self._update_width(width))
     
     def _update_width(self, width):

History