wx-style layout engine for matplotlib: mplsizer

Due to repeated emails by Eric Firing about how something like this
would be nice to have, I finally got around to packaging a little
utility I wrote. I uploaded it to the MPL source repository. The basic
idea is to create a layout engine for matplotlib. Not wanting to
(re-)invent an API, I decided simply to imitate the layout engine I knew
best, which is wxPython.

Disclaimer: This isn't bug-free, complete, or well-documented and this
announcement should merely be taken as a call to "commence hacking now".

That being said, I've used mplsizer for several figures for publication,
so I don't consider it that bad or incomplete, either.

Anyhow, the source code, licensed under the MIT license, is available at:
https://svn.sourceforge.net/svnroot/matplotlib/trunk/toolkits/mplsizer

One note: aside from depending on matplotlib, this requires setuptools.
Sorry to those of you who hate setuptools, but it simply gives me tools
that make my life easier. If you want to change mplsizer to not require
setuptools, that's fine by me, but we should keep it setuptools compatible.

Here's the last part of the demo_basic.py script. "fig" is a matplotlib
Figure instance, "a","b","b2", and "lowest" are matplotlib Axes instances.

    # Now perform the mplsizer stuff

    import matplotlib.toolkits.mplsizer as mplsizer

    frame = mplsizer.MplSizerFrame( fig )
    sizer = mplsizer.MplBoxSizer()
    frame.SetSizer(sizer)

    sizer.Add(a,name='a',expand=1)
    sizer.Add(b,name='b',all=0,left=1,border=0.2)
    sizer.Add(b2,name='b2')

    if more_plots:
        hsizer = mplsizer.MplBoxSizer(orientation='horizontal')
        hsizer.Add(c,name='c',option=1,align_bottom=1)
        hsizer.Add(d,name='d',align_centre=1)
        sizer.Add(hsizer,all=0,bottom=1,border=0.5,expand=1,option=1)

    frame.Layout() # triggers layout

    # It's naughty to use the private attribute, but, hey, this is for
    # debugging only, so it's OK, right?
    lowest.set_position(hsizer._rect)
pylab.show()

Very cool!

···

On 7/30/06, Andrew Straw <strawman@...36...> wrote:

The basic
idea is to create a layout engine for matplotlib. Not wanting to
(re-)invent an API, I decided simply to imitate the layout engine I knew
best, which is wxPython.

Andrew,

This looks very cool, and I'm looking forward to playing around with it. Thanks for the hard work!

Shooting from the hip, here are some initial comments. I may be able to submit patches for some of the more innocuous items later in the week.

1. It appears that as_sizer_element() uses the _axes_sizer_elements dictionary to cache MplAxesSizerElement instances. Using a WeakKeyDictionary from the "weakref" module instead of a regular dictionary may be necessary to allow the garbage collection of the MplAxesSizerElements when their associated Axes gets GC'd.

2. Convenience MplBoxSizer subclasses that let you omit the "orient" keyword might be nice:

     class MplHBoxSizer(MplBoxSizer):
         def __init__(self):
             MplBoxSizer.__init__(self, orientation='vertical')

3. Couldn't you just drop mplsizer.py into the "matplotlib.toolkits" virtual package? Maybe you can't -- I'm pretty new to applied python-eggery.

4. I feel we should avoid the whole European/American spelling problem that WX has. Why not make both 'align_centre' and 'align_center' do the same thing?

5. Why not use shorter names, with less redundancy? (e.g. "matplotlib.toolkits.sizer", FigureSizer, Box, HBox, Grid, etc)

Ken

Hi Ken,

Thanks for your comments.

Ken McIvor wrote:

1. It appears that as_sizer_element() uses the _axes_sizer_elements
dictionary to cache MplAxesSizerElement instances. Using a
WeakKeyDictionary from the "weakref" module instead of a regular
dictionary may be necessary to allow the garbage collection of the
MplAxesSizerElements when their associated Axes gets GC'd.

I'll check into this.

2. Convenience MplBoxSizer subclasses that let you omit the "orient"
keyword might be nice:

    class MplHBoxSizer(MplBoxSizer):
        def __init__(self):
            MplBoxSizer.__init__(self, orientation='vertical')

I was thinking I want an exact a replica of the wx API as possible,
simply so my feeble mind doesn't get (more) confused. Is there anything
like this in stock wx? I think it's a decent enough idea, but I'd rather
not cause namespace bloat. But, if you're really into it, let's do it.

3. Couldn't you just drop mplsizer.py into the "matplotlib.toolkits"
virtual package? Maybe you can't -- I'm pretty new to applied python-
eggery.

I think this is possible, and I hope for not too much effort. This is
the result of historic accident and personal time-allocation issues.
Mplsizer started life as a private setuptools-ized package, and I'm not
spending my time to de-setuptools-ize software -- I see setuptools as
the way forward, not something (I will spend my time) to remove. I
understand that some people aren't too fond of setuptools, and to them,
I say, "patches welcome".

4. I feel we should avoid the whole European/American spelling
problem that WX has. Why not make both 'align_centre' and
'align_center' do the same thing?

Same answer as to point 2...

5. Why not use shorter names, with less redundancy? (e.g.
"matplotlib.toolkits.sizer", FigureSizer, Box, HBox, Grid, etc)

Same answer as to point 2... (I'd be happy to drop the "Mpl" prefix,
though.)

Andrew Straw wrote:

I was thinking I want an exact a replica of the wx API as possible,

well, I'm a big fan of wx.Sizers, but we all know that they confuses folks.

Given that grid-like layouts are likely to be the most common with MPL, maybe just make a GridBagSizer, and forget the rest? All the other sizers are just a simplification of that anyway.

just my $0.2

Despite that small suggestion -- very nice work!

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@...236...