Panes closed by AuiManager are always docked

Riccardo Borgani <[email protected]> Thu, 12 Apr 2018 00:59:58 -0700 (PDT)
Newsgroups gmane.comp.python.wxpython.devel
Message-ID <[email protected]>
Hi all,

I have an application where multiple panes are managed by 
wx.lib.agw.aui.AuiManager. Some of them are usually docked and some are 
floating.
If a floating pane is closed, next time I open it I would like it to be 
still floating. Instead, it always appears docked.



Looking at the method AuiManager.ClosePane in 
wx/lib/agw/aui/framemanager.py:
https://github.com/wxWidgets/Phoenix/blob/wxPython-4.0.1/wx/lib/agw/aui/framemanager.py#L5068
            pane_info.Dock().Hide()
The pane is always docked before being hidden.
Is this a bug, or is there a reason for this behavior?



To fix/avoid this behavior, I bind to EVT_AUI_PANE_CLOSE and hide the pane 
myself:
    def OnAuiPaneClose(self, evt):
        pane = evt.GetPane()
        if not pane.IsDestroyOnClose():
            pane.Hide()
            self._mgr.Update()
            evt.Veto()
Next time I open the pane it is indeed floating, but now the title bar and 
the close button are gone! Which means I can't move or close the pane 
again...
Why is this happening?



See attached script and screenshots for a demonstration of this behavior.
You can open two panes from the View menu.
Pane 1 is left to AuiManager. So if you open it, close it, and open it 
again it will be docked.
Pane 2 is hidden by OnAuiPaneClose and its event vetoed. So if you open it, 
close it, and open it again it will be floating but the title bar will be 
gone.

I've tested this with Python 2.7.14 and wxpython 4.0.1 (from the anaconda 
repository).
On Ubuntu 16.04 and 17.10, and Windows 10 I got the same behavior.
On macOS 10.13 High Sierra, instead, it works fine (the title bar doesn't 
disappear).



An alternative I can think of is to make my own AuiManager which lets 
ClosePane do its work, and then sets the pane floating again afterwards.
import wx.lib.agw.aui as aui

class MyAuiManager(aui.AuiManager):
    def ClosePane(self, pane_info):
        was_floating = False
        if not pane_info.IsDestroyOnClose():
            if pane_info.IsFloating():
                was_floating = True

        super(MyAuiManager, self).ClosePane(pane_info)

        if was_floating:
            pane_info.Float()
This seems to work fine. Is there some reason for not doing it?



Thanks for your help, and for the good work!
Riccardo

-- 
You received this message because you are subscribed to the Google Groups "wxPython-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/d/optout.
test_aui.py (text/x-python, 3.9 KB)
"""
This script will open an empty frame managed by AuiManager.
From the View menu you can open two panes: Pane 1 and Pane 2.

The first time you open the panes, they will both be floating.

The second time you open the panes (after closing them):
    - Pane 1 will be docked to the left;
    - Pane 2 will be floating, but the title bar and the close button
        will be gone!

The difference is in how OnAuiPaneClose treats the two panes:
    - Pane 1 is ignored so AuiManager will handle it as usual,
        it will first dock it and then hide it
        (see method AuiManager.ClosePane in wx/lib/agw/aui/framemanager.py)
    - Pane 2 will be hidden and the related event vetoed.
"""
import wx
import wx.lib.agw.aui as aui

ID_Show1 = wx.NewId()
ID_Show2 = wx.NewId()


class MyGui(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Test Frame")

        self._mgr = aui.AuiManager()
        self._mgr.SetManagedWindow(self)

        # create menu
        mb = wx.MenuBar()
        view_menu = wx.Menu()
        view_menu.Append(ID_Show1, "Show Pane 1")
        view_menu.Append(ID_Show2, "Show Pane 2")
        mb.Append(view_menu, "View")
        self.SetMenuBar(mb)

        self.SetMinSize(wx.Size(400, 300))

        self.Bind(wx.EVT_MENU, self.OnShow1, id=ID_Show1)
        self.Bind(wx.EVT_MENU, self.OnShow2, id=ID_Show2)
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        # Treat closing of Pane 1 and Pane 2 differently
        self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnAuiPaneClose)

        self.Create1()
        self.Create2()
        for pane in self._mgr.GetAllPanes():
            pane.Hide()
        self._mgr.Update()

    def OnAuiPaneClose(self, evt):
        """ Don't let AuiManager close Pane 2, instead we hide it ourselves.
        Ignore Pane 1 (let AuiManager do its thing).
        """
        pane = evt.GetPane()
        if pane.caption == "Pane 2":
            pane.Hide()
            self._mgr.Update()
            evt.Veto()

    def Create1(self):
        self.panel1 = wx.Panel(self)
        szr = wx.BoxSizer(wx.VERTICAL)
        szr.Add(wx.StaticText(self.panel1, label="Panel 1"))
        szr.Add(wx.StaticText(self.panel1, label="OnAuiPaneClose:"))
        szr.Add(wx.StaticText(self.panel1, label="do nothing"))
        szr.Add(wx.StaticText(self.panel1, label="let AuiManager work"))
        self.panel1.SetSizerAndFit(szr)

        self._mgr.AddPane(self.panel1, aui.AuiPaneInfo().
                          Caption("Pane 1").
                          Float().
                          FloatingSize(wx.Size(150, 100)).
                          CloseButton(True).
                          MaximizeButton(True))

    def Create2(self):
        self.panel2 = wx.Panel(self)
        szr = wx.BoxSizer(wx.VERTICAL)
        szr.Add(wx.StaticText(self.panel2, label="Panel 2"))
        szr.Add(wx.StaticText(self.panel2, label="OnAuiPaneClose:"))
        szr.Add(wx.StaticText(self.panel2, label="pane will be hidden"))
        szr.Add(wx.StaticText(self.panel2, label="event will be vetoed"))
        self.panel2.SetSizerAndFit(szr)

        self._mgr.AddPane(self.panel2, aui.AuiPaneInfo().
                          Caption("Pane 2").
                          Float().
                          FloatingSize(wx.Size(150, 100)).
                          CloseButton(True).
                          MaximizeButton(True))

    def OnShow1(self, event):
        pane = self._mgr.GetPane(self.panel1)
        if pane.IsShown():
            pane.Hide()
        else:
            pane.Show()
        self._mgr.Update()

    def OnShow2(self, event):
        pane = self._mgr.GetPane(self.panel2)
        if pane.IsShown():
            pane.Hide()
        else:
            pane.Show()
        self._mgr.Update()

    def OnClose(self, event):
        self._mgr.UnInit()
        del self._mgr
        self.Destroy()


if __name__ == '__main__':
    myapp = wx.App(redirect=False)
    mygui = MyGui()
    mygui.Show(True)
    myapp.MainLoop()
before.png (image/png, 71 KB) - not displayed
after.png (image/png, 81.7 KB) - not displayed