Layout of graph widgets

Hi, I just subscribed to the list and have been looking at

    > the source. I wrote a python graphing library so lots of
    > the internals are familair (how many ways can you draw a
    > line?) but spending a couple hours browsing the code I can't
    > get a handle on how layout is done. Are things generally at
    > fixed positions? Are there some things I can
    > subclass/decorate to get a different look or feel?

Yes, things are placed in fixed positions. Fixed here can mean
relative to something else, as in 20% from the left side of the axes,
but not free in the sense of a layout engine that tries to find
optimal placement.

For example, you can place axes with

  axes([left, bottom, width, height])

and these are expressed in fractions of the figure width and height.
Eg, http://matplotlib.sourceforge.net/screenshots.html#axes_demo. Or
you can place a rectangle in relative axes coords (0,0) bottom left,
(1,1) top right with a custom transform

  Rectangle( xy=(0.25, 0.5), width=0.1, height=0.2,
             transform=ax.transAxes)

There has been a lot of clamoring for a layout engine, particularly
because when you have a number of subplots, the text labels from one
axes can overlap another as discussed here -
http://matplotlib.sourceforge.net/faq.html#TEXTOVERLAP . In order to
incorporate a layout engine, you would need at a minimum to subclass
Figure and Axes. Most of the scene objects (matplotlib.artist.Artist)
can return their bounding box in window extent, and you could use this
information to help with layout. The transforms module has a helper
function bbox_all which takes a sequence of bounding boxes
(transform.Bbox instances) and returns the box that bounds them all --
very useful when doing layout. Basically, the Figure would want to
inspect it's axes and the axes in turn would inspect the bounding
boxes of the objects in contains.

The only catch is that in the current implementation, text instances
do not know their window extent until drawing time. This is because
text sizes are backend dependent, and the class that knows how to size
text is the backend Renderer, which isn't known to the objects until
the call to Artist.draw(renderer). This has proved to be a pain in
the ass since in some classes (eg legend, ticks, tables and text
itself) which need to do some layout involving text, the layout is
deferred until drawing time and then the information is sometimes
cached for efficiency. Typically this leads to hairy, suboptimal, and
hard-to-maintain code, cf Legend._update_positions and
Text._get_layout.

One potential way out of this dilemma is to use the ft2font module
(matplotlib's freetype2 wrapper) to get the layout information
whenever you want it. matplotlib's core backend agg uses this anyhow,
and postscript embeds freetype2 fonts and so uses it too. It remains
to be seen how well this strategy would work on the SVG and Cairo
backends, which are the only other two renderers are actively
supported and used, as far as I know. I know Steve has taken some
pains to get Cairo text sizing similar to Aggs/GTKs, so it may serve
as a good enough approximation for layout purposes for cairo as well.
We've been trying to push GD users -> Agg, and users who use native
GTK/WX drawing to GTKAgg/WXAgg, because this simplifies maintenance.

    > I had a longstanding misimpression that matplotlib was a
    > wrapper around matlab or I might have abandoned my custom

You're not the first to have this misperception. I wonder how we can
combat it. Perhaps renaming the main interface pylab will help. Or a
blinking box on the homepage maybe - "matplotlib does not require
matlab! That's the whole point!!'" :slight_smile:

    > work and just thrown in here. If I can get graphs that look
    > like this[1] I'll do just that.

We'd love to have you on board - then we'd be collaborating on two
projects :slight_smile:

    > Sorry for the vague questions, but I'll try to recompense by
    > porting my PIL and ming backends over Christmas. The
    > unmodified backends are stupid similar to matplotlib (again,
    > there aren't too many ways to spell 'line')

Right, the backends are dumb as bricks by design, because this lowers
the barrier of entry in writing a new backend. Actually, they're too
dumb currently, because the frontend handles the transformation to
display coordinates, which isn't optimal for backends that have
transformations built in (eg PS, SVG) and leads to some problems with
subpixel antialiasing in agg. Fixing this is on the list of things to
do.

    > Ignore the "shape_*" methods, they just return a handle to
    > paintable objects instead of doing the paint right away
    > (required for doing hovers and sprites in ming/Flash).

As I mentioned to you earlier, I think the hover capabilities of
ming/Flash would be very appealing to webapp developers. Actually, I
think these are great for all domains, because they let you pack so
much information into a figure. Could this be embedded in a GUI,
bychance?

I've been meaning to make some alterations in the frontend to support
clickable elements, eg in SVG, and this wouldn't be hard. The same
framework could be used for tagging hover elements. If you want to
take the lead here, by all means. Otherwise, when you get to this
point let me know and I can work with you on it.

    > [1] I only need a few kinds of graphs but they are for
    > marketing folks instead of tech folks so purtier is better.
    > I put up some samples of our current output here (png &
    > Flash) http://jackdied.com/jackdied/graph_sample.html

Everything except the 3D bar look is already supported. We have major
and minor ticking, solid date support, legends, bar and line charts.
I'm personally not too interested in 3D bars because I don't think the
3D effect adds any information and arguably reduces it. I would not
oppose a patch, though. It would be fairly trivial to write, I
suspect.

See examples/major_minor_demo*.py, examples/date_demo*.py
examples/barchart_demo.py and examples/legend_demo*.py in the src
distro.

A few brief notes:

  - you can set the figure background color with the figure command
    and customize the default in the rc file

  - you can customize the default grid style (eg a solid gray line)
    using the rc file, the rc command, or directly setting the line
    properties on the result of, eg, ax.get_xgridlines()

      lines = ax.get_xgridlines() + ax.get_ygridlines()
      set(lines, linestyle='-', color='lightslategray', linewidth=1)
    
  - You have a "bar" and a "plot" overlayed. Just make sure hold is
    set to True so that subsequent plots don't overwrite old plots.
    You can set the default state in rc, toggle it with the "hold
    command" or pass hold as a kwarg on a given plot command to
    temporarily override the hold state

  - To offset the major ticks ('Nov', 'Dec') below the minor ticks,
    you may want to simply prepend them with a newline ('\nNov',
    '\nDec'). You'll want to use custom date tick formatters as
    illustrated in the date demo files referenced above. There are a
    number of other ways to control the vertical offset (see also the
    verticalalignment of the text instances) but the newline trick may
    be enough to get them into the position you want.

  - The legend doesn't have a shadow feature built in, though you
    could add one trivially and I would be happy to accept a patch.
    This could be a general feature of patches, actually.

    JDH