casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
TablePlotTkAgg.py
Go to the documentation of this file.
00001 import sys
00002 import os
00003 import pylab as pl
00004 import Tkinter as Tk
00005 from matplotlib.backend_bases import cursors
00006 import matplotlib
00007 rcParams = matplotlib.rcParams
00008 from matplotlib._pylab_helpers import Gcf
00009 
00010 cursord = {
00011     cursors.MOVE: "fleur",    
00012     cursors.HAND: "hand2",
00013     cursors.POINTER: "arrow",
00014     cursors.SELECT_REGION: "tcross",
00015     }
00016 
00017 class PlotFlag:   
00018     """
00019     (1) Start the internal python interpreter... 
00020     and make the 'pylab' module from the main python/casapy namespace 
00021     visible inside it. ( done inside TPPlotter )  Note that 'pylab' is the 
00022     only module of casapy that is visible from this internal interpreter.
00023     
00024     (2) figmanager = pl.get_current_fig_manager() 
00025     -> This gets a handle to the current window, canvas, toolbar.
00026         
00027     (3) Create the python-C++ call-back module -> PyBind. 
00028         ( description in tables/implement/TablePlot/PlotterGlobals.cc )
00029         
00030     (3) TablePlotTkAgg.py  implements a python class called 'PlotFlag'
00031         which takes an instance of 'PyBind' and 'figmanager' and makes 
00032         the connection between the two.
00033 
00034         - Additional buttons are placed in the toolbar, and their callbacks
00035           defined to call methods of PyBind.
00036         - The toolbar event-loop is captured - by explicitly disconnecting
00037           previous bindings, and re-defining them for 'pan','zoom','mark-region'
00038           modes. (need to do all three, to get them to interact properly with each other)
00039         - Some Canvas events are also redefined to allow mark-region boxes to
00040           automatically resize and move around, when the window is resized or
00041           when in pan or zoom modes. ( This is needed to allow flagging with
00042           zooming ).
00043          
00044     (4) Back to the internal python interpreter. The following steps are carried out.
00045         -> figmanager = pl.get_current_fig_manager()
00046         -> import PyBind
00047         -> from TablePlotTkagg import PlotFlag
00048         -> pf = PlotFlag( PyBind )
00049         -> pf.setup_custom_features( figmanager )
00050 
00051         ----> All binding is complete at this point.
00052         ----> All other logic is to ensure things like... make sure new buttons are
00053               added only when needed... make sure they *are* added when needed... and
00054               this has to keep up with the native TkAgg matplotlib backend's
00055               whimsical decisions of when to create a new figure and when not to.
00056         
00057     """
00058     def __init__(self,PyBind):
00059         #print "Init PlotFlag"
00060         self.PyBind = PyBind;
00061         self.newtoolbar = True;
00062         self.quitted = False;
00063 
00064     def sub(self):
00065         #pass
00066         self.quitted = True;
00067         self.PyBind.quit(True);
00068 
00069     def setup_custom_features(self,cfigman):
00070         if (rcParams['backend'].lower() == 'agg'):
00071             return
00072         self.toolbar = cfigman.toolbar;
00073         self.canvas = self.toolbar.canvas;
00074         self.window = cfigman.window;
00075         self.figmanager = cfigman;
00076         self.figmanager.window.wm_title("CASA Plotter");
00077         self.figmanager.window.protocol("WM_DELETE_WINDOW", self.sub);
00078         
00079         if self.newtoolbar is True:
00080                 # Add new buttons
00081                 self.add_buttons();
00082                 
00083                 # Reconfigure buttons.
00084                 self.configure_buttons();
00085 
00086                 self.newtoolbar = False;
00087 
00088         # Toolbar parameters
00089         self.panel=0;
00090         self.rows=0;
00091         self.cols=0;
00092         
00093         # Canvas parameters
00094         self.regionlist=[];
00095         self.panelregionlist=[];
00096         self.axeslist=[];
00097         self.erase_rects();
00098 
00099         # Re-Make event bindings
00100         self.canvas.keyvald.update({65307 : 'escape'});
00101         self.canvas.mpl_disconnect(self.toolbar._idDrag)
00102         self.toolbar._idDrag=self.canvas.mpl_connect('motion_notify_event', self.mouse_move)
00103         self.canvas._tkcanvas.bind("<KeyRelease>", self.key_release);
00104         self.canvas._tkcanvas.bind("<Configure>", self.resize);
00105         self.canvas._tkcanvas.bind("<Destroy>", self.destroy);
00106         #self.window.bind("<Destroy>", self.destroy);
00107 
00108 
00109     def plotflag_cleanup(self):
00110         self.canvas._tkcanvas.bind("<Destroy>", None);
00111 
00112     def set_cursor(self, cursor):
00113         self.toolbar.set_cursor(cursor);
00114         #self.toolbar.window.configure(cursor=cursord[cursor]);
00115 
00116     def _NewButton(self, frame, text, file, command, side=Tk.LEFT):
00117         #file = os.path.join(rcParams['datapath'], 'images', file)
00118         #file = '/opt/casa/stable/darwin/python/2.5' + file;
00119         #im = Tk.PhotoImage(master=frame, file=file)
00120         if(os.uname()[0] == 'Darwin'):
00121                 b = Tk.Button(master=frame, text=text, command=command)
00122         else:
00123                 b = Tk.Button(master=frame, text=text, padx=2, pady=2, command=command)
00124         #master=frame, text=text, padx=2, pady=2, image=im, command=command)
00125         #b._ntimage = im
00126         b.pack(side=side)
00127         return b
00128 
00129     def add_buttons(self):
00130         #self.newframe = Tk.Frame()
00131         self.newframe = Tk.Frame(master=self.window)
00132         bside = Tk.LEFT;
00133         self.toolbar.bMarkRegion = self._NewButton( frame=self.newframe, 
00134                                             text="Mark Region", 
00135                                             file="markregion.ppm",
00136                                             #file="markregion2.ppm",
00137                                             command=self.markregion,
00138                                             side=bside)
00139         self.toolbar.bFlag = self._NewButton(frame=self.newframe,
00140                                      text="Flag", 
00141                                      file="flag4.ppm",
00142                                      command=None,
00143                                      side=bside)
00144         
00145         self.toolbar.bUnflag = self._NewButton(frame=self.newframe,
00146                                        text="Unflag", 
00147                                        file="unflag4.ppm",
00148                                        command=None,
00149                                        side=bside)
00150         
00151         self.toolbar.bLocate = self._NewButton(frame=self.newframe,
00152                                        text="Locate", 
00153                                        file="locate4.ppm",
00154                                        command=None,
00155                                        side=bside)
00156         
00157         self.toolbar.bIterNext = self._NewButton(frame=self.newframe,
00158                                        text=" Next ", 
00159                                        file="locate4.ppm",
00160                                        command=None,
00161                                        side=bside)
00162         
00163         self.toolbar.bClear =None;
00164         #self.toolbar.bClear = self._NewButton(frame=self.newframe,
00165         #                               text=" Clear ", 
00166         #                              file="locate4.ppm",
00167         #                              command=None,
00168         #                              side=bside)
00169         
00170         self.toolbar.bQuit = self._NewButton(frame=self.newframe,
00171                                        text=" Quit ", 
00172                                        file="locate4.ppm",
00173                                        command=None,
00174                                        side=bside)
00175         self.toolbar.bMarkRegion.config(background='lightblue');
00176         self.toolbar.bFlag.config(background='lightblue');
00177         self.toolbar.bUnflag.config(background='lightblue');
00178         self.toolbar.bLocate.config(background='lightblue');
00179         self.toolbar.bIterNext.config(background='lightblue',state='disabled');
00180         #self.toolbar.bClear.config(background='lightblue');
00181         self.toolbar.bQuit.config(background='lightblue');
00182 
00183         self.newframe.pack(side=Tk.BOTTOM,fill=Tk.BOTH);
00184         #self.newframe.pack_propagate();
00185 
00186     def configure_buttons(self):
00187         self.toolbar.bHome.config(command=self.home);
00188         self.toolbar.bForward.config(command=self.forward);
00189         self.toolbar.bBack.config(command=self.back);
00190         self.toolbar.bsubplot.config(command=self.configure_subplots);
00191         self.toolbar.bPan.config(command=self.pan);
00192         self.toolbar.bZoom.config(command=self.zoom);
00193         self.toolbar.bMarkRegion.config(command=self.markregion);
00194         self.toolbar.bFlag.config(command=self.flag);
00195         self.toolbar.bUnflag.config(command=self.unflag);
00196         self.toolbar.bLocate.config(command=self.locate);
00197         self.toolbar.bIterNext.config(command=self.iterplotnext);
00198         #self.toolbar.bClear.config(command=self.clearplot);
00199         self.toolbar.bQuit.config(command=self.quit);
00200         #self.toolbar.bIterstop.config(command=self.iterplotstop);
00201         ### comment the next line when Wes updates matplotlib.
00202         #self.toolbar.bsave.config(command=self.savefig);
00203 
00204     def flag(self, *args):
00205         self.operate(1);
00206 
00207     def unflag(self, *args):
00208         self.operate(0);
00209 
00210     def locate(self, *args):
00211         self.operate(2);
00212 
00213     def operate(self, flag=1):
00214         #print '** Record the following regions'
00215         #for pr in self.canvas.panelregionlist:
00216                 #print 'Region on panel [%(r)d,%(c)d,%(p)d] : [%(t1).3f, %(t2).3f, %(t3).3f, %(t4).3f] '%{'r':pr[5],'c':pr[6], 'p':pr[4],'t1':pr[0],'t2':pr[2], 't3':pr[1], 't4':pr[3]};
00217         self.PyBind.markregion(self.panelregionlist);
00218         self.erase_rects();
00219         if( flag is 1 ):
00220                 #print "**Flag !!";
00221                 self.PyBind.flagdata();
00222         if( flag is 0 ):
00223                 #print "**UnFlag !!";
00224                 self.PyBind.unflagdata();
00225         if( flag is 2 ):
00226                 #print "**Locate !!";
00227                 self.PyBind.locatedata();
00228 
00229     def iterplotnext(self, *args):
00230         self.PyBind.iterplotnext();
00231 
00232     def iterplotstop(self, *args):
00233         self.PyBind.iterplotstop();
00234 
00235     def clearplot(self, *args):
00236         #print 'Gui::calling clearplot'
00237         self.PyBind.clearplot();
00238         #print 'Gui::finished clearplot'
00239 
00240     def savefig(self, *args):
00241         import time;
00242         fname = 'plot-casapy-'+time.strftime('%Y-%m-%dT%H:%M:%S')+'.png';
00243         print 'Saving figure as ', fname, ' in current working directory.'
00244         self.canvas.figure.savefig(fname);
00245 
00246     def enable_iter_button(self):
00247         if (rcParams['backend'].lower() == 'agg'):
00248             return
00249         if( self.toolbar.bIterNext is not None ):
00250                 self.toolbar.bIterNext.config(state='normal');
00251 
00252     def disable_iter_button(self):
00253         if (rcParams['backend'].lower() == 'agg'):
00254             return
00255         if( self.toolbar.bIterNext is not None ):
00256                 self.toolbar.bIterNext.config(state='disabled');
00257 
00258     def enable_markregion_button(self):
00259         if (rcParams['backend'].lower() == 'agg'):
00260             return
00261         if( self.toolbar.bMarkRegion is not None ):
00262                 self.toolbar.bMarkRegion.config(state='normal');
00263 
00264     def disable_markregion_button(self):
00265         if (rcParams['backend'].lower() == 'agg'):
00266             return
00267         if( self.toolbar.bMarkRegion is not None ):
00268                 self.toolbar.bMarkRegion.config(state='disabled');
00269 
00270     def enable_flag_button(self):
00271         if (rcParams['backend'].lower() == 'agg'):
00272             return
00273         if( self.toolbar.bFlag is not None ):
00274                 self.toolbar.bFlag.config(state='normal');
00275 
00276     def disable_flag_button(self):
00277         if (rcParams['backend'].lower() == 'agg'):
00278             return
00279         if( self.toolbar.bFlag is not None ):
00280                 self.toolbar.bFlag.config(state='disabled');
00281 
00282     def enable_unflag_button(self):
00283         if (rcParams['backend'].lower() == 'agg'):
00284             return
00285         if( self.toolbar.bUnflag is not None ):
00286                 self.toolbar.bUnflag.config(state='normal');
00287 
00288     def disable_unflag_button(self):
00289         if (rcParams['backend'].lower() == 'agg'):
00290             return
00291         if( self.toolbar.bUnflag is not None ):
00292                 self.toolbar.bUnflag.config(state='disabled');
00293 
00294     def enable_locate_button(self):
00295         if (rcParams['backend'].lower() == 'agg'):
00296             return
00297         if( self.toolbar.bLocate is not None ):
00298                 self.toolbar.bLocate.config(state='normal');
00299 
00300     def disable_locate_button(self):
00301         if (rcParams['backend'].lower() == 'agg'):
00302             return
00303         if( self.toolbar.bLocate is not None ):
00304                 self.toolbar.bLocate.config(state='disabled');
00305 
00306     def enable_clear_button(self):
00307         if (rcParams['backend'].lower() == 'agg'):
00308             return
00309         if( self.toolbar.bClear is not None ):
00310                 self.toolbar.bClear.config(state='normal');
00311 
00312     def disable_clear_button(self):
00313         if (rcParams['backend'].lower() == 'agg'):
00314             return
00315         if( self.toolbar.bClear is not None ):
00316                 self.toolbar.bClear.config(state='disabled');
00317 
00318     def enable_quit_button(self):
00319         if (rcParams['backend'].lower() == 'agg'):
00320             return
00321         if( self.toolbar.bQuit is not None ):
00322                 self.toolbar.bQuit.config(state='normal');
00323 
00324     def disable_quit_button(self):
00325         if (rcParams['backend'].lower() == 'agg'):
00326             return
00327         if( self.toolbar.bQuit is not None ):
00328                 self.toolbar.bQuit.config(state='disabled');
00329 
00330     def draw_rubberband(self, event, x0, y0, x1, y1):
00331         if (rcParams['backend'].lower() == 'agg'):
00332             return
00333         ### workaround for matplotlib API changes
00334         #height = self.canvas.figure.bbox.height()  #0.91.4
00335         #height = self.canvas.figure.bbox.height    #>=0.98
00336         height = self.get_bbox_size(self.canvas.figure.bbox,"height") #workaround
00337         y0 =  height-y0
00338         y1 =  height-y1
00339         try: self.toolbar.lastrect
00340         except AttributeError: pass
00341         else: self.canvas._tkcanvas.delete(self.toolbar.lastrect)
00342         self.toolbar.lastrect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1, width=2,outline='black')
00343 
00344 
00345     def draw_rect(self, x0, y0, x1, y1, x0data, y0data, x1data, y1data,a,panel,rows,cols):
00346         self.panelregionlist.append([x0data,y0data,x1data,y1data,panel+1,rows,cols]);
00347         self.axeslist.append(a);
00348         ### workaround for matplotlib API changes
00349         #height = self.canvas.figure.bbox.height()  #0.91.4
00350         #height = self.canvas.figure.bbox.height    #>=0.98
00351         height = self.get_bbox_size(self.canvas.figure.bbox,"height") #workaround
00352         y0 =  height-y0
00353         y1 =  height-y1
00354         if(os.uname()[0] == 'Darwin'):
00355                rect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1, width=2,outline='black')
00356         else:
00357                rect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1, width=2,fill='black',stipple='gray50',outline='black')
00358         self.regionlist.append(rect);
00359 
00360     def erase_rects(self):
00361         #print "erase rects"
00362         if (rcParams['backend'].lower() == 'agg'):
00363             return
00364         for q in self.regionlist:
00365           self.canvas._tkcanvas.delete(q);
00366         self.regionlist = [];
00367         self.panelregionlist = [];
00368         self.axeslist = [];
00369 
00370 
00371     def redraw_rects(self):
00372         for q in self.regionlist:
00373           self.canvas._tkcanvas.delete(q);
00374         self.regionlist = [];
00375         
00376         for z in range(0,len(self.panelregionlist)):
00377           q = self.panelregionlist[z];
00378           a = self.axeslist[z];
00379           x0=q[0]; y0=q[1]; x1=q[2]; y1=q[3];
00380           # map to new zoom limits (current fig co-ords)
00381           ### workaround for matplotlib API changes
00382           #px0,py0 = a.transData.xy_tup( (x0, y0) )     #0.91
00383           #px0,py0 = a.transData.transform( (x0, y0) )  #>=0.98
00384           px0,py0 = self.get_xy(a.transData, (x0, y0) ) #workaround
00385           ### workaround for matplotlib API changes
00386           #px1,py1 = a.transData.xy_tup( (x1, y1) )     #0.91
00387           #px1,py1 = a.transData.transform( (x1, y1) )  #>=0.98
00388           px1,py1 = self.get_xy(a.transData, (x1, y1) )
00389 
00390           ### workaround for matplotlib API changes
00391           #height = self.canvas.figure.bbox.height()   #0.91
00392           #height = self.canvas.figure.bbox.height     #>=0.98
00393           height = self.get_bbox_size(self.canvas.figure.bbox,"height") #workaround
00394           py0 =  height-py0
00395           py1 =  height-py1
00396           if(os.uname()[0] == 'Darwin'):
00397                rect = self.canvas._tkcanvas.create_rectangle(px0, py0, px1, py1, width=2,outline='black')
00398           else:
00399                rect = self.canvas._tkcanvas.create_rectangle(px0, py0, px1, py1, width=2,fill='black',stipple='gray50',outline='black')
00400           self.regionlist.append(rect);
00401 
00402     def resize(self, event):
00403         #print 'canvas resize'
00404         self.canvas.resize(event);
00405         self.redraw_rects();
00406 
00407     def destroy(self,*args):
00408         #print 'Gui::destroy.'
00409         self.erase_rects();
00410         self.newtoolbar = True;
00411         if self.quitted is False:
00412                 self.quit(closewin=True);
00413                 print " ";
00414                 #print "................................................................";
00415                 #print "............. Please IGNORE Tkinter error message. .............";
00416                 #print "................................................................";
00417 
00418     def quit(self, closewin=True):
00419         #print 'quit with close-window : ', closewin;
00420         self.quitted = True;
00421         self.PyBind.quit(closewin);
00422 
00423     def key_release(self, event):
00424         #print 'key release'
00425         self.canvas.key_release(event);
00426         key = self.canvas._get_key(event);
00427         if(key=='escape'):
00428                 numreg = len(self.regionlist);
00429                 if(numreg>0):
00430                         self.canvas._tkcanvas.delete(self.regionlist[numreg-1]);
00431                         self.regionlist.pop();
00432                         self.panelregionlist.pop();
00433                         self.axeslist.pop();
00434 
00435 
00436     def home(self, *args):
00437         'restore the original view'
00438         if (rcParams['backend'].lower() == 'agg'):
00439             return
00440         self.toolbar.home();
00441         self.redraw_rects();
00442 
00443     def back(self, *args):
00444         'move back up the view lim stack'
00445         if (rcParams['backend'].lower() == 'agg'):
00446             return
00447         self.toolbar.back();
00448         self.redraw_rects();
00449 
00450     def forward(self, *args):
00451         'move forward in the view lim stack'
00452         if (rcParams['backend'].lower() == 'agg'):
00453             return
00454         self.toolbar.forward();
00455         self.redraw_rects();
00456 
00457     def configure_subplots(self):
00458         'configure subplots'
00459         if (rcParams['backend'].lower() == 'agg'):
00460             return
00461         self.toolbar.configure_subplots();
00462         self.redraw_rects();
00463 
00464 
00465     def markregion(self, *args):
00466         'activate mark-region mode'
00467         if (rcParams['backend'].lower() == 'agg'):
00468             return
00469         if self.toolbar._active == 'MARKREGION':
00470             #self.toolbar._active = None
00471             self.erase_rects();
00472             self.update_relief(newmode=None);
00473         else:
00474             #self.toolbar._active = 'MARKREGION'
00475             self.update_relief(newmode='MARKREGION');
00476             
00477 
00478         if self.toolbar._idPress is not None:
00479             self.toolbar._idPress=self.canvas.mpl_disconnect(self.toolbar._idPress)
00480             self.toolbar.mode = ''
00481 
00482         if self.toolbar._idRelease is not None:
00483             self.toolbar._idRelease=self.canvas.mpl_disconnect(self.toolbar._idRelease)
00484             self.toolbar.mode = ''
00485 
00486         if  self.toolbar._active:
00487             self.toolbar._idPress = self.canvas.mpl_connect('button_press_event', self.press_markregion)
00488             self.toolbar._idRelease = self.canvas.mpl_connect('button_release_event', self.release_markregion)
00489             self.toolbar.mode = 'Mark Region mode'
00490             self.canvas.widgetlock(self.toolbar)
00491         else:
00492             self.canvas.widgetlock.release(self.toolbar)
00493 
00494         for a in self.canvas.figure.get_axes():
00495             a.set_navigate_mode(self.toolbar._active)
00496 
00497         self.toolbar.set_message(self.toolbar.mode)
00498 
00499     def press_markregion(self, event):
00500         'the press mouse button in mark region mode callback'
00501         if (rcParams['backend'].lower() == 'agg'):
00502             return
00503         if event.button == 1:
00504             self.toolbar._button_pressed=1
00505         elif  event.button == 3:
00506             self.toolbar._button_pressed=3
00507         else:
00508             self.toolbar._button_pressed=None
00509             return
00510         
00511         # Check that the click is inside the canvas.
00512 
00513         x, y = event.x, event.y
00514 
00515         # push the current view to define home if stack is empty
00516         if self.toolbar._views.empty(): self.toolbar.push_current()
00517 
00518         self.toolbar._xypress=[]
00519         for i, a in enumerate(self.canvas.figure.get_axes()):
00520             #if x is not None and y is not None and a.in_axes(x, y) and a.get_navigate():
00521             if x is not None and y is not None and event.inaxes==a and a.get_navigate():
00522                 xmin, xmax = a.get_xlim()
00523                 ymin, ymax = a.get_ylim()
00524                 lim = xmin, xmax, ymin, ymax
00525                 ### workaround for matplotlib API changes
00526                 #self.toolbar._xypress.append(( x, y, a, i, lim, a.transData.deepcopy() ))  #0.91.4
00527                 #self.toolbar._xypress.append(( x, y, a, i, lim, a.transData.frozen() ))    #>=0.98
00528                 self.toolbar._xypress.append(( x, y, a, i, lim, self.copy_trans(a.transData)))  #workaround
00529                 one, two, three = event.inaxes.get_geometry()
00530                 self.panel = three-1
00531                 self.rows = one
00532                 self.cols = two
00533 
00534         self.toolbar.press(event)
00535 
00536     def release_markregion(self, event):
00537         'the release mouse button callback in mark region mode'
00538         if (rcParams['backend'].lower() == 'agg'):
00539             return
00540         if not self.toolbar._xypress: return
00541 
00542         for cur_xypress in self.toolbar._xypress:
00543             x, y = event.x, event.y
00544             lastx, lasty, a, ind, lim, trans = cur_xypress
00545 
00546             xmin, ymin, xmax, ymax = lim
00547 
00548             # mark rect
00549             ### workaround for matplotlib API changes
00550             #lastx, lasty = a.transData.inverse_xy_tup( (lastx, lasty) )        #0.91.4
00551             #lastx, lasty = a.transData.inverted().transform( (lastx, lasty) )  #>=0.98
00552             lastx, lasty = self.get_inverse_xy(a.transData, (lastx, lasty) )    #workaround
00553             ### workaround for matplotlib API changes
00554             #x, y = a.transData.inverse_xy_tup( (x, y) )        #0.91.4
00555             #x, y = a.transData.inverted().transform( (x, y) )  #>=0.98
00556             x, y = self.get_inverse_xy(a.transData, (x, y) )    #workaround
00557             Xmin,Xmax=a.get_xlim()
00558             Ymin,Ymax=a.get_ylim()
00559 
00560             if Xmin < Xmax:
00561                 if x<lastx:  xmin, xmax = x, lastx
00562                 else: xmin, xmax = lastx, x
00563                 if xmin < Xmin: xmin=Xmin
00564                 if xmax > Xmax: xmax=Xmax
00565             else:
00566                 if x>lastx:  xmin, xmax = x, lastx
00567                 else: xmin, xmax = lastx, x
00568                 if xmin > Xmin: xmin=Xmin
00569                 if xmax < Xmax: xmax=Xmax
00570 
00571             if Ymin < Ymax:
00572                 if y<lasty:  ymin, ymax = y, lasty
00573                 else: ymin, ymax = lasty, y
00574                 if ymin < Ymin: ymin=Ymin
00575                 if ymax > Ymax: ymax=Ymax
00576             else:
00577                 if y>lasty:  ymin, ymax = y, lasty
00578                 else: ymin, ymax = lasty, y
00579                 if ymin > Ymin: ymin=Ymin
00580                 if ymax < Ymax: ymax=Ymax
00581 
00582         ### workaround for matplotlib API changes
00583         #px1,py1 = a.transData.xy_tup( (xmin, ymin) )     #0.91.4
00584         #px1,py1 = a.transData.transform( (xmin, ymin) )  #>=0.98
00585         px1,py1 = self.get_xy(a.transData, (xmin, ymin) ) #workaround
00586         #px2,py2 = a.transData.xy_tup( (xmax, ymax) )     #0.91.4
00587         #px2,py2 = a.transData.transform( (xmax, ymax) )  #>=0.98
00588         px2,py2 = self.get_xy(a.transData, (xmax, ymax) ) #workaround
00589             
00590         self.draw_rect(px1, py1, px2, py2, xmin, ymin, xmax, ymax, a, self.panel, self.rows, self.cols)
00591         #print 'Region on panel [%(r)d,%(c)d,%(p)d] : [%(t1).3f, %(t2).3f, %(t3).3f, %(t4).3f] '%{'r':self.rows,'c':self.cols, 'p':self.panel+1,'t1':xmin,'t2':xmax, 't3':ymin, 't4':ymax};
00592         
00593                 
00594         #self.toolbar.draw()
00595         self.toolbar._xypress = None
00596         self.toolbar._button_pressed = None
00597 
00598         self.toolbar.push_current()
00599         self.toolbar.release(event)
00600 
00601 
00602     def zoom(self, *args):
00603         'activate zoom to rect mode'
00604         if (rcParams['backend'].lower() == 'agg'):
00605             return
00606         if self.toolbar._active == 'ZOOM':
00607             #self.toolbar._active = None
00608             self.update_relief(newmode=None);
00609         else:
00610             #self.toolbar._active = 'ZOOM'
00611             self.update_relief(newmode='ZOOM');
00612 
00613         if self.toolbar._idPress is not None:
00614             self.toolbar._idPress=self.canvas.mpl_disconnect(self.toolbar._idPress)
00615             self.toolbar.mode = ''
00616 
00617         if self.toolbar._idRelease is not None:
00618             self.toolbar._idRelease=self.canvas.mpl_disconnect(self.toolbar._idRelease)
00619             self.toolbar.mode = ''
00620 
00621         if  self.toolbar._active:
00622             self.toolbar._idPress = self.canvas.mpl_connect('button_press_event', self.press_zoom)
00623             self.toolbar._idRelease = self.canvas.mpl_connect('button_release_event', self.release_zoom)
00624             self.toolbar.mode = 'Zoom to rect mode'
00625             self.canvas.widgetlock(self.toolbar)
00626         else:
00627             self.canvas.widgetlock.release(self.toolbar)
00628 
00629         for a in self.canvas.figure.get_axes():
00630             a.set_navigate_mode(self.toolbar._active)
00631 
00632         self.toolbar.set_message(self.toolbar.mode)
00633 
00634 
00635     def press_zoom(self, event):
00636         'the press mouse button in zoom to rect mode callback'
00637         if (rcParams['backend'].lower() == 'agg'):
00638             return
00639         if event.button == 1:
00640             self.toolbar._button_pressed=1
00641         elif  event.button == 3:
00642             self.toolbar._button_pressed=3
00643         else:
00644             self.toolbar._button_pressed=None
00645             return
00646 
00647         x, y = event.x, event.y
00648 
00649         # push the current view to define home if stack is empty
00650         if self.toolbar._views.empty(): self.toolbar.push_current()
00651 
00652         self.toolbar._xypress=[]
00653         for i, a in enumerate(self.canvas.figure.get_axes()):
00654             #if x is not None and y is not None and a.in_axes(x, y) and a.get_navigate():
00655             if x is not None and y is not None and event.inaxes==a and a.get_navigate():
00656                 xmin, xmax = a.get_xlim()
00657                 ymin, ymax = a.get_ylim()
00658                 lim = xmin, xmax, ymin, ymax
00659                 ### workaround for matplotlib API changes
00660                 #self.toolbar._xypress.append(( x, y, a, i, lim, a.transData.deepcopy() ))   #0.91.4
00661                 #self.toolbar._xypress.append(( x, y, a, i, lim, a.transData.frozen() ))     #>=0.98
00662                 self.toolbar._xypress.append(( x, y, a, i, lim, self.copy_trans(a.transData)))  #workaround
00663 
00664         self.toolbar.press(event)
00665 
00666 
00667     def release_zoom(self, event):
00668         'the release mouse button callback in zoom to rect mode'
00669         if (rcParams['backend'].lower() == 'agg'):
00670             return
00671         if not self.toolbar._xypress: return
00672 
00673         for cur_xypress in self.toolbar._xypress:
00674             x, y = event.x, event.y
00675             lastx, lasty, a, ind, lim, trans = cur_xypress
00676             # ignore singular clicks - 5 pixels is a threshold
00677             if abs(x-lastx)<5 or abs(y-lasty)<5:
00678                 self.toolbar._xypress = None
00679                 self.toolbar.release(event)
00680                 self.toolbar.draw()
00681                 return
00682 
00683             xmin, ymin, xmax, ymax = lim
00684 
00685             # zoom to rect
00686             ### workaround for matplotlib API changes
00687             #lastx, lasty = a.transData.inverse_xy_tup( (lastx, lasty) )        #0.91.4
00688             #lastx, lasty = a.transData.inverted().transform( (lastx, lasty) )  #>=0.98
00689             lastx, lasty = self.get_inverse_xy(a.transData, (lastx, lasty) )    #workaround
00690             ### workaround for matplotlib API changes
00691             #x, y = a.transData.inverse_xy_tup( (x, y) )        #0.91.4
00692             #x, y = a.transData.inverted().transform( (x, y) )  #>=0.98
00693             x, y = self.get_inverse_xy(a.transData, (x, y) )    #workaround
00694             Xmin,Xmax=a.get_xlim()
00695             Ymin,Ymax=a.get_ylim()
00696 
00697             if Xmin < Xmax:
00698                 if x<lastx:  xmin, xmax = x, lastx
00699                 else: xmin, xmax = lastx, x
00700                 if xmin < Xmin: xmin=Xmin
00701                 if xmax > Xmax: xmax=Xmax
00702             else:
00703                 if x>lastx:  xmin, xmax = x, lastx
00704                 else: xmin, xmax = lastx, x
00705                 if xmin > Xmin: xmin=Xmin
00706                 if xmax < Xmax: xmax=Xmax
00707 
00708             if Ymin < Ymax:
00709                 if y<lasty:  ymin, ymax = y, lasty
00710                 else: ymin, ymax = lasty, y
00711                 if ymin < Ymin: ymin=Ymin
00712                 if ymax > Ymax: ymax=Ymax
00713             else:
00714                 if y>lasty:  ymin, ymax = y, lasty
00715                 else: ymin, ymax = lasty, y
00716                 if ymin > Ymin: ymin=Ymin
00717                 if ymax < Ymax: ymax=Ymax
00718 
00719             if self.toolbar._button_pressed == 1:
00720                 a.set_xlim((xmin, xmax))
00721                 a.set_ylim((ymin, ymax))
00722             elif self.toolbar._button_pressed == 3:
00723                 if a.get_xscale()=='log':
00724                     alpha=log(Xmax/Xmin)/log(xmax/xmin)
00725                     x1=pow(Xmin/xmin,alpha)*Xmin
00726                     x2=pow(Xmax/xmin,alpha)*Xmin
00727                 else:
00728                     alpha=(Xmax-Xmin)/(xmax-xmin)
00729                     x1=alpha*(Xmin-xmin)+Xmin
00730                     x2=alpha*(Xmax-xmin)+Xmin
00731                 if a.get_yscale()=='log':
00732                     alpha=log(Ymax/Ymin)/log(ymax/ymin)
00733                     y1=pow(Ymin/ymin,alpha)*Ymin
00734                     y2=pow(Ymax/ymin,alpha)*Ymin
00735                 else:
00736                     alpha=(Ymax-Ymin)/(ymax-ymin)
00737                     y1=alpha*(Ymin-ymin)+Ymin
00738                     y2=alpha*(Ymax-ymin)+Ymin
00739                 a.set_xlim((x1, x2))
00740                 a.set_ylim((y1, y2))
00741 
00742         self.toolbar.draw()
00743         self.redraw_rects();
00744         self.toolbar._xypress = None
00745         self.toolbar._button_pressed = None
00746 
00747         self.toolbar.push_current()
00748         self.toolbar.release(event)
00749 
00750 
00751     def pan(self,*args):
00752         'Activate the pan/zoom tool. pan with left button, zoom with right'
00753         # set the pointer icon and button press funcs to the
00754         # appropriate callbacks
00755         if (rcParams['backend'].lower() == 'agg'):
00756             return
00757 
00758         if self.toolbar._active == 'PAN':
00759             #self.toolbar._active = None
00760             self.update_relief(newmode=None);
00761         else:
00762             #self.toolbar._active = 'PAN'
00763             self.update_relief(newmode='PAN');
00764 
00765         if self.toolbar._idPress is not None:
00766             self.toolbar._idPress = self.canvas.mpl_disconnect(self.toolbar._idPress)
00767             self.toolbar.mode = ''
00768 
00769         if self.toolbar._idRelease is not None:
00770             self.toolbar._idRelease = self.canvas.mpl_disconnect(self.toolbar._idRelease)
00771             self.toolbar.mode = ''
00772 
00773         if self.toolbar._active:
00774             self.toolbar._idPress = self.canvas.mpl_connect(
00775                 'button_press_event', self.press_pan)
00776             self.toolbar._idRelease = self.canvas.mpl_connect(
00777                 'button_release_event', self.release_pan)
00778             self.toolbar.mode = 'pan/zoom mode'
00779             self.canvas.widgetlock(self.toolbar)
00780         else:
00781             self.canvas.widgetlock.release(self.toolbar)
00782 
00783         for a in self.canvas.figure.get_axes():
00784             a.set_navigate_mode(self.toolbar._active)
00785 
00786         self.toolbar.set_message(self.toolbar.mode)
00787 
00788 
00789     def press_pan(self, event):
00790         'the press mouse button in pan/zoom mode callback'
00791         if (rcParams['backend'].lower() == 'agg'):
00792             return
00793 
00794         if event.button == 1:
00795             self.toolbar._button_pressed=1
00796         elif  event.button == 3:
00797             self.toolbar._button_pressed=3
00798         else:
00799             self.toolbar._button_pressed=None
00800             return
00801 
00802         x, y = event.x, event.y
00803 
00804         # push the current view to define home if stack is empty
00805         if self.toolbar._views.empty(): self.toolbar.push_current()
00806 
00807         self.toolbar._xypress=[]
00808         for i, a in enumerate(self.canvas.figure.get_axes()):
00809             #if x is not None and y is not None and a.in_axes(x, y) and a.get_navigate():
00810             if x is not None and y is not None and event.inaxes==a and a.get_navigate():
00811                 xmin, xmax = a.get_xlim()
00812                 ymin, ymax = a.get_ylim()
00813                 lim = xmin, xmax, ymin, ymax
00814                 ### workaround for matplotlib API changes
00815                 #self.toolbar._xypress.append((x, y, a, i, lim,a.transData.deepcopy()))  #0.91.4
00816                 #self.toolbar._xypress.append((x, y, a, i, lim,a.transData.frozen()))    #>=0.98
00817                 self.toolbar._xypress.append((x, y, a, i, lim,self.copy_trans(a.transData))) #workaround
00818                 self.canvas.mpl_disconnect(self.toolbar._idDrag)
00819                 self.toolbar._idDrag=self.canvas.mpl_connect('motion_notify_event', self.drag_pan)
00820 
00821         self.toolbar.press(event)
00822 
00823 
00824     def release_pan(self, event):
00825         'the release mouse button callback in pan/zoom mode'
00826         if (rcParams['backend'].lower() == 'agg'):
00827             return
00828         self.canvas.mpl_disconnect(self.toolbar._idDrag)
00829         self.toolbar._idDrag=self.canvas.mpl_connect('motion_notify_event', self.mouse_move)
00830         if not self.toolbar._xypress: return
00831         self.toolbar._xypress = None
00832         self.toolbar._button_pressed=None
00833         self.toolbar.push_current()
00834         self.toolbar.release(event)
00835         self.toolbar.draw()
00836         self.redraw_rects();
00837 
00838 
00839     def drag_pan(self, event):
00840         'the drag callback in pan/zoom mode'
00841         if (rcParams['backend'].lower() == 'agg'):
00842             return
00843 
00844         def format_deltas(event,dx,dy):
00845             if event.key=='control':
00846                 if(abs(dx)>abs(dy)):
00847                     dy = dx
00848                 else:
00849                     dx = dy
00850             elif event.key=='x':
00851                 dy = 0
00852             elif event.key=='y':
00853                 dx = 0
00854             elif event.key=='shift':
00855                 if 2*abs(dx) < abs(dy):
00856                     dx=0
00857                 elif 2*abs(dy) < abs(dx):
00858                     dy=0
00859                 elif(abs(dx)>abs(dy)):
00860                     dy=dy/abs(dy)*abs(dx)
00861                 else:
00862                     dx=dx/abs(dx)*abs(dy)
00863             return (dx,dy)
00864 
00865         for cur_xypress in self.toolbar._xypress:
00866             lastx, lasty, a, ind, lim, trans = cur_xypress
00867             xmin, xmax, ymin, ymax = lim
00868             #safer to use the recorded button at the press than current button:
00869             #multiple button can get pressed during motion...
00870             if self.toolbar._button_pressed==1:
00871                 ### workaround for matplotlib API changes
00872                 #lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) )          #0.91.4
00873                 #lastx, lasty = trans.inverted().transform( (lastx, lasty) )    #>=0.98
00874                 lastx, lasty = self.get_inverse_xy(trans, (lastx, lasty) )      #workaround
00875                 ### workaround for matplotlib API changes
00876                 #x, y = trans.inverse_xy_tup( (event.x, event.y) )        #0.91.4
00877                 #x, y = trans.inverted().transform( (event.x, event.y) )  #>=0.98
00878                 x, y = self.get_inverse_xy(trans, (event.x, event.y) )    #workaround
00879                 if a.get_xscale()=='log':
00880                     dx=1-lastx/x
00881                 else:
00882                     dx=x-lastx
00883                 if a.get_yscale()=='log':
00884                     dy=1-lasty/y
00885                 else:
00886                     dy=y-lasty
00887 
00888                 dx,dy=format_deltas(event,dx,dy)
00889 
00890                 if a.get_xscale()=='log':
00891                     xmin *= 1-dx
00892                     xmax *= 1-dx
00893                 else:
00894                     xmin -= dx
00895                     xmax -= dx
00896                 if a.get_yscale()=='log':
00897                     ymin *= 1-dy
00898                     ymax *= 1-dy
00899                 else:
00900                     ymin -= dy
00901                     ymax -= dy
00902             elif self.toolbar._button_pressed==3:
00903                 try:
00904                     ### workaround for matplotlib API changes
00905                     #dx=(lastx-event.x)/float(a.bbox.width())  #0.91.4
00906                     #dx=(lastx-event.x)/float(a.bbox.width)    #>=0.98
00907                     dx=(lastx-event.x)/float(self.get_bbox_size(a.bbox,"width")) #workaround
00908                     ### workaround for matplotlib API changes
00909                     #dy=(lasty-event.y)/float(a.bbox.height()) #0.91.4
00910                     #dy=(lasty-event.y)/float(a.bbox.height)   #>=0.98
00911                     dy=(lasty-event.y)/float(self.get_bbox_size(a.bbox,"height"))  #workaround
00912                     dx,dy=format_deltas(event,dx,dy)
00913                     if a.get_aspect() != 'auto':
00914                         dx = 0.5*(dx + dy)
00915                         dy = dx
00916                     alphax = pow(10.0,dx)
00917                     alphay = pow(10.0,dy)#use logscaling, avoid singularities and smother scaling...
00918                     ### workaround for matplotlib API changes
00919                     #lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) )       #0.91.4
00920                     #lastx, lasty = trans.inverted().transform( (lastx, lasty) ) #>=0.98
00921                     lastx, lasty = self.get_inverse_xy(trans, (lastx, lasty) )   #workaround
00922                     if a.get_xscale()=='log':
00923                         xmin = lastx*(xmin/lastx)**alphax
00924                         xmax = lastx*(xmax/lastx)**alphax
00925                     else:
00926                         xmin = lastx+alphax*(xmin-lastx)
00927                         xmax = lastx+alphax*(xmax-lastx)
00928                     if a.get_yscale()=='log':
00929                         ymin = lasty*(ymin/lasty)**alphay
00930                         ymax = lasty*(ymax/lasty)**alphay
00931                     else:
00932                         ymin = lasty+alphay*(ymin-lasty)
00933                         ymax = lasty+alphay*(ymax-lasty)
00934                 except OverflowError:
00935                     warnings.warn('Overflow while panning')
00936                     return
00937             a.set_xlim(xmin, xmax)
00938             a.set_ylim(ymin, ymax)
00939             self.redraw_rects();
00940 
00941         self.toolbar.dynamic_update()
00942 
00943 
00944     def mouse_move(self, event):
00945         #print 'mouse_move', event.button
00946         if (rcParams['backend'].lower() == 'agg'):
00947             return
00948 
00949         if not event.inaxes or not self.toolbar._active:
00950             if self.toolbar._lastCursor != cursors.POINTER:
00951                 self.set_cursor(cursors.POINTER)
00952                 self.toolbar._lastCursor = cursors.POINTER
00953         else:
00954             if self.toolbar._active=='ZOOM':
00955                 if self.toolbar._lastCursor != cursors.SELECT_REGION:
00956                     self.set_cursor(cursors.SELECT_REGION)
00957                     self.toolbar._lastCursor = cursors.SELECT_REGION
00958                 if self.toolbar._xypress:
00959                     x, y = event.x, event.y
00960                     lastx, lasty, a, ind, lim, trans= self.toolbar._xypress[0]
00961                     self.draw_rubberband(event, x, y, lastx, lasty)
00962             elif self.toolbar._active=='MARKREGION':
00963                 if self.toolbar._lastCursor != cursors.SELECT_REGION:
00964                     self.set_cursor(cursors.SELECT_REGION)
00965                     self.toolbar._lastCursor = cursors.SELECT_REGION
00966                 if self.toolbar._xypress:
00967                     x, y = event.x, event.y
00968                     lastx, lasty, a, ind, lim, trans= self.toolbar._xypress[0]
00969                     self.draw_rubberband(event, x, y, lastx, lasty)
00970             elif (self.toolbar._active=='PAN' and
00971                   self.toolbar._lastCursor != cursors.MOVE):
00972                 self.set_cursor(cursors.MOVE)
00973 
00974                 self.toolbar._lastCursor = cursors.MOVE
00975 
00976         if event.inaxes and event.inaxes.get_navigate():
00977 
00978 
00979             try: s = event.inaxes.format_coord(event.xdata, event.ydata)
00980             except ValueError: pass
00981             except OverflowError: pass
00982             else:
00983                 if len(self.toolbar.mode):
00984                     self.toolbar.set_message('%s : %s' % (self.toolbar.mode, s))
00985                 else:
00986                     self.toolbar.set_message(s)
00987         else: self.toolbar.set_message(self.toolbar.mode)
00988 
00989 
00990     def update_relief(self,newmode):
00991         'activate new mode'
00992         if (rcParams['backend'].lower() == 'agg'):
00993             return
00994         if self.toolbar._active == 'ZOOM':
00995             self.toolbar.bZoom.config(relief='raised');
00996         if self.toolbar._active == 'PAN':
00997             self.toolbar.bPan.config(relief='raised');
00998         if self.toolbar._active == 'MARKREGION':
00999             self.toolbar.bMarkRegion.config(relief='raised');
01000          
01001         self.toolbar._active = newmode;
01002 
01003         if self.toolbar._active == 'ZOOM':
01004             self.toolbar.bZoom.config(relief='sunken');
01005         if self.toolbar._active == 'PAN':
01006             self.toolbar.bPan.config(relief='sunken');
01007         if self.toolbar._active == 'MARKREGION':
01008             self.toolbar.bMarkRegion.config(relief='sunken');
01009 
01010 
01011     #### Workarounds for Matplotlib version handling (ugly) ####
01012     def ismatlab_new(self):
01013         verstr=matplotlib.__version__.split(".")
01014         maj=int(verstr[0])
01015         sub=int(verstr[1])
01016         return (maj>0 or sub>=98)
01017 
01018     def get_inverse_xy(self,trans,(x,y)):
01019         if hasattr(trans,"inverse_xy_tup"): return trans.inverse_xy_tup((x, y))
01020         elif hasattr(trans,"inverted"): return trans.inverted().transform((x, y))
01021         else: return None
01022 
01023     def get_xy(self,trans,(x,y)):
01024         return self.switch_func(trans,["xy_tup","transform"],(x,y))
01025 
01026     def copy_trans(self,trans): 
01027         return self.switch_func(trans,["deepcopy","frozen"])
01028         
01029     def get_bbox_size(self,obj,func=""):
01030         return self.get_called_or_attr(obj,func)
01031 
01032     def switch_func(self,obj,funcs=[],*args,**kwargs):
01033         """
01034         Tries a list of functions and return a result from callable one.
01035         Deals with function name changes but parameters have to be unchanged.
01036         """
01037         for func in funcs:
01038             called_func=self.get_called_or_attr(obj,func,*args,**kwargs)
01039             if called_func != None: break
01040         return called_func
01041 
01042     def get_called_or_attr(self,obj,func="",*args,**kwargs):
01043         """
01044         Returns a result from function call if it's callable.
01045         If not callable, returns the attribute or False (non-existent). 
01046         """
01047         #if not hasattr(obj,func): return False
01048         #else: called=getattr(obj,func)
01049         try: called=getattr(obj,func)
01050         #except: return False
01051         except: return None
01052         if callable(called): return called(*args,**kwargs)
01053         else: return called
01054         
01055