source: trunk/python/asaplotbase.py @ 1479

Last change on this file since 1479 was 1479, checked in by Malte Marquarding, 15 years ago

fix some matplotlib deprecation warnings

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