views:

627

answers:

3

I have my main program window, and I would like to make a foldable panel. What I mean is, a panel which is aligned to one of the sides of the window, with a fold/unfold button. It's important that when the panel gets folded/unfolded, the other widgets change their size accordingly to take advantage of the space they have.

How do I do this?

A: 

The layout managers for wxPython (and Swing and others) should be able to do this for you if you create the hierarchy properly. Let's assume it's bound to the right hand side, thus:

+-----------------------------+
|+----------------+ +--------+|
||                | | This is||
||                | | your   ||
||   Other stuff  | | panel  ||
||                | +--------+|
||                | +--------+|
||                | | Another||
||                | | panel  ||
|+----------------+ +--------+|
+-----------------------------+

If your layout is done right, you will have a top-level layout with two columns, one for the other stuff and one for the right-side container.

That container will have it's own layout manager with two rows, one for the top panel, one for the bottom.

That way, when you resize the top panel (your foldable one) to be shorter (fold) or taller (unfold), the layout manager should expand or contract the bottom panel to fit.

Obviously you can use more complicated layout managers, I've chosen the simplest ones to illustrate how to do it without cluttering the discussion with column/row spans and anchors and so on. You can also change the direction of folding by reversing the window managers (horizontal <-> vertical).

paxdiablo
Are you suggesting that I write the fold/unfold functionality myself?I was hoping for an existing widget.
cool-RR
And another thing: By "container", do you mean `wx.Sizer`? Because I did a little experiment like that: I put two windows in a sizer, and then resized one of them. The sizer did not adapt.
cool-RR
+1  A: 

Here is one way using wx.SplitterWindow

import wx, wx.calendar


class FoldableWindowContainer(wx.Panel):
    def __init__(self, parent, left, right):
        wx.Panel.__init__(self, parent)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.SetSizer(sizer)
        self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
        left.Reparent(self.splitter)
        right.Reparent(self.splitter)
        self.left = left
        self.right = right
        self.splitter.SplitVertically(self.left, self.right)
        self.splitter.SetMinimumPaneSize(50)
        self.sash_pos = self.splitter.SashPosition
        sizer.Add(self.splitter, 1, wx.EXPAND)

        fold_button = wx.Button(self, size=(10, -1))
        fold_button.Bind(wx.EVT_BUTTON, self.On_FoldToggle)
        sizer.Add(fold_button, 0, wx.EXPAND)

    def On_FoldToggle(self, event):
        if self.splitter.IsSplit():
            self.sash_pos = self.splitter.SashPosition
            self.splitter.Unsplit()
        else:
            self.splitter.SplitVertically(self.left, self.right, self.sash_pos)


class FoldTest(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)

        left = wx.Panel(self, style=wx.BORDER_SUNKEN)
        right = wx.Panel(self, style=wx.BORDER_SUNKEN)

        left_sizer = wx.BoxSizer(wx.VERTICAL)
        left.SetSizer(left_sizer)
        left_sizer.Add(wx.calendar.CalendarCtrl(left), 1, wx.EXPAND | wx.ALL, 5)
        left_sizer.Add(wx.Button(left, label="Act"), 0, wx.EXPAND | wx.ALL, 5)

        right_sizer = wx.BoxSizer(wx.VERTICAL)
        right.SetSizer(right_sizer)
        right_sizer.Add(
            wx.StaticText(right, label="Fold panel", style=wx.BORDER_RAISED),
            1, wx.EXPAND | wx.ALL, 5
        )

        FoldableWindowContainer(self, left, right)


app = wx.PySimpleApp()
app.TopWindow = FoldTest()
app.TopWindow.Show()
app.MainLoop()

Also, check out wx.CollapsiblePane in the wxPython demos.

Toni Ruža
Works well! Now, follow-up question. I want to make a widget that will do this automatically, so I won't have to create the button and the sizer myself. How shall I set about doing that? I've been trying in the last hour, and I ran into some problems:Let's say I call my class FoldableWindowContainer (FWC for short). Should I subclass SplitterWindow? Or should I subclass Window, and create a SplitterWindow inside of it? Because I tried doing that, but the problem is, how do I add windows the FWC? I mean, who's their parent, the FWC or the SplitterWindow inside of it?Please advise.
cool-RR
You can use the example I gave only subclass from wx.Panel instead of wx.Frame and pass the two panels that go in the splitter window as arguments to the __init__ (use it so that those panels and your FWC have the same parent). One more thing on another note, you might find the SetSashGravity method of wx.SplitterWindow useful.
Toni Ruža
Oh yeah, and you should reparent the panels in the __init__ of the FWC. I made the changes and updated my example.
Toni Ruža
A: 

Hi... how do you add a title to your main frame? I tried changing

wx.Frame.init(self, None)

to

wx.Frame.init(self, None, wx.ID_ANY, title, size)

but it gives this error:

TypeError: in method 'new_Frame', expected argument 2 of type 'int'

please help me find out what's wrong...

Reyn