source: trunk/python/asaplotbase.py @ 1535

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

Fix for Ticket #153; matplotlib changed the api in 0.98. Using conditional to test for this

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.3 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                    if asaprcParams['plotter.xaxisformatting'] == 'mpl':
623                        self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
624                else:
625                    if i == 0:
626                        self.subplots[i]['axes'] = self.figure.add_subplot(rows,
627                                                cols, i+1)
628                        if asaprcParams['plotter.xaxisformatting'] != 'mpl':
629                           
630                            self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
631                    else:
632                        self.subplots[i]['axes'] = self.figure.add_subplot(rows,
633                                                cols, i+1,
634                                                sharex=self.subplots[0]['axes'],
635                                                sharey=self.subplots[0]['axes'])
636
637                    # Suppress tick labelling for interior subplots.
638                    if i <= (rows-1)*cols - 1:
639                        if i+cols < nplots:
640                            # Suppress x-labels for frames width
641                            # adjacent frames
642                            for tick in self.subplots[i]['axes'].xaxis.majorTicks:
643                                tick.label1On = False
644                            self.subplots[i]['axes'].xaxis.label.set_visible(False)
645                    if i%cols:
646                        # Suppress y-labels for frames not in the left column.
647                        for tick in self.subplots[i]['axes'].yaxis.majorTicks:
648                            tick.label1On = False
649                        self.subplots[i]['axes'].yaxis.label.set_visible(False)
650                    # disable the first tick of [1:ncol-1] of the last row
651                    #if i+1 < nplots:
652                    #    self.subplots[i]['axes'].xaxis.majorTicks[0].label1On = False
653                self.rows = rows
654                self.cols = cols
655            self.subplot(0)
656
657    def tidy(self):
658        # this needs to be exceuted after the first "refresh"
659        nplots = len(self.subplots)
660        if nplots == 1: return
661        for i in xrange(nplots):
662            ax = self.subplots[i]['axes']
663            if i%self.cols:
664                ax.xaxis.majorTicks[0].label1On = False
665            else:
666                if i != 0:
667                    ax.yaxis.majorTicks[-1].label1On = False
668
669
670    def set_title(self, title=None):
671        """
672        Set the title of the plot window.  Use the previous title if title is
673        omitted.
674        """
675        if title is not None:
676            self.title = title
677
678        self.figure.text(0.5, 0.95, self.title, horizontalalignment='center')
679
680
681    def show(self, hardrefresh=True):
682        """
683        Show graphics dependent on the current buffering state.
684        """
685        if not hardrefresh: return
686        if not self.buffering:
687            if self.loc is not None:
688                for sp in self.subplots:
689                    lines  = []
690                    labels = []
691                    i = 0
692                    for line in sp['lines']:
693                        i += 1
694                        if line is not None:
695                            lines.append(line[0])
696                            lbl = line[0].get_label()
697                            if lbl == '':
698                                lbl = str(i)
699                            labels.append(lbl)
700
701                    if len(lines):
702                        fp = FP(size=rcParams['legend.fontsize'])
703                        fsz = fp.get_size_in_points() - len(lines)
704                        fp.set_size(max(fsz,6))
705                        sp['axes'].legend(tuple(lines), tuple(labels),
706                                          self.loc, prop=fp)
707                    else:
708                        sp['axes'].legend((' '))
709
710            from matplotlib.artist import setp
711            fp = FP(size=rcParams['xtick.labelsize'])
712            xts = fp.get_size_in_points()- (self.cols)/2
713            fp = FP(size=rcParams['ytick.labelsize'])
714            yts = fp.get_size_in_points() - (self.rows)/2
715            for sp in self.subplots:
716                ax = sp['axes']
717                s = ax.title.get_size()
718                tsize = s-(self.cols+self.rows)
719                ax.title.set_size(tsize)
720                fp = FP(size=rcParams['axes.labelsize'])
721                setp(ax.get_xticklabels(), fontsize=xts)
722                setp(ax.get_yticklabels(), fontsize=yts)
723                origx =  fp.get_size_in_points()
724                origy = origx
725                off = 0
726                if self.cols > 1: off = self.cols
727                xfsize = origx-off
728                ax.xaxis.label.set_size(xfsize)
729                off = 0
730                if self.rows > 1: off = self.rows
731                yfsize = origy-off
732                ax.yaxis.label.set_size(yfsize)
733
734    def subplot(self, i=None, inc=None):
735        """
736        Set the subplot to the 0-relative panel number as defined by one or
737        more invokations of set_panels().
738        """
739        l = len(self.subplots)
740        if l:
741            if i is not None:
742                self.i = i
743
744            if inc is not None:
745                self.i += inc
746
747            self.i %= l
748            self.axes  = self.subplots[self.i]['axes']
749            self.lines = self.subplots[self.i]['lines']
750
751    def text(self, *args, **kwargs):
752        """
753        Add text to the figure.
754        """
755        self.figure.text(*args, **kwargs)
756        self.show()
757
758    def vline_with_label(self, x, y, label,
759                         location='bottom', rotate=0.0, **kwargs):
760        """
761        Plot a vertical line with label.
762        It takes "world" values fo x and y.
763        """
764        ax = self.axes
765        # need this to suppress autoscaling during this function
766        self.axes.set_autoscale_on(False)
767        ymin = 0.0
768        ymax = 1.0
769        valign = 'center'
770        if location.lower() == 'top':
771            y = max(0.0, y)
772        elif location.lower() == 'bottom':
773            y = min(0.0, y)
774        lbloffset = 0.06
775        # a rough estimate for the bb of the text
776        if rotate > 0.0: lbloffset = 0.03*len(label)
777        peakoffset = 0.01
778        xy = None
779        xy0 = None
780        # matplotlib api change 0.98 is using transform now
781        if hasattr(ax.transData, "inverse_xy_tup"):
782            # get relative coords
783            xy0 = ax.transData.xy_tup((x,y))
784            xy = ax.transAxes.inverse_xy_tup(xy0)
785        else:
786            xy0 = ax.transData.transform((x,y))
787            # get relative coords
788            xy = ax.transAxes.inverted().transform(xy0)
789        if location.lower() == 'top':
790            ymax = 1.0-lbloffset
791            ymin = xy[1]+peakoffset
792            valign = 'bottom'
793            ylbl = ymax+0.01
794        elif location.lower() == 'bottom':
795            ymin = lbloffset
796            ymax = xy[1]-peakoffset
797            valign = 'top'
798            ylbl = ymin-0.01
799        trans = blended_transform_factory(ax.transData, ax.transAxes)
800        l = ax.axvline(x, ymin, ymax, color='black', **kwargs)
801        t = ax.text(x, ylbl ,label, verticalalignment=valign,
802                                    horizontalalignment='center',
803                    rotation=rotate,transform = trans)
804        self.axes.set_autoscale_on(True)
Note: See TracBrowser for help on using the repository browser.