source: tags/Release2.1.0b/python/asaplotbase.py @ 1240

Last change on this file since 1240 was 1240, checked in by mar637, 18 years ago

Ticket #74 - changed xaxis tick labelling to use OldScalarFormatter?

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