== 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.