source: trunk/python/asaplotbase.py@ 1527

Last change on this file since 1527 was 1513, checked in by Malte Marquarding, 16 years ago

added rc parameter tp control ax-axis formatting

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.9 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 xy0 = ax.transData.xy_tup((x,y))
779 # get relative coords
780 xy = ax.transAxes.inverse_xy_tup(xy0)
781 if location.lower() == 'top':
782 ymax = 1.0-lbloffset
783 ymin = xy[1]+peakoffset
784 valign = 'bottom'
785 ylbl = ymax+0.01
786 elif location.lower() == 'bottom':
787 ymin = lbloffset
788 ymax = xy[1]-peakoffset
789 valign = 'top'
790 ylbl = ymin-0.01
791 trans = blended_transform_factory(ax.transData, ax.transAxes)
792 l = ax.axvline(x, ymin, ymax, color='black', **kwargs)
793 t = ax.text(x, ylbl ,label, verticalalignment=valign,
794 horizontalalignment='center',
795 rotation=rotate,transform = trans)
796 self.axes.set_autoscale_on(True)
Note: See TracBrowser for help on using the repository browser.