Limit wxPython MultiSplitterWindow panes

Edit: I leave the question open as it is, since it still remains a good question, and the answer may be useful to others. However, I want to note that I found an actual solution to my problem using a completely different approach with AuiManager ; see below .

I am working on setting up MultiSplitterWindow (having spent a lot of time struggling with SashLayoutWindow ). Unfortunately, when I create a MultiSplitterWindow , I see some unexpected behavior when dragging scraps: sashes can be dragged outside the window containing the layout direction. At the very least, this is a behavior I would like to avoid.

Here is the basic setup (you can confirm the behavior below in wxPython demo by simply replacing leftwin with leftwin , etc., also see the sample application below). Where I have RootPanel/BoxSizer , there is a panel (or Frame , or some type of container that you like) using BoxSizer to which MultiSplitterWindow is added - again, as in the demo.

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

When you drag, you can get something like this, where ~ and ! that the panel "exists" there, but is not displayed:

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

If at this moment you drag the RootPanel so that it is wider than the general set of panels, you will see all the panels again. Similarly, if you drag the width back onto Panel1 , you can access the sash for Panel3 (assuming Panel2 not too wide, of course). Moreover, this is what the Inspection Tool reports about: RootPanel keeps its size, but MultiSplitterWindow goes beyond the size of RootPanel/BoxSizer .

Further inspection using the Inspection tool shows that the width values ​​of the virtual and client values ​​are 0, but the actual size value is negative (by the corresponding number of pixels that it was pulled out of the window) when it is out of range. Again, this is crazy behavior; I can’t imagine why it would ever be necessary for a window to be that way.

Now, if you hold Shift so that the _OnMouse method in MultiSplitterWindow sets up neighbors, this does not happen. So one of my approaches was to simply override this method. This works, but I would prefer to override methods this way if absolutely necessary. Is there any other, better way to solve this problem? It does not seem like this would be the expected or desirable behavior in general, so I assume there is a standard way to fix it.

Other things I've tried:

  • Check if the sum of the values ​​in the MultiWindowSplitter width of the containing window using each of the EVT_SPLITTER_SASH_POS_CHANGED and EVT_SPLITTER_SASH_POS_CHANGING events, and then try to fix the problem:
    • Using the call to event.Veto()
    • Using the SetSashPosition() Method on a Separator
  • Overriding the _OnMouse() method to use the behavior that is usually associated with holding the Shift . It works, but it ends up giving me other results that I don't like.
  • Setting minimum panel sizes using the SetMinimumPaneSize method
  • Setting maximum size on MultiSplitterWindow via SetMaxSize()
  • Setting the maximum size on RootPanel/BoxSizer using SetMaxSize() and SetSizeHints() on RootPanel .
    • I even did this with an event handler for wx.EVT_SIZE in the container, so the RootPanel always has the corresponding maximum size from the parent of the frame
    • I tried using the same event handling method for MultiSplitterWindow , which also has no effect.

Version Information

I have confirmed that this is displayed on 32-bit and 64-bit versions of Windows, from the latest wxPython snapshot assembly, for both Python 2.7 and 3.3.

Working example (with the inspection tool enabled)

The following is essentially a duplicate (and a little simplified) of the demo source. This is 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() 
+4
source share
2 answers

Depending on what it is for, one of the possible uses instead of custom MultiSplitterWindow (or SashLayoutWindow combinations, etc.) is the AuiManager advanced user interface AuiManager (documentation for the pre- Phoenix Version here ; Phoenix docs here ). AuiManager automates a lot of these things for you. In my case, I tried to use MultiSplitterWindow as a way to manage collapsible and resizable panels for the corresponding user interface, so AuiManager perfect: it already has all the controls and restrictions that I need.

In this case, all you have to do is create an instance of AuiManager

(I leave this here as an answer in the hope that others who can take the very naive approach that I took will find it useful, but did not choose it as the answer, because it does not directly answer the original question.)

AUI example under Phoenix

This sample code does exactly what I tried to do with MultiSplitterWindow , but was automatically controlled by 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() 
+3
source

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

0
source

Source: https://habr.com/ru/post/1493035/


All Articles