#Simple foldable panels. Layout lifted off Windows. Use at your own risk!
#Code put in the public domain, for someone else could find a use for it.

import wx
#import images

#---------------------------------------------------------------------------
#following code is normaly hidden in images.py
#---------------------------------------------------------------------------
import wx
import cStringIO

class Images:
    def __init__(self):
        for name in ["bu","bd","wu","wd","bub","bdb","wub","wdb"]:
            img = wx.ImageFromStream(cStringIO.StringIO(eval("_"+name)))
            #img.ConvertAlphaToMask(100) #needed? not needed? not in windows
            exec("self.bmp_%s = img.ConvertToBitmap()"%name)

_bu="\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x11\x00\x00\
\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\
\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\
\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd5\x08\x02\x10*\x16\x93\xc7~\x7f\x00\
\x00\x00\x1dtEXtComment\x00Created with The GIMP\xefd%n\x00\x00\x00+IDATx\xdac\
`\x18q\xe0?!\x05\x8c$\x18\x80S-\x13\x11\x060\x12\xeb\"l\x06\xfc\'Bl4L(\x08\x93\
Q@\x06\x00\x00\x09\x14\x0f\xfbA\xb6\x94u\x00\x00\x00\x00IEND\xaeB`\x82"
_bd="\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x11\x00\x00\
\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\
\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\
\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd5\x08\x02\x10-\x1f\xa5ZP\x1c\x00\x00\
\x00\x1dtEXtComment\x00Created with The GIMP\xefd%n\x00\x00\x00\'IDATx\xdac`\
\x18\x05\xc4\x82\xffPLH\x8c$\x83\xf0\x1a\xc0H\x84A\xc4\xaa%\xda\xa0\xd10\xa10LF\
2\x00\x00\'\x04\x13\xf1\x01\xa6\x8d\x96\x00\x00\x00\x00IEND\xaeB`\x82"
_wu="\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x11\x00\x00\
\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\
\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\
\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd5\x08\x02\x10+\x08p\xd3r]\x00\x00\
\x00\x1dtEXtComment\x00Created with The GIMP\xefd%n\x00\x00\x003IDATx\xdac`\x18\
Y\xe0\xff\xff\xff\xff\x09\xa9a$\xd6\x00FFF\x9cj\x99\x08\x19\x00\xd3L\x8c\x8b0\
\x0c@\xd7\x84Ml4L(\x08\x93Q@\x1e\x00\x00I\xd0?\xd7\x1b)XG\x00\x00\x00\x00IEND\
\xaeB`\x82"
_wd="\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x11\x00\x00\
\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\
\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\
\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd5\x08\x02\x10,-t\x960\xdd\x00\x00\
\x00\x1dtEXtComment\x00Created with The GIMP\xefd%n\x00\x00\x00)IDATx\xdac`\
\x18\x05D\x81\xffP@H\x8c$\x83\x08\x19\xc0H\xc8 \xb8BFFF\x8a\xbc6\x1a&T\x0a\x93\
\x11\x0e\x00\xf9\xc4O\xbbBM\\o\x00\x00\x00\x00IEND\xaeB`\x82"
_bub="\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x11\
\x00\x00\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\x00\x00\x00\x06bKGD\x00\xff\x00\
\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\
\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd5\x08\x02\x10*,U\xcb\xa7\
\xcd\x00\x00\x00\x1dtEXtComment\x00Created with The GIMP\xefd%n\x00\x00\x00MID\
ATx\xda\xe5\xd21\x0a\xc00\x0cC\xd1\xc8\xf4\xe0\xf6\xc9\x7f\xf7@\x13\x81C\x97h\
\xd4\xf0\x06!\x01\x8cn\xbaHf\x12\xe3@\x1c\x84.\x82\x03\x85\x01h\x07\x85\x09,\
\xa1\xe7\x03\xd1\x04j\xean\xdfd\xd5\xd9g;\xf2\xd8\x7f\x10\x01TU\x0by\x01cq\x1e\
\x14\x88R=\xfb\x00\x00\x00\x00IEND\xaeB`\x82"
_bdb="\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x11\
\x00\x00\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\x00\x00\x00\x06bKGD\x00\xff\x00\
\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\
\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd5\x08\x02\x10-\x09Q\x8e\xe5M\
\x00\x00\x00\x1dtEXtComment\x00Created with The GIMP\xefd%n\x00\x00\x00NIDATx\
\xda\xed\x92A\x0a\xc00\x08\x04\xb3}\xb9\xbe|z\xc9!\x97V\x83\x09\xb9d@\x04\xc1\
\x01\x17\x05\xd0\xaaT%f\xc6\xd3\x16\xb0UB\xafh\xf6+\xd1\xb08v\xcd\x9e\xa3\x8c \
\x93\x89\"A6\x13\xddL>Eg>v\x0a\x01\xb8{I\xf2\x02\xea\x94\x1e\x13\x86\x0e\xf7$\
\x00\x00\x00\x00IEND\xaeB`\x82"
_wub="\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x11\
\x00\x00\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\x00\x00\x00\x06bKGD\x00\xff\x00\
\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\
\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd5\x08\x02\x10+ Ef\xda\xa7\
\x00\x00\x00\x1dtEXtComment\x00Created with The GIMP\xefd%n\x00\x00\x00OIDATx\
\xda\xed\xd2\xc1\x0a\xc00\x08\x03\xd0&\xec\xc3\xf5\xcb\xb3\xcbz+DPvj\x8e\x01\
\x1f\"B\x92V7]$\"\xc45\x10Nl\xca\x0a\xe0 :\x00\x00\x1c\xc4\x0a\xe0\xa0\xe7T\
\xee\xa1=\x88/\xf7&\xbe+?\xdb\xc8\xc7\xfe\x86@\x922\xb3\x85\xbc\xd1\xb7H\x0b\
\x91\xd8G\xde\x00\x00\x00\x00IEND\xaeB`\x82"
_wdb="\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x11\
\x00\x00\x00\x10\x08\x06\x00\x00\x00\xf01\x94_\x00\x00\x00\x06bKGD\x00\xff\x00\
\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\
\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xd5\x08\x02\x10,\x10,\xfe|\
\xcc\x00\x00\x00\x1dtEXtComment\x00Created with The GIMP\xefd%n\x00\x00\x00UID\
ATx\xda\xe5\x92K\x0a\xc00\x08\x05\xf3zr=\xf9tS!\x946\x1fl\xc8\xa2o#\x08\x0e:(\
\x80\x92M\x16bf\x1c\xe5\x83\xac\x83p\xa5\xd7kB$\xa9\xf6\x155\xfa\xc3\xe7\xdcAo\
\x80\xae\x93\x18l\x01\x86\x9c\xd4\x1b\xfd\xd9\xc9\x13h\xcf\xc7\xceF\x00\xee\x9e\
\x82\x9c\x05&H\x13x\x1e\xe3\x8c\x00\x00\x00\x00IEND\xaeB`\x82"
#---------------------------------------------------------------------------
#end of images.py
#---------------------------------------------------------------------------

class FoldPanel(wx.Panel,Images):
    def __init__(self,parent,psizer,title,tooltip=None,rimcolour="light grey",
                                bgcolour="white",txtcolour="black",is_open=1):
        wx.Panel.__init__(self, parent, -1)
        Images.__init__(self)

        self.is_open=is_open
        #XXX It won't work properly without refreshing the parent sizer/panel.
        self.parentsizer=psizer
        self.parent=parent

        self.rimcolour,self.bgcolour,self.txtcolour=rimcolour,bgcolour,txtcolour
        self.SetBackgroundColour(rimcolour)

        self.vbox = wx.BoxSizer(wx.VERTICAL)
        box = wx.BoxSizer(wx.HORIZONTAL)

        #title panel
        self.tpanel = wx.Panel(self, -1)
        if tooltip is not None:
            self.tpanel.SetToolTipString(tooltip)
        self.title=wx.StaticText( self.tpanel, -1, title)
        self.title.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD))
        self.title.SetForegroundColour(txtcolour)
        
        if is_open==1:
            if txtcolour=="black": bmp = self.bmp_bu
            else: bmp = self.bmp_wu
        else:
            if txtcolour=="black": bmp = self.bmp_bd
            else: bmp = self.bmp_wd

        self.sb=wx.StaticBitmap(self.tpanel, -1, bmp,
                size=(bmp.GetWidth(), bmp.GetHeight()))

        box.Add(self.title,1, wx.EXPAND|wx.LEFT|wx.TOP,5)
        box.Add(self.sb,0,wx.ALL,4)

        cursor = wx.StockCursor(wx.CURSOR_HAND)
        self.tpanel.SetCursor(cursor)
        self.tpanel.SetSizer(box)

        self.vbox.Add(self.tpanel,0,wx.EXPAND)

        #page panel
        self.ppanel = wx.Panel(self, -1)
        self.ppanel.SetBackgroundColour(self.bgcolour)

        self.pbox = wx.BoxSizer(wx.VERTICAL)
        self.pbox.Layout()
        self.ppanel.SetSizer(self.pbox)

        self.vbox.Add(self.ppanel,1,wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT,1)

        if self.is_open==0:
            self.vbox.Hide(self.ppanel)

        self.vbox.Layout()
        self.SetSizer(self.vbox)

        self.tpanel.Bind(wx.EVT_LEFT_UP, self.OnButton)
        self.title.Bind(wx.EVT_LEFT_UP, self.OnButton)
        self.sb.Bind(wx.EVT_LEFT_UP, self.OnButton)

        self.tpanel.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
        self.tpanel.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)

    def OnEnter(self,event):
        if self.txtcolour=="black":
            if self.is_open == 1: bmp = self.bmp_bub
            else: bmp = self.bmp_bdb
        else:
            if self.is_open == 1: bmp = self.bmp_wub
            else: bmp = self.bmp_wdb

        self.sb.SetBitmap(bmp)

    def OnLeave(self,event):
        if self.txtcolour=="black":
            if self.is_open == 1: bmp = self.bmp_bu
            else: bmp = self.bmp_bd
        else:
            if self.is_open == 1: bmp = self.bmp_wu
            else: bmp = self.bmp_wd

        self.sb.SetBitmap(bmp)

    def OnButton(self,event):
        if self.is_open == 1:
            self.vbox.Hide(self.ppanel)
            if self.txtcolour=="black": bmp = self.bmp_bdb
            else: bmp = self.bmp_wdb
        else:
            self.vbox.Show(self.ppanel)
            if self.txtcolour=="black": bmp = self.bmp_bub
            else: bmp = self.bmp_wub

        #Ugly, all three statements are needed (only tested in windows, though)
        self.parentsizer.RecalcSizes()
        self.parentsizer.Layout()
        self.parent.Refresh()

        self.sb.SetBitmap(bmp)
        self.is_open=1-self.is_open
        event.Skip()

    def SetPage(self,page,setbg=0):
        page.Reparent(self.ppanel)
        self.page=page

        self.pbox.Add(page,1,wx.EXPAND|wx.ALL,5)
        self.pbox.Layout()

        if setbg:
            page.SetBackgroundColour(self.bgcolour)

if __name__ == "__main__":

    class ColorPanel(wx.Panel):
        def __init__(self, parent, color):
            wx.Panel.__init__(self, parent, -1,size=(-1,75))
            self.SetBackgroundColour(color)

    class MainPanel(wx.Panel):
        def __init__(self, parent):
            wx.Panel.__init__(self, parent, -1)
            self.SetBackgroundColour("white")

            self.vbox = wx.BoxSizer(wx.VERTICAL)

            tooltip="Click to unfold the green panel"
            p1=FoldPanel(self,self.vbox,"Green Panel",tooltip,
                                wx.Colour(10,10,96),"white","white",is_open=0)
            p1.SetPage(ColorPanel(p1,wx.Colour(128,255,128)),0)
            self.vbox.Add(p1,0,wx.EXPAND|wx.ALL,5)

            tooltip="The titlebar arrow icon is only available in two colours\n"\
                    "... but is transparent."
            p2=FoldPanel(self,self.vbox,"Red Panel",tooltip,"light grey","white")
            p2.SetPage(ColorPanel(p2,wx.Colour(255,128,128)),0)
            self.vbox.Add(p2,0,wx.EXPAND|wx.ALL,5)

            self.vbox.Layout()
            self.SetSizer(self.vbox)

    class MyFrame(wx.Frame):
        def __init__(self, pos=wx.DefaultPosition, size=(180,290)):
            wx.Frame.__init__(self, None, -1, "Foldable Panels", pos, size)
            self.CreateStatusBar()
            mp= MainPanel(self)

    app = wx.PySimpleApp()
    f = MyFrame(None)
    f.Show()
    app.MainLoop()

