source: trunk/python/asaplotbase.py@ 2396

Last change on this file since 2396 was 2169, checked in by Malte Marquarding, 14 years ago

Fix version check fro matplotlib >=1

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