source: trunk/python/asaplotbase.py@ 1547

Last change on this file since 1547 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.