source: trunk/python/asaplotbase.py

Last change on this file was 2715, checked in by Kana Sugimoto, 11 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.