5

Edit: I'm leaving the question open as is, as it's still a good question and the answer may be useful to others. However, I'll note that I found an actual solution to my issue by using a completely different approach with AuiManager; see the answer below.

I'm working on a MultiSplitterWindow setup (after spending a good deal of time struggling against SashLayoutWindow layout quirks). Unfortunately, when I create a MultiSplitterWindow, I see some unexpected behavior when dragging the sashes around: the sashes can be dragged outside the containing window in the direction of the layout. To say the least, this is behavior I'd like to avoid.

Here is the basic setup (you can confirm the behavior below in the wxPython demo, just substituting leftwin for Panel1, etc., also see below for an example app). Where I have RootPanel/BoxSizer, there is a panel (or Frame, or whatever kind of container element you like) with a BoxSizer to which the MultiSplitterWindow is added – again, as in demo.

+--------------------------------+
| RootPanel/BoxSizer |
|+------------------------------+|
|| MultiSplitterWindow ||
||+--------++--------++--------+||
||| Panel1 || Panel2 || Panel3 |||
||| || || |||
||+--------++--------++--------+||
|+------------------------------+|
+--------------------------------+

When you drag, you can end up with something like this, where ~ and ! indicate that the panel "exists" there but isn't being displayed:

+--------------------------------+
| RootPanel/BoxSizer |
|+-------------------------------|~~~~~~~~~~~~~+
|| MultiSplitterWindow | !
||+-----------------++-----------|~~++~~~~~~~~+!
||| Panel1 || Panel2 | !! Panel3 !!
||| || | !! !!
||+-----------------++-----------|~~++~~~~~~~~+!
|+-------------------------------|~~~~~~~~~~~~~+
+--------------------------------+

If at this point, you drag the RootPanel to be wider than the overall set of panels, you will see all the panels again. Likewise, if you drag the width back down on Panel1, you can get access to the sash for Panel3 again (assuming the Panel2 isn't already too wide, of course). Moreover, this is precisely the situation reported by the Inspection Tool: the RootPanel retains its size, but the MultiSplitterWindow grows beyond the size of the RootPanel/BoxSizer.

Further examination with the Inspection Tool reveals that the virtual and client width values are both 0, but the actual size value is negative (by the corresponding number of pixels it was dragged out of the window) whenever it's out of range. Again, this is nutty behavior; I can't imagine why one would ever want a window to behave this way.

Now, if one holds down Shift so that the _OnMouse method in MultiSplitterWindow adjusts neighbors, this doesn't happen. Thus, one of my approaches was to simply override that method. It works, but I'd prefer to override methods that way if absolutely necessary. Is there another, better way to solve this problem? It doesn't seem like this would be expected or desirable behavior in general, so I imagine there is a standard way of fixing it.

Other things I've tried:

  • Checking whether the sum of the values in the MultiWindowSplitter exceeds the width of the containing window, using each of the EVT_SPLITTER_SASH_POS_CHANGED AND EVT_SPLITTER_SASH_POS_CHANGING events, and then trying to fix the issue by:
    • Using an event.Veto() call
    • Using the SetSashPosition() method on the splitter
  • Overriding the _OnMouse() method to use the behavior that is normally associated with holding down the Shift key. This works, but it ends up giving me other results I don't like.
  • Setting the minimum pane sizes via SetMinimumPaneSize method
  • Setting the maximum size on MultiSplitterWindow via SetMaxSize()
  • Setting the maximum size on RootPanel/BoxSizer using both SetMaxSize() and SetSizeHints() on the RootPanel.
    • I've even done this with an event handler for wx.EVT_SIZE on the container so that the RootPanel always has the appropriate maximum size from the parent frame element
    • I've attempted the same event handling approach for the MultiSplitterWindow, also to no effect.

Version info

I have confirmed that this appears in Windows 32-bit and OS X 64-bit, with the latest snapshot build of wxPython, against both Python 2.7 and 3.3.

Working example (with Inspection tool included)

The following essentially duplicates (and slightly simplifies) the demo source. It's a working demonstration of the problem.

import wx, wx.adv
import wx.lib.mixins.inspection as wit
from wx.lib.splitter import MultiSplitterWindow
class AppWInspection(wx.App, wit.InspectionMixin):
 def OnInit(self):
 self.Init() # enable Inspection tool
 return True
class MultiSplitterFrame(wx.Frame):
 def __init__(self, *args, **kwargs):
 super().__init__(size=(800, 800), *args, **kwargs)
 self.SetMinSize((600, 600))
 self.top_sizer = wx.BoxSizer(orient=wx.HORIZONTAL)
 self.SetSizer(self.top_sizer)
 self.splitter = MultiSplitterWindow(parent=self, style=wx.SP_LIVE_UPDATE)
 self.top_sizer.Add(self.splitter, wx.SizerFlags().Expand().Proportion(1).Border(wx.ALL, 10))
 inner_panel1 = wx.Panel(parent=self.splitter)
 inner_panel1.SetBackgroundColour('#999980')
 inner_panel1_text = wx.StaticText(inner_panel1, -1, 'Inner Panel 1')
 inner_panel1.SetMinSize((100, -1))
 inner_panel2 = wx.Panel(parent=self.splitter)
 inner_panel2.SetBackgroundColour('#999990')
 inner_panel2_text = wx.StaticText(inner_panel2, -1, 'Inner Panel 2')
 inner_panel2.SetMinSize((100, -1))
 inner_panel2.SetMaxSize((100, -1))
 inner_panel3 = wx.Panel(parent=self.splitter)
 inner_panel3.SetBackgroundColour('#9999A0')
 inner_panel3_text = wx.StaticText(inner_panel3, -1, 'Inner Panel 3')
 inner_panel3.SetMinSize((100, -1))
 self.splitter.AppendWindow(inner_panel1)
 self.splitter.AppendWindow(inner_panel2)
 self.splitter.AppendWindow(inner_panel3)
if __name__ == '__main__':
 app = AppWInspection(0)
 frame = MultiSplitterFrame(parent=None, title='MultiSplitterFrame Test')
 app.SetTopWindow(frame)
 frame.Show()
 app.MainLoop()
asked Jul 23, 2013 at 19:07
7
  • post a small example code that demonstrates the problem ... the explanation is good, but seeing implementation will help get you an answer Commented Jul 23, 2013 at 19:11
  • @JoranBeasley As noted in the explanation, the best place for anyone to look is in the wxPython demo. I can duplicate that if you think it's actually useful, though. Commented Jul 23, 2013 at 19:14
  • i see ... yeah thats weird ... sorry :/ you can bind a size event listener to the parent and onsize check the boundaries and update them ... but yeah kind of a pain Commented Jul 23, 2013 at 19:20
  • Yeah, I've thought of doing that. At that point, you don't have much gain over just using a set of SashLayoutWindow components, though (other than a little bit simpler initial layout), and a few things that are potentially weirder because you have default behaviors to override. Commented Jul 23, 2013 at 19:25
  • 1
    Layouts really aren't that bad. You just have to plan out your applications ahead of time. I usually sketch out mine and draw boxes to get an idea of what needs to go where. Then I can decide if I just want nested BoxSizers (usually) or a mix of nested BoxSizers and other sizers. Commented Jul 23, 2013 at 20:26

2 Answers 2

4

Depending on what one needs this for, one possible option to use instead of a custom-managed MultiSplitterWindow (or SashLayoutWindow combinations, etc.) is the Advanced User Interface kit's AuiManager tool (documentation for pre-Phoenix version here; Phoenix docs here). AuiManager automates a lot of these kinds of things for you. In my case, I was attempting to use the MultiSplitterWindow as a way of controlling collapsible and resizable panels for the UI in question, so the AuiManager is a perfect fit: it already has all the controls and constraints I need built in.

In that case, all one needs to do is create an AuiManager instance

(I'm leaving this here as an answer in hopes that others who may be taking the same naive approach I was taking will find it useful, but not selecting it as the answer because it does not directly answer the original question.)

Sample use of AUI under Phoenix

This code sample does exactly what I was trying to do with the MultiSplitterWindow, but managed automatically by the AuiManager.

import wx, wx.adv
import wx.lib.mixins.inspection as wit
from wx.lib.agw import aui
class AppWInspection(wx.App, wit.InspectionMixin):
 def OnInit(self):
 self.Init() # enable Inspection tool
 return True
class AuiFrame(wx.Frame):
 def __init__(self, *args, **kwargs):
 super().__init__(size=(800, 800), *args, **kwargs)
 self.SetMinSize((600, 600))
 # Create an AUI Manager and tell it to manage this Frame
 self._manager = aui.AuiManager()
 self._manager.SetManagedWindow(self)
 inner_panel1 = wx.Panel(parent=self)
 inner_panel1.SetBackgroundColour('#999980')
 inner_panel1.SetMinSize((100, 100))
 inner_panel1_info = aui.AuiPaneInfo().Name('inner_panel1').Caption('Inner Panel 1').Left().\
 CloseButton(True).MaximizeButton(True).MinimizeButton(True).Show().Floatable(True)
 inner_panel2 = wx.Panel(parent=self)
 inner_panel2.SetBackgroundColour('#999990')
 inner_panel2_info = aui.AuiPaneInfo().Name('inner_panel2').Caption('Inner Panel 2').Left().Row(1).\
 Show().Floatable(False)
 inner_panel3 = wx.Panel(parent=self)
 inner_panel3.SetBackgroundColour('#9999A0')
 inner_panel3.SetMinSize((100, 100))
 inner_panel3_info = aui.AuiPaneInfo().Name('inner_panel3').Caption('Inner Panel 3').CenterPane()
 self._manager.AddPane(inner_panel1, inner_panel1_info)
 self._manager.AddPane(inner_panel2, inner_panel2_info)
 self._manager.AddPane(inner_panel3, inner_panel3_info)
 self._manager.Update()
 def __OnQuit(self, event):
 self.manager.UnInit()
 del self.manager
 self.Destroy()
if __name__ == '__main__':
 app = AppWInspection(0)
 frame = AuiFrame(parent=None, title='AUI Manager Test')
 app.SetTopWindow(frame)
 frame.Show()
 app.MainLoop()
answered Jul 24, 2013 at 14:31
Sign up to request clarification or add additional context in comments.

Comments

0

I would try setting the panel's MaxSize or even resort to SetSizeHints(). Both of these allow you to set some constraints on the size of the widget.

answered Jul 23, 2013 at 20:25

4 Comments

I actually thought I had tried that, but I'll give it a go and see.
Okay, so, after some further testing: (1) neither SetMaxSize() nor SetSizeHints() has any effect. Moreover, further investigation with the inspection tool indicates that the offscreen panels have zero virtual or client width, but an actual width with negative values. There has got to be a way to disallow this behavior; I can't think of why you would ever want it. Note that I'm adding this info to the question as well.
I linked to your question from the wxPython mailing list since I'm not sure what's going on here.
Thanks. I've subscribed to that topic, so I should see any updates that come in there.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.