source: trunk/python/asaplotbase.py@ 1457

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

Handle api change in matplotlib-0.98

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