happyhuman
happyhuman

Reputation: 1651

Attach/Detach two frames in wxpython

I am designing a GUI with several components and two wx.Frame objects F1 and F2. F1 is the main frame and F2 is the secondary frame. I would like to have a mechanism, so the user can attach these two frames into one frame, and also detach them into two frames again if needed.

Assume F1 and F2 contain panels P1 and P2 respectively. When detached, the use should be able to move and resize each frame independently, and closing F1 will close the entire GUI. When attached, F1 will contain both P1 and P2 vertically and F2 will seem to vanish and become a part of F1. There is a lot of wiring and events and messages passed between P1 and P2 which should work in both attached and detached modes.

I have seen this effect in some modern GUI's, but I was unable to find a proper technique online to carry this out. What is a proper way to do this?

Thanks

Upvotes: 2

Views: 950

Answers (3)

happyhuman
happyhuman

Reputation: 1651

There is a library in wxPython called AUI. It provides the mechanism to detach a panel from a frame. The following link has an example along with some other information:

http://wiki.wxpython.org/AuiNotebook%20(AGW)

Upvotes: 0

happyhuman
happyhuman

Reputation: 1651

I came up with a solution for this using the pubsub module. Following is a little example I wrote to show how it is done:

import wx
import gettext
from wx.lib.pubsub import pub

class SubFramePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY)
        self.attachDetachButton = wx.Button(self, wx.ID_ANY, _("Attach"))
        self.sayHelloButton = wx.Button(self, wx.ID_ANY, _("Say Hello"))        
        subPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
        subPanelSizer.Add(self.attachDetachButton, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 0)
        subPanelSizer.Add(self.sayHelloButton, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 0)
        self.SetSizer(subPanelSizer)
        self.attachDetachButton.Bind(wx.EVT_BUTTON, self.OnAttachDetachButton)
        self.sayHelloButton.Bind(wx.EVT_BUTTON, self.OnSayHelloButton)

    def OnAttachDetachButton(self, event):
        if self.attachDetachButton.GetLabel() == "Attach":
            self.attachDetachButton.SetLabel("Detach")
            pub.sendMessage("show.mainframe.OnAttach", data=self)
        else:
            self.attachDetachButton.SetLabel("Attach")
            pub.sendMessage("show.mainframe.OnDetach", data=self)
        event.Skip()

    def OnSayHelloButton(self, event):
        pub.sendMessage("show.mainframe.addText", data="Say Hello\n")
        event.Skip()


class SubFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        if kwds.has_key("panel"):
            self.panel = kwds["panel"]
            del kwds["panel"]
        else:
            self.panel = None

        wx.Frame.__init__(self, *args, **kwds)

        if self.panel is None:
            self.panel = SubFramePanel(self)
        else:
            self.panel.Reparent(self)

        self.SetTitle(_("Sub Frame"))
        self.SetSize((291, 93))

        subFrameSizer = wx.BoxSizer(wx.VERTICAL)

        subFrameSizer.Add(self.panel, 1, wx.EXPAND | wx.LEFT, 5)
        self.SetSizer(subFrameSizer)
        self.Layout()
        pub.subscribe(self.OnClose, "show.subframe.OnClose")

    def OnClose(self, data=None):
        self.Close()


# end of class SubFrame

class MainFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.text_ctrl_1 = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE)

        pub.subscribe(self.OnAddText, "show.mainframe.addText")
        pub.subscribe(self.OnAttach, "show.mainframe.OnAttach")
        pub.subscribe(self.OnDetach, "show.mainframe.OnDetach")

        self.SetTitle(_("Main Frame"))
        self.SetSize((492, 271))
        self.mainFrameSizer = wx.BoxSizer(wx.VERTICAL)
        self.mainFrameSizer.Add(self.text_ctrl_1, 1, wx.ALL | wx.EXPAND, 5)
        self.SetSizer(self.mainFrameSizer)
        self.Layout()

    def OnAddText(self, data):
        self.text_ctrl_1.WriteText(data)

    def OnAttach(self, data):
        self.mainFrameSizer.Add(data, 0, wx.ALL | wx.EXPAND, 5)
        data.Reparent(self)
        self.Layout()
        pub.sendMessage("show.subframe.OnClose")

    def OnDetach(self, data):
        subFrame = SubFrame(self, wx.ID_ANY, "", panel=data)
        self.mainFrameSizer.Remove(data)
        self.Layout()
        subFrame.Show()

class MyApp(wx.App):
    def OnInit(self):
        mainFrame = MainFrame(None, wx.ID_ANY, "")
        self.SetTopWindow(mainFrame)
        mainFrame.Show()
        subFrame = SubFrame(mainFrame, wx.ID_ANY, "")
        subFrame.Show()
        return 1

if __name__ == "__main__":
    gettext.install("app")
    app = MyApp(0)
    app.MainLoop()

Upvotes: 2

0x26res
0x26res

Reputation: 13882

I'm not sure you can move a wxPanel from a wxFrame to another on the fly on wx.

The main reason is that the panel is dependent of its parent and you can't change it on the fly.

Now if you really want to do it, you'll have to create copy the panel in the other frame and delete the previous panel and frame (or just hide them).

There's no built in copy but you can find a way to get the content of your original panel and copy it on the other one.

Upvotes: 0

Related Questions