casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
asapplotter.py
Go to the documentation of this file.
00001 from asap.parameters import rcParams
00002 from asap.selector import selector
00003 from asap.scantable import scantable
00004 from asap.logging import asaplog, asaplog_post_dec
00005 import matplotlib.axes
00006 from matplotlib.font_manager import FontProperties
00007 from matplotlib.text import Text
00008 from matplotlib import _pylab_helpers
00009 
00010 import re
00011 
00012 def new_asaplot(visible=None,**kwargs):
00013     """
00014     Returns a new asaplot instance based on the backend settings.
00015     """
00016     if visible == None:
00017         visible = rcParams['plotter.gui']
00018 
00019     backend=matplotlib.get_backend()
00020     if not visible:
00021         from asap.asaplot import asaplot
00022     elif backend == 'TkAgg':
00023         from asap.asaplotgui import asaplotgui as asaplot
00024     elif backend == 'Qt4Agg':
00025         from asap.asaplotgui_qt4 import asaplotgui as asaplot
00026     elif backend == 'GTkAgg':
00027         from asap.asaplotgui_gtk import asaplotgui as asaplot
00028     else:
00029         from asap.asaplot import asaplot
00030     return asaplot(**kwargs)
00031 
00032 class asapplotter:
00033     """
00034     The ASAP plotter.
00035     By default the plotter is set up to plot polarisations
00036     'colour stacked' and scantables across panels.
00037 
00038     .. note::
00039 
00040         Currenly it only plots 'spectra' not Tsys or
00041         other variables.
00042 
00043     """
00044     def __init__(self, visible=None , **kwargs):
00045         self._visible = rcParams['plotter.gui']
00046         if visible is not None:
00047             self._visible = visible
00048         self._plotter = None
00049         self._inikwg = kwargs
00050 
00051         self._panelling = None
00052         self._stacking = None
00053         self.set_panelling()
00054         self.set_stacking()
00055         self._rows = None
00056         self._cols = None
00057         self._minmaxx = None
00058         self._minmaxy = None
00059         self._datamask = None
00060         self._data = None
00061         self._lmap = None
00062         self._title = None
00063         self._ordinate = None
00064         self._abcissa = None
00065         self._abcunit = None
00066         self._usermask = []
00067         self._maskselection = None
00068         self._selection = selector()
00069         self._hist = rcParams['plotter.histogram']
00070         self._fp = FontProperties()
00071         self._margins = self.set_margin(refresh=False)
00072         self._offset = None
00073         self._startrow = 0
00074         self._ipanel = -1
00075         self._panelrows = []
00076         self._headtext={'string': None, 'textobj': None}
00077         self._colormap = None
00078         self._linestyles = None
00079         self._legendloc = None
00080 
00081     def _translate(self, instr):
00082         keys = "s b i p t r".split()
00083         if isinstance(instr, str):
00084             for key in keys:
00085                 if instr.lower().startswith(key):
00086                     return key
00087         return None
00088 
00089     @asaplog_post_dec
00090     def _reload_plotter(self):
00091         if self._plotter is not None:
00092             #if not self._plotter.is_dead:
00093             #    # clear lines and axes
00094             #    try:
00095             #        self._plotter.clear()
00096             #    except: # Can't remove when already killed.
00097             #        pass
00098             if self.casabar_exists():
00099                 del self._plotter.figmgr.casabar
00100             self._plotter.quit()
00101             del self._plotter
00102         asaplog.push('Loading new plotter')
00103         self._plotter = new_asaplot(self._visible,**self._inikwg)
00104         self._plotter.figmgr.casabar=self._new_custombar()
00105         # just to make sure they're set
00106         self._plotter.palette(color=0,colormap=self._colormap,
00107                               linestyle=0,linestyles=self._linestyles)
00108         self._plotter.legend(self._legendloc)
00109 
00110     def _new_custombar(self):
00111         backend=matplotlib.get_backend()
00112         if not self._visible:
00113             return None
00114         elif backend == "TkAgg":
00115             from asap.customgui_tkagg import CustomToolbarTkAgg
00116             return CustomToolbarTkAgg(self)
00117         elif backend == "Qt4Agg":
00118             from asap.customgui_qt4agg import CustomToolbarQT4Agg
00119             return CustomToolbarQT4Agg(self)
00120         return None
00121 
00122     def casabar_exists(self):
00123         if not hasattr(self._plotter.figmgr,'casabar'):
00124             return False
00125         elif self._plotter.figmgr.casabar:
00126             return True
00127         return False
00128 
00129     def _assert_plotter(self,action="status",errmsg=None):
00130         """
00131         Check plot window status. Returns True if plot window is alive.
00132         Parameters
00133             action:    An action to take if the plotter window is not alive.
00134                        ['status'|'reload'|'halt']
00135                        The action 'status' simply returns False if asaplot
00136                        is not alive. When action='reload', plot window is
00137                        reloaded and the method returns True. Finally, an
00138                        error is raised when action='halt'.
00139             errmsg:    An error (warning) message to send to the logger,
00140                        when plot window is not alive.
00141         """
00142         isAlive = (self._plotter is not None) and self._plotter._alive()
00143         # More tests
00144         #if isAlive:
00145         #    if self._plotter.figmgr:
00146         #        figmgr = self._plotter.figmgr
00147         #        figid = figmgr.num
00148         #        # Make sure figid=0 is what asapplotter expects.
00149         #        # It might be already destroied/overridden by matplotlib
00150         #        # commands or other plotting methods using asaplot.
00151         #        isAlive = _pylab_helpers.Gcf.has_fignum(figid) and \
00152         #                  (figmgr == \
00153         #                   _pylab_helpers.Gcf.get_fig_manager(figid))
00154         #    else:
00155         #        isAlive = False
00156             
00157         if isAlive:
00158             return True
00159         # Plotter is not alive.
00160         haltmsg = "Plotter window has not yet been loaded or is closed."
00161         if type(errmsg)==str and len(errmsg) > 0:
00162             haltmsg = errmsg
00163         
00164         if action.upper().startswith("R"):
00165             # reload plotter
00166             self._reload_plotter()
00167             return True
00168         elif action.upper().startswith("H"):
00169             # halt
00170             asaplog.push(haltmsg)
00171             asaplog.post("ERROR")
00172             raise RuntimeError(haltmsg)
00173         else:
00174             if errmsg:
00175                 asaplog.push(errmsg)
00176                 asaplog.post("WARN")
00177             return False
00178 
00179 
00180     @asaplog_post_dec
00181     def plot(self, scan=None):
00182         """
00183         Plot a scantable.
00184         Parameters:
00185             scan:   a scantable
00186         Note:
00187             If a scantable was specified in a previous call
00188             to plot, no argument has to be given to 'replot'
00189             NO checking is done that the abcissas of the scantable
00190             are consistent e.g. all 'channel' or all 'velocity' etc.
00191         """
00192         if not self._data and not scan:
00193             msg = "Input is not a scantable"
00194             raise TypeError(msg)
00195         self._startrow = 0
00196         self._ipanel = -1
00197         self._reset_header()
00198         self._panelrows = []
00199 
00200         self._assert_plotter(action="reload")
00201         if self.casabar_exists():
00202             self._plotter.figmgr.casabar.set_pagecounter(1)
00203 
00204         self._plotter.hold()
00205         #self._plotter.clear()
00206         if scan: 
00207             self.set_data(scan, refresh=False)
00208         self._plotter.palette(color=0,colormap=self._colormap,
00209                               linestyle=0,linestyles=self._linestyles)
00210         self._plotter.legend(self._legendloc)
00211         self._plot(self._data)
00212         if self._minmaxy is not None:
00213             self._plotter.set_limits(ylim=self._minmaxy)
00214         if self.casabar_exists(): self._plotter.figmgr.casabar.enable_button()
00215         self._plotter.release()
00216         self._plotter.tidy()
00217         self._plotter.show(hardrefresh=False)
00218         return
00219 
00220     def gca(self):
00221         errmsg = "No axis to retun. Need to plot first."
00222         if not self._assert_plotter(action="status",errmsg=errmsg):
00223             return None
00224         return self._plotter.figure.gca()
00225 
00226     def refresh(self):
00227         """Do a soft refresh"""
00228         errmsg = "No figure to re-plot. Need to plot first."
00229         self._assert_plotter(action="halt",errmsg=errmsg)
00230 
00231         self._plotter.figure.show()
00232 
00233     def create_mask(self, nwin=1, panel=0, color=None):
00234         """
00235         Interactively define a mask. It retruns a mask that is equivalent to
00236         the one created manually with scantable.create_mask.
00237         Parameters:
00238             nwin:       The number of mask windows to create interactively
00239                         default is 1.
00240             panel:      Which panel to use for mask selection. This is useful
00241                         if different IFs are spread over panels (default 0)
00242         """
00243         ## this method relies on already plotted figure
00244         if not self._assert_plotter(action="status") or (self._data is None):
00245             msg = "Cannot create mask interactively on plot. Can only create mask after plotting."
00246             asaplog.push( msg )
00247             asaplog.post( "ERROR" )
00248             return []
00249         outmask = []
00250         self._plotter.subplot(panel)
00251         xmin, xmax = self._plotter.axes.get_xlim()
00252         marg = 0.05*(xmax-xmin)
00253         self._plotter.axes.set_xlim(xmin-marg, xmax+marg)
00254         self.refresh()
00255 
00256         def cleanup(lines=False, texts=False, refresh=False):
00257             if lines:
00258                 del self._plotter.axes.lines[-1]
00259             if texts:
00260                 del self._plotter.axes.texts[-1]
00261             if refresh:
00262                 self.refresh()
00263 
00264         for w in xrange(nwin):
00265             wpos = []
00266             self.text(0.05,1.0, "Add start boundary",
00267                       coords="relative", fontsize=10)
00268             point = self._plotter.get_point()
00269             cleanup(texts=True)
00270             if point is None:
00271                 continue
00272             wpos.append(point[0])
00273             self.axvline(wpos[0], color=color)
00274             self.text(0.05,1.0, "Add end boundary", coords="relative", fontsize=10)
00275             point = self._plotter.get_point()
00276             cleanup(texts=True, lines=True)
00277             if point is None:
00278                 self.refresh()
00279                 continue
00280             wpos.append(point[0])
00281             self.axvspan(wpos[0], wpos[1], alpha=0.1,
00282                          edgecolor=color, facecolor=color)
00283             ymin, ymax = self._plotter.axes.get_ylim()
00284             outmask.append(wpos)
00285 
00286         self._plotter.axes.set_xlim(xmin, xmax)
00287         self.refresh()
00288         if len(outmask) > 0:
00289             return self._data.create_mask(*outmask)
00290         return []
00291 
00292     # forwards to matplotlib axes
00293     def text(self, *args, **kwargs):
00294         self._assert_plotter(action="reload")
00295         if kwargs.has_key("interactive"):
00296             if kwargs.pop("interactive"):
00297                 pos = self._plotter.get_point()
00298                 args = tuple(pos)+args
00299         self._axes_callback("text", *args, **kwargs)
00300 
00301     text.__doc__ = matplotlib.axes.Axes.text.__doc__
00302 
00303     def arrow(self, *args, **kwargs):
00304         self._assert_plotter(action="reload")
00305         if kwargs.has_key("interactive"):
00306             if kwargs.pop("interactive"):
00307                 pos = self._plotter.get_region()
00308                 dpos = (pos[0][0], pos[0][1],
00309                         pos[1][0]-pos[0][0],
00310                         pos[1][1] - pos[0][1])
00311                 args = dpos + args
00312         self._axes_callback("arrow", *args, **kwargs)
00313 
00314     arrow.__doc__ = matplotlib.axes.Axes.arrow.__doc__
00315 
00316     def annotate(self, text, xy=None, xytext=None, **kwargs):
00317         self._assert_plotter(action="reload")
00318         if kwargs.has_key("interactive"):
00319             if kwargs.pop("interactive"):
00320                 xy = self._plotter.get_point()
00321                 xytext = self._plotter.get_point()
00322         if not kwargs.has_key("arrowprops"):
00323             kwargs["arrowprops"] = dict(arrowstyle="->")
00324         self._axes_callback("annotate", text, xy, xytext, **kwargs)
00325 
00326     annotate.__doc__ = matplotlib.axes.Axes.annotate.__doc__
00327 
00328     def axvline(self, *args, **kwargs):
00329         self._assert_plotter(action="reload")
00330         if kwargs.has_key("interactive"):
00331             if kwargs.pop("interactive"):
00332                 pos = self._plotter.get_point()
00333                 args = (pos[0],)+args
00334         self._axes_callback("axvline", *args, **kwargs)
00335 
00336     axvline.__doc__ = matplotlib.axes.Axes.axvline.__doc__
00337 
00338     def axhline(self, *args, **kwargs):
00339         self._assert_plotter(action="reload")
00340         if kwargs.has_key("interactive"):
00341             if kwargs.pop("interactive"):
00342                 pos = self._plotter.get_point()
00343                 args = (pos[1],)+args
00344         self._axes_callback("axhline", *args, **kwargs)
00345 
00346     axhline.__doc__ = matplotlib.axes.Axes.axhline.__doc__
00347 
00348     def axvspan(self, *args, **kwargs):
00349         self._assert_plotter(action="reload")
00350         if kwargs.has_key("interactive"):
00351             if kwargs.pop("interactive"):
00352                 pos = self._plotter.get_region()
00353                 dpos = (pos[0][0], pos[1][0])
00354                 args = dpos + args
00355         self._axes_callback("axvspan", *args, **kwargs)
00356         # hack to preventy mpl from redrawing the patch
00357         # it seem to convert the patch into lines on every draw.
00358         # This doesn't happen in a test script???
00359         #del self._plotter.axes.patches[-1]
00360 
00361     axvspan.__doc__ = matplotlib.axes.Axes.axvspan.__doc__
00362 
00363     def axhspan(self, *args, **kwargs):
00364         self._assert_plotter(action="reload")
00365         if kwargs.has_key("interactive"):
00366             if kwargs.pop("interactive"):
00367                 pos = self._plotter.get_region()
00368                 dpos = (pos[0][1], pos[1][1])
00369                 args = dpos + args
00370         self._axes_callback("axhspan", *args, **kwargs)
00371         # hack to preventy mpl from redrawing the patch
00372         # it seem to convert the patch into lines on every draw.
00373         # This doesn't happen in a test script???
00374         #del self._plotter.axes.patches[-1]
00375 
00376     axhspan.__doc__ = matplotlib.axes.Axes.axhspan.__doc__
00377 
00378     def _axes_callback(self, axesfunc, *args, **kwargs):
00379         self._assert_plotter(action="reload")
00380         panel = 0
00381         if kwargs.has_key("panel"):
00382             panel = kwargs.pop("panel")
00383         coords = None
00384         if kwargs.has_key("coords"):
00385             coords = kwargs.pop("coords")
00386             if coords.lower() == 'world':
00387                 kwargs["transform"] = self._plotter.axes.transData
00388             elif coords.lower() == 'relative':
00389                 kwargs["transform"] = self._plotter.axes.transAxes
00390         self._plotter.subplot(panel)
00391         self._plotter.axes.set_autoscale_on(False)
00392         getattr(self._plotter.axes, axesfunc)(*args, **kwargs)
00393         self._plotter.show(False)
00394         self._plotter.axes.set_autoscale_on(True)
00395     # end matplotlib.axes fowarding functions
00396 
00397     @asaplog_post_dec
00398     def set_data(self, scan, refresh=True):
00399         """
00400         Set a scantable to plot.
00401         Parameters:
00402             scan:      a scantable
00403             refresh:   True (default) or False. If True, the plot is
00404                        replotted based on the new parameter setting(s).
00405                        Otherwise,the parameter(s) are set without replotting.
00406         Note:
00407            The user specified masks and data selections will be reset
00408            if a new scantable is set. This method should be called before
00409            setting data selections (set_selection) and/or masks (set_mask).
00410         """
00411         from asap import scantable
00412         if isinstance(scan, scantable):
00413             if (self._data is not None) and (scan != self._data):
00414                 del self._data
00415                 msg = "A new scantable is set to the plotter. "\
00416                       "The masks and data selections are reset."
00417                 asaplog.push( msg )
00418             self._data = scan
00419             # reset
00420             self._reset()
00421         else:
00422             msg = "Input is not a scantable"
00423             raise TypeError(msg)
00424 
00425         # ranges become invalid when unit changes
00426         if self._abcunit and self._abcunit != self._data.get_unit():
00427             self._minmaxx = None
00428             self._minmaxy = None
00429             self._abcunit = self._data.get_unit()
00430             self._datamask = None
00431         if refresh: self.plot()
00432 
00433     @asaplog_post_dec
00434     def set_mode(self, stacking=None, panelling=None, refresh=True):
00435         """
00436         Set the plots look and feel, i.e. what you want to see on the plot.
00437         Parameters:
00438             stacking:     tell the plotter which variable to plot
00439                           as line colour overlays (default 'pol')
00440             panelling:    tell the plotter which variable to plot
00441                           across multiple panels (default 'scan'
00442             refresh:      True (default) or False. If True, the plot is
00443                           replotted based on the new parameter setting(s).
00444                           Otherwise,the parameter(s) are set without replotting.
00445         Note:
00446             Valid modes are:
00447                  'beam' 'Beam' 'b':     Beams
00448                  'if' 'IF' 'i':         IFs
00449                  'pol' 'Pol' 'p':       Polarisations
00450                  'scan' 'Scan' 's':     Scans
00451                  'time' 'Time' 't':     Times
00452                  'row' 'Row' 'r':       Rows
00453             When either 'stacking' or 'panelling' is set to 'row',
00454             the other parameter setting is ignored.
00455         """
00456         msg = "Invalid mode"
00457         if not self.set_panelling(panelling) or \
00458                not self.set_stacking(stacking):
00459             raise TypeError(msg)
00460         #if self._panelling == 'r':
00461         #    self._stacking = '_r'
00462         #if self._stacking == 'r':
00463         #    self._panelling = '_r'
00464         if refresh and self._data: self.plot(self._data)
00465         return
00466 
00467     def set_panelling(self, what=None):
00468         """Set the 'panelling' mode i.e. which type of spectra should be
00469         spread across different panels.
00470         """
00471 
00472         mode = what
00473         if mode is None:
00474              mode = rcParams['plotter.panelling']
00475         md = self._translate(mode)
00476         if md:
00477             self._panelling = md
00478             self._title = None
00479             #if md == 'r':
00480             #    self._stacking = '_r'
00481             # you need to reset counters for multi page plotting
00482             self._reset_counters()
00483             return True
00484         return False
00485 
00486     def set_layout(self,rows=None,cols=None,refresh=True):
00487         """
00488         Set the multi-panel layout, i.e. how many rows and columns plots
00489         are visible.
00490         Parameters:
00491              rows:   The number of rows of plots
00492              cols:   The number of columns of plots
00493              refresh:  True (default) or False. If True, the plot is
00494                        replotted based on the new parameter setting(s).
00495                        Otherwise,the parameter(s) are set without replotting.
00496         Note:
00497              If no argument is given, the potter reverts to its auto-plot
00498              behaviour.
00499         """
00500         self._rows = rows
00501         self._cols = cols
00502         if refresh and self._data: self.plot(self._data)
00503         return
00504 
00505     def set_stacking(self, what=None):
00506         """Set the 'stacking' mode i.e. which type of spectra should be
00507         overlayed.
00508         """
00509         mode = what
00510         if mode is None:
00511              mode = rcParams['plotter.stacking']
00512         md = self._translate(mode)
00513         if md:
00514             self._stacking = md
00515             self._lmap = None
00516             #if md == 'r':
00517             #    self._panelling = '_r'
00518             # you need to reset counters for multi page plotting
00519             self._reset_counters()
00520             return True
00521         return False
00522 
00523     def _reset_counters(self):
00524         self._startrow = 0
00525         self._ipanel = -1
00526         self._panelrows = []
00527 
00528     def set_range(self,xstart=None,xend=None,ystart=None,yend=None,refresh=True, offset=None):
00529         """
00530         Set the range of interest on the abcissa of the plot
00531         Parameters:
00532             [x,y]start,[x,y]end:  The start and end points of the 'zoom' window
00533             refresh:  True (default) or False. If True, the plot is
00534                       replotted based on the new parameter setting(s).
00535                       Otherwise,the parameter(s) are set without replotting.
00536             offset:   shift the abcissa by the given amount. The abcissa label will
00537                       have '(relative)' appended to it.
00538         Note:
00539             These become non-sensical when the unit changes.
00540             use plotter.set_range() without parameters to reset
00541 
00542         """
00543         self._offset = offset
00544         if xstart is None and xend is None:
00545             self._minmaxx = None
00546         else:
00547             self._minmaxx = [xstart,xend]
00548         if ystart is None and yend is None:
00549             self._minmaxy = None
00550         else:
00551             self._minmaxy = [ystart,yend]
00552         if refresh and self._data: self.plot(self._data)
00553         return
00554 
00555     def set_legend(self, mp=None, fontsize = None, mode = 0, refresh=True):
00556         """
00557         Specify a mapping for the legend instead of using the default
00558         indices:
00559         Parameters:
00560             mp:        a list of 'strings'. This should have the same length
00561                        as the number of elements on the legend and then maps
00562                        to the indeces in order. It is possible to uses latex
00563                        math expression. These have to be enclosed in r'',
00564                        e.g. r'$x^{2}$'
00565             fontsize:  The font size of the label (default None)
00566             mode:      where to display the legend
00567                        Any other value for loc else disables the legend:
00568                         0: auto
00569                         1: upper right
00570                         2: upper left
00571                         3: lower left
00572                         4: lower right
00573                         5: right
00574                         6: center left
00575                         7: center right
00576                         8: lower center
00577                         9: upper center
00578                         10: center
00579             refresh:    True (default) or False. If True, the plot is
00580                         replotted based on the new parameter setting(s).
00581                         Otherwise,the parameter(s) are set without replotting.
00582 
00583         Example:
00584              If the data has two IFs/rest frequencies with index 0 and 1
00585              for CO and SiO:
00586              plotter.set_stacking('i')
00587              plotter.set_legend(['CO','SiO'])
00588              plotter.plot()
00589              plotter.set_legend([r'$^{12}CO$', r'SiO'])
00590         """
00591         self._lmap = mp
00592         #self._plotter.legend(mode)
00593         self._legendloc = mode
00594         if isinstance(fontsize, int):
00595             from matplotlib import rc as rcp
00596             rcp('legend', fontsize=fontsize)
00597         if refresh and self._data: self.plot(self._data)
00598         return
00599 
00600     def set_title(self, title=None, fontsize=None, refresh=True):
00601         """
00602         Set the title of sub-plots. If multiple sub-plots are plotted,
00603         multiple titles have to be specified.
00604         Parameters:
00605             title:      a list of titles of sub-plots.
00606             fontsize:   a font size of titles (integer)
00607             refresh:    True (default) or False. If True, the plot is
00608                         replotted based on the new parameter setting(s).
00609                         Otherwise,the parameter(s) are set without replotting.
00610         Example:
00611              # two panels are visible on the plotter
00612              plotter.set_title(['First Panel','Second Panel'])
00613         """
00614         self._title = title
00615         if isinstance(fontsize, int):
00616             from matplotlib import rc as rcp
00617             rcp('axes', titlesize=fontsize)
00618         if refresh and self._data: self.plot(self._data)
00619         return
00620 
00621     def set_ordinate(self, ordinate=None, fontsize=None, refresh=True):
00622         """
00623         Set the y-axis label of the plot. If multiple panels are plotted,
00624         multiple labels have to be specified.
00625         Parameters:
00626             ordinate:    a list of ordinate labels. None (default) let
00627                          data determine the labels
00628             fontsize:    a font size of vertical axis labels (integer)
00629             refresh:     True (default) or False. If True, the plot is
00630                          replotted based on the new parameter setting(s).
00631                          Otherwise,the parameter(s) are set without replotting.
00632         Example:
00633              # two panels are visible on the plotter
00634              plotter.set_ordinate(['First Y-Axis','Second Y-Axis'])
00635         """
00636         self._ordinate = ordinate
00637         if isinstance(fontsize, int):
00638             from matplotlib import rc as rcp
00639             rcp('axes', labelsize=fontsize)
00640             rcp('ytick', labelsize=fontsize)
00641         if refresh and self._data: self.plot(self._data)
00642         return
00643 
00644     def set_abcissa(self, abcissa=None, fontsize=None, refresh=True):
00645         """
00646         Set the x-axis label of the plot. If multiple panels are plotted,
00647         multiple labels have to be specified.
00648         Parameters:
00649             abcissa:     a list of abcissa labels. None (default) let
00650                          data determine the labels
00651             fontsize:    a font size of horizontal axis labels (integer)
00652             refresh:     True (default) or False. If True, the plot is
00653                          replotted based on the new parameter setting(s).
00654                          Otherwise,the parameter(s) are set without replotting.
00655         Example:
00656              # two panels are visible on the plotter
00657              plotter.set_ordinate(['First X-Axis','Second X-Axis'])
00658         """
00659         self._abcissa = abcissa
00660         if isinstance(fontsize, int):
00661             from matplotlib import rc as rcp
00662             rcp('axes', labelsize=fontsize)
00663             rcp('xtick', labelsize=fontsize)
00664         if refresh and self._data: self.plot(self._data)
00665         return
00666 
00667     def set_colors(self, colmap, refresh=True):
00668         """
00669         Set the colours to be used. The plotter will cycle through
00670         these colours when lines are overlaid (stacking mode).
00671         Parameters:
00672             colmap:     a list of colour names
00673             refresh:    True (default) or False. If True, the plot is
00674                         replotted based on the new parameter setting(s).
00675                         Otherwise,the parameter(s) are set without replotting.
00676         Example:
00677              plotter.set_colors('red green blue')
00678              # If for example four lines are overlaid e.g I Q U V
00679              # 'I' will be 'red', 'Q' will be 'green', U will be 'blue'
00680              # and 'V' will be 'red' again.
00681         """
00682         #if isinstance(colmap,str):
00683         #    colmap = colmap.split()
00684         #self._plotter.palette(0, colormap=colmap)
00685         self._colormap = colmap
00686         if refresh and self._data: self.plot(self._data)
00687 
00688     # alias for english speakers
00689     set_colours = set_colors
00690 
00691     def set_histogram(self, hist=True, linewidth=None, refresh=True):
00692         """
00693         Enable/Disable histogram-like plotting.
00694         Parameters:
00695             hist:        True (default) or False. The fisrt default
00696                          is taken from the .asaprc setting
00697                          plotter.histogram
00698             linewidth:   a line width
00699             refresh:     True (default) or False. If True, the plot is
00700                          replotted based on the new parameter setting(s).
00701                          Otherwise,the parameter(s) are set without replotting.
00702         """
00703         self._hist = hist
00704         if isinstance(linewidth, float) or isinstance(linewidth, int):
00705             from matplotlib import rc as rcp
00706             rcp('lines', linewidth=linewidth)
00707         if refresh and self._data: self.plot(self._data)
00708 
00709     def set_linestyles(self, linestyles=None, linewidth=None, refresh=True):
00710         """
00711         Set the linestyles to be used. The plotter will cycle through
00712         these linestyles when lines are overlaid (stacking mode) AND
00713         only one color has been set.
00714         Parameters:
00715             linestyles:      a list of linestyles to use.
00716                              'line', 'dashed', 'dotted', 'dashdot',
00717                              'dashdotdot' and 'dashdashdot' are
00718                              possible
00719             linewidth:       a line width
00720             refresh:         True (default) or False. If True, the plot is
00721                              replotted based on the new parameter setting(s).
00722                              Otherwise,the parameter(s) are set without replotting.
00723         Example:
00724              plotter.set_colors('black')
00725              plotter.set_linestyles('line dashed dotted dashdot')
00726              # If for example four lines are overlaid e.g I Q U V
00727              # 'I' will be 'solid', 'Q' will be 'dashed',
00728              # U will be 'dotted' and 'V' will be 'dashdot'.
00729         """
00730         #if isinstance(linestyles,str):
00731         #    linestyles = linestyles.split()
00732         #self._plotter.palette(color=0,linestyle=0,linestyles=linestyles)
00733         self._linestyles = linestyles
00734         if isinstance(linewidth, float) or isinstance(linewidth, int):
00735             from matplotlib import rc as rcp
00736             rcp('lines', linewidth=linewidth)
00737         if refresh and self._data: self.plot(self._data)
00738 
00739     def set_font(self, refresh=True,**kwargs):
00740         """
00741         Set font properties.
00742         Parameters:
00743             family:    one of 'sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'
00744             style:     one of 'normal' (or 'roman'), 'italic'  or 'oblique'
00745             weight:    one of 'normal or 'bold'
00746             size:      the 'general' font size, individual elements can be adjusted
00747                        seperately
00748             refresh:   True (default) or False. If True, the plot is
00749                        replotted based on the new parameter setting(s).
00750                        Otherwise,the parameter(s) are set without replotting.
00751         """
00752         from matplotlib import rc as rcp
00753         fdict = {}
00754         for k,v in kwargs.iteritems():
00755             if v:
00756                 fdict[k] = v
00757         self._fp = FontProperties(**fdict)
00758         if refresh and self._data: self.plot(self._data)
00759 
00760     def set_margin(self,margin=[],refresh=True):
00761         """
00762         Set margins between subplots and plot edges.
00763         Parameters:
00764             margin:   a list of margins in figure coordinate (0-1),
00765                       i.e., fraction of the figure width or height.
00766                       The order of elements should be:
00767                       [left, bottom, right, top, horizontal space btw panels,
00768                       vertical space btw panels].
00769             refresh:  True (default) or False. If True, the plot is
00770                       replotted based on the new parameter setting(s).
00771                       Otherwise,the parameter(s) are set without replotting.
00772         Note
00773         * When margin is not specified, the values are reset to the defaults
00774           of matplotlib.
00775         * If any element is set to be None, the current value is adopted.
00776         """
00777         if margin == []: self._margins=self._reset_margin()
00778         else:
00779             self._margins=[None]*6
00780             self._margins[0:len(margin)]=margin
00781         #print "panel margin set to ",self._margins
00782         if refresh and self._data: self.plot(self._data)
00783 
00784     def _reset_margin(self):
00785         ks=map(lambda x: 'figure.subplot.'+x,
00786                ['left','bottom','right','top','hspace','wspace'])
00787         return map(matplotlib.rcParams.get,ks)
00788 
00789     def plot_lines(self, linecat=None, doppler=0.0, deltachan=10, rotate=90.0,
00790                    location=None):
00791         """
00792         Plot a line catalog.
00793         Parameters:
00794             linecat:      the linecatalog to plot
00795             doppler:      the velocity shift to apply to the frequencies
00796             deltachan:    the number of channels to include each side of the
00797                           line to determine a local maximum/minimum
00798             rotate:       the rotation (in degrees) for the text label (default 90.0)
00799             location:     the location of the line annotation from the 'top',
00800                           'bottom' or alternate (None - the default)
00801         Notes:
00802         If the spectrum is flagged no line will be drawn in that location.
00803         """
00804         errmsg = "Cannot plot spectral lines. Need to plot scantable first."
00805         self._assert_plotter(action="halt",errmsg=errmsg)
00806         if not self._data:
00807             raise RuntimeError("No scantable has been plotted yet.")
00808         from asap._asap import linecatalog
00809         if not isinstance(linecat, linecatalog):
00810             raise ValueError("'linecat' isn't of type linecatalog.")
00811         if not self._data.get_unit().endswith("Hz"):
00812             raise RuntimeError("Can only overlay linecatalogs when data is in frequency.")
00813         from numpy import ma
00814         for j in range(len(self._plotter.subplots)):
00815             self._plotter.subplot(j)
00816             lims = self._plotter.axes.get_xlim()
00817             for row in range(linecat.nrow()):
00818                 # get_frequency returns MHz
00819                 base = { "GHz": 1000.0, "MHz": 1.0, "Hz": 1.0e-6 }
00820                 restf = linecat.get_frequency(row)/base[self._data.get_unit()]
00821                 c = 299792.458
00822                 freq = restf*(1.0-doppler/c)
00823                 if lims[0] < freq < lims[1]:
00824                     if location is None:
00825                         loc = 'bottom'
00826                         if row%2: loc='top'
00827                     else: loc = location
00828                     maxys = []
00829                     for line in self._plotter.axes.lines:
00830                         v = line._x
00831                         asc = v[0] < v[-1]
00832 
00833                         idx = None
00834                         if not asc:
00835                             if v[len(v)-1] <= freq <= v[0]:
00836                                 i = len(v)-1
00837                                 while i>=0 and v[i] < freq:
00838                                     idx = i
00839                                     i-=1
00840                         else:
00841                            if v[0] <= freq <= v[len(v)-1]:
00842                                 i = 0
00843                                 while  i<len(v) and v[i] < freq:
00844                                     idx = i
00845                                     i+=1
00846                         if idx is not None:
00847                             lower = idx - deltachan
00848                             upper = idx + deltachan
00849                             if lower < 0: lower = 0
00850                             if upper > len(v): upper = len(v)
00851                             s = slice(lower, upper)
00852                             y = line._y[s]
00853                             maxy = ma.maximum(y)
00854                             if isinstance( maxy, float):
00855                                 maxys.append(maxy)
00856                     if len(maxys):
00857                         peak = max(maxys)
00858                         if peak > self._plotter.axes.get_ylim()[1]:
00859                             loc = 'bottom'
00860                     else:
00861                         continue
00862                     self._plotter.vline_with_label(freq, peak,
00863                                                    linecat.get_name(row),
00864                                                    location=loc, rotate=rotate)
00865         self._plotter.show(hardrefresh=False)
00866 
00867 
00868     def save(self, filename=None, orientation=None, dpi=None):
00869         """
00870         Save the plot to a file. The known formats are 'png', 'ps', 'eps'.
00871         Parameters:
00872              filename:    The name of the output file. This is optional
00873                           and autodetects the image format from the file
00874                           suffix. If non filename is specified a file
00875                           called 'yyyymmdd_hhmmss.png' is created in the
00876                           current directory.
00877              orientation: optional parameter for postscript only (not eps).
00878                           'landscape', 'portrait' or None (default) are valid.
00879                           If None is choosen for 'ps' output, the plot is
00880                           automatically oriented to fill the page.
00881              dpi:         The dpi of the output non-ps plot
00882         """
00883         errmsg = "Cannot save figure. Need to plot first."
00884         self._assert_plotter(action="halt",errmsg=errmsg)
00885         
00886         self._plotter.save(filename,orientation,dpi)
00887         return
00888 
00889     @asaplog_post_dec
00890     def set_mask(self, mask=None, selection=None, refresh=True):
00891         """
00892         Set a plotting mask for a specific polarization.
00893         This is useful for masking out 'noise' Pangle outside a source.
00894         Parameters:
00895              mask:           a mask from scantable.create_mask
00896              selection:      the spectra to apply the mask to.
00897              refresh:        True (default) or False. If True, the plot is
00898                              replotted based on the new parameter setting(s).
00899                              Otherwise,the parameter(s) are set without replotting.
00900         Example:
00901              select = selector()
00902              select.setpolstrings('Pangle')
00903              plotter.set_mask(mymask, select)
00904         """
00905         if not self._data:
00906             msg = "Can only set mask after a first call to plot()"
00907             raise RuntimeError(msg)
00908         if len(mask):
00909             if isinstance(mask, list) or isinstance(mask, tuple):
00910                 self._usermask = array(mask)
00911             else:
00912                 self._usermask = mask
00913         if mask is None and selection is None:
00914             self._usermask = []
00915             self._maskselection = None
00916         if isinstance(selection, selector):
00917             self._maskselection = {'b': selection.get_beams(),
00918                                    's': selection.get_scans(),
00919                                    'i': selection.get_ifs(),
00920                                    'p': selection.get_pols(),
00921                                    't': [] }
00922         else:
00923             self._maskselection = None
00924         if refresh: self.plot(self._data)
00925 
00926     def _slice_indeces(self, data):
00927         mn = self._minmaxx[0]
00928         mx = self._minmaxx[1]
00929         asc = data[0] < data[-1]
00930         start=0
00931         end = len(data)-1
00932         inc = 1
00933         if not asc:
00934             start = len(data)-1
00935             end = 0
00936             inc = -1
00937         # find min index
00938         #while start > 0 and data[start] < mn:
00939         #    start+= inc
00940         minind=start
00941         for ind in xrange(start,end+inc,inc):
00942             if data[ind] > mn: break
00943             minind=ind
00944         # find max index
00945         #while end > 0 and data[end] > mx:
00946         #    end-=inc
00947         #if end > 0: end +=1
00948         maxind=end
00949         for ind in xrange(end,start-inc,-inc):
00950             if data[ind] < mx: break
00951             maxind=ind
00952         start=minind
00953         end=maxind
00954         if start > end:
00955             return end,start+1
00956         elif start < end:
00957             return start,end+1
00958         else:
00959             return start,end
00960 
00961     def _reset(self):
00962         self._usermask = []
00963         self._usermaskspectra = None
00964         self._offset = None
00965         self.set_selection(None, False)
00966         self._reset_header()
00967 
00968     def _reset_header(self):
00969         self._headtext={'string': None, 'textobj': None}
00970 
00971     def _plot(self, scan):
00972         savesel = scan.get_selection()
00973         sel = savesel +  self._selection
00974         order = self._get_sortstring([self._panelling,self._stacking])
00975         if order:
00976             sel.set_order(order)
00977         scan.set_selection(sel)
00978         d = {'b': scan.getbeam, 's': scan.getscan,
00979              'i': scan.getif, 'p': scan.getpol, 't': scan.get_time,
00980              'r': int}#, '_r': int}
00981 
00982         polmodes = dict(zip(sel.get_pols(), sel.get_poltypes()))
00983         # this returns either a tuple of numbers or a length  (ncycles)
00984         # convert this into lengths
00985         n0,nstack0 = self._get_selected_n(scan)
00986         if isinstance(n0, int): n = n0
00987         else: n = len(n0)
00988         if isinstance(nstack0, int): nstack = nstack0
00989         else: nstack = len(nstack0)
00990         # In case of row stacking
00991         rowstack = False
00992         titlemode = self._panelling
00993         if self._stacking == "r" and self._panelling != "r":
00994             rowstack = True
00995             titlemode = '_r'
00996         nptot = n
00997         maxpanel, maxstack = 16,16
00998         if nstack > maxstack:
00999             msg ="Scan to be overlayed contains more than %d selections.\n" \
01000                   "Selecting first %d selections..." % (maxstack, maxstack)
01001             asaplog.push(msg)
01002             asaplog.post('WARN')
01003             nstack = min(nstack,maxstack)
01004         #n = min(n-self._ipanel-1,maxpanel)
01005         n = n-self._ipanel-1
01006 
01007         ganged = False
01008         if n > 1:
01009             ganged = rcParams['plotter.ganged']
01010             if self._panelling == 'i':
01011                 ganged = False
01012             if self._rows and self._cols:
01013                 n = min(n,self._rows*self._cols)
01014                 self._plotter.set_panels(rows=self._rows,cols=self._cols,
01015                                          nplots=n,margin=self._margins,
01016                                          ganged=ganged)
01017             else:
01018                 n = min(n,maxpanel)
01019                 self._plotter.set_panels(rows=n,cols=0,nplots=n,
01020                                          margin=self._margins,ganged=ganged)
01021         else:
01022             self._plotter.set_panels(margin=self._margins)
01023         #r = 0
01024         r = self._startrow
01025         nr = scan.nrow()
01026         a0,b0 = -1,-1
01027         allxlim = []
01028         allylim = []
01029         #newpanel=True
01030         newpanel=False
01031         panelcount,stackcount = 0,0
01032         # If this is not the first page
01033         if r > 0:
01034             # panelling value of the prev page 
01035             a0 = d[self._panelling](r-1)
01036             # set the initial stackcount large not to plot
01037             # the start row automatically
01038             stackcount = nstack
01039 
01040         while r < nr:
01041             a = d[self._panelling](r)
01042             b = d[self._stacking](r)
01043             if a > a0 and panelcount < n:
01044                 if n > 1:
01045                     self._plotter.subplot(panelcount)
01046                 self._plotter.palette(0)
01047                 #title
01048                 xlab = self._abcissa and self._abcissa[panelcount] \
01049                        or scan._getabcissalabel()
01050                 if self._offset and not self._abcissa:
01051                     xlab += " (relative)"
01052                 ylab = self._ordinate and self._ordinate[panelcount] \
01053                        or scan._get_ordinate_label()
01054                 self._plotter.set_axes('xlabel', xlab)
01055                 self._plotter.set_axes('ylabel', ylab)
01056                 #lbl = self._get_label(scan, r, self._panelling, self._title)
01057                 lbl = self._get_label(scan, r, titlemode, self._title)
01058                 if isinstance(lbl, list) or isinstance(lbl, tuple):
01059                     if 0 <= panelcount < len(lbl):
01060                         lbl = lbl[panelcount]
01061                     else:
01062                         # get default label
01063                         #lbl = self._get_label(scan, r, self._panelling, None)
01064                         lbl = self._get_label(scan, r, titlemode, None)
01065                 self._plotter.set_axes('title',lbl)
01066                 newpanel = True
01067                 stackcount = 0
01068                 panelcount += 1
01069                 # save the start row to plot this panel for future revisit.
01070                 if self._panelling != 'r' and \
01071                        len(self._panelrows) < self._ipanel+1+panelcount:
01072                     self._panelrows += [r]
01073                     
01074             #if (b > b0 or newpanel) and stackcount < nstack:
01075             if stackcount < nstack and (newpanel or \
01076                                             rowstack or (a == a0 and b > b0)):
01077                 y = []
01078                 if len(polmodes):
01079                     y = scan._getspectrum(r, polmodes[scan.getpol(r)])
01080                 else:
01081                     y = scan._getspectrum(r)
01082                 # flag application
01083                 mr = scan._getflagrow(r)
01084                 from numpy import ma, array
01085                 if mr:
01086                     y = ma.masked_array(y,mask=mr)
01087                 else:
01088                     m = scan._getmask(r)
01089                     from numpy import logical_not, logical_and
01090                     if self._maskselection and len(self._usermask) == len(m):
01091                         if d[self._stacking](r) in self._maskselection[self._stacking]:
01092                             m = logical_and(m, self._usermask)
01093                     y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
01094 
01095                 x = array(scan._getabcissa(r))
01096                 if self._offset:
01097                     x += self._offset
01098                 if self._minmaxx is not None:
01099                     s,e = self._slice_indeces(x)
01100                     x = x[s:e]
01101                     y = y[s:e]
01102                 if len(x) > 1024 and rcParams['plotter.decimate']:
01103                     fac = len(x)/1024
01104                     x = x[::fac]
01105                     y = y[::fac]
01106                 llbl = self._get_label(scan, r, self._stacking, self._lmap)
01107                 if isinstance(llbl, list) or isinstance(llbl, tuple):
01108                     if 0 <= stackcount < len(llbl):
01109                         # use user label
01110                         llbl = llbl[stackcount]
01111                     else:
01112                         # get default label
01113                         llbl = self._get_label(scan, r, self._stacking, None)
01114                 self._plotter.set_line(label=llbl)
01115                 plotit = self._plotter.plot
01116                 if self._hist: plotit = self._plotter.hist
01117                 if len(x) > 0 and not mr:
01118                     plotit(x,y)
01119                     xlim= self._minmaxx or [min(x),max(x)]
01120                     allxlim += xlim
01121                     ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
01122                     allylim += ylim
01123                 else:
01124                     xlim = self._minmaxx or []
01125                     allxlim += xlim
01126                     ylim= self._minmaxy or []
01127                     allylim += ylim
01128                 stackcount += 1
01129                 a0=a
01130                 b0=b
01131                 # last in colour stack -> autoscale x
01132                 if stackcount == nstack and len(allxlim) > 0:
01133                     allxlim.sort()
01134                     self._plotter.subplots[panelcount-1]['axes'].set_xlim([allxlim[0],allxlim[-1]])
01135                     if ganged:
01136                         allxlim = [allxlim[0],allxlim[-1]]
01137                     else:
01138                         # clear
01139                         allxlim =[]
01140 
01141             newpanel = False
01142             #a0=a
01143             #b0=b
01144             # ignore following rows
01145             if (panelcount == n and stackcount == nstack) or (r == nr-1):
01146                 # last panel -> autoscale y if ganged
01147                 #if rcParams['plotter.ganged'] and len(allylim) > 0:
01148                 if ganged and len(allylim) > 0:
01149                     allylim.sort()
01150                     self._plotter.set_limits(ylim=[allylim[0],allylim[-1]])
01151                 break
01152             r+=1 # next row
01153 
01154         # save the current counter for multi-page plotting
01155         self._startrow = r+1
01156         self._ipanel += panelcount
01157         if self.casabar_exists():
01158             if self._ipanel >= nptot-1:
01159                 self._plotter.figmgr.casabar.disable_next()
01160             else:
01161                 self._plotter.figmgr.casabar.enable_next()
01162             if self._ipanel + 1 - panelcount > 0:
01163                 self._plotter.figmgr.casabar.enable_prev()
01164             else:
01165                 self._plotter.figmgr.casabar.disable_prev()
01166 
01167         #reset the selector to the scantable's original
01168         scan.set_selection(savesel)
01169 
01170         #temporary switch-off for older matplotlib
01171         #if self._fp is not None:
01172         if self._fp is not None and getattr(self._plotter.figure,'findobj',False):
01173             for o in self._plotter.figure.findobj(Text):
01174                 o.set_fontproperties(self._fp)
01175 
01176     def _get_sortstring(self, lorders):
01177         d0 = {'s': 'SCANNO', 'b': 'BEAMNO', 'i':'IFNO',
01178               'p': 'POLNO', 'c': 'CYCLENO', 't' : 'TIME', 'r':None, '_r':None }
01179         if not (type(lorders) == list) and not (type(lorders) == tuple):
01180             return None
01181         if len(lorders) > 0:
01182             lsorts = []
01183             for order in lorders:
01184                 if order == "r":
01185                     # don't sort if row panelling/stacking
01186                     return None
01187                 ssort = d0[order]
01188                 if ssort:
01189                     lsorts.append(ssort)
01190             return lsorts
01191         return None
01192 
01193     def set_selection(self, selection=None, refresh=True, **kw):
01194         """
01195         Parameters:
01196             selection:  a selector object (default unset the selection)
01197             refresh:    True (default) or False. If True, the plot is
01198                         replotted based on the new parameter setting(s).
01199                         Otherwise,the parameter(s) are set without replotting.
01200         """
01201         if selection is None:
01202             # reset
01203             if len(kw) == 0:
01204                 self._selection = selector()
01205             else:
01206                 # try keywords
01207                 for k in kw:
01208                     if k not in selector.fields:
01209                         raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
01210                 self._selection = selector(**kw)
01211         elif isinstance(selection, selector):
01212             self._selection = selection
01213         else:
01214             raise TypeError("'selection' is not of type selector")
01215 
01216         order = self._get_sortstring([self._panelling,self._stacking])
01217         if order:
01218             self._selection.set_order(order)
01219         if refresh and self._data: 
01220             self.plot()
01221 
01222     def _get_selected_n(self, scan):
01223         d1 = {'b': scan.getbeamnos, 's': scan.getscannos,
01224              'i': scan.getifnos, 'p': scan.getpolnos, 't': scan.ncycle,
01225              'r': scan.nrow}#, '_r': False}
01226         d2 = { 'b': self._selection.get_beams(),
01227                's': self._selection.get_scans(),
01228                'i': self._selection.get_ifs(),
01229                'p': self._selection.get_pols(),
01230                't': self._selection.get_cycles(),
01231                'r': False}#, '_r': 1}
01232         n =  d2[self._panelling] or d1[self._panelling]()
01233         nstack = d2[self._stacking] or d1[self._stacking]()
01234         # handle row panelling/stacking
01235         if self._panelling == 'r':
01236             nstack = 1
01237         elif self._stacking == 'r':
01238             n = 1
01239         return n,nstack
01240 
01241     def _get_label(self, scan, row, mode, userlabel=None):
01242         if isinstance(userlabel, list) and len(userlabel) == 0:
01243             userlabel = " "
01244         pms = dict(zip(self._selection.get_pols(),self._selection.get_poltypes()))
01245         if len(pms):
01246             poleval = scan._getpollabel(scan.getpol(row),pms[scan.getpol(row)])
01247         else:
01248             poleval = scan._getpollabel(scan.getpol(row),scan.poltype())
01249         d = {'b': "Beam "+str(scan.getbeam(row)),
01250              #'s': scan._getsourcename(row),
01251              's': "Scan "+str(scan.getscan(row))+\
01252                   " ("+str(scan._getsourcename(row))+")",
01253              'i': "IF"+str(scan.getif(row)),
01254              'p': poleval,
01255              't': str(scan.get_time(row)),
01256              'r': "row "+str(row),
01257              #'_r': str(scan.get_time(row))+",\nIF"+str(scan.getif(row))+", "+poleval+", Beam"+str(scan.getbeam(row)) }
01258              '_r': "" }
01259         return userlabel or d[mode]
01260 
01261     def plotazel(self, scan=None, outfile=None):
01262         """
01263         plot azimuth and elevation versus time of a scantable
01264         """
01265         visible = rcParams['plotter.gui']
01266         from matplotlib import pylab as PL
01267         from matplotlib.dates import DateFormatter
01268         from pytz import timezone
01269         from matplotlib.dates import HourLocator, MinuteLocator,SecondLocator, DayLocator
01270         from matplotlib.ticker import MultipleLocator
01271         from numpy import array, pi
01272         if not visible or not self._visible:
01273             PL.ioff()
01274             from matplotlib.backends.backend_agg import FigureCanvasAgg
01275             PL.gcf().canvas.switch_backends(FigureCanvasAgg)
01276         self._data = scan
01277         dates = self._data.get_time(asdatetime=True)
01278         t = PL.date2num(dates)
01279         tz = timezone('UTC')
01280         PL.cla()
01281         PL.ioff()
01282         PL.clf()
01283         # Adjust subplot margins
01284         if not self._margins or len(self._margins) != 6:
01285             self.set_margin(refresh=False)
01286         lef, bot, rig, top, wsp, hsp = self._margins
01287         PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
01288                                  wspace=wsp,hspace=hsp)
01289 
01290         tdel = max(t) - min(t)
01291         ax = PL.subplot(2,1,1)
01292         el = array(self._data.get_elevation())*180./pi
01293         PL.ylabel('El [deg.]')
01294         dstr = dates[0].strftime('%Y/%m/%d')
01295         if tdel > 1.0:
01296             dstr2 = dates[len(dates)-1].strftime('%Y/%m/%d')
01297             dstr = dstr + " - " + dstr2
01298             majloc = DayLocator()
01299             minloc = HourLocator(range(0,23,12))
01300             timefmt = DateFormatter("%b%d")
01301         elif tdel > 24./60.:
01302             timefmt = DateFormatter('%H:%M')
01303             majloc = HourLocator()
01304             minloc = MinuteLocator(30)
01305         else:
01306             timefmt = DateFormatter('%H:%M')
01307             majloc = MinuteLocator(interval=5)
01308             minloc = SecondLocator(30)
01309 
01310         PL.title(dstr)
01311         if tdel == 0.0:
01312             th = (t - PL.floor(t))*24.0
01313             PL.plot(th,el,'o',markersize=2, markerfacecolor='b', markeredgecolor='b')
01314         else:
01315             PL.plot_date(t,el,'o', markersize=2, markerfacecolor='b', markeredgecolor='b',tz=tz)
01316             #ax.grid(True)
01317             ax.xaxis.set_major_formatter(timefmt)
01318             ax.xaxis.set_major_locator(majloc)
01319             ax.xaxis.set_minor_locator(minloc)
01320         ax.yaxis.grid(True)
01321         yloc = MultipleLocator(30)
01322         ax.set_ylim(0,90)
01323         ax.yaxis.set_major_locator(yloc)
01324         if tdel > 1.0:
01325             labels = ax.get_xticklabels()
01326         #    PL.setp(labels, fontsize=10, rotation=45)
01327             PL.setp(labels, fontsize=10)
01328 
01329         # Az plot
01330         az = array(self._data.get_azimuth())*180./pi
01331         if min(az) < 0:
01332             for irow in range(len(az)):
01333                 if az[irow] < 0: az[irow] += 360.0
01334 
01335         ax2 = PL.subplot(2,1,2)
01336         #PL.xlabel('Time (UT [hour])')
01337         PL.ylabel('Az [deg.]')
01338         if tdel == 0.0:
01339             PL.plot(th,az,'o',markersize=2, markeredgecolor='b',markerfacecolor='b')
01340         else:
01341             PL.plot_date(t,az,'o', markersize=2,markeredgecolor='b',markerfacecolor='b',tz=tz)
01342             ax2.xaxis.set_major_formatter(timefmt)
01343             ax2.xaxis.set_major_locator(majloc)
01344             ax2.xaxis.set_minor_locator(minloc)
01345         #ax2.grid(True)
01346         ax2.set_ylim(0,360)
01347         ax2.yaxis.grid(True)
01348         #hfmt = DateFormatter('%H')
01349         #hloc = HourLocator()
01350         yloc = MultipleLocator(60)
01351         ax2.yaxis.set_major_locator(yloc)
01352         if tdel > 1.0:
01353             labels = ax2.get_xticklabels()
01354             PL.setp(labels, fontsize=10)
01355             PL.xlabel('Time (UT [day])')
01356         else:
01357             PL.xlabel('Time (UT [hour])')
01358 
01359         PL.ion()
01360         PL.draw()
01361         if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
01362         if (outfile is not None):
01363            PL.savefig(outfile)
01364 
01365     def plotpointing(self, scan=None, outfile=None):
01366         """
01367         plot telescope pointings
01368         """
01369         visible = rcParams['plotter.gui']
01370         from matplotlib import pylab as PL
01371         from numpy import array, pi
01372         if not visible or not self._visible:
01373             PL.ioff()
01374             from matplotlib.backends.backend_agg import FigureCanvasAgg
01375             PL.gcf().canvas.switch_backends(FigureCanvasAgg)
01376         self._data = scan
01377         dir = array(self._data.get_directionval()).transpose()
01378         ra = dir[0]*180./pi
01379         dec = dir[1]*180./pi
01380         PL.cla()
01381         #PL.ioff()
01382         PL.clf()
01383         # Adjust subplot margins
01384         if not self._margins or len(self._margins) != 6:
01385             self.set_margin(refresh=False)
01386         lef, bot, rig, top, wsp, hsp = self._margins
01387         PL.gcf().subplots_adjust(left=lef,bottom=bot,right=rig,top=top,
01388                                  wspace=wsp,hspace=hsp)
01389         ax = PL.gca()
01390         #ax = PL.axes([0.1,0.1,0.8,0.8])
01391         #ax = PL.axes([0.1,0.1,0.8,0.8])
01392         ax.set_aspect('equal')
01393         PL.plot(ra, dec, 'b,')
01394         PL.xlabel('RA [deg.]')
01395         PL.ylabel('Declination [deg.]')
01396         PL.title('Telescope pointings')
01397         [xmin,xmax,ymin,ymax] = PL.axis()
01398         PL.axis([xmax,xmin,ymin,ymax])
01399         PL.ion()
01400         PL.draw()
01401         if matplotlib.get_backend() == 'Qt4Agg': PL.gcf().show()
01402         if (outfile is not None):
01403            PL.savefig(outfile)
01404 
01405     # plot total power data
01406     # plotting in time is not yet implemented..
01407     @asaplog_post_dec
01408     def plottp(self, scan=None):
01409         self._assert_plotter(action="reload")
01410         self._plotter.hold()
01411         self._plotter.clear()
01412         from asap import scantable
01413         if not self._data and not scan:
01414             msg = "Input is not a scantable"
01415             raise TypeError(msg)
01416         if isinstance(scan, scantable):
01417             if self._data is not None:
01418                 if scan != self._data:
01419                     self._data = scan
01420                     # reset
01421                     self._reset()
01422             else:
01423                 self._data = scan
01424                 self._reset()
01425         # ranges become invalid when abcissa changes?
01426         #if self._abcunit and self._abcunit != self._data.get_unit():
01427         #    self._minmaxx = None
01428         #    self._minmaxy = None
01429         #    self._abcunit = self._data.get_unit()
01430         #    self._datamask = None
01431 
01432         # Adjust subplot margins
01433         if not self._margins or len(self._margins) !=6:
01434             self.set_margin(refresh=False)
01435         lef, bot, rig, top, wsp, hsp = self._margins
01436         self._plotter.figure.subplots_adjust(
01437             left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
01438         if self.casabar_exists(): self._plotter.figmgr.casabar.disable_button()
01439         self._plottp(self._data)
01440         if self._minmaxy is not None:
01441             self._plotter.set_limits(ylim=self._minmaxy)
01442         self._plotter.release()
01443         self._plotter.tidy()
01444         self._plotter.show(hardrefresh=False)
01445         return
01446 
01447     def _plottp(self,scan):
01448         """
01449         private method for plotting total power data
01450         """
01451         from numpy import ma, array, arange, logical_not
01452         r=0
01453         nr = scan.nrow()
01454         a0,b0 = -1,-1
01455         allxlim = []
01456         allylim = []
01457         y=[]
01458         self._plotter.set_panels()
01459         self._plotter.palette(0)
01460         #title
01461         #xlab = self._abcissa and self._abcissa[panelcount] \
01462         #       or scan._getabcissalabel()
01463         #ylab = self._ordinate and self._ordinate[panelcount] \
01464         #       or scan._get_ordinate_label()
01465         xlab = self._abcissa or 'row number' #or Time
01466         ylab = self._ordinate or scan._get_ordinate_label()
01467         self._plotter.set_axes('xlabel',xlab)
01468         self._plotter.set_axes('ylabel',ylab)
01469         lbl = self._get_label(scan, r, 's', self._title)
01470         if isinstance(lbl, list) or isinstance(lbl, tuple):
01471         #    if 0 <= panelcount < len(lbl):
01472         #        lbl = lbl[panelcount]
01473         #    else:
01474                 # get default label
01475              lbl = self._get_label(scan, r, self._panelling, None)
01476         self._plotter.set_axes('title',lbl)
01477         y=array(scan._get_column(scan._getspectrum,-1))
01478         m = array(scan._get_column(scan._getmask,-1))
01479         y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
01480         x = arange(len(y))
01481         # try to handle spectral data somewhat...
01482         l,m = y.shape
01483         if m > 1:
01484             y=y.mean(axis=1)
01485         plotit = self._plotter.plot
01486         llbl = self._get_label(scan, r, self._stacking, None)
01487         self._plotter.set_line(label=llbl)
01488         if len(x) > 0:
01489             plotit(x,y)
01490 
01491 
01492     # forwards to matplotlib.Figure.text
01493     def figtext(self, *args, **kwargs):
01494         """
01495         Add text to figure at location x,y (relative 0-1 coords).
01496         This method forwards *args and **kwargs to a Matplotlib method,
01497         matplotlib.Figure.text.
01498         See the method help for detailed information.
01499         """
01500         self._assert_plotter(action="reload")
01501         self._plotter.text(*args, **kwargs)
01502     # end matplotlib.Figure.text forwarding function
01503 
01504 
01505     # printing header information
01506     @asaplog_post_dec
01507     def print_header(self, plot=True, fontsize=9, logger=False, selstr='', extrastr=''):
01508         """
01509         print data (scantable) header on the plot and/or logger.
01510         To plot the header on the plot, this method should be called after
01511         plotting spectra by the method, asapplotter.plot.
01512         Parameters:
01513             plot:      whether or not print header info on the plot.
01514             fontsize:  header font size (valid only plot=True)
01515             logger:    whether or not print header info on the logger.
01516             selstr:    additional selection string (not verified)
01517             extrastr:  additional string to print at the beginning (not verified)
01518         """
01519         if not plot and not logger:
01520             return
01521         if not self._data:
01522             raise RuntimeError("No scantable has been set yet.")
01523         # Now header will be printed on plot and/or logger.
01524         # Get header information and format it.
01525         ssum=self._data._list_header()
01526         # Print Observation header to the upper-left corner of plot
01527         headstr=[ssum[0:ssum.find('Obs. Type:')]]
01528         headstr.append(ssum[ssum.find('Obs. Type:'):ssum.find('Flux Unit:')])
01529         if extrastr != '':
01530             headstr[0]=extrastr+'\n'+headstr[0]
01531             self._headtext['extrastr'] = extrastr
01532         if selstr != '':
01533             selstr += '\n'
01534             self._headtext['selstr'] = selstr
01535         ssel=(selstr+self._data.get_selection().__str__()+self._selection.__str__() or 'none')
01536         headstr.append('***Selections***\n'+ssel)
01537 
01538         if plot:
01539             errmsg = "Can plot header only after the first call to plot()."
01540             self._assert_plotter(action="halt",errmsg=errmsg)
01541             self._plotter.hold()
01542             self._header_plot(headstr,fontsize=fontsize)
01543             import time
01544             self._plotter.figure.text(0.99,0.01,
01545                             time.strftime("%a %d %b %Y  %H:%M:%S %Z"),
01546                             horizontalalignment='right',
01547                             verticalalignment='bottom',fontsize=8)
01548             self._plotter.release()
01549         if logger:
01550             selstr = "Selections:    "+ssel
01551             asaplog.push("----------------\n  Plot Summary\n----------------")
01552             asaplog.push(extrastr)
01553             asaplog.push(ssum[0:ssum.find('Selection:')]\
01554                          + selstr)
01555         self._headtext['string'] = headstr
01556         del ssel, ssum, headstr
01557 
01558     def _header_plot(self, texts, fontsize=9):
01559         self._headtext['textobj']=[]
01560         nstcol=len(texts)
01561         for i in range(nstcol):
01562             self._headtext['textobj'].append(
01563                 self._plotter.figure.text(0.03+float(i)/nstcol,0.98,
01564                                           texts[i],
01565                                           horizontalalignment='left',
01566                                           verticalalignment='top',
01567                                           fontsize=fontsize))
01568 
01569     def clear_header(self):
01570         if not self._headtext['textobj']:
01571             asaplog.push("No header has been plotted. Exit without any operation")
01572             asaplog.post("WARN")
01573         elif self._assert_plotter(action="status"):
01574             self._plotter.hold()
01575             for textobj in self._headtext['textobj']:
01576                 #if textobj.get_text() in self._headstring:
01577                 try:
01578                     textobj.remove()
01579                 except NotImplementedError:
01580                     self._plotter.figure.texts.pop(self._plotter.figure.texts.index(textobj))
01581             self._plotter.release()
01582         self._reset_header()
01583 
01584     # plot spectra by pointing
01585     @asaplog_post_dec
01586     def plotgrid(self, scan=None,center=None,spacing=None,rows=None,cols=None):
01587         """
01588         Plot spectra based on direction.
01589         
01590         Parameters:
01591             scan:      a scantable to plot
01592             center:    the grid center direction (a list) of plots in the
01593                        unit of DIRECTION column.
01594                        (default) the center of map region
01595             spacing:   a list of horizontal (R.A.) and vertical (Dec.)
01596                        spacing in the unit of DIRECTION column.
01597                        (default) Calculated by the extent of map region and
01598                        the number of rows and cols to cover
01599             rows:      number of panels (grid points) in horizontal direction
01600             cols:      number of panels (grid points) in vertical direction
01601 
01602         Note:
01603         - Only the first IFNO, POLNO, and BEAM in the scantable will be
01604         plotted.
01605         - This method doesn't re-grid and average spectra in scantable. Use
01606         asapgrid module to re-grid spectra before plotting with this method.
01607         Only the first spectrum is plotted in case there are multiple
01608         spectra which belong to a grid.
01609         """
01610         from asap import scantable
01611         from numpy import array, ma, cos
01612         if not self._data and not scan:
01613             msg = "No scantable is specified to plot"
01614             raise TypeError(msg)
01615         if scan: 
01616             self.set_data(scan, refresh=False)
01617             del scan
01618 
01619         # Rows and cols
01620         if rows:
01621             self._rows = int(rows)
01622         else:
01623             msg = "Number of rows to plot are not specified. "
01624             if self._rows:
01625                 msg += "Using previous value = %d" % (self._rows)
01626                 asaplog.push(msg)
01627             else:
01628                 self._rows = 1
01629                 msg += "Setting rows = %d" % (self._rows)
01630                 asaplog.post()
01631                 asaplog.push(msg)
01632                 asaplog.post("WARN")
01633         if cols:
01634             self._cols = int(cols)
01635         else:
01636             msg = "Number of cols to plot are not specified. "
01637             if self._cols:
01638                 msg += "Using previous value = %d" % (self._cols)
01639                 asaplog.push(msg)
01640             else:
01641                 self._cols = 1
01642                 msg += "Setting cols = %d" % (self._cols)
01643                 asaplog.post()
01644                 asaplog.push(msg)
01645                 asaplog.post("WARN")
01646 
01647         # Center and spacing
01648         if center is None:
01649             #asaplog.post()
01650             asaplog.push("Grid center is not specified. Automatically calculated from pointing center.")
01651             #asaplog.post("WARN")
01652             dirarr = array(self._data.get_directionval()).transpose()
01653             #center = [dirarr[0].mean(), dirarr[1].mean()]
01654             center = [0.5*(dirarr[0].max() + dirarr[0].min()),
01655                       0.5*(dirarr[1].max() + dirarr[1].min())]
01656             del dirarr
01657         elif (type(center) in (list, tuple)) and len(center) > 1:
01658             center = center[0:2]
01659         else:
01660             msg = "Direction of grid center should be a list of float (R.A., Dec.)"
01661             raise ValueError, msg
01662         asaplog.push("Grid center: (%f, %f) " % (center[0],center[1]))
01663 
01664         if spacing is None:
01665             #asaplog.post()
01666             asaplog.push("Grid spacing not specified. Automatically calculated from map coverage")
01667             #asaplog.post("WARN")
01668             # automatically get spacing
01669             dirarr = array(self._data.get_directionval()).transpose()
01670             wx = 2. * max(abs(dirarr[0].max()-center[0]),
01671                           abs(dirarr[0].min()-center[0]))
01672             wy = 2. * max(abs(dirarr[1].max()-center[1]),
01673                           abs(dirarr[1].min()-center[1]))
01674             ## slightly expand area to plot the edges
01675             #wx *= 1.01
01676             #wy *= 1.01
01677             #xgrid = wx/self._cols
01678             #ygrid = wy/self._rows
01679             xgrid = wx/max(self._cols-1.,1.)
01680             ygrid = wy/max(self._rows-1.,1.)
01681             #print "Pointing range: (x, y) = (%f - %f, %f - %f)" %\
01682             #  (dirarr[0].min(),dirarr[0].max(),dirarr[1].min(),dirarr[1].max())
01683             # identical R.A. and/or Dec. for all spectra.
01684             if xgrid == 0:
01685                 xgrid = 1.
01686             if ygrid == 0:
01687                 ygrid = 1.
01688             # spacing should be negative to transpose plot
01689             spacing = [- xgrid, - ygrid]
01690             del dirarr, xgrid, ygrid
01691         #elif isinstance(spacing, str):
01692         #    # spacing is a quantity
01693         elif (type(spacing) in (list, tuple)) and len(spacing) > 1:
01694             for i in xrange(2):
01695                 val = spacing[i]
01696                 if not isinstance(val, float):
01697                     raise TypeError("spacing should be a list of float")
01698                 if val > 0.:
01699                     spacing[i] = -val
01700             spacing = spacing[0:2]
01701             # Correction of Dec. effect
01702             spacing[0] /= cos(center[1])
01703         else:
01704             msg = "Invalid spacing."
01705             raise TypeError(msg)
01706         asaplog.push("Spacing: (%f, %f) (projected)" % (spacing[0],spacing[1]))
01707 
01708         ntotpl = self._rows * self._cols
01709         minpos = [center[0]-spacing[0]*self._cols/2.,
01710                   center[1]-spacing[1]*self._rows/2.]
01711         #print "Plot range: (x, y) = (%f - %f, %f - %f)" %\
01712         #      (minpos[0],minpos[0]+spacing[0]*self._cols,
01713         #       minpos[1],minpos[1]+spacing[1]*self._rows)
01714         ifs = self._data.getifnos()
01715         if len(ifs) > 1:
01716             msg = "Found multiple IFs in scantable. Only the first IF (IFNO=%d) will be plotted." % ifs[0]
01717             asaplog.post()
01718             asaplog.push(msg)
01719             asaplog.post("WARN")
01720         pols = self._data.getpolnos()
01721         if len(pols) > 1:
01722             msg = "Found multiple POLs in scantable. Only the first POL (POLNO=%d) will be plotted." % pols[0]
01723             asaplog.post()
01724             asaplog.push(msg)
01725             asaplog.post("WARN")
01726         beams = self._data.getbeamnos()
01727         if len(beams) > 1:
01728             msg = "Found multiple BEAMs in scantable. Only the first BEAM (BEAMNO=%d) will be plotted." % beams[0]
01729             asaplog.post()
01730             asaplog.push(msg)
01731             asaplog.post("WARN")
01732         self._data.set_selection(ifs=[ifs[0]],pols=[pols[0]],beams=[beams[0]])
01733         if self._data.nrow() > ntotpl:
01734             msg = "Scantable is finely sampled than plotting grids. "\
01735                   + "Only the first spectrum is plotted in each grid."
01736             asaplog.post()
01737             asaplog.push(msg)
01738             asaplog.post("WARN")
01739         
01740         self._assert_plotter(action="reload")
01741         self._plotter.hold()
01742         self._plotter.clear()
01743         self._plotter.legend()
01744         
01745         # Adjust subplot margins
01746         if not self._margins or len(self._margins) !=6:
01747             self.set_margin(refresh=False)
01748         self._plotter.set_panels(rows=self._rows,cols=self._cols,
01749                                  nplots=ntotpl,margin=self._margins,ganged=True)
01750         if self.casabar_exists():
01751             self._plotter.figmgr.casabar.set_pagecounter(1)
01752             self._plotter.figmgr.casabar.enable_button()
01753         # Actual plot
01754         npl = 0
01755         for irow in range(self._data.nrow()):
01756             pos = self._data.get_directionval(irow)
01757             ix = int((pos[0] - minpos[0])/spacing[0])
01758             if ix < 0 or ix >= self._cols:
01759                 #print "Row %d : Out of X-range (x = %f) ... skipped" % (irow, pos[0])
01760                 continue
01761             iy = int((pos[1]- minpos[1])/spacing[1])
01762             if iy < 0 or iy >= self._cols:
01763                 #print "Row %d : Out of Y-range (y = %f) ... skipped" % (irow,pos[1])
01764                 continue
01765             ipanel = ix + iy*self._cols
01766             if len(self._plotter.subplots[ipanel]['lines']) > 0:
01767                 #print "Row %d : panel %d lready plotted ... skipped" % (irow,ipanel)
01768                 # a spectrum already plotted in the panel
01769                 continue
01770             # Plotting this row
01771             #print "PLOTTING row %d (panel=%d)" % (irow, ipanel)
01772             npl += 1
01773             self._plotter.subplot(ipanel)
01774             self._plotter.palette(0,colormap=self._colormap, \
01775                                   linestyle=0,linestyles=self._linestyles)
01776             xlab = self._abcissa and self._abcissa[ipanel] \
01777                    or self._data._getabcissalabel(irow)
01778             if self._offset and not self._abcissa:
01779                 xlab += " (relative)"
01780             ylab = self._ordinate and self._ordinate[ipanel] \
01781                    or self._data._get_ordinate_label()
01782             self._plotter.set_axes('xlabel', xlab)
01783             self._plotter.set_axes('ylabel', ylab)
01784             #from numpy import pi
01785             #lbl = "(%f, %f)" % (self._data.get_directionval(irow)[0]*180/pi,self._data.get_directionval(irow)[1]*180./pi)
01786             lbl = self._data.get_direction(irow)
01787             self._plotter.set_axes('title',lbl)
01788 
01789             y = self._data._getspectrum(irow)
01790             # flag application
01791             mr = self._data._getflagrow(irow)
01792             if mr:  # FLAGROW=True
01793                 y = ma.masked_array(y,mask=mr)
01794             else:
01795                 m = self._data._getmask(irow)
01796                 from numpy import logical_not, logical_and
01797                 ### user mask is not available so far
01798                 #if self._maskselection and len(self._usermask) == len(m):
01799                 #    if d[self._stacking](irow) in self._maskselection[self._stacking]:
01800                 #            m = logical_and(m, self._usermask)
01801                 y = ma.masked_array(y,mask=logical_not(array(m,copy=False)))
01802 
01803             x = array(self._data._getabcissa(irow))
01804             if self._offset:
01805                 x += self._offset
01806             if self._minmaxx is not None:
01807                 s,e = self._slice_indeces(x)
01808                 x = x[s:e]
01809                 y = y[s:e]
01810             if len(x) > 1024 and rcParams['plotter.decimate']:
01811                 fac = len(x)/1024
01812                 x = x[::fac]
01813                 y = y[::fac]
01814             self._plotter.set_line(label=lbl)
01815             plotit = self._plotter.plot
01816             if self._hist: plotit = self._plotter.hist
01817             if len(x) > 0 and not mr:
01818                 plotit(x,y)
01819 #                 xlim= self._minmaxx or [min(x),max(x)]
01820 #                 allxlim += xlim
01821 #                 ylim= self._minmaxy or [ma.minimum(y),ma.maximum(y)]
01822 #                 allylim += ylim
01823 #             else:
01824 #                 xlim = self._minmaxx or []
01825 #                 allxlim += xlim
01826 #                 ylim= self._minmaxy or []
01827 #                 allylim += ylim
01828             
01829             if npl >= ntotpl:
01830                 break
01831             
01832         if self._minmaxy is not None:
01833             self._plotter.set_limits(ylim=self._minmaxy)
01834         self._plotter.release()
01835         self._plotter.tidy()
01836         self._plotter.show(hardrefresh=False)
01837         return