== A Proportional Splitter Window ==
This is code for a splitter window that keeps its relative pane sizes when the window is resized.  Instead of an absolute sash position, you pass a proportion between 0.0 & 1.0.

The proportional value is altered when the user drags the sash.  Also, it enforces a minimum pane size so that a pane will not disappear on resizing like the default splitter window behavior.

The code for the Proportional``Splitter class is below along with an example using it.  See the original mailing list post at: http://aspn.activestate.com/ASPN/Mail/Message/wxPython-users/1718892

Tiziano Tissino updated the code below to work with the latest version of wxPython.

=== ProportionalSplitter Class ===
{{{
#!python
import wx

class ProportionalSplitter(wx.SplitterWindow):
        def __init__(self,parent, id = -1, proportion=0.66, size = wx.DefaultSize, **kwargs):
                wx.SplitterWindow.__init__(self,parent,id,wx.Point(0, 0),size, **kwargs)
                self.SetMinimumPaneSize(50) #the minimum size of a pane.
                self.proportion = proportion
                if not 0 < self.proportion < 1:
                        raise ValueError, "proportion value for ProportionalSplitter must be between 0 and 1."
                self.ResetSash()
                self.Bind(wx.EVT_SIZE, self.OnReSize)
                self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged, id=id)
                ##hack to set sizes on first paint event
                self.Bind(wx.EVT_PAINT, self.OnPaint)
                self.firstpaint = True

        def SplitHorizontally(self, win1, win2):
                if self.GetParent() is None: return False
                return wx.SplitterWindow.SplitHorizontally(self, win1, win2,
                        int(round(self.GetParent().GetSize().GetHeight() * self.proportion)))

        def SplitVertically(self, win1, win2):
                if self.GetParent() is None: return False
                return wx.SplitterWindow.SplitVertically(self, win1, win2,
                        int(round(self.GetParent().GetSize().GetWidth() * self.proportion)))

        def GetExpectedSashPosition(self):
                if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
                        tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
                else:
                        tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
                return int(round(tot * self.proportion))

        def ResetSash(self):
                self.SetSashPosition(self.GetExpectedSashPosition())

        def OnReSize(self, event):
                "Window has been resized, so we need to adjust the sash based on self.proportion."
                self.ResetSash()
                event.Skip()

        def OnSashChanged(self, event):
                "We'll change self.proportion now based on where user dragged the sash."
                pos = float(self.GetSashPosition())
                if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
                        tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
                else:
                        tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
                self.proportion = pos / tot
                event.Skip()

        def OnPaint(self,event):
                if self.firstpaint:
                        if self.GetSashPosition() != self.GetExpectedSashPosition():
                                self.ResetSash()
                        self.firstpaint = False
                event.Skip()
}}}
=== Example Usage ===
This example may not work with the latest version of wxPython.

{{{
#!python

# Example window with two splitters:
# _________________
# |          |    |
# |          |    |
# |__________|    |
# |          |    |
# |          |    |
# |__________|____|

from ProportionalSplitter import ProportionalSplitter

class MainFrame(wx.Frame):

    def __init__(self,parent,id,title,position,size):
        wx.Frame.__init__(self, parent, id, title, position, size)

        ## example code that would be in your window's init handler:

        ## create splitters:
        self.split1 = ProportionalSplitter(self,-1, 0.66)
        self.split2 = ProportionalSplitter(self.split1,-1, 0.50)

        ## create controls to go in the splitter windows...

        ## add your controls to the splitters:
        self.split1.SplitVertically(self.split2, self.rightpanel)
        self.split2.SplitHorizontally(self.topleftpanel, self.bottomleftpanel)

}}}
== Comments ==
Great job!

Here is code rewritten for new wx namespace (wxWidget 2.5.3)

Tiziano Tissino

 * Thanks, I replaced the old code with your code.

== Comment by Franz Steinhaeusler ==
This doesn't run in my case. (My comment can be removed again, when it will work)

I created this sample:

{{{
#!python
class MainFrame(wx.Frame):

    def __init__(self,parent,id,title,position,size):
        wx.Frame.__init__(self, parent, id, title, position, size)

        ## example code that would be in your window's init handler:

        ## create splitters:
        self.split1 = ProportionalSplitter(self,-1, 0.66)
        self.split2 = ProportionalSplitter(self.split1,-1, 0.50)

        #self.split1 = wx.SplitterWindow(self)
        #self.split2 = wx.SplitterWindow(self.split1)
        ## create controls to go in the splitter windows...
        self.rightpanel = wx.Panel (self.split1)
        self.rightpanel.SetBackgroundColour('cyan')
        self.topleftpanel = wx.Panel (self.split2)
        self.topleftpanel.SetBackgroundColour('pink')
        self.bottomleftpanel = wx.Panel (self.split2)
        self.bottomleftpanel.SetBackgroundColour('sky Blue')

        ## add your controls to the splitters:
        self.split1.SplitVertically(self.split2, self.rightpanel)
        self.split2.SplitHorizontally(self.topleftpanel, self.bottomleftpanel)

app = wx.App(0)
win = MainFrame(None, -1, "Hello!", (50, 50), (600, 400))
win.Show()
app.MainLoop()
}}}
It doesn't show any splitter (wxPython 2.6.2.1). If I comment ProportionalSplitter out, and remove the comment of wx.Splitterwindow, than it seems ok.

{{{
#!python
        #self.split1 = ProportionalSplitter(self,-1, 0.66)
        #self.split2 = ProportionalSplitter(self.split1,-1, 0.50)

        self.split1 = wx.SplitterWindow(self)
        self.split2 = wx.SplitterWindow(self.split1)
}}}

== Comment by Roger Wolf ==
@ Franz:
Actually, the code does work - but you just didn't see it :). Contrary to the default behaviour of a SplitterWindow, the style by ProportionalSplitter was explicitly set to 0 - which means that the dividers were made invisible.

I think it makes more sense that ProportionalSplitter keeps to the default SplitterWindow style. I've removed the explicit style setting in the ProportionalSplitter code.

Roger

== Comment by Nate Moon ==
When I tried the code, the dividers are still invisible at least on Windows 7. I was at the same conclusion of Franz Steinhaeusler, however, I looked for a cursor change inside the window to see if the dividers where invisible/same color of the panels and found that the dividers are actually there. So, I think its logical that you make the panels different colors to help avoid confusion. Besides, a little color in an example doesn't hurt does it? 

I added SetBackgroundColour(x) to each panel in Franz Steinhaeusler's code above.