source: branches/casa-prerelease/pre-asap/python/asaplotbase.py@ 2171

Last change on this file since 2171 was 2069, checked in by Kana Sugimoto, 14 years ago

merged a bug fix in trunk (r2068)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.0 KB
Line 
1"""
2ASAP plotting class based on matplotlib.
3"""
4
5import sys
6from re import match
7import matplotlib
8
9from matplotlib.figure import Figure, Text
10from matplotlib.font_manager import FontProperties as FP
11from numpy import sqrt
12from matplotlib import rc, rcParams
13from matplotlib.ticker import OldScalarFormatter
14
15from asap.parameters import rcParams as asaprcParams
16from asap.logging import asaplog
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]) < 99:
25 #print "Warning: matplotlib version < 0.87. This might cause errors. Please upgrade."
26 asaplog.push( "matplotlib version < 0.99. This might cause errors. Please upgrade." )
27 asaplog.post( 'WARN' )
28
29class asaplotbase:
30 """
31 ASAP plotting base class based on matplotlib.
32 """
33
34 def __init__(self, rows=1, cols=0, title='', size=None, 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 numpy import array
151 from numpy.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 = None
164 ydat = None
165 if hasattr(y, "raw_mask"):
166 # numpy < 1.1
167 ymsk = y.raw_mask()
168 ydat = y.raw_data()
169 else:
170 ymsk = y.mask
171 ydat = y.data
172 for i in range(l2):
173 x2[i] = x[i/2]
174 m2[i] = ymsk[i/2]
175
176 y2[0] = 0.0
177 for i in range(1,l2):
178 y2[i] = ydat[(i-1)/2]
179
180 self.plot(x2, MaskedArray(y2,mask=m2,copy=0), fmt, add)
181
182
183 def hold(self, hold=True):
184 """
185 Buffer graphics until subsequently released.
186 """
187 self.buffering = hold
188
189
190 def legend(self, loc=None):
191 """
192 Add a legend to the plot.
193
194 Any other value for loc else disables the legend:
195 1: upper right
196 2: upper left
197 3: lower left
198 4: lower right
199 5: right
200 6: center left
201 7: center right
202 8: lower center
203 9: upper center
204 10: center
205
206 """
207 if isinstance(loc, int):
208 self.loc = None
209 if 0 <= loc <= 10: self.loc = loc
210 else:
211 self.loc = None
212 #self.show()
213
214
215 def plot(self, x=None, y=None, fmt=None, add=None):
216 """
217 Plot the next line in the current frame using the current line
218 attributes. The ASAPlot graphics window will be mapped and raised.
219
220 The argument list works a bit like the matlab plot() function.
221 """
222 if x is None:
223 if y is None: return
224 x = range(len(y))
225
226 elif y is None:
227 y = x
228 x = range(len(y))
229 if fmt is None:
230 line = self.axes.plot(x, y)
231 else:
232 line = self.axes.plot(x, y, fmt)
233 # add a picker to lines for spectral value mode.
234 # matplotlib.axes.plot returns a list of line object (1 element)
235 line[0].set_picker(5.0)
236
237 # Add to an existing line?
238 i = None
239 if add is None or len(self.lines) < add < 0:
240 # Don't add.
241 self.lines.append(line)
242 i = len(self.lines) - 1
243 else:
244 if add == 0: add = len(self.lines)
245 i = add - 1
246 self.lines[i].extend(line)
247
248 # Set/reset attributes for the line.
249 gotcolour = False
250 for k, v in self.attributes.iteritems():
251 if k == 'color': gotcolour = True
252 for segment in self.lines[i]:
253 getattr(segment, "set_%s"%k)(v)
254
255 if not gotcolour and len(self.colormap):
256 for segment in self.lines[i]:
257 getattr(segment, "set_color")(self.colormap[self.color])
258 if len(self.colormap) == 1:
259 getattr(segment, "set_dashes")(self.linestyles[self.linestyle])
260
261 self.color += 1
262 if self.color >= len(self.colormap):
263 self.color = 0
264
265 if len(self.colormap) == 1:
266 self.linestyle += 1
267 if self.linestyle >= len(self.linestyles):
268 self.linestyle = 0
269
270 self.show()
271
272
273 def position(self):
274 """
275 Use the mouse to get a position from a graph.
276 """
277
278 def position_disable(event):
279 self.register('button_press', None)
280 print '%.4f, %.4f' % (event.xdata, event.ydata)
281
282 print 'Press any mouse button...'
283 self.register('button_press', position_disable)
284
285
286 def get_region(self):
287 pos = []
288 print "Please select the bottom/left point"
289 pos.append(self.figure.ginput(n=1, show_clicks=False)[0])
290 print "Please select the top/right point"
291 pos.append(self.figure.ginput(n=1, show_clicks=False)[0])
292 return pos
293
294 def get_point(self):
295 print "Please select the point"
296 pt = self.figure.ginput(n=1, show_clicks=False)
297 if pt:
298 return pt[0]
299 else:
300 return None
301
302 def region(self):
303 """
304 Use the mouse to get a rectangular region from a plot.
305
306 The return value is [x0, y0, x1, y1] in world coordinates.
307 """
308
309 def region_start(event):
310 self.rect = {'x': event.x, 'y': event.y,
311 'world': [event.xdata, event.ydata,
312 event.xdata, event.ydata]}
313 self.register('button_press', None)
314 self.register('motion_notify', region_draw)
315 self.register('button_release', region_disable)
316
317 def region_draw(event):
318 self.figmgr.toolbar.draw_rubberband(event, event.x, event.y,
319 self.rect['x'], self.rect['y'])
320
321 def region_disable(event):
322 self.register('motion_notify', None)
323 self.register('button_release', None)
324
325 self.rect['world'][2:4] = [event.xdata, event.ydata]
326 print '(%.2f, %.2f) (%.2f, %.2f)' % (self.rect['world'][0],
327 self.rect['world'][1], self.rect['world'][2],
328 self.rect['world'][3])
329 self.figmgr.toolbar.release(event)
330
331 self.register('button_press', region_start)
332
333 # This has to be modified to block and return the result (currently
334 # printed by region_disable) when that becomes possible in matplotlib.
335
336 return [0.0, 0.0, 0.0, 0.0]
337
338
339 def register(self, type=None, func=None):
340 """
341 Register, reregister, or deregister events of type 'button_press',
342 'button_release', or 'motion_notify'.
343
344 The specified callback function should have the following signature:
345
346 def func(event)
347
348 where event is an MplEvent instance containing the following data:
349
350 name # Event name.
351 canvas # FigureCanvas instance generating the event.
352 x = None # x position - pixels from left of canvas.
353 y = None # y position - pixels from bottom of canvas.
354 button = None # Button pressed: None, 1, 2, 3.
355 key = None # Key pressed: None, chr(range(255)), shift,
356 win, or control
357 inaxes = None # Axes instance if cursor within axes.
358 xdata = None # x world coordinate.
359 ydata = None # y world coordinate.
360
361 For example:
362
363 def mouse_move(event):
364 print event.xdata, event.ydata
365
366 a = asaplot()
367 a.register('motion_notify', mouse_move)
368
369 If func is None, the event is deregistered.
370
371 Note that in TkAgg keyboard button presses don't generate an event.
372 """
373
374 if not self.events.has_key(type): return
375
376 if func is None:
377 if self.events[type] is not None:
378 # It's not clear that this does anything.
379 self.canvas.mpl_disconnect(self.events[type])
380 self.events[type] = None
381
382 # It seems to be necessary to return events to the toolbar. <-- Not ture. 2010.Jul.14.kana.
383 #if type == 'motion_notify':
384 # self.canvas.mpl_connect(type + '_event',
385 # self.figmgr.toolbar.mouse_move)
386 #elif type == 'button_press':
387 # self.canvas.mpl_connect(type + '_event',
388 # self.figmgr.toolbar.press)
389 #elif type == 'button_release':
390 # self.canvas.mpl_connect(type + '_event',
391 # self.figmgr.toolbar.release)
392
393 else:
394 self.events[type] = self.canvas.mpl_connect(type + '_event', func)
395
396
397 def release(self):
398 """
399 Release buffered graphics.
400 """
401 self.buffering = False
402 self.show()
403
404
405 def save(self, fname=None, orientation=None, dpi=None, papertype=None):
406 """
407 Save the plot to a file.
408
409 fname is the name of the output file. The image format is determined
410 from the file suffix; 'png', 'ps', and 'eps' are recognized. If no
411 file name is specified 'yyyymmdd_hhmmss.png' is created in the current
412 directory.
413 """
414 from asap import rcParams
415 if papertype is None:
416 papertype = rcParams['plotter.papertype']
417 if fname is None:
418 from datetime import datetime
419 dstr = datetime.now().strftime('%Y%m%d_%H%M%S')
420 fname = 'asap'+dstr+'.png'
421
422 d = ['png','.ps','eps', 'svg']
423
424 from os.path import expandvars
425 fname = expandvars(fname)
426
427 if fname[-3:].lower() in d:
428 try:
429 if fname[-3:].lower() == ".ps":
430 from matplotlib import __version__ as mv
431 w = self.figure.get_figwidth()
432 h = self.figure.get_figheight()
433
434 if orientation is None:
435 # oriented
436 if w > h:
437 orientation = 'landscape'
438 else:
439 orientation = 'portrait'
440 from matplotlib.backends.backend_ps import papersize
441 pw,ph = papersize[papertype.lower()]
442 ds = None
443 if orientation == 'landscape':
444 ds = min(ph/w, pw/h)
445 else:
446 ds = min(pw/w, ph/h)
447 ow = ds * w
448 oh = ds * h
449 self.figure.set_size_inches((ow, oh))
450 self.figure.savefig(fname, orientation=orientation,
451 papertype=papertype.lower())
452 self.figure.set_size_inches((w, h))
453 print 'Written file %s' % (fname)
454 else:
455 if dpi is None:
456 dpi =150
457 self.figure.savefig(fname,dpi=dpi)
458 print 'Written file %s' % (fname)
459 except IOError, msg:
460 #print 'Failed to save %s: Error msg was\n\n%s' % (fname, err)
461 asaplog.post()
462 asaplog.push('Failed to save %s: Error msg was\n\n%s' % (fname, str(msg)))
463 asaplog.post( 'ERROR' )
464 return
465 else:
466 #print "Invalid image type. Valid types are:"
467 #print "'ps', 'eps', 'png'"
468 asaplog.push( "Invalid image type. Valid types are:" )
469 asaplog.push( "'ps', 'eps', 'png', 'svg'" )
470 asaplog.post('WARN')
471
472
473 def set_axes(self, what=None, *args, **kwargs):
474 """
475 Set attributes for the axes by calling the relevant Axes.set_*()
476 method. Colour translation is done as described in the doctext
477 for palette().
478 """
479
480 if what is None: return
481 if what[-6:] == 'colour': what = what[:-6] + 'color'
482
483 key = "colour"
484 if kwargs.has_key(key):
485 val = kwargs.pop(key)
486 kwargs["color"] = val
487
488 getattr(self.axes, "set_%s"%what)(*args, **kwargs)
489
490 self.show(hardrefresh=False)
491
492
493 def set_figure(self, what=None, *args, **kwargs):
494 """
495 Set attributes for the figure by calling the relevant Figure.set_*()
496 method. Colour translation is done as described in the doctext
497 for palette().
498 """
499
500 if what is None: return
501 if what[-6:] == 'colour': what = what[:-6] + 'color'
502 #if what[-5:] == 'color' and len(args):
503 # args = (get_colour(args[0]),)
504
505 newargs = {}
506 for k, v in kwargs.iteritems():
507 k = k.lower()
508 if k == 'colour': k = 'color'
509 newargs[k] = v
510
511 getattr(self.figure, "set_%s"%what)(*args, **newargs)
512 self.show(hardrefresh=False)
513
514
515 def set_limits(self, xlim=None, ylim=None):
516 """
517 Set x-, and y-limits for each subplot.
518
519 xlim = [xmin, xmax] as in axes.set_xlim().
520 ylim = [ymin, ymax] as in axes.set_ylim().
521 """
522 for s in self.subplots:
523 self.axes = s['axes']
524 self.lines = s['lines']
525 oldxlim = list(self.axes.get_xlim())
526 oldylim = list(self.axes.get_ylim())
527 if xlim is not None:
528 for i in range(len(xlim)):
529 if xlim[i] is not None:
530 oldxlim[i] = xlim[i]
531 if ylim is not None:
532 for i in range(len(ylim)):
533 if ylim[i] is not None:
534 oldylim[i] = ylim[i]
535 self.axes.set_xlim(oldxlim)
536 self.axes.set_ylim(oldylim)
537 return
538
539
540 def set_line(self, number=None, **kwargs):
541 """
542 Set attributes for the specified line, or else the next line(s)
543 to be plotted.
544
545 number is the 0-relative number of a line that has already been
546 plotted. If no such line exists, attributes are recorded and used
547 for the next line(s) to be plotted.
548
549 Keyword arguments specify Line2D attributes, e.g. color='r'. Do
550
551 import matplotlib
552 help(matplotlib.lines)
553
554 The set_* methods of class Line2D define the attribute names and
555 values. For non-US usage, "colour" is recognized as synonymous with
556 "color".
557
558 Set the value to None to delete an attribute.
559
560 Colour translation is done as described in the doctext for palette().
561 """
562
563 redraw = False
564 for k, v in kwargs.iteritems():
565 k = k.lower()
566 if k == 'colour': k = 'color'
567
568 if 0 <= number < len(self.lines):
569 if self.lines[number] is not None:
570 for line in self.lines[number]:
571 getattr(line, "set_%s"%k)(v)
572 redraw = True
573 else:
574 if v is None:
575 del self.attributes[k]
576 else:
577 self.attributes[k] = v
578
579 if redraw: self.show(hardrefresh=False)
580
581
582 #def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, ganged=True):
583 def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, margin=None,ganged=True):
584 """
585 Set the panel layout.
586
587 rows and cols, if cols != 0, specify the number of rows and columns in
588 a regular layout. (Indexing of these panels in matplotlib is row-
589 major, i.e. column varies fastest.)
590
591 cols == 0 is interpreted as a retangular layout that accomodates
592 'rows' panels, e.g. rows == 6, cols == 0 is equivalent to
593 rows == 2, cols == 3.
594
595 0 <= n < rows*cols is interpreted as the 0-relative panel number in
596 the configuration specified by rows and cols to be added to the
597 current figure as its next 0-relative panel number (i). This allows
598 non-regular panel layouts to be constructed via multiple calls. Any
599 other value of n clears the plot and produces a rectangular array of
600 empty panels. The number of these may be limited by nplots.
601 """
602 if n < 0 and len(self.subplots):
603 self.figure.clear()
604 self.set_title()
605
606 if margin:
607 lef, bot, rig, top, wsp, hsp = margin
608 self.figure.subplots_adjust(
609 left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
610 del lef,bot,rig,top,wsp,hsp
611
612 if rows < 1: rows = 1
613
614 if cols <= 0:
615 i = int(sqrt(rows))
616 if i*i < rows: i += 1
617 cols = i
618
619 if i*(i-1) >= rows: i -= 1
620 rows = i
621
622 if 0 <= n < rows*cols:
623 i = len(self.subplots)
624
625 self.subplots.append({})
626
627 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
628 cols, n+1)
629 self.subplots[i]['lines'] = []
630
631 if i == 0: self.subplot(0)
632
633 self.rows = 0
634 self.cols = 0
635
636 else:
637 self.subplots = []
638
639 if nplots < 1 or rows*cols < nplots:
640 nplots = rows*cols
641 if ganged:
642 hsp,wsp = None,None
643 if rows > 1: hsp = 0.0001
644 if cols > 1: wsp = 0.0001
645 self.figure.subplots_adjust(wspace=wsp,hspace=hsp)
646 for i in range(nplots):
647 self.subplots.append({})
648 self.subplots[i]['lines'] = []
649 if not ganged:
650 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
651 cols, i+1)
652 if asaprcParams['plotter.axesformatting'] != 'mpl':
653 self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
654 else:
655 if i == 0:
656 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
657 cols, i+1)
658 if asaprcParams['plotter.axesformatting'] != 'mpl':
659
660 self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
661 else:
662 self.subplots[i]['axes'] = self.figure.add_subplot(rows,
663 cols, i+1,
664 sharex=self.subplots[0]['axes'],
665 sharey=self.subplots[0]['axes'])
666
667 # Suppress tick labelling for interior subplots.
668 if i <= (rows-1)*cols - 1:
669 if i+cols < nplots:
670 # Suppress x-labels for frames width
671 # adjacent frames
672 for tick in self.subplots[i]['axes'].xaxis.majorTicks:
673 tick.label1On = False
674 #self.subplots[i]['axes'].xaxis.label.set_visible(False)
675 if i%cols:
676 # Suppress y-labels for frames not in the left column.
677 for tick in self.subplots[i]['axes'].yaxis.majorTicks:
678 tick.label1On = False
679 #self.subplots[i]['axes'].yaxis.label.set_visible(False)
680 # disable the first tick of [1:ncol-1] of the last row
681 #if i+1 < nplots:
682 # self.subplots[i]['axes'].xaxis.majorTicks[0].label1On = False
683 # set axes label state for interior subplots.
684 if i%cols:
685 self.subplots[i]['axes'].yaxis.label.set_visible(False)
686 if (i <= (rows-1)*cols - 1) and (i+cols < nplots):
687 self.subplots[i]['axes'].xaxis.label.set_visible(False)
688 self.rows = rows
689 self.cols = cols
690 self.subplot(0)
691 del rows,cols,n,nplots,margin,ganged,i
692
693
694 def tidy(self):
695 # this needs to be exceuted after the first "refresh"
696 nplots = len(self.subplots)
697 if nplots == 1: return
698 for i in xrange(nplots):
699 ax = self.subplots[i]['axes']
700 if i%self.cols:
701 ax.xaxis.majorTicks[0].label1On = False
702 else:
703 if i != 0:
704 ax.yaxis.majorTicks[-1].label1On = False
705 ## set axes label state for interior subplots.
706 #innerax=False
707 #if i%self.cols:
708 # ax.yaxis.label.set_visible(innerax)
709 #if (i <= (self.rows-1)*self.cols - 1) and (i+self.cols < nplots):
710 # ax.xaxis.label.set_visible(innerax)
711
712
713 def set_title(self, title=None):
714 """
715 Set the title of the plot window. Use the previous title if title is
716 omitted.
717 """
718 if title is not None:
719 self.title = title
720
721 self.figure.text(0.5, 0.95, self.title, horizontalalignment='center')
722
723
724 def show(self, hardrefresh=True):
725 """
726 Show graphics dependent on the current buffering state.
727 """
728 if not hardrefresh: return
729 if not self.buffering:
730 if self.loc is not None:
731 for sp in self.subplots:
732 lines = []
733 labels = []
734 i = 0
735 for line in sp['lines']:
736 i += 1
737 if line is not None:
738 lines.append(line[0])
739 lbl = line[0].get_label()
740 if lbl == '':
741 lbl = str(i)
742 labels.append(lbl)
743
744 if len(lines):
745 fp = FP(size=rcParams['legend.fontsize'])
746 #fsz = fp.get_size_in_points() - len(lines)
747 fsz = fp.get_size_in_points() - max(len(lines),self.cols)
748 #fp.set_size(max(fsz,6))
749 fp.set_size(max(fsz,8))
750 sp['axes'].legend(tuple(lines), tuple(labels),
751 self.loc, prop=fp)
752 #else:
753 # sp['axes'].legend((' '))
754
755 from matplotlib.artist import setp
756 fpx = FP(size=rcParams['xtick.labelsize'])
757 xts = fpx.get_size_in_points()- (self.cols)/2
758 fpy = FP(size=rcParams['ytick.labelsize'])
759 yts = fpy.get_size_in_points() - (self.rows)/2
760 fpa = FP(size=rcParams['axes.labelsize'])
761 fpat = FP(size=rcParams['axes.titlesize'])
762 axsize = fpa.get_size_in_points()
763 tsize = fpat.get_size_in_points()-(self.cols)/2
764 for sp in self.subplots:
765 ax = sp['axes']
766 ax.title.set_size(tsize)
767 setp(ax.get_xticklabels(), fontsize=xts)
768 setp(ax.get_yticklabels(), fontsize=yts)
769 off = 0
770 if self.cols > 1: off = self.cols
771 ax.xaxis.label.set_size(axsize-off)
772 off = 0
773 if self.rows > 1: off = self.rows
774 ax.yaxis.label.set_size(axsize-off)
775
776 def subplot(self, i=None, inc=None):
777 """
778 Set the subplot to the 0-relative panel number as defined by one or
779 more invokations of set_panels().
780 """
781 l = len(self.subplots)
782 if l:
783 if i is not None:
784 self.i = i
785
786 if inc is not None:
787 self.i += inc
788
789 self.i %= l
790 self.axes = self.subplots[self.i]['axes']
791 self.lines = self.subplots[self.i]['lines']
792
793 def text(self, *args, **kwargs):
794 """
795 Add text to the figure.
796 """
797 self.figure.text(*args, **kwargs)
798 self.show()
799
800 def vline_with_label(self, x, y, label,
801 location='bottom', rotate=0.0, **kwargs):
802 """
803 Plot a vertical line with label.
804 It takes "world" values fo x and y.
805 """
806 ax = self.axes
807 # need this to suppress autoscaling during this function
808 self.axes.set_autoscale_on(False)
809 ymin = 0.0
810 ymax = 1.0
811 valign = 'center'
812 if location.lower() == 'top':
813 y = max(0.0, y)
814 elif location.lower() == 'bottom':
815 y = min(0.0, y)
816 lbloffset = 0.06
817 # a rough estimate for the bb of the text
818 if rotate > 0.0: lbloffset = 0.03*len(label)
819 peakoffset = 0.01
820 xy = None
821 xy0 = None
822 # matplotlib api change 0.98 is using transform now
823 if hasattr(ax.transData, "inverse_xy_tup"):
824 # get relative coords
825 xy0 = ax.transData.xy_tup((x,y))
826 xy = ax.transAxes.inverse_xy_tup(xy0)
827 else:
828 xy0 = ax.transData.transform((x,y))
829 # get relative coords
830 xy = ax.transAxes.inverted().transform(xy0)
831 if location.lower() == 'top':
832 ymax = 1.0-lbloffset
833 ymin = xy[1]+peakoffset
834 valign = 'bottom'
835 ylbl = ymax+0.01
836 elif location.lower() == 'bottom':
837 ymin = lbloffset
838 ymax = xy[1]-peakoffset
839 valign = 'top'
840 ylbl = ymin-0.01
841 trans = blended_transform_factory(ax.transData, ax.transAxes)
842 l = ax.axvline(x, ymin, ymax, color='black', **kwargs)
843 t = ax.text(x, ylbl ,label, verticalalignment=valign,
844 horizontalalignment='center',
845 rotation=rotate,transform = trans)
846 self.axes.set_autoscale_on(True)
Note: See TracBrowser for help on using the repository browser.