source: trunk/python/asaplotbase.py@ 3006

Last change on this file since 3006 was 2715, checked in by Kana Sugimoto, 12 years ago

New Development: No

JIRA Issue: No (minor improvements)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: added a 'private' method, _subplotOk,

to check subplot layout in asaplotbase class.

Test Programs: do page iteration

Put in Release Notes: No

Module(s): asaplotbase, asapplotter, and sdplot

Description:

Implemented a better procedure to check if re-generation of
subplot instanses are necessary in page iteration.
Also, reset page counter when layout is changed by asapplotter.set_layout.


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