Using matplotlib pythonic API

Hey, i'm using matplotlib to create plots inside the twisted

    > asynchronous web framework, so i am forced to use the
    > pythonic API to avoid making global calls, however i have 3
    > questions and would be extremely grateful if anyone could
    > help me out.

You may not be forced to do this; it depends on the circumstances.
I've used the matlab interface to serve web pages before, though not
with twisted. The use of the word global (in the example script
pythonic_matplotlib) may not be the best choice, since the "globals"
like the current figure and current axes are really module level
variables that many modules access. But if you are creating figures
in threads, this would be a problem. So the matplotlib API is a sound
choice.

    > 1) The online docs say that Figure.__init__ takes a linewidth
    > keyword argument, but it does not. how can i set the
    > linewidth for a figure, and how can i set the fontsize for a

I think you are confusing the figure linewidth with the plot
linewidth. The Figure constructor does have a linewidth parameter in
the __init__ method, though this is almost certainly not what you
want. This parameter controls the width of the line around the border
of the figure, a frame if you will, which is normally invisible
because the figure edge line and figure background color are both set
to white by default for saving figures. You are probably interested
in the lines.linewidth parameter which controls the default thickness
of all plot lines. This, and many more parameters, can be set in your
http://matplotlib.sf.net/.matplotlibrc file. You can also control the
default tick sizes, label sizes, colors, etc...

    > figure, without doing weird things like map( lambda t:
    > t.set_fontsize( 6 ), legend.get_texts() )

There are no default legend parameters in rc, so you'll have to
manually do it. You give up some of the convenience of the matlab
interface by using the api. Normally, one would use the matlab
interface 'set' command for this. In the example below, I include an
implementation of set called setapi that you may want to use in your
api code.

    > 2) all of the examples using the pythonic API instantiate a
    > Figure, and then do add_subplot(). this works for me, but
    > matplotlib adds a bunch of space around the figure that i
    > dont need. is there a way around this, or an alternative to
    > add_subplot if i want only one plot?

Sure; use fig.add_axes. Then you can specify the left, bottom, width
and height of your axes as fractions of the entire figure size.
add_subplot is merely a convenience function to predefine some useful
axes sizes, but for full control, you'll want to set the axes size
yourself.

    > 3) my ticklabels for the X axis happen to be quite lengthy
    > strings, and they overlap if placed horizontally, adjacent to
    > each other. i am using rotation = 45 but then a lot of the
    > text gets clipped off of the bottom of the figure (im setting
    > figsize in the Figure constructor). i am happy with the size
    > of the figure, but would like more space for the text at the
    > bottom.

Again, this is a problem with the default axes size. By making the
bottom a little higher, you can prevent your text from getting
clipped. The other option (you may want to do both) is use a smaller
fontsize for your tick labels.

    > any help will be greatly appreciated. Moe Aboulkheir

Since there really was no true OO example for the agg backend, I wrote
one. It gives examples of how to solve all the problems you
encountered - this is examples/webapp_demo.py in CVS.

#!/usr/bin/env python
# This example shows how to use the agg backend directly to create
# images, which may be of use to web application developers who want
# full control over their code without using the matlab interface to
# manage figures, figure closing etc.

···

#
# I am making no effort here to make a figure that looks good --
# rather I am just trying to show the various ways to use matplotlib
# to customize your figure using the matplotlib API

import matplotlib
matplotlib.use('Agg') # force the antigrain backend
from matplotlib import rc
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.figure import Figure
from matplotlib.cbook import iterable
import matplotlib.numerix as nx

# Set some properties that affect the defaults of all figures. The rc
# command is used to create per-script default figure customizations
# of the rc parameters; see http://matplotlib.sf.net/.matplotlibrc .
# You may prefer to set the rc parameters in the rc file itself. Note
# that you can keep directory level default configurations by placing
# different rc files in the directory that the script runs in.
rc('lines', linewidth=2) # thicker plot lines
rc('grid', color=0.75, linestyle='-') # solid gray grid lines
rc('axes', hold=True, # hold state is on
   grid=True, facecolor='y') # yellow background, grid on
rc('tick', color='r', labelsize=20) # big red ticks

def setapi(o, **kwargs):
    """
    for all key, value pairs in kwargs, and all objects in (possibly)
    iterable o, look for a method o.set_key and try to call
    o.set_key(value) if it exists. This is basically a refinition of
    the matlab interface set command
    """
    if not iterable(o): o = [o]
    for thiso in o: # iterate over the objects
        for k,v in kwargs.items():
            func = getattr(thiso, 'set_'+k)
            if func is None: continue
            func(v)

def make_fig():
    """
    make a figure

    No need to close figures or clean up since the objects will be
    destroyed when they go out of scope
    """
    fig = Figure()
    #ax = fig.add_subplot(111) # add a standard subplot

    # add an axes at left, bottom, width, height; by making the bottom
    # at 0.3, we save some extra room for tick labels
    ax = fig.add_axes([0.2, 0.3, 0.7, 0.6])

    line, = ax.plot([1,2,3], 'ro--', markersize=12, markerfacecolor='g')

    # make a translucent scatter collection
    x = nx.rand(100)
    y = nx.rand(100)
    area = nx.pi*(10 * nx.rand(100))**2 # 0 to 10 point radiuses
    c = ax.scatter(x,y,area)
    c.set_alpha(0.5)

    # add some text decoration
    ax.set_title('My first image')
    ax.set_ylabel('Some numbers')
    ax.set_xticks( (.2,.4,.6,.8) )
    labels = ax.set_xticklabels(('Bill', 'Fred', 'Ted', 'Ed'))

    # To set object properties, you can either iterate over the
    # objects manually, or define you own set command, as in setapi
    # above.
    #setapi(labels, rotation=45, fontsize=12)
    for l in labels:
        l.set_rotation(45)
        l.set_fontsize(12)
    
    canvas = FigureCanvasAgg(fig)
    canvas.print_figure('webapp.png', dpi=150)

make_fig()

John,

thanks for your very quick response.

You may not be forced to do this; it depends on the circumstances.
I've used the matlab interface to serve web pages before, though not
with twisted. The use of the word global (in the example script
pythonic_matplotlib) may not be the best choice, since the "globals"
like the current figure and current axes are really module level
variables that many modules access. But if you are creating figures
in threads, this would be a problem. So the matplotlib API is a sound
choice.

sorry i did not explain fully. due to the asynchronous nature of the twisted framework, most blocking operations are generally executed inside threads, or at least, they are in my case.

I think you are confusing the figure linewidth with the plot
linewidth. The Figure constructor does have a linewidth parameter in
the __init__ method, though this is almost certainly not what you
want.

thank you, and indeed i am confused, however i am using matplotlib 0.60.2, and here is the method declaration for Figure's __init__ (from matplotlib/figure.py):

class Figure(Artist):

     def __init__(self,
                  figsize = None, # defaults to rc figure.figsize
                  dpi = None, # defaults to rc figure.dpi
                  facecolor = None, # defaults to rc figure.facecolor
                  edgecolor = None, # defaults to rc figure.edgecolor
                  frameon = True,
                  ):

also in 0.60.2 i noticed that ticker.DayMultiLocator is used on line 1518 of matplotlib/axes.py but is not imported

the example you sent looks to be most helpful, thanks for taking the time to help me out.

Moe

also in 0.60.2 i noticed that ticker.DayMultiLocator is used on line 1518 of matplotlib/axes.py but is not imported

That was just after DayMultiLocator was added; it's a bug. You can just add it yourself.

I use (a little modified) 60.2 as well on two production servers. It seems very stable at this point. Great majority of my plotting uses plot_date and with the new changes (haven't tried - just read about) I am afraid that lots of my code will break. So might take this opportunity to ask:

Does the new date plotting mechanism support the old way of using "seconds from epoch"? It was very efficient as a lot of data (not only mine I would imagine) is simply in that format.

···

--
Peter Groszkowski Gemini Observatory
Tel: +1 808 974-2509 670 N. A'ohoku Place
Fax: +1 808 935-9235 Hilo, Hawai'i 96720, USA