source: branches/alma/python/asaplotbase.py@ 1523

Last change on this file since 1523 was 1446, checked in by TakTsutsumi, 16 years ago

Merged recent updates (since 2007) from nrao-asap

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.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
17from matplotlib.transforms import blend_xy_sep_transform
18
19if int(matplotlib.__version__.split(".")[1]) < 87:
20 print "Warning: matplotlib version < 0.87. This might cause errors. Please upgrade."
21
22#class MyFormatter(OldScalarFormatter):
23# def __call__(self, x, pos=None):
24# last = len(self.locs)-2
25# if pos==0:
26# return ''
27# else: return OldScalarFormatter.__call__(self, x, pos)
28
29class asaplotbase:
30 """
31 ASAP plotting base class based on matplotlib.
32 """
33
34 def __init__(self, rows=1, cols=0, title='', size=(8,6), buffering=False):
35 """
36 Create a new instance of the ASAPlot plotting class.
37
38 If rows < 1 then a separate call to set_panels() is required to define
39 the panel layout; refer to the doctext for set_panels().
40 """
41 self.is_dead = False
42 self.figure = Figure(figsize=size, facecolor='#ddddee')
43 self.canvas = None
44
45 self.set_title(title)
46 self.subplots = []
47 if rows > 0:
48 self.set_panels(rows, cols)
49
50 # Set matplotlib default colour sequence.
51 self.colormap = "green red black cyan magenta orange blue purple yellow pink".split()
52
53 c = asaprcParams['plotter.colours']
54 if isinstance(c,str) and len(c) > 0:
55 self.colormap = c.split()
56
57 self.lsalias = {"line": [1,0],
58 "dashdot": [4,2,1,2],
59 "dashed" : [4,2,4,2],
60 "dotted" : [1,2],
61 "dashdotdot": [4,2,1,2,1,2],
62 "dashdashdot": [4,2,4,2,1,2]
63 }
64
65 styles = "line dashed dotted dashdot".split()
66 c = asaprcParams['plotter.linestyles']
67 if isinstance(c,str) and len(c) > 0:
68 styles = c.split()
69 s = []
70 for ls in styles:
71 if self.lsalias.has_key(ls):
72 s.append(self.lsalias.get(ls))
73 else:
74 s.append('-')
75 self.linestyles = s
76
77 self.color = 0;
78 self.linestyle = 0;
79 self.attributes = {}
80 self.loc = 0
81
82 self.buffering = buffering
83
84 def clear(self):
85 """
86 Delete all lines from the plot. Line numbering will restart from 0.
87 """
88
89 for i in range(len(self.lines)):
90 self.delete(i)
91 self.axes.clear()
92 self.color = 0
93 self.lines = []
94
95 def palette(self, color, colormap=None, linestyle=0, linestyles=None):
96 if colormap:
97 if isinstance(colormap,list):
98 self.colormap = colormap
99 elif isinstance(colormap,str):
100 self.colormap = colormap.split()
101 if 0 <= color < len(self.colormap):
102 self.color = color
103 if linestyles:
104 self.linestyles = []
105 if isinstance(linestyles,list):
106 styles = linestyles
107 elif isinstance(linestyles,str):
108 styles = linestyles.split()
109 for ls in styles:
110 if self.lsalias.has_key(ls):
111 self.linestyles.append(self.lsalias.get(ls))
112 else:
113 self.linestyles.append(self.lsalias.get('line'))
114 if 0 <= linestyle < len(self.linestyles):
115 self.linestyle = linestyle
116
117 def delete(self, numbers=None):
118 """
119 Delete the 0-relative line number, default is to delete the last.
120 The remaining lines are NOT renumbered.
121 """
122
123 if numbers is None: numbers = [len(self.lines)-1]
124
125 if not hasattr(numbers, '__iter__'):
126 numbers = [numbers]
127
128 for number in numbers:
129 if 0 <= number < len(self.lines):
130 if self.lines[number] is not None:
131 for line in self.lines[number]:
132 line.set_linestyle('None')
133 self.lines[number] = None
134 self.show()
135
136 def get_line(self):
137 """
138 Get the current default line attributes.
139 """
140 return self.attributes
141
142
143 def hist(self, x=None, y=None, fmt=None, add=None):
144 """
145 Plot a histogram. N.B. the x values refer to the start of the
146 histogram bin.
147
148 fmt is the line style as in plot().
149 """
150 from matplotlib.numerix import array
151 from matplotlib.numerix.ma import MaskedArray
152 if x is None:
153 if y is None: return
154 x = range(len(y))
155
156 if len(x) != len(y):
157 return
158 l2 = 2*len(x)
159 x2 = range(l2)
160 y2 = range(12)
161 y2 = range(l2)
162 m2 = range(l2)
163 #ymsk = y.raw_mask()
164 #ydat = y.raw_data()
165 ymsk = y.mask
166 ydat = y.data
167 for i in range(l2):
168 x2[i] = x[i/2]
169 m2[i] = ymsk[i/2]
170
171 y2[0] = 0.0
172 for i in range(1,l2):
173 y2[i] = ydat[(i-1)/2]
174
175 self.plot(x2, MaskedArray(y2,mask=m2,copy=0), fmt, add)
176
177
178 def hold(self, hold=True):
179 """
180 Buffer graphics until subsequently released.
181 """
182 self.buffering = hold
183
184
185 def legend(self, loc=None):
186 """
187 Add a legend to the plot.
188
189 Any other value for loc else disables the legend:
190 1: upper right
191 2: upper left
192 3: lower left
193 4: lower right
194 5: right
195 6: center left
196 7: center right
197 8: lower center
198 9: upper center
199 10: center
200
201 """
202 if isinstance(loc, int):
203 self.loc = None
204 if 0 <= loc <= 10: self.loc = loc
205 else:
206 self.loc = None
207 #self.show()
208
209
210 def plot(self, x=None, y=None, fmt=None, add=None):
211 """
212 Plot the next line in the current frame using the current line
213 attributes. The ASAPlot graphics window will be mapped and raised.
214
215 The argument list works a bit like the matlab plot() function.
216 """
217 if x is None:
218 if y is None: return
219 x = range(len(y))
220
221 elif y is None:
222 y = x
223 x = range(len(y))
224 if fmt is None:
225 line = self.axes.plot(x, y)
226 else:
227 line = self.axes.plot(x, y, fmt)
228
229 # Add to an existing line?
230 i = None
231 if add is None or len(self.lines) < add < 0:
232 # Don't add.
233 self.lines.append(line)
234 i = len(self.lines) - 1
235 else:
236 if add == 0: add = len(self.lines)
237 i = add - 1
238 self.lines[i].extend(line)
239
240 # Set/reset attributes for the line.
241 gotcolour = False
242 for k, v in self.attributes.iteritems():
243 if k == 'color': gotcolour = True
244 for segment in self.lines[i]:
245 getattr(segment, "set_%s"%k)(v)
246
247 if not gotcolour and len(self.colormap):
248 for segment in self.lines[i]:
249 getattr(segment, "set_color")(self.colormap[self.color])
250 if len(self.colormap) == 1:
251 getattr(segment, "set_dashes")(self.linestyles[self.linestyle])
252
253 self.color += 1
254 if self.color >= len(self.colormap):
255 self.color = 0
256
257 if len(self.colormap) == 1:
258 self.linestyle += 1
259 if self.linestyle >= len(self.linestyles):
260 self.linestyle = 0
261
262 self.show()
263
264
265 def position(self):
266 """
267 Use the mouse to get a position from a graph.
268 """
269
270 def position_disable(event):
271 self.register('button_press', None)
272 print '%.4f, %.4f' % (event.xdata, event.ydata)
273
274 print 'Press any mouse button...'
275 self.register('button_press', position_disable)
276
277
278 def region(self):
279 """
280 Use the mouse to get a rectangular region from a plot.
281
282 The return value is [x0, y0, x1, y1] in world coordinates.
283 """
284
285 def region_start(event):
286 height = self.canvas.figure.bbox.height()
287 self.rect = {'fig': None, 'height': height,
288 'x': event.x, 'y': height - event.y,
289 'world': [event.xdata, event.ydata,
290 event.xdata, event.ydata]}
291 self.register('button_press', None)
292 self.register('motion_notify', region_draw)
293 self.register('button_release', region_disable)
294
295 def region_draw(event):
296 self.canvas._tkcanvas.delete(self.rect['fig'])
297 self.rect['fig'] = self.canvas._tkcanvas.create_rectangle(
298 self.rect['x'], self.rect['y'],
299 event.x, self.rect['height'] - event.y)
300
301 def region_disable(event):
302 self.register('motion_notify', None)
303 self.register('button_release', None)
304
305 self.canvas._tkcanvas.delete(self.rect['fig'])
306
307 self.rect['world'][2:4] = [event.xdata, event.ydata]
308 print '(%.2f, %.2f) (%.2f, %.2f)' % (self.rect['world'][0],
309 self.rect['world'][1], self.rect['world'][2],
310 self.rect['world'][3])
311
312 self.register('button_press', region_start)
313
314 # This has to be modified to block and return the result (currently
315 # printed by region_disable) when that becomes possible in matplotlib.
316
317 return [0.0, 0.0, 0.0, 0.0]
318
319
320 def register(self, type=None, func=None):
321 """
322 Register, reregister, or deregister events of type 'button_press',
323 'button_release', or 'motion_notify'.
324
325 The specified callback function should have the following signature:
326
327 def func(event)
328
329 where event is an MplEvent instance containing the following data:
330
331 name # Event name.
332 canvas # FigureCanvas instance generating the event.
333 x = None # x position - pixels from left of canvas.
334 y = None # y position - pixels from bottom of canvas.
335 button = None # Button pressed: None, 1, 2, 3.
336 key = None # Key pressed: None, chr(range(255)), shift,
337 win, or control
338 inaxes = None # Axes instance if cursor within axes.
339 xdata = None # x world coordinate.
340 ydata = None # y world coordinate.
341
342 For example:
343
344 def mouse_move(event):
345 print event.xdata, event.ydata
346
347 a = asaplot()
348 a.register('motion_notify', mouse_move)
349
350 If func is None, the event is deregistered.
351
352 Note that in TkAgg keyboard button presses don't generate an event.
353 """
354
355 if not self.events.has_key(type): return
356
357 if func is None:
358 if self.events[type] is not None:
359 # It's not clear that this does anything.
360 self.canvas.mpl_disconnect(self.events[type])
361 self.events[type] = None
362
363 # It seems to be necessary to return events to the toolbar.
364 if type == 'motion_notify':
365 self.canvas.mpl_connect(type + '_event',
366 self.figmgr.toolbar.mouse_move)
367 elif type == 'button_press':
368 self.canvas.mpl_connect(type + '_event',
369 self.figmgr.toolbar.press)
370 elif type == 'button_release':
371 self.canvas.mpl_connect(type + '_event',
372 self.figmgr.toolbar.release)
373
374 else:
375 self.events[type] = self.canvas.mpl_connect(type + '_event', func)
376
377
378 def release(self):
379 """
380 Release buffered graphics.
381 """
382 self.buffering = False
383 self.show()
384
385
386 def save(self, fname=None, orientation=None, dpi=None, papertype=None):
387 """
388 Save the plot to a file.
389
390 fname is the name of the output file. The image format is determined
391 from the file suffix; 'png', 'ps', and 'eps' are recognized. If no
392 file name is specified 'yyyymmdd_hhmmss.png' is created in the current
393 directory.
394 """
395 from asap import rcParams
396 if papertype is None:
397 papertype = rcParams['plotter.papertype']
398 if fname is None:
399 from datetime import datetime
400 dstr = datetime.now().strftime('%Y%m%d_%H%M%S')
401 fname = 'asap'+dstr+'.png'
402
403 d = ['png','.ps','eps']
404
405 from os.path import expandvars
406 fname = expandvars(fname)
407
408 if fname[-3:].lower() in d:
409 try:
410 if fname[-3:].lower() == ".ps":
411 from matplotlib import __version__ as mv
412 w = self.figure.figwidth.get()
413 h = self.figure.figheight.get()
414
415 if orientation is None:
416 # oriented
417 if w > h:
418 orientation = 'landscape'
419 else:
420 orientation = 'portrait'
421 from matplotlib.backends.backend_ps import papersize
422 pw,ph = papersize[papertype.lower()]
423 ds = None
424 if orientation == 'landscape':
425 ds = min(ph/w, pw/h)
426 else:
427 ds = min(pw/w, ph/h)
428 ow = ds * w
429 oh = ds * h
430 self.figure.set_figsize_inches((ow, oh))
431 self.figure.savefig(fname, orientation=orientation,
432 papertype=papertype.lower())
433 self.figure.set_figsize_inches((w, h))
434 print 'Written file %s' % (fname)
435 else:
436 if dpi is None:
437 dpi =150
438 self.figure.savefig(fname,dpi=dpi)
439 print 'Written file %s' % (fname)
440 except IOError, msg:
441 print 'Failed to save %s: Error msg was\n\n%s' % (fname, err)
442 return
443 else:
444 print "Invalid image type. Valid types are:"
445 print "'ps', 'eps', 'png'"
446
447
448 def set_axes(self, what=None, *args, **kwargs):
449 """
450 Set attributes for the axes by calling the relevant Axes.set_*()
451 method. Colour translation is done as described in the doctext
452 for palette().
453 """
454
455 if what is None: return
456 if what[-6:] == 'colour': what = what[:-6] + 'color'
457
458 key = "colour"
459 if kwargs.has_key(key):
460 val = kwargs.pop(key)
461 kwargs["color"] = val
462
463 getattr(self.axes, "set_%s"%what)(*args, **kwargs)
464
465 self.show(hardrefresh=False)
466
467
468 def set_figure(self, what=None, *args, **kwargs):
469 """
470 Set attributes for the figure by calling the relevant Figure.set_*()
471 method. Colour translation is done as described in the doctext
472 for palette().
473 """
474
475 if what is None: return
476 if what[-6:] == 'colour': what = what[:-6] + 'color'
477 #if what[-5:] == 'color' and len(args):
478 # args = (get_colour(args[0]),)
479
480 newargs = {}
481 for k, v in kwargs.iteritems():
482 k = k.lower()
483 if k == 'colour': k = 'color'
484 newargs[k] = v
485
486 getattr(self.figure, "set_%s"%what)(*args, **newargs)
487 self.show(hardrefresh=False)
488
489
490 def set_limits(self, xlim=None, ylim=None):
491 """
492 Set x-, and y-limits for each subplot.
493
494 xlim = [xmin, xmax] as in axes.set_xlim().
495 ylim = [ymin, ymax] as in axes.set_ylim().
496 """
497 for s in self.subplots:
498 self.axes = s['axes']
499 self.lines = s['lines']
500 oldxlim = list(self.axes.get_xlim())
501 oldylim = list(self.axes.get_ylim())
502 if xlim is not None:
503 for i in range(len(xlim)):
504 if xlim[i] is not None:
505 oldxlim[i] = xlim[i]
506 if ylim is not None:
507 for i in range(len(ylim)):
508 if ylim[i] is not None:
509 oldylim[i] = ylim[i]
510 self.axes.set_xlim(oldxlim)
511 self.axes.set_ylim(oldylim)
512 return
513
514
515 def set_line(self, number=None, **kwargs):
516 """
517 Set attributes for the specified line, or else the next line(s)
518 to be plotted.
519
520 number is the 0-relative number of a line that has already been
521 plotted. If no such line exists, attributes are recorded and used
522 for the next line(s) to be plotted.
523
524 Keyword arguments specify Line2D attributes, e.g. color='r'. Do
525
526 import matplotlib
527 help(matplotlib.lines)
528
529 The set_* methods of class Line2D define the attribute names and
530 values. For non-US usage, "colour" is recognized as synonymous with
531 "color".
532
533 Set the value to None to delete an attribute.
534
535 Colour translation is done as described in the doctext for palette().
536 """
537
538 redraw = False
539 for k, v in kwargs.iteritems():
540 k = k.lower()
541 if k == 'colour': k = 'color'
542
543 if 0 <= number < len(self.lines):
544 if self.lines[number] is not None:
545 for line in self.lines[number]:
546 getattr(line, "set_%s"%k)(v)
547 redraw = True
548 else:
549 if v is None:
550 del self.attributes[k]
551 else:
552 self.attributes[k] = v
553
554 if redraw: self.show(hardrefresh=False)
555
556
557 def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, ganged=True):
558 """
559 Set the panel layout.
560
561 rows and cols, if cols != 0, specify the number of rows and columns in
562 a regular layout. (Indexing of these panels in matplotlib is row-
563 major, i.e. column varies fastest.)
564
565 cols == 0 is interpreted as a retangular layout that accomodates
566 'rows' panels, e.g. rows == 6, cols == 0 is equivalent to
567 rows == 2, cols == 3.
568
569 0 <= n < rows*cols is interpreted as the 0-relative panel number in
570 the configuration specified by rows and cols to be added to the
571 current figure as its next 0-relative panel number (i). This allows
572 non-regular panel layouts to be constructed via multiple calls. Any
573 other value of n clears the plot and produces a rectangular array of
574 empty panels. The number of these may be limited by nplots.
575 """
576 if n < 0 and len(self.subplots):
577 self.figure.clear()
578 self.set_title()
579
580 if rows < 1: rows = 1
581
582 if cols <= 0:
583 i = int(sqrt(rows))
584 if i*i < rows: i += 1
585 cols = i
586
587 if i*(i-1) >= rows: i -= 1
588 rows = i
589
590 if 0 <= n < rows*cols:
591 i = len(self.subplots)
592 self.subplots.append({})
593
594 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
595 cols, n+1)
596 self.subplots[i]['lines'] = []
597
598 if i == 0: self.subplot(0)
599
600 self.rows = 0
601 self.cols = 0
602
603 else:
604 self.subplots = []
605
606 if nplots < 1 or rows*cols < nplots:
607 nplots = rows*cols
608 if ganged:
609 hsp,wsp = None,None
610 if rows > 1: hsp = 0.0001
611 if cols > 1: wsp = 0.0001
612 self.figure.subplots_adjust(wspace=wsp,hspace=hsp)
613 for i in range(nplots):
614 self.subplots.append({})
615 self.subplots[i]['lines'] = []
616 if not ganged:
617 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
618 cols, i+1)
619 self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
620 else:
621 if i == 0:
622 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
623 cols, i+1)
624 self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
625 else:
626 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
627 cols, i+1,
628 sharex=self.subplots[0]['axes'],
629 sharey=self.subplots[0]['axes'])
630
631 # Suppress tick labelling for interior subplots.
632 if i <= (rows-1)*cols - 1:
633 if i+cols < nplots:
634 # Suppress x-labels for frames width
635 # adjacent frames
636 for tick in self.subplots[i]['axes'].xaxis.majorTicks:
637 tick.label1On = False
638 self.subplots[i]['axes'].xaxis.label.set_visible(False)
639 if i%cols:
640 # Suppress y-labels for frames not in the left column.
641 for tick in self.subplots[i]['axes'].yaxis.majorTicks:
642 tick.label1On = False
643 self.subplots[i]['axes'].yaxis.label.set_visible(False)
644 # disable the first tick of [1:ncol-1] of the last row
645 #if i+1 < nplots:
646 # self.subplots[i]['axes'].xaxis.majorTicks[0].label1On = False
647 self.rows = rows
648 self.cols = cols
649 self.subplot(0)
650
651 def tidy(self):
652 # this needs to be exceuted after the first "refresh"
653 nplots = len(self.subplots)
654 if nplots == 1: return
655 for i in xrange(nplots):
656 ax = self.subplots[i]['axes']
657 if i%self.cols:
658 ax.xaxis.majorTicks[0].label1On = False
659 else:
660 if i != 0:
661 ax.yaxis.majorTicks[-1].label1On = False
662
663
664 def set_title(self, title=None):
665 """
666 Set the title of the plot window. Use the previous title if title is
667 omitted.
668 """
669 if title is not None:
670 self.title = title
671
672 self.figure.text(0.5, 0.95, self.title, horizontalalignment='center')
673
674
675 def show(self, hardrefresh=True):
676 """
677 Show graphics dependent on the current buffering state.
678 """
679 if not hardrefresh: return
680 if not self.buffering:
681 if self.loc is not None:
682 for sp in self.subplots:
683 lines = []
684 labels = []
685 i = 0
686 for line in sp['lines']:
687 i += 1
688 if line is not None:
689 lines.append(line[0])
690 lbl = line[0].get_label()
691 if lbl == '':
692 lbl = str(i)
693 labels.append(lbl)
694
695 if len(lines):
696 fp = FP(size=rcParams['legend.fontsize'])
697 fsz = fp.get_size_in_points() - len(lines)
698 fp.set_size(max(fsz,6))
699 sp['axes'].legend(tuple(lines), tuple(labels),
700 self.loc, prop=fp)
701 else:
702 sp['axes'].legend((' '))
703
704 from matplotlib.artist import setp
705 fp = FP(size=rcParams['xtick.labelsize'])
706 xts = fp.get_size_in_points()- (self.cols)/2
707 fp = FP(size=rcParams['ytick.labelsize'])
708 yts = fp.get_size_in_points() - (self.rows)/2
709 for sp in self.subplots:
710 ax = sp['axes']
711 s = rcParams['axes.titlesize']
712 tsize = s-(self.cols+self.rows-1)
713 ax.title.set_size(max(tsize,9))
714 fp = FP(size=rcParams['axes.labelsize'])
715 setp(ax.get_xticklabels(), fontsize=xts)
716 setp(ax.get_yticklabels(), fontsize=yts)
717 origx = fp.get_size_in_points()
718 origy = origx
719 off = 0
720 if self.cols > 1: off = self.cols
721 xfsize = origx-off
722 ax.xaxis.label.set_size(xfsize)
723 off = 0
724 if self.rows > 1: off = self.rows
725 yfsize = origy-off
726 ax.yaxis.label.set_size(yfsize)
727
728 def subplot(self, i=None, inc=None):
729 """
730 Set the subplot to the 0-relative panel number as defined by one or
731 more invokations of set_panels().
732 """
733 l = len(self.subplots)
734 if l:
735 if i is not None:
736 self.i = i
737
738 if inc is not None:
739 self.i += inc
740
741 self.i %= l
742 self.axes = self.subplots[self.i]['axes']
743 self.lines = self.subplots[self.i]['lines']
744
745 def text(self, *args, **kwargs):
746 """
747 Add text to the figure.
748 """
749 self.figure.text(*args, **kwargs)
750 self.show()
751
752 def vline_with_label(self, x, y, label,
753 location='bottom', rotate=0.0, **kwargs):
754 """
755 Plot a vertical line with label.
756 It takes "world" values fo x and y.
757 """
758 ax = self.axes
759 # need this to suppress autoscaling during this function
760 self.axes.set_autoscale_on(False)
761 ymin = 0.0
762 ymax = 1.0
763 valign = 'center'
764 if location.lower() == 'top':
765 y = max(0.0, y)
766 elif location.lower() == 'bottom':
767 y = min(0.0, y)
768 lbloffset = 0.06
769 # a rough estimate for the bb of the text
770 if rotate > 0.0: lbloffset = 0.03*len(label)
771 peakoffset = 0.01
772 xy0 = ax.transData.xy_tup((x,y))
773 # get relative coords
774 xy = ax.transAxes.inverse_xy_tup(xy0)
775 if location.lower() == 'top':
776 ymax = 1.0-lbloffset
777 ymin = xy[1]+peakoffset
778 valign = 'bottom'
779 ylbl = ymax+0.01
780 elif location.lower() == 'bottom':
781 ymin = lbloffset
782 ymax = xy[1]-peakoffset
783 valign = 'top'
784 ylbl = ymin-0.01
785 trans = blend_xy_sep_transform(ax.transData, ax.transAxes)
786 l = ax.axvline(x, ymin, ymax, color='black', **kwargs)
787 t = ax.text(x, ylbl ,label, verticalalignment=valign,
788 horizontalalignment='center',
789 rotation=rotate,transform = trans)
790 self.axes.set_autoscale_on(True)
Note: See TracBrowser for help on using the repository browser.