source: trunk/python/asaplotbase.py @ 2700

Last change on this file since 2700 was 2700, checked in by Kana Sugimoto, 11 years ago

New Development: No (just changed the order of methods)

JIRA Issue: No

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s):

Description:

No actual change to codes.
Just changed the order of methods for future modifications.


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