source: trunk/python/asaplotbase.py @ 1546

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

support cursor read out via matplotlib.figure.ginput

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.7 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 get_region(self):
282        pos = []
283        print "Please select the bottom/left point"
284        pos.append(self.figure.ginput(n=1, show_clicks=False)[0])
285        print "Please select the top/right point"
286        pos.append(self.figure.ginput(n=1, show_clicks=False)[0])
287        return pos
288
289    def get_point(self):
290        print "Please select the point"
291        return self.figure.ginput(n=1, show_clicks=False)[0]
292
293    def region(self):
294        """
295        Use the mouse to get a rectangular region from a plot.
296
297        The return value is [x0, y0, x1, y1] in world coordinates.
298        """
299
300        def region_start(event):
301            height = self.canvas.figure.bbox.height()
302            self.rect = {'fig': None, 'height': height,
303                         'x': event.x, 'y': height - event.y,
304                         'world': [event.xdata, event.ydata,
305                                   event.xdata, event.ydata]}
306            self.register('button_press', None)
307            self.register('motion_notify', region_draw)
308            self.register('button_release', region_disable)
309
310        def region_draw(event):
311            self.canvas._tkcanvas.delete(self.rect['fig'])
312            self.rect['fig'] = self.canvas._tkcanvas.create_rectangle(
313                                self.rect['x'], self.rect['y'],
314                                event.x, self.rect['height'] - event.y)
315
316        def region_disable(event):
317            self.register('motion_notify', None)
318            self.register('button_release', None)
319
320            self.canvas._tkcanvas.delete(self.rect['fig'])
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
327        self.register('button_press', region_start)
328
329        # This has to be modified to block and return the result (currently
330        # printed by region_disable) when that becomes possible in matplotlib.
331
332        return [0.0, 0.0, 0.0, 0.0]
333
334
335    def register(self, type=None, func=None):
336        """
337        Register, reregister, or deregister events of type 'button_press',
338        'button_release', or 'motion_notify'.
339
340        The specified callback function should have the following signature:
341
342            def func(event)
343
344        where event is an MplEvent instance containing the following data:
345
346            name                # Event name.
347            canvas              # FigureCanvas instance generating the event.
348            x      = None       # x position - pixels from left of canvas.
349            y      = None       # y position - pixels from bottom of canvas.
350            button = None       # Button pressed: None, 1, 2, 3.
351            key    = None       # Key pressed: None, chr(range(255)), shift,
352                                  win, or control
353            inaxes = None       # Axes instance if cursor within axes.
354            xdata  = None       # x world coordinate.
355            ydata  = None       # y world coordinate.
356
357        For example:
358
359            def mouse_move(event):
360                print event.xdata, event.ydata
361
362            a = asaplot()
363            a.register('motion_notify', mouse_move)
364
365        If func is None, the event is deregistered.
366
367        Note that in TkAgg keyboard button presses don't generate an event.
368        """
369
370        if not self.events.has_key(type): return
371
372        if func is None:
373            if self.events[type] is not None:
374                # It's not clear that this does anything.
375                self.canvas.mpl_disconnect(self.events[type])
376                self.events[type] = None
377
378                # It seems to be necessary to return events to the toolbar.
379                if type == 'motion_notify':
380                    self.canvas.mpl_connect(type + '_event',
381                        self.figmgr.toolbar.mouse_move)
382                elif type == 'button_press':
383                    self.canvas.mpl_connect(type + '_event',
384                        self.figmgr.toolbar.press)
385                elif type == 'button_release':
386                    self.canvas.mpl_connect(type + '_event',
387                        self.figmgr.toolbar.release)
388
389        else:
390            self.events[type] = self.canvas.mpl_connect(type + '_event', func)
391
392
393    def release(self):
394        """
395        Release buffered graphics.
396        """
397        self.buffering = False
398        self.show()
399
400
401    def save(self, fname=None, orientation=None, dpi=None, papertype=None):
402        """
403        Save the plot to a file.
404
405        fname is the name of the output file.  The image format is determined
406        from the file suffix; 'png', 'ps', and 'eps' are recognized.  If no
407        file name is specified 'yyyymmdd_hhmmss.png' is created in the current
408        directory.
409        """
410        from asap import rcParams
411        if papertype is None:
412            papertype = rcParams['plotter.papertype']
413        if fname is None:
414            from datetime import datetime
415            dstr = datetime.now().strftime('%Y%m%d_%H%M%S')
416            fname = 'asap'+dstr+'.png'
417
418        d = ['png','.ps','eps']
419
420        from os.path import expandvars
421        fname = expandvars(fname)
422
423        if fname[-3:].lower() in d:
424            try:
425                if fname[-3:].lower() == ".ps":
426                    from matplotlib import __version__ as mv
427                    w = self.figure.get_figwidth()
428                    h = self.figure.get_figheight()
429
430                    if orientation is None:
431                        # oriented
432                        if w > h:
433                            orientation = 'landscape'
434                        else:
435                            orientation = 'portrait'
436                    from matplotlib.backends.backend_ps import papersize
437                    pw,ph = papersize[papertype.lower()]
438                    ds = None
439                    if orientation == 'landscape':
440                        ds = min(ph/w, pw/h)
441                    else:
442                        ds = min(pw/w, ph/h)
443                    ow = ds * w
444                    oh = ds * h
445                    self.figure.set_size_inches((ow, oh))
446                    self.figure.savefig(fname, orientation=orientation,
447                                        papertype=papertype.lower())
448                    self.figure.set_size_inches((w, h))
449                    print 'Written file %s' % (fname)
450                else:
451                    if dpi is None:
452                        dpi =150
453                    self.figure.savefig(fname,dpi=dpi)
454                    print 'Written file %s' % (fname)
455            except IOError, msg:
456                print 'Failed to save %s: Error msg was\n\n%s' % (fname, err)
457                return
458        else:
459            print "Invalid image type. Valid types are:"
460            print "'ps', 'eps', 'png'"
461
462
463    def set_axes(self, what=None, *args, **kwargs):
464        """
465        Set attributes for the axes by calling the relevant Axes.set_*()
466        method.  Colour translation is done as described in the doctext
467        for palette().
468        """
469
470        if what is None: return
471        if what[-6:] == 'colour': what = what[:-6] + 'color'
472
473        key = "colour"
474        if kwargs.has_key(key):
475            val = kwargs.pop(key)
476            kwargs["color"] = val
477
478        getattr(self.axes, "set_%s"%what)(*args, **kwargs)
479
480        self.show(hardrefresh=False)
481
482
483    def set_figure(self, what=None, *args, **kwargs):
484        """
485        Set attributes for the figure by calling the relevant Figure.set_*()
486        method.  Colour translation is done as described in the doctext
487        for palette().
488        """
489
490        if what is None: return
491        if what[-6:] == 'colour': what = what[:-6] + 'color'
492        #if what[-5:] == 'color' and len(args):
493        #    args = (get_colour(args[0]),)
494
495        newargs = {}
496        for k, v in kwargs.iteritems():
497            k = k.lower()
498            if k == 'colour': k = 'color'
499            newargs[k] = v
500
501        getattr(self.figure, "set_%s"%what)(*args, **newargs)
502        self.show(hardrefresh=False)
503
504
505    def set_limits(self, xlim=None, ylim=None):
506        """
507        Set x-, and y-limits for each subplot.
508
509        xlim = [xmin, xmax] as in axes.set_xlim().
510        ylim = [ymin, ymax] as in axes.set_ylim().
511        """
512        for s in self.subplots:
513            self.axes  = s['axes']
514            self.lines = s['lines']
515            oldxlim =  list(self.axes.get_xlim())
516            oldylim =  list(self.axes.get_ylim())
517            if xlim is not None:
518                for i in range(len(xlim)):
519                    if xlim[i] is not None:
520                        oldxlim[i] = xlim[i]
521            if ylim is not None:
522                for i in range(len(ylim)):
523                    if ylim[i] is not None:
524                        oldylim[i] = ylim[i]
525            self.axes.set_xlim(oldxlim)
526            self.axes.set_ylim(oldylim)
527        return
528
529
530    def set_line(self, number=None, **kwargs):
531        """
532        Set attributes for the specified line, or else the next line(s)
533        to be plotted.
534
535        number is the 0-relative number of a line that has already been
536        plotted.  If no such line exists, attributes are recorded and used
537        for the next line(s) to be plotted.
538
539        Keyword arguments specify Line2D attributes, e.g. color='r'.  Do
540
541            import matplotlib
542            help(matplotlib.lines)
543
544        The set_* methods of class Line2D define the attribute names and
545        values.  For non-US usage, "colour" is recognized as synonymous with
546        "color".
547
548        Set the value to None to delete an attribute.
549
550        Colour translation is done as described in the doctext for palette().
551        """
552
553        redraw = False
554        for k, v in kwargs.iteritems():
555            k = k.lower()
556            if k == 'colour': k = 'color'
557
558            if 0 <= number < len(self.lines):
559                if self.lines[number] is not None:
560                    for line in self.lines[number]:
561                        getattr(line, "set_%s"%k)(v)
562                    redraw = True
563            else:
564                if v is None:
565                    del self.attributes[k]
566                else:
567                    self.attributes[k] = v
568
569        if redraw: self.show(hardrefresh=False)
570
571
572    def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, ganged=True):
573        """
574        Set the panel layout.
575
576        rows and cols, if cols != 0, specify the number of rows and columns in
577        a regular layout.   (Indexing of these panels in matplotlib is row-
578        major, i.e. column varies fastest.)
579
580        cols == 0 is interpreted as a retangular layout that accomodates
581        'rows' panels, e.g. rows == 6, cols == 0 is equivalent to
582        rows == 2, cols == 3.
583
584        0 <= n < rows*cols is interpreted as the 0-relative panel number in
585        the configuration specified by rows and cols to be added to the
586        current figure as its next 0-relative panel number (i).  This allows
587        non-regular panel layouts to be constructed via multiple calls.  Any
588        other value of n clears the plot and produces a rectangular array of
589        empty panels.  The number of these may be limited by nplots.
590        """
591        if n < 0 and len(self.subplots):
592            self.figure.clear()
593            self.set_title()
594
595        if rows < 1: rows = 1
596
597        if cols <= 0:
598            i = int(sqrt(rows))
599            if i*i < rows: i += 1
600            cols = i
601
602            if i*(i-1) >= rows: i -= 1
603            rows = i
604
605        if 0 <= n < rows*cols:
606            i = len(self.subplots)
607            self.subplots.append({})
608
609            self.subplots[i]['axes']  = self.figure.add_subplot(rows,
610                                            cols, n+1)
611            self.subplots[i]['lines'] = []
612
613            if i == 0: self.subplot(0)
614
615            self.rows = 0
616            self.cols = 0
617
618        else:
619            self.subplots = []
620
621            if nplots < 1 or rows*cols < nplots:
622                nplots = rows*cols
623            if ganged:
624                hsp,wsp = None,None
625                if rows > 1: hsp = 0.0001
626                if cols > 1: wsp = 0.0001
627                self.figure.subplots_adjust(wspace=wsp,hspace=hsp)
628            for i in range(nplots):
629                self.subplots.append({})
630                self.subplots[i]['lines'] = []
631                if not ganged:
632                    self.subplots[i]['axes'] = self.figure.add_subplot(rows,
633                                                cols, i+1)
634                    if asaprcParams['plotter.xaxisformatting'] == 'mpl':
635                        self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
636                else:
637                    if i == 0:
638                        self.subplots[i]['axes'] = self.figure.add_subplot(rows,
639                                                cols, i+1)
640                        if asaprcParams['plotter.xaxisformatting'] != 'mpl':
641                           
642                            self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
643                    else:
644                        self.subplots[i]['axes'] = self.figure.add_subplot(rows,
645                                                cols, i+1,
646                                                sharex=self.subplots[0]['axes'],
647                                                sharey=self.subplots[0]['axes'])
648
649                    # Suppress tick labelling for interior subplots.
650                    if i <= (rows-1)*cols - 1:
651                        if i+cols < nplots:
652                            # Suppress x-labels for frames width
653                            # adjacent frames
654                            for tick in self.subplots[i]['axes'].xaxis.majorTicks:
655                                tick.label1On = False
656                            self.subplots[i]['axes'].xaxis.label.set_visible(False)
657                    if i%cols:
658                        # Suppress y-labels for frames not in the left column.
659                        for tick in self.subplots[i]['axes'].yaxis.majorTicks:
660                            tick.label1On = False
661                        self.subplots[i]['axes'].yaxis.label.set_visible(False)
662                    # disable the first tick of [1:ncol-1] of the last row
663                    #if i+1 < nplots:
664                    #    self.subplots[i]['axes'].xaxis.majorTicks[0].label1On = False
665                self.rows = rows
666                self.cols = cols
667            self.subplot(0)
668
669    def tidy(self):
670        # this needs to be exceuted after the first "refresh"
671        nplots = len(self.subplots)
672        if nplots == 1: return
673        for i in xrange(nplots):
674            ax = self.subplots[i]['axes']
675            if i%self.cols:
676                ax.xaxis.majorTicks[0].label1On = False
677            else:
678                if i != 0:
679                    ax.yaxis.majorTicks[-1].label1On = False
680
681
682    def set_title(self, title=None):
683        """
684        Set the title of the plot window.  Use the previous title if title is
685        omitted.
686        """
687        if title is not None:
688            self.title = title
689
690        self.figure.text(0.5, 0.95, self.title, horizontalalignment='center')
691
692
693    def show(self, hardrefresh=True):
694        """
695        Show graphics dependent on the current buffering state.
696        """
697        if not hardrefresh: return
698        if not self.buffering:
699            if self.loc is not None:
700                for sp in self.subplots:
701                    lines  = []
702                    labels = []
703                    i = 0
704                    for line in sp['lines']:
705                        i += 1
706                        if line is not None:
707                            lines.append(line[0])
708                            lbl = line[0].get_label()
709                            if lbl == '':
710                                lbl = str(i)
711                            labels.append(lbl)
712
713                    if len(lines):
714                        fp = FP(size=rcParams['legend.fontsize'])
715                        fsz = fp.get_size_in_points() - len(lines)
716                        fp.set_size(max(fsz,6))
717                        sp['axes'].legend(tuple(lines), tuple(labels),
718                                          self.loc, prop=fp)
719                    else:
720                        sp['axes'].legend((' '))
721
722            from matplotlib.artist import setp
723            fp = FP(size=rcParams['xtick.labelsize'])
724            xts = fp.get_size_in_points()- (self.cols)/2
725            fp = FP(size=rcParams['ytick.labelsize'])
726            yts = fp.get_size_in_points() - (self.rows)/2
727            for sp in self.subplots:
728                ax = sp['axes']
729                s = ax.title.get_size()
730                tsize = s-(self.cols+self.rows)
731                ax.title.set_size(tsize)
732                fp = FP(size=rcParams['axes.labelsize'])
733                setp(ax.get_xticklabels(), fontsize=xts)
734                setp(ax.get_yticklabels(), fontsize=yts)
735                origx =  fp.get_size_in_points()
736                origy = origx
737                off = 0
738                if self.cols > 1: off = self.cols
739                xfsize = origx-off
740                ax.xaxis.label.set_size(xfsize)
741                off = 0
742                if self.rows > 1: off = self.rows
743                yfsize = origy-off
744                ax.yaxis.label.set_size(yfsize)
745
746    def subplot(self, i=None, inc=None):
747        """
748        Set the subplot to the 0-relative panel number as defined by one or
749        more invokations of set_panels().
750        """
751        l = len(self.subplots)
752        if l:
753            if i is not None:
754                self.i = i
755
756            if inc is not None:
757                self.i += inc
758
759            self.i %= l
760            self.axes  = self.subplots[self.i]['axes']
761            self.lines = self.subplots[self.i]['lines']
762
763    def text(self, *args, **kwargs):
764        """
765        Add text to the figure.
766        """
767        self.figure.text(*args, **kwargs)
768        self.show()
769
770    def vline_with_label(self, x, y, label,
771                         location='bottom', rotate=0.0, **kwargs):
772        """
773        Plot a vertical line with label.
774        It takes "world" values fo x and y.
775        """
776        ax = self.axes
777        # need this to suppress autoscaling during this function
778        self.axes.set_autoscale_on(False)
779        ymin = 0.0
780        ymax = 1.0
781        valign = 'center'
782        if location.lower() == 'top':
783            y = max(0.0, y)
784        elif location.lower() == 'bottom':
785            y = min(0.0, y)
786        lbloffset = 0.06
787        # a rough estimate for the bb of the text
788        if rotate > 0.0: lbloffset = 0.03*len(label)
789        peakoffset = 0.01
790        xy = None
791        xy0 = None
792        # matplotlib api change 0.98 is using transform now
793        if hasattr(ax.transData, "inverse_xy_tup"):
794            # get relative coords
795            xy0 = ax.transData.xy_tup((x,y))
796            xy = ax.transAxes.inverse_xy_tup(xy0)
797        else:
798            xy0 = ax.transData.transform((x,y))
799            # get relative coords
800            xy = ax.transAxes.inverted().transform(xy0)
801        if location.lower() == 'top':
802            ymax = 1.0-lbloffset
803            ymin = xy[1]+peakoffset
804            valign = 'bottom'
805            ylbl = ymax+0.01
806        elif location.lower() == 'bottom':
807            ymin = lbloffset
808            ymax = xy[1]-peakoffset
809            valign = 'top'
810            ylbl = ymin-0.01
811        trans = blended_transform_factory(ax.transData, ax.transAxes)
812        l = ax.axvline(x, ymin, ymax, color='black', **kwargs)
813        t = ax.text(x, ylbl ,label, verticalalignment=valign,
814                                    horizontalalignment='center',
815                    rotation=rotate,transform = trans)
816        self.axes.set_autoscale_on(True)
Note: See TracBrowser for help on using the repository browser.