source: trunk/python/asaplotbase.py @ 1870

Last change on this file since 1870 was 1870, checked in by Malte Marquarding, 14 years ago

Added svg support

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.2 KB
Line 
1"""
2ASAP plotting class based on matplotlib.
3"""
4
5import sys
6from re import match
7import matplotlib
8
9from matplotlib.figure import Figure, Text
10from matplotlib.font_manager import FontProperties as FP
11from numpy import sqrt
12from matplotlib import rc, rcParams
13from matplotlib.ticker import OldScalarFormatter
14
15from asap.parameters import rcParams as asaprcParams
16from asap.logging import asaplog
17
18# API change in mpl >= 0.98
19try:
20    from matplotlib.transforms import blended_transform_factory
21except ImportError:
22    from matplotlib.transforms import blend_xy_sep_transform as blended_transform_factory
23
24if int(matplotlib.__version__.split(".")[1]) < 99:
25    #print "Warning: matplotlib version < 0.87. This might cause errors. Please upgrade."
26    asaplog.push( "matplotlib version < 0.99. This might cause errors. Please upgrade." )
27    asaplog.post( 'WARN' )
28
29class asaplotbase:
30    """
31    ASAP plotting base class based on matplotlib.
32    """
33
34    def __init__(self, rows=1, cols=0, title='', size=None, buffering=False):
35        """
36        Create a new instance of the ASAPlot plotting class.
37
38        If rows < 1 then a separate call to set_panels() is required to define
39        the panel layout; refer to the doctext for set_panels().
40        """
41        self.is_dead = False
42        self.figure = Figure(figsize=size, facecolor='#ddddee')
43        self.canvas = None
44
45        self.set_title(title)
46        self.subplots = []
47        if rows > 0:
48            self.set_panels(rows, cols)
49
50        # Set matplotlib default colour sequence.
51        self.colormap = "green red black cyan magenta orange blue purple yellow pink".split()
52
53        c = asaprcParams['plotter.colours']
54        if isinstance(c,str) and len(c) > 0:
55            self.colormap = c.split()
56
57        self.lsalias = {"line":  [1,0],
58                        "dashdot": [4,2,1,2],
59                        "dashed" : [4,2,4,2],
60                        "dotted" : [1,2],
61                        "dashdotdot": [4,2,1,2,1,2],
62                        "dashdashdot": [4,2,4,2,1,2]
63                        }
64
65        styles = "line dashed dotted dashdot".split()
66        c = asaprcParams['plotter.linestyles']
67        if isinstance(c,str) and len(c) > 0:
68            styles = c.split()
69        s = []
70        for ls in styles:
71            if self.lsalias.has_key(ls):
72                s.append(self.lsalias.get(ls))
73            else:
74                s.append('-')
75        self.linestyles = s
76
77        self.color = 0;
78        self.linestyle = 0;
79        self.attributes = {}
80        self.loc = 0
81
82        self.buffering = buffering
83
84    def clear(self):
85        """
86        Delete all lines from the plot.  Line numbering will restart from 0.
87        """
88
89        for i in range(len(self.lines)):
90           self.delete(i)
91        self.axes.clear()
92        self.color = 0
93        self.lines = []
94
95    def palette(self, color, colormap=None, linestyle=0, linestyles=None):
96        if colormap:
97            if isinstance(colormap,list):
98                self.colormap = colormap
99            elif isinstance(colormap,str):
100                self.colormap = colormap.split()
101        if 0 <= color < len(self.colormap):
102            self.color = color
103        if linestyles:
104            self.linestyles = []
105            if isinstance(linestyles,list):
106                styles = linestyles
107            elif isinstance(linestyles,str):
108                styles = linestyles.split()
109            for ls in styles:
110                if self.lsalias.has_key(ls):
111                    self.linestyles.append(self.lsalias.get(ls))
112                else:
113                    self.linestyles.append(self.lsalias.get('line'))
114        if 0 <= linestyle < len(self.linestyles):
115            self.linestyle = linestyle
116
117    def delete(self, numbers=None):
118        """
119        Delete the 0-relative line number, default is to delete the last.
120        The remaining lines are NOT renumbered.
121        """
122
123        if numbers is None: numbers = [len(self.lines)-1]
124
125        if not hasattr(numbers, '__iter__'):
126            numbers = [numbers]
127
128        for number in numbers:
129            if 0 <= number < len(self.lines):
130                if self.lines[number] is not None:
131                    for line in self.lines[number]:
132                        line.set_linestyle('None')
133                        self.lines[number] = None
134        self.show()
135
136    def get_line(self):
137        """
138        Get the current default line attributes.
139        """
140        return self.attributes
141
142
143    def hist(self, x=None, y=None, fmt=None, add=None):
144        """
145        Plot a histogram.  N.B. the x values refer to the start of the
146        histogram bin.
147
148        fmt is the line style as in plot().
149        """
150        from numpy import array
151        from numpy.ma import MaskedArray
152        if x is None:
153            if y is None: return
154            x = range(len(y))
155
156        if len(x) != len(y):
157            return
158        l2 = 2*len(x)
159        x2 = range(l2)
160        y2 = range(12)
161        y2 = range(l2)
162        m2 = range(l2)
163        ymsk = None
164        ydat = None
165        if hasattr(y, "raw_mask"):
166            # numpy < 1.1
167            ymsk = y.raw_mask()
168            ydat = y.raw_data()
169        else:
170            ymsk = y.mask
171            ydat = y.data
172        for i in range(l2):
173            x2[i] = x[i/2]
174            m2[i] = ymsk[i/2]
175
176        y2[0] = 0.0
177        for i in range(1,l2):
178            y2[i] = ydat[(i-1)/2]
179
180        self.plot(x2, MaskedArray(y2,mask=m2,copy=0), fmt, add)
181
182
183    def hold(self, hold=True):
184        """
185        Buffer graphics until subsequently released.
186        """
187        self.buffering = hold
188
189
190    def legend(self, loc=None):
191        """
192        Add a legend to the plot.
193
194        Any other value for loc else disables the legend:
195             1: upper right
196             2: upper left
197             3: lower left
198             4: lower right
199             5: right
200             6: center left
201             7: center right
202             8: lower center
203             9: upper center
204            10: center
205
206        """
207        if isinstance(loc, int):
208            self.loc = None
209            if 0 <= loc <= 10: self.loc = loc
210        else:
211            self.loc = None
212        #self.show()
213
214
215    def plot(self, x=None, y=None, fmt=None, add=None):
216        """
217        Plot the next line in the current frame using the current line
218        attributes.  The ASAPlot graphics window will be mapped and raised.
219
220        The argument list works a bit like the matlab plot() function.
221        """
222        if x is None:
223            if y is None: return
224            x = range(len(y))
225
226        elif y is None:
227            y = x
228            x = range(len(y))
229        if fmt is None:
230            line = self.axes.plot(x, y)
231        else:
232            line = self.axes.plot(x, y, fmt)
233
234        # Add to an existing line?
235        i = None
236        if add is None or len(self.lines) < add < 0:
237            # Don't add.
238            self.lines.append(line)
239            i = len(self.lines) - 1
240        else:
241            if add == 0: add = len(self.lines)
242            i = add - 1
243            self.lines[i].extend(line)
244
245        # Set/reset attributes for the line.
246        gotcolour = False
247        for k, v in self.attributes.iteritems():
248            if k == 'color': gotcolour = True
249            for segment in self.lines[i]:
250                getattr(segment, "set_%s"%k)(v)
251
252        if not gotcolour and len(self.colormap):
253            for segment in self.lines[i]:
254                getattr(segment, "set_color")(self.colormap[self.color])
255                if len(self.colormap)  == 1:
256                    getattr(segment, "set_dashes")(self.linestyles[self.linestyle])
257
258            self.color += 1
259            if self.color >= len(self.colormap):
260                self.color = 0
261
262            if len(self.colormap) == 1:
263                self.linestyle += 1
264            if self.linestyle >= len(self.linestyles):
265                self.linestyle = 0
266
267        self.show()
268
269
270    def position(self):
271        """
272        Use the mouse to get a position from a graph.
273        """
274
275        def position_disable(event):
276            self.register('button_press', None)
277            print '%.4f, %.4f' % (event.xdata, event.ydata)
278
279        print 'Press any mouse button...'
280        self.register('button_press', position_disable)
281
282
283    def get_region(self):
284        pos = []
285        print "Please select the bottom/left point"
286        pos.append(self.figure.ginput(n=1, show_clicks=False)[0])
287        print "Please select the top/right point"
288        pos.append(self.figure.ginput(n=1, show_clicks=False)[0])
289        return pos
290
291    def get_point(self):
292        print "Please select the point"
293        pt = self.figure.ginput(n=1, show_clicks=False)
294        if pt:
295            return pt[0]
296        else:
297            return None
298
299    def region(self):
300        """
301        Use the mouse to get a rectangular region from a plot.
302
303        The return value is [x0, y0, x1, y1] in world coordinates.
304        """
305
306        def region_start(event):
307            self.rect = {'x': event.x, 'y': event.y,
308                         'world': [event.xdata, event.ydata,
309                                   event.xdata, event.ydata]}
310            self.register('button_press', None)
311            self.register('motion_notify', region_draw)
312            self.register('button_release', region_disable)
313
314        def region_draw(event):
315            self.figmgr.toolbar.draw_rubberband(event, event.x, event.y,
316                                                self.rect['x'], self.rect['y'])
317
318        def region_disable(event):
319            self.register('motion_notify', None)
320            self.register('button_release', None)
321
322            self.rect['world'][2:4] = [event.xdata, event.ydata]
323            print '(%.2f, %.2f)  (%.2f, %.2f)' % (self.rect['world'][0],
324                self.rect['world'][1], self.rect['world'][2],
325                self.rect['world'][3])
326            self.figmgr.toolbar.release(event)
327
328        self.register('button_press', region_start)
329
330        # This has to be modified to block and return the result (currently
331        # printed by region_disable) when that becomes possible in matplotlib.
332
333        return [0.0, 0.0, 0.0, 0.0]
334
335
336    def register(self, type=None, func=None):
337        """
338        Register, reregister, or deregister events of type 'button_press',
339        'button_release', or 'motion_notify'.
340
341        The specified callback function should have the following signature:
342
343            def func(event)
344
345        where event is an MplEvent instance containing the following data:
346
347            name                # Event name.
348            canvas              # FigureCanvas instance generating the event.
349            x      = None       # x position - pixels from left of canvas.
350            y      = None       # y position - pixels from bottom of canvas.
351            button = None       # Button pressed: None, 1, 2, 3.
352            key    = None       # Key pressed: None, chr(range(255)), shift,
353                                  win, or control
354            inaxes = None       # Axes instance if cursor within axes.
355            xdata  = None       # x world coordinate.
356            ydata  = None       # y world coordinate.
357
358        For example:
359
360            def mouse_move(event):
361                print event.xdata, event.ydata
362
363            a = asaplot()
364            a.register('motion_notify', mouse_move)
365
366        If func is None, the event is deregistered.
367
368        Note that in TkAgg keyboard button presses don't generate an event.
369        """
370
371        if not self.events.has_key(type): return
372
373        if func is None:
374            if self.events[type] is not None:
375                # It's not clear that this does anything.
376                self.canvas.mpl_disconnect(self.events[type])
377                self.events[type] = None
378
379                # It seems to be necessary to return events to the toolbar. <-- Not ture. 2010.Jul.14.kana.
380                #if type == 'motion_notify':
381                #    self.canvas.mpl_connect(type + '_event',
382                #        self.figmgr.toolbar.mouse_move)
383                #elif type == 'button_press':
384                #    self.canvas.mpl_connect(type + '_event',
385                #        self.figmgr.toolbar.press)
386                #elif type == 'button_release':
387                #    self.canvas.mpl_connect(type + '_event',
388                #        self.figmgr.toolbar.release)
389
390        else:
391            self.events[type] = self.canvas.mpl_connect(type + '_event', func)
392
393
394    def release(self):
395        """
396        Release buffered graphics.
397        """
398        self.buffering = False
399        self.show()
400
401
402    def save(self, fname=None, orientation=None, dpi=None, papertype=None):
403        """
404        Save the plot to a file.
405
406        fname is the name of the output file.  The image format is determined
407        from the file suffix; 'png', 'ps', and 'eps' are recognized.  If no
408        file name is specified 'yyyymmdd_hhmmss.png' is created in the current
409        directory.
410        """
411        from asap import rcParams
412        if papertype is None:
413            papertype = rcParams['plotter.papertype']
414        if fname is None:
415            from datetime import datetime
416            dstr = datetime.now().strftime('%Y%m%d_%H%M%S')
417            fname = 'asap'+dstr+'.png'
418
419        d = ['png','.ps','eps', 'svg']
420
421        from os.path import expandvars
422        fname = expandvars(fname)
423
424        if fname[-3:].lower() in d:
425            try:
426                if fname[-3:].lower() == ".ps":
427                    from matplotlib import __version__ as mv
428                    w = self.figure.get_figwidth()
429                    h = self.figure.get_figheight()
430
431                    if orientation is None:
432                        # oriented
433                        if w > h:
434                            orientation = 'landscape'
435                        else:
436                            orientation = 'portrait'
437                    from matplotlib.backends.backend_ps import papersize
438                    pw,ph = papersize[papertype.lower()]
439                    ds = None
440                    if orientation == 'landscape':
441                        ds = min(ph/w, pw/h)
442                    else:
443                        ds = min(pw/w, ph/h)
444                    ow = ds * w
445                    oh = ds * h
446                    self.figure.set_size_inches((ow, oh))
447                    self.figure.savefig(fname, orientation=orientation,
448                                        papertype=papertype.lower())
449                    self.figure.set_size_inches((w, h))
450                    print 'Written file %s' % (fname)
451                else:
452                    if dpi is None:
453                        dpi =150
454                    self.figure.savefig(fname,dpi=dpi)
455                    print 'Written file %s' % (fname)
456            except IOError, msg:
457                #print 'Failed to save %s: Error msg was\n\n%s' % (fname, err)
458                asaplog.post()
459                asaplog.push('Failed to save %s: Error msg was\n\n%s' % (fname, str(msg)))
460                asaplog.post( 'ERROR' )
461                return
462        else:
463            #print "Invalid image type. Valid types are:"
464            #print "'ps', 'eps', 'png'"
465            asaplog.push( "Invalid image type. Valid types are:" )
466            asaplog.push( "'ps', 'eps', 'png', 'svg'" )
467            asaplog.post('WARN')
468
469
470    def set_axes(self, what=None, *args, **kwargs):
471        """
472        Set attributes for the axes by calling the relevant Axes.set_*()
473        method.  Colour translation is done as described in the doctext
474        for palette().
475        """
476
477        if what is None: return
478        if what[-6:] == 'colour': what = what[:-6] + 'color'
479
480        key = "colour"
481        if kwargs.has_key(key):
482            val = kwargs.pop(key)
483            kwargs["color"] = val
484
485        getattr(self.axes, "set_%s"%what)(*args, **kwargs)
486
487        self.show(hardrefresh=False)
488
489
490    def set_figure(self, what=None, *args, **kwargs):
491        """
492        Set attributes for the figure by calling the relevant Figure.set_*()
493        method.  Colour translation is done as described in the doctext
494        for palette().
495        """
496
497        if what is None: return
498        if what[-6:] == 'colour': what = what[:-6] + 'color'
499        #if what[-5:] == 'color' and len(args):
500        #    args = (get_colour(args[0]),)
501
502        newargs = {}
503        for k, v in kwargs.iteritems():
504            k = k.lower()
505            if k == 'colour': k = 'color'
506            newargs[k] = v
507
508        getattr(self.figure, "set_%s"%what)(*args, **newargs)
509        self.show(hardrefresh=False)
510
511
512    def set_limits(self, xlim=None, ylim=None):
513        """
514        Set x-, and y-limits for each subplot.
515
516        xlim = [xmin, xmax] as in axes.set_xlim().
517        ylim = [ymin, ymax] as in axes.set_ylim().
518        """
519        for s in self.subplots:
520            self.axes  = s['axes']
521            self.lines = s['lines']
522            oldxlim =  list(self.axes.get_xlim())
523            oldylim =  list(self.axes.get_ylim())
524            if xlim is not None:
525                for i in range(len(xlim)):
526                    if xlim[i] is not None:
527                        oldxlim[i] = xlim[i]
528            if ylim is not None:
529                for i in range(len(ylim)):
530                    if ylim[i] is not None:
531                        oldylim[i] = ylim[i]
532            self.axes.set_xlim(oldxlim)
533            self.axes.set_ylim(oldylim)
534        return
535
536
537    def set_line(self, number=None, **kwargs):
538        """
539        Set attributes for the specified line, or else the next line(s)
540        to be plotted.
541
542        number is the 0-relative number of a line that has already been
543        plotted.  If no such line exists, attributes are recorded and used
544        for the next line(s) to be plotted.
545
546        Keyword arguments specify Line2D attributes, e.g. color='r'.  Do
547
548            import matplotlib
549            help(matplotlib.lines)
550
551        The set_* methods of class Line2D define the attribute names and
552        values.  For non-US usage, "colour" is recognized as synonymous with
553        "color".
554
555        Set the value to None to delete an attribute.
556
557        Colour translation is done as described in the doctext for palette().
558        """
559
560        redraw = False
561        for k, v in kwargs.iteritems():
562            k = k.lower()
563            if k == 'colour': k = 'color'
564
565            if 0 <= number < len(self.lines):
566                if self.lines[number] is not None:
567                    for line in self.lines[number]:
568                        getattr(line, "set_%s"%k)(v)
569                    redraw = True
570            else:
571                if v is None:
572                    del self.attributes[k]
573                else:
574                    self.attributes[k] = v
575
576        if redraw: self.show(hardrefresh=False)
577
578
579    #def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, ganged=True):
580    def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, layout=None,ganged=True):
581        """
582        Set the panel layout.
583
584        rows and cols, if cols != 0, specify the number of rows and columns in
585        a regular layout.   (Indexing of these panels in matplotlib is row-
586        major, i.e. column varies fastest.)
587
588        cols == 0 is interpreted as a retangular layout that accomodates
589        'rows' panels, e.g. rows == 6, cols == 0 is equivalent to
590        rows == 2, cols == 3.
591
592        0 <= n < rows*cols is interpreted as the 0-relative panel number in
593        the configuration specified by rows and cols to be added to the
594        current figure as its next 0-relative panel number (i).  This allows
595        non-regular panel layouts to be constructed via multiple calls.  Any
596        other value of n clears the plot and produces a rectangular array of
597        empty panels.  The number of these may be limited by nplots.
598        """
599        if n < 0 and len(self.subplots):
600            self.figure.clear()
601            self.set_title()
602
603        if layout:
604            lef, bot, rig, top, wsp, hsp = layout
605            self.figure.subplots_adjust(
606                left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
607            del lef,bot,rig,top,wsp,hsp
608
609        if rows < 1: rows = 1
610
611        if cols <= 0:
612            i = int(sqrt(rows))
613            if i*i < rows: i += 1
614            cols = i
615
616            if i*(i-1) >= rows: i -= 1
617            rows = i
618
619        if 0 <= n < rows*cols:
620            i = len(self.subplots)
621
622            self.subplots.append({})
623
624            self.subplots[i]['axes']  = self.figure.add_subplot(rows,
625                                            cols, n+1)
626            self.subplots[i]['lines'] = []
627
628            if i == 0: self.subplot(0)
629
630            self.rows = 0
631            self.cols = 0
632
633        else:
634            self.subplots = []
635
636            if nplots < 1 or rows*cols < nplots:
637                nplots = rows*cols
638            if ganged:
639                hsp,wsp = None,None
640                if rows > 1: hsp = 0.0001
641                if cols > 1: wsp = 0.0001
642                self.figure.subplots_adjust(wspace=wsp,hspace=hsp)
643            for i in range(nplots):
644                self.subplots.append({})
645                self.subplots[i]['lines'] = []
646                if not ganged:
647                    self.subplots[i]['axes'] = self.figure.add_subplot(rows,
648                                                cols, i+1)
649                    if asaprcParams['plotter.axesformatting'] != 'mpl':
650                        self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
651                else:
652                    if i == 0:
653                        self.subplots[i]['axes'] = self.figure.add_subplot(rows,
654                                                cols, i+1)
655                        if asaprcParams['plotter.axesformatting'] != 'mpl':
656
657                            self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
658                    else:
659                        self.subplots[i]['axes'] = self.figure.add_subplot(rows,
660                                                cols, i+1,
661                                                sharex=self.subplots[0]['axes'],
662                                                sharey=self.subplots[0]['axes'])
663
664                    # Suppress tick labelling for interior subplots.
665                    if i <= (rows-1)*cols - 1:
666                        if i+cols < nplots:
667                            # Suppress x-labels for frames width
668                            # adjacent frames
669                            for tick in self.subplots[i]['axes'].xaxis.majorTicks:
670                                tick.label1On = False
671                            self.subplots[i]['axes'].xaxis.label.set_visible(False)
672                    if i%cols:
673                        # Suppress y-labels for frames not in the left column.
674                        for tick in self.subplots[i]['axes'].yaxis.majorTicks:
675                            tick.label1On = False
676                        self.subplots[i]['axes'].yaxis.label.set_visible(False)
677                    # disable the first tick of [1:ncol-1] of the last row
678                    #if i+1 < nplots:
679                    #    self.subplots[i]['axes'].xaxis.majorTicks[0].label1On = False
680                self.rows = rows
681                self.cols = cols
682            self.subplot(0)
683        del rows,cols,n,nplots,layout,ganged,i
684
685    def tidy(self):
686        # this needs to be exceuted after the first "refresh"
687        nplots = len(self.subplots)
688        if nplots == 1: return
689        for i in xrange(nplots):
690            ax = self.subplots[i]['axes']
691            if i%self.cols:
692                ax.xaxis.majorTicks[0].label1On = False
693            else:
694                if i != 0:
695                    ax.yaxis.majorTicks[-1].label1On = False
696
697
698    def set_title(self, title=None):
699        """
700        Set the title of the plot window.  Use the previous title if title is
701        omitted.
702        """
703        if title is not None:
704            self.title = title
705
706        self.figure.text(0.5, 0.95, self.title, horizontalalignment='center')
707
708
709    def show(self, hardrefresh=True):
710        """
711        Show graphics dependent on the current buffering state.
712        """
713        if not hardrefresh: return
714        if not self.buffering:
715            if self.loc is not None:
716                for sp in self.subplots:
717                    lines  = []
718                    labels = []
719                    i = 0
720                    for line in sp['lines']:
721                        i += 1
722                        if line is not None:
723                            lines.append(line[0])
724                            lbl = line[0].get_label()
725                            if lbl == '':
726                                lbl = str(i)
727                            labels.append(lbl)
728
729                    if len(lines):
730                        fp = FP(size=rcParams['legend.fontsize'])
731                        fsz = fp.get_size_in_points() - len(lines)
732                        fp.set_size(max(fsz,6))
733                        sp['axes'].legend(tuple(lines), tuple(labels),
734                                          self.loc, prop=fp)
735                    else:
736                        sp['axes'].legend((' '))
737
738            from matplotlib.artist import setp
739            fpx = FP(size=rcParams['xtick.labelsize'])
740            xts = fpx.get_size_in_points()- (self.cols)/2
741            fpy = FP(size=rcParams['ytick.labelsize'])
742            yts = fpy.get_size_in_points() - (self.rows)/2
743            fpa = FP(size=rcParams['axes.labelsize'])
744            fpat = FP(size=rcParams['axes.titlesize'])
745            axsize =  fpa.get_size_in_points()
746            tsize =  fpat.get_size_in_points()-(self.cols)/2
747            for sp in self.subplots:
748                ax = sp['axes']
749                ax.title.set_size(tsize)
750                setp(ax.get_xticklabels(), fontsize=xts)
751                setp(ax.get_yticklabels(), fontsize=yts)
752                off = 0
753                if self.cols > 1: off = self.cols
754                ax.xaxis.label.set_size(axsize-off)
755                off = 0
756                if self.rows > 1: off = self.rows
757                ax.yaxis.label.set_size(axsize-off)
758
759    def subplot(self, i=None, inc=None):
760        """
761        Set the subplot to the 0-relative panel number as defined by one or
762        more invokations of set_panels().
763        """
764        l = len(self.subplots)
765        if l:
766            if i is not None:
767                self.i = i
768
769            if inc is not None:
770                self.i += inc
771
772            self.i %= l
773            self.axes  = self.subplots[self.i]['axes']
774            self.lines = self.subplots[self.i]['lines']
775
776    def text(self, *args, **kwargs):
777        """
778        Add text to the figure.
779        """
780        self.figure.text(*args, **kwargs)
781        self.show()
782
783    def vline_with_label(self, x, y, label,
784                         location='bottom', rotate=0.0, **kwargs):
785        """
786        Plot a vertical line with label.
787        It takes "world" values fo x and y.
788        """
789        ax = self.axes
790        # need this to suppress autoscaling during this function
791        self.axes.set_autoscale_on(False)
792        ymin = 0.0
793        ymax = 1.0
794        valign = 'center'
795        if location.lower() == 'top':
796            y = max(0.0, y)
797        elif location.lower() == 'bottom':
798            y = min(0.0, y)
799        lbloffset = 0.06
800        # a rough estimate for the bb of the text
801        if rotate > 0.0: lbloffset = 0.03*len(label)
802        peakoffset = 0.01
803        xy = None
804        xy0 = None
805        # matplotlib api change 0.98 is using transform now
806        if hasattr(ax.transData, "inverse_xy_tup"):
807            # get relative coords
808            xy0 = ax.transData.xy_tup((x,y))
809            xy = ax.transAxes.inverse_xy_tup(xy0)
810        else:
811            xy0 = ax.transData.transform((x,y))
812            # get relative coords
813            xy = ax.transAxes.inverted().transform(xy0)
814        if location.lower() == 'top':
815            ymax = 1.0-lbloffset
816            ymin = xy[1]+peakoffset
817            valign = 'bottom'
818            ylbl = ymax+0.01
819        elif location.lower() == 'bottom':
820            ymin = lbloffset
821            ymax = xy[1]-peakoffset
822            valign = 'top'
823            ylbl = ymin-0.01
824        trans = blended_transform_factory(ax.transData, ax.transAxes)
825        l = ax.axvline(x, ymin, ymax, color='black', **kwargs)
826        t = ax.text(x, ylbl ,label, verticalalignment=valign,
827                                    horizontalalignment='center',
828                    rotation=rotate,transform = trans)
829        self.axes.set_autoscale_on(True)
Note: See TracBrowser for help on using the repository browser.